git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commit4182e9cf0c36b16b4572c26bd371df1dcb9a20c7
parenta3dd4cf63e
authorLucas Galante <[email protected]>
date2026-07-28 10:15
feat: wheel-scroll pan glides via the pan animation

Background/overview/super scroll panning splits by source: discrete
wheel clicks advance the pan animation target so successive clicks
accumulate into one smooth run instead of a stutter of jumps;
finger/continuous scroll keeps tracking 1:1 with the gesture.

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/server/cursor.rs | 34 ++++++++++++++++++++++++----------
 1 file changed, 24 insertions(+), 10 deletions(-)

diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index a08faf7..956a648 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -1622,21 +1622,35 @@ unsafe extern "C" fn handle_axis(listener: *mut ffi::wl_listener, data: *mut std
 
     if (modifiers & 0x40) != 0 || is_on_background || is_overview || was_panning {
         let wm = &mut (*seat.server).wm;
-        wm.stop_panning_animation();
         let step = delta / wm.desk_zoom;
-        match (*event).orientation {
-            ffi::wl_pointer_axis_WL_POINTER_AXIS_VERTICAL_SCROLL => {
+        let vertical =
+            (*event).orientation == ffi::wl_pointer_axis_WL_POINTER_AXIS_VERTICAL_SCROLL;
+        if is_finger {
+            // Finger/continuous scroll tracks 1:1 — the surface follows the
+            // gesture directly, no easing between the two.
+            wm.stop_panning_animation();
+            if vertical {
                 wm.desk_pan_y += step;
-            }
-            ffi::wl_pointer_axis_WL_POINTER_AXIS_HORIZONTAL_SCROLL => {
+            } else {
                 wm.desk_pan_x += step;
             }
-            _ => {}
-        }
-        if matches!(wm.state, crate::window_manager::WindowManagerState::Idle) {
-            wm.update_viewport_local();
+            if matches!(wm.state, crate::window_manager::WindowManagerState::Idle) {
+                wm.update_viewport_local();
+            } else {
+                wm.dirty_windowing();
+            }
         } else {
-            wm.dirty_windowing();
+            // Discrete wheel clicks glide: each click advances the pan
+            // animation target, so successive clicks accumulate into one
+            // smooth run instead of a stutter of jumps.
+            if vertical {
+                let base = wm.target_desk_pan_y.unwrap_or(wm.desk_pan_y);
+                wm.target_desk_pan_y = Some(base + step);
+            } else {
+                let base = wm.target_desk_pan_x.unwrap_or(wm.desk_pan_x);
+                wm.target_desk_pan_x = Some(base + step);
+            }
+            wm.start_panning_animation();
         }
         return;
     }