diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..e698b2e --- /dev/null +++ b/AGENTS.md @@ -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. diff --git a/GUIDE.md b/GUIDE.md new file mode 100644 index 0000000..d504ac7 --- /dev/null +++ b/GUIDE.md @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9e7a922 --- /dev/null +++ b/README.md @@ -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).