feat(mouse): add Zoom In/Out actions with continuous thumb-wheel zoom - #764
feat(mouse): add Zoom In/Out actions with continuous thumb-wheel zoom#764NatsUIJM wants to merge 3 commits into
Conversation
Adds ZoomIn/ZoomOut as first-class bindable actions across the stack: - core: two new scroll-family actions wired through effects, labels, action-ring icons and binding tests. - inject: macOS renders zoom as a trackpad-pinch magnify gesture (CGEvent type 29 with the f32 bit-pattern payload in fields 115/117, the wire format reverse-engineered by CalfTrail and validated by AbnormalMouseApp). A button press fires one discrete began-to-ended gesture; the thumb wheel instead streams every rotation increment as changed events inside one ongoing session that closes ~250 ms after the wheel rests, so zoom reads as continuous like Logi Options+ rather than stepping per detent. Linux/Windows bank fractional deltas into whole modifier-stamped wheel detents. - agent-core: thumb-wheel rotation bound to zoom no longer gates on whole-line accumulation; each report emits its sensitivity-scaled fraction immediately, keeping total gain identical. - desktop: paired Zoom presets in both polarities for the thumb-wheel picker, mirroring the volume rows. Preset labels now compose from each side's already-translated action name: the flat "X / Y" strings were keys in no locale file, so every preset row fell back to English regardless of app language; composing fixes all locales at once. - ui: zoom-in/zoom-out icons registered and "Zoom In"/"Zoom Out" translated in all locales. Co-Authored-By: Claude <noreply@anthropic.com>
Greptile SummaryThis PR adds bindable Zoom In and Zoom Out actions, platform-specific zoom injection, continuous thumb-wheel streaming, localized picker presets, and matching icons.
Confidence Score: 3/5The PR is not yet safe to merge because fractional zoom state remains mixed across streams on Linux and Windows, while macOS likewise shares one pinch lifecycle across independent devices. Independent devices can cancel or complete each other's banked zoom on Linux and Windows, and macOS discards device ownership before assigning deltas to its single ongoing pinch session. Files Needing Attention: crates/openlogi-inject/src/inject/linux.rs, crates/openlogi-inject/src/inject/windows.rs, crates/openlogi-inject/src/inject/macos.rs, crates/openlogi-agent-core/src/watchers/gesture.rs
|
| Filename | Overview |
|---|---|
| crates/openlogi-agent-core/src/watchers/gesture.rs | Adds immediate sensitivity-scaled ZoomContinuous output, but drops the originating device identity at the injection boundary. |
| crates/openlogi-inject/src/inject/macos.rs | Adds pinch event synthesis and fixes nested discrete gestures, while retaining one lifecycle shared across all device streams. |
| crates/openlogi-inject/src/inject/linux.rs | Adds Ctrl-wheel zoom, but the previously reported process-global fractional accumulator remains. |
| crates/openlogi-inject/src/inject/windows.rs | Adds modifier-wheel zoom and retains the same outstanding cross-stream fractional-state mixing as Linux. |
| crates/openlogi-core/src/binding/action.rs | Safely appends ZoomIn and ZoomOut to the serialized action vocabulary and catalog. |
| crates/openlogi-desktop/src/features/mouse/thumbwheel.rs | Adds paired zoom presets using independently translated action labels. |
Sequence Diagram
sequenceDiagram
participant A as Device A
participant B as Device B
participant W as Gesture watcher
participant Z as Global macOS ZOOM_SESSION
participant App as Focused application
A->>W: Zoom delta with device key A
W->>Z: post_zoom_continuous(delta)
Z->>App: began
B->>W: Zoom delta with device key B
W->>Z: post_zoom_continuous(delta)
Note over W,Z: Device identity is not forwarded
Z->>App: changed in A's existing gesture
Z-->>App: ended after global idle timeout
Reviews (3): Last reviewed commit: "fix(inject): replace nonexistent f32::Fr..." | Re-trigger Greptile
A button-bound Zoom In/Out press always posted a standalone began→ended micro-gesture. While a thumb-wheel pinch session was open, that pair ended the application's active gesture without clearing ZOOM_SESSION, so subsequent wheel deltas posted as orphaned changed events after ended — apps ignored them and zoom froze until the idle closer reset the session. While a session is open, the button step now folds in as one more changed delta instead. Also documents the Linux/Windows fractional-bank design: a single signed process-global accumulator is intentional — the output is focus-directed (there is no per-stream identity for state to cross) and net-proportional cancellation is the correct wheel semantics. Co-Authored-By: Claude <noreply@anthropic.com>
|
Thanks for the review. Both points are addressed in 8de4ae0:
|
|
@NatsUIJM Looks like most of the github actions are failing can you fix those? |
f32 implements From only for the lossless integer types (i8/i16/u8/u16), so `f32::from(whole)` failed to compile on Linux and Windows — seven CI jobs (MSRV, both clippies, rustdoc, tests-linux, both nix builds) all traced back to that one line in each platform's post_zoom_continuous. The macOS build never saw it because the per-OS inject modules are cfg-gated out. Cast explicitly instead; cross-target clippy for x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc now passes locally, with the expect list covering both casts' pedantic lints. Co-Authored-By: Claude <noreply@anthropic.com>
|
Fixed in 7406efa. All seven failing jobs traced to a single root cause: Replaced with explicit casts (the pedantic lint expects cover both), and validated locally with cross-target clippy for |
| static ZOOM_SESSION: std::sync::LazyLock<std::sync::Mutex<ZoomSession>> = | ||
| std::sync::LazyLock::new(|| std::sync::Mutex::new(ZoomSession::default())); |
There was a problem hiding this comment.
Global pinch session mixes devices
If two zoom-bound devices emit thumb-wheel deltas within the same 250 ms session window, the watcher discards their device identities and both update the process-global ZOOM_SESSION. The second device therefore posts changed events into the first device's pinch and extends its lifetime, merging otherwise independent zoom streams in the focused application.
Knowledge Base Used:
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
Heads-up: the workflow runs for the fix commit (7406efa) are sitting in |
Motivation
Logi Options+ lets users turn the MX Master thumb wheel into a zoom control; OpenLogi has no equivalent. On macOS, iWork/Preview-class apps implement zoom only behind the trackpad magnify gesture — they ignore modifier-stamped scroll entirely — so this feature is synthesised as a real pinch gesture rather than ⌘+wheel.
What's added
Core / bindings
ZoomIn/ZoomOutscroll-family actions, bindable to any button or thumb-wheel direction, translated in all locales.Injection (
openlogi-inject)EmulateEventPoster).changedevent inside a single ongoing pinch session (began on the first delta after a quiet spell, ended by a watcher ~250 ms after the wheel rests) — the same lifecycle as AbnormalMouse'sZoomAndRotateController, which makes zoom read as continuous like Logi Options+ instead of stepping per detent.Capture watcher (
openlogi-agent-core)Desktop UI
"X / Y"strings were keys in no locale file; composing fixes all 21 locales at once with zero new strings.Verification
cargo test -p openlogi-inject -p openlogi-agent-core -p openlogi-desktopgreen (incl. new streaming-semantics and preset-pair tests); clippy clean on all three crates.🤖 Generated with Claude Code