Skip to content

fix(macos): capture side-button gestures over HID++ - #722

Open
juan-apa wants to merge 5 commits into
AprilNEA:masterfrom
juan-apa:fix/bluetooth-button-source
Open

fix(macos): capture side-button gestures over HID++#722
juan-apa wants to merge 5 commits into
AprilNEA:masterfrom
juan-apa:fix/bluetooth-button-source

Conversation

@juan-apa

@juan-apa juan-apa commented Aug 20, 2026

Copy link
Copy Markdown

Summary

I ran into this while configuring gestures on the Forward button of my MX Master 3. The configuration looked valid, but clicking Forward only performed the normal browser action, and holding the button while moving the mouse never triggered a gesture.

While debugging it, I captured the macOS Back/Forward events directly and found that every Forward press and release arrived with device=None. OpenLogi correctly rejects unattributed events in its global macOS hook to avoid remapping input from unrelated devices, so the gesture hold never began.

My first attempt allowed senderless Back/Forward events through that hook. Review feedback pointed out the important flaw in that approach: without a source identity, OpenLogi cannot prove that the event came from the configured Logitech mouse.

This changes the implementation to get the Back/Forward button lifecycle from the mouse's device-specific HID++ channel instead. The global hook is only used for pointer movement while a verified HID++ hold is active, so anonymous button events remain blocked.

Changes

  • Capture divertable Back/Forward down and up edges through HID++ for macOS gesture bindings.
  • Keep unattributed events blocked in the global macOS hook.
  • Combine the verified HID++ hold with pointer movement from the OS hook to resolve click and swipe actions.
  • Keep gesture state keyed to the originating device and dispatch through that device's effective per-app bindings.
  • Share the inventory-owned HID++ channel instead of opening a competing connection.
  • Re-arm volatile control diversion after a device reconnect and restart capture when inventory replaces the underlying channel.
  • Add regression coverage for capture planning, single-owner dispatch, verified button edges, gesture state, and reconnect re-arming.

Testing

I first built a small event-source diagnostic and confirmed that the MX Master 3 consistently reported Forward events as device=None.

I then built and ran the agent against an isolated copy of my real configuration. On the physical mouse I verified:

  • Click → Mission Control
  • Up → Show Desktop
  • Down → App Exposé
  • Left → Previous Desktop
  • Right → Next Desktop

During reconnect testing, turning the mouse off and back on exposed a second issue: diversion was briefly restored and then lost when inventory replaced the HID++ connection. After moving capture onto the inventory-owned channel and restarting it when that channel changes, I repeated the power-cycle test and confirmed that all gestures continued working afterward.

The repository validation also passes:

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace
  • rustdoc for the workspace with warnings denied

Fixes #716

@juan-apa
juan-apa requested a review from AprilNEA as a code owner August 20, 2026 15:38
@greptile-apps

greptile-apps Bot commented Aug 20, 2026

Copy link
Copy Markdown

Greptile Summary

The PR replaces anonymous macOS Back/Forward button handling with device-specific HID++ edges while retaining global pointer movement for swipe resolution.

  • Adds per-device side-gesture state and HID++ down/up capture.
  • Couples side-button diversion to OS-hook availability and capture settings.
  • Shares inventory-owned HID++ channels and re-arms volatile diversion after reconnects.
  • Adds regression coverage for capture planning, edge handling, reconnects, and teardown behavior.

Confidence Score: 3/5

The PR does not yet appear safe to merge because unrelated global pointer movement can resolve a device-owned gesture, and Accessibility teardown can still divert a side button after its movement source is gone.

The HID++ edge path establishes button ownership but the macOS movement event remains unattributed and is accumulated without an ownership check; separately, hook revocation does not wait for asynchronous HID++ disarm, leaving a window where a gesture cannot receive movement and may resolve as its click action.

Files Needing Attention: crates/openlogi-agent-core/src/hook_runtime.rs, crates/openlogi-agent-core/src/side_gesture.rs, crates/openlogi-agent-core/src/watchers/gesture.rs, crates/openlogi-agent/src/main.rs

Important Files Changed

Filename Overview
crates/openlogi-agent-core/src/capture_plan.rs Builds macOS HID++ side-gesture plans while honoring capture opt-out and hook availability.
crates/openlogi-agent-core/src/hook_runtime.rs Integrates device-owned gesture state into the global movement callback.
crates/openlogi-agent-core/src/side_gesture.rs Implements synchronized per-device hold, swipe, click, cancellation, and stale-hold handling.
crates/openlogi-agent-core/src/watchers/gesture.rs Reconciles per-device capture sessions and dispatches verified HID++ button edges.
crates/openlogi-agent/src/main.rs Connects hook availability and shared side-gesture state to daemon lifecycle management.
crates/openlogi-hid/src/session/gesture.rs Adds both-edge standard-button capture, registry-owned channels, reconnect re-arming, and channel replacement detection.

Sequence Diagram

sequenceDiagram
    participant Mouse as Logitech mouse
    participant HID as HID++ session
    participant State as SideGestureRuntime
    participant Hook as Global OS movement hook
    participant Dispatch as Action dispatcher
    Mouse->>HID: Back/Forward down
    HID->>State: Begin verified device hold
    Hook->>State: Global pointer deltas
    State-->>Dispatch: Swipe action on threshold
    Mouse->>HID: Back/Forward up
    HID->>State: End verified hold
    State-->>Dispatch: Click action if no swipe committed
Loading

Reviews (5): Last reviewed commit: "fix(agent): preserve side buttons during..." | Re-trigger Greptile

Comment thread crates/openlogi-agent-core/src/hook_runtime.rs Outdated
@juan-apa juan-apa changed the title fix(macos): allow senderless side-button gestures fix(macos): capture side-button gestures over HID++ Aug 20, 2026
Comment thread crates/openlogi-agent/src/main.rs
let _ = try_queue_action(action_tx, action, None);
}
}
let device_commit = side_gesture.try_accumulate(delta_x, delta_y);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Unrelated movement resolves device gesture

When a Logitech Back or Forward hold is active while a trackpad or another mouse moves the pointer, every global movement delta enters the shared side-gesture accumulator, causing the unrelated device's movement to trigger the Logitech mouse's configured swipe action.

Knowledge Base Used: openlogi-agent-core

Fix in Codex Fix in Claude Code

Comment thread crates/openlogi-agent/src/main.rs Outdated
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]: gestures are not working + no option to activate theAction ring (MX Anywhere 3)

1 participant