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

commita12385f56ad0c062a6790442fca54e6fd106d2f8
parent98057426b7
authorLucas Galante <[email protected]>
date2026-08-05 14:41
fix: size status-segment blur from committed geometry, not rendering_sent

Contracting an expanded status segment left a menu-sized blur ghost
below the strip for ~1s: the commit-path blur sizing reads
rendering_sent, a render-start snapshot that still holds the expanded
height when the contract commit lands, so the blur node was re-sized
to the stale expanded box until the next commit re-ran the path.
Status segments are self-sizing (bounds track their own box), so all
three blur-sizing sites now prefer the committed toplevel geometry for
them. Also adds the status corner-clip exemption (9805742) to the
commit-path radius mirror in xdg_toplevel.rs, which was missed.

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

 src/server/window.rs       | 25 +++++++++++++++++++++----
 src/server/xdg_toplevel.rs | 25 ++++++++++++++++++++-----
 2 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/src/server/window.rs b/src/server/window.rs
index cf60474..1779a7c 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -2061,8 +2061,18 @@ impl Window {
                 }
                 _ => 0,
             };
-            let actual_w = if self.rendering_sent.width > 0 { self.rendering_sent.width } else { toplevel_w as u32 };
-            let actual_h = if self.rendering_sent.height > 0 { self.rendering_sent.height } else { toplevel_h as u32 };
+            // Status segments are self-sizing: their committed geometry is
+            // fresher than the render-start snapshot (`rendering_sent`),
+            // which lags an expand/contract commit by a render pass — same
+            // rule as the commit-path blur sizing in xdg_toplevel.rs.
+            let (actual_w, actual_h) = if is_status && toplevel_w > 0 && toplevel_h > 0 {
+                (toplevel_w as u32, toplevel_h as u32)
+            } else {
+                (
+                    if self.rendering_sent.width > 0 { self.rendering_sent.width } else { toplevel_w as u32 },
+                    if self.rendering_sent.height > 0 { self.rendering_sent.height } else { toplevel_h as u32 },
+                )
+            };
             // Widen squircle corners to the span the clients draw (see
             // widen_corner_radius); circles already sit at the half-extent cap.
             let radius = if requested.circular { radius } else { widen_corner_radius(radius, actual_w as i32, actual_h as i32) };
@@ -2503,8 +2513,15 @@ impl Window {
                     }
                     _ => 0,
                 };
-                let actual_w = if self.rendering_sent.width > 0 { self.rendering_sent.width } else { toplevel_w as u32 };
-                let actual_h = if self.rendering_sent.height > 0 { self.rendering_sent.height } else { toplevel_h as u32 };
+                // Same self-sizing rule as set_rendering_state above.
+                let (actual_w, actual_h) = if is_status && toplevel_w > 0 && toplevel_h > 0 {
+                    (toplevel_w as u32, toplevel_h as u32)
+                } else {
+                    (
+                        if self.rendering_sent.width > 0 { self.rendering_sent.width } else { toplevel_w as u32 },
+                        if self.rendering_sent.height > 0 { self.rendering_sent.height } else { toplevel_h as u32 },
+                    )
+                };
                 // Same span widening as set_rendering_state — the two paths
                 // drive the same blur node and must agree.
                 let radius = if requested.circular { radius } else { widen_corner_radius(radius, actual_w as i32, actual_h as i32) };
diff --git a/src/server/xdg_toplevel.rs b/src/server/xdg_toplevel.rs
index c0b8018..a2a3540 100644
--- a/src/server/xdg_toplevel.rs
+++ b/src/server/xdg_toplevel.rs
@@ -530,13 +530,24 @@ unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut
         ignore_transparent = (*(*window).server).wm.layout.status_backdrop_blur_ignore_transparent;
     }
     let scale = (*window).scale;
-    let actual_w = if (*window).rendering_sent.width > 0 { (*window).rendering_sent.width } else { (*toplevel).geometry.width as u32 };
-    let actual_h = if (*window).rendering_sent.height > 0 { (*window).rendering_sent.height } else { (*toplevel).geometry.height as u32 };
-    let geom_w = (actual_w as f64 * scale) as i32;
-    let geom_h = (actual_h as f64 * scale) as i32;
-    let is_status = (*window).tiling_mode == crate::tiling::TilingMode::Status || 
+    let is_status = (*window).tiling_mode == crate::tiling::TilingMode::Status ||
                     app_id.starts_with("cce-status");
     let is_cce_app = app_id.starts_with("cce-");
+    // Status segments are SELF-sizing (their bounds track their own box), so
+    // the geometry of the commit being handled is the truth. `rendering_sent`
+    // is a render-start snapshot that lags a contract commit by a render pass
+    // — sizing the blur from it left a menu-sized blur ghost hanging below
+    // the strip until the next commit re-ran this path.
+    let (actual_w, actual_h) = if is_status && (*toplevel).geometry.width > 0 && (*toplevel).geometry.height > 0 {
+        ((*toplevel).geometry.width as u32, (*toplevel).geometry.height as u32)
+    } else {
+        (
+            if (*window).rendering_sent.width > 0 { (*window).rendering_sent.width } else { (*toplevel).geometry.width as u32 },
+            if (*window).rendering_sent.height > 0 { (*window).rendering_sent.height } else { (*toplevel).geometry.height as u32 },
+        )
+    };
+    let geom_w = (actual_w as f64 * scale) as i32;
+    let geom_h = (actual_h as f64 * scale) as i32;
     // Must mirror Window::set_rendering_state's radius exactly: both paths drive the same
     // blur node, so if they disagree the corners flip between rounded and square depending
     // on which one ran last.
@@ -546,6 +557,10 @@ unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut
         let w = (*window).rendering_sent.width as i32;
         let h = (*window).rendering_sent.height as i32;
         w.min(h) / 2
+    } else if is_status {
+        // Same status exemption as Window::set_rendering_state (part of the
+        // mirror): status segments draw their own module-box corners.
+        0
     } else if (*window).wm_requested.ssd || is_cce_app {
         (*(*window).server).wm.layout.backplate_corner_radius
     } else {