sdl2-compat: Updated for SDL3 timer changes

From 6f856adb9fc5b3ad70f0731576c0f3897aa61a47 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 27 May 2024 06:37:41 -0700
Subject: [PATCH] Updated for SDL3 timer changes

---
 src/sdl2_compat.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++
 src/sdl2_compat.h |  3 ++
 src/sdl2_protos.h |  4 +--
 src/sdl3_syms.h   |  5 ++--
 4 files changed, 82 insertions(+), 4 deletions(-)

diff --git a/src/sdl2_compat.c b/src/sdl2_compat.c
index e417b8d..b369b0a 100644
--- a/src/sdl2_compat.c
+++ b/src/sdl2_compat.c
@@ -1257,6 +1257,8 @@ static SDL_TouchID TouchFingersDeviceID = 0;
 static SDL_Finger **TouchFingers = NULL;
 static int NumTouchFingers = 0;
 
+static SDL_PropertiesID timers = 0;
+
 /* Functions! */
 
 /* this stuff _might_ move to SDL_Init later */
@@ -4551,6 +4553,73 @@ SDL_UnlockMutex(SDL_Mutex *a)
     return 0;
 }
 
+#define PROP_TIMER_CALLBACK_POINTER "sdl2-compat.timer.callback"
+#define PROP_TIMER_USERDATA_POINTER "sdl2-compat.timer.userdata"
+
+static void SDLCALL CleanupTimerProperties(void *userdata, void *value)
+{
+    SDL_PropertiesID props = (SDL_PropertiesID)(uintptr_t)value;
+    SDL3_DestroyProperties(props);
+}
+
+static Uint32 SDLCALL CompatTimerCallback(void *userdata, SDL_TimerID timerID, Uint32 interval)
+{
+    SDL_PropertiesID props = (SDL_PropertiesID)(uintptr_t)userdata;
+    SDL2_TimerCallback callback = (SDL2_TimerCallback)SDL3_GetProperty(props, PROP_TIMER_CALLBACK_POINTER, NULL);
+    void *param = SDL3_GetProperty(props, PROP_TIMER_USERDATA_POINTER, NULL);
+    if (callback) {
+        return callback(interval, param);
+    } else {
+        return 0;
+    }
+}
+
+SDL_DECLSPEC SDL2_TimerID SDLCALL
+SDL_AddTimer(Uint32 interval, SDL2_TimerCallback callback, void *param)
+{
+    SDL_TimerID timerID;
+    SDL_PropertiesID props;
+
+    if (!timers) {
+        timers = SDL3_CreateProperties();
+        if (!timers) {
+            return 0;
+        }
+    }
+
+    props = SDL3_CreateProperties();
+    if (!props) {
+        return 0;
+    }
+    SDL3_SetProperty(props, PROP_TIMER_CALLBACK_POINTER, (void *)callback);
+    SDL3_SetProperty(props, PROP_TIMER_USERDATA_POINTER, param);
+    timerID = SDL3_AddTimer(interval, CompatTimerCallback, (void *)(uintptr_t)props);
+    if (timerID) {
+        char name[32];
+
+        SDL3_snprintf(name, sizeof(name), "%" SDL_PRIu32, timerID);
+        SDL3_SetPropertyWithCleanup(timers, name, (void *)(uintptr_t)props, CleanupTimerProperties, NULL);
+    } else {
+        SDL3_DestroyProperties(props);
+    }
+    return (SDL2_TimerID)timerID;
+}
+
+SDL_DECLSPEC SDL_bool SDLCALL
+SDL_RemoveTimer(SDL2_TimerID id)
+{
+    char name[32];
+
+    SDL3_snprintf(name, sizeof(name), "%" SDL_PRIu32, id);
+    SDL3_ClearProperty(timers, name);
+
+    if (SDL3_RemoveTimer((SDL_TimerID)id) == 0) {
+        return SDL_TRUE;
+    } else {
+        return SDL_FALSE;
+    }
+}
+
 
 SDL_DECLSPEC int SDLCALL
 SDL_AudioInit(const char *driver_name)
@@ -4668,6 +4737,11 @@ SDL_Quit(void)
     TouchFingers = NULL;
     NumTouchFingers = 0;
 
