9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 1 | # Codebase review |
9b084abcb9c0 | atagen | 2026-10-17 01:31 UTC | 2 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 3 | A read of every crate on 2026-10-17, after M5 to M7 and the M6 follow-ups. Findings are grouped by theme and ordered by payback within each group. The proposed order of work is at the end. |
9b084abcb9c0 | atagen | 2026-10-17 01:31 UTC | 4 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 5 | ## 1. The monolith crate has no internal boundaries |
9b084abcb9c0 | atagen | 2026-10-17 01:31 UTC | 6 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 7 | - **`State` (`state.rs:55`) has 70 public fields**, and all 22 modules are `impl State`. Nothing stops any function touching anything, and every new protocol adds a field and a `new()` line. The fields fall into a few groups: |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 8 | - Inert protocol globals that only their own handler touches. These are the 20 or so `*_state` fields and `_dmabuf_global`. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 9 | - The backend: `host`, `drm`, `output_id`, `render_node`, `syncobj`. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 10 | - The window model, spread over seven maps (see below). |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 11 | - Subsystems with their own state: `lock`, `captures`, `imports`, `xwayland`, `launches`, `ipc`, plus `inhibitors` and `inhibited`. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 12 | - Input: `suppressed`, `drag`, `pointer_pos`, `move_inverted`, `layer_focus`, `cursor_status`. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 13 | - Redraw flags: `needs_redraw`, `cursor_moved`. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 14 | - **The backend is two `Option`s plus side fields.** `host: Option<Host>` and `drm: Option<Drm>` both exist. `output_id` only means something nested and `render_node` only on bare metal. `after_dispatch` calls both `nested::render` and `drm::render` every time, and each returns early when its option is `None`. A `Backend` enum, or a small trait for render, present and outputs, would make the split explicit. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 15 | - **One window's state lives in seven places:** |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 16 | - `windows` |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 17 | - `last_kind` |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 18 | - `awaiting` |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 19 | - `placed_by_rule` |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 20 | - `scene::Borders` |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 21 | - `toplevels::ListHandles` |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 22 | - the animator's tracks, glides and spawns |
9b084abcb9c0 | atagen | 2026-10-17 01:31 UTC | 23 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 24 | `remove_window` (`state.rs:404`) cleans only some of them. `awaiting` and `last_kind` keep stale ids, and `ListHandles` is cleaned lazily in `publish_toplevels`. A per-window record (`Managed { window, kind, awaiting, by_rule, list_handle }`) would make removal one operation. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 25 | - **`apply_layout` (`state.rs:469`, about 110 lines)** does six jobs: it collects the shown windows, detects kind switches for glides, retargets animations, configures clients, maps the space, then syncs focus and idle inhibit. It runs after every command, including `Spawn`, `Quit`, `Reload` and `Close`, through `commands.rs:166`. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 26 | - **The commit handler (`handlers.rs:63`)** runs eight steps in sequence: |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 27 | - the buffer handler |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 28 | - early import |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 29 | - the window's own commit |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 30 | - capture damage |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 31 | - first-commit mapping |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 32 | - first-buffer detection |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 33 | - popups |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 34 | - layers |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 35 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 36 | It looks up the window by root surface twice, scanning `windows` and `unmapped` linearly each time. Routing on the surface's role first (toplevel, popup, layer, lock, cursor, X11) would make each path short. |
9b084abcb9c0 | atagen | 2026-10-17 01:31 UTC | 37 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 38 | ## 2. Drawing an output exists three times |
9b084abcb9c0 | atagen | 2026-10-17 01:31 UTC | 39 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 40 | `drm::render_surface` (`drm.rs:585`, 140 lines), `nested::render` (`nested/mod.rs:169`, 135 lines) and `capture::output_elements` (`capture.rs:265`) each assemble the cursor plus lock-or-scene. Each destructures `State` field by field to get past the borrow checker. On top of that, `render_surface` handles VRR switching, budget accounting, slow-frame logging, lock confirmation, capture damage and frame callbacks. Frame callbacks are written twice, once in each backend, with the lock and layer variants on top. |
9b084abcb9c0 | atagen | 2026-10-17 01:31 UTC | 41 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 42 | - One `frame_elements(world, output) -> Vec<Draw>` used by all three. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 43 | - One `send_frame_callbacks(world, output, elapsed)`. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 44 | - The backends keep pacing and presentation only. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 45 | - `scene::build` takes ten arguments (`scene.rs:45`, `#[expect(clippy::too_many_arguments)]`). That disappears once it takes a borrowed view struct. |
9b084abcb9c0 | atagen | 2026-10-17 01:31 UTC | 46 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 47 | **Correctness smell.** `anim.prune(now)` runs inside each output's render with that output's own `now` (`drm.rs:658`). With two outputs at different rates, the later one can prune state the earlier one is still drawing. It's also why `animating` only re-arms the output that noticed it. Pruning belongs once per dispatch, after every output has drawn. |
9b084abcb9c0 | atagen | 2026-10-17 01:31 UTC | 48 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 49 | ## 3. Snapshots for clients are rebuilt three ways, every dispatch |
9b084abcb9c0 | atagen | 2026-10-17 01:31 UTC | 50 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 51 | `ipc::broadcast`, `publish_workspaces` and `publish_toplevels` each derive a view of tags, windows and outputs from `Layout` plus `windows`. They run after every batch of events, and they lock every toplevel's role data for titles even when nothing changed. |
9b084abcb9c0 | atagen | 2026-10-17 01:31 UTC | 52 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 53 | - A single derived model, computed only when a dirty flag is set by commits, layout and focus changes, would feed all three. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 54 | - `slotmap::Key::data(&id).as_ffi()` appears in both `ipc.rs:177` and `toplevels.rs:26`. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 55 | - Tag names are looked up by id with a linear `tags().find` twice in `ipc.rs`. `Layout` lacks a `tag(id) -> &Tag` accessor. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 56 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 57 | ## 4. Duplication |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 58 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 59 | - `CLEAR` is defined identically in `drm.rs:40`, `nested/mod.rs:32` and `capture.rs`. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 60 | - Closing a window (xdg `send_close` or X11 `close`) is written in both `commands.rs:63` and `toplevels.rs:57`. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 61 | - Window lookup is a linear scan in four places: `State::window_id`, `x11_window_id`, the key search in `toplevels.rs:42` and `ListHandles::window`. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 62 | - Point-in-rect is written out inline in `drm.rs:787`, `drm.rs:645`, `capture.rs:270` and `state.rs` `surface_under`. The planner's `Rect` has no `contains`. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 63 | - Rect helpers are scattered: `inset`, `centred` and `translate` in `layout/lib.rs:979`, `pixels` in `scene.rs:279`, `span` and `with_span` in `tree.rs`. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 64 | - `Dir`, `Axis` and `ExpandScope` are defined in both `monolith-ipc` and `monolith-layout`, with hand-written mapping functions in `commands.rs:42`. `Modifier` and `Mods` from config are matched against smithay's `ModifiersState` by hand in two places (`commands.rs:29`, `input.rs:182`). |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 65 | - `protocols/src/workspace.rs` and `toplevel.rs` repeat the same skeleton: a manager list, a snapshot, a per-manager sync, bind-then-sync, and stop/destroyed pruning. If a third such protocol arrives, factor out the skeleton. With two, they're tolerable. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 66 | - **Config defaults live in two places:** the `Default` impls in `config/src/lib.rs` and `default.toml`, 111 lines. The only test (`config/lib.rs:415`) checks that the file parses, not that it agrees with `Default`. `Raw` (`lib.rs:264`) mirrors `Config` field by field as well. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 67 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 68 | ## 5. Module placement |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 69 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 70 | - `state.rs` free functions belong elsewhere. `root`, `identity` and `configure` are window helpers. `gaps` and `xkb_config` are config adapters. `now_ns` is a clock. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 71 | - `handlers.rs` mixes core compositor, xdg-shell, seat, selection, DnD, syncobj, decorations and workspace actions. The newer protocols each got their own module (`layers`, `lock`, `idle`, `capture`, `toplevels`). The older ones should follow so the file stops being a junk drawer. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 72 | - `render-vk/src/renderer.rs` (1113 lines) holds: |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 73 | - the smithay trait impls |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 74 | - submission and the NVIDIA stall logic |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 75 | - the 160-line `submit` that records all ops |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 76 | - upload and read-back |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 77 | - the shm cache |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 78 | - `DmabufImporter` |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 79 | - the image barrier helpers (`barrier`, `acquire`, `release` at `renderer.rs:690`) |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 80 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 81 | The barriers belong next to `sync.rs`. Stall handling and the shm cache would be self-contained modules. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 82 | - `layout/src/lib.rs` (1008 lines) covers tags, outputs and parking, windows, focus history, scratchpads and arrangement in one `impl`. Scratchpads (`765–853`) and output parking (`225–339`) are the clean cuts. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 83 | - `anim.rs` keeps `VulkanTexture` inside `Ghost`, which ties the animation model to the GPU. The ghost's texture could live in the scene, keyed by window, with the animator holding only geometry and timing. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 84 | - The DRM frame pacing (`Budget`, `Surface::next_deadline`, `drm.rs:86–163`) and nested's `Host::predict` are pure timing logic stuck inside backend files. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 85 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 86 | ## 6. Code quality |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 87 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 88 | - **render-vk has 84 `unsafe` sites and no `// SAFETY:` comments.** The monolith crate does comment its few. `submit` keeps one `unsafe` block across about 120 lines of recording. Narrower blocks with an invariant stated per helper would make the Vulkan code reviewable. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 89 | - **Silent `let _ =`** sits on X11 `configure` and `set_*` (`state.rs`, `xwayland.rs`), `notifier.successful`, `popup.send_configure`, `display.flush_clients` and protocol sends. The X11 ones can hide real failures, so log those at debug. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 90 | - `commands::spawn` starts one OS thread per child just to `wait()` on it. A SIGCHLD source on the event loop reaps them all without threads. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 91 | - `drm::on_input` maps absolute pointer motion onto the first output only (`drm.rs:751`). That's wrong for tablets and multi-monitor. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 92 | - `Budget::ns` allocates and sorts 32 samples on every call, which is several times per frame. It's cheap, but a fixed array or a running quantile would do. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 93 | - A SIGKILLed monolith leaves `/tmp/.X*-lock` and its socket behind (`xwayland.rs:31` cleans up only on `Drop`). `claim` recovers stale locks by pid, but a recycled pid wastes a display number. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 94 | - Comment density is uneven across modules. Do one pass against the commenting rules. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 95 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 96 | ## 7. Tests: what earns its keep and what's missing |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 97 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 98 | What exists: |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 99 | - **planner:** 5 named strategic cases plus 3 proptests (random transitions, rewinding, interrupts). High value, low count. The custom `Rng` inside proptest is unusual but fine. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 100 | - **layout:** a 2000-case proptest of random commands with `check_geometry`, plus two named tree cases. One invariant added this week found a real bug at once. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 101 | - **motion:** five small curve and velocity tests. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 102 | - **render-vk:** golden image tests, dmabuf round-trips and a transform test. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 103 | - **config:** one parse test. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 104 | - **monolith:** none. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 105 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 106 | Gaps, by value: |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 107 | 1. **Layout invariants are cheap and catch real bugs.** Add these to `check_geometry`: |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 108 | - the focused window is visible |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 109 | - every visible window's tag is viewed on its output, or the window is sticky or a shown scratchpad |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 110 | - no window appears in two trees |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 111 | - parked outputs restore their windows |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 112 | 2. **Frame pacing.** `Budget` and `next_deadline` are pure, and they have been the source of most of the visible bugs: speed changes, periodic misses, slack decay. A small test that drives a timing sequence through them (a spike, then recovery, then miss and hold) would pin the behaviour that took several bare-metal rounds to find. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 113 | 3. **Protocol snapshot diffing** in `workspace.rs` and `toplevel.rs` is fiddly and has a history of hidden-state bugs. Pull the diff into a pure function from (sent, snapshot) to events and test it with the cases that matter: removal, output enter and leave, and a property change with no structural change. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 114 | 4. **Config:** assert that `default.toml` parses to the same values as `Default`, which closes the drift in section 4. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 115 | 5. **Nested smoke harness.** Every protocol this week was verified with a throwaway shell script that starts nested monolith with clients and greps logs. One `.nu` harness (or an `xtask`) that runs scenarios (layer-shell plus a client, lock, capture, toplevel list, Xwayland) would make those checks repeatable. It could run in `flake check` against lavapipe later. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 116 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 117 | ## Proposed order |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 118 | |
a265da4681aa | atagen | 2026-10-17 04:56 UTC | 119 | Done as of 2026-10-17: 1 (State split, see state-split.md), 2 (frame path in frame.rs, pruning once per dispatch), 3 (snapshots gated on Dirty::snapshot, identity cached per window). CLEAR and close-window from item 4 went in along the way. 4 (dedup) and 5 (file moves) are done too. From 5, `now_ns`, `gaps` and `xkb_config` stay in state.rs, and the ghost texture is still held by `anim.rs`. The texture is a design change, not a move. |
14dab738af1a | atagen | 2026-10-17 03:14 UTC | 120 | |
14dab738af1a | atagen | 2026-10-17 03:14 UTC | 121 | |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 122 | 1. **Split `State`** into protocol globals, the backend enum, the window model with per-window records, subsystems and input. This unlocks most of the rest. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 123 | 2. **One output frame path:** element assembly and frame callbacks shared by both backends and capture, with pruning once per dispatch. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 124 | 3. **One derived snapshot with a dirty flag**, feeding ipc, ext-workspace and wlr-toplevel. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 125 | 4. **Mechanical dedup:** `CLEAR`, close, window index, `Rect::contains`, the shared enums between ipc and layout, the modifier mapping, and single-source config defaults. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 126 | 5. **File moves:** `handlers.rs` per protocol, the render-vk barriers, stall and shm cache modules, layout scratchpads and parking, frame pacing out of `drm.rs`. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 127 | 6. **Tests:** layout invariants, pacing, snapshot diffs, config defaults, then the smoke harness. |
9c7ef7f4bc24 | atagen | 2026-10-17 01:31 UTC | 128 | 7. **Hygiene sweep:** SAFETY comments, `let _ =` triage, the child reaper, absolute pointer mapping, and comments. |