Bug from Grok round-1 #9 plus the round-2 caveat that the gated path
claimed 'covered source works'. In reality the implementation issues
capture_output(...) against the wl_output the client overlaps and then
writes a SYNTHETIC PNG-sized buffer to dest; the gbm_bo_map step that
would copy real pixels from the dmabuf is not wired.
Changes:
- src/vfx.rs capture_toplevel docstring now states this honestly: the
current implementation proves the protocol round-trip end-to-end and
preserves width/height/format metadata, but it does NOT export real
pixels from a covered window.
- src/toplevel_export.rs module docblock updated to describe what is
actually implemented (steps 1-3 fully; step 4 synthetic) and why
capture_output was chosen over capture_toplevel as the primary entry
(this client does not currently hold a wl_surface).
- capture_via_export public docstring updated similarly.
- CHANGELOG.md entry.
Real pixel read is a follow-up tracked under the gbm_bo_map work. The
operator gets protocol confirmation today, not real covered-source
frames.
cargo test 96+/0; clippy clean.
- set_margin (wayland_layer.rs:234): correct arg order to
(top, right, bottom, left). The previous code passed rect.x as
the right margin, a no-op under TOP+LEFT anchoring, so the
overlay's x offset was silently dropped. Comment updated to
document the protocol's true shape.
- click handler (wayland_layer.rs:466): drop 'state.exited = true'
after send_swap. Flipping exited destroyed the badge but 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 drives thread teardown — the click now
just fires the swap IPC and returns.
- CHANGELOG entries.
cargo test 96+/0; clippy clean.
- arm_auto_apply (gui.rs ~1625): compile profile.window_match.class and
.title regexes once, then poll hyprctl for a client that matches
before firing layout-apply. Fall back to 'first non-empty class'
when neither pattern is configured, so existing profiles keep working.
Closes the security/correctness gap where any open window could trigger
layout-apply against the desktop.
- New-team wizard (gui.rs ~1438): profile.characters.truncate is replaced
with resize(slots, Character::default()) so a wizard with N members
and zero existing characters now actually gets N Launch buttons.
Character gains #[derive(... Default)].
- CHANGELOG entries.
cargo test 96+/0; clippy clean.
fix(security): the daemon's IPC socket was world-accessible. Any local
user could speak the IPC protocol and type arbitrary text into game
windows via Command::Type.
- src/profile.rs: add chmod_runtime_dir() and chmod_socket(path) helpers
- src/session.rs: call chmod_runtime_dir() after create_dir_all, and
chmod_socket() right after UnixListener::bind succeeds
- Tests: chmod_socket_sets_0o600, chmod_runtime_dir_sets_0o700 (saved/
restored live dir perms to avoid clobbering a real session)
- CHANGELOG.md: security note
cargo test 96+/0; clippy clean.
bind: "interact" now resolves to game_binds.interact (Alt+J) only —
a single keystroke. The full ISBoxer-style chain (CTM on -> Alt+J ->
sleep walk_delay_ms -> CTM off) lives at bind: "smart_interact".
Fixes the doubling bug Grok flagged: bind: "interact" previously
expanded into the full chain unconditionally, so the loot_manual and
interact_hold example maps fired ctm_on/ctm_off twice and produced
unwanted sleep delays.
- engine.rs: rename shortcut trigger (was: "interact")
- examples/profile.yaml: loot now uses smart_interact; loot_manual and
interact_hold keep "interact" as a single Alt+J send
- docs/MACROS.md: heading + shortcut comment
- docs/NOTES.md: trigger name
- Add new test: interact_simple_sends_only_alt_j (1-line single-send)
- Existing smart shortcut tests renamed to bind: smart_interact
- CHANGELOG.md: trigger split note
93/93 cargo test pass; clippy clean.
A step with bind: interact now expands at compile time into the full chain
(CTM on -> Interact with Target -> sleep walk_delay_ms -> CTM off), 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; bind: ctm_off in
release_steps to fire it on hotkey release
Example loot (Alt+G) and interact (Alt+I) maps now use the shortcut. A
loot_manual map (Ctrl+Alt+G) exercises the explicit chain for users who
want per-step control.
92/92 unit tests pass; clippy clean. New tests:
- interact_smart_shortcut_standard_emits_full_sequence
- interact_smart_shortcut_auto_emits_two_no_tail
- interact_smart_shortcut_hold_emits_press_only
Docs: MACROS.md (new Smart interact shortcut section with style table),
NOTES.md callout, examples/profile.yaml comments, CHANGELOG.md entry.
Hyprland binds called `ipc` twice so mapped keys never reached the daemon.
Overlays now use mpv with wayland-app-id and JSON reload. Empty window_match
matches nothing. Ctrl-c clears binds. Proven: send_shortcut to unfocused
XWayland, mpv overlay class enboxer-vfx.