- Rust 95.1%
- Shell 3.2%
- Python 1.7%
| .forgejo/workflows | ||
| docs | ||
| examples | ||
| scripts | ||
| src | ||
| tests | ||
| .gitignore | ||
| build.rs | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CONTRIBUTING.md | ||
| LICENSE | ||
| README.md | ||
| rust-toolchain.toml | ||
| rustfmt.toml | ||
evil
A Wayland compositor where you write the window manager in Lua. Place windows, move them, tile them, float them — your config, your rules.
Rust provides facts. Lua provides policy.
Rust handles protocols, rendering, input, DRM — the hard real-time stuff. Lua owns everything else: where windows go, how they move, who gets focus, what keys do. No recompiling, no patching C.
What makes it different
Any model you want. The default is an infinite canvas — pan, zoom, place windows anywhere in world-space. But Lua owns every policy decision, so you can shape it into anything. The tiling example turns it into a page-based tiler. The snap-grid example makes everything grid-aligned. You decide.
Lua all the way down. Focus, placement, move/resize, keybindings, compositor-side drawing, fullscreen handling — all scriptable through evil.on.* hooks and the evil.window / evil.canvas / evil.draw APIs.
Headless testing. Write and test window management policy without a display. The headless runtime (--backend headless) gives you deterministic state snapshots you can assert on. Config validation (--check-config) catches mistakes before you run.
Nested and bare-metal. Develop in a window with the winit backend, then deploy to real hardware with the udev/DRM backend.
Quick start
cargo build
# Validate a config
cargo run --bin evil -- --check-config --config examples/tiling.lua
# Run in a window (nested — great for hacking)
cargo run --bin evil -- --backend winit --config examples/tiling.lua
# Run headless (no display needed — perfect for testing)
cargo run --bin evil -- --backend headless --config examples/tiling.lua
# Brave? Run on real hardware (spare VT only)
scripts/start-tty.sh
Shipped examples
| Config | What it shows |
| examples/default.lua | Production-ready daily-driver default — free-floating on the infinite canvas |
| examples/tiling.lua | Page-based dynamic tiling |
| examples/tty-baseline.lua | Bare-metal TTY profile |
| examples/rules.lua | Window-rules helper module (loaded by other configs) |
IPC
evil exposes a Unix socket for external tooling. Query runtime state, list windows, focus, spawn commands, lock/unlock the session, take screenshots.
evil --ipc-socket /path/to/socket --ipc-command snapshot
evil --ipc-socket /path/to/socket --ipc-command list-windows
evil --ipc-socket /path/to/socket --ipc-command spawn --ipc-arg foot
Docs
| Topic | Link |
|---|---|
| Writing your first config | Writing Your First Config |
Full evil.* API reference |
Lua API Guide |
| Hook reference | Hooks Guide |
| Quick reference card | Lua API Cheat Sheet |
| Architecture & philosophy | Architecture and Philosophy |
| Testing & debugging | Testing and Debugging |
| Feature status | Feature Status and Limitations |
Repo docs: CONTRIBUTING.md, tests/testing.md, docs/llms.md, docs/dev/plan.md.
Development
cargo build
cargo test --all-targets
cargo test --no-default-features
cargo clippy --all-targets --all-features -- -D warnings
cargo fmt --check
# Validate all shipped configs
for cfg in examples/*.lua; do
cargo run --bin evil -- --check-config --config "$cfg"
done
| Features | Result |
|---|---|
default (winit,lua,xwayland) |
Nested compositor + Lua + Xwayland |
--features lua,udev (no defaults) |
Standalone TTY/DRM + Lua |
--all-features |
Everything |
License
GPL-3.0-only