diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 48ef0dea79..695af204a8 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -215,16 +215,32 @@ static Uint64 Wayland_AdjustEventTimestampBase(Uint64 nsTimestamp) */ static Uint64 Wayland_EventTimestampMSToNS(Uint32 wl_timestamp_ms) { + static const Uint32 ROLLOVER_INTERVAL_LOW = SDL_MAX_UINT32 / 16; + static const Uint32 ROLLOVER_INTERVAL_HIGH = ROLLOVER_INTERVAL_LOW * 15; + static Uint64 timestamp_offset = 0; static Uint32 last = 0; + Uint64 timestamp = SDL_MS_TO_NS(wl_timestamp_ms) + timestamp_offset; - // Handle 32-bit timer rollover. - if (wl_timestamp_ms < last) { - timestamp_offset += SDL_MS_TO_NS(SDL_UINT64_C(0x100000000)); + if (wl_timestamp_ms >= last) { + if (timestamp_offset && last < ROLLOVER_INTERVAL_LOW && wl_timestamp_ms > ROLLOVER_INTERVAL_HIGH) { + // A time that crossed backwards across zero was received. Subtract the increased time base offset. + timestamp -= SDL_MS_TO_NS(SDL_UINT64_C(0x100000000)); + } else { + last = wl_timestamp_ms; + } + } else { + /* Only increment the base time offset if the timer actually crossed forward across 0, + * and not if this is just a timestamp from a slightly older event. + */ + if (wl_timestamp_ms < ROLLOVER_INTERVAL_LOW && last > ROLLOVER_INTERVAL_HIGH) { + timestamp_offset += SDL_MS_TO_NS(SDL_UINT64_C(0x100000000)); + timestamp += SDL_MS_TO_NS(SDL_UINT64_C(0x100000000)); + last = wl_timestamp_ms; + } } - last = wl_timestamp_ms; - return SDL_MS_TO_NS(wl_timestamp_ms) + timestamp_offset; + return timestamp; } /* Even if high-res timestamps are available, the millisecond timestamps are still processed