SDL: Add an invalid #define for SDL_ThreadID() to SDL_oldnames.h (#15801)

From b04d026458cd762db3499bec54348e51acd76508 Mon Sep 17 00:00:00 2001
From: Daniel Gibson <[EMAIL REDACTED]>
Date: Fri, 12 Jun 2026 19:24:56 +0200
Subject: [PATCH] Add an invalid #define for SDL_ThreadID() to SDL_oldnames.h
 (#15801)

in SDL2 SDL_ThreadID() was a function that's now
SDL_GetCurrentThreadID(), but in SDL3 SDL_ThreadID is a type, so
in C++ `x = SDL_ThreadID()` is valid code (default constructor which
in case of integers means 0), so that's a massive footgun.

See the big comment in SDL_oldnames.h for more details.

Added `#undef SDL_ThreadID` in SDL_dynapi.c because it has one of the
(quite rare) cases  where "SDL_ThreadID" followed by a "(" is actually
correct and necessary (function pointer returning SDL_ThreadID).
---
 docs/README-migration.md    | 11 ++++++++
 include/SDL3/SDL_oldnames.h | 54 +++++++++++++++++++++++++++++++++++++
 src/dynapi/SDL_dynapi.c     |  6 +++++
 3 files changed, 71 insertions(+)

diff --git a/docs/README-migration.md b/docs/README-migration.md
index 3fd243c8f2a99..b77a9a28d164f 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -41,6 +41,8 @@ Many functions and symbols have been renamed. We have provided a handy Python sc
 rename_symbols.py --all-symbols source_code_path

+Note that rename_symbols.py can’t completely handle SDL_ThreadID, see the SDL_Thread.h section below.
+
It’s also possible to apply a semantic patch to migrate more easily to SDL3: SDL_migration.cocci

SDL headers should now be included as #include <SDL3/SDL.h>. Typically that’s the only SDL header you’ll need in your application unless you are using OpenGL or Vulkan functionality. SDL_image, SDL_mixer, SDL_net, SDL_ttf and SDL_rtf have also their preferred include path changed: for SDL_image, it becomes #include <SDL3_image/SDL_image.h>. We have provided a handy Python script rename_headers.py to rename SDL2 headers to their SDL3 counterparts:
@@ -2046,6 +2048,15 @@ The following functions have been removed:
The following symbols have been renamed:

  • SDL_threadID => SDL_ThreadID

+### Attention: Potential problems with SDL_ThreadID
+
+SDL_ThreadID is a case that needs special attention, because if you forget to rename a call to SDL_ThreadID() to SDL_GetCurrentThreadID() in C++ code, it will still compile fine, calling the default constructor of the SDL_ThreadID type, initializing it to 0 - but if SDL_GetCurrentThreadID() was intended, that of course leads to wrong behavior that can be hard to catch.
+
+Furthermore rename_symbols.py does not handle it (because it would be hard to do in a way that doesn’t break anything when running that script twice), which makes it even more error-prone.
+
+SDL_oldnames.h now has a #define that should catch these cases, but it’s not available in SDL 3.4.10 and older. So if you’re using 3.4.10 or older, check your code carefully for occurences of SDL_ThreadID() and make sure it wasn’t supposed to be SDL_GetCurrentThreadID().
+In some edge cases this #define can cause false positives (compile errors even though SDL_ThreadID was used correctly); right above that definition in the header is a long comment explaining possible problems and solutions.
+

SDL_timer.h

SDL_GetTicks() now returns a 64-bit value. Instead of using the SDL_TICKS_PASSED macro, you can directly compare tick values, e.g.
diff --git a/include/SDL3/SDL_oldnames.h b/include/SDL3/SDL_oldnames.h
index cbf045330769b..6fd95216f7d84 100644
— a/include/SDL3/SDL_oldnames.h
+++ b/include/SDL3/SDL_oldnames.h
@@ -1336,4 +1336,58 @@

#endif /* SDL_ENABLE_OLD_NAMES */

+/* In SDL2, SDL_ThreadID() was a function that is now called SDL_GetCurrentThreadID().

    • In SDL3, the thread ID type is called SDL_ThreadID (in SDL2 it was
    • SDL_threadID with lower ‘t’).
    • Unfortunately, at least in C++ writing SDL_ThreadID() compiles fine even for
    • the type, it’s the default constructor of the type, so e.g.
    • myID = SDL_ThreadID(); is equivalent to myID = 0;
    • Of course if you’ve been porting SDL2 code and have missed renaming this case
    • of SDL_ThreadID() to SDL_GetCurrentThreadID() this is quite a pitfall:
    • The code compiles fine, but behaves wrong: it will set myID to 0 instead
    • of the current thread’s ID.
    • This makes it impossible to provide a “proper” SDL_ENABLE_OLD_NAMES treatment
    • of SDL_ThreadID() in this header, so the following #define sets SDL_ThreadID()
    • to an invalid value, forcing you to consciously adjust it to your needs, i.e.
    • replacing it with SDL_GetCurrentThreadID() or 0 depending on your intentions.
    • Note that using SDL_ThreadID x = ...; is unaffected, only “SDL_ThreadID” followed
    • by “(” will lead to a compile error.
    • Unfortunately, there are some cases where this breaks legit SDL3 code that
    • actually means to use SDL_ThreadID(..., like:
      • Definitions of function pointers with SDL_ThreadID as return type, like
    • typedef SDL_ThreadID (SDLCALL *SDL_DYNAPIFN_SDL_GetCurrentThreadID) (void);
      • C++ casts like SDL_ThreadID tid = SDL_ThreadID(id);
    • For C++ casts, just replace SDL_ThreadID(id) with either a C-style cast
    • ( (SDL_ThreadID)id ) or one of the other C++ cast types like
    • static_cast<SDL_ThreadID>(id).
    • For the (hopefully rare) case of function pointers returning SDL_ThreadID,
    • or any other legit case where SDL_ThreadID is followed by ( that can’t be
    • easily avoided by using slightly different syntax, you can #undef SDL_ThreadID
    • before your code using it to get rid of this define causing the compiler error.
    • If you’re using SDL_ENABLE_OLD_NAMES to support both SDL2 and SDL3 with
    • the same code, consider adding:
    • #if SDL_MAJOR_VERSION == 2
    • #define SDL_GetCurrentThreadID() SDL_ThreadID()
    • #endif
    • and using SDL_GetCurrentThreadID() in your code, even if you otherwise use
    • the SDL2 names.
    • The gain of catching the bugs caused by accidentally using SDL_ThreadID()
    • when SDL_GetCurrentThreadID() was intended hopefully outweight the annoyances
    • caused by this in some rare cases.
  • */
    +#define SDL_ThreadID() SDL_ThreadID_renamed_SDL_GetCurrentThreadID

#endif /* SDL_oldnames_h_ */
diff --git a/src/dynapi/SDL_dynapi.c b/src/dynapi/SDL_dynapi.c
index fcbbbd6d612d0..0f9e767e6fda6 100644
— a/src/dynapi/SDL_dynapi.c
+++ b/src/dynapi/SDL_dynapi.c
@@ -55,6 +55,12 @@
#include <windows.h>
#endif

+#ifdef SDL_ThreadID
+/* prevent the SDL_ThreadID() define from SDL_oldnames.h from breaking

  • function pointer definitions */
    +#undef SDL_ThreadID
    +#endif

/* This is the version of the dynamic API. This doesn’t match the SDL version
and should not change until there’s been a major revamp in API/ABI.
So 2.0.5 adds functions over 2.0.4? This number doesn’t change;