Compare commits

...

10 Commits

Author SHA1 Message Date
Mārtiņš Možeiko
b069b041b8
Merge 11d96a4ab4 into 232164f62b 2026-02-05 20:57:42 +03:00
Camilla Löwy
232164f62b Wayland: Remove duplicate cursor state
The shared window struct already tracks the current cursor.
2026-02-05 17:28:32 +01:00
Camilla Löwy
abb9db0d75 Wayland: Fix fallback decoration button input
Fallback decoration mouse button actions were performed for both press
and release events.  The requests made using a mouse button release
serials were presumably (and correctly) discarded by the compositor.

This commit removes the generation of these nuisance requests.
2026-02-05 17:25:11 +01:00
Camilla Löwy
0aa77a9a3a Wayland: Release input devices where possible
On systems with wl_seat verison 3 or later, use release instead of
destroy on input devices when they are disconnected.
2026-02-04 18:08:37 +01:00
Camilla Löwy
7b370d8df0 Wayland: Simplify pointer surface state
Track which surface contains the pointer with one piece of state instead
of three partially overlapping ones.
2026-02-04 18:08:37 +01:00
Camilla Löwy
f871f14d5e Add update flag to timeout test
Redrawing the window contents for every possible event is not
a reasonable thing for an application to do.  The test should be whether
redrawing on demand plus periodic updates works with near-idle CPU use
and no extra input latency.

This change incidentally makes the test well-behaved on Wayland, where
it was previously broken due to every EGL buffer swap causing an event
to be received.
2026-02-04 18:07:19 +01:00
Camilla Löwy
bafb134769 Documentation work
Elaborate on what 'work' means in the context of joystick input.
2026-01-27 20:51:00 +01:00
Camilla Löwy
2d7ae8f2d0 Wayland: Cleanup
This is only a semantic change.  The values are the same.
2026-01-15 21:09:01 +01:00
Camilla Löwy
96e0f49395 X11: Place argument assertion after platform check
This lets automated testing check that GLFW_NOT_INITIALIZED is emitted
for every public function.
2026-01-14 21:03:13 +01:00
Mārtiņš Možeiko
11d96a4ab4 Win32: Do the event processing during window resize/move 2025-01-30 01:34:18 -08:00
12 changed files with 273 additions and 105 deletions

View File

