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

commitb803e43e370f2cfaed409fd0843aa7f562962089
parent291f8fe76d
authorLucas Galante <[email protected]>
date2026-07-17 14:39
fix: regular blur for Top/Overlay layer surfaces (stop notifier flicker)

An optimized (cached) blur node is re-dirtied whenever any node below it
updates (scenefx wlr_scene.c:744). Top/Overlay layer surfaces sit above all
windows, so window content panning underneath forces a full blur re-bake every
frame — a fixed-position shimmer at the surface's screen rect. The always-mapped
cce-notifier overlay (720x200, top-right) exhibited this continuously, ~40
re-bakes/sec even while idle, visible whenever a window panned under that spot.

Fall back to regular blur for Top/Overlay surfaces, which is immune to the
below-node re-dirty and only re-bakes on real damage — exactly as status
surfaces already do. Bottom/Background layers sit below windows and are
unaffected, so they keep the cache.

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

 src/server/layer_shell.rs | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/server/layer_shell.rs b/src/server/layer_shell.rs
index 0f88d27..85fcaf4 100644
--- a/src/server/layer_shell.rs
+++ b/src/server/layer_shell.rs
@@ -597,7 +597,18 @@ unsafe extern "C" fn handle_layer_surface_commit(listener: *mut ffi::wl_listener
                 is_status = true;
             }
         }
-        let use_optimized = if is_status { false } else { (*server).wm.layout.scenefx_optimized_blur };
+        // Optimized (cached) blur is counterproductive for surfaces stacked ABOVE
+        // windows (Top/Overlay): the scene graph re-dirties an optimized-blur node
+        // whenever any node below it updates (scenefx wlr_scene.c:744), so window
+        // content panning underneath forces a full re-bake every frame — a fixed-
+        // position shimmer (e.g. the always-mapped cce-notifier overlay). Regular
+        // blur is immune to that path and only re-bakes on real damage, so fall back
+        // to it here, exactly as status surfaces already do. Bottom/Background layers
+        // sit below windows and are unaffected, so they keep the cache.
+        let layer = (*wlr_layer_surface).current.layer;
+        let above_windows = layer == ffi::zwlr_layer_shell_v1_layer_ZWLR_LAYER_SHELL_V1_LAYER_TOP
+            || layer == ffi::zwlr_layer_shell_v1_layer_ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY;
+        let use_optimized = if is_status || above_windows { false } else { (*server).wm.layout.scenefx_optimized_blur };
         let wlr_surface = (*wlr_layer_surface).surface;
         let geom_w = if !wlr_surface.is_null() {
             ffi::river_wlr_surface_get_width(wlr_surface)