Diff
diff --git a/Cargo.lock b/Cargo.lock
index 70c684a..31b4bcb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -23,11 +23,20 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
[[package]]
name = "bitflags"
version = "2.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
+dependencies = [
+ "serde",
+]
[[package]]
name = "bumpalo"
@@ -213,11 +222,13 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "oxwm"
-version = "0.1.11"
+version = "0.1.12"
dependencies = [
"anyhow",
"chrono",
"dirs",
+ "ron",
+ "serde",
"x11",
"x11rb",
]
@@ -257,6 +268,18 @@ dependencies = [
"thiserror",
]
+[[package]]
+name = "ron"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
+dependencies = [
+ "base64",
+ "bitflags",
+ "serde",
+ "serde_derive",
+]
+
[[package]]
name = "rustix"
version = "1.1.2"
@@ -276,6 +299,36 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "shlex"
version = "1.3.0"
diff --git a/Cargo.toml b/Cargo.toml
index a53158a..581dd72 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "oxwm"
-version = "0.1.11"
+version = "0.1.12"
edition = "2024"
[lib]
@@ -17,4 +17,5 @@ x11rb = { version = "0.13", features = ["cursor"] }
anyhow = "1"
chrono = "0.4"
dirs = "5.0"
-# xcursor-rs = "0.1"
+serde = { version = "1.0", features = ["derive"] }
+ron = "0.8"
diff --git a/justfile b/justfile
index 463cc02..5f92a0a 100644
--- a/justfile
+++ b/justfile
@@ -5,12 +5,12 @@ install: build
cp target/release/oxwm ~/.local/bin/oxwm
chmod +x ~/.local/bin/oxwm
@echo "✓ oxwm installed to ~/.local/bin/oxwm"
- @echo " Run 'oxwm --init' to set up your config"
+ @echo " Run 'oxwm --init' to create your config"
uninstall:
rm -f ~/.local/bin/oxwm
@echo "✓ oxwm uninstalled"
- @echo " Your config at ~/.config/oxwm is preserved"
+ @echo " Your config at ~/.config/oxwm/config.ron is preserved"
clean:
cargo clean
@@ -23,24 +23,14 @@ test-clean:
test:
pkill Xephyr || true
- rm -f ~/.config/oxwm/oxwm-user
Xephyr -screen 1280x800 :1 & sleep 1
DISPLAY=:1 cargo run --release
-test-user:
- pkill Xephyr || true
- cargo run --release -- --recompile
- Xephyr -screen 1280x800 :1 & sleep 1
- DISPLAY=:1 ~/.config/oxwm/oxwm-user
-
init:
- cargo run -- --init
-
-recompile:
- cargo run -- --recompile
+ cargo run --release -- --init
edit:
- $EDITOR ~/.config/oxwm/config.rs
+ $EDITOR ~/.config/oxwm/config.ron
check:
cargo clippy -- -W clippy::all
diff --git a/src/bin/main.rs b/src/bin/main.rs
index b3b6974..9809a58 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -15,36 +15,13 @@ fn main() -> Result<()> {
}
Some("--init") => {
init_config()?;
- println!("✓ Config created at ~/.config/oxwm/config.rs");
- println!(" Edit and reload with Mod+Shift+R");
- return Ok(());
- }
- Some("--recompile") => {
- recompile_config()?;
return Ok(());
}
_ => {}
}
- let config_dir = get_config_path();
- let user_bin = config_dir.join("target/release/oxwm-user");
-
- if user_bin.exists() {
- use std::os::unix::process::CommandExt;
- let err = std::process::Command::new(&user_bin)
- .args(&args[1..])
- .exec();
- eprintln!("Failed to exec user binary: {}", err);
- std::process::exit(1);
- }
+ let config = load_config()?;
- let config = oxwm::Config::default();
- run_wm_with_config(config, &args)?;
-
- Ok(())
-}
-
-fn run_wm_with_config(config: oxwm::Config, args: &[String]) -> Result<()> {
let mut wm = oxwm::window_manager::WindowManager::new(config)?;
let should_restart = wm.run()?;
@@ -59,171 +36,57 @@ fn run_wm_with_config(config: oxwm::Config, args: &[String]) -> Result<()> {
Ok(())
}
-fn should_recompile(config: &PathBuf, binary: &PathBuf) -> Result<bool> {
- let config_dir = get_config_path();
- let binary_time = std::fs::metadata(binary)?.modified()?;
-
- let watch_files = ["config.rs", "Cargo.toml"];
-
- for filename in &watch_files {
- let path = config_dir.join(filename);
- if !path.exists() {
- continue;
- }
+fn load_config() -> Result<oxwm::Config> {
+ let config_path = get_config_path().join("config.ron");
- let file_time = std::fs::metadata(&path)?.modified()?;
- if file_time > binary_time {
- return Ok(true);
- }
+ if !config_path.exists() {
+ println!("No config found at {:?}", config_path);
+ println!("Creating default config...");
+ init_config()?;
}
- Ok(false)
+ let config_str = std::fs::read_to_string(&config_path)
+ .map_err(|e| anyhow::anyhow!("Failed to read config file: {}", e))?;
+
+ oxwm::config::parse_config(&config_str)
+ .map_err(|e| anyhow::anyhow!("Failed to parse config: {}", e))
}
fn init_config() -> Result<()> {
let config_dir = get_config_path();
std::fs::create_dir_all(&config_dir)?;
- let config_template = include_str!("../../templates/config.rs");
- std::fs::write(config_dir.join("config.rs"), config_template)?;
-
- let main_template = include_str!("../../templates/main.rs");
- std::fs::write(config_dir.join("main.rs"), main_template)?;
-
- let cargo_toml = r#"[package]
-name = "oxwm-user"
-version = "0.1.0"
-edition = "2024"
-
-[dependencies]
-oxwm = { git = "https://github.com/tonybanters/oxwm" }
-anyhow = "1"
-
-[[bin]]
-name = "oxwm-user"
-path = "main.rs"
-"#;
-
- std::fs::write(config_dir.join("Cargo.toml"), cargo_toml)?;
- std::fs::write(config_dir.join(".gitignore"), "target/\nCargo.lock\n")?;
-
- if is_nixos() {
- let shell_nix = r#"{ pkgs ? import <nixpkgs> {} }:
-pkgs.mkShell {
- buildInputs = with pkgs; [
- rustc
- cargo
- pkg-config
- xorg.libX11
- xorg.libXft
- xorg.libXrender
- freetype
- fontconfig
- ];
-}
-"#;
- std::fs::write(config_dir.join("shell.nix"), shell_nix)?;
- println!("✓ Created shell.nix for NixOS");
- }
-
- Ok(())
-}
-
-fn recompile_config() -> Result<()> {
- let config_dir = get_config_path();
-
- if !config_dir.join("config.rs").exists() {
- anyhow::bail!("No config found. Run: oxwm --init");
- }
+ let config_template = include_str!("../../templates/config.ron");
+ let config_path = config_dir.join("config.ron");
- println!("Compiling oxwm configuration...");
+ std::fs::write(&config_path, config_template)?;
- let build_method = detect_build_method();
-
- let output = match build_method {
- BuildMethod::NixFlake => {
- println!("Using nix flake build...");
- std::process::Command::new("nix")
- .args(&["build", ".#", "--no-link"])
- .current_dir(&config_dir)
- .output()?
- }
- BuildMethod::NixBuild => {
- println!("Using nix-shell...");
- std::process::Command::new("nix-shell")
- .args(&["--run", "cargo build --release"])
- .current_dir(&config_dir)
- .output()?
- }
- BuildMethod::Cargo => std::process::Command::new("cargo")
- .args(&["build", "--release"])
- .current_dir(&config_dir)
- .output()?,
- };
-
- if !output.status.success() {
- let stderr = String::from_utf8_lossy(&output.stderr);
- eprintln!("\n❌ Compilation failed:\n{}", stderr);
- anyhow::bail!("Failed to compile configuration");
- }
-
- println!("✓ Compiled successfully.");
- println!(" Restart oxwm to use new config");
+ println!("✓ Config created at {:?}", config_path);
+ println!(" Edit the file and reload with Mod+Shift+R");
+ println!(" No compilation needed - changes take effect immediately!");
Ok(())
}
-#[derive(Debug)]
-enum BuildMethod {
- NixFlake,
- NixBuild,
- Cargo,
-}
-
-fn detect_build_method() -> BuildMethod {
- let config_dir = get_config_path();
-
- if config_dir.join("flake.nix").exists() {
- println!("Detected flake.nix, will use nix flake for recompilation");
- return BuildMethod::NixFlake;
- }
-
- if config_dir.join("default.nix").exists() || config_dir.join("shell.nix").exists() {
- println!("Detected nix files, will use nix-shell for recompilation");
- return BuildMethod::NixBuild;
- }
-
- println!("Will use cargo for recompilation");
- BuildMethod::Cargo
-}
-
-fn is_nixos() -> bool {
- std::path::Path::new("/etc/NIXOS").exists()
- || std::path::Path::new("/run/current-system/nixos-version").exists()
- || std::env::var("NIX_PATH").is_ok()
-}
-
fn get_config_path() -> PathBuf {
dirs::config_dir()
.expect("Could not find config directory")
.join("oxwm")
}
-fn get_user_binary_path() -> PathBuf {
- get_config_path().join("oxwm-user")
-}
-
fn print_help() {
println!("OXWM - A dynamic window manager written in Rust\n");
println!("USAGE:");
println!(" oxwm [OPTIONS]\n");
println!("OPTIONS:");
- println!(" --init Create default config in ~/.config/oxwm");
- println!(" --recompile Recompile user configuration");
+ println!(" --init Create default config in ~/.config/oxwm/config.ron");
println!(" --version Print version information");
println!(" --help Print this help message\n");
println!("CONFIG:");
- println!(" First run: Creates config at ~/.config/oxwm/config.rs");
- println!(" Edit your config and run 'oxwm --recompile'");
- println!(" Use Mod+Shift+R to hot-reload after recompiling\n");
+ println!(" Location: ~/.config/oxwm/config.ron");
+ println!(" Edit the config file and use Mod+Shift+R to reload");
+ println!(" No compilation needed - instant hot-reload!\n");
+ println!("FIRST RUN:");
+ println!(" Run 'oxwm --init' to create a config file");
+ println!(" Or just start oxwm and it will create one automatically\n");
}
diff --git a/src/bin/main.rs.backup b/src/bin/main.rs.backup
new file mode 100644
index 0000000..b3b6974
--- /dev/null
+++ b/src/bin/main.rs.backup
@@ -0,0 +1,229 @@
+use anyhow::Result;
+use std::path::PathBuf;
+
+fn main() -> Result<()> {
+ let args: Vec<String> = std::env::args().collect();
+
+ match args.get(1).map(|s| s.as_str()) {
+ Some("--version") => {
+ println!("oxwm {}", env!("CARGO_PKG_VERSION"));
+ return Ok(());
+ }
+ Some("--help") => {
+ print_help();
+ return Ok(());
+ }
+ Some("--init") => {
+ init_config()?;
+ println!("✓ Config created at ~/.config/oxwm/config.rs");
+ println!(" Edit and reload with Mod+Shift+R");
+ return Ok(());
+ }
+ Some("--recompile") => {
+ recompile_config()?;
+ return Ok(());
+ }
+ _ => {}
+ }
+
+ let config_dir = get_config_path();
+ let user_bin = config_dir.join("target/release/oxwm-user");
+
+ if user_bin.exists() {
+ use std::os::unix::process::CommandExt;
+ let err = std::process::Command::new(&user_bin)
+ .args(&args[1..])
+ .exec();
+ eprintln!("Failed to exec user binary: {}", err);
+ std::process::exit(1);
+ }
+
+ let config = oxwm::Config::default();
+ run_wm_with_config(config, &args)?;
+
+ Ok(())
+}
+
+fn run_wm_with_config(config: oxwm::Config, args: &[String]) -> Result<()> {
+ let mut wm = oxwm::window_manager::WindowManager::new(config)?;
+ let should_restart = wm.run()?;
+
+ drop(wm);
+
+ if should_restart {
+ use std::os::unix::process::CommandExt;
+ let err = std::process::Command::new(&args[0]).args(&args[1..]).exec();
+ eprintln!("Failed to restart: {}", err);
+ }
+
+ Ok(())
+}
+
+fn should_recompile(config: &PathBuf, binary: &PathBuf) -> Result<bool> {
+ let config_dir = get_config_path();
+ let binary_time = std::fs::metadata(binary)?.modified()?;
+
+ let watch_files = ["config.rs", "Cargo.toml"];
+
+ for filename in &watch_files {
+ let path = config_dir.join(filename);
+ if !path.exists() {
+ continue;
+ }
+
+ let file_time = std::fs::metadata(&path)?.modified()?;
+ if file_time > binary_time {
+ return Ok(true);
+ }
+ }
+
+ Ok(false)
+}
+
+fn init_config() -> Result<()> {
+ let config_dir = get_config_path();
+ std::fs::create_dir_all(&config_dir)?;
+
+ let config_template = include_str!("../../templates/config.rs");
+ std::fs::write(config_dir.join("config.rs"), config_template)?;
+
+ let main_template = include_str!("../../templates/main.rs");
+ std::fs::write(config_dir.join("main.rs"), main_template)?;
+
+ let cargo_toml = r#"[package]
+name = "oxwm-user"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+oxwm = { git = "https://github.com/tonybanters/oxwm" }
+anyhow = "1"
+
+[[bin]]
+name = "oxwm-user"
+path = "main.rs"
+"#;
+
+ std::fs::write(config_dir.join("Cargo.toml"), cargo_toml)?;
+ std::fs::write(config_dir.join(".gitignore"), "target/\nCargo.lock\n")?;
+
+ if is_nixos() {
+ let shell_nix = r#"{ pkgs ? import <nixpkgs> {} }:
+pkgs.mkShell {
+ buildInputs = with pkgs; [
+ rustc
+ cargo
+ pkg-config
+ xorg.libX11
+ xorg.libXft
+ xorg.libXrender
+ freetype
+ fontconfig
+ ];
+}
+"#;
+ std::fs::write(config_dir.join("shell.nix"), shell_nix)?;
+ println!("✓ Created shell.nix for NixOS");
+ }
+
+ Ok(())
+}
+
+fn recompile_config() -> Result<()> {
+ let config_dir = get_config_path();
+
+ if !config_dir.join("config.rs").exists() {
+ anyhow::bail!("No config found. Run: oxwm --init");
+ }
+
+ println!("Compiling oxwm configuration...");
+
+ let build_method = detect_build_method();
+
+ let output = match build_method {
+ BuildMethod::NixFlake => {
+ println!("Using nix flake build...");
+ std::process::Command::new("nix")
+ .args(&["build", ".#", "--no-link"])
+ .current_dir(&config_dir)
+ .output()?
+ }
+ BuildMethod::NixBuild => {
+ println!("Using nix-shell...");
+ std::process::Command::new("nix-shell")
+ .args(&["--run", "cargo build --release"])
+ .current_dir(&config_dir)
+ .output()?
+ }
+ BuildMethod::Cargo => std::process::Command::new("cargo")
+ .args(&["build", "--release"])
+ .current_dir(&config_dir)
+ .output()?,
+ };
+
+ if !output.status.success() {
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ eprintln!("\n❌ Compilation failed:\n{}", stderr);
+ anyhow::bail!("Failed to compile configuration");
+ }
+
+ println!("✓ Compiled successfully.");
+ println!(" Restart oxwm to use new config");
+
+ Ok(())
+}
+
+#[derive(Debug)]
+enum BuildMethod {
+ NixFlake,
+ NixBuild,
+ Cargo,
+}
+
+fn detect_build_method() -> BuildMethod {
+ let config_dir = get_config_path();
+
+ if config_dir.join("flake.nix").exists() {
+ println!("Detected flake.nix, will use nix flake for recompilation");
+ return BuildMethod::NixFlake;
+ }
+
+ if config_dir.join("default.nix").exists() || config_dir.join("shell.nix").exists() {
+ println!("Detected nix files, will use nix-shell for recompilation");
+ return BuildMethod::NixBuild;
+ }
+
+ println!("Will use cargo for recompilation");
+ BuildMethod::Cargo
+}
+
+fn is_nixos() -> bool {
+ std::path::Path::new("/etc/NIXOS").exists()
+ || std::path::Path::new("/run/current-system/nixos-version").exists()
+ || std::env::var("NIX_PATH").is_ok()
+}
+
+fn get_config_path() -> PathBuf {
+ dirs::config_dir()
+ .expect("Could not find config directory")
+ .join("oxwm")
+}
+
+fn get_user_binary_path() -> PathBuf {
+ get_config_path().join("oxwm-user")
+}
+
+fn print_help() {
+ println!("OXWM - A dynamic window manager written in Rust\n");
+ println!("USAGE:");
+ println!(" oxwm [OPTIONS]\n");
+ println!("OPTIONS:");
+ println!(" --init Create default config in ~/.config/oxwm");
+ println!(" --recompile Recompile user configuration");
+ println!(" --version Print version information");
+ println!(" --help Print this help message\n");
+ println!("CONFIG:");
+ println!(" First run: Creates config at ~/.config/oxwm/config.rs");
+ println!(" Edit your config and run 'oxwm --recompile'");
+ println!(" Use Mod+Shift+R to hot-reload after recompiling\n");
+}
diff --git a/src/config.rs.backup b/src/config.rs.backup
deleted file mode 100644
index c02129c..0000000
--- a/src/config.rs.backup
+++ /dev/null
@@ -1,190 +0,0 @@
-use crate::bar::{BlockCommand, BlockConfig};
-use crate::keyboard::handlers::Key;
-use crate::keyboard::{Arg, KeyAction, keycodes};
-use x11rb::protocol::xproto::KeyButMask;
-
-// ========================================
-// APPEARANCE
-// ========================================
-pub const BORDER_WIDTH: u32 = 2;
-pub const BORDER_FOCUSED: u32 = 0x6dade3;
-pub const BORDER_UNFOCUSED: u32 = 0xbbbbbb;
-pub const FONT: &str = "JetBrainsMono Nerd Font:style=Bold:size=12";
-
-// ========================================
-// GAPS (Vanity Gaps)
-// ========================================
-pub const GAPS_ENABLED: bool = false;
-pub const GAP_INNER_HORIZONTAL: u32 = 3;
-pub const GAP_INNER_VERTICAL: u32 = 3;
-pub const GAP_OUTER_HORIZONTAL: u32 = 3;
-pub const GAP_OUTER_VERTICAL: u32 = 3;
-//
-// ========================================
-// DEFAULTS
-// ========================================
-pub const TERMINAL: &str = "st";
-pub const XCLOCK: &str = "xclock";
-pub const MODKEY: KeyButMask = KeyButMask::MOD4;
-
-// ========================================
-// BAR COLORS
-// ========================================
-
-// Base colors
-const GRAY_DARK: u32 = 0x1a1b26;
-const GRAY_SEP: u32 = 0xa9b1d6;
-const GRAY_MID: u32 = 0x444444;
-const GRAY_LIGHT: u32 = 0xbbbbbb;
-const CYAN: u32 = 0x0db9d7;
-const MAGENTA: u32 = 0xad8ee6;
-const RED: u32 = 0xf7768e;
-const GREEN: u32 = 0x9ece6a;
-const BLUE: u32 = 0x7aa2f7;
-const YELLOW: u32 = 0xe0af68;
-
-pub struct ColorScheme {
- pub foreground: u32,
- pub background: u32,
- pub underline: u32,
-}
-
-pub const SCHEME_NORMAL: ColorScheme = ColorScheme {
- foreground: GRAY_LIGHT,
- background: GRAY_DARK,
- underline: GRAY_MID,
-};
-
-pub const SCHEME_OCCUPIED: ColorScheme = ColorScheme {
- foreground: CYAN,
- background: GRAY_DARK,
- underline: CYAN,
-};
-
-pub const SCHEME_SELECTED: ColorScheme = ColorScheme {
- foreground: CYAN,
- background: GRAY_DARK,
- underline: MAGENTA,
-};
-
-// ========================================
-// Commands
-// ========================================
-const SCREENSHOT_CMD: &[&str] = &[
- "sh",
- "-c",
- "maim -s | xclip -selection clipboard -t image/png",
-];
-
-const DMENU_CMD: &[&str] = &["sh", "-c", "dmenu_run -l 10"];
-
-// ========================================
-// TAGS
-// ========================================
-pub const TAG_COUNT: usize = 9;
-pub const TAGS: [&str; TAG_COUNT] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"];
-//pub const TAGS: [&str; TAG_COUNT] = ["", "", "", "", "", "", "", "", ""];
-// pub const TAGS: [&str; TAG_COUNT] = [
-// "DEV", "WWW", "SYS", "DOC", "VBOX", "CHAT", "MUS", "VID", "MISC",
-// ];
-
-// ========================================
-// KEYBINDINGS
-// ========================================
-#[rustfmt::skip]
-pub const KEYBINDINGS: &[Key] = &[
- Key::new(&[MODKEY], keycodes::RETURN, KeyAction::Spawn, Arg::Str(TERMINAL)),
- Key::new(&[MODKEY], keycodes::F, KeyAction::Spawn, Arg::Str(XCLOCK)),
-
- Key::new(&[MODKEY], keycodes::S, KeyAction::Spawn, Arg::Array(SCREENSHOT_CMD)),
- Key::new(&[MODKEY], keycodes::D, KeyAction::Spawn, Arg::Array(DMENU_CMD)),
- Key::new(&[MODKEY], keycodes::Q, KeyAction::KillClient, Arg::None),
- Key::new(&[MODKEY, SHIFT], keycodes::F, KeyAction::ToggleFullScreen, Arg::None),
- Key::new(&[MODKEY], keycodes::A, KeyAction::ToggleGaps, Arg::None),
- Key::new(&[MODKEY, SHIFT], keycodes::Q, KeyAction::Quit, Arg::None),
- Key::new(&[MODKEY, SHIFT], keycodes::R, KeyAction::Restart, Arg::None),
- Key::new(&[MODKEY], keycodes::J, KeyAction::FocusStack, Arg::Int(-1)),
- Key::new(&[MODKEY], keycodes::K, KeyAction::FocusStack, Arg::Int(1)),
-
- Key::new(&[MODKEY], keycodes::KEY_1, KeyAction::ViewTag, Arg::Int(0)),
- Key::new(&[MODKEY], keycodes::KEY_2, KeyAction::ViewTag, Arg::Int(1)),
- Key::new(&[MODKEY], keycodes::KEY_3, KeyAction::ViewTag, Arg::Int(2)),
- Key::new(&[MODKEY], keycodes::KEY_4, KeyAction::ViewTag, Arg::Int(3)),
- Key::new(&[MODKEY], keycodes::KEY_5, KeyAction::ViewTag, Arg::Int(4)),
- Key::new(&[MODKEY], keycodes::KEY_6, KeyAction::ViewTag, Arg::Int(5)),
- Key::new(&[MODKEY], keycodes::KEY_7, KeyAction::ViewTag, Arg::Int(6)),
- Key::new(&[MODKEY], keycodes::KEY_8, KeyAction::ViewTag, Arg::Int(7)),
- Key::new(&[MODKEY], keycodes::KEY_9, KeyAction::ViewTag, Arg::Int(8)),
-
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_1, KeyAction::MoveToTag, Arg::Int(0)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_2, KeyAction::MoveToTag, Arg::Int(1)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_3, KeyAction::MoveToTag, Arg::Int(2)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_4, KeyAction::MoveToTag, Arg::Int(3)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_5, KeyAction::MoveToTag, Arg::Int(4)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_6, KeyAction::MoveToTag, Arg::Int(5)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_7, KeyAction::MoveToTag, Arg::Int(6)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_8, KeyAction::MoveToTag, Arg::Int(7)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_9, KeyAction::MoveToTag, Arg::Int(8)),
-];
-
-// ========================================
-// STATUS BAR BLOCKS
-// ========================================
-pub const STATUS_BLOCKS: &[BlockConfig] = &[
- BlockConfig {
- format: "",
- command: BlockCommand::Battery {
- format_charging: " Bat: {}%",
- format_discharging: " Bat:{}%",
- format_full: " Bat: {}%",
- },
- interval_secs: 30,
- color: GREEN,
- underline: true,
- },
- BlockConfig {
- format: " │ ",
- command: BlockCommand::Static(""),
- interval_secs: u64::MAX,
- color: GRAY_SEP,
- underline: false,
- },
- BlockConfig {
- format: " {used}/{total} GB",
- command: BlockCommand::Ram,
- interval_secs: 5,
- color: BLUE,
- underline: true,
- },
- BlockConfig {
- format: " │ ",
- command: BlockCommand::Static(""),
- interval_secs: u64::MAX,
- color: GRAY_SEP,
- underline: false,
- },
- BlockConfig {
- format: " {}",
- command: BlockCommand::Shell("uname -r"),
- interval_secs: u64::MAX,
- color: RED,
- underline: true,
- },
- BlockConfig {
- format: " │ ",
- command: BlockCommand::Static(""),
- interval_secs: u64::MAX,
- color: GRAY_SEP,
- underline: false,
- },
- BlockConfig {
- format: " {}",
- command: BlockCommand::DateTime("%a, %b %d - %-I:%M %P"),
- interval_secs: 1,
- color: CYAN,
- underline: true,
- },
-];
-
-const SHIFT: KeyButMask = KeyButMask::SHIFT;
-pub const WM_BINARY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/target/release/oxwm");
diff --git a/src/config/mod.rs b/src/config/mod.rs
new file mode 100644
index 0000000..01c16b0
--- /dev/null
+++ b/src/config/mod.rs
@@ -0,0 +1,343 @@
+use anyhow::{Context, Result, bail};
+use serde::Deserialize;
+
+pub fn parse_config(input: &str) -> Result<crate::Config> {
+ let config_data: ConfigData = ron::from_str(input).map_err(|e| {
+ eprintln!("RON Parse Error Details: {}", e);
+ anyhow::anyhow!("Failed to parse RON config: {}", e)
+ })?;
+
+ config_data_to_config(config_data)
+}
+
+#[derive(Debug, Deserialize)]
+struct ConfigData {
+ border_width: u32,
+ border_focused: u32,
+ border_unfocused: u32,
+ font: String,
+
+ gaps_enabled: bool,
+ gap_inner_horizontal: u32,
+ gap_inner_vertical: u32,
+ gap_outer_horizontal: u32,
+ gap_outer_vertical: u32,
+
+ terminal: String,
+ modkey: String,
+
+ tags: Vec<String>,
+ keybindings: Vec<KeybindingData>,
+ status_blocks: Vec<StatusBlockData>,
+
+ scheme_normal: ColorSchemeData,
+ scheme_occupied: ColorSchemeData,
+ scheme_selected: ColorSchemeData,
+}
+
+#[derive(Debug, Deserialize)]
+struct KeybindingData {
+ modifiers: Vec<String>,
+ key: String,
+ action: String,
+ #[serde(default)]
+ arg: ArgData,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(untagged)]
+enum ArgData {
+ None,
+ String(String),
+ Int(i32),
+ Array(Vec<String>),
+}
+
+impl Default for ArgData {
+ fn default() -> Self {
+ ArgData::None
+ }
+}
+
+#[derive(Debug, Deserialize)]
+struct StatusBlockData {
+ format: String,
+ command: String,
+ #[serde(default)]
+ command_arg: Option<String>,
+ #[serde(default)]
+ battery_formats: Option<BatteryFormats>,
+ interval_secs: u64,
+ color: u32,
+ underline: bool,
+}
+
+#[derive(Debug, Deserialize)]
+struct BatteryFormats {
+ charging: String,
+ discharging: String,
+ full: String,
+}
+
+#[derive(Debug, Deserialize)]
+struct ColorSchemeData {
+ foreground: u32,
+ background: u32,
+ underline: u32,
+}
+
+fn config_data_to_config(data: ConfigData) -> Result<crate::Config> {
+ use crate::keyboard::handlers::Key;
+ use crate::keyboard::{Arg, KeyAction};
+ use x11rb::protocol::xproto::KeyButMask;
+
+ // Parse modkey
+ let modkey = parse_modkey(&data.modkey)?;
+
+ // Parse keybindings
+ let mut keybindings = Vec::new();
+ for kb_data in data.keybindings {
+ let modifiers = kb_data
+ .modifiers
+ .iter()
+ .map(|s| parse_modkey(s))
+ .collect::<Result<Vec<_>>>()?;
+
+ let modifiers_static: &'static [KeyButMask] = Box::leak(modifiers.into_boxed_slice());
+
+ let key = string_to_keycode(&kb_data.key)?;
+ let action = parse_key_action(&kb_data.action)?;
+ let arg = arg_data_to_arg(kb_data.arg)?;
+
+ keybindings.push(Key::new(modifiers_static, key, action, arg));
+ }
+
+ // Parse status blocks
+ let mut status_blocks = Vec::new();
+ for block_data in data.status_blocks {
+ use crate::bar::{BlockCommand, BlockConfig};
+
+ let format_static: &'static str = Box::leak(block_data.format.into_boxed_str());
+
+ let command = match block_data.command.as_str() {
+ "DateTime" => {
+ let fmt = block_data
+ .command_arg
+ .context("DateTime command requires command_arg")?;
+ let fmt_static: &'static str = Box::leak(fmt.into_boxed_str());
+ BlockCommand::DateTime(fmt_static)
+ }
+ "Shell" => {
+ let cmd = block_data
+ .command_arg
+ .context("Shell command requires command_arg")?;
+ let cmd_static: &'static str = Box::leak(cmd.into_boxed_str());
+ BlockCommand::Shell(cmd_static)
+ }
+ "Ram" => BlockCommand::Ram,
+ "Static" => {
+ let text = block_data.command_arg.unwrap_or_default();
+ let text_static: &'static str = Box::leak(text.into_boxed_str());
+ BlockCommand::Static(text_static)
+ }
+ "Battery" => {
+ let formats = block_data
+ .battery_formats
+ .context("Battery command requires battery_formats")?;
+ BlockCommand::Battery {
+ format_charging: Box::leak(formats.charging.into_boxed_str()),
+ format_discharging: Box::leak(formats.discharging.into_boxed_str()),
+ format_full: Box::leak(formats.full.into_boxed_str()),
+ }
+ }
+ _ => bail!("Unknown block command: {}", block_data.command),
+ };
+
+ status_blocks.push(BlockConfig {
+ format: format_static,
+ command,
+ interval_secs: block_data.interval_secs,
+ color: block_data.color,
+ underline: block_data.underline,
+ });
+ }
+
+ Ok(crate::Config {
+ border_width: data.border_width,
+ border_focused: data.border_focused,
+ border_unfocused: data.border_unfocused,
+ font: data.font,
+ gaps_enabled: data.gaps_enabled,
+ gap_inner_horizontal: data.gap_inner_horizontal,
+ gap_inner_vertical: data.gap_inner_vertical,
+ gap_outer_horizontal: data.gap_outer_horizontal,
+ gap_outer_vertical: data.gap_outer_vertical,
+ terminal: data.terminal,
+ modkey,
+ tags: data.tags,
+ keybindings,
+ status_blocks,
+ scheme_normal: crate::ColorScheme {
+ foreground: data.scheme_normal.foreground,
+ background: data.scheme_normal.background,
+ underline: data.scheme_normal.underline,
+ },
+ scheme_occupied: crate::ColorScheme {
+ foreground: data.scheme_occupied.foreground,
+ background: data.scheme_occupied.background,
+ underline: data.scheme_occupied.underline,
+ },
+ scheme_selected: crate::ColorScheme {
+ foreground: data.scheme_selected.foreground,
+ background: data.scheme_selected.background,
+ underline: data.scheme_selected.underline,
+ },
+ })
+}
+
+fn parse_modkey(s: &str) -> Result<x11rb::protocol::xproto::KeyButMask> {
+ use x11rb::protocol::xproto::KeyButMask;
+
+ match s {
+ "Mod1" => Ok(KeyButMask::MOD1),
+ "Mod2" => Ok(KeyButMask::MOD2),
+ "Mod3" => Ok(KeyButMask::MOD3),
+ "Mod4" => Ok(KeyButMask::MOD4),
+ "Mod5" => Ok(KeyButMask::MOD5),
+ "Shift" => Ok(KeyButMask::SHIFT),
+ "Control" => Ok(KeyButMask::CONTROL),
+ _ => bail!("Invalid modkey: {}", s),
+ }
+}
+
+fn string_to_keycode(s: &str) -> Result<x11rb::protocol::xproto::Keycode> {
+ use crate::keyboard::keycodes;
+
+ match s.to_lowercase().as_str() {
+ "return" => Ok(keycodes::RETURN),
+ "q" => Ok(keycodes::Q),
+ "escape" => Ok(keycodes::ESCAPE),
+ "space" => Ok(keycodes::SPACE),
+ "tab" => Ok(keycodes::TAB),
+ "backspace" => Ok(keycodes::BACKSPACE),
+ "delete" => Ok(keycodes::DELETE),
+
+ // Function keys
+ "f1" => Ok(keycodes::F1),
+ "f2" => Ok(keycodes::F2),
+ "f3" => Ok(keycodes::F3),
+ "f4" => Ok(keycodes::F4),
+ "f5" => Ok(keycodes::F5),
+ "f6" => Ok(keycodes::F6),
+ "f7" => Ok(keycodes::F7),
+ "f8" => Ok(keycodes::F8),
+ "f9" => Ok(keycodes::F9),
+ "f10" => Ok(keycodes::F10),
+ "f11" => Ok(keycodes::F11),
+ "f12" => Ok(keycodes::F12),
+
+ // Letters
+ "a" => Ok(keycodes::A),
+ "b" => Ok(keycodes::B),
+ "c" => Ok(keycodes::C),
+ "d" => Ok(keycodes::D),
+ "e" => Ok(keycodes::E),
+ "f" => Ok(keycodes::F),
+ "g" => Ok(keycodes::G),
+ "h" => Ok(keycodes::H),
+ "i" => Ok(keycodes::I),
+ "j" => Ok(keycodes::J),
+ "k" => Ok(keycodes::K),
+ "l" => Ok(keycodes::L),
+ "m" => Ok(keycodes::M),
+ "n" => Ok(keycodes::N),
+ "o" => Ok(keycodes::O),
+ "p" => Ok(keycodes::P),
+ "r" => Ok(keycodes::R),
+ "s" => Ok(keycodes::S),
+ "t" => Ok(keycodes::T),
+ "u" => Ok(keycodes::U),
+ "v" => Ok(keycodes::V),
+ "w" => Ok(keycodes::W),
+ "x" => Ok(keycodes::X),
+ "y" => Ok(keycodes::Y),
+ "z" => Ok(keycodes::Z),
+
+ // Numbers
+ "0" => Ok(keycodes::KEY_0),
+ "1" => Ok(keycodes::KEY_1),
+ "2" => Ok(keycodes::KEY_2),
+ "3" => Ok(keycodes::KEY_3),
+ "4" => Ok(keycodes::KEY_4),
+ "5" => Ok(keycodes::KEY_5),
+ "6" => Ok(keycodes::KEY_6),
+ "7" => Ok(keycodes::KEY_7),
+ "8" => Ok(keycodes::KEY_8),
+ "9" => Ok(keycodes::KEY_9),
+
+ // Arrows
+ "left" => Ok(keycodes::LEFT),
+ "right" => Ok(keycodes::RIGHT),
+ "up" => Ok(keycodes::UP),
+ "down" => Ok(keycodes::DOWN),
+ "home" => Ok(keycodes::HOME),
+ "end" => Ok(keycodes::END),
+ "pageup" => Ok(keycodes::PAGE_UP),
+ "pagedown" => Ok(keycodes::PAGE_DOWN),
+ "insert" => Ok(keycodes::INSERT),
+
+ // Symbols
+ "minus" | "-" => Ok(keycodes::MINUS),
+ "equal" | "=" => Ok(keycodes::EQUAL),
+ "bracketleft" | "[" => Ok(keycodes::LEFT_BRACKET),
+ "bracketright" | "]" => Ok(keycodes::RIGHT_BRACKET),
+ "semicolon" | ";" => Ok(keycodes::SEMICOLON),
+ "apostrophe" | "'" => Ok(keycodes::APOSTROPHE),
+ "grave" | "`" => Ok(keycodes::GRAVE),
+ "backslash" | "\\" => Ok(keycodes::BACKSLASH),
+ "comma" | "," => Ok(keycodes::COMMA),
+ "period" | "." => Ok(keycodes::PERIOD),
+ "slash" | "/" => Ok(keycodes::SLASH),
+
+ _ => bail!("Unknown key: {}", s),
+ }
+}
+
+fn parse_key_action(s: &str) -> Result<crate::keyboard::KeyAction> {
+ use crate::keyboard::KeyAction;
+
+ match s {
+ "Spawn" => Ok(KeyAction::Spawn),
+ "KillClient" => Ok(KeyAction::KillClient),
+ "FocusStack" => Ok(KeyAction::FocusStack),
+ "Quit" => Ok(KeyAction::Quit),
+ "Restart" => Ok(KeyAction::Restart),
+ "ViewTag" => Ok(KeyAction::ViewTag),
+ "MoveToTag" => Ok(KeyAction::MoveToTag),
+ "ToggleGaps" => Ok(KeyAction::ToggleGaps),
+ "ToggleFullScreen" => Ok(KeyAction::ToggleFullScreen),
+ "ToggleFloating" => Ok(KeyAction::ToggleFloating),
+ _ => bail!("Unknown action: {}", s),
+ }
+}
+
+fn arg_data_to_arg(data: ArgData) -> Result<crate::keyboard::Arg> {
+ use crate::keyboard::Arg;
+
+ match data {
+ ArgData::None => Ok(Arg::None),
+ ArgData::String(s) => {
+ let static_str: &'static str = Box::leak(s.into_boxed_str());
+ Ok(Arg::Str(static_str))
+ }
+ ArgData::Int(n) => Ok(Arg::Int(n)),
+ ArgData::Array(arr) => {
+ let static_strs: Vec<&'static str> = arr
+ .into_iter()
+ .map(|s| Box::leak(s.into_boxed_str()) as &'static str)
+ .collect();
+ let static_slice: &'static [&'static str] = Box::leak(static_strs.into_boxed_slice());
+ Ok(Arg::Array(static_slice))
+ }
+ }
+}
diff --git a/src/default_config.rs b/src/default_config.rs
deleted file mode 100644
index 98efd35..0000000
--- a/src/default_config.rs
+++ /dev/null
@@ -1,191 +0,0 @@
-use crate::bar::{BlockCommand, BlockConfig};
-use crate::keyboard::handlers::Key;
-use crate::keyboard::{Arg, KeyAction, keycodes};
-use x11rb::protocol::xproto::KeyButMask;
-
-// ========================================
-// APPEARANCE
-// ========================================
-pub const BORDER_WIDTH: u32 = 2;
-pub const BORDER_FOCUSED: u32 = 0x6dade3;
-pub const BORDER_UNFOCUSED: u32 = 0xbbbbbb;
-pub const FONT: &str = "JetBrainsMono Nerd Font:style=Bold:size=12";
-
-// ========================================
-// GAPS (Vanity Gaps)
-// ========================================
-pub const GAPS_ENABLED: bool = false;
-pub const GAP_INNER_HORIZONTAL: u32 = 3;
-pub const GAP_INNER_VERTICAL: u32 = 3;
-pub const GAP_OUTER_HORIZONTAL: u32 = 3;
-pub const GAP_OUTER_VERTICAL: u32 = 3;
-//
-// ========================================
-// DEFAULTS
-// ========================================
-pub const TERMINAL: &str = "st";
-pub const XCLOCK: &str = "xclock";
-pub const MODKEY: KeyButMask = KeyButMask::MOD4;
-
-// ========================================
-// BAR COLORS
-// ========================================
-
-// Base colors
-const GRAY_DARK: u32 = 0x1a1b26;
-const GRAY_SEP: u32 = 0xa9b1d6;
-const GRAY_MID: u32 = 0x444444;
-const GRAY_LIGHT: u32 = 0xbbbbbb;
-const CYAN: u32 = 0x0db9d7;
-const MAGENTA: u32 = 0xad8ee6;
-const RED: u32 = 0xf7768e;
-const GREEN: u32 = 0x9ece6a;
-const BLUE: u32 = 0x7aa2f7;
-const YELLOW: u32 = 0xe0af68;
-
-pub struct ColorScheme {
- pub foreground: u32,
- pub background: u32,
- pub underline: u32,
-}
-
-pub const SCHEME_NORMAL: ColorScheme = ColorScheme {
- foreground: GRAY_LIGHT,
- background: GRAY_DARK,
- underline: GRAY_MID,
-};
-
-pub const SCHEME_OCCUPIED: ColorScheme = ColorScheme {
- foreground: CYAN,
- background: GRAY_DARK,
- underline: CYAN,
-};
-
-pub const SCHEME_SELECTED: ColorScheme = ColorScheme {
- foreground: CYAN,
- background: GRAY_DARK,
- underline: MAGENTA,
-};
-
-// ========================================
-// Commands
-// ========================================
-const SCREENSHOT_CMD: &[&str] = &[
- "sh",
- "-c",
- "maim -s | xclip -selection clipboard -t image/png",
-];
-
-const DMENU_CMD: &[&str] = &["sh", "-c", "dmenu_run -l 10"];
-
-// ========================================
-// TAGS
-// ========================================
-pub const TAG_COUNT: usize = 9;
-pub const TAGS: [&str; TAG_COUNT] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"];
-//pub const TAGS: [&str; TAG_COUNT] = ["", "", "", "", "", "", "", "", ""];
-// pub const TAGS: [&str; TAG_COUNT] = [
-// "DEV", "WWW", "SYS", "DOC", "VBOX", "CHAT", "MUS", "VID", "MISC",
-// ];
-
-// ========================================
-// KEYBINDINGS
-// ========================================
-#[rustfmt::skip]
-pub const KEYBINDINGS: &[Key] = &[
- Key::new(&[MODKEY], keycodes::RETURN, KeyAction::Spawn, Arg::Str(TERMINAL)),
- Key::new(&[MODKEY], keycodes::F, KeyAction::Spawn, Arg::Str(XCLOCK)),
-
- Key::new(&[MODKEY], keycodes::S, KeyAction::Spawn, Arg::Array(SCREENSHOT_CMD)),
- Key::new(&[MODKEY], keycodes::D, KeyAction::Spawn, Arg::Array(DMENU_CMD)),
- Key::new(&[MODKEY], keycodes::Q, KeyAction::KillClient, Arg::None),
- Key::new(&[MODKEY, SHIFT], keycodes::F, KeyAction::ToggleFullScreen, Arg::None),
- Key::new(&[MODKEY, SHIFT], keycodes::SPACE, KeyAction::ToggleFloating, Arg::None),
- Key::new(&[MODKEY], keycodes::A, KeyAction::ToggleGaps, Arg::None),
- Key::new(&[MODKEY, SHIFT], keycodes::Q, KeyAction::Quit, Arg::None),
- Key::new(&[MODKEY, SHIFT], keycodes::R, KeyAction::Restart, Arg::None),
- Key::new(&[MODKEY], keycodes::J, KeyAction::FocusStack, Arg::Int(-1)),
- Key::new(&[MODKEY], keycodes::K, KeyAction::FocusStack, Arg::Int(1)),
-
- Key::new(&[MODKEY], keycodes::KEY_1, KeyAction::ViewTag, Arg::Int(0)),
- Key::new(&[MODKEY], keycodes::KEY_2, KeyAction::ViewTag, Arg::Int(1)),
- Key::new(&[MODKEY], keycodes::KEY_3, KeyAction::ViewTag, Arg::Int(2)),
- Key::new(&[MODKEY], keycodes::KEY_4, KeyAction::ViewTag, Arg::Int(3)),
- Key::new(&[MODKEY], keycodes::KEY_5, KeyAction::ViewTag, Arg::Int(4)),
- Key::new(&[MODKEY], keycodes::KEY_6, KeyAction::ViewTag, Arg::Int(5)),
- Key::new(&[MODKEY], keycodes::KEY_7, KeyAction::ViewTag, Arg::Int(6)),
- Key::new(&[MODKEY], keycodes::KEY_8, KeyAction::ViewTag, Arg::Int(7)),
- Key::new(&[MODKEY], keycodes::KEY_9, KeyAction::ViewTag, Arg::Int(8)),
-
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_1, KeyAction::MoveToTag, Arg::Int(0)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_2, KeyAction::MoveToTag, Arg::Int(1)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_3, KeyAction::MoveToTag, Arg::Int(2)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_4, KeyAction::MoveToTag, Arg::Int(3)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_5, KeyAction::MoveToTag, Arg::Int(4)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_6, KeyAction::MoveToTag, Arg::Int(5)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_7, KeyAction::MoveToTag, Arg::Int(6)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_8, KeyAction::MoveToTag, Arg::Int(7)),
- Key::new(&[MODKEY, SHIFT], keycodes::KEY_9, KeyAction::MoveToTag, Arg::Int(8)),
-];
-
-// ========================================
-// STATUS BAR BLOCKS
-// ========================================
-pub const STATUS_BLOCKS: &[BlockConfig] = &[
- BlockConfig {
- format: "",
- command: BlockCommand::Battery {
- format_charging: " Bat: {}%",
- format_discharging: " Bat:{}%",
- format_full: " Bat: {}%",
- },
- interval_secs: 30,
- color: GREEN,
- underline: true,
- },
- BlockConfig {
- format: " │ ",
- command: BlockCommand::Static(""),
- interval_secs: u64::MAX,
- color: GRAY_SEP,
- underline: false,
- },
- BlockConfig {
- format: " {used}/{total} GB",
- command: BlockCommand::Ram,
- interval_secs: 5,
- color: BLUE,
- underline: true,
- },
- BlockConfig {
- format: " │ ",
- command: BlockCommand::Static(""),
- interval_secs: u64::MAX,
- color: GRAY_SEP,
- underline: false,
- },
- BlockConfig {
- format: " {}",
- command: BlockCommand::Shell("uname -r"),
- interval_secs: u64::MAX,
- color: RED,
- underline: true,
- },
- BlockConfig {
- format: " │ ",
- command: BlockCommand::Static(""),
- interval_secs: u64::MAX,
- color: GRAY_SEP,
- underline: false,
- },
- BlockConfig {
- format: " {}",
- command: BlockCommand::DateTime("%a, %b %d - %-I:%M %P"),
- interval_secs: 1,
- color: CYAN,
- underline: true,
- },
-];
-
-const SHIFT: KeyButMask = KeyButMask::SHIFT;
-pub const WM_BINARY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/target/release/oxwm");
diff --git a/src/lib.rs b/src/lib.rs
index 5a16b85..2da26e3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,5 @@
pub mod bar;
+pub mod config;
pub mod errors;
pub mod keyboard;
pub mod layout;
@@ -67,7 +68,7 @@ impl Default for Config {
border_width: 2,
border_focused: 0x6dade3,
border_unfocused: 0xbbbbbb,
- font: "monospace:size=12".to_string(),
+ font: "monospace:size=10".to_string(),
gaps_enabled: false,
gap_inner_horizontal: 0,
gap_inner_vertical: 0,
diff --git a/src/main.rs.backup b/src/main.rs.backup
deleted file mode 100644
index 2e3a92e..0000000
--- a/src/main.rs.backup
+++ /dev/null
@@ -1,23 +0,0 @@
-use anyhow::Result;
-mod bar;
-mod config;
-mod keyboard;
-mod layout;
-mod window_manager;
-
-fn main() -> Result<()> {
- let args: Vec<String> = std::env::args().collect();
-
- let mut window_manager = window_manager::WindowManager::new()?;
- let should_restart = window_manager.run()?;
-
- drop(window_manager);
-
- if should_restart {
- use std::os::unix::process::CommandExt;
- let err = std::process::Command::new(&args[0]).args(&args[1..]).exec();
- eprintln!("Failed to restart: {}", err);
- }
-
- Ok(())
-}
diff --git a/templates/config.ron b/templates/config.ron
new file mode 100644
index 0000000..0660274
--- /dev/null
+++ b/templates/config.ron
@@ -0,0 +1,66 @@
+#![enable(implicit_some)]
+// OXWM Configuration File
+// Edit this file and reload with Mod+Shift+R (no compilation needed!)
+
+(
+ border_width: 2,
+ border_focused: 0x6dade3,
+ border_unfocused: 0xbbbbbb,
+ 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",
+ modkey: "Mod4",
+
+ tags: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
+
+ keybindings: [
+ (modifiers: ["Mod4"], key: "return", action: "Spawn", arg: "st"),
+ (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"),
+ (modifiers: ["Mod4", "Shift"], key: "f", action: "ToggleFullScreen"),
+ (modifiers: ["Mod4", "Shift"], key: "space", action: "ToggleFloating"),
+ (modifiers: ["Mod4"], key: "a", action: "ToggleGaps"),
+ (modifiers: ["Mod4", "Shift"], key: "q", action: "Quit"),
+ (modifiers: ["Mod4", "Shift"], key: "r", action: "Restart"),
+ (modifiers: ["Mod4"], key: "j", action: "FocusStack", arg: -1),
+ (modifiers: ["Mod4"], key: "k", action: "FocusStack", arg: 1),
+ (modifiers: ["Mod4"], key: "1", action: "ViewTag", arg: 0),
+ (modifiers: ["Mod4"], key: "2", action: "ViewTag", arg: 1),
+ (modifiers: ["Mod4"], key: "3", action: "ViewTag", arg: 2),
+ (modifiers: ["Mod4"], key: "4", action: "ViewTag", arg: 3),
+ (modifiers: ["Mod4"], key: "5", action: "ViewTag", arg: 4),
+ (modifiers: ["Mod4"], key: "6", action: "ViewTag", arg: 5),
+ (modifiers: ["Mod4"], key: "7", action: "ViewTag", arg: 6),
+ (modifiers: ["Mod4"], key: "8", action: "ViewTag", arg: 7),
+ (modifiers: ["Mod4"], key: "9", action: "ViewTag", arg: 8),
+ (modifiers: ["Mod4", "Shift"], key: "1", action: "MoveToTag", arg: 0),
+ (modifiers: ["Mod4", "Shift"], key: "2", action: "MoveToTag", arg: 1),
+ (modifiers: ["Mod4", "Shift"], key: "3", action: "MoveToTag", arg: 2),
+ (modifiers: ["Mod4", "Shift"], key: "4", action: "MoveToTag", arg: 3),
+ (modifiers: ["Mod4", "Shift"], key: "5", action: "MoveToTag", arg: 4),
+ (modifiers: ["Mod4", "Shift"], key: "6", action: "MoveToTag", arg: 5),
+ (modifiers: ["Mod4", "Shift"], key: "7", action: "MoveToTag", arg: 6),
+ (modifiers: ["Mod4", "Shift"], key: "8", action: "MoveToTag", arg: 7),
+ (modifiers: ["Mod4", "Shift"], key: "9", action: "MoveToTag", arg: 8),
+ ],
+
+ status_blocks: [
+ (format: " {used}/{total} GB", command: "Ram", interval_secs: 5, color: 0x7aa2f7, underline: true),
+ (format: " │ ", command: "Static", interval_secs: 18446744073709551615, color: 0xa9b1d6, underline: false),
+ (format: " {}", 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),
+ ],
+
+ scheme_normal: (foreground: 0xbbbbbb, background: 0x1a1b26, underline: 0x444444),
+ scheme_occupied: (foreground: 0x0db9d7, background: 0x1a1b26, underline: 0x0db9d7),
+ scheme_selected: (foreground: 0x0db9d7, background: 0x1a1b26, underline: 0xad8ee6),
+)
+