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 <slouken@libsdl.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
diff --git a/include/SDL_events.h b/include/SDL_events.h
index c3037b2..882c4ee 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 40c38bd..5536a43 100644
--- a/include/SDL_hints.h
+++ b/include/SDL_hints.h
@@ -959,6 +959,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()
*
* If set, this will be favored over anything the OS might report for the
diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c
index 599ec76..9089b0b 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);