From 7ecf2d8dfdbb3a49b6817ec9918e53744927d6bd Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Sat, 13 Jun 2026 12:37:07 +0000 Subject: [PATCH] fix(inject/libei): emit the continuous scroll axis so small scrolls register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The libei backend forwarded mouse wheel only via scroll_discrete (120-per-detent). Mutter floors a sub-detent delta — a trackpad, a precise/high-res wheel, or a fractional smooth-scroll event — to zero whole clicks, so small scrolls never land and you have to spin the wheel a lot before anything moves. Emit the continuous `scroll` axis (logical px, ~15 px/detent) alongside the discrete steps, matching the wlroots backend's 15-px/notch behaviour, so every delta moves proportionally while full detents still drive line/page scrolling. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/punktfunk-host/src/inject/libei.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/crates/punktfunk-host/src/inject/libei.rs b/crates/punktfunk-host/src/inject/libei.rs index aa4b18b..1cebdf3 100644 --- a/crates/punktfunk-host/src/inject/libei.rs +++ b/crates/punktfunk-host/src/inject/libei.rs @@ -615,15 +615,24 @@ impl EiState { } InputKind::MouseScroll => match slot.interface::() { Some(s) => { - // GameStream sends WHEEL_DELTA(120)-scaled deltas in `x`; ei scroll_discrete - // uses the same 120-per-detent unit. Positive GameStream = up (vertical), - // which is negative on the ei axis, but = RIGHT (horizontal), which is - // already positive there (moonlight-qt/Sunshine pass horizontal through - // unnegated) — only the vertical axis flips. + // Wire deltas are WHEEL_DELTA(120)-scaled in `x`. Emit BOTH ei scroll axes + // from it: `scroll_discrete` (120-per-detent — drives line/page scrolling) + // AND the continuous `scroll` axis in logical px (≈15 px/detent). Without + // the continuous axis Mutter floors a sub-detent delta (trackpad / precise + // wheel / fractional smooth scroll) to zero whole clicks, so small scrolls + // never register and you have to spin the wheel a lot — emitting the pixel + // axis too makes every delta move proportionally (matches the wlr backend's + // 15 px/notch). Positive wire = up (vertical, negated on the ei axis) / + // RIGHT (horizontal, already positive — moonlight-qt/Sunshine pass it + // through unnegated); only the vertical axis flips. + const PX_PER_DETENT: f32 = 15.0; + let px = ev.x as f32 / 120.0 * PX_PER_DETENT; if ev.code == SCROLL_HORIZONTAL { s.scroll_discrete(ev.x, 0); + s.scroll(px, 0.0); } else { s.scroll_discrete(0, -ev.x); + s.scroll(0.0, -px); } } None => emitted = false,