From 31267feb032b9dd6da1e6adbc23e7b06f9ba4bcf Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Tue, 27 May 2025 10:31:31 -0400
Subject: [PATCH] wayland: Use raw timestamps to calculate the elapsed repeat
time on a key up event
Using processed timestamps can result in anomalies that cause excessive repeat events, and hard caps can cause issues.
In the key event handler, use the raw elapsed time to calculate any remaining repeat events to avoid the artifacts that can result from using processed timestamps.
The Wayland key repeat rate ranges from 0 to 1000 per second, so millisecond resolution doesn't lose any precision.
(cherry picked from commit e3d44cdd51155946bd0762cd0d8e8c75f5e9ed4e)
---
src/video/wayland/SDL_waylandevents.c | 34 ++++++++-----------------
src/video/wayland/SDL_waylandevents_c.h | 13 +++++-----
2 files changed, 18 insertions(+), 29 deletions(-)
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 5111908a0bc0e..291a4676471e8 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -212,11 +212,6 @@ static Uint64 Wayland_GetKeyboardTimestamp(struct SDL_WaylandInput *input, Uint3
return 0;
}
-static Uint64 Wayland_GetKeyboardTimestampRaw(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
-{
- return input->keyboard_timestamp_ns ? input->keyboard_timestamp_ns : Wayland_EventTimestampMSToNS(wl_timestamp_ms);
-}
-
static Uint64 Wayland_GetPointerTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
{
if (wl_timestamp_ms) {
@@ -273,18 +268,10 @@ static bool keyboard_repeat_handle(SDL_WaylandKeyboardRepeat *repeat_info, Uint6
{
bool ret = false;
- /* Cap the elapsed time to something sane in case the compositor sends a bad timestamp,
- * which can result it in it looking like the key has been pressed for a *very* long time,
- * bringing everything to a halt while it tries to enqueue all the repeat events.
- *
- * 3 seconds seems reasonable.
- */
- elapsed = SDL_min(elapsed, SDL_MS_TO_NS(3000));
-
while (elapsed >= repeat_info->next_repeat_ns) {
if (repeat_info->scancode != SDL_SCANCODE_UNKNOWN) {
- const Uint64 timestamp = repeat_info->wl_press_time_ns + repeat_info->next_repeat_ns;
- SDL_SendKeyboardKeyIgnoreModifiers(Wayland_GetEventTimestamp(timestamp), repeat_info->keyboard_id, repeat_info->key, repeat_info->scancode, true);
+ const Uint64 timestamp = repeat_info->base_time_ns + repeat_info->next_repeat_ns;
+ SDL_SendKeyboardKeyIgnoreModifiers(timestamp, repeat_info->keyboard_id, repeat_info->key, repeat_info->scancode, true);
}
if (repeat_info->text[0]) {
SDL_SendKeyboardText(repeat_info->text);
@@ -303,8 +290,8 @@ static void keyboard_repeat_clear(SDL_WaylandKeyboardRepeat *repeat_info)
repeat_info->is_key_down = false;
}
-static void keyboard_repeat_set(SDL_WaylandKeyboardRepeat *repeat_info, Uint32 keyboard_id, uint32_t key, Uint64 wl_press_time_ns,
- uint32_t scancode, bool has_text, char text[8])
+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])
{
if (!repeat_info->is_initialized || !repeat_info->repeat_rate) {
return;
@@ -312,7 +299,8 @@ static void keyboard_repeat_set(SDL_WaylandKeyboardRepeat *repeat_info, Uint32 k
repeat_info->is_key_down = true;
repeat_info->keyboard_id = keyboard_id;
repeat_info->key = key;
- repeat_info->wl_press_time_ns = wl_press_time_ns;
+ repeat_info->wl_press_time_ms = wl_press_time_ms;
+ repeat_info->base_time_ns = base_time_ns;
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;
@@ -1867,7 +1855,7 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
char text[8];
bool has_text = false;
bool handled_by_ime = false;
- const Uint64 timestamp_raw_ns = Wayland_GetKeyboardTimestampRaw(input, time);
+ const Uint64 timestamp_ns = Wayland_GetKeyboardTimestamp(input, time);
Wayland_UpdateImplicitGrabSerial(input, serial);
@@ -1883,7 +1871,8 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
* Using SDL_GetTicks would be wrong, as it would report when the release event is processed,
* which may be off if the application hasn't pumped events for a while.
*/
- keyboard_repeat_handle(&input->keyboard_repeat, timestamp_raw_ns - input->keyboard_repeat.wl_press_time_ns);
+ const Uint64 elapsed = SDL_MS_TO_NS(time - input->keyboard_repeat.wl_press_time_ms);
+ keyboard_repeat_handle(&input->keyboard_repeat, elapsed);
keyboard_repeat_clear(&input->keyboard_repeat);
}
keyboard_input_get_text(text, input, key, false, &handled_by_ime);
@@ -1891,9 +1880,8 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
const SDL_Scancode scancode = Wayland_GetScancodeForKey(input, key);
Wayland_HandleModifierKeys(input, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED);
- Uint64 timestamp = Wayland_GetKeyboardTimestamp(input, time);
- SDL_SendKeyboardKeyIgnoreModifiers(timestamp, input->keyboard_id, key, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED);
+ SDL_SendKeyboardKeyIgnoreModifiers(timestamp_ns, input->keyboard_id, key, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED);
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
if (has_text && !(SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) {
@@ -1902,7 +1890,7 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
}
}
if (input->xkb.keymap && WAYLAND_xkb_keymap_key_repeats(input->xkb.keymap, key + 8)) {
- keyboard_repeat_set(&input->keyboard_repeat, input->keyboard_id, key, timestamp_raw_ns, scancode, has_text, text);
+ keyboard_repeat_set(&input->keyboard_repeat, input->keyboard_id, key, time, timestamp_ns, scancode, has_text, text);
}
}
}
diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h
index 6158882ac5b66..d961b37ce5577 100644
--- a/src/video/wayland/SDL_waylandevents_c.h
+++ b/src/video/wayland/SDL_waylandevents_c.h
@@ -49,17 +49,18 @@ typedef struct SDL_WaylandTabletInput
typedef struct
{
- int32_t repeat_rate; // Repeat rate in range of [1, 1000] character(s) per second
- int32_t repeat_delay_ms; // Time to first repeat event in milliseconds
- Uint32 keyboard_id; // ID of the source keyboard.
+ 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_t key;
- Uint64 wl_press_time_ns; // Key press time as reported by the Wayland API
+ Uint32 key;
+ 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_t scancode;
+ Uint32 scancode;
char text[8];
} SDL_WaylandKeyboardRepeat;