Implement keyboard repeat in wayland video driver
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 52d9dc7..ee4c3db 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -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,53 @@
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_Log("handle_key: posting: %s", text);
SDL_SendKeyboardText(text);
}
+ keyboard_repeat_set(&input->keyboard_repeat, scancode, has_text, text);
+ } else {
+ keyboard_repeat_clear(&input->keyboard_repeat);
}
}
@@ -709,7 +780,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 +860,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 +909,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 +979,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 +1033,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 +1059,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 +1099,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) {