Back and Forward mouse buttons now work in Safari and Chrome#363
Conversation
463796e to
356ab84
Compare
fcdcdf4 to
4c8beed
Compare
Greptile SummaryThis PR adds browser Back and Forward support for Logitech mouse buttons. The main changes are:
Confidence Score: 4/5This is close, but one cleanup path should be fixed before merging.
crates/openlogi-hid/src/gesture.rs Important Files Changed
|
4c8beed to
dc770e7
Compare
|
Thanks for the review! All 5 issues addressed in the latest commit:
|
dc770e7 to
fc200f0
Compare
|
Addressed the 3 remaining issues:
|
fc200f0 to
375c52e
Compare
|
Fixed: Later Arm Failure Leaks Diversions Changed |
The MX Vertical (and similar Logitech mice) report Back and Forward buttons as HID++ 0x1b04 ReprogControls CIDs rather than standard OS mouse button 4/5 events. macOS never translates these into OtherMouseDown CGEvents, so OpenLogi's CGEventTap hook never saw them. Changes: - reprog_controls.rs: add BACK_CIDS (0x0053, 0xBD, 0xCE, 0xDB) and FORWARD_CIDS (0x0056, 0xCF) covering the MX Vertical's classic CIDs and the MultiPlatform Back/Forward CIDs used by other models - gesture.rs: divert Back/Forward CIDs in arm_controls; emit ButtonPressed(Back/Forward) on rising edge with 350ms debounce (the MX Vertical sends multiple DivertedButtons frames per physical click); capture frontmost app PID on the listener thread at press time so AX dispatch uses the correct app regardless of async dispatch delay; CapturedInput::ButtonPressed now carries Option<i32> frontmost_pid - inject.rs: add ax_browser_navigate() — uses AXPress on the toolbar's 'Go back'/'Go forward' AXButton for Safari (WKWebView ignores synthetic CGEvents); skips AXSplitGroup/AXTabGroup/AXRadioButton to avoid exhausting search depth on Safari's tab bar; falls back to Cmd+[/] for Chrome and other browsers - watchers/gesture.rs: use captured PID for AX navigation; fall back to dispatch_action (Cmd+[/]) when AX finds no matching button Config: add Back='BrowserBack' and Forward='BrowserForward' under the correct per-physical-device key in config.toml. Tested on MX Vertical (046d:b020) over Bluetooth on macOS: - Chrome: works via Cmd+[ / Cmd+] - Safari: works via AXPress on toolbar navigation button Closes: back/forward buttons not working on macOS with MX Vertical
375c52e to
ec20ae7
Compare
|
All 3 issues fixed: 1. Recursive Retain Leak — moved 2. Earlier Diversion Leaks — the gesture and DPI CID diversion loops now use explicit error handling with rollback (matching the Back/Forward pattern already in place). A DPI failure rolls back gesture; a gesture failure has nothing prior to roll back. 3. Localized Fallback Fails — added |
What this fixes
If you have a Logitech MX Vertical (or any Logitech mouse with dedicated Back/Forward buttons), those buttons previously did nothing in Safari and other browsers on macOS — even though Chrome eventually worked. This PR fixes that: pressing the Back button navigates back in both Safari and Chrome, and Forward navigates forward.
Why it didn't work
Logitech mice don't send Back/Forward as ordinary mouse button clicks. They use a proprietary HID++ firmware protocol to report those buttons, and macOS never translates them into the standard mouse events that apps like Safari listen for. OpenLogi's existing input hook only captured standard mouse events, so it never saw Back/Forward presses at all.
On top of that, Safari has a special restriction: even when you synthesize the correct mouse button events or keyboard shortcuts in software, Safari's web engine (WKWebView) runs in a sandboxed subprocess and ignores them entirely. Chrome doesn't have this restriction, which is why Chrome worked once the buttons were being captured — but Safari didn't.
There was also a subtle timing problem: the MX Vertical's buttons are visible at two levels simultaneously — as standard OS mouse button events (intercepted by OpenLogi's CGEventTap hook) and as HID++ firmware events. The hook path was firing first and sending
Cmd+[to Safari (which Safari ignores), while the correct AX path was running too late.How it's fixed
Capturing the buttons (works for all apps)
The fix adds the Back and Forward button IDs to OpenLogi's HID++ capture session — the same mechanism already used for the DPI button and gesture controls. When you press Back or Forward, the firmware event is now intercepted, identified, and forwarded into OpenLogi's remapping pipeline.
A 350ms debounce prevents the MX Vertical's firmware from registering a single physical click as multiple rapid presses.
Making Safari work
Since Safari ignores synthetic input events, the fix uses macOS's Accessibility API instead. Rather than injecting a fake button press, OpenLogi finds Safari's actual Back/Forward toolbar button and programmatically clicks it — the same way a user would with a mouse cursor. Safari responds to this correctly.
For Chrome and other browsers, the fix continues to use keyboard shortcuts (
Cmd+[/Cmd+]), which those apps handle natively.Fixing the timing race
The frontmost app's process ID is captured at the exact moment you press the physical button — on the CGEventTap callback thread, where macOS guarantees the app context is current. This ensures OpenLogi always acts on the right browser window, whether the event comes through the hook path or the HID++ path.
Files changed
openlogi-hidopenlogi-injectax_navigate_browser()— finds and presses Safari's toolbar button via Accessibility API; falls back to keyboard shortcut for other browsersopenlogi-agent-coreTechnical details
HID++ CIDs for Back/Forward
The MX Vertical uses CIDs
0x0053(Back) and0x0056(Forward) — below the documented0xB8range in Logitech's control-ID list. This PR also adds the higher-range MultiPlatform CIDs (0x00BD,0x00CE,0x00DBfor Back;0x00CFfor Forward) used on other Logitech models.Why the Accessibility approach for Safari
Safari's WKWebView runs in a separate sandboxed process. CGEvents posted at the HID or Session tap level don't reach it — this is by design. Tools like Karabiner-Elements and Mac Mouse Fix that support Safari use a DriverKit virtual HID device (kernel-level injection), which requires a special Apple entitlement. The AX approach achieves the same result without requiring a kernel extension.
The AX tree search skips
AXSplitGroup,AXTabGroup,AXOpaqueProviderGroup, andAXRadioButtonsubtrees to avoid exhausting search depth on Safari's tab bar.The dual-path timing issue
The MX Vertical's buttons are visible at two layers simultaneously: as
OtherMouseDownOS events (CGEventTap hook) and as HID++ DivertedButtons CID events (gesture capture session). The hook path was firing first and sendingCmd+[to Safari (ignored). Now both paths captureNSWorkspace.frontmostApplication.processIdentifieron the tap callback thread and callAXPresson the app's toolbar button directly.Notes for reviewers
CapturedInput::ButtonPressednow carries#[serde(skip)] Option<i32>(frontmost PID). Skipped in serialization — dispatch hint only, not part of the wire format.Testing
Tested on MX Vertical (046d:b020) over Bluetooth on macOS: