From 8bf32e12d85598fcb7dcedfc9bf30445beb9d42a Mon Sep 17 00:00:00 2001 From: Brick <6098371+0x1F9F1@users.noreply.github.com> Date: Fri, 15 Oct 2021 06:26:10 +0100 Subject: [PATCH] Improved SDL_PollEvent usage (#4794) * Avoid unnecessary SDL_PumpEvents calls in SDL_WaitEventTimeout * Add a sentinel event to avoid infinite poll loops * Move SDL_POLLSENTINEL to new internal event category * Tweak documentation to indicate SDL_PumpEvents isn't always called * Avoid shadowing event variable * Ignore poll sentinel if more (user) events have been added after Co-authored-by: Sam Lantinga --- include/SDL_events.h | 9 ++++++--- include/SDL_hints.h | 16 ++++++++++++++++ src/events/SDL_events.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/include/SDL_events.h b/include/SDL_events.h index c3037b26d..882c4ee18 100644 --- a/include/SDL_events.h +++ b/include/SDL_events.h @@ -160,6 +160,9 @@ typedef enum SDL_RENDER_TARGETS_RESET = 0x2000, /**< The render targets have been reset and their contents need to be updated */ SDL_RENDER_DEVICE_RESET, /**< The device has been reset and all textures need to be recreated */ + /* Internal events */ + SDL_POLLSENTINEL = 0x7F00, /**< Signals the end of an event poll cycle */ + /** Events ::SDL_USEREVENT through ::SDL_LASTEVENT are for your use, * and should be allocated with SDL_RegisterEvents() */ @@ -798,7 +801,7 @@ extern DECLSPEC void SDLCALL SDL_FlushEvents(Uint32 minType, Uint32 maxType); * If `event` is NULL, it simply returns 1 if there is an event in the queue, * but will not remove it from the queue. * - * As this function implicitly calls SDL_PumpEvents(), you can only call this + * As this function may implicitly call SDL_PumpEvents(), you can only call this * function in the thread that set the video mode. * * SDL_PollEvent() is the favored way of receiving system events since it can @@ -838,7 +841,7 @@ extern DECLSPEC int SDLCALL SDL_PollEvent(SDL_Event * event); * If `event` is not NULL, the next event is removed from the queue and stored * in the SDL_Event structure pointed to by `event`. * - * As this function implicitly calls SDL_PumpEvents(), you can only call this + * As this function may implicitly call SDL_PumpEvents(), you can only call this * function in the thread that initialized the video subsystem. * * \param event the SDL_Event structure to be filled in with the next event @@ -859,7 +862,7 @@ extern DECLSPEC int SDLCALL SDL_WaitEvent(SDL_Event * event); * If `event` is not NULL, the next event is removed from the queue and stored * in the SDL_Event structure pointed to by `event`. * - * As this function implicitly calls SDL_PumpEvents(), you can only call this + * As this function may implicitly call SDL_PumpEvents(), you can only call this * function in the thread that initialized the video subsystem. * * \param event the SDL_Event structure to be filled in with the next event diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 40c38bda5..5536a4373 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -958,6 +958,22 @@ extern "C" { */ #define SDL_HINT_ORIENTATIONS "SDL_IOS_ORIENTATIONS" +/** + * \brief A variable controlling the use of a sentinel event when polling the event queue + * + * This variable can be set to the following values: + * "0" - Disable poll sentinels + * "1" - Enable poll sentinels + * + * When polling for events, SDL_PumpEvents is used to gather new events from devices. + * If a device keeps producing new events between calls to SDL_PumpEvents, a poll loop will + * become stuck until the new events stop. + * This is most noticable when moving a high frequency mouse. + * + * By default, poll sentinels are enabled. + */ +#define SDL_HINT_POLL_SENTINEL "SDL_POLL_SENTINEL" + /** * \brief Override for SDL_GetPreferredLocales() * diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index 599ec760a..9089b0b51 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -26,6 +26,7 @@ #include "SDL_events.h" #include "SDL_thread.h" #include "SDL_events_c.h" +#include "../SDL_hints_c.h" #include "../timer/SDL_timer_c.h" #if !SDL_JOYSTICK_DISABLED #include "../joystick/SDL_joystick_c.h" @@ -139,6 +140,11 @@ SDL_AutoUpdateSensorsChanged(void *userdata, const char *name, const char *oldVa #endif /* !SDL_SENSOR_DISABLED */ +static void SDLCALL +SDL_PollSentinelChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + SDL_EventState(SDL_POLLSENTINEL, SDL_GetStringBoolean(hint, SDL_TRUE) ? SDL_ENABLE : SDL_DISABLE); +} /* 0 (default) means no logging, 1 means logging, 2 means logging with mouse and finger motion */ static int SDL_DoEventLogging = 0; @@ -886,6 +892,21 @@ SDL_WaitEventTimeout(SDL_Event * event, int timeout) Uint32 start = 0; Uint32 expiration = 0; + /* First check for existing events */ + switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) { + case -1: + return 0; + case 0: + break; + default: + /* Check whether we have reached the end of the poll cycle, and no more events are left */ + if (timeout == 0 && event && event->type == SDL_POLLSENTINEL) { + return SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) == 1; + } + /* Has existing events */ + return 1; + } + if (timeout > 0) { start = SDL_GetTicks(); expiration = start + timeout; @@ -922,6 +943,13 @@ SDL_WaitEventTimeout(SDL_Event * event, int timeout) SDL_Delay(1); break; default: + if (timeout == 0 && SDL_GetEventState(SDL_POLLSENTINEL) == SDL_ENABLE) { + /* We are at the start of a poll cycle with at least one new event. + Add a sentinel event to mark the end of the cycle. */ + SDL_Event sentinel; + sentinel.type = SDL_POLLSENTINEL; + SDL_PushEvent(&sentinel); + } /* Has events */ return 1; } @@ -1216,6 +1244,7 @@ SDL_EventsInit(void) SDL_AddHintCallback(SDL_HINT_AUTO_UPDATE_SENSORS, SDL_AutoUpdateSensorsChanged, NULL); #endif SDL_AddHintCallback(SDL_HINT_EVENT_LOGGING, SDL_EventLoggingChanged, NULL); + SDL_AddHintCallback(SDL_HINT_POLL_SENTINEL, SDL_PollSentinelChanged, NULL); if (SDL_StartEventLoop() < 0) { SDL_DelHintCallback(SDL_HINT_EVENT_LOGGING, SDL_EventLoggingChanged, NULL); return -1; @@ -1231,6 +1260,7 @@ SDL_EventsQuit(void) { SDL_QuitQuit(); SDL_StopEventLoop(); + SDL_DelHintCallback(SDL_HINT_POLL_SENTINEL, SDL_PollSentinelChanged, NULL); SDL_DelHintCallback(SDL_HINT_EVENT_LOGGING, SDL_EventLoggingChanged, NULL); #if !SDL_JOYSTICK_DISABLED SDL_DelHintCallback(SDL_HINT_AUTO_UPDATE_JOYSTICKS, SDL_AutoUpdateJoysticksChanged, NULL);