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

commit74a0f75d1c608a8b7cfd361737c6222114f5a4b9
parente42a2d1a5a
authorLucas Galante <[email protected]>
date2026-08-05 13:00
fix: expanded status segments stack in the popups layer

The strip-band click routing bug root-caused: the render_finish reorder
pass re-parents and re-raises EVERY window each frame in render-list
order, so all status segments shared layers.top and whichever sibling
rendered last sat on top — clobbering the arrange-time raise of an
expanded segment and letting a collapsed neighbor win the pointer
hit-test over the open menu's strip band (which is also why the failure
was order-dependent and flaky).

Stacking authority stays with the reorder pass: a Status window thicker
than the bar (in-surface menu open) now routes to layers.popups — above
every segment and normal window, like Popup-mode — and returns to
layers.top on collapse, re-evaluated every pass. The redundant
arrange-time raise is removed.

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

 src/server/window_manager.rs | 27 ++++++++++++++++++++++++---
 1 file changed, 24 insertions(+), 3 deletions(-)

diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 848e7d0..f34a9be 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -1456,7 +1456,26 @@ impl WindowManager {
                             } else if (*window).tiling_mode == crate::tiling::TilingMode::Popup {
                                 (*self.server).scene.layers.popups
                             } else if (*window).tiling_mode == crate::tiling::TilingMode::Status {
-                                (*self.server).scene.layers.top
+                                // An EXPANDED segment (in-surface menu open;
+                                // thicker than the bar) stacks like a popup:
+                                // this loop re-raises every window in
+                                // render-list order each pass, so leaving it
+                                // in the shared Status layer let whichever
+                                // sibling rendered last cover the menu's
+                                // strip band (the strip-band click routing
+                                // bug — an arrange-time raise was clobbered
+                                // here every frame).
+                                let bg = (*window).box_geom;
+                                let thickness = match (*window).status_edge {
+                                    crate::policy::arrange::StatusEdge::Left
+                                    | crate::policy::arrange::StatusEdge::Right => bg.width,
+                                    _ => bg.height,
+                                };
+                                if thickness > self.layout.bar_height {
+                                    (*self.server).scene.layers.popups
+                                } else {
+                                    (*self.server).scene.layers.top
+                                }
                             } else if (*window).rendering_requested.circular {
                                 (*self.server).scene.layers.top
                             } else if (*window).tiling_mode == crate::tiling::TilingMode::Overlay && self.layout.overlay_behavior == "above" {
@@ -1730,9 +1749,11 @@ impl WindowManager {
                 };
                 if thickness > 0 && thickness <= self.layout.bar_height {
                     (*win_ptr).status_collapsed_len = len;
-                } else if thickness > self.layout.bar_height {
-                    ffi::wlr_scene_node_raise_to_top((*win_ptr).tree as *mut _);
                 }
+                // Stacking of the expanded segment lives in the
+                // render_finish reorder pass (→ layers.popups), which
+                // re-stacks every window each frame — a raise here was
+                // clobbered by it.
             }
             window_snaps.push(crate::policy::arrange::WindowSnapshot {
                 app_id: (*win_ptr).get_app_id_string(),