Commit graph

18 commits

Author SHA1 Message Date
7f756ba8ee Launch CLI tabs in parallel; slot-claim by process group
- All command-line tabs fork at startup; the last one is active and shown
  black until its first frame maps, matching the existing README contract
  that the last command's window appears immediately.
- Claim incoming toplevels by process group, not exact pid, so wrapped
  commands (sh -c "foot -e htop") keep their own slot even though the
  sh wrapper forks the real client.
- Closed tabs are fully removed: remove_app() compacts the array, frees the
  slot, clears parent refs, and prefers the left neighbor as the successor
  (right on explicit close, mirroring the old behavior on destroy).
- Black (pending) tabs are live slots: cycle/reorder walk them, activation
  logs '[diag] active slot=N (black, awaiting map)', and Alt+Delete closes
  one outright.  A SIGCHLD-latched sweep drops black tabs whose whole
  process group exits before ever connecting (e.g. /bin/false).
- The map-reveal rule keys on the scene node still being disabled, since a
  slot's toplevel is set at claim time (before its map event).
- Update the standalone/headless and nested tests to wait for a mapped
  frame (the startup tab is active before it maps, so the old 'no tab was
  ever activated' wait no longer fires), and add two tests covering
  Alt+Delete on a black active tab and the never-connecting-command sweep.
2026-08-18 22:41:27 +10:00
5d840a09dd new-tab chord is Alt+Shift+T (prefix-based) and grabbed
Change Ctrl+Shift+T to Alt+Shift+T (Super+Shift+T under --modifier super),
matching the other tab-management chords.  It is now grabbed: process_key_event
returns true so the focused client never sees the chord, instead of passing it
through.
2026-08-18 13:47:00 +10:00
13154c78b5 Launch CLI tabs serially at the connection gate; crown the last command
Commands fork one at a time, each gated on the previous one's client
connecting, so tab order always mirrors the command-line order (no pid
matching, no /proc ancestry).  The window stays black until the last
command's tab maps, which is then crowned active.  A new tab never
steals focus on its own: only Ctrl+Shift+T terminals, sub-windows of
the tab currently shown, and the startup crown activate.  Sub-windows
(a second toplevel from an already-connected client) get their own slot
glued right after their parent tab.  A command that never connects
stalls the chain and the crown, leaving the window black (accepted).

Tests: wait_any_active waits for the single deterministic crown; an
externally-spawned client is now born hidden (cycle to it).  Add wltest
--extra (same-client second toplevel) plus tests for sub-window
placement/focus and Ctrl+Shift+T focus-on-map.  Update README.
2026-08-18 11:20:29 +10:00
e0af748a4b add --modifier alt|super to switch tab chords from Alt to Super
Standalone/DRM and labwc-style hosts (e.g. RPi5) keep Super free, while
GNOME steals it, so Alt stays the default.  The tab-management prefix
modifier (hold=overlay, Up/Down=cycle, Shift+Up/Down=reorder,
Delete=close) is now chosen at startup from the xkb keymap (Mod1 or
Mod4) with a raw wl_keyboard bit fallback when xkb init fails;
Ctrl+Shift+T is unchanged.
2026-08-17 11:44:27 +10:00
f6c1a6b92e Grab Alt+arrows and rotate tabs when reorder wraps past an end
A focused terminal acted on Alt+Shift+Up/Down (selection/typing) because
the reorder chord was forwarded like plain Alt+Up/Down cycling was.  Both
are now grabbed: tabbed consumes them so the focused client never sees
them.

Reordering off an end previously swapped the moved tab with the far-end
tab, leaving the middle of the list in place ([A,B,C,D] moving D off the
end gave [D,B,C,A]).  reorder_active_tab now shifts every slot between the
two positions, so the whole list rotates: [D,A,B,C].  Adjacent moves are
unchanged (rotation is a swap there).

The reorder test now runs three tabs to distinguish swap from rotation
([R,G,B] moving B off the end must land [B,R,G]) and keeps the
Ctrl+Shift+T anchor-slot check.  The clipboard tests got more patience
(retry on stalled wl-paste; longer socket-start wait) since a stalled host
GNOME display was flaking them.

Full suite 5/5 green.
2026-08-17 11:12:28 +10:00
6677012a9a Alt+Shift+Up/Down reorder the active tab within the overlay list
Move the active tab's slot in the tab order without switching the active
surface: the tab stays active and only its position changes, wrapping from
either end, and holding the chord keeps moving it via the same synthesized
repeat as cycling.  Only live tabs (a toplevel, not mid-close) count as
positions, mirroring cycle_tab.

The repeat latch is generalized with a repeat_kind so one timer serves both
cycle and reorder.  Ctrl+Shift+T inside the moved tab anchors right after
its new position (add_tab uses the active slot's index), which the new
test verifies.

Tests: 5x standalone + full suite 3x green (40 passed, 1 skipped).
2026-08-17 10:45:21 +10:00
fb29f03dc0 Alt+Delete closes the active tab; kill only pgids the compositor owns
Close the active tab on Alt+Delete: the tab vanishes immediately (active
moves to the right neighbor, or the left one for a rightmost tab), the
client's whole process group gets SIGTERM with a short SIGKILL escalation
if it ignores the TERM. Grabbed and repeat-latched so a single hold closes
one tab.

Slots are claimed in connection order (not launch order), so close signals
the process group recorded at claim time. Only groups the compositor owns
are group-signaled; a client started from the compositor's own terminal
shares our group and is killed by pid instead, so teardown can't group-kill
the launcher's session (a pytest run died 143/137 for exactly this).

Tests: settle the active-tab log before cycling (wait_any_active +
cycle_ack quiesce), spawn the extra client in its own session, retry
wl-paste when the host clipboard stalls, and only skip the host-colour
assertion when the host pixel is unobservable. Full suite 5/5 green.
2026-08-17 10:26:48 +10:00
382809b126 Handle Ctrl-Alt-Fn virtual-terminal switching in standalone mode 2026-08-16 07:39:37 +10:00
72b95f1204 Support standalone (native-output) mode alongside nested hosting
TABBED_MODE=standalone (or a visible WLR_BACKENDS) makes tabbed-wlroots its
own Wayland compositor rendering onto the first native output instead of a
nested host window. DRM+libinput is the default backend set; a headless
backend (created directly, since wlr_backend_autocreate wraps backends in a
multi backend that wlr_headless_add_output rejects) keeps the mode testable
in CI over the existing stdin feed.

Standalone:
- server_init creates a headless or autocreated backend and attaches a
  new_input listener; skips seat bridging (no host client to mirror).
- handle_new_output keeps the first output only, drives it at its preferred
  mode, and sizes the swapchain to the output instead of a host window.
- Font/icon overlays build CPU wlr_textures (premultiplied ARGB) alongside
  the existing GL textures so the tab overlay can be drawn by a second
  wlr_renderer pass over the committed output buffer.
- draw_tab_overlay_pass mirrors draw_tab_overlay's geometry with pass
  rects/textures; render_standalone builds scene state, applies the overlay,
  commits the buffer, and sends frame done. SIGUSR1 now also readbacks the
  composited frame (GLES2 FBO, or CPU data-ptr fallback) as /tmp/host_N.ppm.
- Native keyboards/pointers are attached to the seat and share the existing
  process_key_event/process_modifiers hotkey handling and the main loop's
  standalone branch (blocking dispatch + render on frame/redraw).

The nested mode is unchanged. Adds standalone smoke tests (boot/cycle and
Alt overlay readback under WLR_BACKENDS=headless); full suite 21 passing.
README documents the new mode.
2026-08-15 18:06:55 +10:00
5be0b23e68 Open a terminal tab by default and fail fast on missing commands 2026-08-15 11:29:53 +10:00
0632f8e3d0 Open a new terminal tab with Ctrl+Shift+T ($TERMINAL)
Adds a new-tab binding that launches a terminal (default alacritty,
overridable via the $TERMINAL environment variable, e.g.
TERMINAL="foot -e htop" tabbed-wlroots ...), inserting it right of the
active tab and activating it once it maps.

Also lets handle_new_toplevel claim exactly the add_tab()-prepared slot
instead of an earlier stale free one (a closed tab's hole), so the new
terminal keeps its position next to the tab it was launched from.
2026-08-15 09:16:09 +10:00
b8875f7292 Show per-tab app icons in the Alt-hold overlay 2026-08-14 12:55:42 +10:00
db6bbac84c Draw passive Alt-hold tab-title overlay with FreeType text 2026-08-14 12:39:51 +10:00
e52ac98afe Pass function keys through and mirror fullscreen between host and clients
Stop grabbing F9/F10/F11 so they reach the focused client (e.g. a
browser's F11); frame dumps move to SIGUSR1 only.  Honor client
fullscreen requests and mirror state both ways: a fullscreen client
fullscreens the host window, and a fullscreen host window fullscreens
every tab (anything else re-maximizes them).

wltest gains a wl_keyboard binding that toggles fullscreen on 'F', and
the smoke tests cover the full round trip plus function-key passthrough.
2026-08-14 11:31:32 +10:00
54c3c99e1d Cycle tabs with Alt+Up/Down matched by keysym
Ctrl+PageUp/PageDown and Ctrl+Tab are needed by real apps (browser tabs,
terminal scrollback), and a tabbed-wlroots hotkey consumes the combo before
the nested client ever sees it.  Alt+Up/Down is free on both the
GNOME host (no wm/mutter/shell binding) and labwc, so the keys reach the
compositor instead of being grabbed or swallowed. Alt+Up/Down is used in
some apps but in minor ways. I tried to use Alt+PageUp/PageDown but it
fails on some laptops like mine.
2026-08-14 10:31:50 +10:00
5052a9b3ba Fix Debian x64 build: drop nonexistent libwlroots-dev, build wlroots 0.19.1 without -Werror
Debian >= trixie packages wlroots versioned (libwlroots-0.20-dev,
pkg-config wlroots-0.20), which falls outside the >=0.19,<0.20 pin, so
the bundled 0.19.1 source build is used. Fix the README apt line
accordingly and build the wlroots subproject with werror=false so the
newer libinput (1.31.3) enum LIBINPUT_SWITCH_KEYPAD_SLIDE is only a
warning, not a fatal -Werror=switch error.
2026-08-10 09:25:52 +10:00
8acd384666 Document usage, keybindings, TABBED_LOG/dump semantics; fix F8->F9/F10 comment 2026-08-09 13:02:46 +10:00
6b049debf0 Build with meson; portable wlroots fallback (pinned wrap) + README 2026-08-09 12:46:14 +10:00