oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git

Added variables to config parser.

Commit
5a7bcf5de6389df296a552bd754dedfc541de361
Parent
c46b018
Author
tonybtw <tonybtw@tonybtw.com>
Date
2025-10-29 05:22:08

Diff

diff --git a/src/config/mod.rs b/src/config/mod.rs
index 2fc4649..4e30d22 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -4,6 +4,7 @@ use crate::keyboard::handlers::Key;
 use crate::keyboard::keycodes;
 use crate::keyboard::{Arg, KeyAction};
 use serde::Deserialize;
+use std::collections::HashMap;
 use x11rb::protocol::xproto::{KeyButMask, Keycode};
 
 #[derive(Debug, Deserialize)]
@@ -156,8 +157,57 @@ impl KeyData {
     }
 }
 
+fn preprocess_variables(input: &str) -> Result<String, ConfigError> {
+    let mut variables: HashMap<String, String> = HashMap::new();
+    let mut result = String::new();
+
+    for line in input.lines() {
+        let trimmed = line.trim();
+
+        if trimmed.starts_with("#DEFINE") {
+            let rest = trimmed.strip_prefix("#DEFINE").unwrap().trim();
+
+            if let Some(eq_pos) = rest.find('=') {
+                let var_name = rest[..eq_pos].trim();
+                let value = rest[eq_pos + 1..].trim().trim_end_matches(',');
+
+                if !var_name.starts_with('$') {
+                    return Err(ConfigError::InvalidVariableName(var_name.to_string()));
+                }
+
+                variables.insert(var_name.to_string(), value.to_string());
+            } else {
+                return Err(ConfigError::InvalidDefine(trimmed.to_string()));
+            }
+
+            result.push('\n');
+        } else {
+            let mut processed_line = line.to_string();
+            for (var_name, value) in &variables {
+                processed_line = processed_line.replace(var_name, value);
+            }
+            result.push_str(&processed_line);
+            result.push('\n');
+        }
+    }
+
+    for line in result.lines() {
+        if let Some(var_start) = line.find('$') {
+            let rest = &line[var_start..];
+            let var_end = rest[1..]
+                .find(|c: char| !c.is_alphanumeric() && c != '_')
+                .unwrap_or(rest.len() - 1)
+                + 1;
+            let undefined_var = &rest[..var_end];
+            return Err(ConfigError::UndefinedVariable(undefined_var.to_string()));
+        }
+    }
+    Ok(result)
+}
+
 pub fn parse_config(input: &str) -> Result<crate::Config, ConfigError> {
-    let config_data: ConfigData = ron::from_str(input)?;
+    let preprocessed = preprocess_variables(input)?;
+    let config_data: ConfigData = ron::from_str(&preprocessed)?;
     config_data_to_config(config_data)
 }
 
diff --git a/src/errors.rs b/src/errors.rs
index f698f07..0a43192 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -25,6 +25,9 @@ pub enum ConfigError {
     UnknownAction(String),
     UnknownBlockCommand(String),
     MissingCommandArg { command: String, field: String },
+    InvalidVariableName(String),
+    InvalidDefine(String),
+    UndefinedVariable(String),
 }
 
 impl std::fmt::Display for WmError {
@@ -67,6 +70,15 @@ impl std::fmt::Display for ConfigError {
             Self::MissingCommandArg { command, field } => {
                 write!(f, "{} command requires {}", command, field)
             }
+            Self::InvalidVariableName(name) => {
+                write!(f, "Invalid variable name '{}': must start with $", name)
+            }
+            Self::InvalidDefine(line) => {
+                write!(f, "Invalid #DEFINE syntax: '{}'. Expected: #DEFINE $var_name = value", line)
+            }
+            Self::UndefinedVariable(var) => {
+                write!(f, "Undefined variable '{}': define it with #DEFINE before use", var)
+            }
         }
     }
 }
diff --git a/templates/config.ron b/templates/config.ron
index 69af9f1..80b676e 100644
--- a/templates/config.ron
+++ b/templates/config.ron
@@ -2,19 +2,31 @@
 // OXWM Configuration File
 // Edit this file and reload with Mod+Shift+R (no compilation needed!)
 
