xserver

xserver with xephyr scale patch
git clone https://git.neptards.moe/u3shit/xserver.git
Log | Files | Refs | README | LICENSE

indirect.c (18682B)


      1 /*
      2  * GLX implementation that uses Apple's OpenGL.framework
      3  * (Indirect rendering path -- it's also used for some direct mode code too)
      4  *
      5  * Copyright (c) 2007-2012 Apple Inc.
      6  * Copyright (c) 2004 Torrey T. Lyons. All Rights Reserved.
      7  * Copyright (c) 2002 Greg Parker. All Rights Reserved.
      8  *
      9  * Portions of this file are copied from Mesa's xf86glx.c,
     10  * which contains the following copyright:
     11  *
     12  * Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
     13  * All Rights Reserved.
     14  *
     15  * Permission is hereby granted, free of charge, to any person obtaining a
     16  * copy of this software and associated documentation files (the "Software"),
     17  * to deal in the Software without restriction, including without limitation
     18  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     19  * and/or sell copies of the Software, and to permit persons to whom the
     20  * Software is furnished to do so, subject to the following conditions:
     21  *
     22  * The above copyright notice and this permission notice shall be included in
     23  * all copies or substantial portions of the Software.
     24  *
     25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     28  * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     29  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     30  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     31  * DEALINGS IN THE SOFTWARE.
     32  */
     33 
     34 #ifdef HAVE_DIX_CONFIG_H
     35 #include <dix-config.h>
     36 #endif
     37 
     38 #include <dlfcn.h>
     39 
     40 #include <OpenGL/OpenGL.h>
     41 #include <OpenGL/gl.h>     /* Just to prevent glxserver.h from loading mesa's and colliding with OpenGL.h */
     42 
     43 #include <X11/Xproto.h>
     44 #include <GL/glxproto.h>
     45 
     46 #include <glxserver.h>
     47 #include <glxutil.h>
     48 
     49 #include "x-hash.h"
     50 
     51 #include "visualConfigs.h"
     52 #include "dri.h"
     53 #include "extension_string.h"
     54 
     55 #include "darwin.h"
     56 #define GLAQUA_DEBUG_MSG(msg, args ...) ASL_LOG(ASL_LEVEL_DEBUG, "GLXAqua", \
     57                                                 msg, \
     58                                                 ## args)
     59 
     60 __GLXprovider *
     61 GlxGetDRISWrastProvider(void);
     62 
     63 static void
     64 setup_dispatch_table(void);
     65 GLuint
     66 __glFloorLog2(GLuint val);
     67 void
     68 warn_func(void * p1, char *format, ...);
     69 
     70 // some prototypes
     71 static __GLXscreen *
     72 __glXAquaScreenProbe(ScreenPtr pScreen);
     73 static __GLXdrawable *
     74 __glXAquaScreenCreateDrawable(ClientPtr client, __GLXscreen *screen,
     75                               DrawablePtr pDraw, XID drawId, int type,
     76                               XID glxDrawId,
     77                               __GLXconfig *conf);
     78 
     79 static void
     80 __glXAquaContextDestroy(__GLXcontext *baseContext);
     81 static int
     82 __glXAquaContextMakeCurrent(__GLXcontext *baseContext);
     83 static int
     84 __glXAquaContextLoseCurrent(__GLXcontext *baseContext);
     85 static int
     86 __glXAquaContextCopy(__GLXcontext *baseDst, __GLXcontext *baseSrc,
     87                      unsigned long mask);
     88 
     89 static CGLPixelFormatObj
     90 makeFormat(__GLXconfig *conf);
     91 
     92 __GLXprovider __glXDRISWRastProvider = {
     93     __glXAquaScreenProbe,
     94     "Core OpenGL",
     95     NULL
     96 };
     97 
     98 typedef struct __GLXAquaScreen __GLXAquaScreen;
     99 typedef struct __GLXAquaContext __GLXAquaContext;
    100 typedef struct __GLXAquaDrawable __GLXAquaDrawable;
    101 
    102 /*
    103  * The following structs must keep the base as the first member.
    104  * It's used to treat the start of the struct as a different struct
    105  * in GLX.
    106  *
    107  * Note: these structs should be initialized with xcalloc or memset
    108  * prior to usage, and some of them require initializing
    109  * the base with function pointers.
    110  */
    111 struct __GLXAquaScreen {
    112     __GLXscreen base;
    113 };
    114 
    115 struct __GLXAquaContext {
    116     __GLXcontext base;
    117     CGLContextObj ctx;
    118     CGLPixelFormatObj pixelFormat;
    119     xp_surface_id sid;
    120     unsigned isAttached : 1;
    121 };
    122 
    123 struct __GLXAquaDrawable {
    124     __GLXdrawable base;
    125     DrawablePtr pDraw;
    126     xp_surface_id sid;
    127     __GLXAquaContext *context;
    128 };
    129 
    130 static __GLXcontext *
    131 __glXAquaScreenCreateContext(__GLXscreen *screen,
    132                              __GLXconfig *conf,
    133                              __GLXcontext *baseShareContext,
    134                              unsigned num_attribs,
    135                              const uint32_t *attribs,
    136                              int *error)
    137 {
    138     __GLXAquaContext *context;
    139     __GLXAquaContext *shareContext = (__GLXAquaContext *)baseShareContext;
    140     CGLError gl_err;
    141 
    142     /* Unused (for now?) */
    143     (void)num_attribs;
    144     (void)attribs;
    145     (void)error;
    146 
    147     GLAQUA_DEBUG_MSG("glXAquaScreenCreateContext\n");
    148 
    149     context = calloc(1, sizeof(__GLXAquaContext));
    150 
    151     if (context == NULL)
    152         return NULL;
    153 
    154     memset(context, 0, sizeof *context);
    155 
    156     context->base.pGlxScreen = screen;
    157     context->base.config = conf;
    158     context->base.destroy = __glXAquaContextDestroy;
    159     context->base.makeCurrent = __glXAquaContextMakeCurrent;
    160     context->base.loseCurrent = __glXAquaContextLoseCurrent;
    161     context->base.copy = __glXAquaContextCopy;
    162     /*FIXME verify that the context->base is fully initialized. */
    163 
    164     context->pixelFormat = makeFormat(conf);
    165 
    166     if (!context->pixelFormat) {
    167         free(context);
    168         return NULL;
    169     }
    170 
    171     context->ctx = NULL;
    172     gl_err = CGLCreateContext(context->pixelFormat,
    173                               shareContext ? shareContext->ctx : NULL,
    174                               &context->ctx);
    175 
    176     if (gl_err != 0) {
    177         ErrorF("CGLCreateContext error: %s\n", CGLErrorString(gl_err));
    178         CGLDestroyPixelFormat(context->pixelFormat);
    179         free(context);
    180         return NULL;
    181     }
    182 
    183     setup_dispatch_table();
    184     GLAQUA_DEBUG_MSG("glAquaCreateContext done\n");
    185 
    186     return &context->base;
    187 }
    188 
    189 /* maps from surface id -> list of __GLcontext */
    190 static x_hash_table *surface_hash;
    191 
    192 static void
    193 __glXAquaContextDestroy(__GLXcontext *baseContext)
    194 {
    195     x_list *lst;
    196 
    197     __GLXAquaContext *context = (__GLXAquaContext *)baseContext;
    198 
    199     GLAQUA_DEBUG_MSG("glAquaContextDestroy (ctx %p)\n", baseContext);
    200     if (context != NULL) {
    201         if (context->sid != 0 && surface_hash != NULL) {
    202             lst =
    203                 x_hash_table_lookup(surface_hash, x_cvt_uint_to_vptr(
    204                                         context->sid), NULL);
    205             lst = x_list_remove(lst, context);
    206             x_hash_table_insert(surface_hash, x_cvt_uint_to_vptr(
    207                                     context->sid), lst);
    208         }
    209 
    210         if (context->ctx != NULL)
    211             CGLDestroyContext(context->ctx);
    212 
    213         if (context->pixelFormat != NULL)
    214             CGLDestroyPixelFormat(context->pixelFormat);
    215 
    216         free(context);
    217     }
    218 }
    219 
    220 static int
    221 __glXAquaContextLoseCurrent(__GLXcontext *baseContext)
    222 {
    223     CGLError gl_err;
    224 
    225     GLAQUA_DEBUG_MSG("glAquaLoseCurrent (ctx 0x%p)\n", baseContext);
    226 
    227     gl_err = CGLSetCurrentContext(NULL);
    228     if (gl_err != 0)
    229         ErrorF("CGLSetCurrentContext error: %s\n", CGLErrorString(gl_err));
    230 
    231     /*
    232      * There should be no need to set __glXLastContext to NULL here, because
    233      * glxcmds.c does it as part of the context cache flush after calling
    234      * this.
    235      */
    236 
    237     return GL_TRUE;
    238 }
    239 
    240 /* Called when a surface is destroyed as a side effect of destroying
    241    the window it's attached to. */
    242 static void
    243 surface_notify(void *_arg, void *data)
    244 {
    245     DRISurfaceNotifyArg *arg = (DRISurfaceNotifyArg *)_arg;
    246     __GLXAquaDrawable *draw = (__GLXAquaDrawable *)data;
    247     __GLXAquaContext *context;
    248     x_list *lst;
    249     if (_arg == NULL || data == NULL) {
    250         ErrorF("surface_notify called with bad params");
    251         return;
    252     }
    253 
    254     GLAQUA_DEBUG_MSG("surface_notify(%p, %p)\n", _arg, data);
    255     switch (arg->kind) {
    256     case AppleDRISurfaceNotifyDestroyed:
    257         if (surface_hash != NULL)
    258             x_hash_table_remove(surface_hash, x_cvt_uint_to_vptr(arg->id));
    259         draw->pDraw = NULL;
    260         draw->sid = 0;
    261         break;
    262 
    263     case AppleDRISurfaceNotifyChanged:
    264         if (surface_hash != NULL) {
    265             lst =
    266                 x_hash_table_lookup(surface_hash, x_cvt_uint_to_vptr(
    267                                         arg->id), NULL);
    268             for (; lst != NULL; lst = lst->next) {
    269                 context = lst->data;
    270                 xp_update_gl_context(context->ctx);
    271             }
    272         }
    273         break;
    274 
    275     default:
    276         ErrorF("surface_notify: unknown kind %d\n", arg->kind);
    277         break;
    278     }
    279 }
    280 
    281 static BOOL
    282 attach(__GLXAquaContext *context, __GLXAquaDrawable *draw)
    283 {
    284     DrawablePtr pDraw;
    285 
    286     GLAQUA_DEBUG_MSG("attach(%p, %p)\n", context, draw);
    287 
    288     if (NULL == context || NULL == draw)
    289         return TRUE;
    290 
    291     pDraw = draw->base.pDraw;
    292 
    293     if (NULL == pDraw) {
    294         ErrorF("%s:%s() pDraw is NULL!\n", __FILE__, __func__);
    295         return TRUE;
    296     }
    297 
    298     if (draw->sid == 0) {
    299         //if (!quartzProcs->CreateSurface(pDraw->pScreen, pDraw->id, pDraw,
    300         if (!DRICreateSurface(pDraw->pScreen, pDraw->id, pDraw,
    301                               0, &draw->sid, NULL,
    302                               surface_notify, draw))
    303             return TRUE;
    304         draw->pDraw = pDraw;
    305     }
    306 
    307     if (!context->isAttached || context->sid != draw->sid) {
    308         x_list *lst;
    309 
    310         if (xp_attach_gl_context(context->ctx, draw->sid) != Success) {
    311             //quartzProcs->DestroySurface(pDraw->pScreen, pDraw->id, pDraw,
    312             DRIDestroySurface(pDraw->pScreen, pDraw->id, pDraw,
    313                               surface_notify, draw);
    314             if (surface_hash != NULL)
    315                 x_hash_table_remove(surface_hash,
    316                                     x_cvt_uint_to_vptr(draw->sid));
    317 
    318             draw->sid = 0;
    319             return TRUE;
    320         }
    321 
    322         context->isAttached = TRUE;
    323         context->sid = draw->sid;
    324 
    325         if (surface_hash == NULL)
    326             surface_hash = x_hash_table_new(NULL, NULL, NULL, NULL);
    327 
    328         lst =
    329             x_hash_table_lookup(surface_hash, x_cvt_uint_to_vptr(
    330                                     context->sid), NULL);
    331         if (x_list_find(lst, context) == NULL) {
    332             lst = x_list_prepend(lst, context);
    333             x_hash_table_insert(surface_hash, x_cvt_uint_to_vptr(
    334                                     context->sid), lst);
    335         }
    336 
    337         GLAQUA_DEBUG_MSG("attached 0x%x to 0x%x\n", (unsigned int)pDraw->id,
    338                          (unsigned int)draw->sid);
    339     }
    340 
    341     draw->context = context;
    342 
    343     return FALSE;
    344 }
    345 
    346 #if 0     // unused
    347 static void
    348 unattach(__GLXAquaContext *context)
    349 {
    350     x_list *lst;
    351     GLAQUA_DEBUG_MSG("unattach\n");
    352     if (context == NULL) {
    353         ErrorF("Tried to unattach a null context\n");
    354         return;
    355     }
    356     if (context->isAttached) {
    357         GLAQUA_DEBUG_MSG("unattaching\n");
    358 
    359         if (surface_hash != NULL) {
    360             lst = x_hash_table_lookup(surface_hash, (void *)context->sid,
    361                                       NULL);
    362             lst = x_list_remove(lst, context);
    363             x_hash_table_insert(surface_hash, (void *)context->sid, lst);
    364         }
    365 
    366         CGLClearDrawable(context->ctx);
    367         context->isAttached = FALSE;
    368         context->sid = 0;
    369     }
    370 }
    371 #endif
    372 
    373 static int
    374 __glXAquaContextMakeCurrent(__GLXcontext *baseContext)
    375 {
    376     CGLError gl_err;
    377     __GLXAquaContext *context = (__GLXAquaContext *)baseContext;
    378     __GLXAquaDrawable *drawPriv = (__GLXAquaDrawable *)context->base.drawPriv;
    379 
    380     GLAQUA_DEBUG_MSG("glAquaMakeCurrent (ctx 0x%p)\n", baseContext);
    381 
    382     if (context->base.drawPriv != context->base.readPriv)
    383         return 0;
    384 
    385     if (attach(context, drawPriv))
    386         return /*error*/ 0;
    387 
    388     gl_err = CGLSetCurrentContext(context->ctx);
    389     if (gl_err != 0)
    390         ErrorF("CGLSetCurrentContext error: %s\n", CGLErrorString(gl_err));
    391 
    392     return gl_err == 0;
    393 }
    394 
    395 static int
    396 __glXAquaContextCopy(__GLXcontext *baseDst, __GLXcontext *baseSrc,
    397                      unsigned long mask)
    398 {
    399     CGLError gl_err;
    400 
    401     __GLXAquaContext *dst = (__GLXAquaContext *)baseDst;
    402     __GLXAquaContext *src = (__GLXAquaContext *)baseSrc;
    403 
    404     GLAQUA_DEBUG_MSG("GLXAquaContextCopy\n");
    405 
    406     gl_err = CGLCopyContext(src->ctx, dst->ctx, mask);
    407     if (gl_err != 0)
    408         ErrorF("CGLCopyContext error: %s\n", CGLErrorString(gl_err));
    409 
    410     return gl_err == 0;
    411 }
    412 
    413 /* Drawing surface notification callbacks */
    414 static GLboolean
    415 __glXAquaDrawableSwapBuffers(ClientPtr client, __GLXdrawable *base)
    416 {
    417     CGLError err;
    418     __GLXAquaDrawable *drawable;
    419 
    420     //    GLAQUA_DEBUG_MSG("glAquaDrawableSwapBuffers(%p)\n",base);
    421 
    422     if (!base) {
    423         ErrorF("%s passed NULL\n", __func__);
    424         return GL_FALSE;
    425     }
    426 
    427     drawable = (__GLXAquaDrawable *)base;
    428 
    429     if (NULL == drawable->context) {
    430         ErrorF("%s called with a NULL->context for drawable %p!\n",
    431                __func__, (void *)drawable);
    432         return GL_FALSE;
    433     }
    434 
    435     err = CGLFlushDrawable(drawable->context->ctx);
    436 
    437     if (kCGLNoError != err) {
    438         ErrorF("CGLFlushDrawable error: %s in %s\n", CGLErrorString(err),
    439                __func__);
    440         return GL_FALSE;
    441     }
    442 
    443     return GL_TRUE;
    444 }
    445 
    446 static CGLPixelFormatObj
    447 makeFormat(__GLXconfig *conf)
    448 {
    449     CGLPixelFormatAttribute attr[64];
    450     CGLPixelFormatObj fobj;
    451     GLint formats;
    452     CGLError error;
    453     int i = 0;
    454 
    455     if (conf->doubleBufferMode)
    456         attr[i++] = kCGLPFADoubleBuffer;
    457 
    458     if (conf->stereoMode)
    459         attr[i++] = kCGLPFAStereo;
    460 
    461     attr[i++] = kCGLPFAColorSize;
    462     attr[i++] = conf->redBits + conf->greenBits + conf->blueBits;
    463     attr[i++] = kCGLPFAAlphaSize;
    464     attr[i++] = conf->alphaBits;
    465 
    466     if ((conf->accumRedBits + conf->accumGreenBits + conf->accumBlueBits +
    467          conf->accumAlphaBits) > 0) {
    468 
    469         attr[i++] = kCGLPFAAccumSize;
    470         attr[i++] = conf->accumRedBits + conf->accumGreenBits
    471                     + conf->accumBlueBits + conf->accumAlphaBits;
    472     }
    473 
    474     attr[i++] = kCGLPFADepthSize;
    475     attr[i++] = conf->depthBits;
    476 
    477     if (conf->stencilBits) {
    478         attr[i++] = kCGLPFAStencilSize;
    479         attr[i++] = conf->stencilBits;
    480     }
    481 
    482     if (conf->numAuxBuffers > 0) {
    483         attr[i++] = kCGLPFAAuxBuffers;
    484         attr[i++] = conf->numAuxBuffers;
    485     }
    486 
    487     if (conf->sampleBuffers > 0) {
    488         attr[i++] = kCGLPFASampleBuffers;
    489         attr[i++] = conf->sampleBuffers;
    490         attr[i++] = kCGLPFASamples;
    491         attr[i++] = conf->samples;
    492     }
    493 
    494     attr[i] = 0;
    495 
    496     error = CGLChoosePixelFormat(attr, &fobj, &formats);
    497     if (error) {
    498         ErrorF("error: creating pixel format %s\n", CGLErrorString(error));
    499         return NULL;
    500     }
    501 
    502     return fobj;
    503 }
    504 
    505 static void
    506 __glXAquaScreenDestroy(__GLXscreen *screen)
    507 {
    508 
    509     GLAQUA_DEBUG_MSG("glXAquaScreenDestroy(%p)\n", screen);
    510     __glXScreenDestroy(screen);
    511 
    512     free(screen);
    513 }
    514 
    515 /* This is called by __glXInitScreens(). */
    516 static __GLXscreen *
    517 __glXAquaScreenProbe(ScreenPtr pScreen)
    518 {
    519     __GLXAquaScreen *screen;
    520 
    521     GLAQUA_DEBUG_MSG("glXAquaScreenProbe\n");
    522 
    523     if (pScreen == NULL)
    524         return NULL;
    525 
    526     screen = calloc(1, sizeof *screen);
    527 
    528     if (NULL == screen)
    529         return NULL;
    530 
    531     screen->base.destroy = __glXAquaScreenDestroy;
    532     screen->base.createContext = __glXAquaScreenCreateContext;
    533     screen->base.createDrawable = __glXAquaScreenCreateDrawable;
    534     screen->base.swapInterval = /*FIXME*/ NULL;
    535     screen->base.pScreen = pScreen;
    536 
    537     screen->base.fbconfigs = __glXAquaCreateVisualConfigs(
    538         &screen->base.numFBConfigs, pScreen->myNum);
    539 
    540     __glXInitExtensionEnableBits(screen->base.glx_enable_bits);
    541     __glXScreenInit(&screen->base, pScreen);
    542 
    543     return &screen->base;
    544 }
    545 
    546 #if 0 // unused
    547 static void
    548 __glXAquaDrawableCopySubBuffer(__GLXdrawable *drawable,
    549                                int x, int y, int w, int h)
    550 {
    551     /*TODO finish me*/
    552 }
    553 #endif
    554 
    555 static void
    556 __glXAquaDrawableDestroy(__GLXdrawable *base)
    557 {
    558     /* gstaplin: base is the head of the structure, so it's at the same
    559      * offset in memory.
    560      * Is this safe with strict aliasing?   I noticed that the other dri code
    561      * does this too...
    562      */
    563     __GLXAquaDrawable *glxPriv = (__GLXAquaDrawable *)base;
    564 
    565     GLAQUA_DEBUG_MSG("TRACE");
    566 
    567     /* It doesn't work to call DRIDestroySurface here, the drawable's
    568        already gone.. But dri.c notices the window destruction and
    569        frees the surface itself. */
    570 
    571     /*gstaplin: verify the statement above.  The surface destroy
    572        *messages weren't making it through, and may still not be.
    573        *We need a good test case for surface creation and destruction.
    574        *We also need a good way to enable introspection on the server
    575        *to validate the test, beyond using gdb with print.
    576      */
    577 
    578     free(glxPriv);
    579 }
    580 
    581 static __GLXdrawable *
    582 __glXAquaScreenCreateDrawable(ClientPtr client,
    583                               __GLXscreen *screen,
    584                               DrawablePtr pDraw,
    585                               XID drawId,
    586                               int type,
    587                               XID glxDrawId,
    588                               __GLXconfig *conf)
    589 {
    590     __GLXAquaDrawable *glxPriv;
    591 
    592     glxPriv = malloc(sizeof *glxPriv);
    593 
    594     if (glxPriv == NULL)
    595         return NULL;
    596 
    597     memset(glxPriv, 0, sizeof *glxPriv);
    598 
    599     if (!__glXDrawableInit(&glxPriv->base, screen, pDraw, type, glxDrawId,
    600                            conf)) {
    601         free(glxPriv);
    602         return NULL;
    603     }
    604 
    605     glxPriv->base.destroy = __glXAquaDrawableDestroy;
    606     glxPriv->base.swapBuffers = __glXAquaDrawableSwapBuffers;
    607     glxPriv->base.copySubBuffer = NULL; /* __glXAquaDrawableCopySubBuffer; */
    608 
    609     glxPriv->pDraw = pDraw;
    610     glxPriv->sid = 0;
    611     glxPriv->context = NULL;
    612 
    613     return &glxPriv->base;
    614 }
    615 
    616 // Extra goodies for glx
    617 
    618 GLuint
    619 __glFloorLog2(GLuint val)
    620 {
    621     int c = 0;
    622 
    623     while (val > 1) {
    624         c++;
    625         val >>= 1;
    626     }
    627     return c;
    628 }
    629 
    630 #ifndef OPENGL_FRAMEWORK_PATH
    631 #define OPENGL_FRAMEWORK_PATH \
    632     "/System/Library/Frameworks/OpenGL.framework/OpenGL"
    633 #endif
    634 
    635 static void *opengl_framework_handle;
    636 
    637 static glx_func_ptr
    638 get_proc_address(const char *sym)
    639 {
    640     return (glx_func_ptr) dlsym(opengl_framework_handle, sym);
    641 }
    642 
    643 static void
    644 setup_dispatch_table(void)
    645 {
    646     const char *opengl_framework_path;
    647 
    648     if (opengl_framework_handle) {
    649         return;
    650     }
    651 
    652     opengl_framework_path = getenv("OPENGL_FRAMEWORK_PATH");
    653     if (!opengl_framework_path) {
    654         opengl_framework_path = OPENGL_FRAMEWORK_PATH;
    655     }
    656 
    657     (void)dlerror();             /*drain dlerror */
    658     opengl_framework_handle = dlopen(opengl_framework_path, RTLD_LOCAL);
    659 
    660     if (!opengl_framework_handle) {
    661         ErrorF("unable to dlopen %s : %s, using RTLD_DEFAULT\n",
    662                opengl_framework_path, dlerror());
    663         opengl_framework_handle = RTLD_DEFAULT;
    664     }
    665 
    666     __glXsetGetProcAddress(get_proc_address);
    667 }