Sprint 0: Project Setup
Goal: Buildable skeleton that connects to X server and appears in the display manager greeter.
Objectives
- Initialize Rust project with proper workspace structure
- Establish logging and error handling patterns
- Connect to X server and become a window manager
- Create a stable event loop
- Make gar selectable from the login greeter
Tasks
0.1 Initialize Cargo Project
- Create
Cargo.tomlwith workspace members (gar, garctl) - Set up
src/main.rsentry point - Set up
src/lib.rsfor library code - Create module structure skeleton
Dependencies to add:
[dependencies]
x11rb = { version = "0.13", features = ["allow-unsafe-code"] }
tracing = "0.1"
tracing-subscriber = "0.3"
thiserror = "1.0"
0.2 Logging Infrastructure
- Initialize tracing subscriber in main
- Set up log levels (RUST_LOG environment variable)
- Add structured logging for key events
- Log to file for debugging sessions
0.3 X Server Connection
- Create
src/x11/mod.rsmodule - Create
src/x11/connection.rs - Connect to X server using
x11rb::connect() - Get root window and screen info
- Handle connection errors gracefully
Key code pattern:
use x11rb::connection::Connection;
use x11rb::rust_connection::RustConnection;
pub fn connect() -> Result<(RustConnection, usize), Error> {
let (conn, screen_num) = x11rb::connect(None)?;
Ok((conn, screen_num))
}
0.4 Become Window Manager
- Request SubstructureRedirect on root window
- Handle "another WM running" error gracefully
- Set up necessary event masks on root window
Critical X11 concept: Only ONE client can have SubstructureRedirect on the root window. This is how X11 ensures only one window manager runs.
let change = ChangeWindowAttributesAux::new()
.event_mask(EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY);
conn.change_window_attributes(root, &change)?.check()?;
0.5 Event Loop
- Create
src/x11/events.rs - Implement basic event loop structure
- Handle ConfigureRequest (pass through for now)
- Handle MapRequest (map windows for now)
- Log all received events
- Clean shutdown on SIGTERM/SIGINT
Event loop pattern:
loop {
let event = conn.wait_for_event()?;
match event {
Event::ConfigureRequest(e) => handle_configure_request(&conn, e)?,
Event::MapRequest(e) => handle_map_request(&conn, e)?,
_ => tracing::trace!("Unhandled event: {:?}", event),
}
}
0.6 Desktop Entry
- Create
gar.desktopfile - Install to
/usr/share/xsessions/(or document install location) - Test selection from GDM/LightDM/SDDM
Desktop entry content:
[Desktop Entry]
Name=gar
Comment=Tiling window manager with smart splits
Exec=gar
Type=XSession
Acceptance Criteria
cargo buildsucceeds without errorscargo runconnects to X server (or fails gracefully if one isn't available)- Running in a nested X session (Xephyr) shows gar as the WM
gar.desktopcan be created and would appear in a greeter- Existing windows are not lost (ConfigureRequest/MapRequest passthrough)
- Logs show event activity
Testing Strategy
Manual testing with Xephyr:
# Terminal 1: Start nested X server
Xephyr -br -ac -noreset -screen 1280x720 :1
# Terminal 2: Run gar in nested session
DISPLAY=:1 cargo run
# Terminal 3: Launch apps in nested session
DISPLAY=:1 xterm
Notes
- Keep this sprint minimal - just enough to have a working foundation
- Don't implement actual window management yet (Sprint 1)
- Focus on correctness over features
- Document any X11 quirks encountered
Resources
View source
| 1 | # Sprint 0: Project Setup |
| 2 | |
| 3 | **Goal:** Buildable skeleton that connects to X server and appears in the display manager greeter. |
| 4 | |
| 5 | ## Objectives |
| 6 | |
| 7 | - Initialize Rust project with proper workspace structure |
| 8 | - Establish logging and error handling patterns |
| 9 | - Connect to X server and become a window manager |
| 10 | - Create a stable event loop |
| 11 | - Make gar selectable from the login greeter |
| 12 | |
| 13 | ## Tasks |
| 14 | |
| 15 | ### 0.1 Initialize Cargo Project |
| 16 | - [ ] Create `Cargo.toml` with workspace members (gar, garctl) |
| 17 | - [ ] Set up `src/main.rs` entry point |
| 18 | - [ ] Set up `src/lib.rs` for library code |
| 19 | - [ ] Create module structure skeleton |
| 20 | |
| 21 | **Dependencies to add:** |
| 22 | ```toml |
| 23 | [dependencies] |
| 24 | x11rb = { version = "0.13", features = ["allow-unsafe-code"] } |
| 25 | tracing = "0.1" |
| 26 | tracing-subscriber = "0.3" |
| 27 | thiserror = "1.0" |
| 28 | ``` |
| 29 | |
| 30 | ### 0.2 Logging Infrastructure |
| 31 | - [ ] Initialize tracing subscriber in main |
| 32 | - [ ] Set up log levels (RUST_LOG environment variable) |
| 33 | - [ ] Add structured logging for key events |
| 34 | - [ ] Log to file for debugging sessions |
| 35 | |
| 36 | ### 0.3 X Server Connection |
| 37 | - [ ] Create `src/x11/mod.rs` module |
| 38 | - [ ] Create `src/x11/connection.rs` |
| 39 | - [ ] Connect to X server using `x11rb::connect()` |
| 40 | - [ ] Get root window and screen info |
| 41 | - [ ] Handle connection errors gracefully |
| 42 | |
| 43 | **Key code pattern:** |
| 44 | ```rust |
| 45 | use x11rb::connection::Connection; |
| 46 | use x11rb::rust_connection::RustConnection; |
| 47 | |
| 48 | pub fn connect() -> Result<(RustConnection, usize), Error> { |
| 49 | let (conn, screen_num) = x11rb::connect(None)?; |
| 50 | Ok((conn, screen_num)) |
| 51 | } |
| 52 | ``` |
| 53 | |
| 54 | ### 0.4 Become Window Manager |
| 55 | - [ ] Request SubstructureRedirect on root window |
| 56 | - [ ] Handle "another WM running" error gracefully |
| 57 | - [ ] Set up necessary event masks on root window |
| 58 | |
| 59 | **Critical X11 concept:** Only ONE client can have SubstructureRedirect on the root window. This is how X11 ensures only one window manager runs. |
| 60 | |
| 61 | ```rust |
| 62 | let change = ChangeWindowAttributesAux::new() |
| 63 | .event_mask(EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY); |
| 64 | conn.change_window_attributes(root, &change)?.check()?; |
| 65 | ``` |
| 66 | |
| 67 | ### 0.5 Event Loop |
| 68 | - [ ] Create `src/x11/events.rs` |
| 69 | - [ ] Implement basic event loop structure |
| 70 | - [ ] Handle ConfigureRequest (pass through for now) |
| 71 | - [ ] Handle MapRequest (map windows for now) |
| 72 | - [ ] Log all received events |
| 73 | - [ ] Clean shutdown on SIGTERM/SIGINT |
| 74 | |
| 75 | **Event loop pattern:** |
| 76 | ```rust |
| 77 | loop { |
| 78 | let event = conn.wait_for_event()?; |
| 79 | match event { |
| 80 | Event::ConfigureRequest(e) => handle_configure_request(&conn, e)?, |
| 81 | Event::MapRequest(e) => handle_map_request(&conn, e)?, |
| 82 | _ => tracing::trace!("Unhandled event: {:?}", event), |
| 83 | } |
| 84 | } |
| 85 | ``` |
| 86 | |
| 87 | ### 0.6 Desktop Entry |
| 88 | - [ ] Create `gar.desktop` file |
| 89 | - [ ] Install to `/usr/share/xsessions/` (or document install location) |
| 90 | - [ ] Test selection from GDM/LightDM/SDDM |
| 91 | |
| 92 | **Desktop entry content:** |
| 93 | ```ini |
| 94 | [Desktop Entry] |
| 95 | Name=gar |
| 96 | Comment=Tiling window manager with smart splits |
| 97 | Exec=gar |
| 98 | Type=XSession |
| 99 | ``` |
| 100 | |
| 101 | ## Acceptance Criteria |
| 102 | |
| 103 | 1. `cargo build` succeeds without errors |
| 104 | 2. `cargo run` connects to X server (or fails gracefully if one isn't available) |
| 105 | 3. Running in a nested X session (Xephyr) shows gar as the WM |
| 106 | 4. `gar.desktop` can be created and would appear in a greeter |
| 107 | 5. Existing windows are not lost (ConfigureRequest/MapRequest passthrough) |
| 108 | 6. Logs show event activity |
| 109 | |
| 110 | ## Testing Strategy |
| 111 | |
| 112 | **Manual testing with Xephyr:** |
| 113 | ```bash |
| 114 | # Terminal 1: Start nested X server |
| 115 | Xephyr -br -ac -noreset -screen 1280x720 :1 |
| 116 | |
| 117 | # Terminal 2: Run gar in nested session |
| 118 | DISPLAY=:1 cargo run |
| 119 | |
| 120 | # Terminal 3: Launch apps in nested session |
| 121 | DISPLAY=:1 xterm |
| 122 | ``` |
| 123 | |
| 124 | ## Notes |
| 125 | |
| 126 | - Keep this sprint minimal - just enough to have a working foundation |
| 127 | - Don't implement actual window management yet (Sprint 1) |
| 128 | - Focus on correctness over features |
| 129 | - Document any X11 quirks encountered |
| 130 | |
| 131 | ## Resources |
| 132 | |
| 133 | - [x11rb documentation](https://docs.rs/x11rb/latest/x11rb/) |
| 134 | - [X11 Protocol Reference](https://www.x.org/releases/current/doc/xproto/x11protocol.html) |
| 135 | - [ICCCM Spec](https://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html) |