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

commite9a74e3558d94a3fd2a78ec760c9a8f2f5f79301
parentf7c07d38a0
authorLucas Galante <[email protected]>
date2026-06-29 22:25
Add backdrop_blur_ignore_transparent configuration support to decouple status background color and blur opacity

 src/server/config.rs             | 16 ++++++++++++++++
 src/server/layer_shell.rs        |  3 +++
 src/server/window.rs             | 14 ++++++++++++--
 src/server/wlroots_log_wrapper.c | 10 ++++++----
 src/server/xdg_toplevel.rs       |  6 ++++++
 wrapper.h                        |  2 +-
 6 files changed, 44 insertions(+), 7 deletions(-)

diff --git a/src/server/config.rs b/src/server/config.rs
index 396726b..b56c293 100644
--- a/src/server/config.rs
+++ b/src/server/config.rs
@@ -45,6 +45,8 @@ pub struct Layout {
     pub desktop_grid_scale: f64,
     pub desktop_line_width: i32,
     pub scenefx_optimized_blur: bool,
+    pub status_backdrop_blur_ignore_transparent: bool,
+    pub window_backdrop_blur_ignore_transparent: bool,
 }
 
 impl Default for Layout {
@@ -88,6 +90,8 @@ impl Default for Layout {
             desktop_grid_scale: 100.0,
             desktop_line_width: 1,
             scenefx_optimized_blur: true,
+            status_backdrop_blur_ignore_transparent: true,
+            window_backdrop_blur_ignore_transparent: true,
         }
     }
 }
@@ -355,6 +359,10 @@ pub struct LayoutConfig {
     pub status_background_blur: f64,
     #[serde(default = "default_window_opacity")]
     pub window_opacity: bool,
+    #[serde(default = "default_status_backdrop_blur_ignore_transparent")]
+    pub status_backdrop_blur_ignore_transparent: bool,
+    #[serde(default = "default_window_backdrop_blur_ignore_transparent")]
+    pub window_backdrop_blur_ignore_transparent: bool,
 }
 
 impl Default for LayoutConfig {
@@ -377,6 +385,8 @@ impl Default for LayoutConfig {
             status_normal_color: default_status_normal_color(),
             status_background_blur: default_status_background_blur(),
             window_opacity: default_window_opacity(),
+            status_backdrop_blur_ignore_transparent: default_status_backdrop_blur_ignore_transparent(),
+            window_backdrop_blur_ignore_transparent: default_window_backdrop_blur_ignore_transparent(),
         }
     }
 }
@@ -398,6 +408,8 @@ fn default_overlay_border_gap() -> i64 { 0 }
 fn default_status_normal_color() -> String { "#ccccd8".to_string() }
 fn default_status_background_blur() -> f64 { 0.8 }
 fn default_window_opacity() -> bool { true }
