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

commit603bfe44af8c1cfe37f1a44245c8c1eb9c47998d
parentc2d429e056
authorLucas Galante <[email protected]>
date2026-08-10 11:40
fix: raise light_source explicitly in the reorder pass

The status-window move-order sort didn't survive the render list's
insert semantics (the final order proved independent of move order in
both directions). Raise the segment's tree once per reorder pass,
after the main loop — same per-frame authority, no list-order
dependence. Verified: the sphere draws over the segment it crosses.

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

 src/server/window_manager.rs | 26 ++++++++++++++++++--------
 1 file changed, 18 insertions(+), 8 deletions(-)

diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index be9b4f7..670939d 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -1550,6 +1550,24 @@ impl WindowManager {
             curr = next;
         }
 
+        // The traveling light_source segment crosses over its sibling
+        // segments; raise it after the loop so it stacks in front of them
+        // within its layer regardless of render-list order. Re-applied on
+        // every reorder pass — a rule in the stacking authority, not a
+        // one-shot raise.
+        if reorder {
+            for &w in self.windows.iter() {
+                if !w.is_null()
+                    && !(*w).closed
+                    && matches!((*w).state, crate::window::WindowState::Mapped)
+                    && (*w).get_app_id_string().map_or(false, |id| id.ends_with("light_source"))
+                {
+                    ffi::wlr_scene_node_raise_to_top((*w).tree as *mut ffi::wlr_scene_node);
+                    ffi::wlr_scene_node_place_above((*w).popup_tree as *mut _, (*w).tree as *mut _);
+                }
+            }
+        }
+
         (*self.server).om.commit_output_state(self.server);
 
         let seats = &mut (*self.server).input_manager.seats as *mut ffi::wl_list as *mut WlList;
@@ -2411,14 +2429,6 @@ impl WindowManager {
                 }
             }
         }
-        // The traveling light_source segment crosses over its siblings on
-        // the top edge; sorting it to the tail makes it the last segment
-        // moved to the render-list end, so the reorder pass raises it last
-        // — in front of every other segment. (Stacking rules live in this
-        // per-frame path, never as one-shot raises.)
-        status_bar_windows.sort_by_key(|&w| {
-            (*w).get_app_id_string().map_or(false, |id| id.ends_with("light_source"))
-        });
         for win_ptr in status_bar_windows {
             let node_link = &mut (*win_ptr).node.link as *mut ffi::wl_list as *mut WlList;
             let list_head = &mut self.rendering_requested.list as *mut ffi::wl_list as *mut WlList;