1e52adb7bSmrg/* 2e52adb7bSmrg * Copyright © 2013-2014 Intel Corporation 3e52adb7bSmrg * 4e52adb7bSmrg * Permission is hereby granted, free of charge, to any person obtaining a 5e52adb7bSmrg * copy of this software and associated documentation files (the "Software"), 6e52adb7bSmrg * to deal in the Software without restriction, including without limitation 7e52adb7bSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8e52adb7bSmrg * and/or sell copies of the Software, and to permit persons to whom the 9e52adb7bSmrg * Software is furnished to do so, subject to the following conditions: 10e52adb7bSmrg * 11e52adb7bSmrg * The above copyright notice and this permission notice (including the next 12e52adb7bSmrg * paragraph) shall be included in all copies or substantial portions of the 13e52adb7bSmrg * Software. 14e52adb7bSmrg * 15e52adb7bSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16e52adb7bSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17e52adb7bSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18e52adb7bSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19e52adb7bSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20e52adb7bSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21e52adb7bSmrg * IN THE SOFTWARE. 22e52adb7bSmrg */ 23e52adb7bSmrg 24de84f9a0Smrg/** 25de84f9a0Smrg * \mainpage Epoxy 26de84f9a0Smrg * 27de84f9a0Smrg * \section intro_sec Introduction 28de84f9a0Smrg * 29de84f9a0Smrg * Epoxy is a library for handling OpenGL function pointer management for 30de84f9a0Smrg * you. 31de84f9a0Smrg * 32de84f9a0Smrg * It hides the complexity of `dlopen()`, `dlsym()`, `glXGetProcAddress()`, 33de84f9a0Smrg * `eglGetProcAddress()`, etc. from the app developer, with very little 34de84f9a0Smrg * knowledge needed on their part. They get to read GL specs and write 35de84f9a0Smrg * code using undecorated function names like `glCompileShader()`. 36de84f9a0Smrg * 37de84f9a0Smrg * Don't forget to check for your extensions or versions being present 38de84f9a0Smrg * before you use them, just like before! We'll tell you what you forgot 39de84f9a0Smrg * to check for instead of just segfaulting, though. 40de84f9a0Smrg * 41de84f9a0Smrg * \section features_sec Features 42de84f9a0Smrg * 43de84f9a0Smrg * - Automatically initializes as new GL functions are used. 44b94deca5Smrg * - GL 4.6 core and compatibility context support. 45de84f9a0Smrg * - GLES 1/2/3 context support. 46de84f9a0Smrg * - Knows about function aliases so (e.g.) `glBufferData()` can be 47de84f9a0Smrg * used with `GL_ARB_vertex_buffer_object` implementations, along 48de84f9a0Smrg * with GL 1.5+ implementations. 49de84f9a0Smrg * - EGL, GLX, and WGL support. 50de84f9a0Smrg * - Can be mixed with non-epoxy GL usage. 51de84f9a0Smrg * 52de84f9a0Smrg * \section using_sec Using Epoxy 53de84f9a0Smrg * 54de84f9a0Smrg * Using Epoxy should be as easy as replacing: 55de84f9a0Smrg * 56de84f9a0Smrg * ```cpp 57de84f9a0Smrg * #include <GL/gl.h> 58de84f9a0Smrg * #include <GL/glx.h> 59de84f9a0Smrg * #include <GL/glext.h> 60de84f9a0Smrg * ``` 61de84f9a0Smrg * 62de84f9a0Smrg * with: 63de84f9a0Smrg * 64de84f9a0Smrg * ```cpp 65de84f9a0Smrg * #include <epoxy/gl.h> 66de84f9a0Smrg * #include <epoxy/glx.h> 67de84f9a0Smrg * ``` 68de84f9a0Smrg * 69de84f9a0Smrg * \subsection using_include_sec Headers 70de84f9a0Smrg * 71de84f9a0Smrg * Epoxy comes with the following public headers: 72de84f9a0Smrg * 73de84f9a0Smrg * - `epoxy/gl.h` - For GL API 74de84f9a0Smrg * - `epoxy/egl.h` - For EGL API 75de84f9a0Smrg * - `epoxy/glx.h` - For GLX API 76de84f9a0Smrg * - `epoxy/wgl.h` - For WGL API 77de84f9a0Smrg * 78de84f9a0Smrg * \section links_sec Additional links 79de84f9a0Smrg * 80de84f9a0Smrg * The latest version of the Epoxy code is available on [GitHub](https://github.com/anholt/libepoxy). 81de84f9a0Smrg * 82de84f9a0Smrg * For bug reports and enhancements, please use the [Issues](https://github.com/anholt/libepoxy/issues) 83de84f9a0Smrg * link. 84de84f9a0Smrg * 85de84f9a0Smrg * The scope of this API reference does not include the documentation for 86de84f9a0Smrg * OpenGL and OpenGL ES. For more information on those programming interfaces 87de84f9a0Smrg * please visit: 88de84f9a0Smrg * 89de84f9a0Smrg * - [Khronos](https://www.khronos.org/) 90de84f9a0Smrg * - [OpenGL page on Khronos.org](https://www.khronos.org/opengl/) 91de84f9a0Smrg * - [OpenGL ES page on Khronos.org](https://www.khronos.org/opengles/) 92de84f9a0Smrg * - [docs.GL](http://docs.gl/) 93de84f9a0Smrg */ 94de84f9a0Smrg 95e52adb7bSmrg/** 96e52adb7bSmrg * @file dispatch_common.c 97e52adb7bSmrg * 98de84f9a0Smrg * @brief Implements common code shared by the generated GL/EGL/GLX dispatch code. 99e52adb7bSmrg * 100e52adb7bSmrg * A collection of some important specs on getting GL function pointers. 101e52adb7bSmrg * 102e52adb7bSmrg * From the linux GL ABI (http://www.opengl.org/registry/ABI/): 103e52adb7bSmrg * 104e52adb7bSmrg * "3.4. The libraries must export all OpenGL 1.2, GLU 1.3, GLX 1.3, and 105e52adb7bSmrg * ARB_multitexture entry points statically. 106e52adb7bSmrg * 107e52adb7bSmrg * 3.5. Because non-ARB extensions vary so widely and are constantly 108e52adb7bSmrg * increasing in number, it's infeasible to require that they all be 109e52adb7bSmrg * supported, and extensions can always be added to hardware drivers 110e52adb7bSmrg * after the base link libraries are released. These drivers are 111e52adb7bSmrg * dynamically loaded by libGL, so extensions not in the base 112e52adb7bSmrg * library must also be obtained dynamically. 113e52adb7bSmrg * 114e52adb7bSmrg * 3.6. To perform the dynamic query, libGL also must export an entry 115e52adb7bSmrg * point called 116e52adb7bSmrg * 117e52adb7bSmrg * void (*glXGetProcAddressARB(const GLubyte *))(); 118e52adb7bSmrg * 119e52adb7bSmrg * The full specification of this function is available separately. It 120e52adb7bSmrg * takes the string name of a GL or GLX entry point and returns a pointer 121e52adb7bSmrg * to a function implementing that entry point. It is functionally 122e52adb7bSmrg * identical to the wglGetProcAddress query defined by the Windows OpenGL 123e52adb7bSmrg * library, except that the function pointers returned are context 124e52adb7bSmrg * independent, unlike the WGL query." 125e52adb7bSmrg * 126e52adb7bSmrg * From the EGL 1.4 spec: 127e52adb7bSmrg * 128e52adb7bSmrg * "Client API function pointers returned by eglGetProcAddress are 129e52adb7bSmrg * independent of the display and the currently bound client API context, 130e52adb7bSmrg * and may be used by any client API context which supports the extension. 131e52adb7bSmrg * 132e52adb7bSmrg * eglGetProcAddress may be queried for all of the following functions: 133e52adb7bSmrg * 134e52adb7bSmrg * • All EGL and client API extension functions supported by the 135e52adb7bSmrg * implementation (whether those extensions are supported by the current 136e52adb7bSmrg * client API context or not). This includes any mandatory OpenGL ES 137e52adb7bSmrg * extensions. 138e52adb7bSmrg * 139e52adb7bSmrg * eglGetProcAddress may not be queried for core (non-extension) functions 140e52adb7bSmrg * in EGL or client APIs 20 . 141e52adb7bSmrg * 142e52adb7bSmrg * For functions that are queryable with eglGetProcAddress, 143e52adb7bSmrg * implementations may choose to also export those functions statically 144e52adb7bSmrg * from the object libraries im- plementing those functions. However, 145e52adb7bSmrg * portable clients cannot rely on this behavior. 146e52adb7bSmrg * 147e52adb7bSmrg * From the GLX 1.4 spec: 148e52adb7bSmrg * 149e52adb7bSmrg * "glXGetProcAddress may be queried for all of the following functions: 150e52adb7bSmrg * 151e52adb7bSmrg * • All GL and GLX extension functions supported by the implementation 152e52adb7bSmrg * (whether those extensions are supported by the current context or 153e52adb7bSmrg * not). 154e52adb7bSmrg * 155e52adb7bSmrg * • All core (non-extension) functions in GL and GLX from version 1.0 up 156e52adb7bSmrg * to and including the versions of those specifications supported by 157e52adb7bSmrg * the implementation, as determined by glGetString(GL VERSION) and 158e52adb7bSmrg * glXQueryVersion queries." 159e52adb7bSmrg */ 160e52adb7bSmrg 161e52adb7bSmrg#include <assert.h> 162e52adb7bSmrg#include <stdlib.h> 163e52adb7bSmrg#ifdef _WIN32 164e52adb7bSmrg#include <windows.h> 165e52adb7bSmrg#else 166e52adb7bSmrg#include <dlfcn.h> 167e52adb7bSmrg#include <err.h> 168e52adb7bSmrg#include <pthread.h> 169e52adb7bSmrg#endif 170e52adb7bSmrg#include <string.h> 171e52adb7bSmrg#include <ctype.h> 172e52adb7bSmrg#include <stdio.h> 173e52adb7bSmrg 174e52adb7bSmrg#include "dispatch_common.h" 175e52adb7bSmrg 176b94deca5Smrg#if defined(__APPLE__) 177e52adb7bSmrg#define GLX_LIB "/opt/X11/lib/libGL.1.dylib" 178b94deca5Smrg#define OPENGL_LIB "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL" 179b94deca5Smrg#define GLES1_LIB "libGLESv1_CM.so" 180b94deca5Smrg#define GLES2_LIB "libGLESv2.so" 181b94deca5Smrg#elif defined(__ANDROID__) 182e52adb7bSmrg#define GLX_LIB "libGLESv2.so" 183b94deca5Smrg#elif __NetBSD__ 184b795ef6aSmrg#define GLX_LIB "libGL.so" 185b795ef6aSmrg#endif 186e52adb7bSmrg 187b795ef6aSmrg#if defined(ANDROID) || defined(__NetBSD__) 188e52adb7bSmrg#define EGL_LIB "libEGL.so" 189e52adb7bSmrg#define GLES1_LIB "libGLESv1_CM.so" 190e52adb7bSmrg#define GLES2_LIB "libGLESv2.so" 191b94deca5Smrg#elif defined(_WIN32) 192de84f9a0Smrg#define EGL_LIB "libEGL.dll" 193de84f9a0Smrg#define GLES1_LIB "libGLES_CM.dll" 194de84f9a0Smrg#define GLES2_LIB "libGLESv2.dll" 195b94deca5Smrg#define OPENGL_LIB "OPENGL32" 196e52adb7bSmrg#else 197b94deca5Smrg#define GLVND_GLX_LIB "libGLX.so.1" 198b94deca5Smrg#define GLX_LIB "libGL.so.1" 199e52adb7bSmrg#define EGL_LIB "libEGL.so.1" 200e52adb7bSmrg#define GLES1_LIB "libGLESv1_CM.so.1" 201e52adb7bSmrg#define GLES2_LIB "libGLESv2.so.2" 202b94deca5Smrg#define OPENGL_LIB "libOpenGL.so.0" 203e52adb7bSmrg#endif 204e52adb7bSmrg 205e52adb7bSmrg#ifdef __GNUC__ 206e52adb7bSmrg#define CONSTRUCT(_func) static void _func (void) __attribute__((constructor)); 207e52adb7bSmrg#define DESTRUCT(_func) static void _func (void) __attribute__((destructor)); 208e52adb7bSmrg#elif defined (_MSC_VER) && (_MSC_VER >= 1500) 209e52adb7bSmrg#define CONSTRUCT(_func) \ 210e52adb7bSmrg static void _func(void); \ 211e52adb7bSmrg static int _func ## _wrapper(void) { _func(); return 0; } \ 212e52adb7bSmrg __pragma(section(".CRT$XCU",read)) \ 213e52adb7bSmrg __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _wrapper; 214e52adb7bSmrg 215e52adb7bSmrg#define DESTRUCT(_func) \ 216e52adb7bSmrg static void _func(void); \ 217e52adb7bSmrg static int _func ## _constructor(void) { atexit (_func); return 0; } \ 218e52adb7bSmrg __pragma(section(".CRT$XCU",read)) \ 219e52adb7bSmrg __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor; 220e52adb7bSmrg 22182f995d3Schristos#elif defined(__lint__) 22282f995d3Schristos#define CONSTRUCT(_func) 22382f995d3Schristos#define DESTRUCT(_func) 224e52adb7bSmrg#else 225e52adb7bSmrg#error "You will need constructor support for your compiler" 226e52adb7bSmrg#endif 227e52adb7bSmrg 228e52adb7bSmrgstruct api { 229e52adb7bSmrg#ifndef _WIN32 230de84f9a0Smrg /* 231e52adb7bSmrg * Locking for making sure we don't double-dlopen(). 232e52adb7bSmrg */ 233e52adb7bSmrg pthread_mutex_t mutex; 234e52adb7bSmrg#endif 235e52adb7bSmrg 236b94deca5Smrg /* 237b94deca5Smrg * dlopen() return value for the GLX API. This is libGLX.so.1 if the 238b94deca5Smrg * runtime is glvnd-enabled, else libGL.so.1 239b94deca5Smrg */ 240e52adb7bSmrg void *glx_handle; 241e52adb7bSmrg 242de84f9a0Smrg /* 243b94deca5Smrg * dlopen() return value for the desktop GL library. 244e52adb7bSmrg * 245b94deca5Smrg * On Windows this is OPENGL32. On OSX this is classic libGL. On Linux 246b94deca5Smrg * this is either libOpenGL (if the runtime is glvnd-enabled) or 247b94deca5Smrg * classic libGL.so.1 248e52adb7bSmrg */ 249e52adb7bSmrg void *gl_handle; 250e52adb7bSmrg 251de84f9a0Smrg /* dlopen() return value for libEGL.so.1 */ 252e52adb7bSmrg void *egl_handle; 253e52adb7bSmrg 254de84f9a0Smrg /* dlopen() return value for libGLESv1_CM.so.1 */ 255e52adb7bSmrg void *gles1_handle; 256e52adb7bSmrg 257de84f9a0Smrg /* dlopen() return value for libGLESv2.so.2 */ 258e52adb7bSmrg void *gles2_handle; 259e52adb7bSmrg 260de84f9a0Smrg /* 261e52adb7bSmrg * This value gets incremented when any thread is in 262e52adb7bSmrg * glBegin()/glEnd() called through epoxy. 263e52adb7bSmrg * 264e52adb7bSmrg * We're not guaranteed to be called through our wrapper, so the 265e52adb7bSmrg * conservative paths also try to handle the failure cases they'll 266e52adb7bSmrg * see if begin_count didn't reflect reality. It's also a bit of 267e52adb7bSmrg * a bug that the conservative paths might return success because 268e52adb7bSmrg * some other thread was in epoxy glBegin/glEnd while our thread 269e52adb7bSmrg * is trying to resolve, but given that it's basically just for 270e52adb7bSmrg * informative error messages, we shouldn't need to care. 271e52adb7bSmrg */ 272e52adb7bSmrg long begin_count; 273e52adb7bSmrg}; 274e52adb7bSmrg 275e52adb7bSmrgstatic struct api api = { 276e52adb7bSmrg#ifndef _WIN32 277e52adb7bSmrg .mutex = PTHREAD_MUTEX_INITIALIZER, 278e52adb7bSmrg#else 279e52adb7bSmrg 0, 280e52adb7bSmrg#endif 281e52adb7bSmrg}; 282e52adb7bSmrg 283e52adb7bSmrgstatic bool library_initialized; 284e52adb7bSmrg 285e52adb7bSmrgstatic bool epoxy_current_context_is_glx(void); 286e52adb7bSmrg 287e52adb7bSmrg#if PLATFORM_HAS_EGL 288e52adb7bSmrgstatic EGLenum 289e52adb7bSmrgepoxy_egl_get_current_gl_context_api(void); 290e52adb7bSmrg#endif 291e52adb7bSmrg 292e52adb7bSmrgCONSTRUCT (library_init) 293e52adb7bSmrg 294e52adb7bSmrgstatic void 295e52adb7bSmrglibrary_init(void) 296e52adb7bSmrg{ 297e52adb7bSmrg library_initialized = true; 298e52adb7bSmrg} 299e52adb7bSmrg 300e52adb7bSmrgstatic bool 301b94deca5Smrgget_dlopen_handle(void **handle, const char *lib_name, bool exit_on_fail, bool load) 302e52adb7bSmrg{ 303e52adb7bSmrg if (*handle) 304e52adb7bSmrg return true; 305e52adb7bSmrg 306e52adb7bSmrg if (!library_initialized) { 307b94deca5Smrg fputs("Attempting to dlopen() while in the dynamic linker.\n", stderr); 308e52adb7bSmrg abort(); 309e52adb7bSmrg } 310e52adb7bSmrg 311e52adb7bSmrg#ifdef _WIN32 312e52adb7bSmrg *handle = LoadLibraryA(lib_name); 313e52adb7bSmrg#else 314e52adb7bSmrg pthread_mutex_lock(&api.mutex); 315e52adb7bSmrg if (!*handle) { 316b94deca5Smrg int flags = RTLD_LAZY | RTLD_LOCAL; 317b94deca5Smrg if (!load) 318b94deca5Smrg flags |= RTLD_NOLOAD; 319b94deca5Smrg 320b94deca5Smrg *handle = dlopen(lib_name, flags); 321e52adb7bSmrg if (!*handle) { 322e52adb7bSmrg if (exit_on_fail) { 323e52adb7bSmrg fprintf(stderr, "Couldn't open %s: %s\n", lib_name, dlerror()); 324b94deca5Smrg abort(); 325e52adb7bSmrg } else { 326e52adb7bSmrg (void)dlerror(); 327e52adb7bSmrg } 328e52adb7bSmrg } 329e52adb7bSmrg } 330e52adb7bSmrg pthread_mutex_unlock(&api.mutex); 331e52adb7bSmrg#endif 332e52adb7bSmrg 333e52adb7bSmrg return *handle != NULL; 334e52adb7bSmrg} 335e52adb7bSmrg 336e52adb7bSmrgstatic void * 337b94deca5Smrgdo_dlsym(void **handle, const char *name, bool exit_on_fail) 338e52adb7bSmrg{ 339e52adb7bSmrg void *result; 340e52adb7bSmrg const char *error = ""; 341e52adb7bSmrg 342e52adb7bSmrg#ifdef _WIN32 343e52adb7bSmrg result = GetProcAddress(*handle, name); 344e52adb7bSmrg#else 345e52adb7bSmrg result = dlsym(*handle, name); 346e52adb7bSmrg if (!result) 347e52adb7bSmrg error = dlerror(); 348e52adb7bSmrg#endif 349e52adb7bSmrg if (!result && exit_on_fail) { 350b94deca5Smrg fprintf(stderr, "%s() not found: %s\n", name, error); 351b94deca5Smrg abort(); 352e52adb7bSmrg } 353e52adb7bSmrg 354e52adb7bSmrg return result; 355e52adb7bSmrg} 356e52adb7bSmrg 357de84f9a0Smrg/** 358de84f9a0Smrg * @brief Checks whether we're using OpenGL or OpenGL ES 359de84f9a0Smrg * 360de84f9a0Smrg * @return `true` if we're using OpenGL 361de84f9a0Smrg */ 362de84f9a0Smrgbool 363e52adb7bSmrgepoxy_is_desktop_gl(void) 364e52adb7bSmrg{ 365e52adb7bSmrg const char *es_prefix = "OpenGL ES"; 366e52adb7bSmrg const char *version; 367e52adb7bSmrg 368e52adb7bSmrg#if PLATFORM_HAS_EGL 369e52adb7bSmrg /* PowerVR's OpenGL ES implementation (and perhaps other) don't 370e52adb7bSmrg * comply with the standard, which states that 371e52adb7bSmrg * "glGetString(GL_VERSION)" should return a string starting with 372e52adb7bSmrg * "OpenGL ES". Therefore, to distinguish desktop OpenGL from 373e52adb7bSmrg * OpenGL ES, we must also check the context type through EGL (we 374e52adb7bSmrg * can do that as PowerVR is only usable through EGL). 375e52adb7bSmrg */ 376e52adb7bSmrg if (!epoxy_current_context_is_glx()) { 377e52adb7bSmrg switch (epoxy_egl_get_current_gl_context_api()) { 378e52adb7bSmrg case EGL_OPENGL_API: return true; 379e52adb7bSmrg case EGL_OPENGL_ES_API: return false; 380e52adb7bSmrg case EGL_NONE: 381e52adb7bSmrg default: break; 382e52adb7bSmrg } 383e52adb7bSmrg } 384e52adb7bSmrg#endif 385e52adb7bSmrg 386e52adb7bSmrg if (api.begin_count) 387e52adb7bSmrg return true; 388e52adb7bSmrg 389e52adb7bSmrg version = (const char *)glGetString(GL_VERSION); 390e52adb7bSmrg 391e52adb7bSmrg /* If we didn't get a version back, there are only two things that 392e52adb7bSmrg * could have happened: either malloc failure (which basically 393e52adb7bSmrg * doesn't exist), or we were called within a glBegin()/glEnd(). 394e52adb7bSmrg * Assume the second, which only exists for desktop GL. 395e52adb7bSmrg */ 396e52adb7bSmrg if (!version) 397e52adb7bSmrg return true; 398e52adb7bSmrg 399e52adb7bSmrg return strncmp(es_prefix, version, strlen(es_prefix)); 400e52adb7bSmrg} 401e52adb7bSmrg 402e52adb7bSmrgstatic int 403b94deca5Smrgepoxy_internal_gl_version(GLenum version_string, int error_version) 404e52adb7bSmrg{ 405b94deca5Smrg const char *version = (const char *)glGetString(version_string); 406b94deca5Smrg GLint major, minor, factor; 407e52adb7bSmrg int scanf_count; 408e52adb7bSmrg 409e52adb7bSmrg if (!version) 410e52adb7bSmrg return error_version; 411e52adb7bSmrg 412e52adb7bSmrg /* skip to version number */ 413e52adb7bSmrg while (!isdigit(*version) && *version != '\0') 414e52adb7bSmrg version++; 415e52adb7bSmrg 416e52adb7bSmrg /* Interpret version number */ 417e52adb7bSmrg scanf_count = sscanf(version, "%i.%i", &major, &minor); 418e52adb7bSmrg if (scanf_count != 2) { 419e52adb7bSmrg fprintf(stderr, "Unable to interpret GL_VERSION string: %s\n", 420e52adb7bSmrg version); 421b94deca5Smrg abort(); 422e52adb7bSmrg } 423b94deca5Smrg 424b94deca5Smrg if (minor >= 10) 425b94deca5Smrg factor = 100; 426b94deca5Smrg else 427b94deca5Smrg factor = 10; 428b94deca5Smrg 429b94deca5Smrg return factor * major + minor; 430e52adb7bSmrg} 431e52adb7bSmrg 432de84f9a0Smrg/** 433de84f9a0Smrg * @brief Returns the version of OpenGL we are using 434de84f9a0Smrg * 435de84f9a0Smrg * The version is encoded as: 436de84f9a0Smrg * 437de84f9a0Smrg * ``` 438de84f9a0Smrg * 439de84f9a0Smrg * version = major * 10 + minor 440de84f9a0Smrg * 441de84f9a0Smrg * ``` 442de84f9a0Smrg * 443de84f9a0Smrg * So it can be easily used for version comparisons. 444de84f9a0Smrg * 445de84f9a0Smrg * @return The encoded version of OpenGL we are using 446de84f9a0Smrg */ 447de84f9a0Smrgint 448e52adb7bSmrgepoxy_gl_version(void) 449e52adb7bSmrg{ 450b94deca5Smrg return epoxy_internal_gl_version(GL_VERSION, 0); 451e52adb7bSmrg} 452e52adb7bSmrg 453e52adb7bSmrgint 454e52adb7bSmrgepoxy_conservative_gl_version(void) 455e52adb7bSmrg{ 456e52adb7bSmrg if (api.begin_count) 457e52adb7bSmrg return 100; 458e52adb7bSmrg 459b94deca5Smrg return epoxy_internal_gl_version(GL_VERSION, 100); 460b94deca5Smrg} 461b94deca5Smrg 462b94deca5Smrg/** 463b94deca5Smrg * @brief Returns the version of the GL Shading Language we are using 464b94deca5Smrg * 465b94deca5Smrg * The version is encoded as: 466b94deca5Smrg * 467b94deca5Smrg * ``` 468b94deca5Smrg * 469b94deca5Smrg * version = major * 100 + minor 470b94deca5Smrg * 471b94deca5Smrg * ``` 472b94deca5Smrg * 473b94deca5Smrg * So it can be easily used for version comparisons. 474b94deca5Smrg * 475b94deca5Smrg * @return The encoded version of the GL Shading Language we are using 476b94deca5Smrg */ 477b94deca5Smrgint 478b94deca5Smrgepoxy_glsl_version(void) 479b94deca5Smrg{ 480b94deca5Smrg if (epoxy_gl_version() >= 20 || 481b94deca5Smrg epoxy_has_gl_extension ("GL_ARB_shading_language_100")) 482b94deca5Smrg return epoxy_internal_gl_version(GL_SHADING_LANGUAGE_VERSION, 0); 483b94deca5Smrg 484b94deca5Smrg return 0; 485e52adb7bSmrg} 486e52adb7bSmrg 487b94deca5Smrg/** 488b94deca5Smrg * @brief Checks for the presence of an extension in an OpenGL extension string 489b94deca5Smrg * 490b94deca5Smrg * @param extension_list The string containing the list of extensions to check 491b94deca5Smrg * @param ext The name of the GL extension 492b94deca5Smrg * @return `true` if the extension is available' 493b94deca5Smrg * 494b94deca5Smrg * @note If you are looking to check whether a normal GL, EGL or GLX extension 495b94deca5Smrg * is supported by the client, this probably isn't the function you want. 496b94deca5Smrg * 497b94deca5Smrg * Some parts of the spec for OpenGL and friends will return an OpenGL formatted 498b94deca5Smrg * extension string that is separate from the usual extension strings for the 499b94deca5Smrg * spec. This function provides easy parsing of those strings. 500b94deca5Smrg * 501b94deca5Smrg * @see epoxy_has_gl_extension() 502b94deca5Smrg * @see epoxy_has_egl_extension() 503b94deca5Smrg * @see epoxy_has_glx_extension() 504b94deca5Smrg */ 505e52adb7bSmrgbool 506e52adb7bSmrgepoxy_extension_in_string(const char *extension_list, const char *ext) 507e52adb7bSmrg{ 508e52adb7bSmrg const char *ptr = extension_list; 509de84f9a0Smrg int len; 510de84f9a0Smrg 511de84f9a0Smrg if (!ext) 512de84f9a0Smrg return false; 513de84f9a0Smrg 514de84f9a0Smrg len = strlen(ext); 515de84f9a0Smrg 516de84f9a0Smrg if (extension_list == NULL || *extension_list == '\0') 517de84f9a0Smrg return false; 518e52adb7bSmrg 519e52adb7bSmrg /* Make sure that don't just find an extension with our name as a prefix. */ 520e52adb7bSmrg while (true) { 521e52adb7bSmrg ptr = strstr(ptr, ext); 522e52adb7bSmrg if (!ptr) 523e52adb7bSmrg return false; 524e52adb7bSmrg 525e52adb7bSmrg if (ptr[len] == ' ' || ptr[len] == 0) 526e52adb7bSmrg return true; 527e52adb7bSmrg ptr += len; 528e52adb7bSmrg } 529e52adb7bSmrg} 530e52adb7bSmrg 531e52adb7bSmrgstatic bool 532e52adb7bSmrgepoxy_internal_has_gl_extension(const char *ext, bool invalid_op_mode) 533e52adb7bSmrg{ 534e52adb7bSmrg if (epoxy_gl_version() < 30) { 535e52adb7bSmrg const char *exts = (const char *)glGetString(GL_EXTENSIONS); 536e52adb7bSmrg if (!exts) 537e52adb7bSmrg return invalid_op_mode; 538e52adb7bSmrg return epoxy_extension_in_string(exts, ext); 539e52adb7bSmrg } else { 540e52adb7bSmrg int num_extensions; 541e52adb7bSmrg int i; 542e52adb7bSmrg 543e52adb7bSmrg glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); 544e52adb7bSmrg if (num_extensions == 0) 545e52adb7bSmrg return invalid_op_mode; 546e52adb7bSmrg 547e52adb7bSmrg for (i = 0; i < num_extensions; i++) { 548e52adb7bSmrg const char *gl_ext = (const char *)glGetStringi(GL_EXTENSIONS, i); 549de84f9a0Smrg if (!gl_ext) 550de84f9a0Smrg return false; 551e52adb7bSmrg if (strcmp(ext, gl_ext) == 0) 552e52adb7bSmrg return true; 553e52adb7bSmrg } 554e52adb7bSmrg 555e52adb7bSmrg return false; 556e52adb7bSmrg } 557e52adb7bSmrg} 558e52adb7bSmrg 559b94deca5Smrgbool 560b94deca5Smrgepoxy_load_glx(bool exit_if_fails, bool load) 561b94deca5Smrg{ 562b94deca5Smrg#if PLATFORM_HAS_GLX 563b94deca5Smrg# ifdef GLVND_GLX_LIB 564b94deca5Smrg /* prefer the glvnd library if it exists */ 565b94deca5Smrg if (!api.glx_handle) 566b94deca5Smrg get_dlopen_handle(&api.glx_handle, GLVND_GLX_LIB, false, load); 567b94deca5Smrg# endif 568b94deca5Smrg if (!api.glx_handle) 569b94deca5Smrg get_dlopen_handle(&api.glx_handle, GLX_LIB, exit_if_fails, load); 570b94deca5Smrg#endif 571b94deca5Smrg return api.glx_handle != NULL; 572b94deca5Smrg} 573b94deca5Smrg 574b94deca5Smrgvoid * 575b94deca5Smrgepoxy_conservative_glx_dlsym(const char *name, bool exit_if_fails) 576b94deca5Smrg{ 577b94deca5Smrg#if PLATFORM_HAS_GLX 578b94deca5Smrg if (epoxy_load_glx(exit_if_fails, exit_if_fails)) 579b94deca5Smrg return do_dlsym(&api.glx_handle, name, exit_if_fails); 580b94deca5Smrg#endif 581b94deca5Smrg return NULL; 582b94deca5Smrg} 583b94deca5Smrg 584e52adb7bSmrg/** 585e52adb7bSmrg * Tests whether the currently bound context is EGL or GLX, trying to 586e52adb7bSmrg * avoid loading libraries unless necessary. 587e52adb7bSmrg */ 588e52adb7bSmrgstatic bool 589e52adb7bSmrgepoxy_current_context_is_glx(void) 590e52adb7bSmrg{ 591e52adb7bSmrg#if !PLATFORM_HAS_GLX 592e52adb7bSmrg return false; 593e52adb7bSmrg#else 594e52adb7bSmrg void *sym; 595e52adb7bSmrg 596b94deca5Smrg sym = epoxy_conservative_glx_dlsym("glXGetCurrentContext", false); 597e52adb7bSmrg if (sym) { 598e52adb7bSmrg if (glXGetCurrentContext()) 599e52adb7bSmrg return true; 600e52adb7bSmrg } else { 601e52adb7bSmrg (void)dlerror(); 602e52adb7bSmrg } 603e52adb7bSmrg 604e52adb7bSmrg#if PLATFORM_HAS_EGL 605b94deca5Smrg sym = epoxy_conservative_egl_dlsym("eglGetCurrentContext", false); 606e52adb7bSmrg if (sym) { 607e52adb7bSmrg if (epoxy_egl_get_current_gl_context_api() != EGL_NONE) 608e52adb7bSmrg return false; 609e52adb7bSmrg } else { 610e52adb7bSmrg (void)dlerror(); 611e52adb7bSmrg } 612e52adb7bSmrg#endif /* PLATFORM_HAS_EGL */ 613e52adb7bSmrg 614e52adb7bSmrg return false; 615e52adb7bSmrg#endif /* PLATFORM_HAS_GLX */ 616e52adb7bSmrg} 617e52adb7bSmrg 618e52adb7bSmrg/** 619de84f9a0Smrg * @brief Returns true if the given GL extension is supported in the current context. 620de84f9a0Smrg * 621de84f9a0Smrg * @param ext The name of the GL extension 622de84f9a0Smrg * @return `true` if the extension is available 623e52adb7bSmrg * 624de84f9a0Smrg * @note that this function can't be called from within `glBegin()` and `glEnd()`. 625e52adb7bSmrg * 626de84f9a0Smrg * @see epoxy_has_egl_extension() 627de84f9a0Smrg * @see epoxy_has_glx_extension() 628e52adb7bSmrg */ 629de84f9a0Smrgbool 630e52adb7bSmrgepoxy_has_gl_extension(const char *ext) 631e52adb7bSmrg{ 632e52adb7bSmrg return epoxy_internal_has_gl_extension(ext, false); 633e52adb7bSmrg} 634e52adb7bSmrg 635e52adb7bSmrgbool 636e52adb7bSmrgepoxy_conservative_has_gl_extension(const char *ext) 637e52adb7bSmrg{ 638e52adb7bSmrg if (api.begin_count) 639e52adb7bSmrg return true; 640e52adb7bSmrg 641e52adb7bSmrg return epoxy_internal_has_gl_extension(ext, true); 642e52adb7bSmrg} 643e52adb7bSmrg 644b94deca5Smrgbool 645b94deca5Smrgepoxy_load_egl(bool exit_if_fails, bool load) 646b94deca5Smrg{ 647b94deca5Smrg#if PLATFORM_HAS_EGL 648b94deca5Smrg return get_dlopen_handle(&api.egl_handle, EGL_LIB, exit_if_fails, load); 649b94deca5Smrg#else 650b94deca5Smrg return false; 651b94deca5Smrg#endif 652b94deca5Smrg} 653b94deca5Smrg 654de84f9a0Smrgvoid * 655de84f9a0Smrgepoxy_conservative_egl_dlsym(const char *name, bool exit_if_fails) 656de84f9a0Smrg{ 657b94deca5Smrg#if PLATFORM_HAS_EGL 658b94deca5Smrg if (epoxy_load_egl(exit_if_fails, exit_if_fails)) 659b94deca5Smrg return do_dlsym(&api.egl_handle, name, exit_if_fails); 660b94deca5Smrg#endif 661b94deca5Smrg return NULL; 662de84f9a0Smrg} 663de84f9a0Smrg 664e52adb7bSmrgvoid * 665e52adb7bSmrgepoxy_egl_dlsym(const char *name) 666e52adb7bSmrg{ 667de84f9a0Smrg return epoxy_conservative_egl_dlsym(name, true); 668de84f9a0Smrg} 669de84f9a0Smrg 670de84f9a0Smrgvoid * 671b94deca5Smrgepoxy_glx_dlsym(const char *name) 672de84f9a0Smrg{ 673b94deca5Smrg return epoxy_conservative_glx_dlsym(name, true); 674e52adb7bSmrg} 675e52adb7bSmrg 676b94deca5Smrgstatic void 677b94deca5Smrgepoxy_load_gl(void) 678e52adb7bSmrg{ 679b94deca5Smrg if (api.gl_handle) 680b94deca5Smrg return; 681b94deca5Smrg 682b94deca5Smrg#if defined(_WIN32) || defined(__APPLE__) 683b94deca5Smrg get_dlopen_handle(&api.gl_handle, OPENGL_LIB, true, true); 684b94deca5Smrg#else 685b94deca5Smrg 686b94deca5Smrg#if defined(OPENGL_LIB) 687b94deca5Smrg if (!api.gl_handle) 688b94deca5Smrg get_dlopen_handle(&api.gl_handle, OPENGL_LIB, false, true); 689b94deca5Smrg#endif 690b94deca5Smrg 691b94deca5Smrg get_dlopen_handle(&api.glx_handle, GLX_LIB, true, true); 692b94deca5Smrg api.gl_handle = api.glx_handle; 693b94deca5Smrg#endif 694e52adb7bSmrg} 695e52adb7bSmrg 696e52adb7bSmrgvoid * 697e52adb7bSmrgepoxy_gl_dlsym(const char *name) 698e52adb7bSmrg{ 699b94deca5Smrg epoxy_load_gl(); 700b94deca5Smrg 701b94deca5Smrg return do_dlsym(&api.gl_handle, name, true); 702e52adb7bSmrg} 703e52adb7bSmrg 704e52adb7bSmrgvoid * 705e52adb7bSmrgepoxy_gles1_dlsym(const char *name) 706e52adb7bSmrg{ 707e52adb7bSmrg if (epoxy_current_context_is_glx()) { 708e52adb7bSmrg return epoxy_get_proc_address(name); 709e52adb7bSmrg } else { 710b94deca5Smrg get_dlopen_handle(&api.gles1_handle, GLES1_LIB, true, true); 711b94deca5Smrg return do_dlsym(&api.gles1_handle, name, true); 712e52adb7bSmrg } 713e52adb7bSmrg} 714e52adb7bSmrg 715e52adb7bSmrgvoid * 716e52adb7bSmrgepoxy_gles2_dlsym(const char *name) 717e52adb7bSmrg{ 718e52adb7bSmrg if (epoxy_current_context_is_glx()) { 719e52adb7bSmrg return epoxy_get_proc_address(name); 720e52adb7bSmrg } else { 721b94deca5Smrg get_dlopen_handle(&api.gles2_handle, GLES2_LIB, true, true); 722b94deca5Smrg return do_dlsym(&api.gles2_handle, name, true); 723e52adb7bSmrg } 724e52adb7bSmrg} 725e52adb7bSmrg 726e52adb7bSmrg/** 727e52adb7bSmrg * Does the appropriate dlsym() or eglGetProcAddress() for GLES3 728e52adb7bSmrg * functions. 729e52adb7bSmrg * 730e52adb7bSmrg * Mesa interpreted GLES as intending that the GLES3 functions were 731e52adb7bSmrg * available only through eglGetProcAddress() and not dlsym(), while 732e52adb7bSmrg * ARM's Mali drivers interpreted GLES as intending that GLES3 733e52adb7bSmrg * functions were available only through dlsym() and not 734e52adb7bSmrg * eglGetProcAddress(). Thanks, Khronos. 735e52adb7bSmrg */ 736e52adb7bSmrgvoid * 737e52adb7bSmrgepoxy_gles3_dlsym(const char *name) 738e52adb7bSmrg{ 739e52adb7bSmrg if (epoxy_current_context_is_glx()) { 740e52adb7bSmrg return epoxy_get_proc_address(name); 741e52adb7bSmrg } else { 742b94deca5Smrg if (get_dlopen_handle(&api.gles2_handle, GLES2_LIB, false, true)) { 743b94deca5Smrg void *func = do_dlsym(&api.gles2_handle, name, false); 744e52adb7bSmrg 745b94deca5Smrg if (func) 746b94deca5Smrg return func; 747b94deca5Smrg } 748e52adb7bSmrg 749e52adb7bSmrg return epoxy_get_proc_address(name); 750e52adb7bSmrg } 751e52adb7bSmrg} 752e52adb7bSmrg 753e52adb7bSmrg/** 754e52adb7bSmrg * Performs either the dlsym or glXGetProcAddress()-equivalent for 755e52adb7bSmrg * core functions in desktop GL. 756e52adb7bSmrg */ 757e52adb7bSmrgvoid * 758e52adb7bSmrgepoxy_get_core_proc_address(const char *name, int core_version) 759e52adb7bSmrg{ 760e52adb7bSmrg#ifdef _WIN32 761e52adb7bSmrg int core_symbol_support = 11; 762b94deca5Smrg#elif defined(__ANDROID__) 763e52adb7bSmrg /** 764e52adb7bSmrg * All symbols must be resolved through eglGetProcAddress 765e52adb7bSmrg * on Android 766e52adb7bSmrg */ 767e52adb7bSmrg int core_symbol_support = 0; 768e52adb7bSmrg#else 769e52adb7bSmrg int core_symbol_support = 12; 770e52adb7bSmrg#endif 771e52adb7bSmrg 772e52adb7bSmrg if (core_version <= core_symbol_support) { 773e52adb7bSmrg return epoxy_gl_dlsym(name); 774e52adb7bSmrg } else { 775e52adb7bSmrg return epoxy_get_proc_address(name); 776e52adb7bSmrg } 777e52adb7bSmrg} 778e52adb7bSmrg 779e52adb7bSmrg#if PLATFORM_HAS_EGL 780e52adb7bSmrgstatic EGLenum 781e52adb7bSmrgepoxy_egl_get_current_gl_context_api(void) 782e52adb7bSmrg{ 783de84f9a0Smrg EGLint curapi; 784e52adb7bSmrg 785de84f9a0Smrg if (eglQueryContext(eglGetCurrentDisplay(), eglGetCurrentContext(), 786de84f9a0Smrg EGL_CONTEXT_CLIENT_TYPE, &curapi) == EGL_FALSE) { 787de84f9a0Smrg (void)eglGetError(); 788de84f9a0Smrg return EGL_NONE; 789e52adb7bSmrg } 790e52adb7bSmrg 791de84f9a0Smrg return (EGLenum) curapi; 792e52adb7bSmrg} 793e52adb7bSmrg#endif /* PLATFORM_HAS_EGL */ 794e52adb7bSmrg 795e52adb7bSmrg/** 796e52adb7bSmrg * Performs the dlsym() for the core GL 1.0 functions that we use for 797e52adb7bSmrg * determining version and extension support for deciding on dlsym 798e52adb7bSmrg * versus glXGetProcAddress() for all other functions. 799e52adb7bSmrg * 800e52adb7bSmrg * This needs to succeed on implementations without GLX (since 801e52adb7bSmrg * glGetString() and glGetIntegerv() are both in GLES1/2 as well, and 802e52adb7bSmrg * at call time we don't know for sure what API they're trying to use 803e52adb7bSmrg * without inspecting contexts ourselves). 804e52adb7bSmrg */ 805e52adb7bSmrgvoid * 806e52adb7bSmrgepoxy_get_bootstrap_proc_address(const char *name) 807e52adb7bSmrg{ 808e52adb7bSmrg /* If we already have a library that links to libglapi loaded, 809e52adb7bSmrg * use that. 810e52adb7bSmrg */ 811e52adb7bSmrg#if PLATFORM_HAS_GLX 812e52adb7bSmrg if (api.glx_handle && glXGetCurrentContext()) 813e52adb7bSmrg return epoxy_gl_dlsym(name); 814e52adb7bSmrg#endif 815e52adb7bSmrg 816e52adb7bSmrg /* If epoxy hasn't loaded any API-specific library yet, try to 817e52adb7bSmrg * figure out what API the context is using and use that library, 818e52adb7bSmrg * since future calls will also use that API (this prevents a 819e52adb7bSmrg * non-X11 ES2 context from loading a bunch of X11 junk). 820e52adb7bSmrg */ 821e52adb7bSmrg#if PLATFORM_HAS_EGL 822b94deca5Smrg get_dlopen_handle(&api.egl_handle, EGL_LIB, false, true); 823e52adb7bSmrg if (api.egl_handle) { 824b94deca5Smrg int version = 0; 825e52adb7bSmrg switch (epoxy_egl_get_current_gl_context_api()) { 826e52adb7bSmrg case EGL_OPENGL_API: 827e52adb7bSmrg return epoxy_gl_dlsym(name); 828e52adb7bSmrg case EGL_OPENGL_ES_API: 829b94deca5Smrg if (eglQueryContext(eglGetCurrentDisplay(), 830b94deca5Smrg eglGetCurrentContext(), 831b94deca5Smrg EGL_CONTEXT_CLIENT_VERSION, 832b94deca5Smrg &version)) { 833b94deca5Smrg if (version >= 2) 834b94deca5Smrg return epoxy_gles2_dlsym(name); 835b94deca5Smrg else 836b94deca5Smrg return epoxy_gles1_dlsym(name); 837b94deca5Smrg } 838e52adb7bSmrg } 839e52adb7bSmrg } 840e52adb7bSmrg#endif /* PLATFORM_HAS_EGL */ 841e52adb7bSmrg 842e52adb7bSmrg /* Fall back to GLX */ 843e52adb7bSmrg return epoxy_gl_dlsym(name); 844e52adb7bSmrg} 845e52adb7bSmrg 846e52adb7bSmrgvoid * 847e52adb7bSmrgepoxy_get_proc_address(const char *name) 848e52adb7bSmrg{ 849de84f9a0Smrg#if PLATFORM_HAS_EGL 850de84f9a0Smrg GLenum egl_api = EGL_NONE; 851de84f9a0Smrg 852de84f9a0Smrg if (!epoxy_current_context_is_glx()) 853de84f9a0Smrg egl_api = epoxy_egl_get_current_gl_context_api(); 854de84f9a0Smrg 855de84f9a0Smrg switch (egl_api) { 856de84f9a0Smrg case EGL_OPENGL_API: 857de84f9a0Smrg case EGL_OPENGL_ES_API: 858de84f9a0Smrg return eglGetProcAddress(name); 859de84f9a0Smrg case EGL_NONE: 860de84f9a0Smrg break; 861de84f9a0Smrg } 862de84f9a0Smrg#endif 863de84f9a0Smrg 864de84f9a0Smrg#if defined(_WIN32) 865e52adb7bSmrg return wglGetProcAddress(name); 866e52adb7bSmrg#elif defined(__APPLE__) 867e52adb7bSmrg return epoxy_gl_dlsym(name); 868de84f9a0Smrg#elif PLATFORM_HAS_GLX 869de84f9a0Smrg if (epoxy_current_context_is_glx()) 870e52adb7bSmrg return glXGetProcAddressARB((const GLubyte *)name); 871b94deca5Smrg assert(0 && "Couldn't find current GLX or EGL context.\n"); 872e52adb7bSmrg#endif 873de84f9a0Smrg 874de84f9a0Smrg return NULL; 875e52adb7bSmrg} 876e52adb7bSmrg 877e52adb7bSmrgWRAPPER_VISIBILITY (void) 878e52adb7bSmrgWRAPPER(epoxy_glBegin)(GLenum primtype) 879e52adb7bSmrg{ 880e52adb7bSmrg#ifdef _WIN32 881e52adb7bSmrg InterlockedIncrement(&api.begin_count); 882e52adb7bSmrg#else 883e52adb7bSmrg pthread_mutex_lock(&api.mutex); 884e52adb7bSmrg api.begin_count++; 885e52adb7bSmrg pthread_mutex_unlock(&api.mutex); 886e52adb7bSmrg#endif 887e52adb7bSmrg 888e52adb7bSmrg epoxy_glBegin_unwrapped(primtype); 889e52adb7bSmrg} 890e52adb7bSmrg 891e52adb7bSmrgWRAPPER_VISIBILITY (void) 892e52adb7bSmrgWRAPPER(epoxy_glEnd)(void) 893e52adb7bSmrg{ 894e52adb7bSmrg epoxy_glEnd_unwrapped(); 895e52adb7bSmrg 896e52adb7bSmrg#ifdef _WIN32 897e52adb7bSmrg InterlockedDecrement(&api.begin_count); 898e52adb7bSmrg#else 899e52adb7bSmrg pthread_mutex_lock(&api.mutex); 900e52adb7bSmrg api.begin_count--; 901e52adb7bSmrg pthread_mutex_unlock(&api.mutex); 902e52adb7bSmrg#endif 903e52adb7bSmrg} 904e52adb7bSmrg 905de84f9a0SmrgPFNGLBEGINPROC epoxy_glBegin = epoxy_glBegin_wrapped; 906de84f9a0SmrgPFNGLENDPROC epoxy_glEnd = epoxy_glEnd_wrapped; 907b94deca5Smrg 908b94deca5Smrgepoxy_resolver_failure_handler_t epoxy_resolver_failure_handler; 909b94deca5Smrg 910b94deca5Smrg/** 911b94deca5Smrg * Sets the function that will be called every time Epoxy fails to 912b94deca5Smrg * resolve a symbol. 913b94deca5Smrg * 914b94deca5Smrg * @param handler The new handler function 915b94deca5Smrg * @return The previous handler function 916b94deca5Smrg */ 917b94deca5Smrgepoxy_resolver_failure_handler_t 918b94deca5Smrgepoxy_set_resolver_failure_handler(epoxy_resolver_failure_handler_t handler) 919b94deca5Smrg{ 920b94deca5Smrg#ifdef _WIN32 921b94deca5Smrg return InterlockedExchangePointer((void**)&epoxy_resolver_failure_handler, 922b94deca5Smrg handler); 923b94deca5Smrg#else 924b94deca5Smrg epoxy_resolver_failure_handler_t old; 925b94deca5Smrg pthread_mutex_lock(&api.mutex); 926b94deca5Smrg old = epoxy_resolver_failure_handler; 927b94deca5Smrg epoxy_resolver_failure_handler = handler; 928b94deca5Smrg pthread_mutex_unlock(&api.mutex); 929b94deca5Smrg return old; 930b94deca5Smrg#endif 931b94deca5Smrg} 932