diff --git a/pkgbuilds/edge/dell-xps-touchpad-haptics/.SRCINFO b/pkgbuilds/edge/dell-xps-touchpad-haptics/.SRCINFO new file mode 100644 index 00000000..6964772a --- /dev/null +++ b/pkgbuilds/edge/dell-xps-touchpad-haptics/.SRCINFO @@ -0,0 +1,27 @@ +pkgbase = dell-xps-touchpad-haptics + pkgdesc = Synaptics haptic touchpad presets for Dell XPS on Omarchy + pkgver = 1.0.0 + pkgrel = 3 + url = https://github.com/omacom-io/omarchy-pkgs + install = dell-xps-touchpad-haptics.install + arch = x86_64 + license = MIT + depends = python + provides = omarchy-dell-haptic-touchpad + conflicts = omarchy-dell-haptic-touchpad + replaces = omarchy-dell-haptic-touchpad + options = !debug + source = 99-dell-xps-touchpad-haptics.rules + source = README.package.md + source = dell-xps-touchpad-haptics + source = dell-xps-touchpad-haptics-daemon + source = dell-xps-touchpad-haptics.install + source = dell-xps-touchpad-haptics.service + sha256sums = SKIP + sha256sums = SKIP + sha256sums = SKIP + sha256sums = SKIP + sha256sums = SKIP + sha256sums = SKIP + +pkgname = dell-xps-touchpad-haptics diff --git a/pkgbuilds/edge/dell-xps-touchpad-haptics/99-dell-xps-touchpad-haptics.rules b/pkgbuilds/edge/dell-xps-touchpad-haptics/99-dell-xps-touchpad-haptics.rules new file mode 100644 index 00000000..c917bf17 --- /dev/null +++ b/pkgbuilds/edge/dell-xps-touchpad-haptics/99-dell-xps-touchpad-haptics.rules @@ -0,0 +1,2 @@ +ACTION=="add", SUBSYSTEM=="pci", KERNEL=="0000:00:19.0", ATTR{power/control}="on" +ACTION=="add", SUBSYSTEM=="platform", KERNEL=="i2c_designware.0", ATTR{power/control}="on" diff --git a/pkgbuilds/edge/dell-xps-touchpad-haptics/PKGBUILD b/pkgbuilds/edge/dell-xps-touchpad-haptics/PKGBUILD new file mode 100644 index 00000000..68d9967f --- /dev/null +++ b/pkgbuilds/edge/dell-xps-touchpad-haptics/PKGBUILD @@ -0,0 +1,32 @@ +# Maintainer: Jacob Mink + +pkgname=dell-xps-touchpad-haptics +pkgver=1.0.0 +pkgrel=3 +pkgdesc="Synaptics haptic touchpad presets for Dell XPS on Omarchy" +arch=('x86_64') +url="https://github.com/omacom-io/omarchy-pkgs" +license=('MIT') +depends=('python') +conflicts=('omarchy-dell-haptic-touchpad') +provides=('omarchy-dell-haptic-touchpad') +replaces=('omarchy-dell-haptic-touchpad') +install=dell-xps-touchpad-haptics.install +options=('!debug') +source=( + '99-dell-xps-touchpad-haptics.rules' + 'README.package.md' + 'dell-xps-touchpad-haptics' + 'dell-xps-touchpad-haptics-daemon' + 'dell-xps-touchpad-haptics.install' + 'dell-xps-touchpad-haptics.service' +) +sha256sums=('SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP') + +package() { + install -Dm644 99-dell-xps-touchpad-haptics.rules "${pkgdir}/usr/lib/udev/rules.d/99-dell-xps-touchpad-haptics.rules" + install -Dm755 dell-xps-touchpad-haptics "${pkgdir}/usr/bin/dell-xps-touchpad-haptics" + install -Dm755 dell-xps-touchpad-haptics-daemon "${pkgdir}/usr/bin/dell-xps-touchpad-haptics-daemon" + install -Dm644 dell-xps-touchpad-haptics.service "${pkgdir}/usr/lib/systemd/system/dell-xps-touchpad-haptics.service" + install -Dm644 README.package.md "${pkgdir}/usr/share/doc/${pkgname}/README.md" +} diff --git a/pkgbuilds/edge/dell-xps-touchpad-haptics/README.package.md b/pkgbuilds/edge/dell-xps-touchpad-haptics/README.package.md new file mode 100644 index 00000000..13b41719 --- /dev/null +++ b/pkgbuilds/edge/dell-xps-touchpad-haptics/README.package.md @@ -0,0 +1,40 @@ +# dell-xps-touchpad-haptics + +This package installs the Dell XPS Synaptics haptic touchpad daemon, its +systemd service, and a small CLI for switching between Omarchy's preset haptic +levels. + +What it installs: + +- `/usr/bin/dell-xps-touchpad-haptics` +- `/usr/bin/dell-xps-touchpad-haptics-daemon` +- `/usr/lib/systemd/system/dell-xps-touchpad-haptics.service` +- `/usr/lib/udev/rules.d/99-dell-xps-touchpad-haptics.rules` + +What the pacman install hook does: + +- picks a desktop user and writes `/etc/dell-xps-touchpad-haptics.env` +- creates `~/.config/omarchy/dell-haptic.conf` if needed +- enables the service and restarts it when a running systemd manager is available + +Usage: + +```bash +dell-xps-touchpad-haptics get +dell-xps-touchpad-haptics set low +dell-xps-touchpad-haptics set mid +dell-xps-touchpad-haptics set high +``` + +Preset levels: + +- `low` = `10` +- `mid` = `50` +- `high` = `100` + +Debugging: + +- level changes are logged by the service when it notices the config change and + attempts HID gain readback +- set `DELL_XPS_TOUCHPAD_HAPTICS_DEBUG=1` in `/etc/dell-xps-touchpad-haptics.env` + to log each press and release report to `journalctl -u dell-xps-touchpad-haptics.service` diff --git a/pkgbuilds/edge/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics b/pkgbuilds/edge/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics new file mode 100755 index 00000000..19cd7df1 --- /dev/null +++ b/pkgbuilds/edge/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics @@ -0,0 +1,126 @@ +#!/bin/bash + +set -euo pipefail + +CONFIG_PATH="$HOME/.config/omarchy/dell-haptic.conf" +DEFAULT_INTENSITY=100 +LOW_INTENSITY=10 +MID_INTENSITY=50 +HIGH_INTENSITY=100 +SERVICE_NAME="dell-xps-touchpad-haptics.service" + +usage() { + printf 'Usage: %s get | set \n' "$(basename -- "$0")" +} + +clamp_intensity() { + local value=$1 + + if ! [[ $value =~ ^[0-9]+$ ]]; then + printf '%s\n' "$DEFAULT_INTENSITY" + return 0 + fi + + if (( value < 0 )); then + printf '0\n' + elif (( value > 100 )); then + printf '100\n' + else + printf '%s\n' "$value" + fi +} + +read_intensity() { + local line="" + local value="" + + [[ -f $CONFIG_PATH ]] || { + printf '%s\n' "$DEFAULT_INTENSITY" + return 0 + } + + while IFS= read -r line; do + line=${line#${line%%[![:space:]]*}} + line=${line%${line##*[![:space:]]}} + [[ -z $line || ${line:0:1} == "#" || $line != *=* ]] && continue + + if [[ ${line%%=*} == "INTENSITY" ]]; then + value=${line#*=} + value=${value#${value%%[![:space:]]*}} + value=${value%${value##*[![:space:]]}} + clamp_intensity "$value" + return 0 + fi + done < "$CONFIG_PATH" + + printf '%s\n' "$DEFAULT_INTENSITY" +} + +level_for_intensity() { + local intensity=$1 + + if (( intensity <= 30 )); then + printf 'low\n' + elif (( intensity < 75 )); then + printf 'mid\n' + else + printf 'high\n' + fi +} + +intensity_for_level() { + case "$1" in + low) printf '%s\n' "$LOW_INTENSITY" ;; + mid | medium) printf '%s\n' "$MID_INTENSITY" ;; + high) printf '%s\n' "$HIGH_INTENSITY" ;; + *) + echo "Haptic level must be one of: low, mid, high." >&2 + exit 1 + ;; + esac +} + +write_config() { + local intensity=$1 + + install -d -m 755 "$(dirname -- "$CONFIG_PATH")" + printf '# Dell XPS touchpad haptic intensity.\n# Presets: low=10, mid=50, high=100.\nINTENSITY=%s\n' "$intensity" >"$CONFIG_PATH" +} + +service_active() { + systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null +} + +case "${1:-}" in +get) + if (( $# != 1 )); then + usage >&2 + exit 1 + fi + + level_for_intensity "$(read_intensity)" + ;; +set) + if (( $# != 2 )); then + usage >&2 + exit 1 + fi + + previous_level=$(level_for_intensity "$(read_intensity)") + intensity=$(intensity_for_level "$2") + write_config "$intensity" + + if service_active; then + printf 'Set Dell XPS touchpad haptics from %s to %s. The running service will apply and verify the new gain in the journal shortly.\n' "$previous_level" "$(level_for_intensity "$intensity")" + else + printf 'Set Dell XPS touchpad haptics from %s to %s. Start %s to apply it.\n' "$previous_level" "$(level_for_intensity "$intensity")" "$SERVICE_NAME" + fi + ;; +--help | -h) + usage + ;; +*) + usage >&2 + exit 1 + ;; +esac diff --git a/pkgbuilds/edge/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics-daemon b/pkgbuilds/edge/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics-daemon new file mode 100755 index 00000000..74b7727c --- /dev/null +++ b/pkgbuilds/edge/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics-daemon @@ -0,0 +1,381 @@ +#!/usr/bin/env python3 + +"""Haptic feedback daemon for Synaptics touchpads with Manual Trigger.""" + +import fcntl +import glob +import os +import select +import signal +import struct +import sys +import time +import errno +from pathlib import Path + +VENDOR = "06CB" +REPORT_ID = 0x37 +DEFAULT_INTENSITY = 100 +DEBUG = os.environ.get("DELL_XPS_TOUCHPAD_HAPTICS_DEBUG", "").lower() not in ( + "", + "0", + "false", + "no", +) +USER_HOME = Path( + os.environ.get("DELL_XPS_TOUCHPAD_HAPTICS_HOME", str(Path.home())) +) +CONFIG_PATH = USER_HOME / ".config/omarchy/dell-haptic.conf" + +EVENT_FORMAT = "llHHi" +EVENT_SIZE = struct.calcsize(EVENT_FORMAT) +EV_KEY = 0x01 +BTN_LEFT = 272 +BTN_RIGHT = 273 +BTN_MIDDLE = 274 +BUTTON_NAMES = { + BTN_LEFT: "left", + BTN_RIGHT: "right", + BTN_MIDDLE: "middle", +} +DEVICE_GONE_ERRNOS = { + errno.ENODEV, + errno.ENXIO, + errno.EIO, +} +UNSUPPORTED_READBACK_ERRNOS = { + getattr(errno, "EREMOTEIO", 121), + errno.EINVAL, + errno.EOPNOTSUPP, +} +READBACK_UNSUPPORTED = object() + +STATE = { + "apply_requested": None, + "intensity": DEFAULT_INTENSITY, + "config_mtime_ns": None, + "readback_supported": None, + "report": struct.pack("BB", REPORT_ID, DEFAULT_INTENSITY), +} + + +class DeviceDisconnected(Exception): + pass + + +def HIDIOCSFEATURE(length): + return 0xC0000000 | (length << 16) | (ord("H") << 8) | 0x06 + + +def HIDIOCGFEATURE(length): + return 0xC0000000 | (length << 16) | (ord("H") << 8) | 0x07 + + +def clamp_intensity(value): + return max(0, min(100, value)) + + +def level_for_intensity(intensity): + if intensity <= 30: + return "low" + if intensity < 75: + return "mid" + return "high" + + +def log(message): + print(message, flush=True) + + +def log_debug(message): + if DEBUG: + log(message) + + +def load_intensity(): + try: + raw = CONFIG_PATH.read_text(encoding="utf-8") + except OSError: + return DEFAULT_INTENSITY + + for line in raw.splitlines(): + line = line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + + key, value = (part.strip() for part in line.split("=", 1)) + if key != "INTENSITY": + continue + + try: + return clamp_intensity(int(value)) + except ValueError: + print( + f"Ignoring invalid intensity in {CONFIG_PATH}: {value!r}; using default {DEFAULT_INTENSITY}", + file=sys.stderr, + flush=True, + ) + return DEFAULT_INTENSITY + + return DEFAULT_INTENSITY + + +def config_mtime_ns(): + try: + return CONFIG_PATH.stat().st_mtime_ns + except OSError: + return None + + +def refresh_intensity(signum=None, _frame=None, force=False): + mtime_ns = config_mtime_ns() + previous_intensity = STATE["intensity"] + previous_mtime_ns = STATE["config_mtime_ns"] + + if not force and signum != signal.SIGHUP and mtime_ns == STATE["config_mtime_ns"]: + return + + intensity = load_intensity() + STATE["intensity"] = intensity + STATE["config_mtime_ns"] = mtime_ns + STATE["report"] = struct.pack("BB", REPORT_ID, intensity) + + if force: + reason = "startup" + elif signum == signal.SIGHUP: + reason = "sighup" + else: + reason = "config-change" + + if ( + force + or signum == signal.SIGHUP + or mtime_ns != previous_mtime_ns + or intensity != previous_intensity + ): + log( + f"Loaded haptic level={level_for_intensity(intensity)} intensity={intensity} reason={reason} config={CONFIG_PATH}" + ) + + return True + + return False + + +def request_refresh(signum, _frame): + refresh_intensity(signum=signum) + STATE["apply_requested"] = "sighup" + + +def send_haptic_report(hidraw_fd, ioctl_req, report): + try: + fcntl.ioctl(hidraw_fd, ioctl_req, report) + return True + except OSError as error: + if error.errno in DEVICE_GONE_ERRNOS: + raise DeviceDisconnected(error) from error + + print(f"Failed to send haptic report: {error}", file=sys.stderr, flush=True) + return False + + +def read_haptic_report(hidraw_fd, ioctl_req): + if STATE["readback_supported"] is False: + return READBACK_UNSUPPORTED + + report = bytearray((REPORT_ID, 0)) + + try: + fcntl.ioctl(hidraw_fd, ioctl_req, report, True) + except OSError as error: + if error.errno in DEVICE_GONE_ERRNOS: + raise DeviceDisconnected(error) from error + + if error.errno in UNSUPPORTED_READBACK_ERRNOS: + if STATE["readback_supported"] is not False: + log( + f"Haptic gain readback is unsupported on this device ({error}); continuing in write-only mode" + ) + STATE["readback_supported"] = False + return READBACK_UNSUPPORTED + + print(f"Failed to read haptic report: {error}", file=sys.stderr, flush=True) + return None + + STATE["readback_supported"] = True + return report[1] + + +def apply_current_report(hidraw_fd, ioctl_set_req, ioctl_get_req, reason): + intensity = STATE["intensity"] + + if not send_haptic_report(hidraw_fd, ioctl_set_req, STATE["report"]): + log(f"Failed to apply haptic level={level_for_intensity(intensity)} intensity={intensity} reason={reason}") + return False + + readback = read_haptic_report(hidraw_fd, ioctl_get_req) + if readback is READBACK_UNSUPPORTED: + log( + f"Applied haptic level={level_for_intensity(intensity)} intensity={intensity} reason={reason} readback=unsupported" + ) + return True + + if readback is None: + log( + f"Applied haptic level={level_for_intensity(intensity)} intensity={intensity} reason={reason} readback=unavailable" + ) + return True + + readback_level = level_for_intensity(readback) + if readback == intensity: + log( + f"Verified haptic level={readback_level} intensity={intensity} reason={reason} readback={readback}" + ) + else: + log( + f"Haptic gain mismatch expected={intensity}({level_for_intensity(intensity)}) readback={readback}({readback_level}) reason={reason}" + ) + + return True + + +def find_hidraw(): + for path in sorted(glob.glob("/sys/class/hidraw/hidraw*")): + uevent = os.path.join(path, "device", "uevent") + try: + with open(uevent, encoding="utf-8") as file: + content = file.read().upper() + if f"0000{VENDOR}" in content: + return os.path.join("/dev", os.path.basename(path)) + except OSError: + continue + return None + + +def find_touchpad_event(): + for path in sorted(glob.glob("/sys/class/input/event*/device/name")): + try: + with open(path, encoding="utf-8") as file: + name = file.read().strip().upper() + if VENDOR in name and "TOUCHPAD" in name: + event = path.split("/")[-3] + return os.path.join("/dev/input", event) + except OSError: + continue + return None + + +def open_device_pair(): + waiting_logged = False + + while True: + hidraw = find_hidraw() + event = find_touchpad_event() + + if not hidraw or not event: + if not waiting_logged: + log("Waiting for Synaptics haptic touchpad devices to appear") + waiting_logged = True + time.sleep(1) + continue + + hidraw_fd = None + event_fd = None + + try: + hidraw_fd = os.open(hidraw, os.O_RDWR) + event_fd = os.open(event, os.O_RDONLY) + return hidraw, event, hidraw_fd, event_fd + except OSError as error: + try: + os.close(hidraw_fd) + except Exception: + pass + try: + os.close(event_fd) + except Exception: + pass + + log(f"Retrying touchpad device open after {error}") + waiting_logged = True + time.sleep(1) + + +def main(): + refresh_intensity(force=True) + signal.signal(signal.SIGHUP, request_refresh) + + ioctl_set_req = HIDIOCSFEATURE(len(STATE["report"])) + ioctl_get_req = HIDIOCGFEATURE(len(STATE["report"])) + + while True: + hidraw_fd = None + event_fd = None + + try: + hidraw, event, hidraw_fd, event_fd = open_device_pair() + + log( + f"Haptic touchpad ready hidraw={hidraw} input={event} level={level_for_intensity(STATE['intensity'])} intensity={STATE['intensity']} debug={int(DEBUG)} config={CONFIG_PATH}" + ) + apply_current_report(hidraw_fd, ioctl_set_req, ioctl_get_req, "startup") + + while True: + if STATE["apply_requested"]: + apply_current_report( + hidraw_fd, + ioctl_set_req, + ioctl_get_req, + STATE["apply_requested"], + ) + STATE["apply_requested"] = None + + ready, _, _ = select.select([event_fd], [], [], 0.5) + if not ready: + if refresh_intensity(): + apply_current_report( + hidraw_fd, + ioctl_set_req, + ioctl_get_req, + "config-change", + ) + continue + + try: + data = os.read(event_fd, EVENT_SIZE) + except OSError as error: + if error.errno in DEVICE_GONE_ERRNOS: + raise DeviceDisconnected(error) from error + raise + + if len(data) < EVENT_SIZE: + continue + + _, _, ev_type, code, value = struct.unpack(EVENT_FORMAT, data) + if ev_type == EV_KEY and code in (BTN_LEFT, BTN_RIGHT, BTN_MIDDLE) and value in (0, 1): + if refresh_intensity(): + apply_current_report( + hidraw_fd, + ioctl_set_req, + ioctl_get_req, + "config-change", + ) + + if send_haptic_report(hidraw_fd, ioctl_set_req, STATE["report"]): + log_debug( + f"Applied haptic report button={BUTTON_NAMES.get(code, code)} event={'press' if value == 1 else 'release'} level={level_for_intensity(STATE['intensity'])} intensity={STATE['intensity']}" + ) + except DeviceDisconnected as error: + log(f"Touchpad device disappeared ({error}); waiting for it to return") + time.sleep(1) + except KeyboardInterrupt: + return + finally: + if event_fd is not None: + os.close(event_fd) + if hidraw_fd is not None: + os.close(hidraw_fd) + + +if __name__ == "__main__": + main() diff --git a/pkgbuilds/edge/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics.install b/pkgbuilds/edge/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics.install new file mode 100644 index 00000000..587f96cc --- /dev/null +++ b/pkgbuilds/edge/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics.install @@ -0,0 +1,248 @@ +_service_name="dell-xps-touchpad-haptics.service" +_default_level="high" +_env_path="/etc/dell-xps-touchpad-haptics.env" +_legacy_env_path="/etc/omarchy-dell-haptic-touchpad.env" +_legacy_override_dir="/etc/systemd/system/dell-xps-haptic-touchpad.service.d" + +_existing_home() { + local line value + + [[ -f $_env_path ]] || return 0 + + while IFS= read -r line; do + line=${line#${line%%[![:space:]]*}} + line=${line%${line##*[![:space:]]}} + [[ -z $line || ${line:0:1} == "#" || $line != *=* ]] && continue + + if [[ ${line%%=*} == "DELL_XPS_TOUCHPAD_HAPTICS_HOME" ]]; then + value=${line#*=} + value=${value#${value%%[![:space:]]*}} + value=${value%${value##*[![:space:]]}} + printf '%s\n' "$value" + return 0 + fi + done < "$_env_path" +} + +_resolve_target_user() { + if [[ -n ${OMARCHY_HAPTIC_USER:-} && ${OMARCHY_HAPTIC_USER} != "root" ]]; then + printf '%s\n' "$OMARCHY_HAPTIC_USER" + return 0 + fi + + if [[ -n ${SUDO_USER:-} && ${SUDO_USER} != "root" ]]; then + printf '%s\n' "$SUDO_USER" + return 0 + fi + + if command -v logname >/dev/null 2>&1; then + local login_name + login_name=$(logname 2>/dev/null || true) + if [[ -n $login_name && $login_name != "root" ]]; then + printf '%s\n' "$login_name" + return 0 + fi + fi + + if command -v loginctl >/dev/null 2>&1; then + local active_user + active_user=$(loginctl list-users --no-legend 2>/dev/null | awk '$2 != "root" && $4 == "active" { print $2; exit }') + if [[ -n $active_user ]]; then + printf '%s\n' "$active_user" + return 0 + fi + fi + + getent passwd | awk -F: '$3 >= 1000 && $1 != "nobody" && $7 !~ /(nologin|false)$/ { print $1; exit }' +} + +_user_home() { + getent passwd "$1" | cut -d: -f6 +} + +_debug_value() { + local line value + + [[ -f $_env_path ]] || return 0 + + while IFS= read -r line; do + line=${line#${line%%[![:space:]]*}} + line=${line%${line##*[![:space:]]}} + [[ -z $line || ${line:0:1} == "#" || $line != *=* ]] && continue + + if [[ ${line%%=*} == "DELL_XPS_TOUCHPAD_HAPTICS_DEBUG" ]]; then + value=${line#*=} + value=${value#${value%%[![:space:]]*}} + value=${value%${value##*[![:space:]]}} + printf '%s\n' "$value" + return 0 + fi + done < "$_env_path" +} + +_write_env_file() { + local home=$1 + local debug_value=$2 + local tmpfile + + tmpfile=$(mktemp) + printf 'DELL_XPS_TOUCHPAD_HAPTICS_HOME=%s\n' "$home" >"$tmpfile" + if [[ -n $debug_value ]]; then + printf 'DELL_XPS_TOUCHPAD_HAPTICS_DEBUG=%s\n' "$debug_value" >>"$tmpfile" + fi + install -D -m 644 "$tmpfile" "$_env_path" + rm -f "$tmpfile" +} + +_systemd_runtime_available() { + [[ -d /run/systemd/system ]] +} + +_udev_runtime_available() { + [[ -S /run/udev/control ]] +} + +_service_unit_present() { + [[ -f /usr/lib/systemd/system/$_service_name ]] +} + +_reload_runtime_state() { + if _systemd_runtime_available && ! systemctl daemon-reload; then + echo ":: Failed to reload the systemd manager for ${_service_name}." >&2 + return 1 + fi + + if _udev_runtime_available && ! udevadm control --reload-rules; then + echo ":: Failed to reload udev rules for ${_service_name}." >&2 + return 1 + fi +} + +_ensure_user_config() { + local user=$1 + local home=$2 + local config_dir="$home/.config/omarchy" + local config_path="$config_dir/dell-haptic.conf" + + if [[ ! -f $config_path ]] && ! env HOME="$home" USER="$user" LOGNAME="$user" \ + /usr/bin/dell-xps-touchpad-haptics set "$_default_level"; then + echo ":: Failed to create ${config_path} for user '$user'." >&2 + return 1 + fi + + if [[ -f $config_path ]]; then + chown "$user:$user" "$home/.config" 2>/dev/null || true + chown "$user:$user" "$config_dir" 2>/dev/null || true + chown "$user:$user" "$config_path" 2>/dev/null || true + fi + + return 0 +} + +_reset_trackpad() { + if command -v omarchy-restart-trackpad >/dev/null 2>&1; then + omarchy-restart-trackpad 2>/dev/null || true + fi +} + +_cleanup_legacy_state() { + rm -f "$_legacy_env_path" "$_legacy_override_dir/override.conf" + rmdir "$_legacy_override_dir" 2>/dev/null || true +} + +_enable_service() { + if ! systemctl enable "$_service_name" >/dev/null; then + echo ":: Failed to enable ${_service_name}." >&2 + return 1 + fi +} + +_start_service_if_possible() { + if ! _systemd_runtime_available; then + echo ":: ${_service_name} enabled; it will start on next boot." + return 0 + fi + + if ! systemctl restart "$_service_name" >/dev/null; then + echo ":: Failed to start ${_service_name}." >&2 + return 1 + fi +} + +_configure_package() { + local user="" + local home="" + local debug_value + local config_path="" + + _cleanup_legacy_state + + if ! _service_unit_present; then + echo ":: Required unit file for ${_service_name} was not installed." >&2 + return 1 + fi + + if ! _reload_runtime_state; then + return 1 + fi + + user=$(_resolve_target_user) + if [[ -n $user ]]; then + home=$(_user_home "$user") + fi + + if [[ -z $home || ! -d $home ]]; then + home=$(_existing_home) + fi + + if [[ -z $home || ! -d $home ]]; then + echo ":: Could not determine a usable home directory for ${_service_name}." >&2 + echo ":: Reinstall the package after logging in as your desktop user to finish setup." >&2 + return 1 + fi + + debug_value=$(_debug_value) + _write_env_file "$home" "$debug_value" + + if [[ -n $user ]]; then + if ! _ensure_user_config "$user" "$home"; then + return 1 + fi + else + config_path="$home/.config/omarchy/dell-haptic.conf" + if [[ ! -f $config_path ]]; then + echo ":: Could not seed ${config_path} because no desktop user could be identified." >&2 + return 1 + fi + fi + + if ! _enable_service; then + return 1 + fi + + _reset_trackpad + + if ! _start_service_if_possible; then + return 1 + fi + + return 0 +} + +post_install() { + _configure_package +} + +post_upgrade() { + _configure_package +} + +pre_remove() { + systemctl disable "$_service_name" 2>/dev/null || true + systemctl stop "$_service_name" 2>/dev/null || true +} + +post_remove() { + rm -f "$_env_path" + _reload_runtime_state || true +} diff --git a/pkgbuilds/edge/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics.service b/pkgbuilds/edge/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics.service new file mode 100644 index 00000000..ae1ca260 --- /dev/null +++ b/pkgbuilds/edge/dell-xps-touchpad-haptics/dell-xps-touchpad-haptics.service @@ -0,0 +1,14 @@ +[Unit] +Description=Dell XPS touchpad haptics +After=systemd-udev-settle.service + +[Service] +Type=simple +SyslogIdentifier=dell-xps-touchpad-haptics +EnvironmentFile=-/etc/dell-xps-touchpad-haptics.env +ExecStart=/usr/bin/dell-xps-touchpad-haptics-daemon +Restart=on-failure +RestartSec=2 + +[Install] +WantedBy=multi-user.target