Grok round-6 verification was No-Go. Fixes:
A. LayoutSlot.id was written as 0 by every constructor
(generate Stacked/Grid/main_strip, capture_from, the GUI pad
literals), so the find-by-id lookups in resize_slot / move_slot
never hit a tile and Free-mode Apply size/position always
failed with "no layout slot for slot id N". Constructors now
assign real 1-based ids (index+1, out.len()+1, i+2 for the
strip); capture_from uses the slot id from the window tuple.
layout::apply and reset_slot_lock now find the tile by id
instead of by Vec index.
B. The `slots` IPC formatter emitted "{id} 0x{address} ..." while
addresses already carry their own 0x prefix ("0xa"), producing
"1 0x0xa ..."; the parser split on whitespace so any multi-word
window title broke the field alignment. Both sides now use a
tab separator and the address passes through unchanged.
C. examples/profile.yaml still shipped the dropped schema
(window_match block + passthrough list). Replaced with a note
that matching is by process tree and every mapped hotkey is
intercepted.
D. CHANGELOG 0.1.0 still advertised passthrough (lines 19, 24)
and window_match (27, 78). Annotated as removed.
E. Lying comments: launcher.rs called the prefix "per-team" (it is
per-character); gui.rs::arm_auto_apply doc claimed it matched a
regex.
F. arm_auto_apply still hardcoded an empty spawned-pid set and
fell back to matching any client with a non-empty class -- the
round-4 Item-3 placeholder was what actually ran. It now takes
the real child pid and matches via pid_is_ancestor.
G. spawn_plan dropped the Child with no wait thread (zombie, same
bug round-6 fixed in launch_game). Now reaps in a background
thread.
H. page_session never refreshed the games list, so the dropdown
was empty on first paint. Added a games_loaded flag and a
one-shot refresh_games on first paint.
Plus: vfx env-var test race. toplevel_enabled_defaults_off_... and
capture_toplevel_is_gated_when_disabled both touch
ENBOXER_ENABLE_TOPLEVEL and cargo runs unit tests in parallel, so
the gated test intermittently saw the var set by its sibling (the
per-function `static` in session.rs does not serialise across
functions). Added a module-level ENV_LOCK in vfx::tests and
guarded both tests. Verified with three consecutive full runs.
Also: clippy unnecessary_cast in main_strip, and two rustdoc
warnings (raw <pid> and Arc<GbmDevice> read as HTML tags).
cargo test 103/103 (x3); clippy --all-targets -D warnings clean;
cargo doc --no-deps clean.
116 lines
8.5 KiB
Markdown
116 lines
8.5 KiB
Markdown
# Changelog
|
||
|
||
## Round 4 (2026-09-17)
|
||
- Dropped the `passthrough` skip-list feature (Item 6).
|
||
- Dropped `WindowMatch` regex matching; replaced with process-tree
|
||
tracking via `crate::process::pid_is_ancestor` (Item 3).
|
||
- Renamed default profile from `esdf-team` to `team` (Item 5).
|
||
- Window layout now supports Managed + Free-arrange modes
|
||
(Item 1) with `resize-slot` / `move-slot` / `reset-slot` IPC.
|
||
- Session game-launcher dropdown reads `~/.config/enboxer/games.yaml`
|
||
via `launch-game` / `list-games` IPC (Item 4).
|
||
- Keybinds + VFX page replaced with isboxer-style tree UI
|
||
grouped by category (Item 2).
|
||
|
||
## 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` (removed in round-4 Item 6)
|
||
- 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, 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 (round-4 Item 3 replaced window_match with process-tree matching)
|
||
- 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
|
||
the process tree rooted at a spawned PID before firing (round-4 Item 3)
|
||
`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 shipped (commit `0ba3c59`) via a runtime dlopen of `libgbm.so.1`; setup needs no `libgbm-dev` build dep.
|
||
- **T9 (overlay live render):** already shipped as part of the T7..T15
|
||
batch (). creates a wl_shm pool from a
|
||
temp file containing pixels,
|
||
builds a wl_buffer from the pool,
|
||
attaches + commits it to the layer surface, and runs the dispatch
|
||
loop for pointer click + compositor Closed events. Combined with
|
||
commit (overlay handle kill switch), the overlay thread
|
||
lifecycle is complete: create on slot enter, repaint on event,
|
||
tear down cleanly when the hub flips the stop bit or the
|
||
compositor sends Closed.
|