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
29 lines
2.0 KiB
Markdown
29 lines
2.0 KiB
Markdown
# Live crop (Video FX)
|
||
|
||
You pick **two rectangles**:
|
||
|
||
1. **Source** — which part of another client to copy, and how large that crop is.
|
||
2. **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.
|