Skip to content

Commit a0c8744

Browse files
committed
Make sure we only dispatch events on the main thread when using application callbacks
1 parent ad9dcdb commit a0c8744

File tree

1 file changed

+51
-30
lines changed

1 file changed

+51
-30
lines changed

src/main/SDL_main_callbacks.c

+51-30
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,57 @@ static SDL_AppIterate_func SDL_main_iteration_callback;
2727
static SDL_AppQuit_func SDL_main_quit_callback;
2828
static SDL_AtomicInt apprc; // use an atomic, since events might land from any thread and we don't want to wrap this all in a mutex. A CAS makes sure we only move from zero once.
2929

30-
static int SDLCALL EventWatcher(void *userdata, SDL_Event *event)
30+
// Return true if this event needs to be processed before returning from the event watcher
31+
static SDL_bool ShouldDispatchImmediately(SDL_Event *event)
3132
{
32-
if (SDL_AtomicGet(&apprc) == 0) { // if already quitting, don't send the event to the app.
33+
switch (event->type) {
34+
case SDL_EVENT_TERMINATING:
35+
case SDL_EVENT_LOW_MEMORY:
36+
case SDL_EVENT_WILL_ENTER_BACKGROUND:
37+
case SDL_EVENT_DID_ENTER_BACKGROUND:
38+
case SDL_EVENT_WILL_ENTER_FOREGROUND:
39+
case SDL_EVENT_DID_ENTER_FOREGROUND:
40+
return SDL_TRUE;
41+
default:
42+
return SDL_FALSE;
43+
}
44+
}
45+
46+
static void SDL_DispatchMainCallbackEvent(SDL_Event *event)
47+
{
48+
if (SDL_AtomicGet(&apprc) == 0) { // if already quitting, don't send the event to the app.
3349
SDL_AtomicCAS(&apprc, 0, SDL_main_event_callback(event));
3450
}
51+
SDL_CleanupEvent(event);
52+
}
53+
54+
static void SDL_DispatchMainCallbackEvents()
55+
{
56+
SDL_Event events[16];
57+
58+
for (;;) {
59+
int count = SDL_PeepEvents(events, SDL_arraysize(events), SDL_GETEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST);
60+
if (count <= 0) {
61+
break;
62+
}
63+
for (int i = 0; i < count; ++i) {
64+
SDL_Event *event = &events[i];
65+
if (!ShouldDispatchImmediately(event)) {
66+
SDL_DispatchMainCallbackEvent(event);
67+
}
68+
}
69+
}
70+
}
71+
72+
static int SDLCALL SDL_MainCallbackEventWatcher(void *userdata, SDL_Event *event)
73+
{
74+
if (ShouldDispatchImmediately(event)) {
75+
// Make sure any currently queued events are processed then dispatch this before continuing
76+
SDL_DispatchMainCallbackEvents();
77+
SDL_DispatchMainCallbackEvent(event);
78+
} else {
79+
// We'll process this event later from the main event queue
80+
}
3581
return 0;
3682
}
3783

@@ -50,44 +96,19 @@ int SDL_InitMainCallbacks(int argc, char* argv[], SDL_AppInit_func appinit, SDL_
5096
return -1;
5197
}
5298

53-
// drain any initial events that might have arrived before we added a watcher.
54-
SDL_Event event;
55-
SDL_Event *pending_events = NULL;
56-
int total_pending_events = 0;
57-
while (SDL_PollEvent(&event)) {
58-
void *ptr = SDL_realloc(pending_events, sizeof (SDL_Event) * (total_pending_events + 1));
59-
if (!ptr) {
60-
SDL_OutOfMemory();
61-
SDL_free(pending_events);
62-
SDL_AtomicSet(&apprc, -1);
63-
return -1;
64-
}
65-
pending_events = (SDL_Event *) ptr;
66-
SDL_copyp(&pending_events[total_pending_events], &event);
67-
total_pending_events++;
68-
}
69-
70-
if (SDL_AddEventWatch(EventWatcher, NULL) == -1) {
71-
SDL_free(pending_events);
99+
if (SDL_AddEventWatch(SDL_MainCallbackEventWatcher, NULL) < 0) {
72100
SDL_AtomicSet(&apprc, -1);
73101
return -1;
74102
}
75-
76-
for (int i = 0; i < total_pending_events; i++) {
77-
SDL_PushEvent(&pending_events[i]);
78-
}
79-
80-
SDL_free(pending_events);
81103
}
82104

83105
return SDL_AtomicGet(&apprc);
84106
}
85107

86108
int SDL_IterateMainCallbacks(void)
87109
{
88-
// Just pump events and empty the queue, EventWatcher sends the events to the app.
89110
SDL_PumpEvents();
90-
SDL_FlushEvents(SDL_EVENT_FIRST, SDL_EVENT_LAST);
111+
SDL_DispatchMainCallbackEvents();
91112

92113
int rc = SDL_main_iteration_callback();
93114
if (!SDL_AtomicCAS(&apprc, 0, rc)) {
@@ -99,7 +120,7 @@ int SDL_IterateMainCallbacks(void)
99120

100121
void SDL_QuitMainCallbacks(void)
101122
{
102-
SDL_DelEventWatch(EventWatcher, NULL);
123+
SDL_DelEventWatch(SDL_MainCallbackEventWatcher, NULL);
103124
SDL_main_quit_callback();
104125

105126
// for symmetry, you should explicitly Quit what you Init, but we might come through here uninitialized and SDL_Quit() will clear everything anyhow.

0 commit comments

Comments
 (0)