Compare commits

...

66 Commits

Author SHA1 Message Date
Doug Binks
3a79504b14
Merge af6e36f250 into 63a7e8b7f8 2025-08-29 17:33:07 +02:00
Camilla Löwy
63a7e8b7f8 Add and update Wayland-specific notes in docs
Fixes #2746
2025-08-20 20:13:07 +02:00
Doug Binks
acb92944d4 Revert readme for "Wayland: Keyboard leave event handler now processes key repeats" 2025-08-19 11:30:52 +02:00
Camilla Löwy
7ef6efeb66 Wayland: Fix cursor position after a modal
If a modal surface like the window menu was active, clicking on the GLFW
window content area to close it would correctly emit the cursor enter
event but would not propagate the cursor position from the event.
2025-08-18 20:58:12 +02:00
Camilla Löwy
3cf9f6726d Wayland: Fix fallback decoration cursor updating
When a click through to the fallback decorations caused the end of
a modal like the window menu, the cursor shape would not be updated
until the next time the cursor moved.

This commit adds an update of the cursor for the pointer enter event for
fallback decoration surfaces, in addition to the updates at pointer
motion events.
2025-08-18 18:06:46 +02:00
Camilla Löwy
bfa1c424e5 Wayland: Fix fallback decoration menu placement
The fallback decorations would place the menu at the wrong position, by
not translating the last decoration surface position into toplevel
surface coordinates.

