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

95
AGENTS.md Normal file
View file

@ -0,0 +1,95 @@
# AGENTS.md
## Project intent
This repository is for building a Rust Wayland tiling compositor, not an X11 window manager.
The intended progression is:
1. nested compositor first
2. single output first
3. single workspace first
4. single tiling layout first
5. standalone session later
Use `smithay` as the foundation unless the user explicitly directs otherwise.
## Working rules
- Prefer small, staged changes that match the roadmap in `README.md` and `GUIDE.md`.
- Do not jump straight to DRM/KMS, multi-monitor, XWayland, animations, or IPC unless the current lower stage is already solid.
- Keep compositor integration code near the edges of the codebase.
- Keep layout, workspace, and focus logic in plain Rust types that are testable without Smithay-heavy setup.
- Prefer one feature-complete path over many half-implemented systems.
## Early priorities
When implementing the compositor, the preferred order is:
1. event loop boots
2. Wayland client connects
3. XDG toplevel maps
4. one surface renders
5. keyboard and pointer input work
6. one tiling layout works
7. workspaces work
8. multi-output works
9. standalone session works
## Dependency guidance
Expected direct crates for the early stages:
- `smithay`
- `calloop`
- `tracing`
- `tracing-subscriber`
- `xkbcommon`
- `anyhow`
- `thiserror`
- `bitflags`
Possible later crates:
- `serde`
- `toml`
- `clap`
- `zbus`
## Code structure guidance
Prefer a layout like:
```text
src/
main.rs
app.rs
state.rs
backend/
input/
layout/
shell/
render/
workspace/
config/
```
Guidelines:
- `backend/` should isolate nested vs tty/session-specific code.
- `layout/` should not depend heavily on Smithay types.
- `shell/` should handle XDG/toplevel lifecycle.
- `render/` should translate compositor state into frame output.
- `workspace/` should own workspace and window placement policy.
## Documentation guidance
- If the architecture changes materially, update `README.md`.
- If the build progression or crate recommendations change materially, update `GUIDE.md`.
- Keep docs practical and staged rather than aspirational.
## Validation guidance
- Prefer targeted checks over broad guesses.
- If code is added, run the narrowest useful verification command available.
- If verification is not possible, state that explicitly.

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.

242
README.md Normal file
View file

@ -0,0 +1,242 @@
# tile-manager
A Rust Wayland tiling compositor project, built in small stages.
The target is a simple desktop tile manager in the same general space as Hyprland or niri, but with a much narrower first scope:
1. nested compositor first
2. one output first
3. one workspace first
4. one tiling layout first
This project should grow in the same order a Codecrafters-style guide would teach it: build the minimum working compositor, then add input, tiling, workspaces, multi-output, and finally standalone session support and polish.
## Scope
This is not an X11 window manager.
It is intended to become a Wayland compositor that can:
1. accept Wayland clients
2. track windows, focus, outputs, and workspaces
3. process keyboard and pointer input
4. render surfaces
5. apply a tiling layout
## Planned Stack
Primary Rust crates:
- `smithay`: Wayland compositor foundation
- `calloop`: event loop
- `xkbcommon`: keyboard layout and modifiers
- `tracing`: structured logging
- `tracing-subscriber`: log output/filtering
- `anyhow`: application-level error handling
- `thiserror`: typed internal errors
- `bitflags`: internal state flags
Likely later additions:
- `serde` + `toml`: config loading
- `clap`: CLI flags
- `zbus`: desktop integration
- XWayland-related Smithay features: X11 app support
## Development Order
### Stage 0: Nested compositor
Start inside an existing desktop session before touching DRM/KMS.
Goal:
- boot the compositor
- open a Wayland client
- confirm logs and state changes
### Stage 1: Event loop and Wayland server
Implement:
1. logging setup
2. `calloop::EventLoop`
3. Wayland display state
4. core Smithay globals
Deliverable:
- the process starts and stays alive cleanly
### Stage 2: XDG toplevel support
Implement:
1. compositor state
2. shared memory support
3. XDG shell support
4. seat support
Deliverable:
- a client can create a toplevel surface
### Stage 3: Internal state model
Add plain Rust types for:
- compositor state
- outputs
- seats
- workspaces
- windows
- tile layout state
Deliverable:
- stable state snapshots after every map/unmap/focus/layout event
### Stage 4: Render one output
Implement:
1. one output
2. solid background
3. mapped surface rendering
4. frame presentation
Deliverable:
- one client is visible on screen
### Stage 5: Input and bindings
Implement:
1. keyboard input
2. pointer input
3. compositor modifier handling
4. a few core commands
Minimum commands:
- spawn terminal
- close focused window
- cycle focus
### Stage 6: Tiling
Start with one layout only.
Recommended first layouts:
1. equal columns
2. master-stack
Deliverable:
- new windows retile the workspace correctly
### Stage 7: Workspaces
Implement:
1. workspace switching
2. moving windows between workspaces
3. independent workspace layout state
### Stage 8: Multi-output
Implement:
1. output tracking
2. per-output active workspace
3. correct focus and new-window placement
### Stage 9: Standalone session
Only after the nested compositor is solid, add:
1. DRM/KMS
2. libinput
3. session/seat handling
4. VT switching
### Stage 10: Polish
Later features:
- config file
- IPC
- floating windows
- fullscreen
- layer-shell support
- screencopy
- animations
- XWayland
## Suggested Layout
```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
```
Guiding rule:
- keep Smithay integration near the edges
- keep layout and workspace logic in plain Rust types
## System Dependencies
The final compositor will also need Linux graphics/input libraries, depending on backend choice:
- `libwayland`
- `libxkbcommon`
- `libudev`
- `libinput`
- `libdrm`
- `gbm`
- `egl` / `gles2`
- `pixman`
- `xwayland` for X11 clients
## References
- Smithay docs: https://docs.rs/smithay/latest/smithay/
- Smithay project docs: https://smithay.github.io/smithay/smithay/
- wayland-server docs: https://smithay.github.io/wayland-rs/wayland_server/
- calloop docs: https://docs.rs/calloop/latest/calloop/
- xkbcommon docs: https://docs.rs/xkbcommon/latest/xkbcommon/
## Status
Right now the repo is still at the very beginning. The current documentation is the roadmap.
For a fuller step-by-step build guide, see [GUIDE.md](/home/henry/rust-projects/tile-manager/GUIDE.md).