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

commitb6af11a2432c20cbd003bb662b0c805d7595a345
parentf41dbdb1c7
authorLucas Galante <[email protected]>
date2026-07-15 12:49
fix(resize): render_finish sizes from current committed geometry mid-resize

Root cause of the residual resize jitter, confirmed by tracing: the WM
render transaction snapshots the window size at render_start
(rendering_sent), but the client keeps committing while the
transaction is in flight, and the anchored position
(rendering_requested.x, updated per commit) tracks the newest commit.
render_finish then paired last commit's width with this commit's
position and clipped the surface to the stale width — bouncing the
anchored edge by the commit lag every cycle (state cycled through
TimedOut, so the Acked-arm size refresh never ran).

While resize_edges is active, render_finish now takes box_geom
dimensions from the toplevel's current committed geometry — the same
source the position was computed from — so size, clip, borders, and
position always describe the same commit. Snapshot behavior is
unchanged outside resizes.

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

 src/server/window.rs | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/src/server/window.rs b/src/server/window.rs
index 918fc9a..835ccb3 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -1858,11 +1858,30 @@ impl Window {
             self.last_applied_scale = self.scale;
         }
 
-        if self.rendering_sent.width > 0 {
-            self.box_geom.width = self.rendering_sent.width as i32;
+        // During an interactive resize, size the box from the client's
+        // CURRENT committed geometry instead of the render-start snapshot
+        // (rendering_sent): commits land between render_start and
+        // render_finish, and the anchored position (rendering_requested.x,
+        // updated by the commit handler) always tracks the newest commit.
+        // Pairing it with the older snapshot size clips the surface short
+        // and makes the anchored edge bounce every cycle.
+        let mut resize_synced = false;
+        if self.resize_edges.is_some() {
+            if let WindowImpl::Toplevel(toplevel) = self.impl_type {
+                if !toplevel.is_null() {
+                    self.box_geom.width = (*toplevel).geometry.width;
+                    self.box_geom.height = (*toplevel).geometry.height;
+                    resize_synced = true;
+                }
+            }
         }
-        if self.rendering_sent.height > 0 {
-            self.box_geom.height = self.rendering_sent.height as i32;
+        if !resize_synced {
+            if self.rendering_sent.width > 0 {
+                self.box_geom.width = self.rendering_sent.width as i32;
+            }
+            if self.rendering_sent.height > 0 {
+                self.box_geom.height = self.rendering_sent.height as i32;
+            }
         }
 
         let mut clip = requested.clip;