oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
18,259 bytes raw
1
#+AUTHOR: Tony
2
#+STARTUP: overview
3
4
[[file:./images/oxwm1.png]]
5
6
* Table of Contents :toc:
7
- [[#oxwm--dwm-but-better-and-oxidized][OXWM — DWM but Better (and oxidized)]]
8
- [[#installation][Installation]]
9
  - [[#nixos-with-flakes][NixOS (with Flakes)]]
10
  - [[#arch-linux][Arch Linux]]
11
  - [[#building-from-source][Building from Source]]
12
  - [[#setting-up-oxwm][Setting up OXWM]]
13
- [[#configuration][Configuration]]
14
  - [[#quick-example][Quick Example]]
15
  - [[#features][Features]]
16
  - [[#key-configuration-areas][Key Configuration Areas]]
17
  - [[#creating-your-config][Creating Your Config]]
18
- [[#contributing][Contributing]]
19
- [[#key-bindings][Key Bindings]]
20
- [[#features-1][Features]]
21
- [[#testing-with-xephyr][Testing with Xephyr]]
22
- [[#project-structure][Project Structure]]
23
- [[#architecture-notes][Architecture Notes]]
24
  - [[#lua-configuration-system][Lua Configuration System]]
25
  - [[#tag-system][Tag System]]
26
  - [[#status-bar][Status Bar]]
27
  - [[#layout-system][Layout System]]
28
- [[#current-todo-list][Current Todo List:]]
29
  - [[#priority-high-04][PRIORITY High]]
30
- [[#development-roadmap][Development Roadmap]]
31
  - [[#current-focus-v080][Current Focus (v0.8.0)]]
32
  - [[#completed-features-88][Completed Features]]
33
  - [[#future-enhancements-][Future Enhancements]]
34
- [[#license][License]]
35
36
* OXWM — DWM but Better (and oxidized)
37
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.
38
39
*Documentation:* [[https://ox-docs.vercel.app/][ox-docs.vercel.app]]
40
41
* Installation
42
** NixOS (with Flakes)
43
*** Adding OXWM to your flake inputs
44
Add oxwm to your =flake.nix= inputs:
45
46
#+begin_src nix
47
{
48
  inputs = {
49
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
50
    oxwm.url = "github:tonybanters/oxwm";
51
    oxwm.inputs.nixpkgs.follows = "nixpkgs";
52
  };
53
54
  outputs = { self, nixpkgs, oxwm, ... }: {
55
    nixosConfigurations.yourhost = nixpkgs.lib.nixosSystem {
56
      system = "x86_64-linux";
57
      modules = [
58
        ./configuration.nix
59
        oxwm.nixosModules.default
60
      ];
61
    };
62
  };
63
}
64
#+end_src
65
66
*** Enabling OXWM in configuration.nix
67
Add this to your =configuration.nix=:
68
69
#+begin_src nix
70
{ config, pkgs, ... }:
71
72
{
73
  services.xserver = {
74
    enable = true;
75
    
76
    windowManager.oxwm.enable = true;
77
  };
78
79
  # Recommended: Install a display manager
80
  services.xserver.displayManager.lightdm.enable = true;
81
  # Or use another display manager like sddm, gdm, etc.
82
}
83
#+end_src
84
85
*** Initialize your config
86
After rebuilding your system with =sudo nixos-rebuild switch=, log in via your display manager.
87
88
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=.
89
90
*** Advanced: Using a specific oxwm version
91
If you want to pin or customize the oxwm package:
92
93
#+begin_src nix
94
{
95
  services.xserver.windowManager.oxwm = {
96
    enable = true;
97
    # Use a specific version or build with custom options
98
    package = oxwm.packages.${pkgs.system}.default;
99
  };
100
}
101
#+end_src
102
103
*** Development setup with Nix
104
For development, use the provided dev shell:
105
106
#+begin_src sh
107
# Clone the repository
108
git clone https://github.com/tonybanters/oxwm
109
cd oxwm
110
111
# Enter the development environment
112
nix develop
113
114
# Build and test
115
cargo build
116
just test
117
#+end_src
118
119
** Arch Linux
120
121
AUR: [[https://aur.archlinux.org/packages/oxwm-git][AUR URL]]
122
#+begin_src
123
yay -S oxwm-git
124
#+end_src
125
This will automatically put a desktop session file into your xsessions directory.
126
127
Manually:
128
Install dependencies:
129
#+begin_src sh
130
sudo pacman -S rust cargo libx11 libxft freetype2 fontconfig pkg-config
131
#+end_src
132
See Build from source
133
134
** Building from Source
135
#+begin_src sh
136
git clone https://github.com/tonybanters/oxwm
137
cd oxwm
138
cargo build --release
139
sudo cp target/release/oxwm /usr/local/bin/
140
#+end_src
141
142
Or use the justfile:
143
#+begin_src sh
144
just install
145
#+end_src
146
147
** Setting up OXWM
148
*** Without a display manager (startx)
149
Add the following to your =~/.xinitrc=:
150
#+begin_src sh
151
exec oxwm
152
#+end_src
153
154
Then start X with:
155
#+begin_src sh
156
startx
157
#+end_src
158
159
*** With a display manager
160
If using a display manager (LightDM, GDM, SDDM), OXWM should appear in the session list after installation.
161
162
* Configuration
163
OXWM uses a clean, functional Lua API for configuration. On first run, a default config is automatically created at =~/.config/oxwm/config.lua=.
164
165
** Quick Example
166
Here's what the new functional API looks like:
167
168
#+begin_src lua
169
-- Set basic options
170
oxwm.set_terminal("st")
171
oxwm.set_modkey("Mod4")
172
oxwm.set_tags({ "1", "2", "3", "4", "5", "6", "7", "8", "9" })
173
174
-- Configure borders
175
oxwm.border.set_width(2)
176
oxwm.border.set_focused_color("#6dade3")
177
oxwm.border.set_unfocused_color("#bbbbbb")
178
179
-- Configure gaps
180
oxwm.gaps.set_enabled(true)
181
oxwm.gaps.set_inner(5, 5)  -- horizontal, vertical
182
oxwm.gaps.set_outer(5, 5)
183
184
-- Set up keybindings
185
oxwm.key.bind({ "Mod4" }, "Return", oxwm.spawn("st"))
186
oxwm.key.bind({ "Mod4" }, "Q", oxwm.client.kill())
187
oxwm.key.bind({ "Mod4", "Shift" }, "Q", oxwm.quit())
188
189
-- Add status bar blocks
190
oxwm.bar.set_blocks({
191
    oxwm.bar.block.datetime({
192
        format = "{}",
193
        date_format = "%H:%M",
194
        interval = 60,
195
        color = "#0db9d7",
196
        underline = true,
197
    }),
198
    oxwm.bar.block.ram({
199
        format = "RAM: {used}/{total} GB",
200
        interval = 5,
201
        color = "#7aa2f7",
202
        underline = true,
203
    }),
204
})
205
#+end_src
206
207
** Features
208
- *Hot-reload*: Changes take effect immediately with =Mod+Shift+R= (no X restart needed)
209
- *LSP Support*: Full autocomplete and type hints for the API (=oxwm.lua= definitions included)
210
- *Functional API*: Clean, discoverable functions instead of nested tables
211
- *No compilation*: Edit and reload instantly
212
213
** Key Configuration Areas
214
Edit =~/.config/oxwm/config.lua= to customize:
215
- Basic settings (terminal, modkey, tags)
216
- Borders and colors
217
- Window gaps
218
- Status bar (font, blocks, color schemes)
219
- Keybindings and keychords
220
- Layout symbols
221
- Autostart commands
222
223
After making changes, reload OXWM with =Mod+Shift+R=
224
225
** Creating Your Config
226
Generate the default config:
227
#+begin_src sh
228
oxwm --init
229
#+end_src
230
231
Or just start OXWM - it will create one automatically on first run. 
232
233
* Contributing
234
When contributing to OXWM:
235
236
1. Never commit your personal =~/.config/oxwm/config.lua=
237
2. Only modify =templates/config.lua= if adding new configuration options
238
3. Test your changes with =just test= using Xephyr/Xwayland
239
4. Document any new features or keybindings
240
241
* Key Bindings
242
Default keybindings (fully customizable in =~/.config/oxwm/config.lua=):
243
244
| Binding                | Action                            |
245
|------------------------+-----------------------------------|
246
| Super+Return           | Spawn terminal                    |
247
| Super+J/K              | Cycle focus through stack         |
248
| Super+Q                | Kill focused window               |
249
| Super+Shift+Q          | Quit WM                           |
250
| Super+Shift+R          | Hot reload WM                     |
251
| Super+1-9              | View tag 1-9                      |
252
| Super+Shift+1-9        | Move window to tag 1-9            |
253
| Super+Ctrl+1-9         | Toggle tag view (multi-tag)       |
254
| Super+Ctrl+Shift+1-9   | Toggle window tag (sticky)        |
255
| Super+S                | Screenshot (maim)                 |
256
| Super+D                | dmenu launcher                    |
257
| Super+A                | Toggle gaps                       |
258
| Super+Shift+F          | Toggle fullscreen                 |
259
| Super+Shift+Space      | Toggle floating                   |
260
| Super+F                | Set normie (floating) layout      |
261
| Super+C                | Set tiling layout                 |
262
| Super+N                | Cycle layouts                     |
263
| Super+Comma/Period     | Focus prev/next monitor           |
264
| Super+Shift+Comma/.    | Send window to prev/next monitor  |
265
| Super+[/]              | Decrease/increase master area     |
266
| Super+I/P              | Inc/dec number of master windows  |
267
| Super+Shift+/          | Show keybinds overlay             |
268
| Super+Button1 (drag)   | Move window (floating)            |
269
| Super+Button3 (drag)   | Resize window (floating)          |
270
271
* Features
272
- *Dynamic Tiling Layout* with adjustable master/stack split
273
  - Master area resizing (mfact)
274
  - Multiple master windows support (nmaster)
275
- *Tag-Based Workspaces* (9 tags by default)
276
  - Multi-tag viewing (see multiple tags at once)
277
  - Sticky windows (window visible on multiple tags)
278
- *Multiple Layouts*
279
  - Tiling (master/stack)
280
  - Normie (floating-by-default)
281
  - Monocle (fullscreen stacking)
282
  - Grid (equal-sized grid)
283
  - Tabbed (tabbed windows)
284
- *Lua Configuration System*
285
  - Hot reload without restarting X (=Mod+Shift+R=)
286
  - LSP support with type definitions and autocomplete
287
  - No compilation needed - instant config changes
288
- *Built-in Status Bar* with modular block system
289
  - Battery, RAM, datetime, shell commands, static text
290
  - Custom colors, update intervals, and underlines
291
  - Click-to-switch tags
292
  - Multi-monitor support (one bar per monitor)
293
- *Advanced Window Management*
294
  - Window focus cycling through stack
295
  - Fullscreen mode
296
  - Floating window support
297
  - Mouse hover to focus (follow mouse)
298
  - Border indicators for focused windows
299
  - Configurable gaps (smartgaps support)
300
  - Window rules (auto-tag, auto-float by class/title)
301
- *Multi-Monitor Support*
302
  - RandR multi-monitor detection
303
  - Independent tags per monitor
304
  - Move windows between monitors
305
- *Keychord Support*
306
  - Multi-key sequences (Emacs/Vim style)
307
  - Example: =Mod+Space= then =T= to spawn terminal
308
- *Persistent State*
309
  - Window tags persist across WM restarts
310
  - Uses X11 properties for state storage
311
312
* Testing with Xephyr
313
Test OXWM in a nested X server without affecting your current session:
314
315
#+begin_src sh
316
just test
317
#+end_src
318
319
This starts Xephyr on display :1 and launches OXWM inside it.
320
321
Or manually:
322
#+begin_src sh
323
Xephyr -screen 1280x800 :1 &
324
DISPLAY=:1 cargo run
325
#+end_src
326
327
* Project Structure
328
#+begin_src sh
329
src/
330
├── bin/
331
│   └── main.rs                          [Entry point - handles CLI args, config loading, WM init]
332
│       ├── main()                       [Parse args, load config, start WM]
333
│       ├── load_config()                [Lua config loading with auto-init]
334
│       └── init_config()                [Create default config.lua + oxwm.lua]
335
336
├── lib.rs                               [Library exports]
337
338
├── window_manager.rs                    [CORE - X11 event handling]
339
│   ├── struct WindowManager
340
│   │   ├── connection: RustConnection   [X11 connection]
341
│   │   ├── windows: Vec<Window>         [All managed windows]
342
│   │   ├── focused_window: Option<Window>
343
│   │   ├── layout: Box<dyn Layout>
344
│   │   ├── window_tags: HashMap<Window, TagMask>
345
│   │   ├── selected_tags: TagMask
346
│   │   └── bars: Vec<Bar>               [Status bars (multi-monitor)]
347
│   │
348
│   ├── new()                            [Initialize WM, grab root, restore tags, scan windows]
349
│   ├── run()                            [Main event loop with block updates]
350
│   ├── handle_event()                   [Route X11 events]
351
│   ├── try_reload_config()              [Hot-reload Lua config]
352
│   └── ...                              [Window/tag/focus management]
353
354
├── config/
355
│   ├── mod.rs                           [Config module exports]
356
│   ├── lua.rs                           [Lua config parser - loads and executes config.lua]
357
│   └── lua_api.rs                       [Functional Lua API implementation]
358
│       ├── register_api()               [Set up oxwm.* functions in Lua]
359
│       ├── register_spawn()             [oxwm.spawn()]
360
│       ├── register_key_module()        [oxwm.key.bind(), oxwm.key.chord()]
361
│       ├── register_gaps_module()       [oxwm.gaps.*]
362
│       ├── register_border_module()     [oxwm.border.*]
363
│       ├── register_client_module()     [oxwm.client.*]
364
│       ├── register_layout_module()     [oxwm.layout.*]
365
│       ├── register_tag_module()        [oxwm.tag.*]
366
│       ├── register_bar_module()        [oxwm.bar.*]
367
│       └── register_misc()              [oxwm.set_terminal(), etc.]
368
369
├── bar/
370
│   ├── mod.rs                           [Re-exports: Bar, BlockCommand, BlockConfig]
371
│   ├── bar.rs
372
│   │   ├── struct Bar                   [Status bar window with XFT support]
373
│   │   ├── new()                        [Create bar X11 window, load font, init blocks]
374
│   │   ├── draw()                       [Render tags + blocks with underlines]
375
│   │   ├── update_blocks()              [Update block content based on intervals]
376
│   │   └── handle_click()               [Detect which tag was clicked]
377
│   ├── font.rs
378
│   │   ├── struct Font                  [XFT font wrapper]
379
│   │   └── draw_text()                  [Render text with color]
380
│   └── blocks/
381
│       ├── mod.rs                       [Block trait, BlockConfig, BlockCommand enum]
382
│       ├── battery.rs                   [Battery status block]
383
│       ├── datetime.rs                  [Date/time formatting block]
384
│       ├── ram.rs                       [RAM usage block]
385
│       ├── shell.rs                     [Shell command execution block]
386
│       └── static.rs                    [Static text block]
387
388
├── keyboard/
389
│   ├── mod.rs                           [Re-exports]
390
│   ├── keysyms.rs                       [X11 keysym constants]
391
│   └── handlers.rs
392
│       ├── enum KeyAction               [All keyboard actions]
393
│       ├── enum Arg                     [Action arguments]
394
│       ├── struct KeyBinding            [Keybinding + keychord support]
395
│       └── struct KeyPress              [Individual key press in chord]
396
397
├── layout/
398
│   ├── mod.rs                           [Layout trait definition]
399
│   ├── tiling.rs                        [Tiling layout with master/stack]
400
│   ├── monocle.rs                       [Fullscreen stacking layout]
401
│   ├── grid.rs                          [Equal-sized grid layout]
402
│   ├── tabbed.rs                        [Tabbed container layout]
403
│   └── normie.rs                        [Floating-by-default layout]
404
405
└── errors.rs                            [Error types: WmError, ConfigError, etc.]
406
407
templates/
408
├── config.lua                           [Default config with functional API]
409
└── oxwm.lua                             [LSP type definitions for autocomplete]
410
#+end_src
411
412
* Architecture Notes
413
** Lua Configuration System
414
OXWM uses mlua to embed a Lua interpreter. The functional API is implemented in =src/config/lua_api.rs=:
415
- Each API function (e.g., =oxwm.border.set_width()=) is registered as a Lua function
416
- Functions modify a shared ConfigBuilder that accumulates settings
417
- When config execution completes, the builder produces the final Config struct
418
- Type definitions in =templates/oxwm.lua= provide LSP autocomplete and documentation
419
420
** Tag System
421
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).
422
423
** Status Bar
424
The bar uses a performance-optimized approach with a modular block system:
425
- Only redraws when invalidated
426
- Pre-calculates tag widths on creation
427
- Blocks update independently based on their configured intervals
428
- Supports custom colors and underline indicators
429
- Color schemes (normal/occupied/selected) control tag appearance
430
- Easily extensible - add new block types in src/bar/blocks/
431
432
** Layout System
433
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.
434
435
* TODO Current Todo List:
436
** PRIORITY High [0/4]
437
- [ ] Add Window Titles to Bar
438
  - Show focused window title in status bar
439
  - Truncate if too long
440
  - Update on window focus change
441
- [ ] Add Horizontal Scroll Layout
442
  - Master window on left, stack scrolls horizontally
443
  - Alternative to vertical tiling
444
- [ ] Add Hide Empty Tag Numbers Option
445
  - Option to hide tags with no windows
446
  - Configurable via =oxwm.bar.set_hide_empty_tags(bool)=
447
- [ ] Add Swap Stack Bind
448
  - Keybind to swap focused window with master
449
  - Similar to dwm's zoom function (=Mod+Return=)
450
  - Should work bidirectionally
451
* Development Roadmap
452
** Current Focus (v0.8.0)
453
- Refactoring to align with dwm's proven patterns
454
- Improving core window management reliability
455
- Maintaining Lua config and bar features while simplifying internals
456
457
** Completed Features [8/8]
458
- [X] Multi-monitor support with RandR
459
- [X] Multiple layouts (tiling, monocle, grid, tabbed, normie)
460
- [X] Master area resizing (mfact) and nmaster support
461
- [X] Window rules (per-program auto-tag, floating)
462
- [X] Lua configuration with hot-reload
463
- [X] Built-in status bar with modular blocks
464
- [X] Keychord support (multi-key sequences)
465
- [X] Tag persistence across restarts
466
467
** Future Enhancements [/]
468
- [ ] Scratchpad functionality
469
- [ ] Dynamic monitor hotplugging (currently only on startup)
470
- [ ] External bar support (polybar, waybar compatibility)
471
- [ ] Additional layouts (deck, spiral, dwindle)
472
- [ ] Window minimize/restore
473
474
* License
475
[[https://www.gnu.org/licenses/gpl-3.0.en.html][GPL v3]]