Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
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 {