+#DEFINE $terminal = "st"
+#DEFINE $color_blue = 0x6dade3
+#DEFINE $color_grey = 0xbbbbbb
+#DEFINE $color_green = 0x9ece6a
+#DEFINE $color_red = 0xf7768e
+#DEFINE $color_cyan = 0x0db9d7
+#DEFINE $color_purple = 0xad8ee6
+#DEFINE $color_lavender = 0xa9b1d6
+#DEFINE $color_bg = 0x1a1b26
+#DEFINE $color_fg = 0xbbbbbb
+#DEFINE $color_light_blue = 0x7aa2f7
+
 (
     border_width: 2,
-    border_focused: 0x6dade3,
-    border_unfocused: 0xbbbbbb,
+    border_focused: $color_blue,
+    border_unfocused: $color_grey,
     font: "monospace:style=Bold:size=10",
-    
+
     gaps_enabled: true,
     gap_inner_horizontal: 5,
     gap_inner_vertical: 5,
     gap_outer_horizontal: 5,
     gap_outer_vertical: 5,
-    
-    terminal: "st",
+
+    terminal: $terminal,
     modkey: Mod4,
 
     tags: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
@@ -24,7 +36,7 @@
     ],
 
     keybindings: [
-        (modifiers: [Mod4], key: Return, action: Spawn, arg: "st"),
+        (modifiers: [Mod4], key: Return, action: Spawn, arg: $terminal),
         (modifiers: [Mod4], key: D, action: Spawn, arg: ["sh", "-c", "dmenu_run -l 10"]),
         (modifiers: [Mod4], key: S, action: Spawn, arg: ["sh", "-c", "maim -s | xclip -selection clipboard -t image/png"]),
         (modifiers: [Mod4], key: Q, action: KillClient),
@@ -73,15 +85,15 @@
     ],
     
     status_blocks: [
-        (format: "Ram: {used}/{total} GB", command: "Ram", interval_secs: 5, color: 0x7aa2f7, underline: true),
-        (format: " │  ", command: "Static", interval_secs: 18446744073709551615, color: 0xa9b1d6, underline: false),
-        (format: "Kernel: {}", command: "Shell", command_arg: "uname -r", interval_secs: 18446744073709551615, color: 0xf7768e, underline: true),
-        (format: " │  ", command: "Static", interval_secs: 18446744073709551615, color: 0xa9b1d6, underline: false),
-        (format: "{}", command: "DateTime", command_arg: "%a, %b %d - %-I:%M %P", interval_secs: 1, color: 0x0db9d7, underline: true),
+        (format: "Ram: {used}/{total} GB", command: "Ram", interval_secs: 5, color: $color_light_blue, underline: true),
+        (format: " │  ", command: "Static", interval_secs: 18446744073709551615, color: $color_lavender, underline: false),
+        (format: "Kernel: {}", command: "Shell", command_arg: "uname -r", interval_secs: 18446744073709551615, color: $color_red, underline: true),
+        (format: " │  ", command: "Static", interval_secs: 18446744073709551615, color: $color_lavender, underline: false),
+        (format: "{}", command: "DateTime", command_arg: "%a, %b %d - %-I:%M %P", interval_secs: 1, color: $color_cyan, underline: true),
     ],
-    
-    scheme_normal: (foreground: 0xbbbbbb, background: 0x1a1b26, underline: 0x444444),
-    scheme_occupied: (foreground: 0x0db9d7, background: 0x1a1b26, underline: 0x0db9d7),
-    scheme_selected: (foreground: 0x0db9d7, background: 0x1a1b26, underline: 0xad8ee6),
+
+    scheme_normal: (foreground: $color_fg, background: $color_bg, underline: 0x444444),
+    scheme_occupied: (foreground: $color_cyan, background: $color_bg, underline: $color_cyan),
+    scheme_selected: (foreground: $color_cyan, background: $color_bg, underline: $color_purple),
 )
 
diff --git a/test-config.ron b/test-config.ron
index ac3303a..1553106 100644
--- a/test-config.ron
+++ b/test-config.ron
@@ -2,19 +2,31 @@
 // OXWM Configuration File
 // Edit this file and reload with Mod+Shift+R (no compilation needed!)
 
