Merge branch 'wayland-output-grab-input' into 'master'

Add the ability for wayland output to grab input

See merge request wlroots/wlroots!4441
This commit is contained in:
Lahav Tsur 2024-01-24 22:52:42 +00:00
commit c873399d01
7 changed files with 266 additions and 5 deletions

View file

@ -4,6 +4,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <strings.h>
#include <xkbcommon/xkbcommon.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/backend/headless.h> #include <wlr/backend/headless.h>
@ -13,6 +15,7 @@
#include <wlr/config.h> #include <wlr/config.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <wlr/types/wlr_keyboard.h>
#include "backend/backend.h" #include "backend/backend.h"
#include "backend/multi.h" #include "backend/multi.h"
#include "render/allocator/allocator.h" #include "render/allocator/allocator.h"
@ -146,6 +149,37 @@ static size_t parse_outputs_env(const char *name) {
return outputs; return outputs;
} }
// This code was copied from sway wm source code,
// from https://github.com/swaywm/sway/blob/7cf4e1d/sway/input/keyboard.c
// ____________________________________________________
static struct modifier_key {
char *name;
uint32_t mod;
} modifiers[] = {
{ XKB_MOD_NAME_SHIFT, WLR_MODIFIER_SHIFT },
{ XKB_MOD_NAME_CAPS , WLR_MODIFIER_CAPS },
{ XKB_MOD_NAME_CTRL , WLR_MODIFIER_CTRL },
{ "Ctrl" , WLR_MODIFIER_CTRL },
{ XKB_MOD_NAME_ALT , WLR_MODIFIER_ALT },
{ "Alt" , WLR_MODIFIER_ALT },
{ XKB_MOD_NAME_NUM , WLR_MODIFIER_MOD2 },
{ "Mod3" , WLR_MODIFIER_MOD3 },
{ XKB_MOD_NAME_LOGO , WLR_MODIFIER_LOGO },
{ "Mod5" , WLR_MODIFIER_MOD5 },
};
static uint32_t get_modifier_mask_by_name(const char *name) {
int i;
for (i = 0; i < (int)(sizeof(modifiers) / sizeof(struct modifier_key)); ++i) {
if (strcasecmp(modifiers[i].name, name) == 0) {
return modifiers[i].mod;
}
}
return 0;
}
// ____________________________________________________)
static struct wlr_backend *attempt_wl_backend(struct wl_display *display) { static struct wlr_backend *attempt_wl_backend(struct wl_display *display) {
struct wlr_backend *backend = wlr_wl_backend_create(display, NULL); struct wlr_backend *backend = wlr_wl_backend_create(display, NULL);
if (backend == NULL) { if (backend == NULL) {
@ -157,6 +191,37 @@ static struct wlr_backend *attempt_wl_backend(struct wl_display *display) {
wlr_wl_output_create(backend); wlr_wl_output_create(backend);
} }
char* keyboard_shortcut = getenv("WLR_WL_GRAB_INPUT_SHORTCUT");
if (keyboard_shortcut) {
wlr_log(WLR_INFO, "Loading user-specified input grab keyboard shortcut: %s",
keyboard_shortcut);
xkb_keysym_t input_grab_keysym = 0;
uint32_t input_grab_modifier_mask = 0;
keyboard_shortcut = strdup(keyboard_shortcut);
char *saveptr;
char *key_name = strtok_r(keyboard_shortcut, "+", &saveptr);
while (key_name != NULL) {
uint32_t modifier_mask = get_modifier_mask_by_name(key_name);
input_grab_modifier_mask = input_grab_modifier_mask | modifier_mask;
if(modifier_mask == 0) {
input_grab_keysym = xkb_keysym_from_name(key_name,XKB_KEYSYM_CASE_INSENSITIVE);
if(input_grab_keysym == 0) {
wlr_log(WLR_ERROR,
"The key shortcut contains an unrecognized key name. ignoring key shortcut");
input_grab_keysym = 0;
input_grab_modifier_mask = 0;
free(keyboard_shortcut);
return backend;
}
}
key_name = strtok_r(NULL, "+", &saveptr);
}
free(keyboard_shortcut);
wlr_wl_backend_set_grab_input_shortcut(backend, input_grab_modifier_mask, input_grab_keysym);
}
return backend; return backend;
} }

View file

@ -30,6 +30,8 @@
#include "tablet-unstable-v2-client-protocol.h" #include "tablet-unstable-v2-client-protocol.h"
#include "relative-pointer-unstable-v1-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h"
#include "viewporter-client-protocol.h" #include "viewporter-client-protocol.h"
#include "pointer-constraints-unstable-v1-client-protocol.h"
#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
struct wlr_wl_linux_dmabuf_feedback_v1 { struct wlr_wl_linux_dmabuf_feedback_v1 {
struct wlr_wl_backend *backend; struct wlr_wl_backend *backend;
@ -405,6 +407,12 @@ static void registry_global(void *data, struct wl_registry *registry,
} else if (strcmp(iface, wp_viewporter_interface.name) == 0) { } else if (strcmp(iface, wp_viewporter_interface.name) == 0) {
wl->viewporter = wl_registry_bind(registry, name, wl->viewporter = wl_registry_bind(registry, name,
&wp_viewporter_interface, 1); &wp_viewporter_interface, 1);
} else if (strcmp(iface,zwp_pointer_constraints_v1_interface.name)==0) {
wl->pointer_constraints = wl_registry_bind(registry, name,
&zwp_pointer_constraints_v1_interface, 1);
} else if(strcmp(iface,zwp_keyboard_shortcuts_inhibit_manager_v1_interface.name)==0) {
wl->shortcuts_inhibit_manager = wl_registry_bind(registry, name,
&zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
} }
} }
@ -506,6 +514,12 @@ static void backend_destroy(struct wlr_backend *backend) {
if (wl->tablet_manager) { if (wl->tablet_manager) {
zwp_tablet_manager_v2_destroy(wl->tablet_manager); zwp_tablet_manager_v2_destroy(wl->tablet_manager);
} }
if(wl->pointer_constraints) {
zwp_pointer_constraints_v1_destroy (wl->pointer_constraints);
}
if(wl->shortcuts_inhibit_manager) {
zwp_keyboard_shortcuts_inhibit_manager_v1_destroy (wl->shortcuts_inhibit_manager);
}
if (wl->presentation) { if (wl->presentation) {
wp_presentation_destroy(wl->presentation); wp_presentation_destroy(wl->presentation);
} }
@ -702,3 +716,10 @@ struct wl_display *wlr_wl_backend_get_remote_display(struct wlr_backend *backend
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend); struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
return wl->remote_display; return wl->remote_display;
} }
void wlr_wl_backend_set_grab_input_shortcut(struct wlr_backend *backend, uint32_t modifiers_mask,
xkb_keysym_t keysym) {
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
wl->input_grab_modifiers_mask = modifiers_mask;
wl->input_grab_keysym = keysym;
}

