From c4684b288dc2e43030f26ffc00ef50b25440dd37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 13 Feb 2026 13:24:09 +0100 Subject: [PATCH 01/10] Remove some pointless comments --- src/egl_context.c | 6 ------ src/glx_context.c | 6 ------ src/nsgl_context.m | 6 ------ src/wgl_context.c | 6 ------ 4 files changed, 24 deletions(-) diff --git a/src/egl_context.c b/src/egl_context.c index 921d5c6e..46b098f5 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -358,8 +358,6 @@ static void destroyContextEGL(_GLFWwindow* window) ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// -// Initialize EGL -// GLFWbool _glfwInitEGL(void) { int i; @@ -545,8 +543,6 @@ GLFWbool _glfwInitEGL(void) return GLFW_TRUE; } -// Terminate EGL -// void _glfwTerminateEGL(void) { if (_glfw.egl.display) @@ -570,8 +566,6 @@ void _glfwTerminateEGL(void) attribs[index++] = v; \ } -// Create the OpenGL or OpenGL ES context -// GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) diff --git a/src/glx_context.c b/src/glx_context.c index 098c4bad..84fca9dd 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -253,8 +253,6 @@ static void destroyContextGLX(_GLFWwindow* window) ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// -// Initialize GLX -// GLFWbool _glfwInitGLX(void) { const char* sonames[] = @@ -426,8 +424,6 @@ GLFWbool _glfwInitGLX(void) return GLFW_TRUE; } -// Terminate GLX -// void _glfwTerminateGLX(void) { // NOTE: This function must not call any X11 functions, as it is called @@ -447,8 +443,6 @@ void _glfwTerminateGLX(void) attribs[index++] = v; \ } -// Create the OpenGL or OpenGL ES context -// GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) diff --git a/src/nsgl_context.m b/src/nsgl_context.m index df729800..cc676171 100644 --- a/src/nsgl_context.m +++ b/src/nsgl_context.m @@ -128,8 +128,6 @@ static void destroyContextNSGL(_GLFWwindow* window) ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// -// Initialize OpenGL support -// GLFWbool _glfwInitNSGL(void) { if (_glfw.nsgl.framework) @@ -147,14 +145,10 @@ GLFWbool _glfwInitNSGL(void) return GLFW_TRUE; } -// Terminate OpenGL support -// void _glfwTerminateNSGL(void) { } -// Create the OpenGL context -// GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) diff --git a/src/wgl_context.c b/src/wgl_context.c index 3c7d71c2..04429066 100644 --- a/src/wgl_context.c +++ b/src/wgl_context.c @@ -401,8 +401,6 @@ static void destroyContextWGL(_GLFWwindow* window) } } -// Initialize WGL -// GLFWbool _glfwInitWGL(void) { PIXELFORMATDESCRIPTOR pfd; @@ -521,8 +519,6 @@ GLFWbool _glfwInitWGL(void) return GLFW_TRUE; } -// Terminate WGL -// void _glfwTerminateWGL(void) { if (_glfw.wgl.instance) @@ -536,8 +532,6 @@ void _glfwTerminateWGL(void) attribs[index++] = v; \ } -// Create the OpenGL or OpenGL ES context -// GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) From a98badf088284a29c5adbb9d2376d6e5935b5c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 9 Feb 2026 19:48:45 +0100 Subject: [PATCH 02/10] Wayland: Fix key repeat on very old compositors The client-side key repeat timer fd was only created for wl_seat version 4 or later, but was then used unconditionally during event processing. Rather than have this mechanism do nothing in a more correct way on wl_seat version 3 or earlier (which is very old by now), this commit creates the key repeat timer fd and then sets (hopefully gentle) hardcoded repeat delay and rate. --- README.md | 1 + src/wl_init.c | 17 ++++++++++------- src/wl_window.c | 7 +++++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b05132e7..a5689a5c 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,7 @@ information on what to include when reporting a bug. potential segmentation fault (#2744) - [Wayland] Bugfix: Confining or disabling the cursor could segfault on compositors without `pointer-constraints-unstable-v1` + - [Wayland] Bugfix: Key repeat did not function on very old compositors - [X11] Bugfix: Running without a WM could trigger an assert (#2593,#2601,#2631) - [X11] Bugfix: Occasional crash when an idle display awakes (#2766) - [X11] Bugfix: Prevent BadWindow when creating small windows with a content scale diff --git a/src/wl_init.c b/src/wl_init.c index 1d0d3ef8..41cbc57c 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -137,13 +137,6 @@ static void registryHandleGlobal(void* userData, wl_registry_bind(registry, name, &wl_seat_interface, _glfw_min(4, version)); _glfwAddSeatListenerWayland(_glfw.wl.seat); - - if (wl_seat_get_version(_glfw.wl.seat) >= - WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) - { - _glfw.wl.keyRepeatTimerfd = - timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); - } } } else if (strcmp(interface, "wl_data_device_manager") == 0) @@ -834,6 +827,16 @@ int _glfwInitWayland(void) createKeyTables(); + _glfw.wl.keyRepeatTimerfd = + timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); + if (_glfw.wl.keyRepeatTimerfd == -1) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create timerfd: %s", + strerror(errno)); + return GLFW_FALSE; + } + _glfw.wl.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!_glfw.wl.xkb.context) { diff --git a/src/wl_window.c b/src/wl_window.c index b00b8ced..1efb7256 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1942,6 +1942,13 @@ static void seatHandleCapabilities(void* userData, { _glfw.wl.keyboard = wl_seat_get_keyboard(seat); wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL); + + if (wl_keyboard_get_version(_glfw.wl.keyboard) < + WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) + { + _glfw.wl.keyRepeatRate = 4; + _glfw.wl.keyRepeatDelay = 500; + } } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard) { From a3095e1aa594062616a1bfc76005011d8123d377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 5 Feb 2026 22:29:50 +0100 Subject: [PATCH 03/10] X11: Fix libXext not unloaded at termination --- README.md | 1 + src/x11_init.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/README.md b/README.md index a5689a5c..29bd91f7 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ information on what to include when reporting a bug. less than 1 (#2754) - [X11] Bugfix: Clamp width and height to >= 1 to prevent BadValue error and app exit - [X11] Bugfix: Floating windows silently became non-floating when hidden (#2276) + - [X11] Bugfix: The `libXext` library was not unloaded at termination - [Linux] Bugfix: The header for `ioctl` was only implicitly included (#2778) - [Null] Added Vulkan 'window' surface creation via `VK_EXT_headless_surface` - [Null] Added EGL context creation on Mesa via `EGL_MESA_platform_surfaceless` diff --git a/src/x11_init.c b/src/x11_init.c index 6b34c263..5f760f43 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -1622,6 +1622,12 @@ void _glfwTerminateX11(void) _glfw.x11.xrender.handle = NULL; } + if (_glfw.x11.xshape.handle) + { + _glfwPlatformFreeModule(_glfw.x11.xshape.handle); + _glfw.x11.xshape.handle = NULL; + } + if (_glfw.x11.vidmode.handle) { _glfwPlatformFreeModule(_glfw.x11.vidmode.handle); From b579ea6792fe882394bf6cb615397d15db29a945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 5 Feb 2026 22:30:20 +0100 Subject: [PATCH 04/10] Wayland: Fix libwayland-client not unloaded --- README.md | 1 + src/wl_init.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/README.md b/README.md index 29bd91f7..518d31cb 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,7 @@ information on what to include when reporting a bug. - [Wayland] Bugfix: Confining or disabling the cursor could segfault on compositors without `pointer-constraints-unstable-v1` - [Wayland] Bugfix: Key repeat did not function on very old compositors + - [Wayland] Bugfix: The `libwayland-client` library was not unloaded at termination - [X11] Bugfix: Running without a WM could trigger an assert (#2593,#2601,#2631) - [X11] Bugfix: Occasional crash when an idle display awakes (#2766) - [X11] Bugfix: Prevent BadWindow when creating small windows with a content scale diff --git a/src/wl_init.c b/src/wl_init.c index 41cbc57c..025f7746 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -1014,6 +1014,12 @@ void _glfwTerminateWayland(void) _glfw.wl.cursor.handle = NULL; } + if (_glfw.wl.client.handle) + { + _glfwPlatformFreeModule(_glfw.wl.client.handle); + _glfw.wl.client.handle = NULL; + } + _glfw_free(_glfw.wl.clipboardString); } From f0b2992b3ae579f808f3fc2ede5292d009e60b5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 4 Dec 2025 15:33:24 +0100 Subject: [PATCH 05/10] Allow NULL argument to _glfwPlatformFreeModule This matches the behavior of _glfw_free and the public API, simplifying cleanup code. --- src/cocoa_init.m | 2 ++ src/egl_context.c | 9 +++---- src/glx_context.c | 7 ++--- src/null_init.c | 1 + src/osmesa_context.c | 7 ++--- src/posix_module.c | 3 ++- src/vulkan.c | 4 +-- src/wgl_context.c | 4 +-- src/win32_init.c | 32 ++++++---------------- src/win32_module.c | 3 ++- src/wl_init.c | 43 ++++++----------------------- src/x11_init.c | 64 ++++++++------------------------------------ 12 files changed, 45 insertions(+), 134 deletions(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 15dc4ec4..531d8b45 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -688,6 +688,8 @@ void _glfwTerminateCocoa(void) _glfwTerminateEGL(); _glfwTerminateOSMesa(); + memset(&_glfw.ns, 0, sizeof(_glfw.ns)); + } // autoreleasepool } diff --git a/src/egl_context.c b/src/egl_context.c index 46b098f5..4e59d78b 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -333,11 +333,8 @@ static void destroyContextEGL(_GLFWwindow* window) if (_glfw.platform.platformID != GLFW_PLATFORM_X11 || window->context.client != GLFW_OPENGL_API) { - if (window->context.egl.client) - { - _glfwPlatformFreeModule(window->context.egl.client); - window->context.egl.client = NULL; - } + _glfwPlatformFreeModule(window->context.egl.client); + window->context.egl.client = NULL; } if (window->context.egl.surface) @@ -552,7 +549,7 @@ void _glfwTerminateEGL(void) } // Free modules only after all wayland termination functions are called - if (_glfw.egl.handle && _glfw.platform.platformID != GLFW_PLATFORM_WAYLAND) + if (_glfw.platform.platformID != GLFW_PLATFORM_WAYLAND) { _glfwPlatformFreeModule(_glfw.egl.handle); _glfw.egl.handle = NULL; diff --git a/src/glx_context.c b/src/glx_context.c index 84fca9dd..f08cdd5b 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -429,11 +429,8 @@ void _glfwTerminateGLX(void) // NOTE: This function must not call any X11 functions, as it is called // after XCloseDisplay (see _glfwTerminateX11 for details) - if (_glfw.glx.handle) - { - _glfwPlatformFreeModule(_glfw.glx.handle); - _glfw.glx.handle = NULL; - } + _glfwPlatformFreeModule(_glfw.glx.handle); + _glfw.glx.handle = NULL; } #define SET_ATTRIB(a, v) \ diff --git a/src/null_init.c b/src/null_init.c index 8c10f5e6..fd0d9e96 100644 --- a/src/null_init.c +++ b/src/null_init.c @@ -260,5 +260,6 @@ void _glfwTerminateNull(void) free(_glfw.null.clipboardString); _glfwTerminateOSMesa(); _glfwTerminateEGL(); + memset(&_glfw.null, 0, sizeof(_glfw.null)); } diff --git a/src/osmesa_context.c b/src/osmesa_context.c index 0bb375a8..c7a4c868 100644 --- a/src/osmesa_context.c +++ b/src/osmesa_context.c @@ -180,11 +180,8 @@ GLFWbool _glfwInitOSMesa(void) void _glfwTerminateOSMesa(void) { - if (_glfw.osmesa.handle) - { - _glfwPlatformFreeModule(_glfw.osmesa.handle); - _glfw.osmesa.handle = NULL; - } + _glfwPlatformFreeModule(_glfw.osmesa.handle); + _glfw.osmesa.handle = NULL; } #define SET_ATTRIB(a, v) \ diff --git a/src/posix_module.c b/src/posix_module.c index b3482d26..de99bc69 100644 --- a/src/posix_module.c +++ b/src/posix_module.c @@ -41,7 +41,8 @@ void* _glfwPlatformLoadModule(const char* path) void _glfwPlatformFreeModule(void* module) { - dlclose(module); + if (module) + dlclose(module); } GLFWproc _glfwPlatformGetModuleSymbol(void* module, const char* name) diff --git a/src/vulkan.c b/src/vulkan.c index 9c87fcfe..8f25f5ee 100644 --- a/src/vulkan.c +++ b/src/vulkan.c @@ -157,8 +157,8 @@ GLFWbool _glfwInitVulkan(int mode) void _glfwTerminateVulkan(void) { - if (_glfw.vk.handle) - _glfwPlatformFreeModule(_glfw.vk.handle); + _glfwPlatformFreeModule(_glfw.vk.handle); + _glfw.vk.handle = NULL; } const char* _glfwGetVulkanResultString(VkResult result) diff --git a/src/wgl_context.c b/src/wgl_context.c index 04429066..1fb7d322 100644 --- a/src/wgl_context.c +++ b/src/wgl_context.c @@ -521,8 +521,8 @@ GLFWbool _glfwInitWGL(void) void _glfwTerminateWGL(void) { - if (_glfw.wgl.instance) - _glfwPlatformFreeModule(_glfw.wgl.instance); + _glfwPlatformFreeModule(_glfw.wgl.instance); + _glfw.wgl.instance = NULL; } #define SET_ATTRIB(a, v) \ diff --git a/src/win32_init.c b/src/win32_init.c index 90e47670..d526fe8a 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -166,29 +166,6 @@ static GLFWbool loadLibraries(void) return GLFW_TRUE; } -// Unload used libraries (DLLs) -// -static void freeLibraries(void) -{ - if (_glfw.win32.xinput.instance) - _glfwPlatformFreeModule(_glfw.win32.xinput.instance); - - if (_glfw.win32.dinput8.instance) - _glfwPlatformFreeModule(_glfw.win32.dinput8.instance); - - if (_glfw.win32.user32.instance) - _glfwPlatformFreeModule(_glfw.win32.user32.instance); - - if (_glfw.win32.dwmapi.instance) - _glfwPlatformFreeModule(_glfw.win32.dwmapi.instance); - - if (_glfw.win32.shcore.instance) - _glfwPlatformFreeModule(_glfw.win32.shcore.instance); - - if (_glfw.win32.ntdll.instance) - _glfwPlatformFreeModule(_glfw.win32.ntdll.instance); -} - // Create key code translation tables // static void createKeyTables(void) @@ -721,7 +698,14 @@ void _glfwTerminateWin32(void) _glfwTerminateEGL(); _glfwTerminateOSMesa(); - freeLibraries(); + _glfwPlatformFreeModule(_glfw.win32.xinput.instance); + _glfwPlatformFreeModule(_glfw.win32.dinput8.instance); + _glfwPlatformFreeModule(_glfw.win32.user32.instance); + _glfwPlatformFreeModule(_glfw.win32.dwmapi.instance); + _glfwPlatformFreeModule(_glfw.win32.shcore.instance); + _glfwPlatformFreeModule(_glfw.win32.ntdll.instance); + + memset(&_glfw.win32, 0, sizeof(_glfw.win32)); } #endif // _GLFW_WIN32 diff --git a/src/win32_module.c b/src/win32_module.c index b76599b6..ca5ce9cf 100644 --- a/src/win32_module.c +++ b/src/win32_module.c @@ -39,7 +39,8 @@ void* _glfwPlatformLoadModule(const char* path) void _glfwPlatformFreeModule(void* module) { - FreeLibrary((HMODULE) module); + if (module) + FreeLibrary((HMODULE) module); } GLFWproc _glfwPlatformGetModuleSymbol(void* module, const char* name) diff --git a/src/wl_init.c b/src/wl_init.c index 025f7746..b2fd6a88 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -984,43 +984,16 @@ void _glfwTerminateWayland(void) // Free modules only after all Wayland termination functions are called - if (_glfw.egl.handle) - { - _glfwPlatformFreeModule(_glfw.egl.handle); - _glfw.egl.handle = NULL; - } - - if (_glfw.wl.libdecor.handle) - { - _glfwPlatformFreeModule(_glfw.wl.libdecor.handle); - _glfw.wl.libdecor.handle = NULL; - } - - if (_glfw.wl.egl.handle) - { - _glfwPlatformFreeModule(_glfw.wl.egl.handle); - _glfw.wl.egl.handle = NULL; - } - - if (_glfw.wl.xkb.handle) - { - _glfwPlatformFreeModule(_glfw.wl.xkb.handle); - _glfw.wl.xkb.handle = NULL; - } - - if (_glfw.wl.cursor.handle) - { - _glfwPlatformFreeModule(_glfw.wl.cursor.handle); - _glfw.wl.cursor.handle = NULL; - } - - if (_glfw.wl.client.handle) - { - _glfwPlatformFreeModule(_glfw.wl.client.handle); - _glfw.wl.client.handle = NULL; - } + _glfwPlatformFreeModule(_glfw.egl.handle); + _glfwPlatformFreeModule(_glfw.wl.libdecor.handle); + _glfwPlatformFreeModule(_glfw.wl.egl.handle); + _glfwPlatformFreeModule(_glfw.wl.xkb.handle); + _glfwPlatformFreeModule(_glfw.wl.cursor.handle); + _glfwPlatformFreeModule(_glfw.wl.client.handle); _glfw_free(_glfw.wl.clipboardString); + + memset(&_glfw.wl, 0, sizeof(_glfw.wl)); } #endif // _GLFW_WAYLAND diff --git a/src/x11_init.c b/src/x11_init.c index 5f760f43..b0acbf7f 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -1592,71 +1592,29 @@ void _glfwTerminateX11(void) _glfw.x11.display = NULL; } - if (_glfw.x11.x11xcb.handle) - { - _glfwPlatformFreeModule(_glfw.x11.x11xcb.handle); - _glfw.x11.x11xcb.handle = NULL; - } - - if (_glfw.x11.xcursor.handle) - { - _glfwPlatformFreeModule(_glfw.x11.xcursor.handle); - _glfw.x11.xcursor.handle = NULL; - } - - if (_glfw.x11.randr.handle) - { - _glfwPlatformFreeModule(_glfw.x11.randr.handle); - _glfw.x11.randr.handle = NULL; - } - - if (_glfw.x11.xinerama.handle) - { - _glfwPlatformFreeModule(_glfw.x11.xinerama.handle); - _glfw.x11.xinerama.handle = NULL; - } - - if (_glfw.x11.xrender.handle) - { - _glfwPlatformFreeModule(_glfw.x11.xrender.handle); - _glfw.x11.xrender.handle = NULL; - } - - if (_glfw.x11.xshape.handle) - { - _glfwPlatformFreeModule(_glfw.x11.xshape.handle); - _glfw.x11.xshape.handle = NULL; - } - - if (_glfw.x11.vidmode.handle) - { - _glfwPlatformFreeModule(_glfw.x11.vidmode.handle); - _glfw.x11.vidmode.handle = NULL; - } - - if (_glfw.x11.xi.handle) - { - _glfwPlatformFreeModule(_glfw.x11.xi.handle); - _glfw.x11.xi.handle = NULL; - } - _glfwTerminateOSMesa(); // NOTE: These need to be unloaded after XCloseDisplay, as they register // cleanup callbacks that get called by that function _glfwTerminateEGL(); _glfwTerminateGLX(); - if (_glfw.x11.xlib.handle) - { - _glfwPlatformFreeModule(_glfw.x11.xlib.handle); - _glfw.x11.xlib.handle = NULL; - } + _glfwPlatformFreeModule(_glfw.x11.x11xcb.handle); + _glfwPlatformFreeModule(_glfw.x11.xcursor.handle); + _glfwPlatformFreeModule(_glfw.x11.randr.handle); + _glfwPlatformFreeModule(_glfw.x11.xinerama.handle); + _glfwPlatformFreeModule(_glfw.x11.xrender.handle); + _glfwPlatformFreeModule(_glfw.x11.xshape.handle); + _glfwPlatformFreeModule(_glfw.x11.vidmode.handle); + _glfwPlatformFreeModule(_glfw.x11.xi.handle); + _glfwPlatformFreeModule(_glfw.x11.xlib.handle); if (_glfw.x11.emptyEventPipe[0] || _glfw.x11.emptyEventPipe[1]) { close(_glfw.x11.emptyEventPipe[0]); close(_glfw.x11.emptyEventPipe[1]); } + + memset(&_glfw.x11, 0, sizeof(_glfw.x11)); } #endif // _GLFW_X11 From 3f14b6feed2b66a70c08e73d5e17afa659b55648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 12 Feb 2026 16:04:00 +0100 Subject: [PATCH 06/10] EGL: Try eglGetProcAddress before fallback method This order matches what is done for every other context creation API that has a similar fallback method. --- src/egl_context.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/egl_context.c b/src/egl_context.c index 4e59d78b..9aec5a2a 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -312,18 +312,19 @@ static int extensionSupportedEGL(const char* extension) static GLFWglproc getProcAddressEGL(const char* procname) { - _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); - assert(window != NULL); + const GLFWglproc proc = (GLFWglproc) eglGetProcAddress(procname); + if (proc) + return proc; - if (window->context.egl.client) + if (!_glfw.egl.KHR_get_all_proc_addresses) { - GLFWglproc proc = (GLFWglproc) - _glfwPlatformGetModuleSymbol(window->context.egl.client, procname); - if (proc) - return proc; + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); + assert(window != NULL); + + return _glfwPlatformGetModuleSymbol(window->context.egl.client, procname); } - return eglGetProcAddress(procname); + return NULL; } static void destroyContextEGL(_GLFWwindow* window) From 001f94eaa7b47d84cb64d721fbd24ab4cc44ef4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 1 Sep 2025 17:13:05 +0200 Subject: [PATCH 07/10] Wayland: Add support for pointer event frames This adds support for wl_seat version 5, which adds pointer frames. This allows two-dimensional scroll input to be emitted as single events instead of one event per axis per motion. It's also the the foundation for future enhancements like properly scaled discrete scroll events and server-side key repeat. Fixes #2494 --- CONTRIBUTORS.md | 1 + README.md | 2 + src/wl_init.c | 2 +- src/wl_platform.h | 14 ++- src/wl_window.c | 268 ++++++++++++++++++++++++++++++++++++---------- 5 files changed, 230 insertions(+), 57 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 031962ec..7a544eb6 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -79,6 +79,7 @@ video tutorials. - Jason Francis - Gerald Franz - Mário Freitas + - Friz64 - GeO4d - Marcus Geelnard - Gegy diff --git a/README.md b/README.md index 518d31cb..ab346ae6 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,8 @@ information on what to include when reporting a bug. compositors without `pointer-constraints-unstable-v1` - [Wayland] Bugfix: Key repeat did not function on very old compositors - [Wayland] Bugfix: The `libwayland-client` library was not unloaded at termination + - [Wayland] Bugfix: Scroll events were sent twice on some versions of GNOME (#2494) + - [Wayland] Bugfix: Two-dimensional scroll input was emitted as separate axes - [X11] Bugfix: Running without a WM could trigger an assert (#2593,#2601,#2631) - [X11] Bugfix: Occasional crash when an idle display awakes (#2766) - [X11] Bugfix: Prevent BadWindow when creating small windows with a content scale diff --git a/src/wl_init.c b/src/wl_init.c index b2fd6a88..1e527649 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -135,7 +135,7 @@ static void registryHandleGlobal(void* userData, { _glfw.wl.seat = wl_registry_bind(registry, name, &wl_seat_interface, - _glfw_min(4, version)); + _glfw_min(5, version)); _glfwAddSeatListenerWayland(_glfw.wl.seat); } } diff --git a/src/wl_platform.h b/src/wl_platform.h index c2ddb732..c9d1c251 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -414,7 +414,8 @@ typedef struct _GLFWwindowWayland GLFWbool decorations; struct wl_buffer* buffer; _GLFWfallbackEdgeWayland top, left, right, bottom; - wl_fixed_t pointerX, pointerY; + double pointerX, pointerY; + uint32_t buttonPressSerial; const char* cursorName; } fallback; } _GLFWwindowWayland; @@ -472,6 +473,17 @@ typedef struct _GLFWlibraryWayland short int scancodes[GLFW_KEY_LAST + 1]; char keynames[GLFW_KEY_LAST + 1][5]; + struct { + struct wl_surface* pointerSurface; + unsigned int events; + double pointerX; + double pointerY; + double scrollX; + double scrollY; + int button; + int action; + } pending; + struct { void* handle; struct xkb_context* context; diff --git a/src/wl_window.c b/src/wl_window.c index 1efb7256..f089f243 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -55,6 +55,11 @@ #define GLFW_BORDER_SIZE 4 #define GLFW_CAPTION_HEIGHT 24 +#define GLFW_PENDING_SURFACE 1 +#define GLFW_PENDING_BUTTON 2 +#define GLFW_PENDING_MOTION 4 +#define GLFW_PENDING_SCROLL 8 + static int createTmpfileCloexec(char* tmpname) { int fd; @@ -278,15 +283,11 @@ static void destroyFallbackDecorations(_GLFWwindow* window) destroyFallbackEdge(&window->wl.fallback.bottom); } -static void updateFallbackDecorationCursor(_GLFWwindow* window, - wl_fixed_t sx, - wl_fixed_t sy) +static void updateFallbackDecorationCursor(_GLFWwindow* window, double xpos, double ypos) { - window->wl.fallback.pointerX = sx; - window->wl.fallback.pointerY = sy; + window->wl.fallback.pointerX = xpos; + window->wl.fallback.pointerY = ypos; - const double xpos = wl_fixed_to_double(sx); - const double ypos = wl_fixed_to_double(sy); const char* cursorName = "left_ptr"; if (window->resizable) @@ -361,18 +362,16 @@ static void updateFallbackDecorationCursor(_GLFWwindow* window, } } -static void handleFallbackDecorationButton(_GLFWwindow* window, - uint32_t serial, - uint32_t button, - uint32_t state) +static void handleFallbackDecorationButton(_GLFWwindow* window, int button, int action) { - if (state != WL_POINTER_BUTTON_STATE_PRESSED) + if (action != GLFW_PRESS) return; - const double xpos = wl_fixed_to_double(window->wl.fallback.pointerX); - const double ypos = wl_fixed_to_double(window->wl.fallback.pointerY); + const double xpos = window->wl.fallback.pointerX; + const double ypos = window->wl.fallback.pointerY; + const uint32_t serial = window->wl.fallback.buttonPressSerial; - if (button == BTN_LEFT) + if (button == GLFW_MOUSE_BUTTON_LEFT) { uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE; @@ -410,7 +409,7 @@ static void handleFallbackDecorationButton(_GLFWwindow* window, if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE) xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat, serial, edges); } - else if (button == BTN_RIGHT) + else if (button == GLFW_MOUSE_BUTTON_RIGHT) { if (!window->wl.xdg.toplevel) return; @@ -421,10 +420,8 @@ static void handleFallbackDecorationButton(_GLFWwindow* window, 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); + xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, _glfw.wl.seat, serial, + xpos, ypos - GLFW_CAPTION_HEIGHT - GLFW_BORDER_SIZE); } } @@ -1538,25 +1535,39 @@ static void pointerHandleEnter(void* userData, _glfw.wl.serial = serial; _glfw.wl.pointerEnterSerial = serial; - _glfw.wl.pointerSurface = surface; _GLFWwindow* window = wl_surface_get_user_data(surface); - if (window->wl.surface == surface) - { - _glfwSetCursorWayland(window, window->cursor); - _glfwInputCursorEnter(window, GLFW_TRUE); + const double xpos = wl_fixed_to_double(sx); + const double ypos = wl_fixed_to_double(sy); - 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); - } + if (wl_pointer_get_version(pointer) >= WL_POINTER_FRAME_SINCE_VERSION) + { + _glfw.wl.pending.events |= (GLFW_PENDING_SURFACE | GLFW_PENDING_MOTION); + _glfw.wl.pending.pointerSurface = surface; + _glfw.wl.pending.pointerX = xpos; + _glfw.wl.pending.pointerY = ypos; } else { - if (window->wl.fallback.decorations) - updateFallbackDecorationCursor(window, sx, sy); + _glfw.wl.pointerSurface = surface; + + if (window->wl.surface == _glfw.wl.pointerSurface) + { + _glfwSetCursorWayland(window, window->cursor); + _glfwInputCursorEnter(window, GLFW_TRUE); + + if (window->cursorMode != GLFW_CURSOR_DISABLED) + { + window->wl.cursorPosX = xpos; + window->wl.cursorPosY = ypos; + _glfwInputCursorPos(window, window->wl.cursorPosX, window->wl.cursorPosY); + } + } + else + { + if (window->wl.fallback.decorations) + updateFallbackDecorationCursor(window, xpos, ypos); + } } } @@ -1572,15 +1583,25 @@ static void pointerHandleLeave(void* userData, return; _glfw.wl.serial = serial; - _glfw.wl.pointerSurface = NULL; _GLFWwindow* window = wl_surface_get_user_data(surface); - if (window->wl.surface == surface) - _glfwInputCursorEnter(window, GLFW_FALSE); + + if (wl_pointer_get_version(pointer) >= WL_POINTER_FRAME_SINCE_VERSION) + { + _glfw.wl.pending.events |= GLFW_PENDING_SURFACE; + _glfw.wl.pending.pointerSurface = NULL; + } else { - if (window->wl.fallback.decorations) - window->wl.fallback.cursorName = NULL; + _glfw.wl.pointerSurface = NULL; + + if (window->wl.surface == surface) + _glfwInputCursorEnter(window, GLFW_FALSE); + else + { + if (window->wl.fallback.decorations) + window->wl.fallback.cursorName = NULL; + } } } @@ -1594,20 +1615,28 @@ static void pointerHandleMotion(void* userData, return; _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); - if (window->cursorMode == GLFW_CURSOR_DISABLED) return; - if (window->wl.surface == _glfw.wl.pointerSurface) + const double xpos = wl_fixed_to_double(sx); + const double ypos = wl_fixed_to_double(sy); + + if (wl_pointer_get_version(pointer) >= WL_POINTER_FRAME_SINCE_VERSION) { - window->wl.cursorPosX = wl_fixed_to_double(sx); - window->wl.cursorPosY = wl_fixed_to_double(sy); - _glfwInputCursorPos(window, window->wl.cursorPosX, window->wl.cursorPosY); + _glfw.wl.pending.events |= GLFW_PENDING_MOTION; + _glfw.wl.pending.pointerX = xpos; + _glfw.wl.pending.pointerY = ypos; } else { - if (window->wl.fallback.decorations) - updateFallbackDecorationCursor(window, sx, sy); + if (window->wl.surface == _glfw.wl.pointerSurface) + { + window->wl.cursorPosX = xpos; + window->wl.cursorPosY = ypos; + _glfwInputCursorPos(window, window->wl.cursorPosX, window->wl.cursorPosY); + } + else + updateFallbackDecorationCursor(window, xpos, ypos); } } @@ -1615,27 +1644,39 @@ static void pointerHandleButton(void* userData, struct wl_pointer* pointer, uint32_t serial, uint32_t time, - uint32_t button, + uint32_t buttonID, uint32_t state) { if (!_glfw.wl.pointerSurface) return; + _glfw.wl.serial = serial; + _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); + const int button = buttonID - BTN_LEFT; + const int action = (state == WL_POINTER_BUTTON_STATE_PRESSED); - if (window->wl.surface == _glfw.wl.pointerSurface) + if (window->wl.fallback.decorations) { - _glfw.wl.serial = serial; + if (action == GLFW_PRESS) + window->wl.fallback.buttonPressSerial = serial; + } - _glfwInputMouseClick(window, - button - BTN_LEFT, - state == WL_POINTER_BUTTON_STATE_PRESSED, - _glfw.wl.xkb.modifiers); + if (wl_pointer_get_version(pointer) >= WL_POINTER_FRAME_SINCE_VERSION) + { + _glfw.wl.pending.events |= GLFW_PENDING_BUTTON; + _glfw.wl.pending.button = button; + _glfw.wl.pending.action = action; } else { - if (window->wl.fallback.decorations) - handleFallbackDecorationButton(window, serial, button, state); + if (window->wl.surface == _glfw.wl.pointerSurface) + _glfwInputMouseClick(window, button, action, _glfw.wl.xkb.modifiers); + else + { + if (window->wl.fallback.decorations) + handleFallbackDecorationButton(window, button, action); + } } } @@ -1649,8 +1690,18 @@ static void pointerHandleAxis(void* userData, return; _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); + if (window->wl.surface != _glfw.wl.pointerSurface) + return; - if (window->wl.surface == _glfw.wl.pointerSurface) + if (wl_pointer_get_version(pointer) >= WL_POINTER_FRAME_SINCE_VERSION) + { + _glfw.wl.pending.events |= GLFW_PENDING_SCROLL; + if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) + _glfw.wl.pending.scrollX = -wl_fixed_to_double(value) / 10.0; + else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) + _glfw.wl.pending.scrollY = -wl_fixed_to_double(value) / 10.0; + } + else { // NOTE: 10 units of motion per mouse wheel step seems to be a common ratio if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) @@ -1660,6 +1711,109 @@ static void pointerHandleAxis(void* userData, } } +static void pointerHandleFrame(void* userData, struct wl_pointer* pointer) +{ + if (_glfw.wl.pending.events & GLFW_PENDING_SURFACE) + { + if (_glfw.wl.pointerSurface) + { + _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); + if (window->wl.surface == _glfw.wl.pointerSurface) + _glfwInputCursorEnter(window, GLFW_FALSE); + else + { + if (window->wl.fallback.decorations) + window->wl.fallback.cursorName = NULL; + } + } + + _glfw.wl.pointerSurface = _glfw.wl.pending.pointerSurface; + + if (_glfw.wl.pointerSurface) + { + _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); + if (window->wl.surface == _glfw.wl.pointerSurface) + { + _glfwSetCursorWayland(window, window->cursor); + _glfwInputCursorEnter(window, GLFW_TRUE); + } + } + } + + if (!_glfw.wl.pointerSurface) + return; + + _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); + + if (_glfw.wl.pending.events & GLFW_PENDING_MOTION) + { + if (window->wl.surface == _glfw.wl.pointerSurface) + { + window->wl.cursorPosX = _glfw.wl.pending.pointerX; + window->wl.cursorPosY = _glfw.wl.pending.pointerY; + _glfwInputCursorPos(window, window->wl.cursorPosX, window->wl.cursorPosY); + } + else + { + updateFallbackDecorationCursor(window, + _glfw.wl.pending.pointerX, + _glfw.wl.pending.pointerY); + } + } + + if (_glfw.wl.pending.events & GLFW_PENDING_BUTTON) + { + if (window->wl.surface == _glfw.wl.pointerSurface) + { + _glfwInputMouseClick(window, + _glfw.wl.pending.button, + _glfw.wl.pending.action, + _glfw.wl.xkb.modifiers); + } + else + { + if (window->wl.fallback.decorations) + { + handleFallbackDecorationButton(window, + _glfw.wl.pending.button, + _glfw.wl.pending.action); + } + } + } + + if (_glfw.wl.pending.events & GLFW_PENDING_SCROLL) + { + if (window->wl.surface == _glfw.wl.pointerSurface) + { + _glfwInputScroll(window, + _glfw.wl.pending.scrollX, + _glfw.wl.pending.scrollY); + } + } + + memset(&_glfw.wl.pending, 0, sizeof(_glfw.wl.pending)); +} + +static void pointerHandleAxisSource(void* userData, + struct wl_pointer* pointer, + uint32_t axisSource) +{ +} + +static void pointerHandleAxisStop(void* userData, + struct wl_pointer* pointer, + uint32_t time, + uint32_t axis) +{ +} + +static void pointerHandleAxisDiscrete(void* userData, + struct wl_pointer* pointer, + uint32_t axis, + int32_t discrete) +{ +} + static const struct wl_pointer_listener pointerListener = { pointerHandleEnter, @@ -1667,6 +1821,10 @@ static const struct wl_pointer_listener pointerListener = pointerHandleMotion, pointerHandleButton, pointerHandleAxis, + pointerHandleFrame, + pointerHandleAxisSource, + pointerHandleAxisStop, + pointerHandleAxisDiscrete }; static void keyboardHandleKeymap(void* userData, From 99cdcfb0a4265fab866da889c241c35d800116a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 30 Jan 2026 16:13:58 +0100 Subject: [PATCH 08/10] Wayland: Unify pointer input paths This reduces pointer frame support to a small amount of plumbing. --- src/wl_window.c | 204 ++++++++++++++++++------------------------------ 1 file changed, 78 insertions(+), 126 deletions(-) diff --git a/src/wl_window.c b/src/wl_window.c index f089f243..dd097cf9 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1519,6 +1519,70 @@ static char* readDataOfferAsString(struct wl_data_offer* offer, const char* mime return string; } +static void processPointerEnterSurface(struct wl_surface* surface) +{ + _glfw.wl.pointerSurface = surface; + + _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); + if (window->wl.surface == _glfw.wl.pointerSurface) + { + _glfwSetCursorWayland(window, window->cursor); + _glfwInputCursorEnter(window, GLFW_TRUE); + } +} + +static void processPointerLeaveSurface(struct wl_surface* surface) +{ + _glfw.wl.pointerSurface = NULL; + + _GLFWwindow* window = wl_surface_get_user_data(surface); + if (window->wl.surface == surface) + _glfwInputCursorEnter(window, GLFW_FALSE); + else + { + if (window->wl.fallback.decorations) + window->wl.fallback.cursorName = NULL; + } +} + +static void processPointerMotion(double xpos, double ypos) +{ + _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); + if (window->wl.surface == _glfw.wl.pointerSurface) + { + if (window->cursorMode != GLFW_CURSOR_DISABLED) + { + window->wl.cursorPosX = xpos; + window->wl.cursorPosY = ypos; + _glfwInputCursorPos(window, window->wl.cursorPosX, window->wl.cursorPosY); + } + } + else + { + if (window->wl.fallback.decorations) + updateFallbackDecorationCursor(window, xpos, ypos); + } +} + +static void processPointerButton(int button, int action) +{ + _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); + if (window->wl.surface == _glfw.wl.pointerSurface) + _glfwInputMouseClick(window, button, action, _glfw.wl.xkb.modifiers); + else + { + if (window->wl.fallback.decorations) + handleFallbackDecorationButton(window, button, action); + } +} + +static void processPointerScroll(double xoffset, double yoffset) +{ + _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); + if (window->wl.surface == _glfw.wl.pointerSurface) + _glfwInputScroll(window, xoffset, yoffset); +} + static void pointerHandleEnter(void* userData, struct wl_pointer* pointer, uint32_t serial, @@ -1536,7 +1600,6 @@ static void pointerHandleEnter(void* userData, _glfw.wl.serial = serial; _glfw.wl.pointerEnterSerial = serial; - _GLFWwindow* window = wl_surface_get_user_data(surface); const double xpos = wl_fixed_to_double(sx); const double ypos = wl_fixed_to_double(sy); @@ -1549,25 +1612,8 @@ static void pointerHandleEnter(void* userData, } else { - _glfw.wl.pointerSurface = surface; - - if (window->wl.surface == _glfw.wl.pointerSurface) - { - _glfwSetCursorWayland(window, window->cursor); - _glfwInputCursorEnter(window, GLFW_TRUE); - - if (window->cursorMode != GLFW_CURSOR_DISABLED) - { - window->wl.cursorPosX = xpos; - window->wl.cursorPosY = ypos; - _glfwInputCursorPos(window, window->wl.cursorPosX, window->wl.cursorPosY); - } - } - else - { - if (window->wl.fallback.decorations) - updateFallbackDecorationCursor(window, xpos, ypos); - } + processPointerEnterSurface(surface); + processPointerMotion(xpos, ypos); } } @@ -1584,25 +1630,13 @@ static void pointerHandleLeave(void* userData, _glfw.wl.serial = serial; - _GLFWwindow* window = wl_surface_get_user_data(surface); - if (wl_pointer_get_version(pointer) >= WL_POINTER_FRAME_SINCE_VERSION) { _glfw.wl.pending.events |= GLFW_PENDING_SURFACE; _glfw.wl.pending.pointerSurface = NULL; } else - { - _glfw.wl.pointerSurface = NULL; - - if (window->wl.surface == surface) - _glfwInputCursorEnter(window, GLFW_FALSE); - else - { - if (window->wl.fallback.decorations) - window->wl.fallback.cursorName = NULL; - } - } + processPointerLeaveSurface(surface); } static void pointerHandleMotion(void* userData, @@ -1614,10 +1648,6 @@ static void pointerHandleMotion(void* userData, if (!_glfw.wl.pointerSurface) return; - _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); - if (window->cursorMode == GLFW_CURSOR_DISABLED) - return; - const double xpos = wl_fixed_to_double(sx); const double ypos = wl_fixed_to_double(sy); @@ -1628,16 +1658,7 @@ static void pointerHandleMotion(void* userData, _glfw.wl.pending.pointerY = ypos; } else - { - if (window->wl.surface == _glfw.wl.pointerSurface) - { - window->wl.cursorPosX = xpos; - window->wl.cursorPosY = ypos; - _glfwInputCursorPos(window, window->wl.cursorPosX, window->wl.cursorPosY); - } - else - updateFallbackDecorationCursor(window, xpos, ypos); - } + processPointerMotion(xpos, ypos); } static void pointerHandleButton(void* userData, @@ -1652,10 +1673,10 @@ static void pointerHandleButton(void* userData, _glfw.wl.serial = serial; - _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); const int button = buttonID - BTN_LEFT; const int action = (state == WL_POINTER_BUTTON_STATE_PRESSED); + _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); if (window->wl.fallback.decorations) { if (action == GLFW_PRESS) @@ -1669,15 +1690,7 @@ static void pointerHandleButton(void* userData, _glfw.wl.pending.action = action; } else - { - if (window->wl.surface == _glfw.wl.pointerSurface) - _glfwInputMouseClick(window, button, action, _glfw.wl.xkb.modifiers); - else - { - if (window->wl.fallback.decorations) - handleFallbackDecorationButton(window, button, action); - } - } + processPointerButton(button, action); } static void pointerHandleAxis(void* userData, @@ -1689,10 +1702,6 @@ static void pointerHandleAxis(void* userData, if (!_glfw.wl.pointerSurface) return; - _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); - if (window->wl.surface != _glfw.wl.pointerSurface) - return; - if (wl_pointer_get_version(pointer) >= WL_POINTER_FRAME_SINCE_VERSION) { _glfw.wl.pending.events |= GLFW_PENDING_SCROLL; @@ -1705,9 +1714,9 @@ static void pointerHandleAxis(void* userData, { // NOTE: 10 units of motion per mouse wheel step seems to be a common ratio if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) - _glfwInputScroll(window, -wl_fixed_to_double(value) / 10.0, 0.0); + processPointerScroll(-wl_fixed_to_double(value) / 10.0, 0.0); else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) - _glfwInputScroll(window, 0.0, -wl_fixed_to_double(value) / 10.0); + processPointerScroll(0.0, -wl_fixed_to_double(value) / 10.0); } } @@ -1716,80 +1725,23 @@ static void pointerHandleFrame(void* userData, struct wl_pointer* pointer) if (_glfw.wl.pending.events & GLFW_PENDING_SURFACE) { if (_glfw.wl.pointerSurface) - { - _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); - if (window->wl.surface == _glfw.wl.pointerSurface) - _glfwInputCursorEnter(window, GLFW_FALSE); - else - { - if (window->wl.fallback.decorations) - window->wl.fallback.cursorName = NULL; - } - } + processPointerLeaveSurface(_glfw.wl.pointerSurface); - _glfw.wl.pointerSurface = _glfw.wl.pending.pointerSurface; - - if (_glfw.wl.pointerSurface) - { - _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); - if (window->wl.surface == _glfw.wl.pointerSurface) - { - _glfwSetCursorWayland(window, window->cursor); - _glfwInputCursorEnter(window, GLFW_TRUE); - } - } + if (_glfw.wl.pending.pointerSurface) + processPointerEnterSurface(_glfw.wl.pending.pointerSurface); } if (!_glfw.wl.pointerSurface) return; - _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface); - if (_glfw.wl.pending.events & GLFW_PENDING_MOTION) - { - if (window->wl.surface == _glfw.wl.pointerSurface) - { - window->wl.cursorPosX = _glfw.wl.pending.pointerX; - window->wl.cursorPosY = _glfw.wl.pending.pointerY; - _glfwInputCursorPos(window, window->wl.cursorPosX, window->wl.cursorPosY); - } - else - { - updateFallbackDecorationCursor(window, - _glfw.wl.pending.pointerX, - _glfw.wl.pending.pointerY); - } - } + processPointerMotion(_glfw.wl.pending.pointerX, _glfw.wl.pending.pointerY); if (_glfw.wl.pending.events & GLFW_PENDING_BUTTON) - { - if (window->wl.surface == _glfw.wl.pointerSurface) - { - _glfwInputMouseClick(window, - _glfw.wl.pending.button, - _glfw.wl.pending.action, - _glfw.wl.xkb.modifiers); - } - else - { - if (window->wl.fallback.decorations) - { - handleFallbackDecorationButton(window, - _glfw.wl.pending.button, - _glfw.wl.pending.action); - } - } - } + processPointerButton(_glfw.wl.pending.button, _glfw.wl.pending.action); if (_glfw.wl.pending.events & GLFW_PENDING_SCROLL) - { - if (window->wl.surface == _glfw.wl.pointerSurface) - { - _glfwInputScroll(window, - _glfw.wl.pending.scrollX, - _glfw.wl.pending.scrollY); - } - } + processPointerScroll(_glfw.wl.pending.scrollX, _glfw.wl.pending.scrollY); memset(&_glfw.wl.pending, 0, sizeof(_glfw.wl.pending)); } From abfbf078306ceded825a967e5f582013b3f62d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 13 Feb 2026 15:16:14 +0100 Subject: [PATCH 09/10] Null: Fix dynamic library name selection The name selection for the Vulkan loader and the EGL and client libraries used the GLFW platform macros instead of the platform's own macros. This caused the selection to be incorrect when building only the Null platform on any platform but Linux (where it only accidentally worked due to being the default case). OSMesa was already using the correct method. --- src/egl_context.c | 16 ++++++++-------- src/vulkan.c | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/egl_context.c b/src/egl_context.c index 9aec5a2a..156adb40 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -365,10 +365,10 @@ GLFWbool _glfwInitEGL(void) { #if defined(_GLFW_EGL_LIBRARY) _GLFW_EGL_LIBRARY, -#elif defined(_GLFW_WIN32) +#elif defined(_WIN32) "libEGL.dll", "EGL.dll", -#elif defined(_GLFW_COCOA) +#elif defined(__APPLE__) "libEGL.dylib", #elif defined(__CYGWIN__) "libEGL-1.so", @@ -761,10 +761,10 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, { #if defined(_GLFW_GLESV1_LIBRARY) _GLFW_GLESV1_LIBRARY, -#elif defined(_GLFW_WIN32) +#elif defined(_WIN32) "GLESv1_CM.dll", "libGLES_CM.dll", -#elif defined(_GLFW_COCOA) +#elif defined(__APPLE__) "libGLESv1_CM.dylib", #elif defined(__OpenBSD__) || defined(__NetBSD__) "libGLESv1_CM.so", @@ -778,10 +778,10 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, { #if defined(_GLFW_GLESV2_LIBRARY) _GLFW_GLESV2_LIBRARY, -#elif defined(_GLFW_WIN32) +#elif defined(_WIN32) "GLESv2.dll", "libGLESv2.dll", -#elif defined(_GLFW_COCOA) +#elif defined(__APPLE__) "libGLESv2.dylib", #elif defined(__CYGWIN__) "libGLESv2-2.so", @@ -796,8 +796,8 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, { #if defined(_GLFW_OPENGL_LIBRARY) _GLFW_OPENGL_LIBRARY, -#elif defined(_GLFW_WIN32) -#elif defined(_GLFW_COCOA) +#elif defined(_WIN32) +#elif defined(__APPLE__) #elif defined(__OpenBSD__) || defined(__NetBSD__) "libGL.so", #else diff --git a/src/vulkan.c b/src/vulkan.c index 8f25f5ee..f5cc4872 100644 --- a/src/vulkan.c +++ b/src/vulkan.c @@ -55,9 +55,9 @@ GLFWbool _glfwInitVulkan(int mode) { #if defined(_GLFW_VULKAN_LIBRARY) _glfw.vk.handle = _glfwPlatformLoadModule(_GLFW_VULKAN_LIBRARY); -#elif defined(_GLFW_WIN32) +#elif defined(_WIN32) _glfw.vk.handle = _glfwPlatformLoadModule("vulkan-1.dll"); -#elif defined(_GLFW_COCOA) +#elif defined(__APPLE__) _glfw.vk.handle = _glfwPlatformLoadModule("libvulkan.1.dylib"); if (!_glfw.vk.handle) _glfw.vk.handle = _glfwLoadLocalVulkanLoaderCocoa(); From 7c9ea952338b792cb2f70d7c663e498725073797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 13 Feb 2026 15:31:52 +0100 Subject: [PATCH 10/10] Null: Disable Vulkan on macOS for the moment This only affects builds on macOS where only the Null platform is enabled. --- src/vulkan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vulkan.c b/src/vulkan.c index f5cc4872..b52a57fc 100644 --- a/src/vulkan.c +++ b/src/vulkan.c @@ -57,10 +57,11 @@ GLFWbool _glfwInitVulkan(int mode) _glfw.vk.handle = _glfwPlatformLoadModule(_GLFW_VULKAN_LIBRARY); #elif defined(_WIN32) _glfw.vk.handle = _glfwPlatformLoadModule("vulkan-1.dll"); -#elif defined(__APPLE__) +#elif defined(_GLFW_COCOA) _glfw.vk.handle = _glfwPlatformLoadModule("libvulkan.1.dylib"); if (!_glfw.vk.handle) _glfw.vk.handle = _glfwLoadLocalVulkanLoaderCocoa(); +#elif defined(__APPLE__) #elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.vk.handle = _glfwPlatformLoadModule("libvulkan.so"); #else