From 3b55b3107080e53d2580030bd1bb0b36d1411353 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Wed, 2 Nov 2022 16:37:24 -0400 Subject: [PATCH] keyboard: Implement key repeat for keybindings It seems that every Wayland client is expected to implement its own key-repeat logic, rather than doing it server-side as in X11. This means that labwc also has to implement its own key-repeat logic for compositor keybindings. This is a very simplistic timer-based implementation. It doesn't attempt to synthesize accurate timestamps, and may lag depending on system load, but it appears to get the job done. v2: Use server->wl_event_loop v3: Comments and formatting --- include/labwc.h | 5 ++++ src/keyboard.c | 61 +++++++++++++++++++++++++++++++++++++++++++++---- src/seat.c | 1 + 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 950dd31a..0325bcf4 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -88,6 +88,10 @@ struct keyboard { bool is_virtual; struct wl_listener modifier; struct wl_listener key; + /* key repeat for compositor keybinds */ + uint32_t keybind_repeat_keycode; + int32_t keybind_repeat_rate; + struct wl_event_source *keybind_repeat; }; struct seat { @@ -533,6 +537,7 @@ struct view *desktop_focused_view(struct server *server); void desktop_focus_topmost_mapped_view(struct server *server); bool isfocusable(struct view *view); +void keyboard_cancel_keybind_repeat(struct keyboard *keyboard); void keyboard_key_notify(struct wl_listener *listener, void *data); void keyboard_modifiers_notify(struct wl_listener *listener, void *data); void keyboard_init(struct seat *seat); diff --git a/src/keyboard.c b/src/keyboard.c index e4fa16e6..63bc7016 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include #include "action.h" -#include "buffer.h" #include "key-state.h" #include "labwc.h" #include "workspaces.h" @@ -105,10 +105,9 @@ static bool is_modifier_key(xkb_keysym_t sym) } static bool -handle_compositor_keybindings(struct wl_listener *listener, +handle_compositor_keybindings(struct keyboard *keyboard, struct wlr_keyboard_key_event *event) { - struct keyboard *keyboard = wl_container_of(listener, keyboard, key); struct seat *seat = keyboard->base.seat; struct server *server = seat->server; struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard; @@ -231,6 +230,52 @@ out: return handled; } +static int +handle_keybind_repeat(void *data) +{ + struct keyboard *keyboard = data; + assert(keyboard->keybind_repeat); + + /* synthesize event */ + struct wlr_keyboard_key_event event = { + .keycode = keyboard->keybind_repeat_keycode, + .state = WL_KEYBOARD_KEY_STATE_PRESSED + }; + + handle_compositor_keybindings(keyboard, &event); + wl_event_source_timer_update(keyboard->keybind_repeat, + keyboard->keybind_repeat_rate); + + return 0; /* ignored per wl_event_loop docs */ +} + +static void +start_keybind_repeat(struct server *server, struct keyboard *keyboard, + struct wlr_keyboard_key_event *event) +{ + struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard; + assert(!keyboard->keybind_repeat); + + if (wlr_keyboard->repeat_info.rate > 0 + && wlr_keyboard->repeat_info.delay > 0) { + keyboard->keybind_repeat_keycode = event->keycode; + keyboard->keybind_repeat_rate = wlr_keyboard->repeat_info.rate; + keyboard->keybind_repeat = wl_event_loop_add_timer( + server->wl_event_loop, handle_keybind_repeat, keyboard); + wl_event_source_timer_update(keyboard->keybind_repeat, + wlr_keyboard->repeat_info.delay); + } +} + +void +keyboard_cancel_keybind_repeat(struct keyboard *keyboard) +{ + if (keyboard->keybind_repeat) { + wl_event_source_remove(keyboard->keybind_repeat); + keyboard->keybind_repeat = NULL; + } +} + void keyboard_key_notify(struct wl_listener *listener, void *data) { @@ -242,9 +287,15 @@ keyboard_key_notify(struct wl_listener *listener, void *data) struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard; wlr_idle_notify_activity(seat->wlr_idle, seat->seat); - bool handled = handle_compositor_keybindings(listener, event); + /* any new press/release cancels current keybind repeat */ + keyboard_cancel_keybind_repeat(keyboard); - if (!handled) { + bool handled = handle_compositor_keybindings(keyboard, event); + if (handled) { + if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + start_keybind_repeat(seat->server, keyboard, event); + } + } else { wlr_seat_set_keyboard(wlr_seat, wlr_keyboard); wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, event->keycode, event->state); diff --git a/src/seat.c b/src/seat.c index 64ddd87b..0a099bc2 100644 --- a/src/seat.c +++ b/src/seat.c @@ -24,6 +24,7 @@ input_device_destroy(struct wl_listener *listener, void *data) struct keyboard *keyboard = (struct keyboard *)input; wl_list_remove(&keyboard->key.link); wl_list_remove(&keyboard->modifier.link); + keyboard_cancel_keybind_repeat(keyboard); } free(input); }