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 e3d44cdd51)
This commit is contained in:
Frank Praznik 2025-05-27 10:31:31 -04:00
parent 6a5bac72cb
commit 31267feb03
No known key found for this signature in database
2 changed files with 18 additions and 29 deletions

View File

@ -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);
}
}
}

View File

@ -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;