• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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