diff --git a/hosts/wim/config/ags/package.json b/hosts/wim/config/ags/package.json index be1419f6..82a2eb71 100644 --- a/hosts/wim/config/ags/package.json +++ b/hosts/wim/config/ags/package.json @@ -1,6 +1,7 @@ { "dependencies": { "@girs/gtk-3.0": "^3.24.39-3.2.2", + "@girs/gudev-1.0": "^1.0.0-3.2.2", "eslint": "^8.52.0", "stylelint-config-standard-scss": "^11.0.0" } diff --git a/hosts/wim/config/ags/services/pointers.js b/hosts/wim/config/ags/services/pointers.js index 10601d80..626d6bbd 100644 --- a/hosts/wim/config/ags/services/pointers.js +++ b/hosts/wim/config/ags/services/pointers.js @@ -1,6 +1,19 @@ -// TODO: read /dev to recalculate devices and remake subprocess - import { Service, Utils } from '../imports.js'; +import GUdev from 'gi://GUdev'; + +const UDEV_POINTERS = [ + 'ID_INPUT_MOUSE', + 'ID_INPUT_POINTINGSTICK', + 'ID_INPUT_TOUCHPAD', + 'ID_INPUT_TOUCHSCREEN', + 'ID_INPUT_TABLET', +]; +const LIB_POINTERS = [ + 'BTN', + 'released', + 'TOUCH_UP', + 'HOLD_END', +]; class Pointers extends Service { @@ -15,7 +28,8 @@ class Pointers extends Service { proc = undefined; output = ''; - devices = new Map(); + devices = []; + udevClient = GUdev.Client.new(['input']); get process() { return this.proc; } get lastLine() { return this.output; } @@ -23,32 +37,36 @@ class Pointers extends Service { constructor() { super(); - this.parseDevices(); + this.initUdevConnection(); } - parseDevices() { - Utils.execAsync(['libinput', 'list-devices']).then(out => { - const lines = out.split('\n'); - let device = null; - const devices = new Map(); + // FIXME: logitech mouse screws everything up on disconnect + getDevices() { + this.devices = []; + this.udevClient.query_by_subsystem('input').forEach(dev => { + const isPointer = UDEV_POINTERS.some(p => dev.has_property(p)); + if (isPointer) { + const hasEventFile = dev.has_property('DEVNAME') && + dev.get_property('DEVNAME').includes('event'); + if (hasEventFile) + this.devices.push(dev.get_property('DEVNAME')); + } + }); - lines.forEach(line => { - const parts = line.split(':'); + this.emit('device-fetched', true); + } - if (parts[0] === 'Device') { - device = {}; - devices.set(parts[1].trim(), device); + initUdevConnection() { + this.getDevices(); + this.udevClient.connect('uevent', (_, action) => { + if (action === 'add' || action === 'remove') { + this.getDevices(); + if (this.proc) { + this.killProc(); + this.startProc(); } - else if (device && parts[1]) { - const key = parts[0].trim(); - const value = parts[1].trim(); - device[key] = value; - } - }); - this.devices = Array.from(devices).filter(dev => dev.Capabilities && - dev.Capabilities.includes('pointer')); - this.emit('device-fetched', true); - }).catch(print); + } + }); } startProc() { @@ -57,20 +75,18 @@ class Pointers extends Service { const args = []; this.devices.forEach(dev => { - if (dev.Kernel) { - args.push('--device'); - args.push(dev.Kernel); - } + args.push('--device'); + args.push(dev); }); this.proc = Utils.subprocess( ['libinput', 'debug-events', ...args], output => { - if (output.includes('BTN') && output.includes('released') || - output.includes('TOUCH_UP') || - output.includes('HOLD_END') && !output.includes('cancelled')) { - this.output = output; - this.emit('new-line', output); + if (!output.includes('cancelled')) { + if (LIB_POINTERS.some(p => output.includes(p))) { + this.output = output; + this.emit('new-line', output); + } } }, err => logError(err), diff --git a/hosts/wim/home/hyprland.nix b/hosts/wim/home/hyprland.nix index 954207f4..f919c425 100644 --- a/hosts/wim/home/hyprland.nix +++ b/hosts/wim/home/hyprland.nix @@ -11,8 +11,14 @@ in { programs.ags = { enable = true; - package = ags.packages.x86_64-linux.default; configDir = symlink "${configDir}/ags"; + package = (ags.packages.x86_64-linux.default.overrideAttrs + (_: prev: { + buildInputs = with pkgs; prev.buildInputs ++ [ + libgudev + ]; + }) + ); }; wayland.windowManager.hyprland = {