emscripten: Make timers work (if used with emscripten_set_main_loop) Co-authored-by: aidanhs <aidanhs@cantab.net>
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
diff --git a/src/timer/SDL_timer.c b/src/timer/SDL_timer.c
index 559fdf5..7ed1720 100644
--- a/src/timer/SDL_timer.c
+++ b/src/timer/SDL_timer.c
@@ -28,6 +28,8 @@
/* #define DEBUG_TIMERS */
+#if !defined(__EMSCRIPTEN__) || !SDL_THREADS_DISABLED
+
typedef struct _SDL_Timer
{
int timerID;
@@ -370,6 +372,116 @@ SDL_RemoveTimer(SDL_TimerID id)
return canceled;
}
+#else
+
+#include <emscripten/emscripten.h>
+
+typedef struct _SDL_TimerMap
+{
+ int timerID;
+ int timeoutID;
+ struct _SDL_TimerMap *next;
+} SDL_TimerMap;
+
+typedef struct {
+ int nextID;
+ SDL_TimerMap *timermap;
+} SDL_TimerData;
+
+static SDL_TimerData SDL_timer_data;
+
+static void
+SDL_Emscripten_TimerHelper(SDL_TimerMap *entry, Uint32 interval, SDL_TimerCallback callback, void *param)
+{
+ Uint32 new_timeout;
+
+ new_timeout = callback(interval, param);
+
+ if (new_timeout != 0) {
+ entry->timeoutID = EM_ASM_INT({
+ return Browser.safeSetTimeout(function() {
+ dynCall('viiii', $0, [$1, $2, $3, $4]);
+ }, $2);
+ }, &SDL_Emscripten_TimerHelper, entry, interval, callback, param);
+ }
+}
+
+int
+SDL_TimerInit(void)
+{
+ return 0;
+}
+
+void
+SDL_TimerQuit(void)
+{
+ SDL_TimerData *data = &SDL_timer_data;
+ SDL_TimerMap *entry;
+
+ while (data->timermap) {
+ entry = data->timermap;
+ data->timermap = entry->next;
+ SDL_free(entry);
+ }
+}
+
+SDL_TimerID
+SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *param)
+{
+ SDL_TimerData *data = &SDL_timer_data;
+ SDL_TimerMap *entry;
+
+ entry = (SDL_TimerMap *)SDL_malloc(sizeof(*entry));
+ if (!entry) {
+ SDL_OutOfMemory();
+ return 0;
+ }
+ entry->timerID = ++data->nextID;
+
+ entry->timeoutID = EM_ASM_INT({
+ return Browser.safeSetTimeout(function() {
+ dynCall('viiii', $0, [$1, $2, $3, $4]);
+ }, $2);
+ }, &SDL_Emscripten_TimerHelper, entry, interval, callback, param);
+
+ entry->next = data->timermap;
+ data->timermap = entry;
+
+ return entry->timerID;
+}
+
+SDL_bool
+SDL_RemoveTimer(SDL_TimerID id)
+{
+ SDL_TimerData *data = &SDL_timer_data;
+ SDL_TimerMap *prev, *entry;
+
+ /* Find the timer */
+ prev = NULL;
+ for (entry = data->timermap; entry; prev = entry, entry = entry->next) {
+ if (entry->timerID == id) {
+ if (prev) {
+ prev->next = entry->next;
+ } else {
+ data->timermap = entry->next;
+ }
+ break;
+ }
+ }
+
+ if (entry) {
+ EM_ASM_({
+ window.clearTimeout($0);
+ }, entry->timeoutID);
+ SDL_free(entry);
+
+ return SDL_TRUE;
+ }
+ return SDL_FALSE;
+}
+
+#endif
+
/* This is a legacy support function; SDL_GetTicks() returns a Uint32,
which wraps back to zero every ~49 days. The newer SDL_GetTicks64()
doesn't have this problem, so we just wrap that function and clamp to