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