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

commit22ae4ce421c6c06b677d2cbe472b41d4bb6d3f7d
parent4ceea93bbd
authorLucas Galante <[email protected]>
date2026-06-25 10:05
feat(server): add desktop grid configuration controls (color, scale, line width)

 src/server/config.rs         | 65 ++++++++++++++++++++++++++++++++++++++++++++
 src/server/output.rs         | 10 +++----
 src/server/window_manager.rs | 13 +++++++++
 3 files changed, 83 insertions(+), 5 deletions(-)

diff --git a/src/server/config.rs b/src/server/config.rs
index cfba380..8e8da2a 100644
--- a/src/server/config.rs
+++ b/src/server/config.rs
@@ -41,6 +41,9 @@ pub struct Layout {
     pub desktop_background: String,
     pub transparency_opacity: f32,
     pub window_opacity: bool,
+    pub desktop_grid_color: [f32; 4],
+    pub desktop_grid_scale: f64,
+    pub desktop_line_width: i32,
 }
 
 impl Default for Layout {
@@ -80,6 +83,9 @@ impl Default for Layout {
             desktop_background: "#000000".to_string(),
             transparency_opacity: 0.9,
             window_opacity: true,
+            desktop_grid_color: [1.0, 1.0, 1.0, 0.05],
+            desktop_grid_scale: 100.0,
+            desktop_line_width: 1,
         }
     }
 }
