atagen

monolith

public
Star0
Watch0
3521a747467e70a959a7eb08b34caf08896a4f5e
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
design/review-2026-10-19.mdMarkdown86 lines10.8 KB

Review, 2026-10-19

A whole-compositor pass after the memory work. Four reviewers covered protocols and client behaviour, backend and pacing, the Vulkan renderer, and the WM core. Each read plan.md, decisions.md, parked.md, review-notes.md and the test checklist first, so what follows is unmapped unless marked. The high items were rechecked against the code. Items marked (verify) rest on client or driver behaviour that was not tested.

Broken for users today

  1. Popups. xdg.rs grab is empty, so menus never get popup_done and stay open when you click elsewhere. Positioners use get_geometry() and never unconstrain, so menus near an edge go off screen. scene.rs draws popups inside their window’s z-slot, so a menu from the left tile is hidden and unclickable under the right one.
  2. Drag and drop. seat.rs dnd_requested cancels every source. No tab tear-off, no file drags, no text drags.
  3. X11 and Wayland clipboards are separate. XwmHandler implements none of the selection hooks, and smithay’s default refuses access. Copy and paste between Steam, Wine or Java and Wayland apps fails both ways.
  4. Hide-to-tray leaves a blank tile. Nothing treats a null-buffer commit as an unmap, so Telegram, Discord and Steam keep their slot and focus with nothing drawn, then remap without going through map_window.
  5. Fullscreen asked for before the first commit is lost. set_fullscreen needs a mapped window id and map_window never reads the pending state. The X11 path ignores _NET_WM_STATE_FULLSCREEN at map. mpv --fs, SDL and Wine games start windowed. fullscreen_request also ignores its output argument.
  6. A fullscreen window hides its own dialogs. Fullscreen is raised last, frame callbacks go only to the covering window, and new windows still take focus. A save dialog from a fullscreen game is invisible yet focused, and focus left walks into covered tiles.
  7. VRR content frames wait on the cursor cap. In drm.rs schedule, an armed cursor-wait timer makes a later content commit bail on scheduled.is_some(), so a fullscreen VRR game is held to the cursor cadence while the mouse moves. draw’s one-refresh retry timer has the same shape.
  8. Monitor unplug and replug. The GlobalId from create_global is dropped and never removed, so clients keep phantom outputs and see duplicates on replug. Layer and lock surfaces stay on the dead Output. DrmScanEvent::Changed, which smithay-drm-extras emits for the EDID race, falls into _ => {}. A failed initialize_output is never retried and there is no rescan on VT return, so a monitor can stay dark until the next hotplug.
  9. A failed frame can wedge an output. After a render_frame or queue_frame error, redraw is reset to animating. A lost flip leaves queued true forever, with no watchdog.
  10. Output scale is fixed at 1. Scale::Integer(1) is hard-coded, there is no scale option, and neither fractional-scale nor viewporter is advertised. A HiDPI panel is unusable, and Xwayland’s RandR emulation for games that pick a non-native mode needs viewporter (verify).

Missing protocols, by what clients lose

  • wp_presentation, fifo-v1, commit-timing. mpv display-sync, Mesa WSI present-wait and Proton FIFO pacing all fall back to frame-callback guesses. DRM already has exact flip times to feed it.
  • xdg-foreign and xdg-dialog. Portal file choosers arrive with no parent, so they tile and reshuffle the layout.
  • text-input-v3, input-method-v2, virtual-keyboard, virtual-pointer. No fcitx5 or ibus, no CJK input in Electron, no wtype or on-screen keyboards.
  • Output power (wlr-output-power-management or an IPC command). Monitors can never be blanked, and idle-notify has nothing to act on.
  • Touch, tablet, pointer-gestures, switch events, keyboard LEDs. All dropped in drm.rs on_input. Lid close does nothing and Caps Lock never lights.
  • security-context. Data-control, session-lock, capture and foreign-toplevel are open to every client, sandboxed ones included.
  • KDE server-decoration. Promised in decisions.md but not advertised.
  • wlr-screencopy. Older recorders and OBS plugins only speak that.
  • Per-surface dmabuf feedback with a scanout tranche. Fullscreen clients are never steered to plane-capable modifiers, and window elements are Kind::Unspecified, so overlay-plane scanout can never trigger on Intel or AMD.

