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>
This commit is contained in:
Olivier Fourdan 2022-04-28 12:26:49 +02:00
parent 503e7663f8
commit c03e582f0c
7 changed files with 143 additions and 0 deletions

View File

@ -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

View File

@ -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 = []

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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