This also limits the menu to the caption area of the top decoration
surface, similar to how other toolkits work.
2025-08-18 18:06:46 +02:00
Doug Binks
af6e36f250 Merge branch 'master' into multi-context-windows 2025-08-10 18:36:47 +02:00
Doug Binks
5c08ecbb5c Merge branch 'master' into multi-context-windows 2025-08-10 13:04:37 +02:00
Doug Binks
d35fdc21eb Merge branch 'master' into multi-context-windows 2025-07-05 19:22:48 +02:00
Doug Binks
32e0089b86 Merge branch 'master' into multi-context-windows 2025-07-05 19:22:07 +02:00
Doug Binks
e2f9340a8c Improved usercontext test with threading 2025-02-11 16:17:45 +00:00
Doug Binks
58aad6c136 Fix for not being able to clear the user context 2025-02-10 18:36:01 +00:00
Doug Binks
e4e46b4cc5 Merge branch 'multi-context-windows-merge-master' into multi-context-windows 2024-10-09 18:03:32 +02:00
Doug Binks
587025f9cb Merge branch 'master' into multi-context-windows-merge-master 2024-10-09 18:03:07 +02:00
Doug Binks
ca4485e12e Merge branch 'multi-context-windows-merge-master' into multi-context-windows 2024-03-07 16:05:47 +00:00
Doug Binks
ece28b0f19 Merge branch 'master' into multi-context-windows-merge-master 2024-03-07 15:21:24 +00:00
Doug Binks
df70d8e9bf Merge branch 'multi-context-windows-merge-master' into multi-context-windows 2024-01-25 18:10:38 +00:00
Doug Binks
6148990427 Merge branch 'master' into multi-context-windows-merge-master 2024-01-25 17:44:13 +00:00
Doug Binks
64694f17ab Merge branch 'multi-context-windows-merge-master' into multi-context-windows 2022-12-20 18:01:02 +00:00
Doug Binks
bbe6c4cfa1 Merge branch 'master' into multi-context-windows-merge-master 2022-12-20 18:00:47 +00:00
Doug Binks
c01d16a9ee Merge branch 'multi-context-windows-merge-master' into multi-context-windows 2022-06-26 17:20:39 +02:00
Doug Binks
f40442fa59 Merge branch 'master' into multi-context-windows-merge-master 2022-06-26 17:17:14 +02:00
Doug Binks
f2a9ea7b7e Merge branch 'multi-context-windows-merge-master' into multi-context-windows 2022-06-09 17:49:19 +01:00
Doug Binks
48c0cea73f Merge branch 'master' into multi-context-windows-merge-master 2022-06-09 17:49:06 +01:00
Doug Binks
7de1c5c10a Minor formating fix 2022-06-09 13:55:33 +01:00
Doug Binks
fc32eb48c5 Merge branch 'master' 2022-06-06 12:55:45 +01:00
Doug Binks
41bb2515ef Merge branch 'master' into multi-context-windows-merge-master 2022-04-05 20:17:23 +01:00
Doug Binks
dcf9a51b78 UserContext: fixed wayland platform 2022-03-31 19:53:09 +01:00
Doug Binks
23d31b805a UserContext: fixed x11 platform 2022-03-31 19:29:44 +01:00
Doug Binks
3b003dd5f0 UserContext: error return for CreateUserContext should be NULL 2022-03-31 19:21:42 +01:00
Doug Binks
9dfa1649ff UserContext: Null platform fixes 2022-03-31 16:59:01 +01:00
Doug Binks
c97e3a657b Merge branch 'master' into multi-context-windows-merge-master 2022-03-31 16:49:50 +01:00
Doug Binks
b88a8c9364 Fixed usercontext glad use after merge 2021-09-04 12:41:39 +01:00
Camilla Löwy
7a55ca34b9
Merge branch 'master' into multi-context-windows 2021-09-02 19:31:33 +02:00
Doug Binks
b0d196c43c Formatting consistency fixes 2021-03-23 18:45:29 +00:00
Doug Binks
7b6f25c360 Removed more trailing white space 2021-03-23 16:11:07 +00:00
Doug Binks
c01aa3fec4 Removed trailing white space 2021-03-23 15:56:56 +00:00
Doug Binks
1ba83ede44 User contexts: added context documentation and updated references 2021-03-23 14:40:54 +00:00
Doug Binks
7874992888 User contexts: added news entry 2021-03-23 13:09:17 +00:00
Doug Binks
350aebf20a User contexts: added change log entry in README.md, listing all new symbols 2021-03-23 12:53:17 +00:00
Doug Binks
e91b1820dd Modified clear colour of usercontext test to remove transparency 2021-03-23 12:37:54 +00:00
Doug Binks
ab4b4e95f6 Merge branch 'master' into merge 2021-03-22 15:33:40 +00:00
Doug Binks
e2016291f1 Merge branch 'master' into multi-context-windows 2020-09-04 17:44:36 +01:00
Doug Binks
375fcdeadb Added GLFW_NO_API check to glfwCreateUserContext and error documentation. 2020-07-16 15:20:25 +01:00
Doug Binks
1d647668af EGL user context code simplification 2020-07-16 14:02:32 +01:00
Doug Binks
cd68bac78d Initial user context documentation 2020-07-16 11:59:23 +01:00
Doug Binks
1cb8ab8dc6 Make user context non current when destroyed 2020-07-16 11:59:11 +01:00
Doug Binks
87ddca831a Removed unused EGL defines. 2020-07-15 19:49:35 +01:00
Doug Binks
2f2e7f041a Simplified fallback pbuffer surface path. 2020-07-15 19:40:00 +01:00
Doug Binks
e55a552eee User context EGL fixes. 2020-07-15 14:31:05 +01:00
Doug Binks
5f52f2a7f8 Fix for ELG wrong surface passed 2020-07-15 13:23:38 +01:00
Doug Binks
3de9ed6453 Fixed EGL glfwMakeUserContextCurrent - requires different surface 2020-07-15 13:21:02 +01:00
Doug Binks
d01ad1a78d Improved usercontext.c test 2020-07-15 13:19:47 +01:00
Doug Binks
f2806aa9ab Added glfwGetCurrentUserContext 2020-07-15 13:19:33 +01:00
Doug Binks
6539d101f3 Added TLS for current user context and simplified code 2020-07-15 13:19:14 +01:00
Doug Binks
a1a1b77150 Added _GLFW_REQUIRE* to user context functions. 2020-07-15 11:19:18 +01:00
Doug Binks
4caca8b20c glfwMakeUserContextCurrent now calls glfwMakeContextCurrent(NULL) to ensure TLS set. 2020-07-15 11:18:58 +01:00
Doug Binks
f2b86a25b3 Cocoa and NSGL Implementation 2020-07-14 19:10:31 +01:00
Doug Binks
dd854e47ba Formatting 2020-07-14 12:38:18 +01:00
Doug Binks
931ba89aad User context null platform and OSMESA implementation. 2020-07-14 11:41:42 +01:00
Doug Binks
51f11929f3 User context Wayland and EGL implementations 2020-07-13 19:05:36 +01:00
Doug Binks
5e94092263 Refactor user context implementation to use the standard GLFW platform / context approach 2020-07-13 18:06:48 +01:00
Doug Binks
3a0a3c540c Merge branch 'master' into multi-context-windows 2020-07-13 11:04:23 +01:00
Doug Binks
998036654c GLX user context implementation 2020-07-13 10:50:06 +01:00
Doug Binks
0ae4eb4d26 Improved User Context Win32 implementation
_glfwCreateContextWGL and _glfwPlatformCreateUserContext now use  new function _glfwCreateContextForDCWGL.
2020-07-12 12:21:18 +01:00
Doug Binks
a08bfd9891 User Contexts API with Win32 implementation. 2020-06-03 19:46:22 +01:00
33 changed files with 1194 additions and 121 deletions