Wrong or wasteful behaviour

  • Frame callbacks are never sent to X11 override-redirect surfaces or client cursor surfaces, so animated cursors freeze and X11 menus may stop updating (verify).
  • A paused session re-arms the deadline timer at refresh rate, because render_surface returns before clearing redraw. On resume the vblank grid and target_ns are stale, the first frame logs a miss and adds slack for 30 s.
  • Redraw and animation state are global. Any commit, even from a hidden tag, recomposes every output, and an animation on one monitor keeps a fullscreen VRR output recomposing.
  • layout.arrange rebuilds the arrangement per output on every pointer motion, several times per frame and per drag event, and identity() allocates two Strings per root commit.
  • Window capture keeps streaming while the session is locked. Output capture is safe.
  • The pointer cursor is never reset when it leaves a client, so a hidden or I-beam cursor persists over gaps, layers and Xwayland.
  • Client move, resize, maximize and minimize requests are ignored on both xdg and X11, while the default wm_capabilities still advertise them, so titlebar buttons are dead.
  • Suspended and configure_bounds are never sent. Browsers on hidden tags keep decoding.
  • The 300 ms first-buffer timeout consumes natural_size even with no buffer yet, so a slow dialog floats at the 60 % default forever.
  • Floats are second class. They are never resized on client request, never clamped on drag, never rescaled when the output area changes, and X11 configure_request is ignored.
  • Urgency is dropped for visible windows, and there is no per-window urgent anywhere.
  • Window rules only cover tag, output, floating, sticky and scratchpad, and run once at map, before many apps set their real title.
  • Config errors are invisible. Reload replies Ok with warnings, and a TOML syntax error at startup silently replaces the whole config, binds included, with the default.
  • IPC addresses only the focused window and active output, and subscribe sends whole snapshots with no event kind, so window switchers and bars cannot target windows or learn what changed.
  • Duplicate binds (Mod+h and Super+h) resolve alphabetically without a warning. Binds die on the lock screen, media keys included.
  • The nested backend ignores host focus leave, so a modifier held while alt-tabbing out stays stuck.

Renderer

  • Every shm upload is its own queue submit. A frame uploading more than eight surfaces can skip right past the window absorb_submit_stall aims for, letting the NVIDIA 1024th-submit stall land mid-frame. Batch uploads into one submit.
  • No wait has a timeout and ERROR_DEVICE_LOST maps to Interrupted. Reap treats failed fence queries as signalled. A GPU hang blocks the main thread forever, and a lost device spins warnings over a frozen screen.
  • Pipelines compile lazily mid-frame with no VkPipelineCache, so the first spawn animation hitches.
  • Blending is always on and opaque regions are ignored.
  • Barriers are ALL_COMMANDS to ALL_COMMANDS, one call per image, and every upload waits on all earlier queue work.
  • Screencopy to shm allocates a fresh full-size image per frame, waits twice on the main thread, copies twice and reports full damage.
  • Formats are 8-bit RGB only. Multi-plane modifiers are dropped, which loses Intel CCS and AMD DCC compression. There is no NV12 or P010 for video, and no 10-bit SDR.
  • Shm damage collapses to one bounding box, so a cursor blink plus a clock copies most of the buffer.
  • The device block is still 256 MiB of VRAM reserved on first use.
  • Layouts are recorded as changed before submit succeeds, and a failed submit leaks the command buffer.
  • copy_texture would read a sample-only dmabuf import without TRANSFER_SRC (latent).
  • The attachment view of X-format targets carries the alpha swizzle (verify against the dynamic-rendering VUIDs).
  • y_inverted dmabufs are ignored. has_dmabuf is all-or-nothing over five extensions.

Structure and readability

  • apply_layout is still about 150 lines. Its glide and summon diff should be a pure, testable function, and “mutate layout, then call apply_layout” is an unenforced protocol across about 36 sites.
  • CompositorHandler::commit does eight jobs and looks the window up twice. review-notes item 1 is closed but the role-based routing it proposed never landed.
  • drm.rs mixes session, connectors, output config, pacing and libinput. Surface has 17 fields, render_surface is 130 lines, and one lookup pattern repeats eight times. Split it into session, outputs, present and input modules.
  • renderer/mod.rs is about 900 lines. Recording, transfer and the trait impls want their own modules. The staging pool lives in image.rs, which makes device and image depend on each other, and its destroy-and-free sequence is written three times.
  • Animation vocabulary (Transition, Effect) lives in the renderer crate.
  • The fullscreen toggle is written three times (xdg, xwayland, toplevels) and the root-surface walk twice. x11_window_id is a linear scan beside by_surface. Output setup and the end-of-render tail are duplicated between drm and nested.
  • There are three unrelated types named Kind, and the CLI Backend enum shadows backend::Backend. config depends on ipc, which depends on layout, just to share Command.
  • layer_commit locks every output’s layer map on every commit, and reserve_layers ends in sync_focus even when nothing changed, so a 60 Hz bar reruns focus sync at 60 Hz.
  • Failures are swallowed in many places, including scheduled = token.ok(), which stops an output drawing with no log line. Staging::bytes_mut returns an empty slice on a failed map and stage_rows then panics.
  • Slow spans go to stderr and skip the file log. There is no panic hook and no SIGTERM handling, so logout skips socket and X11 lock cleanup. monolith.log at the repo root is tracked in git.

Stale docs

  • parked.md says xdg dialogs with a parent tile like any window, but xdg.rs floats them.
  • review-notes.md item 1 is marked closed although commit routing by role was not done.
  • plan.md says DRM gets presentation feedback, but only the nested host side exists.

Already mapped, not counted

M8 tearing, pointer constraints, relative pointer and shortcuts inhibit. Gamma, HDR, output management and hybrid GPUs under Later. The NVIDIA submit stall, direct scanout setup cost, SCHED_RR needing CAP_SYS_NICE, wlr-foreign-toplevel minimize, X11 stacking, parent and cgroup launch ranking, and the state-split move of apply_layout.