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