This commit is contained in:
Doug Binks 2025-02-11 16:17:59 +00:00 committed by GitHub
commit 481f83d218
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 1124 additions and 93 deletions

View file

@ -121,6 +121,9 @@ information on what to include when reporting a bug.
## Changelog since 3.4
- Added OpenGL and OpenGL ES user contexts for multiple window contexts via
`GLFWusercontext`, `glfwCreateUserContext`, `glfwDestroyUserContext`,
`glfwMakeUserContextCurrent`, `glfwGetCurrentUserContext` (#1687,#1870)
- Added `GLFW_UNLIMITED_MOUSE_BUTTONS` input mode that allows mouse buttons beyond
the limit of the mouse button tokens to be reported (#2423)
- Updated minimum CMake version to 3.16 (#2541)
@ -135,7 +138,6 @@ information on what to include when reporting a bug.
- [EGL] Allowed native access on Wayland with `GLFW_CONTEXT_CREATION_API` set to
`GLFW_NATIVE_CONTEXT_API` (#2518)
## Contact
On [glfw.org](https://www.glfw.org/) you can find the latest version of GLFW, as

View file

@ -91,6 +91,28 @@ You can disable context creation by setting the
Windows without contexts should not be passed to @ref glfwMakeContextCurrent or
@ref glfwSwapBuffers. Doing this generates a @ref GLFW_NO_WINDOW_CONTEXT error.
@subsection context_user User contexts for multi context windows
GLFW supports multiple OpenGL or OpenGL ES contexts per window. Providing
a window with an existing OpenGL or OpenGL ES context has been created further
user contexts can be created using @ref glfwCreateUserContext with the same
API sharing the window context objects.
@code
GLFWusercontext* usercontext = glfwCreateUserContext(window);
/* make the user context current */
glfwMakeUserContextCurrent(usercontext);
/* make the window context current */
glfwMakeContextCurrent(window);
/* destroy the user context */
glfwDestroyUserContext(usercontext);
@endcode
User contexts See also the test program `usercontext`.
## Current context {#context_current}
@ -121,6 +143,26 @@ error.
- @ref glfwExtensionSupported
- @ref glfwGetProcAddress
@subsection context_current_user Current user context
When using [user contexts](@ref context_user) the user context can be
made current using @ref glfwMakeUserContextCurrent.
@code
glfwMakeUserContextCurrent(usercontext);
@endcode
This makes the any window context non-current on the calling thread, such that
a call to @ref glfwGetCurrentContext will return `NULL`.
The current user context is returned by @ref glfwGetCurrentUserContext.
@code
GLFWusercontext* usercontext = glfwGetCurrentUserContext();
@endcode
This will return the current user context or `NULL` if either the main window context
or no context is current.
## Buffer swapping {#context_swap}

View file

@ -5,6 +5,15 @@
## New features {#features}
### Multiple window contexts {#multiple_window_contexts}
GLFW now provides the ability to create multiple OpenGL and OpenGL ES
contexts for a given window. Called user contexts, a [GLFWusercontext](@ref context_user)
can be created using @ref glfwCreateUserContext,
destroyed using @ref glfwDestroyUserContext, and managed with
@ref glfwMakeUserContextCurrent and @ref glfwGetCurrentUserContext.
For more information see the [user context](@ref context_user) documentation.
### Unlimited mouse buttons {#unlimited_mouse_buttons}
GLFW now has an input mode which allows an unlimited number of mouse buttons to

View file

@ -1403,6 +1403,18 @@ typedef struct GLFWmonitor GLFWmonitor;
*/
typedef struct GLFWwindow GLFWwindow;
/*! @brief Opaque user OpenGL & OpenGL ES context object.
*
* Opaque user OpenGL OpenGL ES context object.
*
* @see @ref context_user
*
* @since Added in version 3.4.
*
* @ingroup window
*/
typedef struct GLFWusercontext GLFWusercontext;
/*! @brief Opaque cursor object.
*
* Opaque cursor object.
@ -6088,6 +6100,9 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void);
* thread can have only a single current context at a time. Making a context
* current detaches any previously current context on the calling thread.
*
* Making a context of a window current on a given thread will detach
* any user context which is current on that thread and visa versa.
*
* When moving a context between threads, you must detach it (make it
* non-current) on the old thread before making it current on the new one.
*
@ -6115,6 +6130,9 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void);
*
* @sa @ref context_current
* @sa @ref glfwGetCurrentContext
* @sa @ref context_current_user
* @sa @ref glfwMakeUserContextCurrent
* @sa @ref glfwGetCurrentUserContext
*
* @since Added in version 3.0.
*
@ -6303,6 +6321,147 @@ GLFWAPI int glfwExtensionSupported(const char* extension);
*/
GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname);
/*! @brief Create a new OpenGL or OpenGL ES user context for a window
*
* This function creates a new OpenGL or OpenGL ES user context for a
* window, which can be used to call OpenGL or OpenGL ES functions on
* another thread. For a valid user context the window must be created
* with a [GLFW_CLIENT_API](@ref GLFW_CLIENT_API_hint) other than
* `GLFW_NO_API`.
*
* User context creation uses the window context and framebuffer related
* hints to ensure a valid context is created for that window, these hints
* should be the same at the time of user context creation as when the
* window was created.
*
* Contexts share resources with the window context and with any other
* user context created for that window.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED,
* @ref GLFW_INVALID_VALUE the window parameter is `NULL`,
* @ref GLFW_NO_WINDOW_CONTEXT if the window has no OpenGL or
* OpenGL US context, and @ref GLFW_PLATFORM_ERROR.
*
* @param[in] window The Window for which the user context is to be
* created.
* @return The handle of the user context created, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref context_user
* @sa @ref usercontext_creation
* @sa @ref glfwDestroyUserContext
* @sa @ref window_creation
* @sa @ref glfwCreateWindow
* @sa @ref glfwDestroyWindow
*
* @since Added in version 3.4.
*
* @ingroup context
*/
GLFWAPI GLFWusercontext* glfwCreateUserContext(GLFWwindow* window);
/*! @brief Destroys the specified user context
*
* This function destroys the specified user context.
* User contexts should be destroyed before destroying the
* window they were made with.
*
* If the user context is current on the main thread, it is
* detached before being destroyed.
*
* @param[in] context The user context to destroy.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @note The user context must not be current on any other
* thread when this function is called.
*
* @reentrancy This function must not be called from a callback.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref context_user
* @sa @ref usercontext_creation
* @sa @ref glfwCreateUserContext
* @sa @ref window_creation
* @sa @ref glfwCreateWindow
* @sa @ref glfwDestroyWindow
*
* @since Added in version 3.4.
*
* @ingroup context
*/
GLFWAPI void glfwDestroyUserContext(GLFWusercontext* context);
/*! @brief Makes the user context current for the calling thread.
*
* This function makes the OpenGL or OpenGL ES context of the specified user
* context current on the calling thread. A context must only be made current on
* a single thread at a time and each thread can have only a single current
* context at a time.
*
* Making a user context current on a given thread will detach the context of
* any window which is current on that thread and visa versa.
*
* When moving a context between threads, you must make it non-current on the
* old thread before making it current on the new one.
*
* By default, making a context non-current implicitly forces a pipeline flush.
* On machines that support `GL_KHR_context_flush_control`, you can control
* whether a context performs this flush by setting the
* [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint)
* hint.
*
* @param[in] context The user context to make current, or `NULL` to
* detach the current context.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED,
* and @ref GLFW_PLATFORM_ERROR.
*
* @thread_safety This function may be called from any thread.
*
* @sa @ref context_user
* @sa @ref context_current_user
* @sa @ref glfwGetCurrentUserContext
* @sa @ref context_current
* @sa @ref glfwMakeContextCurrent
* @sa @ref glfwGetCurrentContext
*
* @since Added in version 3.4.
*
* @ingroup context
*/
GLFWAPI void glfwMakeUserContextCurrent(GLFWusercontext* context);
/*! @brief Returns the current OpenGL or OpenGL ES user context
*
* This function returns the user context which is current
* on the calling thread.
*
* @return The user context current, or `NULL` if no user context
* is current.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread.
*
* @sa @ref context_user
* @sa @ref context_current_user
* @sa @ref glfwMakeUserContextCurrent
* @sa @ref context_current
* @sa @ref glfwMakeContextCurrent
* @sa @ref glfwGetCurrentContext
*
* @since Added in version 3.4.
*
* @ingroup context
*/
GLFWAPI GLFWusercontext* glfwGetCurrentUserContext(void);
/*! @brief Returns whether the Vulkan loader and an ICD have been found.
*
* This function returns whether the Vulkan loader and any minimally functional

View file

@ -559,6 +559,7 @@ GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform)
.waitEvents = _glfwWaitEventsCocoa,
.waitEventsTimeout = _glfwWaitEventsTimeoutCocoa,
.postEmptyEvent = _glfwPostEmptyEventCocoa,
.createUserContext = _glfwCreateUserContextCocoa,
.getEGLPlatform = _glfwGetEGLPlatformCocoa,
.getEGLNativeDisplay = _glfwGetEGLNativeDisplayCocoa,
.getEGLNativeWindow = _glfwGetEGLNativeWindowCocoa,

View file

@ -107,6 +107,7 @@ typedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMeta
#define GLFW_NSGL_CONTEXT_STATE _GLFWcontextNSGL nsgl;
#define GLFW_NSGL_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl;
#define GLFW_NSGL_USER_CONTEXT_STATE _GLFWusercontextNSGL nsgl;
// HIToolbox.framework pointer typedefs
#define kTISPropertyUnicodeKeyLayoutData _glfw.ns.tis.kPropertyUnicodeKeyLayoutData
@ -134,6 +135,14 @@ typedef struct _GLFWlibraryNSGL
CFBundleRef framework;
} _GLFWlibraryNSGL;
// NSGL-specific per usercontext data
//
typedef struct _GLFWusercontextNSGL
{
id object;
} _GLFWusercontextNSGL;
// Cocoa-specific per-window data
//
typedef struct _GLFWwindowNS
@ -300,3 +309,5 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
const _GLFWfbconfig* fbconfig);
void _glfwDestroyContextNSGL(_GLFWwindow* window);
_GLFWusercontext* _glfwCreateUserContextCocoa(_GLFWwindow* window);
_GLFWusercontext* _glfwCreateUserContextNSGL(_GLFWwindow* window);

View file

@ -2021,6 +2021,25 @@ VkResult _glfwCreateWindowSurfaceCocoa(VkInstance instance,
}
_GLFWusercontext* _glfwCreateUserContextCocoa(_GLFWwindow* window)
{
if (window->context.nsgl.object)
{
return _glfwCreateUserContextNSGL(window);
}
else if (window->context.egl.handle)
{
return _glfwCreateUserContextEGL(window);
}
else if (window->context.osmesa.handle)
{
return _glfwCreateUserContextOSMesa(window);
}
return NULL;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////
//////////////////////////////////////////////////////////////////////////

View file

@ -619,7 +619,9 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle)
_GLFWwindow* window = (_GLFWwindow*) handle;
_GLFWwindow* previous;
_GLFWusercontext* previousUserContext;
previousUserContext = _glfwPlatformGetTls(&_glfw.usercontextSlot);
previous = _glfwPlatformGetTls(&_glfw.contextSlot);
if (window && window->context.client == GLFW_NO_API)
@ -629,6 +631,12 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle)
return;
}
if (previousUserContext)
{
assert(previous==NULL);
previousUserContext->makeCurrent(NULL);
}
if (previous)
{
if (!window || window->context.source != previous->context.source)
@ -763,3 +771,65 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname)
return window->context.getProcAddress(procname);
}
GLFWAPI GLFWusercontext* glfwCreateUserContext(GLFWwindow* handle)
{
_GLFWusercontext* context;
_GLFWwindow* window = (_GLFWwindow*)handle;
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
if (!window)
{
_glfwInputError(GLFW_INVALID_VALUE,
"Cannot create a user context without a valid window handle");
return NULL;
}
if (window->context.client == GLFW_NO_API)
{
_glfwInputError(GLFW_NO_WINDOW_CONTEXT,
"Cannot create a user context for a window that has no OpenGL or OpenGL ES context");
return NULL;
}
context = _glfw.platform.createUserContext(window);
return (GLFWusercontext*)context;
}
GLFWAPI void glfwDestroyUserContext(GLFWusercontext* handle)
{
_GLFWusercontext* context = (_GLFWusercontext*)handle;
_GLFWusercontext* current = _glfwPlatformGetTls(&_glfw.usercontextSlot);
_GLFW_REQUIRE_INIT();
if (context)
{
if(current==context)
glfwMakeContextCurrent(NULL);
context->destroy(context);
}
}
GLFWAPI void glfwMakeUserContextCurrent(GLFWusercontext* handle)
{
_GLFWusercontext* context = (_GLFWusercontext*)handle;
_GLFW_REQUIRE_INIT();
// Call glfwMakeContextCurrent(NULL) to both clear context TLS and set
// context to NULL if required by platform & context, and this
// handles case of calling glfwMakeUserContextCurrent(NULL)
glfwMakeContextCurrent(NULL);
if (context)
context->makeCurrent(context);
}
GLFWAPI GLFWusercontext* glfwGetCurrentUserContext(void)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return _glfwPlatformGetTls(&_glfw.usercontextSlot);
}

View file

@ -436,6 +436,10 @@ GLFWbool _glfwInitEGL(void)
_glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglQueryString");
_glfw.egl.GetProcAddress = (PFN_eglGetProcAddress)
_glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglGetProcAddress");
_glfw.egl.CreatePbufferSurface = (PFN_eglCreatePbufferSurface)
_glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglCreatePbufferSurface");
_glfw.egl.ChooseConfig = (PFN_eglChooseConfig)
_glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglChooseConfig");
if (!_glfw.egl.GetConfigAttrib ||
!_glfw.egl.GetConfigs ||
@ -453,7 +457,9 @@ GLFWbool _glfwInitEGL(void)
!_glfw.egl.SwapBuffers ||
!_glfw.egl.SwapInterval ||
!_glfw.egl.QueryString ||
!_glfw.egl.GetProcAddress)
!_glfw.egl.GetProcAddress ||
!_glfw.egl.CreatePbufferSurface||
!_glfw.egl.ChooseConfig)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"EGL: Failed to load required entry points");
@ -569,17 +575,15 @@ void _glfwTerminateEGL(void)
attribs[index++] = v; \
}
// Create the OpenGL or OpenGL ES context
// Create the OpenGL or OpenGL ES context for the window eglConfig
//
GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
GLFWbool _glfwCreateContextForConfigEGL(EGLConfig eglConfig,
const _GLFWctxconfig* ctxconfig,
EGLContext* context)
{
EGLint attribs[40];
EGLConfig config;
EGLContext share = NULL;
EGLNativeWindowType native;
int index = 0;
EGLContext share = NULL;
if (!_glfw.egl.display)
{
@ -590,9 +594,6 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
if (ctxconfig->share)
share = ctxconfig->share->context.egl.handle;
if (!chooseEGLConfig(ctxconfig, fbconfig, &config))
return GLFW_FALSE;
if (ctxconfig->client == GLFW_OPENGL_ES_API)
{
if (!eglBindAPI(EGL_OPENGL_ES_API))
@ -688,10 +689,9 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
SET_ATTRIB(EGL_NONE, EGL_NONE);
window->context.egl.handle = eglCreateContext(_glfw.egl.display,
config, share, attribs);
*context = eglCreateContext(_glfw.egl.display, eglConfig, share, attribs);
if (window->context.egl.handle == EGL_NO_CONTEXT)
if (*context == EGL_NO_CONTEXT)
{
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
"EGL: Failed to create context: %s",
@ -699,9 +699,32 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
return GLFW_FALSE;
}
// Set up attributes for surface creation
index = 0;
return GLFW_TRUE;
}
// Create the OpenGL or OpenGL ES context
//
GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
{
EGLNativeWindowType native;
EGLint attribs[40];
int index = 0;
if (!chooseEGLConfig(ctxconfig, fbconfig, &window->context.egl.config))
{
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
"EGL: Failed to find a suitable EGLConfig");
return GLFW_FALSE;
}
if (!_glfwCreateContextForConfigEGL(window->context.egl.config,ctxconfig,&window->context.egl.handle))
{
return GLFW_FALSE;
}
// Set up attributes for surface creation
if (fbconfig->sRGB)
{
if (_glfw.egl.KHR_gl_colorspace)
@ -735,18 +758,18 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
// implement eglCreatePlatformWindowSurfaceEXT despite reporting
// support for EGL_EXT_platform_base
window->context.egl.surface =
eglCreateWindowSurface(_glfw.egl.display, config, native, attribs);
eglCreateWindowSurface(_glfw.egl.display, window->context.egl.config, native, attribs);
}
else if (_glfw.egl.platform == EGL_PLATFORM_SURFACELESS_MESA)
{
// HACK: Use a pbuffer surface as the default framebuffer
window->context.egl.surface =
eglCreatePbufferSurface(_glfw.egl.display, config, attribs);
eglCreatePbufferSurface(_glfw.egl.display, window->context.egl.config, attribs);
}
else
{
window->context.egl.surface =
eglCreatePlatformWindowSurfaceEXT(_glfw.egl.display, config, native, attribs);
eglCreatePlatformWindowSurfaceEXT(_glfw.egl.display, window->context.egl.config, native, attribs);
}
if (window->context.egl.surface == EGL_NO_SURFACE)
@ -757,7 +780,6 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
return GLFW_FALSE;
}
window->context.egl.config = config;
// Load the appropriate client library
if (!_glfw.egl.KHR_get_all_proc_addresses)
@ -895,6 +917,109 @@ GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig,
}
#endif // _GLFW_X11
static void _glfwMakeUserContextCurrentEGL(_GLFWusercontext* context)
{
if (context)
{
if (!eglMakeCurrent(_glfw.egl.display,
context->egl.surface,
context->egl.surface,
context->egl.handle))
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"EGL: Failed to make user context current: %s",
getEGLErrorString(eglGetError()));
_glfwPlatformSetTls(&_glfw.usercontextSlot, NULL);
return;
}
}
else
{
if (!eglMakeCurrent(_glfw.egl.display,
EGL_NO_SURFACE,
EGL_NO_SURFACE,
EGL_NO_CONTEXT))
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"EGL: Failed to clear current user context: %s",
getEGLErrorString(eglGetError()));
return;
}
}
_glfwPlatformSetTls(&_glfw.usercontextSlot, context);
}
static void _glfwDestroyUserContextEGL(_GLFWusercontext* context)
{
if (context->egl.surface!=EGL_NO_SURFACE)
eglDestroySurface(_glfw.egl.display,context->egl.surface);
eglDestroyContext(_glfw.egl.display, context->egl.handle);
free(context);
}
_GLFWusercontext* _glfwCreateUserContextEGL(_GLFWwindow* window)
{
_GLFWusercontext* context;
_GLFWctxconfig ctxconfig;
EGLint dummyConfigAttribs[] =
{
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1,
EGL_NONE
};
EGLint dummySurfaceAttribs[] =
{
EGL_WIDTH, 1, EGL_HEIGHT, 1,
EGL_NONE
};
EGLint dummySurfaceNumConfigs;
EGLConfig dummySurfaceConfig;
context = calloc(1, sizeof(_GLFWusercontext));
context->window = window;
ctxconfig = _glfw.hints.context;
ctxconfig.share = window;
if (!_glfwCreateContextForConfigEGL(window->context.egl.config,&ctxconfig,&context->egl.handle))
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"EGL: Failed to create user OpenGL context");
free(context);
return NULL;
}
if (glfwExtensionSupported("EGL_KHR_surfaceless_context"))
context->egl.surface = EGL_NO_SURFACE;
else
{
eglChooseConfig(_glfw.egl.display, dummyConfigAttribs, &dummySurfaceConfig, 1, &dummySurfaceNumConfigs);
if (!dummySurfaceNumConfigs)
{
eglDestroyContext(_glfw.egl.display, context->egl.handle);
_glfwInputError(GLFW_PLATFORM_ERROR,
"EGL: Failed to find surface config for user context: %s", getEGLErrorString(eglGetError()));
free(context);
return NULL;
}
context->egl.surface = eglCreatePbufferSurface(_glfw.egl.display, dummySurfaceConfig, dummySurfaceAttribs);
if (context->egl.surface == EGL_NO_SURFACE)
{
eglDestroyContext(_glfw.egl.display, context->egl.handle);
_glfwInputError(GLFW_PLATFORM_ERROR,
"EGL: Failed to create surface for user context: %s for %s", getEGLErrorString(eglGetError()), eglQueryString(_glfw.egl.display,0x3054));
free(context);
return NULL;
}
}
context->makeCurrent = _glfwMakeUserContextCurrentEGL;
context->destroy = _glfwDestroyUserContextEGL;
return context;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////

View file

@ -447,26 +447,19 @@ void _glfwTerminateGLX(void)
attribs[index++] = v; \
}
// Create the OpenGL or OpenGL ES context
// Create the OpenGL or OpenGL ES context for the window fbConfig
//
GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
GLFWbool _glfwCreateContextForFBGLX(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
GLXContext* context)
{
int attribs[40];
GLXFBConfig native = NULL;
GLXContext share = NULL;
if (ctxconfig->share)
share = ctxconfig->share->context.glx.handle;
if (!chooseGLXFBConfig(fbconfig, &native))
{
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
"GLX: Failed to find a suitable GLXFBConfig");
return GLFW_FALSE;
}
if (ctxconfig->client == GLFW_OPENGL_ES_API)
{
if (!_glfw.glx.ARB_create_context ||
@ -581,9 +574,9 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
SET_ATTRIB(None, None);
window->context.glx.handle =
*context =
_glfw.glx.CreateContextAttribsARB(_glfw.x11.display,
native,
window->context.glx.fbconfig,
share,
True,
attribs);
@ -592,34 +585,56 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
// implementation of GLX_ARB_create_context_profile that fail
// default 1.0 context creation with a GLXBadProfileARB error in
// violation of the extension spec
if (!window->context.glx.handle)
if (!(*context))
{
if (_glfw.x11.errorCode == _glfw.glx.errorBase + GLXBadProfileARB &&
ctxconfig->client == GLFW_OPENGL_API &&
ctxconfig->profile == GLFW_OPENGL_ANY_PROFILE &&
ctxconfig->forward == GLFW_FALSE)
{
window->context.glx.handle =
createLegacyContextGLX(window, native, share);
*context =
createLegacyContextGLX(window, window->context.glx.fbconfig, share);
}
}
}
else
{
window->context.glx.handle =
createLegacyContextGLX(window, native, share);
*context =
createLegacyContextGLX(window, window->context.glx.fbconfig, share);
}
_glfwReleaseErrorHandlerX11();
if (!window->context.glx.handle)
if (!(*context))
{
_glfwInputErrorX11(GLFW_VERSION_UNAVAILABLE, "GLX: Failed to create context");
return GLFW_FALSE;
}
return GLFW_TRUE;
}
// Create the OpenGL or OpenGL ES context
//
GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
{
if (!chooseGLXFBConfig(fbconfig, &window->context.glx.fbconfig))
{
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
"GLX: Failed to find a suitable GLXFBConfig");
return GLFW_FALSE;
}
if(!_glfwCreateContextForFBGLX(window,ctxconfig,&window->context.glx.handle))
{
return GLFW_FALSE;
}
window->context.glx.window =
glXCreateWindow(_glfw.x11.display, native, window->x11.handle, NULL);
glXCreateWindow(_glfw.x11.display, window->context.glx.fbconfig, window->x11.handle, NULL);
if (!window->context.glx.window)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to create window");
@ -670,6 +685,60 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig,
return GLFW_TRUE;
}
static void _glfwMakeUserContextCurrentGLX(_GLFWusercontext* context)
{
if (context)
{
if(!glXMakeCurrent(_glfw.x11.display, context->window->context.glx.window,context->glx.handle))
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"GLX: Failed to make user context current");
_glfwPlatformSetTls(&_glfw.usercontextSlot, NULL);
return;
}
}
else
{
if (!glXMakeCurrent(_glfw.x11.display, None, NULL))
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"GLX: Failed to clear current user context");
return;
}
}
_glfwPlatformSetTls(&_glfw.usercontextSlot, context);
}
static void _glfwDestroyUserContextGLX(_GLFWusercontext* context)
{
glXDestroyContext(_glfw.x11.display, context->glx.handle);
free(context);
}
_GLFWusercontext* _glfwCreateUserContextGLX(_GLFWwindow* window)
{
_GLFWusercontext* context;
_GLFWctxconfig ctxconfig;
context = calloc(1, sizeof(_GLFWusercontext));
context->window = window;
ctxconfig = _glfw.hints.context;
ctxconfig.share = window;
if(!_glfwCreateContextForFBGLX(window,&ctxconfig,&context->glx.handle))
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"GLX: Failed to create user OpenGL context");
free(context);
return NULL;
}
context->makeCurrent = _glfwMakeUserContextCurrentGLX;
context->destroy = _glfwDestroyUserContextGLX;
return context;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////

View file

@ -132,6 +132,7 @@ static void terminate(void)
_glfw_free(error);
}
_glfwPlatformDestroyTls(&_glfw.usercontextSlot);
_glfwPlatformDestroyTls(&_glfw.contextSlot);
_glfwPlatformDestroyTls(&_glfw.errorSlot);
_glfwPlatformDestroyMutex(&_glfw.errorLock);
@ -410,7 +411,8 @@ GLFWAPI int glfwInit(void)
if (!_glfwPlatformCreateMutex(&_glfw.errorLock) ||
!_glfwPlatformCreateTls(&_glfw.errorSlot) ||
!_glfwPlatformCreateTls(&_glfw.contextSlot))
!_glfwPlatformCreateTls(&_glfw.contextSlot) ||
!_glfwPlatformCreateTls(&_glfw.usercontextSlot))
{
terminate();
return GLFW_FALSE;

View file

@ -79,6 +79,7 @@ typedef struct _GLFWmapping _GLFWmapping;
typedef struct _GLFWjoystick _GLFWjoystick;
typedef struct _GLFWtls _GLFWtls;
typedef struct _GLFWmutex _GLFWmutex;
typedef struct _GLFWusercontext _GLFWusercontext;
#define GL_VERSION 0x1f02
#define GL_NONE 0
@ -217,6 +218,7 @@ typedef EGLBoolean (APIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface);
typedef EGLBoolean (APIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint);
typedef const char* (APIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint);
typedef GLFWglproc (APIENTRY * PFN_eglGetProcAddress)(const char*);
typedef EGLBoolean (APIENTRY * PFN_eglChooseConfig)(EGLDisplay,EGLint const*,EGLConfig*,EGLint,EGLint*);
#define eglGetConfigAttrib _glfw.egl.GetConfigAttrib
#define eglGetConfigs _glfw.egl.GetConfigs
#define eglGetDisplay _glfw.egl.GetDisplay
@ -234,6 +236,8 @@ typedef GLFWglproc (APIENTRY * PFN_eglGetProcAddress)(const char*);
#define eglSwapInterval _glfw.egl.SwapInterval
#define eglQueryString _glfw.egl.QueryString
#define eglGetProcAddress _glfw.egl.GetProcAddress
#define eglCreatePbufferSurface _glfw.egl.CreatePbufferSurface
#define eglChooseConfig _glfw.egl.ChooseConfig
typedef EGLDisplay (APIENTRY * PFNEGLGETPLATFORMDISPLAYEXTPROC)(EGLenum,void*,const EGLint*);
typedef EGLSurface (APIENTRY * PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)(EGLDisplay,EGLConfig,void*,const EGLint*);
@ -525,6 +529,29 @@ struct _GLFWcontext
GLFW_PLATFORM_CONTEXT_STATE
};
// User Context structure
//
struct _GLFWusercontext
{
_GLFWwindow* window;
void (*makeCurrent)(_GLFWusercontext* context);
void (*destroy)(_GLFWusercontext* context);
struct {
EGLContext handle;
EGLSurface surface;
} egl;
struct {
OSMesaContext handle;
} osmesa;
// This is defined in platform.h
GLFW_PLATFORM_USER_CONTEXT_STATE
};
// Window and context structure
//
struct _GLFWwindow
@ -751,6 +778,7 @@ struct _GLFWplatform
void (*waitEvents)(void);
void (*waitEventsTimeout)(double);
void (*postEmptyEvent)(void);
_GLFWusercontext* (*createUserContext)(_GLFWwindow*);
// EGL
EGLenum (*getEGLPlatform)(EGLint**);
EGLNativeDisplayType (*getEGLNativeDisplay)(void);
@ -792,6 +820,7 @@ struct _GLFWlibrary
_GLFWtls errorSlot;
_GLFWtls contextSlot;
_GLFWtls usercontextSlot;
_GLFWmutex errorLock;
struct {
@ -842,6 +871,7 @@ struct _GLFWlibrary
PFN_eglSwapInterval SwapInterval;
PFN_eglQueryString QueryString;
PFN_eglGetProcAddress GetProcAddress;
PFN_eglChooseConfig ChooseConfig;
PFNEGLGETPLATFORMDISPLAYEXTPROC GetPlatformDisplayEXT;
PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC CreatePlatformWindowSurfaceEXT;
@ -992,6 +1022,7 @@ void _glfwTerminateEGL(void);
GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig);
_GLFWusercontext* _glfwCreateUserContextEGL(_GLFWwindow* window);
#if defined(_GLFW_X11)
GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig,
const _GLFWctxconfig* ctxconfig,
@ -1004,6 +1035,7 @@ void _glfwTerminateOSMesa(void);
GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig);
_GLFWusercontext* _glfwCreateUserContextOSMesa(_GLFWwindow* window);
GLFWbool _glfwInitVulkan(int mode);
void _glfwTerminateVulkan(void);

View file

@ -353,6 +353,54 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
}
static void _glfwMakeUserContextCurrentNSGL(_GLFWusercontext* context)
{
@autoreleasepool {
if (context)
[context->nsgl.object makeCurrentContext];
else
[NSOpenGLContext clearCurrentContext];
_glfwPlatformSetTls(&_glfw.usercontextSlot, context);
} // autoreleasepool
}
static void _glfwDestroyUserContextNSGL(_GLFWusercontext* context)
{
@autoreleasepool {
[context->nsgl.object release];
} // autoreleasepool
free(context);
}
_GLFWusercontext* _glfwCreateUserContextNSGL(_GLFWwindow* window)
{
_GLFWusercontext* context;
context = calloc(1, sizeof(_GLFWusercontext));
context->window = window;
context->nsgl.object =
[[NSOpenGLContext alloc] initWithFormat:window->context.nsgl.pixelFormat
shareContext:window->context.nsgl.object];
if (window->context.nsgl.object == nil)
{
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
"NSGL: Failed to create OpenGL user context");
free(context);
return NULL;
}
context->makeCurrent = _glfwMakeUserContextCurrentNSGL;
context->destroy = _glfwDestroyUserContextNSGL;
return context;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////
//////////////////////////////////////////////////////////////////////////

View file

@ -105,6 +105,7 @@ GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform)
.waitEvents = _glfwWaitEventsNull,
.waitEventsTimeout = _glfwWaitEventsTimeoutNull,
.postEmptyEvent = _glfwPostEmptyEventNull,
.createUserContext = _glfwCreateUserContextNull,
.getEGLPlatform = _glfwGetEGLPlatformNull,
.getEGLNativeDisplay = _glfwGetEGLNativeDisplayNull,
.getEGLNativeWindow = _glfwGetEGLNativeWindowNull,

View file

@ -32,6 +32,7 @@
#define GLFW_NULL_CONTEXT_STATE
#define GLFW_NULL_CURSOR_STATE
#define GLFW_NULL_LIBRARY_CONTEXT_STATE
#define GLFW_NULL_USER_CONTEXT_STATE
#define GLFW_NULL_SC_FIRST GLFW_NULL_SC_SPACE
#define GLFW_NULL_SC_SPACE 1
@ -280,3 +281,5 @@ VkResult _glfwCreateWindowSurfaceNull(VkInstance instance, _GLFWwindow* window,
void _glfwPollMonitorsNull(void);
_GLFWusercontext* _glfwCreateUserContextNull(_GLFWwindow* window);

View file

@ -747,3 +747,12 @@ VkResult _glfwCreateWindowSurfaceNull(VkInstance instance,
return err;
}
_GLFWusercontext* _glfwCreateUserContextNull(_GLFWwindow* window)
{
if (window->context.osmesa.handle)
{
return _glfwCreateUserContextOSMesa(window);
}
return NULL;
}

View file

@ -194,9 +194,9 @@ void _glfwTerminateOSMesa(void)
attribs[index++] = v; \
}
GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
GLFWbool _glfwCreateContextForConfigOSMesa(const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig,
OSMesaContext* context )
{
OSMesaContext share = NULL;
const int accumBits = fbconfig->accumRedBits +
@ -247,7 +247,7 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
SET_ATTRIB(0, 0);
window->context.osmesa.handle =
*context =
OSMesaCreateContextAttribs(attribs, share);
}
else
@ -259,7 +259,7 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
return GLFW_FALSE;
}
window->context.osmesa.handle =
*context =
OSMesaCreateContextExt(OSMESA_RGBA,
fbconfig->depthBits,
fbconfig->stencilBits,
@ -267,13 +267,27 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
share);
}
if (window->context.osmesa.handle == NULL)
if (*context == NULL)
{
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
"OSMesa: Failed to create context");
return GLFW_FALSE;
}
return GLFW_TRUE;
}
#undef setAttrib
GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
{
if(!_glfwCreateContextForConfigOSMesa(ctxconfig,fbconfig,&window->context.osmesa.handle))
{
return GLFW_FALSE;
}
window->context.makeCurrent = makeContextCurrentOSMesa;
window->context.swapBuffers = swapBuffersOSMesa;
window->context.swapInterval = swapIntervalOSMesa;
@ -284,8 +298,62 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
return GLFW_TRUE;
}
#undef SET_ATTRIB
static void _glfwMakeUserContextCurrentOSMesa(_GLFWusercontext* context)
{
if (context)
{
if (!OSMesaMakeCurrent(context->osmesa.handle,
context->window->context.osmesa.buffer,
GL_UNSIGNED_BYTE,
context->window->context.osmesa.width, context->window->context.osmesa.height))
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"OSMesa: Failed to make user context current");
_glfwPlatformSetTls(&_glfw.usercontextSlot, NULL);
return;
}
}
_glfwPlatformSetTls(&_glfw.usercontextSlot, context);
}
static void _glfwDestroyUserContextOSMesa(_GLFWusercontext* context)
{
if (context->osmesa.handle)
{
OSMesaDestroyContext(context->osmesa.handle);
}
free(context);
}
_GLFWusercontext* _glfwCreateUserContextOSMesa(_GLFWwindow* window)
{
_GLFWusercontext* context;
_GLFWctxconfig ctxconfig;
_GLFWfbconfig fbconfig;
context = calloc(1, sizeof(_GLFWusercontext));
context->window = window;
ctxconfig = _glfw.hints.context;
ctxconfig.share = window;
fbconfig = _glfw.hints.framebuffer;
if(!_glfwCreateContextForConfigOSMesa(&ctxconfig,&fbconfig,&context->osmesa.handle))
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"OSMesa: Failed to create user OpenGL context");
free(context);
return NULL;
}
context->makeCurrent = _glfwMakeUserContextCurrentOSMesa;
context->destroy = _glfwDestroyUserContextOSMesa;
return context;
}
#undef SET_ATTRIB
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////

View file

@ -52,6 +52,7 @@
#define GLFW_WIN32_LIBRARY_WINDOW_STATE
#define GLFW_WGL_CONTEXT_STATE
#define GLFW_WGL_LIBRARY_CONTEXT_STATE
#define GLFW_WGL_USER_CONTEXT_STATE
#endif
#if defined(_GLFW_COCOA)
@ -65,6 +66,7 @@
#define GLFW_COCOA_LIBRARY_WINDOW_STATE
#define GLFW_NSGL_CONTEXT_STATE
#define GLFW_NSGL_LIBRARY_CONTEXT_STATE
#define GLFW_NSGL_USER_CONTEXT_STATE
#endif
#if defined(_GLFW_WAYLAND)
@ -88,6 +90,7 @@
#define GLFW_X11_LIBRARY_WINDOW_STATE
#define GLFW_GLX_CONTEXT_STATE
#define GLFW_GLX_LIBRARY_CONTEXT_STATE
#define GLFW_GLX_USER_CONTEXT_STATE
#endif
#include "null_joystick.h"
@ -165,6 +168,11 @@
GLFW_NSGL_LIBRARY_CONTEXT_STATE \
GLFW_GLX_LIBRARY_CONTEXT_STATE
#define GLFW_PLATFORM_USER_CONTEXT_STATE \
GLFW_WGL_USER_CONTEXT_STATE \
GLFW_NSGL_USER_CONTEXT_STATE \
GLFW_GLX_USER_CONTEXT_STATE
#if defined(_WIN32)
#define GLFW_BUILD_WIN32_THREAD
#else

View file

@ -536,47 +536,17 @@ void _glfwTerminateWGL(void)
attribs[index++] = v; \
}
// Create the OpenGL or OpenGL ES context
// Create the OpenGL or OpenGL ES context for the given HDC
//
GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
GLFWbool _glfwCreateContextForDCWGL(HDC dc, const _GLFWctxconfig* ctxconfig, HGLRC* context)
{
int attribs[40];
int pixelFormat;
PIXELFORMATDESCRIPTOR pfd;
HGLRC share = NULL;
*context = NULL;
if (ctxconfig->share)
share = ctxconfig->share->context.wgl.handle;
window->context.wgl.dc = GetDC(window->win32.handle);
if (!window->context.wgl.dc)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"WGL: Failed to retrieve DC for window");
return GLFW_FALSE;
}
pixelFormat = choosePixelFormatWGL(window, ctxconfig, fbconfig);
if (!pixelFormat)
return GLFW_FALSE;
if (!DescribePixelFormat(window->context.wgl.dc,
pixelFormat, sizeof(pfd), &pfd))
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"WGL: Failed to retrieve PFD for selected pixel format");
return GLFW_FALSE;
}
if (!SetPixelFormat(window->context.wgl.dc, pixelFormat, &pfd))
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"WGL: Failed to set selected pixel format");
return GLFW_FALSE;
}
if (ctxconfig->client == GLFW_OPENGL_API)
{
if (ctxconfig->forward)
@ -690,9 +660,9 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
SET_ATTRIB(0, 0);
window->context.wgl.handle =
wglCreateContextAttribsARB(window->context.wgl.dc, share, attribs);
if (!window->context.wgl.handle)
*context =
wglCreateContextAttribsARB(dc, share, attribs);
if (!(*context))
{
const DWORD error = GetLastError();
@ -742,8 +712,8 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
}
else
{
window->context.wgl.handle = wglCreateContext(window->context.wgl.dc);
if (!window->context.wgl.handle)
*context = wglCreateContext(dc);
if (!(*context) )
{
_glfwInputErrorWin32(GLFW_VERSION_UNAVAILABLE,
"WGL: Failed to create OpenGL context");
@ -752,7 +722,7 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
if (share)
{
if (!wglShareLists(share, window->context.wgl.handle))
if (!wglShareLists(share, *context))
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"WGL: Failed to enable sharing with specified OpenGL context");
@ -761,6 +731,50 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
}
}
return GLFW_TRUE;
}
// Create the OpenGL or OpenGL ES context
//
GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
{
int pixelFormat;
PIXELFORMATDESCRIPTOR pfd;
window->context.wgl.dc = GetDC(window->win32.handle);
if (!window->context.wgl.dc)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"WGL: Failed to retrieve DC for window");
return GLFW_FALSE;
}
pixelFormat = choosePixelFormatWGL(window, ctxconfig, fbconfig);
if (!pixelFormat)
return GLFW_FALSE;
if (!DescribePixelFormat(window->context.wgl.dc,
pixelFormat, sizeof(pfd), &pfd))
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"WGL: Failed to retrieve PFD for selected pixel format");
return GLFW_FALSE;
}
if (!SetPixelFormat(window->context.wgl.dc, pixelFormat, &pfd))
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"WGL: Failed to set selected pixel format");
return GLFW_FALSE;
}
if(!_glfwCreateContextForDCWGL( window->context.wgl.dc, ctxconfig, &window->context.wgl.handle ))
{
return GLFW_FALSE;
}
window->context.makeCurrent = makeContextCurrentWGL;
window->context.swapBuffers = swapBuffersWGL;
window->context.swapInterval = swapIntervalWGL;
@ -771,6 +785,65 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
return GLFW_TRUE;
}
static void _glfwMakeUserContextCurrentWGL(_GLFWusercontext* context)
{
if (context)
{
if (!wglMakeCurrent(context->window->context.wgl.dc,context->wgl.handle))
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"WGL: Failed to make user context current");
_glfwPlatformSetTls(&_glfw.usercontextSlot, NULL);
return;
}
}
else
{
if (!wglMakeCurrent(NULL, NULL))
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"WGL: Failed to clear current user context");
}
}
_glfwPlatformSetTls(&_glfw.usercontextSlot, context);
}
static void _glfwDestroyUserContextWGL(_GLFWusercontext* context)
{
wglDeleteContext(context->wgl.handle);
free(context);
}
_GLFWusercontext* _glfwCreateUserContextWGL(_GLFWwindow* window)
{
_GLFWusercontext* context;
_GLFWctxconfig ctxconfig;
context = calloc(1, sizeof(_GLFWusercontext));
context->window = window;
ctxconfig = _glfw.hints.context;
ctxconfig.share = window;
if (!_glfwCreateContextForDCWGL(window->context.wgl.dc, &ctxconfig, &context->wgl.handle))
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"WGL: Failed to create user OpenGL context");
free(context);
return NULL;
}
context->makeCurrent = _glfwMakeUserContextCurrentWGL;
context->destroy = _glfwDestroyUserContextWGL;
return context;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////
//////////////////////////////////////////////////////////////////////////
#undef SET_ATTRIB
GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* handle)

View file

@ -668,6 +668,7 @@ GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform)
.waitEvents = _glfwWaitEventsWin32,
.waitEventsTimeout = _glfwWaitEventsTimeoutWin32,
.postEmptyEvent = _glfwPostEmptyEventWin32,
.createUserContext = _glfwCreateUserContextWin32,
.getEGLPlatform = _glfwGetEGLPlatformWin32,
.getEGLNativeDisplay = _glfwGetEGLNativeDisplayWin32,
.getEGLNativeWindow = _glfwGetEGLNativeWindowWin32,

View file

@ -363,8 +363,9 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(
#define GLFW_WIN32_MONITOR_STATE _GLFWmonitorWin32 win32;
#define GLFW_WIN32_CURSOR_STATE _GLFWcursorWin32 win32;
#define GLFW_WGL_CONTEXT_STATE _GLFWcontextWGL wgl;
#define GLFW_WGL_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl;
#define GLFW_WGL_CONTEXT_STATE _GLFWcontextWGL wgl;
#define GLFW_WGL_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl;
#define GLFW_WGL_USER_CONTEXT_STATE _GLFWusercontextWGL wgl;
// WGL-specific per-context data
@ -408,6 +409,14 @@ typedef struct _GLFWlibraryWGL
GLFWbool ARB_context_flush_control;
} _GLFWlibraryWGL;
// WGL-specific per-usercontext data
//
typedef struct _GLFWusercontextWGL
{
HDC dc;
HGLRC handle;
} _GLFWusercontextWGL;
// Win32-specific per-window data
//
typedef struct _GLFWwindowWin32
@ -625,3 +634,5 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig);
_GLFWusercontext* _glfwCreateUserContextWin32(_GLFWwindow* window);
_GLFWusercontext* _glfwCreateUserContextWGL(_GLFWwindow* window);

View file

@ -2575,6 +2575,28 @@ VkResult _glfwCreateWindowSurfaceWin32(VkInstance instance,
return err;
}
_GLFWusercontext* _glfwCreateUserContextWin32(_GLFWwindow* window)
{
if (window->context.wgl.handle)
{
return _glfwCreateUserContextWGL(window);
}
else if (window->context.egl.handle)
{
return _glfwCreateUserContextEGL(window);
}
else if (window->context.osmesa.handle)
{
return _glfwCreateUserContextOSMesa(window);
}
return GLFW_FALSE;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////
//////////////////////////////////////////////////////////////////////////
GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);

View file

@ -510,6 +510,7 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform)
.waitEvents = _glfwWaitEventsWayland,
.waitEventsTimeout = _glfwWaitEventsTimeoutWayland,
.postEmptyEvent = _glfwPostEmptyEventWayland,
.createUserContext = _glfwCreateUserContextWayland,
.getEGLPlatform = _glfwGetEGLPlatformWayland,
.getEGLNativeDisplay = _glfwGetEGLNativeDisplayWayland,
.getEGLNativeWindow = _glfwGetEGLNativeWindowWayland,

View file

@ -689,6 +689,8 @@ void _glfwSetGammaRampWayland(_GLFWmonitor* monitor, const GLFWgammaramp* ramp);
void _glfwAddOutputWayland(uint32_t name, uint32_t version);
void _glfwUpdateBufferScaleFromOutputsWayland(_GLFWwindow* window);
_GLFWusercontext* _glfwCreateUserContextWayland(_GLFWwindow* window);
void _glfwAddSeatListenerWayland(struct wl_seat* seat);
void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device);

View file

@ -3278,6 +3278,19 @@ VkResult _glfwCreateWindowSurfaceWayland(VkInstance instance,
return err;
}
_GLFWusercontext* _glfwCreateUserContextWayland(_GLFWwindow* window)
{
if (window->context.egl.handle)
{
return _glfwCreateUserContextEGL(window);
}
else if (window->context.osmesa.handle)
{
return _glfwCreateUserContextOSMesa(window);
}
return NULL;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////

View file

@ -1241,6 +1241,7 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform)
.waitEvents = _glfwWaitEventsX11,
.waitEventsTimeout = _glfwWaitEventsTimeoutX11,
.postEmptyEvent = _glfwPostEmptyEventX11,
.createUserContext = _glfwCreateUserContextX11,
.getEGLPlatform = _glfwGetEGLPlatformX11,
.getEGLNativeDisplay = _glfwGetEGLNativeDisplayX11,
.getEGLNativeWindow = _glfwGetEGLNativeWindowX11,

View file

@ -462,7 +462,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(Vk
#define GLFW_GLX_CONTEXT_STATE _GLFWcontextGLX glx;
#define GLFW_GLX_LIBRARY_CONTEXT_STATE _GLFWlibraryGLX glx;
#define GLFW_GLX_USER_CONTEXT_STATE _GLFWusercontextGLX glx;
// GLX-specific per-context data
//
@ -470,6 +470,7 @@ typedef struct _GLFWcontextGLX
{
GLXContext handle;
GLXWindow window;
GLXFBConfig fbconfig;
} _GLFWcontextGLX;
// GLX-specific global data
@ -518,6 +519,13 @@ typedef struct _GLFWlibraryGLX
GLFWbool ARB_context_flush_control;
} _GLFWlibraryGLX;
// GLX-specific per usercontext data
//
typedef struct _GLFWusercontextGLX
{
GLXContext handle;
} _GLFWusercontextGLX;
// X11-specific per-window data
//
typedef struct _GLFWwindowX11
@ -1002,3 +1010,5 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig,
const _GLFWfbconfig* fbconfig,
Visual** visual, int* depth);
_GLFWusercontext* _glfwCreateUserContextX11(_GLFWwindow* window);
_GLFWusercontext* _glfwCreateUserContextGLX(_GLFWwindow* window);

View file

@ -3282,6 +3282,24 @@ VkResult _glfwCreateWindowSurfaceX11(VkInstance instance,
}
}
_GLFWusercontext* _glfwCreateUserContextX11(_GLFWwindow* window)
{
if (window->context.glx.handle)
{
return _glfwCreateUserContextGLX(window);
}
else if (window->context.egl.handle)
{
return _glfwCreateUserContextEGL(window);
}
else if (window->context.osmesa.handle)
{
return _glfwCreateUserContextOSMesa(window);
}
return NULL;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////

View file

@ -28,6 +28,7 @@ add_executable(iconify iconify.c ${GETOPT} ${GLAD_GL})
add_executable(monitors monitors.c ${GETOPT} ${GLAD_GL})
add_executable(reopen reopen.c ${GLAD_GL})
add_executable(cursor cursor.c ${GLAD_GL})
add_executable(usercontext usercontext.c ${TINYCTHREAD} ${GLAD_GL})
add_executable(empty WIN32 MACOSX_BUNDLE empty.c ${TINYCTHREAD} ${GLAD_GL})
add_executable(gamma WIN32 MACOSX_BUNDLE gamma.c ${GLAD_GL})
@ -51,7 +52,7 @@ endif()
set(GUI_ONLY_BINARIES empty gamma icon inputlag joysticks tearing threads
timeout title triangle-vulkan window)
set(CONSOLE_BINARIES allocator clipboard events msaa glfwinfo iconify monitors
reopen cursor)
reopen cursor usercontext)
set_target_properties(${GUI_ONLY_BINARIES} ${CONSOLE_BINARIES} PROPERTIES
C_STANDARD 99

200
tests/usercontext.c Normal file
View file

@ -0,0 +1,200 @@
//========================================================================
// User context test
// Copyright (c) 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.
//
//========================================================================
//
// This test is intended to verify whether the OpenGL user context part of
// the GLFW API is able to be used from multiple threads
//
//========================================================================
#include "tinycthread.h"
#define GLAD_GL_IMPLEMENTATION
#include <glad/gl.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <stdio.h>
static void error_callback(int error, const char* description)
{
fprintf(stderr, "Error: %s\n", description);
}
static int thread_main(void* data)
{
GLFWusercontext* usercontext = (GLFWusercontext*)data;
/* set the user context current */
glfwMakeUserContextCurrent(usercontext);
if (glfwGetCurrentContext() != NULL)
{
fprintf(stderr, "Current glfw window context not NULL after glfwMakeUserContextCurrent\n");
glfwTerminate();
return -1;
}
if (glfwGetCurrentUserContext() != usercontext)
{
fprintf(stderr, "Current user context not correct after glfwMakeUserContextCurrent\n");
glfwTerminate();
return -1;
}
/* set the user context to NULL */
glfwMakeUserContextCurrent(NULL);
if (glfwGetCurrentUserContext() != NULL)
{
fprintf(stderr, "Current user context not NULL after glfwMakeContextCurrent\n");
glfwTerminate();
return -1;
}
return 0;
}
int main(void)
{
GLFWwindow* window;
GLFWusercontext* usercontext;
thrd_t thread_id;
int result, count;
glfwSetErrorCallback(error_callback);
/* Initialize the library */
if (!glfwInit())
return -1;
/* Create a windowed mode window and its OpenGL context */
window = glfwCreateWindow(640, 480, "User Context", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window);
gladLoadGL(glfwGetProcAddress);
/* make a new context */
usercontext = glfwCreateUserContext(window);
if (!usercontext)
{
fprintf(stderr, "Failed to create user context\n");
glfwTerminate();
return -1;
}
/* set the user context current */
glfwMakeUserContextCurrent(usercontext);
if (glfwGetCurrentContext() != NULL)
{
fprintf(stderr, "Current glfw window context not NULL after glfwMakeUserContextCurrent\n");
glfwTerminate();
return -1;
}
if (glfwGetCurrentUserContext() != usercontext)
{
fprintf(stderr, "Current user context not correct after glfwMakeUserContextCurrent\n");
glfwTerminate();
return -1;
}
/* set the window context current */
glfwMakeContextCurrent(window);
if (glfwGetCurrentUserContext() != NULL)
{
fprintf(stderr, "Current user context not NULL after glfwMakeContextCurrent\n");
glfwTerminate();
return -1;
}
if (glfwGetCurrentContext() != window)
{
fprintf(stderr, "Current glfw window context not correct after glfwMakeContextCurrent\n");
glfwTerminate();
return -1;
}
glClearColor( 0.4f, 0.3f, 0.4f, 1.0f );
// Launch a thread which should create and use the usercontext
if (thrd_create(&thread_id, thread_main, usercontext ) !=
thrd_success)
{
fprintf(stderr, "Failed to create secondary thread\n");
glfwTerminate();
exit(EXIT_FAILURE);
}
/* Loop 60 times or until the user closes the window */
count = 0;
while (!glfwWindowShouldClose(window) && count++ < 60)
{
/* Render here */
glClear(GL_COLOR_BUFFER_BIT);
/* Swap front and back buffers */
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
thrd_join(thread_id, &result);
/* One more test now the thread has joined */
/* set the user context current */
glfwMakeUserContextCurrent(usercontext);
if (glfwGetCurrentContext() != NULL)
{
fprintf(stderr, "Current glfw window context not NULL after glfwMakeUserContextCurrent\n");
glfwTerminate();
return -1;
}
if (glfwGetCurrentUserContext() != usercontext)
{
fprintf(stderr, "Current user context not correct after glfwMakeUserContextCurrent\n");
glfwTerminate();
return -1;
}
/* set the user context to NULL */
glfwMakeUserContextCurrent(NULL);
if (glfwGetCurrentUserContext() != NULL)
{
fprintf(stderr, "Current user context not NULL after glfwMakeContextCurrent\n");
glfwTerminate();
return -1;
}
glfwDestroyUserContext(usercontext);
glfwTerminate();
return 0;
}