diff --git a/docs/riverwm-evaluation.md b/docs/riverwm-evaluation.md new file mode 100644 index 00000000..9399259a --- /dev/null +++ b/docs/riverwm-evaluation.md @@ -0,0 +1,472 @@ +# River Window Manager Evaluation + +This evaluates window managers for the current River split-compositor model, +where River delegates policy to a client implementing +`river-window-management-v1`. + +It is intentionally separate from `docs/tiling-wm-experience.md`: that document +describes the target experience, while this document evaluates current River +implementation options against it. + +## Short Recommendation + +The best current candidates are: + +1. `kwm` if the goal is the most usable latest-protocol experiment quickly. +2. `orilla` if the goal is the closest long-term match to an XMonad-like, + code-driven, extensible environment. +3. `rhine` if Hyprland-like IPC and directional commands are more important + than layout maturity. + +No current candidate appears to satisfy the full target experience out of the +box. The largest gaps are equal-width vertical columns, XMonad-like directional +window control, scratchpad/minimize semantics, and taffybar-compatible state. + +## Current River Model + +River is now a non-monolithic Wayland compositor. It does not combine the +compositor and window manager into one process; a separate window manager +implements `river-window-management-v1`. + +Older River layout generators, such as `rivertile`, `river-filtile`, and +`wideriver`, are useful historical context but are not the primary path if the +goal is to use the latest River architecture. + +## Candidate Ranking + +### 1. kwm + +Repository: + +Fit: best practical starting point. + +Reasons to consider it: + +- Zig. +- Most popular candidate found. +- Very active as of late April 2026. +- Implements `river-window-management-v1`. +- Dynamic tiling with `tile`, `grid`, `monocle`, `deck`, `scroller`, and + floating layouts. +- Has tags, shift-tags, regex window rules, modes, runtime configuration reload, + and several window states. +- Uses a runtime `config.zon` file with partial overrides and live reload; it is + DWM-like, but ordinary configuration is not patch-based. + +Risks: + +- It is DWM-like, not XMonad-like. +- Default bindings are mostly next/previous rather than geometric WASD + directional focus and movement. +- The closest built-in layout to equal columns is probably `grid`, but this + needs empirical testing. +- It has its own status bar model; taffybar integration is not solved. + +Use this first if the goal is to get a working River session and discover +practical gaps quickly. + +### 2. orilla + +Repository: + +Fit: best philosophical match. + +Reasons to consider it: + +- Rust. +- Explicitly inspired by XMonad. +- Code-first configuration model. +- Dynamic tiling with composable layouts. +- Has tag actions for switching, shifting, previous tag, next/previous tag, + next empty tag, and shift-to-empty tag. +- Has a fullscreen/monocle-like layout. + +Risks: + +- Early project. +- Low adoption compared with `kwm`. +- Built-in layouts do not appear to include the exact equal-width vertical + columns layout. +- Current exposed actions are ordered focus/swap actions, not geometric + directional navigation. +- The implementation stores window rectangles, so directional navigation looks + patchable, but it is not available today. +- Current layout application appears early in its multi-output support; layout + calculation uses the first output rather than a mature per-output model. +- Output movement, scratchpads, minimization, and taffybar state would likely + require extension work. + +Use this first if the goal is to build toward the exact target experience rather +than maximize out-of-the-box coverage. + +### 3. rhine + +Repository: + +Fit: interesting integration candidate. + +Reasons to consider it: + +- Zig. +- Active as of late April 2026. +- Modular design. +- Has directional commands such as `moveFocus ` and `moveWindow `. +- Has workspace commands including send-to-workspace and move-to-workspace. +- Provides partial Hyprland IPC compatibility, which may be useful for tooling + and taffybar experiments. + +Risks: + +- Only a BSP-style layout appears to be implemented currently. +- Special workspaces and some monitor/workspace behavior are still TODO-level. +- Less popular than `kwm`. +- Equal vertical columns and tabbed/monocle behavior are not obvious fits. + +Use this for a small spike if the Hyprland IPC bridge looks like the shortest +path to taffybar state. + +### 4. beansprout + +Repository: + +Fit: viable but less aligned. + +Reasons to consider it: + +- Zig. +- Active as of late April 2026. +- DWM-style dynamic tiling. +- KDL configuration. + +Risks: + +- Primary/stack model conflicts with equal-width columns. +- Per-output tags conflict with the target shared global workspace model. +- Less popular than `kwm`. + +### 5. rill + +Repository: + +Fit: low. + +Reasons to consider it: + +- Zig. +- Active. +- More popular than several smaller candidates. + +Risks: + +- It is a scrolling window manager, which does not match the target layout + model. + +### 6. notion-river + +Repository: + +Fit: low despite good tab support. + +Reasons to consider it: + +- Rust. +- Has persistent frames and tabbed windows. +- Has IPC and cross-monitor focus/move behavior. + +Risks: + +- It is explicitly static tiling, not dynamic tiling. +- Persistent frames conflict with the target equal dynamic columns behavior. +- Workspaces are assigned per output. + +### 7. rivulet + +Repository: + +Fit: watchlist only. + +Reasons to consider it: + +- Haskell. +- Configured in a Haskell DSL. +- Conceptually closest to the desire for an XMonad-like programmable WM. + +Risks: + +- The README currently says not to use it and that it is not usable as a window + manager yet. +- Many core behaviors are still unfinished. + +### Historical Option: wideriver + +Repository: + +Fit: not a latest-protocol candidate. + +Reasons it is still worth knowing about: + +- Inspired by DWM and XMonad. +- Has layouts closer to classic dynamic tiling expectations. +- More established than several current-protocol candidates. + +Reason not to choose it now: + +- It belongs to the older layout-generator model, not the latest River + `river-window-management-v1` window-manager model. + +## Requirement Fit + +### Dynamic Tiling + +Best candidates: `kwm`, `orilla`, `beansprout`. + +`kwm` has the broadest ready-made layout set. `orilla` is the better extension +target if writing a custom layout is acceptable. + +### Equal Vertical Columns + +No candidate clearly satisfies this out of the box. + +Likely approaches: + +- Test `kwm` grid layout first. +- Implement an `EqualColumns` layout in `orilla`. +- Avoid primary/stack defaults unless they can be configured into equal columns. + +### Monocle or Fullscreen-Like Layout + +Best candidates: `kwm`, `orilla`, `notion-river`. + +`kwm` has monocle. `orilla` has a fullscreen/monocle-like layout. `notion-river` +has strong tabbed behavior but fails the dynamic-layout requirement. + +### Directional Navigation and Window Movement + +Best candidate: `rhine`. + +`rhine` exposes directional focus and movement commands. `kwm` and `orilla` +appear to need either custom action work or careful testing to determine whether +geometric WASD behavior can be made exact. + +### Workspace Movement + +Best candidates: `orilla`, `kwm`, `rhine`. + +`orilla` has explicit tag actions for last tag, next/previous tag, next empty +tag, and shift-to-empty. `kwm` has tags and shift-tags. `rhine` has explicit +send-to-workspace and move-to-workspace commands. + +### Scratchpads and Minimization + +No candidate clearly solves this in the target form. + +Likely implementation requires reserved hidden/special state in the window +manager, not just shell scripts. River's protocol has hide/show primitives, so a +native WM implementation has enough substrate, but each candidate needs review or +extension. + +### Overview and Discovery + +No candidate clearly provides a Hyprexpo-like overview. + +Practical first implementation is probably a rofi/launcher workflow built from +window state. `lswt` may be useful as a compositor-neutral Wayland toplevel +source, but accurate workspace positioning is likely WM-specific. + +### Taffybar State + +Best candidate to investigate: `rhine`. + +The partial Hyprland IPC compatibility is the only candidate feature that looks +directly relevant to existing Hyprland-oriented tooling. Otherwise, taffybar +will need a River/current-WM backend or a normalization daemon. + +Required state includes: + +- workspaces/tags +- focused output +- active workspace per output +- workspace history per output +- windows per workspace +- app-id/class, title, active state, urgency if available +- minimized and scratchpad filtering +- approximate window position + +## Proposed Prototype Plan + +1. Package or run `kwm` and `orilla` locally without making either a default + login session. +2. Start with `kwm` to measure out-of-the-box usability: tags, previous tag, + grid, monocle, send-to-tag, shift-to-empty, and output movement. +3. In parallel or immediately after, sketch an `orilla` configuration with an + `EqualColumns` layout and the workspace actions needed by the spec. +4. Do a small `rhine` spike only for Hyprland IPC and taffybar feasibility. +5. Choose between `kwm` and `orilla` after testing layout fidelity and state + export. + +Expected decision: + +- Choose `kwm` if it gets close enough with configuration and light scripting. +- Choose `orilla` if exact XMonad-like behavior matters enough to write Rust. +- Do not choose `rivulet` until it becomes usable. +- Do not choose `notion-river` unless the dynamic-layout requirement changes. + +## Orilla Deep Dive + +Commit checked: `bd77afb`, "adds support for finding next empty tag". + +### What Orilla Already Has + +- Rust code-first configuration. +- A small action model for keybindings. +- Dynamic layout abstraction through the `Layout` trait. +- Built-in `Tall` and `Full` layouts. +- Per-tag layout selection hooks. +- Tags with switch, toggle, shift, last, next, previous, next empty, and + shift-to-empty actions. +- Window tracking for internal IDs, app-id, title, geometry, focus, tags, + hidden state, and closed state. +- River hide/show calls for non-visible windows. +- A simple JSON IPC stream over a Unix socket containing tags, focused tag, + windows, app-id/title, focused window state, and current layout. +- Passing test suite at the checked commit. + +### Directional Navigation + +Orilla does not currently provide geometric directional navigation. + +Existing window actions are: + +- close focused window +- focus primary +- focus next +- focus previous +- promote focused window +- swap next +- swap previous + +This means `Super+WASD` cannot be mapped directly to "focus the closest window +in that direction" today. + +The good news is that Orilla stores enough geometry to implement it. Each window +has `x`, `y`, `width`, and `height`, and layout application updates those fields. +A plausible patch would add: + +- `Direction::{Up, Down, Left, Right}` +- `WindowAction::FocusDirection(Direction)` +- `WindowAction::SwapDirection(Direction)` +- geometry helpers on `WindowState` +- keybinding constructors such as `action::focus_direction(Direction::Left)` + +The selection heuristic should match XMonad-style behavior: compare candidate +window rectangles against the focused window rectangle, filter to the requested +direction, prefer overlapping windows on the perpendicular axis, then minimize +distance. + +### Equal Columns Layout + +Orilla does not currently include the desired equal-width vertical columns +layout. + +This is straightforward to implement as a new `Layout`: divide the output width +by the number of visible windows, give each visible window full output height, +and distribute remainder pixels across the first columns. + +This is likely easier in Orilla than in most alternatives because layouts are +plain Rust implementations of a small trait. + +### Fullscreen and Monocle + +Orilla has a `Full` layout. It places every visible window at the full output +rectangle and sorts the focused window last so it renders on top. + +This covers monocle-style behavior, but it is not tabbed. Tab UI would need a +separate layer-shell surface, IPC consumer, or native extension. + +Directional focus could still work in `Full` if implemented against window order +as a fallback when all rectangles overlap. + +### Workspaces and Tags + +Orilla's tag model is useful for the target workspace behavior: + +- switch to specific tag +- move focused window to specific tag +- toggle visible tag +- last tagset +- next/previous tag +- next empty tag +- move focused window to next empty tag + +Gaps: + +- "move window to workspace and follow" is not a first-class action, but can be + added by combining shift and switch semantics. +- Last workspace is global to the tag manager, not per monitor. +- There is no monitor-specific workspace history yet. + +### Monitors and Outputs + +This is the largest architectural gap after directional navigation. + +Orilla tracks River outputs, but windows do not currently carry an output +assignment. Layout application uses the first output from the output map and +lays out the global visible window set there. That is enough for early/single +monitor use, but not enough for the target multi-monitor experience. + +To meet the spec, Orilla likely needs: + +- active output tracking +- window-to-output assignment +- per-output visible tag/history model, or a shared tag model with explicit + output occupancy +- layout calculation per output +- focus-output and send-window-to-output actions +- useful focus behavior after moving a window between outputs + +### Scratchpads and Minimization + +Orilla has the basic substrate but not the feature. + +The substrate: + +- windows have tags +- windows have hidden state +- the WM can call River hide/show +- app-id/title are tracked + +Missing: + +- named scratchpad rules +- delayed title/app-id matching policy +- restore behavior +- minimized-window list/order +- keybinding actions for hide/restore +- IPC fields distinguishing normal, scratchpad, and minimized windows + +### Taffybar State + +Orilla's existing IPC is promising but too small. + +Current IPC exports tags, focused tag, current layout, and each tag's windows +with app-id/title/focused. It does not yet export output identity, active output, +window geometry, hidden/minimized/scratchpad classification, urgency, or +per-monitor history. + +This is still a better starting point than having no state export, because it is +already JSON over a Unix socket and sits inside the WM where the necessary state +should live. + +### Bottom Line + +Orilla has the right shape if we are willing to write WM code. It does not have +the exact daily-driver behavior today. + +The likely minimum patch set before serious use is: + +1. Equal-columns layout. +2. Geometric directional focus and swap. +3. Move-focused-window-to-tag-and-follow. +4. Multi-output model. +5. IPC expansion for taffybar and window menus. +6. Scratchpad/minimize state.