View file

@ -23,6 +23,8 @@ client_protos = [
'xdg-activation-v1', 'xdg-activation-v1',
'xdg-decoration-unstable-v1', 'xdg-decoration-unstable-v1',
'xdg-shell', 'xdg-shell',
'pointer-constraints-unstable-v1',
'keyboard-shortcuts-inhibit-unstable-v1',
] ]
foreach proto : client_protos foreach proto : client_protos

View file

@ -6,6 +6,7 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <xkbcommon/xkbcommon.h>
#include <wayland-client.h> #include <wayland-client.h>
@ -19,6 +20,151 @@
#include "backend/wayland.h" #include "backend/wayland.h"
#include "util/time.h" #include "util/time.h"
#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
#include "pointer-constraints-unstable-v1-client-protocol.h"
//these functions are based on functions with simillar names from xwayland code
// (https://gitlab.freedesktop.org/xorg/xserver/-/blob/befef003/hw/xwayland/xwayland-input.c)
// ______________________________________________________________________
static void wlr_wl_seat_destroy_confined_pointer(struct wlr_wl_seat *seat) {
if(seat->confined_pointer != NULL) {
zwp_confined_pointer_v1_destroy(seat->confined_pointer);
seat->confined_pointer = NULL;
}
}
static void wlr_wl_seat_unconfine_pointer(struct wlr_wl_seat *seat) {
if (seat->confined_pointer) {
wlr_wl_seat_destroy_confined_pointer(seat);
}
}
static void wlr_wl_seat_confine_pointer(struct wlr_wl_seat *seat) {
struct zwp_pointer_constraints_v1 *pointer_constraints =
seat->backend->pointer_constraints;
if (!pointer_constraints) {
return;
}
if (!seat->wl_pointer) {
return;
}
if (seat->confined_pointer) {
return;
}
wlr_wl_seat_unconfine_pointer(seat);
struct wl_surface *surface = seat->grab_surface;
if(surface == NULL) {
wlr_log (WLR_INFO, "surface is null!!!");
return;
}
seat->confined_pointer =
zwp_pointer_constraints_v1_confine_pointer(pointer_constraints,
surface,
seat->wl_pointer,
NULL,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
}
static void maybe_fake_grab_devices(struct wlr_wl_seat *seat) {
wlr_log (WLR_INFO,"Grabbed seat '%s' input!!!!", seat->name);
struct wlr_wl_backend *backend = seat->backend;
wlr_wl_seat_confine_pointer(seat);
if (!backend->shortcuts_inhibit_manager) {
return;
}
if (backend->shortcuts_inhibit) {
return;
}
struct wl_surface *surface = seat->grab_surface;
if(surface == NULL) {
wlr_log (WLR_INFO, "surface is null!!!");
return;
}
backend->shortcuts_inhibit =
zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts (
backend->shortcuts_inhibit_manager,
surface,
seat->wl_seat);
}
static void maybe_fake_ungrab_devices(struct wlr_wl_seat *seat)
{
wlr_log (WLR_INFO,"Released seat '%s' input!!!!", seat->name);
struct wlr_wl_backend *backend = seat->backend;
wlr_wl_seat_unconfine_pointer(seat);
if (!backend->shortcuts_inhibit) {
return;
}
zwp_keyboard_shortcuts_inhibitor_v1_destroy (backend->shortcuts_inhibit);
backend->shortcuts_inhibit = NULL;
}
// ______________________________________________________________________
static bool fake_grab_input_shortcut_was_pressed(xkb_keysym_t keysym, uint32_t depressed_modifiers,
xkb_keysym_t input_grab_keysym, uint32_t input_grab_modifiers_mask) {
xkb_keysym_t input_grab_keysym_lowercase = xkb_keysym_to_lower(input_grab_keysym);
xkb_keysym_t keysym_lowercase = xkb_keysym_to_lower(keysym);
if(!input_grab_keysym && !input_grab_modifiers_mask) {
return false;
}
bool input_grab_only_modifiers = (input_grab_modifiers_mask == depressed_modifiers)
&& !input_grab_keysym;
bool input_grab_only_keysym = !input_grab_modifiers_mask
&& (input_grab_keysym_lowercase == keysym_lowercase);
bool input_grab_modifiers_and_keysym= (input_grab_modifiers_mask == depressed_modifiers)
&& (input_grab_keysym_lowercase==keysym_lowercase);
return input_grab_only_modifiers || input_grab_only_keysym || input_grab_modifiers_and_keysym;
}
static void maybe_toggle_fake_grab(struct wlr_wl_seat *seat, uint32_t key, uint32_t state) {
if(seat->grab_surface == NULL) {
wlr_log (WLR_INFO, "input surface is null. not grabbing/releasing!");
return;
}
struct wlr_keyboard *keyboard = &seat->wlr_keyboard;
struct xkb_state *xkb_state = keyboard->xkb_state;
xkb_keysym_t input_grab_keysym = seat->backend->input_grab_keysym;
uint32_t input_grab_modifiers_mask = seat->backend->input_grab_modifiers_mask;
uint32_t depressed_modifiers = keyboard->modifiers.depressed;
xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, key + 8);
if(state != WL_KEYBOARD_KEY_STATE_RELEASED) {
return;
}
if(fake_grab_input_shortcut_was_pressed(keysym, depressed_modifiers, input_grab_keysym,
input_grab_modifiers_mask)) {
seat->has_grab = !seat->has_grab;
if (seat->has_grab) {
maybe_fake_grab_devices(seat);
}
else {
maybe_fake_ungrab_devices(seat);
}
}
}
static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
uint32_t format, int32_t fd, uint32_t size) { uint32_t format, int32_t fd, uint32_t size) {
@ -27,8 +173,10 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
struct wlr_keyboard *keyboard = data; struct wlr_wl_seat *seat = data;
struct wlr_keyboard *keyboard = &seat->wlr_keyboard;
seat->grab_surface = surface;
uint32_t *keycode_ptr; uint32_t *keycode_ptr;
wl_array_for_each(keycode_ptr, keys) { wl_array_for_each(keycode_ptr, keys) {
struct wlr_keyboard_key_event event = { struct wlr_keyboard_key_event event = {
@ -43,8 +191,10 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface) { uint32_t serial, struct wl_surface *surface) {
struct wlr_keyboard *keyboard = data; struct wlr_wl_seat *seat = data;
struct wlr_keyboard *keyboard = &seat->wlr_keyboard;
seat->grab_surface = NULL;
size_t num_keycodes = keyboard->num_keycodes; size_t num_keycodes = keyboard->num_keycodes;
uint32_t pressed[num_keycodes + 1]; uint32_t pressed[num_keycodes + 1];
memcpy(pressed, keyboard->keycodes, memcpy(pressed, keyboard->keycodes,
@ -65,7 +215,8 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
struct wlr_keyboard *keyboard = data; struct wlr_wl_seat *seat = data;
struct wlr_keyboard *keyboard = &seat->wlr_keyboard;
struct wlr_keyboard_key_event wlr_event = { struct wlr_keyboard_key_event wlr_event = {
.keycode = key, .keycode = key,
@ -74,12 +225,14 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
.update_state = false, .update_state = false,
}; };
wlr_keyboard_notify_key(keyboard, &wlr_event); wlr_keyboard_notify_key(keyboard, &wlr_event);
maybe_toggle_fake_grab (seat,key,state);
} }
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard, static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group) { uint32_t mods_locked, uint32_t group) {
struct wlr_keyboard *keyboard = data; struct wlr_wl_seat *seat = data;
struct wlr_keyboard *keyboard = &seat->wlr_keyboard;
wlr_keyboard_notify_modifiers(keyboard, mods_depressed, mods_latched, wlr_keyboard_notify_modifiers(keyboard, mods_depressed, mods_latched,
mods_locked, group); mods_locked, group);
} }
@ -109,8 +262,9 @@ void init_seat_keyboard(struct wlr_wl_seat *seat) {
snprintf(name, sizeof(name), "wayland-keyboard-%s", seat->name); snprintf(name, sizeof(name), "wayland-keyboard-%s", seat->name);
wlr_keyboard_init(&seat->wlr_keyboard, &keyboard_impl, name); wlr_keyboard_init(&seat->wlr_keyboard, &keyboard_impl, name);
//passing wlr_wl_seat instead of wlr_keyboard to enable the fake input grab functionality
wl_keyboard_add_listener(seat->wl_keyboard, &keyboard_listener, wl_keyboard_add_listener(seat->wl_keyboard, &keyboard_listener,
&seat->wlr_keyboard); seat);
wl_signal_emit_mutable(&seat->backend->backend.events.new_input, wl_signal_emit_mutable(&seat->backend->backend.events.new_input,
&seat->wlr_keyboard.base); &seat->wlr_keyboard.base);

View file

@ -39,6 +39,7 @@ wlroots reads these environment variables
## Wayland backend ## Wayland backend
* *WLR_WL_OUTPUTS*: when using the wayland backend specifies the number of outputs * *WLR_WL_OUTPUTS*: when using the wayland backend specifies the number of outputs
* *WLR_WL_GRAB_INPUT_SHORTCUT*: when using the wayland backend, specify the shortcut (mods+key) to toggle input grab of the focused wayland output. Examples: control+shift+alt, control_r, alt_l.
## X11 backend ## X11 backend

View file

@ -18,6 +18,8 @@
struct wlr_wl_backend { struct wlr_wl_backend {
struct wlr_backend backend; struct wlr_backend backend;
uint32_t input_grab_modifiers_mask; //for input grab.
xkb_keysym_t input_grab_keysym; //for input grab.
/* local state */ /* local state */
bool started; bool started;
struct wl_display *local_display; struct wl_display *local_display;
@ -49,6 +51,9 @@ struct wlr_wl_backend {
struct xdg_activation_v1 *activation_v1; struct xdg_activation_v1 *activation_v1;
struct wl_subcompositor *subcompositor; struct wl_subcompositor *subcompositor;
struct wp_viewporter *viewporter; struct wp_viewporter *viewporter;
struct zwp_pointer_constraints_v1 *pointer_constraints;
struct zwp_keyboard_shortcuts_inhibit_manager_v1 *shortcuts_inhibit_manager;
struct zwp_keyboard_shortcuts_inhibitor_v1 *shortcuts_inhibit;
char *drm_render_name; char *drm_render_name;
}; };
@ -135,6 +140,7 @@ struct wlr_wl_seat {
struct wlr_wl_pointer *active_pointer; struct wlr_wl_pointer *active_pointer;
struct wl_list pointers; // wlr_wl_pointer.link struct wl_list pointers; // wlr_wl_pointer.link
struct wl_surface *grab_surface; //for fake grabbing input. may be NULL when keyboard focus is not on any wayland output
struct zwp_pointer_gesture_swipe_v1 *gesture_swipe; struct zwp_pointer_gesture_swipe_v1 *gesture_swipe;
struct zwp_pointer_gesture_pinch_v1 *gesture_pinch; struct zwp_pointer_gesture_pinch_v1 *gesture_pinch;
struct zwp_pointer_gesture_hold_v1 *gesture_hold; struct zwp_pointer_gesture_hold_v1 *gesture_hold;
@ -151,6 +157,8 @@ struct wlr_wl_seat {
struct wlr_tablet_tool wlr_tablet_tool; struct wlr_tablet_tool wlr_tablet_tool;
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2; struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2;
struct wlr_tablet_pad wlr_tablet_pad; struct wlr_tablet_pad wlr_tablet_pad;
struct zwp_confined_pointer_v1 *confined_pointer;
bool has_grab; //for fake grabbing input. may be NULL when no grab was initiated yet.
struct wl_list link; // wlr_wl_backend.seats struct wl_list link; // wlr_wl_backend.seats
}; };

View file

@ -5,6 +5,7 @@
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/backend.h> #include <wlr/backend.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
#include <xkbcommon/xkbcommon.h>
struct wlr_input_device; struct wlr_input_device;
@ -23,6 +24,15 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
*/ */
struct wl_display *wlr_wl_backend_get_remote_display(struct wlr_backend *backend); struct wl_display *wlr_wl_backend_get_remote_display(struct wlr_backend *backend);
/**
* Sets the keyboard shortcut for toggle grabbing input of the focused wayland backend output.
*
* If modifiers_mask and keysym are zero or null, no shortcut will be used.
*
*/
void wlr_wl_backend_set_grab_input_shortcut(struct wlr_backend *backend, uint32_t modifiers_mask,
xkb_keysym_t keysym);
/** /**
* Adds a new output to this backend. * Adds a new output to this backend.
* *