chore(build): use SDL3

This commit is contained in:
phaneron 2025-04-12 04:38:19 -04:00
parent 9d04a35d87
commit b3c0734a9e
3286 changed files with 866354 additions and 554996 deletions

View file

@ -0,0 +1,611 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_build_config.h"
#include "SDL_dynapi.h"
#include "SDL_dynapi_unsupported.h"
#if SDL_DYNAMIC_API
#define SDL_DYNAMIC_API_ENVVAR "SDL3_DYNAMIC_API"
#define SDL_SLOW_MEMCPY
#define SDL_SLOW_MEMMOVE
#define SDL_SLOW_MEMSET
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <SDL3/SDL.h>
#define SDL_MAIN_NOIMPL // don't drag in header-only implementation of SDL_main
#include <SDL3/SDL_main.h>
// These headers have system specific definitions, so aren't included above
#include <SDL3/SDL_vulkan.h>
#if defined(WIN32) || defined(_WIN32) || defined(SDL_PLATFORM_CYGWIN)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h>
#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;
the sizeof(jump_table) changes instead. But 2.1.0 changes how a function
works in an incompatible way or removes a function? This number changes,
since sizeof(jump_table) isn't sufficient anymore. It's likely
we'll forget to bump every time we add a function, so this is the
failsafe switch for major API change decisions. Respect it and use it
sparingly. */
#define SDL_DYNAPI_VERSION 2
#ifdef __cplusplus
extern "C" {
#endif
static void SDL_InitDynamicAPI(void);
/* BE CAREFUL CALLING ANY SDL CODE IN HERE, IT WILL BLOW UP.
Even self-contained stuff might call SDL_SetError() and break everything. */
// behold, the macro salsa!
// Can't use the macro for varargs nonsense. This is atrocious.
#define SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, logname, prio) \
_static void SDLCALL SDL_Log##logname##name(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \
{ \
va_list ap; \
initcall; \
va_start(ap, fmt); \
jump_table.SDL_LogMessageV(category, SDL_LOG_PRIORITY_##prio, fmt, ap); \
va_end(ap); \
}
#define SDL_DYNAPI_VARARGS(_static, name, initcall) \
_static bool SDLCALL SDL_SetError##name(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \
{ \
char buf[128], *str = buf; \
int result; \
va_list ap; \
initcall; \
va_start(ap, fmt); \
result = jump_table.SDL_vsnprintf(buf, sizeof(buf), fmt, ap); \
va_end(ap); \
if (result >= 0 && (size_t)result >= sizeof(buf)) { \
str = NULL; \
va_start(ap, fmt); \
result = jump_table.SDL_vasprintf(&str, fmt, ap); \
va_end(ap); \
} \
if (result >= 0) { \
jump_table.SDL_SetError("%s", str); \
} \
if (str != buf) { \
jump_table.SDL_free(str); \
} \
return false; \
} \
_static int SDLCALL SDL_sscanf##name(const char *buf, SDL_SCANF_FORMAT_STRING const char *fmt, ...) \
{ \
int result; \
va_list ap; \
initcall; \
va_start(ap, fmt); \
result = jump_table.SDL_vsscanf(buf, fmt, ap); \
va_end(ap); \
return result; \
} \
_static int SDLCALL SDL_snprintf##name(SDL_OUT_Z_CAP(maxlen) char *buf, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \
{ \
int result; \
va_list ap; \
initcall; \
va_start(ap, fmt); \
result = jump_table.SDL_vsnprintf(buf, maxlen, fmt, ap); \
va_end(ap); \
return result; \
} \
_static int SDLCALL SDL_swprintf##name(SDL_OUT_Z_CAP(maxlen) wchar_t *buf, size_t maxlen, SDL_PRINTF_FORMAT_STRING const wchar_t *fmt, ...) \
{ \
int result; \
va_list ap; \
initcall; \
va_start(ap, fmt); \
result = jump_table.SDL_vswprintf(buf, maxlen, fmt, ap); \
va_end(ap); \
return result; \
} \
_static int SDLCALL SDL_asprintf##name(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \
{ \
int result; \
va_list ap; \
initcall; \
va_start(ap, fmt); \
result = jump_table.SDL_vasprintf(strp, fmt, ap); \
va_end(ap); \
return result; \
} \
_static size_t SDLCALL SDL_IOprintf##name(SDL_IOStream *context, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \
{ \
size_t result; \
va_list ap; \
initcall; \
va_start(ap, fmt); \
result = jump_table.SDL_IOvprintf(context, fmt, ap); \
va_end(ap); \
return result; \
} \
_static bool SDLCALL SDL_RenderDebugTextFormat##name(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \
{ \
char buf[128], *str = buf; \
int result; \
va_list ap; \
initcall; \
va_start(ap, fmt); \
result = jump_table.SDL_vsnprintf(buf, sizeof(buf), fmt, ap); \
va_end(ap); \
if (result >= 0 && (size_t)result >= sizeof(buf)) { \
str = NULL; \
va_start(ap, fmt); \
result = jump_table.SDL_vasprintf(&str, fmt, ap); \
va_end(ap); \
} \
bool retval = false; \
if (result >= 0) { \
retval = jump_table.SDL_RenderDebugTextFormat(renderer, x, y, "%s", str); \
} \
if (str != buf) { \
jump_table.SDL_free(str); \
} \
return retval; \
} \
_static void SDLCALL SDL_Log##name(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \
{ \
va_list ap; \
initcall; \
va_start(ap, fmt); \
jump_table.SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap); \
va_end(ap); \
} \
_static void SDLCALL SDL_LogMessage##name(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \
{ \
va_list ap; \
initcall; \
va_start(ap, fmt); \
jump_table.SDL_LogMessageV(category, priority, fmt, ap); \
va_end(ap); \
} \
SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Trace, TRACE) \
SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Verbose, VERBOSE) \
SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Debug, DEBUG) \
SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Info, INFO) \
SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Warn, WARN) \
SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Error, ERROR) \
SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Critical, CRITICAL)
// Typedefs for function pointers for jump table, and predeclare funcs
// The DEFAULT funcs will init jump table and then call real function.
// The REAL funcs are the actual functions, name-mangled to not clash.
#define SDL_DYNAPI_PROC(rc, fn, params, args, ret) \
typedef rc (SDLCALL *SDL_DYNAPIFN_##fn) params;\
static rc SDLCALL fn##_DEFAULT params; \
extern rc SDLCALL fn##_REAL params;
#include "SDL_dynapi_procs.h"
#undef SDL_DYNAPI_PROC
// The jump table!
typedef struct
{
#define SDL_DYNAPI_PROC(rc, fn, params, args, ret) SDL_DYNAPIFN_##fn fn;
#include "SDL_dynapi_procs.h"
#undef SDL_DYNAPI_PROC
} SDL_DYNAPI_jump_table;
// The actual jump table.
static SDL_DYNAPI_jump_table jump_table = {
#define SDL_DYNAPI_PROC(rc, fn, params, args, ret) fn##_DEFAULT,
#include "SDL_dynapi_procs.h"
#undef SDL_DYNAPI_PROC
};
// Default functions init the function table then call right thing.
#define SDL_DYNAPI_PROC(rc, fn, params, args, ret) \
static rc SDLCALL fn##_DEFAULT params \
{ \
SDL_InitDynamicAPI(); \
ret jump_table.fn args; \
}
#define SDL_DYNAPI_PROC_NO_VARARGS 1
#include "SDL_dynapi_procs.h"
#undef SDL_DYNAPI_PROC
#undef SDL_DYNAPI_PROC_NO_VARARGS
SDL_DYNAPI_VARARGS(static, _DEFAULT, SDL_InitDynamicAPI())
// Public API functions to jump into the jump table.
#define SDL_DYNAPI_PROC(rc, fn, params, args, ret) \
rc SDLCALL fn params \
{ \
ret jump_table.fn args; \
}
#define SDL_DYNAPI_PROC_NO_VARARGS 1
#include "SDL_dynapi_procs.h"
#undef SDL_DYNAPI_PROC
#undef SDL_DYNAPI_PROC_NO_VARARGS
SDL_DYNAPI_VARARGS(, , )
#define ENABLE_SDL_CALL_LOGGING 0
#if ENABLE_SDL_CALL_LOGGING
static bool SDLCALL SDL_SetError_LOGSDLCALLS(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
{
char buf[512]; // !!! FIXME: dynamic allocation
va_list ap;
SDL_Log_REAL("SDL3CALL SDL_SetError");
va_start(ap, fmt);
SDL_vsnprintf_REAL(buf, sizeof(buf), fmt, ap);
va_end(ap);
return SDL_SetError_REAL("%s", buf);
}
static int SDLCALL SDL_sscanf_LOGSDLCALLS(const char *buf, SDL_SCANF_FORMAT_STRING const char *fmt, ...)
{
int result;
va_list ap;
SDL_Log_REAL("SDL3CALL SDL_sscanf");
va_start(ap, fmt);
result = SDL_vsscanf_REAL(buf, fmt, ap);
va_end(ap);
return result;
}
static int SDLCALL SDL_snprintf_LOGSDLCALLS(SDL_OUT_Z_CAP(maxlen) char *buf, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
{
int result;
va_list ap;
SDL_Log_REAL("SDL3CALL SDL_snprintf");
va_start(ap, fmt);
result = SDL_vsnprintf_REAL(buf, maxlen, fmt, ap);
va_end(ap);
return result;
}
static int SDLCALL SDL_asprintf_LOGSDLCALLS(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
{
int result;
va_list ap;
SDL_Log_REAL("SDL3CALL SDL_asprintf");
va_start(ap, fmt);
result = SDL_vasprintf_REAL(strp, fmt, ap);
va_end(ap);
return result;
}
static int SDLCALL SDL_swprintf_LOGSDLCALLS(SDL_OUT_Z_CAP(maxlen) wchar_t *buf, size_t maxlen, SDL_PRINTF_FORMAT_STRING const wchar_t *fmt, ...)
{
int result;
va_list ap;
SDL_Log_REAL("SDL3CALL SDL_swprintf");
va_start(ap, fmt);
result = SDL_vswprintf_REAL(buf, maxlen, fmt, ap);
va_end(ap);
return result;
}
static size_t SDLCALL SDL_IOprintf_LOGSDLCALLS(SDL_IOStream *context, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
{
size_t result;
va_list ap;
SDL_Log_REAL("SDL3CALL SDL_IOprintf");
va_start(ap, fmt);
result = SDL_IOvprintf_REAL(context, fmt, ap);
va_end(ap);
return result;
}
static bool SDLCALL SDL_RenderDebugTextFormat_LOGSDLCALLS(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
{
char buf[128], *str = buf;
int result;
va_list ap;
SDL_Log_REAL("SDL3CALL SDL_RenderDebugTextFormat");
va_start(ap, fmt);
result = jump_table.SDL_vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (result >= 0 && (size_t)result >= sizeof(buf)) {
str = NULL;
va_start(ap, fmt);
result = SDL_vasprintf_REAL(&str, fmt, ap);
va_end(ap);
}
bool retval = false;
if (result >= 0) {
retval = SDL_RenderDebugTextFormat_REAL(renderer, x, y, "%s", str);
}
if (str != buf) {
jump_table.SDL_free(str);
}
return retval;
}
static void SDLCALL SDL_Log_LOGSDLCALLS(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
{
va_list ap;
SDL_Log_REAL("SDL3CALL SDL_Log");
va_start(ap, fmt);
SDL_LogMessageV_REAL(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap);
va_end(ap);
}
static void SDLCALL SDL_LogMessage_LOGSDLCALLS(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
{
va_list ap;
SDL_Log_REAL("SDL3CALL SDL_LogMessage");
va_start(ap, fmt);
SDL_LogMessageV_REAL(category, priority, fmt, ap);
va_end(ap);
}
#define SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(logname, prio) \
static void SDLCALL SDL_Log##logname##_LOGSDLCALLS(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \
{ \
va_list ap; \
va_start(ap, fmt); \
SDL_Log_REAL("SDL3CALL SDL_Log%s", #logname); \
SDL_LogMessageV_REAL(category, SDL_LOG_PRIORITY_##prio, fmt, ap); \
va_end(ap); \
}
SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Trace, TRACE)
SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Verbose, VERBOSE)
SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Debug, DEBUG)
SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Info, INFO)
SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Warn, WARN)
SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Error, ERROR)
SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Critical, CRITICAL)
#define SDL_DYNAPI_PROC(rc, fn, params, args, ret) \
rc SDLCALL fn##_LOGSDLCALLS params \
{ \
SDL_Log_REAL("SDL3CALL %s", #fn); \
ret fn##_REAL args; \
}
#define SDL_DYNAPI_PROC_NO_VARARGS 1
#include "SDL_dynapi_procs.h"
#undef SDL_DYNAPI_PROC
#undef SDL_DYNAPI_PROC_NO_VARARGS
#endif
/* we make this a static function so we can call the correct one without the
system's dynamic linker resolving to the wrong version of this. */
static Sint32 initialize_jumptable(Uint32 apiver, void *table, Uint32 tablesize)
{
SDL_DYNAPI_jump_table *output_jump_table = (SDL_DYNAPI_jump_table *)table;
if (apiver != SDL_DYNAPI_VERSION) {
// !!! FIXME: can maybe handle older versions?
return -1; // not compatible.
} else if (tablesize > sizeof(jump_table)) {
return -1; // newer version of SDL with functions we can't provide.
}
// Init our jump table first.
#if ENABLE_SDL_CALL_LOGGING
{
const char *env = SDL_getenv_unsafe_REAL("SDL_DYNAPI_LOG_CALLS");
const bool log_calls = (env && SDL_atoi_REAL(env));
if (log_calls) {
#define SDL_DYNAPI_PROC(rc, fn, params, args, ret) jump_table.fn = fn##_LOGSDLCALLS;
#include "SDL_dynapi_procs.h"
#undef SDL_DYNAPI_PROC
} else {
#define SDL_DYNAPI_PROC(rc, fn, params, args, ret) jump_table.fn = fn##_REAL;
#include "SDL_dynapi_procs.h"
#undef SDL_DYNAPI_PROC
}
}
#else
#define SDL_DYNAPI_PROC(rc, fn, params, args, ret) jump_table.fn = fn##_REAL;
#include "SDL_dynapi_procs.h"
#undef SDL_DYNAPI_PROC
#endif
// Then the external table...
if (output_jump_table != &jump_table) {
jump_table.SDL_memcpy(output_jump_table, &jump_table, tablesize);
}
// Safe to call SDL functions now; jump table is initialized!
return 0; // success!
}
// Here's the exported entry point that fills in the jump table.
// Use specific types when an "int" might suffice to keep this sane.
typedef Sint32 (SDLCALL *SDL_DYNAPI_ENTRYFN)(Uint32 apiver, void *table, Uint32 tablesize);
extern SDL_DECLSPEC Sint32 SDLCALL SDL_DYNAPI_entry(Uint32, void *, Uint32);
Sint32 SDL_DYNAPI_entry(Uint32 apiver, void *table, Uint32 tablesize)
{
return initialize_jumptable(apiver, table, tablesize);
}
#ifdef __cplusplus
}
#endif
// Obviously we can't use SDL_LoadObject() to load SDL. :)
// Also obviously, we never close the loaded library.
#if defined(WIN32) || defined(_WIN32) || defined(SDL_PLATFORM_CYGWIN)
static SDL_INLINE void *get_sdlapi_entry(const char *fname, const char *sym)
{
HMODULE lib = LoadLibraryA(fname);
void *result = NULL;
if (lib) {
result = (void *) GetProcAddress(lib, sym);
if (!result) {
FreeLibrary(lib);
}
}
return result;
}
#elif defined(SDL_PLATFORM_UNIX) || defined(SDL_PLATFORM_APPLE) || defined(SDL_PLATFORM_HAIKU)
#include <dlfcn.h>
static SDL_INLINE void *get_sdlapi_entry(const char *fname, const char *sym)
{
void *lib = dlopen(fname, RTLD_NOW | RTLD_LOCAL);
void *result = NULL;
if (lib) {
result = dlsym(lib, sym);
if (!result) {
dlclose(lib);
}
}
return result;
}
#else
#error Please define your platform.
#endif
static void dynapi_warn(const char *msg)
{
const char *caption = "SDL Dynamic API Failure!";
(void)caption;
// SDL_ShowSimpleMessageBox() is a too heavy for here.
#if (defined(WIN32) || defined(_WIN32) || defined(SDL_PLATFORM_CYGWIN)) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
MessageBoxA(NULL, msg, caption, MB_OK | MB_ICONERROR);
#elif defined(HAVE_STDIO_H)
fprintf(stderr, "\n\n%s\n%s\n\n", caption, msg);
fflush(stderr);
#endif
}
/* This is not declared in any header, although it is shared between some
parts of SDL, because we don't want anything calling it without an
extremely good reason. */
#ifdef __cplusplus
extern "C" {
#endif
extern SDL_NORETURN void SDL_ExitProcess(int exitcode);
#ifdef __WATCOMC__
#pragma aux SDL_ExitProcess aborts;
#endif
#ifdef __cplusplus
}
#endif
static void SDL_InitDynamicAPILocked(void)
{
// this can't use SDL_getenv_unsafe_REAL, because it might allocate memory before the app can set their allocator.
#if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__)
// We've always used LoadLibraryA for this, so this has never worked with Unicode paths on Windows. Sorry.
char envbuf[512]; // overflows will just report as environment variable being unset, but LoadLibraryA has a MAX_PATH of 260 anyhow, apparently.
const DWORD rc = GetEnvironmentVariableA(SDL_DYNAMIC_API_ENVVAR, envbuf, (DWORD) sizeof (envbuf));
char *libname = ((rc != 0) && (rc < sizeof (envbuf))) ? envbuf : NULL;
#else
char *libname = getenv(SDL_DYNAMIC_API_ENVVAR);
#endif
SDL_DYNAPI_ENTRYFN entry = NULL; // funcs from here by default.
bool use_internal = true;
if (libname) {
while (*libname && !entry) {
// This is evil, but we're not making any permanent changes...
char *ptr = (char *)libname;
while (true) {
char ch = *ptr;
if ((ch == ',') || (ch == '\0')) {
*ptr = '\0';
entry = (SDL_DYNAPI_ENTRYFN)get_sdlapi_entry(libname, "SDL_DYNAPI_entry");
*ptr = ch;
libname = (ch == '\0') ? ptr : (ptr + 1);
break;
} else {
ptr++;
}
}
}
if (!entry) {
dynapi_warn("Couldn't load an overriding SDL library. Please fix or remove the " SDL_DYNAMIC_API_ENVVAR " environment variable. Using the default SDL.");
// Just fill in the function pointers from this library, later.
}
}
if (entry) {
if (entry(SDL_DYNAPI_VERSION, &jump_table, sizeof(jump_table)) < 0) {
dynapi_warn("Couldn't override SDL library. Using a newer SDL build might help. Please fix or remove the " SDL_DYNAMIC_API_ENVVAR " environment variable. Using the default SDL.");
// Just fill in the function pointers from this library, later.
} else {
use_internal = false; // We overrode SDL! Don't use the internal version!
}
}
// Just fill in the function pointers from this library.
if (use_internal) {
if (initialize_jumptable(SDL_DYNAPI_VERSION, &jump_table, sizeof(jump_table)) < 0) {
// Now we're screwed. Should definitely abort now.
dynapi_warn("Failed to initialize internal SDL dynapi. As this would otherwise crash, we have to abort now.");
#ifndef NDEBUG
SDL_TriggerBreakpoint();
#endif
SDL_ExitProcess(86);
}
}
// we intentionally never close the newly-loaded lib, of course.
}
static void SDL_InitDynamicAPI(void)
{
/* So the theory is that every function in the jump table defaults to
* calling this function, and then replaces itself with a version that
* doesn't call this function anymore. But it's possible that, in an
* extreme corner case, you can have a second thread hit this function
* while the jump table is being initialized by the first.
* In this case, a spinlock is really painful compared to what spinlocks
* _should_ be used for, but this would only happen once, and should be
* insanely rare, as you would have to spin a thread outside of SDL (as
* SDL_CreateThread() would also call this function before building the
* new thread).
*/
static bool already_initialized = false;
static SDL_SpinLock lock = 0;
SDL_LockSpinlock_REAL(&lock);
if (!already_initialized) {
SDL_InitDynamicAPILocked();
already_initialized = true;
}
SDL_UnlockSpinlock_REAL(&lock);
}
#else // SDL_DYNAMIC_API
#include <SDL3/SDL.h>
Sint32 SDL_DYNAPI_entry(Uint32 apiver, void *table, Uint32 tablesize);
Sint32 SDL_DYNAPI_entry(Uint32 apiver, void *table, Uint32 tablesize)
{
(void)apiver;
(void)table;
(void)tablesize;
return -1; // not compatible.
}
#endif // SDL_DYNAMIC_API

View file

@ -0,0 +1,75 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_dynapi_h_
#define SDL_dynapi_h_
/* IMPORTANT:
This is the master switch to disabling the dynamic API. We made it so you
have to hand-edit an internal source file in SDL to turn it off; you
can do it if you want it badly enough, but hopefully you won't want to.
You should understand the ramifications of turning this off: it makes it
hard to update your SDL in the field, and impossible if you've statically
linked SDL into your app. Understand that platforms change, and if we can't
drop in an updated SDL, your application can definitely break some time
in the future, even if it's fine today.
To be sure, as new system-level video and audio APIs are introduced, an
updated SDL can transparently take advantage of them, but your program will
not without this feature. Think hard before turning it off.
*/
#ifdef SDL_DYNAMIC_API // Tried to force it on the command line?
#error Nope, you have to edit this file to force this off.
#endif
#ifdef SDL_PLATFORM_APPLE
#include "TargetConditionals.h"
#endif
#if defined(SDL_PLATFORM_PRIVATE) // probably not useful on private platforms.
#define SDL_DYNAMIC_API 0
#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE // probably not useful on iOS.
#define SDL_DYNAMIC_API 0
#elif defined(SDL_PLATFORM_ANDROID) // probably not useful on Android.
#define SDL_DYNAMIC_API 0
#elif defined(SDL_PLATFORM_EMSCRIPTEN) // probably not useful on Emscripten.
#define SDL_DYNAMIC_API 0
#elif defined(SDL_PLATFORM_PS2) && SDL_PLATFORM_PS2
#define SDL_DYNAMIC_API 0
#elif defined(SDL_PLATFORM_PSP) && SDL_PLATFORM_PSP
#define SDL_DYNAMIC_API 0
#elif defined(SDL_PLATFORM_RISCOS) // probably not useful on RISC OS, since dlopen() can't be used when using static linking.
#define SDL_DYNAMIC_API 0
#elif defined(__clang_analyzer__) || defined(__INTELLISENSE__) || defined(SDL_THREAD_SAFETY_ANALYSIS)
#define SDL_DYNAMIC_API 0 // Turn off for static analysis, so reports are more clear.
#elif defined(SDL_PLATFORM_VITA)
#define SDL_DYNAMIC_API 0 // vitasdk doesn't support dynamic linking
#elif defined(SDL_PLATFORM_3DS)
#define SDL_DYNAMIC_API 0 // devkitARM doesn't support dynamic linking
#elif defined(DYNAPI_NEEDS_DLOPEN) && !defined(HAVE_DLOPEN)
#define SDL_DYNAMIC_API 0 // we need dlopen(), but don't have it....
#endif
// everyone else. This is where we turn on the API if nothing forced it off.
#ifndef SDL_DYNAMIC_API
#define SDL_DYNAMIC_API 1
#endif
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,52 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_dynapi_unsupported_h_
#define SDL_dynapi_unsupported_h_
#if !defined(SDL_PLATFORM_WINDOWS)
typedef struct ID3D12Device ID3D12Device;
typedef void *SDL_WindowsMessageHook;
#endif
#if !(defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK))
typedef struct ID3D11Device ID3D11Device;
typedef struct IDirect3DDevice9 IDirect3DDevice9;
#endif
#ifndef SDL_PLATFORM_GDK
typedef struct XTaskQueueHandle XTaskQueueHandle;
#endif
#ifndef SDL_PLATFORM_GDK
typedef struct XUserHandle XUserHandle;
#endif
#ifndef SDL_PLATFORM_ANDROID
typedef void *SDL_RequestAndroidPermissionCallback;
#endif
#ifndef SDL_PLATFORM_IOS
typedef void *SDL_iOSAnimationCallback;
#endif
#endif

547
vendor/sdl-3.2.10/src/dynapi/gendynapi.py vendored Executable file
View file

@ -0,0 +1,547 @@
#!/usr/bin/env python3
# Simple DirectMedia Layer
# Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
#
# This software is provided 'as-is', without any express or implied
# warranty. In no event will the authors be held liable for any damages
# arising from the use of this software.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
#
# 1. The origin of this software must not be misrepresented; you must not
# claim that you wrote the original software. If you use this software
# in a product, an acknowledgment in the product documentation would be
# appreciated but is not required.
# 2. Altered source versions must be plainly marked as such, and must not be
# misrepresented as being the original software.
# 3. This notice may not be removed or altered from any source distribution.
# WHAT IS THIS?
# When you add a public API to SDL, please run this script, make sure the
# output looks sane (git diff, it adds to existing files), and commit it.
# It keeps the dynamic API jump table operating correctly.
#
# Platform-specific API:
# After running the script, you have to manually add #ifdef SDL_PLATFORM_WIN32
# or similar around the function in 'SDL_dynapi_procs.h'.
#
import argparse
import dataclasses
import json
import logging
import os
from pathlib import Path
import pprint
import re
SDL_ROOT = Path(__file__).resolve().parents[2]
SDL_INCLUDE_DIR = SDL_ROOT / "include/SDL3"
SDL_DYNAPI_PROCS_H = SDL_ROOT / "src/dynapi/SDL_dynapi_procs.h"
SDL_DYNAPI_OVERRIDES_H = SDL_ROOT / "src/dynapi/SDL_dynapi_overrides.h"
SDL_DYNAPI_SYM = SDL_ROOT / "src/dynapi/SDL_dynapi.sym"
RE_EXTERN_C = re.compile(r'.*extern[ "]*C[ "].*')
RE_COMMENT_REMOVE_CONTENT = re.compile(r'\/\*.*\*/')
RE_PARSING_FUNCTION = re.compile(r'(.*SDLCALL[^\(\)]*) ([a-zA-Z0-9_]+) *\((.*)\) *;.*')
#eg:
# void (SDLCALL *callback)(void*, int)
# \1(\2)\3
RE_PARSING_CALLBACK = re.compile(r'([^\(\)]*)\(([^\(\)]+)\)(.*)')
logger = logging.getLogger(__name__)
@dataclasses.dataclass(frozen=True)
class SdlProcedure:
retval: str
name: str
parameter: list[str]
parameter_name: list[str]
header: str
comment: str
@property
def variadic(self) -> bool:
return "..." in self.parameter
def parse_header(header_path: Path) -> list[SdlProcedure]:
logger.debug("Parse header: %s", header_path)
header_procedures = []
parsing_function = False
current_func = ""
parsing_comment = False
current_comment = ""
ignore_wiki_documentation = False
with header_path.open() as f:
for line in f:
# Skip lines if we're in a wiki documentation block.
if ignore_wiki_documentation:
if line.startswith("#endif"):
ignore_wiki_documentation = False
continue
# Discard wiki documentations blocks.
if line.startswith("#ifdef SDL_WIKI_DOCUMENTATION_SECTION"):
ignore_wiki_documentation = True
continue
# Discard pre-processor directives ^#.*
if line.startswith("#"):
continue
# Discard "extern C" line
match = RE_EXTERN_C.match(line)
if match:
continue
# Remove one line comment // ...
# eg: extern SDL_DECLSPEC SDL_hid_device * SDLCALL SDL_hid_open_path(const char *path, int bExclusive /* = false */)
line = RE_COMMENT_REMOVE_CONTENT.sub('', line)
# Get the comment block /* ... */ across several lines
match_start = "/*" in line
match_end = "*/" in line
if match_start and match_end:
continue
if match_start:
parsing_comment = True
current_comment = line
continue
if match_end:
parsing_comment = False
current_comment += line
continue
if parsing_comment:
current_comment += line
continue
# Get the function prototype across several lines
if parsing_function:
# Append to the current function
current_func += " "
current_func += line.strip()
else:
# if is contains "extern", start grabbing
if "extern" not in line:
continue
# Start grabbing the new function
current_func = line.strip()
parsing_function = True
# If it contains ';', then the function is complete
if ";" not in current_func:
continue
# Got function/comment, reset vars
parsing_function = False
func = current_func
comment = current_comment
current_func = ""
current_comment = ""
# Discard if it doesn't contain 'SDLCALL'
if "SDLCALL" not in func:
logger.debug(" Discard, doesn't have SDLCALL: %r", func)
continue
# Discard if it contains 'SDLMAIN_DECLSPEC' (these are not SDL symbols).
if "SDLMAIN_DECLSPEC" in func:
logger.debug(" Discard, has SDLMAIN_DECLSPEC: %r", func)
continue
logger.debug("Raw data: %r", func)
# Replace unusual stuff...
func = func.replace(" SDL_PRINTF_VARARG_FUNC(1)", "")
func = func.replace(" SDL_PRINTF_VARARG_FUNC(2)", "")
func = func.replace(" SDL_PRINTF_VARARG_FUNC(3)", "")
func = func.replace(" SDL_PRINTF_VARARG_FUNC(4)", "")
func = func.replace(" SDL_PRINTF_VARARG_FUNCV(1)", "")
func = func.replace(" SDL_PRINTF_VARARG_FUNCV(2)", "")
func = func.replace(" SDL_PRINTF_VARARG_FUNCV(3)", "")
func = func.replace(" SDL_PRINTF_VARARG_FUNCV(4)", "")
func = func.replace(" SDL_WPRINTF_VARARG_FUNC(3)", "")
func = func.replace(" SDL_WPRINTF_VARARG_FUNCV(3)", "")
func = func.replace(" SDL_SCANF_VARARG_FUNC(2)", "")
func = func.replace(" SDL_SCANF_VARARG_FUNCV(2)", "")
func = func.replace(" SDL_ANALYZER_NORETURN", "")
func = func.replace(" SDL_MALLOC", "")
func = func.replace(" SDL_ALLOC_SIZE2(1, 2)", "")
func = func.replace(" SDL_ALLOC_SIZE(2)", "")
func = re.sub(r" SDL_ACQUIRE\(.*\)", "", func)
func = re.sub(r" SDL_ACQUIRE_SHARED\(.*\)", "", func)
func = re.sub(r" SDL_TRY_ACQUIRE\(.*\)", "", func)
func = re.sub(r" SDL_TRY_ACQUIRE_SHARED\(.*\)", "", func)
func = re.sub(r" SDL_RELEASE\(.*\)", "", func)
func = re.sub(r" SDL_RELEASE_SHARED\(.*\)", "", func)
func = re.sub(r" SDL_RELEASE_GENERIC\(.*\)", "", func)
func = re.sub(r"([ (),])(SDL_IN_BYTECAP\([^)]*\))", r"\1", func)
func = re.sub(r"([ (),])(SDL_OUT_BYTECAP\([^)]*\))", r"\1", func)
func = re.sub(r"([ (),])(SDL_INOUT_Z_CAP\([^)]*\))", r"\1", func)
func = re.sub(r"([ (),])(SDL_OUT_Z_CAP\([^)]*\))", r"\1", func)
# Should be a valid function here
match = RE_PARSING_FUNCTION.match(func)
if not match:
logger.error("Cannot parse: %s", func)
raise ValueError(func)
func_ret = match.group(1)
func_name = match.group(2)
func_params = match.group(3)
#
# Parse return value
#
func_ret = func_ret.replace('extern', ' ')
func_ret = func_ret.replace('SDLCALL', ' ')
func_ret = func_ret.replace('SDL_DECLSPEC', ' ')
func_ret, _ = re.subn('([ ]{2,})', ' ', func_ret)
# Remove trailing spaces in front of '*'
func_ret = func_ret.replace(' *', '*')
func_ret = func_ret.strip()
#
# Parse parameters
#
func_params = func_params.strip()
if func_params == "":
func_params = "void"
# Identify each function parameters with type and name
# (eventually there are callbacks of several parameters)
tmp = func_params.split(',')
tmp2 = []
param = ""
for t in tmp:
if param == "":
param = t
else:
param = param + "," + t
# Identify a callback or parameter when there is same count of '(' and ')'
if param.count('(') == param.count(')'):
tmp2.append(param.strip())
param = ""
# Process each parameters, separation name and type
func_param_type = []
func_param_name = []
for t in tmp2:
if t == "void":
func_param_type.append(t)
func_param_name.append("")
continue
if t == "...":
func_param_type.append(t)
func_param_name.append("")
continue
param_name = ""
# parameter is a callback
if '(' in t:
match = RE_PARSING_CALLBACK.match(t)
if not match:
logger.error("cannot parse callback: %s", t)
raise ValueError(t)
a = match.group(1).strip()
b = match.group(2).strip()
c = match.group(3).strip()
try:
(param_type, param_name) = b.rsplit('*', 1)
except:
param_type = t
param_name = "param_name_not_specified"
# bug rsplit ??
if param_name == "":
param_name = "param_name_not_specified"
# reconstruct a callback name for future parsing
func_param_type.append(a + " (" + param_type.strip() + " *REWRITE_NAME)" + c)
func_param_name.append(param_name.strip())
continue
# array like "char *buf[]"
has_array = False
if t.endswith("[]"):
t = t.replace("[]", "")
has_array = True
# pointer
if '*' in t:
try:
(param_type, param_name) = t.rsplit('*', 1)
except:
param_type = t
param_name = "param_name_not_specified"
# bug rsplit ??
if param_name == "":
param_name = "param_name_not_specified"
val = param_type.strip() + "*REWRITE_NAME"
# Remove trailing spaces in front of '*'
tmp = ""
while val != tmp:
tmp = val
val = val.replace(' ', ' ')
val = val.replace(' *', '*')
# first occurrence
val = val.replace('*', ' *', 1)
val = val.strip()
else: # non pointer
# cut-off last word on
try:
(param_type, param_name) = t.rsplit(' ', 1)
except:
param_type = t
param_name = "param_name_not_specified"
val = param_type.strip() + " REWRITE_NAME"
# set back array
if has_array:
val += "[]"
func_param_type.append(val)
func_param_name.append(param_name.strip())
new_proc = SdlProcedure(
retval=func_ret, # Return value type
name=func_name, # Function name
comment=comment, # Function comment
header=header_path.name, # Header file
parameter=func_param_type, # List of parameters (type + anonymized param name 'REWRITE_NAME')
parameter_name=func_param_name, # Real parameter name, or 'param_name_not_specified'
)
header_procedures.append(new_proc)
if logger.getEffectiveLevel() <= logging.DEBUG:
logger.debug("%s", pprint.pformat(new_proc))
return header_procedures
# Dump API into a json file
def full_API_json(path: Path, procedures: list[SdlProcedure]):
with path.open('w', newline='') as f:
json.dump([dataclasses.asdict(proc) for proc in procedures], f, indent=4, sort_keys=True)
logger.info("dump API to '%s'", path)
class CallOnce:
def __init__(self, cb):
self._cb = cb
self._called = False
def __call__(self, *args, **kwargs):
if self._called:
return
self._called = True
self._cb(*args, **kwargs)
# Check public function comments are correct
def print_check_comment_header():
logger.warning("")
logger.warning("Please fix following warning(s):")
logger.warning("--------------------------------")
def check_documentations(procedures: list[SdlProcedure]) -> None:
check_comment_header = CallOnce(print_check_comment_header)
warning_header_printed = False
# Check \param
for proc in procedures:
expected = len(proc.parameter)
if expected == 1:
if proc.parameter[0] == 'void':
expected = 0
count = proc.comment.count("\\param")
if count != expected:
# skip SDL_stdinc.h
if proc.header != 'SDL_stdinc.h':
# Warning mismatch \param and function prototype
check_comment_header()
logger.warning(" In file %s: function %s() has %d '\\param' but expected %d", proc.header, proc.name, count, expected)
# Warning check \param uses the correct parameter name
# skip SDL_stdinc.h
if proc.header != 'SDL_stdinc.h':
for n in proc.parameter_name:
if n != "" and "\\param " + n not in proc.comment and "\\param[out] " + n not in proc.comment:
check_comment_header()
logger.warning(" In file %s: function %s() missing '\\param %s'", proc.header, proc.name, n)
# Check \returns
for proc in procedures:
expected = 1
if proc.retval == 'void':
expected = 0
count = proc.comment.count("\\returns")
if count != expected:
# skip SDL_stdinc.h
if proc.header != 'SDL_stdinc.h':
# Warning mismatch \param and function prototype
check_comment_header()
logger.warning(" In file %s: function %s() has %d '\\returns' but expected %d" % (proc.header, proc.name, count, expected))
# Check \since
for proc in procedures:
expected = 1
count = proc.comment.count("\\since")
if count != expected:
# skip SDL_stdinc.h
if proc.header != 'SDL_stdinc.h':
# Warning mismatch \param and function prototype
check_comment_header()
logger.warning(" In file %s: function %s() has %d '\\since' but expected %d" % (proc.header, proc.name, count, expected))
# Parse 'sdl_dynapi_procs_h' file to find existing functions
def find_existing_proc_names() -> list[str]:
reg = re.compile(r'SDL_DYNAPI_PROC\([^,]*,([^,]*),.*\)')
ret = []
with SDL_DYNAPI_PROCS_H.open() as f:
for line in f:
match = reg.match(line)
if not match:
continue
existing_func = match.group(1)
ret.append(existing_func)
return ret
# Get list of SDL headers
def get_header_list() -> list[Path]:
ret = []
for f in SDL_INCLUDE_DIR.iterdir():
# Only *.h files
if f.is_file() and f.suffix == ".h":
ret.append(f)
else:
logger.debug("Skip %s", f)
# Order headers for reproducible behavior
ret.sort()
return ret
# Write the new API in files: _procs.h _overrivides.h and .sym
def add_dyn_api(proc: SdlProcedure) -> None:
decl_args: list[str] = []
call_args = []
for i, argtype in enumerate(proc.parameter):
# Special case, void has no parameter name
if argtype == "void":
assert len(decl_args) == 0
assert len(proc.parameter) == 1
decl_args.append("void")
continue
# Var name: a, b, c, ...
varname = chr(ord('a') + i)
decl_args.append(argtype.replace("REWRITE_NAME", varname))
if argtype != "...":
call_args.append(varname)
macro_args = (
proc.retval,
proc.name,
"({})".format(",".join(decl_args)),
"({})".format(",".join(call_args)),
"" if proc.retval == "void" else "return",
)
# File: SDL_dynapi_procs.h
#
# Add at last
# SDL_DYNAPI_PROC(SDL_EGLConfig,SDL_EGL_GetCurrentConfig,(void),(),return)
with SDL_DYNAPI_PROCS_H.open("a", newline="") as f:
if proc.variadic:
f.write("#ifndef SDL_DYNAPI_PROC_NO_VARARGS\n")
f.write(f"SDL_DYNAPI_PROC({','.join(macro_args)})\n")
if proc.variadic:
f.write("#endif\n")
# File: SDL_dynapi_overrides.h
#
# Add at last
# "#define SDL_DelayNS SDL_DelayNS_REAL
f = open(SDL_DYNAPI_OVERRIDES_H, "a", newline="")
f.write(f"#define {proc.name} {proc.name}_REAL\n")
f.close()
# File: SDL_dynapi.sym
#
# Add before "extra symbols go here" line
with SDL_DYNAPI_SYM.open() as f:
new_input = []
for line in f:
if "extra symbols go here" in line:
new_input.append(f" {proc.name};\n")
new_input.append(line)
with SDL_DYNAPI_SYM.open('w', newline='') as f:
for line in new_input:
f.write(line)
def main():
parser = argparse.ArgumentParser()
parser.set_defaults(loglevel=logging.INFO)
parser.add_argument('--dump', nargs='?', default=None, const="sdl.json", metavar="JSON", help='output all SDL API into a .json file')
parser.add_argument('--debug', action='store_const', const=logging.DEBUG, dest="loglevel", help='add debug traces')
args = parser.parse_args()
logging.basicConfig(level=args.loglevel, format='[%(levelname)s] %(message)s')
# Get list of SDL headers
sdl_list_includes = get_header_list()
procedures = []
for filename in sdl_list_includes:
header_procedures = parse_header(filename)
procedures.extend(header_procedures)
# Parse 'sdl_dynapi_procs_h' file to find existing functions
existing_proc_names = find_existing_proc_names()
for procedure in procedures:
if procedure.name not in existing_proc_names:
logger.info("NEW %s", procedure.name)
add_dyn_api(procedure)
if args.dump:
# Dump API into a json file
full_API_json(path=Path(args.dump), procedures=procedures)
# Check comment formatting
check_documentations(procedures)
if __name__ == '__main__':
raise SystemExit(main())