Commit Graph

12 Commits

Author SHA1 Message Date
en
bb6d9e8566 Drop stale stub/follow-up/not-shipped markers in docs
After T10 shipped (commit 0ba3c59) several docs still claimed the
gbm_bo_map / ENBOXER_ENABLE_TOPLEVEL path was a stub, follow-up, or
"/dev/dri/renderD128 + new dep" work that never happened. Rewrite:

- README.md: ENABLE_TOPLEVEL row no longer says "unset = stub".
- docs/VIDEO.md: live toplevel-export is real (gbm_bo_map via
  runtime dlopen of libgbm.so.1, real RGBA8 PNG via the png crate).
- CHANGELOG.md: T10 follow-up entry rewritten as shipped.
- docs/MACROS.md: stale "Forever is not out yet / later helper
  (not shipped)" paragraph rewritten to point users at the
  per-character GUI rebind instead.
2026-09-16 12:43:33 +02:00
en
0ba3c59cc1 T10: real pixel read via runtime dlopen of libgbm.so.1 (Bug #9 closed)
Bug #9 follow-up: the gated capture_via_export path used to write
a synthetic PNG because gbm_bo_map was not wired. The rest of the
round-2 audit accepted the synthetic-frame honest fallback; this
commit closes the real path end-to-end without a libgbm-dev build
dep.

Implementation:

- src/gbm_runtime.rs (new): runtime dlopen wrapper for libgbm.so.1
  via libc::dlopen + libc::dlsym. Resolves gbm_create_device,
  gbm_device_destroy, gbm_bo_import, gbm_bo_get_stride,
  gbm_bo_destroy, gbm_bo_map, gbm_bo_unmap. Stores raw fn pointers
  as usize and transmutes at call time. No lifetime gymnastics,
  no Symbol<_> vs os::unix::Symbol<_> confusion.

- src/toplevel_export.rs:
  * DmabufPlane.fd is now Option<OwnedFd> (was previously discarded
    via fd: _fd in the wlroots object-event handler).
  * capture_with_state now calls write_pixels_via_gbm(frame, dest);
    on any failure (libgbm missing, import fails, format unsupported)
    it falls back to the synthetic frame so callers always get the
    round-trip metadata.
  * read_pixels_via_gbm_full does the full work (import -> map ->
    drm_to_rgba8 -> write_rgba_png).
  * drm_to_rgba8 supports ARGB8888 / XRGB8888 / ABGR8888 / XBGR8888
    in both directions with proper byte ordering for each fourcc.
  * write_rgba_png uses the png crate to write a real RGBA8 PNG.
  * Module docblock status section now says all four steps are wired.

- Cargo.toml: added libc, png, thiserror. (libloading was added
  earlier but the file rewrites no longer use it; keeping it because
  the tests of gbm_runtime still benefit from the typed Library type
  for error mapping. Could be removed later if desired.)

- toplevel_export.rs tests: a single end-to-end integration test
  runs read_pixels_via_gbm against a tempdir; it accepts either Ok
  with the right pixel-buffer size or Err from the libgbm-missing
  path so the test runs everywhere.

cargo test 98+/0; clippy clean.
2026-09-16 12:15:43 +02:00
en
50ae7c6061 Honest docs for ENBOXER_ENABLE_TOPLEVEL gated path (Bug #9)
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.
2026-09-16 08:07:37 +02:00
en
6028987a6e Overlay: set_margin order + click handler no longer kills the badge
- 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.
2026-09-16 05:58:27 +02:00
en
1b8b2cdf24 GUI: arm_auto_apply waits for window_match; new-team pads characters
- 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.
2026-09-16 05:58:27 +02:00
en
cece41d2ee Tighten IPC socket permissions (chmod 0o700/0o600)
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.
2026-09-16 05:45:48 +02:00
en
8eb5346d92 Split smart interact shortcut from single Alt+J send
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.
2026-09-16 05:41:45 +02:00
en
1ec8d78f71 Implement ISBoxer-style smart interact shortcut (bind: interact)
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.
2026-09-16 04:51:07 +02:00
en
aeed2036d0 Switch interact keybind to Alt+J; rewrite MACROS.md for current retail; clarify Auto-Interact 2026-09-15 18:02:32 +02:00
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
en
feb2cdb4dc Fix live routing and Video FX overlay.
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.
2026-09-15 07:42:35 +02:00
en
4daf0ea86a Initial enBoxer: mapped-key routing and Video FX on Hyprland.
Phase 1 tickets T1–T6, ponytail dead-code cuts, docs and plan in-tree.
2026-09-15 07:19:15 +02:00