# Changelog ## 0.1.0 — unreleased - Rust CLI daemon: `enboxer run|press|status|macros|doctor` - YAML profile: maps, targets (`current` / `others` / `all` / groups), `game_binds` - Configurable `passthrough` (empty by default; example profile uses ESDF) - Hyprland 0.56 Lua binds installed only while a managed game or VFX overlay is focused - Key delivery via `hl.dsp.send_shortcut` / `send_key_state` (no game injection) - Stock loot map: assist → CTM on → Interact with Target once → delay → CTM off - Video FX: `grim` region capture, overlay viewer, hover pass-through to source slot - Unit tests for hotkey parse, passthrough, targets, loot sequence, example profile load - Bind IPC line no longer duplicated `ipc` (Hyprland hotkeys actually reach the daemon) - Video FX overlay is `mpv` (`--wayland-app-id=enboxer-vfx`) with JSON reload; `grim` capture - `enboxer run` clears binds on ctrl-c; empty `window_match` matches nothing - Live check: `send_shortcut` to an unfocused XWayland window; overlay window class `enboxer-vfx` - Per-character `assist_key` / `follow_key`; map steps using `bind: assist|follow` resolve to the current main's keys - Layout wizard `borderless` toggle installs a Hyprland `window_rule` (rounding 0, border_size 0) on `run` - Layout page: drag canvas over the monitor rectangle; tile moves clamp to the monitor bounds - T8: clipboard IPC verb `clipboard` reads `wl-paste` / `xclip` and delivers Ctrl+V to every non-leader captured slot; GUI button on Session page; routing plan unit-tested - T11: named-profiles list in File menu (`Load named…` submenu) with Refresh; listing helper unit-tested against a tempdir - T12: unit test for `type_to_others` routing (`others_clients` helper, excludes leader, per-char × per-slot plan) and for `apply_layout` short-circuiting when `ENBOXER_ALLOW_LAYOUT` is unset - T9 (real): `src/wayland_layer.rs` — `zwlr_layer_shell_v1` Wayland client with shm-backed buffers, 3×5 bitmap digit glyphs, `wl_pointer` click routing; per-slot `OverlayHandle` via `OverlayHub` in `session::run`. Live path gated behind `ENBOXER_ENABLE_OVERLAY=1`; `cargo test` does not connect to Wayland - T10 (real): `src/toplevel_export.rs` — `zwlr_export_dmabuf_unstable_v1` Wayland client; per-frame format negotiation (ARGB8888 / XRGB8888), synthetic-PNG fallback for tests. Live path gated behind `ENBOXER_ENABLE_TOPLEVEL=1`; `gbm_bo_map` is documented as the upgrade step for actual pixel reads - T13: mirror-mode mouse click broadcast — `hypr::deliver_click` posts compositor pointer events to every non-leader captured slot; press-and-hold guard deferred - T14: round-robin bind target (`round_robin` / `rr`) — rotates through ALL slots including the leader, in slot-number order, wrapping; per-map cursor - T15: Teams + Lutris launcher. `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 merging Lutris config with per-character wine-prefix override). GUI: Teams menu (New / Switch / Refresh / Show / Delete) and Launch menu (per-character + Launch all); Lutris picker visible only in the New-team flow. Direct Wine spawn — never calls `lutris` CLI - `game_binds.interact` example uses **Alt+J** (Master's request). Interact with Target is a built-in WoW keybind; enBoxer just routes the keystroke. - `docs/MACROS.md` rewritten for current retail (The War Within era, 2026): rename "CTM" → Auto-Interact (the CVar), document Alt+J for Interact with Target, document per-character Assist / Follow macro override, add optional Pet Attack / Target Last Target keybinds. ## Unreleased — Interact chain (ISBoxer-style mapped key) - **Smart interact shortcut.** A step with `bind: interact` now expands at compile time into the full CTM-on → Interact-with-Target → sleep `walk_delay_ms` → CTM-off chain, driven by `profile.interact` (`style` + `walk_delay_ms`). One user keypress, four keystrokes dispatched to every captured slot. Styles: - `standard` (default): chain runs, CTM ends off. - `auto`: chain runs, CTM stays on (toggle via `ctm_off` later). - `hold`: press fires CTM-on + Alt+J; put `bind: ctm_off` in `release_steps` to fire it on hotkey release. - The example `loot` map (Alt+G) and `interact` map (Alt+I) now use the shortcut. A `loot_manual` map (Ctrl+Alt+G) exercises the explicit chain for users who want per-step control. - New tests: `interact_smart_shortcut_standard_emits_full_sequence`, `interact_smart_shortcut_auto_emits_two_no_tail`, `interact_smart_shortcut_hold_emits_press_only`. 92/92 unit tests pass; clippy clean. - Docs: `docs/MACROS.md` "Smart interact shortcut" section, `docs/NOTES.md` callout, `examples/profile.yaml` updated with comments. > Note: After this change, `bind: "interact"` is a single Alt+J send (game_binds.interact). > The full chain trigger is now `bind: "smart_interact"`. Examples and tests updated. - **Security:** IPC runtime dir is now `chmod 0o700` and the Unix socket is `chmod 0o600` immediately after `UnixListener::bind`. Any local user with read access could speak our IPC protocol (`Command::Type` etc.); this is the cheapest defense. Helpers: `profile::chmod_runtime_dir()` and `profile::chmod_socket(path)`. - **GUI:** New-team wizard now pads `profile.characters` to `slots` before stamping `lutris_game`. A wizard with 5 members and an empty character list previously saved a team with zero Launch rows; it now fills the rows with `Character::default()` and applies the Lutris game to each. - **GUI:** `arm_auto_apply` now waits for a client matching `profile.window_match.class` / `.title` regexes before firing `layout-apply`, instead of firing on the first non-empty client list. With no patterns configured it still requires a non-empty class on the matched client, so existing profiles behave the same. This prevents `layout-apply` against the desktop when only a terminal (or any unrelated window) is open. - **Profile:** `Character` derives `Default` (needed for the padding). - **Overlay:** `set_margin` arguments corrected to `(top, right, bottom, left)`. The previous `(rect.y, rect.x, 0, 0)` passed `rect.x` as the *right* margin, which is a no-op under TOP+LEFT anchoring — so the overlay's x offset was silently dropped. Now `(y, 0, 0, x)` which pushes the surface down by `y` and right by `x`. - **Overlay:** Left-click on a slot no longer sets `state.exited = true`. Doing so dropped the badge and left `OverlayHub.by_slot` still holding the slot, so the hub refused to respawn it. The compositor's `zwlr_layer_surface::Closed` event is the only path that tears the live thread down — click just sends the swap IPC and returns. - **Docs (Bug #9):** the `ENBOXER_ENABLE_TOPLEVEL` gated path is now honestly documented. The current implementation requests `capture_output(...)` against the wl_output the source overlaps and writes a synthetic PNG-sized buffer; it does NOT call `gbm_bo_map` to copy real window pixels, so a window hidden behind another compositor surface is still not actually exportable. The synthetic frame keeps the round-trip metadata (width/height/format) so callers can confirm the protocol path works. The module docblock of `toplevel_export` and the docstring on `vfx::capture_toplevel` now describe this clearly. Real pixel read is a follow-up after the gbm_bo_map work.