e14743d1 |
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 | #include "../../events/SDL_events_c.h" |
26 | #include "SDL_x11dga_c.h" |
27 | #include "SDL_x11gl_c.h" |
28 | |
29 | #if defined(__IRIX__) |
30 | /* IRIX doesn't have a GL library versioning system */ |
31 | #define DEFAULT_OPENGL "libGL.so" |
32 | #elif defined(__MACOSX__) |
33 | #define DEFAULT_OPENGL "/usr/X11R6/lib/libGL.1.dylib" |
34 | #elif defined(__QNXNTO__) |
35 | #define DEFAULT_OPENGL "libGL.so.3" |
36 | #elif defined(__OpenBSD__) |
37 | #define DEFAULT_OPENGL "libGL.so.4.0" |
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 | /* GLX_EXT_visual_rating stuff that might not be in the system headers... */ |
49 | #ifndef GLX_VISUAL_CAVEAT_EXT |
50 | #define GLX_VISUAL_CAVEAT_EXT 0x20 |
51 | #endif |
52 | #ifndef GLX_NONE_EXT |
53 | #define GLX_NONE_EXT 0x8000 |
54 | #endif |
55 | #ifndef GLX_SLOW_VISUAL_EXT |
56 | #define GLX_SLOW_VISUAL_EXT 0x8001 |
57 | #endif |
58 | #ifndef GLX_NON_CONFORMANT_VISUAL_EXT |
59 | #define GLX_NON_CONFORMANT_VISUAL_EXT 0x800D |
60 | #endif |
61 | |
62 | |
63 | #if SDL_VIDEO_OPENGL_GLX |
64 | static int glXExtensionSupported(_THIS, const char *extension) |
65 | { |
66 | const char *extensions; |
67 | const char *start; |
68 | const char *where, *terminator; |
69 | |
70 | /* Extension names should not have spaces. */ |
71 | where = SDL_strchr(extension, ' '); |
72 | if ( where || *extension == '\0' ) { |
73 | return 0; |
74 | } |
75 | |
76 | extensions = this->gl_data->glXQueryExtensionsString(GFX_Display,SDL_Screen); |
77 | /* It takes a bit of care to be fool-proof about parsing the |
78 | * OpenGL extensions string. Don't be fooled by sub-strings, etc. |
79 | */ |
80 | |
81 | /* http://bugs.debian.org/537487 */ |
82 | if (extensions == NULL) { |
83 | return 0; |
84 | } |
85 | |
86 | start = extensions; |
87 | |
88 | for (;;) { |
89 | where = SDL_strstr(start, extension); |
90 | if (!where) break; |
91 | |
92 | terminator = where + strlen(extension); |
93 | if (where == start || *(where - 1) == ' ') |
94 | if (*terminator == ' ' || *terminator == '\0') return 1; |
95 | |
96 | start = terminator; |
97 | } |
98 | return 0; |
99 | } |
100 | #endif /* SDL_VIDEO_OPENGL_GLX */ |
101 | |
102 | XVisualInfo *X11_GL_GetVisual(_THIS) |
103 | { |
104 | #if SDL_VIDEO_OPENGL_GLX |
105 | /* 64 seems nice. */ |
106 | int attribs[64]; |
107 | int i; |
108 | |
109 | /* load the gl driver from a default path */ |
110 | if ( ! this->gl_config.driver_loaded ) { |
111 | /* no driver has been loaded, use default (ourselves) */ |
112 | if ( X11_GL_LoadLibrary(this, NULL) < 0 ) { |
113 | return NULL; |
114 | } |
115 | } |
116 | |
117 | /* See if we already have a window which we must use */ |
118 | if ( SDL_windowid ) { |
119 | XWindowAttributes a; |
120 | XVisualInfo vi_in; |
121 | int out_count; |
122 | |
123 | XGetWindowAttributes(SDL_Display, SDL_Window, &a); |
124 | vi_in.screen = SDL_Screen; |
125 | vi_in.visualid = XVisualIDFromVisual(a.visual); |
126 | glx_visualinfo = XGetVisualInfo(SDL_Display, |
127 | VisualScreenMask|VisualIDMask, &vi_in, &out_count); |
128 | return glx_visualinfo; |
129 | } |
130 | |
131 | /* Setup our GLX attributes according to the gl_config. */ |
132 | i = 0; |
133 | attribs[i++] = GLX_RGBA; |
134 | attribs[i++] = GLX_RED_SIZE; |
135 | attribs[i++] = this->gl_config.red_size; |
136 | attribs[i++] = GLX_GREEN_SIZE; |
137 | attribs[i++] = this->gl_config.green_size; |
138 | attribs[i++] = GLX_BLUE_SIZE; |
139 | attribs[i++] = this->gl_config.blue_size; |
140 | |
141 | if( this->gl_config.alpha_size ) { |
142 | attribs[i++] = GLX_ALPHA_SIZE; |
143 | attribs[i++] = this->gl_config.alpha_size; |
144 | } |
145 | |
146 | if( this->gl_config.buffer_size ) { |
147 | attribs[i++] = GLX_BUFFER_SIZE; |
148 | attribs[i++] = this->gl_config.buffer_size; |
149 | } |
150 | |
151 | if( this->gl_config.double_buffer ) { |
152 | attribs[i++] = GLX_DOUBLEBUFFER; |
153 | } |
154 | |
155 | attribs[i++] = GLX_DEPTH_SIZE; |
156 | attribs[i++] = this->gl_config.depth_size; |
157 | |
158 | if( this->gl_config.stencil_size ) { |
159 | attribs[i++] = GLX_STENCIL_SIZE; |
160 | attribs[i++] = this->gl_config.stencil_size; |
161 | } |
162 | |
163 | if( this->gl_config.accum_red_size ) { |
164 | attribs[i++] = GLX_ACCUM_RED_SIZE; |
165 | attribs[i++] = this->gl_config.accum_red_size; |
166 | } |
167 | |
168 | if( this->gl_config.accum_green_size ) { |
169 | attribs[i++] = GLX_ACCUM_GREEN_SIZE; |
170 | attribs[i++] = this->gl_config.accum_green_size; |
171 | } |
172 | |
173 | if( this->gl_config.accum_blue_size ) { |
174 | attribs[i++] = GLX_ACCUM_BLUE_SIZE; |
175 | attribs[i++] = this->gl_config.accum_blue_size; |
176 | } |
177 | |
178 | if( this->gl_config.accum_alpha_size ) { |
179 | attribs[i++] = GLX_ACCUM_ALPHA_SIZE; |
180 | attribs[i++] = this->gl_config.accum_alpha_size; |
181 | } |
182 | |
183 | if( this->gl_config.stereo ) { |
184 | attribs[i++] = GLX_STEREO; |
185 | } |
186 | |
187 | if( this->gl_config.multisamplebuffers ) { |
188 | attribs[i++] = GLX_SAMPLE_BUFFERS_ARB; |
189 | attribs[i++] = this->gl_config.multisamplebuffers; |
190 | } |
191 | |
192 | if( this->gl_config.multisamplesamples ) { |
193 | attribs[i++] = GLX_SAMPLES_ARB; |
194 | attribs[i++] = this->gl_config.multisamplesamples; |
195 | } |
196 | |
197 | if( this->gl_config.accelerated >= 0 && |
198 | glXExtensionSupported(this, "GLX_EXT_visual_rating") ) { |
199 | attribs[i++] = GLX_VISUAL_CAVEAT_EXT; |
200 | attribs[i++] = GLX_NONE_EXT; |
201 | } |
202 | |
203 | #ifdef GLX_DIRECT_COLOR /* Try for a DirectColor visual for gamma support */ |
204 | if ( !SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ) { |
205 | attribs[i++] = GLX_X_VISUAL_TYPE; |
206 | attribs[i++] = GLX_DIRECT_COLOR; |
207 | } |
208 | #endif |
209 | attribs[i++] = None; |
210 | |
211 | glx_visualinfo = this->gl_data->glXChooseVisual(GFX_Display, |
212 | SDL_Screen, attribs); |
213 | #ifdef GLX_DIRECT_COLOR |
214 | if( !glx_visualinfo && !SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ) { /* No DirectColor visual? Try again.. */ |
215 | attribs[i-3] = None; |
216 | glx_visualinfo = this->gl_data->glXChooseVisual(GFX_Display, |
217 | SDL_Screen, attribs); |
218 | } |
219 | #endif |
220 | if( !glx_visualinfo ) { |
221 | SDL_SetError( "Couldn't find matching GLX visual"); |
222 | return NULL; |
223 | } |
224 | /* |
225 | printf("Found GLX visual 0x%x\n", glx_visualinfo->visualid); |
226 | */ |
227 | return glx_visualinfo; |
228 | #else |
229 | SDL_SetError("X11 driver not configured with OpenGL"); |
230 | return NULL; |
231 | #endif |
232 | } |
233 | |
234 | int X11_GL_CreateWindow(_THIS, int w, int h) |
235 | { |
236 | int retval; |
237 | #if SDL_VIDEO_OPENGL_GLX |
238 | XSetWindowAttributes attributes; |
239 | unsigned long mask; |
240 | unsigned long black; |
241 | |
242 | black = (glx_visualinfo->visual == DefaultVisual(SDL_Display, |
243 | SDL_Screen)) |
244 | ? BlackPixel(SDL_Display, SDL_Screen) : 0; |
245 | attributes.background_pixel = black; |
246 | attributes.border_pixel = black; |
247 | attributes.colormap = SDL_XColorMap; |
248 | mask = CWBackPixel | CWBorderPixel | CWColormap; |
249 | |
250 | SDL_Window = XCreateWindow(SDL_Display, WMwindow, |
251 | 0, 0, w, h, 0, glx_visualinfo->depth, |
252 | InputOutput, glx_visualinfo->visual, |
253 | mask, &attributes); |
254 | if ( !SDL_Window ) { |
255 | SDL_SetError("Could not create window"); |
256 | return -1; |
257 | } |
258 | retval = 0; |
259 | #else |
260 | SDL_SetError("X11 driver not configured with OpenGL"); |
261 | retval = -1; |
262 | #endif |
263 | return(retval); |
264 | } |
265 | |
266 | int X11_GL_CreateContext(_THIS) |
267 | { |
268 | int retval; |
269 | #if SDL_VIDEO_OPENGL_GLX |
270 | |
271 | /* We do this to create a clean separation between X and GLX errors. */ |
272 | XSync( SDL_Display, False ); |
273 | glx_context = this->gl_data->glXCreateContext(GFX_Display, |
274 | glx_visualinfo, NULL, True); |
275 | XSync( GFX_Display, False ); |
276 | |
277 | if ( glx_context == NULL ) { |
278 | SDL_SetError("Could not create GL context"); |
279 | return(-1); |
280 | } |
281 | if ( X11_GL_MakeCurrent(this) < 0 ) { |
282 | return(-1); |
283 | } |
284 | gl_active = 1; |
285 | |
286 | if ( !glXExtensionSupported(this, "GLX_SGI_swap_control") ) { |
287 | this->gl_data->glXSwapIntervalSGI = NULL; |
288 | } |
289 | if ( !glXExtensionSupported(this, "GLX_MESA_swap_control") ) { |
290 | this->gl_data->glXSwapIntervalMESA = NULL; |
291 | } |
292 | if ( this->gl_config.swap_control >= 0 ) { |
293 | int rc = -1; |
294 | if ( this->gl_data->glXSwapIntervalMESA ) { |
295 | rc = this->gl_data->glXSwapIntervalMESA(this->gl_config.swap_control); |
296 | } else if ( this->gl_data->glXSwapIntervalSGI ) { |
297 | rc = this->gl_data->glXSwapIntervalSGI(this->gl_config.swap_control); |
298 | } |
299 | if (rc == 0) { |
300 | this->gl_data->swap_interval = this->gl_config.swap_control; |
301 | } |
302 | } |
303 | #else |
304 | SDL_SetError("X11 driver not configured with OpenGL"); |
305 | #endif |
306 | if ( gl_active ) { |
307 | retval = 0; |
308 | } else { |
309 | retval = -1; |
310 | } |
311 | return(retval); |
312 | } |
313 | |
314 | void X11_GL_Shutdown(_THIS) |
315 | { |
316 | #if SDL_VIDEO_OPENGL_GLX |
317 | /* Clean up OpenGL */ |
318 | if( glx_context ) { |
319 | this->gl_data->glXMakeCurrent(GFX_Display, None, NULL); |
320 | |
321 | if (glx_context != NULL) |
322 | this->gl_data->glXDestroyContext(GFX_Display, glx_context); |
323 | |
324 | glx_context = NULL; |
325 | } |
326 | gl_active = 0; |
327 | #endif /* SDL_VIDEO_OPENGL_GLX */ |
328 | } |
329 | |
330 | #if SDL_VIDEO_OPENGL_GLX |
331 | |
332 | /* Make the current context active */ |
333 | int X11_GL_MakeCurrent(_THIS) |
334 | { |
335 | int retval; |
336 | |
337 | retval = 0; |
338 | if ( ! this->gl_data->glXMakeCurrent(GFX_Display, |
339 | SDL_Window, glx_context) ) { |
340 | SDL_SetError("Unable to make GL context current"); |
341 | retval = -1; |
342 | } |
343 | XSync( GFX_Display, False ); |
344 | |
345 | /* More Voodoo X server workarounds... Grr... */ |
346 | SDL_Lock_EventThread(); |
347 | X11_CheckDGAMouse(this); |
348 | SDL_Unlock_EventThread(); |
349 | |
350 | return(retval); |
351 | } |
352 | |
353 | /* Get attribute data from glX. */ |
354 | int X11_GL_GetAttribute(_THIS, SDL_GLattr attrib, int* value) |
355 | { |
356 | int retval = -1; |
357 | int unsupported = 0; |
358 | int glx_attrib = None; |
359 | |
360 | switch( attrib ) { |
361 | case SDL_GL_RED_SIZE: |
362 | glx_attrib = GLX_RED_SIZE; |
363 | break; |
364 | case SDL_GL_GREEN_SIZE: |
365 | glx_attrib = GLX_GREEN_SIZE; |
366 | break; |
367 | case SDL_GL_BLUE_SIZE: |
368 | glx_attrib = GLX_BLUE_SIZE; |
369 | break; |
370 | case SDL_GL_ALPHA_SIZE: |
371 | glx_attrib = GLX_ALPHA_SIZE; |
372 | break; |
373 | case SDL_GL_DOUBLEBUFFER: |
374 | glx_attrib = GLX_DOUBLEBUFFER; |
375 | break; |
376 | case SDL_GL_BUFFER_SIZE: |
377 | glx_attrib = GLX_BUFFER_SIZE; |
378 | break; |
379 | case SDL_GL_DEPTH_SIZE: |
380 | glx_attrib = GLX_DEPTH_SIZE; |
381 | break; |
382 | case SDL_GL_STENCIL_SIZE: |
383 | glx_attrib = GLX_STENCIL_SIZE; |
384 | break; |
385 | case SDL_GL_ACCUM_RED_SIZE: |
386 | glx_attrib = GLX_ACCUM_RED_SIZE; |
387 | break; |
388 | case SDL_GL_ACCUM_GREEN_SIZE: |
389 | glx_attrib = GLX_ACCUM_GREEN_SIZE; |
390 | break; |
391 | case SDL_GL_ACCUM_BLUE_SIZE: |
392 | glx_attrib = GLX_ACCUM_BLUE_SIZE; |
393 | break; |
394 | case SDL_GL_ACCUM_ALPHA_SIZE: |
395 | glx_attrib = GLX_ACCUM_ALPHA_SIZE; |
396 | break; |
397 | case SDL_GL_STEREO: |
398 | glx_attrib = GLX_STEREO; |
399 | break; |
400 | case SDL_GL_MULTISAMPLEBUFFERS: |
401 | glx_attrib = GLX_SAMPLE_BUFFERS_ARB; |
402 | break; |
403 | case SDL_GL_MULTISAMPLESAMPLES: |
404 | glx_attrib = GLX_SAMPLES_ARB; |
405 | break; |
406 | case SDL_GL_ACCELERATED_VISUAL: |
407 | if ( glXExtensionSupported(this, "GLX_EXT_visual_rating") ) { |
408 | glx_attrib = GLX_VISUAL_CAVEAT_EXT; |
409 | retval = this->gl_data->glXGetConfig(GFX_Display, glx_visualinfo, glx_attrib, value); |
410 | if ( *value == GLX_SLOW_VISUAL_EXT ) { |
411 | *value = SDL_FALSE; |
412 | } else { |
413 | *value = SDL_TRUE; |
414 | } |
415 | return retval; |
416 | } else { |
417 | unsupported = 1; |
418 | } |
419 | break; |
420 | case SDL_GL_SWAP_CONTROL: |
421 | if ( ( this->gl_data->glXSwapIntervalMESA ) || |
422 | ( this->gl_data->glXSwapIntervalSGI ) ) { |
423 | *value = this->gl_data->swap_interval; |
424 | return 0; |
425 | } else { |
426 | unsupported = 1; |
427 | } |
428 | break; |
429 | default: |
430 | unsupported = 1; |
431 | break; |
432 | } |
433 | |
434 | if (unsupported) { |
435 | SDL_SetError("OpenGL attribute is unsupported on this system"); |
436 | } else { |
437 | retval = this->gl_data->glXGetConfig(GFX_Display, glx_visualinfo, glx_attrib, value); |
438 | } |
439 | return retval; |
440 | } |
441 | |
442 | void X11_GL_SwapBuffers(_THIS) |
443 | { |
444 | this->gl_data->glXSwapBuffers(GFX_Display, SDL_Window); |
445 | } |
446 | |
447 | #endif /* SDL_VIDEO_OPENGL_GLX */ |
448 | |
449 | #define OPENGL_REQUIRS_DLOPEN |
450 | #if defined(OPENGL_REQUIRS_DLOPEN) && defined(SDL_LOADSO_DLOPEN) |
451 | #include <dlfcn.h> |
452 | #define GL_LoadObject(X) dlopen(X, (RTLD_NOW|RTLD_GLOBAL)) |
453 | #define GL_LoadFunction dlsym |
454 | #define GL_UnloadObject dlclose |
455 | #else |
456 | #define GL_LoadObject SDL_LoadObject |
457 | #define GL_LoadFunction SDL_LoadFunction |
458 | #define GL_UnloadObject SDL_UnloadObject |
459 | #endif |
460 | |
461 | void X11_GL_UnloadLibrary(_THIS) |
462 | { |
463 | #if SDL_VIDEO_OPENGL_GLX |
464 | if ( this->gl_config.driver_loaded ) { |
465 | |
466 | GL_UnloadObject(this->gl_config.dll_handle); |
467 | |
468 | this->gl_data->glXGetProcAddress = NULL; |
469 | this->gl_data->glXChooseVisual = NULL; |
470 | this->gl_data->glXCreateContext = NULL; |
471 | this->gl_data->glXDestroyContext = NULL; |
472 | this->gl_data->glXMakeCurrent = NULL; |
473 | this->gl_data->glXSwapBuffers = NULL; |
474 | this->gl_data->glXSwapIntervalSGI = NULL; |
475 | this->gl_data->glXSwapIntervalMESA = NULL; |
476 | |
477 | this->gl_config.dll_handle = NULL; |
478 | this->gl_config.driver_loaded = 0; |
479 | } |
480 | #endif |
481 | } |
482 | |
483 | #if SDL_VIDEO_OPENGL_GLX |
484 | |
485 | /* Passing a NULL path means load pointers from the application */ |
486 | int X11_GL_LoadLibrary(_THIS, const char* path) |
487 | { |
488 | void* handle = NULL; |
489 | |
490 | if ( gl_active ) { |
491 | SDL_SetError("OpenGL context already created"); |
492 | return -1; |
493 | } |
494 | |
495 | if ( path == NULL ) { |
496 | path = SDL_getenv("SDL_VIDEO_GL_DRIVER"); |
497 | if ( path == NULL ) { |
498 | path = DEFAULT_OPENGL; |
499 | } |
500 | } |
501 | |
502 | handle = GL_LoadObject(path); |
503 | if ( handle == NULL ) { |
504 | #if defined(OPENGL_REQUIRS_DLOPEN) && defined(SDL_LOADSO_DLOPEN) |
505 | SDL_SetError("Failed loading %s", path); |
506 | #else |
507 | /* SDL_LoadObject() will call SDL_SetError() for us. */ |
508 | #endif |
509 | return -1; |
510 | } |
511 | |
512 | /* Unload the old driver and reset the pointers */ |
513 | X11_GL_UnloadLibrary(this); |
514 | |
515 | /* Load new function pointers */ |
516 | this->gl_data->glXGetProcAddress = |
517 | (void *(*)(const GLubyte *)) GL_LoadFunction(handle, "glXGetProcAddressARB"); |
518 | this->gl_data->glXChooseVisual = |
519 | (XVisualInfo *(*)(Display *, int, int *)) GL_LoadFunction(handle, "glXChooseVisual"); |
520 | this->gl_data->glXCreateContext = |
521 | (GLXContext (*)(Display *, XVisualInfo *, GLXContext, int)) GL_LoadFunction(handle, "glXCreateContext"); |
522 | this->gl_data->glXDestroyContext = |
523 | (void (*)(Display *, GLXContext)) GL_LoadFunction(handle, "glXDestroyContext"); |
524 | this->gl_data->glXMakeCurrent = |
525 | (int (*)(Display *, GLXDrawable, GLXContext)) GL_LoadFunction(handle, "glXMakeCurrent"); |
526 | this->gl_data->glXSwapBuffers = |
527 | (void (*)(Display *, GLXDrawable)) GL_LoadFunction(handle, "glXSwapBuffers"); |
528 | this->gl_data->glXGetConfig = |
529 | (int (*)(Display *, XVisualInfo *, int, int *)) GL_LoadFunction(handle, "glXGetConfig"); |
530 | this->gl_data->glXQueryExtensionsString = |
531 | (const char *(*)(Display *, int)) GL_LoadFunction(handle, "glXQueryExtensionsString"); |
532 | this->gl_data->glXSwapIntervalSGI = |
533 | (int (*)(int)) GL_LoadFunction(handle, "glXSwapIntervalSGI"); |
534 | this->gl_data->glXSwapIntervalMESA = |
535 | (GLint (*)(unsigned)) GL_LoadFunction(handle, "glXSwapIntervalMESA"); |
536 | |
537 | if ( (this->gl_data->glXChooseVisual == NULL) || |
538 | (this->gl_data->glXCreateContext == NULL) || |
539 | (this->gl_data->glXDestroyContext == NULL) || |
540 | (this->gl_data->glXMakeCurrent == NULL) || |
541 | (this->gl_data->glXSwapBuffers == NULL) || |
542 | (this->gl_data->glXGetConfig == NULL) || |
543 | (this->gl_data->glXQueryExtensionsString == NULL)) { |
544 | SDL_SetError("Could not retrieve OpenGL functions"); |
545 | return -1; |
546 | } |
547 | |
548 | this->gl_config.dll_handle = handle; |
549 | this->gl_config.driver_loaded = 1; |
550 | if ( path ) { |
551 | SDL_strlcpy(this->gl_config.driver_path, path, |
552 | SDL_arraysize(this->gl_config.driver_path)); |
553 | } else { |
554 | *this->gl_config.driver_path = '\0'; |
555 | } |
556 | return 0; |
557 | } |
558 | |
559 | void *X11_GL_GetProcAddress(_THIS, const char* proc) |
560 | { |
561 | void* handle; |
562 | |
563 | handle = this->gl_config.dll_handle; |
564 | if ( this->gl_data->glXGetProcAddress ) { |
565 | return this->gl_data->glXGetProcAddress((const GLubyte *)proc); |
566 | } |
567 | return GL_LoadFunction(handle, proc); |
568 | } |
569 | |
570 | #endif /* SDL_VIDEO_OPENGL_GLX */ |