diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 52d9dc75a..ee4c3db92 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 #include +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 @@ struct SDL_WaylandInput { SDL_bool is_y_discrete; float y; } pointer_curr_axis_info; + + SDL_WaylandKeyboardRepeat keyboard_repeat; }; struct SDL_WaylandTouchPoint { @@ -196,14 +211,62 @@ touch_surface(SDL_TouchID id) 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 @@ pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_t serial, 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 @@ keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, 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 @@ static void 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 @@ data_source_handle_send(void *data, struct wl_data_source *wl_data_source, { 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 @@ Wayland_data_source_create(_THIS) 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 @@ data_device_handle_enter(void *data, struct wl_data_device *wl_data_device, { 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 @@ data_device_handle_drop(void *data, struct wl_data_device *wl_data_device) 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 @@ data_device_handle_drop(void *data, struct wl_data_device *wl_data_device) 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 @@ Wayland_display_add_input(SDL_VideoData *d, uint32_t id, uint32_t version) 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) {