We are currently migrating Bugzilla to GitHub issues.
Any changes made to the bug tracker now will be lost, so please do not post new bugs or make changes to them.
When we're done, all bug URLs will redirect to their equivalent location on the new bug tracker.

Bug 4445 - broken key repeat/delay on wayland
Summary: broken key repeat/delay on wayland
Status: RESOLVED FIXED
Alias: None
Product: SDL
Classification: Unclassified
Component: events (show other bugs)
Version: 2.0.10
Hardware: x86_64 Linux
: P2 major
Assignee: Ryan C. Gordon
QA Contact: Sam Lantinga
URL:
Keywords: target-2.0.16
Depends on:
Blocks:
 
Reported: 2019-01-09 05:53 UTC by rvi64sv
Modified: 2020-10-20 00:30 UTC (History)
2 users (show)

See Also:


Attachments
implement keyboard repeat in wayland video driver (8.26 KB, patch)
2020-10-15 04:39 UTC, Yiding Jia
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description rvi64sv 2019-01-09 05:53:44 UTC
i'm currently using sway which is based on wl-roots, that is an abstraction over wayland. sway has built-in xkb repeat/delay options that work with xwayland and some other wayland-native apps like termite (based on GTK).
if i'm using the default SDL_VIDEODRIVER=x11 the key repeat and delays work as expected in my SDL2 application, that is every WaitEvent() i get the KEYDOWN if i hold it down. but if i set SDL_VIDEODRIVER=wayland my repeat and delay stops working, i only get events on KEYDOWN and KEYUP 1 time.
Comment 1 greg 2020-05-01 21:03:47 UTC
This makes games like Cataclysm: Dark Days Ahead really hard to play :(
Comment 2 Yiding Jia 2020-10-15 04:39:20 UTC
Created attachment 4479 [details]
implement keyboard repeat in wayland video driver

Wayland passes keyboard repeat info to the client and expect client to handle keyboard repeats. I attached a patch that implements this.
Comment 3 Yiding Jia 2020-10-15 04:41:32 UTC
Comment on attachment 4479 [details]
implement keyboard repeat in wayland video driver

# HG changeset patch
# User Yiding Jia <yiding.jia@gmail.com>
# Date 1601950672 25200
#      Mon Oct 05 19:17:52 2020 -0700
# Node ID a81c7618de8f780de5a9caf71380ededaaa6f0f3
# Parent  1b1fae168557617cc221ec2fc4f58d8cd7d0e141
Implement keyboard repeat in wayland video driver

diff -r 1b1fae168557 -r a81c7618de8f src/video/wayland/SDL_waylandevents.c
--- a/src/video/wayland/SDL_waylandevents.c	Fri Oct 02 10:48:27 2020 +0200
+++ b/src/video/wayland/SDL_waylandevents.c	Mon Oct 05 19:17:52 2020 -0700
@@ -25,6 +25,7 @@
 
 #include "SDL_stdinc.h"
 #include "SDL_assert.h"
+#include "SDL_timer.h"
 
 #include "../../core/unix/SDL_poll.h"
 #include "../../events/SDL_sysevents.h"
@@ -57,6 +58,18 @@
 #include <unistd.h>
 #include <xkbcommon/xkbcommon.h>
 
+typedef struct {
+    // repeat_rate in range of [1, 1000]
+    int32_t repeat_rate;
+    int32_t repeat_delay;
+    SDL_bool is_initialized;
+
+    SDL_bool is_key_down;
+    uint32_t next_repeat_ms;
+    uint32_t scancode;
+    char text[8];
+} SDL_WaylandKeyboardRepeat;
+
 struct SDL_WaylandInput {
     SDL_VideoData *display;
     struct wl_seat *seat;
@@ -90,6 +103,8 @@
         SDL_bool is_y_discrete;
         float y;
     } pointer_curr_axis_info;
+
+    SDL_WaylandKeyboardRepeat keyboard_repeat;
 };
 
 struct SDL_WaylandTouchPoint {
@@ -196,14 +211,62 @@
     return NULL;
 }
 
