dell-xps-touchpad-haptics
This commit is contained in:
9 files changed
+3
No files matched your search
@@ -1,27 +0,0 @@
|
||||
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
|
||||
@@ -1,2 +0,0 @@
|
||||
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"
|
||||
@@ -1,32 +0,0 @@
|
||||
# Maintainer: Jacob Mink <Jacob[dot]Mink[at]Dell[dot]com>
|
||||
|
||||
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"
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
# 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`
|
||||
@@ -1,126 +0,0 @@
|
||||
#!/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 <low|mid|high>\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
|
||||
@@ -1,381 +0,0 @@
|
||||
#!/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()
|
||||
@@ -1,248 +0,0 @@
|
||||
_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
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
[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
|
||||
Reference in new issue
Block a user