wayland: Phase 20 — real input forwarding + root background
In nested mode the wlr_wl_backend creates virtual pointer/keyboard devices that mirror the host compositor's seat. Wire handle_backend_new_input on backend->events.new_input (before wlr_backend_start) to capture these real devices and add separate wl_listener structs for their key/modifier/button/ motion_absolute/frame events. At the end of wl_display_open, switch the seat to the real keyboard so focus notifications carry the correct keymap and modifier state from the host. handle_keyboard_key / handle_keyboard_modifiers updated to use seat->keyboard_state.keyboard instead of wl_state.keyboard_dev so the correct modifier state is used regardless of which keyboard fired the event. Add a grey wlr_scene_rect root background in handle_backend_new_output so the compositor output shows a proper desktop instead of solid black. The rect is inserted first in the scene tree (always below client surfaces). Pointer bounds clamping in handle_pointer_motion, handle_pointer_motion_- absolute, wl_pointer_warp and wl_pointer_warp_to updated to use actual output dimensions instead of hardcoded 1920x1079. All 6 new wl_listener nodes removed in wl_display_close. Smoke test 22/22 PASS, exit 0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
177e13f995
commit
aa294ed13c
|
|
@ -72,6 +72,12 @@
|
|||
* compositing client surfaces; wlr_scene_output_commit renders each frame.
|
||||
* A per-view scene node is created for each XDG/XWayland surface and kept
|
||||
* in sync with the WM-assigned position.
|
||||
* Phase 20: real input device forwarding for nested mode.
|
||||
* handle_backend_new_input wires listeners for the host compositor's real
|
||||
* pointer/keyboard devices so key presses and mouse motion are forwarded to
|
||||
* focused Wayland clients. A grey wlr_scene_rect root background is added
|
||||
* so the compositor output shows a proper desktop instead of solid black.
|
||||
* Pointer bounds clamping updated to use actual output dimensions.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
@ -210,6 +216,15 @@ struct wl_compositor_state {
|
|||
/* Phase 17: XWayland server for X11 client support */
|
||||
struct wlr_xwayland *xwayland;
|
||||
struct wl_listener xwayland_new_surface_listener;
|
||||
/* Phase 20: real input devices from nested backend + their listeners */
|
||||
struct wlr_input_device *real_keyboard_dev;
|
||||
struct wlr_input_device *real_pointer_dev;
|
||||
struct wl_listener backend_new_input_listener;
|
||||
struct wl_listener real_keyboard_key_listener;
|
||||
struct wl_listener real_keyboard_modifiers_listener;
|
||||
struct wl_listener real_pointer_motion_abs_listener;
|
||||
struct wl_listener real_pointer_button_listener;
|
||||
struct wl_listener real_pointer_frame_listener;
|
||||
/* Phase 18: nested Wayland + wlr_scene */
|
||||
int is_nested; /* 1 if using wlr_wl_backend */
|
||||
struct wlr_backend *headless_inner; /* headless backend (always) */
|
||||
|
|
@ -282,6 +297,8 @@ static void wl_pointer_warp_to(int x, int y);
|
|||
static void handle_pointer_motion(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. */
|
||||
static void handle_backend_new_input(struct wl_listener *listener, void *data);
|
||||
/* Phase 18: output lifecycle and frame rendering forward declarations. */
|
||||
static void handle_backend_new_output(struct wl_listener *listener, void *data);
|
||||
static void handle_output_frame(struct wl_listener *listener, void *data);
|
||||
|
|
@ -816,10 +833,17 @@ handle_keyboard_key(struct wl_listener *listener, void *data)
|
|||
struct wlr_event_keyboard_key *event = data;
|
||||
(void)listener;
|
||||
|
||||
if (!wl_state.seat || !wl_state.keyboard_dev || !wl_state.keyboard_dev->keyboard)
|
||||
if (!wl_state.seat)
|
||||
return;
|
||||
|
||||
struct wlr_keyboard *kb = wl_state.keyboard_dev->keyboard;
|
||||
/* Use the seat's active keyboard — in headless mode that is the headless
|
||||
* keyboard; in nested mode it is the real host keyboard (updated at the
|
||||
* end of wl_display_open after handle_backend_new_input runs). */
|
||||
struct wlr_keyboard *kb = wl_state.seat->keyboard_state.keyboard;
|
||||
if (!kb) {
|
||||
if (!wl_state.keyboard_dev || !wl_state.keyboard_dev->keyboard) return;
|
||||
kb = wl_state.keyboard_dev->keyboard;
|
||||
}
|
||||
|
||||
/* Update modifier state */
|
||||
wlr_seat_keyboard_notify_modifiers(wl_state.seat, &kb->modifiers);
|
||||
|
|
@ -842,12 +866,76 @@ static void
|
|||
handle_keyboard_modifiers(struct wl_listener *listener, void *data)
|
||||
{
|
||||
(void)listener; (void)data;
|
||||
if (!wl_state.seat || !wl_state.keyboard_dev || !wl_state.keyboard_dev->keyboard)
|
||||
return;
|
||||
struct wlr_keyboard *kb = wl_state.keyboard_dev->keyboard;
|
||||
if (!wl_state.seat) return;
|
||||
struct wlr_keyboard *kb = wl_state.seat->keyboard_state.keyboard;
|
||||
if (!kb) {
|
||||
if (!wl_state.keyboard_dev || !wl_state.keyboard_dev->keyboard) return;
|
||||
kb = wl_state.keyboard_dev->keyboard;
|
||||
}
|
||||
wlr_seat_keyboard_notify_modifiers(wl_state.seat, &kb->modifiers);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Phase 20: real input device forwarding for nested mode */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/*
|
||||
* handle_backend_new_input — fired for every input device added to any
|
||||
* sub-backend. Headless synthetic devices (which never generate events)
|
||||
* are skipped; the explicit setup code in wl_display_open handles those.
|
||||
* Real devices from wlr_wl_backend (the nested compositor path) carry
|
||||
* events from the host compositor's seat and must be wired so key presses
|
||||
* and mouse motion reach focused Wayland clients.
|
||||
*/
|
||||
static void
|
||||
handle_backend_new_input(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct wlr_input_device *dev = data;
|
||||
(void)listener;
|
||||
|
||||
/* Skip headless synthetic devices — they never generate events. */
|
||||
if (wlr_input_device_is_headless(dev))
|
||||
return;
|
||||
|
||||
/* Real device from the nested Wayland backend. */
|
||||
switch (dev->type) {
|
||||
case WLR_INPUT_DEVICE_KEYBOARD:
|
||||
if (!dev->keyboard) break;
|
||||
/* wlroots' nested backend auto-receives the host keymap via the
|
||||
* wl_keyboard.keymap event — no need to set XKB manually here. */
|
||||
wl_state.real_keyboard_dev = dev;
|
||||
wl_state.real_keyboard_key_listener.notify = handle_keyboard_key;
|
||||
wl_signal_add(&dev->keyboard->events.key,
|
||||
&wl_state.real_keyboard_key_listener);
|
||||
wl_state.real_keyboard_modifiers_listener.notify = handle_keyboard_modifiers;
|
||||
wl_signal_add(&dev->keyboard->events.modifiers,
|
||||
&wl_state.real_keyboard_modifiers_listener);
|
||||
wwarning("wl_backend: real keyboard wired from nested backend");
|
||||
break;
|
||||
|
||||
case WLR_INPUT_DEVICE_POINTER:
|
||||
if (!dev->pointer) break;
|
||||
/* Nested backend delivers absolute motion events (host cursor
|
||||
* position normalised to [0,1]); no relative motion needed. */
|
||||
wl_state.real_pointer_dev = dev;
|
||||
wl_state.real_pointer_motion_abs_listener.notify =
|
||||
handle_pointer_motion_absolute;
|
||||
wl_signal_add(&dev->pointer->events.motion_absolute,
|
||||
&wl_state.real_pointer_motion_abs_listener);
|
||||
wl_state.real_pointer_button_listener.notify = handle_pointer_button;
|
||||
wl_signal_add(&dev->pointer->events.button,
|
||||
&wl_state.real_pointer_button_listener);
|
||||
wl_state.real_pointer_frame_listener.notify = handle_pointer_frame;
|
||||
wl_signal_add(&dev->pointer->events.frame,
|
||||
&wl_state.real_pointer_frame_listener);
|
||||
wwarning("wl_backend: real pointer wired from nested backend");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Phase 18: output frame rendering and backend new-output handler */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -930,6 +1018,18 @@ handle_backend_new_output(struct wl_listener *listener, void *data)
|
|||
wl_signal_add(&output->events.frame, &wl_state.output_frame_listener);
|
||||
wwarning("wl_backend: scene output created for %s (%dx%d)",
|
||||
output->name, output->width, output->height);
|
||||
|
||||
/* Phase 20: root background rectangle.
|
||||
* Inserted first in the scene tree so it is always below client
|
||||
* surfaces. WindowMaker grey (0.35, 0.35, 0.35) matches the
|
||||
* classic WindowMaker desktop colour. */
|
||||
{
|
||||
float bg[4] = { 0.35f, 0.35f, 0.35f, 1.0f };
|
||||
wlr_scene_rect_create(&wl_state.scene->node,
|
||||
(int)output->width, (int)output->height, bg);
|
||||
wwarning("wl_backend: root background created (%dx%d, grey)",
|
||||
(int)output->width, (int)output->height);
|
||||
}
|
||||
}
|
||||
|
||||
wwarning("wl_backend: new output %s (%dx%d)",
|
||||
|
|
@ -1044,6 +1144,14 @@ wl_display_open(const char *name)
|
|||
else
|
||||
wwarning("wl_backend: wlr_scene_create failed (no compositing)");
|
||||
|
||||
/* Phase 20: wire new_input BEFORE starting the backend so that real
|
||||
* input devices from the nested Wayland backend (fired during
|
||||
* wlr_backend_start) are captured before the headless devices are
|
||||
* added explicitly below. */
|
||||
wl_state.backend_new_input_listener.notify = handle_backend_new_input;
|
||||
wl_signal_add(&wl_state.backend->events.new_input,
|
||||
&wl_state.backend_new_input_listener);
|
||||
|
||||
/* Wire new_output BEFORE starting the backend. */
|
||||
wl_state.backend_new_output_listener.notify = handle_backend_new_output;
|
||||
wl_signal_add(&wl_state.backend->events.new_output,
|
||||
|
|
@ -1252,6 +1360,15 @@ wl_display_open(const char *name)
|
|||
wwarning("wl_backend: shutdown pipe creation failed; SIGTERM may crash");
|
||||
}
|
||||
|
||||
/* Phase 20: if a real keyboard was found from the nested backend
|
||||
* (captured in handle_backend_new_input during wlr_backend_start),
|
||||
* switch the seat to it now so keyboard focus notifications use the
|
||||
* correct keymap and modifier state from the host compositor. This
|
||||
* must happen after the headless keyboard setup above (which may have
|
||||
* called wlr_seat_set_keyboard with the headless device). */
|
||||
if (wl_state.real_keyboard_dev)
|
||||
wlr_seat_set_keyboard(wl_state.seat, wl_state.real_keyboard_dev);
|
||||
|
||||
wl_state.started = 1;
|
||||
return NULL; /* no X11 Display* in the Wayland path */
|
||||
}
|
||||
|
|
@ -1349,6 +1466,13 @@ wl_display_close(void)
|
|||
WL_LISTENER_REMOVE(wl_state.pointer_motion_abs_listener);
|
||||
WL_LISTENER_REMOVE(wl_state.pointer_button_listener);
|
||||
WL_LISTENER_REMOVE(wl_state.pointer_frame_listener);
|
||||
/* Phase 20: backend new_input listener and real device listeners */
|
||||
WL_LISTENER_REMOVE(wl_state.backend_new_input_listener);
|
||||
WL_LISTENER_REMOVE(wl_state.real_keyboard_key_listener);
|
||||
WL_LISTENER_REMOVE(wl_state.real_keyboard_modifiers_listener);
|
||||
WL_LISTENER_REMOVE(wl_state.real_pointer_motion_abs_listener);
|
||||
WL_LISTENER_REMOVE(wl_state.real_pointer_button_listener);
|
||||
WL_LISTENER_REMOVE(wl_state.real_pointer_frame_listener);
|
||||
#undef WL_LISTENER_REMOVE
|
||||
|
||||
/* Skip wl_display_destroy() and wl_display_destroy_clients().
|
||||
|
|
@ -1592,12 +1716,14 @@ handle_pointer_motion(struct wl_listener *listener, void *data)
|
|||
{
|
||||
(void)listener;
|
||||
struct wlr_event_pointer_motion *ev = data;
|
||||
int ow = wl_state.output ? (int)wl_state.output->width : 1920;
|
||||
int oh = wl_state.output ? (int)wl_state.output->height : 1080;
|
||||
wl_state.pointer_x += (int)ev->delta_x;
|
||||
wl_state.pointer_y += (int)ev->delta_y;
|
||||
if (wl_state.pointer_x < 0) wl_state.pointer_x = 0;
|
||||
if (wl_state.pointer_y < 0) wl_state.pointer_y = 0;
|
||||
if (wl_state.pointer_x > 1919) wl_state.pointer_x = 1919;
|
||||
if (wl_state.pointer_y > 1079) wl_state.pointer_y = 1079;
|
||||
if (wl_state.pointer_x >= ow) wl_state.pointer_x = ow - 1;
|
||||
if (wl_state.pointer_y >= oh) wl_state.pointer_y = oh - 1;
|
||||
wl_update_pointer_focus(ev->time_msec);
|
||||
}
|
||||
|
||||
|
|
@ -1606,12 +1732,14 @@ handle_pointer_motion_absolute(struct wl_listener *listener, void *data)
|
|||
{
|
||||
(void)listener;
|
||||
struct wlr_event_pointer_motion_absolute *ev = data;
|
||||
wl_state.pointer_x = (int)(ev->x * 1920);
|
||||
wl_state.pointer_y = (int)(ev->y * 1080);
|
||||
int ow = wl_state.output ? (int)wl_state.output->width : 1920;
|
||||
int oh = wl_state.output ? (int)wl_state.output->height : 1080;
|
||||
wl_state.pointer_x = (int)(ev->x * (double)ow);
|
||||
wl_state.pointer_y = (int)(ev->y * (double)oh);
|
||||
if (wl_state.pointer_x < 0) wl_state.pointer_x = 0;
|
||||
if (wl_state.pointer_y < 0) wl_state.pointer_y = 0;
|
||||
if (wl_state.pointer_x > 1919) wl_state.pointer_x = 1919;
|
||||
if (wl_state.pointer_y > 1079) wl_state.pointer_y = 1079;
|
||||
if (wl_state.pointer_x >= ow) wl_state.pointer_x = ow - 1;
|
||||
if (wl_state.pointer_y >= oh) wl_state.pointer_y = oh - 1;
|
||||
wl_update_pointer_focus(ev->time_msec);
|
||||
}
|
||||
|
||||
|
|
@ -1740,12 +1868,14 @@ static void
|
|||
wl_pointer_warp(int dx, int dy)
|
||||
{
|
||||
/* Phase 54: relative warp — clamp to output bounds. */
|
||||
int ow = wl_state.output ? (int)wl_state.output->width : 1920;
|
||||
int oh = wl_state.output ? (int)wl_state.output->height : 1080;
|
||||
wl_state.pointer_x += dx;
|
||||
wl_state.pointer_y += dy;
|
||||
if (wl_state.pointer_x < 0) wl_state.pointer_x = 0;
|
||||
if (wl_state.pointer_y < 0) wl_state.pointer_y = 0;
|
||||
if (wl_state.pointer_x > 1919) wl_state.pointer_x = 1919;
|
||||
if (wl_state.pointer_y > 1079) wl_state.pointer_y = 1079;
|
||||
if (wl_state.pointer_x >= ow) wl_state.pointer_x = ow - 1;
|
||||
if (wl_state.pointer_y >= oh) wl_state.pointer_y = oh - 1;
|
||||
/* Phase 13: update pointer focus after relative warp */
|
||||
wl_update_pointer_focus(0);
|
||||
}
|
||||
|
|
@ -1754,10 +1884,12 @@ static void
|
|||
wl_pointer_warp_to(int x, int y)
|
||||
{
|
||||
/* Phase 54: absolute warp — clamp to output bounds. */
|
||||
int ow = wl_state.output ? (int)wl_state.output->width : 1920;
|
||||
int oh = wl_state.output ? (int)wl_state.output->height : 1080;
|
||||
if (x < 0) x = 0;
|
||||
if (y < 0) y = 0;
|
||||
if (x > 1919) x = 1919;
|
||||
if (y > 1079) y = 1079;
|
||||
if (x >= ow) x = ow - 1;
|
||||
if (y >= oh) y = oh - 1;
|
||||
wl_state.pointer_x = x;
|
||||
wl_state.pointer_y = y;
|
||||
/* Phase 13: update pointer focus after absolute warp */
|
||||
|
|
|
|||
Loading…
Reference in New Issue