+/* Returns the time till next repeat, or 0 if no key is down. */
+static void
+keyboard_repeat_handle(SDL_WaylandKeyboardRepeat* repeat_info, uint32_t now)
+{
+    if (!repeat_info->is_key_down || !repeat_info->is_initialized) {
+        return;
+    }
+    while (repeat_info->next_repeat_ms <= now) {
+        if (repeat_info->scancode != SDL_SCANCODE_UNKNOWN) {
+            SDL_SendKeyboardKey(SDL_PRESSED, repeat_info->scancode);
+        }
+        if (repeat_info->text[0]) {
+            SDL_SendKeyboardText(repeat_info->text);
+        }
+        repeat_info->next_repeat_ms += 1000 / repeat_info->repeat_rate;
+    }
+}
+
+static void
+keyboard_repeat_clear(SDL_WaylandKeyboardRepeat* repeat_info) {
+    if (!repeat_info->is_initialized) {
+        return;
+    }
+    repeat_info->is_key_down = SDL_FALSE;
+}
+
+static void
+keyboard_repeat_set(SDL_WaylandKeyboardRepeat* repeat_info,
+                    uint32_t scancode, SDL_bool has_text, char text[8]) {
+    if (!repeat_info->is_initialized) {
+        return;
+    }
+    repeat_info->is_key_down = SDL_TRUE;
+    repeat_info->next_repeat_ms = SDL_GetTicks() + repeat_info->repeat_delay;
+    repeat_info->scancode = scancode;
+    if (has_text) {
+        memcpy(repeat_info->text, text, 8);
+    } else {
+        repeat_info->text[0] = '\0';
+    }
+}
+
 void
 Wayland_PumpEvents(_THIS)
 {
     SDL_VideoData *d = _this->driverdata;
+    struct SDL_WaylandInput *input = d->input;
     int err;
 
     WAYLAND_wl_display_flush(d->display);
 
+    if (input) {
+        uint32_t now = SDL_GetTicks();
+        keyboard_repeat_handle(&input->keyboard_repeat, now);
+    }
+
     if (SDL_IOReady(WAYLAND_wl_display_get_fd(d->display), SDL_FALSE, 0)) {
         err = WAYLAND_wl_display_dispatch(d->display);
     } else {
@@ -365,8 +428,8 @@
             default:
                 return;
         }
-            
-        Wayland_data_device_set_serial(input->data_device, serial); 
+
+        Wayland_data_device_set_serial(input->data_device, serial);
 
         SDL_SendMouseButton(window->sdlwindow, 0,
                             state ? SDL_PRESSED : SDL_RELEASED, sdl_button);
@@ -651,45 +714,52 @@
     SDL_SetKeyboardFocus(NULL);
 }
 
