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

commit24c328c5621f4b9a5995ca74d50df44bf8037070
parentfe46e6e098
authorLucas Galante <[email protected]>
date2026-06-24 12:46
fix: stop panning animation on interaction and use correct module path for animation timer

 src/server/cursor.rs         |  4 +++
 src/server/seat.rs           | 22 ++++++++++++++---
 src/server/window_manager.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++
 src/server/xdg_toplevel.rs   |  2 ++
 4 files changed, 83 insertions(+), 3 deletions(-)

diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index 5ca86f1..379e0a7 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -659,6 +659,7 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
                 };
                 
                 if let Some(ot) = op_type {
+                    (*server).wm.stop_panning_animation();
                     let cursor_x = (*cursor.wlr_cursor).x;
                     let cursor_y = (*cursor.wlr_cursor).y;
                     seat.op = Some(crate::seat::SeatOp {
@@ -718,6 +719,7 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
                         }
 
                         seat.focus(Focus::Window(border_target_win));
+                        (*server).wm.stop_panning_animation();
                         let cursor_x = (*cursor.wlr_cursor).x;
                         let cursor_y = (*cursor.wlr_cursor).y;
                         seat.op = Some(crate::seat::SeatOp {
@@ -754,6 +756,7 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
                         }
 
                         seat.focus(Focus::Window(border_target_win));
+                        (*server).wm.stop_panning_animation();
                         let cursor_x = (*cursor.wlr_cursor).x;
                         let cursor_y = (*cursor.wlr_cursor).y;
                         seat.op = Some(crate::seat::SeatOp {
@@ -885,6 +888,7 @@ unsafe extern "C" fn handle_axis(listener: *mut ffi::wl_listener, data: *mut std
 
     if (modifiers & 0x40) != 0 {
         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 => {
diff --git a/src/server/seat.rs b/src/server/seat.rs
index 477dc0f..ccdba2e 100644
--- a/src/server/seat.rs
+++ b/src/server/seat.rs
@@ -433,9 +433,24 @@ impl Seat {
                         let wm = &mut (*self.server).wm;
                         let scale = wm.desk_zoom;
 
-                        wm.desk_pan_x = (*window).virtual_x + (fw / 2.0 - viewport_w / 2.0) / scale;
-                        wm.desk_pan_y = (*window).virtual_y + (fh / 2.0 - viewport_h / 2.0) / scale;
-                        wm.dirty_windowing();
+                        let target_x = (*window).virtual_x + (fw / 2.0 - viewport_w / 2.0) / scale;
+                        let target_y = (*window).virtual_y + (fh / 2.0 - viewport_h / 2.0) / scale;
+
+                        wm.target_desk_pan_x = Some(target_x);
+                        wm.target_desk_pan_y = Some(target_y);
+
+                        if wm.animation_timer.is_null() {
+                            let event_loop = ffi::wl_display_get_event_loop((*self.server).wl_server);
+                            wm.animation_timer = ffi::wl_event_loop_add_timer(
+                                event_loop,
+                                Some(crate::window_manager::handle_panning_animation_tick),
+                                wm as *mut crate::window_manager::WindowManager as *mut _,
+                            );
+                        }
+
+                        if !wm.animation_timer.is_null() {
+                            ffi::wl_event_source_timer_update(wm.animation_timer, 16);
+                        }
                     }
                 }
 
@@ -1238,6 +1253,7 @@ unsafe extern "C" fn seat_op_start_pointer(
             start_win_virtual_x: 0.0,
             start_win_virtual_y: 0.0,
         });
+        (*(*seat).server).wm.stop_panning_animation();
         (*seat).cursor.op_start_pointer();
     }
 }
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 4f1603b..2b9be44 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -101,6 +101,9 @@ pub struct WindowManager {
     pub last_status_update: std::cell::RefCell<Option<crate::status_server::StatusUpdate>>,
     pub restore_queue: Vec<SavedWindowState>,
     pub shutting_down: bool,
+    pub target_desk_pan_x: Option<f64>,
+    pub target_desk_pan_y: Option<f64>,
+    pub animation_timer: *mut ffi::wl_event_source,
 }
 
 impl WindowManager {
@@ -143,6 +146,9 @@ impl WindowManager {
         self.dirty_idle = std::ptr::null_mut();
         self.desk_pan_x = 0.0;
         self.desk_pan_y = 0.0;
+        self.target_desk_pan_x = None;
+        self.target_desk_pan_y = None;
+        self.animation_timer = std::ptr::null_mut();
         self.desk_zoom = 1.0;
         self.global_layout = crate::tiling::TilingMode::Cascade;
         self.restore_queue = Vec::new();
@@ -331,9 +337,18 @@ impl WindowManager {
             ffi::wl_event_source_remove(self.timeout);
             self.timeout = std::ptr::null_mut();
         }
+        if !self.animation_timer.is_null() {
+            ffi::wl_event_source_remove(self.animation_timer);
+            self.animation_timer = std::ptr::null_mut();
+        }
         wl_listener_remove(&mut self.server_destroy);
     }
 
+    pub unsafe fn stop_panning_animation(&mut self) {
+        self.target_desk_pan_x = None;
+        self.target_desk_pan_y = None;
+    }
+
     pub unsafe fn ensure_windowing(&self) -> bool {
         match self.state {
             WindowManagerState::Manage => true,
@@ -1274,6 +1289,7 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
 
     pub unsafe fn execute_action(&mut self, action: &crate::config::Action, command: Option<&str>) {
         use crate::config::Action;
+        self.stop_panning_animation();
         match action {
             Action::None => {}
             Action::Spawn => {
@@ -1631,6 +1647,7 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
     }
 
     pub unsafe fn process_ipc_command(&mut self, cmd: &str) -> String {
+        self.stop_panning_animation();
         let parts: Vec<&str> = cmd.split_whitespace().collect();
         if parts.is_empty() {
             return "error: empty command\n".to_string();
@@ -2421,3 +2438,44 @@ unsafe extern "C" fn handle_destroy_wm_resource(resource: *mut ffi::wl_resource)
         WindowManagerState::Render => (*wm).render_finish(),
     }
 }
+
+pub(crate) unsafe extern "C" fn handle_panning_animation_tick(data: *mut std::ffi::c_void) -> std::os::raw::c_int {
+    let wm = data as *mut WindowManager;
+    if wm.is_null() {
+        return 0;
+    }
+    
+    let mut done = true;
+    let factor = 0.15;
+    
+    if let Some(target_x) = (*wm).target_desk_pan_x {
+        let dx = target_x - (*wm).desk_pan_x;
+        if dx.abs() > 0.5 {
+            (*wm).desk_pan_x += dx * factor;
+            done = false;
+        } else {
+            (*wm).desk_pan_x = target_x;
+            (*wm).target_desk_pan_x = None;
+        }
+    }
+    
+    if let Some(target_y) = (*wm).target_desk_pan_y {
+        let dy = target_y - (*wm).desk_pan_y;
+        if dy.abs() > 0.5 {
+            (*wm).desk_pan_y += dy * factor;
+            done = false;
+        } else {
+            (*wm).desk_pan_y = target_y;
+            (*wm).target_desk_pan_y = None;
+        }
+    }
+    
+    (*wm).dirty_windowing();
+    
+    if !done {
+        if !(*wm).animation_timer.is_null() {
+            ffi::wl_event_source_timer_update((*wm).animation_timer, 16);
+        }
+    }
+    0
+}
diff --git a/src/server/xdg_toplevel.rs b/src/server/xdg_toplevel.rs
index dc242be..7bde7da 100644
--- a/src/server/xdg_toplevel.rs
+++ b/src/server/xdg_toplevel.rs
@@ -594,6 +594,7 @@ unsafe extern "C" fn handle_request_move(
         }
 
         (*seat).focus(crate::seat::Focus::Window(window));
+        (*(*window).server).wm.stop_panning_animation();
         let cursor = &mut (*seat).cursor;
         let cursor_x = (*cursor.wlr_cursor).x;
         let cursor_y = (*cursor.wlr_cursor).y;
@@ -641,6 +642,7 @@ unsafe extern "C" fn handle_request_resize(
         }
 
         (*seat).focus(crate::seat::Focus::Window(window));
+        (*(*window).server).wm.stop_panning_animation();
         let cursor = &mut (*seat).cursor;
         let cursor_x = (*cursor.wlr_cursor).x;
         let cursor_y = (*cursor.wlr_cursor).y;