Mercurial > sdl-ios-xcode
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: */