Commit 496f559c authored by Camilla Berglund's avatar Camilla Berglund
Browse files

Add GLFW_NO_API for creating context-less windows

parent 0fda5b7b
......@@ -66,6 +66,7 @@ used by the tests and examples and are not required to build the library.
- Added `glfwSetWindowSizeLimits` and `glfwSetWindowAspectRatio` for setting
absolute and relative window size limits
- Added `GLFW_NO_API` for creating window without contexts
- Added `GLFW_TRUE` and `GLFW_FALSE` as client API independent boolean values
- Removed dependency on external OpenGL or OpenGL ES headers
- [Cocoa] Removed support for OS X 10.6
......
......@@ -613,6 +613,15 @@ extern "C" {
* the user, as appropriate.
*/
#define GLFW_FORMAT_UNAVAILABLE 0x00010009
/*! @brief The specified window does not have an OpenGL or OpenGL ES context.
*
* A window that does not have an OpenGL or OpenGL ES context was passed to
* a function that requires it to have one.
*
* @par Analysis
* Application programmer error. Fix the offending call.
*/
#define GLFW_NO_WINDOW_CONTEXT 0x0001000A
/*! @} */
#define GLFW_FOCUSED 0x00020001
......@@ -650,6 +659,7 @@ extern "C" {
#define GLFW_OPENGL_PROFILE 0x00022008
#define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009
#define GLFW_NO_API 0
#define GLFW_OPENGL_API 0x00030001
#define GLFW_OPENGL_ES_API 0x00030002
......
......@@ -887,10 +887,11 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
if (!createWindow(window, wndconfig))
return GLFW_FALSE;
if (!_glfwCreateContext(window, ctxconfig, fbconfig))
return GLFW_FALSE;
[window->nsgl.context setView:window->ns.view];
if (ctxconfig->api != GLFW_NO_API)
{
if (!_glfwCreateContext(window, ctxconfig, fbconfig))
return GLFW_FALSE;
}
if (wndconfig->monitor)
{
......@@ -909,7 +910,8 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
if (window->monitor)
leaveFullscreenMode(window);
_glfwDestroyContext(window);
if (window->context.api != GLFW_NO_API)
_glfwDestroyContext(window);
[window->ns.object setDelegate:nil];
[window->ns.delegate release];
......
......@@ -89,7 +89,8 @@ static GLFWbool parseVersionString(int* api, int* major, int* minor, int* rev)
GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
{
if (ctxconfig->api != GLFW_OPENGL_API &&
if (ctxconfig->api != GLFW_NO_API &&
ctxconfig->api != GLFW_OPENGL_API &&
ctxconfig->api != GLFW_OPENGL_ES_API)
{
_glfwInputError(GLFW_INVALID_ENUM, "Invalid client API");
......@@ -536,7 +537,15 @@ int _glfwStringInExtensionString(const char* string, const char* extensions)
GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
_GLFW_REQUIRE_INIT();
if (window->context.api == GLFW_NO_API)
{
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
return;
}
_glfwPlatformMakeContextCurrent(window);
}
......@@ -549,7 +558,15 @@ GLFWAPI GLFWwindow* glfwGetCurrentContext(void)
GLFWAPI void glfwSwapBuffers(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
_GLFW_REQUIRE_INIT();
if (window->context.api == GLFW_NO_API)
{
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
return;
}
_glfwPlatformSwapBuffers(window);
}
......
......@@ -112,12 +112,6 @@ static GLFWbool chooseFBConfigs(const _GLFWctxconfig* ctxconfig,
const EGLConfig n = nativeConfigs[i];
_GLFWfbconfig* u = usableConfigs + usableCount;
#if defined(_GLFW_X11)
// Only consider EGLConfigs with associated visuals
if (!getConfigAttrib(n, EGL_NATIVE_VISUAL_ID))
continue;
#endif // _GLFW_X11
// Only consider RGB(A) EGLConfigs
if (!(getConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) & EGL_RGB_BUFFER))
continue;
......@@ -126,6 +120,12 @@ static GLFWbool chooseFBConfigs(const _GLFWctxconfig* ctxconfig,
if (!(getConfigAttrib(n, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT))
continue;
#if defined(_GLFW_X11)
// Only consider EGLConfigs with associated Visuals
if (!getConfigAttrib(n, EGL_NATIVE_VISUAL_ID))
continue;
#endif // _GLFW_X11
if (ctxconfig->api == GLFW_OPENGL_ES_API)
{
if (ctxconfig->major == 1)
......@@ -311,55 +311,6 @@ int _glfwCreateContext(_GLFWwindow* window,
return GLFW_FALSE;
}
#if defined(_GLFW_X11)
// Retrieve the visual corresponding to the chosen EGL config
{
EGLint count = 0;
int mask;
EGLint redBits, greenBits, blueBits, alphaBits, visualID = 0;
XVisualInfo info;
_glfw_eglGetConfigAttrib(_glfw.egl.display, config,
EGL_NATIVE_VISUAL_ID, &visualID);
info.screen = _glfw.x11.screen;
mask = VisualScreenMask;
if (visualID)
{
// The X window visual must match the EGL config
info.visualid = visualID;
mask |= VisualIDMask;
}
else
{
// Some EGL drivers do not implement the EGL_NATIVE_VISUAL_ID
// attribute, so attempt to find the closest match
_glfw_eglGetConfigAttrib(_glfw.egl.display, config,
EGL_RED_SIZE, &redBits);
_glfw_eglGetConfigAttrib(_glfw.egl.display, config,
EGL_GREEN_SIZE, &greenBits);
_glfw_eglGetConfigAttrib(_glfw.egl.display, config,
EGL_BLUE_SIZE, &blueBits);
_glfw_eglGetConfigAttrib(_glfw.egl.display, config,
EGL_ALPHA_SIZE, &alphaBits);
info.depth = redBits + greenBits + blueBits + alphaBits;
mask |= VisualDepthMask;
}
window->egl.visual = XGetVisualInfo(_glfw.x11.display,
mask, &info, &count);
if (!window->egl.visual)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"EGL: Failed to retrieve visual for EGLConfig");
return GLFW_FALSE;
}
}
#endif // _GLFW_X11
if (ctxconfig->api == GLFW_OPENGL_ES_API)
{
if (!_glfw_eglBindAPI(EGL_OPENGL_ES_API))
......@@ -453,6 +404,19 @@ int _glfwCreateContext(_GLFWwindow* window,
return GLFW_FALSE;
}
window->egl.surface =
_glfw_eglCreateWindowSurface(_glfw.egl.display,
config,
(EGLNativeWindowType)_GLFW_EGL_NATIVE_WINDOW,
NULL);
if (window->egl.surface == EGL_NO_SURFACE)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"EGL: Failed to create window surface: %s",
getErrorString(_glfw_eglGetError()));
return GLFW_FALSE;
}
window->egl.config = config;
// Load the appropriate client library
......@@ -541,14 +505,6 @@ void _glfwDestroyContext(_GLFWwindow* window)
}
}
#if defined(_GLFW_X11)
if (window->egl.visual)
{
XFree(window->egl.visual);
window->egl.visual = NULL;
}
#endif // _GLFW_X11
if (window->egl.surface)
{
_glfw_eglDestroySurface(_glfw.egl.display, window->egl.surface);
......@@ -564,16 +520,56 @@ void _glfwDestroyContext(_GLFWwindow* window)
// Analyzes the specified context for possible recreation
//
#if defined(_GLFW_WIN32)
int _glfwAnalyzeContext(const _GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
{
#if defined(_GLFW_WIN32)
return _GLFW_RECREATION_NOT_NEEDED;
#else
return 0;
#endif
}
#endif // _GLFW_WIN32
// Returns the Visual and depth of the chosen EGLConfig
//
#if defined(_GLFW_X11)
GLFWbool _glfwChooseVisual(const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig,
Visual** visual, int* depth)
{
XVisualInfo* result;
XVisualInfo desired;
EGLConfig native;
EGLint visualID = 0, count = 0;
const long vimask = VisualScreenMask | VisualIDMask;
if (!chooseFBConfigs(ctxconfig, fbconfig, &native))
{
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
"EGL: Failed to find a suitable EGLConfig");
return GLFW_FALSE;
}
_glfw_eglGetConfigAttrib(_glfw.egl.display, native,
EGL_NATIVE_VISUAL_ID, &visualID);
desired.screen = _glfw.x11.screen;
desired.visualid = visualID;
result = XGetVisualInfo(_glfw.x11.display, vimask, &desired, &count);
if (!result)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"EGL: Failed to retrieve Visual for EGLConfig");
return GLFW_FALSE;
}
*visual = result->visual;
*depth = result->depth;
XFree(result);
return GLFW_TRUE;
}
#endif // _GLFW_X11
//////////////////////////////////////////////////////////////////////////
......@@ -584,21 +580,6 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window)
{
if (window)
{
if (window->egl.surface == EGL_NO_SURFACE)
{
window->egl.surface =
_glfw_eglCreateWindowSurface(_glfw.egl.display,
window->egl.config,
(EGLNativeWindowType)_GLFW_EGL_NATIVE_WINDOW,
NULL);
if (window->egl.surface == EGL_NO_SURFACE)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"EGL: Failed to create window surface: %s",
getErrorString(_glfw_eglGetError()));
}
}
_glfw_eglMakeCurrent(_glfw.egl.display,
window->egl.surface,
window->egl.surface,
......@@ -667,6 +648,13 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
_GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_CONTEXT);
if (window->context.api == GLFW_NO_API)
{
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
return EGL_NO_CONTEXT;
}
return window->egl.context;
}
......@@ -674,6 +662,13 @@ GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
_GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_SURFACE);
if (window->context.api == GLFW_NO_API)
{
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
return EGL_NO_SURFACE;
}
return window->egl.surface;
}
......@@ -147,10 +147,6 @@ typedef struct _GLFWcontextEGL
EGLContext context;
EGLSurface surface;
#if defined(_GLFW_X11)
XVisualInfo* visual;
#endif
void* client;
} _GLFWcontextEGL;
......@@ -193,8 +189,15 @@ int _glfwCreateContext(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig);
void _glfwDestroyContext(_GLFWwindow* window);
#if defined(_GLFW_WIN32)
int _glfwAnalyzeContext(const _GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig);
#endif /*_GLFW_WIN32*/
#if defined(_GLFW_X11)
GLFWbool _glfwChooseVisual(const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig,
Visual** visual, int* depth);
#endif /*_GLFW_X11*/
#endif // _glfw3_egl_context_h_
......@@ -79,10 +79,6 @@ static GLFWbool chooseFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* result
const GLXFBConfig n = nativeConfigs[i];
_GLFWfbconfig* u = usableConfigs + usableCount;
// Only consider GLXFBConfigs with associated visuals
if (!getFBConfigAttrib(n, GLX_VISUAL_ID))
continue;
// Only consider RGBA GLXFBConfigs
if (!(getFBConfigAttrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT))
continue;
......@@ -192,12 +188,12 @@ int _glfwInitContextAPI(void)
dlsym(_glfw.glx.handle, "glXQueryExtensionsString");
_glfw.glx.CreateNewContext =
dlsym(_glfw.glx.handle, "glXCreateNewContext");
_glfw.glx.GetVisualFromFBConfig =
dlsym(_glfw.glx.handle, "glXGetVisualFromFBConfig");
_glfw.glx.GetProcAddress =
dlsym(_glfw.glx.handle, "glXGetProcAddress");
_glfw.glx.GetProcAddressARB =
dlsym(_glfw.glx.handle, "glXGetProcAddressARB");
_glfw.glx.GetVisualFromFBConfig =
dlsym(_glfw.glx.handle, "glXGetVisualFromFBConfig");
if (!_glfw_glXQueryExtension(_glfw.x11.display,
&_glfw.glx.errorBase,
......@@ -326,15 +322,6 @@ int _glfwCreateContext(_GLFWwindow* window,
return GLFW_FALSE;
}
window->glx.visual = _glfw_glXGetVisualFromFBConfig(_glfw.x11.display,
native);
if (!window->glx.visual)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"GLX: Failed to retrieve visual for GLXFBConfig");
return GLFW_FALSE;
}
if (ctxconfig->api == GLFW_OPENGL_ES_API)
{
if (!_glfw.glx.ARB_create_context ||
......@@ -485,12 +472,6 @@ int _glfwCreateContext(_GLFWwindow* window,
//
void _glfwDestroyContext(_GLFWwindow* window)
{
if (window->glx.visual)
{
XFree(window->glx.visual);
window->glx.visual = NULL;
}
if (window->glx.context)
{
_glfw_glXDestroyContext(_glfw.x11.display, window->glx.context);
......@@ -498,6 +479,37 @@ void _glfwDestroyContext(_GLFWwindow* window)
}
}
// Returns the Visual and depth of the chosen GLXFBConfig
//
GLFWbool _glfwChooseVisual(const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig,
Visual** visual, int* depth)
{
GLXFBConfig native;
XVisualInfo* result;
if (!chooseFBConfig(fbconfig, &native))
{
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
"GLX: Failed to find a suitable GLXFBConfig");
return GLFW_FALSE;
}
result = _glfw_glXGetVisualFromFBConfig(_glfw.x11.display, native);
if (!result)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"GLX: Failed to retrieve Visual for GLXFBConfig");
return GLFW_FALSE;
}
*visual = result->visual;
*depth = result->depth;
XFree(result);
return GLFW_TRUE;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
......@@ -573,6 +585,13 @@ GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
if (window->context.api == GLFW_NO_API)
{
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
return NULL;
}
return window->glx.context;
}
......@@ -111,10 +111,7 @@ typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig);
//
typedef struct _GLFWcontextGLX
{
// Rendering context
GLXContext context;
// Visual of selected GLXFBConfig
XVisualInfo* visual;
} _GLFWcontextGLX;
......@@ -171,5 +168,8 @@ int _glfwCreateContext(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig);
void _glfwDestroyContext(_GLFWwindow* window);
GLFWbool _glfwChooseVisual(const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig,
Visual** visual, int* depth);
#endif // _glfw3_glx_context_h_
......@@ -72,6 +72,8 @@ static const char* getErrorString(int error)
return "A platform-specific error occurred";
case GLFW_FORMAT_UNAVAILABLE:
return "The requested format is unavailable";
case GLFW_NO_WINDOW_CONTEXT:
return "The specified window has no context";
}
return "ERROR: UNKNOWN ERROR TOKEN PASSED TO glfwErrorString";
......
......@@ -462,8 +462,11 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
{
if (!_glfwCreateContext(window, ctxconfig, fbconfig))
return GLFW_FALSE;
if (ctxconfig->api != GLFW_NO_API)
{
if (!_glfwCreateContext(window, ctxconfig, fbconfig))
return GLFW_FALSE;
}
if (wndconfig->monitor)
{
......
......@@ -219,6 +219,7 @@ int _glfwCreateContext(_GLFWwindow* window,
return GLFW_FALSE;
}
[window->nsgl.context setView:window->ns.view];
return GLFW_TRUE;
}
......@@ -291,6 +292,13 @@ GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
_GLFW_REQUIRE_INIT_OR_RETURN(nil);
if (window->context.api == GLFW_NO_API)
{
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
return NULL;
}
return window->nsgl.context;
}
......@@ -672,6 +672,13 @@ GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
if (window->context.api == GLFW_NO_API)
{
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
return NULL;
}
return window->wgl.context;
}
......@@ -665,10 +665,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
// Creates the GLFW window and rendering context
//
static GLFWbool createWindow(_GLFWwindow* window,
const _GLFWwndconfig* wndconfig,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
static int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig)
{
int xpos, ypos, fullWidth, fullHeight;
WCHAR* wideTitle;
......@@ -742,9 +739,6 @@ static GLFWbool createWindow(_GLFWwindow* window,
DragAcceptFiles(window->win32.handle, TRUE);
if (!_glfwCreateContext(window, ctxconfig, fbconfig))
return GLFW_FALSE;
window->win32.minwidth = GLFW_DONT_CARE;
window->win32.minheight = GLFW_DONT_CARE;
window->win32.maxwidth = GLFW_DONT_CARE;
......@@ -759,8 +753,6 @@ static GLFWbool createWindow(_GLFWwindow* window,
//
static void destroyWindow(_GLFWwindow* window)
{
_glfwDestroyContext(window);
if (window->win32.handle)
{
DestroyWindow(window->win32.handle);
......@@ -830,43 +822,53 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
{
int status;
if (!createWindow(window, wndconfig, ctxconfig, fbconfig))
return GLFW_FALSE;
status = _glfwAnalyzeContext(window, ctxconfig, fbconfig);
if (status == _GLFW_RECREATION_IMPOSSIBLE)
if (!createWindow(window, wndconfig))
return GLFW_FALSE;
if (status == _GLFW_RECREATION_REQUIRED)
if (ctxconfig->api != GLFW_NO_API)
{
// Some window hints require us to re-create the context using WGL
// extensions retrieved through the current context, as we cannot check
// for WGL extensions or retrieve WGL entry points before we have a
// current context (actually until we have implicitly loaded the ICD)
// Yes, this is strange, and yes, this is the proper way on Win32
// As Windows only allows you to set the pixel format once for a
// window, we need to destroy the current window and create a new one
// to be able to use the new pixel format
// Technically, it may be possible to keep the old window around if
// we're just creating an OpenGL 3.0+ context with the same pixel
// format, but it's not worth the added code complexity
// First we clear the current context (the one we just created)
// This is usually done by glfwDestroyWindow, but as we're not doing
// full GLFW window destruction, it's duplicated here
_glfwPlatformMakeContextCurrent(NULL);
if (!_glfwCreateContext(window, ctxconfig, fbconfig))
return GLFW_FALSE;
// Next destroy the Win32 window and WGL context (without resetting or
// destroying the GLFW window object)
destroyWindow(window);
status = _glfwAnalyzeContext(window, ctxconfig, fbconfig);
// ...and then create them again, this time with better APIs
if (!createWindow(window, wndconfig, ctxconfig, fbconfig))
if (status == _GLFW_RECREATION_IMPOSSIBLE)
return GLFW_FALSE;
if (status == _GLFW_RECREATION_REQUIRED)
{
// Some window hints require us to re-create the context using WGL
// extensions retrieved through the current context, as we cannot
// check for WGL extensions or retrieve WGL entry points before we
// have a current context (actually until we have implicitly loaded
// the vendor ICD)
// Yes, this is strange, and yes, this is the proper way on Win32
// As Windows only allows you to set the pixel format once for
// a window, we need to destroy the current window and create a new
// one to be able to use the new pixel format
// Technically, it may be possible to keep the old window around if
// we're just creating an OpenGL 3.0+ context with the same pixel
// format, but it's not worth the added code complexity
// First we clear the current context (the one we just created)
// This is usually done by glfwDestroyWindow, but as we're not doing