src/video/x11/SDL_x11opengl.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 09 Feb 2009 05:32:12 +0000
changeset 3057 089a77aebb7d
parent 3013 8cc00819c8d6
child 3100 7dc982143c06
permissions -rw-r--r--
Added test program for SDL_CreateWindowFrom()
Make sure OpenGL library is loaded before working with OpenGL windows,
even those created with SDL_CreateWindowFrom()
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with _this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #include "SDL_x11video.h"
    25 
    26 /* GLX implementation of SDL OpenGL support */
    27 
    28 #if SDL_VIDEO_OPENGL_GLX
    29 #include "SDL_loadso.h"
    30 
    31 #if defined(__IRIX__)
    32 /* IRIX doesn't have a GL library versioning system */
    33 #define DEFAULT_OPENGL	"libGL.so"
    34 #elif defined(__MACOSX__)
    35 #define DEFAULT_OPENGL	"/usr/X11R6/lib/libGL.1.dylib"
    36 #elif defined(__QNXNTO__)
    37 #define DEFAULT_OPENGL	"libGL.so.3"
    38 #else
    39 #define DEFAULT_OPENGL	"libGL.so.1"
    40 #endif
    41 
    42 #ifndef GLX_ARB_multisample
    43 #define GLX_ARB_multisample
    44 #define GLX_SAMPLE_BUFFERS_ARB             100000
    45 #define GLX_SAMPLES_ARB                    100001
    46 #endif
    47 
    48 #ifndef GLX_EXT_visual_rating
    49 #define GLX_EXT_visual_rating
    50 #define GLX_VISUAL_CAVEAT_EXT              0x20
    51 #define GLX_NONE_EXT                       0x8000
    52 #define GLX_SLOW_VISUAL_EXT                0x8001
    53 #define GLX_NON_CONFORMANT_VISUAL_EXT      0x800D
    54 #endif
    55 
    56 #define OPENGL_REQUIRS_DLOPEN
    57 #if defined(OPENGL_REQUIRS_DLOPEN) && defined(SDL_LOADSO_DLOPEN)
    58 #include <dlfcn.h>
    59 #define GL_LoadObject(X)	dlopen(X, (RTLD_NOW|RTLD_GLOBAL))
    60 #define GL_LoadFunction		dlsym
    61 #define GL_UnloadObject		dlclose
    62 #else
    63 #define GL_LoadObject	SDL_LoadObject
    64 #define GL_LoadFunction	SDL_LoadFunction
    65 #define GL_UnloadObject	SDL_UnloadObject
    66 #endif
    67 
    68 static void X11_GL_InitExtensions(_THIS);
    69 
    70 int
    71 X11_GL_LoadLibrary(_THIS, const char *path)
    72 {
    73     void *handle;
    74 
    75     /* Load the OpenGL library */
    76     if (path == NULL) {
    77         path = SDL_getenv("SDL_OPENGL_LIBRARY");
    78     }
    79     if (path == NULL) {
    80         path = DEFAULT_OPENGL;
    81     }
    82     _this->gl_config.dll_handle = SDL_LoadObject(path);
    83     if (!_this->gl_config.dll_handle) {
    84         return -1;
    85     }
    86     SDL_strlcpy(_this->gl_config.driver_path, path,
    87                 SDL_arraysize(_this->gl_config.driver_path));
    88 
    89     /* Allocate OpenGL memory */
    90     _this->gl_data =
    91         (struct SDL_GLDriverData *) SDL_calloc(1,
    92                                                sizeof(struct
    93                                                       SDL_GLDriverData));
    94     if (!_this->gl_data) {
    95         SDL_OutOfMemory();
    96         return -1;
    97     }
    98 
    99     /* Load function pointers */
   100     handle = _this->gl_config.dll_handle;
   101     _this->gl_data->glXGetProcAddress =
   102         (void *(*)(const GLubyte *)) GL_LoadFunction(handle,
   103                                                      "glXGetProcAddressARB");
   104     _this->gl_data->glXChooseVisual =
   105         (XVisualInfo * (*)(Display *, int, int *)) GL_LoadFunction(handle,
   106                                                                    "glXChooseVisual");
   107     _this->gl_data->glXCreateContext =
   108         (GLXContext(*)(Display *, XVisualInfo *, GLXContext, int))
   109         GL_LoadFunction(handle, "glXCreateContext");
   110     _this->gl_data->glXDestroyContext =
   111         (void (*)(Display *, GLXContext)) GL_LoadFunction(handle,
   112                                                           "glXDestroyContext");
   113     _this->gl_data->glXMakeCurrent =
   114         (int (*)(Display *, GLXDrawable, GLXContext)) GL_LoadFunction(handle,
   115                                                                       "glXMakeCurrent");
   116     _this->gl_data->glXSwapBuffers =
   117         (void (*)(Display *, GLXDrawable)) GL_LoadFunction(handle,
   118                                                            "glXSwapBuffers");
   119 
   120     if (!_this->gl_data->glXChooseVisual ||
   121         !_this->gl_data->glXCreateContext ||
   122         !_this->gl_data->glXDestroyContext ||
   123         !_this->gl_data->glXMakeCurrent || !_this->gl_data->glXSwapBuffers) {
   124         SDL_SetError("Could not retrieve OpenGL functions");
   125         return -1;
   126     }
   127 
   128     /* Initialize extensions */
   129     X11_GL_InitExtensions(_this);
   130 
   131     return 0;
   132 }
   133 
   134 void *
   135 X11_GL_GetProcAddress(_THIS, const char *proc)
   136 {
   137     void *handle;
   138 
   139     handle = _this->gl_config.dll_handle;
   140     if (_this->gl_data->glXGetProcAddress) {
   141         return _this->gl_data->glXGetProcAddress((const GLubyte *) proc);
   142     }
   143     return GL_LoadFunction(handle, proc);
   144 }
   145 
   146 void
   147 X11_GL_UnloadLibrary(_THIS)
   148 {
   149     /* Don't actually unload the library, since it may have registered
   150      * X11 shutdown hooks, per the notes at:
   151      * http://dri.sourceforge.net/doc/DRIuserguide.html
   152      */
   153 #if 0
   154     GL_UnloadObject(_this->gl_config.dll_handle);
   155     _this->gl_config.dll_handle = NULL;
   156 #endif
   157 
   158     /* Free OpenGL memory */
   159     SDL_free(_this->gl_data);
   160     _this->gl_data = NULL;
   161 }
   162 
   163 static SDL_bool
   164 HasExtension(const char *extension, const char *extensions)
   165 {
   166     const char *start;
   167     const char *where, *terminator;
   168 
   169     /* Extension names should not have spaces. */
   170     where = SDL_strchr(extension, ' ');
   171     if (where || *extension == '\0')
   172         return SDL_FALSE;
   173 
   174     if (!extensions)
   175         return SDL_FALSE;
   176 
   177     /* It takes a bit of care to be fool-proof about parsing the
   178      * OpenGL extensions string. Don't be fooled by sub-strings,
   179      * etc. */
   180 
   181     start = extensions;
   182 
   183     for (;;) {
   184         where = SDL_strstr(start, extension);
   185         if (!where)
   186             break;
   187 
   188         terminator = where + SDL_strlen(extension);
   189         if (where == start || *(where - 1) == ' ')
   190             if (*terminator == ' ' || *terminator == '\0')
   191                 return SDL_TRUE;
   192 
   193         start = terminator;
   194     }
   195     return SDL_FALSE;
   196 }
   197 
   198 static void
   199 X11_GL_InitExtensions(_THIS)
   200 {
   201     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   202     int screen = ((SDL_DisplayData *) SDL_CurrentDisplay.driverdata)->screen;
   203     XVisualInfo *vinfo;
   204     XSetWindowAttributes xattr;
   205     Window w;
   206     GLXContext context;
   207     const char *(*glXQueryExtensionsStringFunc) (Display *, int);
   208     const char *extensions;
   209 
   210     vinfo = X11_GL_GetVisual(_this, display, screen);
   211     if (!vinfo) {
   212         return;
   213     }
   214     xattr.background_pixel = 0;
   215     xattr.border_pixel = 0;
   216     xattr.colormap =
   217         XCreateColormap(display, RootWindow(display, screen), vinfo->visual,
   218                         AllocNone);
   219     w = XCreateWindow(display, RootWindow(display, screen), 0, 0, 32, 32, 0,
   220                       vinfo->depth, InputOutput, vinfo->visual,
   221                       (CWBackPixel | CWBorderPixel | CWColormap), &xattr);
   222     context = _this->gl_data->glXCreateContext(display, vinfo, NULL, True);
   223     if (context) {
   224         _this->gl_data->glXMakeCurrent(display, w, context);
   225     }
   226     XFree(vinfo);
   227 
   228     glXQueryExtensionsStringFunc =
   229         (const char *(*)(Display *, int)) X11_GL_GetProcAddress(_this,
   230                                                                 "glXQueryExtensionsString");
   231     if (glXQueryExtensionsStringFunc) {
   232         extensions = glXQueryExtensionsStringFunc(display, screen);
   233     } else {
   234         extensions = NULL;
   235     }
   236 
   237     /* Check for SGI_swap_control */
   238     if (HasExtension("GLX_SGI_swap_control", extensions)) {
   239         _this->gl_data->glXSwapIntervalSGI =
   240             (int (*)(int)) X11_GL_GetProcAddress(_this, "glXSwapIntervalSGI");
   241     }
   242 
   243     /* Check for GLX_MESA_swap_control */
   244     if (HasExtension("GLX_MESA_swap_control", extensions)) {
   245         _this->gl_data->glXSwapIntervalMESA =
   246             (GLint(*)(unsigned)) X11_GL_GetProcAddress(_this,
   247                                                        "glXSwapIntervalMESA");
   248         _this->gl_data->glXGetSwapIntervalMESA =
   249             (GLint(*)(void)) X11_GL_GetProcAddress(_this,
   250                                                    "glXGetSwapIntervalMESA");
   251     }
   252 
   253     /* Check for GLX_EXT_visual_rating */
   254     if (HasExtension("GLX_EXT_visual_rating", extensions)) {
   255         _this->gl_data->HAS_GLX_EXT_visual_rating = SDL_TRUE;
   256     }
   257 
   258     if (context) {
   259         _this->gl_data->glXMakeCurrent(display, None, NULL);
   260         _this->gl_data->glXDestroyContext(display, context);
   261     }
   262     XDestroyWindow(display, w);
   263     X11_PumpEvents(_this);
   264 }
   265 
   266 XVisualInfo *
   267 X11_GL_GetVisual(_THIS, Display * display, int screen)
   268 {
   269     XVisualInfo *vinfo;
   270 
   271     /* 64 seems nice. */
   272     int attribs[64];
   273     int i;
   274 
   275     /* Setup our GLX attributes according to the gl_config. */
   276     i = 0;
   277     attribs[i++] = GLX_RGBA;
   278     attribs[i++] = GLX_RED_SIZE;
   279     attribs[i++] = _this->gl_config.red_size;
   280     attribs[i++] = GLX_GREEN_SIZE;
   281     attribs[i++] = _this->gl_config.green_size;
   282     attribs[i++] = GLX_BLUE_SIZE;
   283     attribs[i++] = _this->gl_config.blue_size;
   284 
   285     if (_this->gl_config.alpha_size) {
   286         attribs[i++] = GLX_ALPHA_SIZE;
   287         attribs[i++] = _this->gl_config.alpha_size;
   288     }
   289 
   290     if (_this->gl_config.buffer_size) {
   291         attribs[i++] = GLX_BUFFER_SIZE;
   292         attribs[i++] = _this->gl_config.buffer_size;
   293     }
   294 
   295     if (_this->gl_config.double_buffer) {
   296         attribs[i++] = GLX_DOUBLEBUFFER;
   297     }
   298 
   299     attribs[i++] = GLX_DEPTH_SIZE;
   300     attribs[i++] = _this->gl_config.depth_size;
   301 
   302     if (_this->gl_config.stencil_size) {
   303         attribs[i++] = GLX_STENCIL_SIZE;
   304         attribs[i++] = _this->gl_config.stencil_size;
   305     }
   306 
   307     if (_this->gl_config.accum_red_size) {
   308         attribs[i++] = GLX_ACCUM_RED_SIZE;
   309         attribs[i++] = _this->gl_config.accum_red_size;
   310     }
   311 
   312     if (_this->gl_config.accum_green_size) {
   313         attribs[i++] = GLX_ACCUM_GREEN_SIZE;
   314         attribs[i++] = _this->gl_config.accum_green_size;
   315     }
   316 
   317     if (_this->gl_config.accum_blue_size) {
   318         attribs[i++] = GLX_ACCUM_BLUE_SIZE;
   319         attribs[i++] = _this->gl_config.accum_blue_size;
   320     }
   321 
   322     if (_this->gl_config.accum_alpha_size) {
   323         attribs[i++] = GLX_ACCUM_ALPHA_SIZE;
   324         attribs[i++] = _this->gl_config.accum_alpha_size;
   325     }
   326 
   327     if (_this->gl_config.stereo) {
   328         attribs[i++] = GLX_STEREO;
   329     }
   330 
   331     if (_this->gl_config.multisamplebuffers) {
   332         attribs[i++] = GLX_SAMPLE_BUFFERS_ARB;
   333         attribs[i++] = _this->gl_config.multisamplebuffers;
   334     }
   335 
   336     if (_this->gl_config.multisamplesamples) {
   337         attribs[i++] = GLX_SAMPLES_ARB;
   338         attribs[i++] = _this->gl_config.multisamplesamples;
   339     }
   340 
   341     if (_this->gl_config.accelerated >= 0
   342         && _this->gl_data->HAS_GLX_EXT_visual_rating) {
   343         attribs[i++] = GLX_VISUAL_CAVEAT_EXT;
   344         attribs[i++] = GLX_NONE_EXT;
   345     }
   346 #ifdef GLX_DIRECT_COLOR         /* Try for a DirectColor visual for gamma support */
   347     if (X11_UseDirectColorVisuals()) {
   348         attribs[i++] = GLX_X_VISUAL_TYPE;
   349         attribs[i++] = GLX_DIRECT_COLOR;
   350     }
   351 #endif
   352     attribs[i++] = None;
   353 
   354     vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
   355 #ifdef GLX_DIRECT_COLOR
   356     if (!vinfo && X11_UseDirectColorVisuals()) {        /* No DirectColor visual?  Try again.. */
   357         attribs[i - 3] = None;
   358         vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
   359     }
   360 #endif
   361     if (!vinfo) {
   362         SDL_SetError("Couldn't find matching GLX visual");
   363     }
   364     return vinfo;
   365 }
   366 
   367 SDL_GLContext
   368 X11_GL_CreateContext(_THIS, SDL_Window * window)
   369 {
   370     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   371     Display *display = data->videodata->display;
   372     int screen =
   373         ((SDL_DisplayData *) SDL_GetDisplayFromWindow(window)->
   374          driverdata)->screen;
   375     XWindowAttributes xattr;
   376     XVisualInfo v, *vinfo;
   377     int n;
   378     GLXContext context = NULL;
   379 
   380     /* We do this to create a clean separation between X and GLX errors. */
   381     XSync(display, False);
   382     XGetWindowAttributes(display, data->window, &xattr);
   383     v.screen = screen;
   384     v.visualid = XVisualIDFromVisual(xattr.visual);
   385     vinfo = XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &v, &n);
   386     if (vinfo) {
   387         context =
   388             _this->gl_data->glXCreateContext(display, vinfo, NULL, True);
   389         XFree(vinfo);
   390     }
   391     XSync(display, False);
   392 
   393     if (!context) {
   394         SDL_SetError("Could not create GL context");
   395         return NULL;
   396     }
   397 
   398     if (X11_GL_MakeCurrent(_this, window, context) < 0) {
   399         X11_GL_DeleteContext(_this, context);
   400         return NULL;
   401     }
   402 
   403     return context;
   404 }
   405 
   406 int
   407 X11_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
   408 {
   409     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   410     Window drawable =
   411         (window ? ((SDL_WindowData *) window->driverdata)->window : None);
   412     GLXContext glx_context = (GLXContext) context;
   413     int status;
   414 
   415     status = 0;
   416     if (!_this->gl_data->glXMakeCurrent(display, drawable, glx_context)) {
   417         SDL_SetError("Unable to make GL context current");
   418         status = -1;
   419     }
   420     XSync(display, False);
   421 
   422     return (status);
   423 }
   424 
   425 /* 
   426    0 is a valid argument to glxSwapIntervalMESA and setting it to 0
   427    with the MESA version of the extension will undo the effect of a
   428    previous call with a value that is greater than zero (or at least
   429    that is what the FM says. OTOH, 0 is an invalid argument to
   430    glxSwapIntervalSGI and it returns an error if you call it with 0 as
   431    an argument.
   432 */
   433 
   434 static int swapinterval = -1;
   435 int
   436 X11_GL_SetSwapInterval(_THIS, int interval)
   437 {
   438     int status;
   439 
   440     if (_this->gl_data->glXSwapIntervalMESA) {
   441         status = _this->gl_data->glXSwapIntervalMESA(interval);
   442         if (status != 0) {
   443             SDL_SetError("glxSwapIntervalMESA failed");
   444             status = -1;
   445         } else {
   446             swapinterval = interval;
   447         }
   448     } else if (_this->gl_data->glXSwapIntervalSGI) {
   449         status = _this->gl_data->glXSwapIntervalSGI(interval);
   450         if (status != 0) {
   451             SDL_SetError("glxSwapIntervalSGI failed");
   452             status = -1;
   453         } else {
   454             swapinterval = interval;
   455         }
   456     } else {
   457         SDL_Unsupported();
   458         status = -1;
   459     }
   460     return status;
   461 }
   462 
   463 int
   464 X11_GL_GetSwapInterval(_THIS)
   465 {
   466     if (_this->gl_data->glXGetSwapIntervalMESA) {
   467         return _this->gl_data->glXGetSwapIntervalMESA();
   468     } else {
   469         return swapinterval;
   470     }
   471 }
   472 
   473 void
   474 X11_GL_SwapWindow(_THIS, SDL_Window * window)
   475 {
   476     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   477     Display *display = data->videodata->display;
   478 
   479     _this->gl_data->glXSwapBuffers(display, data->window);
   480 }
   481 
   482 void
   483 X11_GL_DeleteContext(_THIS, SDL_GLContext context)
   484 {
   485     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   486     GLXContext glx_context = (GLXContext) context;
   487 
   488     _this->gl_data->glXDestroyContext(display, glx_context);
   489     XSync(display, False);
   490 }
   491 
   492 #endif /* SDL_VIDEO_OPENGL_GLX */
   493 
   494 /* vi: set ts=4 sw=4 expandtab: */