24
24
25
25
#include <emscripten.h>
26
26
27
+ // For Emscripten, we let you use SDL_HINT_MAIN_CALLBACK_RATE, because it might be useful to drop it super-low for
28
+ // things like loopwave that don't really do much but wait on the audio device, but be warned that browser timers
29
+ // are super-unreliable in modern times, so you likely won't hit your desired callback rate with good precision.
30
+ // Almost all apps should leave this alone, so we can use requestAnimationFrame, which is intended to run reliably
31
+ // at the refresh rate of the user's display.
32
+ static Uint32 callback_rate_increment = 0 ;
33
+ static bool iterate_after_waitevent = false;
34
+ static bool callback_rate_changed = false;
35
+ static void SDLCALL MainCallbackRateHintChanged (void * userdata , const char * name , const char * oldValue , const char * newValue )
36
+ {
37
+ callback_rate_changed = true;
38
+ iterate_after_waitevent = newValue && (SDL_strcmp (newValue , "waitevent" ) == 0 );
39
+ if (iterate_after_waitevent ) {
40
+ callback_rate_increment = 0 ;
41
+ } else {
42
+ const double callback_rate = newValue ? SDL_atof (newValue ) : 0.0 ;
43
+ if (callback_rate > 0.0 ) {
44
+ callback_rate_increment = (Uint32 ) SDL_NS_TO_MS ((double ) SDL_NS_PER_SECOND / callback_rate );
45
+ } else {
46
+ callback_rate_increment = 0 ;
47
+ }
48
+ }
49
+ }
50
+
51
+ // just tell us when any new event is pushed on the queue, so we can check a flag for "waitevent" mode.
52
+ static bool saw_new_event = false;
53
+ static bool SDLCALL EmscriptenMainCallbackEventWatcher (void * userdata , SDL_Event * event )
54
+ {
55
+ saw_new_event = true;
56
+ return true;
57
+ }
58
+
27
59
static void EmscriptenInternalMainloop (void )
28
60
{
29
- const SDL_AppResult rc = SDL_IterateMainCallbacks (true);
61
+ // callback rate changed? Update emscripten's mainloop iteration speed.
62
+ if (callback_rate_changed ) {
63
+ callback_rate_changed = false;
64
+ if (callback_rate_increment == 0 ) {
65
+ emscripten_set_main_loop_timing (EM_TIMING_RAF , 1 );
66
+ } else {
67
+ emscripten_set_main_loop_timing (EM_TIMING_SETTIMEOUT , callback_rate_increment );
68
+ }
69
+ }
70
+
71
+ if (iterate_after_waitevent ) {
72
+ SDL_PumpEvents ();
73
+ if (!saw_new_event ) {
74
+ // do nothing yet. Note that we're still going to iterate here because we can't block,
75
+ // but we can stop the app's iteration from progressing until there's an event.
76
+ return ;
77
+ }
78
+ saw_new_event = false;
79
+ }
80
+
81
+ const SDL_AppResult rc = SDL_IterateMainCallbacks (!iterate_after_waitevent );
30
82
if (rc != SDL_APP_CONTINUE ) {
31
83
SDL_QuitMainCallbacks (rc );
32
84
emscripten_cancel_main_loop (); // kill" the mainloop, so it stops calling back into it.
@@ -36,9 +88,18 @@ static void EmscriptenInternalMainloop(void)
36
88
37
89
int SDL_EnterAppMainCallbacks (int argc , char * argv [], SDL_AppInit_func appinit , SDL_AppIterate_func appiter , SDL_AppEvent_func appevent , SDL_AppQuit_func appquit )
38
90
{
39
- const SDL_AppResult rc = SDL_InitMainCallbacks (argc , argv , appinit , appiter , appevent , appquit );
91
+ SDL_AppResult rc = SDL_InitMainCallbacks (argc , argv , appinit , appiter , appevent , appquit );
40
92
if (rc == SDL_APP_CONTINUE ) {
41
- emscripten_set_main_loop (EmscriptenInternalMainloop , 0 , 0 ); // run at refresh rate, don't throw an exception since we do an orderly return.
93
+ if (!SDL_AddEventWatch (EmscriptenMainCallbackEventWatcher , NULL )) {
94
+ rc = SDL_APP_FAILURE ;
95
+ } else {
96
+ SDL_AddHintCallback (SDL_HINT_MAIN_CALLBACK_RATE , MainCallbackRateHintChanged , NULL );
97
+ callback_rate_changed = false;
98
+ emscripten_set_main_loop (EmscriptenInternalMainloop , 0 , 0 ); // don't throw an exception since we do an orderly return.
99
+ if (callback_rate_increment > 0.0 ) {
100
+ emscripten_set_main_loop_timing (EM_TIMING_SETTIMEOUT , callback_rate_increment );
101
+ }
102
+ }
42
103
} else {
43
104
SDL_QuitMainCallbacks (rc );
44
105
}
0 commit comments