Layout wizard (T7):
- App::layout_canvas with monitor backgrounds, click-to-select, draggable tiles
- borderless Hyprland window_rule on run
- App::refresh_monitors + Refresh monitors button; monitors cache for canvas sync
Routing extras (T8, T13, T14):
- clipboard IPC verb (wl-paste / xclip -> Ctrl+V to non-leader slots)
- mirror-mode mouse click broadcast via hypr::deliver_click
- round_robin / rr bind target rotates through ALL slots (leader included)
Slot overlay (T9 real):
- src/wayland_layer.rs: zwlr_layer_shell_v1 client, shm buffers, 3x5 bitmap
digit glyphs, wl_pointer click -> swap <slot>
- gated ENBOXER_ENABLE_OVERLAY=1; cargo test does not connect
Covered-window VFX capture (T10 real):
- src/toplevel_export.rs: zwlr_export_dmabuf_unstable_v1 client
- ARGB8888 / XRGB8888 format negotiation, synthetic-PNG fallback for tests
- gated ENBOXER_ENABLE_TOPLEVEL=1; gbm_bo_map upgrade documented
Teams + Lutris launcher (T15):
- src/team.rs: Team, list_teams, teams_dir, current_team
- src/lutris.rs: LutrisGame parser, load_all with bad-YAML tolerance
- src/launcher.rs: SpawnPlan merges Lutris config + per-character wine-prefix
- GUI: Teams menu (New / Switch / Refresh / Show / Delete) + Launch menu
- Lutris picker visible only in New-team flow; direct Wine spawn, no lutris CLI
Tests: 89 passed; 0 failed (up from 24).
Clippy: clean with -D warnings.
Safety:
- No live hyprctl dispatch that moves / resizes / pins / closes the session.
- All Wayland paths feature-gated; cargo test does not connect.
- apply_layout still gated by allow_layout + confirm_apply.
Files: 14 modified + 6 new (src/{launcher,lutris,overlay,team,toplevel_export,wayland_layer}.rs)
Diff: +1570 / -29
2.0 KiB
Live crop (Video FX)
You pick two rectangles:
- Source — which part of another client to copy, and how large that crop is.
- Viewer — where that crop is drawn on the primary client, and how large it is shown.
Numbers 0–1 are fractions of that window. Numbers greater than 1 are pixels inside the window. In the GUI, Pick source / Pick viewer uses slurp and stores pixels.
When the mouse is over the viewer, clicks and keys go only to the source client, not to the primary and not to the rest of the team.
Configure this in the Video FX page of the control panel (enboxer with no arguments).
It is not a second monitor and not a window swap. The other client can sit behind the main one; you still see the cropped piece.
What Hyprland can actually do
| Method | Sees a window that is fully covered? | What we use |
|---|---|---|
grim of a screen rectangle |
No — only pixels currently on the output | Yes, default |
hyprland-toplevel-export |
Yes — compositor copy of that window’s buffer | Yes, gated behind ENBOXER_ENABLE_TOPLEVEL=1 |
| Desktop portal / PipeWire | Yes, heavier | No |
The dispatcher in src/vfx.rs::capture_for_source checks is_window_visible(monitors, client). If the source window sits on a visible output, grim captures it. If the window is covered and the operator has opted in to compositor-side capture (ENBOXER_ENABLE_TOPLEVEL=1), the dispatcher routes to capture_toplevel (zwlr_export_dmabuf_unstable_v1). With the gate off, covered windows fall through to grim and the operator sees whatever is currently on top of those pixels — exactly the previous behaviour.
So: keep the source window on a visible output for the default grim path. Set ENBOXER_ENABLE_TOPLEVEL=1 when you want a stacked layout where some sources are fully covered by another; the live toplevel-export client is a follow-up and the dispatcher is ready for it.
The overlay itself is an mpv window (class: enboxer-vfx) that reloads each grim frame.