A from-scratch tiling Wayland compositor, written in Rust on top of wlroots.
0xide is a personal, learning-first compositor built directly on wlroots 0.19 rather than on top of any desktop. It's a dynamic tiling compositor — windows are arranged automatically to fill the screen instead of floating and overlapping.
Status: early but real. It runs nested inside another Wayland session for development, and on actual hardware as a real DRM/KMS session on a TTY. It is being grown into something to daily-drive, one capability at a time.
- Spiral / dwindle tiling — each new window splits the remaining space, alternating vertical (left/right) then horizontal (top/bottom).
- 9 workspaces — switch between them and move windows across them from the keyboard.
- Multi-monitor with focus-follows-monitor — new windows open on the monitor
your cursor is on; each monitor shows its own workspace. Position/scale per
named output are configurable (
monitor =lines), otherwise auto-placed. - Keyboard-driven, configured by a small Rust-parsed config file (modifier, gaps, background colour, keybindings, terminal command).
- Pointer + cursor with click-to-focus.
- Runs real xdg-shell apps (terminals, browsers, …).
- Runs on a real TTY via libseat/logind, and survives VT switching (Ctrl+Alt+Fn away and back) without crashing or losing your windows.
- Layer-shell (
wlr-layer-shell-unstable-v1) — bars, panels and wallpaper (e.g. quickshell) render in the correct z-order and reserve their screen space, so tiled windows never sit underneath them. - Server-side decorations (
xdg-decoration-unstable-v1) — 0xide always claims decoration, so clients don't draw their own title bar/border: bare, borderless windows. - Screenshots/screen recording (
wlr-screencopy-unstable-v1+xdg-output) — tools likegrimandwf-recordercapture 0xide's real composited output directly.
The full story — architecture, environment/toolchain, and a phase-by-phase
build log (Stage 0 through Stage 8, each with its deliverable and how it
actually went) — lives in an mdBook
under docs/, published at
termworks.github.io/0xide.
Preview it locally with:
mdbook serveThe split is deliberate:
- Rust owns all policy — the window list, tiling layout, workspaces, keybindings,
config parsing, and overall flow (
src/main.rs,src/config.rs). - A thin C shim (
shim/oxide_shim.{c,h}) owns the parts that are awkward or unsafe to model through FFI: the wlrootswl_listener/wl_signalglue (intrusive linked lists) and anything that needs to read wlroots struct fields directly. It exposes clean(userdata, data)callbacks to Rust. - wlroots is the C library doing the heavy lifting (DRM/KMS modesetting, the GLES2
renderer, libinput, the scene graph, protocol plumbing). We bind to it with
bindgen+ the shim; we don't rewrite it.
In short: wlroots = mechanism, 0xide = policy. See
notes/architecture.md for the full division of labour.
Built and run on Arch Linux. System dependencies:
wlroots0.19 wayland wayland-protocols libxkbcommon libinput libdrm seatd mesa pixman pkgconf clang
The Rust toolchain is pinned in rust-toolchain.toml. Then:
cargo buildThe build script (build.rs) finds wlroots via pkg-config, generates the
xdg-shell protocol header with wayland-scanner, compiles the C shim, and runs
bindgen over wrapper.h.
Inside an existing Wayland session, 0xide opens as a window:
OXIDE_MOD=alt cargo nested -- kittycargo nested is an alias for cargo run. OXIDE_MOD=alt makes the modifier key
Alt instead of Super, because the host compositor grabs Super-chords before 0xide
sees them. The trailing -- kitty launches a test client against 0xide's socket.
From a free virtual terminal (e.g. Ctrl+Alt+F5), logged in:
LIBSEAT_BACKEND=logind ~/Projects/0xide/target/debug/0xide kitty 2>~/0xide-tty.logLIBSEAT_BACKEND=logind lets logind grant the active VT its devices (no seat group
needed). Here the modifier is the real Super key. Ctrl+Alt+F1 gets you back to your
main session. More detail and verification recipes are in
notes/running-and-verifying.md.
Mod is Super by default (Alt when running nested with OXIDE_MOD=alt).
| Keys | Action |
|---|---|
Mod + Return |
Open the terminal |
Mod + Q |
Close the focused window |
Mod + Shift + Q |
Quit 0xide |
Mod + H/J/K/L |
Focus the window left/down/up/right |
Mod + Shift + H/J/K/L |
Move the focused window left/down/up/right |
Mod + F |
Toggle fullscreen for the focused window |
Mod + 1…9 |
Switch to workspace 1–9 |
Mod + Shift + 1…9 |
Move focused window to workspace 1–9 |
Ctrl + Alt + F1…F12 |
Switch virtual terminal |
0xide reads ~/.config/0xide/0xide.conf (or $XDG_CONFIG_HOME/0xide/0xide.conf).
With no config file it uses the built-in defaults above. The format is key = value
with # comments, plus bind lines. Binds always start from the defaults above;
each bind line in your config overrides just that key combination and leaves every
other default bind in place — so a config with only a couple of bind lines still
has working workspace switches, close/quit, etc:
modifier = super
gap = 10
background = 0.0 0.6 0.6
bind = MOD, Return, spawn, kitty
bind = MOD, Q, close
bind = MOD SHIFT, Q, quit
bind = MOD, H, movefocus, l
bind = MOD SHIFT, H, movewindow, l
bind = MOD, 1, workspace, 1
bind = MOD SHIFT, 1, movetoworkspace, 1
# monitor = NAME, XxY[, SCALE] — explicit position for a named output
# (connector name, as logged: "output <name> online..."). Unlisted outputs
# keep the default auto-placement.
monitor = HDMI-A-1, 0x-1080, 1.0
A line 0xide can't parse is warned about on stderr and skipped — never fatal. See
0xide.conf.example for the full annotated example.
| Path | What it is |
|---|---|
src/main.rs |
Compositor orchestrator + all policy (layout, workspaces, input, keybindings) |
src/config.rs |
Dependency-free config-file parser |
shim/oxide_shim.{c,h} |
Thin C shim: wlroots listener glue + struct access |
build.rs, wrapper.h |
The FFI pipeline (pkg-config, wayland-scanner, cc, bindgen) |
notes/ |
Architecture, toolchain, and run/verify notes (working reference) |
docs/, book.toml |
The mdBook doc site source — narrative chapters + phase build log |
KICKOFF.md |
The project's mission and learning-first working rules |
0xide is a personal, learning-first project — built concept-by-concept with every
file and function understood rather than assembled. Its working rules live in
KICKOFF.md. No license yet!