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