Diff
diff --git a/Cargo.lock b/Cargo.lock
index d90c691..e029d9b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -297,7 +297,7 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "oxwm"
-version = "0.6.0"
+version = "0.7.0"
dependencies = [
"anyhow",
"chrono",
diff --git a/Cargo.toml b/Cargo.toml
index bce4eb0..d1dbed4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "oxwm"
-version = "0.6.0"
+version = "0.7.0"
edition = "2024"
[lib]
diff --git a/resources/test-config.lua b/resources/test-config.lua
index 4ea370a..f2c3a15 100644
--- a/resources/test-config.lua
+++ b/resources/test-config.lua
@@ -6,7 +6,9 @@
---Load type definitions for LSP
---@module 'oxwm'
--- Color palette
+
+-- Set variables as needed
+-- Colors
local colors = {
lavender = 0xa9b1d6,
light_blue = 0x7aa2f7,
@@ -20,9 +22,11 @@ local colors = {
blue = 0x6dade3,
}
+local modkey = "Mod1";
+
-- Basic settings
oxwm.set_terminal("st")
-oxwm.set_modkey("Mod1")
+oxwm.set_modkey(modkey)
oxwm.set_tags({ "1", "2", "3", "4", "5", "6", "7", "8", "9" })
-- Layout symbol overrides
@@ -51,68 +55,68 @@ oxwm.bar.set_scheme_selected(colors.cyan, colors.bg, colors.purple)
-- Keychord: Mod1+Space then T to spawn terminal
oxwm.key.chord({
- { { "Mod1" }, "Space" },
+ { { modkey }, "Space" },
{ {}, "T" }
}, oxwm.spawn("st"))
-- Basic window management
-oxwm.key.bind({ "Mod1" }, "Return", oxwm.spawn("st"))
-oxwm.key.bind({ "Mod1" }, "D", oxwm.spawn({ "sh", "-c", "dmenu_run -l 10" }))
-oxwm.key.bind({ "Mod1" }, "S", oxwm.spawn({ "sh", "-c", "maim -s | xclip -selection clipboard -t image/png" }))
-oxwm.key.bind({ "Mod1" }, "Q", oxwm.client.kill())
+oxwm.key.bind({ modkey }, "Return", oxwm.spawn("st"))
+oxwm.key.bind({ modkey }, "D", oxwm.spawn({ "sh", "-c", "dmenu_run -l 10" }))
+oxwm.key.bind({ modkey }, "S", oxwm.spawn({ "sh", "-c", "maim -s | xclip -selection clipboard -t image/png" }))
+oxwm.key.bind({ modkey }, "Q", oxwm.client.kill())
-- Keybind overlay
-oxwm.key.bind({ "Mod1", "Shift" }, "Slash", oxwm.show_keybinds())
+oxwm.key.bind({ modkey, "Shift" }, "Slash", oxwm.show_keybinds())
-- Client actions
-oxwm.key.bind({ "Mod1", "Shift" }, "F", oxwm.client.toggle_fullscreen())
-oxwm.key.bind({ "Mod1", "Shift" }, "Space", oxwm.client.toggle_floating())
+oxwm.key.bind({ modkey, "Shift" }, "F", oxwm.client.toggle_fullscreen())
+oxwm.key.bind({ modkey, "Shift" }, "Space", oxwm.client.toggle_floating())
-- Layout management
-oxwm.key.bind({ "Mod1" }, "F", oxwm.layout.set("normie"))
-oxwm.key.bind({ "Mod1" }, "C", oxwm.layout.set("tiling"))
-oxwm.key.bind({ "Mod1" }, "N", oxwm.layout.cycle())
+oxwm.key.bind({ modkey }, "F", oxwm.layout.set("normie"))
+oxwm.key.bind({ modkey }, "C", oxwm.layout.set("tiling"))
+oxwm.key.bind({ modkey }, "N", oxwm.layout.cycle())
-- Gaps toggle
-oxwm.key.bind({ "Mod1" }, "A", oxwm.toggle_gaps())
+oxwm.key.bind({ modkey }, "A", oxwm.toggle_gaps())
-- WM controls
-oxwm.key.bind({ "Mod1", "Shift" }, "Q", oxwm.quit())
-oxwm.key.bind({ "Mod1", "Shift" }, "R", oxwm.restart())
+oxwm.key.bind({ modkey, "Shift" }, "Q", oxwm.quit())
+oxwm.key.bind({ modkey, "Shift" }, "R", oxwm.restart())
-- Focus direction (vim keys)
-oxwm.key.bind({ "Mod1" }, "H", oxwm.client.focus_direction("left"))
-oxwm.key.bind({ "Mod1" }, "J", oxwm.client.focus_direction("down"))
-oxwm.key.bind({ "Mod1" }, "K", oxwm.client.focus_direction("up"))
-oxwm.key.bind({ "Mod1" }, "L", oxwm.client.focus_direction("right"))
+oxwm.key.bind({ modkey }, "H", oxwm.client.focus_direction("left"))
+oxwm.key.bind({ modkey }, "J", oxwm.client.focus_direction("down"))
+oxwm.key.bind({ modkey }, "K", oxwm.client.focus_direction("up"))
+oxwm.key.bind({ modkey }, "L", oxwm.client.focus_direction("right"))
-- Swap windows in direction
-oxwm.key.bind({ "Mod1", "Shift" }, "H", oxwm.client.swap_direction("left"))
-oxwm.key.bind({ "Mod1", "Shift" }, "J", oxwm.client.swap_direction("down"))
-oxwm.key.bind({ "Mod1", "Shift" }, "K", oxwm.client.swap_direction("up"))
-oxwm.key.bind({ "Mod1", "Shift" }, "L", oxwm.client.swap_direction("right"))
+oxwm.key.bind({ modkey, "Shift" }, "H", oxwm.client.swap_direction("left"))
+oxwm.key.bind({ modkey, "Shift" }, "J", oxwm.client.swap_direction("down"))
+oxwm.key.bind({ modkey, "Shift" }, "K", oxwm.client.swap_direction("up"))
+oxwm.key.bind({ modkey, "Shift" }, "L", oxwm.client.swap_direction("right"))
-- Tag viewing
-oxwm.key.bind({ "Mod1" }, "1", oxwm.tag.view(0))
-oxwm.key.bind({ "Mod1" }, "2", oxwm.tag.view(1))
-oxwm.key.bind({ "Mod1" }, "3", oxwm.tag.view(2))
-oxwm.key.bind({ "Mod1" }, "4", oxwm.tag.view(3))
-oxwm.key.bind({ "Mod1" }, "5", oxwm.tag.view(4))
-oxwm.key.bind({ "Mod1" }, "6", oxwm.tag.view(5))
-oxwm.key.bind({ "Mod1" }, "7", oxwm.tag.view(6))
-oxwm.key.bind({ "Mod1" }, "8", oxwm.tag.view(7))
-oxwm.key.bind({ "Mod1" }, "9", oxwm.tag.view(8))
+oxwm.key.bind({ modkey }, "1", oxwm.tag.view(0))
+oxwm.key.bind({ modkey }, "2", oxwm.tag.view(1))
+oxwm.key.bind({ modkey }, "3", oxwm.tag.view(2))
+oxwm.key.bind({ modkey }, "4", oxwm.tag.view(3))
+oxwm.key.bind({ modkey }, "5", oxwm.tag.view(4))
+oxwm.key.bind({ modkey }, "6", oxwm.tag.view(5))
+oxwm.key.bind({ modkey }, "7", oxwm.tag.view(6))
+oxwm.key.bind({ modkey }, "8", oxwm.tag.view(7))
+oxwm.key.bind({ modkey }, "9", oxwm.tag.view(8))
-- Move window to tag
-oxwm.key.bind({ "Mod1", "Shift" }, "1", oxwm.tag.move_to(0))
-oxwm.key.bind({ "Mod1", "Shift" }, "2", oxwm.tag.move_to(1))
-oxwm.key.bind({ "Mod1", "Shift" }, "3", oxwm.tag.move_to(2))
-oxwm.key.bind({ "Mod1", "Shift" }, "4", oxwm.tag.move_to(3))
-oxwm.key.bind({ "Mod1", "Shift" }, "5", oxwm.tag.move_to(4))
-oxwm.key.bind({ "Mod1", "Shift" }, "6", oxwm.tag.move_to(5))
-oxwm.key.bind({ "Mod1", "Shift" }, "7", oxwm.tag.move_to(6))
-oxwm.key.bind({ "Mod1", "Shift" }, "8", oxwm.tag.move_to(7))
-oxwm.key.bind({ "Mod1", "Shift" }, "9", oxwm.tag.move_to(8))
+oxwm.key.bind({ modkey, "Shift" }, "1", oxwm.tag.move_to(0))
+oxwm.key.bind({ modkey, "Shift" }, "2", oxwm.tag.move_to(1))
+oxwm.key.bind({ modkey, "Shift" }, "3", oxwm.tag.move_to(2))
+oxwm.key.bind({ modkey, "Shift" }, "4", oxwm.tag.move_to(3))
+oxwm.key.bind({ modkey, "Shift" }, "5", oxwm.tag.move_to(4))
+oxwm.key.bind({ modkey, "Shift" }, "6", oxwm.tag.move_to(5))
+oxwm.key.bind({ modkey, "Shift" }, "7", oxwm.tag.move_to(6))
+oxwm.key.bind({ modkey, "Shift" }, "8", oxwm.tag.move_to(7))
+oxwm.key.bind({ modkey, "Shift" }, "9", oxwm.tag.move_to(8))
-- Status bar blocks
oxwm.bar.add_block("", "Battery", {
diff --git a/src/bar/bar.rs b/src/bar/bar.rs
index 652d5df..756b998 100644
--- a/src/bar/bar.rs
+++ b/src/bar/bar.rs
@@ -347,4 +347,28 @@ impl Bar {
pub fn needs_redraw(&self) -> bool {
self.needs_redraw
}
+
+ pub fn update_from_config(&mut self, config: &Config) {
+ self.blocks = config
+ .status_blocks
+ .iter()
+ .map(|block_config| block_config.to_block())
+ .collect();
+
+ self.block_underlines = config
+ .status_blocks
+ .iter()
+ .map(|block_config| block_config.underline)
+ .collect();
+
+ self.block_last_updates = vec![Instant::now(); self.blocks.len()];
+
+ self.tags = config.tags.clone();
+ self.scheme_normal = config.scheme_normal;
+ self.scheme_occupied = config.scheme_occupied;
+ self.scheme_selected = config.scheme_selected;
+
+ self.status_text.clear();
+ self.needs_redraw = true;
+ }
}
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 519965b..484fa22 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -54,7 +54,6 @@ fn load_config(custom_path: Option<PathBuf>) -> Result<oxwm::Config> {
let lua_path = config_dir.join("config.lua");
if !lua_path.exists() {
- // Check if user had an old RON config
let ron_path = config_dir.join("config.ron");
let had_ron_config = ron_path.exists();
@@ -63,7 +62,7 @@ fn load_config(custom_path: Option<PathBuf>) -> Result<oxwm::Config> {
init_config()?;
if had_ron_config {
- println!("\n⚠️ NOTICE: OXWM has migrated to Lua configuration.");
+ println!("\n NOTICE: OXWM has migrated to Lua configuration.");
println!(" Your old config.ron has been preserved, but is no longer used.");
println!(" Your settings have been reset to defaults.");
println!(" Please manually port your configuration to the new Lua format.");
@@ -86,12 +85,10 @@ fn init_config() -> Result<()> {
let config_dir = get_config_path();
std::fs::create_dir_all(&config_dir)?;
- // Copy config.lua template
let config_template = include_str!("../../templates/config.lua");
let config_path = config_dir.join("config.lua");
std::fs::write(&config_path, config_template)?;
- // Copy oxwm.lua API definitions for LSP support
let oxwm_lua_template = include_str!("../../templates/oxwm.lua");
let oxwm_lua_path = config_dir.join("oxwm.lua");
std::fs::write(&oxwm_lua_path, oxwm_lua_template)?;
diff --git a/src/config/lua.rs b/src/config/lua.rs
index 74a0973..49a8fcb 100644
--- a/src/config/lua.rs
+++ b/src/config/lua.rs
@@ -28,7 +28,7 @@ pub fn parse_lua_config(
lua.load(input)
.exec()
- .map_err(|e| ConfigError::LuaError(format!("Failed to execute Lua config: {}", e)))?;
+ .map_err(|e| ConfigError::LuaError(format!("{}", e)))?;
let builder_data = builder.borrow().clone();
@@ -59,7 +59,7 @@ pub fn parse_lua_config(
let config: Table = lua
.load(input)
.eval()
- .map_err(|e| ConfigError::LuaError(format!("Failed to execute Lua config: {}", e)))?;
+ .map_err(|e| ConfigError::LuaError(format!("{}", e)))?;
let border_width: u32 = get_table_field(&config, "border_width")?;
let border_focused: u32 = parse_color(&config, "border_focused")?;
let border_unfocused: u32 = parse_color(&config, "border_unfocused")?;
diff --git a/src/config/lua_api.rs b/src/config/lua_api.rs
index d1c1840..a7b535e 100644
--- a/src/config/lua_api.rs
+++ b/src/config/lua_api.rs
@@ -363,7 +363,7 @@ fn register_bar_module(lua: &Lua, parent: &Table, builder: SharedBuilder) -> Res
} else {
None
}
- }).ok_or_else(|| mlua::Error::RuntimeError("DateTime requires format string".into()))?;
+ }).ok_or_else(|| mlua::Error::RuntimeError("oxwm.bar.add_block: DateTime command requires a format string as the third argument. example: oxwm.bar.add_block(\"\", \"DateTime\", \"%H:%M\", 60, 0xffffff, false)".into()))?;
BlockCommand::DateTime(fmt)
}
"Shell" => {
@@ -373,7 +373,7 @@ fn register_bar_module(lua: &Lua, parent: &Table, builder: SharedBuilder) -> Res
} else {
None
}
- }).ok_or_else(|| mlua::Error::RuntimeError("Shell requires command string".into()))?;
+ }).ok_or_else(|| mlua::Error::RuntimeError("oxwm.bar.add_block: Shell command requires a shell command string as the third argument. example: oxwm.bar.add_block(\"\", \"Shell\", \"date +%H:%M\", 60, 0xffffff, false)".into()))?;
BlockCommand::Shell(cmd_str)
}
"Ram" => BlockCommand::Ram,
@@ -394,7 +394,7 @@ fn register_bar_module(lua: &Lua, parent: &Table, builder: SharedBuilder) -> Res
} else {
None
}
- }).ok_or_else(|| mlua::Error::RuntimeError("Battery requires formats table".into()))?;
+ }).ok_or_else(|| mlua::Error::RuntimeError("oxwm.bar.add_block: Battery command requires a formats table as the third argument. example: {charging=\"CHR {percentage}%\", discharging=\"BAT {percentage}%\", full=\"FULL\"}".into()))?;
let charging: String = formats.get("charging")?;
let discharging: String = formats.get("discharging")?;
@@ -406,7 +406,7 @@ fn register_bar_module(lua: &Lua, parent: &Table, builder: SharedBuilder) -> Res
format_full: full,
}
}
- _ => return Err(mlua::Error::RuntimeError(format!("Unknown block command: {}", command))),
+ _ => return Err(mlua::Error::RuntimeError(format!("oxwm.bar.add_block: unknown block command '{}'. valid commands: DateTime, Shell, Ram, Static, Battery", command))),
};
let color_u32 = parse_color_value(color)?;
@@ -572,7 +572,7 @@ fn parse_modifiers_value(_lua: &Lua, value: Value) -> mlua::Result<Vec<KeyButMas
for i in 1..=t.len()? {
let mod_str: String = t.get(i)?;
let mask = parse_modkey_string(&mod_str)
- .map_err(|e| mlua::Error::RuntimeError(format!("{}", e)))?;
+ .map_err(|e| mlua::Error::RuntimeError(format!("oxwm.key.bind: invalid modifier - {}", e)))?;
mods.push(mask);
}
Ok(mods)
@@ -580,11 +580,11 @@ fn parse_modifiers_value(_lua: &Lua, value: Value) -> mlua::Result<Vec<KeyButMas
Value::String(s) => {
let s_str = s.to_str()?;
let mask = parse_modkey_string(&s_str)
- .map_err(|e| mlua::Error::RuntimeError(format!("{}", e)))?;
+ .map_err(|e| mlua::Error::RuntimeError(format!("oxwm.key.bind: invalid modifier - {}", e)))?;
Ok(vec![mask])
}
_ => Err(mlua::Error::RuntimeError(
- "modifiers must be string or table".into(),
+ "oxwm.key.bind: first argument must be a table of modifiers like {\"Mod4\"} or {\"Mod4\", \"Shift\"}".into(),
)),
}
}
@@ -598,19 +598,23 @@ fn parse_modkey_string(s: &str) -> Result<KeyButMask, ConfigError> {
"Mod5" => Ok(KeyButMask::MOD5),
"Shift" => Ok(KeyButMask::SHIFT),
"Control" => Ok(KeyButMask::CONTROL),
- _ => Err(ConfigError::InvalidModkey(s.to_string())),
+ _ => Err(ConfigError::InvalidModkey(format!("'{}' is not a valid modifier. Use one of: Mod1, Mod4, Shift, Control", s))),
}
}
fn parse_keysym(key: &str) -> mlua::Result<Keysym> {
keysyms::keysym_from_str(key)
- .ok_or_else(|| mlua::Error::RuntimeError(format!("Unknown key: {}", key)))
+ .ok_or_else(|| mlua::Error::RuntimeError(format!("unknown key '{}'. valid keys include: Return, Space, A-Z, 0-9, F1-F12, Left, Right, Up, Down, etc. check oxwm.lua type definitions for the complete list", key)))
}
fn parse_action_value(_lua: &Lua, value: Value) -> mlua::Result<(KeyAction, Arg)> {
match value {
+ Value::Function(_) => {
+ Err(mlua::Error::RuntimeError(
+ "action must be a function call, not a function reference. did you forget ()? example: oxwm.spawn('st') not oxwm.spawn".into()
+ ))
+ }
Value::Table(t) => {
- // Check if this table has our action metadata
if let Ok(action_name) = t.get::<String>("__action") {
let action = string_to_action(&action_name)?;
let arg = if let Ok(arg_val) = t.get::<Value>("__arg") {
@@ -622,11 +626,11 @@ fn parse_action_value(_lua: &Lua, value: Value) -> mlua::Result<(KeyAction, Arg)
}
Err(mlua::Error::RuntimeError(
- "action table missing __action field".into(),
+ "action must be a table returned by oxwm functions like oxwm.spawn(), oxwm.client.kill(), oxwm.quit(), etc.".into(),
))
}
_ => Err(mlua::Error::RuntimeError(
- "action must be a table returned by oxwm actions".into(),
+ "action must be a table returned by oxwm functions like oxwm.spawn(), oxwm.client.kill(), oxwm.quit(), etc.".into(),
)),
}
}
@@ -652,7 +656,7 @@ fn string_to_action(s: &str) -> mlua::Result<KeyAction> {
"SmartMoveWin" => Ok(KeyAction::SmartMoveWin),
"ExchangeClient" => Ok(KeyAction::ExchangeClient),
"ShowKeybindOverlay" => Ok(KeyAction::ShowKeybindOverlay),
- _ => Err(mlua::Error::RuntimeError(format!("Unknown action: {}", s))),
+ _ => Err(mlua::Error::RuntimeError(format!("unknown action '{}'. this is an internal error, please report it", s))),
}
}
@@ -688,7 +692,7 @@ fn direction_string_to_int(dir: &str) -> mlua::Result<i64> {
"left" => Ok(2),
"right" => Ok(3),
_ => Err(mlua::Error::RuntimeError(
- format!("Invalid direction '{}', must be one of: up, down, left, right", dir)
+ format!("invalid direction '{}'. must be one of: up, down, left, right", dir)
)),
}
}
@@ -701,17 +705,17 @@ fn parse_color_value(value: Value) -> mlua::Result<u32> {
let s = s.to_str()?;
if s.starts_with('#') {
u32::from_str_radix(&s[1..], 16)
- .map_err(|e| mlua::Error::RuntimeError(format!("Invalid hex color: {}", e)))
+ .map_err(|e| mlua::Error::RuntimeError(format!("invalid hex color '{}': {}. use format like #ff0000 or 0xff0000", s, e)))
} else if s.starts_with("0x") {
u32::from_str_radix(&s[2..], 16)
- .map_err(|e| mlua::Error::RuntimeError(format!("Invalid hex color: {}", e)))
+ .map_err(|e| mlua::Error::RuntimeError(format!("invalid hex color '{}': {}. use format like 0xff0000 or #ff0000", s, e)))
} else {
s.parse::<u32>()
- .map_err(|e| mlua::Error::RuntimeError(format!("Invalid color: {}", e)))
+ .map_err(|e| mlua::Error::RuntimeError(format!("invalid color '{}': {}. use hex format like 0xff0000 or #ff0000", s, e)))
}
}
_ => Err(mlua::Error::RuntimeError(
- "color must be number or string".into(),
+ "color must be a number (0xff0000) or string ('#ff0000' or '0xff0000')".into(),
)),
}
}
diff --git a/src/errors.rs b/src/errors.rs
index 256ae38..f2286e2 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -73,15 +73,15 @@ impl std::error::Error for X11Error {}
impl std::fmt::Display for ConfigError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- Self::LuaError(msg) => write!(f, "Lua config error: {}", msg),
- Self::InvalidModkey(key) => write!(f, "Invalid modkey: {}", key),
- Self::UnknownKey(key) => write!(f, "Unknown key: {}", key),
- Self::UnknownAction(action) => write!(f, "Unknown action: {}", action),
- Self::UnknownBlockCommand(cmd) => write!(f, "Unknown block command: {}", cmd),
+ Self::LuaError(msg) => write!(f, "{}", msg),
+ Self::InvalidModkey(msg) => write!(f, "{}", msg),
+ Self::UnknownKey(msg) => write!(f, "{}", msg),
+ Self::UnknownAction(msg) => write!(f, "{}", msg),
+ Self::UnknownBlockCommand(msg) => write!(f, "{}", msg),
Self::MissingCommandArg { command, field } => {
write!(f, "{} command requires {}", command, field)
}
- Self::ValidationError(msg) => write!(f, "Config validation error: {}", msg),
+ Self::ValidationError(msg) => write!(f, "{}", msg),
}
}
}
diff --git a/src/overlay/error.rs b/src/overlay/error.rs
index 42ef9cc..256c584 100644
--- a/src/overlay/error.rs
+++ b/src/overlay/error.rs
@@ -55,8 +55,9 @@ impl ErrorOverlay {
screen_width: u16,
screen_height: u16,
) -> Result<(), X11Error> {
- let max_line_width = (screen_width as i16 - PADDING * 4).max(300) as u16;
- self.lines = self.wrap_text(error_text, font, max_line_width);
+ let max_line_width = (screen_width as i16 / 2 - PADDING * 4).max(300) as u16;
+ let error_with_instruction = format!("{}\n\nFix the config file and reload.", error_text);
+ self.lines = self.wrap_text(&error_with_instruction, font, max_line_width);
let mut content_width = 0u16;
for line in &self.lines {
diff --git a/src/window_manager.rs b/src/window_manager.rs
index 8e68e30..ed717b1 100644
--- a/src/window_manager.rs
+++ b/src/window_manager.rs
@@ -263,10 +263,15 @@ impl WindowManager {
.map_err(|e| format!("Failed to read config: {}", e))?;
let new_config = crate::config::parse_lua_config(&config_str, Some(&config_dir))
- .map_err(|e| format!("Config error: {}", e))?;
+ .map_err(|e| format!("{}", e))?;
self.config = new_config;
self.error_message = None;
+
+ for bar in &mut self.bars {
+ bar.update_from_config(&self.config);
+ }
+
Ok(())
}
@@ -1773,16 +1778,20 @@ impl WindowManager {
self.update_bar()?;
}
Err(err) => {
+ eprintln!("Config reload error: {}", err);
self.error_message = Some(err.clone());
let screen_width = self.screen.width_in_pixels;
let screen_height = self.screen.height_in_pixels;
- let _ = self.overlay.show_error(
+ match self.overlay.show_error(
&self.connection,
&self.font,
&err,
screen_width,
screen_height,
- );
+ ) {
+ Ok(()) => eprintln!("Error modal displayed"),
+ Err(e) => eprintln!("Failed to show error modal: {:?}", e),
+ }
}
},
_ => self.handle_key_action(action, &arg)?,
diff --git a/templates/config.lua b/templates/config.lua
index 08e1523..f73a125 100644
--- a/templates/config.lua
+++ b/templates/config.lua
@@ -1,12 +1,30 @@
---@meta
----OXWM Configuration File (Lua)
----Using the new functional API
----Edit this file and reload with Mod+Shift+R (no compilation needed!)
+-------------------------------------------------------------------------------
+-- OXWM Configuration File
+-------------------------------------------------------------------------------
+-- This is the default configuration for OXWM, a dynamic window manager.
+-- Edit this file and reload with Mod+Shift+R (no compilation needed!)
+--
+-- For more information about configuring OXWM, see the documentation.
+-- The Lua Language Server provides autocomplete and type checking.
+-------------------------------------------------------------------------------
---Load type definitions for LSP
---@module 'oxwm'
--- Color palette
+-------------------------------------------------------------------------------
+-- Variables
+-------------------------------------------------------------------------------
+-- Define your variables here for easy customization throughout the config.
+-- This makes it simple to change keybindings, colors, and settings in one place.
+
+-- Modifier key: "Mod4" is the Super/Windows key, "Mod1" is Alt
+local modkey = "Mod4"
+
+-- Terminal emulator command
+local terminal = "st"
+
+-- Color palette - customize these to match your theme
local colors = {
fg = "#bbbbbb",
red = "#f7768e",
@@ -20,111 +38,170 @@ local colors = {
purple = "#ad8ee6",
}
--- Basic settings
-oxwm.set_terminal("st")
-oxwm.set_modkey("Mod4")
-oxwm.set_tags({ "1", "2", "3", "4", "5", "6", "7", "8", "9" })
+-- Workspace tags - can be numbers, names, or icons (requires a Nerd Font)
+local tags = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }
+
+-- Font for the status bar (use "fc-list" to see available fonts)
+local bar_font = "monospace:style=Bold:size=10"
+
+-------------------------------------------------------------------------------
+-- Basic Settings
+-------------------------------------------------------------------------------
+oxwm.set_terminal(terminal)
+oxwm.set_modkey(modkey)
+oxwm.set_tags(tags)
--- Layout symbol overrides
+-------------------------------------------------------------------------------
+-- Layouts
+-------------------------------------------------------------------------------
+-- Set custom symbols for layouts (displayed in the status bar)
+-- Available layouts: "tiling" (master-stack), "normie" (floating)
oxwm.set_layout_symbol("tiling", "[T]")
oxwm.set_layout_symbol("normie", "[F]")
+-------------------------------------------------------------------------------
+-- Appearance
+-------------------------------------------------------------------------------
-- Border configuration
-oxwm.border.set_width(2)
-oxwm.border.set_focused_color(colors.blue)
-oxwm.border.set_unfocused_color(colors.grey)
-
--- Gap configuration
-oxwm.gaps.set_enabled(true)
-oxwm.gaps.set_inner(5, 5) -- horizontal, vertical
-oxwm.gaps.set_outer(5, 5) -- horizontal, vertical
-
--- Bar configuration
-oxwm.bar.set_font("monospace:style=Bold:size=10")
-
--- Bar color schemes (for tag display)
-oxwm.bar.set_scheme_normal(colors.fg, colors.bg, "#444444")
-oxwm.bar.set_scheme_occupied(colors.cyan, colors.bg, colors.cyan)
-oxwm.bar.set_scheme_selected(colors.cyan, colors.bg, colors.purple)
-
+oxwm.border.set_width(2) -- Width in pixels
+oxwm.border.set_focused_color(colors.blue) -- Color of focused window border
+oxwm.border.set_unfocused_color(colors.grey) -- Color of unfocused window borders
+
+-- Gap configuration (space between windows and screen edges)
+oxwm.gaps.set_enabled(true) -- Enable or disable gaps
+oxwm.gaps.set_inner(5, 5) -- Inner gaps (horizontal, vertical) in pixels
+oxwm.gaps.set_outer(5, 5) -- Outer gaps (horizontal, vertical) in pixels
+
+-------------------------------------------------------------------------------
+-- Status Bar Configuration
+-------------------------------------------------------------------------------
+-- Font configuration
+oxwm.bar.set_font(bar_font)
+
+-- Bar color schemes (for workspace tag display)
+-- Parameters: foreground, background, border
+oxwm.bar.set_scheme_normal(colors.fg, colors.bg, "#444444") -- Unoccupied tags
+oxwm.bar.set_scheme_occupied(colors.cyan, colors.bg, colors.cyan) -- Occupied tags
+oxwm.bar.set_scheme_selected(colors.cyan, colors.bg, colors.purple) -- Currently selected tag
+
+-------------------------------------------------------------------------------
-- Keybindings
+-------------------------------------------------------------------------------
+-- Keybindings are defined using oxwm.key.bind(modifiers, key, action)
+-- Modifiers: {"Mod4"}, {"Mod1"}, {"Shift"}, {"Control"}, or combinations like {"Mod4", "Shift"}
+-- Keys: Use uppercase for letters (e.g., "Return", "H", "J", "K", "L")
+-- Actions: Functions that return actions (e.g., oxwm.spawn(), oxwm.client.kill())
+--
+-- A list of available keysyms can be found in the X11 keysym definitions.
+-- Common keys: Return, Space, Tab, Escape, Backspace, Delete, Left, Right, Up, Down
-- Basic window management
-oxwm.key.bind({ "Mod4" }, "Return", oxwm.spawn("st"))
-oxwm.key.bind({ "Mod4" }, "D", oxwm.spawn({ "sh", "-c", "dmenu_run -l 10" }))
-oxwm.key.bind({ "Mod4" }, "S", oxwm.spawn({ "sh", "-c", "maim -s | xclip -selection clipboard -t image/png" }))
-oxwm.key.bind({ "Mod4" }, "Q", oxwm.client.kill())
+oxwm.key.bind({ modkey }, "Return", oxwm.spawn(terminal)) -- Spawn terminal
+oxwm.key.bind({ modkey }, "D", oxwm.spawn({ "sh", "-c", "dmenu_run -l 10" })) -- Application launcher
+oxwm.key.bind({ modkey }, "S", oxwm.spawn({ "sh", "-c", "maim -s | xclip -selection clipboard -t image/png" })) -- Screenshot selection
+oxwm.key.bind({ modkey }, "Q", oxwm.client.kill()) -- Close focused window
--- Keybind overlay
-oxwm.key.bind({ "Mod4", "Shift" }, "Slash", oxwm.show_keybinds())
+-- Keybind overlay - Shows important keybindings on screen
+oxwm.key.bind({ modkey, "Shift" }, "Slash", oxwm.show_keybinds())
--- Client actions
-oxwm.key.bind({ "Mod4", "Shift" }, "F", oxwm.client.toggle_fullscreen())
-oxwm.key.bind({ "Mod4", "Shift" }, "Space", oxwm.client.toggle_floating())
+-- Window state toggles
+oxwm.key.bind({ modkey, "Shift" }, "F", oxwm.client.toggle_fullscreen()) -- Toggle fullscreen
+oxwm.key.bind({ modkey, "Shift" }, "Space", oxwm.client.toggle_floating()) -- Toggle floating mode
-- Layout management
-oxwm.key.bind({ "Mod4" }, "F", oxwm.layout.set("normie"))
-oxwm.key.bind({ "Mod4" }, "C", oxwm.layout.set("tiling"))
-oxwm.key.bind({ "Mod1" }, "N", oxwm.layout.cycle())
+oxwm.key.bind({ modkey }, "F", oxwm.layout.set("normie")) -- Set floating layout
+oxwm.key.bind({ modkey }, "C", oxwm.layout.set("tiling")) -- Set tiling layout
+oxwm.key.bind({ "Mod1" }, "N", oxwm.layout.cycle()) -- Cycle through layouts
-- Gaps toggle
-oxwm.key.bind({ "Mod4" }, "A", oxwm.toggle_gaps())
-
--- WM controls
-oxwm.key.bind({ "Mod4", "Shift" }, "Q", oxwm.quit())
-oxwm.key.bind({ "Mod4", "Shift" }, "R", oxwm.restart())
-
--- Focus direction (vim keys: h=left=2, j=down=1, k=up=0, l=right=3)
-oxwm.key.bind({ "Mod4" }, "H", oxwm.client.focus_direction("left"))
-oxwm.key.bind({ "Mod4" }, "J", oxwm.client.focus_direction("down"))
-oxwm.key.bind({ "Mod4" }, "K", oxwm.client.focus_direction("up"))
-oxwm.key.bind({ "Mod4" }, "L", oxwm.client.focus_direction("right"))
-
--- Monitor focus
-oxwm.key.bind({ "Mod4" }, "Comma", oxwm.focus_monitor(-1))
-oxwm.key.bind({ "Mod4" }, "Period", oxwm.focus_monitor(1))
-
--- Tag viewing
-oxwm.key.bind({ "Mod4" }, "1", oxwm.tag.view(0))
-oxwm.key.bind({ "Mod4" }, "2", oxwm.tag.view(1))
-oxwm.key.bind({ "Mod4" }, "3", oxwm.tag.view(2))
-oxwm.key.bind({ "Mod4" }, "4", oxwm.tag.view(3))
-oxwm.key.bind({ "Mod4" }, "5", oxwm.tag.view(4))
-oxwm.key.bind({ "Mod4" }, "6", oxwm.tag.view(5))
-oxwm.key.bind({ "Mod4" }, "7", oxwm.tag.view(6))
-oxwm.key.bind({ "Mod4" }, "8", oxwm.tag.view(7))
-oxwm.key.bind({ "Mod4" }, "9", oxwm.tag.view(8))
-
--- Move window to tag
-oxwm.key.bind({ "Mod4", "Shift" }, "1", oxwm.tag.move_to(0))
-oxwm.key.bind({ "Mod4", "Shift" }, "2", oxwm.tag.move_to(1))
-oxwm.key.bind({ "Mod4", "Shift" }, "3", oxwm.tag.move_to(2))
-oxwm.key.bind({ "Mod4", "Shift" }, "4", oxwm.tag.move_to(3))
-oxwm.key.bind({ "Mod4", "Shift" }, "5", oxwm.tag.move_to(4))
-oxwm.key.bind({ "Mod4", "Shift" }, "6", oxwm.tag.move_to(5))
-oxwm.key.bind({ "Mod4", "Shift" }, "7", oxwm.tag.move_to(6))
-oxwm.key.bind({ "Mod4", "Shift" }, "8", oxwm.tag.move_to(7))
-oxwm.key.bind({ "Mod4", "Shift" }, "9", oxwm.tag.move_to(8))
-
--- Swap windows in direction
-oxwm.key.bind({ "Mod4", "Shift" }, "H", oxwm.client.swap_direction("left"))
-oxwm.key.bind({ "Mod4", "Shift" }, "J", oxwm.client.swap_direction("down"))
-oxwm.key.bind({ "Mod4", "Shift" }, "K", oxwm.client.swap_direction("up"))
-oxwm.key.bind({ "Mod4", "Shift" }, "L", oxwm.client.swap_direction("right"))
-
--- Keychord example: Mod4+Space then T to spawn terminal
+oxwm.key.bind({ modkey }, "A", oxwm.toggle_gaps()) -- Toggle gaps on/off
+
+-- Window manager controls
+oxwm.key.bind({ modkey, "Shift" }, "Q", oxwm.quit()) -- Quit OXWM
+oxwm.key.bind({ modkey, "Shift" }, "R", oxwm.restart()) -- Restart OXWM (reloads config)
+
+-- Focus movement (vim keys)
+oxwm.key.bind({ modkey }, "H", oxwm.client.focus_direction("left")) -- Focus window to the left
+oxwm.key.bind({ modkey }, "J", oxwm.client.focus_direction("down")) -- Focus window below
+oxwm.key.bind({ modkey }, "K", oxwm.client.focus_direction("up")) -- Focus window above
+oxwm.key.bind({ modkey }, "L", oxwm.client.focus_direction("right")) -- Focus window to the right
+
+-- Multi-monitor support
+oxwm.key.bind({ modkey }, "Comma", oxwm.focus_monitor(-1)) -- Focus previous monitor
+oxwm.key.bind({ modkey }, "Period", oxwm.focus_monitor(1)) -- Focus next monitor
+
+-- Workspace (tag) navigation
+-- Switch to workspace N (tags are 0-indexed, so tag "1" is index 0)
+oxwm.key.bind({ modkey }, "1", oxwm.tag.view(0))
+oxwm.key.bind({ modkey }, "2", oxwm.tag.view(1))
+oxwm.key.bind({ modkey }, "3", oxwm.tag.view(2))
+oxwm.key.bind({ modkey }, "4", oxwm.tag.view(3))
+oxwm.key.bind({ modkey }, "5", oxwm.tag.view(4))
+oxwm.key.bind({ modkey }, "6", oxwm.tag.view(5))
+oxwm.key.bind({ modkey }, "7", oxwm.tag.view(6))
+oxwm.key.bind({ modkey }, "8", oxwm.tag.view(7))
+oxwm.key.bind({ modkey }, "9", oxwm.tag.view(8))
+
+-- Move focused window to workspace N
+oxwm.key.bind({ modkey, "Shift" }, "1", oxwm.tag.move_to(0))
+oxwm.key.bind({ modkey, "Shift" }, "2", oxwm.tag.move_to(1))
+oxwm.key.bind({ modkey, "Shift" }, "3", oxwm.tag.move_to(2))
+oxwm.key.bind({ modkey, "Shift" }, "4", oxwm.tag.move_to(3))
+oxwm.key.bind({ modkey, "Shift" }, "5", oxwm.tag.move_to(4))
+oxwm.key.bind({ modkey, "Shift" }, "6", oxwm.tag.move_to(5))
+oxwm.key.bind({ modkey, "Shift" }, "7", oxwm.tag.move_to(6))
+oxwm.key.bind({ modkey, "Shift" }, "8", oxwm.tag.move_to(7))
+oxwm.key.bind({ modkey, "Shift" }, "9", oxwm.tag.move_to(8))
+
+-- Swap windows in direction (vim keys with Shift)
+oxwm.key.bind({ modkey, "Shift" }, "H", oxwm.client.swap_direction("left")) -- Swap with window to the left
+oxwm.key.bind({ modkey, "Shift" }, "J", oxwm.client.swap_direction("down")) -- Swap with window below
+oxwm.key.bind({ modkey, "Shift" }, "K", oxwm.client.swap_direction("up")) -- Swap with window above
+oxwm.key.bind({ modkey, "Shift" }, "L", oxwm.client.swap_direction("right")) -- Swap with window to the right
+
+-------------------------------------------------------------------------------
+-- Advanced: Keychords
+-------------------------------------------------------------------------------
+-- Keychords allow you to bind multiple-key sequences (like Emacs or Vim)
+-- Format: {{modifiers}, key1}, {{modifiers}, key2}, ...
+-- Example: Press Mod4+Space, then release and press T to spawn a terminal
oxwm.key.chord({
- { { "Mod4" }, "Space" },
- { {}, "T" }
-}, oxwm.spawn("st"))
+ { { modkey }, "Space" },
+ { {}, "T" }
+}, oxwm.spawn(terminal))
+
+-------------------------------------------------------------------------------
+-- Status Bar Blocks
+-------------------------------------------------------------------------------
+-- Add informational blocks to the status bar
+-- Format: oxwm.bar.add_block(format, type, data, update_interval, color, separator)
+-- format: Display format with {} placeholders
+-- type: Block type ("Ram", "DateTime", "Shell", "Static", "Battery")
+-- data: Type-specific data (command for Shell, format for DateTime, etc.)
+-- update_interval: Seconds between updates (large number for static content)
+-- color: Text color (from color palette)
+-- separator: Whether to add space after this block
--- Status bar blocks
oxwm.bar.add_block("Ram: {used}/{total} GB", "Ram", nil, 5, colors.light_blue, true)
oxwm.bar.add_block(" │ ", "Static", " │ ", 999999999, colors.lavender, false)
oxwm.bar.add_block("Kernel: {}", "Shell", "uname -r", 999999999, colors.red, true)
oxwm.bar.add_block(" │ ", "Static", " │ ", 999999999, colors.lavender, false)
oxwm.bar.add_block("{}", "DateTime", "%a, %b %d - %-I:%M %P", 1, colors.cyan, true)
--- Autostart commands (runs once at startup)
--- oxwm.autostart("picom")
--- oxwm.autostart("feh --bg-scale ~/wallpaper.jpg")
+-- Uncomment to add battery status (useful for laptops)
+-- oxwm.bar.add_block("Bat: {}%", "Battery", {
+-- charging = "⚡ Bat: {}%",
+-- discharging = "🔋 Bat: {}%",
+-- full = "✓ Bat: {}%"
+-- }, 30, colors.green, true)
+
+-------------------------------------------------------------------------------
+-- Autostart
+-------------------------------------------------------------------------------
+-- Commands to run once when OXWM starts
+-- Uncomment and modify these examples, or add your own
+
+-- oxwm.autostart("picom") -- Compositor for transparency and effects
+-- oxwm.autostart("feh --bg-scale ~/wallpaper.jpg") -- Set wallpaper
+-- oxwm.autostart("dunst") -- Notification daemon
+-- oxwm.autostart("nm-applet") -- Network manager applet