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

commit9d276941fb20a467f2d4a15a3fead0026d568a13
parentca03542f62
authorLucas Galante <[email protected]>
date2026-07-01 20:34
Add desktop.grid_fade_mode menu key and implement linear, smoothstep, quadratic, and cosine fade modes in quad_round shader

 scenefx/include/render/fx_renderer/shaders.h       |  1 +
 scenefx/render/fx_renderer/fx_pass.c               |  5 ++++-
 scenefx/render/fx_renderer/shaders.c               |  1 +
 scenefx/render/fx_renderer/shaders/quad_round.frag | 11 ++++++++++-
 src/server/config.rs                               | 16 ++++++++++++++++
 src/server/output.rs                               | 17 ++++++++++++++++-
 src/server/window_manager.rs                       |  3 +++
 7 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/scenefx/include/render/fx_renderer/shaders.h b/scenefx/include/render/fx_renderer/shaders.h
index bd05f70..98656fc 100644
--- a/scenefx/include/render/fx_renderer/shaders.h
+++ b/scenefx/include/render/fx_renderer/shaders.h
@@ -80,6 +80,7 @@ struct quad_round_shader {
 	GLint clip_position;
 	struct shader_corner_radii clip_radius;
 	GLint fade_inset;
+	GLint fade_mode;
 };
 
 bool link_quad_round_program(struct quad_round_shader *shader);
