| Summary: | broken key repeat/delay on wayland | ||
|---|---|---|---|
| Product: | SDL | Reporter: | rvi64sv |
| Component: | events | Assignee: | Ryan C. Gordon <icculus> |
| Status: | RESOLVED FIXED | QA Contact: | Sam Lantinga <slouken> |
| Severity: | major | ||
| Priority: | P2 | CC: | greg, yiding.jia |
| Version: | 2.0.10 | Keywords: | target-2.0.16 |
| Hardware: | x86_64 | ||
| OS: | Linux | ||
| Attachments: | implement keyboard repeat in wayland video driver | ||
|
Description
rvi64sv
2019-01-09 05:53:44 UTC
This makes games like Cataclysm: Dark Days Ahead really hard to play :( 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 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) { 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. Patch added, thanks! https://hg.libsdl.org/SDL/rev/d3fa6ce33db3 https://hg.libsdl.org/SDL/rev/71e97db0ba5d |