Repository navigation
feat(hook): reverse the wheel in software for devices without 0x2121 - #847
Conversation
|
fa99cfd to
9e1bccc
Compare
316eb23 to
334a2f1
Compare
|
Thanks for the PR, I built openlogi with your change and it's working perfectly with my MX Vertical! |
334a2f1 to
e8dc57b
Compare
|
I went through the code and the approach is solid — negating at the native event level avoids the unit-conversion pitfalls of suppress+reinject, and scoping the macOS path to attributed non-trackpad sources is the right guard. I have a Signature M650 on macOS + Bolt, which is exactly the configuration #694 was reported on and the macOS path you haven't been able to verify. Happy to build and test if you rebase past the merge conflicts — the locale files migrated from One note on the process-global flag: you documented the limitation honestly, but it's worth noting that |
|
Rebased this onto current master ( Conflicts. Three, all mechanical except the last:
One thing that wasn't wired up. macOS, verified on hardware. Signature M650 over a Logi Bolt receiver, macOS 26.6.2, Apple Silicon — the configuration #694 was reported on. Worth noting for anyone testing: the macOS transform runs on the CGEventTap, so the agent needs Accessibility, not just Input Monitoring. On a config with no button remappings that is easy to miss — nothing else would have started the hook. Gates. macOS (Apple Silicon): |
0c5a9e2 to
46a265a
Compare
Scroll inversion was offered only where the firmware exposes `0x2121 HiResWheel` with its invert bit. A large part of the current Logitech line does not: the Signature M650 / M750 family exposes `0x2130 RatchetWheel` and no `0x2121` at all, and the M575S is in the same position, so the GUI answered "This device does not report native HID++ scroll inversion support" for a mouse Logi Options+ happily reverses. Reverse it in the capture layer instead, for devices that cannot do it themselves. `openlogi_hook::set_scroll_inversion` turns it on; each backend transforms the event in place, where it still carries its native units — the fields the reader used are the fields the writer negates, so nothing is converted or rounded. Re-injecting from the normalised `delta_y` would have had to pick a unit, and a macOS hi-res wheel reports pixels in one field while a detented one reports lines in another. The orchestrator publishes the flag for the selected device only, and only when the firmware has no invert bit of its own — a device that has one still gets the HID++ write, and doing both would cancel out. The GUI stops gating the toggle on native support and says which of the two is in play, because "OpenLogi reverses this as it captures it" is a different guarantee from the device doing it. `SOFTWARE_SCROLL_INVERSION` is false on Windows, and not because `WH_MOUSE_LL` cannot rewrite an event — suppressing and re-injecting would be exact there. The blocker is attribution: the Windows hook reports no device for a wheel event, so the inversion could not be confined to the configured mouse, and applying it to every wheel event would reverse a precision touchpad too. That is the one thing this setting must not do. Horizontal scrolling is untouched throughout: the setting reverses the wheel, not the thumb wheel. `scrolling_card()` was reading `current_scroll_inversion_supported()` instead of `current_scroll_inversion_available()`, so on a `0x2130` device the toggle stayed disabled and the software-inversion path — the entire point of this change — was never reachable from the GUI. Fixed, and rebased onto current master (locale keys carried through AprilNEA#1169's `.yml` → `.toml` migration, `orchestrator.rs`'s `publish_hook_maps()` extraction). Fixes AprilNEA#694 Fixes AprilNEA#776 Co-authored-by: mingtsay <mt@mingtsay.tw>
46a265a to
165350a
Compare
|
Adopted @mingtsay's rebase and fix — credited via a co-author trailer on the commit.
Full local gate (fmt/clippy/test --workspace) green on the rebased tree. Verified on macOS hardware per mingtsay's report (Signature M650 over a Bolt receiver, the #694 configuration) — thanks for that, and for catching that Accessibility (not just Input Monitoring) is what the macOS transform needs since it runs on the CGEventTap. |
|
@AprilNEA please have a look. Many devices will be unblocked by this. |
# Conflicts: # crates/openlogi-agent-core/src/orchestrator.rs # crates/openlogi-desktop/src/state/scroll.rs
Co-authored-by: David Budnick <david@budnick.ca>
davidbudnick
left a comment
There was a problem hiding this comment.
Thanks for everyone's work on this change!
Summary
Scroll inversion was offered only where the firmware exposes
0x2121 HiResWheelwith its invert bit. A large part of the current Logitech linedoes not — the Signature M650 / M750 family exposes
0x2130 RatchetWheelandno
0x2121at all, and the M575S is in the same position — so the GUI answered"This device does not report native HID++ scroll inversion support" for a mouse
Logi Options+ happily reverses.
This reverses the wheel in the capture layer for exactly those devices. One
implementation covers both reports, so it closes both.
Fixes #694
Fixes #776
Where the inversion happens, and why there
Each backend transforms the event in place, where it still carries its native
units: the fields the reader used are the fields the writer negates, so
nothing is converted or rounded.
Re-injecting from the normalised
delta_y— which #694 offers as the primaryoption — was the first design and it is wrong on macOS.
usable_scroll_deltareads whichever of the point / fixed / line fields is non-zero, so a hi-res
wheel arrives as pixels and a detented one as lines; re-synthesising from the
single normalised value would have to pick a unit and would be wrong for one of
them. Negating the same fields sidesteps the choice.
REL_WHEEL/REL_WHEEL_HI_RESnegated at the point the grabbedevent is re-emitted through the pass-through uinput device.
it is passed through, scoped to non-trackpad sources the hook would remap
buttons for, so a second vendor's mouse and the trackpad are untouched.
WH_MOUSE_LLhands back aread-only event: suppressing and re-injecting would be exact there. The
blocker is attribution. The Windows hook reports no device for a wheel event
(
MouseEvent::Scrollalways carriesdevice: None), so the inversion couldnot be confined to the configured mouse, and applying it to every wheel event
would reverse a precision touchpad too — the one thing this setting must not
do.
SOFTWARE_SCROLL_INVERSIONisfalsethere and the GUI keeps reportingthe device as unsupported.
Changes
openlogi-hookset_scroll_inversion/scroll_inversion, and theSOFTWARE_SCROLL_INVERSIONcapability const.examples/invert_scroll.rs: a manual smoke test that grabs, runs one windowwith inversion off and one with it on, then releases. Two phases on purpose —
a single window cannot tell an applied inversion from a desktop that was
already scrolling that way.
openlogi-agent-corewhen its firmware has no invert bit: a device that has one still gets the
HID++ write from
configured_wheel_mode, and doing both would cancel out.Disabling the device, deselecting it, or turning the setting off clears it.
openlogi-desktopcurrent_scroll_inversion_available/_is_software; the toggle no longergates on native support.
it captures it" is a different guarantee from the device doing it. New string
added to every catalog at the same position.
Horizontal scrolling is untouched throughout: the setting reverses the wheel,
not the thumb wheel.
Known scope
The flag is process-global, so it applies to every pointer the hook captures.
The hook attributes events to a device, but nothing maps an OS event source
back to the config key the setting is stored under, so a second Logitech mouse
would follow the first. Documented on the flag rather than left to be
discovered; per-device scoping needs that mapping to exist first.
Testing
Linux, x86_64, Rust 1.98.0:
All green. Four unit tests on the Linux transform: both vertical axes negated,
off is a byte-for-byte pass-through, horizontal and pointer axes untouched, and
i32::MINsaturates instead of wrapping back to the same sign.Verified on hardware — Linux, Signature M650 over Bluetooth. That device is
the class in question:
openlogi diag featureslists0x2130and no0x2121,and it advertises both
REL_WHEELandREL_WHEEL_HI_RES, so both axes thetransform handles are live on it. Running the two-phase example, the hook
grabbed the mouse,
OpenLogi virtual mouseappeared, 620 wheel events werecaptured across the two windows, and the scroll direction was normal in the
pass-through phase, reversed in the inverted phase, and normal again once the
grab was released. Horizontal events: zero, as expected — the M650 has no thumb
wheel.
Not verified: macOS. There is no macOS SDK on this host, so that backend is
neither run nor compiled here. The
core-graphicssetters it uses(
set_double_value_field,set_integer_value_field) were checked against thevendored 0.25 source, and it negates exactly the fields
usable_scroll_deltareads, but it needs a real Mac and a
0x2130mouse — the configuration #694 wasreported on. Also not run here:
tests (macos),cargo-deny, macOS clippy.To check on macOS: connect an M650/M750, enable the toggle (it should now be
offered, labelled as the software fallback), and confirm the wheel reverses
while the trackpad keeps the system direction.