Skip to content

fix(hook): grab only relative pointer devices, never touchpads or pointing sticks#401

Open
kesleyfort wants to merge 3 commits into
AprilNEA:masterfrom
kesleyfort:fix/hook-touchpad-grab
Open

fix(hook): grab only relative pointer devices, never touchpads or pointing sticks#401
kesleyfort wants to merge 3 commits into
AprilNEA:masterfrom
kesleyfort:fix/hook-touchpad-grab

Conversation

@kesleyfort

Copy link
Copy Markdown
Contributor

Summary

The Linux hook selected every evdev device advertising BTN_LEFT, which
includes touchpads: they got an exclusive grab whose multitouch EV_ABS
stream the relative-only virtual mouse cannot re-inject, so the built-in
touchpad went dead the moment the agent started. This tightens device
selection and adds an opt-out for users who only want HID++ configuration.

Changes

  • openlogi-hook (src/linux.rs): device selection now requires a real
    relative pointer (BTN_LEFT + REL_X/REL_Y) and refuses anything
    touch-driven (BTN_TOUCH/BTN_TOOL_FINGER, ABS_MT_POSITION_X, or the
    BUTTONPAD/SEMI_MT/DIRECT input properties) as well as pointing sticks
    (POINTING_STICK, whose libinput behaviors a re-injected stream would lose).
    Our own uinput devices are excluded by the shared OpenLogi name prefix,
    covering the action injector too.
  • openlogi-core (src/config.rs) + openlogi-agent (src/main.rs): add
    app_settings.capture_mouse_events (default true). When false, the agent
    never installs the OS-level mouse hook, so no input device is grabbed or
    intercepted on any platform; DPI, SmartShift, gesture-button, and thumb-wheel
    features are unaffected. Read once at agent startup (like show_in_menu_bar);
    flipping it requires an agent restart.
  • docs/CONFIGURATION.md: document the new setting.

Testing

  • cargo build / cargo clippy on the hook, agent, and core crates.
  • Not yet runtime-tested on Linux hardware with a built-in touchpad — needs
    maintainer verification that the touchpad stays live while a relative mouse is
    still hooked, and that capture_mouse_events = false leaves no device grabbed.

Fixes #355

Related: #356 (contributor draft targeting the same issue via device selection)
— overlapping; left open for the maintainer to compare/adopt.

@greptile-apps

greptile-apps Bot commented Jul 17, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes the Linux hook grabbing touchpads (and pointing sticks) by replacing the single BTN_LEFT filter with a multi-criterion is_hookable_mouse predicate, and adds a cross-platform capture_mouse_events escape hatch that prevents any OS-level input grab from being installed.

  • crates/openlogi-hook/src/linux.rs: is_hookable_mouse now requires BTN_LEFT + REL_X/REL_Y, and rejects devices advertising touch keys (BTN_TOUCH/BTN_TOOL_FINGER), multitouch axes (ABS_MT_POSITION_X), touch input properties (BUTTONPAD/SEMI_MT/DIRECT), or pointing-stick identity; own uinput devices are excluded by the new OPENLOGI_DEVICE_PREFIX constant, covering the action injector as well. A thorough unit-test suite exercises each exclusion path.
  • crates/openlogi-core/src/config/settings.rs + crates/openlogi-agent/src/main.rs: a new app_settings.capture_mouse_events boolean (default true) gates hook installation on all platforms; when false, the macOS Accessibility prompt is also suppressed.
  • docs/CONFIGURATION.md: documents the new setting, its scope, and the restart requirement.

Confidence Score: 5/5

Safe to merge; the device-selection logic is correct and well-covered by tests, and the new config flag is cleanly isolated to the startup path.

The core fix — is_hookable_mouse in linux.rs — is logically sound and exercised by distinct test cases for every exclusion criterion. The capture_mouse_events setting is additive and defaults to the existing behavior. The one note (a log line that could repeat if the accessibility watcher is not deduplicated) is informational-only and does not affect device grabbing or correctness.

No files require special attention. The main.rs change is straightforward; the disabled-hook log re-entry is worth a follow-up but does not affect safety.

Important Files Changed

