Added README

This commit is contained in:
ManOfGoldForever 2026-03-16 00:37:00 -04:00
parent 40cb952c26
commit 3e1c412fea
3 changed files with 757 additions and 0 deletions

420
GUIDE.md Normal file
View file

@ -0,0 +1,420 @@
# Build a Rust Tile Manager, Step by Step
This is a small, Codecrafters-style roadmap for building a Wayland desktop tile manager in Rust, in the same general family as Hyprland or niri.
The simplest practical path is:
1. Build a Wayland compositor, not an X11 window manager.
2. Use `smithay` as the foundation.
3. Start with one monitor, one keyboard, one pointer, one tiled layout.
4. Add polish only after mapping, focus, input, and rendering work.
## What You Are Actually Building
At a high level, your app needs to do five jobs:
1. Start a Wayland server and accept client connections.
2. Track outputs, seats, windows, focus, and layout state.
3. Process input events from keyboard and pointer devices.
4. Render client surfaces to one or more outputs.
5. Apply a tiling policy when windows are created, resized, focused, or closed.
If you want something "like Hyprland or niri", that does not mean cloning their code structure. It means building these capabilities in order:
1. Working compositor loop.
2. Window mapping and focus.
3. Basic tiling.
4. Multi-monitor support.
5. Animations, gestures, rules, and IPC.
## Direct Crates You Will Likely Add
These are the direct dependencies worth adding to your own `Cargo.toml`.
### Core crates
- `smithay`
What it does: the main compositor toolkit. It provides Wayland protocol helpers, backend abstractions, input handling, output handling, rendering helpers, and optional XWayland support.
- `calloop`
What it does: callback-based event loop. Smithay is designed around it.
- `tracing`
What it does: structured logging. Smithay uses it internally, so using it yourself keeps diagnostics consistent.
- `tracing-subscriber`
What it does: prints and filters `tracing` logs.
- `anyhow`
What it does: fast application-level error handling while the project is still evolving.
- `thiserror`
What it does: typed errors for your own subsystems once the codebase grows.
- `bitflags`
What it does: ergonomic flag types for key modifiers, window state, output capabilities, and internal options.
- `xkbcommon`
What it does: keyboard layout and keymap handling through `libxkbcommon`.
### Optional but common
- `serde`
What it does: config file parsing and IPC payloads.
- `toml`
What it does: TOML config parsing.
- `clap`
What it does: command-line flags like `--config`, `--socket`, `--debug`.
- `tracing-appender`
What it does: file logging.
### Optional later-stage crates
- `smithay` with XWayland-related features
What it does: lets X11 apps run through XWayland.
- `zbus`
What it does: DBus integration for desktop services.
- `async-channel` or `crossbeam-channel`
What it does: internal message passing if you split rendering, IPC, or config reload paths.
## Minimum `Cargo.toml` Shape
Use this as a starting point, then adjust feature flags to match the backend you choose:
```toml
[dependencies]
anyhow = "1"
bitflags = "2"
calloop = "0.14"
smithay = "0.7"
thiserror = "2"
tracing = { version = "0.1", features = ["max_level_trace", "release_max_level_info"] }
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
xkbcommon = "0.9"
```
Important: `smithay` feature selection matters more than the exact crate list. You will eventually enable only the backends you are actually using.
## System Dependencies
Rust crates are not the whole story. A Wayland compositor also needs Linux graphics/input stack libraries installed on the system.
Expect to need some combination of:
- `libwayland`
- `libxkbcommon`
- `libudev`
- `libinput`
- `libdrm`
- `gbm`
- `egl` / `gles2`
- `pixman`
- `xwayland` if you want X11 app support
The exact set depends on whether you target:
- `winit` backend first
Easier for learning and debugging.
- `udev`/DRM backend
Required for a real standalone desktop session.
## The Learning Order
Treat each stage as a checkpoint. Do not skip ahead.
### Stage 0: Pick the first target
Start with a nested compositor using Smithay's easier development path.
Goal:
- Run your compositor inside an existing desktop session.
- Open a test client.
- See logs and state changes.
Why:
- This avoids debugging KMS/DRM, seat management, and GPU initialization on day one.
### Stage 1: Boot the event loop
Build the smallest app that:
1. Initializes `tracing`.
2. Creates a `calloop::EventLoop`.
3. Creates Wayland display state.
4. Registers the core Smithay globals you need.
5. Enters the dispatch loop.
Deliverable:
- The process starts cleanly and stays alive.
### Stage 2: Accept clients and map surfaces
Add the core Wayland protocol pieces:
1. Compositor state.
2. Shared-memory buffer support.
3. XDG shell support.
4. Seat support.
Track:
- New toplevel windows.
- Window title and app id.
- Configure/commit lifecycle.
- Mapped vs unmapped state.
Deliverable:
- A client can connect and create a toplevel surface.
### Stage 3: Build your internal model
Before fancy rendering, define your own data structures.
You need types roughly like:
- `CompositorState`
- `OutputState`
- `SeatState`
- `Workspace`
- `TileTree` or `ColumnLayout`
- `WindowId`
- `ManagedWindow`
Store:
- Which workspace a window belongs to.
- Whether the window is tiled, floating, fullscreen, or urgent.
- Focus order.
- Geometry requested by your layout engine.
Deliverable:
- You can print a stable snapshot of layout state after every window event.
### Stage 4: Render one output
Now connect your scene state to rendering.
Keep it simple:
1. Support one output first.
2. Draw a background color.
3. Render mapped client surfaces.
4. Present frames continuously or when damaged.
Deliverable:
- One client window is visible on screen.
### Stage 5: Keyboard and pointer input
Wire up:
1. Pointer motion.
2. Pointer button press.
3. Keyboard key press.
4. Modifiers through `xkbcommon`.
Implement:
- Focus follows click, or focus follows keyboard only.
- A compositor modifier key.
- At least three commands: spawn terminal, close focused window, cycle focus.
Deliverable:
- You can interact with a client and trigger compositor actions.
### Stage 6: First tiling layout
Do not start with dynamic animations or fancy trees. Start with one deterministic layout.
Best beginner layouts:
1. Master-stack.
2. Equal vertical columns.
3. Niri-style scrolling columns.
Recommended first choice:
- Equal vertical columns or a simple master-stack.
Rules:
1. When a new tiled window appears, insert it into the active workspace.
2. Recompute all tile rectangles.
3. Send configure events with the new size.
4. Render using those computed rectangles.
Deliverable:
- Opening a second and third window retile the workspace correctly.
### Stage 7: Workspaces
Add:
1. Multiple workspaces per output.
2. Active workspace switching.
3. Move focused window to another workspace.
Deliverable:
- You can switch workspaces and preserve layout state independently.
### Stage 8: Multi-output support
Track:
- Connected outputs.
- Per-output current workspace.
- Output geometry and scale.
Implement:
- New windows land on the focused output.
- Workspaces are attached either globally or per output.
Deliverable:
- Two monitors work without corrupting focus or layout state.
### Stage 9: Real standalone session
After the nested version works, move toward a real desktop session.
This is where backend complexity increases:
1. DRM/KMS
2. GBM/EGL or another rendering path
3. Libinput
4. Session/seat handling
5. VT switching
Deliverable:
- The compositor can run on a TTY as the main desktop session.
### Stage 10: Quality-of-life features
Only now add polish:
1. Config file.
2. IPC socket.
3. Window rules.
4. Floating windows.
5. Fullscreen.
6. Pointer constraints.
7. Layer-shell panels and wallpapers.
8. Screencopy.
9. Idle inhibit.
10. Animations.
## A Good Project Structure
Once the prototype starts working, split it like this:
```text
src/
main.rs
app.rs
state.rs
backend/
mod.rs
nested.rs
tty.rs
input/
mod.rs
keyboard.rs
pointer.rs
bindings.rs
layout/
mod.rs
columns.rs
master.rs
shell/
mod.rs
xdg.rs
window.rs
render/
mod.rs
scene.rs
workspace/
mod.rs
config/
mod.rs
```
The important rule is simple:
- Smithay-facing code should stay near the edges.
- Your layout and workspace logic should be mostly your own plain Rust types.
## What Each Crate Does in the Architecture
Think of the crates like this:
- `smithay`: Wayland compositor plumbing and backend integration.
- `calloop`: the main event loop that everything runs inside.
- `xkbcommon`: keyboard interpretation and modifiers.
- `tracing` + `tracing-subscriber`: logs, spans, filtering, debugging.
- `anyhow`: ergonomic top-level errors during rapid iteration.
- `thiserror`: precise library-style errors for your own modules.
- `bitflags`: compact internal state flags.
- `serde` + `toml`: config loading.
- `clap`: startup options.
- `zbus`: desktop integration later.
## Suggested Milestone Checklist
If you want a clean progression, build in this exact order:
1. Process boots and enters event loop.
2. Wayland client connects.
3. XDG toplevel appears.
4. One window renders.
5. Focus works.
6. Keyboard shortcuts work.
7. Two windows tile correctly.
8. Workspaces work.
9. Multi-output works.
10. Standalone TTY session works.
11. XWayland works.
12. Config and IPC work.
## Practical Advice
- Build a nested compositor first. This is the highest-leverage simplification.
- Keep layout logic pure. A layout engine should accept windows and output rectangles and return tile rectangles.
- Add exactly one layout first.
- Keep a strong debug log for every surface map, unmap, commit, focus change, output change, and configure.
- Do not implement animations until you trust your state model.
- Do not add XWayland until native Wayland windows are solid.
## Recommended References
These are the most relevant primary sources I used:
- Smithay crate docs: https://docs.rs/smithay/latest/smithay/
- Smithay project docs: https://smithay.github.io/smithay/smithay/
- Smithay `wayland` module docs: https://docs.rs/smithay/latest/smithay/wayland/
- Smithay `xwayland` module docs: https://docs.rs/smithay/latest/smithay/xwayland/
- `wayland_server` docs: https://smithay.github.io/wayland-rs/wayland_server/
- `calloop` docs: https://docs.rs/calloop/latest/calloop/
- `tracing` docs: https://docs.rs/crate/tracing/latest
- `anyhow` docs: https://docs.rs/crate/anyhow/latest
- `thiserror` docs: https://docs.rs/crate/thiserror/latest
- `bitflags` docs: https://docs.rs/bitflags
- `xkbcommon` docs: https://docs.rs/xkbcommon/latest/xkbcommon/
## Final Recommendation
If your goal is "a simple tile manager similar to Hyprland or niri", the best first version is:
1. Rust
2. Wayland
3. `smithay`
4. `calloop`
5. one output
6. one workspace
7. one tiling layout
8. nested backend first
That path is realistic. Starting with standalone DRM, multi-monitor, XWayland, animations, and IPC all at once is not.