Every tab is maximized and kick draws no frames, so new toplevels
are pinned to client-side mode (headerbars keep working) and later
server-side requests are ignored. Pinning happens at map -- set_mode
asserts on a never-committed surface, and decoration-before-commit is
the normal order (calling it in new_toplevel_decoration aborts
wlroots); late binds pin immediately when already initialized.
Adds an xdeco probe (maps one shm-backed toplevel, reports the
configured mode) with a regression test asserting mode=1, tracks it
in build.sh staleness, and covers the global in protocol_globals.
Full suite: 153 passed, 2 skipped.
Deeper-X11 instrumentation (permanent, DEBUG): every associate event
now logs surface null/mapped/hooked state, classify logs wid/xpid/pgid
at entry, and pending-map has its own line -- closing the blind spot
between xwm pairing and tab claim. Plus kick_monotonic_ms() timestamps
on the new lines for stall measurement (prefix-safe for all test
regexes).
Paid off immediately: two stalls autopsied to paired-but-never-mapped
wlr_surfaces (associate ran with surface mapped=0, no map for 15-30s,
classify never invoked -- proven by the absent entry line). The stall
is therefore upstream of kick classification (X MapNotify delivery /
xwm mapping under -n15), not in it; locating it further needs live
capture during a stall, not post-hoc logs.
test_xwayland_ready_and_claim failed once the same way (paired xmessage
surface never mapped within 15s): marked flaky per the seen-it rule
(passes serially).
hold_alt_down_repeats + alt_tab_hold_repeats asserted repeat COUNTS in
fixed windows -- pure wall-clock. Now: press -> wait [repeat] start
(new DEBUG lifecycle lines in kbd_repeat_start/cancel; stop logged only
when disarming a live timer) -> wait for ONE synthesized repeat
(count-based, unbounded time) -> release -> wait [repeat] stop ->
immediate count assert (no timer exists post-disarm, so no silence
window needed). Generous timeouts bound hangs only; slow boxes take
longer without failing a rate. hold_alt unmarked (6/6 at -n15).
xwayland_alt_delete_closes_x11_tab: 1 failure in 13 runs, no body --
marked flaky per the seen-it rule.
Evidence: -n15 gate clean (153 passed), serial gate clean (153).
Killing the embedded Xwayland server aborted the compositor:
surface_handle_resource_destroy assert (non-empty map listener list)
via wl_client_destroy in the main loop's flush_clients.
Mechanism: the dead server's Wayland client connection is purged before
wlroots' xwm cascade dissociates anything, so wlr_surfaces die with our
map/unmap/commit listeners still attached.
Fix: watch the Xwayland Wayland client (its destroy signal fires before
its resources are purged) and proactively detach surface listeners only
-- across claimed windows, override-redirect windows and unclaimed
pendings -- leaving xsurface-level listeners for the later tab teardown.
Idempotent; later dissociate/xsurface-destroy find clean flags. The
restart test now also waits for the X11 window's map before killing, so
the crash path (listeners attached at death) is deterministically
exercised instead of raced.
Also re-mark output_mgmt_apply flaky (1 post-fix assertion under -n15;
budgets help but the modeset roundtrip stays load-sensitive).
Evidence: restart test 12/12 at the -n15 repro config (was ~2/9
crashing), detach handler firing on every server death, -n15 gate
clean, serial gate clean (148), flaky set green (11).
Mechanisms found by autopsying failing sessions' logs, each fixed with
logging + event-anchored waits instead of sleeps/fixed assumptions:
- Input-consumed acks: new Session.tap_ack/chord_ack snapshot the log
offset BEFORE sending (send-then-snapshot races a descheduled test and
misses its own line) and wait for the existing [kbd] press line.
wait_for_new_after covers release-side effects ([mru] commit).
- Wall-clock repeats displacing exact MRU/cycle/reorder state: a tap whose
press/release processing spans the 600ms repeat delay catches synthesized
repeats (autopsied: 3 preview steps from 1 press). KICK_REPEAT_DELAY_MS
env override (default 600); all test sessions stretch to 60s except the
two hold-to-repeat tests, which get private sessions with real repeats.
- Startup-crown flips (last mapper promotes itself): all crown reads
settled (both maps = quiescence) + relative navigation; black-tab crowns
log [diag] active slot= (no active app= line) -- waited accordingly.
- Title/data decoupling: [title] now emitted at data sources (map-end with
final title, post-refocus destroy, renames, focus promotion, subcycle,
tag-group join), not just overlay draws; premature claim-time hook tried
and reverted (wrong pick poisons the dedup cache).
- Pointer tests: new [cursor] pos= DEBUG line on warp/motion; dump-poll
loops become cursor-line + single-dump asserts. Also fixed a latent
_spawn_standalone bug: env -i dropped KICK_DUMP_DIR, so parallel
standalone sessions shared /tmp/host_N.ppm and read each other's dumps.
- Spawn races: _nested_session/no_args retry instant client death.
- X11/layer/modal: settle+nav, map-anchored claims, content dump-polls,
settle-poll instead of fixed sleeps.
Genuine product race fixed: fork->setpgid window let SIGCHLD-driven
sweep_dead_startup_groups conclude ESRCH on a not-yet-existing group and
drop a LIVE tab (orphaned sleep survivors found on the box). Parent-side
setpgid + pid-liveness double-check before sweeping.
Remaining 9 flaky (wall-clock timers, host-compositor roundtrips):
gestures, hold_alt, fullscreen x3, clipboard host x3, cursor-shape.
Evidence: -n10 clean 5x (150 passed, ~28s), serial clean (150 passed),
flaky set green (9 passed).
Pilot A (input-consumed signal): test_close_black_tab_active_last waits
for [kbd] key=111 state=1 after the chord, proving the compositor
consumed the key before waiting for the effect. Unmarked flaky.
Pilot B (data-source logging): [title] was only emitted from overlay
draw passes, so title tests needed Alt held + live frames. tab_row_title
is now also called at the data-source points where the topmost window's
title can change -- end of the map handler (first commit carries the
final title with tab assigned and focus settled), after refocus in
window_destroy_common, on set_title renames, and on the focus-promotion
path -- so row titles are log-observable with no overlay frames.
test_sub_windows_stack_mode_row_title_follows_top drops the Alt hold and
is unmarked. (A premature hook at claim time was tried and reverted: the
title has not arrived yet there, and resolving with the wrong pick
poisons that window's dedup cache and kills the later fallback line.)
Startup-crown races (found when full-suite validation failed 9-19 tests
that pass in isolation): each tab's first map promotes it, so under load
the LAST mapper steals the crown and neither the transient
wait_any_active read nor 'settled crown == 1' holds. Five tests now
settle (both tabs mapped = quiescence: no more first-maps, no more
promotions) and navigate relatively: alt_tab_walks/commit/1/repeats and
the sigkill-trap test.
-n10 clean 3x (123 passed, ~21s); serial clean (123 passed).
Make ./test.sh -n4 work (~47s vs ~156s serial):
- kick.c: KICK_DUMP_DIR env override for frame-dump .ppm paths
(default /tmp); per-boot dump_seq counters no longer collide
across concurrent compositors.
- tests: every spawn gets a unique log file and a per-session dump
directory owned by the Session (close removes the tree instead of
globbing /tmp, which raced parallel workers' live dumps).
- 14 protocol-globals tests share one module-scoped keeper session
via short-lived --globals-out clients instead of 14 boots.
- Two presence-waits converted from fixed sleeps to event waits.
- Harden the fullscreen round-trip: wait for seat keyboard focus
([diag] kbd-enter, new debug line in window_enter) before tapping
F -- map alone does not guarantee enter under load and the tap was
silently dropped.
Serial remains the deterministic gate (documented in test.sh and
README): a few pre-existing timing-sensitive tests can still flake
under parallel load; they pass alone and serially.
Three of the four protocols new in wlroots 0.20 (ext-workspace has no
meaningful mapping onto kick's tab model, so it stays out):
- wl_fixes (core, v1): advertise the global; wlroots serves
destroy_registry itself. Clients like Firefox use it to tear down
registries without tripping protocol errors.
- xdg-toplevel-tag-v1: record each window's tag + translated
description ([tag] log line per change), show 'title [tag]' in the
overlay, and with --tab-groups on stack an unmapped external window
onto the tab whose window carries the same tag (browser-style tag
groups). Tags arriving before claim wait in a pending list keyed by
toplevel; mapped windows never move.
- color-representation-v1: advertise an explicit SDR sRGB set
(premultiplied-electrical alpha, identity coefficients in full
range) rather than the renderer's query, which reports empty on
GLES2; wlroots validates client values against it.
Tests: wltest gains --tag-set/--tag-desc and --color-probe/--color-out
(bind-time listener, since the manager bursts on bind); the globals
test covers all three new globals; new tag, tag-grouping and color
round-trip tests; README documents all three.
The clipboard bridge backend has been ext-data-control since the
wlr-data-control migration; three comments/log strings still named the
old protocol. Cosmetic-only, no behavior change.
The wlr-screencopy unstable protocol is deprecated; its superseding
staging protocols ext-image-copy-capture-v1 and
ext-image-capture-source-v1 ship in wayland-protocols. Migrate:
- advertise wlr_ext_image_copy_capture_manager_v1 and
wlr_ext_output_image_capture_source_manager_v1 instead of the
wlr_screencopy_manager_v1 global (wlroots runs the whole session/frame
flow off the output commit path, so no listener wiring is needed)
- rewrite wltest --screencopy for the new client flow: bind the capture
managers, create an output capture source, open a copy-capture session,
wait for the constraint batch (buffer_size/shm_format/done), then attach
a shm buffer and ask for one frame
- generate the two client stubs from wayland-protocols and drop the
bundled wlr-screencopy-unstable-v1.xml
- update the globals test to the new interface names
Existing wl_data_device-based clipboard bridge code is unaffected.
The wlr-data-control unstable protocol is deprecated; its superseding
staging protocol ext-data-control-v1 ships in wayland-protocols.
Migrate the host-side clipboard/primary bridge to it:
- bind ext_data_control_manager_v1 version 1 instead of zwlr_manager
(ext-data-control v1 always supports primary selection, so the
dc_primary_supported gate is dropped and the registry bind is plain)
- rename all zwlr_data_control_* to ext_data_control_* types/calls
- generate ext-data-control-v1 from wayland-protocols and drop the
bundled wlr-data-control-unstable-v1.xml
- update README and test log-message expectations
wltest --screencopy is unaffected; the screencopy migration is separate.
Prefix+Tab and prefix+1 preview the session's windows most-recently-used
first: each press steps to the next window after the shown one in server.mru
order, wrapping at the bottom, without mutating the stack until the modifier
is released. Releasing after navigating commits the shown window to the
front; a bare hold leaves the order untouched. Focus changes with the
modifier up move the entered window to the front immediately.
Alt+1 is the chord that survives hosts like GNOME, which grab Alt+Tab,
Alt+Esc and Alt+Backtick (the latter via the metacity-era switch_group
default) before a nested compositor ever sees them; Alt+Tab is kept for
hosts that leave it free. Windows join the stack least-recent on allocation
and are removed on teardown; reachable stops mirror cycle_tab (live or
pending-claim, not closing). Holding the chord repeats via the existing
synthesized-repeat machinery.
Tests cover stepping, commit-on-release, the Alt+1 synonym, and hold-to-
repeat.
Advertise zwlr_output_manager_v1 with the single head, published on
setup and re-published from the commit path whenever the size lands
(resizes apply lazily, so the commit -- not the request -- is where
the new size becomes observable).
Client sizes apply standalone through the normal resize path (tabs
reflow, swapchain follows); nested refuses anything but the live
size since the host owns the geometry. Disabling, scale and
transform changes are refused; superseded configs are destroyed
after feedback.
Standalone output_resize now owns the canvas size so client resizes
reach the lazy modeset. wltest gains --output-mgmt; smoke tests
cover the global, head listing, and an 800x600 apply with tab
reflow.
Advertise xdg_toplevel_icon_manager_v1 (28px, the overlay draw size)
and use the largest uploaded buffer as the tab's overlay texture.
The desktop-file guesser stays as the fallback -- and the only source
for X11 windows -- so every window still gets an icon; clearing the
icon drops back to guessing.
wltest gains --icon; smoke tests cover the global and the end-to-end
protocol-icon preference.
Drive the output commit's async-flip flag from the shown surface's
tearing hint (a real tearing flip on DRM; correctly ignored on
headless/nested), with per-object hint tracking that requests a frame
on change. Notify each surface of the output scale at claim so
clients never guess preferred_scale.
wltest gains --tearing and --fractional-out (plus linking the two
protocol stubs it now uses); smoke tests cover the async commit
transition and the notified scale value.
Advertise zwp_pointer_gestures_v1 (v3, with hold) and fan libinput
touchpad gestures out to the client holding seat pointer focus.
The stdin test feed gains swipe/pinch/hold messages driving the same
forwarders in any mode (nested has no host gesture source).
wltest gains --gesture-log plus swipe/pinch/hold feed helpers; smoke
tests cover the global and an exact-value round trip of every gesture
and cancel path.
Advertise xdg_wm_dialog_v1 and mirror each dialog object onto its
window. A modal dialog demands attention: on map (or when set modal
while mapped) its tab is shown and it takes window focus, in both
--tab-groups modes; placement itself is unchanged and session lock
still wins.
wltest gains --dialog-modal/--extra-modal/--extra-delay; smoke tests
cover the global, the background-modal steal with tab-groups on, and
the own-tab steal with tab-groups off.
Advertise zwlr_gamma_control_manager_v1 and commit client ramps to
the single output (reset when the control goes away); other outputs
and failed commits fail the control so clients retry. Outputs with
no LUT (headless/nested) are failed by wlroots itself.
wltest gains a --gamma mode reporting gamma_size/failed; smoke tests
cover the global and the clean headless failure path.
Advertise zwp_text_input_manager_v3 + zwp_input_method_manager_v2 and
relay the focused field to the in-session IME and back (enter/leave on
seat keyboard focus, keyboard-grab forwarding, IME popups above tabs).
Also advertise wp_content_type_manager_v1 (logged on map) and
wp_single_pixel_buffer_manager_v1 (scaled via viewporter).
wltest gains --text-input/--input-method/--content-type/--single-pixel
modes; smoke tests cover globals, the IME round trip, focus follow,
single-pixel rendering and the content-type log line.
Deleting a window's WM_CLASS emits set_class with class==NULL while the
title is set; handle_xs_set_class passed the raw NULLs into
wlr_foreign_toplevel_handle_v1_set_app_id / ext update_state, which
strdup(NULL) -> SIGSEGV (exit 139; UBSan flags the nonnull argument
first). The sibling set_title handler and every other mirror site use
foreign_str(); apply the same guard here.
Add tests/xclass.c (title set, WM_CLASS set-then-deleted probe) and a
headless smoke test asserting the compositor survives the delete with
no crash indicators (fails pre-fix: compositor dead).
An override-redirect menu that takes keyboard focus stashes the active
tab in server.or_focus_owner; dismissing the menu later reactivates it.
If the owner tab was closed first, the release activated freed memory
(heap-use-after-free in set_active_tab via tab_current_window, caught
by ASan: free in remove_tab <- drop_tab <- window_destroy_common <-
handle_xs_destroy; use in or_window_focus_release <- or_handle_unmap).
Clear the stash in remove_tab (the single live tab-free choke point),
so the release drops keyboard focus instead. Without ASan the bug is
silent (stale memory usually still mapped), which is why it survived:
repro needs a focus-stealing OR menu (real menus paint content; an
unpainted probe never completes the wl map handshake).
Add tests/xormenu.c (frame + OR menu in separate processes, after the
xpopup pattern) and a headless smoke test driving the exact sequence:
menu takes focus, owner tab closed, menu dismissed, compositor alive
and crash-indicator-free.