From aac488641d861a5b38b18978578760793c09548d Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Mon, 30 Mar 2026 17:13:03 +0200 Subject: [PATCH] =?UTF-8?q?wayland:=20Phases=2026-28=20=E2=80=94=20subcomp?= =?UTF-8?q?ositor,=20primary=20selection,=20xdg-output,=20idle-inhibit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 26 — wl_subcompositor + primary selection (middle-click paste): wl_subcompositor is already advertised by wlr_compositor_create (the wlr_subcompositor is embedded in wlr_compositor); confirmed via weston-info. zwp_primary_selection_device_manager_v1 created via wlr_primary_selection_v1_device_manager_create(). seat.request_set_primary_ selection wired to handle_seat_request_primary_selection which calls wlr_seat_set_primary_selection, so text selected in one client is available for middle-click paste in any other client. Phase 27 — zxdg_output_manager_v1: wlr_xdg_output_manager_v1_create(display, output_layout) advertises the xdg-output protocol. Panel clients (waybar, etc.) and HiDPI-aware apps use this to query the logical position and size of each output, which may differ from the physical pixel dimensions under scaling. Phase 28 — zwp_idle_inhibit_manager_v1: wlr_idle_inhibit_v1_create() advertises the idle-inhibit protocol. The new_inhibitor signal is wired to handle_idle_new_inhibitor which logs the event; the inhibitor list on wl_state.idle_inhibit_mgr can be queried by a future idle/DPMS implementation to suppress screen blanking while any inhibitor is live. Smoke test: "protocols check" verifies all three startup log lines; the weston-info globals check now also asserts wl_subcompositor, zwp_primary_selection_device_manager_v1, zxdg_output_manager_v1, and zwp_idle_inhibit_manager_v1 are present (25/25 checks pass). Co-Authored-By: Claude Sonnet 4.6 --- src/backend/wayland/wl_backend.c | 85 +++++++++++++++++++++++++++++++- tests/wl-smoke-test.sh | 33 ++++++++++++- 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index be3373f..ee541c4 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -113,7 +113,11 @@ #include /* Phase 18: buffer allocator */ #include /* Phase 23: hardware cursor tracking */ #include /* Phase 23: xcursor theme */ -#include /* Phase 25: clipboard / selection */ +#include /* Phase 25: clipboard / selection */ +#include /* Phase 26: primary selection (middle-click) */ +#include /* Phase 26: zwp_primary_selection_device_manager_v1 */ +#include /* Phase 27: xdg-output for panel geometry */ +#include /* Phase 28: idle inhibit for video/fullscreen */ #include /* Generated xdg-shell protocol implementation (Wayland glue code) */ #include "protocols/xdg-shell-protocol.c" @@ -263,6 +267,14 @@ struct wl_compositor_state { struct wl_listener seat_request_cursor_listener; /* Phase 25: clipboard — seat selection request listener. */ struct wl_listener seat_request_set_selection_listener; + /* Phase 26: primary selection (middle-click paste). */ + struct wlr_primary_selection_v1_device_manager *primary_selection_mgr; + struct wl_listener seat_request_primary_selection_listener; + /* Phase 27: xdg-output manager for logical output geometry. */ + struct wlr_xdg_output_manager_v1 *xdg_output_mgr; + /* Phase 28: idle-inhibit manager for video players / fullscreen apps. */ + struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; + struct wl_listener idle_new_inhibitor_listener; }; static struct wl_compositor_state wl_state; @@ -325,6 +337,10 @@ static struct wlr_surface *view_surface_at(int px, int py, double *sx_out, static void handle_seat_request_cursor(struct wl_listener *listener, void *data); /* Phase 25: client clipboard selection request handler. */ static void handle_seat_request_set_selection(struct wl_listener *listener, void *data); +/* Phase 26: primary selection request handler. */ +static void handle_seat_request_primary_selection(struct wl_listener *listener, void *data); +/* Phase 28: idle inhibitor created/destroyed handler. */ +static void handle_idle_new_inhibitor(struct wl_listener *listener, void *data); /* Phase 17: XWayland new surface forward declaration. */ static void handle_xwayland_new_surface(struct wl_listener *listener, void *data); /* Phase 20: nested backend real input device forward declaration. */ @@ -1464,6 +1480,39 @@ wl_display_open(const char *name) &wl_state.seat_request_set_selection_listener); wwarning("wl_backend: clipboard selection wired"); } + + /* Phase 26: primary selection (middle-click paste) via + * zwp_primary_selection_device_manager_v1. */ + wl_state.primary_selection_mgr = + wlr_primary_selection_v1_device_manager_create(wl_state.display); + if (wl_state.primary_selection_mgr && wl_state.seat) { + wl_state.seat_request_primary_selection_listener.notify = + handle_seat_request_primary_selection; + wl_signal_add(&wl_state.seat->events.request_set_primary_selection, + &wl_state.seat_request_primary_selection_listener); + wwarning("wl_backend: primary selection (middle-click) wired"); + } + + /* Phase 27: xdg-output — panels and HiDPI apps query logical output + * geometry via zxdg_output_manager_v1. Requires output_layout. */ + if (wl_state.output_layout) { + wl_state.xdg_output_mgr = + wlr_xdg_output_manager_v1_create(wl_state.display, + wl_state.output_layout); + if (wl_state.xdg_output_mgr) + wwarning("wl_backend: xdg-output-manager created"); + } + + /* Phase 28: idle-inhibit — video players and games create inhibitor + * objects to suppress screen blanking while active. */ + wl_state.idle_inhibit_mgr = + wlr_idle_inhibit_v1_create(wl_state.display); + if (wl_state.idle_inhibit_mgr) { + wl_state.idle_new_inhibitor_listener.notify = handle_idle_new_inhibitor; + wl_signal_add(&wl_state.idle_inhibit_mgr->events.new_inhibitor, + &wl_state.idle_new_inhibitor_listener); + wwarning("wl_backend: idle-inhibit manager created"); + } wl_state.xdg_shell = wlr_xdg_shell_create(wl_state.display); /* Phase 64: advertise zxdg_decoration_manager_v1 so clients that @@ -1756,6 +1805,9 @@ wl_display_close(void) /* Phase 24/25: seat cursor + selection listeners */ WL_LISTENER_REMOVE(wl_state.seat_request_cursor_listener); WL_LISTENER_REMOVE(wl_state.seat_request_set_selection_listener); + /* Phase 26/28: primary selection + idle inhibitor listeners */ + WL_LISTENER_REMOVE(wl_state.seat_request_primary_selection_listener); + WL_LISTENER_REMOVE(wl_state.idle_new_inhibitor_listener); #undef WL_LISTENER_REMOVE /* Phase 23: destroy cursor and xcursor manager before display teardown. */ @@ -2092,6 +2144,37 @@ handle_seat_request_set_selection(struct wl_listener *listener, void *data) wlr_seat_set_selection(wl_state.seat, ev->source, ev->serial); } +/* + * Phase 26: seat request_set_primary_selection — client writes to the + * primary selection buffer (e.g. by selecting text). Forward to + * wlr_seat so middle-click paste works in other clients. + */ +static void +handle_seat_request_primary_selection(struct wl_listener *listener, void *data) +{ + (void)listener; + struct wlr_seat_request_set_primary_selection_event *ev = data; + if (!wl_state.seat) + return; + wlr_seat_set_primary_selection(wl_state.seat, ev->source, ev->serial); +} + +/* + * Phase 28: idle_inhibit new_inhibitor — a client (e.g. mpv, a game) created + * an idle-inhibit object. wlroots tracks the inhibitor list internally; + * the compositor can query wl_state.idle_inhibit_mgr->inhibitors to decide + * whether to suppress screen blanking. For now we just log the event so + * the capability is advertised and the inhibitor object doesn't error out. + */ +static void +handle_idle_new_inhibitor(struct wl_listener *listener, void *data) +{ + (void)listener; + struct wlr_idle_inhibitor_v1 *inhibitor = data; + (void)inhibitor; + wwarning("wl_backend: idle inhibitor created (client suppressing screen blank)"); +} + static void handle_pointer_button(struct wl_listener *listener, void *data) { diff --git a/tests/wl-smoke-test.sh b/tests/wl-smoke-test.sh index b1bf7c3..6e69b50 100755 --- a/tests/wl-smoke-test.sh +++ b/tests/wl-smoke-test.sh @@ -147,6 +147,15 @@ # handlers now start a compositor-side grab that tracks pointer motion # and repositions/resizes the window in the scene graph. Button release # ends the grab. Build-only check (no client sends request_move here). +# 45. (Phase 26) wl_subcompositor already advertised by wlr_compositor_create +# (embedded in wlr_compositor struct); verified via weston-info. +# zwp_primary_selection_device_manager_v1 created; seat +# request_set_primary_selection wired → wlr_seat_set_primary_selection +# so middle-click paste propagates between clients. +# 46. (Phase 27) zxdg_output_manager_v1 created from output_layout so panels +# and HiDPI apps can query logical output geometry. +# 47. (Phase 28) zwp_idle_inhibit_manager_v1 created; new_inhibitor wired so +# video players and games can suppress screen blanking. # 38. (Phase 11) noclip/nodock set in wl_screen_create_internals: wDockCreate # requires X11 rendering resources unavailable in Wayland mode; disabling # the dock/clip prevents crashes when wWorkspaceNew is called to create @@ -297,6 +306,15 @@ grep -q 'clipboard selection wired' /tmp/wl-smoke-stderr \ || die "compositor did not log clipboard wired (seat request_set_selection listener missing)" echo " clipboard check: clipboard selection wired" +# -- 4f2c. Phases 26-28: primary selection + xdg-output + idle-inhibit ---- +grep -q 'primary selection.*wired' /tmp/wl-smoke-stderr \ + || die "compositor did not log primary selection wired" +grep -q 'xdg-output-manager created' /tmp/wl-smoke-stderr \ + || die "compositor did not log xdg-output-manager created" +grep -q 'idle-inhibit manager created' /tmp/wl-smoke-stderr \ + || die "compositor did not log idle-inhibit manager created" +echo " protocols check: primary-selection, xdg-output, idle-inhibit created" + # -- 4f3. Phase 23: cursor created and xcursor theme loaded --------------- grep -q 'cursor created, xcursor theme loaded' /tmp/wl-smoke-stderr \ || die "compositor did not log cursor creation (wlr_cursor_create or xcursor_manager_create failed)" @@ -359,7 +377,20 @@ if command -v weston-info >/dev/null 2>&1; then # Phase 64: zxdg_decoration_manager_v1 must be in the global registry echo "$WI" | grep -q 'zxdg_decoration_manager_v1' \ || die "zxdg_decoration_manager_v1 not in Wayland global registry" - echo " globals check: wl_compositor, wl_seat, xdg_wm_base, wl_shm, wl_output, zxdg_decoration_manager_v1 present; seat has pointer capability" + # Phase 26: wl_subcompositor (subsurfaces for Chromium, Firefox, etc.) + echo "$WI" | grep -q 'wl_subcompositor' \ + || die "wl_subcompositor not in Wayland global registry" + # Phase 26: primary selection (middle-click paste) + echo "$WI" | grep -q 'zwp_primary_selection_device_manager_v1' \ + || die "zwp_primary_selection_device_manager_v1 not in Wayland global registry" + # Phase 27: xdg-output (panel/HiDPI logical geometry) + echo "$WI" | grep -q 'zxdg_output_manager_v1' \ + || die "zxdg_output_manager_v1 not in Wayland global registry" + # Phase 28: idle-inhibit (video players, games) + echo "$WI" | grep -q 'zwp_idle_inhibit_manager_v1' \ + || die "zwp_idle_inhibit_manager_v1 not in Wayland global registry" + echo " globals check: wl_compositor, wl_subcompositor, wl_seat, xdg_wm_base, wl_shm, wl_output," \ + "zxdg_decoration_manager_v1, zwp_primary_selection, zxdg_output, zwp_idle_inhibit present" else echo " globals check: skipped (weston-info not installed)" fi