@@ -230,12 +236,21 @@ pub struct TransparencyConfig {
 pub struct SurfacesConfig {
     #[serde(default = "default_desktop_background")]
     pub desktop_background: String,
+    #[serde(default = "default_desktop_grid_color")]
+    pub desktop_grid_color: String,
+    #[serde(default = "default_desktop_grid_scale")]
+    pub desktop_grid_scale: i64,
+    #[serde(default = "default_desktop_line_width")]
+    pub desktop_line_width: i64,
 }
 
 impl Default for SurfacesConfig {
     fn default() -> Self {
         Self {
             desktop_background: default_desktop_background(),
+            desktop_grid_color: default_desktop_grid_color(),
+            desktop_grid_scale: default_desktop_grid_scale(),
+            desktop_line_width: default_desktop_line_width(),
         }
     }
 }
@@ -244,6 +259,18 @@ fn default_desktop_background() -> String {
     "#000000".to_string()
 }
 
+fn default_desktop_grid_color() -> String {
+    "#ffffff0d".to_string()
+}
+
+fn default_desktop_grid_scale() -> i64 {
+    100
+}
+
+fn default_desktop_line_width() -> i64 {
+    1
+}
+
 
 #[derive(Debug, Deserialize)]
 pub struct Config {
@@ -421,6 +448,40 @@ pub fn parse_hex_color(hex_str: &str) -> u32 {
     }
 }
 
+pub fn parse_hex_color_rgba(hex_str: &str) -> [f32; 4] {
+    let hex = hex_str.trim_matches(|c| c == '"' || c == '\'' || c == ' ');
+    let hex = hex.trim_start_matches('#');
+    if hex.len() == 8 {
+        if let (Ok(r), Ok(g), Ok(b), Ok(a)) = (
+            u8::from_str_radix(&hex[0..2], 16),
+            u8::from_str_radix(&hex[2..4], 16),
+            u8::from_str_radix(&hex[4..6], 16),
+            u8::from_str_radix(&hex[6..8], 16),
+        ) {
+            return [
+                r as f32 / 255.0,
+                g as f32 / 255.0,
+                b as f32 / 255.0,
+                a as f32 / 255.0,
+            ];
+        }
+    } else if hex.len() == 6 {
+        if let (Ok(r), Ok(g), Ok(b)) = (
+            u8::from_str_radix(&hex[0..2], 16),
+            u8::from_str_radix(&hex[2..4], 16),
+            u8::from_str_radix(&hex[4..6], 16),
+        ) {
+            return [
+                r as f32 / 255.0,
+                g as f32 / 255.0,
+                b as f32 / 255.0,
+                1.0,
+            ];
+        }
+    }
+    [1.0, 1.0, 1.0, 0.05] // fallback default (5% white)
+}
+
 pub fn parse_tiling_mode(s: &str) -> TilingMode {
     match s.to_lowercase().as_str() {
         "floating" => TilingMode::Floating,
@@ -685,6 +746,10 @@ pub fn parse_config(path: &str, state: &mut crate::window_manager::WindowManager
     state.layout.background_b = (background_color_val & 0xFF) * 0x01010101;
     state.layout.background_a = 0xFFFFFFFF;
 
+    state.layout.desktop_grid_color = parse_hex_color_rgba(&config.surfaces.desktop_grid_color);
+    state.layout.desktop_grid_scale = config.surfaces.desktop_grid_scale as f64;
+    state.layout.desktop_line_width = config.surfaces.desktop_line_width as i32;
+
     state.layout.border_font_size = config.layout.border_font_size as i32;
     state.layout.transition_duration = config.layout.transition_duration as i32;
     state.layout.grid_gap = config.layout.grid_gap as i32;
diff --git a/src/server/output.rs b/src/server/output.rs
index 986c586..1ca10e0 100644
--- a/src/server/output.rs
+++ b/src/server/output.rs
@@ -521,7 +521,7 @@ impl Output {
         let zoom = wm.desk_zoom;
 
         // Dynamic spacing based on zoom to avoid rendering too many lines (LOD)
-        let mut grid_spacing = 100.0;
+        let mut grid_spacing = wm.layout.desktop_grid_scale;
         while grid_spacing * zoom < 40.0 {
             grid_spacing *= 2.0;
         }
@@ -531,8 +531,8 @@ impl Output {
         let min_y = wm.desk_pan_y;
         let max_y = wm.desk_pan_y + (viewport_h as f64) / zoom;
 
-        // Subtle semi-transparent grid color (e.g. 5% white)
-        let grid_color: [f32; 4] = [1.0, 1.0, 1.0, 0.05];
+        // Subtle semi-transparent grid color from layout config
+        let grid_color: [f32; 4] = wm.layout.desktop_grid_color;
 
         // Draw vertical lines
         let mut x_val = (min_x / grid_spacing).ceil() * grid_spacing;
@@ -540,7 +540,7 @@ impl Output {
             let rel_x = ((x_val - wm.desk_pan_x) * zoom) as i32;
             let line_rect = ffi::wlr_scene_rect_create(
                 self.grid_tree,
-                1, // width of line
+                wm.layout.desktop_line_width, // width of line
                 viewport_h,
                 grid_color.as_ptr(),
             );
@@ -561,7 +561,7 @@ impl Output {
             let line_rect = ffi::wlr_scene_rect_create(
                 self.grid_tree,
                 viewport_w,
-                1, // height of line
+                wm.layout.desktop_line_width, // height of line
                 grid_color.as_ptr(),
             );
             if !line_rect.is_null() {
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index c889ee4..741a36c 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -2026,6 +2026,19 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
                             }
                         }
                     }
+                    "desktop_grid_color" => {
+                        self.layout.desktop_grid_color = crate::config::parse_hex_color_rgba(val);
+                    }
+                    "desktop_grid_scale" => {
+                        if let Ok(v) = val.parse::<f64>() {
+                            self.layout.desktop_grid_scale = v;
+                        }
+                    }
+                    "desktop_line_width" => {
+                        if let Ok(v) = val.parse::<i32>() {
+                            self.layout.desktop_line_width = v;
+                        }
+                    }
                     "gap" => { if let Ok(v) = val.parse::<i32>() { self.layout.gap = v; } }
                     "gap_top" => { if let Ok(v) = val.parse::<i32>() { self.layout.gap_top = v; } }
                     "gap_left" => { if let Ok(v) = val.parse::<i32>() { self.layout.gap_left = v; } }