1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2021 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_config.h" |
23 | #include "SDL_dynapi.h" |
24 | |
25 | #if SDL_DYNAMIC_API |
26 | |
27 | #if defined(__OS2__) |
28 | #define INCL_DOS |
29 | #define INCL_DOSERRORS |
30 | #include <os2.h> |
31 | #include <dos.h> |
32 | #endif |
33 | |
34 | #include "SDL.h" |
35 | |
36 | /* These headers have system specific definitions, so aren't included above */ |
37 | #include "SDL_syswm.h" |
38 | #include "SDL_vulkan.h" |
39 | |
40 | /* This is the version of the dynamic API. This doesn't match the SDL version |
41 | and should not change until there's been a major revamp in API/ABI. |
42 | So 2.0.5 adds functions over 2.0.4? This number doesn't change; |
43 | the sizeof (jump_table) changes instead. But 2.1.0 changes how a function |
44 | works in an incompatible way or removes a function? This number changes, |
45 | since sizeof (jump_table) isn't sufficient anymore. It's likely |
46 | we'll forget to bump every time we add a function, so this is the |
47 | failsafe switch for major API change decisions. Respect it and use it |
48 | sparingly. */ |
49 | #define SDL_DYNAPI_VERSION 1 |
50 | |
51 | static void SDL_InitDynamicAPI(void); |
52 | |
53 | |
54 | /* BE CAREFUL CALLING ANY SDL CODE IN HERE, IT WILL BLOW UP. |
55 | Even self-contained stuff might call SDL_Error and break everything. */ |
56 | |
57 | |
58 | /* behold, the macro salsa! */ |
59 | |
60 | /* !!! FIXME: ...disabled...until we write it. :) */ |
61 | #define DISABLE_JUMP_MAGIC 1 |
62 | |
63 | #if DISABLE_JUMP_MAGIC |
64 | /* Can't use the macro for varargs nonsense. This is atrocious. */ |
65 | #define SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, logname, prio) \ |
66 | _static void SDLCALL SDL_Log##logname##name(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) { \ |
67 | va_list ap; initcall; va_start(ap, fmt); \ |
68 | jump_table.SDL_LogMessageV(category, SDL_LOG_PRIORITY_##prio, fmt, ap); \ |
69 | va_end(ap); \ |
70 | } |
71 | |
72 | #define SDL_DYNAPI_VARARGS(_static, name, initcall) \ |
73 | _static int SDLCALL SDL_SetError##name(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) { \ |
74 | char buf[512]; /* !!! FIXME: dynamic allocation */ \ |
75 | va_list ap; initcall; va_start(ap, fmt); \ |
76 | jump_table.SDL_vsnprintf(buf, sizeof (buf), fmt, ap); \ |
77 | va_end(ap); \ |
78 | return jump_table.SDL_SetError("%s", buf); \ |
79 | } \ |
80 | _static int SDLCALL SDL_sscanf##name(const char *buf, SDL_SCANF_FORMAT_STRING const char *fmt, ...) { \ |
81 | int retval; va_list ap; initcall; va_start(ap, fmt); \ |
82 | retval = jump_table.SDL_vsscanf(buf, fmt, ap); \ |
83 | va_end(ap); \ |
84 | return retval; \ |
85 | } \ |
86 | _static int SDLCALL SDL_snprintf##name(SDL_OUT_Z_CAP(maxlen) char *buf, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) { \ |
87 | int retval; va_list ap; initcall; va_start(ap, fmt); \ |
88 | retval = jump_table.SDL_vsnprintf(buf, maxlen, fmt, ap); \ |
89 | va_end(ap); \ |
90 | return retval; \ |
91 | } \ |
92 | _static void SDLCALL SDL_Log##name(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) { \ |
93 | va_list ap; initcall; va_start(ap, fmt); \ |
94 | jump_table.SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap); \ |
95 | va_end(ap); \ |
96 | } \ |
97 | _static void SDLCALL SDL_LogMessage##name(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) { \ |
98 | va_list ap; initcall; va_start(ap, fmt); \ |
99 | jump_table.SDL_LogMessageV(category, priority, fmt, ap); \ |
100 | va_end(ap); \ |
101 | } \ |
102 | SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Verbose, VERBOSE) \ |
103 | SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Debug, DEBUG) \ |
104 | SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Info, INFO) \ |
105 | SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Warn, WARN) \ |
106 | SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Error, ERROR) \ |
107 | SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Critical, CRITICAL) |
108 | #endif |
109 | |
110 | |
111 | /* Typedefs for function pointers for jump table, and predeclare funcs */ |
112 | /* The DEFAULT funcs will init jump table and then call real function. */ |
113 | /* The REAL funcs are the actual functions, name-mangled to not clash. */ |
114 | #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) \ |
115 | typedef rc (SDLCALL *SDL_DYNAPIFN_##fn) params; \ |
116 | static rc SDLCALL fn##_DEFAULT params; \ |
117 | extern rc SDLCALL fn##_REAL params; |
118 | #include "SDL_dynapi_procs.h" |
119 | #undef SDL_DYNAPI_PROC |
120 | |
121 | /* The jump table! */ |
122 | typedef struct { |
123 | #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) SDL_DYNAPIFN_##fn fn; |
124 | #include "SDL_dynapi_procs.h" |
125 | #undef SDL_DYNAPI_PROC |
126 | } SDL_DYNAPI_jump_table; |
127 | |
128 | /* Predeclare the default functions for initializing the jump table. */ |
129 | #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) static rc SDLCALL fn##_DEFAULT params; |
130 | #include "SDL_dynapi_procs.h" |
131 | #undef SDL_DYNAPI_PROC |
132 | |
133 | /* The actual jump table. */ |
134 | static SDL_DYNAPI_jump_table jump_table = { |
135 | #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) fn##_DEFAULT, |
136 | #include "SDL_dynapi_procs.h" |
137 | #undef SDL_DYNAPI_PROC |
138 | }; |
139 | |
140 | /* Default functions init the function table then call right thing. */ |
141 | #if DISABLE_JUMP_MAGIC |
142 | #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) \ |
143 | static rc SDLCALL fn##_DEFAULT params { \ |
144 | SDL_InitDynamicAPI(); \ |
145 | ret jump_table.fn args; \ |
146 | } |
147 | #define SDL_DYNAPI_PROC_NO_VARARGS 1 |
148 | #include "SDL_dynapi_procs.h" |
149 | #undef SDL_DYNAPI_PROC |
150 | #undef SDL_DYNAPI_PROC_NO_VARARGS |
151 | SDL_DYNAPI_VARARGS(static, _DEFAULT, SDL_InitDynamicAPI()) |
152 | #else |
153 | /* !!! FIXME: need the jump magic. */ |
154 | #error Write me. |
155 | #endif |
156 | |
157 | /* Public API functions to jump into the jump table. */ |
158 | #if DISABLE_JUMP_MAGIC |
159 | #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) \ |
160 | rc SDLCALL fn params { ret jump_table.fn args; } |
161 | #define SDL_DYNAPI_PROC_NO_VARARGS 1 |
162 | #include "SDL_dynapi_procs.h" |
163 | #undef SDL_DYNAPI_PROC |
164 | #undef SDL_DYNAPI_PROC_NO_VARARGS |
165 | SDL_DYNAPI_VARARGS(,,) |
166 | #else |
167 | /* !!! FIXME: need the jump magic. */ |
168 | #error Write me. |
169 | #endif |
170 | |
171 | /* we make this a static function so we can call the correct one without the |
172 | system's dynamic linker resolving to the wrong version of this. */ |
173 | static Sint32 |
174 | initialize_jumptable(Uint32 apiver, void *table, Uint32 tablesize) |
175 | { |
176 | SDL_DYNAPI_jump_table *output_jump_table = (SDL_DYNAPI_jump_table *) table; |
177 | |
178 | if (apiver != SDL_DYNAPI_VERSION) { |
179 | /* !!! FIXME: can maybe handle older versions? */ |
180 | return -1; /* not compatible. */ |
181 | } else if (tablesize > sizeof (jump_table)) { |
182 | return -1; /* newer version of SDL with functions we can't provide. */ |
183 | } |
184 | |
185 | /* Init our jump table first. */ |
186 | #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) jump_table.fn = fn##_REAL; |
187 | #include "SDL_dynapi_procs.h" |
188 | #undef SDL_DYNAPI_PROC |
189 | |
190 | /* Then the external table... */ |
191 | if (output_jump_table != &jump_table) { |
192 | jump_table.SDL_memcpy(output_jump_table, &jump_table, tablesize); |
193 | } |
194 | |
195 | /* Safe to call SDL functions now; jump table is initialized! */ |
196 | |
197 | return 0; /* success! */ |
198 | } |
199 | |
200 | |
201 | /* Here's the exported entry point that fills in the jump table. */ |
202 | /* Use specific types when an "int" might suffice to keep this sane. */ |
203 | typedef Sint32 (SDLCALL *SDL_DYNAPI_ENTRYFN)(Uint32 apiver, void *table, Uint32 tablesize); |
204 | extern DECLSPEC Sint32 SDLCALL SDL_DYNAPI_entry(Uint32, void *, Uint32); |
205 | |
206 | Sint32 |
207 | SDL_DYNAPI_entry(Uint32 apiver, void *table, Uint32 tablesize) |
208 | { |
209 | return initialize_jumptable(apiver, table, tablesize); |
210 | } |
211 | |
212 | |
213 | /* Obviously we can't use SDL_LoadObject() to load SDL. :) */ |
214 | /* Also obviously, we never close the loaded library. */ |
215 | #if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) |
216 | #ifndef WIN32_LEAN_AND_MEAN |
217 | #define WIN32_LEAN_AND_MEAN 1 |
218 | #endif |
219 | #include <windows.h> |
220 | static SDL_INLINE void *get_sdlapi_entry(const char *fname, const char *sym) |
221 | { |
222 | HANDLE lib = LoadLibraryA(fname); |
223 | void *retval = NULL; |
224 | if (lib) { |
225 | retval = GetProcAddress(lib, sym); |
226 | if (retval == NULL) { |
227 | FreeLibrary(lib); |
228 | } |
229 | } |
230 | return retval; |
231 | } |
232 | |
233 | #elif defined(unix) || defined(__unix__) || defined(__APPLE__) || defined(__HAIKU__) || defined(__QNX__) |
234 | #include <dlfcn.h> |
235 | static SDL_INLINE void *get_sdlapi_entry(const char *fname, const char *sym) |
236 | { |
237 | void *lib = dlopen(fname, RTLD_NOW | RTLD_LOCAL); |
238 | void *retval = NULL; |
239 | if (lib != NULL) { |
240 | retval = dlsym(lib, sym); |
241 | if (retval == NULL) { |
242 | dlclose(lib); |
243 | } |
244 | } |
245 | return retval; |
246 | } |
247 | |
248 | #elif defined(__OS2__) |
249 | static SDL_INLINE void *get_sdlapi_entry(const char *fname, const char *sym) |
250 | { |
251 | HMODULE hmodule; |
252 | PFN retval = NULL; |
253 | char error[256]; |
254 | if (DosLoadModule(error, sizeof(error), fname, &hmodule) == NO_ERROR) { |
255 | if (DosQueryProcAddr(hmodule, 0, sym, &retval) != NO_ERROR) { |
256 | DosFreeModule(hmodule); |
257 | } |
258 | } |
259 | return (void *)retval; |
260 | } |
261 | |
262 | #else |
263 | #error Please define your platform. |
264 | #endif |
265 | |
266 | |
267 | static void dynapi_warn(const char *msg) |
268 | { |
269 | const char *caption = "SDL Dynamic API Failure!" ; |
270 | /* SDL_ShowSimpleMessageBox() is a too heavy for here. */ |
271 | #if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) |
272 | MessageBoxA(NULL, msg, caption, MB_OK | MB_ICONERROR); |
273 | #else |
274 | fprintf(stderr, "\n\n%s\n%s\n\n" , caption, msg); |
275 | fflush(stderr); |
276 | #endif |
277 | } |
278 | |
279 | /* This is not declared in any header, although it is shared between some |
280 | parts of SDL, because we don't want anything calling it without an |
281 | extremely good reason. */ |
282 | #if defined(__WATCOMC__) |
283 | void SDL_ExitProcess(int exitcode); |
284 | #pragma aux SDL_ExitProcess aborts; |
285 | #endif |
286 | SDL_NORETURN void SDL_ExitProcess(int exitcode); |
287 | |
288 | |
289 | static void |
290 | SDL_InitDynamicAPILocked(void) |
291 | { |
292 | const char *libname = SDL_getenv_REAL("SDL_DYNAMIC_API" ); |
293 | SDL_DYNAPI_ENTRYFN entry = NULL; /* funcs from here by default. */ |
294 | SDL_bool use_internal = SDL_TRUE; |
295 | |
296 | if (libname) { |
297 | entry = (SDL_DYNAPI_ENTRYFN) get_sdlapi_entry(libname, "SDL_DYNAPI_entry" ); |
298 | if (!entry) { |
299 | dynapi_warn("Couldn't load overriding SDL library. Please fix or remove the SDL_DYNAMIC_API environment variable. Using the default SDL." ); |
300 | /* Just fill in the function pointers from this library, later. */ |
301 | } |
302 | } |
303 | |
304 | if (entry) { |
305 | if (entry(SDL_DYNAPI_VERSION, &jump_table, sizeof (jump_table)) < 0) { |
306 | dynapi_warn("Couldn't override SDL library. Using a newer SDL build might help. Please fix or remove the SDL_DYNAMIC_API environment variable. Using the default SDL." ); |
307 | /* Just fill in the function pointers from this library, later. */ |
308 | } else { |
309 | use_internal = SDL_FALSE; /* We overrode SDL! Don't use the internal version! */ |
310 | } |
311 | } |
312 | |
313 | /* Just fill in the function pointers from this library. */ |
314 | if (use_internal) { |
315 | if (initialize_jumptable(SDL_DYNAPI_VERSION, &jump_table, sizeof (jump_table)) < 0) { |
316 | /* Now we're screwed. Should definitely abort now. */ |
317 | dynapi_warn("Failed to initialize internal SDL dynapi. As this would otherwise crash, we have to abort now." ); |
318 | SDL_ExitProcess(86); |
319 | } |
320 | } |
321 | |
322 | /* we intentionally never close the newly-loaded lib, of course. */ |
323 | } |
324 | |
325 | static void |
326 | SDL_InitDynamicAPI(void) |
327 | { |
328 | /* So the theory is that every function in the jump table defaults to |
329 | * calling this function, and then replaces itself with a version that |
330 | * doesn't call this function anymore. But it's possible that, in an |
331 | * extreme corner case, you can have a second thread hit this function |
332 | * while the jump table is being initialized by the first. |
333 | * In this case, a spinlock is really painful compared to what spinlocks |
334 | * _should_ be used for, but this would only happen once, and should be |
335 | * insanely rare, as you would have to spin a thread outside of SDL (as |
336 | * SDL_CreateThread() would also call this function before building the |
337 | * new thread). |
338 | */ |
339 | static SDL_bool already_initialized = SDL_FALSE; |
340 | |
341 | /* SDL_AtomicLock calls SDL mutex functions to emulate if |
342 | SDL_ATOMIC_DISABLED, which we can't do here, so in such a |
343 | configuration, you're on your own. */ |
344 | #if !SDL_ATOMIC_DISABLED |
345 | static SDL_SpinLock lock = 0; |
346 | SDL_AtomicLock_REAL(&lock); |
347 | #endif |
348 | |
349 | if (!already_initialized) { |
350 | SDL_InitDynamicAPILocked(); |
351 | already_initialized = SDL_TRUE; |
352 | } |
353 | |
354 | #if !SDL_ATOMIC_DISABLED |
355 | SDL_AtomicUnlock_REAL(&lock); |
356 | #endif |
357 | } |
358 | |
359 | #endif /* SDL_DYNAMIC_API */ |
360 | |
361 | /* vi: set ts=4 sw=4 expandtab: */ |
362 | |