+    if (timers) {
+        SDL3_DestroyProperties(timers);
+        timers = 0;
+    }
+
     SDL3_Quit();
 }
 
diff --git a/src/sdl2_compat.h b/src/sdl2_compat.h
index e903821..67fd6c3 100644
--- a/src/sdl2_compat.h
+++ b/src/sdl2_compat.h
@@ -422,4 +422,7 @@ typedef struct SDL2_VirtualJoystickDesc
     int (SDLCALL *SendEffect)(void *userdata, const void *data, int size); /**< Implements SDL_SendJoystickEffect() */
 } SDL2_VirtualJoystickDesc;
 
+typedef int SDL2_TimerID;
+typedef Uint32 (SDLCALL * SDL2_TimerCallback) (Uint32 interval, void *param);
+
 #endif /* sdl2_compat_h */
diff --git a/src/sdl2_protos.h b/src/sdl2_protos.h
index 6aac2d9..0deef59 100644
--- a/src/sdl2_protos.h
+++ b/src/sdl2_protos.h
@@ -505,8 +505,8 @@ SDL2_PROTO(Uint32,GetTicks,(void))
 SDL2_PROTO(Uint64,GetPerformanceCounter,(void))
 SDL2_PROTO(Uint64,GetPerformanceFrequency,(void))
 SDL2_PROTO(void,Delay,(Uint32 a))
-SDL2_PROTO(SDL_TimerID,AddTimer,(Uint32 a, SDL_TimerCallback b, void *c))
-SDL2_PROTO(SDL_bool,RemoveTimer,(SDL_TimerID a))
+SDL2_PROTO(SDL2_TimerID,AddTimer,(Uint32 a, SDL2_TimerCallback b, void *c))
+SDL2_PROTO(SDL_bool,RemoveTimer,(SDL2_TimerID a))
 SDL2_PROTO(int,GetNumTouchDevices,(void))
 SDL2_PROTO(SDL_TouchID,GetTouchDevice,(int a))
 SDL2_PROTO(int,GetNumTouchFingers,(SDL_TouchID a))
diff --git a/src/sdl3_syms.h b/src/sdl3_syms.h
index 7f9c18a..2c29e12 100644
--- a/src/sdl3_syms.h
+++ b/src/sdl3_syms.h
@@ -447,8 +447,8 @@ SDL3_SYM(Uint64,GetTicks,(void),(),return)
 SDL3_SYM_PASSTHROUGH(Uint64,GetPerformanceCounter,(void),(),return)
 SDL3_SYM_PASSTHROUGH(Uint64,GetPerformanceFrequency,(void),(),return)
 SDL3_SYM_PASSTHROUGH(void,Delay,(Uint32 a),(a),)
-SDL3_SYM_PASSTHROUGH(SDL_TimerID,AddTimer,(Uint32 a, SDL_TimerCallback b, void *c),(a,b,c),return)
-SDL3_SYM_PASSTHROUGH(SDL_bool,RemoveTimer,(SDL_TimerID a),(a),return)
+SDL3_SYM(SDL_TimerID,AddTimer,(Uint32 a, SDL_TimerCallback b, void *c),(a,b,c),return)
+SDL3_SYM(int,RemoveTimer,(SDL_TimerID a),(a),return)
 SDL3_SYM(SDL_TouchID*,GetTouchDevices,(int *a),(a),return)
 SDL3_SYM(SDL_Finger**,GetTouchFingers,(SDL_TouchID a, int *b),(a,b),return)
 SDL3_SYM(int,GetVersion,(void),(),return)
@@ -849,6 +849,7 @@ SDL3_SYM(SDL_PropertiesID,GetGamepadProperties,(SDL_GameController *a),(a),retur
 SDL3_SYM(int,GetSurfaceColorspace,(SDL_Surface *a, SDL_Colorspace *b),(a,b),return)
 SDL3_SYM(int,SetWindowShape,(SDL_Window *a, SDL_Surface *b),(a,b),return)
 SDL3_SYM(SDL_PropertiesID,GetSurfaceProperties,(SDL_Surface *a),(a),return)
+SDL3_SYM(int,ClearProperty,(SDL_PropertiesID a, const char *b),(a,b),return)
 
 #undef SDL3_SYM
 #undef SDL3_SYM_PASSTHROUGH