feat: wheel tilt-left/right as bindable buttons (verified on MX Ergo)#357
feat: wheel tilt-left/right as bindable buttons (verified on MX Ergo)#357g30r93g wants to merge 6 commits into
Conversation
Address code-review: the module docs mentioned wheel tilt but the CapturedInput::ButtonPressed variant doc, the agent-core watcher module doc, and the ButtonId doc still described the pre-tilt set. Also clarify that ButtonId variants are appended (TOML-stable) while ButtonId::ALL carries the physical layout order.
Greptile SummaryThis PR adds wheel tilt-left and tilt-right as bindable mouse controls. The main changes are:
Confidence Score: 5/5This looks safe to merge after small follow-ups to tilt capture edge cases.
crates/openlogi-hid/src/gesture.rs and crates/openlogi-agent-core/src/watchers/gesture.rs Important Files Changed
Reviews (1): Last reviewed commit: "docs(tilt): note wheel-tilt buttons in c..." | Re-trigger Greptile |
| let tilt_left_down = cids.contains(&reprog_controls::TILT_LEFT_CID); | ||
| if tilt_left_down && !acc.tilt_left_down { | ||
| let _ = sink.send(CapturedInput::ButtonPressed(ButtonId::TiltLeft)); | ||
| } | ||
| acc.tilt_left_down = tilt_left_down; | ||
|
|
||
| let tilt_right_down = cids.contains(&reprog_controls::TILT_RIGHT_CID); | ||
| if tilt_right_down && !acc.tilt_right_down { | ||
| let _ = sink.send(CapturedInput::ButtonPressed(ButtonId::TiltRight)); | ||
| } | ||
| acc.tilt_right_down = tilt_right_down; |
There was a problem hiding this comment.
When more than four diverted controls are held, the HID++ DivertedButtons frame cannot include every pressed CID. This code treats an absent tilt CID as a release, so a held tilt can fire a second ButtonPressed when it reappears in a later frame after another control is released.
There was a problem hiding this comment.
Fair observation, though it isn't introduced by the tilt change — the rising-edge detection here is a direct mirror of the existing DPI-button handling a few lines up in the same handle_reprog, so it shares the identical 4-slot DivertedButtons limitation. It also can't trigger on the MX Ergo, which only ever diverts ≤4 controls (DPI + the two tilts + the optional gesture button), so a held CID never gets crowded out of a frame. A robust fix would have to change the shared rising-edge logic for the DPI button too, so I've kept it consistent here — happy to do that hardening as a separate shared-layer follow-up.
| @@ -235,6 +260,7 @@ async fn manage( | |||
| if let Err(e) = run_capture_session( | |||
There was a problem hiding this comment.
When a tilt binding changes from default to a custom action, the manager stops the old capture session and waits for the next poll before starting the new one. During that gap the tilt CIDs are restored to native behavior, so a tilt press made immediately after rebinding scrolls horizontally instead of running the new binding.
There was a problem hiding this comment.
This is the existing behavior of the shared manage() loop rather than something specific to tilt — thumbwheel_armed and gesture-owner changes have the same ~1s re-arm transient. During the gap the tilt just falls back to native horizontal scroll (no lost input) and self-heals on the next poll. Happy to harden the shared restart timing in a separate follow-up so DPI / thumbwheel / gesture all benefit.
What
Adds the scroll-wheel tilt-left / tilt-right controls as first-class bindable buttons. These horizontal-tilt controls (HID++
0x1b04reprogrammable controls, CIDs0x5b/0x5d) weren't detected or configurable before; they now appear on the mouse diagram with an action picker and default to horizontal scroll.Addresses #100 (wheel tilt not detected / not configurable). That issue is filed against the M500S; this implements the general mechanism and is verified end-to-end on an MX Ergo (same standard Logitech tilt CIDs). I don't have an M500S to confirm, but the capture path and slot-name mapping are device-agnostic.
How (mirrors existing patterns)
ButtonId::TiltLeft/TiltRight(appended, TOML-stable), default-bound toHorizontalScrollLeft/HorizontalScrollRight.thumbwheel_armed), so native horizontal scrolling is untouched otherwise.SLOT_NAME_SCROLL_LEFT/SLOT_NAME_SCROLL_RIGHT— no asset-registry change.Verification
cargo fmt --check,cargo clippy --workspace -D warnings, andcargo test --workspace(417 passed, 0 failed) all green.tilt_armedgating, and the slot-name mapping.diag controlsreports0x5b/0x5dasdivertable, and a live capture session recorded 142 physical tilt events correctly mapped toTiltLeft/TiltRight(not swapped).Not in scope
0xd7).