diff src/video/x11/SDL_x11opengl.c @ 1952:420716272158

Implemented X11 OpenGL support. Added support for the SDL_VIDEO_OPENGL environment variable.
author Sam Lantinga <slouken@libsdl.org>
date Fri, 28 Jul 2006 08:43:17 +0000
parents
children 214880ed48c3
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/x11/SDL_x11opengl.c	Fri Jul 28 08:43:17 2006 +0000
@@ -0,0 +1,507 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997-2006 Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with _this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+#include "SDL_x11video.h"
+
+/* GLX implementation of SDL OpenGL support */
+
+#if SDL_VIDEO_OPENGL_GLX
+#include "SDL_loadso.h"
+
+#if defined(__IRIX__)
+/* IRIX doesn't have a GL library versioning system */
+#define DEFAULT_OPENGL	"libGL.so"
+#elif defined(__MACOSX__)
+#define DEFAULT_OPENGL	"/usr/X11R6/lib/libGL.1.dylib"
+#elif defined(__QNXNTO__)
+#define DEFAULT_OPENGL	"libGL.so.3"
+#else
+#define DEFAULT_OPENGL	"libGL.so.1"
+#endif
+
+#ifndef GLX_ARB_multisample
+#define GLX_ARB_multisample
+#define GLX_SAMPLE_BUFFERS_ARB             100000
+#define GLX_SAMPLES_ARB                    100001
+#endif
+
+#ifndef GLX_EXT_visual_rating
+#define GLX_EXT_visual_rating
+#define GLX_VISUAL_CAVEAT_EXT              0x20
+#define GLX_NONE_EXT                       0x8000
+#define GLX_SLOW_VISUAL_EXT                0x8001
+#define GLX_NON_CONFORMANT_VISUAL_EXT      0x800D
+#endif
+
+#define OPENGL_REQUIRS_DLOPEN
+#if defined(OPENGL_REQUIRS_DLOPEN) && defined(SDL_LOADSO_DLOPEN)
+#include <dlfcn.h>
+#define GL_LoadObject(X)	dlopen(X, (RTLD_NOW|RTLD_GLOBAL))
+#define GL_LoadFunction		dlsym
+#define GL_UnloadObject		dlclose
+#else
+#define GL_LoadObject	SDL_LoadObject
+#define GL_LoadFunction	SDL_LoadFunction
+#define GL_UnloadObject	SDL_UnloadObject
+#endif
+
+int
+X11_GL_LoadLibrary(_THIS, const char *path)
+{
+    void *handle;
+
+    if (_this->gl_config.driver_loaded) {
+        if (path) {
+            SDL_SetError("OpenGL library already loaded");
+            return -1;
+        } else {
+            ++_this->gl_config.driver_loaded;
+            return 0;
+        }
+    }
+    if (path == NULL) {
+        path = SDL_getenv("SDL_OPENGL_LIBRARY");
+    }
+    if (path == NULL) {
+        path = DEFAULT_OPENGL;
+    }
+    handle = GL_LoadObject(path);
+    if (!handle) {
+        return -1;
+    }
+
+    /* Load new function pointers */
+    _this->gl_data->glXGetProcAddress =
+        (void *(*)(const GLubyte *)) GL_LoadFunction(handle,
+                                                     "glXGetProcAddressARB");
+    _this->gl_data->glXChooseVisual =
+        (XVisualInfo * (*)(Display *, int, int *)) GL_LoadFunction(handle,
+                                                                   "glXChooseVisual");
+    _this->gl_data->glXCreateContext =
+        (GLXContext(*)(Display *, XVisualInfo *, GLXContext, int))
+        GL_LoadFunction(handle, "glXCreateContext");
+    _this->gl_data->glXDestroyContext =
+        (void (*)(Display *, GLXContext)) GL_LoadFunction(handle,
+                                                          "glXDestroyContext");
+    _this->gl_data->glXMakeCurrent =
+        (int (*)(Display *, GLXDrawable, GLXContext)) GL_LoadFunction(handle,
+                                                                      "glXMakeCurrent");
+    _this->gl_data->glXSwapBuffers =
+        (void (*)(Display *, GLXDrawable)) GL_LoadFunction(handle,
+                                                           "glXSwapBuffers");
+    _this->gl_data->glXGetConfig =
+        (int (*)(Display *, XVisualInfo *, int, int *))
+        GL_LoadFunction(handle, "glXGetConfig");
+
+    if (!_this->gl_data->glXChooseVisual ||
+        !_this->gl_data->glXCreateContext ||
+        !_this->gl_data->glXDestroyContext ||
+        !_this->gl_data->glXMakeCurrent ||
+        !_this->gl_data->glXSwapBuffers || !_this->gl_data->glXGetConfig) {
+        SDL_SetError("Could not retrieve OpenGL functions");
+        return -1;
+    }
+
+    _this->gl_config.dll_handle = handle;
+    SDL_strlcpy(_this->gl_config.driver_path, path,
+                SDL_arraysize(_this->gl_config.driver_path));
+    _this->gl_config.driver_loaded = 1;
+    return 0;
+}
+
+void *
+X11_GL_GetProcAddress(_THIS, const char *proc)
+{
+    void *handle;
+
+    handle = _this->gl_config.dll_handle;
+    if (_this->gl_data->glXGetProcAddress) {
+        return _this->gl_data->glXGetProcAddress((const GLubyte *) proc);
+    }
+    return GL_LoadFunction(handle, proc);
+}
+
+static void
+X11_GL_UnloadLibrary(_THIS)
+{
+    if (_this->gl_config.driver_loaded > 0) {
+        if (--_this->gl_config.driver_loaded > 0) {
+            return;
+        }
+        GL_UnloadObject(_this->gl_config.dll_handle);
+        _this->gl_config.dll_handle = NULL;
+    }
+}
+
+static SDL_bool
+HasExtension(const char *extension, const char *extensions)
+{
+    const char *start;
+    const char *where, *terminator;
+
+    /* Extension names should not have spaces. */
+    where = SDL_strchr(extension, ' ');
+    if (where || *extension == '\0')
+        return SDL_FALSE;
+
+    if (!extensions)
+        return SDL_FALSE;
+
+    /* It takes a bit of care to be fool-proof about parsing the
+     * OpenGL extensions string. Don't be fooled by sub-strings,
+     * etc. */
+
+    start = extensions;
+
+    for (;;) {
+        where = SDL_strstr(start, extension);
+        if (!where)
+            break;
+
+        terminator = where + SDL_strlen(extension);
+        if (where == start || *(where - 1) == ' ')
+            if (*terminator == ' ' || *terminator == '\0')
+                return SDL_TRUE;
+
+        start = terminator;
+    }
+    return SDL_FALSE;
+}
+
+static void
+X11_GL_InitExtensions(_THIS)
+{
+    Display *display = ((SDL_VideoData *) _this->driverdata)->display;
+    int screen = ((SDL_DisplayData *) SDL_CurrentDisplay.driverdata)->screen;
+    XVisualInfo *vinfo;
+    XSetWindowAttributes xattr;
+    Window w;
+    GLXContext context;
+    const char *(*glXQueryExtensionsStringFunc) (Display *, int);
+    const char *extensions;
+
+    vinfo = X11_GL_GetVisual(_this, display, screen);
+    if (!vinfo) {
+        return;
+    }
+    xattr.background_pixel = 0;
+    xattr.border_pixel = 0;
+    xattr.colormap =
+        XCreateColormap(display, RootWindow(display, screen), vinfo->visual,
+                        AllocNone);
+    w = XCreateWindow(display, RootWindow(display, screen), 0, 0, 32, 32, 0,
+                      vinfo->depth, InputOutput, vinfo->visual,
+                      (CWBackPixel | CWBorderPixel | CWColormap), &xattr);
+    context = _this->gl_data->glXCreateContext(display, vinfo, NULL, True);
+    if (context) {
+        _this->gl_data->glXMakeCurrent(display, w, context);
+    }
+    XFree(vinfo);
+
+    glXQueryExtensionsStringFunc =
+        (const char *(*)(Display *, int)) X11_GL_GetProcAddress(_this,
+                                                                "glXQueryExtensionsString");
+    if (glXQueryExtensionsStringFunc) {
+        extensions = glXQueryExtensionsStringFunc(display, screen);
+    } else {
+        extensions = NULL;
+    }
+
+    /* Check for SGI_swap_control */
+    if (HasExtension("SGI_swap_control", extensions)) {
+        _this->gl_data->glXSwapIntervalSGI =
+            (int (*)(int)) X11_GL_GetProcAddress(_this, "glXSwapIntervalSGI");
+    }
+
+    /* Check for GLX_MESA_swap_control */
+    if (HasExtension("GLX_MESA_swap_control", extensions)) {
+        _this->gl_data->glXSwapIntervalMESA =
+            (GLint(*)(unsigned)) X11_GL_GetProcAddress(_this,
+                                                       "glXSwapIntervalMESA");
+        _this->gl_data->glXGetSwapIntervalMESA =
+            (GLint(*)(void)) X11_GL_GetProcAddress(_this,
+                                                   "glXGetSwapIntervalMESA");
+    }
+
+    /* Check for GLX_EXT_visual_rating */
+    if (HasExtension("GLX_EXT_visual_rating", extensions)) {
+        _this->gl_data->HAS_GLX_EXT_visual_rating = SDL_TRUE;
+    }
+
+    if (context) {
+        _this->gl_data->glXMakeCurrent(display, None, NULL);
+        _this->gl_data->glXDestroyContext(display, context);
+    }
+    XDestroyWindow(display, w);
+    X11_PumpEvents(_this);
+}
+
+int
+X11_GL_Initialize(_THIS)
+{
+    if (_this->gl_data) {
+        ++_this->gl_data->initialized;
+        return 0;
+    }
+
+    _this->gl_data =
+        (struct SDL_GLDriverData *) SDL_calloc(1,
+                                               sizeof(struct
+                                                      SDL_GLDriverData));
+    if (!_this->gl_data) {
+        SDL_OutOfMemory();
+        return -1;
+    }
+    _this->gl_data->initialized = 1;
+
+    if (X11_GL_LoadLibrary(_this, NULL) < 0) {
+        return -1;
+    }
+
+    /* Initialize extensions */
+    X11_GL_InitExtensions(_this);
+
+    return 0;
+}
+
+void
+X11_GL_Shutdown(_THIS)
+{
+    if (!_this->gl_data || (--_this->gl_data->initialized > 0)) {
+        return;
+    }
+
+    X11_GL_UnloadLibrary(_this);
+
+    SDL_free(_this->gl_data);
+    _this->gl_data = NULL;
+}
+
+XVisualInfo *
+X11_GL_GetVisual(_THIS, Display * display, int screen)
+{
+    XVisualInfo *vinfo;
+
+    /* 64 seems nice. */
+    int attribs[64];
+    int i;
+
+    /* Setup our GLX attributes according to the gl_config. */
+    i = 0;
+    attribs[i++] = GLX_RGBA;
+    attribs[i++] = GLX_RED_SIZE;
+    attribs[i++] = _this->gl_config.red_size;
+    attribs[i++] = GLX_GREEN_SIZE;
+    attribs[i++] = _this->gl_config.green_size;
+    attribs[i++] = GLX_BLUE_SIZE;
+    attribs[i++] = _this->gl_config.blue_size;
+
+    if (_this->gl_config.alpha_size) {
+        attribs[i++] = GLX_ALPHA_SIZE;
+        attribs[i++] = _this->gl_config.alpha_size;
+    }
+
+    if (_this->gl_config.buffer_size) {
+        attribs[i++] = GLX_BUFFER_SIZE;
+        attribs[i++] = _this->gl_config.buffer_size;
+    }
+
+    if (_this->gl_config.double_buffer) {
+        attribs[i++] = GLX_DOUBLEBUFFER;
+    }
+
+    attribs[i++] = GLX_DEPTH_SIZE;
+    attribs[i++] = _this->gl_config.depth_size;
+
+    if (_this->gl_config.stencil_size) {
+        attribs[i++] = GLX_STENCIL_SIZE;
+        attribs[i++] = _this->gl_config.stencil_size;
+    }
+
+    if (_this->gl_config.accum_red_size) {
+        attribs[i++] = GLX_ACCUM_RED_SIZE;
+        attribs[i++] = _this->gl_config.accum_red_size;
+    }
+
+    if (_this->gl_config.accum_green_size) {
+        attribs[i++] = GLX_ACCUM_GREEN_SIZE;
+        attribs[i++] = _this->gl_config.accum_green_size;
+    }
+
+    if (_this->gl_config.accum_blue_size) {
+        attribs[i++] = GLX_ACCUM_BLUE_SIZE;
+        attribs[i++] = _this->gl_config.accum_blue_size;
+    }
+
+    if (_this->gl_config.accum_alpha_size) {
+        attribs[i++] = GLX_ACCUM_ALPHA_SIZE;
+        attribs[i++] = _this->gl_config.accum_alpha_size;
+    }
+
+    if (_this->gl_config.stereo) {
+        attribs[i++] = GLX_STEREO;
+    }
+
+    if (_this->gl_config.multisamplebuffers) {
+        attribs[i++] = GLX_SAMPLE_BUFFERS_ARB;
+        attribs[i++] = _this->gl_config.multisamplebuffers;
+    }
+
+    if (_this->gl_config.multisamplesamples) {
+        attribs[i++] = GLX_SAMPLES_ARB;
+        attribs[i++] = _this->gl_config.multisamplesamples;
+    }
+
+    if (_this->gl_config.accelerated >= 0
+        && _this->gl_data->HAS_GLX_EXT_visual_rating) {
+        attribs[i++] = GLX_VISUAL_CAVEAT_EXT;
+        attribs[i++] = GLX_NONE_EXT;
+    }
+#ifdef GLX_DIRECT_COLOR         /* Try for a DirectColor visual for gamma support */
+    if (!SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR")) {
+        attribs[i++] = GLX_X_VISUAL_TYPE;
+        attribs[i++] = GLX_DIRECT_COLOR;
+    }
+#endif
+    attribs[i++] = None;
+
+    vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
+#ifdef GLX_DIRECT_COLOR
+    if (!vinfo && !SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR")) { /* No DirectColor visual?  Try again.. */
+        attribs[i - 3] = None;
+        vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
+    }
+#endif
+    if (!vinfo) {
+        SDL_SetError("Couldn't find matching GLX visual");
+    }
+    return vinfo;
+}
+
+SDL_GLContext
+X11_GL_CreateContext(_THIS, SDL_Window * window)
+{
+    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+    Display *display = data->videodata->display;
+    int screen =
+        ((SDL_DisplayData *) SDL_GetDisplayFromWindow(window)->driverdata)->
+        screen;
+    XWindowAttributes xattr;
+    XVisualInfo v, *vinfo;
+    int n;
+    GLXContext context = NULL;
+
+    /* We do _this to create a clean separation between X and GLX errors. */
+    XSync(display, False);
+    XGetWindowAttributes(display, data->window, &xattr);
+    v.screen = screen;
+    v.visualid = XVisualIDFromVisual(xattr.visual);
+    vinfo = XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &v, &n);
+    if (vinfo) {
+        context =
+            _this->gl_data->glXCreateContext(display, vinfo, NULL, True);
+        XFree(vinfo);
+    }
+    XSync(display, False);
+
+    if (!context) {
+        SDL_SetError("Could not create GL context");
+    }
+    return (SDL_GLContext) context;
+}
+
+int
+X11_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
+{
+    Display *display = ((SDL_VideoData *) _this->driverdata)->display;
+    Window drawable =
+        (window ? ((SDL_WindowData *) window->driverdata)->window : None);
+    GLXContext glx_context = (GLXContext) context;
+    int status;
+
+    status = 0;
+    if (!_this->gl_data->glXMakeCurrent(display, drawable, glx_context)) {
+        SDL_SetError("Unable to make GL context current");
+        status = -1;
+    }
+    XSync(display, False);
+
+    return (status);
+}
+
+int
+X11_GL_SetSwapInterval(_THIS, int interval)
+{
+    int status;
+
+    if (_this->gl_data->glXSwapIntervalMESA) {
+        status = _this->gl_data->glXSwapIntervalMESA(interval);
+        if (status != 0) {
+            SDL_SetError("glxSwapIntervalMESA failed");
+            status = -1;
+        }
+    } else if (_this->gl_data->glXSwapIntervalSGI) {
+        status = _this->gl_data->glXSwapIntervalSGI(interval);
+        if (status != 0) {
+            SDL_SetError("glxSwapIntervalSGI failed");
+            status = -1;
+        }
+    } else {
+        SDL_Unsupported();
+        status = -1;
+    }
+    return status;
+}
+
+int
+X11_GL_GetSwapInterval(_THIS)
+{
+    if (_this->gl_data->glXGetSwapIntervalMESA) {
+        return _this->gl_data->glXGetSwapIntervalMESA();
+    } else {
+        SDL_Unsupported();
+        return -1;
+    }
+}
+
+void
+X11_GL_SwapWindow(_THIS, SDL_Window * window)
+{
+    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+    Display *display = data->videodata->display;
+
+    _this->gl_data->glXSwapBuffers(display, data->window);
+}
+
+void
+X11_GL_DeleteContext(_THIS, SDL_GLContext context)
+{
+    Display *display = ((SDL_VideoData *) _this->driverdata)->display;
+    GLXContext glx_context = (GLXContext) context;
+
+    _this->gl_data->glXDestroyContext(display, glx_context);
+}
+
+#endif /* SDL_VIDEO_OPENGL_GLX */
+
+/* vi: set ts=4 sw=4 expandtab: */