Fix weak enforcement of timeouts in SDL_WaitEventTimeout_Device. This will loop pumping events and waiting for a system event to come in. However not all system events will turn into an SDL event. It's not unusual for a Windows message to be some internal thing that SDL doesn't convert into a message. In that case the loop will simple circle but not exit. As long as such messages are coming in the loop will continue to run regardless of the timeout. When messages finally stop it'll still wait for the full timeout so you can have arbitrarily long delays.

Instead do an absolute elapsed time check since the start of the wait.  If that is exceeded during any iteration the routine exits as the timeout has elapsed.
This commit is contained in:
Sam Lantinga 2021-10-13 09:33:54 -07:00
parent 2423c51471
commit 1fa154fda3
1 changed files with 19 additions and 5 deletions

View File

@ -786,8 +786,10 @@ SDL_PollEvent(SDL_Event * event)
} }
static int static int
SDL_WaitEventTimeout_Device(_THIS, SDL_Window *wakeup_window, SDL_Event * event, int timeout) SDL_WaitEventTimeout_Device(_THIS, SDL_Window *wakeup_window, SDL_Event * event, Uint32 start, int timeout)
{ {
int loop_timeout = timeout;
for (;;) { for (;;) {
/* Pump events on entry and each time we wake to ensure: /* Pump events on entry and each time we wake to ensure:
a) All pending events are batch processed after waking up from a wait a) All pending events are batch processed after waking up from a wait
@ -817,7 +819,16 @@ SDL_WaitEventTimeout_Device(_THIS, SDL_Window *wakeup_window, SDL_Event * event,
return 1; return 1;
} }
/* No events found in the queue, call WaitEventTimeout to wait for an event. */ /* No events found in the queue, call WaitEventTimeout to wait for an event. */
status = _this->WaitEventTimeout(_this, timeout); if (timeout > 0) {
Uint32 elapsed = SDL_GetTicks() - start;
if (elapsed >= (Uint32)timeout) {
/* Set wakeup_window to NULL without holding the lock. */
_this->wakeup_window = NULL;
return 0;
}
loop_timeout = (int)((Uint32)timeout - elapsed);
}
status = _this->WaitEventTimeout(_this, loop_timeout);
/* Set wakeup_window to NULL without holding the lock. */ /* Set wakeup_window to NULL without holding the lock. */
_this->wakeup_window = NULL; _this->wakeup_window = NULL;
if (status <= 0) { if (status <= 0) {
@ -872,16 +883,19 @@ SDL_WaitEventTimeout(SDL_Event * event, int timeout)
{ {
SDL_VideoDevice *_this = SDL_GetVideoDevice(); SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_Window *wakeup_window; SDL_Window *wakeup_window;
Uint32 start = 0;
Uint32 expiration = 0; Uint32 expiration = 0;
if (timeout > 0) if (timeout > 0) {
expiration = SDL_GetTicks() + timeout; start = SDL_GetTicks();
expiration = start + timeout;
}
if (timeout != 0 && _this && _this->WaitEventTimeout && _this->SendWakeupEvent && !SDL_events_need_polling()) { if (timeout != 0 && _this && _this->WaitEventTimeout && _this->SendWakeupEvent && !SDL_events_need_polling()) {
/* Look if a shown window is available to send the wakeup event. */ /* Look if a shown window is available to send the wakeup event. */
wakeup_window = SDL_find_active_window(_this); wakeup_window = SDL_find_active_window(_this);
if (wakeup_window) { if (wakeup_window) {
int status = SDL_WaitEventTimeout_Device(_this, wakeup_window, event, timeout); int status = SDL_WaitEventTimeout_Device(_this, wakeup_window, event, start, timeout);
/* There may be implementation-defined conditions where the backend cannot /* There may be implementation-defined conditions where the backend cannot
reliably wait for the next event. If that happens, fall back to polling. */ reliably wait for the next event. If that happens, fall back to polling. */