Skip to content

Commit

Permalink
Implement keyboard repeat in wayland video driver
Browse files Browse the repository at this point in the history
  • Loading branch information
yiding committed Oct 6, 2020
1 parent 73010da commit f31ad57
Showing 1 changed file with 105 additions and 31 deletions.
136 changes: 105 additions & 31 deletions src/video/wayland/SDL_waylandevents.c
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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 = {
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand All @@ -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;

Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit f31ad57

Please sign in to comment.