src/dynapi/SDL_dynapi.c
changeset 8094 9efaae827924
child 8095 15b248f1e5a0
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/dynapi/SDL_dynapi.c	Mon Dec 09 16:03:18 2013 -0500
     1.3 @@ -0,0 +1,302 @@
     1.4 +/*
     1.5 +  Simple DirectMedia Layer
     1.6 +  Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
     1.7 +
     1.8 +  This software is provided 'as-is', without any express or implied
     1.9 +  warranty.  In no event will the authors be held liable for any damages
    1.10 +  arising from the use of this software.
    1.11 +
    1.12 +  Permission is granted to anyone to use this software for any purpose,
    1.13 +  including commercial applications, and to alter it and redistribute it
    1.14 +  freely, subject to the following restrictions:
    1.15 +
    1.16 +  1. The origin of this software must not be misrepresented; you must not
    1.17 +     claim that you wrote the original software. If you use this software
    1.18 +     in a product, an acknowledgment in the product documentation would be
    1.19 +     appreciated but is not required.
    1.20 +  2. Altered source versions must be plainly marked as such, and must not be
    1.21 +     misrepresented as being the original software.
    1.22 +  3. This notice may not be removed or altered from any source distribution.
    1.23 +*/
    1.24 +
    1.25 +#include "SDL_config.h"
    1.26 +#include "SDL_dynapi.h"
    1.27 +
    1.28 +#if SDL_DYNAMIC_API
    1.29 +
    1.30 +#include "SDL.h"
    1.31 +
    1.32 +/* !!! FIXME: Shouldn't these be included in SDL.h? */
    1.33 +#include "SDL_shape.h"
    1.34 +#include "SDL_syswm.h"
    1.35 +
    1.36 +/* This is the version of the dynamic API. This doesn't match the SDL version
    1.37 +   and should not change until there's been a major revamp in API/ABI.
    1.38 +   So 2.0.5 adds functions over 2.0.4? This number doesn't change;
    1.39 +   the sizeof (jump_table) changes instead. But 2.1.0 changes how a function
    1.40 +   works in an incompatible way or removes a function? This number changes,
    1.41 +   since sizeof (jump_table) isn't sufficient anymore. It's likely
    1.42 +   we'll forget to bump every time we add a function, so this is the
    1.43 +   failsafe switch for major API change decisions. Respect it and use it
    1.44 +   sparingly. */
    1.45 +#define SDL_DYNAPI_VERSION 1
    1.46 +
    1.47 +static void SDL_InitDynamicAPI(void);
    1.48 +
    1.49 +
    1.50 +/* BE CAREFUL CALLING ANY SDL CODE IN HERE, IT WILL BLOW UP.
    1.51 +   Even self-contained stuff might call SDL_Error and break everything. */
    1.52 +
    1.53 +
    1.54 +/* behold, the macro salsa! */
    1.55 +
    1.56 +/* !!! FIXME: ...disabled...until we write it.  :) */
    1.57 +#define DISABLE_JUMP_MAGIC 1
    1.58 +
    1.59 +#if DISABLE_JUMP_MAGIC
    1.60 +/* Can't use the macro for varargs nonsense. This is atrocious. */
    1.61 +#define SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, logname, prio) \
    1.62 +    _static void SDL_Log##logname##name(int category, const char *fmt, ...) { \
    1.63 +        va_list ap; initcall; va_start(ap, fmt); \
    1.64 +        jump_table.SDL_LogMessageV(category, SDL_LOG_PRIORITY_##prio, fmt, ap); \
    1.65 +        va_end(ap); \
    1.66 +    }
    1.67 +
    1.68 +#define SDL_DYNAPI_VARARGS(_static, name, initcall) \
    1.69 +    _static int SDL_SetError##name(const char *fmt, ...) { \
    1.70 +        char buf[512]; /* !!! FIXME: dynamic allocation */ \
    1.71 +        va_list ap; initcall; va_start(ap, fmt); \
    1.72 +        jump_table.SDL_vsnprintf(buf, sizeof (buf), fmt, ap); \
    1.73 +        va_end(ap); \
    1.74 +        return jump_table.SDL_SetError("%s", buf); \
    1.75 +    } \
    1.76 +    _static int SDL_sscanf##name(const char *buf, const char *fmt, ...) { \
    1.77 +        int retval; va_list ap; initcall; va_start(ap, fmt); \
    1.78 +        retval = jump_table.SDL_vsscanf(buf, fmt, ap); \
    1.79 +        va_end(ap); \
    1.80 +        return retval; \
    1.81 +    } \
    1.82 +    _static int SDL_snprintf##name(char *buf, size_t buflen, const char *fmt, ...) { \
    1.83 +        int retval; va_list ap; initcall; va_start(ap, fmt); \
    1.84 +        retval = jump_table.SDL_vsnprintf(buf, buflen, fmt, ap); \
    1.85 +        va_end(ap); \
    1.86 +        return retval; \
    1.87 +    } \
    1.88 +    _static void SDL_Log##name(const char *fmt, ...) { \
    1.89 +        va_list ap; initcall; va_start(ap, fmt); \
    1.90 +        jump_table.SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap); \
    1.91 +        va_end(ap); \
    1.92 +    } \
    1.93 +    _static void SDL_LogMessage##name(int category, SDL_LogPriority priority, const char *fmt, ...) { \
    1.94 +        va_list ap; initcall; va_start(ap, fmt); \
    1.95 +        jump_table.SDL_LogMessageV(category, priority, fmt, ap); \
    1.96 +        va_end(ap); \
    1.97 +    } \
    1.98 +    SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Verbose, VERBOSE) \
    1.99 +    SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Debug, DEBUG) \
   1.100 +    SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Info, INFO) \
   1.101 +    SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Warn, WARN) \
   1.102 +    SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Error, ERROR) \
   1.103 +    SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Critical, CRITICAL)
   1.104 +#endif
   1.105 +
   1.106 +
   1.107 +/* Typedefs for function pointers for jump table, and predeclare funcs */
   1.108 +/* The DEFAULT funcs will init jump table and then call real function. */
   1.109 +/* The REAL funcs are the actual functions, name-mangled to not clash. */
   1.110 +#define SDL_DYNAPI_PROC(rc,fn,params,args,ret) \
   1.111 +    typedef rc (*SDL_DYNAPIFN_##fn) params; \
   1.112 +    static rc fn##_DEFAULT params; \
   1.113 +    extern rc fn##_REAL params;
   1.114 +#include "SDL_dynapi_procs.h"
   1.115 +#undef SDL_DYNAPI_PROC
   1.116 +
   1.117 +/* The jump table! */
   1.118 +typedef struct {
   1.119 +    #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) SDL_DYNAPIFN_##fn fn;
   1.120 +    #include "SDL_dynapi_procs.h"
   1.121 +    #undef SDL_DYNAPI_PROC
   1.122 +} SDL_DYNAPI_jump_table;
   1.123 +
   1.124 +/* Predeclare the default functions for initializing the jump table. */
   1.125 +#define SDL_DYNAPI_PROC(rc,fn,params,args,ret) static rc fn##_DEFAULT params;
   1.126 +#include "SDL_dynapi_procs.h"
   1.127 +#undef SDL_DYNAPI_PROC
   1.128 +
   1.129 +/* The actual jump table. */
   1.130 +static SDL_DYNAPI_jump_table jump_table = {
   1.131 +    #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) fn##_DEFAULT,
   1.132 +    #include "SDL_dynapi_procs.h"
   1.133 +    #undef SDL_DYNAPI_PROC
   1.134 +};
   1.135 +
   1.136 +/* Default functions init the function table then call right thing. */
   1.137 +#if DISABLE_JUMP_MAGIC
   1.138 +#define SDL_DYNAPI_PROC(rc,fn,params,args,ret) \
   1.139 +    static rc fn##_DEFAULT params { \
   1.140 +        SDL_InitDynamicAPI(); \
   1.141 +        ret jump_table.fn args; \
   1.142 +    }
   1.143 +#define SDL_DYNAPI_PROC_NO_VARARGS 1
   1.144 +#include "SDL_dynapi_procs.h"
   1.145 +#undef SDL_DYNAPI_PROC
   1.146 +#undef SDL_DYNAPI_PROC_NO_VARARGS
   1.147 +SDL_DYNAPI_VARARGS(static, _DEFAULT, SDL_InitDynamicAPI())
   1.148 +#else
   1.149 +/* !!! FIXME: need the jump magic. */
   1.150 +#error Write me.
   1.151 +#endif
   1.152 +
   1.153 +/* Public API functions to jump into the jump table. */
   1.154 +#if DISABLE_JUMP_MAGIC
   1.155 +#define SDL_DYNAPI_PROC(rc,fn,params,args,ret) \
   1.156 +    rc fn params { ret jump_table.fn args; }
   1.157 +#define SDL_DYNAPI_PROC_NO_VARARGS 1
   1.158 +#include "SDL_dynapi_procs.h"
   1.159 +#undef SDL_DYNAPI_PROC
   1.160 +#undef SDL_DYNAPI_PROC_NO_VARARGS
   1.161 +SDL_DYNAPI_VARARGS(,,)
   1.162 +#else
   1.163 +/* !!! FIXME: need the jump magic. */
   1.164 +#error Write me.
   1.165 +#endif
   1.166 +
   1.167 +
   1.168 +
   1.169 +/* Here's the exported entry point that fills in the jump table. */
   1.170 +/*  Use specific types when an "int" might suffice to keep this sane. */
   1.171 +typedef Sint32 (SDLCALL *SDL_DYNAPI_ENTRYFN)(Uint32 apiver, void *table, Uint32 tablesize);
   1.172 +extern DECLSPEC Sint32 SDLCALL SDL_DYNAPI_entry(Uint32, void *, Uint32);
   1.173 +
   1.174 +Sint32
   1.175 +SDL_DYNAPI_entry(Uint32 apiver, void *table, Uint32 tablesize)
   1.176 +{
   1.177 +    SDL_DYNAPI_jump_table *output_jump_table = (SDL_DYNAPI_jump_table *) table;
   1.178 +
   1.179 +    if (apiver != SDL_DYNAPI_VERSION) {
   1.180 +        /* !!! FIXME: can maybe handle older versions? */
   1.181 +        return -1;  /* not compatible. */
   1.182 +    } else if (tablesize > sizeof (jump_table)) {
   1.183 +        return -1;  /* newer version of SDL with functions we can't provide. */
   1.184 +    }
   1.185 +
   1.186 +    /* Init our jump table first. */
   1.187 +    #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) jump_table.fn = fn##_REAL;
   1.188 +    #include "SDL_dynapi_procs.h"
   1.189 +    #undef SDL_DYNAPI_PROC
   1.190 +
   1.191 +    /* Then the external table... */
   1.192 +    if (output_jump_table != &jump_table) {
   1.193 +        jump_table.SDL_memcpy(output_jump_table, &jump_table, tablesize);
   1.194 +    }
   1.195 +
   1.196 +    /* Safe to call SDL functions now; jump table is initialized! */
   1.197 +
   1.198 +    return 0;  /* success! */
   1.199 +}
   1.200 +
   1.201 +
   1.202 +/* Obviously we can't use SDL_LoadObject() to load SDL.  :)  */
   1.203 +/* Also obviously, we never close the loaded library. */
   1.204 +#if defined(_WINDOWS)
   1.205 +#ifndef WIN32_LEAN_AND_MEAN
   1.206 +#define WIN32_LEAN_AND_MEAN 1
   1.207 +#endif
   1.208 +#include <windows.h>
   1.209 +static SDL_INLINE void *get_sdlapi_entry(const char *fname, const char *sym)
   1.210 +{
   1.211 +    HANDLE lib = LoadLibraryA(fname);
   1.212 +    return lib ? GetProcAddress(lib, sym) : NULL;
   1.213 +}
   1.214 +
   1.215 +#elif defined(__HAIKU__)
   1.216 +#include <os/kernel/image.h>
   1.217 +static SDL_INLINE void *get_sdlapi_entry(const char *fname, const char *sym)
   1.218 +{
   1.219 +    image_id lib = load_add_on(fname);
   1.220 +    void *retval = NULL;
   1.221 +    if ((lib < 0) || (get_image_symbol(lib, sym, B_SYMBOL_TYPE_TEXT, &retval) != B_NO_ERROR)) {
   1.222 +        retval = NULL;
   1.223 +    }
   1.224 +    return retval;
   1.225 +}
   1.226 +#elif defined(unix) || defined(__APPLE__)
   1.227 +#include <dlfcn.h>
   1.228 +static SDL_INLINE void *get_sdlapi_entry(const char *fname, const char *sym)
   1.229 +{
   1.230 +    void *lib = dlopen(fname, RTLD_NOW | RTLD_LOCAL);
   1.231 +    return lib ? dlsym(lib, sym) : NULL;
   1.232 +}
   1.233 +#else
   1.234 +#error Please define your platform.
   1.235 +#endif
   1.236 +
   1.237 +
   1.238 +static void
   1.239 +SDL_InitDynamicAPILocked(void)
   1.240 +{
   1.241 +    const char *libname = SDL_getenv_REAL("SDL_DYNAMIC_API");
   1.242 +    SDL_DYNAPI_ENTRYFN entry = SDL_DYNAPI_entry;  /* funcs from here by default. */
   1.243 +
   1.244 +    if (libname) {
   1.245 +        entry = (SDL_DYNAPI_ENTRYFN) get_sdlapi_entry(libname, "SDL_DYNAPI_entry");
   1.246 +        if (!entry) {
   1.247 +            /* !!! FIXME: fail to startup here instead? */
   1.248 +            /* !!! FIXME: definitely warn user. */
   1.249 +            /* Just fill in the function pointers from this library. */
   1.250 +            entry = SDL_DYNAPI_entry;
   1.251 +        }
   1.252 +    }
   1.253 +
   1.254 +    if (entry(SDL_DYNAPI_VERSION, &jump_table, sizeof (jump_table)) < 0) {
   1.255 +        /* !!! FIXME: fail to startup here instead? */
   1.256 +        /* !!! FIXME: definitely warn user. */
   1.257 +        /* Just fill in the function pointers from this library. */
   1.258 +        if (entry != SDL_DYNAPI_entry) {
   1.259 +            if (!SDL_DYNAPI_entry(SDL_DYNAPI_VERSION, &jump_table, sizeof (jump_table))) {
   1.260 +                /* !!! FIXME: now we're screwed. Should definitely abort now. */
   1.261 +            }
   1.262 +        }
   1.263 +    }
   1.264 +
   1.265 +    /* we intentionally never close the newly-loaded lib, of course. */
   1.266 +}
   1.267 +
   1.268 +static void
   1.269 +SDL_InitDynamicAPI(void)
   1.270 +{
   1.271 +    /* So the theory is that every function in the jump table defaults to
   1.272 +     *  calling this function, and then replaces itself with a version that
   1.273 +     *  doesn't call this function anymore. But it's possible that, in an
   1.274 +     *  extreme corner case, you can have a second thread hit this function
   1.275 +     *  while the jump table is being initialized by the first.
   1.276 +     * In this case, a spinlock is really painful compared to what spinlocks
   1.277 +     *  _should_ be used for, but this would only happen once, and should be
   1.278 +     *  insanely rare, as you would have to spin a thread outside of SDL (as
   1.279 +     *  SDL_CreateThread() would also call this function before building the
   1.280 +     *  new thread).
   1.281 +     */
   1.282 +    static volatile SDL_bool already_initialized = SDL_FALSE;
   1.283 +
   1.284 +    /* SDL_AtomicLock calls SDL mutex functions to emulate if
   1.285 +       SDL_ATOMIC_DISABLED, which we can't do here, so in such a
   1.286 +       configuration, you're on your own. */
   1.287 +    #if !SDL_ATOMIC_DISABLED
   1.288 +    static SDL_SpinLock lock = 0;
   1.289 +    SDL_AtomicLock_REAL(&lock);
   1.290 +    #endif
   1.291 +
   1.292 +    if (!already_initialized) {
   1.293 +        SDL_InitDynamicAPILocked();
   1.294 +        already_initialized = SDL_TRUE;
   1.295 +    }
   1.296 +
   1.297 +    #if !SDL_ATOMIC_DISABLED
   1.298 +    SDL_AtomicUnlock_REAL(&lock);
   1.299 +    #endif
   1.300 +}
   1.301 +
   1.302 +#endif  /* SDL_DYNAMIC_API */
   1.303 +
   1.304 +/* vi: set ts=4 sw=4 expandtab: */
   1.305 +