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

commit3542708fcc22c12161de71cc17685a1652b9aed9
parent8d7c9f1556
authorLucas Galante <[email protected]>
date2026-07-15 13:29
fix(snap): route both resize-size computations through snap::resize_axis

Resize snapping never showed on screen: the seat op computed a snapped
size, but get_active_resize_dimensions independently recomputed an
UNSNAPPED size for the arrange snapshot, and arrange's configure won
every manage cycle. Both paths now call snap::resize_axis (via the new
Layout::snap_params helper), so they cannot diverge.

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

 src/server/config.rs         | 14 +++++++++++++
 src/server/seat.rs           | 48 +++++++++++++-------------------------------
 src/server/window_manager.rs | 26 +++++++++++-------------
 3 files changed, 40 insertions(+), 48 deletions(-)

diff --git a/src/server/config.rs b/src/server/config.rs
index b182916..44d004f 100644
--- a/src/server/config.rs
+++ b/src/server/config.rs
@@ -70,6 +70,20 @@ pub struct Layout {
     pub cloud_position_default: Option<[i32; 2]>,
 }
 
+impl Layout {
+    /// Snap parameters for interactive ops. A zero threshold (snap
+    /// disabled) makes every snap function a no-op.
+    pub fn snap_params(&self) -> crate::policy::snap::SnapParams {
+        crate::policy::snap::SnapParams {
+            cell_size: self.desktop_grid_scale,
+            gap_width: self.desktop_gap_width as f64,
+            cell_inset: self.desktop_cell_fade_inset as f64,
+            threshold: if self.desktop_snap { self.desktop_snap_threshold } else { 0.0 },
+            border_width: self.border_width as f64,
+        }
+    }
+}
+
 impl Default for Layout {
     fn default() -> Self {
         Layout {
diff --git a/src/server/seat.rs b/src/server/seat.rs
index 4924f64..2f07f72 100644
--- a/src/server/seat.rs
+++ b/src/server/seat.rs
@@ -944,16 +944,8 @@ impl Seat {
     }
 
     /// Snap parameters for interactive ops, from the current layout config.
-    /// A zero threshold (snap disabled) makes every snap function a no-op.
     unsafe fn snap_params(&self) -> crate::policy::snap::SnapParams {
-        let layout = &(*self.server).wm.layout;
-        crate::policy::snap::SnapParams {
-            cell_size: layout.desktop_grid_scale,
-            gap_width: layout.desktop_gap_width as f64,
-            cell_inset: layout.desktop_cell_fade_inset as f64,
-            threshold: if layout.desktop_snap { layout.desktop_snap_threshold } else { 0.0 },
-            border_width: layout.border_width as f64,
-        }
+        (*self.server).wm.layout.snap_params()
     }
 
     pub unsafe fn op_update(&mut self, x: i32, y: i32) {
@@ -1196,31 +1188,19 @@ impl Seat {
                             (*win).resize_edges = Some(edges);
                         }
 
-                        // Magnetic grid snap pulls the dragged edge onto grid
-                        // lines; the anchored edge is untouched.
-                        if edges.left {
-                            let left = crate::policy::snap::snap_low_edge(op.start_win_virtual_x + virtual_dx, &sp);
-                            let anchor_right = op.start_win_virtual_x + op.start_win_w as f64;
-                            new_w = std::cmp::max(50, (anchor_right - left) as i32) as u32;
-                        } else if edges.right {
-                            let right = crate::policy::snap::snap_high_edge(
-                                op.start_win_virtual_x + op.start_win_w as f64 + virtual_dx,
-                                &sp,
-                            );
-                            new_w = std::cmp::max(50, (right - op.start_win_virtual_x) as i32) as u32;
-                        }
-
-                        if edges.top {
-                            let top = crate::policy::snap::snap_low_edge(op.start_win_virtual_y + virtual_dy, &sp);
-                            let anchor_bottom = op.start_win_virtual_y + op.start_win_h as f64;
-                            new_h = std::cmp::max(50, (anchor_bottom - top) as i32) as u32;
-                        } else if edges.bottom {
-                            let bottom = crate::policy::snap::snap_high_edge(
-                                op.start_win_virtual_y + op.start_win_h as f64 + virtual_dy,
-                                &sp,
-                            );
-                            new_h = std::cmp::max(50, (bottom - op.start_win_virtual_y) as i32) as u32;
-                        }
+                        // Magnetic grid snap pulls the dragged edge onto the
+                        // visible cell edges; the anchored edge is untouched.
+                        // Must match get_active_resize_dimensions, which
+                        // recomputes this for the arrange snapshot — both go
+                        // through snap::resize_axis.
+                        new_w = crate::policy::snap::resize_axis(
+                            op.start_win_virtual_x, op.start_win_w as f64, virtual_dx,
+                            edges.left, edges.right, 50.0, &sp,
+                        ) as u32;
+                        new_h = crate::policy::snap::resize_axis(
+                            op.start_win_virtual_y, op.start_win_h as f64, virtual_dy,
+                            edges.top, edges.bottom, 50.0, &sp,
+                        ) as u32;
 
                         (*win).virtual_x = vx;
                         (*win).virtual_y = vy;
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 7f0579a..835d201 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -1030,20 +1030,18 @@ impl WindowManager {
                         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 f64 - virtual_dx) as i32) as u32;
-                        } else if edges.right {
-                            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 f64 - virtual_dy) as i32) as u32;
-                        } else if edges.bottom {
-                            new_h = std::cmp::max(50, (op.start_win_h as f64 + virtual_dy) as i32) as u32;
-                        }
+                        // Same math (and snapping) as the seat op's Resize
+                        // arm — this recomputation feeds the arrange
+                        // snapshot and must not diverge from it.
+                        let sp = self.layout.snap_params();
+                        let new_w = crate::policy::snap::resize_axis(
+                            op.start_win_virtual_x, op.start_win_w as f64, virtual_dx,
+                            edges.left, edges.right, 50.0, &sp,
+                        ) as u32;
+                        let new_h = crate::policy::snap::resize_axis(
+                            op.start_win_virtual_y, op.start_win_h as f64, virtual_dy,
+                            edges.top, edges.bottom, 50.0, &sp,
+                        ) as u32;
                         return Some((new_w, new_h));
                     }
                 }