1 //========================================================================
2 // GLFW 3.2 - 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 <assert.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <limits.h>
34 #include <stdio.h>
35
36
37 //////////////////////////////////////////////////////////////////////////
38 ////// GLFW internal API //////
39 //////////////////////////////////////////////////////////////////////////
40
_glfwIsValidContextConfig(const _GLFWctxconfig * ctxconfig)41 GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
42 {
43 if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API &&
44 ctxconfig->source != GLFW_EGL_CONTEXT_API)
45 {
46 _glfwInputError(GLFW_INVALID_ENUM,
47 "Invalid context creation API %i",
48 ctxconfig->source);
49 return GLFW_FALSE;
50 }
51
52 if (ctxconfig->client != GLFW_NO_API &&
53 ctxconfig->client != GLFW_OPENGL_API &&
54 ctxconfig->client != GLFW_OPENGL_ES_API)
55 {
56 _glfwInputError(GLFW_INVALID_ENUM,
57 "Invalid client API %i",
58 ctxconfig->client);
59 return GLFW_FALSE;
60 }
61
62 if (ctxconfig->client == GLFW_OPENGL_API)
63 {
64 if ((ctxconfig->major < 1 || ctxconfig->minor < 0) ||
65 (ctxconfig->major == 1 && ctxconfig->minor > 5) ||
66 (ctxconfig->major == 2 && ctxconfig->minor > 1) ||
67 (ctxconfig->major == 3 && ctxconfig->minor > 3))
68 {
69 // OpenGL 1.0 is the smallest valid version
70 // OpenGL 1.x series ended with version 1.5
71 // OpenGL 2.x series ended with version 2.1
72 // OpenGL 3.x series ended with version 3.3
73 // For now, let everything else through
74
75 _glfwInputError(GLFW_INVALID_VALUE,
76 "Invalid OpenGL version %i.%i",
77 ctxconfig->major, ctxconfig->minor);
78 return GLFW_FALSE;
79 }
80
81 if (ctxconfig->profile)
82 {
83 if (ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE &&
84 ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE)
85 {
86 _glfwInputError(GLFW_INVALID_ENUM,
87 "Invalid OpenGL profile %i",
88 ctxconfig->profile);
89 return GLFW_FALSE;
90 }
91
92 if (ctxconfig->major <= 2 ||
93 (ctxconfig->major == 3 && ctxconfig->minor < 2))
94 {
95 // Desktop OpenGL context profiles are only defined for version 3.2
96 // and above
97
98 _glfwInputError(GLFW_INVALID_VALUE,
99 "Context profiles are only defined for OpenGL version 3.2 and above");
100 return GLFW_FALSE;
101 }
102 }
103
104 if (ctxconfig->forward && ctxconfig->major <= 2)
105 {
106 // Forward-compatible contexts are only defined for OpenGL version 3.0 and above
107 _glfwInputError(GLFW_INVALID_VALUE,
108 "Forward-compatibility is only defined for OpenGL version 3.0 and above");
109 return GLFW_FALSE;
110 }
111 }
112 else if (ctxconfig->client == GLFW_OPENGL_ES_API)
113 {
114 if (ctxconfig->major < 1 || ctxconfig->minor < 0 ||
115 (ctxconfig->major == 1 && ctxconfig->minor > 1) ||
116 (ctxconfig->major == 2 && ctxconfig->minor > 0))
117 {
118 // OpenGL ES 1.0 is the smallest valid version
119 // OpenGL ES 1.x series ended with version 1.1
120 // OpenGL ES 2.x series ended with version 2.0
121 // For now, let everything else through
122
123 _glfwInputError(GLFW_INVALID_VALUE,
124 "Invalid OpenGL ES version %i.%i",
125 ctxconfig->major, ctxconfig->minor);
126 return GLFW_FALSE;
127 }
128 }
129
130 if (ctxconfig->robustness)
131 {
132 if (ctxconfig->robustness != GLFW_NO_RESET_NOTIFICATION &&
133 ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET)
134 {
135 _glfwInputError(GLFW_INVALID_ENUM,
136 "Invalid context robustness mode %i",
137 ctxconfig->robustness);
138 return GLFW_FALSE;
139 }
140 }
141
142 if (ctxconfig->release)
143 {
144 if (ctxconfig->release != GLFW_RELEASE_BEHAVIOR_NONE &&
145 ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH)
146 {
147 _glfwInputError(GLFW_INVALID_ENUM,
148 "Invalid context release behavior %i",
149 ctxconfig->release);
150 return GLFW_FALSE;
151 }
152 }
153
154 return GLFW_TRUE;
155 }
156
_glfwChooseFBConfig(const _GLFWfbconfig * desired,const _GLFWfbconfig * alternatives,unsigned int count)157 const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
158 const _GLFWfbconfig* alternatives,
159 unsigned int count)
160 {
161 unsigned int i;
162 unsigned int missing, leastMissing = UINT_MAX;
163 unsigned int colorDiff, leastColorDiff = UINT_MAX;
164 unsigned int extraDiff, leastExtraDiff = UINT_MAX;
165 const _GLFWfbconfig* current;
166 const _GLFWfbconfig* closest = NULL;
167
168 for (i = 0; i < count; i++)
169 {
170 current = alternatives + i;
171
172 if (desired->stereo > 0 && current->stereo == 0)
173 {
174 // Stereo is a hard constraint
175 continue;
176 }
177
178 if (desired->doublebuffer != current->doublebuffer)
179 {
180 // Double buffering is a hard constraint
181 continue;
182 }
183
184 // Count number of missing buffers
185 {
186 missing = 0;
187
188 if (desired->alphaBits > 0 && current->alphaBits == 0)
189 missing++;
190
191 if (desired->depthBits > 0 && current->depthBits == 0)
192 missing++;
193
194 if (desired->stencilBits > 0 && current->stencilBits == 0)
195 missing++;
196
197 if (desired->auxBuffers > 0 &&
198 current->auxBuffers < desired->auxBuffers)
199 {
200 missing += desired->auxBuffers - current->auxBuffers;
201 }
202
203 if (desired->samples > 0 && current->samples == 0)
204 {
205 // Technically, several multisampling buffers could be
206 // involved, but that's a lower level implementation detail and
207 // not important to us here, so we count them as one
208 missing++;
209 }
210 }
211
212 // These polynomials make many small channel size differences matter
213 // less than one large channel size difference
214
215 // Calculate color channel size difference value
216 {
217 colorDiff = 0;
218
219 if (desired->redBits != GLFW_DONT_CARE)
220 {
221 colorDiff += (desired->redBits - current->redBits) *
222 (desired->redBits - current->redBits);
223 }
224
225 if (desired->greenBits != GLFW_DONT_CARE)
226 {
227 colorDiff += (desired->greenBits - current->greenBits) *
228 (desired->greenBits - current->greenBits);
229 }
230
231 if (desired->blueBits != GLFW_DONT_CARE)
232 {
233 colorDiff += (desired->blueBits - current->blueBits) *
234 (desired->blueBits - current->blueBits);
235 }
236 }
237
238 // Calculate non-color channel size difference value
239 {
240 extraDiff = 0;
241
242 if (desired->alphaBits != GLFW_DONT_CARE)
243 {
244 extraDiff += (desired->alphaBits - current->alphaBits) *
245 (desired->alphaBits - current->alphaBits);
246 }
247
248 if (desired->depthBits != GLFW_DONT_CARE)
249 {
250 extraDiff += (desired->depthBits - current->depthBits) *
251 (desired->depthBits - current->depthBits);
252 }
253
254 if (desired->stencilBits != GLFW_DONT_CARE)
255 {
256 extraDiff += (desired->stencilBits - current->stencilBits) *
257 (desired->stencilBits - current->stencilBits);
258 }
259
260 if (desired->accumRedBits != GLFW_DONT_CARE)
261 {
262 extraDiff += (desired->accumRedBits - current->accumRedBits) *
263 (desired->accumRedBits - current->accumRedBits);
264 }
265
266 if (desired->accumGreenBits != GLFW_DONT_CARE)
267 {
268 extraDiff += (desired->accumGreenBits - current->accumGreenBits) *
269 (desired->accumGreenBits - current->accumGreenBits);
270 }
271
272 if (desired->accumBlueBits != GLFW_DONT_CARE)
273 {
274 extraDiff += (desired->accumBlueBits - current->accumBlueBits) *
275 (desired->accumBlueBits - current->accumBlueBits);
276 }
277
278 if (desired->accumAlphaBits != GLFW_DONT_CARE)
279 {
280 extraDiff += (desired->accumAlphaBits - current->accumAlphaBits) *
281 (desired->accumAlphaBits - current->accumAlphaBits);
282 }
283
284 if (desired->samples != GLFW_DONT_CARE)
285 {
286 extraDiff += (desired->samples - current->samples) *
287 (desired->samples - current->samples);
288 }
289
290 if (desired->sRGB && !current->sRGB)
291 extraDiff++;
292 }
293
294 // Figure out if the current one is better than the best one found so far
295 // Least number of missing buffers is the most important heuristic,
296 // then color buffer size match and lastly size match for other buffers
297
298 if (missing < leastMissing)
299 closest = current;
300 else if (missing == leastMissing)
301 {
302 if ((colorDiff < leastColorDiff) ||
303 (colorDiff == leastColorDiff && extraDiff < leastExtraDiff))
304 {
305 closest = current;
306 }
307 }
308
309 if (current == closest)
310 {
311 leastMissing = missing;
312 leastColorDiff = colorDiff;
313 leastExtraDiff = extraDiff;
314 }
315 }
316
317 return closest;
318 }
319
_glfwRefreshContextAttribs(const _GLFWctxconfig * ctxconfig)320 GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig)
321 {
322 int i;
323 _GLFWwindow* window;
324 const char* version;
325 const char* prefixes[] =
326 {
327 "OpenGL ES-CM ",
328 "OpenGL ES-CL ",
329 "OpenGL ES ",
330 NULL
331 };
332
333 window = _glfwPlatformGetCurrentContext();
334
335 window->context.source = ctxconfig->source;
336 window->context.client = GLFW_OPENGL_API;
337
338 window->context.GetIntegerv = (PFNGLGETINTEGERVPROC)
339 window->context.getProcAddress("glGetIntegerv");
340 window->context.GetString = (PFNGLGETSTRINGPROC)
341 window->context.getProcAddress("glGetString");
342 if (!window->context.GetIntegerv || !window->context.GetString)
343 {
344 _glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken");
345 return GLFW_FALSE;
346 }
347
348 version = (const char*) window->context.GetString(GL_VERSION);
349 if (!version)
350 {
351 if (ctxconfig->client == GLFW_OPENGL_API)
352 {
353 _glfwInputError(GLFW_PLATFORM_ERROR,
354 "OpenGL version string retrieval is broken");
355 }
356 else
357 {
358 _glfwInputError(GLFW_PLATFORM_ERROR,
359 "OpenGL ES version string retrieval is broken");
360 }
361
362 return GLFW_FALSE;
363 }
364
365 for (i = 0; prefixes[i]; i++)
366 {
367 const size_t length = strlen(prefixes[i]);
368
369 if (strncmp(version, prefixes[i], length) == 0)
370 {
371 version += length;
372 window->context.client = GLFW_OPENGL_ES_API;
373 break;
374 }
375 }
376
377 if (!sscanf(version, "%d.%d.%d",
378 &window->context.major,
379 &window->context.minor,
380 &window->context.revision))
381 {
382 if (window->context.client == GLFW_OPENGL_API)
383 {
384 _glfwInputError(GLFW_PLATFORM_ERROR,
385 "No version found in OpenGL version string");
386 }
387 else
388 {
389 _glfwInputError(GLFW_PLATFORM_ERROR,
390 "No version found in OpenGL ES version string");
391 }
392
393 return GLFW_FALSE;
394 }
395
396 if (window->context.major < ctxconfig->major ||
397 (window->context.major == ctxconfig->major &&
398 window->context.minor < ctxconfig->minor))
399 {
400 // The desired OpenGL version is greater than the actual version
401 // This only happens if the machine lacks {GLX|WGL}_ARB_create_context
402 // /and/ the user has requested an OpenGL version greater than 1.0
403
404 // For API consistency, we emulate the behavior of the
405 // {GLX|WGL}_ARB_create_context extension and fail here
406
407 if (window->context.client == GLFW_OPENGL_API)
408 {
409 _glfwInputError(GLFW_VERSION_UNAVAILABLE,
410 "Requested OpenGL version %i.%i, got version %i.%i",
411 ctxconfig->major, ctxconfig->minor,
412 window->context.major, window->context.minor);
413 }
414 else
415 {
416 _glfwInputError(GLFW_VERSION_UNAVAILABLE,
417 "Requested OpenGL ES version %i.%i, got version %i.%i",
418 ctxconfig->major, ctxconfig->minor,
419 window->context.major, window->context.minor);
420 }
421
422 return GLFW_FALSE;
423 }
424
425 if (window->context.major >= 3)
426 {
427 // OpenGL 3.0+ uses a different function for extension string retrieval
428 // We cache it here instead of in glfwExtensionSupported mostly to alert
429 // users as early as possible that their build may be broken
430
431 window->context.GetStringi = (PFNGLGETSTRINGIPROC)
432 window->context.getProcAddress("glGetStringi");
433 if (!window->context.GetStringi)
434 {
435 _glfwInputError(GLFW_PLATFORM_ERROR,
436 "Entry point retrieval is broken");
437 return GLFW_FALSE;
438 }
439 }
440
441 if (window->context.client == GLFW_OPENGL_API)
442 {
443 // Read back context flags (OpenGL 3.0 and above)
444 if (window->context.major >= 3)
445 {
446 GLint flags;
447 window->context.GetIntegerv(GL_CONTEXT_FLAGS, &flags);
448
449 if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)
450 window->context.forward = GLFW_TRUE;
451
452 if (flags & GL_CONTEXT_FLAG_DEBUG_BIT)
453 window->context.debug = GLFW_TRUE;
454 else if (glfwExtensionSupported("GL_ARB_debug_output") &&
455 ctxconfig->debug)
456 {
457 // HACK: This is a workaround for older drivers (pre KHR_debug)
458 // not setting the debug bit in the context flags for
459 // debug contexts
460 window->context.debug = GLFW_TRUE;
461 }
462
463 if (flags & GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR)
464 window->context.noerror = GLFW_TRUE;
465 }
466
467 // Read back OpenGL context profile (OpenGL 3.2 and above)
468 if (window->context.major >= 4 ||
469 (window->context.major == 3 && window->context.minor >= 2))
470 {
471 GLint mask;
472 window->context.GetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask);
473
474 if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)
475 window->context.profile = GLFW_OPENGL_COMPAT_PROFILE;
476 else if (mask & GL_CONTEXT_CORE_PROFILE_BIT)
477 window->context.profile = GLFW_OPENGL_CORE_PROFILE;
478 else if (glfwExtensionSupported("GL_ARB_compatibility"))
479 {
480 // HACK: This is a workaround for the compatibility profile bit
481 // not being set in the context flags if an OpenGL 3.2+
482 // context was created without having requested a specific
483 // version
484 window->context.profile = GLFW_OPENGL_COMPAT_PROFILE;
485 }
486 }
487
488 // Read back robustness strategy
489 if (glfwExtensionSupported("GL_ARB_robustness"))
490 {
491 // NOTE: We avoid using the context flags for detection, as they are
492 // only present from 3.0 while the extension applies from 1.1
493
494 GLint strategy;
495 window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB,
496 &strategy);
497
498 if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB)
499 window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET;
500 else if (strategy == GL_NO_RESET_NOTIFICATION_ARB)
501 window->context.robustness = GLFW_NO_RESET_NOTIFICATION;
502 }
503 }
504 else
505 {
506 // Read back robustness strategy
507 if (glfwExtensionSupported("GL_EXT_robustness"))
508 {
509 // NOTE: The values of these constants match those of the OpenGL ARB
510 // one, so we can reuse them here
511
512 GLint strategy;
513 window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB,
514 &strategy);
515
516 if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB)
517 window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET;
518 else if (strategy == GL_NO_RESET_NOTIFICATION_ARB)
519 window->context.robustness = GLFW_NO_RESET_NOTIFICATION;
520 }
521 }
522
523 if (glfwExtensionSupported("GL_KHR_context_flush_control"))
524 {
525 GLint behavior;
526 window->context.GetIntegerv(GL_CONTEXT_RELEASE_BEHAVIOR, &behavior);
527
528 if (behavior == GL_NONE)
529 window->context.release = GLFW_RELEASE_BEHAVIOR_NONE;
530 else if (behavior == GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH)
531 window->context.release = GLFW_RELEASE_BEHAVIOR_FLUSH;
532 }
533
534 // Clearing the front buffer to black to avoid garbage pixels left over from
535 // previous uses of our bit of VRAM
536 {
537 PFNGLCLEARPROC glClear = (PFNGLCLEARPROC)
538 window->context.getProcAddress("glClear");
539 glClear(GL_COLOR_BUFFER_BIT);
540 window->context.swapBuffers(window);
541 }
542
543 return GLFW_TRUE;
544 }
545
_glfwStringInExtensionString(const char * string,const char * extensions)546 GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions)
547 {
548 const char* start = extensions;
549
550 for (;;)
551 {
552 const char* where;
553 const char* terminator;
554
555 where = strstr(start, string);
556 if (!where)
557 return GLFW_FALSE;
558
559 terminator = where + strlen(string);
560 if (where == start || *(where - 1) == ' ')
561 {
562 if (*terminator == ' ' || *terminator == '\0')
563 break;
564 }
565
566 start = terminator;
567 }
568
569 return GLFW_TRUE;
570 }
571
572
573 //////////////////////////////////////////////////////////////////////////
574 ////// GLFW public API //////
575 //////////////////////////////////////////////////////////////////////////
576
glfwMakeContextCurrent(GLFWwindow * handle)577 GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle)
578 {
579 _GLFWwindow* window = (_GLFWwindow*) handle;
580 _GLFWwindow* previous = _glfwPlatformGetCurrentContext();
581
582 _GLFW_REQUIRE_INIT();
583
584 if (window && window->context.client == GLFW_NO_API)
585 {
586 _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
587 return;
588 }
589
590 if (previous)
591 {
592 if (!window || window->context.source != previous->context.source)
593 previous->context.makeCurrent(NULL);
594 }
595
596 if (window)
597 window->context.makeCurrent(window);
598 }
599
glfwGetCurrentContext(void)600 GLFWAPI GLFWwindow* glfwGetCurrentContext(void)
601 {
602 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
603 return (GLFWwindow*) _glfwPlatformGetCurrentContext();
604 }
605
glfwSwapBuffers(GLFWwindow * handle)606 GLFWAPI void glfwSwapBuffers(GLFWwindow* handle)
607 {
608 _GLFWwindow* window = (_GLFWwindow*) handle;
609 assert(window != NULL);
610
611 _GLFW_REQUIRE_INIT();
612
613 if (window->context.client == GLFW_NO_API)
614 {
615 _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
616 return;
617 }
618
619 window->context.swapBuffers(window);
620 }
621
glfwSwapInterval(int interval)622 GLFWAPI void glfwSwapInterval(int interval)
623 {
624 _GLFWwindow* window;
625
626 _GLFW_REQUIRE_INIT();
627
628 window = _glfwPlatformGetCurrentContext();
629 if (!window)
630 {
631 _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL);
632 return;
633 }
634
635 window->context.swapInterval(interval);
636 }
637
glfwExtensionSupported(const char * extension)638 GLFWAPI int glfwExtensionSupported(const char* extension)
639 {
640 _GLFWwindow* window;
641
642 assert(extension != NULL);
643
644 _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
645
646 window = _glfwPlatformGetCurrentContext();
647 if (!window)
648 {
649 _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL);
650 return GLFW_FALSE;
651 }
652
653 if (*extension == '\0')
654 {
655 _glfwInputError(GLFW_INVALID_VALUE, "Extension name is empty string");
656 return GLFW_FALSE;
657 }
658
659 if (window->context.major >= 3)
660 {
661 int i;
662 GLint count;
663
664 // Check if extension is in the modern OpenGL extensions string list
665
666 window->context.GetIntegerv(GL_NUM_EXTENSIONS, &count);
667
668 for (i = 0; i < count; i++)
669 {
670 const char* en = (const char*)
671 window->context.GetStringi(GL_EXTENSIONS, i);
672 if (!en)
673 {
674 _glfwInputError(GLFW_PLATFORM_ERROR,
675 "Extension string retrieval is broken");
676 return GLFW_FALSE;
677 }
678
679 if (strcmp(en, extension) == 0)
680 return GLFW_TRUE;
681 }
682 }
683 else
684 {
685 // Check if extension is in the old style OpenGL extensions string
686
687 const char* extensions = (const char*)
688 window->context.GetString(GL_EXTENSIONS);
689 if (!extensions)
690 {
691 _glfwInputError(GLFW_PLATFORM_ERROR,
692 "Extension string retrieval is broken");
693 return GLFW_FALSE;
694 }
695
696 if (_glfwStringInExtensionString(extension, extensions))
697 return GLFW_TRUE;
698 }
699
700 // Check if extension is in the platform-specific string
701 return window->context.extensionSupported(extension);
702 }
703
glfwGetProcAddress(const char * procname)704 GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname)
705 {
706 _GLFWwindow* window;
707 assert(procname != NULL);
708
709 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
710
711 window = _glfwPlatformGetCurrentContext();
712 if (!window)
713 {
714 _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL);
715 return NULL;
716 }
717
718 return window->context.getProcAddress(procname);
719 }
720
721