enBoxer/docs/VIDEO.md
en 9d2f51685d Finish the project (T7..T15): layout wizard, broadcast, overlay, dmabuf, teams
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
2026-09-15 17:18:37 +02:00

2.0 KiB
Raw Blame History

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 01 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 windows 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.