Stock wl-clipboard only speaks the legacy zwlr_data_control protocol
kick no longer offers (removed in wlroots 0.20), and its wl_data_device
fallback needs a focus serial a surfaceless tool never receives, so the
six clipboard tests poll-timed-out on hosts where kick is the data-control
endpoint (e.g. labwc).
wltest gains --clipboard-set/--clipboard-demand-expect,
--dc-primary-set/--dc-primary-demand-expect and --dc-clear over
ext-data-control-v1 (no focus or serial needed; setters exit 0 on
cancel like wl-copy -f), and the tests use them on kick sockets while
wl-copy/wl-paste stay on the real-host side.
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 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.
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.
Let launchers focus existing windows via xdg-activation-v1. On
request_activate, find the kick window owning the surface and switch to
its tab (set_active_tab + window_enter, both gated on session_locked so
activation can never bypass the lock screen).
Add a --activate wltest mode (two windows; second steals focus, then a
token activates the first) and two smoke tests proving the global is
advertised and the inactive window receives an activated configure.
Let synthesised input (wtype & co.) drive the compositor through the
exact same paths as real devices: virtual keys flow through
process_key_event (so Alt+Up tab-cycles, chords swallow under lock/
inhibit), virtual pointer motion/buttons drive native_ptr_* and the
seat. A missing client keymap falls back to the server's so modifiers
resolve; cleanup uses resource-destroy listeners (run before wlroots'
finish asserts).
Add --virtual-key (code:state sequence with xkbcommon us keymap),
--virtual-motion/--virtual-click, and --pointer-out (window logs pointer
events) wltest modes; three smoke tests prove the globals are advertised,
a virtual Alt+Up cycles tabs, and virtual motion+click reach the window.
Let a client (wlopm) blank/unblank the single output. OFF overwrites the
composited scene buffer with opaque black before commit (a dedicated
render-pass fill in both render paths), so the committed frame is truly
dark rather than a stale scene frame. ON forces a full scene repaint
(the swapchain recycles the black buffer, so without whole-damage it
would re-commit stale black) and resumes normal compositing.
Extend --screencopy to report full-buffer blackness (black vs content),
add a --power on|off wltest mode, and three smoke tests: the global is
advertised, and a power off/on cycle blanks then restores the visible
window (verified via screencopy).
Let a focused fullscreen client (game/VM) grab the compositor's keyboard
shortcuts. New inhibitors for the focused surface activate immediately;
a lazy sync at grab-chord time (de)activates across focus changes so all
focus paths are covered without extra listeners. While honored, the
tab/window grab chords are swallowed in process_key_event (quit and VT
switch stay live so a stuck client can't trap the session).
Add a --inhibit wltest mode (window + inhibitor, reports active) and two
smoke tests asserting the global is advertised and the inhibitor becomes
active on the focused surface.
Add wp_tearing_control_manager_v1 and wp_fractional_scale_manager_v1 as
pass-through advertisements for client convenience (games, HiDPI). kick
drives a vsynced scene at scale 1.0, so nothing is honoured: a new
tearing object is only logged at DEBUG. Bundle the output-power XML
locally too (needed by a later commit) and generate the server headers
the wlroots headers' quoted includes require.
Composite the scene and let wlroots run the whole capture flow off the
output commit path: the manager is created in server init and every
commit carrying a rendered buffer becomes a copyable frame (both the
standalone and nested render paths already commit a buffer, and
wlr_output_update_needs_frame drives the render gate).
Bundle the wlr-screencopy XML locally (a copy of the one vendored in the
pinned wlroots subproject) since Meson forbids the parent project from
referencing a nested subproject's files; add a --screencopy wltest mode
that captures the output into a shm buffer and reports the frame
geometry, plus two smoke tests asserting the global is advertised and a
rendered frame is delivered.
Blank the screen and hold input when a lock client (swaylock) grabs
ext_session_lock_manager_v1. While locked an opaque black rect covers the
output with the lock client's per-output surface floating above it; no
window may take keyboard focus and the compositor grab chords are swallowed
so tabs can't be cycled behind the lock. kick manages a single output, so a
single lock surface is tracked. If the lock client dies without unlocking
the screen stays blanked (a deliberate security choice); only a fresh lock
request may take over. On unlock the previously active tab is refocused.
Includes a wltest --lock mode (binds the manager, locks, draws a buffer on
the lock surface, and optionally --lock-unlock) and two smoke tests:
advertised-globals and lock/surface-config acceptance.
--layer <bg|bottom|top|overlay> creates a zwlr_layer_surface_v1 instead
of an xdg toplevel, with --layer-anchor/--exclusive-zone/--layer-size
control. Wire the wlr-layer-shell-unstable-v1 client protocol into the
wltest target. Lets the smoke tests exercise kick's exclusive-zone
work-area computation without depending on an external panel.
--bar CMD launches an always-on-top layer-shell panel (e.g. waybar) in a
dedicated launch slot. The bar's footprint reserves the usable area,
shrinking and offsetting maximized tabs below it even when the client never
sends an exclusive zone; the bar's scene tree is held above every window.
Fix a use-after-free in xdg popup handling exposed by panel clients: resolve
a popup's parent by address comparison against kick's live surfaces rather
than dereferencing popup->parent (wlroots may hand a popup whose parent
wl_surface was already freed outside the xdg teardown cascade). Popups whose
parent is no longer live fall back to the active window's tree so they still
get their initial configure instead of hanging the client.
Bar child stdout/stderr is /dev/null'd unless KICK_LOG is at debug level, so
waybar's chatter no longer pollutes kick's console (same convention as CLI
clients).
libdecor sat on the critical path of every host-window transition even
where it drew nothing: on compositors honoring server-side decorations
(labwc/sway/KDE) it negotiated SSD, then still injected permanent
set_window_geometry hints and batched acks inside its own commit call.
labwc 0.9.8 expires a 100 ms configure deadline under load and answers
"move but keep old size" -- anchoring fullscreen views to stale geometry
forever.
the host window now selects its backend at startup: compositors
advertising zxdg_decoration_manager_v1 get kick-owned xdg_shell objects
with immediate configure acks, an explicit server-side decoration request,
and no geometry hints at all (KICK_LIBDECOR=1 forces the libdecor path);
hosts without the manager -- Mutter/GNOME forces CSD -- keep libdecor
unchanged. this also restores mirrored fullscreen semantics: a client's
fullscreen request again fullscreens the host window in one step.
X11 windows now pair their Wayland surface after membership decisions run
(membership starts at first map), so instantly-mapped transients -- GTK
menu dropdowns -- attached without ever wiring their scene surface: emacs
menus did nothing, silently. The associate handlers now replay the map
when the surface is already mapped, members join the owner's list before
that replay (show_current walks the list to pick visibility), X11 members
keep their own geometry instead of being stretched maximized, and late
attaches force whole-scene damage so the first frame is never black.
Members render through wlr_scene_subsurface_tree_create; the ctl test feed
gains CTL_CMD_PTR_BUTTON so tests can click for real; tests/xpopup.c
probes the exact menu shape (instantly-mapped managed transient), and a
regression test locks in the attach/replay/focus lifecycle.
GDK_BACKEND/QT_QPA_PLATFORM forced to wayland stripped toolkits' fallback,
killing X11-only builds like emacs-gtk ('cannot open display' against a
healthy Xwayland). Children now self-select from WAYLAND_DISPLAY/DISPLAY,
and XAUTHORITY is cleared.
Separately, X11 membership decisions moved from window-creation time to
first map: GTK leader windows never map, yet claimed the tab slot at
creation, leaving a permanent black tab while the real frame was demoted
to a stacked member -- and Alt-cycling onto that phantom main member
crashed on wlr_seat_keyboard_notify_enter(NULL). Never-mapped windows now
free silently; keyboard-entry paths guard on live surfaces. New
tests/xleader.c probe (unmapped leader + mapped frame) locks the behaviour
in.
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).
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.
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.
Bridge the nested wlroots seat's clipboard with the host compositor.
Uses wlr-data-control when the host exposes it, otherwise a focus-based
wl_data_device fallback (Mutter). Text selections are eagerly prefetched
at mirror time so every paste is served instantly from a compositor-side
buffer instead of paying the host source's latency per paste; non-text
MIMEs keep the zero-copy live relay.
Also fix a critical bug: prefetch_fd was zero-initialized instead of -1,
so the first bridge_prefetch_cancel() closed stdin, breaking the test
event feed and spinning the main loop on EBADF.
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.
Compositor: read raw keyboard records from stdin and dispatch them into
the real host_kbd_key/host_kbd_modifiers handlers, so keybindings (F11,
Ctrl+PageUp/Down, F9/F10 dumps) can be driven without a focused host
window or wtype. Binary wire: 1|u32 key|u32 state, 2|u32 mods|...
Tests: tests/wltest.c solid-colour xdg_toplevel client, plus a pytest
suite (tests/test_smoke.py) that launches nested instances per test and
feeds events through the new channel. test.sh runs it. F11 fullscreen
toggle now logs its new state.