View File

@ -55,6 +55,7 @@ video tutorials.
- Jason Daly
- danhambleton
- Jarrod Davis
- decce
- Olivier Delannoy
- Paul R. Deppe
- Michael Dickens

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)
@ -134,18 +137,21 @@ information on what to include when reporting a bug.
- [Wayland] Bugfix: Ignore key repeat events when no window has keyboard focus (#2727)
- [Wayland] Bugfix: Reset key repeat timer when window destroyed (#2741,#2727)
- [Wayland] Bugfix: Memory would leak if reading a data offer failed midway
- [Wayland] Bugfix: Keyboard leave event handler now processes key repeats (#2736)
- [Wayland] Bugfix: Retrieved cursor position would be incorrect when hovering over
fallback decorations
- [Wayland] Bugfix: Fallback decorations would report scroll events
- [Wayland] Bugfix: Keyboard repeat events halted when any key is released (#2568)
- [Wayland] Bugfix: Fallback decorations would show menu at wrong position
- [Wayland] Bugfix: The cursor was not updated when clicking through from
a modal to a fallback decoration
- [Wayland] Bugfix: The cursor position was not updated when clicking through
from a modal to the content area
- [X11] Bugfix: Running without a WM could trigger an assert (#2593,#2601,#2631)
- [Null] Added Vulkan 'window' surface creation via `VK_EXT_headless_surface`
- [Null] Added EGL context creation on Mesa via `EGL_MESA_platform_surfaceless`
- [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

@ -255,3 +255,7 @@ hardware gamma correction, which today is typically an approximation of sRGB
gamma. This means that setting a perfectly linear ramp, or gamma 1.0, will
produce the default (usually sRGB-like) behavior.
@note @wayland An application cannot read or modify the monitor gamma ramp. The
@ref glfwGetGammaRamp, @ref glfwSetGammaRamp and @ref glfwSetGamma functions
emit @ref GLFW_FEATURE_UNAVAILABLE.

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

@ -893,6 +893,12 @@ int xpos, ypos;
glfwGetWindowPos(window, &xpos, &ypos);
```
@note @wayland An applications cannot know the positions of its windows or
whether one has been moved. The @ref GLFW_POSITION_X and @ref GLFW_POSITION_Y
window hints are ignored. The @ref glfwGetWindowPos and @ref glfwSetWindowPos
functions emit @ref GLFW_FEATURE_UNAVAILABLE. The window position callback will
not be called.
### Window title {#window_title}
@ -1038,6 +1044,12 @@ You can also get the current iconification state with @ref glfwGetWindowAttrib.
int iconified = glfwGetWindowAttrib(window, GLFW_ICONIFIED);
```
@note @wayland An application cannot know if any of its windows have been
iconified or restore one from iconification. The @ref glfwRestoreWindow
function can only restore windows from maximization and the iconify callback
will not be called. The [GLFW_ICONIFIED](@ref GLFW_ICONIFIED_attrib) attribute
will be false. The @ref glfwIconifyWindow function works normally.
### Window maximization {#window_maximize}

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.
@ -2915,8 +2927,8 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor);
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref GLFW_INVALID_VALUE,
* @ref GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark @wayland Gamma handling is a privileged protocol, this function
* will thus never be implemented and emits @ref GLFW_FEATURE_UNAVAILABLE.
* @remark @wayland Monitor gamma is a privileged protocol, so this function
* cannot be implemented and emits @ref GLFW_FEATURE_UNAVAILABLE.
*
* @thread_safety This function must only be called from the main thread.
*
@ -2939,8 +2951,8 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma);
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref GLFW_PLATFORM_ERROR
* and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark @wayland Gamma handling is a privileged protocol, this function
* will thus never be implemented and emits @ref GLFW_FEATURE_UNAVAILABLE while
* @remark @wayland Monitor gamma is a privileged protocol, so this function
* cannot be implemented and emits @ref GLFW_FEATURE_UNAVAILABLE while
* returning `NULL`.
*
* @pointer_lifetime The returned structure and its arrays are allocated and
@ -2983,8 +2995,8 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor);
*
* @remark @win32 The gamma ramp size must be 256.
*
* @remark @wayland Gamma handling is a privileged protocol, this function
* will thus never be implemented and emits @ref GLFW_FEATURE_UNAVAILABLE.
* @remark @wayland Monitor gamma is a privileged protocol, so this function
* cannot be implemented and emits @ref GLFW_FEATURE_UNAVAILABLE.
*
* @pointer_lifetime The specified gamma ramp is copied before this function
* returns.
@ -3430,8 +3442,8 @@ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* i
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark @wayland There is no way for an application to retrieve the global
* position of its windows. This function will emit @ref
* @remark @wayland Window positions are not currently part of any common
* Wayland protocol, so this function cannot be implemented and will emit @ref
* GLFW_FEATURE_UNAVAILABLE.
*
* @thread_safety This function must only be called from the main thread.
@ -3464,8 +3476,8 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos);
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark @wayland There is no way for an application to set the global
* position of its windows. This function will emit @ref
* @remark @wayland Window positions are not currently part of any common
* Wayland protocol, so this function cannot be implemented and will emit @ref
* GLFW_FEATURE_UNAVAILABLE.
*
* @thread_safety This function must only be called from the main thread.
@ -3807,10 +3819,6 @@ GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity);
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @remark @wayland Once a window is iconified, @ref glfwRestoreWindow wont
* be able to restore it. This is a design decision of the xdg-shell
* protocol.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_iconify
@ -3838,6 +3846,10 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window);
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @remark @wayland Restoring a window from maximization is not currently part
* of any common Wayland protocol, so this function can only restore windows
* from maximization.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_iconify
@ -4058,8 +4070,8 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window);
* affected by any resizing or mode switching, although you may need to update
* your viewport if the framebuffer size has changed.
*
* @remark @wayland The desired window position is ignored, as there is no way
* for an application to set this property.
* @remark @wayland Window positions are not currently part of any common
* Wayland protocol. The window position arguments are ignored.
*
* @thread_safety This function must only be called from the main thread.
*
@ -4096,8 +4108,9 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int
* errors. However, this function should not fail as long as it is passed
* valid arguments and the library has been [initialized](@ref intro_init).
*
* @remark @wayland The Wayland protocol provides no way to check whether a
* window is iconfied, so @ref GLFW_ICONIFIED always returns `GLFW_FALSE`.
* @remark @wayland Checking whether a window is iconified is not currently
* part of any common Wayland protocol, so the @ref GLFW_ICONIFIED attribute
* cannot be implemented and is always `GLFW_FALSE`.
*
* @thread_safety This function must only be called from the main thread.
*
@ -4219,8 +4232,8 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window);
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @remark @wayland This callback will never be called, as there is no way for
* an application to know its global position.
* @remark @wayland This callback will not be called. The Wayland protocol
* provides no way to be notified of when a window is moved.
*
* @thread_safety This function must only be called from the main thread.
*
@ -4395,6 +4408,10 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @remark @wayland This callback will not be called. The Wayland protocol
* provides no way to be notified of when a window is iconified, and no way to
* check whether a window is currently iconified.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_iconify
@ -6088,6 +6105,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 +6135,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 +6326,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

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

