src/video/x11/SDL_x11opengl.c
author Bob Pendleton <bob@pendleton.com>
Fri, 07 Mar 2008 20:54:11 +0000
changeset 2325 c7bcf84ba1b9
parent 2324 3202e4826c57
child 2327 7b53a8401195
permissions -rw-r--r--
Next version of internationalized input for X11. On my machine (famous last words :-) with a US English keyboard and locale I can compose ` and e and get a text
input event with the character รจ. You still get the keypress keyrelease events for the individual keys that go into composing the character.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 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 int X11_GL_InitializeMemory(_THIS);
    69 
    70 int
    71 X11_GL_LoadLibrary(_THIS, const char *path)
    72 {
    73     void *handle;
    74 
    75     if (_this->gl_config.driver_loaded) {
    76         if (path) {
    77             SDL_SetError("OpenGL library already loaded");
    78             return -1;
    79         } else {
    80             ++_this->gl_config.driver_loaded;
    81             return 0;
    82         }
    83     }
    84     if (path == NULL) {
    85         path = SDL_getenv("SDL_OPENGL_LIBRARY");
    86     }
    87     if (path == NULL) {
    88         path = DEFAULT_OPENGL;
    89     }
    90     handle = GL_LoadObject(path);
    91     if (!handle) {
    92         return -1;
    93     }
    94     // LoadLibrary may be called before WindowCreate!
    95     // Must create the memory used by GL
    96     X11_GL_InitializeMemory(_this);
    97 
    98     /* Load new function pointers */
    99     _this->gl_data->glXGetProcAddress =
   100         (void *(*)(const GLubyte *)) GL_LoadFunction(handle,
   101                                                      "glXGetProcAddressARB");
   102     _this->gl_data->glXChooseVisual =
   103         (XVisualInfo * (*)(Display *, int, int *)) GL_LoadFunction(handle,
   104                                                                    "glXChooseVisual");
   105     _this->gl_data->glXCreateContext =
   106         (GLXContext(*)(Display *, XVisualInfo *, GLXContext, int))
   107         GL_LoadFunction(handle, "glXCreateContext");
   108     _this->gl_data->glXDestroyContext =
   109         (void (*)(Display *, GLXContext)) GL_LoadFunction(handle,
   110                                                           "glXDestroyContext");
   111     _this->gl_data->glXMakeCurrent =
   112         (int (*)(Display *, GLXDrawable, GLXContext)) GL_LoadFunction(handle,
   113                                                                       "glXMakeCurrent");
   114     _this->gl_data->glXSwapBuffers =
   115         (void (*)(Display *, GLXDrawable)) GL_LoadFunction(handle,
   116                                                            "glXSwapBuffers");
   117 
   118     if (!_this->gl_data->glXChooseVisual ||
   119         !_this->gl_data->glXCreateContext ||
   120         !_this->gl_data->glXDestroyContext ||
   121         !_this->gl_data->glXMakeCurrent || !_this->gl_data->glXSwapBuffers) {
   122         SDL_SetError("Could not retrieve OpenGL functions");
   123         return -1;
   124     }
   125 
   126     _this->gl_config.dll_handle = handle;
   127     SDL_strlcpy(_this->gl_config.driver_path, path,
   128                 SDL_arraysize(_this->gl_config.driver_path));
   129     _this->gl_config.driver_loaded = 1;
   130     return 0;
   131 }
   132 
   133 void *
   134 X11_GL_GetProcAddress(_THIS, const char *proc)
   135 {
   136     void *handle;
   137 
   138     handle = _this->gl_config.dll_handle;
   139     if (_this->gl_data->glXGetProcAddress) {
   140         return _this->gl_data->glXGetProcAddress((const GLubyte *) proc);
   141     }
   142     return GL_LoadFunction(handle, proc);
   143 }
   144 
   145 static void
   146 X11_GL_UnloadLibrary(_THIS)
   147 {
   148     if (_this->gl_config.driver_loaded > 0) {
   149         if (--_this->gl_config.driver_loaded > 0) {
   150             return;
   151         }
   152         GL_UnloadObject(_this->gl_config.dll_handle);
   153         _this->gl_config.dll_handle = NULL;
   154     }
   155 }
   156 
   157 static SDL_bool
   158 HasExtension(const char *extension, const char *extensions)
   159 {
   160     const char *start;
   161     const char *where, *terminator;
   162 
   163     /* Extension names should not have spaces. */
   164     where = SDL_strchr(extension, ' ');
   165     if (where || *extension == '\0')
   166         return SDL_FALSE;
   167 
   168     if (!extensions)
   169         return SDL_FALSE;
   170 
   171     /* It takes a bit of care to be fool-proof about parsing the
   172      * OpenGL extensions string. Don't be fooled by sub-strings,
   173      * etc. */
   174 
   175     start = extensions;
   176 
   177     for (;;) {
   178         where = SDL_strstr(start, extension);
   179         if (!where)
   180             break;
   181 
   182         terminator = where + SDL_strlen(extension);
   183         if (where == start || *(where - 1) == ' ')
   184             if (*terminator == ' ' || *terminator == '\0')
   185                 return SDL_TRUE;
   186 
   187         start = terminator;
   188     }
   189     return SDL_FALSE;
   190 }
   191 
   192 static void
   193 X11_GL_InitExtensions(_THIS)
   194 {
   195     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   196     int screen = ((SDL_DisplayData *) SDL_CurrentDisplay.driverdata)->screen;
   197     XVisualInfo *vinfo;
   198     XSetWindowAttributes xattr;
   199     Window w;
   200     GLXContext context;
   201     const char *(*glXQueryExtensionsStringFunc) (Display *, int);
   202     const char *extensions;
   203 
   204     vinfo = X11_GL_GetVisual(_this, display, screen);
   205     if (!vinfo) {
   206         return;
   207     }
   208     xattr.background_pixel = 0;
   209     xattr.border_pixel = 0;
   210     xattr.colormap =
   211         XCreateColormap(display, RootWindow(display, screen), vinfo->visual,
   212                         AllocNone);
   213     w = XCreateWindow(display, RootWindow(display, screen), 0, 0, 32, 32, 0,
   214                       vinfo->depth, InputOutput, vinfo->visual,
   215                       (CWBackPixel | CWBorderPixel | CWColormap), &xattr);
   216     context = _this->gl_data->glXCreateContext(display, vinfo, NULL, True);
   217     if (context) {
   218         _this->gl_data->glXMakeCurrent(display, w, context);
   219     }
   220     XFree(vinfo);
   221 
   222     glXQueryExtensionsStringFunc =
   223         (const char *(*)(Display *, int)) X11_GL_GetProcAddress(_this,
   224                                                                 "glXQueryExtensionsString");
   225     if (glXQueryExtensionsStringFunc) {
   226         extensions = glXQueryExtensionsStringFunc(display, screen);
   227     } else {
   228         extensions = NULL;
   229     }
   230 
   231     /* Check for SGI_swap_control */
   232     if (HasExtension("GLX_SGI_swap_control", extensions)) {
   233         _this->gl_data->glXSwapIntervalSGI =
   234             (int (*)(int)) X11_GL_GetProcAddress(_this, "glXSwapIntervalSGI");
   235     }
   236 
   237     /* Check for GLX_MESA_swap_control */
   238     if (HasExtension("GLX_MESA_swap_control", extensions)) {
   239         _this->gl_data->glXSwapIntervalMESA =
   240             (GLint(*)(unsigned)) X11_GL_GetProcAddress(_this,
   241                                                        "glXSwapIntervalMESA");
   242         _this->gl_data->glXGetSwapIntervalMESA =
   243             (GLint(*)(void)) X11_GL_GetProcAddress(_this,
   244                                                    "glXGetSwapIntervalMESA");
   245     }
   246 
   247     /* Check for GLX_EXT_visual_rating */
   248     if (HasExtension("GLX_EXT_visual_rating", extensions)) {
   249         _this->gl_data->HAS_GLX_EXT_visual_rating = SDL_TRUE;
   250     }
   251 
   252     if (context) {
   253         _this->gl_data->glXMakeCurrent(display, None, NULL);
   254         _this->gl_data->glXDestroyContext(display, context);
   255     }
   256     XDestroyWindow(display, w);
   257     X11_PumpEvents(_this);
   258 }
   259 
   260 static int
   261 X11_GL_InitializeMemory(_THIS)
   262 {
   263     if (_this->gl_data) {
   264         return 0;
   265     }
   266 
   267     _this->gl_data =
   268         (struct SDL_GLDriverData *) SDL_calloc(1,
   269                                                sizeof(struct
   270                                                       SDL_GLDriverData));
   271     if (!_this->gl_data) {
   272         SDL_OutOfMemory();
   273         return -1;
   274     }
   275     _this->gl_data->initialized = 0;
   276 
   277     return 0;
   278 }
   279 
   280 int
   281 X11_GL_Initialize(_THIS)
   282 {
   283 
   284     if (X11_GL_InitializeMemory(_this) < 0) {
   285         return -1;
   286     }
   287     ++_this->gl_data->initialized;
   288 
   289     if (X11_GL_LoadLibrary(_this, NULL) < 0) {
   290         return -1;
   291     }
   292 
   293     /* Initialize extensions */
   294     X11_GL_InitExtensions(_this);
   295 
   296     return 0;
   297 }
   298 
   299 void
   300 X11_GL_Shutdown(_THIS)
   301 {
   302     if (!_this->gl_data || (--_this->gl_data->initialized > 0)) {
   303         return;
   304     }
   305 
   306     /* Don't actually unload the library, since it may have registered
   307      * X11 shutdown hooks, per the notes at:
   308      * http://dri.sourceforge.net/doc/DRIuserguide.html
   309      * //X11_GL_UnloadLibrary(_this);
   310      */
   311 
   312     SDL_free(_this->gl_data);
   313     _this->gl_data = NULL;
   314 }
   315 
   316 XVisualInfo *
   317 X11_GL_GetVisual(_THIS, Display * display, int screen)
   318 {
   319     XVisualInfo *vinfo;
   320 
   321     /* 64 seems nice. */
   322     int attribs[64];
   323     int i;
   324 
   325     /* Setup our GLX attributes according to the gl_config. */
   326     i = 0;
   327     attribs[i++] = GLX_RGBA;
   328     attribs[i++] = GLX_RED_SIZE;
   329     attribs[i++] = _this->gl_config.red_size;
   330     attribs[i++] = GLX_GREEN_SIZE;
   331     attribs[i++] = _this->gl_config.green_size;
   332     attribs[i++] = GLX_BLUE_SIZE;
   333     attribs[i++] = _this->gl_config.blue_size;
   334 
   335     if (_this->gl_config.alpha_size) {
   336         attribs[i++] = GLX_ALPHA_SIZE;
   337         attribs[i++] = _this->gl_config.alpha_size;
   338     }
   339 
   340     if (_this->gl_config.buffer_size) {
   341         attribs[i++] = GLX_BUFFER_SIZE;
   342         attribs[i++] = _this->gl_config.buffer_size;
   343     }
   344 
   345     if (_this->gl_config.double_buffer) {
   346         attribs[i++] = GLX_DOUBLEBUFFER;
   347     }
   348 
   349     attribs[i++] = GLX_DEPTH_SIZE;
   350     attribs[i++] = _this->gl_config.depth_size;
   351 
   352     if (_this->gl_config.stencil_size) {
   353         attribs[i++] = GLX_STENCIL_SIZE;
   354         attribs[i++] = _this->gl_config.stencil_size;
   355     }
   356 
   357     if (_this->gl_config.accum_red_size) {
   358         attribs[i++] = GLX_ACCUM_RED_SIZE;
   359         attribs[i++] = _this->gl_config.accum_red_size;
   360     }
   361 
   362     if (_this->gl_config.accum_green_size) {
   363         attribs[i++] = GLX_ACCUM_GREEN_SIZE;
   364         attribs[i++] = _this->gl_config.accum_green_size;
   365     }
   366 
   367     if (_this->gl_config.accum_blue_size) {
   368         attribs[i++] = GLX_ACCUM_BLUE_SIZE;
   369         attribs[i++] = _this->gl_config.accum_blue_size;
   370     }
   371 
   372     if (_this->gl_config.accum_alpha_size) {
   373         attribs[i++] = GLX_ACCUM_ALPHA_SIZE;
   374         attribs[i++] = _this->gl_config.accum_alpha_size;
   375     }
   376 
   377     if (_this->gl_config.stereo) {
   378         attribs[i++] = GLX_STEREO;
   379     }
   380 
   381     if (_this->gl_config.multisamplebuffers) {
   382         attribs[i++] = GLX_SAMPLE_BUFFERS_ARB;
   383         attribs[i++] = _this->gl_config.multisamplebuffers;
   384     }
   385 
   386     if (_this->gl_config.multisamplesamples) {
   387         attribs[i++] = GLX_SAMPLES_ARB;
   388         attribs[i++] = _this->gl_config.multisamplesamples;
   389     }
   390 
   391     if (_this->gl_config.accelerated >= 0
   392         && _this->gl_data->HAS_GLX_EXT_visual_rating) {
   393         attribs[i++] = GLX_VISUAL_CAVEAT_EXT;
   394         attribs[i++] = GLX_NONE_EXT;
   395     }
   396 #ifdef GLX_DIRECT_COLOR         /* Try for a DirectColor visual for gamma support */
   397     if (!SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR")) {
   398         attribs[i++] = GLX_X_VISUAL_TYPE;
   399         attribs[i++] = GLX_DIRECT_COLOR;
   400     }
   401 #endif
   402     attribs[i++] = None;
   403 
   404     vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
   405 #ifdef GLX_DIRECT_COLOR
   406     if (!vinfo && !SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR")) { /* No DirectColor visual?  Try again.. */
   407         attribs[i - 3] = None;
   408         vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
   409     }
   410 #endif
   411     if (!vinfo) {
   412         SDL_SetError("Couldn't find matching GLX visual");
   413     }
   414     return vinfo;
   415 }
   416 
   417 SDL_GLContext
   418 X11_GL_CreateContext(_THIS, SDL_Window * window)
   419 {
   420     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   421     Display *display = data->videodata->display;
   422     int screen =
   423         ((SDL_DisplayData *) SDL_GetDisplayFromWindow(window)->driverdata)->
   424         screen;
   425     XWindowAttributes xattr;
   426     XVisualInfo v, *vinfo;
   427     int n;
   428     GLXContext context = NULL;
   429 
   430     /* We do this to create a clean separation between X and GLX errors. */
   431     XSync(display, False);
   432     XGetWindowAttributes(display, data->window, &xattr);
   433     v.screen = screen;
   434     v.visualid = XVisualIDFromVisual(xattr.visual);
   435     vinfo = XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &v, &n);
   436     if (vinfo) {
   437         context =
   438             _this->gl_data->glXCreateContext(display, vinfo, NULL, True);
   439         XFree(vinfo);
   440     }
   441     XSync(display, False);
   442 
   443     if (!context) {
   444         SDL_SetError("Could not create GL context");
   445         return NULL;
   446     }
   447 
   448     if (X11_GL_MakeCurrent(_this, window, context) < 0) {
   449         X11_GL_DeleteContext(_this, context);
   450         return NULL;
   451     }
   452 
   453     return context;
   454 }
   455 
   456 int
   457 X11_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
   458 {
   459     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   460     Window drawable =
   461         (window ? ((SDL_WindowData *) window->driverdata)->window : None);
   462     GLXContext glx_context = (GLXContext) context;
   463     int status;
   464 
   465     status = 0;
   466     if (!_this->gl_data->glXMakeCurrent(display, drawable, glx_context)) {
   467         SDL_SetError("Unable to make GL context current");
   468         status = -1;
   469     }
   470     XSync(display, False);
   471 
   472     return (status);
   473 }
   474 
   475 int
   476 X11_GL_SetSwapInterval(_THIS, int interval)
   477 {
   478     int status;
   479 
   480     if (_this->gl_data->glXSwapIntervalMESA) {
   481         status = _this->gl_data->glXSwapIntervalMESA(interval);
   482         if (status != 0) {
   483             SDL_SetError("glxSwapIntervalMESA failed");
   484             status = -1;
   485         }
   486     } else if (_this->gl_data->glXSwapIntervalSGI) {
   487         status = _this->gl_data->glXSwapIntervalSGI(interval);
   488         if (status != 0) {
   489             SDL_SetError("glxSwapIntervalSGI failed");
   490             status = -1;
   491         }
   492     } else {
   493         SDL_Unsupported();
   494         status = -1;
   495     }
   496     return status;
   497 }
   498 
   499 int
   500 X11_GL_GetSwapInterval(_THIS)
   501 {
   502     if (_this->gl_data->glXGetSwapIntervalMESA) {
   503         return _this->gl_data->glXGetSwapIntervalMESA();
   504     } else {
   505         SDL_Unsupported();
   506         return -1;
   507     }
   508 }
   509 
   510 void
   511 X11_GL_SwapWindow(_THIS, SDL_Window * window)
   512 {
   513     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   514     Display *display = data->videodata->display;
   515 
   516     _this->gl_data->glXSwapBuffers(display, data->window);
   517 }
   518 
   519 void
   520 X11_GL_DeleteContext(_THIS, SDL_GLContext context)
   521 {
   522     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   523     GLXContext glx_context = (GLXContext) context;
   524 
   525     _this->gl_data->glXDestroyContext(display, glx_context);
   526     XSync(display, False);
   527 }
   528 
   529 #endif /* SDL_VIDEO_OPENGL_GLX */
   530 
   531 /* vi: set ts=4 sw=4 expandtab: */