@ -153,17 +153,31 @@ the `VK_KHR_xlib_surface` extension. Possible values are `GLFW_TRUE` and
`GLFW_FALSE`. This is ignored on other platforms. `GLFW_FALSE`. This is ignored on other platforms.
#### Windows specific init hints {#init_hints_windows}
@anchor GLFW_WIN32_MESSAGES_IN_FIBER_hint
__GLFW_WIN32_MESSAGES_IN_FIBER__ specifies whether to use separate fiber for
processing Windows message, and not the main thread. This allows to unblock
window move and resize operations, and application can do updates and rendering
in its main loop. __Warning__ - setting this hint is not safe for environments
or frameworks where stack inspection is required (for example, moving garbage
collected languages such as Java or C#). Set this with @ref glfwInitHint.
#### Supported and default values {#init_hints_values} #### Supported and default values {#init_hints_values}
Initialization hint | Default value | Supported values Initialization hint | Default value | Supported values
-------------------------------- | ------------------------------- | ---------------- --------------------------------- | ------------------------------- | ----------------
@ref GLFW_PLATFORM | `GLFW_ANY_PLATFORM` | `GLFW_ANY_PLATFORM`, `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`, `GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` or `GLFW_PLATFORM_NULL`
@ref GLFW_JOYSTICK_HAT_BUTTONS | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` --------------------------------- | ------------------------------- | ----------------
@ref GLFW_ANGLE_PLATFORM_TYPE | `GLFW_ANGLE_PLATFORM_TYPE_NONE` | `GLFW_ANGLE_PLATFORM_TYPE_NONE`, `GLFW_ANGLE_PLATFORM_TYPE_OPENGL`, `GLFW_ANGLE_PLATFORM_TYPE_OPENGLES`, `GLFW_ANGLE_PLATFORM_TYPE_D3D9`, `GLFW_ANGLE_PLATFORM_TYPE_D3D11`, `GLFW_ANGLE_PLATFORM_TYPE_VULKAN` or `GLFW_ANGLE_PLATFORM_TYPE_METAL` @ref GLFW_PLATFORM | `GLFW_ANY_PLATFORM` | `GLFW_ANY_PLATFORM`, `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`, `GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` or `GLFW_PLATFORM_NULL`
@ref GLFW_COCOA_CHDIR_RESOURCES | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` @ref GLFW_JOYSTICK_HAT_BUTTONS | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
@ref GLFW_COCOA_MENUBAR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` @ref GLFW_ANGLE_PLATFORM_TYPE | `GLFW_ANGLE_PLATFORM_TYPE_NONE` | `GLFW_ANGLE_PLATFORM_TYPE_NONE`, `GLFW_ANGLE_PLATFORM_TYPE_OPENGL`, `GLFW_ANGLE_PLATFORM_TYPE_OPENGLES`, `GLFW_ANGLE_PLATFORM_TYPE_D3D9`, `GLFW_ANGLE_PLATFORM_TYPE_D3D11`, `GLFW_ANGLE_PLATFORM_TYPE_VULKAN` or `GLFW_ANGLE_PLATFORM_TYPE_METAL`
@ref GLFW_WAYLAND_LIBDECOR | `GLFW_WAYLAND_PREFER_LIBDECOR` | `GLFW_WAYLAND_PREFER_LIBDECOR` or `GLFW_WAYLAND_DISABLE_LIBDECOR` @ref GLFW_COCOA_CHDIR_RESOURCES | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
@ref GLFW_X11_XCB_VULKAN_SURFACE | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` @ref GLFW_COCOA_MENUBAR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
@ref GLFW_WAYLAND_LIBDECOR | `GLFW_WAYLAND_PREFER_LIBDECOR` | `GLFW_WAYLAND_PREFER_LIBDECOR` or `GLFW_WAYLAND_DISABLE_LIBDECOR`
@ref GLFW_X11_XCB_VULKAN_SURFACE | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
@ref GLFW_WIN32_MESSAGES_IN_FIBER | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE`
### Runtime platform selection {#platform} ### Runtime platform selection {#platform}

View File

@ -1328,6 +1328,11 @@ extern "C" {
* Wayland specific [init hint](@ref GLFW_WAYLAND_LIBDECOR_hint). * Wayland specific [init hint](@ref GLFW_WAYLAND_LIBDECOR_hint).
*/ */
#define GLFW_WAYLAND_LIBDECOR 0x00053001 #define GLFW_WAYLAND_LIBDECOR 0x00053001
/*! @brief Windows specific init hint.
*
* Windows specific [init hint](@ref GLFW_WIN32_MESSAGES_IN_FIBER_hint).
*/
#define GLFW_WIN32_MESSAGES_IN_FIBER 0x00054001
/*! @} */ /*! @} */
/*! @addtogroup init /*! @addtogroup init
@ -1604,6 +1609,10 @@ typedef void (* GLFWerrorfun)(int error_code, const char* description);
* @param[in] ypos The new y-coordinate, in screen coordinates, of the * @param[in] ypos The new y-coordinate, in screen coordinates, of the
* upper-left corner of the content area of the window. * upper-left corner of the content area of the window.
* *
* @remark @win32 On Windows moving or resizing of window will block event
* processing. Workaround for this is @ref GLFW_WIN32_MESSAGES_IN_FIBER init
* hint.
*
* @sa @ref window_pos * @sa @ref window_pos
* @sa @ref glfwSetWindowPosCallback * @sa @ref glfwSetWindowPosCallback
* *
@ -1625,6 +1634,10 @@ typedef void (* GLFWwindowposfun)(GLFWwindow* window, int xpos, int ypos);
* @param[in] width The new width, in screen coordinates, of the window. * @param[in] width The new width, in screen coordinates, of the window.
* @param[in] height The new height, in screen coordinates, of the window. * @param[in] height The new height, in screen coordinates, of the window.
* *
* @remark @win32 On Windows moving or resizing of window will block event
* processing. Workaround for this is @ref GLFW_WIN32_MESSAGES_IN_FIBER init
* hint.
*
* @sa @ref window_size * @sa @ref window_size
* @sa @ref glfwSetWindowSizeCallback * @sa @ref glfwSetWindowSizeCallback
* *
@ -4520,7 +4533,8 @@ GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow*
* GLFW will pass those events on to the application callbacks before * GLFW will pass those events on to the application callbacks before
* returning. * returning.
* *
* Event processing is not required for joystick input to work. * Event processing is not required to receive joystick input. Joystick state
* is polled when a joystick input or gamepad input function is called.
* *
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR. * GLFW_PLATFORM_ERROR.
@ -4565,7 +4579,8 @@ GLFWAPI void glfwPollEvents(void);
* GLFW will pass those events on to the application callbacks before * GLFW will pass those events on to the application callbacks before
* returning. * returning.
* *
* Event processing is not required for joystick input to work. * Event processing is not required to receive joystick input. Joystick state
* is polled when a joystick input or gamepad input function is called.
* *
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR. * GLFW_PLATFORM_ERROR.
@ -4612,7 +4627,8 @@ GLFWAPI void glfwWaitEvents(void);
* GLFW will pass those events on to the application callbacks before * GLFW will pass those events on to the application callbacks before
* returning. * returning.
* *
* Event processing is not required for joystick input to work. * Event processing is not required to receive joystick input. Joystick state
* is polled when a joystick input or gamepad input function is called.
* *
* @param[in] timeout The maximum amount of time, in seconds, to wait. * @param[in] timeout The maximum amount of time, in seconds, to wait.
* *

View File

@ -66,6 +66,10 @@ static _GLFWinitconfig _glfwInitHints =
{ {
.libdecorMode = GLFW_WAYLAND_PREFER_LIBDECOR .libdecorMode = GLFW_WAYLAND_PREFER_LIBDECOR
}, },
.win32 =
{
.msgInFiber = GLFW_FALSE,
}
}; };
// The allocation function used when no custom allocator is set // The allocation function used when no custom allocator is set
@ -462,6 +466,9 @@ GLFWAPI void glfwInitHint(int hint, int value)
case GLFW_WAYLAND_LIBDECOR: case GLFW_WAYLAND_LIBDECOR:
_glfwInitHints.wl.libdecorMode = value; _glfwInitHints.wl.libdecorMode = value;
return; return;
case GLFW_WIN32_MESSAGES_IN_FIBER:
_glfwInitHints.win32.msgInFiber = value;
return;
} }
_glfwInputError(GLFW_INVALID_ENUM, _glfwInputError(GLFW_INVALID_ENUM,

View File

@ -388,6 +388,9 @@ struct _GLFWinitconfig
struct { struct {
int libdecorMode; int libdecorMode;
} wl; } wl;
struct {
GLFWbool msgInFiber;
} win32;
}; };
// Window configuration // Window configuration

View File

@ -366,7 +366,6 @@ static LRESULT CALLBACK helperWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LP
// //
static GLFWbool createHelperWindow(void) static GLFWbool createHelperWindow(void)
{ {
MSG msg;
WNDCLASSEXW wc = { sizeof(wc) }; WNDCLASSEXW wc = { sizeof(wc) };
wc.style = CS_OWNDC; wc.style = CS_OWNDC;
@ -417,10 +416,18 @@ static GLFWbool createHelperWindow(void)
DEVICE_NOTIFY_WINDOW_HANDLE); DEVICE_NOTIFY_WINDOW_HANDLE);
} }
while (PeekMessageW(&msg, _glfw.win32.helperWindowHandle, 0, 0, PM_REMOVE)) if (_glfw.hints.init.win32.msgInFiber)
{ {
TranslateMessage(&msg); SwitchToFiber(_glfw.win32.messageFiber);
DispatchMessageW(&msg); }
else
{
MSG msg;
while (PeekMessageW(&msg, _glfw.win32.helperWindowHandle, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
} }
return GLFW_TRUE; return GLFW_TRUE;
@ -595,6 +602,45 @@ BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build)
return RtlVerifyVersionInfo(&osvi, mask, cond) == 0; return RtlVerifyVersionInfo(&osvi, mask, cond) == 0;
} }
void _glfwPollMessageLoopWin32(void)
{
_GLFWwindow* window;
MSG msg;
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
// NOTE: While GLFW does not itself post WM_QUIT, other processes
// may post it to this one, for example Task Manager
// HACK: Treat WM_QUIT as a close on all windows
window = _glfw.windowListHead;
while (window)
{
_glfwInputWindowCloseRequest(window);
window = window->next;
}
}
else
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
}
// Windows message dispatch fiber
void CALLBACK messageFiberProc(LPVOID lpFiberParameter)
{
(void)lpFiberParameter;
for (;;)
{
_glfwPollMessageLoopWin32();
SwitchToFiber(_glfw.win32.mainFiber);
}
}
GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform) GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform)
{ {
const _GLFWplatform win32 = const _GLFWplatform win32 =
@ -692,6 +738,17 @@ int _glfwInitWin32(void)
else else
SetProcessDPIAware(); SetProcessDPIAware();
if (_glfw.hints.init.win32.msgInFiber)
{
_glfw.win32.mainFiber = ConvertThreadToFiber(NULL);
if (!_glfw.win32.mainFiber)
return GLFW_FALSE;
_glfw.win32.messageFiber = CreateFiber(0, &messageFiberProc, NULL);
if (!_glfw.win32.messageFiber)
return GLFW_FALSE;
}
if (!createHelperWindow()) if (!createHelperWindow())
return GLFW_FALSE; return GLFW_FALSE;
@ -714,6 +771,12 @@ void _glfwTerminateWin32(void)
if (_glfw.win32.mainWindowClass) if (_glfw.win32.mainWindowClass)
UnregisterClassW(MAKEINTATOM(_glfw.win32.mainWindowClass), _glfw.win32.instance); UnregisterClassW(MAKEINTATOM(_glfw.win32.mainWindowClass), _glfw.win32.instance);
if (_glfw.hints.init.win32.msgInFiber)
{
DeleteFiber(_glfw.win32.messageFiber);
ConvertFiberToThread();
}
_glfw_free(_glfw.win32.clipboardString); _glfw_free(_glfw.win32.clipboardString);
_glfw_free(_glfw.win32.rawInput); _glfw_free(_glfw.win32.rawInput);

View File

@ -375,6 +375,11 @@ typedef struct _GLFWwindowWin32
int lastCursorPosX, lastCursorPosY; int lastCursorPosX, lastCursorPosY;
// The last received high surrogate when decoding pairs of UTF-16 messages // The last received high surrogate when decoding pairs of UTF-16 messages
WCHAR highSurrogate; WCHAR highSurrogate;
// If user pressed mouse button on window title bar
UINT ncMouseButton;
LPARAM ncMousePos;
} _GLFWwindowWin32; } _GLFWwindowWin32;
// Win32-specific global data // Win32-specific global data
@ -403,6 +408,9 @@ typedef struct _GLFWlibraryWin32
// The cursor handle to use to hide the cursor (NULL or a transparent cursor) // The cursor handle to use to hide the cursor (NULL or a transparent cursor)
HCURSOR blankCursor; HCURSOR blankCursor;
LPVOID messageFiber;
LPVOID mainFiber;
struct { struct {
HINSTANCE instance; HINSTANCE instance;
PFN_DirectInput8Create Create; PFN_DirectInput8Create Create;
@ -519,6 +527,7 @@ void _glfwSetWindowOpacityWin32(_GLFWwindow* window, float opacity);
void _glfwSetRawMouseMotionWin32(_GLFWwindow *window, GLFWbool enabled); void _glfwSetRawMouseMotionWin32(_GLFWwindow *window, GLFWbool enabled);
GLFWbool _glfwRawMouseMotionSupportedWin32(void); GLFWbool _glfwRawMouseMotionSupportedWin32(void);
void _glfwPollMessageLoopWin32(void);
void _glfwPollEventsWin32(void); void _glfwPollEventsWin32(void);
void _glfwWaitEventsWin32(void); void _glfwWaitEventsWin32(void);
void _glfwWaitEventsTimeoutWin32(double timeout); void _glfwWaitEventsTimeoutWin32(double timeout);

View File

@ -853,11 +853,46 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
return 0; return 0;
} }
case WM_NCLBUTTONDOWN:
{
if (wParam == HTCAPTION)
{
window->win32.ncMouseButton = uMsg;
window->win32.ncMousePos = lParam;
return 0;
}
break;
}
case WM_NCMOUSEMOVE:
{
if (window->win32.ncMouseButton)
{
if (GET_X_LPARAM(window->win32.ncMousePos) != GET_X_LPARAM(lParam) ||
GET_Y_LPARAM(window->win32.ncMousePos) != GET_Y_LPARAM(lParam))
{
DefWindowProcW(hWnd, window->win32.ncMouseButton, HTCAPTION, window->win32.ncMousePos);
window->win32.ncMouseButton = 0;
}
}
break;
}
case WM_MOUSEMOVE: case WM_MOUSEMOVE:
{ {
const int x = GET_X_LPARAM(lParam); const int x = GET_X_LPARAM(lParam);
const int y = GET_Y_LPARAM(lParam); const int y = GET_Y_LPARAM(lParam);
if (window->win32.ncMouseButton)
{
if (GET_X_LPARAM(window->win32.ncMousePos) != x ||
GET_Y_LPARAM(window->win32.ncMousePos) != y)
{
DefWindowProcW(hWnd, window->win32.ncMouseButton, HTCAPTION, window->win32.ncMousePos);
window->win32.ncMouseButton = 0;
}
}
if (!window->win32.cursorTracked) if (!window->win32.cursorTracked)
{ {
TRACKMOUSEEVENT tme; TRACKMOUSEEVENT tme;
@ -998,6 +1033,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
else if (window->cursorMode == GLFW_CURSOR_CAPTURED) else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
releaseCursor(); releaseCursor();
if (_glfw.hints.init.win32.msgInFiber)
SetTimer(hWnd, 1, 1, NULL);
break; break;
} }
@ -1014,6 +1051,15 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
else if (window->cursorMode == GLFW_CURSOR_CAPTURED) else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
captureCursor(window); captureCursor(window);
if (_glfw.hints.init.win32.msgInFiber)
KillTimer(hWnd, 1);
break;
}
case WM_TIMER:
{
if (_glfw.hints.init.win32.msgInFiber && wParam == 1)
SwitchToFiber(_glfw.win32.mainFiber);
break; break;
} }
@ -2088,31 +2134,13 @@ GLFWbool _glfwRawMouseMotionSupportedWin32(void)
void _glfwPollEventsWin32(void) void _glfwPollEventsWin32(void)
{ {
MSG msg;
HWND handle; HWND handle;
_GLFWwindow* window; _GLFWwindow* window;
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) if (_glfw.hints.init.win32.msgInFiber)
{ SwitchToFiber(_glfw.win32.messageFiber);
if (msg.message == WM_QUIT) else
{ _glfwPollMessageLoopWin32();
// NOTE: While GLFW does not itself post WM_QUIT, other processes
// may post it to this one, for example Task Manager
// HACK: Treat WM_QUIT as a close on all windows
window = _glfw.windowListHead;
while (window)
{
_glfwInputWindowCloseRequest(window);
window = window->next;
}
}
else
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
// HACK: Release modifier keys that the system did not emit KEYUP for // HACK: Release modifier keys that the system did not emit KEYUP for
// NOTE: Shift keys on Windows tend to "stick" when both are pressed as // NOTE: Shift keys on Windows tend to "stick" when both are pressed as

View File

@ -834,7 +834,7 @@ int _glfwInitWayland(void)
createKeyTables(); createKeyTables();
_glfw.wl.xkb.context = xkb_context_new(0); _glfw.wl.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!_glfw.wl.xkb.context) if (!_glfw.wl.xkb.context)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,

View File

@ -360,7 +360,6 @@ typedef struct _GLFWwindowWayland
GLFWbool maximized; GLFWbool maximized;
GLFWbool activated; GLFWbool activated;
GLFWbool fullscreen; GLFWbool fullscreen;
GLFWbool hovered;
GLFWbool transparent; GLFWbool transparent;
GLFWbool scaleFramebuffer; GLFWbool scaleFramebuffer;
struct wl_surface* surface; struct wl_surface* surface;
@ -389,7 +388,6 @@ typedef struct _GLFWwindowWayland
struct libdecor_frame* frame; struct libdecor_frame* frame;
} libdecor; } libdecor;
_GLFWcursor* currentCursor;
double cursorPosX, cursorPosY; double cursorPosX, cursorPosY;
char* appId; char* appId;
@ -416,7 +414,6 @@ typedef struct _GLFWwindowWayland
GLFWbool decorations; GLFWbool decorations;
struct wl_buffer* buffer; struct wl_buffer* buffer;
_GLFWfallbackEdgeWayland top, left, right, bottom; _GLFWfallbackEdgeWayland top, left, right, bottom;
struct wl_surface* focus;
wl_fixed_t pointerX, pointerY; wl_fixed_t pointerX, pointerY;
const char* cursorName; const char* cursorName;
} fallback; } fallback;
@ -457,6 +454,7 @@ typedef struct _GLFWlibraryWayland
const char* tag; const char* tag;
struct wl_surface* pointerSurface;
struct wl_cursor_theme* cursorTheme; struct wl_cursor_theme* cursorTheme;
struct wl_cursor_theme* cursorThemeHiDPI; struct wl_cursor_theme* cursorThemeHiDPI;
struct wl_surface* cursorSurface; struct wl_surface* cursorSurface;
@ -515,7 +513,6 @@ typedef struct _GLFWlibraryWayland
PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym; PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym;
} xkb; } xkb;
_GLFWwindow* pointerFocus;
_GLFWwindow* keyboardFocus; _GLFWwindow* keyboardFocus;
struct { struct {

View File

@ -253,6 +253,9 @@ static void createFallbackDecorations(_GLFWwindow* window)
static void destroyFallbackEdge(_GLFWfallbackEdgeWayland* edge) static void destroyFallbackEdge(_GLFWfallbackEdgeWayland* edge)
{ {
if (edge->surface == _glfw.wl.pointerSurface)
_glfw.wl.pointerSurface = NULL;
if (edge->subsurface) if (edge->subsurface)
wl_subsurface_destroy(edge->subsurface); wl_subsurface_destroy(edge->subsurface);
if (edge->surface) if (edge->surface)
@ -288,26 +291,26 @@ static void updateFallbackDecorationCursor(_GLFWwindow* window,
if (window->resizable) if (window->resizable)
{ {
if (window->wl.fallback.focus == window->wl.fallback.top.surface) if (_glfw.wl.pointerSurface == window->wl.fallback.top.surface)
{ {
if (ypos < GLFW_BORDER_SIZE) if (ypos < GLFW_BORDER_SIZE)
cursorName = "n-resize"; cursorName = "n-resize";
} }
else if (window->wl.fallback.focus == window->wl.fallback.left.surface) else if (_glfw.wl.pointerSurface == window->wl.fallback.left.surface)
{ {
if (ypos < GLFW_BORDER_SIZE) if (ypos < GLFW_BORDER_SIZE)
cursorName = "nw-resize"; cursorName = "nw-resize";
else else
cursorName = "w-resize"; cursorName = "w-resize";
} }
else if (window->wl.fallback.focus == window->wl.fallback.right.surface) else if (_glfw.wl.pointerSurface == window->wl.fallback.right.surface)
{ {
if (ypos < GLFW_BORDER_SIZE) if (ypos < GLFW_BORDER_SIZE)
cursorName = "ne-resize"; cursorName = "ne-resize";
else else
cursorName = "e-resize"; cursorName = "e-resize";
} }
else if (window->wl.fallback.focus == window->wl.fallback.bottom.surface) else if (_glfw.wl.pointerSurface == window->wl.fallback.bottom.surface)
{ {
if (xpos < GLFW_BORDER_SIZE) if (xpos < GLFW_BORDER_SIZE)
cursorName = "sw-resize"; cursorName = "sw-resize";
@ -360,8 +363,12 @@ static void updateFallbackDecorationCursor(_GLFWwindow* window,
static void handleFallbackDecorationButton(_GLFWwindow* window, static void handleFallbackDecorationButton(_GLFWwindow* window,
uint32_t serial, uint32_t serial,
uint32_t button) uint32_t button,
uint32_t state)
{ {
if (state != WL_POINTER_BUTTON_STATE_PRESSED)
return;
const double xpos = wl_fixed_to_double(window->wl.fallback.pointerX); const double xpos = wl_fixed_to_double(window->wl.fallback.pointerX);
const double ypos = wl_fixed_to_double(window->wl.fallback.pointerY); const double ypos = wl_fixed_to_double(window->wl.fallback.pointerY);
@ -369,28 +376,28 @@ static void handleFallbackDecorationButton(_GLFWwindow* window,
{ {
uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE; uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE;
if (window->wl.fallback.focus == window->wl.fallback.top.surface) if (_glfw.wl.pointerSurface == window->wl.fallback.top.surface)
{ {
if (ypos < GLFW_BORDER_SIZE) if (ypos < GLFW_BORDER_SIZE)
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP; edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
else else
xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial); xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial);
} }
else if (window->wl.fallback.focus == window->wl.fallback.left.surface) else if (_glfw.wl.pointerSurface == window->wl.fallback.left.surface)
{ {
if (ypos < GLFW_BORDER_SIZE) if (ypos < GLFW_BORDER_SIZE)
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
else else
edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
} }
else if (window->wl.fallback.focus == window->wl.fallback.right.surface) else if (_glfw.wl.pointerSurface == window->wl.fallback.right.surface)
{ {
if (ypos < GLFW_BORDER_SIZE) if (ypos < GLFW_BORDER_SIZE)
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
else else
edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
} }
else if (window->wl.fallback.focus == window->wl.fallback.bottom.surface) else if (_glfw.wl.pointerSurface == window->wl.fallback.bottom.surface)
{ {
if (xpos < GLFW_BORDER_SIZE) if (xpos < GLFW_BORDER_SIZE)
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
@ -408,7 +415,7 @@ static void handleFallbackDecorationButton(_GLFWwindow* window,
if (!window->wl.xdg.toplevel) if (!window->wl.xdg.toplevel)
return; return;
if (window->wl.fallback.focus != window->wl.fallback.top.surface) if (_glfw.wl.pointerSurface != window->wl.fallback.top.surface)
return; return;
if (ypos < GLFW_BORDER_SIZE) if (ypos < GLFW_BORDER_SIZE)
@ -1267,14 +1274,16 @@ static void setCursorImage(_GLFWwindow* window,
wl_surface_commit(surface); wl_surface_commit(surface);
} }
static void incrementCursorImage(_GLFWwindow* window) static void incrementCursorImage(void)
{ {
_GLFWcursor* cursor; if (!_glfw.wl.pointerSurface)
if (!window || !window->wl.hovered)
return; return;
cursor = window->wl.currentCursor; _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface);
if (window->wl.surface != _glfw.wl.pointerSurface)
return;
_GLFWcursor* cursor = window->cursor;
if (cursor && cursor->wl.cursor) if (cursor && cursor->wl.cursor)
{ {
cursor->wl.currentImage += 1; cursor->wl.currentImage += 1;
@ -1436,7 +1445,7 @@ static void handleEvents(double* timeout)
uint64_t repeats; uint64_t repeats;
if (read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)) == 8) if (read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)) == 8)
incrementCursorImage(_glfw.wl.pointerFocus); incrementCursorImage();
} }
if (fds[LIBDECOR_FD].revents & POLLIN) if (fds[LIBDECOR_FD].revents & POLLIN)
@ -1527,16 +1536,14 @@ static void pointerHandleEnter(void* userData,
if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag) if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
return; return;
_GLFWwindow* window = wl_surface_get_user_data(surface);
_glfw.wl.serial = serial; _glfw.wl.serial = serial;
_glfw.wl.pointerEnterSerial = serial; _glfw.wl.pointerEnterSerial = serial;
_glfw.wl.pointerFocus = window; _glfw.wl.pointerSurface = surface;
if (surface == window->wl.surface) _GLFWwindow* window = wl_surface_get_user_data(surface);
if (window->wl.surface == surface)
{ {
window->wl.hovered = GLFW_TRUE; _glfwSetCursorWayland(window, window->cursor);
_glfwSetCursorWayland(window, window->wl.currentCursor);
_glfwInputCursorEnter(window, GLFW_TRUE); _glfwInputCursorEnter(window, GLFW_TRUE);
if (window->cursorMode != GLFW_CURSOR_DISABLED) if (window->cursorMode != GLFW_CURSOR_DISABLED)
@ -1549,10 +1556,7 @@ static void pointerHandleEnter(void* userData,
else else
{ {
if (window->wl.fallback.decorations) if (window->wl.fallback.decorations)
{
window->wl.fallback.focus = surface;
updateFallbackDecorationCursor(window, sx, sy); updateFallbackDecorationCursor(window, sx, sy);
}
} }
} }
@ -1567,25 +1571,16 @@ static void pointerHandleLeave(void* userData,
if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag) if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
return; return;
_GLFWwindow* window = _glfw.wl.pointerFocus;
if (!window)
return;
_glfw.wl.serial = serial; _glfw.wl.serial = serial;
_glfw.wl.pointerFocus = NULL; _glfw.wl.pointerSurface = NULL;
if (window->wl.hovered) _GLFWwindow* window = wl_surface_get_user_data(surface);
{ if (window->wl.surface == surface)
window->wl.hovered = GLFW_FALSE;
_glfwInputCursorEnter(window, GLFW_FALSE); _glfwInputCursorEnter(window, GLFW_FALSE);
}
else else
{ {
if (window->wl.fallback.decorations) if (window->wl.fallback.decorations)
{
window->wl.fallback.focus = NULL;
window->wl.fallback.cursorName = NULL; window->wl.fallback.cursorName = NULL;
}
} }
} }
@ -1595,14 +1590,15 @@ static void pointerHandleMotion(void* userData,
wl_fixed_t sx, wl_fixed_t sx,
wl_fixed_t sy) wl_fixed_t sy)
{ {
_GLFWwindow* window = _glfw.wl.pointerFocus; if (!_glfw.wl.pointerSurface)
if (!window)
return; return;
_GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface);
if (window->cursorMode == GLFW_CURSOR_DISABLED) if (window->cursorMode == GLFW_CURSOR_DISABLED)
return; return;
if (window->wl.hovered) if (window->wl.surface == _glfw.wl.pointerSurface)
{ {
window->wl.cursorPosX = wl_fixed_to_double(sx); window->wl.cursorPosX = wl_fixed_to_double(sx);
window->wl.cursorPosY = wl_fixed_to_double(sy); window->wl.cursorPosY = wl_fixed_to_double(sy);
@ -1622,11 +1618,12 @@ static void pointerHandleButton(void* userData,
uint32_t button, uint32_t button,
uint32_t state) uint32_t state)
{ {
_GLFWwindow* window = _glfw.wl.pointerFocus; if (!_glfw.wl.pointerSurface)
if (!window)
return; return;
if (window->wl.hovered) _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface);
if (window->wl.surface == _glfw.wl.pointerSurface)
{ {
_glfw.wl.serial = serial; _glfw.wl.serial = serial;
@ -1638,7 +1635,7 @@ static void pointerHandleButton(void* userData,
else else
{ {
if (window->wl.fallback.decorations) if (window->wl.fallback.decorations)
handleFallbackDecorationButton(window, serial, button); handleFallbackDecorationButton(window, serial, button, state);
} }
} }
@ -1648,11 +1645,12 @@ static void pointerHandleAxis(void* userData,
uint32_t axis, uint32_t axis,
wl_fixed_t value) wl_fixed_t value)
{ {
_GLFWwindow* window = _glfw.wl.pointerFocus; if (!_glfw.wl.pointerSurface)
if (!window)
return; return;
if (window->wl.hovered) _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface);
if (window->wl.surface == _glfw.wl.pointerSurface)
{ {
// NOTE: 10 units of motion per mouse wheel step seems to be a common ratio // NOTE: 10 units of motion per mouse wheel step seems to be a common ratio
if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
@ -1701,7 +1699,7 @@ static void keyboardHandleKeymap(void* userData,
keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context, keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context,
mapStr, mapStr,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_FORMAT_TEXT_V1,
0); XKB_KEYMAP_COMPILE_NO_FLAGS);
munmap(mapStr, size); munmap(mapStr, size);
close(fd); close(fd);
@ -1932,7 +1930,11 @@ static void seatHandleCapabilities(void* userData,
} }
else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer) else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer)
{ {
wl_pointer_destroy(_glfw.wl.pointer); if (wl_pointer_get_version(_glfw.wl.pointer) >= WL_POINTER_RELEASE_SINCE_VERSION)
wl_pointer_release(_glfw.wl.pointer);
else
wl_pointer_destroy(_glfw.wl.pointer);
_glfw.wl.pointer = NULL; _glfw.wl.pointer = NULL;
} }
@ -1943,7 +1945,11 @@ static void seatHandleCapabilities(void* userData,
} }
else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard) else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard)
{ {
wl_keyboard_destroy(_glfw.wl.keyboard); if (wl_keyboard_get_version(_glfw.wl.keyboard) >= WL_KEYBOARD_RELEASE_SINCE_VERSION)
wl_keyboard_release(_glfw.wl.keyboard);
else
wl_keyboard_destroy(_glfw.wl.keyboard);
_glfw.wl.keyboard = NULL; _glfw.wl.keyboard = NULL;
} }
} }
@ -2223,8 +2229,8 @@ GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window,
void _glfwDestroyWindowWayland(_GLFWwindow* window) void _glfwDestroyWindowWayland(_GLFWwindow* window)
{ {
if (window == _glfw.wl.pointerFocus) if (window->wl.surface == _glfw.wl.pointerSurface)
_glfw.wl.pointerFocus = NULL; _glfw.wl.pointerSurface = NULL;
if (window == _glfw.wl.keyboardFocus) if (window == _glfw.wl.keyboardFocus)
{ {
@ -2600,7 +2606,7 @@ GLFWbool _glfwWindowMaximizedWayland(_GLFWwindow* window)
GLFWbool _glfwWindowHoveredWayland(_GLFWwindow* window) GLFWbool _glfwWindowHoveredWayland(_GLFWwindow* window)
{ {
return window->wl.hovered; return window->wl.surface == _glfw.wl.pointerSurface;
} }
GLFWbool _glfwFramebufferTransparentWayland(_GLFWwindow* window) GLFWbool _glfwFramebufferTransparentWayland(_GLFWwindow* window)
@ -2730,7 +2736,7 @@ void _glfwSetCursorPosWayland(_GLFWwindow* window, double x, double y)
void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode) void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode)
{ {
_glfwSetCursorWayland(window, window->wl.currentCursor); _glfwSetCursorWayland(window, window->cursor);
} }
const char* _glfwGetScancodeNameWayland(int scancode) const char* _glfwGetScancodeNameWayland(int scancode)
@ -3064,11 +3070,7 @@ void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor)
if (!_glfw.wl.pointer) if (!_glfw.wl.pointer)
return; return;
window->wl.currentCursor = cursor; if (window->wl.surface != _glfw.wl.pointerSurface)
// If we're not in the correct window just save the cursor
// the next time the pointer enters the window the cursor will change
if (!window->wl.hovered)
return; return;
// Update pointer lock to match cursor mode // Update pointer lock to match cursor mode

View File

@ -3344,8 +3344,6 @@ GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
GLFWAPI void glfwSetX11SelectionString(const char* string) GLFWAPI void glfwSetX11SelectionString(const char* string)
{ {
assert(string != NULL);
_GLFW_REQUIRE_INIT(); _GLFW_REQUIRE_INIT();
if (_glfw.platform.platformID != GLFW_PLATFORM_X11) if (_glfw.platform.platformID != GLFW_PLATFORM_X11)
@ -3354,6 +3352,8 @@ GLFWAPI void glfwSetX11SelectionString(const char* string)
return; return;
} }
assert(string != NULL);
_glfw_free(_glfw.x11.primarySelectionString); _glfw_free(_glfw.x11.primarySelectionString);
_glfw.x11.primarySelectionString = _glfw_strdup(string); _glfw.x11.primarySelectionString = _glfw_strdup(string);

View File

@ -36,6 +36,14 @@
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h>
static bool needs_update;
static void window_close_callback(GLFWwindow* window)
{
needs_update = true;
}
static void error_callback(int error, const char* description) static void error_callback(int error, const char* description)
{ {
@ -45,7 +53,15 @@ static void error_callback(int error, const char* description)
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{ {
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, GLFW_TRUE); glfwSetWindowShouldClose(window, GLFW_TRUE);
needs_update = true;
}
}
static void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
needs_update = true;
} }
static float nrand(void) static float nrand(void)
@ -72,8 +88,11 @@ int main(void)
} }
glfwMakeContextCurrent(window); glfwMakeContextCurrent(window);
glfwSwapInterval(0);
gladLoadGL(glfwGetProcAddress); gladLoadGL(glfwGetProcAddress);
glfwSetWindowCloseCallback(window, window_close_callback);
glfwSetKeyCallback(window, key_callback); glfwSetKeyCallback(window, key_callback);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
while (!glfwWindowShouldClose(window)) while (!glfwWindowShouldClose(window))
{ {
@ -87,8 +106,18 @@ int main(void)
glClearColor(r / l, g / l, b / l, 1.f); glClearColor(r / l, g / l, b / l, 1.f);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(window); glfwSwapBuffers(window);
needs_update = false;
glfwWaitEventsTimeout(1.0); const double start = glfwGetTime();
while (!needs_update)
{
const double elapsed = glfwGetTime() - start;
if (elapsed >= 1.0)
needs_update = true;
else
glfwWaitEventsTimeout(1.0 - elapsed);
}
} }
glfwDestroyWindow(window); glfwDestroyWindow(window);