From a3a814155773d878bd82f5cc3afcd7d4557c14f3 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Tue, 7 Jul 2026 12:32:44 -0400
Subject: [PATCH] wayland: Simplify key repeat handling
Remove redundant state variables, remove trivial helper functions, and guard against the unlikely case where a compositor may set the repeat rate to zero while a repeat is active, which would result in a divide-by-zero error.
Key repeat is increasingly handled server-side with seat v10 and above, so consider that the most common path for the future.
---
src/video/wayland/SDL_waylandevents.c | 77 +++++++------------------
src/video/wayland/SDL_waylandevents_c.h | 8 +--
2 files changed, 23 insertions(+), 62 deletions(-)
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index cc3469a22239e..56e6b61b94171 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -411,11 +411,9 @@ static void Wayland_SeatSetKeymap(SDL_WaylandSeat *seat)
}
}
-// Returns true if a key repeat event was due
-static bool keyboard_repeat_handle(SDL_WaylandKeyboardRepeat *repeat_info, Uint64 elapsed)
+// Synthesize key repeat events
+static void keyboard_repeat_handle(SDL_WaylandKeyboardRepeat *repeat_info, Uint64 elapsed)
{
- bool ret = false;
-
while (elapsed >= repeat_info->next_repeat_ns) {
if (repeat_info->scancode != SDL_SCANCODE_UNKNOWN) {
const Uint64 timestamp = repeat_info->base_time_ns + repeat_info->next_repeat_ns;
@@ -425,26 +423,15 @@ static bool keyboard_repeat_handle(SDL_WaylandKeyboardRepeat *repeat_info, Uint6
SDL_SendKeyboardText(repeat_info->text);
}
repeat_info->next_repeat_ns += SDL_NS_PER_SECOND / (Uint64)repeat_info->repeat_rate;
- ret = true;
- }
- return ret;
-}
-
-static void keyboard_repeat_clear(SDL_WaylandKeyboardRepeat *repeat_info)
-{
- if (!repeat_info->is_initialized) {
- return;
}
- repeat_info->is_key_down = false;
}
static void keyboard_repeat_set(SDL_WaylandKeyboardRepeat *repeat_info, Uint32 keyboard_id, uint32_t key, Uint32 wl_press_time_ms,
- Uint64 base_time_ns, uint32_t scancode, bool has_text, char text[8])
+ Uint64 base_time_ns, uint32_t scancode, const char *text)
{
- if (!repeat_info->is_initialized || !repeat_info->repeat_rate) {
+ if (!repeat_info->repeat_rate) {
return;
}
- repeat_info->is_key_down = true;
repeat_info->keyboard_id = keyboard_id;
repeat_info->key = key;
repeat_info->wl_press_time_ms = wl_press_time_ms;
@@ -452,39 +439,13 @@ static void keyboard_repeat_set(SDL_WaylandKeyboardRepeat *repeat_info, Uint32 k
repeat_info->sdl_press_time_ns = SDL_GetTicksNS();
repeat_info->next_repeat_ns = SDL_MS_TO_NS(repeat_info->repeat_delay_ms);
repeat_info->scancode = scancode;
- if (has_text) {
- SDL_memcpy(repeat_info->text, text, sizeof(repeat_info->text));
+ if (text) {
+ SDL_strlcpy(repeat_info->text, text, SDL_arraysize(repeat_info->text));
} else {
repeat_info->text[0] = '\0';
}
}
-static uint32_t keyboard_repeat_get_key(SDL_WaylandKeyboardRepeat *repeat_info)
-{
- if (repeat_info->is_initialized && repeat_info->is_key_down) {
- return repeat_info->key;
- }
-
- return 0;
-}
-
-static void keyboard_repeat_set_text(SDL_WaylandKeyboardRepeat *repeat_info, const char text[8])
-{
- if (repeat_info->is_initialized) {
- SDL_copyp(repeat_info->text, text);
- }
-}
-
-static bool keyboard_repeat_is_set(SDL_WaylandKeyboardRepeat *repeat_info)
-{
- return repeat_info->is_initialized && repeat_info->is_key_down;
-}
-
-static bool keyboard_repeat_key_is_set(SDL_WaylandKeyboardRepeat *repeat_info, uint32_t key)
-{
- return repeat_info->is_initialized && repeat_info->is_key_down && key == repeat_info->key;
-}
-
static void sync_done_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
{
// Nothing to do, just destroy the callback
@@ -527,7 +488,7 @@ int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS)
// If key repeat is active, we'll need to cap our maximum wait time to handle repeats
wl_list_for_each (seat, &d->seat_list, link) {
- if (keyboard_repeat_is_set(&seat->keyboard.repeat)) {
+ if (seat->keyboard.repeat.key) {
const Uint64 elapsed = start - seat->keyboard.repeat.sdl_press_time_ns;
const Uint64 next_repeat_wait_time = (seat->keyboard.repeat.next_repeat_ns - elapsed) + 1;
if (timeoutNS < 0 || next_repeat_wait_time <= timeoutNS) {
@@ -664,7 +625,7 @@ void Wayland_PumpEvents(SDL_VideoDevice *_this)
if (ret >= 0) {
// Synthesize key repeat events.
wl_list_for_each (seat, &d->seat_list, link) {
- if (keyboard_repeat_is_set(&seat->keyboard.repeat)) {
+ if (seat->keyboard.repeat.key) {
Wayland_SeatSetKeymap(seat);
const Uint64 elapsed = SDL_GetTicksNS() - seat->keyboard.repeat.sdl_press_time_ns;
@@ -2181,7 +2142,7 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
}
// Stop key repeat before clearing keyboard focus
- keyboard_repeat_clear(&seat->keyboard.repeat);
+ seat->keyboard.repeat.key = 0;
SDL_Window *keyboard_focus = SDL_GetKeyboardFocus();
@@ -2304,7 +2265,7 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
has_text = keyboard_input_get_text(text, seat, key, true, &handled_by_ime);
}
} else {
- if (keyboard_repeat_key_is_set(&seat->keyboard.repeat, key)) {
+ if (key == seat->keyboard.repeat.key) {
/* Send any due key repeat events before stopping the repeat and generating the key up event.
* Compute time based on the Wayland time, as it reports when the release event happened.
* Using SDL_GetTicks would be wrong, as it would report when the release event is processed,
@@ -2312,7 +2273,7 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
*/
const Uint64 elapsed = SDL_MS_TO_NS(time - seat->keyboard.repeat.wl_press_time_ms);
keyboard_repeat_handle(&seat->keyboard.repeat, elapsed);
- keyboard_repeat_clear(&seat->keyboard.repeat);
+ seat->keyboard.repeat.key = 0;
}
keyboard_input_get_text(text, seat, key, false, &handled_by_ime);
}
@@ -2348,7 +2309,7 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
SDL_SendKeyboardText(text);
}
if (seat->keyboard.xkb.keymap && WAYLAND_xkb_keymap_key_repeats(seat->keyboard.xkb.keymap, key + 8)) {
- keyboard_repeat_set(&seat->keyboard.repeat, seat->keyboard.sdl_id, key, time, timestamp_ns, scancode, has_text, text);
+ keyboard_repeat_set(&seat->keyboard.repeat, seat->keyboard.sdl_id, key, time, timestamp_ns, scancode, has_text ? text : NULL);
}
}
}
@@ -2378,12 +2339,11 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
0, 0, group);
// If a key is repeating, update the text to apply the modifier.
- if (keyboard_repeat_is_set(&seat->keyboard.repeat)) {
+ if (seat->keyboard.repeat.key) {
char text[8];
- const uint32_t key = keyboard_repeat_get_key(&seat->keyboard.repeat);
- if (keyboard_input_get_text(text, seat, key, true, NULL)) {
- keyboard_repeat_set_text(&seat->keyboard.repeat, text);
+ if (keyboard_input_get_text(text, seat, seat->keyboard.repeat.key, true, NULL)) {
+ SDL_strlcpy(seat->keyboard.repeat.text, text, SDL_arraysize(seat->keyboard.repeat.text));
}
}
@@ -2401,9 +2361,12 @@ static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keybo
int32_t rate, int32_t delay)
{
SDL_WaylandSeat *seat = data;
- seat->keyboard.repeat.repeat_rate = SDL_clamp(rate, 0, 1000);
+ seat->keyboard.repeat.repeat_rate = rate;
seat->keyboard.repeat.repeat_delay_ms = delay;
- seat->keyboard.repeat.is_initialized = true;
+
+ if (!rate) {
+ seat->keyboard.repeat.key = 0; // Cancel the repeat to avoid dividing by a rate of zero.
+ }
}
static const struct wl_keyboard_listener keyboard_listener = {
diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h
index 9e3d573b6c00c..42faffd7fab7f 100644
--- a/src/video/wayland/SDL_waylandevents_c.h
+++ b/src/video/wayland/SDL_waylandevents_c.h
@@ -43,16 +43,14 @@ typedef struct
{
Sint32 repeat_rate; // Repeat rate in range of [1, 1000] character(s) per second
Sint32 repeat_delay_ms; // Time to first repeat event in milliseconds
- Uint32 keyboard_id; // ID of the source keyboard.
- bool is_initialized;
- bool is_key_down;
- Uint32 key;
+ Uint32 key; // Key code of the repeating key
+ Uint32 scancode; // Scancode of the repeating key
+ Uint32 keyboard_id; // ID of the source keyboard
Uint32 wl_press_time_ms; // Key press time as reported by the Wayland API in milliseconds
Uint64 base_time_ns; // Key press time as reported by the Wayland API in nanoseconds
Uint64 sdl_press_time_ns; // Key press time expressed in SDL ticks
Uint64 next_repeat_ns; // Next repeat event in nanoseconds
- Uint32 scancode;
char text[8];
} SDL_WaylandKeyboardRepeat;