From ac1aef7ead881c5da5672852a32e437ef93f50f3 Mon Sep 17 00:00:00 2001 From: Silvan Jegen Date: Mon, 29 Mar 2021 15:17:55 +0200 Subject: [PATCH] implement input method keyboard grab --- dwl.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/dwl.c b/dwl.c index 8726ce8..2aa0071 100644 --- a/dwl.c +++ b/dwl.c @@ -227,6 +227,8 @@ struct dwl_input_method_relay { struct wl_listener input_method_commit; struct wl_listener input_method_destroy; struct wl_listener input_method_new_popup_surface; + struct wl_listener input_method_grab_keyboard; + struct wl_listener input_method_keyboard_grab_destroy; }; struct dwl_text_input { @@ -377,6 +379,9 @@ void dwl_input_method_relay_set_focus(struct dwl_input_method_relay *relay, struct dwl_text_input *dwl_text_input_create( struct dwl_input_method_relay *relay, struct wlr_text_input_v3 *text_input); +static void handle_im_grab_keyboard(struct wl_listener *listener, void *data); +static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, + void *data); static void zoom(const Arg *arg); /* variables */ @@ -1369,6 +1374,26 @@ keybinding(uint32_t mods, xkb_keysym_t sym) return handled; } +/** + * Get keyboard grab of the seat from sway_keyboard if we should forward events + * to it. + * + * Returns NULL if the keyboard is not grabbed by an input method, + * or if event is from virtual keyboard of the same client as grab. + * TODO: see https://github.com/swaywm/wlroots/issues/2322 + */ +static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab(Keyboard* kb) { + struct wlr_input_method_v2 *input_method = input_relay->input_method; + struct wlr_virtual_keyboard_v1 *virtual_keyboard = + wlr_input_device_get_virtual_keyboard(kb->device); + if (!input_method || !input_method->keyboard_grab || (virtual_keyboard && + wl_resource_get_client(virtual_keyboard->resource) == + wl_resource_get_client(input_method->keyboard_grab->resource))) { + return NULL; + } + return input_method->keyboard_grab; +} + void keypress(struct wl_listener *listener, void *data) { @@ -1397,6 +1422,16 @@ keypress(struct wl_listener *listener, void *data) handled = keybinding(mods, syms[i]) || handled; if (!handled) { + /* if there is a keyboard grab, we send the key there */ + struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(kb); + if (kb_grab) { + wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, + kb->device->keyboard); + wlr_input_method_keyboard_grab_v2_send_key(kb_grab, + event->time_msec, event->keycode, event->state); + return; + } + /* Pass unhandled keycodes along to the client. */ wlr_seat_set_keyboard(seat, kb->device); wlr_seat_keyboard_notify_key(seat, event->time_msec, @@ -1410,6 +1445,13 @@ keypressmod(struct wl_listener *listener, void *data) /* This event is raised when a modifier key, such as shift or alt, is * pressed. We simply communicate this to the client. */ Keyboard *kb = wl_container_of(listener, kb, modifiers); + struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(kb); + if (kb_grab) { + wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, + &kb->device->keyboard->modifiers); + return; + } + /* * A seat can only have one keyboard, but this is a limitation of the * Wayland protocol - not wlroots. We assign all connected keyboards to the @@ -1489,6 +1531,38 @@ mapnotify(struct wl_listener *listener, void *data) c->mon->un_map = 1; } +static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) { + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + input_method_grab_keyboard); + struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; + + // send modifier state to grab + struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(seat); + wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, + active_keyboard); + wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab, + &active_keyboard->modifiers); + + wl_signal_add(&keyboard_grab->events.destroy, + &relay->input_method_keyboard_grab_destroy); + relay->input_method_keyboard_grab_destroy.notify = + handle_im_keyboard_grab_destroy; +} + +static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void *data) { + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + input_method_keyboard_grab_destroy); + struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; + wl_list_remove(&relay->input_method_keyboard_grab_destroy.link); + + if (keyboard_grab->keyboard) { + // send modifier state to original client + wlr_seat_keyboard_notify_modifiers(keyboard_grab->input_method->seat, + &keyboard_grab->keyboard->modifiers); + } +} + + void monocle(Monitor *m) { @@ -2487,6 +2561,9 @@ static void relay_handle_input_method(struct wl_listener *listener, wl_signal_add(&relay->input_method->events.new_popup_surface, &relay->input_method_new_popup_surface); relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface; + wl_signal_add(&relay->input_method->events.grab_keyboard, + &relay->input_method_grab_keyboard); + relay->input_method_grab_keyboard.notify = handle_im_grab_keyboard; wl_signal_add(&relay->input_method->events.destroy, &relay->input_method_destroy); relay->input_method_destroy.notify = handle_im_destroy;