oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
14,069 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
- [[#contributing][Contributing]]
15
- [[#key-bindings][Key Bindings]]
16
- [[#features][Features]]
17
- [[#testing-with-xephyr][Testing with Xephyr]]
18
- [[#project-structure][Project Structure]]
19
- [[#architecture-notes][Architecture Notes]]
20
  - [[#tag-system][Tag System]]
21
  - [[#status-bar][Status Bar]]
22
  - [[#layout-system][Layout System]]
23
- [[#current-todo-list][Current Todo List:]]
24
  - [[#priority-high-02][PRIORITY High]]
25
  - [[#priority-medium-03][PRIORITY Medium]]
26
  - [[#priority-low-01][PRIORITY Low]]
27
- [[#development-roadmap][Development Roadmap]]
28
  - [[#current-focus][Current Focus]]
29
  - [[#future-enhancements][Future Enhancements]]
30
- [[#license][License]]
31
32
* OXWM — DWM but Better (and oxidized)
33
A dynamic window manager written in Rust, inspired by dwm but designed to evolve
34
on its own. Configuration is done in Rust source code, keeping with the suckless
35
philosophy of *"edit + recompile."*, but with sane defaults and no arbitrary elitism
36
enforcing bad variable names and bad file structure topology.
37
38
* Installation
39
** NixOS (with Flakes)
40
41
*** Adding OXWM to your flake inputs
42
Add oxwm to your =flake.nix= inputs:
43
44
#+begin_src nix
45
{
46
  inputs = {
47
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
48
    oxwm.url = "github:tonybanters/oxwm";
49
    oxwm.inputs.nixpkgs.follows = "nixpkgs";
50
  };
51
52
  outputs = { self, nixpkgs, oxwm, ... }: {
53
    nixosConfigurations.yourhost = nixpkgs.lib.nixosSystem {
54
      system = "x86_64-linux";
55
      modules = [
56
        ./configuration.nix
57
        oxwm.nixosModules.default
58
      ];
59
    };
60
  };
61
}
62
#+end_src
63
64
*** Enabling OXWM in configuration.nix
65
Add this to your =configuration.nix=:
66
67
#+begin_src nix
68
{ config, pkgs, ... }:
69
70
{
71
  services.xserver = {
72
    enable = true;
73
    
74
    windowManager.oxwm.enable = true;
75
  };
76
77
  # Recommended: Install a display manager
78
  services.xserver.displayManager.lightdm.enable = true;
79
  # Or use another display manager like sddm, gdm, etc.
80
}
81
#+end_src
82
83
*** Initialize your config
84
After rebuilding your system with =sudo nixos-rebuild switch=, log in via your display manager.
85
86
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=.
87
88
*** Advanced: Using a specific oxwm version
89
If you want to pin or customize the oxwm package:
90
91
#+begin_src nix
92
{
93
  services.xserver.windowManager.oxwm = {
94
    enable = true;
95
    # Use a specific version or build with custom options
96
    package = oxwm.packages.${pkgs.system}.default;
97
  };
98
}
99
#+end_src
100
101
*** Development setup with Nix
102
For development, use the provided dev shell:
103
104
#+begin_src sh
105
# Clone the repository
106
git clone https://github.com/tonybanters/oxwm
107
cd oxwm
108
109
# Enter the development environment
110
nix develop
111
112
# Build and test
113
cargo build
114
just test
115
#+end_src
116
117
** Arch Linux
118
Install dependencies:
119
#+begin_src sh
120
sudo pacman -S rust cargo libx11 libxft freetype2 fontconfig pkg-config
121
#+end_src
122
123
** Building from Source
124
#+begin_src sh
125
git clone https://github.com/tonybanters/oxwm
126
cd oxwm
127
cargo build --release
128
sudo cp target/release/oxwm /usr/local/bin/
129
#+end_src
130
131
Or use the justfile:
132
#+begin_src sh
133
just install
134
#+end_src
135
136
** Setting up OXWM
137
*** Without a display manager (startx)
138
Add the following to your =~/.xinitrc=:
139
#+begin_src sh
140
exec oxwm
141
#+end_src
142
143
Then start X with:
144
#+begin_src sh
145
startx
146
#+end_src
147
148
*** With a display manager
149
If using a display manager (LightDM, GDM, SDDM), OXWM should appear in the session list after installation.
150
151
* Configuration
152
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).
153
154
Edit =~/.config/oxwm/config.ron= to customize:
155
- Keybindings
156
- Colors and appearance
157
- Status bar blocks
158
- Gaps and borders
159
- Terminal and applications
160
161
After making changes, reload OXWM with =Mod+Shift+R= 
162
163
* Contributing
164
When contributing to OXWM:
165
166
1. Never commit your personal =~/.config/oxwm/config.ron=
167
2. Only modify =templates/config.ron= if adding new configuration options
168
3. Test your changes with =just test= using Xephyr/Xwayland
169
4. Document any new features or keybindings
170
171
* Key Bindings
172
Default keybindings (customizable in config.rs):
173
174
| Binding           | Action                  |
175
|-------------------+-------------------------|
176
| Super+Return      | Spawn terminal          |
177
| Super+J/K         | Cycle focus down/up     |
178
| Super+Q           | Kill focused window     |
179
| Super+Shift+Q     | Quit WM                 |
180
| Super+Shift+R     | Hot reload WM           |
181
| Super+1-9         | View tag 1-9            |
182
| Super+Shift+1-9   | Move window to tag 1-9  |
183
| Super+S           | Screenshot (maim)       |
184
| Super+D           | dmenu launcher          |
185
| Super+A           | Toggle gaps             |
186
| Super+Shift+F     | Toggle fullscreen       |
187
| Super+Shift+Space | Toggle floating         |
188
189
* Features
190
- Dynamic tiling layout with master/stack
191
- Tag-based workspaces (9 tags by default)
192
- Configurable gaps between windows
193
- Status bar with modular block system
194
  - Battery, RAM, datetime, shell commands
195
  - Custom colors and update intervals
196
  - Click-to-switch tags
197
- Window focus cycling
198
- Hot reload without restarting X
199
- Persistent window tags across restarts
200
- Mouse hover to focus
201
- Border indicators for focused windows
202
- Fullscreen mode
203
204
* Testing with Xephyr
205
Test OXWM in a nested X server without affecting your current session:
206
207
#+begin_src sh
208
just test
209
#+end_src
210
211
This starts Xephyr on display :1 and launches OXWM inside it.
212
213
Or manually:
214
#+begin_src sh
215
Xephyr -screen 1280x800 :1 &
216
DISPLAY=:1 cargo run
217
#+end_src
218
219
* Project Structure
220
#+begin_src sh
221
src/
222
├── main.rs
223
│   └── main()
224
│       └── Creates WindowManager and calls .run()
225
226
├── window_manager.rs                    [CORE - X11 event handling]
227
│   ├── struct WindowManager
228
│   │   ├── connection: RustConnection   [X11 connection]
229
│   │   ├── windows: Vec<Window>         [All managed windows]
230
│   │   ├── focused_window: Option<Window>
231
│   │   ├── layout: Box<dyn Layout>
232
│   │   ├── window_tags: HashMap<Window, TagMask>
233
│   │   ├── selected_tags: TagMask
234
│   │   └── bar: Bar                     [Status bar]
235
│   │
236
│   ├── new()                            [Initialize WM, grab root, restore tags, scan windows]
237
│   ├── run()                            [Main event loop with block updates]
238
│   ├── handle_event()                   [Route X11 events]
239
│   │   ├── MapRequest    → add window, apply layout, update bar, save tag
240
│   │   ├── UnmapNotify   → remove window, update bar
241
│   │   ├── DestroyNotify → remove window, update bar
242
│   │   ├── KeyPress      → get action, handle it (includes Restart)
243
│   │   ├── ButtonPress   → handle bar clicks
244
│   │   └── Expose        → redraw bar
245
│   ├── handle_key_action()              [Execute keyboard actions]
246
│   ├── get_saved_selected_tags()        [Restore selected tags from _NET_CURRENT_DESKTOP]
247
│   ├── save_selected_tags()             [Persist selected tags to root window]
248
│   ├── get_saved_tag()                  [Restore window tag from _NET_CLIENT_INFO]
249
│   ├── save_client_tag()                [Persist window tag to window property]
250
│   ├── scan_existing_windows()          [Manage windows on startup]
251
│   ├── remove_window()                  [Remove from Vec, reapply layout]
252
│   ├── set_focus()                      [Focus window, update visuals]
253
│   ├── cycle_focus()                    [Move focus to next/prev window]
254
│   ├── view_tag()                       [Switch to tag/workspace, update visibility]
255
│   ├── move_to_tag()                    [Move window to tag]
256
│   ├── update_bar()                     [Calculate occupied tags, redraw bar]
257
│   ├── update_focus_visuals()           [Set border colors]
258
│   ├── update_window_visibility()       [Map/unmap windows based on tags]
259
│   └── apply_layout()                   [Position all windows below bar]
260
261
├── config.rs                            [CONFIGURATION - all settings here]
262
│   ├── BORDER_WIDTH, BORDER_FOCUSED, BORDER_UNFOCUSED
263
│   ├── FONT                             [XFT font string]
264
│   ├── TAG_COUNT, TAGS                  [Workspace configuration]
265
│   ├── TERMINAL, MODKEY
266
│   ├── ColorScheme                      [Foreground, background, border colors]
267
│   ├── SCHEME_NORMAL, SCHEME_OCCUPIED, SCHEME_SELECTED
268
│   ├── KEYBINDINGS                      [All keybinds as const array]
269
│   └── STATUS_BLOCKS                    [Block configurations with format, command, interval]
270
271
├── bar/
272
│   ├── mod.rs                           [Re-exports: Bar, BlockCommand, BlockConfig]
273
│   ├── bar.rs
274
│   │   ├── struct Bar                   [Status bar window with XFT support]
275
│   │   ├── new()                        [Create bar X11 window, load font, init blocks]
276
│   │   ├── draw()                       [Render tags + blocks with underlines]
277
│   │   ├── update_blocks()              [Update block content based on intervals]
278
│   │   ├── handle_click()               [Detect which tag was clicked]
279
│   │   └── invalidate()                 [Mark bar as needing redraw]
280
│   ├── font.rs
281
│   │   ├── struct Font                  [XFT font wrapper]
282
│   │   ├── struct FontDraw              [XFT drawing context]
283
│   │   └── draw_text()                  [Render text with color]
284
│   └── blocks/
285
│       ├── mod.rs                       [Block trait, BlockConfig, BlockCommand enum]
286
│       ├── battery.rs                   [Battery status block]
287
│       ├── datetime.rs                  [Date/time formatting block]
288
│       └── shell.rs                     [Shell command execution block]
289
290
├── keyboard/
291
│   ├── mod.rs                           [Re-exports]
292
│   ├── keycodes.rs                      [Key constants: Q, J, RETURN, etc]
293
│   └── handlers.rs
294
│       ├── enum KeyAction               [Spawn, KillClient, FocusStack, ViewTag, Restart, etc]
295
│       ├── enum Arg                     [None, Int, Str, Array]
296
│       ├── struct Key                   [Keybinding definition]
297
│       ├── setup_keybinds()             [Register keys with X11]
298
│       └── handle_key_press()           [Parse KeyPressEvent → KeyAction]
299
300
└── layout/
301
    ├── mod.rs                           [Layout trait definition]
302
    └── tiling.rs
303
        └── TilingLayout::arrange()      [Calculate window positions]
304
#+end_src
305
306
* Architecture Notes
307
** Tag System
308
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).
309
310
** Status Bar
311
The bar uses a performance-optimized approach with a modular block system:
312
- Only redraws when invalidated
313
- Pre-calculates tag widths on creation
314
- Blocks update independently based on their configured intervals
315
- Supports custom colors and underline indicators
316
- Easily extensible - add new block types in src/bar/blocks/
317
318
** Layout System
319
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.
320
321
* TODO Current Todo List:
322
** PRIORITY High [0/2]
323
- [ ] Convert keycodes to keysyms for cross-keyboard compatibility
324
  - Current hardcoded keycodes only work on specific keyboards
325
  - Need to use XKeysymToKeycode() for runtime conversion
326
  - Follow DWM's approach: keysym → keycode conversion
327
- [ ] Fix fullscreen to persist across tags
328
  - Fullscreen state should be maintained when switching tags
329
  - Window should remain fullscreen when returning to its tag
330
331
** PRIORITY Medium [0/3]
332
- [ ] Add keybindings to increase/decrease window size
333
  - Master area resize (Mod+H / Mod+L)
334
  - Individual window resize for floating windows
335
- [ ] Fix cursor on hover for bar
336
  - Bar should show pointer cursor on hover
337
  - Indicate clickable tag areas
338
- [ ] Add guess_terminal() function to default config.rs
339
  - Auto-detect available terminal emulator
340
  - Priority order: st → alacritty → kitty → wezterm → xterm
341
  - Fallback to xterm if none found
342
343
** PRIORITY Low [0/1]
344
- [ ] Create AUR package
345
  - Write PKGBUILD
346
  - Submit to AUR
347
  - Add installation instructions to README
348
* Development Roadmap
349
** Current Focus
350
- Multi-monitor support
351
- Additional layouts (monocle, floating)
352
- Master area resizing
353
- Window swapping in layout
354
355
** Future Enhancements
356
- Per-window floating behavior
357
- Per-program rules (auto-tag assignment, floating rules)
358
- External bar support (polybar, lemonbar)
359
- Scratchpad functionality
360
- Window minimize/restore
361
362
* License
363
[[https://www.gnu.org/licenses/gpl-3.0.en.html][GPL v3]]