Skip to content

NullSector-dev/oxwm

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

150 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

./images/oxwm1.png

Table of Contents

OXWM — DWM but Better (and oxidized)

A dynamic window manager written in Rust, inspired by dwm but designed to evolve on its own. Configuration is done in Rust source code, keeping with the suckless philosophy of “edit + recompile.”, but with sane defaults and no arbitrary elitism enforcing bad variable names and bad file structure topology.

Installation

NixOS (with Flakes)

Adding OXWM to your flake inputs

Add oxwm to your flake.nix inputs:

{
  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
      ];
    };
  };
}

Enabling OXWM in configuration.nix

Add this to your configuration.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.
}

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.ron. Edit it and reload with Mod+Shift+R.

Advanced: Using a specific oxwm version

If you want to pin or customize the oxwm package:

{
  services.xserver.windowManager.oxwm = {
    enable = true;
    # Use a specific version or build with custom options
    package = oxwm.packages.${pkgs.system}.default;
  };
}

Development setup with Nix

For development, use the provided dev shell:

# 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

Arch Linux

Install dependencies:

sudo pacman -S rust cargo libx11 libxft freetype2 fontconfig pkg-config

Building from Source

git clone https://github.com/tonybanters/oxwm
cd oxwm
cargo build --release
sudo cp target/release/oxwm /usr/local/bin/

Or use the justfile:

just install

Setting up OXWM

Without a display manager (startx)

Add the following to your ~/.xinitrc:

exec oxwm

Then start X with:

startx

With a display manager

If using a display manager (LightDM, GDM, SDDM), OXWM should appear in the session list after installation.

Configuration

OXWM was inspired by dwm, but ditched the suckless philosophy. This philosophy quite literally discourages users from using the software for the sake of ‘elitism’. I find that quite nonsensical, so I went ahead and created this project to be user friendly. The configuration is done by editing ~/.config/oxwm/config.ron and the binary can be reloaded with a hotkey (Super+Shift+R by efault).

Edit ~/.config/oxwm/config.ron to customize:

  • Keybindings
  • Colors and appearance
  • Status bar blocks
  • Gaps and borders
  • Terminal and applications

After making changes, reload OXWM with Mod+Shift+R

Contributing

When contributing to OXWM:

  1. Never commit your personal ~/.config/oxwm/config.ron
  2. Only modify templates/config.ron 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):

BindingAction
Super+ReturnSpawn terminal
Super+J/KCycle focus down/up
Super+QKill focused window
Super+Shift+QQuit WM
Super+Shift+RHot reload WM
Super+1-9View tag 1-9
Super+Shift+1-9Move window to tag 1-9
Super+SScreenshot (maim)
Super+Ddmenu launcher
Super+AToggle gaps
Super+Shift+FToggle fullscreen
Super+Shift+SpaceToggle 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

Testing with Xephyr

Test OXWM in a nested X server without affecting your current session:

just test

This starts Xephyr on display :1 and launches OXWM inside it.

Or manually:

Xephyr -screen 1280x800 :1 &
DISPLAY=:1 cargo run

Project Structure

src/
├── main.rs
│   └── main()
│       └── Creates WindowManager and calls .run()
│
├── window_manager.rs                    [CORE - X11 event handling]
│   ├── struct WindowManager
│   │   ├── connection: RustConnection   [X11 connection]
│   │   ├── windows: Vec<Window>         [All managed windows]
│   │   ├── focused_window: Option<Window>
│   │   ├── layout: Box<dyn Layout>
│   │   ├── window_tags: HashMap<Window, TagMask>
│   │   ├── selected_tags: TagMask
│   │   └── bar: Bar                     [Status bar]
│   │
│   ├── new()                            [Initialize WM, grab root, restore tags, scan windows]
│   ├── run()                            [Main event loop with block updates]
│   ├── handle_event()                   [Route X11 events]
│   │   ├── MapRequest    → add window, apply layout, update bar, save tag
│   │   ├── UnmapNotify   → remove window, update bar
│   │   ├── DestroyNotify → remove window, update bar
│   │   ├── KeyPress      → get action, handle it (includes Restart)
│   │   ├── ButtonPress   → handle bar clicks
│   │   └── Expose        → redraw bar
│   ├── handle_key_action()              [Execute keyboard actions]
│   ├── get_saved_selected_tags()        [Restore selected tags from _NET_CURRENT_DESKTOP]
│   ├── save_selected_tags()             [Persist selected tags to root window]
│   ├── get_saved_tag()                  [Restore window tag from _NET_CLIENT_INFO]
│   ├── save_client_tag()                [Persist window tag to window property]
│   ├── scan_existing_windows()          [Manage windows on startup]
│   ├── remove_window()                  [Remove from Vec, reapply layout]
│   ├── set_focus()                      [Focus window, update visuals]
│   ├── cycle_focus()                    [Move focus to next/prev window]
│   ├── view_tag()                       [Switch to tag/workspace, update visibility]
│   ├── move_to_tag()                    [Move window to tag]
│   ├── update_bar()                     [Calculate occupied tags, redraw bar]
│   ├── update_focus_visuals()           [Set border colors]
│   ├── update_window_visibility()       [Map/unmap windows based on tags]
│   └── apply_layout()                   [Position all windows below bar]
│
├── config.rs                            [CONFIGURATION - all settings here]
│   ├── BORDER_WIDTH, BORDER_FOCUSED, BORDER_UNFOCUSED
│   ├── FONT                             [XFT font string]
│   ├── TAG_COUNT, TAGS                  [Workspace configuration]
│   ├── TERMINAL, MODKEY
│   ├── ColorScheme                      [Foreground, background, border colors]
│   ├── SCHEME_NORMAL, SCHEME_OCCUPIED, SCHEME_SELECTED
│   ├── KEYBINDINGS                      [All keybinds as const array]
│   └── STATUS_BLOCKS                    [Block configurations with format, command, interval]
│
├── 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]
│   │   └── invalidate()                 [Mark bar as needing redraw]
│   ├── font.rs
│   │   ├── struct Font                  [XFT font wrapper]
│   │   ├── struct FontDraw              [XFT drawing context]
│   │   └── 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]
│       └── shell.rs                     [Shell command execution block]
│
├── keyboard/
│   ├── mod.rs                           [Re-exports]
│   ├── keycodes.rs                      [Key constants: Q, J, RETURN, etc]
│   └── handlers.rs
│       ├── enum KeyAction               [Spawn, KillClient, FocusStack, ViewTag, Restart, etc]
│       ├── enum Arg                     [None, Int, Str, Array]
│       ├── struct Key                   [Keybinding definition]
│       ├── setup_keybinds()             [Register keys with X11]
│       └── handle_key_press()           [Parse KeyPressEvent → KeyAction]
│
└── layout/
    ├── mod.rs                           [Layout trait definition]
    └── tiling.rs
        └── TilingLayout::arrange()      [Calculate window positions]

Architecture Notes

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
  • 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.

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 [0/3]

  • [ ] Add keybindings to increase/decrease window size
    • Master area resize (Mod+H / Mod+L)
    • Individual window resize for floating windows
  • [ ] Fix cursor on hover for bar
    • Bar should show pointer cursor on hover
    • Indicate clickable tag areas
  • [ ] Add guess_terminal() function to default config.rs
    • Auto-detect available terminal emulator
    • Priority order: st → alacritty → kitty → wezterm → xterm
    • Fallback to xterm if none found

PRIORITY Low [0/1]

  • [ ] 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, lemonbar)
  • Scratchpad functionality
  • Window minimize/restore

License

GPL v3

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Rust 95.9%
  • Nix 3.1%
  • Other 1.0%