+static SDL_bool
+keyboard_input_get_text(char text[8], const struct SDL_WaylandInput *input, uint32_t key)
+{
+    SDL_WindowData *window = input->keyboard_focus;
+    const xkb_keysym_t *syms;
+
+    if (!window || window->keyboard_device != input || !input->xkb.state) {
+        return SDL_FALSE;
+    }
+
+    // TODO can this happen?
+    if (WAYLAND_xkb_state_key_get_syms(input->xkb.state, key + 8, &syms) != 1) {
+        return SDL_FALSE;
+    }
+
+    return WAYLAND_xkb_keysym_to_utf8(syms[0], text, 8) > 0;
+}
+
 static void
 keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
                     uint32_t serial, uint32_t time, uint32_t key,
                     uint32_t state_w)
 {
     struct SDL_WaylandInput *input = data;
-    SDL_WindowData *window = input->keyboard_focus;
     enum wl_keyboard_key_state state = state_w;
-    const xkb_keysym_t *syms;
-    uint32_t scancode;
+    uint32_t scancode = SDL_SCANCODE_UNKNOWN;
     char text[8];
-    int size;
 
     if (key < SDL_arraysize(xfree86_scancode_table2)) {
         scancode = xfree86_scancode_table2[key];
 
-        // TODO when do we get WL_KEYBOARD_KEY_STATE_REPEAT?
-        if (scancode != SDL_SCANCODE_UNKNOWN)
+        if (scancode != SDL_SCANCODE_UNKNOWN) {
             SDL_SendKeyboardKey(state == WL_KEYBOARD_KEY_STATE_PRESSED ?
                                 SDL_PRESSED : SDL_RELEASED, scancode);
+        }
     }
 
-    if (!window || window->keyboard_device != input || !input->xkb.state)
-        return;
-
-    // TODO can this happen?
-    if (WAYLAND_xkb_state_key_get_syms(input->xkb.state, key + 8, &syms) != 1)
-        return;
-
-    if (state) {
-        size = WAYLAND_xkb_keysym_to_utf8(syms[0], text, sizeof text);
-
-        if (size > 0) {
-            text[size] = 0;
-
+    if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+        SDL_bool has_text = keyboard_input_get_text(text, input, key);
+        if (has_text) {
             Wayland_data_device_set_serial(input->data_device, serial);
-
             SDL_SendKeyboardText(text);
         }
+        keyboard_repeat_set(&input->keyboard_repeat, scancode, has_text, text);
+    } else {
+        keyboard_repeat_clear(&input->keyboard_repeat);
     }
 }
 
@@ -709,7 +779,10 @@
 keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
                             int32_t rate, int32_t delay)
 {
-    /* unimplemented */
+    struct SDL_WaylandInput *input = data;
+    input->keyboard_repeat.repeat_rate = SDL_max(0, SDL_min(rate, 1000));
+    input->keyboard_repeat.repeat_delay = delay;
+    input->keyboard_repeat.is_initialized = SDL_TRUE;
 }
 
 static const struct wl_keyboard_listener keyboard_listener = {
@@ -786,13 +859,13 @@
 {
     Wayland_data_source_send((SDL_WaylandDataSource *)data, mime_type, fd);
 }
-                       
+
 static void
 data_source_handle_cancelled(void *data, struct wl_data_source *wl_data_source)
 {
     Wayland_data_source_destroy(data);
 }
-                       
+
 static void
 data_source_handle_dnd_drop_performed(void *data, struct wl_data_source *wl_data_source)
 {
@@ -835,7 +908,7 @@
                      driver_data->data_device_manager);
         }
 
-        if (id == NULL) { 
+        if (id == NULL) {
             SDL_SetError("Wayland unable to create data source");
         } else {
             data_source = SDL_calloc(1, sizeof *data_source);
@@ -905,8 +978,8 @@
 {
     SDL_WaylandDataDevice *data_device = data;
     SDL_bool has_mime = SDL_FALSE;
-    uint32_t dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; 
-        
+    uint32_t dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
+
     data_device->drag_serial = serial;
 
     if (id != NULL) {
@@ -959,7 +1032,7 @@
     const char *current_uri = NULL;
     const char *last_char = NULL;
     char *current_char = NULL;
-    
+
     if (data_device->drag_offer != NULL) {
         /* TODO: SDL Support more mime types */
         buffer = Wayland_data_offer_receive(data_device->drag_offer,
@@ -985,7 +1058,7 @@
 static void
 data_device_handle_selection(void *data, struct wl_data_device *wl_data_device,
                              struct wl_data_offer *id)
-{    
+{
     SDL_WaylandDataDevice *data_device = data;
     SDL_WaylandDataOffer *offer = NULL;
 
@@ -1025,7 +1098,7 @@
     input->sx_w = wl_fixed_from_int(0);
     input->sy_w = wl_fixed_from_int(0);
     d->input = input;
-    
+
     if (d->data_device_manager != NULL) {
         data_device = SDL_calloc(1, sizeof *data_device);
         if (data_device == NULL) {
Comment 4 Yiding Jia 2020-10-15 04:44:54 UTC
original patch had an errant SDL_Log in it. I tried to edit the attachment but it just posted a comment. The only difference is the removal of the log.
Comment 5 Sam Lantinga 2020-10-20 00:30:51 UTC
Patch added, thanks!
https://hg.libsdl.org/SDL/rev/d3fa6ce33db3
https://hg.libsdl.org/SDL/rev/71e97db0ba5d