Filename Overview
crates/openlogi-hook/src/linux.rs Adds is_hookable_mouse with multi-criterion touch/pointing-stick exclusion, introduces OPENLOGI_DEVICE_PREFIX to also exclude the action injector, replaces the single BTN_LEFT filter in find_mouse_devices, and adds a comprehensive test suite; logic is sound and all new test paths exercise distinct exclusion criteria
crates/openlogi-agent/src/main.rs Adds capture_mouse_events kill-switch: skips the macOS Accessibility prompt and the hook installation when false; the else branch that logs the disabled message keeps hook as None, so the log can re-fire on every subsequent granted event if the accessibility watcher sends repeated true values
crates/openlogi-core/src/config/settings.rs Adds capture_mouse_events: bool with serde(default = "default_true"), includes it in Default::default(), and updates the default_true doc comment to cover all three on-by-default fields; no issues
docs/CONFIGURATION.md Documents the new capture_mouse_events setting inline with the [app_settings] prose; accurately describes its effect and the restart requirement

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[evdev::enumerate] --> B{name starts with OPENLOGI_DEVICE_PREFIX?}
    B -- yes --> SKIP[skip device]
    B -- no --> C{BTN_LEFT in supported_keys?}
    C -- no --> SKIP
    C -- yes --> D{REL_X AND REL_Y in rel_axes?}
    D -- no --> SKIP
    D -- yes --> E{Touch surface? BTN_TOUCH / BTN_TOOL_FINGER / ABS_MT_POSITION_X / BUTTONPAD / SEMI_MT / DIRECT}
    E -- yes --> SKIP
    E -- no --> F{POINTING_STICK property?}
    F -- yes --> SKIP
    F -- no --> HOOK[grab device & build virtual uinput device]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[evdev::enumerate] --> B{name starts with OPENLOGI_DEVICE_PREFIX?}
    B -- yes --> SKIP[skip device]
    B -- no --> C{BTN_LEFT in supported_keys?}
    C -- no --> SKIP
    C -- yes --> D{REL_X AND REL_Y in rel_axes?}
    D -- no --> SKIP
    D -- yes --> E{Touch surface? BTN_TOUCH / BTN_TOOL_FINGER / ABS_MT_POSITION_X / BUTTONPAD / SEMI_MT / DIRECT}
    E -- yes --> SKIP
    E -- no --> F{POINTING_STICK property?}
    F -- yes --> SKIP
    F -- no --> HOOK[grab device & build virtual uinput device]
Loading

Reviews (3): Last reviewed commit: "fix(agent): skip the accessibility promp..." | Re-trigger Greptile

Comment thread crates/openlogi-hook/src/linux.rs
Comment on lines +161 to +170
if !hookable
&& d.supported_keys()
.is_some_and(|keys| keys.contains(KeyCode::BTN_LEFT))
{
debug!(
"not hooking {} ({}): has mouse buttons but is not a plain relative-pointer mouse",
path.display(),
d.name().unwrap_or("unnamed"),
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Redundant supported_keys() call in debug guard

d.supported_keys() is already called (and its result consumed) inside is_hookable_mouse, but the debug logging condition calls it again on the same Device. Because Device does not cache individual capability tables, this triggers a second parse of the same bitfield. For the fast path (devices that are hookable) no second call happens, but for every non-mouse device that advertises BTN_LEFT the key set is parsed twice. Capturing the keys reference once (or simply accepting the minor duplication) keeps intent clear.

Fix in Codex Fix in Claude Code

kesleyfort and others added 3 commits July 19, 2026 14:43
…nting sticks

The Linux hook selected every evdev device advertising BTN_LEFT, which
includes touchpads: they got an exclusive grab whose multitouch EV_ABS
stream the relative-only virtual mouse cannot re-inject, so the built-in
touchpad went dead the moment the agent started.

Device selection now requires a real relative pointer (BTN_LEFT plus
REL_X/REL_Y) and refuses anything touch-driven (BTN_TOUCH/BTN_TOOL_FINGER,
ABS_MT_POSITION_X, or the BUTTONPAD/SEMI_MT/DIRECT input properties) as
well as pointing sticks, whose POINTING_STICK-derived libinput behaviors
a re-injected stream would lose. Our own uinput devices are excluded by
the shared "OpenLogi " name prefix, covering the action injector too.
… hook

app_settings.capture_mouse_events (default true) is an escape hatch for
users who want OpenLogi purely for HID++ device configuration: set to
false, the agent never installs the OS-level mouse hook, so no input
device is grabbed or intercepted on any platform. DPI, SmartShift,
gesture-button, and thumb-wheel features are unaffected. Read once at
agent startup, like show_in_menu_bar; flipping it requires an agent
restart, as documented.
…bled

With capture_mouse_events = false the agent never installs the CGEventTap,
so it has no use for the Accessibility permission — firing the TCC prompt
at startup contradicts the setting's hands-off intent. Read the toggle
first and prompt only when the hook is actually wanted.
@AprilNEA
AprilNEA force-pushed the fix/hook-touchpad-grab branch from 460b0fa to f9bc554 Compare July 19, 2026 06:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Linux/KDE Wayland: agent virtual mice disable built-in touchpad

2 participants