#+AUTHOR: Tony #+STARTUP: overview [[file:./images/oxwm1.png]] * Table of Contents :toc: - [[#oxwm--dwm-but-better-and-oxidized][OXWM — DWM but Better (and oxidized)]] - [[#installation][Installation]] - [[#nixos-with-flakes][NixOS (with Flakes)]] - [[#arch-linux][Arch Linux]] - [[#building-from-source][Building from Source]] - [[#setting-up-oxwm][Setting up OXWM]] - [[#configuration][Configuration]] - [[#contributing][Contributing]] - [[#key-bindings][Key Bindings]] - [[#features][Features]] - [[#testing-with-xephyr][Testing with Xephyr]] - [[#project-structure][Project Structure]] - [[#architecture-notes][Architecture Notes]] - [[#tag-system][Tag System]] - [[#status-bar][Status Bar]] - [[#layout-system][Layout System]] - [[#current-todo-list][Current Todo List:]] - [[#priority-high-02][PRIORITY High]] - [[#priority-medium-14][PRIORITY Medium]] - [[#priority-low-11][PRIORITY Low]] - [[#development-roadmap][Development Roadmap]] - [[#current-focus][Current Focus]] - [[#future-enhancements][Future Enhancements]] - [[#license][License]] * OXWM — DWM but Better (and oxidized) A dynamic window manager written in Rust, inspired by dwm but designed to evolve beyond it. OXWM features a clean, functional Lua API for configuration with hot-reloading support, ditching the suckless philosophy of *"edit + recompile"*. Instead, we focus on lowering friction for users with sane defaults, LSP-powered autocomplete, and instant configuration changes without restarting your X session. * Installation ** NixOS (with Flakes) *** Adding OXWM to your flake inputs Add oxwm to your =flake.nix= inputs: #+begin_src nix { inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; oxwm.url = "github:tonybanters/oxwm"; oxwm.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { self, nixpkgs, oxwm, ... }: { nixosConfigurations.yourhost = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ ./configuration.nix oxwm.nixosModules.default ]; }; }; } #+end_src *** Enabling OXWM in configuration.nix Add this to your =configuration.nix=: #+begin_src nix { config, pkgs, ... }: { services.xserver = { enable = true; windowManager.oxwm.enable = true; }; # Recommended: Install a display manager services.xserver.displayManager.lightdm.enable = true; # Or use another display manager like sddm, gdm, etc. } #+end_src *** Initialize your config After rebuilding your system with =sudo nixos-rebuild switch=, log in via your display manager. On first launch, your initial config file will be automatically created and placed in =~/.config/oxwm/config.lua=. Edit it and reload with =Mod+Shift+R=. *** Advanced: Using a specific oxwm version If you want to pin or customize the oxwm package: #+begin_src nix { services.xserver.windowManager.oxwm = { enable = true; # Use a specific version or build with custom options package = oxwm.packages.${pkgs.system}.default; }; } #+end_src *** Development setup with Nix For development, use the provided dev shell: #+begin_src sh # Clone the repository git clone https://github.com/tonybanters/oxwm cd oxwm # Enter the development environment nix develop # Build and test cargo build just test #+end_src ** Arch Linux AUR: [[https://aur.archlinux.org/packages/oxwm-git][AUR URL]] #+begin_src yay -S oxwm-git #+end_src This will automatically put a desktop session file into your xsessions directory. Manually: Install dependencies: #+begin_src sh sudo pacman -S rust cargo libx11 libxft freetype2 fontconfig pkg-config #+end_src See Build from source ** Building from Source #+begin_src sh git clone https://github.com/tonybanters/oxwm cd oxwm cargo build --release sudo cp target/release/oxwm /usr/local/bin/ #+end_src Or use the justfile: #+begin_src sh just install #+end_src ** Setting up OXWM *** Without a display manager (startx) Add the following to your =~/.xinitrc=: #+begin_src sh exec oxwm #+end_src Then start X with: #+begin_src sh startx #+end_src *** With a display manager If using a display manager (LightDM, GDM, SDDM), OXWM should appear in the session list after installation. * Configuration OXWM uses a clean, functional Lua API for configuration. On first run, a default config is automatically created at =~/.config/oxwm/config.lua=. ** Quick Example Here's what the new functional API looks like: #+begin_src lua -- Set basic options oxwm.set_terminal("st") oxwm.set_modkey("Mod4") oxwm.set_tags({ "1", "2", "3", "4", "5", "6", "7", "8", "9" }) -- Configure borders oxwm.border.set_width(2) oxwm.border.set_focused_color("#6dade3") oxwm.border.set_unfocused_color("#bbbbbb") -- Configure gaps oxwm.gaps.set_enabled(true) oxwm.gaps.set_inner(5, 5) -- horizontal, vertical oxwm.gaps.set_outer(5, 5) -- Set up keybindings oxwm.key.bind({ "Mod4" }, "Return", oxwm.spawn("st")) oxwm.key.bind({ "Mod4" }, "Q", oxwm.client.kill()) oxwm.key.bind({ "Mod4", "Shift" }, "Q", oxwm.quit()) -- Add status bar blocks oxwm.bar.add_block("{}", "DateTime", "%H:%M", 60, "#0db9d7", true) oxwm.bar.add_block("RAM: {used}/{total} GB", "Ram", nil, 5, "#7aa2f7", true) #+end_src ** Features - *Hot-reload*: Changes take effect immediately with =Mod+Shift+R= (no X restart needed) - *LSP Support*: Full autocomplete and type hints for the API (=oxwm.lua= definitions included) - *Functional API*: Clean, discoverable functions instead of nested tables - *No compilation*: Edit and reload instantly ** Key Configuration Areas Edit =~/.config/oxwm/config.lua= to customize: - Basic settings (terminal, modkey, tags) - Borders and colors - Window gaps - Status bar (font, blocks, color schemes) - Keybindings and keychords - Layout symbols - Autostart commands After making changes, reload OXWM with =Mod+Shift+R= ** Creating Your Config Generate the default config: #+begin_src sh oxwm --init #+end_src Or just start OXWM - it will create one automatically on first run. * Contributing When contributing to OXWM: 1. Never commit your personal =~/.config/oxwm/config.lua= 2. Only modify =templates/config.lua= if adding new configuration options 3. Test your changes with =just test= using Xephyr/Xwayland 4. Document any new features or keybindings * Key Bindings Default keybindings (customizable in config.rs): | Binding | Action | |-------------------+-------------------------| | Super+Return | Spawn terminal | | Super+J/K | Cycle focus down/up | | Super+Q | Kill focused window | | Super+Shift+Q | Quit WM | | Super+Shift+R | Hot reload WM | | Super+1-9 | View tag 1-9 | | Super+Shift+1-9 | Move window to tag 1-9 | | Super+S | Screenshot (maim) | | Super+D | dmenu launcher | | Super+A | Toggle gaps | | Super+Shift+F | Toggle fullscreen | | Super+Shift+Space | Toggle floating | * Features - Dynamic tiling layout with master/stack - Tag-based workspaces (9 tags by default) - Configurable gaps between windows - Status bar with modular block system - Battery, RAM, datetime, shell commands - Custom colors and update intervals - Click-to-switch tags - Window focus cycling - Hot reload without restarting X - Persistent window tags across restarts - Mouse hover to focus - Border indicators for focused windows - Fullscreen mode - Move Windows with Super HJKL * Testing with Xephyr Test OXWM in a nested X server without affecting your current session: #+begin_src sh just test #+end_src This starts Xephyr on display :1 and launches OXWM inside it. Or manually: #+begin_src sh Xephyr -screen 1280x800 :1 & DISPLAY=:1 cargo run #+end_src * Project Structure #+begin_src sh src/ ├── bin/ │ └── main.rs [Entry point - handles CLI args, config loading, WM init] │ ├── main() [Parse args, load config, start WM] │ ├── load_config() [Lua config loading with auto-init] │ └── init_config() [Create default config.lua + oxwm.lua] │ ├── lib.rs [Library exports] │ ├── window_manager.rs [CORE - X11 event handling] │ ├── struct WindowManager │ │ ├── connection: RustConnection [X11 connection] │ │ ├── windows: Vec [All managed windows] │ │ ├── focused_window: Option │ │ ├── layout: Box │ │ ├── window_tags: HashMap │ │ ├── selected_tags: TagMask │ │ └── bars: Vec [Status bars (multi-monitor)] │ │ │ ├── new() [Initialize WM, grab root, restore tags, scan windows] │ ├── run() [Main event loop with block updates] │ ├── handle_event() [Route X11 events] │ ├── try_reload_config() [Hot-reload Lua config] │ └── ... [Window/tag/focus management] │ ├── config/ │ ├── mod.rs [Config module exports] │ ├── lua.rs [Lua config parser - loads and executes config.lua] │ └── lua_api.rs [Functional Lua API implementation] │ ├── register_api() [Set up oxwm.* functions in Lua] │ ├── register_spawn() [oxwm.spawn()] │ ├── register_key_module() [oxwm.key.bind(), oxwm.key.chord()] │ ├── register_gaps_module() [oxwm.gaps.*] │ ├── register_border_module() [oxwm.border.*] │ ├── register_client_module() [oxwm.client.*] │ ├── register_layout_module() [oxwm.layout.*] │ ├── register_tag_module() [oxwm.tag.*] │ ├── register_bar_module() [oxwm.bar.*] │ └── register_misc() [oxwm.set_terminal(), etc.] │ ├── bar/ │ ├── mod.rs [Re-exports: Bar, BlockCommand, BlockConfig] │ ├── bar.rs │ │ ├── struct Bar [Status bar window with XFT support] │ │ ├── new() [Create bar X11 window, load font, init blocks] │ │ ├── draw() [Render tags + blocks with underlines] │ │ ├── update_blocks() [Update block content based on intervals] │ │ └── handle_click() [Detect which tag was clicked] │ ├── font.rs │ │ ├── struct Font [XFT font wrapper] │ │ └── draw_text() [Render text with color] │ └── blocks/ │ ├── mod.rs [Block trait, BlockConfig, BlockCommand enum] │ ├── battery.rs [Battery status block] │ ├── datetime.rs [Date/time formatting block] │ ├── ram.rs [RAM usage block] │ ├── shell.rs [Shell command execution block] │ └── static.rs [Static text block] │ ├── keyboard/ │ ├── mod.rs [Re-exports] │ ├── keysyms.rs [X11 keysym constants] │ └── handlers.rs │ ├── enum KeyAction [All keyboard actions] │ ├── enum Arg [Action arguments] │ ├── struct KeyBinding [Keybinding + keychord support] │ └── struct KeyPress [Individual key press in chord] │ ├── layout/ │ ├── mod.rs [Layout trait definition] │ └── tiling.rs [Tiling layout implementation] │ └── errors.rs [Error types: WmError, ConfigError, etc.] templates/ ├── config.lua [Default config with functional API] └── oxwm.lua [LSP type definitions for autocomplete] #+end_src * Architecture Notes ** Lua Configuration System OXWM uses mlua to embed a Lua interpreter. The functional API is implemented in =src/config/lua_api.rs=: - Each API function (e.g., =oxwm.border.set_width()=) is registered as a Lua function - Functions modify a shared ConfigBuilder that accumulates settings - When config execution completes, the builder produces the final Config struct - Type definitions in =templates/oxwm.lua= provide LSP autocomplete and documentation ** Tag System Tags are implemented as bitmasks (TagMask = u32), allowing windows to belong to multiple tags simultaneously. Each window has an associated TagMask stored in a HashMap. Tags persist across WM restarts using X11 properties (_NET_CURRENT_DESKTOP for selected tags, _NET_CLIENT_INFO for per-window tags). ** Status Bar The bar uses a performance-optimized approach with a modular block system: - Only redraws when invalidated - Pre-calculates tag widths on creation - Blocks update independently based on their configured intervals - Supports custom colors and underline indicators - Color schemes (normal/occupied/selected) control tag appearance - Easily extensible - add new block types in src/bar/blocks/ ** Layout System The tiling layout divides the screen into a master area (left half) and stack area (right half). The master window occupies the full height of the master area, while stack windows split the stack area vertically. Gaps are configurable and can be toggled at runtime. * TODO Current Todo List: ** PRIORITY High [0/2] - [ ] Convert keycodes to keysyms for cross-keyboard compatibility - Current hardcoded keycodes only work on specific keyboards - Need to use XKeysymToKeycode() for runtime conversion - Follow DWM's approach: keysym → keycode conversion - [ ] Fix fullscreen to persist across tags - Fullscreen state should be maintained when switching tags - Window should remain fullscreen when returning to its tag ** PRIORITY Medium [2/4] - [ ] Add keybindings to increase/decrease window size - Master area resize (Mod+H / Mod+L) - Individual window resize for floating windows - [X] Keychords - Implemented with =oxwm.key.chord()= in the functional API - Example: =oxwm.key.chord({ { {"Mod4"}, "Space" }, { {}, "T" } }, oxwm.spawn("st"))= - [X] Fix cursor on hover for bar - Bar should show pointer cursor on hover - Indicate clickable tag areas - [ ] Add auto-detect terminal to default config - Auto-detect available terminal emulator - Priority order: st → alacritty → kitty → wezterm → xterm - Fallback to xterm if none found ** PRIORITY Low [1/1] - [X] Create AUR package - Write PKGBUILD - Submit to AUR - Add installation instructions to README * Development Roadmap ** Current Focus - Multi-monitor support - Additional layouts (monocle, floating) - Master area resizing - Window swapping in layout ** Future Enhancements - Per-window floating behavior - Per-program rules (auto-tag assignment, floating rules) - External bar support (polybar, etc) - Scratchpad functionality - Window minimize/restore * License [[https://www.gnu.org/licenses/gpl-3.0.en.html][GPL v3]]