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

commit2c7ed2c7073c2b0745ecfd39a0684bc93276a883
parent5c5b6741d6
authorLucas Galante <[email protected]>
date2026-06-26 21:47
fix(cce-server): scale resize deltas by desk_zoom to fix resizing choppiness

 src/server/window.rs         | 53 +++++++++++++++++++++++++++++++++++++++++++-
 src/server/window_manager.rs | 17 ++++++++------
 src/server/xdg_toplevel.rs   |  2 +-
 3 files changed, 63 insertions(+), 9 deletions(-)

diff --git a/src/server/window.rs b/src/server/window.rs
index 6480e47..2b90d4c 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -230,6 +230,11 @@ pub struct Window {
     pub resize_start_h: u32,
     pub resize_edges: Option<Edges>,
     pub commit: ffi::wl_listener,
+    pub was_fullscreen: bool,
+    pub saved_width: i32,
+    pub saved_height: i32,
+    pub saved_virtual_x: f64,
+    pub saved_virtual_y: f64,
 
     pub wm_scheduled: WmScheduledState,
     pub wm_sent: WmSentState,
@@ -392,6 +397,11 @@ impl Window {
             resize_start_h: 0,
             resize_edges: None,
             commit: std::mem::zeroed(),
+            was_fullscreen: false,
+            saved_width: 0,
+            saved_height: 0,
+            saved_virtual_x: 0.0,
+            saved_virtual_y: 0.0,
             wm_scheduled: WmScheduledState {
                 dimensions_hint: DimensionsHint { min_width: 0, min_height: 0, max_width: 0, max_height: 0 },
                 decoration_hint: ffi::zcce_window_v1_decoration_hint_ZCCE_WINDOW_V1_DECORATION_HINT_ONLY_SUPPORTS_CSD,
@@ -735,13 +745,22 @@ impl Window {
         Ok(())
     }
 
+    pub unsafe fn set_closing(&mut self) {
+        if self.state != WindowState::Closing {
+            self.state = WindowState::Closing;
+            wl_list_remove(&mut self.node.link as *mut ffi::wl_list as *mut WlList);
+            self.node.link.prev = &mut self.node.link;
+            self.node.link.next = &mut self.node.link;
+        }
+    }
+
     pub unsafe fn unmap(&mut self) {
         log::debug!("window '{:?}' unmapped", self.get_title());
         wl_listener_remove_safe(&mut self.commit);
         self.surfaces.save();
         assert!(!matches!(self.impl_type, WindowImpl::Destroying));
         assert_eq!(self.state, WindowState::Mapped);
-        self.state = WindowState::Closing;
+        self.set_closing();
         (*self.server).wm.dirty_windowing();
 
         if !self.foreign_toplevel_handle.is_null() {
@@ -1281,6 +1300,38 @@ impl Window {
             std::ptr::null_mut()
         };
 
+        let new_fullscreen = !output.is_null();
+        if new_fullscreen && !self.was_fullscreen {
+            if self.box_geom.width > 0 && self.box_geom.height > 0 {
+                self.saved_width = self.box_geom.width;
+                self.saved_height = self.box_geom.height;
+                self.saved_virtual_x = self.virtual_x;
+                self.saved_virtual_y = self.virtual_y;
+                self.was_fullscreen = true;
+                log::info!("[Fullscreen] Saved window {:?} geometry: {}x{} at ({}, {})", self.get_title_string().as_deref().unwrap_or(""), self.saved_width, self.saved_height, self.saved_virtual_x, self.saved_virtual_y);
+            }
+        } else if !new_fullscreen && self.was_fullscreen {
+            if self.saved_width > 0 && self.saved_height > 0 {
+                self.box_geom.width = self.saved_width;
+                self.box_geom.height = self.saved_height;
+                self.virtual_x = self.saved_virtual_x;
+                self.virtual_y = self.saved_virtual_y;
+                self.was_fullscreen = false;
+
+                self.wm_requested.dimensions = Some(crate::window::Dimensions {
+                    width: self.saved_width as u32,
+                    height: self.saved_height as u32,
+                });
+                self.wm_requested.bounds = crate::window::Dimensions {
+                    width: self.saved_width as u32,
+                    height: self.saved_height as u32,
+                };
+
+                (*self.server).wm.dirty_windowing();
+                log::info!("[Fullscreen] Restored window {:?} geometry: {}x{} at ({}, {})", self.get_title_string().as_deref().unwrap_or(""), self.saved_width, self.saved_height, self.saved_virtual_x, self.saved_virtual_y);
+            }
+        }
+
         let (width, height) = if !output.is_null() {
             let (w, h) = (*output).sent.dimensions();
             if self.configure_sent.width != Some(w as u32) || self.configure_sent.height != Some(h as u32) {
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 781f64f..c510d26 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -803,21 +803,24 @@ impl WindowManager {
             if let Some(ref op) = (*seat).op {
                 if op.window_ptr == win_ptr {
                     if let crate::seat::PointerOpType::Resize { edges } = op.op_type {
+                        let scale = self.desk_zoom;
                         let dx = op.x - op.start_x;
                         let dy = op.y - op.start_y;
+                        let virtual_dx = dx as f64 / scale;
+                        let virtual_dy = dy as f64 / scale;
                         let mut new_w = op.start_win_w;
                         let mut new_h = op.start_win_h;
 
                         if edges.left {
-                            new_w = std::cmp::max(50, op.start_win_w as i32 - dx) as u32;
+                            new_w = std::cmp::max(50, (op.start_win_w as f64 - virtual_dx) as i32) as u32;
                         } else if edges.right {
-                            new_w = std::cmp::max(50, op.start_win_w as i32 + dx) as u32;
+                            new_w = std::cmp::max(50, (op.start_win_w as f64 + virtual_dx) as i32) as u32;
                         }
 
                         if edges.top {
-                            new_h = std::cmp::max(50, op.start_win_h as i32 - dy) as u32;
+                            new_h = std::cmp::max(50, (op.start_win_h as f64 - virtual_dy) as i32) as u32;
                         } else if edges.bottom {
-                            new_h = std::cmp::max(50, op.start_win_h as i32 + dy) as u32;
+                            new_h = std::cmp::max(50, (op.start_win_h as f64 + virtual_dy) as i32) as u32;
                         }
                         return Some((new_w, new_h));
                     }
@@ -1985,7 +1988,7 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
                     // Try to match by numerical window index first
                     if let Ok(id) = query.parse::<u32>() {
                         for &w in self.windows.iter() {
-                            if !w.is_null() && !(*w).closed && (*w).ref_key.index == id {
+                            if !w.is_null() && !(*w).closed && matches!((*w).state, crate::window::WindowState::Mapped) && (*w).ref_key.index == id {
                                 best_target = w;
                                 break;
                             }
@@ -1995,9 +1998,9 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
                     // Fallback to matching by app_id
                     if best_target.is_null() {
                         for &w in self.windows.iter() {
-                            if !w.is_null() && !(*w).closed {
+                            if !w.is_null() && !(*w).closed && matches!((*w).state, crate::window::WindowState::Mapped) {
                                 let aid = (*w).get_app_id_string();
-
+                                
                                 let mut score = 0;
                                 if let Some(ref aid_str) = aid {
                                     let aid_lower = aid_str.to_lowercase();
diff --git a/src/server/xdg_toplevel.rs b/src/server/xdg_toplevel.rs
index 85b8236..7168ec9 100644
--- a/src/server/xdg_toplevel.rs
+++ b/src/server/xdg_toplevel.rs
@@ -350,7 +350,7 @@ unsafe extern "C" fn handle_destroy(listener: *mut ffi::wl_listener, _data: *mut
     match (*window).state {
         crate::window::WindowState::Init | crate::window::WindowState::Closing => {}
         crate::window::WindowState::Ready | crate::window::WindowState::Initialized | crate::window::WindowState::Mapped => {
-            (*window).state = crate::window::WindowState::Closing;
+            (*window).set_closing();
             (*(*window).server).wm.dirty_windowing();
         }
     }