From c03e582f0cf3a497ed8475a8cab675b2b6031013 Mon Sep 17 00:00:00 2001 From: Olivier Fourdan <ofourdan@redhat.com> Date: Thu, 28 Apr 2022 12:26:49 +0200 Subject: [PATCH] xwayland: add (fake) device grab support Add a new command line option "-host-grab" to disable the keyboard shortcuts and confine the pointer on the host so that Xwayland can receive all keyboard events. This is useful when running a complete desktop environment within Xwayland rootful. Use [CTRL]+[SHIFT] to release the keyboard and pointer. This option is not compatible with rootless mode. Signed-off-by: Olivier Fourdan <ofourdan@redhat.com> Reviewed-by: Adam Jackson <ajax@redhat.com> --- hw/xwayland/man/Xwayland.man | 13 +++++ hw/xwayland/meson.build | 3 + hw/xwayland/xwayland-input.c | 103 ++++++++++++++++++++++++++++++++++ hw/xwayland/xwayland-screen.c | 15 +++++ hw/xwayland/xwayland-screen.h | 4 ++ hw/xwayland/xwayland.c | 4 ++ hw/xwayland/xwayland.pc.in | 1 + 7 files changed, 143 insertions(+) diff --git a/hw/xwayland/man/Xwayland.man b/hw/xwayland/man/Xwayland.man index 97e3c6957b..4e7877730f 100644 --- a/hw/xwayland/man/Xwayland.man +++ b/hw/xwayland/man/Xwayland.man @@ -61,6 +61,19 @@ This option is not compatible with rootless mode (\fI-rootless\fP). .B \-geometry \fIWxH\fP Sets the geometry of the \fIXwayland\fP window to \fIWxH\fP when running rootful. +This option is not compatible with rootless mode (\fI-rootless\fP). +.TP 8 +.B \-host-grab +Disable host keyboard shorcuts and confine the pointer when running rootful. + +This feature relies on the protocol for inhibiting the compositor keyboard +shortcuts and on the protocol for pointer locking and confinement and may +have no effect if the Wayland compositor in use does not support these +protocols. + +Use the keys [CTRL]+[SHIFT] simultaneously to release the keyboard and +pointer devices. + This option is not compatible with rootless mode (\fI-rootless\fP). .TP 8 .B \-initfd \fIfd\fP diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build index 37c39497c3..cd5bf7a530 100644 --- a/hw/xwayland/meson.build +++ b/hw/xwayland/meson.build @@ -46,6 +46,7 @@ dmabuf_xml = join_paths(protodir, 'unstable', 'linux-dmabuf', 'linux-dmabuf-unst viewporter_xml = join_paths(protodir, 'stable', 'viewporter', 'viewporter.xml') xdg_shell_xml = join_paths(protodir, 'stable', 'xdg-shell', 'xdg-shell.xml') drm_lease_xml = join_paths(protodir, 'staging', 'drm-lease', 'drm-lease-v1.xml') +shortcuts_inhibit_xml = join_paths(protodir, 'unstable', 'keyboard-shortcuts-inhibit', 'keyboard-shortcuts-inhibit-unstable-v1.xml') client_header = generator(scanner, output : '@BASENAME@-client-protocol.h', @@ -72,6 +73,7 @@ srcs += client_header.process(dmabuf_xml) srcs += client_header.process(viewporter_xml) srcs += client_header.process(xdg_shell_xml) srcs += client_header.process(drm_lease_xml) +srcs += client_header.process(shortcuts_inhibit_xml) srcs += code.process(relative_xml) srcs += code.process(pointer_xml) srcs += code.process(gestures_xml) @@ -82,6 +84,7 @@ srcs += code.process(dmabuf_xml) srcs += code.process(viewporter_xml) srcs += code.process(xdg_shell_xml) srcs += code.process(drm_lease_xml) +srcs += code.process(shortcuts_inhibit_xml) xwayland_glamor = [] eglstream_srcs = [] diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c index 177c573033..0cf6fb65fb 100644 --- a/hw/xwayland/xwayland-input.c +++ b/hw/xwayland/xwayland-input.c @@ -49,6 +49,7 @@ #include "tablet-unstable-v2-client-protocol.h" #include "pointer-gestures-unstable-v1-client-protocol.h" #include "xwayland-keyboard-grab-unstable-v1-client-protocol.h" +#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h" struct axis_discrete_pending { struct xorg_list l; @@ -128,6 +129,57 @@ init_pointer_buttons(DeviceIntPtr device) return TRUE; } +static void +maybe_fake_grab_devices(struct xwl_seat *xwl_seat) +{ + struct xwl_screen *xwl_screen = xwl_seat->xwl_screen; + struct xwl_window *xwl_window; + + if (xwl_screen->rootless) + return; + + if (!xwl_screen->host_grab) + return; + + if (!xwl_screen->has_grab) + return; + + if (!xwl_screen->screen->root) + return; + + xwl_window = xwl_window_get(xwl_screen->screen->root); + if (!xwl_window) + return; + + xwl_seat_confine_pointer(xwl_seat, xwl_window); + + if (!xwl_screen->shortcuts_inhibit_manager) + return; + + if (xwl_screen->shortcuts_inhibit) + return; + + xwl_screen->shortcuts_inhibit = + zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts ( + xwl_screen->shortcuts_inhibit_manager, + xwl_window->surface, + xwl_seat->seat); +} + +static void +maybe_fake_ungrab_devices(struct xwl_seat *xwl_seat) +{ + struct xwl_screen *xwl_screen = xwl_seat->xwl_screen; + + xwl_seat_unconfine_pointer(xwl_seat); + + if (!xwl_screen->shortcuts_inhibit) + return; + + zwp_keyboard_shortcuts_inhibitor_v1_destroy (xwl_screen->shortcuts_inhibit); + xwl_screen->shortcuts_inhibit = NULL; +} + static int xwl_pointer_proc(DeviceIntPtr device, int what) { @@ -520,6 +572,8 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer, else { xwl_seat_maybe_lock_on_hidden_cursor(xwl_seat); } + + maybe_fake_grab_devices(xwl_seat); } static void @@ -539,6 +593,8 @@ pointer_handle_leave(void *data, struct wl_pointer *pointer, xwl_seat->focus_window = NULL; CheckMotion(NULL, GetMaster(dev, POINTER_OR_FLOAT)); } + + maybe_fake_ungrab_devices(xwl_seat); } static void @@ -927,6 +983,34 @@ static const struct zwp_pointer_gesture_pinch_v1_listener pointer_gesture_pinch_ pointer_gesture_pinch_handle_end }; +static void +maybe_toggle_fake_grab(struct xwl_seat *xwl_seat, uint32_t key) +{ + struct xwl_screen *xwl_screen = xwl_seat->xwl_screen; + XkbStateRec state_rec; + uint32_t xkb_state; + + if (xwl_screen->rootless) + return; + + if (!xwl_screen->host_grab) + return; + + state_rec = xwl_seat->keyboard->key->xkbInfo->state; + xkb_state = (XkbStateFieldFromRec(&state_rec) & 0xff); + + if (((key == KEY_LEFTSHIFT || key == KEY_RIGHTSHIFT) && (xkb_state & ControlMask)) || + ((key == KEY_LEFTCTRL || key == KEY_RIGHTCTRL) && (xkb_state & ShiftMask))) { + + xwl_screen->has_grab = !xwl_screen->has_grab; + + if (xwl_screen->has_grab) + maybe_fake_grab_devices(xwl_seat); + else + maybe_fake_ungrab_devices(xwl_seat); + } +} + static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) @@ -949,6 +1033,9 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, QueueKeyboardEvents(xwl_seat->keyboard, state ? KeyPress : KeyRelease, key + 8); + + if (!state) + maybe_toggle_fake_grab(xwl_seat, key); } static void @@ -1009,6 +1096,8 @@ keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, wl_array_copy(&xwl_seat->keys, keys); wl_array_for_each(k, &xwl_seat->keys) QueueKeyboardEvents(xwl_seat->keyboard, EnterNotify, *k + 8); + + maybe_fake_grab_devices(xwl_seat); } static void @@ -1024,6 +1113,8 @@ keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, QueueKeyboardEvents(xwl_seat->keyboard, LeaveNotify, *k + 8); xwl_seat->keyboard_focus = NULL; + + maybe_fake_ungrab_devices(xwl_seat); } static void @@ -2824,6 +2915,16 @@ init_keyboard_grab(struct xwl_screen *xwl_screen, } } +static void +init_keyboard_shortcuts_inhibit(struct xwl_screen *xwl_screen, + uint32_t id, uint32_t version) +{ + xwl_screen->shortcuts_inhibit_manager = + wl_registry_bind(xwl_screen->registry, id, + &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, + 1); +} + /* The compositor may send us wl_seat and its capabilities before sending e.g. relative_pointer_manager or pointer_gesture interfaces. This would result in devices being created in capabilities handler, but listeners not, because @@ -2873,6 +2974,8 @@ input_handler(void *data, struct wl_registry *registry, uint32_t id, init_tablet_manager(xwl_screen, id, version); } else if (strcmp(interface, "zwp_xwayland_keyboard_grab_manager_v1") == 0) { init_keyboard_grab(xwl_screen, id, version); + } else if (strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0) { + init_keyboard_shortcuts_inhibit(xwl_screen, id, version); } } diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c index 5261125143..a69fc4dc70 100644 --- a/hw/xwayland/xwayland-screen.c +++ b/hw/xwayland/xwayland-screen.c @@ -306,6 +306,12 @@ xwl_cursor_confined_to(DeviceIntPtr device, struct xwl_seat *xwl_seat = device->public.devicePrivate; struct xwl_window *xwl_window; + /* If running rootful with host grab requested, do not tamper with + * pointer confinement. + */ + if (!xwl_screen->rootless && xwl_screen->host_grab && xwl_screen->has_grab) + return; + if (!xwl_seat) xwl_seat = xwl_screen_get_default_seat(xwl_screen); @@ -679,6 +685,10 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv) else if (strcmp(argv[i], "-fullscreen") == 0) { xwl_screen->fullscreen = 1; } + else if (strcmp(argv[i], "-host-grab") == 0) { + xwl_screen->host_grab = 1; + xwl_screen->has_grab = 1; + } } if (use_fixed_size) { @@ -745,6 +755,11 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv) xwl_screen->fullscreen = FALSE; } + if (xwl_screen->host_grab && xwl_screen->rootless) { + ErrorF("error, cannot use host grab when running rootless\n"); + return FALSE; + } + if (!xwl_screen->rootless && !xwl_screen->xdg_wm_base) { ErrorF("missing XDG-WM-Base protocol\n"); return FALSE; diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h index 360e2aa855..6ddb6f7519 100644 --- a/hw/xwayland/xwayland-screen.h +++ b/hw/xwayland/xwayland-screen.h @@ -59,6 +59,8 @@ struct xwl_screen { int present; int force_xrandr_emulation; int fullscreen; + int host_grab; + int has_grab; CreateScreenResourcesProcPtr CreateScreenResources; CloseScreenProcPtr CloseScreen; @@ -88,6 +90,8 @@ struct xwl_screen { struct zwp_pointer_constraints_v1 *pointer_constraints; struct zwp_pointer_gestures_v1 *pointer_gestures; struct zwp_xwayland_keyboard_grab_manager_v1 *wp_grab; + struct zwp_keyboard_shortcuts_inhibit_manager_v1 *shortcuts_inhibit_manager; + struct zwp_keyboard_shortcuts_inhibitor_v1 *shortcuts_inhibit; struct zwp_linux_dmabuf_v1 *dmabuf; struct zxdg_output_manager_v1 *xdg_output_manager; struct wp_viewporter *viewporter; diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c index 8fb929eeb6..e4983ed1e9 100644 --- a/hw/xwayland/xwayland.c +++ b/hw/xwayland/xwayland.c @@ -93,6 +93,7 @@ ddxUseMsg(void) ErrorF("-rootless run rootless, requires wm support\n"); ErrorF("-fullscreen run fullscreen when rootful\n"); ErrorF("-geometry WxH set Xwayland window size when rootful\n"); + ErrorF("-host-grab disable host keyboard shortcuts when rootful\n"); ErrorF("-wm fd create X client for wm on given fd\n"); ErrorF("-initfd fd add given fd as a listen socket for initialization clients\n"); ErrorF("-listenfd fd add given fd as a listen socket\n"); @@ -233,6 +234,9 @@ ddxProcessArgument(int argc, char *argv[], int i) else if (strcmp(argv[i], "-fullscreen") == 0) { return 1; } + else if (strcmp(argv[i], "-host-grab") == 0) { + return 1; + } return 0; } diff --git a/hw/xwayland/xwayland.pc.in b/hw/xwayland/xwayland.pc.in index 5976bd6b14..70db37db75 100644 --- a/hw/xwayland/xwayland.pc.in +++ b/hw/xwayland/xwayland.pc.in @@ -15,3 +15,4 @@ have_no_touch_pointer_emulation=true have_force_xrandr_emulation=true have_geometry=true have_fullscreen=true +have_host_grab=true -- GitLab