View File

@ -305,8 +305,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
@ -350,6 +351,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
@ -565,3 +574,5 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig);
_GLFWusercontext* _glfwCreateUserContextWin32(_GLFWwindow* window);
_GLFWusercontext* _glfwCreateUserContextWGL(_GLFWwindow* window);

View File

@ -2562,6 +2562,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

@ -690,6 +690,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

@ -405,13 +405,19 @@ static void handleFallbackDecorationButton(_GLFWwindow* window,
}
else if (button == BTN_RIGHT)
{
if (window->wl.xdg.toplevel)
{
xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
_glfw.wl.seat, serial,
window->wl.cursorPosX,
window->wl.cursorPosY);
}
if (!window->wl.xdg.toplevel)
return;
if (window->wl.fallback.focus != window->wl.fallback.top.surface)
return;
if (ypos < GLFW_BORDER_SIZE)
return;
xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
_glfw.wl.seat, serial,
xpos,
ypos - GLFW_CAPTION_HEIGHT - GLFW_BORDER_SIZE);
}
}
@ -1532,11 +1538,21 @@ static void pointerHandleEnter(void* userData,
window->wl.hovered = GLFW_TRUE;
_glfwSetCursorWayland(window, window->wl.currentCursor);
_glfwInputCursorEnter(window, GLFW_TRUE);
if (window->cursorMode != GLFW_CURSOR_DISABLED)
{
window->wl.cursorPosX = wl_fixed_to_double(sx);
window->wl.cursorPosY = wl_fixed_to_double(sy);
_glfwInputCursorPos(window, window->wl.cursorPosX, window->wl.cursorPosY);
}
}
else
{
if (window->wl.fallback.decorations)
{
window->wl.fallback.focus = surface;
updateFallbackDecorationCursor(window, sx, sy);
}
}
}
@ -3308,6 +3324,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;
}