1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> |
4 | |
5 | This software is provided 'as-is', without any express or implied |
6 | warranty. In no event will the authors be held liable for any damages |
7 | arising from the use of this software. |
8 | |
9 | Permission is granted to anyone to use this software for any purpose, |
10 | including commercial applications, and to alter it and redistribute it |
11 | freely, subject to the following restrictions: |
12 | |
13 | 1. The origin of this software must not be misrepresented; you must not |
14 | claim that you wrote the original software. If you use this software |
15 | in a product, an acknowledgment in the product documentation would be |
16 | appreciated but is not required. |
17 | 2. Altered source versions must be plainly marked as such, and must not be |
18 | misrepresented as being the original software. |
19 | 3. This notice may not be removed or altered from any source distribution. |
20 | */ |
21 | |
22 | #include "SDL_build_config.h" |
23 | #include "SDL_dynapi.h" |
24 | #include "SDL_dynapi_unsupported.h" |
25 | |
26 | #if SDL_DYNAMIC_API |
27 | |
28 | #define SDL_DYNAMIC_API_ENVVAR "SDL3_DYNAMIC_API" |
29 | #define SDL_SLOW_MEMCPY |
30 | #define SDL_SLOW_MEMMOVE |
31 | #define SDL_SLOW_MEMSET |
32 | |
33 | #ifdef HAVE_STDIO_H |
34 | #include <stdio.h> |
35 | #endif |
36 | #ifdef HAVE_STDLIB_H |
37 | #include <stdlib.h> |
38 | #endif |
39 | |
40 | #include <SDL3/SDL.h> |
41 | #define SDL_MAIN_NOIMPL // don't drag in header-only implementation of SDL_main |
42 | #include <SDL3/SDL_main.h> |
43 | |
44 | |
45 | // These headers have system specific definitions, so aren't included above |
46 | #include <SDL3/SDL_vulkan.h> |
47 | |
48 | #if defined(WIN32) || defined(_WIN32) || defined(SDL_PLATFORM_CYGWIN) |
49 | #ifndef WIN32_LEAN_AND_MEAN |
50 | #define WIN32_LEAN_AND_MEAN 1 |
51 | #endif |
52 | #include <windows.h> |
53 | #endif |
54 | |
55 | /* This is the version of the dynamic API. This doesn't match the SDL version |
56 | and should not change until there's been a major revamp in API/ABI. |
57 | So 2.0.5 adds functions over 2.0.4? This number doesn't change; |
58 | the sizeof(jump_table) changes instead. But 2.1.0 changes how a function |
59 | works in an incompatible way or removes a function? This number changes, |
60 | since sizeof(jump_table) isn't sufficient anymore. It's likely |
61 | we'll forget to bump every time we add a function, so this is the |
62 | failsafe switch for major API change decisions. Respect it and use it |
63 | sparingly. */ |
64 | #define SDL_DYNAPI_VERSION 2 |
65 | |
66 | #ifdef __cplusplus |
67 | extern "C" { |
68 | #endif |
69 | |
70 | static void SDL_InitDynamicAPI(void); |
71 | |
72 | /* BE CAREFUL CALLING ANY SDL CODE IN HERE, IT WILL BLOW UP. |
73 | Even self-contained stuff might call SDL_SetError() and break everything. */ |
74 | |
75 | // behold, the macro salsa! |
76 | |
77 | // Can't use the macro for varargs nonsense. This is atrocious. |
78 | #define SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, logname, prio) \ |
79 | _static void SDLCALL SDL_Log##logname##name(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ |
80 | { \ |
81 | va_list ap; \ |
82 | initcall; \ |
83 | va_start(ap, fmt); \ |
84 | jump_table.SDL_LogMessageV(category, SDL_LOG_PRIORITY_##prio, fmt, ap); \ |
85 | va_end(ap); \ |
86 | } |
87 | |
88 | #define SDL_DYNAPI_VARARGS(_static, name, initcall) \ |
89 | _static bool SDLCALL SDL_SetError##name(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ |
90 | { \ |
91 | char buf[128], *str = buf; \ |
92 | int result; \ |
93 | va_list ap; \ |
94 | initcall; \ |
95 | va_start(ap, fmt); \ |
96 | result = jump_table.SDL_vsnprintf(buf, sizeof(buf), fmt, ap); \ |
97 | va_end(ap); \ |
98 | if (result >= 0 && (size_t)result >= sizeof(buf)) { \ |
99 | str = NULL; \ |
100 | va_start(ap, fmt); \ |
101 | result = jump_table.SDL_vasprintf(&str, fmt, ap); \ |
102 | va_end(ap); \ |
103 | } \ |
104 | if (result >= 0) { \ |
105 | jump_table.SDL_SetError("%s", str); \ |
106 | } \ |
107 | if (str != buf) { \ |
108 | jump_table.SDL_free(str); \ |
109 | } \ |
110 | return false; \ |
111 | } \ |
112 | _static int SDLCALL SDL_sscanf##name(const char *buf, SDL_SCANF_FORMAT_STRING const char *fmt, ...) \ |
113 | { \ |
114 | int result; \ |
115 | va_list ap; \ |
116 | initcall; \ |
117 | va_start(ap, fmt); \ |
118 | result = jump_table.SDL_vsscanf(buf, fmt, ap); \ |
119 | va_end(ap); \ |
120 | return result; \ |
121 | } \ |
122 | _static int SDLCALL SDL_snprintf##name(SDL_OUT_Z_CAP(maxlen) char *buf, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ |
123 | { \ |
124 | int result; \ |
125 | va_list ap; \ |
126 | initcall; \ |
127 | va_start(ap, fmt); \ |
128 | result = jump_table.SDL_vsnprintf(buf, maxlen, fmt, ap); \ |
129 | va_end(ap); \ |
130 | return result; \ |
131 | } \ |
132 | _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, ...) \ |
133 | { \ |
134 | int result; \ |
135 | va_list ap; \ |
136 | initcall; \ |
137 | va_start(ap, fmt); \ |
138 | result = jump_table.SDL_vswprintf(buf, maxlen, fmt, ap); \ |
139 | va_end(ap); \ |
140 | return result; \ |
141 | } \ |
142 | _static int SDLCALL SDL_asprintf##name(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ |
143 | { \ |
144 | int result; \ |
145 | va_list ap; \ |
146 | initcall; \ |
147 | va_start(ap, fmt); \ |
148 | result = jump_table.SDL_vasprintf(strp, fmt, ap); \ |
149 | va_end(ap); \ |
150 | return result; \ |
151 | } \ |
152 | _static size_t SDLCALL SDL_IOprintf##name(SDL_IOStream *context, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ |
153 | { \ |
154 | size_t result; \ |
155 | va_list ap; \ |
156 | initcall; \ |
157 | va_start(ap, fmt); \ |
158 | result = jump_table.SDL_IOvprintf(context, fmt, ap); \ |
159 | va_end(ap); \ |
160 | return result; \ |
161 | } \ |
162 | _static bool SDLCALL SDL_RenderDebugTextFormat##name(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ |
163 | { \ |
164 | char buf[128], *str = buf; \ |
165 | int result; \ |
166 | va_list ap; \ |
167 | initcall; \ |
168 | va_start(ap, fmt); \ |
169 | result = jump_table.SDL_vsnprintf(buf, sizeof(buf), fmt, ap); \ |
170 | va_end(ap); \ |
171 | if (result >= 0 && (size_t)result >= sizeof(buf)) { \ |
172 | str = NULL; \ |
173 | va_start(ap, fmt); \ |
174 | result = jump_table.SDL_vasprintf(&str, fmt, ap); \ |
175 | va_end(ap); \ |
176 | } \ |
177 | bool retval = false; \ |
178 | if (result >= 0) { \ |
179 | retval = jump_table.SDL_RenderDebugTextFormat(renderer, x, y, "%s", str); \ |
180 | } \ |
181 | if (str != buf) { \ |
182 | jump_table.SDL_free(str); \ |
183 | } \ |
184 | return retval; \ |
185 | } \ |
186 | _static void SDLCALL SDL_Log##name(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ |
187 | { \ |
188 | va_list ap; \ |
189 | initcall; \ |
190 | va_start(ap, fmt); \ |
191 | jump_table.SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap); \ |
192 | va_end(ap); \ |
193 | } \ |
194 | _static void SDLCALL SDL_LogMessage##name(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ |
195 | { \ |
196 | va_list ap; \ |
197 | initcall; \ |
198 | va_start(ap, fmt); \ |
199 | jump_table.SDL_LogMessageV(category, priority, fmt, ap); \ |
200 | va_end(ap); \ |
201 | } \ |
202 | SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Trace, TRACE) \ |
203 | SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Verbose, VERBOSE) \ |
204 | SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Debug, DEBUG) \ |
205 | SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Info, INFO) \ |
206 | SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Warn, WARN) \ |
207 | SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Error, ERROR) \ |
208 | SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Critical, CRITICAL) |
209 | |
210 | // Typedefs for function pointers for jump table, and predeclare funcs |
211 | // The DEFAULT funcs will init jump table and then call real function. |
212 | // The REAL funcs are the actual functions, name-mangled to not clash. |
213 | #define SDL_DYNAPI_PROC(rc, fn, params, args, ret) \ |
214 | typedef rc (SDLCALL *SDL_DYNAPIFN_##fn) params;\ |
215 | static rc SDLCALL fn##_DEFAULT params; \ |
216 | extern rc SDLCALL fn##_REAL params; |
217 | #include "SDL_dynapi_procs.h" |
218 | #undef SDL_DYNAPI_PROC |
219 | |
220 | // The jump table! |
221 | typedef struct |
222 | { |
223 | #define SDL_DYNAPI_PROC(rc, fn, params, args, ret) SDL_DYNAPIFN_##fn fn; |
224 | #include "SDL_dynapi_procs.h" |
225 | #undef SDL_DYNAPI_PROC |
226 | } SDL_DYNAPI_jump_table; |
227 | |
228 | // The actual jump table. |
229 | static SDL_DYNAPI_jump_table jump_table = { |
230 | #define SDL_DYNAPI_PROC(rc, fn, params, args, ret) fn##_DEFAULT, |
231 | #include "SDL_dynapi_procs.h" |
232 | #undef SDL_DYNAPI_PROC |
233 | }; |
234 | |
235 | // Default functions init the function table then call right thing. |
236 | #define SDL_DYNAPI_PROC(rc, fn, params, args, ret) \ |
237 | static rc SDLCALL fn##_DEFAULT params \ |
238 | { \ |
239 | SDL_InitDynamicAPI(); \ |
240 | ret jump_table.fn args; \ |
241 | } |
242 | #define SDL_DYNAPI_PROC_NO_VARARGS 1 |
243 | #include "SDL_dynapi_procs.h" |
244 | #undef SDL_DYNAPI_PROC |
245 | #undef SDL_DYNAPI_PROC_NO_VARARGS |
246 | SDL_DYNAPI_VARARGS(static, _DEFAULT, SDL_InitDynamicAPI()) |
247 | |
248 | // Public API functions to jump into the jump table. |
249 | #define SDL_DYNAPI_PROC(rc, fn, params, args, ret) \ |
250 | rc SDLCALL fn params \ |
251 | { \ |
252 | ret jump_table.fn args; \ |
253 | } |
254 | #define SDL_DYNAPI_PROC_NO_VARARGS 1 |
255 | #include "SDL_dynapi_procs.h" |
256 | #undef SDL_DYNAPI_PROC |
257 | #undef SDL_DYNAPI_PROC_NO_VARARGS |
258 | SDL_DYNAPI_VARARGS(, , ) |
259 | |
260 | #define ENABLE_SDL_CALL_LOGGING 0 |
261 | #if ENABLE_SDL_CALL_LOGGING |
262 | static bool SDLCALL SDL_SetError_LOGSDLCALLS(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) |
263 | { |
264 | char buf[512]; // !!! FIXME: dynamic allocation |
265 | va_list ap; |
266 | SDL_Log_REAL("SDL3CALL SDL_SetError" ); |
267 | va_start(ap, fmt); |
268 | SDL_vsnprintf_REAL(buf, sizeof(buf), fmt, ap); |
269 | va_end(ap); |
270 | return SDL_SetError_REAL("%s" , buf); |
271 | } |
272 | static int SDLCALL SDL_sscanf_LOGSDLCALLS(const char *buf, SDL_SCANF_FORMAT_STRING const char *fmt, ...) |
273 | { |
274 | int result; |
275 | va_list ap; |
276 | SDL_Log_REAL("SDL3CALL SDL_sscanf" ); |
277 | va_start(ap, fmt); |
278 | result = SDL_vsscanf_REAL(buf, fmt, ap); |
279 | va_end(ap); |
280 | return result; |
281 | } |
282 | static int SDLCALL SDL_snprintf_LOGSDLCALLS(SDL_OUT_Z_CAP(maxlen) char *buf, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) |
283 | { |
284 | int result; |
285 | va_list ap; |
286 | SDL_Log_REAL("SDL3CALL SDL_snprintf" ); |
287 | va_start(ap, fmt); |
288 | result = SDL_vsnprintf_REAL(buf, maxlen, fmt, ap); |
289 | va_end(ap); |
290 | return result; |
291 | } |
292 | static int SDLCALL SDL_asprintf_LOGSDLCALLS(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) |
293 | { |
294 | int result; |
295 | va_list ap; |
296 | SDL_Log_REAL("SDL3CALL SDL_asprintf" ); |
297 | va_start(ap, fmt); |
298 | result = SDL_vasprintf_REAL(strp, fmt, ap); |
299 | va_end(ap); |
300 | return result; |
301 | } |
302 | 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, ...) |
303 | { |
304 | int result; |
305 | va_list ap; |
306 | SDL_Log_REAL("SDL3CALL SDL_swprintf" ); |
307 | va_start(ap, fmt); |
308 | result = SDL_vswprintf_REAL(buf, maxlen, fmt, ap); |
309 | va_end(ap); |
310 | return result; |
311 | } |
312 | static size_t SDLCALL SDL_IOprintf_LOGSDLCALLS(SDL_IOStream *context, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) |
313 | { |
314 | size_t result; |
315 | va_list ap; |
316 | SDL_Log_REAL("SDL3CALL SDL_IOprintf" ); |
317 | va_start(ap, fmt); |
318 | result = SDL_IOvprintf_REAL(context, fmt, ap); |
319 | va_end(ap); |
320 | return result; |
321 | } |
322 | static bool SDLCALL SDL_RenderDebugTextFormat_LOGSDLCALLS(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) |
323 | { |
324 | char buf[128], *str = buf; |
325 | int result; |
326 | va_list ap; |
327 | SDL_Log_REAL("SDL3CALL SDL_RenderDebugTextFormat" ); |
328 | va_start(ap, fmt); |
329 | result = jump_table.SDL_vsnprintf(buf, sizeof(buf), fmt, ap); |
330 | va_end(ap); |
331 | if (result >= 0 && (size_t)result >= sizeof(buf)) { |
332 | str = NULL; |
333 | va_start(ap, fmt); |
334 | result = SDL_vasprintf_REAL(&str, fmt, ap); |
335 | va_end(ap); |
336 | } |
337 | bool retval = false; |
338 | if (result >= 0) { |
339 | retval = SDL_RenderDebugTextFormat_REAL(renderer, x, y, "%s" , str); |
340 | } |
341 | if (str != buf) { |
342 | jump_table.SDL_free(str); |
343 | } |
344 | return retval; |
345 | } |
346 | static void SDLCALL SDL_Log_LOGSDLCALLS(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) |
347 | { |
348 | va_list ap; |
349 | SDL_Log_REAL("SDL3CALL SDL_Log" ); |
350 | va_start(ap, fmt); |
351 | SDL_LogMessageV_REAL(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap); |
352 | va_end(ap); |
353 | } |
354 | static void SDLCALL SDL_LogMessage_LOGSDLCALLS(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) |
355 | { |
356 | va_list ap; |
357 | SDL_Log_REAL("SDL3CALL SDL_LogMessage" ); |
358 | va_start(ap, fmt); |
359 | SDL_LogMessageV_REAL(category, priority, fmt, ap); |
360 | va_end(ap); |
361 | } |
362 | #define SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(logname, prio) \ |
363 | static void SDLCALL SDL_Log##logname##_LOGSDLCALLS(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ |
364 | { \ |
365 | va_list ap; \ |
366 | va_start(ap, fmt); \ |
367 | SDL_Log_REAL("SDL3CALL SDL_Log%s", #logname); \ |
368 | SDL_LogMessageV_REAL(category, SDL_LOG_PRIORITY_##prio, fmt, ap); \ |
369 | va_end(ap); \ |
370 | } |
371 | SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Trace, TRACE) |
372 | SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Verbose, VERBOSE) |
373 | SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Debug, DEBUG) |
374 | SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Info, INFO) |
375 | SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Warn, WARN) |
376 | SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Error, ERROR) |
377 | SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Critical, CRITICAL) |
378 | #define SDL_DYNAPI_PROC(rc, fn, params, args, ret) \ |
379 | rc SDLCALL fn##_LOGSDLCALLS params \ |
380 | { \ |
381 | SDL_Log_REAL("SDL3CALL %s", #fn); \ |
382 | ret fn##_REAL args; \ |
383 | } |
384 | #define SDL_DYNAPI_PROC_NO_VARARGS 1 |
385 | #include "SDL_dynapi_procs.h" |
386 | #undef SDL_DYNAPI_PROC |
387 | #undef SDL_DYNAPI_PROC_NO_VARARGS |
388 | #endif |
389 | |
390 | /* we make this a static function so we can call the correct one without the |
391 | system's dynamic linker resolving to the wrong version of this. */ |
392 | static Sint32 initialize_jumptable(Uint32 apiver, void *table, Uint32 tablesize) |
393 | { |
394 | SDL_DYNAPI_jump_table *output_jump_table = (SDL_DYNAPI_jump_table *)table; |
395 | |
396 | if (apiver != SDL_DYNAPI_VERSION) { |
397 | // !!! FIXME: can maybe handle older versions? |
398 | return -1; // not compatible. |
399 | } else if (tablesize > sizeof(jump_table)) { |
400 | return -1; // newer version of SDL with functions we can't provide. |
401 | } |
402 | |
403 | // Init our jump table first. |
404 | #if ENABLE_SDL_CALL_LOGGING |
405 | { |
406 | const char *env = SDL_getenv_unsafe_REAL("SDL_DYNAPI_LOG_CALLS" ); |
407 | const bool log_calls = (env && SDL_atoi_REAL(env)); |
408 | if (log_calls) { |
409 | #define SDL_DYNAPI_PROC(rc, fn, params, args, ret) jump_table.fn = fn##_LOGSDLCALLS; |
410 | #include "SDL_dynapi_procs.h" |
411 | #undef SDL_DYNAPI_PROC |
412 | } else { |
413 | #define SDL_DYNAPI_PROC(rc, fn, params, args, ret) jump_table.fn = fn##_REAL; |
414 | #include "SDL_dynapi_procs.h" |
415 | #undef SDL_DYNAPI_PROC |
416 | } |
417 | } |
418 | #else |
419 | #define SDL_DYNAPI_PROC(rc, fn, params, args, ret) jump_table.fn = fn##_REAL; |
420 | #include "SDL_dynapi_procs.h" |
421 | #undef SDL_DYNAPI_PROC |
422 | #endif |
423 | |
424 | // Then the external table... |
425 | if (output_jump_table != &jump_table) { |
426 | jump_table.SDL_memcpy(output_jump_table, &jump_table, tablesize); |
427 | } |
428 | |
429 | // Safe to call SDL functions now; jump table is initialized! |
430 | |
431 | return 0; // success! |
432 | } |
433 | |
434 | // Here's the exported entry point that fills in the jump table. |
435 | // Use specific types when an "int" might suffice to keep this sane. |
436 | typedef Sint32 (SDLCALL *SDL_DYNAPI_ENTRYFN)(Uint32 apiver, void *table, Uint32 tablesize); |
437 | extern SDL_DECLSPEC Sint32 SDLCALL SDL_DYNAPI_entry(Uint32, void *, Uint32); |
438 | |
439 | Sint32 SDL_DYNAPI_entry(Uint32 apiver, void *table, Uint32 tablesize) |
440 | { |
441 | return initialize_jumptable(apiver, table, tablesize); |
442 | } |
443 | |
444 | #ifdef __cplusplus |
445 | } |
446 | #endif |
447 | |
448 | // Obviously we can't use SDL_LoadObject() to load SDL. :) |
449 | // Also obviously, we never close the loaded library. |
450 | #if defined(WIN32) || defined(_WIN32) || defined(SDL_PLATFORM_CYGWIN) |
451 | static SDL_INLINE void *get_sdlapi_entry(const char *fname, const char *sym) |
452 | { |
453 | HMODULE lib = LoadLibraryA(fname); |
454 | void *result = NULL; |
455 | if (lib) { |
456 | result = (void *) GetProcAddress(lib, sym); |
457 | if (!result) { |
458 | FreeLibrary(lib); |
459 | } |
460 | } |
461 | return result; |
462 | } |
463 | |
464 | #elif defined(SDL_PLATFORM_UNIX) || defined(SDL_PLATFORM_APPLE) || defined(SDL_PLATFORM_HAIKU) |
465 | #include <dlfcn.h> |
466 | static SDL_INLINE void *get_sdlapi_entry(const char *fname, const char *sym) |
467 | { |
468 | void *lib = dlopen(fname, RTLD_NOW | RTLD_LOCAL); |
469 | void *result = NULL; |
470 | if (lib) { |
471 | result = dlsym(lib, sym); |
472 | if (!result) { |
473 | dlclose(lib); |
474 | } |
475 | } |
476 | return result; |
477 | } |
478 | |
479 | #else |
480 | #error Please define your platform. |
481 | #endif |
482 | |
483 | static void dynapi_warn(const char *msg) |
484 | { |
485 | const char *caption = "SDL Dynamic API Failure!" ; |
486 | (void)caption; |
487 | // SDL_ShowSimpleMessageBox() is a too heavy for here. |
488 | #if (defined(WIN32) || defined(_WIN32) || defined(SDL_PLATFORM_CYGWIN)) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) |
489 | MessageBoxA(NULL, msg, caption, MB_OK | MB_ICONERROR); |
490 | #elif defined(HAVE_STDIO_H) |
491 | fprintf(stderr, "\n\n%s\n%s\n\n" , caption, msg); |
492 | fflush(stderr); |
493 | #endif |
494 | } |
495 | |
496 | /* This is not declared in any header, although it is shared between some |
497 | parts of SDL, because we don't want anything calling it without an |
498 | extremely good reason. */ |
499 | #ifdef __cplusplus |
500 | extern "C" { |
501 | #endif |
502 | extern SDL_NORETURN void SDL_ExitProcess(int exitcode); |
503 | #ifdef __WATCOMC__ |
504 | #pragma aux SDL_ExitProcess aborts; |
505 | #endif |
506 | #ifdef __cplusplus |
507 | } |
508 | #endif |
509 | |
510 | static void SDL_InitDynamicAPILocked(void) |
511 | { |
512 | // this can't use SDL_getenv_unsafe_REAL, because it might allocate memory before the app can set their allocator. |
513 | #if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) |
514 | // We've always used LoadLibraryA for this, so this has never worked with Unicode paths on Windows. Sorry. |
515 | char envbuf[512]; // overflows will just report as environment variable being unset, but LoadLibraryA has a MAX_PATH of 260 anyhow, apparently. |
516 | const DWORD rc = GetEnvironmentVariableA(SDL_DYNAMIC_API_ENVVAR, envbuf, (DWORD) sizeof (envbuf)); |
517 | char *libname = ((rc != 0) && (rc < sizeof (envbuf))) ? envbuf : NULL; |
518 | #else |
519 | char *libname = getenv(SDL_DYNAMIC_API_ENVVAR); |
520 | #endif |
521 | |
522 | SDL_DYNAPI_ENTRYFN entry = NULL; // funcs from here by default. |
523 | bool use_internal = true; |
524 | |
525 | if (libname) { |
526 | while (*libname && !entry) { |
527 | // This is evil, but we're not making any permanent changes... |
528 | char *ptr = (char *)libname; |
529 | while (true) { |
530 | char ch = *ptr; |
531 | if ((ch == ',') || (ch == '\0')) { |
532 | *ptr = '\0'; |
533 | entry = (SDL_DYNAPI_ENTRYFN)get_sdlapi_entry(libname, "SDL_DYNAPI_entry" ); |
534 | *ptr = ch; |
535 | libname = (ch == '\0') ? ptr : (ptr + 1); |
536 | break; |
537 | } else { |
538 | ptr++; |
539 | } |
540 | } |
541 | } |
542 | if (!entry) { |
543 | dynapi_warn("Couldn't load an overriding SDL library. Please fix or remove the " SDL_DYNAMIC_API_ENVVAR " environment variable. Using the default SDL." ); |
544 | // Just fill in the function pointers from this library, later. |
545 | } |
546 | } |
547 | |
548 | if (entry) { |
549 | if (entry(SDL_DYNAPI_VERSION, &jump_table, sizeof(jump_table)) < 0) { |
550 | 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." ); |
551 | // Just fill in the function pointers from this library, later. |
552 | } else { |
553 | use_internal = false; // We overrode SDL! Don't use the internal version! |
554 | } |
555 | } |
556 | |
557 | // Just fill in the function pointers from this library. |
558 | if (use_internal) { |
559 | if (initialize_jumptable(SDL_DYNAPI_VERSION, &jump_table, sizeof(jump_table)) < 0) { |
560 | // Now we're screwed. Should definitely abort now. |
561 | dynapi_warn("Failed to initialize internal SDL dynapi. As this would otherwise crash, we have to abort now." ); |
562 | #ifndef NDEBUG |
563 | SDL_TriggerBreakpoint(); |
564 | #endif |
565 | SDL_ExitProcess(86); |
566 | } |
567 | } |
568 | |
569 | // we intentionally never close the newly-loaded lib, of course. |
570 | } |
571 | |
572 | static void SDL_InitDynamicAPI(void) |
573 | { |
574 | /* So the theory is that every function in the jump table defaults to |
575 | * calling this function, and then replaces itself with a version that |
576 | * doesn't call this function anymore. But it's possible that, in an |
577 | * extreme corner case, you can have a second thread hit this function |
578 | * while the jump table is being initialized by the first. |
579 | * In this case, a spinlock is really painful compared to what spinlocks |
580 | * _should_ be used for, but this would only happen once, and should be |
581 | * insanely rare, as you would have to spin a thread outside of SDL (as |
582 | * SDL_CreateThread() would also call this function before building the |
583 | * new thread). |
584 | */ |
585 | static bool already_initialized = false; |
586 | |
587 | static SDL_SpinLock lock = 0; |
588 | SDL_LockSpinlock_REAL(&lock); |
589 | |
590 | if (!already_initialized) { |
591 | SDL_InitDynamicAPILocked(); |
592 | already_initialized = true; |
593 | } |
594 | |
595 | SDL_UnlockSpinlock_REAL(&lock); |
596 | } |
597 | |
598 | #else // SDL_DYNAMIC_API |
599 | |
600 | #include <SDL3/SDL.h> |
601 | |
602 | Sint32 SDL_DYNAPI_entry(Uint32 apiver, void *table, Uint32 tablesize); |
603 | Sint32 SDL_DYNAPI_entry(Uint32 apiver, void *table, Uint32 tablesize) |
604 | { |
605 | (void)apiver; |
606 | (void)table; |
607 | (void)tablesize; |
608 | return -1; // not compatible. |
609 | } |
610 | |
611 | #endif // SDL_DYNAMIC_API |
612 | |