1//========================================================================
2// GLFW 3.2 GLX - www.glfw.org
3//------------------------------------------------------------------------
4// Copyright (c) 2002-2006 Marcus Geelnard
5// Copyright (c) 2006-2016 Camilla Berglund <elmindreda@glfw.org>
6//
7// This software is provided 'as-is', without any express or implied
8// warranty. In no event will the authors be held liable for any damages
9// arising from the use of this software.
10//
11// Permission is granted to anyone to use this software for any purpose,
12// including commercial applications, and to alter it and redistribute it
13// freely, subject to the following restrictions:
14//
15// 1. The origin of this software must not be misrepresented; you must not
16// claim that you wrote the original software. If you use this software
17// in a product, an acknowledgment in the product documentation would
18// be appreciated but is not required.
19//
20// 2. Altered source versions must be plainly marked as such, and must not
21// be misrepresented as being the original software.
22//
23// 3. This notice may not be removed or altered from any source
24// distribution.
25//
26//========================================================================
27
28#include "internal.h"
29
30#include <string.h>
31#include <stdlib.h>
32#include <assert.h>
33
34#ifndef GLXBadProfileARB
35 #define GLXBadProfileARB 13
36#endif
37
38
39// Returns the specified attribute of the specified GLXFBConfig
40//
41static int getFBConfigAttrib(GLXFBConfig fbconfig, int attrib)
42{
43 int value;
44 glXGetFBConfigAttrib(_glfw.x11.display, fbconfig, attrib, &value);
45 return value;
46}
47
48// Return a list of available and usable framebuffer configs
49//
50static GLFWbool chooseFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* result)
51{
52 GLXFBConfig* nativeConfigs;
53 _GLFWfbconfig* usableConfigs;
54 const _GLFWfbconfig* closest;
55 int i, nativeCount, usableCount;
56 const char* vendor;
57 GLFWbool trustWindowBit = GLFW_TRUE;
58
59 // HACK: This is a (hopefully temporary) workaround for Chromium
60 // (VirtualBox GL) not setting the window bit on any GLXFBConfigs
61 vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR);
62 if (strcmp(vendor, "Chromium") == 0)
63 trustWindowBit = GLFW_FALSE;
64
65 nativeConfigs =
66 glXGetFBConfigs(_glfw.x11.display, _glfw.x11.screen, &nativeCount);
67 if (!nativeCount)
68 {
69 _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: No GLXFBConfigs returned");
70 return GLFW_FALSE;
71 }
72
73 usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig));
74 usableCount = 0;
75
76 for (i = 0; i < nativeCount; i++)
77 {
78 const GLXFBConfig n = nativeConfigs[i];
79 _GLFWfbconfig* u = usableConfigs + usableCount;
80
81 // Only consider RGBA GLXFBConfigs
82 if (!(getFBConfigAttrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT))
83 continue;
84
85 // Only consider window GLXFBConfigs
86 if (!(getFBConfigAttrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT))
87 {
88 if (trustWindowBit)
89 continue;
90 }
91
92 u->redBits = getFBConfigAttrib(n, GLX_RED_SIZE);
93 u->greenBits = getFBConfigAttrib(n, GLX_GREEN_SIZE);
94 u->blueBits = getFBConfigAttrib(n, GLX_BLUE_SIZE);
95
96 u->alphaBits = getFBConfigAttrib(n, GLX_ALPHA_SIZE);
97 u->depthBits = getFBConfigAttrib(n, GLX_DEPTH_SIZE);
98 u->stencilBits = getFBConfigAttrib(n, GLX_STENCIL_SIZE);
99
100 u->accumRedBits = getFBConfigAttrib(n, GLX_ACCUM_RED_SIZE);
101 u->accumGreenBits = getFBConfigAttrib(n, GLX_ACCUM_GREEN_SIZE);
102 u->accumBlueBits = getFBConfigAttrib(n, GLX_ACCUM_BLUE_SIZE);
103 u->accumAlphaBits = getFBConfigAttrib(n, GLX_ACCUM_ALPHA_SIZE);
104
105 u->auxBuffers = getFBConfigAttrib(n, GLX_AUX_BUFFERS);
106
107 if (getFBConfigAttrib(n, GLX_STEREO))
108 u->stereo = GLFW_TRUE;
109 if (getFBConfigAttrib(n, GLX_DOUBLEBUFFER))
110 u->doublebuffer = GLFW_TRUE;
111
112 if (_glfw.glx.ARB_multisample)
113 u->samples = getFBConfigAttrib(n, GLX_SAMPLES);
114
115 if (_glfw.glx.ARB_framebuffer_sRGB || _glfw.glx.EXT_framebuffer_sRGB)
116 u->sRGB = getFBConfigAttrib(n, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB);
117
118 u->handle = (uintptr_t) n;
119 usableCount++;
120 }
121
122 closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount);
123 if (closest)
124 *result = (GLXFBConfig) closest->handle;
125
126 XFree(nativeConfigs);
127 free(usableConfigs);
128
129 return closest != NULL;
130}
131
132// Create the OpenGL context using legacy API
133//
134static GLXContext createLegacyContext(_GLFWwindow* window,
135 GLXFBConfig fbconfig,
136 GLXContext share)
137{
138 return glXCreateNewContext(_glfw.x11.display,
139 fbconfig,
140 GLX_RGBA_TYPE,
141 share,
142 True);
143}
144
145static void makeContextCurrent(_GLFWwindow* window)
146{
147 if (window)
148 {
149 if (!glXMakeCurrent(_glfw.x11.display,
150 window->context.glx.window,
151 window->context.glx.handle))
152 {
153 _glfwInputError(GLFW_PLATFORM_ERROR,
154 "GLX: Failed to make context current");
155 return;
156 }
157 }
158 else
159 {
160 if (!glXMakeCurrent(_glfw.x11.display, None, NULL))
161 {
162 _glfwInputError(GLFW_PLATFORM_ERROR,
163 "GLX: Failed to clear current context");
164 return;
165 }
166 }
167
168 _glfwPlatformSetCurrentContext(window);
169}
170
171static void swapBuffers(_GLFWwindow* window)
172{
173 glXSwapBuffers(_glfw.x11.display, window->context.glx.window);
174}
175
176static void swapInterval(int interval)
177{
178 _GLFWwindow* window = _glfwPlatformGetCurrentContext();
179
180 if (_glfw.glx.EXT_swap_control)
181 {
182 _glfw.glx.SwapIntervalEXT(_glfw.x11.display,
183 window->context.glx.window,
184 interval);
185 }
186 else if (_glfw.glx.MESA_swap_control)
187 _glfw.glx.SwapIntervalMESA(interval);
188 else if (_glfw.glx.SGI_swap_control)
189 {
190 if (interval > 0)
191 _glfw.glx.SwapIntervalSGI(interval);
192 }
193}
194
195static int extensionSupported(const char* extension)
196{
197 const char* extensions =
198 glXQueryExtensionsString(_glfw.x11.display, _glfw.x11.screen);
199 if (extensions)
200 {
201 if (_glfwStringInExtensionString(extension, extensions))
202 return GLFW_TRUE;
203 }
204
205 return GLFW_FALSE;
206}
207
208static GLFWglproc getProcAddress(const char* procname)
209{
210 if (_glfw.glx.GetProcAddress)
211 return _glfw.glx.GetProcAddress((const GLubyte*) procname);
212 else if (_glfw.glx.GetProcAddressARB)
213 return _glfw.glx.GetProcAddressARB((const GLubyte*) procname);
214 else
215 return dlsym(_glfw.glx.handle, procname);
216}
217
218// Destroy the OpenGL context
219//
220static void destroyContext(_GLFWwindow* window)
221{
222 if (window->context.glx.window)
223 {
224 glXDestroyWindow(_glfw.x11.display, window->context.glx.window);
225 window->context.glx.window = None;
226 }
227
228 if (window->context.glx.handle)
229 {
230 glXDestroyContext(_glfw.x11.display, window->context.glx.handle);
231 window->context.glx.handle = NULL;
232 }
233}
234
235
236//////////////////////////////////////////////////////////////////////////
237////// GLFW internal API //////
238//////////////////////////////////////////////////////////////////////////
239
240// Initialize GLX
241//
242GLFWbool _glfwInitGLX(void)
243{
244 int i;
245 const char* sonames[] =
246 {
247#if defined(__CYGWIN__)
248 "libGL-1.so",
249#else
250 "libGL.so.1",
251 "libGL.so",
252#endif
253 NULL
254 };
255
256 for (i = 0; sonames[i]; i++)
257 {
258 _glfw.glx.handle = dlopen(sonames[i], RTLD_LAZY | RTLD_GLOBAL);
259 if (_glfw.glx.handle)
260 break;
261 }
262
263 if (!_glfw.glx.handle)
264 {
265 _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: Failed to load GLX");
266 return GLFW_FALSE;
267 }
268
269 _glfw.glx.GetFBConfigs =
270 dlsym(_glfw.glx.handle, "glXGetFBConfigs");
271 _glfw.glx.GetFBConfigAttrib =
272 dlsym(_glfw.glx.handle, "glXGetFBConfigAttrib");
273 _glfw.glx.GetClientString =
274 dlsym(_glfw.glx.handle, "glXGetClientString");
275 _glfw.glx.QueryExtension =
276 dlsym(_glfw.glx.handle, "glXQueryExtension");
277 _glfw.glx.QueryVersion =
278 dlsym(_glfw.glx.handle, "glXQueryVersion");
279 _glfw.glx.DestroyContext =
280 dlsym(_glfw.glx.handle, "glXDestroyContext");
281 _glfw.glx.MakeCurrent =
282 dlsym(_glfw.glx.handle, "glXMakeCurrent");
283 _glfw.glx.SwapBuffers =
284 dlsym(_glfw.glx.handle, "glXSwapBuffers");
285 _glfw.glx.QueryExtensionsString =
286 dlsym(_glfw.glx.handle, "glXQueryExtensionsString");
287 _glfw.glx.CreateNewContext =
288 dlsym(_glfw.glx.handle, "glXCreateNewContext");
289 _glfw.glx.CreateWindow =
290 dlsym(_glfw.glx.handle, "glXCreateWindow");
291 _glfw.glx.DestroyWindow =
292 dlsym(_glfw.glx.handle, "glXDestroyWindow");
293 _glfw.glx.GetProcAddress =
294 dlsym(_glfw.glx.handle, "glXGetProcAddress");
295 _glfw.glx.GetProcAddressARB =
296 dlsym(_glfw.glx.handle, "glXGetProcAddressARB");
297 _glfw.glx.GetVisualFromFBConfig =
298 dlsym(_glfw.glx.handle, "glXGetVisualFromFBConfig");
299
300 if (!glXQueryExtension(_glfw.x11.display,
301 &_glfw.glx.errorBase,
302 &_glfw.glx.eventBase))
303 {
304 _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: GLX extension not found");
305 return GLFW_FALSE;
306 }
307
308 if (!glXQueryVersion(_glfw.x11.display, &_glfw.glx.major, &_glfw.glx.minor))
309 {
310 _glfwInputError(GLFW_API_UNAVAILABLE,
311 "GLX: Failed to query GLX version");
312 return GLFW_FALSE;
313 }
314
315 if (_glfw.glx.major == 1 && _glfw.glx.minor < 3)
316 {
317 _glfwInputError(GLFW_API_UNAVAILABLE,
318 "GLX: GLX version 1.3 is required");
319 return GLFW_FALSE;
320 }
321
322 if (extensionSupported("GLX_EXT_swap_control"))
323 {
324 _glfw.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)
325 getProcAddress("glXSwapIntervalEXT");
326
327 if (_glfw.glx.SwapIntervalEXT)
328 _glfw.glx.EXT_swap_control = GLFW_TRUE;
329 }
330
331 if (extensionSupported("GLX_SGI_swap_control"))
332 {
333 _glfw.glx.SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)
334 getProcAddress("glXSwapIntervalSGI");
335
336 if (_glfw.glx.SwapIntervalSGI)
337 _glfw.glx.SGI_swap_control = GLFW_TRUE;
338 }
339
340 if (extensionSupported("GLX_MESA_swap_control"))
341 {
342 _glfw.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC)
343 getProcAddress("glXSwapIntervalMESA");
344
345 if (_glfw.glx.SwapIntervalMESA)
346 _glfw.glx.MESA_swap_control = GLFW_TRUE;
347 }
348
349 if (extensionSupported("GLX_ARB_multisample"))
350 _glfw.glx.ARB_multisample = GLFW_TRUE;
351
352 if (extensionSupported("GLX_ARB_framebuffer_sRGB"))
353 _glfw.glx.ARB_framebuffer_sRGB = GLFW_TRUE;
354
355 if (extensionSupported("GLX_EXT_framebuffer_sRGB"))
356 _glfw.glx.EXT_framebuffer_sRGB = GLFW_TRUE;
357
358 if (extensionSupported("GLX_ARB_create_context"))
359 {
360 _glfw.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)
361 getProcAddress("glXCreateContextAttribsARB");
362
363 if (_glfw.glx.CreateContextAttribsARB)
364 _glfw.glx.ARB_create_context = GLFW_TRUE;
365 }
366
367 if (extensionSupported("GLX_ARB_create_context_robustness"))
368 _glfw.glx.ARB_create_context_robustness = GLFW_TRUE;
369
370 if (extensionSupported("GLX_ARB_create_context_profile"))
371 _glfw.glx.ARB_create_context_profile = GLFW_TRUE;
372
373 if (extensionSupported("GLX_EXT_create_context_es2_profile"))
374 _glfw.glx.EXT_create_context_es2_profile = GLFW_TRUE;
375
376 if (extensionSupported("GLX_ARB_context_flush_control"))
377 _glfw.glx.ARB_context_flush_control = GLFW_TRUE;
378
379 return GLFW_TRUE;
380}
381
382// Terminate GLX
383//
384void _glfwTerminateGLX(void)
385{
386 // NOTE: This function must not call any X11 functions, as it is called
387 // after XCloseDisplay (see _glfwPlatformTerminate for details)
388
389 if (_glfw.glx.handle)
390 {
391 dlclose(_glfw.glx.handle);
392 _glfw.glx.handle = NULL;
393 }
394}
395
396#define setGLXattrib(attribName, attribValue) \
397{ \
398 attribs[index++] = attribName; \
399 attribs[index++] = attribValue; \
400 assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \
401}
402
403// Create the OpenGL or OpenGL ES context
404//
405GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
406 const _GLFWctxconfig* ctxconfig,
407 const _GLFWfbconfig* fbconfig)
408{
409 int attribs[40];
410 GLXFBConfig native = NULL;
411 GLXContext share = NULL;
412
413 if (ctxconfig->share)
414 share = ctxconfig->share->context.glx.handle;
415
416 if (!chooseFBConfig(fbconfig, &native))
417 {
418 _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
419 "GLX: Failed to find a suitable GLXFBConfig");
420 return GLFW_FALSE;
421 }
422
423 if (ctxconfig->client == GLFW_OPENGL_ES_API)
424 {
425 if (!_glfw.glx.ARB_create_context ||
426 !_glfw.glx.ARB_create_context_profile ||
427 !_glfw.glx.EXT_create_context_es2_profile)
428 {
429 _glfwInputError(GLFW_API_UNAVAILABLE,
430 "GLX: OpenGL ES requested but GLX_EXT_create_context_es2_profile is unavailable");
431 return GLFW_FALSE;
432 }
433 }
434
435 if (ctxconfig->forward)
436 {
437 if (!_glfw.glx.ARB_create_context)
438 {
439 _glfwInputError(GLFW_VERSION_UNAVAILABLE,
440 "GLX: Forward compatibility requested but GLX_ARB_create_context_profile is unavailable");
441 return GLFW_FALSE;
442 }
443 }
444
445 if (ctxconfig->profile)
446 {
447 if (!_glfw.glx.ARB_create_context ||
448 !_glfw.glx.ARB_create_context_profile)
449 {
450 _glfwInputError(GLFW_VERSION_UNAVAILABLE,
451 "GLX: An OpenGL profile requested but GLX_ARB_create_context_profile is unavailable");
452 return GLFW_FALSE;
453 }
454 }
455
456 _glfwGrabErrorHandlerX11();
457
458 if (_glfw.glx.ARB_create_context)
459 {
460 int index = 0, mask = 0, flags = 0;
461
462 if (ctxconfig->client == GLFW_OPENGL_API)
463 {
464 if (ctxconfig->forward)
465 flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
466
467 if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE)
468 mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
469 else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE)
470 mask |= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
471 }
472 else
473 mask |= GLX_CONTEXT_ES2_PROFILE_BIT_EXT;
474
475 if (ctxconfig->debug)
476 flags |= GLX_CONTEXT_DEBUG_BIT_ARB;
477 if (ctxconfig->noerror)
478 flags |= GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR;
479
480 if (ctxconfig->robustness)
481 {
482 if (_glfw.glx.ARB_create_context_robustness)
483 {
484 if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION)
485 {
486 setGLXattrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
487 GLX_NO_RESET_NOTIFICATION_ARB);
488 }
489 else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET)
490 {
491 setGLXattrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
492 GLX_LOSE_CONTEXT_ON_RESET_ARB);
493 }
494
495 flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB;
496 }
497 }
498
499 if (ctxconfig->release)
500 {
501 if (_glfw.glx.ARB_context_flush_control)
502 {
503 if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE)
504 {
505 setGLXattrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB,
506 GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB);
507 }
508 else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH)
509 {
510 setGLXattrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB,
511 GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB);
512 }
513 }
514 }
515
516 // NOTE: Only request an explicitly versioned context when necessary, as
517 // explicitly requesting version 1.0 does not always return the
518 // highest version supported by the driver
519 if (ctxconfig->major != 1 || ctxconfig->minor != 0)
520 {
521 setGLXattrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major);
522 setGLXattrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor);
523 }
524
525 if (mask)
526 setGLXattrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask);
527
528 if (flags)
529 setGLXattrib(GLX_CONTEXT_FLAGS_ARB, flags);
530
531 setGLXattrib(None, None);
532
533 window->context.glx.handle =
534 _glfw.glx.CreateContextAttribsARB(_glfw.x11.display,
535 native,
536 share,
537 True,
538 attribs);
539
540 // HACK: This is a fallback for broken versions of the Mesa
541 // implementation of GLX_ARB_create_context_profile that fail
542 // default 1.0 context creation with a GLXBadProfileARB error in
543 // violation of the extension spec
544 if (!window->context.glx.handle)
545 {
546 if (_glfw.x11.errorCode == _glfw.glx.errorBase + GLXBadProfileARB &&
547 ctxconfig->client == GLFW_OPENGL_API &&
548 ctxconfig->profile == GLFW_OPENGL_ANY_PROFILE &&
549 ctxconfig->forward == GLFW_FALSE)
550 {
551 window->context.glx.handle =
552 createLegacyContext(window, native, share);
553 }
554 }
555 }
556 else
557 window->context.glx.handle = createLegacyContext(window, native, share);
558
559 _glfwReleaseErrorHandlerX11();
560
561 if (!window->context.glx.handle)
562 {
563 _glfwInputErrorX11(GLFW_VERSION_UNAVAILABLE, "GLX: Failed to create context");
564 return GLFW_FALSE;
565 }
566
567 window->context.glx.window =
568 glXCreateWindow(_glfw.x11.display, native, window->x11.handle, NULL);
569 if (!window->context.glx.window)
570 {
571 _glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to create window");
572 return GLFW_FALSE;
573 }
574
575 window->context.makeCurrent = makeContextCurrent;
576 window->context.swapBuffers = swapBuffers;
577 window->context.swapInterval = swapInterval;
578 window->context.extensionSupported = extensionSupported;
579 window->context.getProcAddress = getProcAddress;
580 window->context.destroy = destroyContext;
581
582 return GLFW_TRUE;
583}
584
585#undef setGLXattrib
586
587// Returns the Visual and depth of the chosen GLXFBConfig
588//
589GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig,
590 const _GLFWfbconfig* fbconfig,
591 Visual** visual, int* depth)
592{
593 GLXFBConfig native;
594 XVisualInfo* result;
595
596 if (!chooseFBConfig(fbconfig, &native))
597 {
598 _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
599 "GLX: Failed to find a suitable GLXFBConfig");
600 return GLFW_FALSE;
601 }
602
603 result = glXGetVisualFromFBConfig(_glfw.x11.display, native);
604 if (!result)
605 {
606 _glfwInputError(GLFW_PLATFORM_ERROR,
607 "GLX: Failed to retrieve Visual for GLXFBConfig");
608 return GLFW_FALSE;
609 }
610
611 *visual = result->visual;
612 *depth = result->depth;
613
614 XFree(result);
615 return GLFW_TRUE;
616}
617
618
619//////////////////////////////////////////////////////////////////////////
620////// GLFW native API //////
621//////////////////////////////////////////////////////////////////////////
622
623GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* handle)
624{
625 _GLFWwindow* window = (_GLFWwindow*) handle;
626 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
627
628 if (window->context.client == GLFW_NO_API)
629 {
630 _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
631 return NULL;
632 }
633
634 return window->context.glx.handle;
635}
636
637GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* handle)
638{
639 _GLFWwindow* window = (_GLFWwindow*) handle;
640 _GLFW_REQUIRE_INIT_OR_RETURN(None);
641
642 if (window->context.client == GLFW_NO_API)
643 {
644 _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
645 return None;
646 }
647
648 return window->context.glx.window;
649}
650
651