diff --git a/scenefx/render/fx_renderer/fx_pass.c b/scenefx/render/fx_renderer/fx_pass.c
index 578ba67..115b751 100644
--- a/scenefx/render/fx_renderer/fx_pass.c
+++ b/scenefx/render/fx_renderer/fx_pass.c
@@ -690,7 +690,10 @@ void fx_render_pass_add_rounded_rect(struct fx_gles_render_pass *pass,
 
 	struct fx_corner_fradii corners = fx_options->corners;
 	uniform_corner_radii_set(&shader.radius, &corners);
-	glUniform1f(shader.fade_inset, (float)fx_options->fade_inset / 1000.0f);
+	int fade_mode = fx_options->fade_inset % 10;
+	float fade_inset = (float)(fx_options->fade_inset / 10) / 100.0f;
+	glUniform1f(shader.fade_inset, fade_inset);
+	glUniform1i(shader.fade_mode, fade_mode);
 
 	render(&box, &clip_region, renderer->shaders.quad_round.pos_attrib);
 	pixman_region32_fini(&clip_region);
diff --git a/scenefx/render/fx_renderer/shaders.c b/scenefx/render/fx_renderer/shaders.c
index 7727376..7b8a0d1 100644
--- a/scenefx/render/fx_renderer/shaders.c
+++ b/scenefx/render/fx_renderer/shaders.c
@@ -199,6 +199,7 @@ bool link_quad_round_program(struct quad_round_shader *shader) {
 	shader->clip_radius.bottom_left = glGetUniformLocation(prog, "clip_radius_bottom_left");
 	shader->clip_radius.bottom_right = glGetUniformLocation(prog, "clip_radius_bottom_right");
 	shader->fade_inset = glGetUniformLocation(prog, "fade_inset");
+	shader->fade_mode = glGetUniformLocation(prog, "fade_mode");
 
 	return true;
 }
diff --git a/scenefx/render/fx_renderer/shaders/quad_round.frag b/scenefx/render/fx_renderer/shaders/quad_round.frag
index e5e7768..3b3a063 100644
--- a/scenefx/render/fx_renderer/shaders/quad_round.frag
+++ b/scenefx/render/fx_renderer/shaders/quad_round.frag
@@ -10,6 +10,7 @@ uniform float radius_top_right;
 uniform float radius_bottom_left;
 uniform float radius_bottom_right;
 uniform float fade_inset;
+uniform int fade_mode;
 
 uniform vec2 clip_size;
 uniform vec2 clip_position;
@@ -65,7 +66,15 @@ void main() {
 	vec4 final_color = v_color;
 	if (fade_inset > 0.0) {
 		float inside_dist = 0.5 - dist;
-		float fade_factor = clamp(inside_dist / fade_inset, 0.0, 1.0);
+		float factor = clamp(inside_dist / fade_inset, 0.0, 1.0);
+		float fade_factor = factor;
+		if (fade_mode == 1) {
+			fade_factor = smoothstep(0.0, 1.0, factor);
+		} else if (fade_mode == 2) {
+			fade_factor = factor * factor;
+		} else if (fade_mode == 3) {
+			fade_factor = 0.5 - 0.5 * cos(factor * 3.14159265);
+		}
 		final_color *= fade_factor;
 	}
 
diff --git a/src/server/config.rs b/src/server/config.rs
index c7e17df..c8615c4 100644
--- a/src/server/config.rs
+++ b/src/server/config.rs
@@ -47,6 +47,7 @@ pub struct Layout {
     pub desktop_gap_width: i32,
     pub desktop_cell_corner_radius: i32,
     pub desktop_cell_fade_inset: i64,
+    pub desktop_grid_fade_mode: String,
     pub desktop_enable_solid_color: bool,
     pub desktop_solid_color: [f32; 4],
     pub scenefx_optimized_blur: bool,
@@ -97,6 +98,7 @@ impl Default for Layout {
             desktop_gap_width: 1,
             desktop_cell_corner_radius: 0,
             desktop_cell_fade_inset: 0,
+            desktop_grid_fade_mode: "linear".to_string(),
             desktop_enable_solid_color: false,
             desktop_solid_color: [0.0, 0.0, 0.0, 1.0],
             scenefx_optimized_blur: true,
@@ -287,6 +289,8 @@ pub struct SurfaceConfig {
     pub desktop_cell_fade_inset: i64,
     #[serde(default = "default_desktop_mode")]
     pub desktop_mode: String,
+    #[serde(default = "default_desktop_grid_fade_mode")]
+    pub desktop_grid_fade_mode: String,
     #[serde(default = "default_desktop_solid_color")]
     pub desktop_solid_color: String,
     #[serde(default = "default_backplate_color")]
@@ -307,6 +311,7 @@ impl Default for SurfaceConfig {
             desktop_cell_corner_radius: default_desktop_cell_corner_radius(),
             desktop_cell_fade_inset: default_desktop_cell_fade_inset(),
             desktop_mode: default_desktop_mode(),
+            desktop_grid_fade_mode: default_desktop_grid_fade_mode(),
             desktop_solid_color: default_desktop_solid_color(),
             backplate_color: default_backplate_color(),
             backplate_blur: default_backplate_blur(),
@@ -343,6 +348,10 @@ fn default_desktop_mode() -> String {
     "solid".to_string()
 }
 
+fn default_desktop_grid_fade_mode() -> String {
+    "linear".to_string()
+}
+
 fn default_desktop_solid_color() -> String {
     "#000000".to_string()
 }
@@ -1251,6 +1260,11 @@ fn parse_kdl_config(content: &str) -> Result<Config, String> {
                                             surface.desktop_cell_fade_inset = val;
                                         }
                                     }
+                                    "grid_fade_mode" => {
+                                        if let Some(val) = entry.value().as_string() {
+                                            surface.desktop_grid_fade_mode = val.to_string();
+                                        }
+                                    }
                                     "mode" => {
                                         if let Some(val) = entry.value().as_string() {
                                             surface.desktop_mode = val.to_string();
@@ -1321,6 +1335,7 @@ fn parse_kdl_config(content: &str) -> Result<Config, String> {
             surface.desktop_gap_width = get_child_arg_i64(node, "desktop_gap_width", default_desktop_gap_width());
             surface.desktop_cell_corner_radius = get_child_arg_i64(node, "desktop_cell_corner_radius", default_desktop_cell_corner_radius());
             surface.desktop_cell_fade_inset = get_child_arg_i64(node, "desktop_cell_fade_inset", default_desktop_cell_fade_inset());
+            surface.desktop_grid_fade_mode = get_child_arg_string(node, "grid_fade_mode", &default_desktop_grid_fade_mode());
             surface.desktop_mode = get_child_arg_string(node, "desktop_mode", &default_desktop_mode());
             surface.desktop_solid_color = get_child_arg_string(node, "desktop_solid_color", &default_desktop_solid_color());
             surface.backplate_color = get_child_arg_string(node, "backplate_color", &default_backplate_color());
@@ -1396,6 +1411,7 @@ pub fn parse_config(path: &str, state: &mut crate::window_manager::WindowManager
     state.layout.desktop_gap_width = config.surface.desktop_gap_width as i32;
     state.layout.desktop_cell_corner_radius = config.surface.desktop_cell_corner_radius as i32;
     state.layout.desktop_cell_fade_inset = config.surface.desktop_cell_fade_inset;
+    state.layout.desktop_grid_fade_mode = config.surface.desktop_grid_fade_mode.clone();
     state.layout.desktop_enable_solid_color = enable_solid;
     state.layout.desktop_solid_color = parse_hex_color_rgba(&config.surface.desktop_solid_color);
 
diff --git a/src/server/output.rs b/src/server/output.rs
index fac7d7a..d850522 100644
--- a/src/server/output.rs
+++ b/src/server/output.rs
@@ -165,6 +165,7 @@ pub struct Output {
     pub last_grid_cell_color: [f32; 4],
     pub last_grid_cell_corner_radius: i32,
     pub last_grid_cell_fade_inset: i64,
+    pub last_grid_fade_mode: String,
     pub last_grid_gap_color: String,
     pub last_grid_gap_color_rgba: [f32; 4],
     pub grid_is_low_res: bool,
@@ -419,6 +420,7 @@ impl Output {
             last_grid_cell_color: [0.0, 0.0, 0.0, 0.0],
             last_grid_cell_corner_radius: 0,
             last_grid_cell_fade_inset: 0,
+            last_grid_fade_mode: String::new(),
             last_grid_gap_color: String::new(),
             last_grid_gap_color_rgba: [0.0, 0.0, 0.0, 0.0],
             grid_is_low_res: false,
@@ -607,6 +609,7 @@ impl Output {
         cell_color[3] *= density_fade as f32;
         let cell_corner_radius = wm.layout.desktop_cell_corner_radius;
         let cell_fade_inset = wm.layout.desktop_cell_fade_inset;
+        let fade_mode_str = &wm.layout.desktop_grid_fade_mode;
         let gap_color = &wm.layout.desktop_gap_color;
 
         // Detect layout or viewport changes to request a redraw.
@@ -618,6 +621,7 @@ impl Output {
             || self.last_grid_cell_color != cell_color
             || self.last_grid_cell_corner_radius != cell_corner_radius
             || self.last_grid_cell_fade_inset != cell_fade_inset
+            || &self.last_grid_fade_mode != fade_mode_str
             || &self.last_grid_gap_color != gap_color;
 
         if structure_changed {
@@ -653,6 +657,7 @@ impl Output {
             self.last_grid_cell_color = cell_color;
             self.last_grid_cell_corner_radius = cell_corner_radius;
             self.last_grid_cell_fade_inset = cell_fade_inset;
+            self.last_grid_fade_mode = fade_mode_str.clone();
             self.last_grid_gap_color = gap_color.clone();
             self.last_grid_gap_color_rgba = crate::config::parse_hex_color_rgba(&self.last_grid_gap_color);
             self.grid_is_low_res = self.last_grid_zoom != zoom;
@@ -700,7 +705,17 @@ impl Output {
                 let rw = (cell_size * zoom).round() as i32;
                 let rh = (cell_size * zoom).round() as i32;
                 let scaled_corner_radius = (cell_corner_radius as f64 * zoom).round() as i32;
-                let inset_scaled = (cell_fade_inset as f64 * zoom * 1000.0).round() as i32;
+                let fade_mode = match fade_mode_str.as_str() {
+                    "smoothstep" => 1,
+                    "quadratic" => 2,
+                    "cosine" => 3,
+                    _ => 0, // "linear"
+                };
+                let inset_scaled = if cell_fade_inset > 0 {
+                    ((cell_fade_inset as f64 * zoom * 100.0).round() as i32) * 10 + fade_mode
+                } else {
+                    0
+                };
 
                 if rw > 0 && rh > 0 {
                     for col in 0..=cols {
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 5fbc251..a1ab414 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -2663,6 +2663,9 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
                             self.layout.desktop_cell_fade_inset = v;
                         }
                     }
+                    "desktop_grid_fade_mode" => {
+                        self.layout.desktop_grid_fade_mode = val.to_string();
+                    }
                     "desktop_enable_solid_color" => {
                         if let Ok(v) = val.parse::<bool>() {
                             self.layout.desktop_enable_solid_color = v;