+#DEFINE $terminal = "alacritty"
+#DEFINE $color_blue = 0x6dade3
+#DEFINE $color_grey = 0xbbbbbb
+#DEFINE $color_green = 0x9ece6a
+#DEFINE $color_red = 0xf7768e
+#DEFINE $color_cyan = 0x0db9d7
+#DEFINE $color_purple = 0xad8ee6
+#DEFINE $color_lavender = 0xa9b1d6
+#DEFINE $color_bg = 0x1a1b26
+#DEFINE $color_fg = 0xbbbbbb
+#DEFINE $color_light_blue = 0x7aa2f7
+
 (
     border_width: 2,
-    border_focused: 0x6dade3,
-    border_unfocused: 0xbbbbbb,
+    border_focused: $color_blue,
+    border_unfocused: $color_grey,
     font: "JetBrainsMono Nerd Font:style=Bold:size=12",
-    
+
     gaps_enabled: true,
     gap_inner_horizontal: 5,
     gap_inner_vertical: 5,
     gap_outer_horizontal: 5,
     gap_outer_vertical: 5,
-    
-    terminal: "st",
+
+    terminal: $terminal,
     modkey: Mod1,
     tags: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
 
@@ -30,7 +42,7 @@
     ],
 
     keybindings: [
-        (modifiers: [Mod1], key: Return, action: Spawn, arg: "st"),
+        (modifiers: [Mod1], key: Return, action: Spawn, arg: $terminal),
         (modifiers: [Mod1], key: D, action: Spawn, arg: ["sh", "-c", "dmenu_run -l 10"]),
         (modifiers: [Mod1], key: S, action: Spawn, arg: ["sh", "-c", "maim -s | xclip -selection clipboard -t image/png"]),
         (modifiers: [Mod1], key: Q, action: KillClient),
@@ -84,7 +96,7 @@
                 full: "󰁹 Bat: {}%",
             ),
             interval_secs: 30,
-            color: 0x9ece6a,
+            color: $color_green,
             underline: true,
         ),
         
@@ -93,7 +105,7 @@
             format: " │  ",
             command: "Static",
             interval_secs: 18446744073709551615,
-            color: 0xa9b1d6,
+            color: $color_lavender,
             underline: false,
         ),
         
@@ -102,7 +114,7 @@
             format: "󰍛 {used}/{total} GB",
             command: "Ram",
             interval_secs: 5,
-            color: 0x7aa2f7,
+            color: $color_light_blue,
             underline: true,
         ),
         
@@ -111,7 +123,7 @@
             format: " │  ",
             command: "Static",
             interval_secs: 18446744073709551615,
-            color: 0xa9b1d6,
+            color: $color_lavender,
             underline: false,
         ),
         
@@ -121,7 +133,7 @@
             command: "Shell",
             command_arg: "uname -r",
             interval_secs: 18446744073709551615,
-            color: 0xf7768e,
+            color: $color_red,
             underline: true,
         ),
         
@@ -130,7 +142,7 @@
             format: " │  ",
             command: "Static",
             interval_secs: 18446744073709551615,
-            color: 0xa9b1d6,
+            color: $color_lavender,
             underline: false,
         ),
         
@@ -140,13 +152,13 @@
             command: "DateTime",
             command_arg: "%a, %b %d - %-I:%M %P",
             interval_secs: 1,
-            color: 0x0db9d7,
+            color: $color_cyan,
             underline: true,
         ),
     ],
     
-    scheme_normal: (foreground: 0xbbbbbb, background: 0x1a1b26, underline: 0x444444),
-    scheme_occupied: (foreground: 0x0db9d7, background: 0x1a1b26, underline: 0x0db9d7),
-    scheme_selected: (foreground: 0x0db9d7, background: 0x1a1b26, underline: 0xad8ee6),
+    scheme_normal: (foreground: $color_fg, background: $color_bg, underline: 0x444444),
+    scheme_occupied: (foreground: $color_cyan, background: $color_bg, underline: $color_cyan),
+    scheme_selected: (foreground: $color_cyan, background: $color_bg, underline: $color_purple),
 )