SDL: Added the timerID to the SDL timer callback

From b6360516e4bbd65447e70899a03454c4435699f3 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 26 May 2024 17:56:29 -0700
Subject: [PATCH] Added the timerID to the SDL timer callback

Fixes https://github.com/libsdl-org/SDL/issues/2593
---
 docs/README-migration.md      |  7 +++++++
 include/SDL3/SDL_timer.h      | 32 +++++++++++++++-----------------
 src/dynapi/SDL_dynapi_procs.h |  2 +-
 src/timer/SDL_timer.c         | 32 ++++++++++++++++++--------------
 test/testautomation_timer.c   | 10 +++++-----
 test/testlock.c               |  2 +-
 test/testtimer.c              | 10 ++++++----
 7 files changed, 53 insertions(+), 42 deletions(-)

diff --git a/docs/README-migration.md b/docs/README-migration.md
index 020b1fffc52c2..e141b844fa67e 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -1677,6 +1677,13 @@ If you were using this macro for other things besides SDL ticks values, you can
 #define SDL_TICKS_PASSED(A, B)  ((Sint32)((B) - (A)) <= 0)

+The callback passed to SDL_AddTimer() has changed parameters to:
+```c
+Uint32 SDLCALL TimerCallback(void *userdata, SDL_TimerID timerID, Uint32 interval);
+````
+
+The return value of SDL_RemoveTimer() has changed to the standard int error code.
+

SDL_touch.h

SDL_GetTouchName is replaced with SDL_GetTouchDeviceName(), which takes an SDL_TouchID instead of an index.
diff --git a/include/SDL3/SDL_timer.h b/include/SDL3/SDL_timer.h
index ecaa305c08371…200a7fba0d553 100644
— a/include/SDL3/SDL_timer.h
+++ b/include/SDL3/SDL_timer.h
@@ -124,6 +124,13 @@ extern SDL_DECLSPEC void SDLCALL SDL_Delay(Uint32 ms);
*/
extern SDL_DECLSPEC void SDLCALL SDL_DelayNS(Uint64 ns);

+/**

    • Definition of the timer ID type.
    • \since This datatype is available since SDL 3.0.0.
  • */
    +typedef Uint32 SDL_TimerID;

/**

  • Function prototype for the timer callback function.

@@ -132,9 +139,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_DelayNS(Uint64 ns);

  • the one passed in, the periodic alarm continues, otherwise a new alarm is
  • scheduled. If the callback returns 0, the periodic alarm is cancelled.
    • \param userdata an arbitrary pointer provided by the app through SDL_AddTimer, for its own use.
    • \param timerID the current timer being processed
    • \param interval the current callback time interval.
    • \param param an arbitrary pointer provided by the app through SDL_AddTimer,
    •          for its own use.
      
    • \returns the new callback time interval, or 0 to disable further runs of
    •      the callback.
      

@@ -146,14 +153,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_DelayNS(Uint64 ns);
*

  • \sa SDL_AddTimer
    */
    -typedef Uint32 (SDLCALL *SDL_TimerCallback)(Uint32 interval, void *param);

-/**

    • Definition of the timer ID type.
    • \since This datatype is available since SDL 3.0.0.
  • */
    -typedef Uint32 SDL_TimerID;
    +typedef Uint32 (SDLCALL *SDL_TimerCallback)(void *userdata, SDL_TimerID timerID, Uint32 interval);

/**

  • Call a callback function at a future time.
    @@ -179,7 +179,7 @@ typedef Uint32 SDL_TimerID;
  • \param interval the timer delay, in milliseconds, passed to callback
  • \param callback the SDL_TimerCallback function to call when the specified
  •             `interval` elapses
    
    • \param param a pointer that is passed to callback
    • \param userdata a pointer that is passed to callback
    • \returns a timer ID or 0 if an error occurs; call SDL_GetError() for more
    •      information.
      

@@ -189,22 +189,20 @@ typedef Uint32 SDL_TimerID;
*

  • \sa SDL_RemoveTimer
    */
    -extern SDL_DECLSPEC SDL_TimerID SDLCALL SDL_AddTimer(Uint32 interval,
  •                                             SDL_TimerCallback callback,
    
  •                                             void *param);
    

+extern SDL_DECLSPEC SDL_TimerID SDLCALL SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *userdata);

/**

  • Remove a timer created with SDL_AddTimer().
  • \param id the ID of the timer to remove
    • \returns SDL_TRUE if the timer is removed or SDL_FALSE if the timer wasn’t
    •      found.
      
    • \returns 0 on success or a negative error code on failure; call
    •      SDL_GetError() for more information.
      
    • \since This function is available since SDL 3.0.0.
    • \sa SDL_AddTimer
      */
      -extern SDL_DECLSPEC SDL_bool SDLCALL SDL_RemoveTimer(SDL_TimerID id);
      +extern SDL_DECLSPEC int SDLCALL SDL_RemoveTimer(SDL_TimerID id);

/* Ends C function definitions when using C++ */
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index c69b84796c309…a2e016c00f6fb 100644
— a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -662,7 +662,7 @@ SDL_DYNAPI_PROC(int,SDL_ReleaseCameraFrame,(SDL_Camera *a, SDL_Surface *b),(a,b)
SDL_DYNAPI_PROC(int,SDL_ReloadGamepadMappings,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_RemovePath,(const char *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_RemoveStoragePath,(SDL_Storage *a, const char *b),(a,b),return)
-SDL_DYNAPI_PROC(SDL_bool,SDL_RemoveTimer,(SDL_TimerID a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_RemoveTimer,(SDL_TimerID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_RenamePath,(const char *a, const char *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_RenameStoragePath,(SDL_Storage *a, const char *b, const char *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_RenderClear,(SDL_Renderer *a),(a),return)
diff --git a/src/timer/SDL_timer.c b/src/timer/SDL_timer.c
index b779c7c3f3fc8…9043a2052e750 100644
— a/src/timer/SDL_timer.c
+++ b/src/timer/SDL_timer.c
@@ -31,7 +31,7 @@ typedef struct SDL_Timer
{
SDL_TimerID timerID;
SDL_TimerCallback callback;

  • void *param;
  • void *userdata;
    Uint64 interval;
    Uint64 scheduled;
    SDL_AtomicInt canceled;
    @@ -161,7 +161,7 @@ static int SDLCALL SDL_TimerThread(void _data)
    interval = 0;
    } else {
    /
    FIXME: We could potentially support sub-millisecond timers now */
  •            interval = SDL_MS_TO_NS(current->callback((Uint32)SDL_NS_TO_MS(current->interval), current->param));
    
  •            interval = SDL_MS_TO_NS(current->callback(current->userdata, current->timerID, (Uint32)SDL_NS_TO_MS(current->interval)));
           }
    
           if (interval > 0) {
    

@@ -269,7 +269,7 @@ void SDL_QuitTimers(void)
}
}

-SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *param)
+SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *userdata)
{
SDL_TimerData *data = &SDL_timer_data;
SDL_Timer *timer;
@@ -299,7 +299,7 @@ SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *para
}
timer->timerID = SDL_GetNextObjectID();
timer->callback = callback;

  • timer->param = param;
  • timer->userdata = userdata;
    timer->interval = SDL_MS_TO_NS(interval);
    timer->scheduled = SDL_GetTicksNS() + timer->interval;
    SDL_AtomicSet(&timer->canceled, 0);
    @@ -329,7 +329,7 @@ SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *para
    return entry->timerID;
    }

-SDL_bool SDL_RemoveTimer(SDL_TimerID id)
+int SDL_RemoveTimer(SDL_TimerID id)
{
SDL_TimerData *data = &SDL_timer_data;
SDL_TimerMap *prev, *entry;
@@ -357,7 +357,11 @@ SDL_bool SDL_RemoveTimer(SDL_TimerID id)
}
SDL_free(entry);
}

  • return canceled;
  • if (canceled) {
  •    return 0;
    
  • } else {
  •    return SDL_SetError("Timer not found");
    
  • }
    }

#else
@@ -371,7 +375,7 @@ typedef struct SDL_TimerMap
int timeoutID;
Uint32 interval;
SDL_TimerCallback callback;

  • void *param;
  • void *userdata;
    struct SDL_TimerMap *next;
    } SDL_TimerMap;

@@ -385,7 +389,7 @@ static SDL_TimerData SDL_timer_data;
static void SDL_Emscripten_TimerHelper(void *userdata)
{
SDL_TimerMap *entry = (SDL_TimerMap *)userdata;

  • entry->interval = entry->callback(entry->interval, entry->param);
  • entry->interval = entry->callback(entry->userdata, entry->timerID, entry->interval);
    if (entry->interval > 0) {
    entry->timeoutID = emscripten_set_timeout(&SDL_Emscripten_TimerHelper,
    entry->interval,
    @@ -410,7 +414,7 @@ void SDL_QuitTimers(void)
    }
    }

-SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *param)
+SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *userdata)
{
SDL_TimerData *data = &SDL_timer_data;
SDL_TimerMap *entry;
@@ -421,7 +425,7 @@ SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *para
}
entry->timerID = SDL_GetNextObjectID();
entry->callback = callback;

  • entry->param = param;
  • entry->userdata = userdata;
    entry->interval = interval;

    entry->timeoutID = emscripten_set_timeout(&SDL_Emscripten_TimerHelper,
    @@ -434,7 +438,7 @@ SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *para
    return entry->timerID;
    }

-SDL_bool SDL_RemoveTimer(SDL_TimerID id)
+int SDL_RemoveTimer(SDL_TimerID id)
{
SDL_TimerData *data = &SDL_timer_data;
SDL_TimerMap *prev, *entry;
@@ -455,10 +459,10 @@ SDL_bool SDL_RemoveTimer(SDL_TimerID id)
if (entry) {
emscripten_clear_timeout(entry->timeoutID);
SDL_free(entry);

  •    return SDL_TRUE;
    
  •    return 0;
    
  • } else {
  •    return SDL_SetError("Timer not found");
    
    }
  • return SDL_FALSE;
    }

#endif /* !defined(SDL_PLATFORM_EMSCRIPTEN) || !SDL_THREADS_DISABLED */
diff --git a/test/testautomation_timer.c b/test/testautomation_timer.c
index e2925e97ef0a2…23d971218e374 100644
— a/test/testautomation_timer.c
+++ b/test/testautomation_timer.c
@@ -101,7 +101,7 @@ static int timer_delayAndGetTicks(void *arg)
}

/* Test callback */
-static Uint32 SDLCALL timerTestCallback(Uint32 interval, void *param)
+static Uint32 SDLCALL timerTestCallback(void *param, SDL_TimerID timerID, Uint32 interval)
{
g_timerCallbackCalled = 1;

@@ -121,7 +121,7 @@ static Uint32 SDLCALL timerTestCallback(Uint32 interval, void *param)
static int timer_addRemoveTimer(void *arg)
{
SDL_TimerID id;

  • SDL_bool result;
  • int result;
    int param;

    /* Reset state */
    @@ -136,13 +136,13 @@ static int timer_addRemoveTimer(void arg)
    /
    Remove timer again and check that callback was not called */
    result = SDL_RemoveTimer(id);
    SDLTest_AssertPass(“Call to SDL_RemoveTimer()”);

  • SDLTest_AssertCheck(result == SDL_TRUE, “Check result value, expected: %i, got: %i”, SDL_TRUE, result);
  • SDLTest_AssertCheck(result == 0, “Check result value, expected: 0, got: %i”, result);
    SDLTest_AssertCheck(g_timerCallbackCalled == 0, “Check callback WAS NOT called, expected: 0, got: %i”, g_timerCallbackCalled);

    /* Try to remove timer again (should be a NOOP) */
    result = SDL_RemoveTimer(id);
    SDLTest_AssertPass(“Call to SDL_RemoveTimer()”);

  • SDLTest_AssertCheck(result == SDL_FALSE, “Check result value, expected: %i, got: %i”, SDL_FALSE, result);
  • SDLTest_AssertCheck(result < 0, “Check result value, expected: <0, got: %i”, result);

    /* Reset state */
    param = SDLTest_RandomIntegerInRange(-1024, 1024);
    @@ -162,7 +162,7 @@ static int timer_addRemoveTimer(void arg)
    /
    Remove timer again and check that callback was called */
    result = SDL_RemoveTimer(id);
    SDLTest_AssertPass(“Call to SDL_RemoveTimer()”);

  • SDLTest_AssertCheck(result == SDL_FALSE, “Check result value, expected: %i, got: %i”, SDL_FALSE, result);
  • SDLTest_AssertCheck(result < 0, “Check result value, expected: <0, got: %i”, result);
    SDLTest_AssertCheck(g_timerCallbackCalled == 1, “Check callback WAS called, expected: 1, got: %i”, g_timerCallbackCalled);

    return TEST_COMPLETED;
    diff --git a/test/testlock.c b/test/testlock.c
    index 28baa522fd002…f645802b4ae46 100644
    — a/test/testlock.c
    +++ b/test/testlock.c
    @@ -100,7 +100,7 @@ Run(void *data)
    }

#ifndef _WIN32
-static Uint32 hit_timeout(Uint32 interval, void *param) {
+static Uint32 hit_timeout(void *param, SDL_TimerID timerID, Uint32 interval) {
SDL_Log(“Hit timeout! Sending SIGINT!”);
(void)raise(SIGINT);
return 0;
diff --git a/test/testtimer.c b/test/testtimer.c
index 5714ea3348591…63456f7fc8a1c 100644
— a/test/testtimer.c
+++ b/test/testtimer.c
@@ -50,16 +50,18 @@ static int test_sdl_delay_within_bounds(void) {
static int ticks = 0;

static Uint32 SDLCALL
-ticktock(Uint32 interval, void *param)
+ticktock(void *param, SDL_TimerID timerID, Uint32 interval)
{
++ticks;
return interval;
}

static Uint32 SDLCALL
-callback(Uint32 interval, void *param)
+callback(void *param, SDL_TimerID timerID, Uint32 interval)
{

  • SDL_Log(“Timer %” SDL_PRIu32 " : param = %d\n", interval, (int)(uintptr_t)param);
  • int value = (int)(uintptr_t)param;
  • SDL_assert( value == 1 || value == 2 || value == 3 );
  • SDL_Log(“Timer %” SDL_PRIu32 " : param = %d\n", interval, value);
    return interval;
    }

@@ -182,7 +184,7 @@ int main(int argc, char *argv)

 start_perf = SDL_GetPerformanceCounter();
 for (i = 0; i < 1000000; ++i) {
  •    ticktock(0, NULL);
    
  •    ticktock(NULL, 0, 0);
    
    }
    now_perf = SDL_GetPerformanceCounter();
    SDL_Log(“1 million iterations of ticktock took %f ms\n”, (double)((now_perf - start_perf) * 1000) / SDL_GetPerformanceFrequency());