From 11068cd3cda1720338cdf7eae4536db9c083ba0f Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Wed, 2 Jul 2025 11:07:07 +0200 Subject: [PATCH] xwayland: Check for pointer caps on lock / warp / confine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pointer locking protocol relies on the wl_pointer, if the pointer capability was lost, the wl_pointer is NULL. In that case, trying to use the pointer locking, either to emulate a lock or a pointer warp, will lead to Wayland protocol error which is fatal to Xwayland. To avoid that issue, make sure we (still) have a wl_pointer available before trying to lock or warp the pointer. If not, record the last window and coordinate from the request and resubmit the request as soon as the wl_pointer is restored. Signed-off-by: Olivier Fourdan Suggested-by: Michel Dänzer Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1829 --- hw/xwayland/xwayland-input.c | 51 ++++++++++++++++++++++++++++++++++- hw/xwayland/xwayland-input.h | 10 +++++++ hw/xwayland/xwayland-window.c | 1 + 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c index cd2046e1d..a295eeb6d 100644 --- a/hw/xwayland/xwayland-input.c +++ b/hw/xwayland/xwayland-input.c @@ -1720,6 +1720,19 @@ init_pointer(struct xwl_seat *xwl_seat) ActivateDevice(xwl_seat->pointer, TRUE); } enable_device(xwl_seat, xwl_seat->pointer); + + /* If the pointer comes back after we got a request for a pointer locking + * or confinement, try again now that we've got our pointer back. + */ + if (xwl_seat->pending_pointer_lock.has_pending_pointer_warp) + xwl_seat_emulate_pointer_warp(xwl_seat, + xwl_seat->pending_pointer_lock.xwl_window, + NULL, + xwl_seat->pending_pointer_lock.pending_x, + xwl_seat->pending_pointer_lock.pending_y); + if (xwl_seat->pending_pointer_lock.has_pending_confined_pointer) + xwl_seat_confine_pointer(xwl_seat, + xwl_seat->pending_pointer_lock.xwl_window); } static void @@ -3380,6 +3393,17 @@ xwl_pointer_warp_emulator_maybe_lock(struct xwl_pointer_warp_emulator *warp_emul struct xwl_seat *xwl_seat = warp_emulator->xwl_seat; GrabPtr pointer_grab = xwl_seat->pointer->deviceGrab.grab; + /* Can't lock the pointer without pointer caps */ + if (!xwl_seat->wl_pointer) { + xwl_seat->pending_pointer_lock.has_pending_pointer_warp = TRUE; + xwl_seat->pending_pointer_lock.xwl_window = xwl_window; + xwl_seat->pending_pointer_lock.pending_x = x; + xwl_seat->pending_pointer_lock.pending_y = y; + + return; + } + xwl_seat->pending_pointer_lock.has_pending_pointer_warp = FALSE; + if (warp_emulator->locked_pointer) return; @@ -3410,6 +3434,17 @@ xwl_pointer_warp_emulator_warp(struct xwl_pointer_warp_emulator *warp_emulator, SpritePtr sprite, int x, int y) { + struct xwl_seat *xwl_seat = warp_emulator->xwl_seat; + + if (!xwl_seat->wl_pointer) { + xwl_seat->pending_pointer_lock.has_pending_pointer_warp = TRUE; + xwl_seat->pending_pointer_lock.xwl_window = xwl_window; + xwl_seat->pending_pointer_lock.pending_x = x; + xwl_seat->pending_pointer_lock.pending_y = y; + + return; + } + xwl_pointer_warp_emulator_maybe_lock(warp_emulator, xwl_window, sprite, @@ -3596,8 +3631,11 @@ xwl_seat_confine_pointer(struct xwl_seat *xwl_seat, if (!pointer_constraints) return; - if (!xwl_seat->wl_pointer) + if (!xwl_seat->wl_pointer) { + xwl_seat->pending_pointer_lock.has_pending_confined_pointer = TRUE; return; + } + xwl_seat->pending_pointer_lock.has_pending_confined_pointer = FALSE; if (xwl_seat->cursor_confinement_window == xwl_window && xwl_seat->confined_pointer) @@ -3632,11 +3670,22 @@ void xwl_seat_unconfine_pointer(struct xwl_seat *xwl_seat) { xwl_seat->cursor_confinement_window = NULL; + xwl_seat->pending_pointer_lock.has_pending_confined_pointer = FALSE; if (xwl_seat->confined_pointer) xwl_seat_destroy_confined_pointer(xwl_seat); } +void xwl_seat_clear_pending_pointer_lock(struct xwl_seat *xwl_seat, + struct xwl_window *xwl_window) +{ + if (xwl_seat->pending_pointer_lock.xwl_window == xwl_window) { + xwl_seat->pending_pointer_lock.has_pending_pointer_warp = FALSE; + xwl_seat->pending_pointer_lock.has_pending_confined_pointer = FALSE; + xwl_seat->pending_pointer_lock.xwl_window = NULL; + } +} + void InitInput(int argc, char *argv[]) { diff --git a/hw/xwayland/xwayland-input.h b/hw/xwayland/xwayland-input.h index 0a742f017..38ca2e3ca 100644 --- a/hw/xwayland/xwayland-input.h +++ b/hw/xwayland/xwayland-input.h @@ -125,6 +125,14 @@ struct xwl_seat { struct xorg_list tablet_tools; struct xorg_list tablet_pads; struct zwp_xwayland_keyboard_grab_v1 *keyboard_grab; + + struct { + Bool has_pending_pointer_warp; + Bool has_pending_confined_pointer; + struct xwl_window *xwl_window; + int pending_x; + int pending_y; + } pending_pointer_lock; }; struct xwl_tablet { @@ -213,4 +221,6 @@ void xwl_seat_unconfine_pointer(struct xwl_seat *xwl_seat); void xwl_screen_release_tablet_manager(struct xwl_screen *xwl_screen); +void xwl_seat_clear_pending_pointer_lock(struct xwl_seat *xwl_seat, + struct xwl_window *xwl_window); #endif /* XWAYLAND_INPUT_H */ diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c index b717fcf3c..21ca6d9fb 100644 --- a/hw/xwayland/xwayland-window.c +++ b/hw/xwayland/xwayland-window.c @@ -1677,6 +1677,7 @@ xwl_window_dispose(struct xwl_window *xwl_window) xwl_seat->pointer_warp_emulator->locked_window == xwl_window) xwl_seat_destroy_pointer_warp_emulator(xwl_seat); xwl_seat_clear_touch(xwl_seat, xwl_window); + xwl_seat_clear_pending_pointer_lock(xwl_seat, xwl_window); } if (xwl_window_has_viewport_enabled(xwl_window))