From d950b9e2d9c756c7fc5641d75d9f7dd1a19ceac8 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Sun, 7 Nov 2021 20:40:54 +0000 Subject: [PATCH] emscripten: Make timers work (if used with emscripten_set_main_loop) Co-authored-by: aidanhs --- src/timer/SDL_timer.c | 112 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/src/timer/SDL_timer.c b/src/timer/SDL_timer.c index 559fdf5b2..7ed172045 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 + +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