+fn default_status_backdrop_blur_ignore_transparent() -> bool { true }
+fn default_window_backdrop_blur_ignore_transparent() -> bool { true }
 
 #[derive(Debug, Deserialize)]
 pub struct ModeRuleConfig {
@@ -943,6 +955,7 @@ fn parse_kdl_config(content: &str) -> Result<Config, String> {
         layout.transition_duration = get_nested_prop_i64(node, "window", "transition_duration", default_transition_duration());
         layout.window_blur = get_nested_prop_bool(node, "window", "blur", default_window_blur());
         layout.window_opacity = get_nested_prop_bool(node, "window", "opacity", default_window_opacity());
+        layout.window_backdrop_blur_ignore_transparent = get_nested_prop_bool(node, "window", "backdrop_blur_ignore_transparent", default_window_backdrop_blur_ignore_transparent());
         
         layout.overlay_behavior = get_nested_prop_string(node, "overlay", "behavior", &default_overlay_behavior());
         layout.overlay_width = get_nested_prop_i64(node, "overlay", "width", default_overlay_width());
@@ -951,6 +964,7 @@ fn parse_kdl_config(content: &str) -> Result<Config, String> {
         
         layout.status_normal_color = get_nested_prop_string(node, "status", "normal_color", &default_status_normal_color());
         layout.status_background_blur = get_nested_prop_f64(node, "status", "background_blur", default_status_background_blur());
+        layout.status_backdrop_blur_ignore_transparent = get_nested_prop_bool(node, "status", "backdrop_blur_ignore_transparent", default_status_backdrop_blur_ignore_transparent());
     }
 
     // 2. env
@@ -1212,6 +1226,8 @@ pub fn parse_config(path: &str, state: &mut crate::window_manager::WindowManager
     state.layout.transparency_opacity = config.transparency.as_ref().and_then(|t| t.opacity).unwrap_or(0.9) as f32;
     state.layout.window_opacity = config.layout.window_opacity;
     state.layout.scenefx_optimized_blur = config.output.as_ref().map(|o| o.scenefx_optimized_blur).unwrap_or(true);
+    state.layout.status_backdrop_blur_ignore_transparent = config.layout.status_backdrop_blur_ignore_transparent;
+    state.layout.window_backdrop_blur_ignore_transparent = config.layout.window_backdrop_blur_ignore_transparent;
 
     for (key, val) in &config.env {
         let expanded = expand_env_vars(val);
diff --git a/src/server/layer_shell.rs b/src/server/layer_shell.rs
index 4133111..9f7fe55 100644
--- a/src/server/layer_shell.rs
+++ b/src/server/layer_shell.rs
@@ -585,16 +585,19 @@ unsafe extern "C" fn handle_layer_surface_commit(listener: *mut ffi::wl_listener
     if (*wlr_layer_surface).current.layer != ffi::zwlr_layer_shell_v1_layer_ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND {
         let server = (*layer_surface).server;
         let mut blur_enabled = true;
+        let mut ignore_transparent = (*server).wm.layout.window_backdrop_blur_ignore_transparent;
         if !(*wlr_layer_surface).namespace.is_null() {
             let ns = std::ffi::CStr::from_ptr((*wlr_layer_surface).namespace).to_string_lossy();
             if ns == "cce-status" {
                 blur_enabled = (*server).wm.layout.status_background_blur > 0.001;
+                ignore_transparent = (*server).wm.layout.status_backdrop_blur_ignore_transparent;
             }
         }
         ffi::river_scene_node_enable_blur(
             (*(*layer_surface).scene_layer_surface).tree as *mut ffi::wlr_scene_node,
             blur_enabled,
             (*server).wm.layout.scenefx_optimized_blur,
+            ignore_transparent,
         );
     }
 
diff --git a/src/server/window.rs b/src/server/window.rs
index e4770f7..b5c99f3 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -1538,7 +1538,12 @@ impl Window {
                     blur_enabled = false;
                 }
             }
-            ffi::river_scene_node_enable_blur(self.surfaces.tree as *mut ffi::wlr_scene_node, blur_enabled, (*self.server).wm.layout.scenefx_optimized_blur);
+            let app_id = self.get_app_id_string().unwrap_or_default();
+            let mut ignore_transparent = (*self.server).wm.layout.window_backdrop_blur_ignore_transparent;
+            if app_id.starts_with("cce-status") {
+                ignore_transparent = (*self.server).wm.layout.status_backdrop_blur_ignore_transparent;
+            }
+            ffi::river_scene_node_enable_blur(self.surfaces.tree as *mut ffi::wlr_scene_node, blur_enabled, (*self.server).wm.layout.scenefx_optimized_blur, ignore_transparent);
             ffi::river_scene_node_set_opacity(self.tree as *mut ffi::wlr_scene_node, requested.opacity);
 
             let radius = if requested.circular {
@@ -2744,7 +2749,12 @@ impl Decoration {
         self.surfaces.drop_saved();
 
         let server = (*self.window).server;
-        ffi::river_scene_node_enable_blur(self.surfaces.tree as *mut ffi::wlr_scene_node, self.rendering_requested.blur, (*server).wm.layout.scenefx_optimized_blur);
+        let app_id = (*self.window).get_app_id_string().unwrap_or_default();
+        let mut ignore_transparent = (*server).wm.layout.window_backdrop_blur_ignore_transparent;
+        if app_id.starts_with("cce-status") {
+            ignore_transparent = (*server).wm.layout.status_backdrop_blur_ignore_transparent;
+        }
+        ffi::river_scene_node_enable_blur(self.surfaces.tree as *mut ffi::wlr_scene_node, self.rendering_requested.blur, (*server).wm.layout.scenefx_optimized_blur, ignore_transparent);
 
         let scale = (*self.window).scale;
         let scaled_x = (self.rendering_requested.offset_x as f64 * scale) as i32;
diff --git a/src/server/wlroots_log_wrapper.c b/src/server/wlroots_log_wrapper.c
index bb059c9..401aafa 100644
--- a/src/server/wlroots_log_wrapper.c
+++ b/src/server/wlroots_log_wrapper.c
@@ -655,6 +655,7 @@ void river_wlr_keyboard_init(struct wlr_keyboard *keyboard,
 struct river_blur_data {
 	bool enabled;
 	bool optimized;
+	bool ignore_transparent;
 };
 
 static void enable_blur_iterator(struct wlr_scene_buffer *buffer, int sx, int sy, void *user_data) {
@@ -667,17 +668,18 @@ static void enable_blur_iterator(struct wlr_scene_buffer *buffer, int sx, int sy
 	struct river_blur_data *data = (struct river_blur_data *)user_data;
 	bool enabled = data->enabled;
 	bool optimized = data->optimized && enabled;
+	bool ignore_transparent = data->ignore_transparent && enabled;
 	if (buffer->backdrop_blur != enabled ||
 		buffer->backdrop_blur_optimized != optimized ||
-		buffer->backdrop_blur_ignore_transparent != enabled) {
+		buffer->backdrop_blur_ignore_transparent != ignore_transparent) {
 		wlr_scene_buffer_set_backdrop_blur(buffer, enabled);
 		wlr_scene_buffer_set_backdrop_blur_optimized(buffer, optimized);
-		wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, enabled);
+		wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, ignore_transparent);
 	}
 }
 
-void river_scene_node_enable_blur(struct wlr_scene_node *node, bool enabled, bool optimized) {
-	struct river_blur_data data = { .enabled = enabled, .optimized = optimized };
+void river_scene_node_enable_blur(struct wlr_scene_node *node, bool enabled, bool optimized, bool ignore_transparent) {
+	struct river_blur_data data = { .enabled = enabled, .optimized = optimized, .ignore_transparent = ignore_transparent };
 	wlr_scene_node_for_each_buffer(node, enable_blur_iterator, &data);
 }
 
diff --git a/src/server/xdg_toplevel.rs b/src/server/xdg_toplevel.rs
index bf2a48b..709f578 100644
--- a/src/server/xdg_toplevel.rs
+++ b/src/server/xdg_toplevel.rs
@@ -408,10 +408,16 @@ unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut
     let window = (*toplevel).window;
     let base = ffi::river_wlr_xdg_toplevel_get_base((*toplevel).wlr_toplevel);
 
+    let app_id = (*window).get_app_id_string().unwrap_or_default();
+    let mut ignore_transparent = (*(*window).server).wm.layout.window_backdrop_blur_ignore_transparent;
+    if app_id.starts_with("cce-status") {
+        ignore_transparent = (*(*window).server).wm.layout.status_backdrop_blur_ignore_transparent;
+    }
     ffi::river_scene_node_enable_blur(
         (*window).surfaces.tree as *mut ffi::wlr_scene_node,
         (*window).rendering_requested.blur,
         (*(*window).server).wm.layout.scenefx_optimized_blur,
+        ignore_transparent,
     );
 
     let capture_node = &mut (*(*window).capture_scene).tree as *mut ffi::wlr_scene_tree as *mut ffi::wlr_scene_node;
diff --git a/wrapper.h b/wrapper.h
index 6027d53..51b4c01 100644
--- a/wrapper.h
+++ b/wrapper.h
@@ -257,7 +257,7 @@ struct wlr_seat_client *river_wlr_drag_get_seat_client(struct wlr_drag *drag);
 
 void river_wlr_keyboard_init(struct wlr_keyboard *keyboard, void (*led_update)(struct wlr_keyboard *keyboard, uint32_t leds), const char *name);
 
-void river_scene_node_enable_blur(struct wlr_scene_node *node, bool enabled, bool optimized);
+void river_scene_node_enable_blur(struct wlr_scene_node *node, bool enabled, bool optimized, bool ignore_transparent);
 
 void river_scene_node_set_opacity(struct wlr_scene_node *node, float opacity);