| //======================================================================== |
| // GLFW 3.3 - www.glfw.org |
| //------------------------------------------------------------------------ |
| // Copyright (c) 2002-2006 Marcus Geelnard |
| // Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org> |
| // |
| // This software is provided 'as-is', without any express or implied |
| // warranty. In no event will the authors be held liable for any damages |
| // arising from the use of this software. |
| // |
| // Permission is granted to anyone to use this software for any purpose, |
| // including commercial applications, and to alter it and redistribute it |
| // freely, subject to the following restrictions: |
| // |
| // 1. The origin of this software must not be misrepresented; you must not |
| // claim that you wrote the original software. If you use this software |
| // in a product, an acknowledgment in the product documentation would |
| // be appreciated but is not required. |
| // |
| // 2. Altered source versions must be plainly marked as such, and must not |
| // be misrepresented as being the original software. |
| // |
| // 3. This notice may not be removed or altered from any source |
| // distribution. |
| // |
| //======================================================================== |
| |
| #include "internal.h" |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <limits.h> |
| #include <stdio.h> |
| |
| |
| ////////////////////////////////////////////////////////////////////////// |
| ////// GLFW internal API ////// |
| ////////////////////////////////////////////////////////////////////////// |
| |
| // Checks whether the desired context attributes are valid |
| // |
| // This function checks things like whether the specified client API version |
| // exists and whether all relevant options have supported and non-conflicting |
| // values |
| // |
| GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) |
| { |
| if (ctxconfig->share) |
| { |
| if (ctxconfig->client == GLFW_NO_API || |
| ctxconfig->share->context.client == GLFW_NO_API) |
| { |
| _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); |
| return GLFW_FALSE; |
| } |
| } |
| |
| if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API && |
| ctxconfig->source != GLFW_EGL_CONTEXT_API && |
| ctxconfig->source != GLFW_OSMESA_CONTEXT_API) |
| { |
| _glfwInputError(GLFW_INVALID_ENUM, |
| "Invalid context creation API 0x%08X", |
| ctxconfig->source); |
| return GLFW_FALSE; |
| } |
| |
| if (ctxconfig->client != GLFW_NO_API && |
| ctxconfig->client != GLFW_OPENGL_API && |
| ctxconfig->client != GLFW_OPENGL_ES_API) |
| { |
| _glfwInputError(GLFW_INVALID_ENUM, |
| "Invalid client API 0x%08X", |
| ctxconfig->client); |
| return GLFW_FALSE; |
| } |
| |
| if (ctxconfig->client == GLFW_OPENGL_API) |
| { |
| if ((ctxconfig->major < 1 || ctxconfig->minor < 0) || |
| (ctxconfig->major == 1 && ctxconfig->minor > 5) || |
| (ctxconfig->major == 2 && ctxconfig->minor > 1) || |
| (ctxconfig->major == 3 && ctxconfig->minor > 3)) |
| { |
| // OpenGL 1.0 is the smallest valid version |
| // OpenGL 1.x series ended with version 1.5 |
| // OpenGL 2.x series ended with version 2.1 |
| // OpenGL 3.x series ended with version 3.3 |
| // For now, let everything else through |
| |
| _glfwInputError(GLFW_INVALID_VALUE, |
| "Invalid OpenGL version %i.%i", |
| ctxconfig->major, ctxconfig->minor); |
| return GLFW_FALSE; |
| } |
| |
| if (ctxconfig->profile) |
| { |
| if (ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE && |
| ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE) |
| { |
| _glfwInputError(GLFW_INVALID_ENUM, |
| "Invalid OpenGL profile 0x%08X", |
| ctxconfig->profile); |
| return GLFW_FALSE; |
| } |
| |
| if (ctxconfig->major <= 2 || |
| (ctxconfig->major == 3 && ctxconfig->minor < 2)) |
| { |
| // Desktop OpenGL context profiles are only defined for version 3.2 |
| // and above |
| |
| _glfwInputError(GLFW_INVALID_VALUE, |
| "Context profiles are only defined for OpenGL version 3.2 and above"); |
| return GLFW_FALSE; |
| } |
| } |
| |
| if (ctxconfig->forward && ctxconfig->major <= 2) |
| { |
| // Forward-compatible contexts are only defined for OpenGL version 3.0 and above |
| _glfwInputError(GLFW_INVALID_VALUE, |
| "Forward-compatibility is only defined for OpenGL version 3.0 and above"); |
| return GLFW_FALSE; |
| } |
| } |
| else if (ctxconfig->client == GLFW_OPENGL_ES_API) |
| { |
| if (ctxconfig->major < 1 || ctxconfig->minor < 0 || |
| (ctxconfig->major == 1 && ctxconfig->minor > 1) || |
| (ctxconfig->major == 2 && ctxconfig->minor > 0)) |
| { |
| // OpenGL ES 1.0 is the smallest valid version |
| // OpenGL ES 1.x series ended with version 1.1 |
| // OpenGL ES 2.x series ended with version 2.0 |
| // For now, let everything else through |
| |
| _glfwInputError(GLFW_INVALID_VALUE, |
| "Invalid OpenGL ES version %i.%i", |
| ctxconfig->major, ctxconfig->minor); |
| return GLFW_FALSE; |
| } |
| } |
| |
| if (ctxconfig->robustness) |
| { |
| if (ctxconfig->robustness != GLFW_NO_RESET_NOTIFICATION && |
| ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET) |
| { |
| _glfwInputError(GLFW_INVALID_ENUM, |
| "Invalid context robustness mode 0x%08X", |
| ctxconfig->robustness); |
| return GLFW_FALSE; |
| } |
| } |
| |
| if (ctxconfig->release) |
| { |
| if (ctxconfig->release != GLFW_RELEASE_BEHAVIOR_NONE && |
| ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH) |
| { |
| _glfwInputError(GLFW_INVALID_ENUM, |
| "Invalid context release behavior 0x%08X", |
| ctxconfig->release); |
| return GLFW_FALSE; |
| } |
| } |
| |
| return GLFW_TRUE; |
| } |
| |
| // Chooses the framebuffer config that best matches the desired one |
| // |
| const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, |
| const _GLFWfbconfig* alternatives, |
| unsigned int count) |
| { |
| unsigned int i; |
| unsigned int missing, leastMissing = UINT_MAX; |
| unsigned int colorDiff, leastColorDiff = UINT_MAX; |
| unsigned int extraDiff, leastExtraDiff = UINT_MAX; |
| const _GLFWfbconfig* current; |
| const _GLFWfbconfig* closest = NULL; |
| |
| for (i = 0; i < count; i++) |
| { |
| current = alternatives + i; |
| |
| if (desired->stereo > 0 && current->stereo == 0) |
| { |
| // Stereo is a hard constraint |
| continue; |
| } |
| |
| if (desired->doublebuffer != current->doublebuffer) |
| { |
| // Double buffering is a hard constraint |
| continue; |
| } |
| |
| // Count number of missing buffers |
| { |
| missing = 0; |
| |
| if (desired->alphaBits > 0 && current->alphaBits == 0) |
| missing++; |
| |
| if (desired->depthBits > 0 && current->depthBits == 0) |
| missing++; |
| |
| if (desired->stencilBits > 0 && current->stencilBits == 0) |
| missing++; |
| |
| if (desired->auxBuffers > 0 && |
| current->auxBuffers < desired->auxBuffers) |
| { |
| missing += desired->auxBuffers - current->auxBuffers; |
| } |
| |
| if (desired->samples > 0 && current->samples == 0) |
| { |
| // Technically, several multisampling buffers could be |
| // involved, but that's a lower level implementation detail and |
| // not important to us here, so we count them as one |
| missing++; |
| } |
| |
| if (desired->transparent != current->transparent) |
| missing++; |
| } |
| |
| // These polynomials make many small channel size differences matter |
| // less than one large channel size difference |
| |
| // Calculate color channel size difference value |
| { |
| colorDiff = 0; |
| |
| if (desired->redBits != GLFW_DONT_CARE) |
| { |
| colorDiff += (desired->redBits - current->redBits) * |
| (desired->redBits - current->redBits); |
| } |
| |
| if (desired->greenBits != GLFW_DONT_CARE) |
| { |
| colorDiff += (desired->greenBits - current->greenBits) * |
| (desired->greenBits - current->greenBits); |
| } |
| |
| if (desired->blueBits != GLFW_DONT_CARE) |
| { |
| colorDiff += (desired->blueBits - current->blueBits) * |
| (desired->blueBits - current->blueBits); |
| } |
| } |
| |
| // Calculate non-color channel size difference value |
| { |
| extraDiff = 0; |
| |
| if (desired->alphaBits != GLFW_DONT_CARE) |
| { |
| extraDiff += (desired->alphaBits - current->alphaBits) * |
| (desired->alphaBits - current->alphaBits); |
| } |
| |
| if (desired->depthBits != GLFW_DONT_CARE) |
| { |
| extraDiff += (desired->depthBits - current->depthBits) * |
| (desired->depthBits - current->depthBits); |
| } |
| |
| if (desired->stencilBits != GLFW_DONT_CARE) |
| { |
| extraDiff += (desired->stencilBits - current->stencilBits) * |
| (desired->stencilBits - current->stencilBits); |
| } |
| |
| if (desired->accumRedBits != GLFW_DONT_CARE) |
| { |
| extraDiff += (desired->accumRedBits - current->accumRedBits) * |
| (desired->accumRedBits - current->accumRedBits); |
| } |
| |
| if (desired->accumGreenBits != GLFW_DONT_CARE) |
| { |
| extraDiff += (desired->accumGreenBits - current->accumGreenBits) * |
| (desired->accumGreenBits - current->accumGreenBits); |
| } |
| |
| if (desired->accumBlueBits != GLFW_DONT_CARE) |
| { |
| extraDiff += (desired->accumBlueBits - current->accumBlueBits) * |
| (desired->accumBlueBits - current->accumBlueBits); |
| } |
| |
| if (desired->accumAlphaBits != GLFW_DONT_CARE) |
| { |
| extraDiff += (desired->accumAlphaBits - current->accumAlphaBits) * |
| (desired->accumAlphaBits - current->accumAlphaBits); |
| } |
| |
| if (desired->samples != GLFW_DONT_CARE) |
| { |
| extraDiff += (desired->samples - current->samples) * |
| (desired->samples - current->samples); |
| } |
| |
| if (desired->sRGB && !current->sRGB) |
| extraDiff++; |
| } |
| |
| // Figure out if the current one is better than the best one found so far |
| // Least number of missing buffers is the most important heuristic, |
| // then color buffer size match and lastly size match for other buffers |
| |
| if (missing < leastMissing) |
| closest = current; |
| else if (missing == leastMissing) |
| { |
| if ((colorDiff < leastColorDiff) || |
| (colorDiff == leastColorDiff && extraDiff < leastExtraDiff)) |
| { |
| closest = current; |
| } |
| } |
| |
| if (current == closest) |
| { |
| leastMissing = missing; |
| leastColorDiff = colorDiff; |
| leastExtraDiff = extraDiff; |
| } |
| } |
| |
| return closest; |
| } |
| |
| // Retrieves the attributes of the current context |
| // |
| GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window, |
| const _GLFWctxconfig* ctxconfig) |
| { |
| int i; |
| _GLFWwindow* previous; |
| const char* version; |
| const char* prefixes[] = |
| { |
| "OpenGL ES-CM ", |
| "OpenGL ES-CL ", |
| "OpenGL ES ", |
| NULL |
| }; |
| |
| window->context.source = ctxconfig->source; |
| window->context.client = GLFW_OPENGL_API; |
| |
| previous = _glfwPlatformGetTls(&_glfw.contextSlot); |
| glfwMakeContextCurrent((GLFWwindow*) window); |
| |
| window->context.GetIntegerv = (PFNGLGETINTEGERVPROC) |
| window->context.getProcAddress("glGetIntegerv"); |
| window->context.GetString = (PFNGLGETSTRINGPROC) |
| window->context.getProcAddress("glGetString"); |
| if (!window->context.GetIntegerv || !window->context.GetString) |
| { |
| _glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken"); |
| glfwMakeContextCurrent((GLFWwindow*) previous); |
| return GLFW_FALSE; |
| } |
| |
| version = (const char*) window->context.GetString(GL_VERSION); |
| if (!version) |
| { |
| if (ctxconfig->client == GLFW_OPENGL_API) |
| { |
| _glfwInputError(GLFW_PLATFORM_ERROR, |
| "OpenGL version string retrieval is broken"); |
| } |
| else |
| { |
| _glfwInputError(GLFW_PLATFORM_ERROR, |
| "OpenGL ES version string retrieval is broken"); |
| } |
| |
| glfwMakeContextCurrent((GLFWwindow*) previous); |
| return GLFW_FALSE; |
| } |
| |
| for (i = 0; prefixes[i]; i++) |
| { |
| const size_t length = strlen(prefixes[i]); |
| |
| if (strncmp(version, prefixes[i], length) == 0) |
| { |
| version += length; |
| window->context.client = GLFW_OPENGL_ES_API; |
| break; |
| } |
| } |
| |
| if (!sscanf(version, "%d.%d.%d", |
| &window->context.major, |
| &window->context.minor, |
| &window->context.revision)) |
| { |
| if (window->context.client == GLFW_OPENGL_API) |
| { |
| _glfwInputError(GLFW_PLATFORM_ERROR, |
| "No version found in OpenGL version string"); |
| } |
| else |
| { |
| _glfwInputError(GLFW_PLATFORM_ERROR, |
| "No version found in OpenGL ES version string"); |
| } |
| |
| glfwMakeContextCurrent((GLFWwindow*) previous); |
| return GLFW_FALSE; |
| } |
| |
| if (window->context.major < ctxconfig->major || |
| (window->context.major == ctxconfig->major && |
| window->context.minor < ctxconfig->minor)) |
| { |
| // The desired OpenGL version is greater than the actual version |
| // This only happens if the machine lacks {GLX|WGL}_ARB_create_context |
| // /and/ the user has requested an OpenGL version greater than 1.0 |
| |
| // For API consistency, we emulate the behavior of the |
| // {GLX|WGL}_ARB_create_context extension and fail here |
| |
| if (window->context.client == GLFW_OPENGL_API) |
| { |
| _glfwInputError(GLFW_VERSION_UNAVAILABLE, |
| "Requested OpenGL version %i.%i, got version %i.%i", |
| ctxconfig->major, ctxconfig->minor, |
| window->context.major, window->context.minor); |
| } |
| else |
| { |
| _glfwInputError(GLFW_VERSION_UNAVAILABLE, |
| "Requested OpenGL ES version %i.%i, got version %i.%i", |
| ctxconfig->major, ctxconfig->minor, |
| window->context.major, window->context.minor); |
| } |
| |
| glfwMakeContextCurrent((GLFWwindow*) previous); |
| return GLFW_FALSE; |
| } |
| |
| if (window->context.major >= 3) |
| { |
| // OpenGL 3.0+ uses a different function for extension string retrieval |
| // We cache it here instead of in glfwExtensionSupported mostly to alert |
| // users as early as possible that their build may be broken |
| |
| window->context.GetStringi = (PFNGLGETSTRINGIPROC) |
| window->context.getProcAddress("glGetStringi"); |
| if (!window->context.GetStringi) |
| { |
| _glfwInputError(GLFW_PLATFORM_ERROR, |
| "Entry point retrieval is broken"); |
| glfwMakeContextCurrent((GLFWwindow*) previous); |
| return GLFW_FALSE; |
| } |
| } |
| |
| if (window->context.client == GLFW_OPENGL_API) |
| { |
| // Read back context flags (OpenGL 3.0 and above) |
| if (window->context.major >= 3) |
| { |
| GLint flags; |
| window->context.GetIntegerv(GL_CONTEXT_FLAGS, &flags); |
| |
| if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) |
| window->context.forward = GLFW_TRUE; |
| |
| if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) |
| window->context.debug = GLFW_TRUE; |
| else if (glfwExtensionSupported("GL_ARB_debug_output") && |
| ctxconfig->debug) |
| { |
| // HACK: This is a workaround for older drivers (pre KHR_debug) |
| // not setting the debug bit in the context flags for |
| // debug contexts |
| window->context.debug = GLFW_TRUE; |
| } |
| |
| if (flags & GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR) |
| window->context.noerror = GLFW_TRUE; |
| } |
| |
| // Read back OpenGL context profile (OpenGL 3.2 and above) |
| if (window->context.major >= 4 || |
| (window->context.major == 3 && window->context.minor >= 2)) |
| { |
| GLint mask; |
| window->context.GetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); |
| |
| if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) |
| window->context.profile = GLFW_OPENGL_COMPAT_PROFILE; |
| else if (mask & GL_CONTEXT_CORE_PROFILE_BIT) |
| window->context.profile = GLFW_OPENGL_CORE_PROFILE; |
| else if (glfwExtensionSupported("GL_ARB_compatibility")) |
| { |
| // HACK: This is a workaround for the compatibility profile bit |
| // not being set in the context flags if an OpenGL 3.2+ |
| // context was created without having requested a specific |
| // version |
| window->context.profile = GLFW_OPENGL_COMPAT_PROFILE; |
| } |
| } |
| |
| // Read back robustness strategy |
| if (glfwExtensionSupported("GL_ARB_robustness")) |
| { |
| // NOTE: We avoid using the context flags for detection, as they are |
| // only present from 3.0 while the extension applies from 1.1 |
| |
| GLint strategy; |
| window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, |
| &strategy); |
| |
| if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) |
| window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET; |
| else if (strategy == GL_NO_RESET_NOTIFICATION_ARB) |
| window->context.robustness = GLFW_NO_RESET_NOTIFICATION; |
| } |
| } |
| else |
| { |
| // Read back robustness strategy |
| if (glfwExtensionSupported("GL_EXT_robustness")) |
| { |
| // NOTE: The values of these constants match those of the OpenGL ARB |
| // one, so we can reuse them here |
| |
| GLint strategy; |
| window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, |
| &strategy); |
| |
| if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) |
| window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET; |
| else if (strategy == GL_NO_RESET_NOTIFICATION_ARB) |
| window->context.robustness = GLFW_NO_RESET_NOTIFICATION; |
| } |
| } |
| |
| if (glfwExtensionSupported("GL_KHR_context_flush_control")) |
| { |
| GLint behavior; |
| window->context.GetIntegerv(GL_CONTEXT_RELEASE_BEHAVIOR, &behavior); |
| |
| if (behavior == GL_NONE) |
| window->context.release = GLFW_RELEASE_BEHAVIOR_NONE; |
| else if (behavior == GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH) |
| window->context.release = GLFW_RELEASE_BEHAVIOR_FLUSH; |
| } |
| |
| // Clearing the front buffer to black to avoid garbage pixels left over from |
| // previous uses of our bit of VRAM |
| { |
| PFNGLCLEARPROC glClear = (PFNGLCLEARPROC) |
| window->context.getProcAddress("glClear"); |
| glClear(GL_COLOR_BUFFER_BIT); |
| window->context.swapBuffers(window); |
| } |
| |
| glfwMakeContextCurrent((GLFWwindow*) previous); |
| return GLFW_TRUE; |
| } |
| |
| // Searches an extension string for the specified extension |
| // |
| GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions) |
| { |
| const char* start = extensions; |
| |
| for (;;) |
| { |
| const char* where; |
| const char* terminator; |
| |
| where = strstr(start, string); |
| if (!where) |
| return GLFW_FALSE; |
| |
| terminator = where + strlen(string); |
| if (where == start || *(where - 1) == ' ') |
| { |
| if (*terminator == ' ' || *terminator == '\0') |
| break; |
| } |
| |
| start = terminator; |
| } |
| |
| return GLFW_TRUE; |
| } |
| |
| |
| ////////////////////////////////////////////////////////////////////////// |
| ////// GLFW public API ////// |
| ////////////////////////////////////////////////////////////////////////// |
| |
| GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) |
| { |
| _GLFWwindow* window = (_GLFWwindow*) handle; |
| _GLFWwindow* previous = _glfwPlatformGetTls(&_glfw.contextSlot); |
| |
| _GLFW_REQUIRE_INIT(); |
| |
| if (window && window->context.client == GLFW_NO_API) |
| { |
| _glfwInputError(GLFW_NO_WINDOW_CONTEXT, |
| "Cannot make current with a window that has no OpenGL or OpenGL ES context"); |
| return; |
| } |
| |
| if (previous) |
| { |
| if (!window || window->context.source != previous->context.source) |
| previous->context.makeCurrent(NULL); |
| } |
| |
| if (window) |
| window->context.makeCurrent(window); |
| } |
| |
| GLFWAPI GLFWwindow* glfwGetCurrentContext(void) |
| { |
| _GLFW_REQUIRE_INIT_OR_RETURN(NULL); |
| return _glfwPlatformGetTls(&_glfw.contextSlot); |
| } |
| |
| GLFWAPI void glfwSwapBuffers(GLFWwindow* handle) |
| { |
| _GLFWwindow* window = (_GLFWwindow*) handle; |
| assert(window != NULL); |
| |
| _GLFW_REQUIRE_INIT(); |
| |
| if (window->context.client == GLFW_NO_API) |
| { |
| _glfwInputError(GLFW_NO_WINDOW_CONTEXT, |
| "Cannot swap buffers of a window that has no OpenGL or OpenGL ES context"); |
| return; |
| } |
| |
| window->context.swapBuffers(window); |
| } |
| |
| GLFWAPI void glfwSwapInterval(int interval) |
| { |
| _GLFWwindow* window; |
| |
| _GLFW_REQUIRE_INIT(); |
| |
| window = _glfwPlatformGetTls(&_glfw.contextSlot); |
| if (!window) |
| { |
| _glfwInputError(GLFW_NO_CURRENT_CONTEXT, |
| "Cannot set swap interval without a current OpenGL or OpenGL ES context"); |
| return; |
| } |
| |
| window->context.swapInterval(interval); |
| } |
| |
| GLFWAPI int glfwExtensionSupported(const char* extension) |
| { |
| _GLFWwindow* window; |
| assert(extension != NULL); |
| |
| _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); |
| |
| window = _glfwPlatformGetTls(&_glfw.contextSlot); |
| if (!window) |
| { |
| _glfwInputError(GLFW_NO_CURRENT_CONTEXT, |
| "Cannot query extension without a current OpenGL or OpenGL ES context"); |
| return GLFW_FALSE; |
| } |
| |
| if (*extension == '\0') |
| { |
| _glfwInputError(GLFW_INVALID_VALUE, "Extension name cannot be an empty string"); |
| return GLFW_FALSE; |
| } |
| |
| if (window->context.major >= 3) |
| { |
| int i; |
| GLint count; |
| |
| // Check if extension is in the modern OpenGL extensions string list |
| |
| window->context.GetIntegerv(GL_NUM_EXTENSIONS, &count); |
| |
| for (i = 0; i < count; i++) |
| { |
| const char* en = (const char*) |
| window->context.GetStringi(GL_EXTENSIONS, i); |
| if (!en) |
| { |
| _glfwInputError(GLFW_PLATFORM_ERROR, |
| "Extension string retrieval is broken"); |
| return GLFW_FALSE; |
| } |
| |
| if (strcmp(en, extension) == 0) |
| return GLFW_TRUE; |
| } |
| } |
| else |
| { |
| // Check if extension is in the old style OpenGL extensions string |
| |
| const char* extensions = (const char*) |
| window->context.GetString(GL_EXTENSIONS); |
| if (!extensions) |
| { |
| _glfwInputError(GLFW_PLATFORM_ERROR, |
| "Extension string retrieval is broken"); |
| return GLFW_FALSE; |
| } |
| |
| if (_glfwStringInExtensionString(extension, extensions)) |
| return GLFW_TRUE; |
| } |
| |
| // Check if extension is in the platform-specific string |
| return window->context.extensionSupported(extension); |
| } |
| |
| GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname) |
| { |
| _GLFWwindow* window; |
| assert(procname != NULL); |
| |
| _GLFW_REQUIRE_INIT_OR_RETURN(NULL); |
| |
| window = _glfwPlatformGetTls(&_glfw.contextSlot); |
| if (!window) |
| { |
| _glfwInputError(GLFW_NO_CURRENT_CONTEXT, |
| "Cannot query entry point without a current OpenGL or OpenGL ES context"); |
| return NULL; |
| } |
| |
| return window->context.getProcAddress(procname); |
| } |
| |