Add wheel scroll settings#94
Conversation
There was a problem hiding this comment.
ℹ️ Minor suggestions inline — one observation about the scroll-settings default check.
Reviewed changes — Adds three scroll-preference controls (invert, strength, tactility) to the Settings window and changes default horizontal gesture swipes to desktop/Space switching.
- Default gesture direction update —
PrevTab/NextTabreplaced withPreviousDesktop/NextDesktop. - ScrollSettings shared state —
Arc<RwLock<ScrollSettings>>mirrors the persisted config to the hook runtime. - Scroll event transformation —
transform_scroll/quantize_scrollapply inversion, strength, and chunking to captured wheel events. - Session-tap re-injection — Transformed scroll events are posted at
CGEventTapLocation::Sessionto avoid re-capture by OpenLogi's HID tap. - Settings UI — Invert switch, strength slider (1–10), and tactility slider (0–10) in a new "Scroll" group box.
- Generalized
setting_row— Signature changed fromcontrol: Switchtocontrol: impl IntoElementto accept sliders alongside switches. - Expanded native-click passthrough —
Back→BrowserBackandForward→BrowserForwardadded.
Big Pickle (free) (credentials for Anthropic not configured) | 𝕏
There was a problem hiding this comment.
✅ Prior feedback addressed — no new issues found.
Reviewed changes — Fixed the ScrollSettings::default() mismatch with AppSettings defaults by replacing the derived Default with a manual impl whose strength: 1 matches the app config.
- Manual
Defaultimpl forScrollSettings— Replaced#[derive(Default)]with a manualimpl Defaultwherestrength: 1aligns withAppSettings::wheel_strength. This restores the identity fast-path inhook_runtime.rs.
Big Pickle (free) (credentials for Anthropic not configured) | 𝕏
|
This should close #126 |
|
I'm really interested in the "invert wheel direction" feature. Thanks for bringing it up in this Pull Request, I hope it gets merged into the software soon! |
|
Let's get these conflicts resolved so we can merge this. |
|
for anyone looking for an interim fix, https://pilotmoon.com/scrollreverser/ is compatible with this app |
1813205 to
86fdb0d
Compare
Greptile SummaryThis PR adds app-wide scroll-wheel preferences (invert direction, strength multiplier, and tactility/chunking) to OpenLogi, along with the implementation of PreviousDesktop/NextDesktop actions using the macOS CGS symbolic hotkey SPI. It also consolidates Action::execute() into openlogi-core.
Confidence Score: 5/5Safe to merge. The scroll transform paths are well-bounded, the Windows hook guards against re-injected events via LLMHF_INJECTED, and continuous/trackpad events are correctly excluded. The core transform logic is unit-tested. The zero-delta passthrough concern from the prior review is now correctly handled. The binding.rs refactor compiles clean on all targets. binding.rs carries the CGS symbolic-hotkey SPI for space switching — worth a careful read if macOS space-switching is observed to be unreliable. Important Files Changed
Reviews (7): Last reviewed commit: "Merge master into wheel scroll settings" | Re-trigger Greptile |
86fdb0d to
183eb59
Compare
AprilNEA
left a comment
There was a problem hiding this comment.
Thanks for this — invert-scroll is clearly in demand (#126 plus the comments here). Before merging I want to flag some concerns with the current approach, because the suppress-and-reinject design has a few real problems on macOS, and one of them is that it doesn't actually solve #126 as written.
1. It doesn't solve #126 (per-device inversion)
#126 specifically asks to keep the trackpad on natural scrolling and invert only the mouse. The hook taps at CGEventTapLocation::HID (crates/openlogi-hook/src/macos.rs:257) and captures every ScrollWheel event regardless of source, and transform_scroll never looks at kCGScrollWheelEventIsContinuous. So enabling invert flips the trackpad too — exactly the native-setting behavior #126 wants to avoid. We shouldn't close #126 with this as-is.
2. Modifier flags are dropped
post_scroll_delta builds a fresh CGEvent::new_scroll_event(...) and never copies the original event's flags. Modifier+scroll gestures that apps read off the event flags (zoom, etc.) will stop working while non-default settings are active. (System-level Ctrl+scroll screen zoom reads live key state and may be unaffected — worth verifying on-device.)
3. Momentum / pixel precision are lost
We suppress the original and re-emit ScrollEventUnit::LINE from the rounded AXIS_1/AXIS_2 line deltas. That drops scroll-phase/momentum and ignores the pixel POINT_DELTA_* fields, so continuous input (trackpad, MX free-spin high-res wheel) becomes coarse. Micro-deltas round to 0 → PassThrough, so on a free-spinning wheel small movements aren't inverted while fast ones are — inconsistent. Given our primary devices are MX-class, this matters.
4. Per-event cost on the hottest path
The macOS tap must stay lock-light or it stalls the whole input stream (see the comments in macos.rs). This adds an RwLock read per scroll event and, when active, a fresh CGEventSource::new + CGEvent allocation per event. On a high-rate free-spin stream that risks TapDisabledByTimeout / scroll stutter.
Suggested approach
Instead of suppress + reinject, mutate the event in place and return Keep. We already do in-place field writes elsewhere (crates/openlogi-core/src/binding.rs:1312,1327), and the same set_*_value_field works on the callback's &CGEvent. Negate/scale AXIS_1/AXIS_2 plus the POINT_DELTA_* and fixed-point fields and keep the event — that preserves momentum, pixel precision, flags, and continuity, with zero allocation and no re-entry concern. For #126, branch on kCGScrollWheelEventIsContinuous to apply inversion to discrete (mouse) scroll only. This is essentially how Scroll Reverser does it.
Unrelated change
The default gesture remap (PrevTab/NextTab → PreviousDesktop/NextDesktop) is a separate UX decision — please split it into its own PR so each can be reviewed/reverted independently.
The config plumbing, clamping, and default fast-path are clean; the concern is purely the macOS transform path. Happy to help iterate on the in-place version.

Summary
This proposes two related mouse-control improvements:
Implementation notes
AppSettingsand mirrored into the hook runtime through a sharedScrollSettingsvalue.Verification
Ran locally on macOS:
cargo fmt --all cargo check -p openlogi-gui cargo test -p openlogi-gui -p openlogi-coreResults:
openlogi-core: 34 passedopenlogi-gui: 14 passed