title and app_id arrive as protocol events with no pixel damage, so the
overlay (which reads them live) never repainted while held -- a tab going
from black to content kept showing the command until the menu was closed
and reopened. subscribe to the xdg/x11 set_title and set_app_id signals
(and refresh on map) and repaint in place when the overlay is visible.
a tab whose window has not opened yet has no mapped members, so its
overlay row drew nothing and showed a blank title. draw the launch
command instead, sized into the panel (which now measures and counts
pending rows) and truncated to the row width if it would overflow.
each window title now sits in its own grey box matching the blue
highlight size, lightened so it separates from the panel, and rows are
spaced so the boxes no longer touch.
the tab overlay now lists every window of a tab (main plus stack members)
with a per-window icon and title instead of one icon and title per tab, and
highlights only the active window so the overlay cursor can move across all
of a tab's windows. titles are also centered within each row.
set_active_app used to discard the tab's focus_member when leaving it,
so coming back always re-focused the newest mapped member instead of the
window the user was last on. keep the pointer on leave and restore it on
enter (falling back to the newest member only if it is no longer mapped).
A second toplevel from an already-connected client is a sub-window of
that client's tab. tabs (default) keeps today's behavior: its own slot
glued right of the parent. stack never creates a slot: the window rides
above the parent tab's surface as a scene-tree child, sharing its
visibility, holding the keyboard while the tab is shown, and falling
back to the tab's own surface when it closes. Stacked windows are
invisible to cycling and the overlay; closing the tab closes them.
Overlay rows follow what sits on top of the tab: the row title and icon
resolve from the topmost mapped stacked window and revert when it goes,
with a [title] log line per row-title change for observability.
wltest grows an 'X' key that closes its --extra toplevel so the unstack
path is testable.
The clipboard bridge queues a data-control send to the nested client
while dispatching the host side, but the nested loop then blocked in
poll() before wl_display_flush_clients ran. With an idle server loop the
queued message sat unsent for seconds, so host-side wl-paste never
received the payload (the nested client gave up and the socket broke,
seen as EPIPE). Flush clients right after the host dispatch, before the
blocking poll. Also add DEBUG-gated tracing for the clipboard bridge
paths (host/data-control source sends, offer echoes, inner source
lifecycle) used to diagnose the stall.
The host window previously asked for server-side decoration; Mutter
forces client-side, so the window had no frame at all and could not be
dragged or resized. Hand the window to libdecor: it negotiates a
server-side bar when the host offers one and otherwise draws its own
titlebar with move/resize/close buttons (GNOME).
Host-primary pastes into the nested seat used to hang waiting for EOF:
bridge_inner_primary_source_send forwarded the demander's fd to the host via
data-control receive but never closed it, so opposite-end reads blocked until
the compositor closed it elsewhere (wlroots only does that for its own
client_source_send). Close the fd after the direct receive, and eagerly
prefetch the mirrored text/plain payload in a separate buffer so the common
paste is served synchronously without an async fd round-trip.
wltest gains --primary-demand-expect <file> <text> so a nested test can wait
for a specific payload: the live host may already hold its own primary at
boot, which kick faithfully mirrors first, and wlroots only sends offers to
the focused client on the seat. The new test crowns the demander tab before
the host writes its primary via wl-copy --primary.
Mirror the nested seat's zwp_primary_selection_v1 into the host via the
existing wlr-data-control v2 device (same pipe the clipboard uses), so a
middle-click inside a terminal publishes its primary to the host and the
host's own primary selection shows up in the nested tabs.
- host->nested: a real primary_selection offer is mirrored into the nested
seat as a wlr_primary_selection_source whose send pulls from the host
offer's fd; our own publish is tagged as an echo and swallowed (with the
same NULL-before-echo guard the clipboard already uses).
- nested->host: a nested set publishes a fresh data-control source offered
with the client's mimes; host middle-click requests are served straight
from the originating nested client.
- guarded by the bound data-control version (v1 hosts get no primary
channel, flagged in the init log); the wl_data_device fallback stays
primary-less as documented.
- wltest --primary-set/--primary-demand now trace the offer/send path.
- new nested-mode smoke test exercises the round-trip and asserts the
bridge publishes exactly once (no echo loop); suites now 57 passed.
Advertises zwp_primary_selection_v1 so text editors can publish and
middle-click-paste without a full clipboard roundtrip. The device-manager
global hooks the seat; the request_set_primary_selection handler installs
the source on the seat (wlroots only emits the request), and the seat's
focus-change/set fan-out delivers the offer to the keyboard-focused tab's
device, mirroring the clipboard path. No host bridging: the selection
stays within hosted tabs for now.
wltest gains --primary-set <text> and --primary-demand <file>. The setter
defers its set_selection until a seat-granted serial arrives (wlroots
rejects serials it never handed out), the demander pulls the payload over a
pipe polling both the pipe and the display socket (the compositor forwards
the fd to the owning tab, whose reply arrives with no display event of
ours). The smoke test cycles between the two tabs so the setter gets a
serial and the demander is refocused to flush the offer, then verifies the
payload lands in the file.
Adds zwp_pointer_constraints_v1 and zwp_relative_pointer_v1 so games and
creative apps can grab the cursor and get raw relative motion. The
focused surface's constraint is activated/deactivated on seat pointer
focus change; LOCKED pins the pointer to its activation position and
CONFINED clamps it to the constraint region (empty region = the whole
surface), applied through wlr_cursor_warp so the rendered arrow follows.
Relative deltas are forwarded from the real input paths: native
standalone motion sends the raw delta, nested mode derives it from
consecutive host positions.
meson generates the pointer-constraints protocol header (the system
wlroots header quotes it) and the wltest client stacks. wltest gains
--constrain locked|confined plus --confine-rect, and two smoke tests
verify the cursor stays pinned under LOCKED and clamped to the region
under CONFINED.
Clients relied on these to run at their best and fell back otherwise:
wp_presentation gives frame pacing feedback, zxdg_output_manager_v1
logical output geometry for newer toolkits, wp_viewporter lets video
clients scale buffers instead of reallocating. All three are passive
wlroots globals. The output layout now exists in both modes so the
nested host-window output is part of it; without that, a client's
get_xdg_output request hit wlr_xdg_output_v1.c's layout_output assert
and aborted the compositor. wltest gains --globals-out to dump the
advertised interface list (CLI-tab stdout is /dev/null'd by kick), and
a smoke test asserts the new globals are advertised.
A glyph with an empty bitmap (the space character reports 0x0 from
FreeType, and too-wide glyphs are skipped at atlas build, leaving
pw/ph at 0) was fed to wlr_render_pass_add_texture with an empty src
and dst box. wlr_render_texture_options_get_src_box/get_dst_box treat
an empty box as 'use the whole texture', so the entire 16x6 ASCII atlas
was rasterized into the tab row at the space's pen position -- the
onscreen-keyboard graphic seen over 'About galculator'. Mirror the
nested GL path (draw_texture_rect, which early-returns on src_w/src_h
<= 0) and only emit a quad when the glyph bitmap is non-empty.
Nested clients that switch the pointer cursor's shape now keep a correct
cursor over kick's window: advertise wp_cursor_shape_manager_v1, apply the
requested shape to the compositor cursor in standalone mode, and forward it
to the host pointer in nested mode via a hand-rolled client call (kick stays
a single C file, so the generated client header is not included). Shapes
are cached per tab and reset to default on tab switch so a text cursor never
leaks between apps. Legacy wl_pointer.set_cursor surfaces are not bridged.
The ctl pointer feed now also drives the nested seat directly, so the harness
can give a nested client a pointer enter. wltest gains --cursor-shape and an
explicit flush (wl_display_dispatch does not flush queued requests), plus the
now-required frame handler. New tests cover apply, tab-switch reset, and
nested forwarding; tail of README documents the protocol and its limits.