From a28adba06acc4e5a09e836bd5d4569636c5d3f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 11 Feb 2022 12:37:38 +0100 Subject: [PATCH 01/88] Wayland: Fix multiple copies of single constant --- src/wl_init.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index e7756385..d4a44464 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -1367,14 +1367,15 @@ int _glfwInitWayland(void) wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat); wl_data_device_add_listener(_glfw.wl.dataDevice, &dataDeviceListener, NULL); - _glfw.wl.clipboardString = _glfw_calloc(4096, 1); + + _glfw.wl.clipboardSize = 4096; + _glfw.wl.clipboardString = _glfw_calloc(_glfw.wl.clipboardSize, 1); if (!_glfw.wl.clipboardString) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unable to allocate clipboard memory"); return GLFW_FALSE; } - _glfw.wl.clipboardSize = 4096; } return GLFW_TRUE; From 4a68926bfd999c835009429e6452b2c271252959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 11 Feb 2022 12:40:21 +0100 Subject: [PATCH 02/88] Wayland: Remove unnecessary NULL checks It is fine to pass NULL to _glfw_free. --- src/wl_init.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index d4a44464..769cc084 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -1461,9 +1461,7 @@ void _glfwTerminateWayland(void) if (_glfw.wl.cursorTimerfd >= 0) close(_glfw.wl.cursorTimerfd); - if (_glfw.wl.clipboardString) - _glfw_free(_glfw.wl.clipboardString); - if (_glfw.wl.clipboardSendString) - _glfw_free(_glfw.wl.clipboardSendString); + _glfw_free(_glfw.wl.clipboardString); + _glfw_free(_glfw.wl.clipboardSendString); } From 152f50cd0149ace9242971bf1bae3a5abde24951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 11 Feb 2022 12:36:35 +0100 Subject: [PATCH 03/88] Wayland: Fix error type for allocation failure --- src/wl_init.c | 2 +- src/wl_window.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index 769cc084..e4ae6666 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -1372,7 +1372,7 @@ int _glfwInitWayland(void) _glfw.wl.clipboardString = _glfw_calloc(_glfw.wl.clipboardSize, 1); if (!_glfw.wl.clipboardString) { - _glfwInputError(GLFW_PLATFORM_ERROR, + _glfwInputError(GLFW_OUT_OF_MEMORY, "Wayland: Unable to allocate clipboard memory"); return GLFW_FALSE; } diff --git a/src/wl_window.c b/src/wl_window.c index b2aa1800..0def0746 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1675,7 +1675,7 @@ static GLFWbool growClipboardString(void) clipboard = _glfw_realloc(clipboard, _glfw.wl.clipboardSize * 2); if (!clipboard) { - _glfwInputError(GLFW_PLATFORM_ERROR, + _glfwInputError(GLFW_OUT_OF_MEMORY, "Wayland: Impossible to grow clipboard string"); return GLFW_FALSE; } From 20adc18aa587b75867775942d25356014e11fde9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sat, 1 Jan 2022 20:18:33 +0100 Subject: [PATCH 04/88] Wayland: Clean up monitor scale update --- src/wl_window.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/wl_window.c b/src/wl_window.c index 0def0746..367acacc 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -345,26 +345,25 @@ static void resizeWindow(_GLFWwindow* window) static void checkScaleChange(_GLFWwindow* window) { - int scale = 1; - int monitorScale; - // Check if we will be able to set the buffer scale or not. if (_glfw.wl.compositorVersion < 3) return; // Get the scale factor from the highest scale monitor. - for (int i = 0; i < window->wl.monitorsCount; ++i) + int maxScale = 1; + + for (int i = 0; i < window->wl.monitorsCount; i++) { - monitorScale = window->wl.monitors[i]->wl.scale; - if (scale < monitorScale) - scale = monitorScale; + const int scale = window->wl.monitors[i]->wl.scale; + if (maxScale < scale) + maxScale = scale; } // Only change the framebuffer size if the scale changed. - if (scale != window->wl.scale) + if (window->wl.scale != maxScale) { - window->wl.scale = scale; - wl_surface_set_buffer_scale(window->wl.surface, scale); + window->wl.scale = maxScale; + wl_surface_set_buffer_scale(window->wl.surface, maxScale); resizeWindow(window); } } From 2e656afc4972827930e845c3124a08c42ac5d564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 6 Jan 2022 07:07:53 +0100 Subject: [PATCH 05/88] GLX: Fix context creation failing unnecessarily Regression introduced with 3bb5c459d63d7cf9c990213e39303d9ba5eaebcc. --- README.md | 1 + src/glx_context.c | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7ffd4850..9543919e 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,7 @@ information on what to include when reporting a bug. - [EGL] Added ANGLE backend selection via `EGL_ANGLE_platform_angle` extension (#1380) - [EGL] Bugfix: The `GLFW_DOUBLEBUFFER` context attribute was ignored (#1843) + - [GLX] Bugfix: Context creation failed if GLX 1.4 was not exported by GLX library ## Contact diff --git a/src/glx_context.c b/src/glx_context.c index 31cd34dc..00f38ac3 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -308,10 +308,6 @@ GLFWbool _glfwInitGLX(void) _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXCreateWindow"); _glfw.glx.DestroyWindow = (PFNGLXDESTROYWINDOWPROC) _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXDestroyWindow"); - _glfw.glx.GetProcAddress = (PFNGLXGETPROCADDRESSPROC) - _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXGetProcAddress"); - _glfw.glx.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) - _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXGetProcAddressARB"); _glfw.glx.GetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXGetVisualFromFBConfig"); @@ -327,8 +323,6 @@ GLFWbool _glfwInitGLX(void) !_glfw.glx.CreateNewContext || !_glfw.glx.CreateWindow || !_glfw.glx.DestroyWindow || - !_glfw.glx.GetProcAddress || - !_glfw.glx.GetProcAddressARB || !_glfw.glx.GetVisualFromFBConfig) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -336,6 +330,12 @@ GLFWbool _glfwInitGLX(void) return GLFW_FALSE; } + // NOTE: Unlike GLX 1.3 entry points these are not required to be present + _glfw.glx.GetProcAddress = (PFNGLXGETPROCADDRESSPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXGetProcAddress"); + _glfw.glx.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXGetProcAddressARB"); + if (!glXQueryExtension(_glfw.x11.display, &_glfw.glx.errorBase, &_glfw.glx.eventBase)) From 789d2924c0f865bbba6df3c7097199d1e2f2ecf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 7 Jan 2022 11:51:12 +0100 Subject: [PATCH 06/88] Formatting Make it clear that context attribute helper macros are macros. --- src/egl_context.c | 42 +++++++------- src/glx_context.c | 32 +++++------ src/nsgl_context.m | 44 +++++++-------- src/osmesa_context.c | 22 ++++---- src/wgl_context.c | 130 +++++++++++++++++++++---------------------- 5 files changed, 135 insertions(+), 135 deletions(-) diff --git a/src/egl_context.c b/src/egl_context.c index 89ea78fa..963a59d6 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -506,7 +506,7 @@ void _glfwTerminateEGL(void) } } -#define setAttrib(a, v) \ +#define SET_ATTRIB(a, v) \ { \ assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ @@ -584,13 +584,13 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { - setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, - EGL_NO_RESET_NOTIFICATION_KHR); + SET_ATTRIB(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + EGL_NO_RESET_NOTIFICATION_KHR); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { - setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, - EGL_LOSE_CONTEXT_ON_RESET_KHR); + SET_ATTRIB(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + EGL_LOSE_CONTEXT_ON_RESET_KHR); } flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; @@ -599,42 +599,42 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (ctxconfig->noerror) { if (_glfw.egl.KHR_create_context_no_error) - setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE); + SET_ATTRIB(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE); } if (ctxconfig->major != 1 || ctxconfig->minor != 0) { - setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); - setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); + SET_ATTRIB(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); + SET_ATTRIB(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); } if (mask) - setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); + SET_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); if (flags) - setAttrib(EGL_CONTEXT_FLAGS_KHR, flags); + SET_ATTRIB(EGL_CONTEXT_FLAGS_KHR, flags); } else { if (ctxconfig->client == GLFW_OPENGL_ES_API) - setAttrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); + SET_ATTRIB(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); } if (_glfw.egl.KHR_context_flush_control) { if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) { - setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, - EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR); + SET_ATTRIB(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, + EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR); } else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) { - setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, - EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR); + SET_ATTRIB(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, + EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR); } } - setAttrib(EGL_NONE, EGL_NONE); + SET_ATTRIB(EGL_NONE, EGL_NONE); window->context.egl.handle = eglCreateContext(_glfw.egl.display, config, share, attribs); @@ -653,16 +653,16 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (fbconfig->sRGB) { if (_glfw.egl.KHR_gl_colorspace) - setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); + SET_ATTRIB(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); } if (!fbconfig->doublebuffer) - setAttrib(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER); + SET_ATTRIB(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER); if (_glfw.egl.EXT_present_opaque) - setAttrib(EGL_PRESENT_OPAQUE_EXT, !fbconfig->transparent); + SET_ATTRIB(EGL_PRESENT_OPAQUE_EXT, !fbconfig->transparent); - setAttrib(EGL_NONE, EGL_NONE); + SET_ATTRIB(EGL_NONE, EGL_NONE); native = _glfw.platform.getEGLNativeWindow(window); // HACK: ANGLE does not implement eglCreatePlatformWindowSurfaceEXT @@ -782,7 +782,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, return GLFW_TRUE; } -#undef setAttrib +#undef SET_ATTRIB // Returns the Visual and depth of the chosen EGLConfig // diff --git a/src/glx_context.c b/src/glx_context.c index 00f38ac3..ced9c88f 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -435,7 +435,7 @@ void _glfwTerminateGLX(void) } } -#define setAttrib(a, v) \ +#define SET_ATTRIB(a, v) \ { \ assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ @@ -523,13 +523,13 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { - setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - GLX_NO_RESET_NOTIFICATION_ARB); + SET_ATTRIB(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + GLX_NO_RESET_NOTIFICATION_ARB); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { - setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - GLX_LOSE_CONTEXT_ON_RESET_ARB); + SET_ATTRIB(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + GLX_LOSE_CONTEXT_ON_RESET_ARB); } flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; @@ -542,13 +542,13 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, { if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) { - setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, - GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + SET_ATTRIB(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, + GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); } else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) { - setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, - GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); + SET_ATTRIB(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, + GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); } } } @@ -556,7 +556,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, if (ctxconfig->noerror) { if (_glfw.glx.ARB_create_context_no_error) - setAttrib(GLX_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); + SET_ATTRIB(GLX_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); } // NOTE: Only request an explicitly versioned context when necessary, as @@ -564,17 +564,17 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, // highest version supported by the driver if (ctxconfig->major != 1 || ctxconfig->minor != 0) { - setAttrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); - setAttrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); + SET_ATTRIB(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); + SET_ATTRIB(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); } if (mask) - setAttrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask); + SET_ATTRIB(GLX_CONTEXT_PROFILE_MASK_ARB, mask); if (flags) - setAttrib(GLX_CONTEXT_FLAGS_ARB, flags); + SET_ATTRIB(GLX_CONTEXT_FLAGS_ARB, flags); - setAttrib(None, None); + SET_ATTRIB(None, None); window->context.glx.handle = _glfw.glx.CreateContextAttribsARB(_glfw.x11.display, @@ -631,7 +631,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, return GLFW_TRUE; } -#undef setAttrib +#undef SET_ATTRIB // Returns the Visual and depth of the chosen GLXFBConfig // diff --git a/src/nsgl_context.m b/src/nsgl_context.m index f85ef67b..fc1f7521 100644 --- a/src/nsgl_context.m +++ b/src/nsgl_context.m @@ -188,45 +188,45 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, // No-error contexts (GL_KHR_no_error) are not yet supported by macOS but // are not a hard constraint, so ignore and continue -#define addAttrib(a) \ +#define ADD_ATTRIB(a) \ { \ assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ } -#define setAttrib(a, v) { addAttrib(a); addAttrib(v); } +#define SET_ATTRIB(a, v) { ADD_ATTRIB(a); ADD_ATTRIB(v); } NSOpenGLPixelFormatAttribute attribs[40]; int index = 0; - addAttrib(NSOpenGLPFAAccelerated); - addAttrib(NSOpenGLPFAClosestPolicy); + ADD_ATTRIB(NSOpenGLPFAAccelerated); + ADD_ATTRIB(NSOpenGLPFAClosestPolicy); if (ctxconfig->nsgl.offline) { - addAttrib(NSOpenGLPFAAllowOfflineRenderers); + ADD_ATTRIB(NSOpenGLPFAAllowOfflineRenderers); // NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in // Info.plist for unbundled applications // HACK: This assumes that NSOpenGLPixelFormat will remain // a straightforward wrapper of its CGL counterpart - addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching); + ADD_ATTRIB(kCGLPFASupportsAutomaticGraphicsSwitching); } #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 if (ctxconfig->major >= 4) { - setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); + SET_ATTRIB(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); } else #endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ if (ctxconfig->major >= 3) { - setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); + SET_ATTRIB(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); } if (ctxconfig->major <= 2) { if (fbconfig->auxBuffers != GLFW_DONT_CARE) - setAttrib(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); + SET_ATTRIB(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); if (fbconfig->accumRedBits != GLFW_DONT_CARE && fbconfig->accumGreenBits != GLFW_DONT_CARE && @@ -238,7 +238,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, fbconfig->accumBlueBits + fbconfig->accumAlphaBits; - setAttrib(NSOpenGLPFAAccumSize, accumBits); + SET_ATTRIB(NSOpenGLPFAAccumSize, accumBits); } } @@ -256,17 +256,17 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, else if (colorBits < 15) colorBits = 15; - setAttrib(NSOpenGLPFAColorSize, colorBits); + SET_ATTRIB(NSOpenGLPFAColorSize, colorBits); } if (fbconfig->alphaBits != GLFW_DONT_CARE) - setAttrib(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); + SET_ATTRIB(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); if (fbconfig->depthBits != GLFW_DONT_CARE) - setAttrib(NSOpenGLPFADepthSize, fbconfig->depthBits); + SET_ATTRIB(NSOpenGLPFADepthSize, fbconfig->depthBits); if (fbconfig->stencilBits != GLFW_DONT_CARE) - setAttrib(NSOpenGLPFAStencilSize, fbconfig->stencilBits); + SET_ATTRIB(NSOpenGLPFAStencilSize, fbconfig->stencilBits); if (fbconfig->stereo) { @@ -275,33 +275,33 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, "NSGL: Stereo rendering is deprecated"); return GLFW_FALSE; #else - addAttrib(NSOpenGLPFAStereo); + ADD_ATTRIB(NSOpenGLPFAStereo); #endif } if (fbconfig->doublebuffer) - addAttrib(NSOpenGLPFADoubleBuffer); + ADD_ATTRIB(NSOpenGLPFADoubleBuffer); if (fbconfig->samples != GLFW_DONT_CARE) { if (fbconfig->samples == 0) { - setAttrib(NSOpenGLPFASampleBuffers, 0); + SET_ATTRIB(NSOpenGLPFASampleBuffers, 0); } else { - setAttrib(NSOpenGLPFASampleBuffers, 1); - setAttrib(NSOpenGLPFASamples, fbconfig->samples); + SET_ATTRIB(NSOpenGLPFASampleBuffers, 1); + SET_ATTRIB(NSOpenGLPFASamples, fbconfig->samples); } } // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB // framebuffer, so there's no need (and no way) to request it - addAttrib(0); + ADD_ATTRIB(0); -#undef addAttrib -#undef setAttrib +#undef ADD_ATTRIB +#undef SET_ATTRIB window->context.nsgl.pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; diff --git a/src/osmesa_context.c b/src/osmesa_context.c index 161d9fd8..43ade8dc 100644 --- a/src/osmesa_context.c +++ b/src/osmesa_context.c @@ -190,7 +190,7 @@ void _glfwTerminateOSMesa(void) } } -#define setAttrib(a, v) \ +#define SET_ATTRIB(a, v) \ { \ assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ @@ -221,24 +221,24 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, { int index = 0, attribs[40]; - setAttrib(OSMESA_FORMAT, OSMESA_RGBA); - setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits); - setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits); - setAttrib(OSMESA_ACCUM_BITS, accumBits); + SET_ATTRIB(OSMESA_FORMAT, OSMESA_RGBA); + SET_ATTRIB(OSMESA_DEPTH_BITS, fbconfig->depthBits); + SET_ATTRIB(OSMESA_STENCIL_BITS, fbconfig->stencilBits); + SET_ATTRIB(OSMESA_ACCUM_BITS, accumBits); if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) { - setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE); + SET_ATTRIB(OSMESA_PROFILE, OSMESA_CORE_PROFILE); } else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) { - setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE); + SET_ATTRIB(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE); } if (ctxconfig->major != 1 || ctxconfig->minor != 0) { - setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major); - setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); + SET_ATTRIB(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major); + SET_ATTRIB(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); } if (ctxconfig->forward) @@ -248,7 +248,7 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, return GLFW_FALSE; } - setAttrib(0, 0); + SET_ATTRIB(0, 0); window->context.osmesa.handle = OSMesaCreateContextAttribs(attribs, share); @@ -287,7 +287,7 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, return GLFW_TRUE; } -#undef setAttrib +#undef SET_ATTRIB ////////////////////////////////////////////////////////////////////////// diff --git a/src/wgl_context.c b/src/wgl_context.c index 6586db10..a82c736b 100644 --- a/src/wgl_context.c +++ b/src/wgl_context.c @@ -52,12 +52,12 @@ static int findPixelFormatAttribValueWGL(const int* attribs, return 0; } -#define addAttrib(a) \ +#define ADD_ATTRIB(a) \ { \ assert((size_t) attribCount < sizeof(attribs) / sizeof(attribs[0])); \ attribs[attribCount++] = a; \ } -#define findAttribValue(a) \ +#define FIND_ATTRIB_VALUE(a) \ findPixelFormatAttribValueWGL(attribs, attribCount, values, a) // Return a list of available and usable framebuffer configs @@ -84,41 +84,41 @@ static int choosePixelFormatWGL(_GLFWwindow* window, return 0; } - addAttrib(WGL_SUPPORT_OPENGL_ARB); - addAttrib(WGL_DRAW_TO_WINDOW_ARB); - addAttrib(WGL_PIXEL_TYPE_ARB); - addAttrib(WGL_ACCELERATION_ARB); - addAttrib(WGL_RED_BITS_ARB); - addAttrib(WGL_RED_SHIFT_ARB); - addAttrib(WGL_GREEN_BITS_ARB); - addAttrib(WGL_GREEN_SHIFT_ARB); - addAttrib(WGL_BLUE_BITS_ARB); - addAttrib(WGL_BLUE_SHIFT_ARB); - addAttrib(WGL_ALPHA_BITS_ARB); - addAttrib(WGL_ALPHA_SHIFT_ARB); - addAttrib(WGL_DEPTH_BITS_ARB); - addAttrib(WGL_STENCIL_BITS_ARB); - addAttrib(WGL_ACCUM_BITS_ARB); - addAttrib(WGL_ACCUM_RED_BITS_ARB); - addAttrib(WGL_ACCUM_GREEN_BITS_ARB); - addAttrib(WGL_ACCUM_BLUE_BITS_ARB); - addAttrib(WGL_ACCUM_ALPHA_BITS_ARB); - addAttrib(WGL_AUX_BUFFERS_ARB); - addAttrib(WGL_STEREO_ARB); - addAttrib(WGL_DOUBLE_BUFFER_ARB); + ADD_ATTRIB(WGL_SUPPORT_OPENGL_ARB); + ADD_ATTRIB(WGL_DRAW_TO_WINDOW_ARB); + ADD_ATTRIB(WGL_PIXEL_TYPE_ARB); + ADD_ATTRIB(WGL_ACCELERATION_ARB); + ADD_ATTRIB(WGL_RED_BITS_ARB); + ADD_ATTRIB(WGL_RED_SHIFT_ARB); + ADD_ATTRIB(WGL_GREEN_BITS_ARB); + ADD_ATTRIB(WGL_GREEN_SHIFT_ARB); + ADD_ATTRIB(WGL_BLUE_BITS_ARB); + ADD_ATTRIB(WGL_BLUE_SHIFT_ARB); + ADD_ATTRIB(WGL_ALPHA_BITS_ARB); + ADD_ATTRIB(WGL_ALPHA_SHIFT_ARB); + ADD_ATTRIB(WGL_DEPTH_BITS_ARB); + ADD_ATTRIB(WGL_STENCIL_BITS_ARB); + ADD_ATTRIB(WGL_ACCUM_BITS_ARB); + ADD_ATTRIB(WGL_ACCUM_RED_BITS_ARB); + ADD_ATTRIB(WGL_ACCUM_GREEN_BITS_ARB); + ADD_ATTRIB(WGL_ACCUM_BLUE_BITS_ARB); + ADD_ATTRIB(WGL_ACCUM_ALPHA_BITS_ARB); + ADD_ATTRIB(WGL_AUX_BUFFERS_ARB); + ADD_ATTRIB(WGL_STEREO_ARB); + ADD_ATTRIB(WGL_DOUBLE_BUFFER_ARB); if (_glfw.wgl.ARB_multisample) - addAttrib(WGL_SAMPLES_ARB); + ADD_ATTRIB(WGL_SAMPLES_ARB); if (ctxconfig->client == GLFW_OPENGL_API) { if (_glfw.wgl.ARB_framebuffer_sRGB || _glfw.wgl.EXT_framebuffer_sRGB) - addAttrib(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB); + ADD_ATTRIB(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB); } else { if (_glfw.wgl.EXT_colorspace) - addAttrib(WGL_COLORSPACE_EXT); + ADD_ATTRIB(WGL_COLORSPACE_EXT); } } else @@ -152,48 +152,48 @@ static int choosePixelFormatWGL(_GLFWwindow* window, return 0; } - if (!findAttribValue(WGL_SUPPORT_OPENGL_ARB) || - !findAttribValue(WGL_DRAW_TO_WINDOW_ARB)) + if (!FIND_ATTRIB_VALUE(WGL_SUPPORT_OPENGL_ARB) || + !FIND_ATTRIB_VALUE(WGL_DRAW_TO_WINDOW_ARB)) { continue; } - if (findAttribValue(WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) + if (FIND_ATTRIB_VALUE(WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) continue; - if (findAttribValue(WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) + if (FIND_ATTRIB_VALUE(WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) continue; - if (findAttribValue(WGL_DOUBLE_BUFFER_ARB) != fbconfig->doublebuffer) + if (FIND_ATTRIB_VALUE(WGL_DOUBLE_BUFFER_ARB) != fbconfig->doublebuffer) continue; - u->redBits = findAttribValue(WGL_RED_BITS_ARB); - u->greenBits = findAttribValue(WGL_GREEN_BITS_ARB); - u->blueBits = findAttribValue(WGL_BLUE_BITS_ARB); - u->alphaBits = findAttribValue(WGL_ALPHA_BITS_ARB); + u->redBits = FIND_ATTRIB_VALUE(WGL_RED_BITS_ARB); + u->greenBits = FIND_ATTRIB_VALUE(WGL_GREEN_BITS_ARB); + u->blueBits = FIND_ATTRIB_VALUE(WGL_BLUE_BITS_ARB); + u->alphaBits = FIND_ATTRIB_VALUE(WGL_ALPHA_BITS_ARB); - u->depthBits = findAttribValue(WGL_DEPTH_BITS_ARB); - u->stencilBits = findAttribValue(WGL_STENCIL_BITS_ARB); + u->depthBits = FIND_ATTRIB_VALUE(WGL_DEPTH_BITS_ARB); + u->stencilBits = FIND_ATTRIB_VALUE(WGL_STENCIL_BITS_ARB); - u->accumRedBits = findAttribValue(WGL_ACCUM_RED_BITS_ARB); - u->accumGreenBits = findAttribValue(WGL_ACCUM_GREEN_BITS_ARB); - u->accumBlueBits = findAttribValue(WGL_ACCUM_BLUE_BITS_ARB); - u->accumAlphaBits = findAttribValue(WGL_ACCUM_ALPHA_BITS_ARB); + u->accumRedBits = FIND_ATTRIB_VALUE(WGL_ACCUM_RED_BITS_ARB); + u->accumGreenBits = FIND_ATTRIB_VALUE(WGL_ACCUM_GREEN_BITS_ARB); + u->accumBlueBits = FIND_ATTRIB_VALUE(WGL_ACCUM_BLUE_BITS_ARB); + u->accumAlphaBits = FIND_ATTRIB_VALUE(WGL_ACCUM_ALPHA_BITS_ARB); - u->auxBuffers = findAttribValue(WGL_AUX_BUFFERS_ARB); + u->auxBuffers = FIND_ATTRIB_VALUE(WGL_AUX_BUFFERS_ARB); - if (findAttribValue(WGL_STEREO_ARB)) + if (FIND_ATTRIB_VALUE(WGL_STEREO_ARB)) u->stereo = GLFW_TRUE; if (_glfw.wgl.ARB_multisample) - u->samples = findAttribValue(WGL_SAMPLES_ARB); + u->samples = FIND_ATTRIB_VALUE(WGL_SAMPLES_ARB); if (ctxconfig->client == GLFW_OPENGL_API) { if (_glfw.wgl.ARB_framebuffer_sRGB || _glfw.wgl.EXT_framebuffer_sRGB) { - if (findAttribValue(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB)) + if (FIND_ATTRIB_VALUE(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB)) u->sRGB = GLFW_TRUE; } } @@ -201,7 +201,7 @@ static int choosePixelFormatWGL(_GLFWwindow* window, { if (_glfw.wgl.EXT_colorspace) { - if (findAttribValue(WGL_COLORSPACE_EXT) == WGL_COLORSPACE_SRGB_EXT) + if (FIND_ATTRIB_VALUE(WGL_COLORSPACE_EXT) == WGL_COLORSPACE_SRGB_EXT) u->sRGB = GLFW_TRUE; } } @@ -290,8 +290,8 @@ static int choosePixelFormatWGL(_GLFWwindow* window, return pixelFormat; } -#undef addAttrib -#undef findAttribValue +#undef ADD_ATTRIB +#undef FIND_ATTRIB_VALUE static void makeContextCurrentWGL(_GLFWwindow* window) { @@ -523,7 +523,7 @@ void _glfwTerminateWGL(void) _glfwPlatformFreeModule(_glfw.wgl.instance); } -#define setAttrib(a, v) \ +#define SET_ATTRIB(a, v) \ { \ assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ @@ -631,13 +631,13 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { - setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - WGL_NO_RESET_NOTIFICATION_ARB); + SET_ATTRIB(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + WGL_NO_RESET_NOTIFICATION_ARB); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { - setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - WGL_LOSE_CONTEXT_ON_RESET_ARB); + SET_ATTRIB(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + WGL_LOSE_CONTEXT_ON_RESET_ARB); } flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; @@ -650,13 +650,13 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, { if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) { - setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, - WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + SET_ATTRIB(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, + WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); } else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) { - setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, - WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); + SET_ATTRIB(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, + WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); } } } @@ -664,7 +664,7 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, if (ctxconfig->noerror) { if (_glfw.wgl.ARB_create_context_no_error) - setAttrib(WGL_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); + SET_ATTRIB(WGL_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); } // NOTE: Only request an explicitly versioned context when necessary, as @@ -672,17 +672,17 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, // highest version supported by the driver if (ctxconfig->major != 1 || ctxconfig->minor != 0) { - setAttrib(WGL_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); - setAttrib(WGL_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); + SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); + SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); } if (flags) - setAttrib(WGL_CONTEXT_FLAGS_ARB, flags); + SET_ATTRIB(WGL_CONTEXT_FLAGS_ARB, flags); if (mask) - setAttrib(WGL_CONTEXT_PROFILE_MASK_ARB, mask); + SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, mask); - setAttrib(0, 0); + SET_ATTRIB(0, 0); window->context.wgl.handle = wglCreateContextAttribsARB(window->context.wgl.dc, share, attribs); @@ -765,7 +765,7 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, return GLFW_TRUE; } -#undef setAttrib +#undef SET_ATTRIB GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* handle) { From ad01c1b614868c3cbc79306aa6a19c9fc06f34a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 20 Jan 2022 19:21:48 +0100 Subject: [PATCH 07/88] Win32: Fix key name code changing global key state This prevents glfwInit from potentially clobbering the dead key state for other applications. Closes #2018 --- CONTRIBUTORS.md | 1 + README.md | 2 ++ src/win32_init.c | 9 +++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index cb9b0d58..82e91773 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -10,6 +10,7 @@ video tutorials. - Matt Arsenault - ashishgamedev - David Avedissian + - Luca Bacci - Keith Bauer - John Bartholomew - Coşku Baş diff --git a/README.md b/README.md index 9543919e..0aa309d7 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,8 @@ information on what to include when reporting a bug. - [Win32] Bugfix: Content scale queries could fail silently (#1615) - [Win32] Bugfix: Content scales could have garbage values if monitor was recently disconnected (#1615) + - [Win32] Bugfix: Key name update modified global key state on Windows 10 1607 + and later (#2018) - [Cocoa] Added support for `VK_EXT_metal_surface` (#1619) - [Cocoa] Added locating the Vulkan loader at runtime in an application bundle - [Cocoa] Moved main menu creation to GLFW initialization time (#1649) diff --git a/src/win32_init.c b/src/win32_init.c index e687d770..0479afe5 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -458,6 +458,11 @@ void _glfwUpdateKeyNamesWin32(void) { int key; BYTE state[256] = {0}; + UINT flags = 0; + + // Avoid modifying the global key state if supported + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + flags = (1 << 2); memset(_glfw.win32.keynames, 0, sizeof(_glfw.win32.keynames)); @@ -487,13 +492,13 @@ void _glfwUpdateKeyNamesWin32(void) length = ToUnicode(vk, scancode, state, chars, sizeof(chars) / sizeof(WCHAR), - 0); + flags); if (length == -1) { length = ToUnicode(vk, scancode, state, chars, sizeof(chars) / sizeof(WCHAR), - 0); + flags); } if (length < 1) From d3e4fcf8b7608e7b6f6cf1c102b1e28c478f5a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 1 Feb 2022 22:05:55 +0100 Subject: [PATCH 08/88] X11: Fix event polling when event fd > 1023 This replaces select with poll for checking for data on event file descriptors, as select cannot handle file descriptors larger than 1023. Closes #2024 --- CONTRIBUTORS.md | 1 + README.md | 2 ++ src/x11_window.c | 32 +++++++++++++------------------- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 82e91773..34ca624f 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -82,6 +82,7 @@ video tutorials. - Paul Holden - Warren Hu - Charles Huber + - illustris - InKryption - IntellectualKitty - Aaron Jacobs diff --git a/README.md b/README.md index 0aa309d7..7fc32b98 100644 --- a/README.md +++ b/README.md @@ -268,6 +268,8 @@ information on what to include when reporting a bug. - [X11] Bugfix: Icon pixel format conversion worked only by accident, relying on undefined behavior (#1986) - [X11] Bugfix: Dynamic loading on OpenBSD failed due to soname differences + - [X11] Bugfix: Waiting for events would fail if file descriptor was too large + (#2024) - [Wayland] Added dynamic loading of all Wayland libraries - [Wayland] Added support for key names via xkbcommon - [Wayland] Removed support for `wl_shell` (#1443) diff --git a/src/x11_window.c b/src/x11_window.c index 61e4fd6d..08422e5f 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -32,7 +32,7 @@ #include #include -#include +#include #include #include @@ -57,37 +57,31 @@ #define _GLFW_XDND_VERSION 5 -// Wait for data to arrive using select +// Wait for event data to arrive on any relevant file descriptor // This avoids blocking other threads via the per-display Xlib lock that also // covers GLX functions // static GLFWbool waitForEvent(double* timeout) { - fd_set fds; - const int fd = ConnectionNumber(_glfw.x11.display); - int count = fd + 1; - -#if defined(__linux__) - if (_glfw.linjs.inotify > fd) - count = _glfw.linjs.inotify + 1; -#endif for (;;) { - FD_ZERO(&fds); - FD_SET(fd, &fds); + nfds_t count = 1; + struct pollfd fds[2] = + { + { ConnectionNumber(_glfw.x11.display), POLLIN } + }; + #if defined(__linux__) - if (_glfw.linjs.inotify > 0) - FD_SET(_glfw.linjs.inotify, &fds); + if (_glfw.joysticksInitialized) + fds[count++] = (struct pollfd) { _glfw.linjs.inotify, POLLIN }; #endif if (timeout) { - const long seconds = (long) *timeout; - const long microseconds = (long) ((*timeout - seconds) * 1e6); - struct timeval tv = { seconds, microseconds }; + const int milliseconds = (int) (*timeout * 1e3); const uint64_t base = _glfwPlatformGetTimerValue(); - const int result = select(count, &fds, NULL, NULL, &tv); + const int result = poll(fds, count, milliseconds); const int error = errno; *timeout -= (_glfwPlatformGetTimerValue() - base) / @@ -98,7 +92,7 @@ static GLFWbool waitForEvent(double* timeout) if ((result == -1 && error == EINTR) || *timeout <= 0.0) return GLFW_FALSE; } - else if (select(count, &fds, NULL, NULL, NULL) != -1 || errno != EINTR) + else if (poll(fds, count, -1) != -1 || errno != EINTR) return GLFW_TRUE; } } From 92b5c67b50d2bab9401f464d4a40fecfccb09dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 18 Feb 2022 15:13:18 +0100 Subject: [PATCH 09/88] X11: Retry poll when failed with EINTR or EAGAIN Both of these errors should just lead to local retry. --- src/x11_window.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/x11_window.c b/src/x11_window.c index 08422e5f..f4adbd3e 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -82,18 +82,26 @@ static GLFWbool waitForEvent(double* timeout) const uint64_t base = _glfwPlatformGetTimerValue(); const int result = poll(fds, count, milliseconds); - const int error = errno; + const int error = errno; // clock_gettime may overwrite our error *timeout -= (_glfwPlatformGetTimerValue() - base) / (double) _glfwPlatformGetTimerFrequency(); if (result > 0) return GLFW_TRUE; - if ((result == -1 && error == EINTR) || *timeout <= 0.0) + else if (result == -1 && error != EINTR && error != EAGAIN) + return GLFW_FALSE; + else if (*timeout <= 0.0) + return GLFW_FALSE; + } + else + { + const int result = poll(fds, count, -1); + if (result > 0) + return GLFW_TRUE; + else if (result == -1 && errno != EINTR && errno != EAGAIN) return GLFW_FALSE; } - else if (poll(fds, count, -1) != -1 || errno != EINTR) - return GLFW_TRUE; } } From 1e987cb92ea646282a2e3f7c085f96ae7eeea425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 18 Feb 2022 15:19:16 +0100 Subject: [PATCH 10/88] X11: Fix joystick events causing busy waiting On Linux, the inotify descriptor was included in the set used for select, but could not break the outer loop, leading to busy waiting until timeout or the correct X11 event arrived. This commit adds a new function for waiting just on X11 events. Fixes #1872 --- README.md | 1 + src/x11_window.c | 59 +++++++++++++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 7fc32b98..23cc069a 100644 --- a/README.md +++ b/README.md @@ -270,6 +270,7 @@ information on what to include when reporting a bug. - [X11] Bugfix: Dynamic loading on OpenBSD failed due to soname differences - [X11] Bugfix: Waiting for events would fail if file descriptor was too large (#2024) + - [X11] Bugfix: Joystick events could lead to busy-waiting (#1872) - [Wayland] Added dynamic loading of all Wayland libraries - [Wayland] Added support for key names via xkbcommon - [Wayland] Removed support for `wl_shell` (#1443) diff --git a/src/x11_window.c b/src/x11_window.c index f4adbd3e..122db2ba 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -56,26 +56,12 @@ #define _GLFW_XDND_VERSION 5 - -// Wait for event data to arrive on any relevant file descriptor -// This avoids blocking other threads via the per-display Xlib lock that also -// covers GLX functions +// Wait for data to arrive on any of the specified file descriptors // -static GLFWbool waitForEvent(double* timeout) +static GLFWbool waitForData(struct pollfd* fds, nfds_t count, double* timeout) { for (;;) { - nfds_t count = 1; - struct pollfd fds[2] = - { - { ConnectionNumber(_glfw.x11.display), POLLIN } - }; - -#if defined(__linux__) - if (_glfw.joysticksInitialized) - fds[count++] = (struct pollfd) { _glfw.linjs.inotify, POLLIN }; -#endif - if (timeout) { const int milliseconds = (int) (*timeout * 1e3); @@ -105,6 +91,33 @@ static GLFWbool waitForEvent(double* timeout) } } +// Wait for event data to arrive on the X11 display socket +// This avoids blocking other threads via the per-display Xlib lock that also +// covers GLX functions +// +static GLFWbool waitForX11Event(double* timeout) +{ + struct pollfd fd = { ConnectionNumber(_glfw.x11.display), POLLIN }; + return waitForData(&fd, 1, timeout); +} + +// Wait for event data to arrive on any event file descriptor +// This avoids blocking other threads via the per-display Xlib lock that also +// covers GLX functions +// +static GLFWbool waitForAnyEvent(double* timeout) +{ + nfds_t count = 1; + struct pollfd fds[2] = { { ConnectionNumber(_glfw.x11.display), POLLIN } }; + +#if defined(__linux__) + if (_glfw.joysticksInitialized) + fds[count++] = (struct pollfd) { _glfw.linjs.inotify, POLLIN }; +#endif + + return waitForData(fds, count, timeout); +} + // Waits until a VisibilityNotify event arrives for the specified window or the // timeout period elapses (ICCCM section 4.2.2) // @@ -118,7 +131,7 @@ static GLFWbool waitForVisibilityNotify(_GLFWwindow* window) VisibilityNotify, &dummy)) { - if (!waitForEvent(&timeout)) + if (!waitForX11Event(&timeout)) return GLFW_FALSE; } @@ -960,7 +973,7 @@ static const char* getSelectionString(Atom selection) SelectionNotify, ¬ification)) { - waitForEvent(NULL); + waitForX11Event(NULL); } if (notification.xselection.property == None) @@ -996,7 +1009,7 @@ static const char* getSelectionString(Atom selection) isSelPropNewValueNotify, (XPointer) ¬ification)) { - waitForEvent(NULL); + waitForX11Event(NULL); } XFree(data); @@ -1898,7 +1911,7 @@ void _glfwPushSelectionToManagerX11(void) } } - waitForEvent(NULL); + waitForX11Event(NULL); } } @@ -2240,7 +2253,7 @@ void _glfwGetWindowFrameSizeX11(_GLFWwindow* window, isFrameExtentsEvent, (XPointer) window)) { - if (!waitForEvent(&timeout)) + if (!waitForX11Event(&timeout)) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue"); @@ -2782,7 +2795,7 @@ void _glfwPollEventsX11(void) void _glfwWaitEventsX11(void) { while (!XPending(_glfw.x11.display)) - waitForEvent(NULL); + waitForAnyEvent(NULL); _glfwPollEventsX11(); } @@ -2791,7 +2804,7 @@ void _glfwWaitEventsTimeoutX11(double timeout) { while (!XPending(_glfw.x11.display)) { - if (!waitForEvent(&timeout)) + if (!waitForAnyEvent(&timeout)) break; } From 87970b7f265bbd39595de6428bbd14047affa753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 18 Feb 2022 15:20:10 +0100 Subject: [PATCH 11/88] X11: Fix glfwWaitEvents* ignoring joystick events The data available on the X11 connection may be a reply or an internal event for an X11 extension. Previously the check for whether an event was available for us was done outside waitForEvent. This prevented data available on other file descriptors from breaking the outer wait loop. This commit moves the check for whether an event is available into the wait functions, where there is enough knowledge to limit the check to the X11 connection. Related to #932 --- README.md | 1 + src/x11_window.c | 34 +++++++++++++++++++++++----------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 23cc069a..fefaf500 100644 --- a/README.md +++ b/README.md @@ -271,6 +271,7 @@ information on what to include when reporting a bug. - [X11] Bugfix: Waiting for events would fail if file descriptor was too large (#2024) - [X11] Bugfix: Joystick events could lead to busy-waiting (#1872) + - [X11] Bugfix: `glfwWaitEvents*` did not continue for joystick events - [Wayland] Added dynamic loading of all Wayland libraries - [Wayland] Added support for key names via xkbcommon - [Wayland] Removed support for `wl_shell` (#1443) diff --git a/src/x11_window.c b/src/x11_window.c index 122db2ba..c42be084 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -98,7 +98,14 @@ static GLFWbool waitForData(struct pollfd* fds, nfds_t count, double* timeout) static GLFWbool waitForX11Event(double* timeout) { struct pollfd fd = { ConnectionNumber(_glfw.x11.display), POLLIN }; - return waitForData(&fd, 1, timeout); + + while (!XPending(_glfw.x11.display)) + { + if (!waitForData(&fd, 1, timeout)) + return GLFW_FALSE; + } + + return GLFW_TRUE; } // Wait for event data to arrive on any event file descriptor @@ -115,7 +122,19 @@ static GLFWbool waitForAnyEvent(double* timeout) fds[count++] = (struct pollfd) { _glfw.linjs.inotify, POLLIN }; #endif - return waitForData(fds, count, timeout); + while (!XPending(_glfw.x11.display)) + { + if (!waitForData(fds, count, timeout)) + return GLFW_FALSE; + + for (int i = 1; i < count; i++) + { + if (fds[i].revents & POLLIN) + return GLFW_TRUE; + } + } + + return GLFW_TRUE; } // Waits until a VisibilityNotify event arrives for the specified window or the @@ -2794,20 +2813,13 @@ void _glfwPollEventsX11(void) void _glfwWaitEventsX11(void) { - while (!XPending(_glfw.x11.display)) - waitForAnyEvent(NULL); - + waitForAnyEvent(NULL); _glfwPollEventsX11(); } void _glfwWaitEventsTimeoutX11(double timeout) { - while (!XPending(_glfw.x11.display)) - { - if (!waitForAnyEvent(&timeout)) - break; - } - + waitForAnyEvent(&timeout); _glfwPollEventsX11(); } From 363d4714414e53312cd5cb0be230f040580487ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 18 Feb 2022 15:35:09 +0100 Subject: [PATCH 12/88] Cleanup --- src/x11_window.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/x11_window.c b/src/x11_window.c index c42be084..50fba5e8 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -2778,8 +2778,6 @@ GLFWbool _glfwRawMouseMotionSupportedX11(void) void _glfwPollEventsX11(void) { - _GLFWwindow* window; - #if defined(__linux__) if (_glfw.joysticksInitialized) _glfwDetectJoystickConnectionLinux(); @@ -2793,7 +2791,7 @@ void _glfwPollEventsX11(void) processEvent(&event); } - window = _glfw.x11.disabledCursorWindow; + _GLFWwindow* window = _glfw.x11.disabledCursorWindow; if (window) { int width, height; From cd22e2849512a88d0ab77bc7a3458646625f2c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 18 Feb 2022 14:29:43 +0100 Subject: [PATCH 13/88] X11: Fix empty event race condition with a pipe There is a seemingly unavoidable race condition when waiting for data on the X11 display connection, as long as any other thread is also making Xlib calls. The event data we are waiting for could be read by the other thread as part of looking for the reply to its request, before our poll has begun. This commit replaces the X11 event sent by glfwPostEmptyEvent with writing to an unnamed pipe. The race condition remains if other Xlib calls are made on other threads, but glfwPostEmptyEvent should now be race-free. This commit is based on work by pcwalton, OlivierSohn, kovidgoyal and joaodasilva. Closes #2033 Related to #379 Related to #1281 Related to #1285 --- CONTRIBUTORS.md | 2 ++ README.md | 2 ++ docs/news.dox | 6 ++++++ src/x11_init.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/x11_platform.h | 1 + src/x11_window.c | 44 +++++++++++++++++++++++++++++++++++--------- 6 files changed, 88 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 34ca624f..f20422ec 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -185,6 +185,7 @@ video tutorials. - Ali Sherief - Yoshiki Shibukawa - Dmitri Shuralyov + - Joao da Silva - Daniel Sieger - Daniel Skorupski - Anthony Smith @@ -192,6 +193,7 @@ video tutorials. - Cliff Smolinsky - Patrick Snape - Erlend Sogge Heggen + - Olivier Sohn - Julian Squires - Johannes Stein - Pontus Stenetorp diff --git a/README.md b/README.md index fefaf500..d1dcb600 100644 --- a/README.md +++ b/README.md @@ -272,6 +272,8 @@ information on what to include when reporting a bug. (#2024) - [X11] Bugfix: Joystick events could lead to busy-waiting (#1872) - [X11] Bugfix: `glfwWaitEvents*` did not continue for joystick events + - [X11] Bugfix: `glfwPostEmptyEvent` could be ignored due to race condition + (#379,#1281,#1285,#2033) - [Wayland] Added dynamic loading of all Wayland libraries - [Wayland] Added support for key names via xkbcommon - [Wayland] Removed support for `wl_shell` (#1443) diff --git a/docs/news.dox b/docs/news.dox index 6c55e65f..fbf60319 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -138,6 +138,12 @@ GLFW_TRANSPARENT_FRAMEBUFFER on Windows 7 if DWM transparency is off (the Transparency setting under Personalization > Window Color). +@subsubsection emptyevents_34 Empty events on X11 no longer roundtrip to server + +Events posted with @ref glfwPostEmptyEvent now use a separate unnamed pipe +instead of sending an X11 client event to the helper window. + + @subsection deprecations_34 Deprecations in version 3.4 @subsection removals_34 Removals in 3.4 diff --git a/src/x11_init.c b/src/x11_init.c index acfa7933..203828fc 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include // Translate the X11 KeySyms for a key to a GLFW key code @@ -1042,6 +1044,37 @@ static Window createHelperWindow(void) CWEventMask, &wa); } +// Create the pipe for empty events without assumuing the OS has pipe2(2) +// +static GLFWbool createEmptyEventPipe(void) +{ + if (pipe(_glfw.x11.emptyEventPipe) != 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to create empty event pipe: %s", + strerror(errno)); + return GLFW_FALSE; + } + + for (int i = 0; i < 2; i++) + { + const int sf = fcntl(_glfw.x11.emptyEventPipe[i], F_GETFL, 0); + const int df = fcntl(_glfw.x11.emptyEventPipe[i], F_GETFD, 0); + + if (sf == -1 || df == -1 || + fcntl(_glfw.x11.emptyEventPipe[i], F_SETFL, sf | O_NONBLOCK) == -1 || + fcntl(_glfw.x11.emptyEventPipe[i], F_SETFD, df | FD_CLOEXEC) == -1) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to set flags for empty event pipe: %s", + strerror(errno)); + return GLFW_FALSE; + } + } + + return GLFW_TRUE; +} + // X error handler // static int errorHandler(Display *display, XErrorEvent* event) @@ -1491,6 +1524,9 @@ int _glfwInitX11(void) getSystemContentScale(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY); + if (!createEmptyEventPipe()) + return GLFW_FALSE; + if (!initExtensions()) return GLFW_FALSE; @@ -1604,5 +1640,11 @@ void _glfwTerminateX11(void) _glfwPlatformFreeModule(_glfw.x11.xlib.handle); _glfw.x11.xlib.handle = NULL; } + + if (_glfw.x11.emptyEventPipe[0] || _glfw.x11.emptyEventPipe[1]) + { + close(_glfw.x11.emptyEventPipe[0]); + close(_glfw.x11.emptyEventPipe[1]); + } } diff --git a/src/x11_platform.h b/src/x11_platform.h index 61a0b17d..8da3a2e4 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -582,6 +582,7 @@ typedef struct _GLFWlibraryX11 double restoreCursorPosX, restoreCursorPosY; // The window whose disabled cursor mode is active _GLFWwindow* disabledCursorWindow; + int emptyEventPipe[2]; // Window manager atoms Atom NET_SUPPORTED; diff --git a/src/x11_window.c b/src/x11_window.c index 50fba5e8..76becd6c 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -114,8 +114,12 @@ static GLFWbool waitForX11Event(double* timeout) // static GLFWbool waitForAnyEvent(double* timeout) { - nfds_t count = 1; - struct pollfd fds[2] = { { ConnectionNumber(_glfw.x11.display), POLLIN } }; + nfds_t count = 2; + struct pollfd fds[3] = + { + { ConnectionNumber(_glfw.x11.display), POLLIN }, + { _glfw.x11.emptyEventPipe[0], POLLIN } + }; #if defined(__linux__) if (_glfw.joysticksInitialized) @@ -137,6 +141,32 @@ static GLFWbool waitForAnyEvent(double* timeout) return GLFW_TRUE; } +// Writes a byte to the empty event pipe +// +static void writeEmptyEvent(void) +{ + for (;;) + { + const char byte = 0; + const int result = write(_glfw.x11.emptyEventPipe[1], &byte, 1); + if (result == 1 || (result == -1 && errno != EINTR)) + break; + } +} + +// Drains available data from the empty event pipe +// +static void drainEmptyEvents(void) +{ + for (;;) + { + char dummy[64]; + const int result = read(_glfw.x11.emptyEventPipe[0], dummy, sizeof(dummy)); + if (result == -1 && errno != EINTR) + break; + } +} + // Waits until a VisibilityNotify event arrives for the specified window or the // timeout period elapses (ICCCM section 4.2.2) // @@ -2778,6 +2808,8 @@ GLFWbool _glfwRawMouseMotionSupportedX11(void) void _glfwPollEventsX11(void) { + drainEmptyEvents(); + #if defined(__linux__) if (_glfw.joysticksInitialized) _glfwDetectJoystickConnectionLinux(); @@ -2823,13 +2855,7 @@ void _glfwWaitEventsTimeoutX11(double timeout) void _glfwPostEmptyEventX11(void) { - XEvent event = { ClientMessage }; - event.xclient.window = _glfw.x11.helperWindowHandle; - event.xclient.format = 32; // Data is 32-bit longs - event.xclient.message_type = _glfw.x11.NULL_; - - XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event); - XFlush(_glfw.x11.display); + writeEmptyEvent(); } void _glfwGetCursorPosX11(_GLFWwindow* window, double* xpos, double* ypos) From 84b0923fe61a12bd0bf9241da102053b906265bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 25 Feb 2022 15:05:59 +0100 Subject: [PATCH 14/88] X11: Use lower-latency poll where available This uses ppoll for waiting on file descriptors with a timeout, where that function has been available a while. On NetBSD, which will be getting ppoll in the next release, the equivalent pollts is used. This commit is based on work by OlivierSohn and kovidgoyal. Related to #1281 Related to #1285 --- src/x11_window.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/x11_window.c b/src/x11_window.c index 76becd6c..52cbbcf0 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -27,12 +27,18 @@ // It is fine to use C99 in this file because it will not be built with VS //======================================================================== +#if defined(__linux__) + #define _GNU_SOURCE +#endif + #include "internal.h" #include #include #include +#include +#include #include #include @@ -64,10 +70,22 @@ static GLFWbool waitForData(struct pollfd* fds, nfds_t count, double* timeout) { if (timeout) { - const int milliseconds = (int) (*timeout * 1e3); const uint64_t base = _glfwPlatformGetTimerValue(); +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) + const time_t seconds = (time_t) *timeout; + const long nanoseconds = (long) ((*timeout - seconds) * 1e9); + const struct timespec ts = { seconds, nanoseconds }; + const int result = ppoll(fds, count, &ts, NULL); +#elif defined(__NetBSD__) + const time_t seconds = (time_t) *timeout; + const long nanoseconds = (long) ((*timeout - seconds) * 1e9); + const struct timespec ts = { seconds, nanoseconds }; + const int result = pollts(fds, count, &ts, NULL); +#else + const int milliseconds = (int) (*timeout * 1e3); const int result = poll(fds, count, milliseconds); +#endif const int error = errno; // clock_gettime may overwrite our error *timeout -= (_glfwPlatformGetTimerValue() - base) / From 3c2913dcb96eed94742826f4276282f4e4a7b01e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 3 Mar 2022 16:23:00 +0100 Subject: [PATCH 15/88] Wayland: Fix potential incomplete display flushing The flushing of a Wayland display may need to be done in several steps, signalled by it failing with EAGAIN. --- src/wl_window.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/wl_window.c b/src/wl_window.c index 367acacc..1a9f980d 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -716,6 +716,25 @@ static void incrementCursorImage(_GLFWwindow* window) } } +static GLFWbool flushDisplay(void) +{ + while (wl_display_flush(_glfw.wl.display) == -1) + { + if (errno != EAGAIN) + return GLFW_FALSE; + + struct pollfd fd = { wl_display_get_fd(_glfw.wl.display), POLLOUT }; + + while (poll(&fd, 1, -1) == -1) + { + if (errno != EINTR && errno != EAGAIN) + return GLFW_FALSE; + } + } + + return GLFW_TRUE; +} + static void handleEvents(int timeout) { struct pollfd fds[] = @@ -730,7 +749,7 @@ static void handleEvents(int timeout) // If an error other than EAGAIN happens, we have likely been disconnected // from the Wayland session; try to handle that the best we can. - if (wl_display_flush(_glfw.wl.display) < 0 && errno != EAGAIN) + if (!flushDisplay()) { _GLFWwindow* window = _glfw.windowListHead; while (window) From 203a7c59d25058f7dfd0c7a3b5062de0b87a8517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 3 Mar 2022 16:28:44 +0100 Subject: [PATCH 16/88] Wayland: Cancel display fd read before callbacks Cancel the prepared-to-read state on the calling thread before starting to call back to user code. Emitting close requests here is not a good choice but that is for a future commit to address. --- src/wl_window.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wl_window.c b/src/wl_window.c index 1a9f980d..c150b188 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -751,6 +751,8 @@ static void handleEvents(int timeout) // from the Wayland session; try to handle that the best we can. if (!flushDisplay()) { + wl_display_cancel_read(_glfw.wl.display); + _GLFWwindow* window = _glfw.windowListHead; while (window) { @@ -758,7 +760,6 @@ static void handleEvents(int timeout) window = window->next; } - wl_display_cancel_read(_glfw.wl.display); return; } From bb9d699ae66b2bdc8718995ba13c57c9c8e59602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 3 Mar 2022 21:26:11 +0100 Subject: [PATCH 17/88] Share X11 fd polling logic with Wayland This moves the X11 polling implementation to a separate file where it can be used by either the X11 or Wayland backend or both. This code should be POSIX compatible where necessary but will use the lower latency but non-standard polling functions ppoll or pollts where those are available. This commit is based on work by OlivierSohn and kovidgoyal. Fixes #1281 Closes #1285 --- src/CMakeLists.txt | 1 + src/posix_poll.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++ src/posix_poll.h | 32 ++++++++++++++++++ src/wl_platform.h | 1 + src/wl_window.c | 13 ++++---- src/x11_platform.h | 1 + src/x11_window.c | 57 ++----------------------------- 7 files changed, 127 insertions(+), 61 deletions(-) create mode 100644 src/posix_poll.c create mode 100644 src/posix_poll.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a07ca931..01f191c9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -60,6 +60,7 @@ if (GLFW_BUILD_X11 OR GLFW_BUILD_WAYLAND) if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_sources(glfw PRIVATE linux_joystick.h linux_joystick.c) endif() + target_sources(glfw PRIVATE posix_poll.h posix_poll.c) endif() if (GLFW_BUILD_WAYLAND) diff --git a/src/posix_poll.c b/src/posix_poll.c new file mode 100644 index 00000000..352d9900 --- /dev/null +++ b/src/posix_poll.c @@ -0,0 +1,83 @@ +//======================================================================== +// GLFW 3.4 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2022 Camilla Löwy +// +// 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. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#if defined(__linux__) + #define _GNU_SOURCE +#endif + +#include "internal.h" + +#include +#include +#include + +GLFWbool _glfwPollPOSIX(struct pollfd* fds, nfds_t count, double* timeout) +{ + for (;;) + { + if (timeout) + { + const uint64_t base = _glfwPlatformGetTimerValue(); + +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) + const time_t seconds = (time_t) *timeout; + const long nanoseconds = (long) ((*timeout - seconds) * 1e9); + const struct timespec ts = { seconds, nanoseconds }; + const int result = ppoll(fds, count, &ts, NULL); +#elif defined(__NetBSD__) + const time_t seconds = (time_t) *timeout; + const long nanoseconds = (long) ((*timeout - seconds) * 1e9); + const struct timespec ts = { seconds, nanoseconds }; + const int result = pollts(fds, count, &ts, NULL); +#else + const int milliseconds = (int) (*timeout * 1e3); + const int result = poll(fds, count, milliseconds); +#endif + const int error = errno; // clock_gettime may overwrite our error + + *timeout -= (_glfwPlatformGetTimerValue() - base) / + (double) _glfwPlatformGetTimerFrequency(); + + if (result > 0) + return GLFW_TRUE; + else if (result == -1 && error != EINTR && error != EAGAIN) + return GLFW_FALSE; + else if (*timeout <= 0.0) + return GLFW_FALSE; + } + else + { + const int result = poll(fds, count, -1); + if (result > 0) + return GLFW_TRUE; + else if (result == -1 && errno != EINTR && errno != EAGAIN) + return GLFW_FALSE; + } + } +} + diff --git a/src/posix_poll.h b/src/posix_poll.h new file mode 100644 index 00000000..1effd1cd --- /dev/null +++ b/src/posix_poll.h @@ -0,0 +1,32 @@ +//======================================================================== +// GLFW 3.4 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2022 Camilla Löwy +// +// 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. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include + +GLFWbool _glfwPollPOSIX(struct pollfd* fds, nfds_t count, double* timeout); + diff --git a/src/wl_platform.h b/src/wl_platform.h index 7565411b..ba405714 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -43,6 +43,7 @@ typedef VkResult (APIENTRY *PFN_vkCreateWaylandSurfaceKHR)(VkInstance,const VkWa typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*); #include "xkb_unicode.h" +#include "posix_poll.h" typedef int (* PFN_wl_display_flush)(struct wl_display *display); typedef void (* PFN_wl_display_cancel_read)(struct wl_display *display); diff --git a/src/wl_window.c b/src/wl_window.c index c150b188..4dfde7af 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -735,7 +735,7 @@ static GLFWbool flushDisplay(void) return GLFW_TRUE; } -static void handleEvents(int timeout) +static void handleEvents(double* timeout) { struct pollfd fds[] = { @@ -763,7 +763,7 @@ static void handleEvents(int timeout) return; } - if (poll(fds, 3, timeout) > 0) + if (_glfwPollPOSIX(fds, 3, timeout)) { if (fds[0].revents & POLLIN) { @@ -1168,17 +1168,18 @@ GLFWbool _glfwRawMouseMotionSupportedWayland(void) void _glfwPollEventsWayland(void) { - handleEvents(0); + double timeout = 0.0; + handleEvents(&timeout); } void _glfwWaitEventsWayland(void) { - handleEvents(-1); + handleEvents(NULL); } void _glfwWaitEventsTimeoutWayland(double timeout) { - handleEvents((int) (timeout * 1e3)); + handleEvents(&timeout); } void _glfwPostEmptyEventWayland(void) @@ -1729,7 +1730,7 @@ const char* _glfwGetClipboardStringWayland(void) close(fds[1]); // XXX: this is a huge hack, this function shouldn’t be synchronous! - handleEvents(-1); + handleEvents(NULL); while (1) { diff --git a/src/x11_platform.h b/src/x11_platform.h index 8da3a2e4..956104fa 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -453,6 +453,7 @@ typedef VkResult (APIENTRY *PFN_vkCreateXcbSurfaceKHR)(VkInstance,const VkXcbSur typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice,uint32_t,xcb_connection_t*,xcb_visualid_t); #include "xkb_unicode.h" +#include "posix_poll.h" #define GLFW_X11_WINDOW_STATE _GLFWwindowX11 x11; #define GLFW_X11_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11; diff --git a/src/x11_window.c b/src/x11_window.c index 52cbbcf0..6c329e63 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -27,18 +27,12 @@ // It is fine to use C99 in this file because it will not be built with VS //======================================================================== -#if defined(__linux__) - #define _GNU_SOURCE -#endif - #include "internal.h" #include #include #include -#include -#include #include #include @@ -62,53 +56,6 @@ #define _GLFW_XDND_VERSION 5 -// Wait for data to arrive on any of the specified file descriptors -// -static GLFWbool waitForData(struct pollfd* fds, nfds_t count, double* timeout) -{ - for (;;) - { - if (timeout) - { - const uint64_t base = _glfwPlatformGetTimerValue(); - -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) - const time_t seconds = (time_t) *timeout; - const long nanoseconds = (long) ((*timeout - seconds) * 1e9); - const struct timespec ts = { seconds, nanoseconds }; - const int result = ppoll(fds, count, &ts, NULL); -#elif defined(__NetBSD__) - const time_t seconds = (time_t) *timeout; - const long nanoseconds = (long) ((*timeout - seconds) * 1e9); - const struct timespec ts = { seconds, nanoseconds }; - const int result = pollts(fds, count, &ts, NULL); -#else - const int milliseconds = (int) (*timeout * 1e3); - const int result = poll(fds, count, milliseconds); -#endif - const int error = errno; // clock_gettime may overwrite our error - - *timeout -= (_glfwPlatformGetTimerValue() - base) / - (double) _glfwPlatformGetTimerFrequency(); - - if (result > 0) - return GLFW_TRUE; - else if (result == -1 && error != EINTR && error != EAGAIN) - return GLFW_FALSE; - else if (*timeout <= 0.0) - return GLFW_FALSE; - } - else - { - const int result = poll(fds, count, -1); - if (result > 0) - return GLFW_TRUE; - else if (result == -1 && errno != EINTR && errno != EAGAIN) - return GLFW_FALSE; - } - } -} - // Wait for event data to arrive on the X11 display socket // This avoids blocking other threads via the per-display Xlib lock that also // covers GLX functions @@ -119,7 +66,7 @@ static GLFWbool waitForX11Event(double* timeout) while (!XPending(_glfw.x11.display)) { - if (!waitForData(&fd, 1, timeout)) + if (!_glfwPollPOSIX(&fd, 1, timeout)) return GLFW_FALSE; } @@ -146,7 +93,7 @@ static GLFWbool waitForAnyEvent(double* timeout) while (!XPending(_glfw.x11.display)) { - if (!waitForData(fds, count, timeout)) + if (!_glfwPollPOSIX(fds, count, timeout)) return GLFW_FALSE; for (int i = 1; i < count; i++) From 7ce1f3e1cf5eb86a17f0c739cfdbcf4094e8e395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 3 Mar 2022 21:40:00 +0100 Subject: [PATCH 18/88] Formatting --- src/wl_window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wl_window.c b/src/wl_window.c index 4dfde7af..8ca6f7b6 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1732,7 +1732,7 @@ const char* _glfwGetClipboardStringWayland(void) // XXX: this is a huge hack, this function shouldn’t be synchronous! handleEvents(NULL); - while (1) + for (;;) { // Grow the clipboard if we need to paste something bigger, there is no // shrink operation yet. From a32cbf6d4febde6343167f19529b8bde8966670d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 3 Mar 2022 21:46:50 +0100 Subject: [PATCH 19/88] Wayland: Fix glfwPostEmptyEvent not always working The display sync requests in glfwPostEmptyEvent could just accumulate as the display was never flushed on secondary threads. This adds a proper flush after each sync request. Fixes #1520 Closes #1521 --- README.md | 1 + src/wl_window.c | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index d1dcb600..bb9cbf19 100644 --- a/README.md +++ b/README.md @@ -298,6 +298,7 @@ information on what to include when reporting a bug. - [Wayland] Bugfix: Full screen window creation did not ignore `GLFW_VISIBLE` - [Wayland] Bugfix: Some keys were reported as wrong key or `GLFW_KEY_UNKNOWN` - [Wayland] Bugfix: Text input did not repeat along with key repeat + - [Wayland] Bugfix: `glfwPostEmptyEvent` sometimes had no effect (#1520,#1521) - [POSIX] Removed use of deprecated function `gettimeofday` - [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled - [WGL] Disabled the DWM swap interval hack for Windows 8 and later (#1072) diff --git a/src/wl_window.c b/src/wl_window.c index 8ca6f7b6..15cc5e5f 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1185,6 +1185,7 @@ void _glfwWaitEventsTimeoutWayland(double timeout) void _glfwPostEmptyEventWayland(void) { wl_display_sync(_glfw.wl.display); + flushDisplay(); } void _glfwGetCursorPosWayland(_GLFWwindow* window, double* xpos, double* ypos) From 71742d9a27a88d898ea13fd415fae1650dc2772e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 3 Mar 2022 22:41:36 +0100 Subject: [PATCH 20/88] Wayland: Improve event processing with timeout If the polling was interrupted by a signal or by incomplete or unrelated data on any file descriptor, handleEvents could return before the full timeout had elapsed. This retries the Wayland prepare-to-read and poll until the full timeout has elapsed or until any event was processed. Unfortunately, due to how the Wayland client API is designed, this also includes the delete_id for the frame callback created by eglSwapBuffers. This means glfwWaitEvents* are still not fully functional on Wayland. See #1911 for more details. --- src/wl_window.c | 47 +++++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/wl_window.c b/src/wl_window.c index 15cc5e5f..54257b25 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -737,6 +737,7 @@ static GLFWbool flushDisplay(void) static void handleEvents(double* timeout) { + GLFWbool event = GLFW_FALSE; struct pollfd fds[] = { { wl_display_get_fd(_glfw.wl.display), POLLIN }, @@ -744,31 +745,38 @@ static void handleEvents(double* timeout) { _glfw.wl.cursorTimerfd, POLLIN }, }; - while (wl_display_prepare_read(_glfw.wl.display) != 0) - wl_display_dispatch_pending(_glfw.wl.display); - - // If an error other than EAGAIN happens, we have likely been disconnected - // from the Wayland session; try to handle that the best we can. - if (!flushDisplay()) + while (!event) { - wl_display_cancel_read(_glfw.wl.display); + while (wl_display_prepare_read(_glfw.wl.display) != 0) + wl_display_dispatch_pending(_glfw.wl.display); - _GLFWwindow* window = _glfw.windowListHead; - while (window) + // If an error other than EAGAIN happens, we have likely been disconnected + // from the Wayland session; try to handle that the best we can. + if (!flushDisplay()) { - _glfwInputWindowCloseRequest(window); - window = window->next; + wl_display_cancel_read(_glfw.wl.display); + + _GLFWwindow* window = _glfw.windowListHead; + while (window) + { + _glfwInputWindowCloseRequest(window); + window = window->next; + } + + return; } - return; - } + if (!_glfwPollPOSIX(fds, 3, timeout)) + { + wl_display_cancel_read(_glfw.wl.display); + return; + } - if (_glfwPollPOSIX(fds, 3, timeout)) - { if (fds[0].revents & POLLIN) { wl_display_read_events(_glfw.wl.display); - wl_display_dispatch_pending(_glfw.wl.display); + if (wl_display_dispatch_pending(_glfw.wl.display) > 0) + event = GLFW_TRUE; } else wl_display_cancel_read(_glfw.wl.display); @@ -789,6 +797,8 @@ static void handleEvents(double* timeout) _glfwInputTextWayland(_glfw.wl.keyboardFocus, _glfw.wl.keyboardLastScancode); } + + event = GLFW_TRUE; } } @@ -797,11 +807,12 @@ static void handleEvents(double* timeout) uint64_t repeats; if (read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)) == 8) + { incrementCursorImage(_glfw.wl.pointerFocus); + event = GLFW_TRUE; + } } } - else - wl_display_cancel_read(_glfw.wl.display); } ////////////////////////////////////////////////////////////////////////// From 1e0c3bca7f6da896828e00f3da606d5e306c3034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 3 Mar 2022 23:15:47 +0100 Subject: [PATCH 21/88] Update version of VS used on windows-latest --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 249e8d68..7f798851 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -73,8 +73,8 @@ jobs: - name: Build shared library run: cmake --build build-shared --parallel - build-windows-win32-vs2019: - name: Win32 (Windows, VS2019) + build-windows-win32-vs2022: + name: Win32 (Windows, VS2022) runs-on: windows-latest env: CFLAGS: /WX @@ -82,12 +82,12 @@ jobs: - uses: actions/checkout@v2 - name: Configure static library - run: cmake -S . -B build-static -G "Visual Studio 16 2019" + run: cmake -S . -B build-static -G "Visual Studio 17 2022" - name: Build static library run: cmake --build build-static --parallel - name: Configure shared library - run: cmake -S . -B build-shared -G "Visual Studio 16 2019" -D BUILD_SHARED_LIBS=ON + run: cmake -S . -B build-shared -G "Visual Studio 17 2022" -D BUILD_SHARED_LIBS=ON - name: Build shared library run: cmake --build build-shared --parallel From d78b0a4ead17e30182761b38dbbdfb88b037287b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 23 Feb 2022 18:47:30 +0100 Subject: [PATCH 22/88] X11: Fix sonames for loaded libraries on NetBSD The NetBSD sonames for X11 and related libraries is more stable than on OpenBSD but the version numbers are still bumped more often than their Linux counterparts, even excluding the one-time version bump across all X11 related libraries. This commit moves to using version-less sonames for X11 and related libraries on NetBSD, which will hopefully be more forward-compatible than hard-coding NetBSD-specific sonames. This may not be the correct long-term solution but it runs now. Binaries also appear to need an LD_LIBRARY_PATH or rpath entry of /usr/X11R7/lib in order for the libraries to be found by dlopen. Tested on NetBSD 9.2. --- README.md | 1 + src/egl_context.c | 8 ++++---- src/glx_context.c | 2 +- src/osmesa_context.c | 2 +- src/vulkan.c | 2 +- src/x11_init.c | 18 +++++++++--------- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index bb9cbf19..8c597048 100644 --- a/README.md +++ b/README.md @@ -274,6 +274,7 @@ information on what to include when reporting a bug. - [X11] Bugfix: `glfwWaitEvents*` did not continue for joystick events - [X11] Bugfix: `glfwPostEmptyEvent` could be ignored due to race condition (#379,#1281,#1285,#2033) + - [X11] Bugfix: Dynamic loading on NetBSD failed due to soname differences - [Wayland] Added dynamic loading of all Wayland libraries - [Wayland] Added support for key names via xkbcommon - [Wayland] Removed support for `wl_shell` (#1443) diff --git a/src/egl_context.c b/src/egl_context.c index 963a59d6..f8850fa2 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -325,7 +325,7 @@ GLFWbool _glfwInitEGL(void) "libEGL.dylib", #elif defined(__CYGWIN__) "libEGL-1.so", -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) "libEGL.so", #else "libEGL.so.1", @@ -702,7 +702,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, "libGLES_CM.dll", #elif defined(_GLFW_COCOA) "libGLESv1_CM.dylib", -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) "libGLESv1_CM.so", #else "libGLESv1_CM.so.1", @@ -721,7 +721,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, "libGLESv2.dylib", #elif defined(__CYGWIN__) "libGLESv2-2.so", -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) "libGLESv2.so", #else "libGLESv2.so.2", @@ -734,7 +734,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, _GLFW_OPENGL_LIBRARY, #elif defined(_GLFW_WIN32) #elif defined(_GLFW_COCOA) -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) "libGL.so", #else "libGL.so.1", diff --git a/src/glx_context.c b/src/glx_context.c index ced9c88f..872612d1 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -259,7 +259,7 @@ GLFWbool _glfwInitGLX(void) _GLFW_GLX_LIBRARY, #elif defined(__CYGWIN__) "libGL-1.so", -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) "libGL.so", #else "libGL.so.1", diff --git a/src/osmesa_context.c b/src/osmesa_context.c index 43ade8dc..38adabbc 100644 --- a/src/osmesa_context.c +++ b/src/osmesa_context.c @@ -124,7 +124,7 @@ GLFWbool _glfwInitOSMesa(void) "libOSMesa.8.dylib", #elif defined(__CYGWIN__) "libOSMesa-8.so", -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) "libOSMesa.so", #else "libOSMesa.so.8", diff --git a/src/vulkan.c b/src/vulkan.c index f02b1ede..64a4650f 100644 --- a/src/vulkan.c +++ b/src/vulkan.c @@ -63,7 +63,7 @@ GLFWbool _glfwInitVulkan(int mode) _glfw.vk.handle = _glfwPlatformLoadModule("libvulkan.1.dylib"); if (!_glfw.vk.handle) _glfw.vk.handle = _glfwLoadLocalVulkanLoaderCocoa(); -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.vk.handle = _glfwPlatformLoadModule("libvulkan.so"); #else _glfw.vk.handle = _glfwPlatformLoadModule("libvulkan.so.1"); diff --git a/src/x11_init.c b/src/x11_init.c index 203828fc..78735d69 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -603,7 +603,7 @@ static void detectEWMH(void) // static GLFWbool initExtensions(void) { -#if defined(__OpenBSD__) +#if defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.vidmode.handle = _glfwPlatformLoadModule("libXxf86vm.so"); #else _glfw.x11.vidmode.handle = _glfwPlatformLoadModule("libXxf86vm.so.1"); @@ -627,7 +627,7 @@ static GLFWbool initExtensions(void) #if defined(__CYGWIN__) _glfw.x11.xi.handle = _glfwPlatformLoadModule("libXi-6.so"); -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.xi.handle = _glfwPlatformLoadModule("libXi.so"); #else _glfw.x11.xi.handle = _glfwPlatformLoadModule("libXi.so.6"); @@ -659,7 +659,7 @@ static GLFWbool initExtensions(void) #if defined(__CYGWIN__) _glfw.x11.randr.handle = _glfwPlatformLoadModule("libXrandr-2.so"); -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.randr.handle = _glfwPlatformLoadModule("libXrandr.so"); #else _glfw.x11.randr.handle = _glfwPlatformLoadModule("libXrandr.so.2"); @@ -753,7 +753,7 @@ static GLFWbool initExtensions(void) #if defined(__CYGWIN__) _glfw.x11.xcursor.handle = _glfwPlatformLoadModule("libXcursor-1.so"); -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.xcursor.handle = _glfwPlatformLoadModule("libXcursor.so"); #else _glfw.x11.xcursor.handle = _glfwPlatformLoadModule("libXcursor.so.1"); @@ -776,7 +776,7 @@ static GLFWbool initExtensions(void) #if defined(__CYGWIN__) _glfw.x11.xinerama.handle = _glfwPlatformLoadModule("libXinerama-1.so"); -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.xinerama.handle = _glfwPlatformLoadModule("libXinerama.so"); #else _glfw.x11.xinerama.handle = _glfwPlatformLoadModule("libXinerama.so.1"); @@ -831,7 +831,7 @@ static GLFWbool initExtensions(void) { #if defined(__CYGWIN__) _glfw.x11.x11xcb.handle = _glfwPlatformLoadModule("libX11-xcb-1.so"); -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.x11xcb.handle = _glfwPlatformLoadModule("libX11-xcb.so"); #else _glfw.x11.x11xcb.handle = _glfwPlatformLoadModule("libX11-xcb.so.1"); @@ -846,7 +846,7 @@ static GLFWbool initExtensions(void) #if defined(__CYGWIN__) _glfw.x11.xrender.handle = _glfwPlatformLoadModule("libXrender-1.so"); -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.xrender.handle = _glfwPlatformLoadModule("libXrender.so"); #else _glfw.x11.xrender.handle = _glfwPlatformLoadModule("libXrender.so.1"); @@ -875,7 +875,7 @@ static GLFWbool initExtensions(void) #if defined(__CYGWIN__) _glfw.x11.xshape.handle = _glfwPlatformLoadModule("libXext-6.so"); -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.xshape.handle = _glfwPlatformLoadModule("libXext.so"); #else _glfw.x11.xshape.handle = _glfwPlatformLoadModule("libXext.so.6"); @@ -1254,7 +1254,7 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform) #if defined(__CYGWIN__) void* module = _glfwPlatformLoadModule("libX11-6.so"); -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__NetBSD__) void* module = _glfwPlatformLoadModule("libX11.so"); #else void* module = _glfwPlatformLoadModule("libX11.so.6"); From adc202d2c3182ca6ad8344624941e56d8e0bc493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 4 Mar 2022 13:37:38 +0100 Subject: [PATCH 23/88] POSIX: Fix undeclared function warning on Cygwin --- src/posix_poll.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/posix_poll.c b/src/posix_poll.c index 352d9900..676a8a51 100644 --- a/src/posix_poll.c +++ b/src/posix_poll.c @@ -26,9 +26,7 @@ // It is fine to use C99 in this file because it will not be built with VS //======================================================================== -#if defined(__linux__) - #define _GNU_SOURCE -#endif +#define _GNU_SOURCE #include "internal.h" From a730acf8e50e7efa70e79e42c511a97f27c97e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 7 Mar 2022 18:14:16 +0100 Subject: [PATCH 24/88] Win32: Fix rect of undecorated maximized windows A window created maximized and undecorated would cover the whole monitor Windows placed it on instead of just that monitor's workarea. This commit adjusts the maximized rect to cover just the workarea, similar to how undecorated windows that become maximized are handled during WM_GETMINMAXINFO. Fixes #1806 --- README.md | 2 ++ src/win32_window.c | 29 ++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8c597048..f4ef3791 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,8 @@ information on what to include when reporting a bug. disconnected (#1615) - [Win32] Bugfix: Key name update modified global key state on Windows 10 1607 and later (#2018) + - [Win32] Bugfix: A window created maximized and undecorated would cover the whole + monitor (#1806) - [Cocoa] Added support for `VK_EXT_metal_surface` (#1619) - [Cocoa] Added locating the Vulkan loader at runtime in an application bundle - [Cocoa] Moved main menu creation to GLFW initialization time (#1649) diff --git a/src/win32_window.c b/src/win32_window.c index b3da6b2a..c8f7782e 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1292,18 +1292,22 @@ static int createNativeWindow(_GLFWwindow* window, window->win32.scaleToMonitor = wndconfig->scaleToMonitor; window->win32.keymenu = wndconfig->win32.keymenu; - // Adjust window rect to account for DPI scaling of the window frame and - // (if enabled) DPI scaling of the content area - // This cannot be done until we know what monitor the window was placed on if (!window->monitor) { RECT rect = { 0, 0, wndconfig->width, wndconfig->height }; WINDOWPLACEMENT wp = { sizeof(wp) }; + const HMONITOR mh = MonitorFromWindow(window->win32.handle, + MONITOR_DEFAULTTONEAREST); + + // Adjust window rect to account for DPI scaling of the window frame and + // (if enabled) DPI scaling of the content area + // This cannot be done until we know what monitor the window was placed on + // Only update the restored window rect as the window may be maximized if (wndconfig->scaleToMonitor) { float xscale, yscale; - _glfwGetWindowContentScaleWin32(window, &xscale, &yscale); + _glfwGetHMONITORContentScaleWin32(mh, &xscale, &yscale); if (xscale > 0.f && yscale > 0.f) { @@ -1323,11 +1327,26 @@ static int createNativeWindow(_GLFWwindow* window, else AdjustWindowRectEx(&rect, style, FALSE, exStyle); - // Only update the restored window rect as the window may be maximized GetWindowPlacement(window->win32.handle, &wp); wp.rcNormalPosition = rect; wp.showCmd = SW_HIDE; SetWindowPlacement(window->win32.handle, &wp); + + // Adjust rect of maximized undecorated window, because by default Windows will + // make such a window cover the whole monitor instead of its workarea + + if (wndconfig->maximized && !wndconfig->decorated) + { + MONITORINFO mi = { sizeof(mi) }; + GetMonitorInfo(mh, &mi); + + SetWindowPos(window->win32.handle, HWND_TOP, + mi.rcWork.left, + mi.rcWork.top, + mi.rcWork.right - mi.rcWork.left, + mi.rcWork.bottom - mi.rcWork.top, + SWP_NOACTIVATE | SWP_NOZORDER); + } } DragAcceptFiles(window->win32.handle, TRUE); From 367d06deafb00057850796abc02489756a7c6c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 7 Mar 2022 19:19:31 +0100 Subject: [PATCH 25/88] Win32: Fix scale fixup losing initial position The window content scale correction at creation overwrote the inital, more pleasant placement of the window by CW_USEDEFAULT, if the window was created with GLFW_MAXIMIZED set. This is because the translation to screen coordinates was done using the current position, not the position from the restored window rect. --- README.md | 2 ++ src/win32_window.c | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f4ef3791..cc3e3ccc 100644 --- a/README.md +++ b/README.md @@ -209,6 +209,8 @@ information on what to include when reporting a bug. and later (#2018) - [Win32] Bugfix: A window created maximized and undecorated would cover the whole monitor (#1806) + - [Win32] Bugfix: The default restored window position was lost when creating a maximized + window - [Cocoa] Added support for `VK_EXT_metal_surface` (#1619) - [Cocoa] Added locating the Vulkan loader at runtime in an application bundle - [Cocoa] Moved main menu creation to GLFW initialization time (#1649) diff --git a/src/win32_window.c b/src/win32_window.c index c8f7782e..0f76cf56 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1316,9 +1316,6 @@ static int createNativeWindow(_GLFWwindow* window, } } - ClientToScreen(window->win32.handle, (POINT*) &rect.left); - ClientToScreen(window->win32.handle, (POINT*) &rect.right); - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, @@ -1328,6 +1325,10 @@ static int createNativeWindow(_GLFWwindow* window, AdjustWindowRectEx(&rect, style, FALSE, exStyle); GetWindowPlacement(window->win32.handle, &wp); + OffsetRect(&rect, + wp.rcNormalPosition.left - rect.left, + wp.rcNormalPosition.top - rect.top); + wp.rcNormalPosition = rect; wp.showCmd = SW_HIDE; SetWindowPlacement(window->win32.handle, &wp); From 1eef3a363e2f64718c82122c5d48bff44c4b2e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 10 Mar 2022 19:14:49 +0100 Subject: [PATCH 26/88] Fix dependency list for X11 on Cygwin --- docs/compile.dox | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/compile.dox b/docs/compile.dox index 05da99cf..925ab1ab 100644 --- a/docs/compile.dox +++ b/docs/compile.dox @@ -68,8 +68,10 @@ install the `xorgproto` package. pkg install xorgproto @endcode -On Cygwin the `xorgproto` package in the Devel section of the GUI installer will -install the headers and other development related files for all of X11. +On Cygwin the `libXcursor-devel`, `libXi-devel`, `libXinerama-devel`, +`libXrandr-devel` and `libXrender-devel` packages in the Libs section of the GUI +installer will install all the headers and other development related files GLFW +requires for X11. Once you have the required dependencies, move on to @ref compile_generate. From 723f3eb40db27a26c846f0d38ee24af3365ffa96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 8 Mar 2022 23:00:47 +0100 Subject: [PATCH 27/88] Win32: Fix maximization showing a hidden window The normal way of maximizing a window also makes it visible. This implements window maximization manually for when the window passed to glfwMaximizeWindow is hidden. This will very likely not be forward-compatible and should be replaced. --- README.md | 1 + src/win32_init.c | 2 ++ src/win32_platform.h | 3 +++ src/win32_window.c | 58 +++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 63 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cc3e3ccc..f3bff91d 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,7 @@ information on what to include when reporting a bug. monitor (#1806) - [Win32] Bugfix: The default restored window position was lost when creating a maximized window + - [Win32] Bugfix: `glfwMaximizeWindow` would make a hidden window visible - [Cocoa] Added support for `VK_EXT_metal_surface` (#1619) - [Cocoa] Added locating the Vulkan loader at runtime in an application bundle - [Cocoa] Moved main menu creation to GLFW initialization time (#1649) diff --git a/src/win32_init.c b/src/win32_init.c index 0479afe5..dfaf3d64 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -91,6 +91,8 @@ static GLFWbool loadLibraries(void) _glfwPlatformGetModuleSymbol(_glfw.win32.user32.instance, "GetDpiForWindow"); _glfw.win32.user32.AdjustWindowRectExForDpi_ = (PFN_AdjustWindowRectExForDpi) _glfwPlatformGetModuleSymbol(_glfw.win32.user32.instance, "AdjustWindowRectExForDpi"); + _glfw.win32.user32.GetSystemMetricsForDpi_ = (PFN_GetSystemMetricsForDpi) + _glfwPlatformGetModuleSymbol(_glfw.win32.user32.instance, "GetSystemMetricsForDpi"); _glfw.win32.dinput8.instance = _glfwPlatformLoadModule("dinput8.dll"); if (_glfw.win32.dinput8.instance) diff --git a/src/win32_platform.h b/src/win32_platform.h index a7130377..2e84937a 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -283,12 +283,14 @@ typedef BOOL (WINAPI * PFN_EnableNonClientDpiScaling)(HWND); typedef BOOL (WINAPI * PFN_SetProcessDpiAwarenessContext)(HANDLE); typedef UINT (WINAPI * PFN_GetDpiForWindow)(HWND); typedef BOOL (WINAPI * PFN_AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT); +typedef int (WINAPI * PFN_GetSystemMetricsForDpi)(int,UINT); #define SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware_ #define ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx_ #define EnableNonClientDpiScaling _glfw.win32.user32.EnableNonClientDpiScaling_ #define SetProcessDpiAwarenessContext _glfw.win32.user32.SetProcessDpiAwarenessContext_ #define GetDpiForWindow _glfw.win32.user32.GetDpiForWindow_ #define AdjustWindowRectExForDpi _glfw.win32.user32.AdjustWindowRectExForDpi_ +#define GetSystemMetricsForDpi _glfw.win32.user32.GetSystemMetricsForDpi_ // dwmapi.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); @@ -471,6 +473,7 @@ typedef struct _GLFWlibraryWin32 PFN_SetProcessDpiAwarenessContext SetProcessDpiAwarenessContext_; PFN_GetDpiForWindow GetDpiForWindow_; PFN_AdjustWindowRectExForDpi AdjustWindowRectExForDpi_; + PFN_GetSystemMetricsForDpi GetSystemMetricsForDpi_; } user32; struct { diff --git a/src/win32_window.c b/src/win32_window.c index 0f76cf56..400c50c0 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -484,6 +484,59 @@ static void releaseMonitor(_GLFWwindow* window) _glfwRestoreVideoModeWin32(window->monitor); } +// Manually maximize the window, for when SW_MAXIMIZE cannot be used +// +static void maximizeWindowManually(_GLFWwindow* window) +{ + RECT rect; + DWORD style; + MONITORINFO mi = { sizeof(mi) }; + + GetMonitorInfo(MonitorFromWindow(window->win32.handle, + MONITOR_DEFAULTTONEAREST), &mi); + + rect = mi.rcWork; + + if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE) + { + if (rect.right - rect.left > window->maxwidth) + rect.right = rect.left + window->maxwidth; + if (rect.bottom - rect.top > window->maxheight) + rect.bottom = rect.top + window->maxheight; + } + + style = GetWindowLongW(window->win32.handle, GWL_STYLE); + style |= WS_MAXIMIZE; + SetWindowLongW(window->win32.handle, GWL_STYLE, style); + + if (window->decorated) + { + const DWORD exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + const UINT dpi = GetDpiForWindow(window->win32.handle); + AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi); + OffsetRect(&rect, 0, GetSystemMetricsForDpi(SM_CYCAPTION, dpi)); + } + else + { + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + OffsetRect(&rect, 0, GetSystemMetrics(SM_CYCAPTION)); + } + + if (rect.bottom > mi.rcWork.bottom) + rect.bottom = mi.rcWork.bottom; + } + + SetWindowPos(window->win32.handle, HWND_TOP, + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED); +} + // Window callback function (handles window messages) // static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, @@ -1688,7 +1741,10 @@ void _glfwRestoreWindowWin32(_GLFWwindow* window) void _glfwMaximizeWindowWin32(_GLFWwindow* window) { - ShowWindow(window->win32.handle, SW_MAXIMIZE); + if (IsWindowVisible(window->win32.handle)) + ShowWindow(window->win32.handle, SW_MAXIMIZE); + else + maximizeWindowManually(window); } void _glfwShowWindowWin32(_GLFWwindow* window) From 8ff9ed92b446831e76b434c601b3d1b0d4220c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 8 Mar 2022 23:45:53 +0100 Subject: [PATCH 28/88] Win32: Fix calls to encoding compatibility macros Calls to Unicode specific functions should be made explicit. --- src/win32_init.c | 2 +- src/win32_joystick.c | 2 +- src/win32_monitor.c | 2 +- src/win32_window.c | 26 +++++++++++++------------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/win32_init.c b/src/win32_init.c index dfaf3d64..dce4e682 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -490,7 +490,7 @@ void _glfwUpdateKeyNamesWin32(void) vk = vks[key - GLFW_KEY_KP_0]; } else - vk = MapVirtualKey(scancode, MAPVK_VSC_TO_VK); + vk = MapVirtualKeyW(scancode, MAPVK_VSC_TO_VK); length = ToUnicode(vk, scancode, state, chars, sizeof(chars) / sizeof(WCHAR), diff --git a/src/win32_joystick.c b/src/win32_joystick.c index e45e0798..929a19cf 100644 --- a/src/win32_joystick.c +++ b/src/win32_joystick.c @@ -574,7 +574,7 @@ GLFWbool _glfwInitJoysticksWin32(void) { if (_glfw.win32.dinput8.instance) { - if (FAILED(DirectInput8Create(GetModuleHandle(NULL), + if (FAILED(DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**) &_glfw.win32.dinput8.api, diff --git a/src/win32_monitor.c b/src/win32_monitor.c index cdee49a9..57b44af3 100644 --- a/src/win32_monitor.c +++ b/src/win32_monitor.c @@ -381,7 +381,7 @@ void _glfwGetMonitorWorkareaWin32(_GLFWmonitor* monitor, int* width, int* height) { MONITORINFO mi = { sizeof(mi) }; - GetMonitorInfo(monitor->win32.handle, &mi); + GetMonitorInfoW(monitor->win32.handle, &mi); if (xpos) *xpos = mi.rcWork.left; diff --git a/src/win32_window.c b/src/win32_window.c index 400c50c0..42518934 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -434,7 +434,7 @@ static int getKeyMods(void) static void fitToMonitor(_GLFWwindow* window) { MONITORINFO mi = { sizeof(mi) }; - GetMonitorInfo(window->monitor->win32.handle, &mi); + GetMonitorInfoW(window->monitor->win32.handle, &mi); SetWindowPos(window->win32.handle, HWND_TOPMOST, mi.rcMonitor.left, mi.rcMonitor.top, @@ -453,8 +453,8 @@ static void acquireMonitor(_GLFWwindow* window) // HACK: When mouse trails are enabled the cursor becomes invisible when // the OpenGL ICD switches to page flipping - SystemParametersInfo(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0); - SystemParametersInfo(SPI_SETMOUSETRAILS, 0, 0, 0); + SystemParametersInfoW(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0); + SystemParametersInfoW(SPI_SETMOUSETRAILS, 0, 0, 0); } if (!window->monitor->window) @@ -477,7 +477,7 @@ static void releaseMonitor(_GLFWwindow* window) SetThreadExecutionState(ES_CONTINUOUS); // HACK: Restore mouse trail length saved in acquireMonitor - SystemParametersInfo(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0); + SystemParametersInfoW(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0); } _glfwInputMonitorWindow(window->monitor, NULL); @@ -492,8 +492,8 @@ static void maximizeWindowManually(_GLFWwindow* window) DWORD style; MONITORINFO mi = { sizeof(mi) }; - GetMonitorInfo(MonitorFromWindow(window->win32.handle, - MONITOR_DEFAULTTONEAREST), &mi); + GetMonitorInfoW(MonitorFromWindow(window->win32.handle, + MONITOR_DEFAULTTONEAREST), &mi); rect = mi.rcWork; @@ -1126,7 +1126,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, ZeroMemory(&mi, sizeof(mi)); mi.cbSize = sizeof(mi); - GetMonitorInfo(mh, &mi); + GetMonitorInfoW(mh, &mi); mmi->ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left; mmi->ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top; @@ -1392,7 +1392,7 @@ static int createNativeWindow(_GLFWwindow* window, if (wndconfig->maximized && !wndconfig->decorated) { MONITORINFO mi = { sizeof(mi) }; - GetMonitorInfo(mh, &mi); + GetMonitorInfoW(mh, &mi); SetWindowPos(window->win32.handle, HWND_TOP, mi.rcWork.left, @@ -1561,8 +1561,8 @@ void _glfwSetWindowIconWin32(_GLFWwindow* window, smallIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICONSM); } - SendMessage(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon); - SendMessage(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon); + SendMessageW(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon); + SendMessageW(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon); if (window->win32.bigIcon) DestroyIcon(window->win32.bigIcon); @@ -1831,7 +1831,7 @@ void _glfwSetWindowMonitorWin32(_GLFWwindow* window, acquireMonitor(window); - GetMonitorInfo(window->monitor->win32.handle, &mi); + GetMonitorInfoW(window->monitor->win32.handle, &mi); SetWindowPos(window->win32.handle, HWND_TOPMOST, mi.rcMonitor.left, mi.rcMonitor.top, @@ -2128,7 +2128,7 @@ void _glfwWaitEventsTimeoutWin32(double timeout) void _glfwPostEmptyEventWin32(void) { - PostMessage(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0); + PostMessageW(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0); } void _glfwGetCursorPosWin32(_GLFWwindow* window, double* xpos, double* ypos) @@ -2445,7 +2445,7 @@ VkResult _glfwCreateWindowSurfaceWin32(VkInstance instance, memset(&sci, 0, sizeof(sci)); sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - sci.hinstance = GetModuleHandle(NULL); + sci.hinstance = GetModuleHandleW(NULL); sci.hwnd = window->win32.handle; err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface); From 34573798f4dbbce4bc169ccbfaf9cd189c76ed10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 9 Mar 2022 17:30:06 +0100 Subject: [PATCH 29/88] Win32: Rename Windows 10 macros for clarity This switches the Windows 10 version helper macros to include the version numbers listed in MSDN requirements sections. --- src/win32_init.c | 4 ++-- src/win32_platform.h | 6 ++++-- src/win32_window.c | 28 ++++++++++++++-------------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/win32_init.c b/src/win32_init.c index dce4e682..27527dd1 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -463,7 +463,7 @@ void _glfwUpdateKeyNamesWin32(void) UINT flags = 0; // Avoid modifying the global key state if supported - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) flags = (1 << 2); memset(_glfw.win32.keynames, 0, sizeof(_glfw.win32.keynames)); @@ -633,7 +633,7 @@ int _glfwInitWin32(void) createKeyTables(); _glfwUpdateKeyNamesWin32(); - if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1703OrGreaterWin32()) SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); else if (IsWindows8Point1OrGreater()) SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); diff --git a/src/win32_platform.h b/src/win32_platform.h index 2e84937a..841cf6a0 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -176,9 +176,11 @@ typedef enum _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WINBLUE), \ LOBYTE(_WIN32_WINNT_WINBLUE), 0) -#define _glfwIsWindows10AnniversaryUpdateOrGreaterWin32() \ +// Windows 10 Anniversary Update +#define _glfwIsWindows10Version1607OrGreaterWin32() \ _glfwIsWindows10BuildOrGreaterWin32(14393) -#define _glfwIsWindows10CreatorsUpdateOrGreaterWin32() \ +// Windows 10 Creators Update +#define _glfwIsWindows10Version1703OrGreaterWin32() \ _glfwIsWindows10BuildOrGreaterWin32(15063) // HACK: Define macros that some xinput.h variants don't diff --git a/src/win32_window.c b/src/win32_window.c index 42518934..4a3bc2e0 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -194,7 +194,7 @@ static void getFullWindowSize(DWORD style, DWORD exStyle, { RECT rect = { 0, 0, contentWidth, contentHeight }; - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi); else AdjustWindowRectEx(&rect, style, FALSE, exStyle); @@ -211,7 +211,7 @@ static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) UINT dpi = USER_DEFAULT_SCREEN_DPI; const float ratio = (float) window->numer / (float) window->denom; - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) dpi = GetDpiForWindow(window->win32.handle); getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), @@ -354,7 +354,7 @@ static void updateWindowStyles(const _GLFWwindow* window) GetClientRect(window->win32.handle, &rect); - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, style, FALSE, getWindowExStyle(window), @@ -513,7 +513,7 @@ static void maximizeWindowManually(_GLFWwindow* window) { const DWORD exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { const UINT dpi = GetDpiForWindow(window->win32.handle); AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi); @@ -552,7 +552,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, { case WM_NCCREATE: { - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { const CREATESTRUCTW* cs = (const CREATESTRUCTW*) lParam; const _GLFWwndconfig* wndconfig = cs->lpCreateParams; @@ -1098,7 +1098,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, if (window->monitor) break; - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) dpi = GetDpiForWindow(window->win32.handle); getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), @@ -1173,7 +1173,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, break; // Adjust the window size to keep the content area size constant - if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1703OrGreaterWin32()) { RECT source = {0}, target = {0}; SIZE* size = (SIZE*) lParam; @@ -1204,7 +1204,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, // need it to compensate for non-client area scaling if (!window->monitor && (window->win32.scaleToMonitor || - _glfwIsWindows10CreatorsUpdateOrGreaterWin32())) + _glfwIsWindows10Version1703OrGreaterWin32())) { RECT* suggested = (RECT*) lParam; SetWindowPos(window->win32.handle, HWND_TOP, @@ -1369,7 +1369,7 @@ static int createNativeWindow(_GLFWwindow* window, } } - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, GetDpiForWindow(window->win32.handle)); @@ -1592,7 +1592,7 @@ void _glfwSetWindowPosWin32(_GLFWwindow* window, int xpos, int ypos) { RECT rect = { xpos, ypos, xpos, ypos }; - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), @@ -1633,7 +1633,7 @@ void _glfwSetWindowSizeWin32(_GLFWwindow* window, int width, int height) { RECT rect = { 0, 0, width, height }; - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), @@ -1700,7 +1700,7 @@ void _glfwGetWindowFrameSizeWin32(_GLFWwindow* window, _glfwGetWindowSizeWin32(window, &width, &height); SetRect(&rect, 0, 0, width, height); - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), @@ -1789,7 +1789,7 @@ void _glfwSetWindowMonitorWin32(_GLFWwindow* window, { RECT rect = { xpos, ypos, xpos + width, ypos + height }; - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), @@ -1860,7 +1860,7 @@ void _glfwSetWindowMonitorWin32(_GLFWwindow* window, else after = HWND_NOTOPMOST; - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), From aa803f7de5cc086b1c335161a50016f23c49882a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 9 Mar 2022 18:18:04 +0100 Subject: [PATCH 30/88] Win32: Update rationale for reimplementation It is true that plain MinGW lacks this header, but that is not the main reason for reimplementing IsWindowsVersionOrGreater. --- src/win32_init.c | 3 ++- src/win32_platform.h | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/win32_init.c b/src/win32_init.c index 27527dd1..7b468841 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -513,7 +513,8 @@ void _glfwUpdateKeyNamesWin32(void) } } -// Replacement for IsWindowsVersionOrGreater as MinGW lacks versionhelpers.h +// Replacement for IsWindowsVersionOrGreater, as we cannot rely on the +// application having a correct embedded manifest // BOOL _glfwIsWindowsVersionOrGreaterWin32(WORD major, WORD minor, WORD sp) { diff --git a/src/win32_platform.h b/src/win32_platform.h index 841cf6a0..0958b076 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -162,7 +162,9 @@ typedef enum #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((HANDLE) -4) #endif /*DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2*/ -// HACK: Define versionhelpers.h functions manually as MinGW lacks the header +// Replacement for versionhelpers.h macros, as we cannot rely on the +// application having a correct embedded manifest +// #define IsWindowsVistaOrGreater() \ _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_VISTA), \ LOBYTE(_WIN32_WINNT_VISTA), 0) From 8f9ff8503e0a3dc143c1bdb19327dd29c3ee2d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 9 Mar 2022 23:06:13 +0100 Subject: [PATCH 31/88] Formatting --- src/win32_window.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/win32_window.c b/src/win32_window.c index 4a3bc2e0..c6351a91 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -97,8 +97,7 @@ static const GLFWimage* chooseImage(int count, const GLFWimage* images, // Creates an RGBA icon or cursor // -static HICON createIcon(const GLFWimage* image, - int xhot, int yhot, GLFWbool icon) +static HICON createIcon(const GLFWimage* image, int xhot, int yhot, GLFWbool icon) { int i; HDC dc; @@ -539,8 +538,7 @@ static void maximizeWindowManually(_GLFWwindow* window) // Window callback function (handles window messages) // -static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, - WPARAM wParam, LPARAM lParam) +static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { _GLFWwindow* window = GetPropW(hWnd, L"GLFW"); if (!window) @@ -1538,8 +1536,7 @@ void _glfwSetWindowTitleWin32(_GLFWwindow* window, const char* title) _glfw_free(wideTitle); } -void _glfwSetWindowIconWin32(_GLFWwindow* window, - int count, const GLFWimage* images) +void _glfwSetWindowIconWin32(_GLFWwindow* window, int count, const GLFWimage* images) { HICON bigIcon = NULL, smallIcon = NULL; @@ -1652,8 +1649,8 @@ void _glfwSetWindowSizeWin32(_GLFWwindow* window, int width, int height) } void _glfwSetWindowSizeLimitsWin32(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight) + int minwidth, int minheight, + int maxwidth, int maxheight) { RECT area; @@ -1691,8 +1688,8 @@ void _glfwGetFramebufferSizeWin32(_GLFWwindow* window, int* width, int* height) } void _glfwGetWindowFrameSizeWin32(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom) + int* left, int* top, + int* right, int* bottom) { RECT rect; int width, height; @@ -2189,8 +2186,8 @@ int _glfwGetKeyScancodeWin32(int key) } int _glfwCreateCursorWin32(_GLFWcursor* cursor, - const GLFWimage* image, - int xhot, int yhot) + const GLFWimage* image, + int xhot, int yhot) { cursor->win32.handle = (HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE); if (!cursor->win32.handle) From b54fb0af10bb0167df67fece984350e66bf569ce Mon Sep 17 00:00:00 2001 From: Slemmie Date: Fri, 20 Aug 2021 06:41:59 +0200 Subject: [PATCH 32/88] X11: Fix undefined behavior in bit shift of int Closes #1951 --- src/x11_window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/x11_window.c b/src/x11_window.c index 6c329e63..dcde3443 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -1267,7 +1267,7 @@ static void processEvent(XEvent *event) // (the server never sends a timestamp of zero) // NOTE: Timestamp difference is compared to handle wrap-around Time diff = event->xkey.time - window->x11.keyPressTimes[keycode]; - if (diff == event->xkey.time || (diff > 0 && diff < (1 << 31))) + if (diff == event->xkey.time || (diff > 0 && diff < ((Time)1 << 31))) { if (keycode) _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); From 1461c59aa2426b25503102e62bc8f4b65e079c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 10 Mar 2022 20:04:56 +0100 Subject: [PATCH 33/88] Update changelog and add credit Related to #1951 --- CONTRIBUTORS.md | 1 + README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index f20422ec..406ebd02 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -188,6 +188,7 @@ video tutorials. - Joao da Silva - Daniel Sieger - Daniel Skorupski + - Slemmie - Anthony Smith - Bradley Smith - Cliff Smolinsky diff --git a/README.md b/README.md index f3bff91d..7560284d 100644 --- a/README.md +++ b/README.md @@ -280,6 +280,7 @@ information on what to include when reporting a bug. - [X11] Bugfix: `glfwPostEmptyEvent` could be ignored due to race condition (#379,#1281,#1285,#2033) - [X11] Bugfix: Dynamic loading on NetBSD failed due to soname differences + - [X11] Bugfix: Left shift of int constant relied on undefined behavior (#1951) - [Wayland] Added dynamic loading of all Wayland libraries - [Wayland] Added support for key names via xkbcommon - [Wayland] Removed support for `wl_shell` (#1443) From 46950a5e61ae6febb6dd554aea212f9130927b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sun, 13 Mar 2022 15:18:39 +0100 Subject: [PATCH 34/88] Fix button field names in input guide Fixes #2056 --- CONTRIBUTORS.md | 1 + docs/input.dox | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 406ebd02..88645126 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -30,6 +30,7 @@ video tutorials. - David Carlier - Arturo Castro - Chi-kwan Chan + - TheChocolateOre - Joseph Chua - Ian Clarkson - Michał Cichoń diff --git a/docs/input.dox b/docs/input.dox index dcd8f670..faa94cd4 100644 --- a/docs/input.dox +++ b/docs/input.dox @@ -821,7 +821,7 @@ The second value is always the human-readable name of the gamepad. All subsequent values are in the form `:` and describe the layout of the mapping. These fields may not all be present and may occur in any order. -The button fields are `a`, `b`, `c`, `d`, `back`, `start`, `guide`, `dpup`, +The button fields are `a`, `b`, `x`, `y`, `back`, `start`, `guide`, `dpup`, `dpright`, `dpdown`, `dpleft`, `leftshoulder`, `rightshoulder`, `leftstick` and `rightstick`. From 9cc252a406b79c31ab82648a21b715e2d3fc7ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sun, 13 Mar 2022 15:57:56 +0100 Subject: [PATCH 35/88] Revert ad01c1b614868c3cbc79306aa6a19c9fc06f34a6 This change broke key names for dead keys, because that depends on the effect on the global state of simulating two key presses. Issue found by CTest tests. Reverts #2018 --- README.md | 2 -- src/win32_init.c | 9 ++------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 7560284d..715a5248 100644 --- a/README.md +++ b/README.md @@ -205,8 +205,6 @@ information on what to include when reporting a bug. - [Win32] Bugfix: Content scale queries could fail silently (#1615) - [Win32] Bugfix: Content scales could have garbage values if monitor was recently disconnected (#1615) - - [Win32] Bugfix: Key name update modified global key state on Windows 10 1607 - and later (#2018) - [Win32] Bugfix: A window created maximized and undecorated would cover the whole monitor (#1806) - [Win32] Bugfix: The default restored window position was lost when creating a maximized diff --git a/src/win32_init.c b/src/win32_init.c index 7b468841..ee3dad54 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -460,11 +460,6 @@ void _glfwUpdateKeyNamesWin32(void) { int key; BYTE state[256] = {0}; - UINT flags = 0; - - // Avoid modifying the global key state if supported - if (_glfwIsWindows10Version1607OrGreaterWin32()) - flags = (1 << 2); memset(_glfw.win32.keynames, 0, sizeof(_glfw.win32.keynames)); @@ -494,13 +489,13 @@ void _glfwUpdateKeyNamesWin32(void) length = ToUnicode(vk, scancode, state, chars, sizeof(chars) / sizeof(WCHAR), - flags); + 0); if (length == -1) { length = ToUnicode(vk, scancode, state, chars, sizeof(chars) / sizeof(WCHAR), - flags); + 0); } if (length < 1) From 3ee5031fd7dcac5ecb7387d3dd73195f03e87398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 14 Mar 2022 16:01:34 +0100 Subject: [PATCH 36/88] Add shared min and max functions for int --- src/init.c | 10 ++++++++++ src/internal.h | 2 ++ src/null_window.c | 16 ++++++++-------- src/win32_window.c | 9 +++------ src/wl_init.c | 9 ++------- src/wl_window.c | 6 +----- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/init.c b/src/init.c index 80f424a8..9f0316ac 100644 --- a/src/init.c +++ b/src/init.c @@ -179,6 +179,16 @@ char* _glfw_strdup(const char* source) return result; } +int _glfw_min(int a, int b) +{ + return a < b ? a : b; +} + +int _glfw_max(int a, int b) +{ + return a > b ? a : b; +} + float _glfw_fminf(float a, float b) { if (a != a) diff --git a/src/internal.h b/src/internal.h index f8548fa3..b7dc13a0 100644 --- a/src/internal.h +++ b/src/internal.h @@ -998,6 +998,8 @@ const char* _glfwGetVulkanResultString(VkResult result); size_t _glfwEncodeUTF8(char* s, uint32_t codepoint); char* _glfw_strdup(const char* source); +int _glfw_min(int a, int b); +int _glfw_max(int a, int b); float _glfw_fminf(float a, float b); float _glfw_fmaxf(float a, float b); diff --git a/src/null_window.c b/src/null_window.c index 710d4c9c..7e87b9da 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -39,15 +39,15 @@ static void applySizeLimits(_GLFWwindow* window, int* width, int* height) *height = (int) (*width / ratio); } - if (window->minwidth != GLFW_DONT_CARE && *width < window->minwidth) - *width = window->minwidth; - else if (window->maxwidth != GLFW_DONT_CARE && *width > window->maxwidth) - *width = window->maxwidth; + if (window->minwidth != GLFW_DONT_CARE) + *width = _glfw_max(*width, window->minwidth); + else if (window->maxwidth != GLFW_DONT_CARE) + *width = _glfw_min(*width, window->maxwidth); - if (window->minheight != GLFW_DONT_CARE && *height < window->minheight) - *height = window->minheight; - else if (window->maxheight != GLFW_DONT_CARE && *height > window->maxheight) - *height = window->maxheight; + if (window->minheight != GLFW_DONT_CARE) + *height = _glfw_min(*height, window->minheight); + else if (window->maxheight != GLFW_DONT_CARE) + *height = _glfw_max(*height, window->maxheight); } static void fitToMonitor(_GLFWwindow* window) diff --git a/src/win32_window.c b/src/win32_window.c index c6351a91..5448eddc 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -498,10 +498,8 @@ static void maximizeWindowManually(_GLFWwindow* window) if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE) { - if (rect.right - rect.left > window->maxwidth) - rect.right = rect.left + window->maxwidth; - if (rect.bottom - rect.top > window->maxheight) - rect.bottom = rect.top + window->maxheight; + rect.right = _glfw_min(rect.right, rect.left + window->maxwidth); + rect.bottom = _glfw_min(rect.bottom, rect.top + window->maxheight); } style = GetWindowLongW(window->win32.handle, GWL_STYLE); @@ -524,8 +522,7 @@ static void maximizeWindowManually(_GLFWwindow* window) OffsetRect(&rect, 0, GetSystemMetrics(SM_CYCAPTION)); } - if (rect.bottom > mi.rcWork.bottom) - rect.bottom = mi.rcWork.bottom; + rect.bottom = _glfw_min(rect.bottom, mi.rcWork.bottom); } SetWindowPos(window->win32.handle, HWND_TOP, diff --git a/src/wl_init.c b/src/wl_init.c index e4ae6666..f02c6320 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -57,11 +57,6 @@ #include "wayland-idle-inhibit-unstable-v1-client-protocol-code.h" -static inline int min(int n1, int n2) -{ - return n1 < n2 ? n1 : n2; -} - static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, int* which) { @@ -788,7 +783,7 @@ static void registryHandleGlobal(void* data, { if (strcmp(interface, "wl_compositor") == 0) { - _glfw.wl.compositorVersion = min(3, version); + _glfw.wl.compositorVersion = _glfw_min(3, version); _glfw.wl.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, _glfw.wl.compositorVersion); @@ -811,7 +806,7 @@ static void registryHandleGlobal(void* data, { if (!_glfw.wl.seat) { - _glfw.wl.seatVersion = min(4, version); + _glfw.wl.seatVersion = _glfw_min(4, version); _glfw.wl.seat = wl_registry_bind(registry, name, &wl_seat_interface, _glfw.wl.seatVersion); diff --git a/src/wl_window.c b/src/wl_window.c index 54257b25..8c47d710 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -353,11 +353,7 @@ static void checkScaleChange(_GLFWwindow* window) int maxScale = 1; for (int i = 0; i < window->wl.monitorsCount; i++) - { - const int scale = window->wl.monitors[i]->wl.scale; - if (maxScale < scale) - maxScale = scale; - } + maxScale = _glfw_max(window->wl.monitors[i]->wl.scale, maxScale); // Only change the framebuffer size if the scale changed. if (window->wl.scale != maxScale) From 535c3ce63240ee91f998219b35dc82abef545c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 15 Mar 2022 17:51:57 +0100 Subject: [PATCH 37/88] Fix GLAPIENTRY redefinition warning On a Unix system, if you define GLFW_INCLUDE_NONE and GLFW_EXPOSE_NATIVE_GLX, then include glfw3.h and glfw3native.h, you will get a redefinition warning for GLAPIENTRY. The glfw3.h header defines GLAPIENTRY as a service for OpenGL related headers that assume it's already defined. However, glx.h includes gl.h, which defines GLAPIENTRY unconditionally. If not for Hyrum's law, the better solution would have been not to define GLAPIENTRY if GLFW_INCLUDE_NONE is defined. Fixes #2010 --- include/GLFW/glfw3native.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/GLFW/glfw3native.h b/include/GLFW/glfw3native.h index 41b2f86b..da4b7572 100644 --- a/include/GLFW/glfw3native.h +++ b/include/GLFW/glfw3native.h @@ -111,12 +111,22 @@ extern "C" { /* NSGL is declared by Cocoa.h */ #endif #if defined(GLFW_EXPOSE_NATIVE_GLX) + /* This is a workaround for the fact that glfw3.h defines GLAPIENTRY because by + * default it also acts as an OpenGL header + * However, glx.h will include gl.h, which will define it unconditionally + */ + #undef GLAPIENTRY #include #endif #if defined(GLFW_EXPOSE_NATIVE_EGL) #include #endif #if defined(GLFW_EXPOSE_NATIVE_OSMESA) + /* This is a workaround for the fact that glfw3.h defines GLAPIENTRY because by + * default it also acts as an OpenGL header + * However, osmesa.h will include gl.h, which will define it unconditionally + */ + #undef GLAPIENTRY #include #endif From add0521efb1fc0e95082716748b7aad837753fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 15 Mar 2022 17:59:57 +0100 Subject: [PATCH 38/88] Fix accidental C99 in C89 header --- include/GLFW/glfw3native.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/GLFW/glfw3native.h b/include/GLFW/glfw3native.h index da4b7572..11fee09d 100644 --- a/include/GLFW/glfw3native.h +++ b/include/GLFW/glfw3native.h @@ -82,9 +82,10 @@ extern "C" { *************************************************************************/ #if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL) - // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for - // example to allow applications to correctly declare a GL_KHR_debug callback) - // but windows.h assumes no one will define APIENTRY before it does + /* This is a workaround for the fact that glfw3.h needs to export APIENTRY (for + * example to allow applications to correctly declare a GL_KHR_debug callback) + * but windows.h assumes no one will define APIENTRY before it does + */ #if defined(GLFW_APIENTRY_DEFINED) #undef APIENTRY #undef GLFW_APIENTRY_DEFINED From 03cfe957e739e41cdfd66b554c076b5b31f48c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 15 Mar 2022 19:12:29 +0100 Subject: [PATCH 39/88] Win32: Fix scancode and key for Alt+PrtSc events Alt+PrtSc emits a different scancode than just PrtSc. Since the GLFW API assumes each key corresponds to only one scancode, this cannot be added to the keycodes array. Instead we replace the scancode at the point of entry. Fixes #1993 --- CONTRIBUTORS.md | 1 + README.md | 2 ++ src/win32_window.c | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 88645126..bb70c1f5 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -70,6 +70,7 @@ video tutorials. - Ryan C. Gordon - Stephen Gowen - Kovid Goyal + - Kevin Grandemange - Eloi Marín Gratacós - Stefan Gustavson - Andrew Gutekanst diff --git a/README.md b/README.md index 715a5248..e81997ba 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,8 @@ information on what to include when reporting a bug. - [Win32] Bugfix: The default restored window position was lost when creating a maximized window - [Win32] Bugfix: `glfwMaximizeWindow` would make a hidden window visible + - [Win32] Bugfix: `Alt+PrtSc` would emit `GLFW_KEY_UNKNOWN` and a different + scancode than `PrtSc` (#1993) - [Cocoa] Added support for `VK_EXT_metal_surface` (#1619) - [Cocoa] Added locating the Vulkan loader at runtime in an application bundle - [Cocoa] Moved main menu creation to GLFW initialization time (#1649) diff --git a/src/win32_window.c b/src/win32_window.c index 5448eddc..1d207d8b 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -752,6 +752,10 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l scancode = MapVirtualKeyW((UINT) wParam, MAPVK_VK_TO_VSC); } + // HACK: Alt+PrtSc has a different scancode than just PrtSc + if (scancode == 0x54) + scancode = 0x137; + key = _glfw.win32.keycodes[scancode]; // The Ctrl keys require special handling From 8d9231fe5ec82c3a0a7bddfcd3a409759a7a45d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 15 Mar 2022 19:22:21 +0100 Subject: [PATCH 40/88] Win32: Fix glfwGetKeyScancode for GLFW_KEY_PAUSE The bug described in 03cfe957e739e41cdfd66b554c076b5b31f48c1e was already present for another key where modifiers changes its scancode. Related to #1993 --- README.md | 2 ++ src/win32_init.c | 1 - src/win32_window.c | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e81997ba..d931de54 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,8 @@ information on what to include when reporting a bug. - [Win32] Bugfix: `glfwMaximizeWindow` would make a hidden window visible - [Win32] Bugfix: `Alt+PrtSc` would emit `GLFW_KEY_UNKNOWN` and a different scancode than `PrtSc` (#1993) + - [Win32] Bugfix: `GLFW_KEY_PAUSE` scancode from `glfwGetKeyScancode` did not + match event scancode (#1993) - [Cocoa] Added support for `VK_EXT_metal_surface` (#1619) - [Cocoa] Added locating the Vulkan loader at runtime in an application bundle - [Cocoa] Moved main menu creation to GLFW initialization time (#1649) diff --git a/src/win32_init.c b/src/win32_init.c index ee3dad54..31406401 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -253,7 +253,6 @@ static void createKeyTables(void) _glfw.win32.keycodes[0x151] = GLFW_KEY_PAGE_DOWN; _glfw.win32.keycodes[0x149] = GLFW_KEY_PAGE_UP; _glfw.win32.keycodes[0x045] = GLFW_KEY_PAUSE; - _glfw.win32.keycodes[0x146] = GLFW_KEY_PAUSE; _glfw.win32.keycodes[0x039] = GLFW_KEY_SPACE; _glfw.win32.keycodes[0x00F] = GLFW_KEY_TAB; _glfw.win32.keycodes[0x03A] = GLFW_KEY_CAPS_LOCK; diff --git a/src/win32_window.c b/src/win32_window.c index 1d207d8b..37e42c3d 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -756,6 +756,10 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l if (scancode == 0x54) scancode = 0x137; + // HACK: Ctrl+Pause has a different scancode than just Pause + if (scancode == 0x146) + scancode = 0x45; + key = _glfw.win32.keycodes[scancode]; // The Ctrl keys require special handling From 66a4882eb14344115e9cda9f8c0f0c5d0362ca6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 15 Mar 2022 21:22:49 +0100 Subject: [PATCH 41/88] Add checks for some invalid values to public API There were no checks for invalid values or asserts for all invalid NULL pointers to glfwSetWindowIcon or glfwCreateCursor. Fixes #1862 --- CONTRIBUTORS.md | 1 + include/GLFW/glfw3.h | 7 ++++--- src/input.c | 7 +++++++ src/window.c | 21 +++++++++++++++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index bb70c1f5..577da149 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -84,6 +84,7 @@ video tutorials. - Paul Holden - Warren Hu - Charles Huber + - Brent Huisman - illustris - InKryption - IntellectualKitty diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 72d5fa71..3f320065 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -3258,7 +3258,8 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); * count is zero. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks). + * GLFW_INVALID_VALUE, @ref GLFW_PLATFORM_ERROR and @ref + * GLFW_FEATURE_UNAVAILABLE (see remarks). * * @pointer_lifetime The specified image data is copied before this function * returns. @@ -4892,8 +4893,8 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); * @return The handle of the created cursor, or `NULL` if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified image data is copied before this function * returns. diff --git a/src/input.c b/src/input.c index 6a2c3e1d..90ee5273 100644 --- a/src/input.c +++ b/src/input.c @@ -764,9 +764,16 @@ GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) _GLFWcursor* cursor; assert(image != NULL); + assert(image->pixels != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + if (image->width <= 0 || image->height <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid image dimensions for cursor"); + return NULL; + } + cursor = _glfw_calloc(1, sizeof(_GLFWcursor)); cursor->next = _glfw.cursorListHead; _glfw.cursorListHead = cursor; diff --git a/src/window.c b/src/window.c index a65ed760..a24feb60 100644 --- a/src/window.c +++ b/src/window.c @@ -514,12 +514,33 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* handle, const char* title) GLFWAPI void glfwSetWindowIcon(GLFWwindow* handle, int count, const GLFWimage* images) { + int i; _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); assert(count >= 0); assert(count == 0 || images != NULL); _GLFW_REQUIRE_INIT(); + + if (count < 0) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid image count for window icon"); + return; + } + + for (i = 0; i < count; i++) + { + assert(images[i].pixels != NULL); + + if (images[i].width <= 0 || images[i].height <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid image dimensions for window icon"); + return; + } + } + _glfw.platform.setWindowIcon(window, count, images); } From 07a5518c3e42f4412a5d9fa8620950d8f5041e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 17 Mar 2022 23:54:39 +0100 Subject: [PATCH 42/88] Win32: Fix using executable instance and not ours Operations that take an instance handle should be passed the handle of whatever module we are inside instead of blindly passing the handle of the executable. This commit makes GLFW retrieve its own instance on initialization. This makes the most difference for window classes, which are per-instance. Using the executable instance led to name conflicts if there were several copies of GLFW in a single process. Note that having this is still a bad idea unless you know what things to avoid, and those things are mostly platform-specific. This is partly because the library wasn't designed for it and partly because it needs to save, update and restore various per-process and per-session settings like current context and video mode. However, multiple simultaneous copies of GLFW in a single Win32 process should now at least initialize, like is already the case on other platforms. Fixes #469 Fixes #1296 Fixes #1395 Related to #927 Related to #1885 --- CONTRIBUTORS.md | 2 ++ README.md | 1 + src/win32_init.c | 12 +++++++++++- src/win32_joystick.c | 2 +- src/win32_platform.h | 1 + src/win32_window.c | 8 ++++---- 6 files changed, 20 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 577da149..4011336f 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -90,6 +90,7 @@ video tutorials. - IntellectualKitty - Aaron Jacobs - Erik S. V. Jansson + - jjYBdx4IL - Toni Jovanoski - Arseny Kapoulkine - Cem Karan @@ -210,6 +211,7 @@ video tutorials. - Jared Tiala - Sergey Tikhomirov - Arthur Tombs + - TronicLabs - Ioannis Tsakpinis - Samuli Tuomola - Matthew Turner diff --git a/README.md b/README.md index d931de54..d2499596 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,7 @@ information on what to include when reporting a bug. scancode than `PrtSc` (#1993) - [Win32] Bugfix: `GLFW_KEY_PAUSE` scancode from `glfwGetKeyScancode` did not match event scancode (#1993) + - [Win32] Bugfix: Instance-local operations used executable instance (#469,#1296,#1395) - [Cocoa] Added support for `VK_EXT_metal_surface` (#1619) - [Cocoa] Added locating the Vulkan loader at runtime in an application bundle - [Cocoa] Moved main menu creation to GLFW initialization time (#1649) diff --git a/src/win32_init.c b/src/win32_init.c index 31406401..7aefc78a 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -71,6 +71,16 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) // static GLFWbool loadLibraries(void) { + if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (const WCHAR*) &_glfw, + (HMODULE*) &_glfw.win32.instance)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to retrieve own module handle"); + return GLFW_FALSE; + } + _glfw.win32.user32.instance = _glfwPlatformLoadModule("user32.dll"); if (!_glfw.win32.user32.instance) { @@ -334,7 +344,7 @@ static GLFWbool createHelperWindow(void) WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, 1, 1, NULL, NULL, - GetModuleHandleW(NULL), + _glfw.win32.instance, NULL); if (!_glfw.win32.helperWindowHandle) diff --git a/src/win32_joystick.c b/src/win32_joystick.c index 929a19cf..a6fe8e8d 100644 --- a/src/win32_joystick.c +++ b/src/win32_joystick.c @@ -574,7 +574,7 @@ GLFWbool _glfwInitJoysticksWin32(void) { if (_glfw.win32.dinput8.instance) { - if (FAILED(DirectInput8Create(GetModuleHandleW(NULL), + if (FAILED(DirectInput8Create(_glfw.win32.instance, DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**) &_glfw.win32.dinput8.api, diff --git a/src/win32_platform.h b/src/win32_platform.h index 0958b076..c3dc262f 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -442,6 +442,7 @@ typedef struct _GLFWwindowWin32 // typedef struct _GLFWlibraryWin32 { + HINSTANCE instance; HWND helperWindowHandle; HDEVNOTIFY deviceNotificationHandle; int acquiredMonitorCount; diff --git a/src/win32_window.c b/src/win32_window.c index 37e42c3d..5f4dd7d2 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1321,7 +1321,7 @@ static int createNativeWindow(_GLFWwindow* window, fullWidth, fullHeight, NULL, // No parent window NULL, // No window menu - GetModuleHandleW(NULL), + _glfw.win32.instance, (LPVOID) wndconfig); _glfw_free(wideTitle); @@ -1429,7 +1429,7 @@ GLFWbool _glfwRegisterWindowClassWin32(void) wc.cbSize = sizeof(wc); wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.lpfnWndProc = (WNDPROC) windowProc; - wc.hInstance = GetModuleHandleW(NULL); + wc.hInstance = _glfw.win32.instance; wc.hCursor = LoadCursorW(NULL, IDC_ARROW); wc.lpszClassName = _GLFW_WNDCLASSNAME; @@ -1459,7 +1459,7 @@ GLFWbool _glfwRegisterWindowClassWin32(void) // void _glfwUnregisterWindowClassWin32(void) { - UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL)); + UnregisterClassW(_GLFW_WNDCLASSNAME, _glfw.win32.instance); } int _glfwCreateWindowWin32(_GLFWwindow* window, @@ -2447,7 +2447,7 @@ VkResult _glfwCreateWindowSurfaceWin32(VkInstance instance, memset(&sci, 0, sizeof(sci)); sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - sci.hinstance = GetModuleHandleW(NULL); + sci.hinstance = _glfw.win32.instance; sci.hwnd = window->win32.handle; err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface); From 955fbd9d265fa95adf9cb94896eb9a516aa50420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 18 Mar 2022 00:31:34 +0100 Subject: [PATCH 43/88] Cocoa: Fix AirPlay causing harmless init error Emitting an error for one specific type of failure in retrieving the correct name for a display is not very useful, especially when initialization is otherwise unaffected. There should be a path for information like that but this isn't it. Fixes #1791 --- CONTRIBUTORS.md | 1 + README.md | 1 + src/cocoa_monitor.m | 4 ---- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 4011336f..cc77ff13 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -89,6 +89,7 @@ video tutorials. - InKryption - IntellectualKitty - Aaron Jacobs + - JannikGM - Erik S. V. Jansson - jjYBdx4IL - Toni Jovanoski diff --git a/README.md b/README.md index d2499596..13e3e087 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,7 @@ information on what to include when reporting a bug. a fraction of a second (#1962) - [Cocoa] Bugfix: `kIOMasterPortDefault` was deprecated in macOS 12.0 (#1980) - [Cocoa] Bugfix: `kUTTypeURL` was deprecated in macOS 12.0 (#2003) + - [Cocoa] Bugfix: A connected Apple AirPlay would emit a useless error (#1791) - [X11] Bugfix: The CMake files did not check for the XInput headers (#1480) - [X11] Bugfix: Key names were not updated when the keyboard layout changed (#1462,#1528) diff --git a/src/cocoa_monitor.m b/src/cocoa_monitor.m index 70c75aa3..64d9eb2c 100644 --- a/src/cocoa_monitor.m +++ b/src/cocoa_monitor.m @@ -98,11 +98,7 @@ static char* getMonitorName(CGDirectDisplayID displayID, NSScreen* screen) IOObjectRelease(it); if (!service) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to find service port for display"); return _glfw_strdup("Display"); - } CFDictionaryRef names = CFDictionaryGetValue(info, CFSTR(kDisplayProductName)); From 554cbdb205d71ab09873f887bb75a8a7d3731a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 22 Mar 2022 20:11:42 +0100 Subject: [PATCH 44/88] POSIX: Fix data type of return values --- src/x11_window.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/x11_window.c b/src/x11_window.c index dcde3443..38f579fe 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -113,7 +113,7 @@ static void writeEmptyEvent(void) for (;;) { const char byte = 0; - const int result = write(_glfw.x11.emptyEventPipe[1], &byte, 1); + const ssize_t result = write(_glfw.x11.emptyEventPipe[1], &byte, 1); if (result == 1 || (result == -1 && errno != EINTR)) break; } @@ -126,7 +126,7 @@ static void drainEmptyEvents(void) for (;;) { char dummy[64]; - const int result = read(_glfw.x11.emptyEventPipe[0], dummy, sizeof(dummy)); + const ssize_t result = read(_glfw.x11.emptyEventPipe[0], dummy, sizeof(dummy)); if (result == -1 && errno != EINTR) break; } From 920d110b6c7de21bc1ff60fab62350cbc80bd76f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 21 Mar 2022 23:19:06 +0100 Subject: [PATCH 45/88] Fix missing newline --- src/input.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/input.c b/src/input.c index 90ee5273..e0a12cce 100644 --- a/src/input.c +++ b/src/input.c @@ -1428,3 +1428,4 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void) _GLFW_REQUIRE_INIT_OR_RETURN(0); return _glfwPlatformGetTimerFrequency(); } + From 9c95cfb9f1cdf073856e5b479447f5fca6492cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 22 Mar 2022 18:46:57 +0100 Subject: [PATCH 46/88] Wayland: Fix handling of clipboard set to self Passing any part of the result of glfwGetClipboardString to glfwSetClipboardString would result in, at best, a use-after-free error. --- README.md | 2 ++ src/wl_window.c | 20 +++++++++----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 13e3e087..c0e319bf 100644 --- a/README.md +++ b/README.md @@ -310,6 +310,8 @@ information on what to include when reporting a bug. - [Wayland] Bugfix: Some keys were reported as wrong key or `GLFW_KEY_UNKNOWN` - [Wayland] Bugfix: Text input did not repeat along with key repeat - [Wayland] Bugfix: `glfwPostEmptyEvent` sometimes had no effect (#1520,#1521) + - [Wayland] Bugfix: `glfwSetClipboardString` would fail if set to result of + `glfwGetClipboardString` - [POSIX] Removed use of deprecated function `gettimeofday` - [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled - [WGL] Disabled the DWM swap interval hack for Windows 8 and later (#1072) diff --git a/src/wl_window.c b/src/wl_window.c index 8c47d710..a1b717ee 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1664,20 +1664,18 @@ void _glfwSetClipboardStringWayland(const char* string) _glfw.wl.dataSource = NULL; } - if (_glfw.wl.clipboardSendString) + char* copy = _glfw_strdup(string); + if (!copy) { - _glfw_free(_glfw.wl.clipboardSendString); - _glfw.wl.clipboardSendString = NULL; - } - - _glfw.wl.clipboardSendString = _glfw_strdup(string); - if (!_glfw.wl.clipboardSendString) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Impossible to allocate clipboard string"); + _glfwInputError(GLFW_OUT_OF_MEMORY, + "Wayland: Failed to allocate clipboard string"); return; } - _glfw.wl.clipboardSendSize = strlen(string); + + _glfw_free(_glfw.wl.clipboardSendString); + _glfw.wl.clipboardSendString = copy; + + _glfw.wl.clipboardSendSize = strlen(_glfw.wl.clipboardSendString); _glfw.wl.dataSource = wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); if (!_glfw.wl.dataSource) From b386371f57ba45e6ef15d16e994c6fbe71437427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 22 Mar 2022 18:55:31 +0100 Subject: [PATCH 47/88] Wayland: Fix double free on data source error If data source creation fails, the string containing the data for it would be freed a second time during termination. --- README.md | 1 + src/wl_window.c | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index c0e319bf..210edf82 100644 --- a/README.md +++ b/README.md @@ -312,6 +312,7 @@ information on what to include when reporting a bug. - [Wayland] Bugfix: `glfwPostEmptyEvent` sometimes had no effect (#1520,#1521) - [Wayland] Bugfix: `glfwSetClipboardString` would fail if set to result of `glfwGetClipboardString` + - [Wayland] Bugfix: Data source creation error would cause double free at termination - [POSIX] Removed use of deprecated function `gettimeofday` - [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled - [WGL] Disabled the DWM swap interval hack for Windows 8 and later (#1072) diff --git a/src/wl_window.c b/src/wl_window.c index a1b717ee..c2a88f5b 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1683,6 +1683,7 @@ void _glfwSetClipboardStringWayland(const char* string) _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Impossible to create clipboard source"); _glfw_free(_glfw.wl.clipboardSendString); + _glfw.wl.clipboardSendString = NULL; return; } wl_data_source_add_listener(_glfw.wl.dataSource, From 738b1c73b5433c0a3c76f2b14728a5c0ba515643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 22 Mar 2022 19:04:29 +0100 Subject: [PATCH 48/88] Wayland: Remove superfluous global struct member It seems unlikely that strlen will be a bottleneck when sending clipboard contents. --- src/wl_platform.h | 1 - src/wl_window.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/wl_platform.h b/src/wl_platform.h index ba405714..9e7202bd 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -308,7 +308,6 @@ typedef struct _GLFWlibraryWayland char* clipboardString; size_t clipboardSize; char* clipboardSendString; - size_t clipboardSendSize; int timerfd; short int keycodes[256]; short int scancodes[GLFW_KEY_LAST + 1]; diff --git a/src/wl_window.c b/src/wl_window.c index c2a88f5b..01002061 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1592,7 +1592,7 @@ static void dataSourceHandleSend(void* data, int fd) { const char* string = _glfw.wl.clipboardSendString; - size_t len = _glfw.wl.clipboardSendSize; + size_t len = strlen(string); int ret; if (_glfw.wl.dataSource != dataSource) @@ -1675,7 +1675,6 @@ void _glfwSetClipboardStringWayland(const char* string) _glfw_free(_glfw.wl.clipboardSendString); _glfw.wl.clipboardSendString = copy; - _glfw.wl.clipboardSendSize = strlen(_glfw.wl.clipboardSendString); _glfw.wl.dataSource = wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); if (!_glfw.wl.dataSource) From 4c110bba41b0df8a3b295fdd5e4e95aed6615001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 22 Mar 2022 19:23:25 +0100 Subject: [PATCH 49/88] Wayland: Fix partial writes of clipboard string The string pointer used to write the contents of our clipboard data offer was never updated, causing it to repeat parts of the beginning of the string until the correct number of bytes had been written. --- README.md | 1 + src/wl_window.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 210edf82..ca1bfa88 100644 --- a/README.md +++ b/README.md @@ -313,6 +313,7 @@ information on what to include when reporting a bug. - [Wayland] Bugfix: `glfwSetClipboardString` would fail if set to result of `glfwGetClipboardString` - [Wayland] Bugfix: Data source creation error would cause double free at termination + - [Wayland] Bugfix: Partial writes of clipboard string would cause beginning to repeat - [POSIX] Removed use of deprecated function `gettimeofday` - [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled - [WGL] Disabled the DWM swap interval hack for Windows 8 and later (#1072) diff --git a/src/wl_window.c b/src/wl_window.c index 01002061..6e7f282b 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1591,7 +1591,7 @@ static void dataSourceHandleSend(void* data, const char* mimeType, int fd) { - const char* string = _glfw.wl.clipboardSendString; + char* string = _glfw.wl.clipboardSendString; size_t len = strlen(string); int ret; @@ -1631,6 +1631,7 @@ static void dataSourceHandleSend(void* data, return; } len -= ret; + string += ret; } close(fd); } From 2d45681bc354a50e616082ea37c765c062a1fc09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 22 Mar 2022 19:30:43 +0100 Subject: [PATCH 50/88] Wayland: Fix phrasing and type of error messages --- src/wl_init.c | 16 ++++++++-------- src/wl_window.c | 22 +++++++++++----------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index f02c6320..fd1132ab 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -160,8 +160,8 @@ static void setCursor(_GLFWwindow* window, const char* name) cursor = wl_cursor_theme_get_cursor(theme, name); if (!cursor) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Standard cursor not found"); + _glfwInputError(GLFW_CURSOR_UNAVAILABLE, + "Wayland: Standard cursor shape unavailable"); return; } // TODO: handle animated cursors too. @@ -1116,7 +1116,7 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform) if (platformID == GLFW_PLATFORM_WAYLAND) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to open libwayland-client"); + "Wayland: Failed to load libwayland-client"); } return GLFW_FALSE; @@ -1221,7 +1221,7 @@ int _glfwInitWayland(void) if (!_glfw.wl.cursor.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to open libwayland-cursor"); + "Wayland: Failed to load libwayland-cursor"); return GLFW_FALSE; } @@ -1238,7 +1238,7 @@ int _glfwInitWayland(void) if (!_glfw.wl.egl.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to open libwayland-egl"); + "Wayland: Failed to load libwayland-egl"); return GLFW_FALSE; } @@ -1253,7 +1253,7 @@ int _glfwInitWayland(void) if (!_glfw.wl.xkb.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to open libxkbcommon"); + "Wayland: Failed to load libxkbcommon"); return GLFW_FALSE; } @@ -1345,7 +1345,7 @@ int _glfwInitWayland(void) if (!_glfw.wl.cursorTheme) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Unable to load default cursor theme"); + "Wayland: Failed to load default cursor theme"); return GLFW_FALSE; } // If this happens to be NULL, we just fallback to the scale=1 version. @@ -1368,7 +1368,7 @@ int _glfwInitWayland(void) if (!_glfw.wl.clipboardString) { _glfwInputError(GLFW_OUT_OF_MEMORY, - "Wayland: Unable to allocate clipboard memory"); + "Wayland: Failed to allocate clipboard memory"); return GLFW_FALSE; } } diff --git a/src/wl_window.c b/src/wl_window.c index 6e7f282b..ef457ac2 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -149,7 +149,7 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image) if (fd < 0) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Creating a buffer file for %d B failed: %s", + "Wayland: Failed to create buffer file of size %d: %s", length, strerror(errno)); return NULL; } @@ -158,7 +158,7 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image) if (data == MAP_FAILED) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: mmap failed: %s", strerror(errno)); + "Wayland: Failed to map file: %s", strerror(errno)); close(fd); return NULL; } @@ -418,7 +418,7 @@ static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable) _glfw.wl.idleInhibitManager, window->wl.surface); if (!window->wl.idleInhibitor) _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Idle inhibitor creation failed"); + "Wayland: Failed to create idle inhibitor"); } else if (!enable && window->wl.idleInhibitor) { @@ -556,7 +556,7 @@ static GLFWbool createXdgSurface(_GLFWwindow* window) if (!window->wl.xdg.surface) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: xdg-surface creation failed"); + "Wayland: Failed to create xdg-surface for window"); return GLFW_FALSE; } @@ -568,7 +568,7 @@ static GLFWbool createXdgSurface(_GLFWwindow* window) if (!window->wl.xdg.toplevel) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: xdg-toplevel creation failed"); + "Wayland: Failed to create xdg-toplevel for window"); return GLFW_FALSE; } @@ -1378,7 +1378,7 @@ int _glfwCreateStandardCursorWayland(_GLFWcursor* cursor, int shape) cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name); if (!cursor->wl.cursor) { - _glfwInputError(GLFW_PLATFORM_ERROR, + _glfwInputError(GLFW_CURSOR_UNAVAILABLE, "Wayland: Failed to create standard cursor \"%s\"", name); return GLFW_FALSE; @@ -1681,7 +1681,7 @@ void _glfwSetClipboardStringWayland(const char* string) if (!_glfw.wl.dataSource) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Impossible to create clipboard source"); + "Wayland: Failed to create clipboard data source"); _glfw_free(_glfw.wl.clipboardSendString); _glfw.wl.clipboardSendString = NULL; return; @@ -1703,7 +1703,7 @@ static GLFWbool growClipboardString(void) if (!clipboard) { _glfwInputError(GLFW_OUT_OF_MEMORY, - "Wayland: Impossible to grow clipboard string"); + "Wayland: Failed to grow clipboard string"); return GLFW_FALSE; } _glfw.wl.clipboardString = clipboard; @@ -1720,7 +1720,7 @@ const char* _glfwGetClipboardStringWayland(void) if (!_glfw.wl.dataOffer) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "No clipboard data has been sent yet"); + "Wayland: No clipboard data available"); return NULL; } @@ -1729,7 +1729,7 @@ const char* _glfwGetClipboardStringWayland(void) { // TODO: also report errno maybe? _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Impossible to create clipboard pipe fds"); + "Wayland: Failed to create clipboard pipe fds"); return NULL; } @@ -1762,7 +1762,7 @@ const char* _glfwGetClipboardStringWayland(void) { // TODO: also report errno maybe. _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Impossible to read from clipboard fd"); + "Wayland: Failed to read from clipboard fd"); close(fds[0]); return NULL; } From ba11e60859c22132cb6c11f570f5341fa38ef856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 23 Mar 2022 18:52:06 +0100 Subject: [PATCH 51/88] Wayland: Rename data transfer parameters No rationale beyond personal preference. --- src/wl_init.c | 22 +++++++++++----------- src/wl_window.c | 14 +++++++------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index fd1132ab..720ac53e 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -701,7 +701,7 @@ static const struct wl_seat_listener seatListener = { }; static void dataOfferHandleOffer(void* data, - struct wl_data_offer* dataOffer, + struct wl_data_offer* offer, const char* mimeType) { } @@ -711,33 +711,33 @@ static const struct wl_data_offer_listener dataOfferListener = { }; static void dataDeviceHandleDataOffer(void* data, - struct wl_data_device* dataDevice, - struct wl_data_offer* id) + struct wl_data_device* device, + struct wl_data_offer* offer) { if (_glfw.wl.dataOffer) wl_data_offer_destroy(_glfw.wl.dataOffer); - _glfw.wl.dataOffer = id; + _glfw.wl.dataOffer = offer; wl_data_offer_add_listener(_glfw.wl.dataOffer, &dataOfferListener, NULL); } static void dataDeviceHandleEnter(void* data, - struct wl_data_device* dataDevice, + struct wl_data_device* device, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y, - struct wl_data_offer *id) + struct wl_data_offer *offer) { } static void dataDeviceHandleLeave(void* data, - struct wl_data_device* dataDevice) + struct wl_data_device* device) { } static void dataDeviceHandleMotion(void* data, - struct wl_data_device* dataDevice, + struct wl_data_device* device, uint32_t time, wl_fixed_t x, wl_fixed_t y) @@ -745,13 +745,13 @@ static void dataDeviceHandleMotion(void* data, } static void dataDeviceHandleDrop(void* data, - struct wl_data_device* dataDevice) + struct wl_data_device* device) { } static void dataDeviceHandleSelection(void* data, - struct wl_data_device* dataDevice, - struct wl_data_offer* id) + struct wl_data_device* device, + struct wl_data_offer* offer) { } diff --git a/src/wl_window.c b/src/wl_window.c index ef457ac2..0222b925 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1575,10 +1575,10 @@ void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor) } static void dataSourceHandleTarget(void* data, - struct wl_data_source* dataSource, + struct wl_data_source* source, const char* mimeType) { - if (_glfw.wl.dataSource != dataSource) + if (_glfw.wl.dataSource != source) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unknown clipboard data source"); @@ -1587,7 +1587,7 @@ static void dataSourceHandleTarget(void* data, } static void dataSourceHandleSend(void* data, - struct wl_data_source* dataSource, + struct wl_data_source* source, const char* mimeType, int fd) { @@ -1595,7 +1595,7 @@ static void dataSourceHandleSend(void* data, size_t len = strlen(string); int ret; - if (_glfw.wl.dataSource != dataSource) + if (_glfw.wl.dataSource != source) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unknown clipboard data source"); @@ -1637,11 +1637,11 @@ static void dataSourceHandleSend(void* data, } static void dataSourceHandleCancelled(void* data, - struct wl_data_source* dataSource) + struct wl_data_source* source) { - wl_data_source_destroy(dataSource); + wl_data_source_destroy(source); - if (_glfw.wl.dataSource != dataSource) + if (_glfw.wl.dataSource != source) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unknown clipboard data source"); From 45061750236007f8eaa782a9a8d12ff44733b68a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 23 Mar 2022 18:55:22 +0100 Subject: [PATCH 52/88] Wayland: Clean up pointer declarations Adapt style to match the rest of the project. --- src/wl_init.c | 14 +++++++------- src/wl_monitor.c | 12 ++++++------ src/wl_platform.h | 10 +++++----- src/wl_window.c | 14 +++++++------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index 720ac53e..061d2726 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -724,10 +724,10 @@ static void dataDeviceHandleDataOffer(void* data, static void dataDeviceHandleEnter(void* data, struct wl_data_device* device, uint32_t serial, - struct wl_surface *surface, + struct wl_surface* surface, wl_fixed_t x, wl_fixed_t y, - struct wl_data_offer *offer) + struct wl_data_offer* offer) { } @@ -863,8 +863,8 @@ static void registryHandleGlobal(void* data, } } -static void registryHandleGlobalRemove(void *data, - struct wl_registry *registry, +static void registryHandleGlobalRemove(void* data, + struct wl_registry* registry, uint32_t name) { _GLFWmonitor* monitor; @@ -1155,9 +1155,9 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform) int _glfwInitWayland(void) { - const char *cursorTheme; - const char *cursorSizeStr; - char *cursorSizeEnd; + const char* cursorTheme; + const char* cursorSizeStr; + char* cursorSizeEnd; long cursorSizeLong; int cursorSize; diff --git a/src/wl_monitor.c b/src/wl_monitor.c index 5bdda918..44d9f0e1 100644 --- a/src/wl_monitor.c +++ b/src/wl_monitor.c @@ -48,7 +48,7 @@ static void outputHandleGeometry(void* data, const char* model, int32_t transform) { - struct _GLFWmonitor *monitor = data; + struct _GLFWmonitor* monitor = data; monitor->wl.x = x; monitor->wl.y = y; @@ -65,7 +65,7 @@ static void outputHandleMode(void* data, int32_t height, int32_t refresh) { - struct _GLFWmonitor *monitor = data; + struct _GLFWmonitor* monitor = data; GLFWvidmode mode; mode.width = width; @@ -86,7 +86,7 @@ static void outputHandleMode(void* data, static void outputHandleDone(void* data, struct wl_output* output) { - struct _GLFWmonitor *monitor = data; + struct _GLFWmonitor* monitor = data; if (monitor->widthMM <= 0 || monitor->heightMM <= 0) { @@ -103,7 +103,7 @@ static void outputHandleScale(void* data, struct wl_output* output, int32_t factor) { - struct _GLFWmonitor *monitor = data; + struct _GLFWmonitor* monitor = data; monitor->wl.scale = factor; } @@ -122,8 +122,8 @@ static const struct wl_output_listener outputListener = { void _glfwAddOutputWayland(uint32_t name, uint32_t version) { - _GLFWmonitor *monitor; - struct wl_output *output; + _GLFWmonitor* monitor; + struct wl_output* output; if (version < 2) { diff --git a/src/wl_platform.h b/src/wl_platform.h index 9e7202bd..405f00b3 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -45,10 +45,10 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR #include "xkb_unicode.h" #include "posix_poll.h" -typedef int (* PFN_wl_display_flush)(struct wl_display *display); -typedef void (* PFN_wl_display_cancel_read)(struct wl_display *display); -typedef int (* PFN_wl_display_dispatch_pending)(struct wl_display *display); -typedef int (* PFN_wl_display_read_events)(struct wl_display *display); +typedef int (* PFN_wl_display_flush)(struct wl_display* display); +typedef void (* PFN_wl_display_cancel_read)(struct wl_display* display); +typedef int (* PFN_wl_display_dispatch_pending)(struct wl_display* display); +typedef int (* PFN_wl_display_read_events)(struct wl_display* display); typedef struct wl_display* (* PFN_wl_display_connect)(const char*); typedef void (* PFN_wl_display_disconnect)(struct wl_display*); typedef int (* PFN_wl_display_roundtrip)(struct wl_display*); @@ -457,7 +457,7 @@ float _glfwGetWindowOpacityWayland(_GLFWwindow* window); void _glfwSetWindowOpacityWayland(_GLFWwindow* window, float opacity); void _glfwSetWindowMousePassthroughWayland(_GLFWwindow* window, GLFWbool enabled); -void _glfwSetRawMouseMotionWayland(_GLFWwindow *window, GLFWbool enabled); +void _glfwSetRawMouseMotionWayland(_GLFWwindow* window, GLFWbool enabled); GLFWbool _glfwRawMouseMotionSupportedWayland(void); void _glfwPollEventsWayland(void); diff --git a/src/wl_window.c b/src/wl_window.c index 0222b925..1e743132 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -364,9 +364,9 @@ static void checkScaleChange(_GLFWwindow* window) } } -static void surfaceHandleEnter(void *data, - struct wl_surface *surface, - struct wl_output *output) +static void surfaceHandleEnter(void* data, + struct wl_surface* surface, + struct wl_output* output) { _GLFWwindow* window = data; _GLFWmonitor* monitor = wl_output_get_user_data(output); @@ -384,9 +384,9 @@ static void surfaceHandleEnter(void *data, checkScaleChange(window); } -static void surfaceHandleLeave(void *data, - struct wl_surface *surface, - struct wl_output *output) +static void surfaceHandleLeave(void* data, + struct wl_surface* surface, + struct wl_output* output) { _GLFWwindow* window = data; _GLFWmonitor* monitor = wl_output_get_user_data(output); @@ -1163,7 +1163,7 @@ void _glfwSetWindowOpacityWayland(_GLFWwindow* window, float opacity) "Wayland: The platform does not support setting the window opacity"); } -void _glfwSetRawMouseMotionWayland(_GLFWwindow *window, GLFWbool enabled) +void _glfwSetRawMouseMotionWayland(_GLFWwindow* window, GLFWbool enabled) { // This is handled in relativePointerHandleRelativeMotion } From a4460b694e512e6f4b485055832792d3c22ccbe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 23 Mar 2022 18:58:08 +0100 Subject: [PATCH 53/88] Wayland: Rename listener user data parameters --- src/wl_init.c | 46 +++++++++++++++++++++++----------------------- src/wl_monitor.c | 16 ++++++++-------- src/wl_window.c | 36 ++++++++++++++++++------------------ 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index 061d2726..ba34c1db 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -91,7 +91,7 @@ static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, return window; } -static void pointerHandleEnter(void* data, +static void pointerHandleEnter(void* userData, struct wl_pointer* pointer, uint32_t serial, struct wl_surface* surface, @@ -122,7 +122,7 @@ static void pointerHandleEnter(void* data, _glfwInputCursorEnter(window, GLFW_TRUE); } -static void pointerHandleLeave(void* data, +static void pointerHandleLeave(void* userData, struct wl_pointer* pointer, uint32_t serial, struct wl_surface* surface) @@ -185,7 +185,7 @@ static void setCursor(_GLFWwindow* window, const char* name) _glfw.wl.cursorPreviousName = name; } -static void pointerHandleMotion(void* data, +static void pointerHandleMotion(void* userData, struct wl_pointer* pointer, uint32_t time, wl_fixed_t sx, @@ -244,7 +244,7 @@ static void pointerHandleMotion(void* data, setCursor(window, cursorName); } -static void pointerHandleButton(void* data, +static void pointerHandleButton(void* userData, struct wl_pointer* pointer, uint32_t serial, uint32_t time, @@ -329,7 +329,7 @@ static void pointerHandleButton(void* data, _glfw.wl.xkb.modifiers); } -static void pointerHandleAxis(void* data, +static void pointerHandleAxis(void* userData, struct wl_pointer* pointer, uint32_t time, uint32_t axis, @@ -364,7 +364,7 @@ static const struct wl_pointer_listener pointerListener = { pointerHandleAxis, }; -static void keyboardHandleKeymap(void* data, +static void keyboardHandleKeymap(void* userData, struct wl_keyboard* keyboard, uint32_t format, int fd, @@ -461,7 +461,7 @@ static void keyboardHandleKeymap(void* data, 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2"); } -static void keyboardHandleEnter(void* data, +static void keyboardHandleEnter(void* userData, struct wl_keyboard* keyboard, uint32_t serial, struct wl_surface* surface, @@ -484,7 +484,7 @@ static void keyboardHandleEnter(void* data, _glfwInputWindowFocus(window, GLFW_TRUE); } -static void keyboardHandleLeave(void* data, +static void keyboardHandleLeave(void* userData, struct wl_keyboard* keyboard, uint32_t serial, struct wl_surface* surface) @@ -550,7 +550,7 @@ GLFWbool _glfwInputTextWayland(_GLFWwindow* window, uint32_t scancode) return xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, keycode); } -static void keyboardHandleKey(void* data, +static void keyboardHandleKey(void* userData, struct wl_keyboard* keyboard, uint32_t serial, uint32_t time, @@ -591,7 +591,7 @@ static void keyboardHandleKey(void* data, timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL); } -static void keyboardHandleModifiers(void* data, +static void keyboardHandleModifiers(void* userData, struct wl_keyboard* keyboard, uint32_t serial, uint32_t modsDepressed, @@ -638,7 +638,7 @@ static void keyboardHandleModifiers(void* data, } #ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION -static void keyboardHandleRepeatInfo(void* data, +static void keyboardHandleRepeatInfo(void* userData, struct wl_keyboard* keyboard, int32_t rate, int32_t delay) @@ -662,7 +662,7 @@ static const struct wl_keyboard_listener keyboardListener = { #endif }; -static void seatHandleCapabilities(void* data, +static void seatHandleCapabilities(void* userData, struct wl_seat* seat, enum wl_seat_capability caps) { @@ -689,7 +689,7 @@ static void seatHandleCapabilities(void* data, } } -static void seatHandleName(void* data, +static void seatHandleName(void* userData, struct wl_seat* seat, const char* name) { @@ -700,7 +700,7 @@ static const struct wl_seat_listener seatListener = { seatHandleName, }; -static void dataOfferHandleOffer(void* data, +static void dataOfferHandleOffer(void* userData, struct wl_data_offer* offer, const char* mimeType) { @@ -710,7 +710,7 @@ static const struct wl_data_offer_listener dataOfferListener = { dataOfferHandleOffer, }; -static void dataDeviceHandleDataOffer(void* data, +static void dataDeviceHandleDataOffer(void* userData, struct wl_data_device* device, struct wl_data_offer* offer) { @@ -721,7 +721,7 @@ static void dataDeviceHandleDataOffer(void* data, wl_data_offer_add_listener(_glfw.wl.dataOffer, &dataOfferListener, NULL); } -static void dataDeviceHandleEnter(void* data, +static void dataDeviceHandleEnter(void* userData, struct wl_data_device* device, uint32_t serial, struct wl_surface* surface, @@ -731,12 +731,12 @@ static void dataDeviceHandleEnter(void* data, { } -static void dataDeviceHandleLeave(void* data, +static void dataDeviceHandleLeave(void* userData, struct wl_data_device* device) { } -static void dataDeviceHandleMotion(void* data, +static void dataDeviceHandleMotion(void* userData, struct wl_data_device* device, uint32_t time, wl_fixed_t x, @@ -744,12 +744,12 @@ static void dataDeviceHandleMotion(void* data, { } -static void dataDeviceHandleDrop(void* data, +static void dataDeviceHandleDrop(void* userData, struct wl_data_device* device) { } -static void dataDeviceHandleSelection(void* data, +static void dataDeviceHandleSelection(void* userData, struct wl_data_device* device, struct wl_data_offer* offer) { @@ -764,7 +764,7 @@ static const struct wl_data_device_listener dataDeviceListener = { dataDeviceHandleSelection, }; -static void wmBaseHandlePing(void* data, +static void wmBaseHandlePing(void* userData, struct xdg_wm_base* wmBase, uint32_t serial) { @@ -775,7 +775,7 @@ static const struct xdg_wm_base_listener wmBaseListener = { wmBaseHandlePing }; -static void registryHandleGlobal(void* data, +static void registryHandleGlobal(void* userData, struct wl_registry* registry, uint32_t name, const char* interface, @@ -863,7 +863,7 @@ static void registryHandleGlobal(void* data, } } -static void registryHandleGlobalRemove(void* data, +static void registryHandleGlobalRemove(void* userData, struct wl_registry* registry, uint32_t name) { diff --git a/src/wl_monitor.c b/src/wl_monitor.c index 44d9f0e1..07adaa39 100644 --- a/src/wl_monitor.c +++ b/src/wl_monitor.c @@ -37,7 +37,7 @@ #include "wayland-client-protocol.h" -static void outputHandleGeometry(void* data, +static void outputHandleGeometry(void* userData, struct wl_output* output, int32_t x, int32_t y, @@ -48,7 +48,7 @@ static void outputHandleGeometry(void* data, const char* model, int32_t transform) { - struct _GLFWmonitor* monitor = data; + struct _GLFWmonitor* monitor = userData; monitor->wl.x = x; monitor->wl.y = y; @@ -58,14 +58,14 @@ static void outputHandleGeometry(void* data, snprintf(monitor->name, sizeof(monitor->name), "%s %s", make, model); } -static void outputHandleMode(void* data, +static void outputHandleMode(void* userData, struct wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { - struct _GLFWmonitor* monitor = data; + struct _GLFWmonitor* monitor = userData; GLFWvidmode mode; mode.width = width; @@ -84,9 +84,9 @@ static void outputHandleMode(void* data, monitor->wl.currentMode = monitor->modeCount - 1; } -static void outputHandleDone(void* data, struct wl_output* output) +static void outputHandleDone(void* userData, struct wl_output* output) { - struct _GLFWmonitor* monitor = data; + struct _GLFWmonitor* monitor = userData; if (monitor->widthMM <= 0 || monitor->heightMM <= 0) { @@ -99,11 +99,11 @@ static void outputHandleDone(void* data, struct wl_output* output) _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); } -static void outputHandleScale(void* data, +static void outputHandleScale(void* userData, struct wl_output* output, int32_t factor) { - struct _GLFWmonitor* monitor = data; + struct _GLFWmonitor* monitor = userData; monitor->wl.scale = factor; } diff --git a/src/wl_window.c b/src/wl_window.c index 1e743132..78cdaed8 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -272,11 +272,11 @@ static void destroyDecorations(_GLFWwindow* window) destroyDecoration(&window->wl.decorations.bottom); } -static void xdgDecorationHandleConfigure(void* data, +static void xdgDecorationHandleConfigure(void* userData, struct zxdg_toplevel_decoration_v1* decoration, uint32_t mode) { - _GLFWwindow* window = data; + _GLFWwindow* window = userData; window->wl.decorations.serverSide = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); @@ -364,11 +364,11 @@ static void checkScaleChange(_GLFWwindow* window) } } -static void surfaceHandleEnter(void* data, +static void surfaceHandleEnter(void* userData, struct wl_surface* surface, struct wl_output* output) { - _GLFWwindow* window = data; + _GLFWwindow* window = userData; _GLFWmonitor* monitor = wl_output_get_user_data(output); if (window->wl.monitorsCount + 1 > window->wl.monitorsSize) @@ -384,11 +384,11 @@ static void surfaceHandleEnter(void* data, checkScaleChange(window); } -static void surfaceHandleLeave(void* data, +static void surfaceHandleLeave(void* userData, struct wl_surface* surface, struct wl_output* output) { - _GLFWwindow* window = data; + _GLFWwindow* window = userData; _GLFWmonitor* monitor = wl_output_get_user_data(output); GLFWbool found = GLFW_FALSE; @@ -441,13 +441,13 @@ static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, destroyDecorations(window); } -static void xdgToplevelHandleConfigure(void* data, +static void xdgToplevelHandleConfigure(void* userData, struct xdg_toplevel* toplevel, int32_t width, int32_t height, struct wl_array* states) { - _GLFWwindow* window = data; + _GLFWwindow* window = userData; float aspectRatio; float targetRatio; uint32_t* state; @@ -505,10 +505,10 @@ static void xdgToplevelHandleConfigure(void* data, window->wl.wasFullscreen = GLFW_TRUE; } -static void xdgToplevelHandleClose(void* data, +static void xdgToplevelHandleClose(void* userData, struct xdg_toplevel* toplevel) { - _GLFWwindow* window = data; + _GLFWwindow* window = userData; _glfwInputWindowCloseRequest(window); } @@ -517,7 +517,7 @@ static const struct xdg_toplevel_listener xdgToplevelListener = { xdgToplevelHandleClose }; -static void xdgSurfaceHandleConfigure(void* data, +static void xdgSurfaceHandleConfigure(void* userData, struct xdg_surface* surface, uint32_t serial) { @@ -1407,7 +1407,7 @@ void _glfwDestroyCursorWayland(_GLFWcursor* cursor) wl_buffer_destroy(cursor->wl.buffer); } -static void relativePointerHandleRelativeMotion(void* data, +static void relativePointerHandleRelativeMotion(void* userData, struct zwp_relative_pointer_v1* pointer, uint32_t timeHi, uint32_t timeLo, @@ -1416,7 +1416,7 @@ static void relativePointerHandleRelativeMotion(void* data, wl_fixed_t dxUnaccel, wl_fixed_t dyUnaccel) { - _GLFWwindow* window = data; + _GLFWwindow* window = userData; double xpos = window->virtualCursorPosX; double ypos = window->virtualCursorPosY; @@ -1441,7 +1441,7 @@ static const struct zwp_relative_pointer_v1_listener relativePointerListener = { relativePointerHandleRelativeMotion }; -static void lockedPointerHandleLocked(void* data, +static void lockedPointerHandleLocked(void* userData, struct zwp_locked_pointer_v1* lockedPointer) { } @@ -1462,7 +1462,7 @@ static void unlockPointer(_GLFWwindow* window) static void lockPointer(_GLFWwindow* window); -static void lockedPointerHandleUnlocked(void* data, +static void lockedPointerHandleUnlocked(void* userData, struct zwp_locked_pointer_v1* lockedPointer) { } @@ -1574,7 +1574,7 @@ void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor) } } -static void dataSourceHandleTarget(void* data, +static void dataSourceHandleTarget(void* userData, struct wl_data_source* source, const char* mimeType) { @@ -1586,7 +1586,7 @@ static void dataSourceHandleTarget(void* data, } } -static void dataSourceHandleSend(void* data, +static void dataSourceHandleSend(void* userData, struct wl_data_source* source, const char* mimeType, int fd) @@ -1636,7 +1636,7 @@ static void dataSourceHandleSend(void* data, close(fd); } -static void dataSourceHandleCancelled(void* data, +static void dataSourceHandleCancelled(void* userData, struct wl_data_source* source) { wl_data_source_destroy(source); From 990dc4b3888bbaffdc7c3da65ab6c2f8d24285d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 23 Mar 2022 20:08:12 +0100 Subject: [PATCH 54/88] Wayland: Fix error paths not closing sending fd Whatever error happens on our end, we should still close the fd so the other end can move on. --- README.md | 1 + src/wl_window.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/README.md b/README.md index ca1bfa88..182a23c2 100644 --- a/README.md +++ b/README.md @@ -314,6 +314,7 @@ information on what to include when reporting a bug. `glfwGetClipboardString` - [Wayland] Bugfix: Data source creation error would cause double free at termination - [Wayland] Bugfix: Partial writes of clipboard string would cause beginning to repeat + - [Wayland] Bugfix: Some errors would cause clipboard string transfer to hang - [POSIX] Removed use of deprecated function `gettimeofday` - [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled - [WGL] Disabled the DWM swap interval hack for Windows 8 and later (#1072) diff --git a/src/wl_window.c b/src/wl_window.c index 78cdaed8..e887a4d0 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1599,6 +1599,7 @@ static void dataSourceHandleSend(void* userData, { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unknown clipboard data source"); + close(fd); return; } @@ -1606,6 +1607,7 @@ static void dataSourceHandleSend(void* userData, { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Copy requested from an invalid string"); + close(fd); return; } From 89d3ea8d691016306ca23f26d1dc11e9f51baea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 24 Mar 2022 14:51:24 +0100 Subject: [PATCH 55/88] Wayland: Return our clipboard without roundtrips --- src/wl_window.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/wl_window.c b/src/wl_window.c index e887a4d0..647234e3 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1726,6 +1726,9 @@ const char* _glfwGetClipboardStringWayland(void) return NULL; } + if (_glfw.wl.dataSource) + return _glfw.wl.clipboardSendString; + ret = pipe2(fds, O_CLOEXEC); if (ret < 0) { @@ -1736,10 +1739,9 @@ const char* _glfwGetClipboardStringWayland(void) } wl_data_offer_receive(_glfw.wl.dataOffer, "text/plain;charset=utf-8", fds[1]); - close(fds[1]); - // XXX: this is a huge hack, this function shouldn’t be synchronous! - handleEvents(NULL); + flushDisplay(); + close(fds[1]); for (;;) { From 8d87be1268869af69d59400a1b0403e9ecbc5c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 23 Mar 2022 19:58:33 +0100 Subject: [PATCH 56/88] Wayland: Improve handling of pending data offers The code assumed that all data offers were selections that supported plaintext UTF-8. The initial data offer events are now handled almost tolerably. Only selection data offers are used for clipboard string and only if they provide plaintext UTF-8. Drag and drop data offers are now rejected as soon as they enter a surface. Related to #2040 --- README.md | 2 ++ src/wl_init.c | 72 +++++++++++++++++++++++++++++++++++++++++------ src/wl_platform.h | 14 +++++++-- src/wl_window.c | 30 ++++++++++---------- 4 files changed, 93 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 182a23c2..7b6873f1 100644 --- a/README.md +++ b/README.md @@ -315,6 +315,8 @@ information on what to include when reporting a bug. - [Wayland] Bugfix: Data source creation error would cause double free at termination - [Wayland] Bugfix: Partial writes of clipboard string would cause beginning to repeat - [Wayland] Bugfix: Some errors would cause clipboard string transfer to hang + - [Wayland] Bugfix: Drag and drop data was misinterpreted as clipboard string + - [Wayland] Bugfix: MIME type matching was not performed for clipboard string - [POSIX] Removed use of deprecated function `gettimeofday` - [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled - [WGL] Disabled the DWM swap interval hack for Windows 8 and later (#1072) diff --git a/src/wl_init.c b/src/wl_init.c index ba34c1db..4368f3fa 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -704,6 +704,16 @@ static void dataOfferHandleOffer(void* userData, struct wl_data_offer* offer, const char* mimeType) { + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + { + if (_glfw.wl.offers[i].offer == offer) + { + if (strcmp(mimeType, "text/plain;charset=utf-8") == 0) + _glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE; + + break; + } + } } static const struct wl_data_offer_listener dataOfferListener = { @@ -714,11 +724,19 @@ static void dataDeviceHandleDataOffer(void* userData, struct wl_data_device* device, struct wl_data_offer* offer) { - if (_glfw.wl.dataOffer) - wl_data_offer_destroy(_glfw.wl.dataOffer); + _GLFWofferWayland* offers = + _glfw_realloc(_glfw.wl.offers, _glfw.wl.offerCount + 1); + if (!offers) + { + _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); + return; + } - _glfw.wl.dataOffer = offer; - wl_data_offer_add_listener(_glfw.wl.dataOffer, &dataOfferListener, NULL); + _glfw.wl.offers = offers; + _glfw.wl.offerCount++; + + _glfw.wl.offers[_glfw.wl.offerCount - 1] = (_GLFWofferWayland) { offer }; + wl_data_offer_add_listener(offer, &dataOfferListener, NULL); } static void dataDeviceHandleEnter(void* userData, @@ -729,6 +747,19 @@ static void dataDeviceHandleEnter(void* userData, wl_fixed_t y, struct wl_data_offer* offer) { + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + { + if (_glfw.wl.offers[i].offer == offer) + { + _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; + _glfw.wl.offerCount--; + + // We don't yet handle drag and drop + wl_data_offer_accept(offer, serial, NULL); + wl_data_offer_destroy(offer); + break; + } + } } static void dataDeviceHandleLeave(void* userData, @@ -753,6 +784,26 @@ static void dataDeviceHandleSelection(void* userData, struct wl_data_device* device, struct wl_data_offer* offer) { + if (_glfw.wl.selectionOffer) + { + wl_data_offer_destroy(_glfw.wl.selectionOffer); + _glfw.wl.selectionOffer = NULL; + } + + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + { + if (_glfw.wl.offers[i].offer == offer) + { + if (_glfw.wl.offers[i].text_plain_utf8) + _glfw.wl.selectionOffer = offer; + else + wl_data_offer_destroy(offer); + + _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; + _glfw.wl.offerCount--; + break; + } + } } static const struct wl_data_device_listener dataDeviceListener = { @@ -1409,6 +1460,11 @@ void _glfwTerminateWayland(void) _glfw.wl.cursor.handle = NULL; } + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + wl_data_offer_destroy(_glfw.wl.offers[i].offer); + + _glfw_free(_glfw.wl.offers); + if (_glfw.wl.cursorSurface) wl_surface_destroy(_glfw.wl.cursorSurface); if (_glfw.wl.subcompositor) @@ -1423,12 +1479,12 @@ void _glfwTerminateWayland(void) zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager); if (_glfw.wl.wmBase) xdg_wm_base_destroy(_glfw.wl.wmBase); - if (_glfw.wl.dataSource) - wl_data_source_destroy(_glfw.wl.dataSource); + if (_glfw.wl.selectionOffer) + wl_data_offer_destroy(_glfw.wl.selectionOffer); + if (_glfw.wl.selectionSource) + wl_data_source_destroy(_glfw.wl.selectionSource); if (_glfw.wl.dataDevice) wl_data_device_destroy(_glfw.wl.dataDevice); - if (_glfw.wl.dataOffer) - wl_data_offer_destroy(_glfw.wl.dataOffer); if (_glfw.wl.dataDeviceManager) wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager); if (_glfw.wl.pointer) diff --git a/src/wl_platform.h b/src/wl_platform.h index 405f00b3..7bec2919 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -219,6 +219,12 @@ typedef struct _GLFWdecorationWayland struct wp_viewport* viewport; } _GLFWdecorationWayland; +typedef struct _GLFWofferWayland +{ + struct wl_data_offer* offer; + GLFWbool text_plain_utf8; +} _GLFWofferWayland; + // Wayland-specific per-window data // typedef struct _GLFWwindowWayland @@ -281,8 +287,6 @@ typedef struct _GLFWlibraryWayland struct wl_keyboard* keyboard; struct wl_data_device_manager* dataDeviceManager; struct wl_data_device* dataDevice; - struct wl_data_offer* dataOffer; - struct wl_data_source* dataSource; struct xdg_wm_base* wmBase; struct zxdg_decoration_manager_v1* decorationManager; struct wp_viewporter* viewporter; @@ -290,6 +294,12 @@ typedef struct _GLFWlibraryWayland struct zwp_pointer_constraints_v1* pointerConstraints; struct zwp_idle_inhibit_manager_v1* idleInhibitManager; + _GLFWofferWayland* offers; + unsigned int offerCount; + + struct wl_data_offer* selectionOffer; + struct wl_data_source* selectionSource; + int compositorVersion; int seatVersion; diff --git a/src/wl_window.c b/src/wl_window.c index 647234e3..e1c83fc2 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1578,7 +1578,7 @@ static void dataSourceHandleTarget(void* userData, struct wl_data_source* source, const char* mimeType) { - if (_glfw.wl.dataSource != source) + if (_glfw.wl.selectionSource != source) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unknown clipboard data source"); @@ -1595,7 +1595,7 @@ static void dataSourceHandleSend(void* userData, size_t len = strlen(string); int ret; - if (_glfw.wl.dataSource != source) + if (_glfw.wl.selectionSource != source) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unknown clipboard data source"); @@ -1643,14 +1643,14 @@ static void dataSourceHandleCancelled(void* userData, { wl_data_source_destroy(source); - if (_glfw.wl.dataSource != source) + if (_glfw.wl.selectionSource != source) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unknown clipboard data source"); return; } - _glfw.wl.dataSource = NULL; + _glfw.wl.selectionSource = NULL; } static const struct wl_data_source_listener dataSourceListener = { @@ -1661,10 +1661,10 @@ static const struct wl_data_source_listener dataSourceListener = { void _glfwSetClipboardStringWayland(const char* string) { - if (_glfw.wl.dataSource) + if (_glfw.wl.selectionSource) { - wl_data_source_destroy(_glfw.wl.dataSource); - _glfw.wl.dataSource = NULL; + wl_data_source_destroy(_glfw.wl.selectionSource); + _glfw.wl.selectionSource = NULL; } char* copy = _glfw_strdup(string); @@ -1678,9 +1678,9 @@ void _glfwSetClipboardStringWayland(const char* string) _glfw_free(_glfw.wl.clipboardSendString); _glfw.wl.clipboardSendString = copy; - _glfw.wl.dataSource = + _glfw.wl.selectionSource = wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); - if (!_glfw.wl.dataSource) + if (!_glfw.wl.selectionSource) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create clipboard data source"); @@ -1688,12 +1688,12 @@ void _glfwSetClipboardStringWayland(const char* string) _glfw.wl.clipboardSendString = NULL; return; } - wl_data_source_add_listener(_glfw.wl.dataSource, + wl_data_source_add_listener(_glfw.wl.selectionSource, &dataSourceListener, NULL); - wl_data_source_offer(_glfw.wl.dataSource, "text/plain;charset=utf-8"); + wl_data_source_offer(_glfw.wl.selectionSource, "text/plain;charset=utf-8"); wl_data_device_set_selection(_glfw.wl.dataDevice, - _glfw.wl.dataSource, + _glfw.wl.selectionSource, _glfw.wl.serial); } @@ -1719,14 +1719,14 @@ const char* _glfwGetClipboardStringWayland(void) int ret; size_t len = 0; - if (!_glfw.wl.dataOffer) + if (!_glfw.wl.selectionOffer) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "Wayland: No clipboard data available"); return NULL; } - if (_glfw.wl.dataSource) + if (_glfw.wl.selectionSource) return _glfw.wl.clipboardSendString; ret = pipe2(fds, O_CLOEXEC); @@ -1738,7 +1738,7 @@ const char* _glfwGetClipboardStringWayland(void) return NULL; } - wl_data_offer_receive(_glfw.wl.dataOffer, "text/plain;charset=utf-8", fds[1]); + wl_data_offer_receive(_glfw.wl.selectionOffer, "text/plain;charset=utf-8", fds[1]); flushDisplay(); close(fds[1]); From 46511652726afdaf3d92173f8f14d900f1f75994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 24 Mar 2022 18:02:48 +0100 Subject: [PATCH 57/88] Wayland: Simplify clipboard string allocation --- src/wl_init.c | 10 -------- src/wl_platform.h | 1 - src/wl_window.c | 59 ++++++++++++++++++++--------------------------- 3 files changed, 25 insertions(+), 45 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index 4368f3fa..972c38f6 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -1413,15 +1413,6 @@ int _glfwInitWayland(void) wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat); wl_data_device_add_listener(_glfw.wl.dataDevice, &dataDeviceListener, NULL); - - _glfw.wl.clipboardSize = 4096; - _glfw.wl.clipboardString = _glfw_calloc(_glfw.wl.clipboardSize, 1); - if (!_glfw.wl.clipboardString) - { - _glfwInputError(GLFW_OUT_OF_MEMORY, - "Wayland: Failed to allocate clipboard memory"); - return GLFW_FALSE; - } } return GLFW_TRUE; @@ -1513,6 +1504,5 @@ void _glfwTerminateWayland(void) close(_glfw.wl.cursorTimerfd); _glfw_free(_glfw.wl.clipboardString); - _glfw_free(_glfw.wl.clipboardSendString); } diff --git a/src/wl_platform.h b/src/wl_platform.h index 7bec2919..861f7cfe 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -317,7 +317,6 @@ typedef struct _GLFWlibraryWayland int keyboardLastScancode; char* clipboardString; size_t clipboardSize; - char* clipboardSendString; int timerfd; short int keycodes[256]; short int scancodes[GLFW_KEY_LAST + 1]; diff --git a/src/wl_window.c b/src/wl_window.c index e1c83fc2..f5ca406e 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1591,7 +1591,7 @@ static void dataSourceHandleSend(void* userData, const char* mimeType, int fd) { - char* string = _glfw.wl.clipboardSendString; + char* string = _glfw.wl.clipboardString; size_t len = strlen(string); int ret; @@ -1667,16 +1667,23 @@ void _glfwSetClipboardStringWayland(const char* string) _glfw.wl.selectionSource = NULL; } - char* copy = _glfw_strdup(string); - if (!copy) + const size_t requiredSize = strlen(string) + 1; + if (requiredSize > _glfw.wl.clipboardSize) { - _glfwInputError(GLFW_OUT_OF_MEMORY, - "Wayland: Failed to allocate clipboard string"); - return; + _glfw_free(_glfw.wl.clipboardString); + _glfw.wl.clipboardString = _glfw_calloc(requiredSize, 1); + if (!_glfw.wl.clipboardString) + { + _glfwInputError(GLFW_OUT_OF_MEMORY, + "Wayland: Failed to allocate clipboard string"); + return; + } + + _glfw.wl.clipboardSize = requiredSize; } - _glfw_free(_glfw.wl.clipboardSendString); - _glfw.wl.clipboardSendString = copy; + // The argument may be a substring of the clipboard string + memmove(_glfw.wl.clipboardString, string, requiredSize); _glfw.wl.selectionSource = wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); @@ -1684,8 +1691,6 @@ void _glfwSetClipboardStringWayland(const char* string) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create clipboard data source"); - _glfw_free(_glfw.wl.clipboardSendString); - _glfw.wl.clipboardSendString = NULL; return; } wl_data_source_add_listener(_glfw.wl.selectionSource, @@ -1697,22 +1702,6 @@ void _glfwSetClipboardStringWayland(const char* string) _glfw.wl.serial); } -static GLFWbool growClipboardString(void) -{ - char* clipboard = _glfw.wl.clipboardString; - - clipboard = _glfw_realloc(clipboard, _glfw.wl.clipboardSize * 2); - if (!clipboard) - { - _glfwInputError(GLFW_OUT_OF_MEMORY, - "Wayland: Failed to grow clipboard string"); - return GLFW_FALSE; - } - _glfw.wl.clipboardString = clipboard; - _glfw.wl.clipboardSize = _glfw.wl.clipboardSize * 2; - return GLFW_TRUE; -} - const char* _glfwGetClipboardStringWayland(void) { int fds[2]; @@ -1727,7 +1716,7 @@ const char* _glfwGetClipboardStringWayland(void) } if (_glfw.wl.selectionSource) - return _glfw.wl.clipboardSendString; + return _glfw.wl.clipboardString; ret = pipe2(fds, O_CLOEXEC); if (ret < 0) @@ -1747,13 +1736,20 @@ const char* _glfwGetClipboardStringWayland(void) { // Grow the clipboard if we need to paste something bigger, there is no // shrink operation yet. - if (len + 4096 > _glfw.wl.clipboardSize) + const size_t requiredSize = len + 4096 + 1; + if (requiredSize > _glfw.wl.clipboardSize) { - if (!growClipboardString()) + char* string = _glfw_realloc(_glfw.wl.clipboardString, requiredSize); + if (!string) { + _glfwInputError(GLFW_OUT_OF_MEMORY, + "Wayland: Failed to grow clipboard string"); close(fds[0]); return NULL; } + + _glfw.wl.clipboardString = string; + _glfw.wl.clipboardSize = requiredSize; } // Then read from the fd to the clipboard, handling all known errors. @@ -1773,11 +1769,6 @@ const char* _glfwGetClipboardStringWayland(void) len += ret; } close(fds[0]); - if (len + 1 > _glfw.wl.clipboardSize) - { - if (!growClipboardString()) - return NULL; - } _glfw.wl.clipboardString[len] = '\0'; return _glfw.wl.clipboardString; } From c13213533241d7bbf53da89641a8fe6c71d53c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 24 Mar 2022 19:04:15 +0100 Subject: [PATCH 58/88] Wayland: Clean up clipboard reading --- src/wl_window.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/wl_window.c b/src/wl_window.c index f5ca406e..5f9c7200 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1704,10 +1704,6 @@ void _glfwSetClipboardStringWayland(const char* string) const char* _glfwGetClipboardStringWayland(void) { - int fds[2]; - int ret; - size_t len = 0; - if (!_glfw.wl.selectionOffer) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, @@ -1718,8 +1714,9 @@ const char* _glfwGetClipboardStringWayland(void) if (_glfw.wl.selectionSource) return _glfw.wl.clipboardString; - ret = pipe2(fds, O_CLOEXEC); - if (ret < 0) + int fds[2]; + + if (pipe2(fds, O_CLOEXEC) == -1) { // TODO: also report errno maybe? _glfwInputError(GLFW_PLATFORM_ERROR, @@ -1732,11 +1729,14 @@ const char* _glfwGetClipboardStringWayland(void) flushDisplay(); close(fds[1]); + size_t length = 0; + for (;;) { // Grow the clipboard if we need to paste something bigger, there is no // shrink operation yet. - const size_t requiredSize = len + 4096 + 1; + const size_t readSize = 4096; + const size_t requiredSize = length + readSize + 1; if (requiredSize > _glfw.wl.clipboardSize) { char* string = _glfw_realloc(_glfw.wl.clipboardString, requiredSize); @@ -1753,23 +1753,27 @@ const char* _glfwGetClipboardStringWayland(void) } // Then read from the fd to the clipboard, handling all known errors. - ret = read(fds[0], _glfw.wl.clipboardString + len, 4096); - if (ret == 0) + const ssize_t result = read(fds[0], _glfw.wl.clipboardString + length, readSize); + if (result == 0) break; - if (ret == -1 && errno == EINTR) - continue; - if (ret == -1) + else if (result == -1) { + if (errno == EINTR) + continue; + // TODO: also report errno maybe. _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to read from clipboard fd"); close(fds[0]); return NULL; } - len += ret; + + length += result; } + close(fds[0]); - _glfw.wl.clipboardString[len] = '\0'; + + _glfw.wl.clipboardString[length] = '\0'; return _glfw.wl.clipboardString; } From e0889736fd603d163d87eef9426e4e4ebc68b60d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 24 Mar 2022 19:04:58 +0100 Subject: [PATCH 59/88] Wayland: Add strerror output to error descriptions --- src/wl_window.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/wl_window.c b/src/wl_window.c index 5f9c7200..2ad57378 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1626,9 +1626,9 @@ static void dataSourceHandleSend(void* userData, continue; if (ret == -1) { - // TODO: also report errno maybe. _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Error while writing the clipboard"); + "Wayland: Error while writing the clipboard: %s", + strerror(errno)); close(fd); return; } @@ -1718,9 +1718,9 @@ const char* _glfwGetClipboardStringWayland(void) if (pipe2(fds, O_CLOEXEC) == -1) { - // TODO: also report errno maybe? _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create clipboard pipe fds"); + "Wayland: Failed to create clipboard pipe: %s", + strerror(errno)); return NULL; } @@ -1761,9 +1761,9 @@ const char* _glfwGetClipboardStringWayland(void) if (errno == EINTR) continue; - // TODO: also report errno maybe. _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to read from clipboard fd"); + "Wayland: Failed to read from clipboard pipe: %s", + strerror(errno)); close(fds[0]); return NULL; } From ad4a9e42f0b43994149d590bc08bb4845ba2b86f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 24 Mar 2022 20:17:32 +0100 Subject: [PATCH 60/88] Move URI list parsing to shared code This will soon be used by the Wayland backend. --- src/init.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ src/internal.h | 1 + src/x11_window.c | 53 +----------------------------------------------- 3 files changed, 55 insertions(+), 52 deletions(-) diff --git a/src/init.c b/src/init.c index 9f0316ac..d07a492e 100644 --- a/src/init.c +++ b/src/init.c @@ -171,6 +171,59 @@ size_t _glfwEncodeUTF8(char* s, uint32_t codepoint) return count; } +// Splits and translates a text/uri-list into separate file paths +// NOTE: This function destroys the provided string +// +char** _glfwParseUriList(char* text, int* count) +{ + const char* prefix = "file://"; + char** paths = NULL; + char* line; + + *count = 0; + + while ((line = strtok(text, "\r\n"))) + { + char* path; + + text = NULL; + + if (line[0] == '#') + continue; + + if (strncmp(line, prefix, strlen(prefix)) == 0) + { + line += strlen(prefix); + // TODO: Validate hostname + while (*line != '/') + line++; + } + + (*count)++; + + path = _glfw_calloc(strlen(line) + 1, 1); + paths = _glfw_realloc(paths, *count * sizeof(char*)); + paths[*count - 1] = path; + + while (*line) + { + if (line[0] == '%' && line[1] && line[2]) + { + const char digits[3] = { line[1], line[2], '\0' }; + *path = (char) strtol(digits, NULL, 16); + line += 2; + } + else + *path = *line; + + path++; + line++; + } + } + + return paths; +} + char* _glfw_strdup(const char* source) { const size_t length = strlen(source); diff --git a/src/internal.h b/src/internal.h index b7dc13a0..7babe7e8 100644 --- a/src/internal.h +++ b/src/internal.h @@ -996,6 +996,7 @@ void _glfwTerminateVulkan(void); const char* _glfwGetVulkanResultString(VkResult result); size_t _glfwEncodeUTF8(char* s, uint32_t codepoint); +char** _glfwParseUriList(char* text, int* count); char* _glfw_strdup(const char* source); int _glfw_min(int a, int b); diff --git a/src/x11_window.c b/src/x11_window.c index 38f579fe..e021dfd7 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -407,57 +407,6 @@ static void updateWindowMode(_GLFWwindow* window) } } -// Splits and translates a text/uri-list into separate file paths -// NOTE: This function destroys the provided string -// -static char** parseUriList(char* text, int* count) -{ - const char* prefix = "file://"; - char** paths = NULL; - char* line; - - *count = 0; - - while ((line = strtok(text, "\r\n"))) - { - text = NULL; - - if (line[0] == '#') - continue; - - if (strncmp(line, prefix, strlen(prefix)) == 0) - { - line += strlen(prefix); - // TODO: Validate hostname - while (*line != '/') - line++; - } - - (*count)++; - - char* path = _glfw_calloc(strlen(line) + 1, 1); - paths = _glfw_realloc(paths, *count * sizeof(char*)); - paths[*count - 1] = path; - - while (*line) - { - if (line[0] == '%' && line[1] && line[2]) - { - const char digits[3] = { line[1], line[2], '\0' }; - *path = strtol(digits, NULL, 16); - line += 2; - } - else - *path = *line; - - path++; - line++; - } - } - - return paths; -} - // Decode a Unicode code point from a UTF-8 stream // Based on cutef8 by Jeff Bezanson (Public Domain) // @@ -1715,7 +1664,7 @@ static void processEvent(XEvent *event) if (result) { int count; - char** paths = parseUriList(data, &count); + char** paths = _glfwParseUriList(data, &count); _glfwInputDrop(window, count, (const char**) paths); From 34418951ccc3eeb3380cd3c93b5d4cf357151cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 24 Mar 2022 20:34:04 +0100 Subject: [PATCH 61/88] Wayland: Clean up clipboard writing --- src/wl_window.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/wl_window.c b/src/wl_window.c index 2ad57378..4c4bb0db 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1591,10 +1591,6 @@ static void dataSourceHandleSend(void* userData, const char* mimeType, int fd) { - char* string = _glfw.wl.clipboardString; - size_t len = strlen(string); - int ret; - if (_glfw.wl.selectionSource != source) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -1603,6 +1599,7 @@ static void dataSourceHandleSend(void* userData, return; } + char* string = _glfw.wl.clipboardString; if (!string) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -1619,22 +1616,26 @@ static void dataSourceHandleSend(void* userData, return; } - while (len > 0) + size_t length = strlen(string); + + while (length > 0) { - ret = write(fd, string, len); - if (ret == -1 && errno == EINTR) - continue; - if (ret == -1) + const ssize_t result = write(fd, string, length); + if (result == -1) { + if (errno == EINTR) + continue; + _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Error while writing the clipboard: %s", strerror(errno)); - close(fd); - return; + break; } - len -= ret; - string += ret; + + length -= result; + string += result; } + close(fd); } From 8d216054ad89ecd1030fe066a42d80a9d2765216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 24 Mar 2022 20:45:00 +0100 Subject: [PATCH 62/88] Wayland: Remove error reporting of external bugs Not sure that GLFW should be reporting that another client has made an invalid receive request. --- src/wl_window.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/wl_window.c b/src/wl_window.c index 4c4bb0db..12bda386 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1591,10 +1591,10 @@ static void dataSourceHandleSend(void* userData, const char* mimeType, int fd) { - if (_glfw.wl.selectionSource != source) + // Ignore it if this is an outdated or invalid request + if (_glfw.wl.selectionSource != source || + strcmp(mimeType, "text/plain;charset=utf-8") != 0) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Unknown clipboard data source"); close(fd); return; } @@ -1608,14 +1608,6 @@ static void dataSourceHandleSend(void* userData, return; } - if (strcmp(mimeType, "text/plain;charset=utf-8") != 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Wrong MIME type asked from clipboard"); - close(fd); - return; - } - size_t length = strlen(string); while (length > 0) @@ -1645,11 +1637,7 @@ static void dataSourceHandleCancelled(void* userData, wl_data_source_destroy(source); if (_glfw.wl.selectionSource != source) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Unknown clipboard data source"); return; - } _glfw.wl.selectionSource = NULL; } From 967282c2e664ccaaa074590491108d9100b51123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 24 Mar 2022 21:24:34 +0100 Subject: [PATCH 63/88] Wayland: Remove check for error that cannot happen Famous last words. --- src/wl_window.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/wl_window.c b/src/wl_window.c index 12bda386..e21755a8 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1600,14 +1600,6 @@ static void dataSourceHandleSend(void* userData, } char* string = _glfw.wl.clipboardString; - if (!string) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Copy requested from an invalid string"); - close(fd); - return; - } - size_t length = strlen(string); while (length > 0) From b7a3af9b795912d80f9efc57bbaa0977acaee4a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 29 Mar 2022 18:32:05 +0200 Subject: [PATCH 64/88] Wayland: Move window related code to window module The Wayland backend was the only one where half the window and input related code was in the init module. As those bits want to share more utility code with the window module, the interface between them grows. To prevent that, this gathers nearly all window and input related code into the window module. --- src/wl_init.c | 764 +-------------------------------------------- src/wl_platform.h | 3 + src/wl_window.c | 770 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 775 insertions(+), 762 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index 972c38f6..4ea5e1e4 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -28,7 +28,6 @@ #include "internal.h" -#include #include #include #include @@ -56,765 +55,6 @@ #include "wayland-pointer-constraints-unstable-v1-client-protocol-code.h" #include "wayland-idle-inhibit-unstable-v1-client-protocol-code.h" - -static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, - int* which) -{ - int focus; - _GLFWwindow* window = _glfw.windowListHead; - if (!which) - which = &focus; - while (window) - { - if (surface == window->wl.decorations.top.surface) - { - *which = topDecoration; - break; - } - if (surface == window->wl.decorations.left.surface) - { - *which = leftDecoration; - break; - } - if (surface == window->wl.decorations.right.surface) - { - *which = rightDecoration; - break; - } - if (surface == window->wl.decorations.bottom.surface) - { - *which = bottomDecoration; - break; - } - window = window->next; - } - return window; -} - -static void pointerHandleEnter(void* userData, - struct wl_pointer* pointer, - uint32_t serial, - struct wl_surface* surface, - wl_fixed_t sx, - wl_fixed_t sy) -{ - // Happens in the case we just destroyed the surface. - if (!surface) - return; - - int focus = 0; - _GLFWwindow* window = wl_surface_get_user_data(surface); - if (!window) - { - window = findWindowFromDecorationSurface(surface, &focus); - if (!window) - return; - } - - window->wl.decorations.focus = focus; - _glfw.wl.serial = serial; - _glfw.wl.pointerEnterSerial = serial; - _glfw.wl.pointerFocus = window; - - window->wl.hovered = GLFW_TRUE; - - _glfwSetCursorWayland(window, window->wl.currentCursor); - _glfwInputCursorEnter(window, GLFW_TRUE); -} - -static void pointerHandleLeave(void* userData, - struct wl_pointer* pointer, - uint32_t serial, - struct wl_surface* surface) -{ - _GLFWwindow* window = _glfw.wl.pointerFocus; - - if (!window) - return; - - window->wl.hovered = GLFW_FALSE; - - _glfw.wl.serial = serial; - _glfw.wl.pointerFocus = NULL; - _glfwInputCursorEnter(window, GLFW_FALSE); - _glfw.wl.cursorPreviousName = NULL; -} - -static void setCursor(_GLFWwindow* window, const char* name) -{ - struct wl_buffer* buffer; - struct wl_cursor* cursor; - struct wl_cursor_image* image; - struct wl_surface* surface = _glfw.wl.cursorSurface; - struct wl_cursor_theme* theme = _glfw.wl.cursorTheme; - int scale = 1; - - if (window->wl.scale > 1 && _glfw.wl.cursorThemeHiDPI) - { - // We only support up to scale=2 for now, since libwayland-cursor - // requires us to load a different theme for each size. - scale = 2; - theme = _glfw.wl.cursorThemeHiDPI; - } - - cursor = wl_cursor_theme_get_cursor(theme, name); - if (!cursor) - { - _glfwInputError(GLFW_CURSOR_UNAVAILABLE, - "Wayland: Standard cursor shape unavailable"); - return; - } - // TODO: handle animated cursors too. - image = cursor->images[0]; - - if (!image) - return; - - buffer = wl_cursor_image_get_buffer(image); - if (!buffer) - return; - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, - surface, - image->hotspot_x / scale, - image->hotspot_y / scale); - wl_surface_set_buffer_scale(surface, scale); - wl_surface_attach(surface, buffer, 0, 0); - wl_surface_damage(surface, 0, 0, - image->width, image->height); - wl_surface_commit(surface); - _glfw.wl.cursorPreviousName = name; -} - -static void pointerHandleMotion(void* userData, - struct wl_pointer* pointer, - uint32_t time, - wl_fixed_t sx, - wl_fixed_t sy) -{ - _GLFWwindow* window = _glfw.wl.pointerFocus; - const char* cursorName = NULL; - double x, y; - - if (!window) - return; - - if (window->cursorMode == GLFW_CURSOR_DISABLED) - return; - x = wl_fixed_to_double(sx); - y = wl_fixed_to_double(sy); - window->wl.cursorPosX = x; - window->wl.cursorPosY = y; - - switch (window->wl.decorations.focus) - { - case mainWindow: - _glfwInputCursorPos(window, x, y); - _glfw.wl.cursorPreviousName = NULL; - return; - case topDecoration: - if (y < _GLFW_DECORATION_WIDTH) - cursorName = "n-resize"; - else - cursorName = "left_ptr"; - break; - case leftDecoration: - if (y < _GLFW_DECORATION_WIDTH) - cursorName = "nw-resize"; - else - cursorName = "w-resize"; - break; - case rightDecoration: - if (y < _GLFW_DECORATION_WIDTH) - cursorName = "ne-resize"; - else - cursorName = "e-resize"; - break; - case bottomDecoration: - if (x < _GLFW_DECORATION_WIDTH) - cursorName = "sw-resize"; - else if (x > window->wl.width + _GLFW_DECORATION_WIDTH) - cursorName = "se-resize"; - else - cursorName = "s-resize"; - break; - default: - assert(0); - } - if (_glfw.wl.cursorPreviousName != cursorName) - setCursor(window, cursorName); -} - -static void pointerHandleButton(void* userData, - struct wl_pointer* pointer, - uint32_t serial, - uint32_t time, - uint32_t button, - uint32_t state) -{ - _GLFWwindow* window = _glfw.wl.pointerFocus; - int glfwButton; - uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE; - - if (!window) - return; - if (button == BTN_LEFT) - { - switch (window->wl.decorations.focus) - { - case mainWindow: - break; - case topDecoration: - if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) - edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP; - else - xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial); - break; - case leftDecoration: - if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) - edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; - else - edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; - break; - case rightDecoration: - if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) - edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; - else - edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; - break; - case bottomDecoration: - if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH) - edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; - else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH) - edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; - else - edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; - break; - default: - assert(0); - } - if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE) - { - xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat, - serial, edges); - return; - } - } - else if (button == BTN_RIGHT) - { - if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel) - { - xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, - _glfw.wl.seat, serial, - window->wl.cursorPosX, - window->wl.cursorPosY); - return; - } - } - - // Don’t pass the button to the user if it was related to a decoration. - if (window->wl.decorations.focus != mainWindow) - return; - - _glfw.wl.serial = serial; - - /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev - * codes. */ - glfwButton = button - BTN_LEFT; - - _glfwInputMouseClick(window, - glfwButton, - state == WL_POINTER_BUTTON_STATE_PRESSED - ? GLFW_PRESS - : GLFW_RELEASE, - _glfw.wl.xkb.modifiers); -} - -static void pointerHandleAxis(void* userData, - struct wl_pointer* pointer, - uint32_t time, - uint32_t axis, - wl_fixed_t value) -{ - _GLFWwindow* window = _glfw.wl.pointerFocus; - double x = 0.0, y = 0.0; - // Wayland scroll events are in pointer motion coordinate space (think two - // finger scroll). The factor 10 is commonly used to convert to "scroll - // step means 1.0. - const double scrollFactor = 1.0 / 10.0; - - if (!window) - return; - - assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL || - axis == WL_POINTER_AXIS_VERTICAL_SCROLL); - - if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) - x = -wl_fixed_to_double(value) * scrollFactor; - else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) - y = -wl_fixed_to_double(value) * scrollFactor; - - _glfwInputScroll(window, x, y); -} - -static const struct wl_pointer_listener pointerListener = { - pointerHandleEnter, - pointerHandleLeave, - pointerHandleMotion, - pointerHandleButton, - pointerHandleAxis, -}; - -static void keyboardHandleKeymap(void* userData, - struct wl_keyboard* keyboard, - uint32_t format, - int fd, - uint32_t size) -{ - struct xkb_keymap* keymap; - struct xkb_state* state; - struct xkb_compose_table* composeTable; - struct xkb_compose_state* composeState; - - char* mapStr; - const char* locale; - - if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) - { - close(fd); - return; - } - - mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - if (mapStr == MAP_FAILED) { - close(fd); - return; - } - - keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context, - mapStr, - XKB_KEYMAP_FORMAT_TEXT_V1, - 0); - munmap(mapStr, size); - close(fd); - - if (!keymap) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to compile keymap"); - return; - } - - state = xkb_state_new(keymap); - if (!state) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create XKB state"); - xkb_keymap_unref(keymap); - return; - } - - // Look up the preferred locale, falling back to "C" as default. - locale = getenv("LC_ALL"); - if (!locale) - locale = getenv("LC_CTYPE"); - if (!locale) - locale = getenv("LANG"); - if (!locale) - locale = "C"; - - composeTable = - xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale, - XKB_COMPOSE_COMPILE_NO_FLAGS); - if (composeTable) - { - composeState = - xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS); - xkb_compose_table_unref(composeTable); - if (composeState) - _glfw.wl.xkb.composeState = composeState; - else - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create XKB compose state"); - } - else - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create XKB compose table"); - } - - xkb_keymap_unref(_glfw.wl.xkb.keymap); - xkb_state_unref(_glfw.wl.xkb.state); - _glfw.wl.xkb.keymap = keymap; - _glfw.wl.xkb.state = state; - - _glfw.wl.xkb.controlMask = - 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control"); - _glfw.wl.xkb.altMask = - 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); - _glfw.wl.xkb.shiftMask = - 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); - _glfw.wl.xkb.superMask = - 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); - _glfw.wl.xkb.capsLockMask = - 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock"); - _glfw.wl.xkb.numLockMask = - 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2"); -} - -static void keyboardHandleEnter(void* userData, - struct wl_keyboard* keyboard, - uint32_t serial, - struct wl_surface* surface, - struct wl_array* keys) -{ - // Happens in the case we just destroyed the surface. - if (!surface) - return; - - _GLFWwindow* window = wl_surface_get_user_data(surface); - if (!window) - { - window = findWindowFromDecorationSurface(surface, NULL); - if (!window) - return; - } - - _glfw.wl.serial = serial; - _glfw.wl.keyboardFocus = window; - _glfwInputWindowFocus(window, GLFW_TRUE); -} - -static void keyboardHandleLeave(void* userData, - struct wl_keyboard* keyboard, - uint32_t serial, - struct wl_surface* surface) -{ - _GLFWwindow* window = _glfw.wl.keyboardFocus; - - if (!window) - return; - - struct itimerspec timer = {}; - timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL); - - _glfw.wl.serial = serial; - _glfw.wl.keyboardFocus = NULL; - _glfwInputWindowFocus(window, GLFW_FALSE); -} - -static int translateKey(uint32_t scancode) -{ - if (scancode < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0])) - return _glfw.wl.keycodes[scancode]; - - return GLFW_KEY_UNKNOWN; -} - -static xkb_keysym_t composeSymbol(xkb_keysym_t sym) -{ - if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState) - return sym; - if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym) - != XKB_COMPOSE_FEED_ACCEPTED) - return sym; - switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState)) - { - case XKB_COMPOSE_COMPOSED: - return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState); - case XKB_COMPOSE_COMPOSING: - case XKB_COMPOSE_CANCELLED: - return XKB_KEY_NoSymbol; - case XKB_COMPOSE_NOTHING: - default: - return sym; - } -} - -GLFWbool _glfwInputTextWayland(_GLFWwindow* window, uint32_t scancode) -{ - const xkb_keysym_t* keysyms; - const xkb_keycode_t keycode = scancode + 8; - - if (xkb_state_key_get_syms(_glfw.wl.xkb.state, keycode, &keysyms) == 1) - { - const xkb_keysym_t keysym = composeSymbol(keysyms[0]); - const uint32_t codepoint = _glfwKeySym2Unicode(keysym); - if (codepoint != GLFW_INVALID_CODEPOINT) - { - const int mods = _glfw.wl.xkb.modifiers; - const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); - _glfwInputChar(window, codepoint, mods, plain); - } - } - - return xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, keycode); -} - -static void keyboardHandleKey(void* userData, - struct wl_keyboard* keyboard, - uint32_t serial, - uint32_t time, - uint32_t scancode, - uint32_t state) -{ - _GLFWwindow* window = _glfw.wl.keyboardFocus; - if (!window) - return; - - const int key = translateKey(scancode); - const int action = - state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE; - - _glfw.wl.serial = serial; - _glfwInputKey(window, key, scancode, action, _glfw.wl.xkb.modifiers); - - struct itimerspec timer = {}; - - if (action == GLFW_PRESS) - { - const GLFWbool shouldRepeat = _glfwInputTextWayland(window, scancode); - - if (shouldRepeat && _glfw.wl.keyboardRepeatRate > 0) - { - _glfw.wl.keyboardLastKey = key; - _glfw.wl.keyboardLastScancode = scancode; - if (_glfw.wl.keyboardRepeatRate > 1) - timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyboardRepeatRate; - else - timer.it_interval.tv_sec = 1; - - timer.it_value.tv_sec = _glfw.wl.keyboardRepeatDelay / 1000; - timer.it_value.tv_nsec = (_glfw.wl.keyboardRepeatDelay % 1000) * 1000000; - } - } - - timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL); -} - -static void keyboardHandleModifiers(void* userData, - struct wl_keyboard* keyboard, - uint32_t serial, - uint32_t modsDepressed, - uint32_t modsLatched, - uint32_t modsLocked, - uint32_t group) -{ - _glfw.wl.serial = serial; - - if (!_glfw.wl.xkb.keymap) - return; - - xkb_state_update_mask(_glfw.wl.xkb.state, - modsDepressed, - modsLatched, - modsLocked, - 0, - 0, - group); - - const xkb_mod_mask_t mask = - xkb_state_serialize_mods(_glfw.wl.xkb.state, - XKB_STATE_MODS_DEPRESSED | - XKB_STATE_LAYOUT_DEPRESSED | - XKB_STATE_MODS_LATCHED | - XKB_STATE_LAYOUT_LATCHED); - - unsigned int mods = 0; - - if (mask & _glfw.wl.xkb.controlMask) - mods |= GLFW_MOD_CONTROL; - if (mask & _glfw.wl.xkb.altMask) - mods |= GLFW_MOD_ALT; - if (mask & _glfw.wl.xkb.shiftMask) - mods |= GLFW_MOD_SHIFT; - if (mask & _glfw.wl.xkb.superMask) - mods |= GLFW_MOD_SUPER; - if (mask & _glfw.wl.xkb.capsLockMask) - mods |= GLFW_MOD_CAPS_LOCK; - if (mask & _glfw.wl.xkb.numLockMask) - mods |= GLFW_MOD_NUM_LOCK; - - _glfw.wl.xkb.modifiers = mods; -} - -#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION -static void keyboardHandleRepeatInfo(void* userData, - struct wl_keyboard* keyboard, - int32_t rate, - int32_t delay) -{ - if (keyboard != _glfw.wl.keyboard) - return; - - _glfw.wl.keyboardRepeatRate = rate; - _glfw.wl.keyboardRepeatDelay = delay; -} -#endif - -static const struct wl_keyboard_listener keyboardListener = { - keyboardHandleKeymap, - keyboardHandleEnter, - keyboardHandleLeave, - keyboardHandleKey, - keyboardHandleModifiers, -#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION - keyboardHandleRepeatInfo, -#endif -}; - -static void seatHandleCapabilities(void* userData, - struct wl_seat* seat, - enum wl_seat_capability caps) -{ - if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer) - { - _glfw.wl.pointer = wl_seat_get_pointer(seat); - wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL); - } - else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer) - { - wl_pointer_destroy(_glfw.wl.pointer); - _glfw.wl.pointer = NULL; - } - - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard) - { - _glfw.wl.keyboard = wl_seat_get_keyboard(seat); - wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL); - } - else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard) - { - wl_keyboard_destroy(_glfw.wl.keyboard); - _glfw.wl.keyboard = NULL; - } -} - -static void seatHandleName(void* userData, - struct wl_seat* seat, - const char* name) -{ -} - -static const struct wl_seat_listener seatListener = { - seatHandleCapabilities, - seatHandleName, -}; - -static void dataOfferHandleOffer(void* userData, - struct wl_data_offer* offer, - const char* mimeType) -{ - for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) - { - if (_glfw.wl.offers[i].offer == offer) - { - if (strcmp(mimeType, "text/plain;charset=utf-8") == 0) - _glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE; - - break; - } - } -} - -static const struct wl_data_offer_listener dataOfferListener = { - dataOfferHandleOffer, -}; - -static void dataDeviceHandleDataOffer(void* userData, - struct wl_data_device* device, - struct wl_data_offer* offer) -{ - _GLFWofferWayland* offers = - _glfw_realloc(_glfw.wl.offers, _glfw.wl.offerCount + 1); - if (!offers) - { - _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); - return; - } - - _glfw.wl.offers = offers; - _glfw.wl.offerCount++; - - _glfw.wl.offers[_glfw.wl.offerCount - 1] = (_GLFWofferWayland) { offer }; - wl_data_offer_add_listener(offer, &dataOfferListener, NULL); -} - -static void dataDeviceHandleEnter(void* userData, - struct wl_data_device* device, - uint32_t serial, - struct wl_surface* surface, - wl_fixed_t x, - wl_fixed_t y, - struct wl_data_offer* offer) -{ - for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) - { - if (_glfw.wl.offers[i].offer == offer) - { - _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; - _glfw.wl.offerCount--; - - // We don't yet handle drag and drop - wl_data_offer_accept(offer, serial, NULL); - wl_data_offer_destroy(offer); - break; - } - } -} - -static void dataDeviceHandleLeave(void* userData, - struct wl_data_device* device) -{ -} - -static void dataDeviceHandleMotion(void* userData, - struct wl_data_device* device, - uint32_t time, - wl_fixed_t x, - wl_fixed_t y) -{ -} - -static void dataDeviceHandleDrop(void* userData, - struct wl_data_device* device) -{ -} - -static void dataDeviceHandleSelection(void* userData, - struct wl_data_device* device, - struct wl_data_offer* offer) -{ - if (_glfw.wl.selectionOffer) - { - wl_data_offer_destroy(_glfw.wl.selectionOffer); - _glfw.wl.selectionOffer = NULL; - } - - for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) - { - if (_glfw.wl.offers[i].offer == offer) - { - if (_glfw.wl.offers[i].text_plain_utf8) - _glfw.wl.selectionOffer = offer; - else - wl_data_offer_destroy(offer); - - _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; - _glfw.wl.offerCount--; - break; - } - } -} - -static const struct wl_data_device_listener dataDeviceListener = { - dataDeviceHandleDataOffer, - dataDeviceHandleEnter, - dataDeviceHandleLeave, - dataDeviceHandleMotion, - dataDeviceHandleDrop, - dataDeviceHandleSelection, -}; - static void wmBaseHandlePing(void* userData, struct xdg_wm_base* wmBase, uint32_t serial) @@ -861,7 +101,7 @@ static void registryHandleGlobal(void* userData, _glfw.wl.seat = wl_registry_bind(registry, name, &wl_seat_interface, _glfw.wl.seatVersion); - wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL); + _glfwAddSeatListenerWayland(_glfw.wl.seat); } } else if (strcmp(interface, "wl_data_device_manager") == 0) @@ -1412,7 +652,7 @@ int _glfwInitWayland(void) _glfw.wl.dataDevice = wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat); - wl_data_device_add_listener(_glfw.wl.dataDevice, &dataDeviceListener, NULL); + _glfwAddDataDeviceListenerWayland(_glfw.wl.dataDevice); } return GLFW_TRUE; diff --git a/src/wl_platform.h b/src/wl_platform.h index 861f7cfe..e6d1579f 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -506,3 +506,6 @@ void _glfwSetGammaRampWayland(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); void _glfwAddOutputWayland(uint32_t name, uint32_t version); GLFWbool _glfwInputTextWayland(_GLFWwindow* window, uint32_t scancode); +void _glfwAddSeatListenerWayland(struct wl_seat* seat); +void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device); + diff --git a/src/wl_window.c b/src/wl_window.c index e21755a8..d32ef86a 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -811,6 +812,775 @@ static void handleEvents(double* timeout) } } +static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, + int* which) +{ + int focus; + _GLFWwindow* window = _glfw.windowListHead; + if (!which) + which = &focus; + while (window) + { + if (surface == window->wl.decorations.top.surface) + { + *which = topDecoration; + break; + } + if (surface == window->wl.decorations.left.surface) + { + *which = leftDecoration; + break; + } + if (surface == window->wl.decorations.right.surface) + { + *which = rightDecoration; + break; + } + if (surface == window->wl.decorations.bottom.surface) + { + *which = bottomDecoration; + break; + } + window = window->next; + } + return window; +} + +static void pointerHandleEnter(void* userData, + struct wl_pointer* pointer, + uint32_t serial, + struct wl_surface* surface, + wl_fixed_t sx, + wl_fixed_t sy) +{ + // Happens in the case we just destroyed the surface. + if (!surface) + return; + + int focus = 0; + _GLFWwindow* window = wl_surface_get_user_data(surface); + if (!window) + { + window = findWindowFromDecorationSurface(surface, &focus); + if (!window) + return; + } + + window->wl.decorations.focus = focus; + _glfw.wl.serial = serial; + _glfw.wl.pointerEnterSerial = serial; + _glfw.wl.pointerFocus = window; + + window->wl.hovered = GLFW_TRUE; + + _glfwSetCursorWayland(window, window->wl.currentCursor); + _glfwInputCursorEnter(window, GLFW_TRUE); +} + +static void pointerHandleLeave(void* userData, + struct wl_pointer* pointer, + uint32_t serial, + struct wl_surface* surface) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + + if (!window) + return; + + window->wl.hovered = GLFW_FALSE; + + _glfw.wl.serial = serial; + _glfw.wl.pointerFocus = NULL; + _glfwInputCursorEnter(window, GLFW_FALSE); + _glfw.wl.cursorPreviousName = NULL; +} + +static void setCursor(_GLFWwindow* window, const char* name) +{ + struct wl_buffer* buffer; + struct wl_cursor* cursor; + struct wl_cursor_image* image; + struct wl_surface* surface = _glfw.wl.cursorSurface; + struct wl_cursor_theme* theme = _glfw.wl.cursorTheme; + int scale = 1; + + if (window->wl.scale > 1 && _glfw.wl.cursorThemeHiDPI) + { + // We only support up to scale=2 for now, since libwayland-cursor + // requires us to load a different theme for each size. + scale = 2; + theme = _glfw.wl.cursorThemeHiDPI; + } + + cursor = wl_cursor_theme_get_cursor(theme, name); + if (!cursor) + { + _glfwInputError(GLFW_CURSOR_UNAVAILABLE, + "Wayland: Standard cursor shape unavailable"); + return; + } + // TODO: handle animated cursors too. + image = cursor->images[0]; + + if (!image) + return; + + buffer = wl_cursor_image_get_buffer(image); + if (!buffer) + return; + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, + surface, + image->hotspot_x / scale, + image->hotspot_y / scale); + wl_surface_set_buffer_scale(surface, scale); + wl_surface_attach(surface, buffer, 0, 0); + wl_surface_damage(surface, 0, 0, + image->width, image->height); + wl_surface_commit(surface); + _glfw.wl.cursorPreviousName = name; +} + +static void pointerHandleMotion(void* userData, + struct wl_pointer* pointer, + uint32_t time, + wl_fixed_t sx, + wl_fixed_t sy) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + const char* cursorName = NULL; + double x, y; + + if (!window) + return; + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + return; + x = wl_fixed_to_double(sx); + y = wl_fixed_to_double(sy); + window->wl.cursorPosX = x; + window->wl.cursorPosY = y; + + switch (window->wl.decorations.focus) + { + case mainWindow: + _glfwInputCursorPos(window, x, y); + _glfw.wl.cursorPreviousName = NULL; + return; + case topDecoration: + if (y < _GLFW_DECORATION_WIDTH) + cursorName = "n-resize"; + else + cursorName = "left_ptr"; + break; + case leftDecoration: + if (y < _GLFW_DECORATION_WIDTH) + cursorName = "nw-resize"; + else + cursorName = "w-resize"; + break; + case rightDecoration: + if (y < _GLFW_DECORATION_WIDTH) + cursorName = "ne-resize"; + else + cursorName = "e-resize"; + break; + case bottomDecoration: + if (x < _GLFW_DECORATION_WIDTH) + cursorName = "sw-resize"; + else if (x > window->wl.width + _GLFW_DECORATION_WIDTH) + cursorName = "se-resize"; + else + cursorName = "s-resize"; + break; + default: + assert(0); + } + if (_glfw.wl.cursorPreviousName != cursorName) + setCursor(window, cursorName); +} + +static void pointerHandleButton(void* userData, + struct wl_pointer* pointer, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + int glfwButton; + uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE; + + if (!window) + return; + if (button == BTN_LEFT) + { + switch (window->wl.decorations.focus) + { + case mainWindow: + break; + case topDecoration: + if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP; + else + xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial); + break; + case leftDecoration: + if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; + else + edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; + break; + case rightDecoration: + if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; + else + edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; + break; + case bottomDecoration: + if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH) + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; + else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH) + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; + else + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; + break; + default: + assert(0); + } + if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE) + { + xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat, + serial, edges); + return; + } + } + else if (button == BTN_RIGHT) + { + if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel) + { + xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, + _glfw.wl.seat, serial, + window->wl.cursorPosX, + window->wl.cursorPosY); + return; + } + } + + // Don’t pass the button to the user if it was related to a decoration. + if (window->wl.decorations.focus != mainWindow) + return; + + _glfw.wl.serial = serial; + + /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev + * codes. */ + glfwButton = button - BTN_LEFT; + + _glfwInputMouseClick(window, + glfwButton, + state == WL_POINTER_BUTTON_STATE_PRESSED + ? GLFW_PRESS + : GLFW_RELEASE, + _glfw.wl.xkb.modifiers); +} + +static void pointerHandleAxis(void* userData, + struct wl_pointer* pointer, + uint32_t time, + uint32_t axis, + wl_fixed_t value) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + double x = 0.0, y = 0.0; + // Wayland scroll events are in pointer motion coordinate space (think two + // finger scroll). The factor 10 is commonly used to convert to "scroll + // step means 1.0. + const double scrollFactor = 1.0 / 10.0; + + if (!window) + return; + + assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL || + axis == WL_POINTER_AXIS_VERTICAL_SCROLL); + + if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) + x = -wl_fixed_to_double(value) * scrollFactor; + else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) + y = -wl_fixed_to_double(value) * scrollFactor; + + _glfwInputScroll(window, x, y); +} + +static const struct wl_pointer_listener pointerListener = { + pointerHandleEnter, + pointerHandleLeave, + pointerHandleMotion, + pointerHandleButton, + pointerHandleAxis, +}; + +static void keyboardHandleKeymap(void* userData, + struct wl_keyboard* keyboard, + uint32_t format, + int fd, + uint32_t size) +{ + struct xkb_keymap* keymap; + struct xkb_state* state; + struct xkb_compose_table* composeTable; + struct xkb_compose_state* composeState; + + char* mapStr; + const char* locale; + + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) + { + close(fd); + return; + } + + mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (mapStr == MAP_FAILED) { + close(fd); + return; + } + + keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context, + mapStr, + XKB_KEYMAP_FORMAT_TEXT_V1, + 0); + munmap(mapStr, size); + close(fd); + + if (!keymap) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to compile keymap"); + return; + } + + state = xkb_state_new(keymap); + if (!state) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB state"); + xkb_keymap_unref(keymap); + return; + } + + // Look up the preferred locale, falling back to "C" as default. + locale = getenv("LC_ALL"); + if (!locale) + locale = getenv("LC_CTYPE"); + if (!locale) + locale = getenv("LANG"); + if (!locale) + locale = "C"; + + composeTable = + xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale, + XKB_COMPOSE_COMPILE_NO_FLAGS); + if (composeTable) + { + composeState = + xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS); + xkb_compose_table_unref(composeTable); + if (composeState) + _glfw.wl.xkb.composeState = composeState; + else + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB compose state"); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB compose table"); + } + + xkb_keymap_unref(_glfw.wl.xkb.keymap); + xkb_state_unref(_glfw.wl.xkb.state); + _glfw.wl.xkb.keymap = keymap; + _glfw.wl.xkb.state = state; + + _glfw.wl.xkb.controlMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control"); + _glfw.wl.xkb.altMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); + _glfw.wl.xkb.shiftMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); + _glfw.wl.xkb.superMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); + _glfw.wl.xkb.capsLockMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock"); + _glfw.wl.xkb.numLockMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2"); +} + +static void keyboardHandleEnter(void* userData, + struct wl_keyboard* keyboard, + uint32_t serial, + struct wl_surface* surface, + struct wl_array* keys) +{ + // Happens in the case we just destroyed the surface. + if (!surface) + return; + + _GLFWwindow* window = wl_surface_get_user_data(surface); + if (!window) + { + window = findWindowFromDecorationSurface(surface, NULL); + if (!window) + return; + } + + _glfw.wl.serial = serial; + _glfw.wl.keyboardFocus = window; + _glfwInputWindowFocus(window, GLFW_TRUE); +} + +static void keyboardHandleLeave(void* userData, + struct wl_keyboard* keyboard, + uint32_t serial, + struct wl_surface* surface) +{ + _GLFWwindow* window = _glfw.wl.keyboardFocus; + + if (!window) + return; + + struct itimerspec timer = {}; + timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL); + + _glfw.wl.serial = serial; + _glfw.wl.keyboardFocus = NULL; + _glfwInputWindowFocus(window, GLFW_FALSE); +} + +static int translateKey(uint32_t scancode) +{ + if (scancode < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0])) + return _glfw.wl.keycodes[scancode]; + + return GLFW_KEY_UNKNOWN; +} + +static xkb_keysym_t composeSymbol(xkb_keysym_t sym) +{ + if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState) + return sym; + if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym) + != XKB_COMPOSE_FEED_ACCEPTED) + return sym; + switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState)) + { + case XKB_COMPOSE_COMPOSED: + return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState); + case XKB_COMPOSE_COMPOSING: + case XKB_COMPOSE_CANCELLED: + return XKB_KEY_NoSymbol; + case XKB_COMPOSE_NOTHING: + default: + return sym; + } +} + +GLFWbool _glfwInputTextWayland(_GLFWwindow* window, uint32_t scancode) +{ + const xkb_keysym_t* keysyms; + const xkb_keycode_t keycode = scancode + 8; + + if (xkb_state_key_get_syms(_glfw.wl.xkb.state, keycode, &keysyms) == 1) + { + const xkb_keysym_t keysym = composeSymbol(keysyms[0]); + const uint32_t codepoint = _glfwKeySym2Unicode(keysym); + if (codepoint != GLFW_INVALID_CODEPOINT) + { + const int mods = _glfw.wl.xkb.modifiers; + const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); + _glfwInputChar(window, codepoint, mods, plain); + } + } + + return xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, keycode); +} + +static void keyboardHandleKey(void* userData, + struct wl_keyboard* keyboard, + uint32_t serial, + uint32_t time, + uint32_t scancode, + uint32_t state) +{ + _GLFWwindow* window = _glfw.wl.keyboardFocus; + if (!window) + return; + + const int key = translateKey(scancode); + const int action = + state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE; + + _glfw.wl.serial = serial; + _glfwInputKey(window, key, scancode, action, _glfw.wl.xkb.modifiers); + + struct itimerspec timer = {}; + + if (action == GLFW_PRESS) + { + const GLFWbool shouldRepeat = _glfwInputTextWayland(window, scancode); + + if (shouldRepeat && _glfw.wl.keyboardRepeatRate > 0) + { + _glfw.wl.keyboardLastKey = key; + _glfw.wl.keyboardLastScancode = scancode; + if (_glfw.wl.keyboardRepeatRate > 1) + timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyboardRepeatRate; + else + timer.it_interval.tv_sec = 1; + + timer.it_value.tv_sec = _glfw.wl.keyboardRepeatDelay / 1000; + timer.it_value.tv_nsec = (_glfw.wl.keyboardRepeatDelay % 1000) * 1000000; + } + } + + timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL); +} + +static void keyboardHandleModifiers(void* userData, + struct wl_keyboard* keyboard, + uint32_t serial, + uint32_t modsDepressed, + uint32_t modsLatched, + uint32_t modsLocked, + uint32_t group) +{ + _glfw.wl.serial = serial; + + if (!_glfw.wl.xkb.keymap) + return; + + xkb_state_update_mask(_glfw.wl.xkb.state, + modsDepressed, + modsLatched, + modsLocked, + 0, + 0, + group); + + const xkb_mod_mask_t mask = + xkb_state_serialize_mods(_glfw.wl.xkb.state, + XKB_STATE_MODS_DEPRESSED | + XKB_STATE_LAYOUT_DEPRESSED | + XKB_STATE_MODS_LATCHED | + XKB_STATE_LAYOUT_LATCHED); + + unsigned int mods = 0; + + if (mask & _glfw.wl.xkb.controlMask) + mods |= GLFW_MOD_CONTROL; + if (mask & _glfw.wl.xkb.altMask) + mods |= GLFW_MOD_ALT; + if (mask & _glfw.wl.xkb.shiftMask) + mods |= GLFW_MOD_SHIFT; + if (mask & _glfw.wl.xkb.superMask) + mods |= GLFW_MOD_SUPER; + if (mask & _glfw.wl.xkb.capsLockMask) + mods |= GLFW_MOD_CAPS_LOCK; + if (mask & _glfw.wl.xkb.numLockMask) + mods |= GLFW_MOD_NUM_LOCK; + + _glfw.wl.xkb.modifiers = mods; +} + +#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION +static void keyboardHandleRepeatInfo(void* userData, + struct wl_keyboard* keyboard, + int32_t rate, + int32_t delay) +{ + if (keyboard != _glfw.wl.keyboard) + return; + + _glfw.wl.keyboardRepeatRate = rate; + _glfw.wl.keyboardRepeatDelay = delay; +} +#endif + +static const struct wl_keyboard_listener keyboardListener = { + keyboardHandleKeymap, + keyboardHandleEnter, + keyboardHandleLeave, + keyboardHandleKey, + keyboardHandleModifiers, +#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION + keyboardHandleRepeatInfo, +#endif +}; + +static void seatHandleCapabilities(void* userData, + struct wl_seat* seat, + enum wl_seat_capability caps) +{ + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer) + { + _glfw.wl.pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL); + } + else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer) + { + wl_pointer_destroy(_glfw.wl.pointer); + _glfw.wl.pointer = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard) + { + _glfw.wl.keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL); + } + else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard) + { + wl_keyboard_destroy(_glfw.wl.keyboard); + _glfw.wl.keyboard = NULL; + } +} + +static void seatHandleName(void* userData, + struct wl_seat* seat, + const char* name) +{ +} + +static const struct wl_seat_listener seatListener = { + seatHandleCapabilities, + seatHandleName, +}; + +static void dataOfferHandleOffer(void* userData, + struct wl_data_offer* offer, + const char* mimeType) +{ + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + { + if (_glfw.wl.offers[i].offer == offer) + { + if (strcmp(mimeType, "text/plain;charset=utf-8") == 0) + _glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE; + + break; + } + } +} + +static const struct wl_data_offer_listener dataOfferListener = { + dataOfferHandleOffer +}; + +static void dataDeviceHandleDataOffer(void* userData, + struct wl_data_device* device, + struct wl_data_offer* offer) +{ + _GLFWofferWayland* offers = + _glfw_realloc(_glfw.wl.offers, _glfw.wl.offerCount + 1); + if (!offers) + { + _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); + return; + } + + _glfw.wl.offers = offers; + _glfw.wl.offerCount++; + + _glfw.wl.offers[_glfw.wl.offerCount - 1] = (_GLFWofferWayland) { offer }; + wl_data_offer_add_listener(offer, &dataOfferListener, NULL); +} + +static void dataDeviceHandleEnter(void* userData, + struct wl_data_device* device, + uint32_t serial, + struct wl_surface* surface, + wl_fixed_t x, + wl_fixed_t y, + struct wl_data_offer* offer) +{ + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + { + if (_glfw.wl.offers[i].offer == offer) + { + _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; + _glfw.wl.offerCount--; + + // We don't yet handle drag and drop + wl_data_offer_accept(offer, serial, NULL); + wl_data_offer_destroy(offer); + break; + } + } +} + +static void dataDeviceHandleLeave(void* userData, + struct wl_data_device* device) +{ +} + +static void dataDeviceHandleMotion(void* userData, + struct wl_data_device* device, + uint32_t time, + wl_fixed_t x, + wl_fixed_t y) +{ +} + +static void dataDeviceHandleDrop(void* userData, + struct wl_data_device* device) +{ +} + +static void dataDeviceHandleSelection(void* userData, + struct wl_data_device* device, + struct wl_data_offer* offer) +{ + if (_glfw.wl.selectionOffer) + { + wl_data_offer_destroy(_glfw.wl.selectionOffer); + _glfw.wl.selectionOffer = NULL; + } + + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + { + if (_glfw.wl.offers[i].offer == offer) + { + if (_glfw.wl.offers[i].text_plain_utf8) + _glfw.wl.selectionOffer = offer; + else + wl_data_offer_destroy(offer); + + _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; + _glfw.wl.offerCount--; + break; + } + } +} + +const struct wl_data_device_listener dataDeviceListener = { + dataDeviceHandleDataOffer, + dataDeviceHandleEnter, + dataDeviceHandleLeave, + dataDeviceHandleMotion, + dataDeviceHandleDrop, + dataDeviceHandleSelection, +}; + +void _glfwAddSeatListenerWayland(struct wl_seat* seat) +{ + wl_seat_add_listener(seat, &seatListener, NULL); +} + +void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device) +{ + wl_data_device_add_listener(device, &dataDeviceListener, NULL); +} + + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// From f010335b8cad7937397fcbfd252033899327b551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 29 Mar 2022 19:00:18 +0200 Subject: [PATCH 65/88] Wayland: Make data offer reading a generic utility This will be needed for drag and drop reception as well. --- src/wl_platform.h | 1 - src/wl_window.c | 147 +++++++++++++++++++++++----------------------- 2 files changed, 73 insertions(+), 75 deletions(-) diff --git a/src/wl_platform.h b/src/wl_platform.h index e6d1579f..5ad9fa07 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -316,7 +316,6 @@ typedef struct _GLFWlibraryWayland int keyboardLastKey; int keyboardLastScancode; char* clipboardString; - size_t clipboardSize; int timerfd; short int keycodes[256]; short int scancodes[GLFW_KEY_LAST + 1]; diff --git a/src/wl_window.c b/src/wl_window.c index d32ef86a..57c75cec 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -812,6 +812,70 @@ static void handleEvents(double* timeout) } } +// Reads the specified data offer as the specified MIME type +// +static char* readDataOfferAsString(struct wl_data_offer* offer, const char* mimeType) +{ + int fds[2]; + + if (pipe2(fds, O_CLOEXEC) == -1) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create pipe for data offer: %s", + strerror(errno)); + return NULL; + } + + wl_data_offer_receive(offer, mimeType, fds[1]); + flushDisplay(); + close(fds[1]); + + char* string = NULL; + size_t size = 0; + size_t length = 0; + + for (;;) + { + const size_t readSize = 4096; + const size_t requiredSize = length + readSize + 1; + if (requiredSize > size) + { + char* longer = _glfw_realloc(string, requiredSize); + if (!longer) + { + _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); + close(fds[0]); + return NULL; + } + + string = longer; + size = requiredSize; + } + + const ssize_t result = read(fds[0], string + length, readSize); + if (result == 0) + break; + else if (result == -1) + { + if (errno == EINTR) + continue; + + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to read from data offer pipe: %s", + strerror(errno)); + close(fds[0]); + return NULL; + } + + length += result; + } + + close(fds[0]); + + string[length] = '\0'; + return string; +} + static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, int* which) { @@ -2418,23 +2482,15 @@ void _glfwSetClipboardStringWayland(const char* string) _glfw.wl.selectionSource = NULL; } - const size_t requiredSize = strlen(string) + 1; - if (requiredSize > _glfw.wl.clipboardSize) + char* copy = _glfw_strdup(string); + if (!copy) { - _glfw_free(_glfw.wl.clipboardString); - _glfw.wl.clipboardString = _glfw_calloc(requiredSize, 1); - if (!_glfw.wl.clipboardString) - { - _glfwInputError(GLFW_OUT_OF_MEMORY, - "Wayland: Failed to allocate clipboard string"); - return; - } - - _glfw.wl.clipboardSize = requiredSize; + _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); + return; } - // The argument may be a substring of the clipboard string - memmove(_glfw.wl.clipboardString, string, requiredSize); + _glfw_free(_glfw.wl.clipboardString); + _glfw.wl.clipboardString = copy; _glfw.wl.selectionSource = wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); @@ -2465,66 +2521,9 @@ const char* _glfwGetClipboardStringWayland(void) if (_glfw.wl.selectionSource) return _glfw.wl.clipboardString; - int fds[2]; - - if (pipe2(fds, O_CLOEXEC) == -1) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create clipboard pipe: %s", - strerror(errno)); - return NULL; - } - - wl_data_offer_receive(_glfw.wl.selectionOffer, "text/plain;charset=utf-8", fds[1]); - - flushDisplay(); - close(fds[1]); - - size_t length = 0; - - for (;;) - { - // Grow the clipboard if we need to paste something bigger, there is no - // shrink operation yet. - const size_t readSize = 4096; - const size_t requiredSize = length + readSize + 1; - if (requiredSize > _glfw.wl.clipboardSize) - { - char* string = _glfw_realloc(_glfw.wl.clipboardString, requiredSize); - if (!string) - { - _glfwInputError(GLFW_OUT_OF_MEMORY, - "Wayland: Failed to grow clipboard string"); - close(fds[0]); - return NULL; - } - - _glfw.wl.clipboardString = string; - _glfw.wl.clipboardSize = requiredSize; - } - - // Then read from the fd to the clipboard, handling all known errors. - const ssize_t result = read(fds[0], _glfw.wl.clipboardString + length, readSize); - if (result == 0) - break; - else if (result == -1) - { - if (errno == EINTR) - continue; - - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to read from clipboard pipe: %s", - strerror(errno)); - close(fds[0]); - return NULL; - } - - length += result; - } - - close(fds[0]); - - _glfw.wl.clipboardString[length] = '\0'; + _glfw_free(_glfw.wl.clipboardString); + _glfw.wl.clipboardString = + readDataOfferAsString(_glfw.wl.selectionOffer, "text/plain;charset=utf-8"); return _glfw.wl.clipboardString; } From 0f38382e25f484a8da2660f606c30b32a51b600e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 31 Mar 2022 22:17:08 +0200 Subject: [PATCH 66/88] Wayland: Clean up listener struct initialization Adapt style to match the rest of the project. --- src/wl_init.c | 6 ++++-- src/wl_monitor.c | 3 ++- src/wl_window.c | 30 ++++++++++++++++++++---------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index 4ea5e1e4..6545c41e 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -62,7 +62,8 @@ static void wmBaseHandlePing(void* userData, xdg_wm_base_pong(wmBase, serial); } -static const struct xdg_wm_base_listener wmBaseListener = { +static const struct xdg_wm_base_listener wmBaseListener = +{ wmBaseHandlePing }; @@ -172,7 +173,8 @@ static void registryHandleGlobalRemove(void* userData, } -static const struct wl_registry_listener registryListener = { +static const struct wl_registry_listener registryListener = +{ registryHandleGlobal, registryHandleGlobalRemove }; diff --git a/src/wl_monitor.c b/src/wl_monitor.c index 07adaa39..568bdc5f 100644 --- a/src/wl_monitor.c +++ b/src/wl_monitor.c @@ -108,7 +108,8 @@ static void outputHandleScale(void* userData, monitor->wl.scale = factor; } -static const struct wl_output_listener outputListener = { +static const struct wl_output_listener outputListener = +{ outputHandleGeometry, outputHandleMode, outputHandleDone, diff --git a/src/wl_window.c b/src/wl_window.c index 57c75cec..3fd4a87b 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -285,7 +285,8 @@ static void xdgDecorationHandleConfigure(void* userData, createDecorations(window); } -static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = { +static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = +{ xdgDecorationHandleConfigure, }; @@ -513,7 +514,8 @@ static void xdgToplevelHandleClose(void* userData, _glfwInputWindowCloseRequest(window); } -static const struct xdg_toplevel_listener xdgToplevelListener = { +static const struct xdg_toplevel_listener xdgToplevelListener = +{ xdgToplevelHandleConfigure, xdgToplevelHandleClose }; @@ -1175,7 +1177,8 @@ static void pointerHandleAxis(void* userData, _glfwInputScroll(window, x, y); } -static const struct wl_pointer_listener pointerListener = { +static const struct wl_pointer_listener pointerListener = +{ pointerHandleEnter, pointerHandleLeave, pointerHandleMotion, @@ -1470,7 +1473,8 @@ static void keyboardHandleRepeatInfo(void* userData, } #endif -static const struct wl_keyboard_listener keyboardListener = { +static const struct wl_keyboard_listener keyboardListener = +{ keyboardHandleKeymap, keyboardHandleEnter, keyboardHandleLeave, @@ -1514,7 +1518,8 @@ static void seatHandleName(void* userData, { } -static const struct wl_seat_listener seatListener = { +static const struct wl_seat_listener seatListener = +{ seatHandleCapabilities, seatHandleName, }; @@ -1535,7 +1540,8 @@ static void dataOfferHandleOffer(void* userData, } } -static const struct wl_data_offer_listener dataOfferListener = { +static const struct wl_data_offer_listener dataOfferListener = +{ dataOfferHandleOffer }; @@ -1625,7 +1631,8 @@ static void dataDeviceHandleSelection(void* userData, } } -const struct wl_data_device_listener dataDeviceListener = { +const struct wl_data_device_listener dataDeviceListener = +{ dataDeviceHandleDataOffer, dataDeviceHandleEnter, dataDeviceHandleLeave, @@ -2271,7 +2278,8 @@ static void relativePointerHandleRelativeMotion(void* userData, _glfwInputCursorPos(window, xpos, ypos); } -static const struct zwp_relative_pointer_v1_listener relativePointerListener = { +static const struct zwp_relative_pointer_v1_listener relativePointerListener = +{ relativePointerHandleRelativeMotion }; @@ -2301,7 +2309,8 @@ static void lockedPointerHandleUnlocked(void* userData, { } -static const struct zwp_locked_pointer_v1_listener lockedPointerListener = { +static const struct zwp_locked_pointer_v1_listener lockedPointerListener = +{ lockedPointerHandleLocked, lockedPointerHandleUnlocked }; @@ -2468,7 +2477,8 @@ static void dataSourceHandleCancelled(void* userData, _glfw.wl.selectionSource = NULL; } -static const struct wl_data_source_listener dataSourceListener = { +static const struct wl_data_source_listener dataSourceListener = +{ dataSourceHandleTarget, dataSourceHandleSend, dataSourceHandleCancelled, From 4cb36872a5fe448c205d0b46f0e8c8b57530cfe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 31 Mar 2022 19:56:48 +0200 Subject: [PATCH 67/88] Wayland: Add support for file drop events This adds support for file path drop events in text/uri-list format. It is based on work by Pilzschaf in #2040. Closes #2040 --- CONTRIBUTORS.md | 1 + README.md | 1 + src/wl_init.c | 2 ++ src/wl_platform.h | 5 +++++ src/wl_window.c | 57 +++++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 62 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index cc77ff13..c93c187e 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -162,6 +162,7 @@ video tutorials. - Orson Peters - Emmanuel Gil Peyrot - Cyril Pichard + - Pilzschaf - Keith Pitt - Stanislav Podgorskiy - Konstantin Podsvirov diff --git a/README.md b/README.md index 7b6873f1..334277ed 100644 --- a/README.md +++ b/README.md @@ -287,6 +287,7 @@ information on what to include when reporting a bug. - [X11] Bugfix: Left shift of int constant relied on undefined behavior (#1951) - [Wayland] Added dynamic loading of all Wayland libraries - [Wayland] Added support for key names via xkbcommon + - [Wayland] Added support for file path drop events (#2040) - [Wayland] Removed support for `wl_shell` (#1443) - [Wayland] Bugfix: The `GLFW_HAND_CURSOR` shape used the wrong image (#1432) - [Wayland] Bugfix: `CLOCK_MONOTONIC` was not correctly enabled diff --git a/src/wl_init.c b/src/wl_init.c index 6545c41e..3c0dac47 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -714,6 +714,8 @@ void _glfwTerminateWayland(void) xdg_wm_base_destroy(_glfw.wl.wmBase); if (_glfw.wl.selectionOffer) wl_data_offer_destroy(_glfw.wl.selectionOffer); + if (_glfw.wl.dragOffer) + wl_data_offer_destroy(_glfw.wl.dragOffer); if (_glfw.wl.selectionSource) wl_data_source_destroy(_glfw.wl.selectionSource); if (_glfw.wl.dataDevice) diff --git a/src/wl_platform.h b/src/wl_platform.h index 5ad9fa07..d6c8c4da 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -223,6 +223,7 @@ typedef struct _GLFWofferWayland { struct wl_data_offer* offer; GLFWbool text_plain_utf8; + GLFWbool text_uri_list; } _GLFWofferWayland; // Wayland-specific per-window data @@ -300,6 +301,10 @@ typedef struct _GLFWlibraryWayland struct wl_data_offer* selectionOffer; struct wl_data_source* selectionSource; + struct wl_data_offer* dragOffer; + _GLFWwindow* dragFocus; + uint32_t dragSerial; + int compositorVersion; int seatVersion; diff --git a/src/wl_window.c b/src/wl_window.c index 3fd4a87b..047b9eb6 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1534,6 +1534,8 @@ static void dataOfferHandleOffer(void* userData, { if (strcmp(mimeType, "text/plain;charset=utf-8") == 0) _glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE; + else if (strcmp(mimeType, "text/uri-list") == 0) + _glfw.wl.offers[i].text_uri_list = GLFW_TRUE; break; } @@ -1572,24 +1574,53 @@ static void dataDeviceHandleEnter(void* userData, wl_fixed_t y, struct wl_data_offer* offer) { + if (_glfw.wl.dragOffer) + { + wl_data_offer_destroy(_glfw.wl.dragOffer); + _glfw.wl.dragOffer = NULL; + _glfw.wl.dragFocus = NULL; + } + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) { if (_glfw.wl.offers[i].offer == offer) { + _GLFWwindow* window = NULL; + + if (surface) + window = wl_surface_get_user_data(surface); + + if (window && _glfw.wl.offers[i].text_uri_list) + { + _glfw.wl.dragOffer = offer; + _glfw.wl.dragFocus = window; + _glfw.wl.dragSerial = serial; + } + _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; _glfw.wl.offerCount--; - - // We don't yet handle drag and drop - wl_data_offer_accept(offer, serial, NULL); - wl_data_offer_destroy(offer); break; } } + + if (_glfw.wl.dragOffer) + wl_data_offer_accept(offer, serial, "text/uri-list"); + else + { + wl_data_offer_accept(offer, serial, NULL); + wl_data_offer_destroy(offer); + } } static void dataDeviceHandleLeave(void* userData, struct wl_data_device* device) { + if (_glfw.wl.dragOffer) + { + wl_data_offer_destroy(_glfw.wl.dragOffer); + _glfw.wl.dragOffer = NULL; + _glfw.wl.dragFocus = NULL; + } } static void dataDeviceHandleMotion(void* userData, @@ -1603,6 +1634,24 @@ static void dataDeviceHandleMotion(void* userData, static void dataDeviceHandleDrop(void* userData, struct wl_data_device* device) { + if (!_glfw.wl.dragOffer) + return; + + char* string = readDataOfferAsString(_glfw.wl.dragOffer, "text/uri-list"); + if (string) + { + int count; + char** paths = _glfwParseUriList(string, &count); + if (paths) + _glfwInputDrop(_glfw.wl.dragFocus, count, (const char**) paths); + + for (int i = 0; i < count; i++) + _glfw_free(paths[i]); + + _glfw_free(paths); + } + + _glfw_free(string); } static void dataDeviceHandleSelection(void* userData, From f843d533335db275e0b014311cd7163a2a003b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 8 Apr 2022 18:22:39 +0200 Subject: [PATCH 68/88] Fix glfwMakeContextCurrent using uninitialized TLS Issue reported by danhambleton on the GLFW forum: https://discourse.glfw.org/t/posix-thread-local-storage-tls-error-when-making-context-current/2034 --- CONTRIBUTORS.md | 1 + README.md | 1 + src/context.c | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index c93c187e..030abf7c 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -44,6 +44,7 @@ video tutorials. - Noel Cower - CuriouserThing - Jason Daly + - danhambleton - Jarrod Davis - Olivier Delannoy - Paul R. Deppe diff --git a/README.md b/README.md index 334277ed..b49a8a0d 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,7 @@ information on what to include when reporting a bug. - Bugfix: Gamepad mapping updates could spam `GLFW_INVALID_VALUE` due to incompatible controllers sharing hardware ID (#1763) - Bugfix: Native access functions for context handles did not check that the API matched + - Bugfix: `glfwMakeContextCurrent` would access TLS slot before initialization - [Win32] Added the `GLFW_WIN32_KEYBOARD_MENU` window hint for enabling access to the window menu - [Win32] Added a version info resource to the GLFW DLL diff --git a/src/context.c b/src/context.c index f6629f55..7e0449b4 100644 --- a/src/context.c +++ b/src/context.c @@ -609,10 +609,12 @@ GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFWwindow* previous = _glfwPlatformGetTls(&_glfw.contextSlot); + _GLFWwindow* previous; _GLFW_REQUIRE_INIT(); + previous = _glfwPlatformGetTls(&_glfw.contextSlot); + if (window && window->context.client == GLFW_NO_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, From 29523dbfbfe097bec9ae3a5ec08b08915e1e4a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 21 Apr 2022 15:37:31 +0200 Subject: [PATCH 69/88] Fix order of error checks in glfwSetGammaRamp Initialization should be checked before arguments. --- README.md | 1 + src/monitor.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b49a8a0d..89e28723 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,7 @@ information on what to include when reporting a bug. incompatible controllers sharing hardware ID (#1763) - Bugfix: Native access functions for context handles did not check that the API matched - Bugfix: `glfwMakeContextCurrent` would access TLS slot before initialization + - Bugfix: `glfwSetGammaRamp` could emit `GLFW_INVALID_VALUE` before initialization - [Win32] Added the `GLFW_WIN32_KEYBOARD_MENU` window hint for enabling access to the window menu - [Win32] Added a version info resource to the GLFW DLL diff --git a/src/monitor.c b/src/monitor.c index e6f47d65..082140ad 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -522,6 +522,8 @@ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) assert(ramp->green != NULL); assert(ramp->blue != NULL); + _GLFW_REQUIRE_INIT(); + if (ramp->size <= 0) { _glfwInputError(GLFW_INVALID_VALUE, @@ -530,8 +532,6 @@ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) return; } - _GLFW_REQUIRE_INIT(); - if (!monitor->originalRamp.size) { if (!_glfw.platform.getGammaRamp(monitor, &monitor->originalRamp)) From 10e9033a29ba088275da4363cd2d821ca577416a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 28 Apr 2022 21:12:36 +0200 Subject: [PATCH 70/88] Win32: Fix OSMesa library not being unloaded --- README.md | 1 + src/win32_init.c | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 89e28723..a8b38fbd 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,7 @@ information on what to include when reporting a bug. - [Win32] Bugfix: `GLFW_KEY_PAUSE` scancode from `glfwGetKeyScancode` did not match event scancode (#1993) - [Win32] Bugfix: Instance-local operations used executable instance (#469,#1296,#1395) + - [Win32] Bugfix: The OSMesa library was not unloaded on termination - [Cocoa] Added support for `VK_EXT_metal_surface` (#1619) - [Cocoa] Added locating the Vulkan loader at runtime in an application bundle - [Cocoa] Moved main menu creation to GLFW initialization time (#1649) diff --git a/src/win32_init.c b/src/win32_init.c index 7aefc78a..9465c951 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -670,6 +670,7 @@ void _glfwTerminateWin32(void) _glfwTerminateWGL(); _glfwTerminateEGL(); + _glfwTerminateOSMesa(); freeLibraries(); } From 89514cf0fc33aed4db1310a57cc7436999492d14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 28 Apr 2022 21:14:11 +0200 Subject: [PATCH 71/88] Cocoa: Fix EGL and OSMesa not being unloaded --- README.md | 1 + src/cocoa_init.m | 2 ++ 2 files changed, 3 insertions(+) diff --git a/README.md b/README.md index a8b38fbd..934eb331 100644 --- a/README.md +++ b/README.md @@ -249,6 +249,7 @@ information on what to include when reporting a bug. - [Cocoa] Bugfix: `kIOMasterPortDefault` was deprecated in macOS 12.0 (#1980) - [Cocoa] Bugfix: `kUTTypeURL` was deprecated in macOS 12.0 (#2003) - [Cocoa] Bugfix: A connected Apple AirPlay would emit a useless error (#1791) + - [Cocoa] Bugfix: The EGL and OSMesa libraries were not unloaded on termination - [X11] Bugfix: The CMake files did not check for the XInput headers (#1480) - [X11] Bugfix: Key names were not updated when the keyboard layout changed (#1462,#1528) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 48bc2c29..6bc6496a 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -676,6 +676,8 @@ void _glfwTerminateCocoa(void) _glfw_free(_glfw.ns.clipboardString); _glfwTerminateNSGL(); + _glfwTerminateEGL(); + _glfwTerminateOSMesa(); } // autoreleasepool } From 2d281d7928552c15ea2bc6dd185f7d7a780b1917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 28 Apr 2022 21:16:06 +0200 Subject: [PATCH 72/88] Wayland: Fix OSMesa library not being unloaded --- README.md | 1 + src/wl_init.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/README.md b/README.md index 934eb331..3bf56e07 100644 --- a/README.md +++ b/README.md @@ -322,6 +322,7 @@ information on what to include when reporting a bug. - [Wayland] Bugfix: Some errors would cause clipboard string transfer to hang - [Wayland] Bugfix: Drag and drop data was misinterpreted as clipboard string - [Wayland] Bugfix: MIME type matching was not performed for clipboard string + - [Wayland] Bugfix: The OSMesa library was not unloaded on termination - [POSIX] Removed use of deprecated function `gettimeofday` - [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled - [WGL] Disabled the DWM swap interval hack for Windows 8 and later (#1072) diff --git a/src/wl_init.c b/src/wl_init.c index 3c0dac47..c4e47e99 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -663,6 +663,8 @@ int _glfwInitWayland(void) void _glfwTerminateWayland(void) { _glfwTerminateEGL(); + _glfwTerminateOSMesa(); + if (_glfw.wl.egl.handle) { _glfwPlatformFreeModule(_glfw.wl.egl.handle); From a8d8b760fb6bffb9c7f65297a0bc3d6bf6b54262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 28 Apr 2022 21:17:39 +0200 Subject: [PATCH 73/88] X11: Fix OSMesa library not being unloaded --- README.md | 1 + src/x11_init.c | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 3bf56e07..3b57f9b5 100644 --- a/README.md +++ b/README.md @@ -289,6 +289,7 @@ information on what to include when reporting a bug. (#379,#1281,#1285,#2033) - [X11] Bugfix: Dynamic loading on NetBSD failed due to soname differences - [X11] Bugfix: Left shift of int constant relied on undefined behavior (#1951) + - [X11] Bugfix: The OSMesa libray was not unloaded on termination - [Wayland] Added dynamic loading of all Wayland libraries - [Wayland] Added support for key names via xkbcommon - [Wayland] Added support for file path drop events (#2040) diff --git a/src/x11_init.c b/src/x11_init.c index 78735d69..aefa82db 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -1630,6 +1630,7 @@ void _glfwTerminateX11(void) _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(); From 82ccbb3ef02201498cbf5952083de47989e05978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 18 Apr 2022 23:09:00 +0200 Subject: [PATCH 74/88] Update stb_image_write.h to version 1.16 --- deps/stb_image_write.h | 930 +++++++++++++++++++++++++++++++++++------ 1 file changed, 803 insertions(+), 127 deletions(-) diff --git a/deps/stb_image_write.h b/deps/stb_image_write.h index 4319c0de..e4b32ed1 100644 --- a/deps/stb_image_write.h +++ b/deps/stb_image_write.h @@ -1,5 +1,5 @@ -/* stb_image_write - v1.02 - public domain - http://nothings.org/stb/stb_image_write.h - writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 +/* stb_image_write - v1.16 - public domain - http://nothings.org/stb + writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk Before #including, @@ -12,41 +12,64 @@ ABOUT: - This header file is a library for writing images to C stdio. It could be - adapted to write to memory or a general streaming interface; let me know. + This header file is a library for writing images to C stdio or a callback. The PNG output is not optimal; it is 20-50% larger than the file - written by a decent optimizing implementation. This library is designed - for source code compactness and simplicity, not optimal image file size - or run-time performance. + written by a decent optimizing implementation; though providing a custom + zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. + This library is designed for source code compactness and simplicity, + not optimal image file size or run-time performance. BUILDING: You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace malloc,realloc,free. - You can define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function + for PNG compression (instead of the builtin one), it must have the following signature: + unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); + The returned data will be freed with STBIW_FREE() (free() by default), + so it must be heap allocated with STBIW_MALLOC() (malloc() by default), + +UNICODE: + + If compiling for Windows and you wish to use Unicode filenames, compile + with + #define STBIW_WINDOWS_UTF8 + and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert + Windows wchar_t filenames to utf8. USAGE: - There are four functions, one for each image file format: + There are five functions, one for each image file format: int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); - There are also four equivalent functions that use an arbitrary write function. You are + void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically + + There are also five equivalent functions that use an arbitrary write function. You are expected to open/close your file-equivalent before and after calling these: int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); where the callback is: void stbi_write_func(void *context, void *data, int size); + You can configure it with these global variables: + int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE + int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression + int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these functions, so the library will not use stdio.h at all. However, this will also disable HDR writing, because it requires stdio for formatted output. @@ -73,6 +96,9 @@ USAGE: writer, both because it is in BGR order and because it may have padding at the end of the line.) + PNG allows you to set the deflate compression level by setting the global + variable 'stbi_write_png_compression_level' (it defaults to 8). + HDR expects linear float data. Since the format is always 32-bit rgb(e) data, alpha (if provided) is discarded, and for monochrome data it is replicated across all three channels. @@ -80,20 +106,23 @@ USAGE: TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed data, set the global variable 'stbi_write_tga_with_rle' to 0. + JPEG does ignore alpha channels in input data; quality is between 1 and 100. + Higher quality looks better but results in a bigger image. + JPEG baseline (no JPEG progressive). + CREDITS: - PNG/BMP/TGA - Sean Barrett - HDR - Baldur Karlsson - TGA monochrome: - Jean-Sebastien Guay - misc enhancements: - Tim Kelsey - TGA RLE - Alan Hickman - initial file IO callback implementation - Emmanuel Julien + + Sean Barrett - PNG/BMP/TGA + Baldur Karlsson - HDR + Jean-Sebastien Guay - TGA monochrome + Tim Kelsey - misc enhancements + Alan Hickman - TGA RLE + Emmanuel Julien - initial file IO callback implementation + Jon Olick - original jo_jpeg.cpp code + Daniel Gibson - integrate JPEG, allow external zlib + Aarni Koskela - allow choosing PNG filter + bugfixes: github:Chribba Guillaume Chereau @@ -103,27 +132,44 @@ CREDITS: Jonas Karlsson Filip Wasil Thatcher Ulrich - + github:poppolopoppo + Patrick Boettcher + github:xeekworx + Cap Petschulat + Simon Rodriguez + Ivan Tikhonov + github:ignotion + Adam Schackart + Andrew Kensler + LICENSE -This software is dual-licensed to the public domain and under the following -license: you are granted a perpetual, irrevocable license to copy, modify, -publish, and distribute this file as you see fit. + See end of file for license information. */ #ifndef INCLUDE_STB_IMAGE_WRITE_H #define INCLUDE_STB_IMAGE_WRITE_H +#include + +// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' +#ifndef STBIWDEF +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else #ifdef __cplusplus -extern "C" { +#define STBIWDEF extern "C" +#else +#define STBIWDEF extern +#endif +#endif #endif -#ifdef STB_IMAGE_WRITE_STATIC -#define STBIWDEF static -#else -#define STBIWDEF extern -extern int stbi_write_tga_with_rle; +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations +STBIWDEF int stbi_write_tga_with_rle; +STBIWDEF int stbi_write_png_compression_level; +STBIWDEF int stbi_write_force_png_filter; #endif #ifndef STBI_WRITE_NO_STDIO @@ -131,6 +177,11 @@ STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); + +#ifdef STBIW_WINDOWS_UTF8 +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif #endif typedef void stbi_write_func(void *context, void *data, int size); @@ -139,10 +190,9 @@ STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); -#ifdef __cplusplus -} -#endif +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); #endif//INCLUDE_STB_IMAGE_WRITE_H @@ -197,10 +247,29 @@ STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_png_compression_level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +static int stbi__flip_vertically_on_write = 0; + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + typedef struct { stbi_write_func *func; void *context; + unsigned char buffer[64]; + int buf_used; } stbi__write_context; // initialize a callback-based context @@ -217,9 +286,52 @@ static void stbi__stdio_write(void *context, void *data, int size) fwrite(data,1,size,(FILE*) context); } +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) +#ifdef __cplusplus +#define STBIW_EXTERN extern "C" +#else +#define STBIW_EXTERN extern +#endif +STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); + +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbiw__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + static int stbi__start_write_file(stbi__write_context *s, const char *filename) { - FILE *f = fopen(filename, "wb"); + FILE *f = stbiw__fopen(filename, "wb"); stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); return f != NULL; } @@ -234,12 +346,6 @@ static void stbi__end_write_file(stbi__write_context *s) typedef unsigned int stbiw_uint32; typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; -#ifdef STB_IMAGE_WRITE_STATIC -static int stbi_write_tga_with_rle = 1; -#else -int stbi_write_tga_with_rle = 1; -#endif - static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { while (*fmt) { @@ -277,11 +383,36 @@ static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) va_end(v); } +static void stbiw__write_flush(stbi__write_context *s) +{ + if (s->buf_used) { + s->func(s->context, &s->buffer, s->buf_used); + s->buf_used = 0; + } +} + +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + +static void stbiw__write1(stbi__write_context *s, unsigned char a) +{ + if ((size_t)s->buf_used + 1 > sizeof(s->buffer)) + stbiw__write_flush(s); + s->buffer[s->buf_used++] = a; +} + static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { - unsigned char arr[3]; - arr[0] = a, arr[1] = b, arr[2] = c; - s->func(s->context, arr, 3); + int n; + if ((size_t)s->buf_used + 3 > sizeof(s->buffer)) + stbiw__write_flush(s); + n = s->buf_used; + s->buf_used = n+3; + s->buffer[n+0] = a; + s->buffer[n+1] = b; + s->buffer[n+2] = c; } static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) @@ -290,17 +421,15 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, in int k; if (write_alpha < 0) - s->func(s->context, &d[comp - 1], 1); + stbiw__write1(s, d[comp - 1]); switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case case 1: - s->func(s->context,d,1); - break; - case 2: if (expand_mono) stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp else - s->func(s->context, d, 1); // monochrome TGA + stbiw__write1(s, d[0]); // monochrome TGA break; case 4: if (!write_alpha) { @@ -316,7 +445,7 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, in break; } if (write_alpha > 0) - s->func(s->context, &d[comp - 1], 1); + stbiw__write1(s, d[comp - 1]); } static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) @@ -327,16 +456,21 @@ static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, i if (y <= 0) return; - if (vdir < 0) - j_end = -1, j = y-1; - else - j_end = y, j = 0; + if (stbi__flip_vertically_on_write) + vdir *= -1; + + if (vdir < 0) { + j_end = -1; j = y-1; + } else { + j_end = y; j = 0; + } for (; j != j_end; j += vdir) { for (i=0; i < x; ++i) { unsigned char *d = (unsigned char *) data + (j*x+i)*comp; stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); } + stbiw__write_flush(s); s->func(s->context, &zero, scanline_pad); } } @@ -357,16 +491,27 @@ static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { - int pad = (-x*3) & 3; - return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, - "11 4 22 4" "4 44 22 444444", - 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header - 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header + if (comp != 4) { + // write RGB bitmap + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header + } else { + // RGBA bitmaps need a v4 header + // use BI_BITFIELDS mode with 32bpp and alpha mask + // (straight BI_RGB with alpha mask doesn't work in most readers) + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0, + "11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444", + 'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header + 108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header + } } STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { - stbi__write_context s; + stbi__write_context s = { 0 }; stbi__start_write_callbacks(&s, func, context); return stbi_write_bmp_core(&s, x, y, comp, data); } @@ -374,7 +519,7 @@ STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) { - stbi__write_context s; + stbi__write_context s = { 0 }; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_bmp_core(&s, x, y, comp, data); stbi__end_write_file(&s); @@ -398,11 +543,21 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, v "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); } else { int i,j,k; + int jend, jdir; stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); - for (j = y - 1; j >= 0; --j) { - unsigned char *row = (unsigned char *) data + j * x * comp; + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; int len; for (i = 0; i < x; i += len) { @@ -437,32 +592,33 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, v if (diff) { unsigned char header = STBIW_UCHAR(len - 1); - s->func(s->context, &header, 1); + stbiw__write1(s, header); for (k = 0; k < len; ++k) { stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); } } else { unsigned char header = STBIW_UCHAR(len - 129); - s->func(s->context, &header, 1); + stbiw__write1(s, header); stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); } } } + stbiw__write_flush(s); } return 1; } -int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { - stbi__write_context s; + stbi__write_context s = { 0 }; stbi__start_write_callbacks(&s, func, context); return stbi_write_tga_core(&s, x, y, comp, (void *) data); } #ifndef STBI_WRITE_NO_STDIO -int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) { - stbi__write_context s; + stbi__write_context s = { 0 }; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); stbi__end_write_file(&s); @@ -475,11 +631,12 @@ int stbi_write_tga(char const *filename, int x, int y, int comp, const void *dat // ************************************************************************************************* // Radiance RGBE HDR writer // by Baldur Karlsson -#ifndef STBI_WRITE_NO_STDIO #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) -void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +#ifndef STBI_WRITE_NO_STDIO + +static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) { int exponent; float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); @@ -496,7 +653,7 @@ void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) } } -void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) { unsigned char lengthbyte = STBIW_UCHAR(length+128); STBIW_ASSERT(length+128 <= 255); @@ -504,7 +661,7 @@ void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char dat s->func(s->context, &databyte, 1); } -void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) { unsigned char lengthbyte = STBIW_UCHAR(length); STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code @@ -512,7 +669,7 @@ void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *d s->func(s->context, data, length); } -void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) { unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; unsigned char rgbe[4]; @@ -613,26 +770,30 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; s->func(s->context, header, sizeof(header)-1); +#ifdef __STDC_LIB_EXT1__ + len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif s->func(s->context, buffer, len); for(i=0; i < y; i++) - stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); STBIW_FREE(scratch); return 1; } } -int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) { - stbi__write_context s; + stbi__write_context s = { 0 }; stbi__start_write_callbacks(&s, func, context); return stbi_write_hdr_core(&s, x, y, comp, (float *) data); } -int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { - stbi__write_context s; + stbi__write_context s = { 0 }; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); stbi__end_write_file(&s); @@ -648,8 +809,9 @@ int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *da // PNG writer // +#ifndef STBIW_ZLIB_COMPRESS // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() -#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbraw(a) ((int *) (void *) (a) - 2) #define stbiw__sbm(a) stbiw__sbraw(a)[0] #define stbiw__sbn(a) stbiw__sbraw(a)[1] @@ -728,8 +890,14 @@ static unsigned int stbiw__zhash(unsigned char *data) #define stbiw__ZHASH 16384 -unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +#endif // STBIW_ZLIB_COMPRESS + +STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) { +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; @@ -737,7 +905,9 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l unsigned int bitbuf=0; int i,j, bitcount=0; unsigned char *out = NULL; - unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); + if (hash_table == NULL) + return NULL; if (quality < 5) quality = 5; stbiw__sbpush(out, 0x78); // DEFLATE 32K window @@ -758,7 +928,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l for (j=0; j < n; ++j) { if (hlist[j]-data > i-32768) { // if entry lies within window int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); - if (d >= best) best=d,bestloc=hlist[j]; + if (d >= best) { best=d; bestloc=hlist[j]; } } } // when hash table entry is too long, delete half the entries @@ -811,14 +981,31 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l (void) stbiw__sbfree(hash_table[i]); STBIW_FREE(hash_table); + // store uncompressed instead if compression was worse + if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) { + stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1 + for (j = 0; j < data_len;) { + int blocklen = data_len - j; + if (blocklen > 32767) blocklen = 32767; + stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression + stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN + stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN + stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8)); + memcpy(out+stbiw__sbn(out), data+j, blocklen); + stbiw__sbn(out) += blocklen; + j += blocklen; + } + } + { // compute adler32 on input unsigned int s1=1, s2=0; int blocklen = (int) (data_len % 5552); j=0; while (j < data_len) { - for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; - s1 %= 65521, s2 %= 65521; + for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } + s1 %= 65521; s2 %= 65521; j += blocklen; blocklen = 5552; } @@ -831,10 +1018,14 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l // make returned pointer freeable STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS } static unsigned int stbiw__crc32(unsigned char *buffer, int len) { +#ifdef STBIW_CRC32 + return STBIW_CRC32(buffer, len); +#else static unsigned int crc_table[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, @@ -876,6 +1067,7 @@ static unsigned int stbiw__crc32(unsigned char *buffer, int len) for (i=0; i < len; ++i) crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; return ~crc; +#endif } #define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) @@ -896,61 +1088,92 @@ static unsigned char stbiw__paeth(int a, int b, int c) return STBIW_UCHAR(c); } -unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) { + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + + if (type==0) { + memcpy(line_buffer, z, width*n); + return; + } + + // first loop isn't optimized since it's just one pixel + for (i = 0; i < n; ++i) { + switch (type) { + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + switch (type) { + case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; + case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } +} + +STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int force_filter = stbi_write_force_png_filter; int ctype[5] = { -1, 0, 4, 2, 6 }; unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; unsigned char *out,*o, *filt, *zlib; signed char *line_buffer; - int i,j,k,p,zlen; + int j,zlen; if (stride_bytes == 0) stride_bytes = x * n; + if (force_filter >= 5) { + force_filter = -1; + } + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } for (j=0; j < y; ++j) { - static int mapping[] = { 0,1,2,3,4 }; - static int firstmap[] = { 0,1,0,5,6 }; - int *mymap = j ? mapping : firstmap; - int best = 0, bestval = 0x7fffffff; - for (p=0; p < 2; ++p) { - for (k= p?best:0; k < 5; ++k) { - int type = mymap[k],est=0; - unsigned char *z = pixels + stride_bytes*j; - for (i=0; i < n; ++i) - switch (type) { - case 0: line_buffer[i] = z[i]; break; - case 1: line_buffer[i] = z[i]; break; - case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; - case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; - case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; - case 5: line_buffer[i] = z[i]; break; - case 6: line_buffer[i] = z[i]; break; - } - for (i=n; i < x*n; ++i) { - switch (type) { - case 0: line_buffer[i] = z[i]; break; - case 1: line_buffer[i] = z[i] - z[i-n]; break; - case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; - case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; - case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; - case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; - case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; - } - } - if (p) break; - for (i=0; i < x*n; ++i) + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { est += abs((signed char) line_buffer[i]); - if (est < bestval) { bestval = est; best = k; } + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; } } - // when we get here, best contains the filter type, and line_buffer contains the data - filt[j*(x*n+1)] = (unsigned char) best; + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); } STBIW_FREE(line_buffer); - zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); STBIW_FREE(filt); if (!zlib) return 0; @@ -993,9 +1216,10 @@ STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const { FILE *f; int len; - unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; - f = fopen(filename, "wb"); + + f = stbiw__fopen(filename, "wb"); if (!f) { STBIW_FREE(png); return 0; } fwrite(png, 1, len, f); fclose(f); @@ -1007,16 +1231,426 @@ STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) { int len; - unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; func(context, png, len); STBIW_FREE(png); return 1; } + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k, subsample; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + subsample = quality <= 90 ? 1 : 0; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + const unsigned char *dataR = (const unsigned char *)data; + const unsigned char *dataG = dataR + ofsG; + const unsigned char *dataB = dataR + ofsB; + int x, y, pos; + if(subsample) { + for(y = 0; y < height; y += 16) { + for(x = 0; x < width; x += 16) { + float Y[256], U[256], V[256]; + for(row = y, pos = 0; row < y+16; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+16; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + + // subsample U,V + { + float subU[64], subV[64]; + int yy, xx; + for(yy = 0, pos = 0; yy < 8; ++yy) { + for(xx = 0; xx < 8; ++xx, ++pos) { + int j = yy*32+xx*2; + subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f; + subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f; + } + } + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + } + } else { + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float Y[64], U[64], V[64]; + for(row = y, pos = 0; row < y+8; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+8; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history + 1.16 (2021-07-11) + make Deflate code emit uncompressed blocks when it would otherwise expand + support writing BMPs with alpha channel + 1.15 (2020-07-13) unknown + 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels + 1.13 + 1.12 + 1.11 (2019-08-11) + + 1.10 (2019-02-07) + support utf8 filenames in Windows; fix warnings and platform ifdefs + 1.09 (2018-02-11) + fix typo in zlib quality API, improve STB_I_W_STATIC in C++ + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? 1.02 (2016-04-02) avoid allocating large structures on the stack 1.01 (2016-01-16) @@ -1035,7 +1669,7 @@ STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, add HDR output fix monochrome BMP 0.95 (2014-08-17) - add monochrome TGA output + add monochrome TGA output 0.94 (2014-05-31) rename private functions to avoid conflicts with stb_image.h 0.93 (2014-05-27) @@ -1046,3 +1680,45 @@ STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, first public release 0.90 first internal release */ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ From 06089a91a644b1f1c44465c95ff6cc002792556b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 18 Apr 2022 23:10:04 +0200 Subject: [PATCH 75/88] Document delayed initialization of EGL display --- include/GLFW/glfw3native.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/GLFW/glfw3native.h b/include/GLFW/glfw3native.h index 11fee09d..7f5bf9e4 100644 --- a/include/GLFW/glfw3native.h +++ b/include/GLFW/glfw3native.h @@ -486,6 +486,9 @@ GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * + * @remark Because EGL is initialized on demand, this function will return + * `EGL_NO_DISPLAY` until the first context has been created via EGL. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * From f60547ac80602955fe0af1ab05fd3f6ed5941e6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 18 Apr 2022 23:21:40 +0200 Subject: [PATCH 76/88] X11: Fix returned clipboard string freed too early The clipboard string should not be freed on SelectionClear. The user may have received it from glfwGetClipboardString and it should remain valid until the next call to a public clipboard string function. --- src/x11_window.c | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/src/x11_window.c b/src/x11_window.c index e021dfd7..b77162b5 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -865,20 +865,6 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request) return None; } -static void handleSelectionClear(XEvent* event) -{ - if (event->xselectionclear.selection == _glfw.x11.PRIMARY) - { - _glfw_free(_glfw.x11.primarySelectionString); - _glfw.x11.primarySelectionString = NULL; - } - else - { - _glfw_free(_glfw.x11.clipboardString); - _glfw.x11.clipboardString = NULL; - } -} - static void handleSelectionRequest(XEvent* event) { const XSelectionRequestEvent* request = &event->xselectionrequest; @@ -1171,12 +1157,7 @@ static void processEvent(XEvent *event) return; } - if (event->type == SelectionClear) - { - handleSelectionClear(event); - return; - } - else if (event->type == SelectionRequest) + if (event->type == SelectionRequest) { handleSelectionRequest(event); return; @@ -1853,10 +1834,6 @@ void _glfwPushSelectionToManagerX11(void) handleSelectionRequest(&event); break; - case SelectionClear: - handleSelectionClear(&event); - break; - case SelectionNotify: { if (event.xselection.target == _glfw.x11.SAVE_TARGETS) From 23e6e8e4b7ffe8f7604bd4f6ad1b183d0927bfe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 18 Apr 2022 23:22:25 +0200 Subject: [PATCH 77/88] X11: Fix segfault on malformed INCR response The code assumed that at least some data would be received via the INCR mechanism and that, as a result, the string buffer would be allocated. Bug found by Clang static analysis. --- README.md | 1 + src/x11_window.c | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3b57f9b5..67d57a2f 100644 --- a/README.md +++ b/README.md @@ -290,6 +290,7 @@ information on what to include when reporting a bug. - [X11] Bugfix: Dynamic loading on NetBSD failed due to soname differences - [X11] Bugfix: Left shift of int constant relied on undefined behavior (#1951) - [X11] Bugfix: The OSMesa libray was not unloaded on termination + - [X11] Bugfix: A malformed response during selection transfer could cause a segfault - [Wayland] Added dynamic loading of all Wayland libraries - [Wayland] Added support for key names via xkbcommon - [Wayland] Added support for file path drop events (#2040) diff --git a/src/x11_window.c b/src/x11_window.c index b77162b5..85b844a1 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -985,13 +985,16 @@ static const char* getSelectionString(Atom selection) if (!itemCount) { - if (targets[i] == XA_STRING) + if (string) { - *selectionString = convertLatin1toUTF8(string); - _glfw_free(string); + if (targets[i] == XA_STRING) + { + *selectionString = convertLatin1toUTF8(string); + _glfw_free(string); + } + else + *selectionString = string; } - else - *selectionString = string; break; } From d3c2121221f7bcd9cce2e164acfbdee8820aec06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 18 Apr 2022 23:35:08 +0200 Subject: [PATCH 78/88] Move CODEOWNERS to .github directory This slightly de-clutters a directory mostly intended for human-readable documentation. --- {docs => .github}/CODEOWNERS | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {docs => .github}/CODEOWNERS (100%) diff --git a/docs/CODEOWNERS b/.github/CODEOWNERS similarity index 100% rename from docs/CODEOWNERS rename to .github/CODEOWNERS From 80e31ce9e1e75f51f9cf0af61c9163474675439a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 6 Apr 2022 19:02:52 +0200 Subject: [PATCH 79/88] Win32: Remove superfluous cast --- src/win32_window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/win32_window.c b/src/win32_window.c index 5f4dd7d2..14f2fe5b 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1428,7 +1428,7 @@ GLFWbool _glfwRegisterWindowClassWin32(void) ZeroMemory(&wc, sizeof(wc)); wc.cbSize = sizeof(wc); wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; - wc.lpfnWndProc = (WNDPROC) windowProc; + wc.lpfnWndProc = windowProc; wc.hInstance = _glfw.win32.instance; wc.hCursor = LoadCursorW(NULL, IDC_ARROW); wc.lpszClassName = _GLFW_WNDCLASSNAME; From acf0c10e7aa946b8f1555c78d812528e22e39dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 6 Apr 2022 23:21:28 +0200 Subject: [PATCH 80/88] Win32: Fix message reported by VS 2022 --- src/win32_joystick.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/win32_joystick.c b/src/win32_joystick.c index a6fe8e8d..7eb9b203 100644 --- a/src/win32_joystick.c +++ b/src/win32_joystick.c @@ -607,7 +607,7 @@ int _glfwPollJoystickWin32(_GLFWjoystick* js, int mode) { int i, ai = 0, bi = 0, pi = 0; HRESULT result; - DIJOYSTATE state; + DIJOYSTATE state = {0}; IDirectInputDevice8_Poll(js->win32.device); result = IDirectInputDevice8_GetDeviceState(js->win32.device, From 8f050b56559910641895b5cc4f5d374879dd249d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 8 Apr 2022 17:09:04 +0200 Subject: [PATCH 81/88] Wayland: Add support for older wayland-scanner Versions of wayland-scanner prior to 1.17.91 named every global array of wl_interface pointers 'types', making it impossible to combine several unmodified private-code files into a single compilation unit. This overrides that name with a macro for each file, allowing them to coexist. Fixes #2016 Closes #2032 --- src/wl_init.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/wl_init.c b/src/wl_init.c index c4e47e99..c232ce79 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -47,13 +47,38 @@ #include "wayland-pointer-constraints-unstable-v1-client-protocol.h" #include "wayland-idle-inhibit-unstable-v1-client-protocol.h" +// NOTE: Versions of wayland-scanner prior to 1.17.91 named every global array of +// wl_interface pointers 'types', making it impossible to combine several unmodified +// private-code files into a single compilation unit +// HACK: We override this name with a macro for each file, allowing them to coexist + +#define types _glfw_wayland_types #include "wayland-client-protocol-code.h" +#undef types + +#define types _glfw_xdg_shell_types #include "wayland-xdg-shell-client-protocol-code.h" +#undef types + +#define types _glfw_xdg_decoration_types #include "wayland-xdg-decoration-client-protocol-code.h" +#undef types + +#define types _glfw_viewporter_types #include "wayland-viewporter-client-protocol-code.h" +#undef types + +#define types _glfw_relative_pointer_types #include "wayland-relative-pointer-unstable-v1-client-protocol-code.h" +#undef types + +#define types _glfw_pointer_constraints_types #include "wayland-pointer-constraints-unstable-v1-client-protocol-code.h" +#undef types + +#define types _glfw_idle_inhibit_types #include "wayland-idle-inhibit-unstable-v1-client-protocol-code.h" +#undef types static void wmBaseHandlePing(void* userData, struct xdg_wm_base* wmBase, From 72642ea0d1f3ad75f503bd90b9ba740a274e0422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 9 Nov 2021 19:41:34 +0100 Subject: [PATCH 82/88] Simplify struct stitching for sync primitives There will not currently be more than one set of threading or timer APIs selected regardless of how many window systems are enabled, so there is no need for this extra complexity. --- src/platform.h | 58 ++++++++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 37 deletions(-) diff --git a/src/platform.h b/src/platform.h index cc842eb8..0c593676 100644 --- a/src/platform.h +++ b/src/platform.h @@ -92,30 +92,6 @@ #define GLFW_LINUX_LIBRARY_JOYSTICK_STATE #endif -#if defined(_WIN32) - #include "win32_thread.h" - #define GLFW_POSIX_TLS_STATE - #define GLFW_POSIX_MUTEX_STATE -#else - #include "posix_thread.h" - #define GLFW_WIN32_TLS_STATE - #define GLFW_WIN32_MUTEX_STATE -#endif - -#if defined(_WIN32) - #include "win32_time.h" - #define GLFW_POSIX_LIBRARY_TIMER_STATE - #define GLFW_COCOA_LIBRARY_TIMER_STATE -#elif defined(__APPLE__) - #include "cocoa_time.h" - #define GLFW_WIN32_LIBRARY_TIMER_STATE - #define GLFW_POSIX_LIBRARY_TIMER_STATE -#else - #include "posix_time.h" - #define GLFW_WIN32_LIBRARY_TIMER_STATE - #define GLFW_COCOA_LIBRARY_TIMER_STATE -#endif - #define GLFW_PLATFORM_WINDOW_STATE \ GLFW_WIN32_WINDOW_STATE \ GLFW_COCOA_WINDOW_STATE \ @@ -142,14 +118,6 @@ GLFW_COCOA_JOYSTICK_STATE \ GLFW_LINUX_JOYSTICK_STATE -#define GLFW_PLATFORM_TLS_STATE \ - GLFW_WIN32_TLS_STATE \ - GLFW_POSIX_TLS_STATE \ - -#define GLFW_PLATFORM_MUTEX_STATE \ - GLFW_WIN32_MUTEX_STATE \ - GLFW_POSIX_MUTEX_STATE \ - #define GLFW_PLATFORM_LIBRARY_WINDOW_STATE \ GLFW_WIN32_LIBRARY_WINDOW_STATE \ GLFW_COCOA_LIBRARY_WINDOW_STATE \ @@ -162,11 +130,6 @@ GLFW_COCOA_LIBRARY_JOYSTICK_STATE \ GLFW_LINUX_LIBRARY_JOYSTICK_STATE -#define GLFW_PLATFORM_LIBRARY_TIMER_STATE \ - GLFW_WIN32_LIBRARY_TIMER_STATE \ - GLFW_COCOA_LIBRARY_TIMER_STATE \ - GLFW_POSIX_LIBRARY_TIMER_STATE \ - #define GLFW_PLATFORM_CONTEXT_STATE \ GLFW_WGL_CONTEXT_STATE \ GLFW_NSGL_CONTEXT_STATE \ @@ -177,3 +140,24 @@ GLFW_NSGL_LIBRARY_CONTEXT_STATE \ GLFW_GLX_LIBRARY_CONTEXT_STATE +#if defined(_WIN32) + #include "win32_thread.h" + #define GLFW_PLATFORM_TLS_STATE GLFW_WIN32_TLS_STATE + #define GLFW_PLATFORM_MUTEX_STATE GLFW_WIN32_MUTEX_STATE +#else + #include "posix_thread.h" + #define GLFW_PLATFORM_TLS_STATE GLFW_POSIX_TLS_STATE + #define GLFW_PLATFORM_MUTEX_STATE GLFW_POSIX_MUTEX_STATE +#endif + +#if defined(_WIN32) + #include "win32_time.h" + #define GLFW_PLATFORM_LIBRARY_TIMER_STATE GLFW_WIN32_LIBRARY_TIMER_STATE +#elif defined(__APPLE__) + #include "cocoa_time.h" + #define GLFW_PLATFORM_LIBRARY_TIMER_STATE GLFW_COCOA_LIBRARY_TIMER_STATE +#else + #include "posix_time.h" + #define GLFW_PLATFORM_LIBRARY_TIMER_STATE GLFW_POSIX_LIBRARY_TIMER_STATE +#endif + From 09653b8c5490ebb605933537e897587c9129f159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 6 Apr 2022 21:13:48 +0200 Subject: [PATCH 83/88] Move last bits of window setup to platform code This avoids glfwCreateWindow emitting GLFW_FEATURE_UNAVAILABLE or GLFW_FEATURE_UNIMPLEMENTED on Wayland because shared code was calling unimplemented or unavailable platform functions during final setup. It also makes it consistent with the final setup of full screen windows. --- README.md | 1 + src/cocoa_window.m | 18 ++++++++++++++++++ src/null_window.c | 18 ++++++++++++++++++ src/win32_window.c | 18 ++++++++++++++++++ src/window.c | 28 ---------------------------- src/wl_window.c | 6 ++++++ src/x11_window.c | 18 ++++++++++++++++++ 7 files changed, 79 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 67d57a2f..72cbef36 100644 --- a/README.md +++ b/README.md @@ -325,6 +325,7 @@ information on what to include when reporting a bug. - [Wayland] Bugfix: Drag and drop data was misinterpreted as clipboard string - [Wayland] Bugfix: MIME type matching was not performed for clipboard string - [Wayland] Bugfix: The OSMesa library was not unloaded on termination + - [Wayland] Bugfix: `glfwCreateWindow` could emit `GLFW_FEATURE_UNAVAILABLE` - [POSIX] Removed use of deprecated function `gettimeofday` - [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled - [WGL] Disabled the DWM swap interval hack for Windows 8 and later (#1072) diff --git a/src/cocoa_window.m b/src/cocoa_window.m index e461b267..444bd563 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -931,13 +931,31 @@ int _glfwCreateWindowCocoa(_GLFWwindow* window, if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + + if (!_glfwRefreshContextAttribs(window, ctxconfig)) + return GLFW_FALSE; } + if (wndconfig->mousePassthrough) + _glfwSetWindowMousePassthroughCocoa(window, GLFW_TRUE); + if (window->monitor) { _glfwShowWindowCocoa(window); _glfwFocusWindowCocoa(window); acquireMonitor(window); + + if (wndconfig->centerCursor) + _glfwCenterCursorInContentArea(window); + } + else + { + if (wndconfig->visible) + { + _glfwShowWindowCocoa(window); + if (wndconfig->focused) + _glfwFocusWindowCocoa(window); + } } return GLFW_TRUE; diff --git a/src/null_window.c b/src/null_window.c index 7e87b9da..b40110b8 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -128,13 +128,31 @@ int _glfwCreateWindowNull(_GLFWwindow* window, if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + + if (!_glfwRefreshContextAttribs(window, ctxconfig)) + return GLFW_FALSE; } + if (wndconfig->mousePassthrough) + _glfwSetWindowMousePassthroughNull(window, GLFW_TRUE); + if (window->monitor) { _glfwShowWindowNull(window); _glfwFocusWindowNull(window); acquireMonitor(window); + + if (wndconfig->centerCursor) + _glfwCenterCursorInContentArea(window); + } + else + { + if (wndconfig->visible) + { + _glfwShowWindowNull(window); + if (wndconfig->focused) + _glfwFocusWindowNull(window); + } } return GLFW_TRUE; diff --git a/src/win32_window.c b/src/win32_window.c index 14f2fe5b..87c2b4a1 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1493,14 +1493,32 @@ int _glfwCreateWindowWin32(_GLFWwindow* window, if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + + if (!_glfwRefreshContextAttribs(window, ctxconfig)) + return GLFW_FALSE; } + if (wndconfig->mousePassthrough) + _glfwSetWindowMousePassthroughWin32(window, GLFW_TRUE); + if (window->monitor) { _glfwShowWindowWin32(window); _glfwFocusWindowWin32(window); acquireMonitor(window); fitToMonitor(window); + + if (wndconfig->centerCursor) + _glfwCenterCursorInContentArea(window); + } + else + { + if (wndconfig->visible) + { + _glfwShowWindowWin32(window); + if (wndconfig->focused) + _glfwFocusWindowWin32(window); + } } return GLFW_TRUE; diff --git a/src/window.c b/src/window.c index a24feb60..621e2e64 100644 --- a/src/window.c +++ b/src/window.c @@ -215,40 +215,12 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->numer = GLFW_DONT_CARE; window->denom = GLFW_DONT_CARE; - // Open the actual window and create its context if (!_glfw.platform.createWindow(window, &wndconfig, &ctxconfig, &fbconfig)) { glfwDestroyWindow((GLFWwindow*) window); return NULL; } - if (ctxconfig.client != GLFW_NO_API) - { - if (!_glfwRefreshContextAttribs(window, &ctxconfig)) - { - glfwDestroyWindow((GLFWwindow*) window); - return NULL; - } - } - - if (wndconfig.mousePassthrough) - _glfw.platform.setWindowMousePassthrough(window, GLFW_TRUE); - - if (window->monitor) - { - if (wndconfig.centerCursor) - _glfwCenterCursorInContentArea(window); - } - else - { - if (wndconfig.visible) - { - _glfw.platform.showWindow(window); - if (wndconfig.focused) - _glfw.platform.focusWindow(window); - } - } - return (GLFWwindow*) window; } diff --git a/src/wl_window.c b/src/wl_window.c index 047b9eb6..a1f93185 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1730,8 +1730,14 @@ int _glfwCreateWindowWayland(_GLFWwindow* window, if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + + if (!_glfwRefreshContextAttribs(window, ctxconfig)) + return GLFW_FALSE; } + if (wndconfig->mousePassthrough) + _glfwSetWindowMousePassthroughWayland(window, GLFW_TRUE); + return GLFW_TRUE; } diff --git a/src/x11_window.c b/src/x11_window.c index 85b844a1..280dc986 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -1952,13 +1952,31 @@ int _glfwCreateWindowX11(_GLFWwindow* window, if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + + if (!_glfwRefreshContextAttribs(window, ctxconfig)) + return GLFW_FALSE; } + if (wndconfig->mousePassthrough) + _glfwSetWindowMousePassthroughX11(window, GLFW_TRUE); + if (window->monitor) { _glfwShowWindowX11(window); updateWindowMode(window); acquireMonitor(window); + + if (wndconfig->centerCursor) + _glfwCenterCursorInContentArea(window); + } + else + { + if (wndconfig->visible) + { + _glfwShowWindowX11(window); + if (wndconfig->focused) + _glfwFocusWindowX11(window); + } } XFlush(_glfw.x11.display); From 685de9089f13dab9e284d00aaca24e68947f5bed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 21 Apr 2022 17:35:50 +0200 Subject: [PATCH 84/88] Fix and clarify comments for GLFWAPI --- include/GLFW/glfw3.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 3f320065..6f189274 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -262,13 +262,12 @@ extern "C" { /* We are building GLFW as a Win32 DLL */ #define GLFWAPI __declspec(dllexport) #elif defined(_WIN32) && defined(GLFW_DLL) - /* We are calling GLFW as a Win32 DLL */ + /* We are calling a GLFW Win32 DLL */ #define GLFWAPI __declspec(dllimport) #elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL) - /* We are building GLFW as a shared / dynamic library */ + /* We are building GLFW as a Unix shared library */ #define GLFWAPI __attribute__((visibility("default"))) #else - /* We are building or calling GLFW as a static library */ #define GLFWAPI #endif From 7dfd84c458dfa12b351c932b8281b13faf7b50b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 21 Apr 2022 21:22:24 +0200 Subject: [PATCH 85/88] Win32: Make monitor area retrieval more consistent The rest of this file uses GetMonitorInfoW to retrieve this information. --- src/win32_window.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/win32_window.c b/src/win32_window.c index 87c2b4a1..333cef1b 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1284,15 +1284,16 @@ static int createNativeWindow(_GLFWwindow* window, if (window->monitor) { - GLFWvidmode mode; + MONITORINFO mi = { sizeof(mi) }; + GetMonitorInfoW(window->monitor->win32.handle, &mi); // NOTE: This window placement is temporary and approximate, as the // correct position and size cannot be known until the monitor // video mode has been picked in _glfwSetVideoModeWin32 - _glfwGetMonitorPosWin32(window->monitor, &xpos, &ypos); - _glfwGetVideoModeWin32(window->monitor, &mode); - fullWidth = mode.width; - fullHeight = mode.height; + xpos = mi.rcMonitor.left; + ypos = mi.rcMonitor.top; + fullWidth = mi.rcMonitor.right - mi.rcMonitor.left; + fullHeight = mi.rcMonitor.bottom - mi.rcMonitor.top; } else { From 5c3ebfb9ccba331433620d3ed44a1fe1d9bf879e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 3 May 2022 19:50:28 +0200 Subject: [PATCH 86/88] Remove OSMesa specific code from offscreen example The OSMesa specific path is more appropriate for a test than an example. --- examples/CMakeLists.txt | 5 ----- examples/offscreen.c | 13 ------------- 2 files changed, 18 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index be8927db..e7a03797 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -50,11 +50,6 @@ set_target_properties(${GUI_ONLY_BINARIES} ${CONSOLE_BINARIES} PROPERTIES C_STANDARD 99 FOLDER "GLFW3/Examples") -if (GLFW_USE_OSMESA) - find_package(OSMesa REQUIRED) - target_compile_definitions(offscreen PRIVATE USE_NATIVE_OSMESA) -endif() - if (MSVC) # Tell MSVC to use main instead of WinMain set_target_properties(${GUI_ONLY_BINARIES} PROPERTIES diff --git a/examples/offscreen.c b/examples/offscreen.c index b7cd2fd7..e2852860 100644 --- a/examples/offscreen.c +++ b/examples/offscreen.c @@ -28,11 +28,6 @@ #define GLFW_INCLUDE_NONE #include -#if USE_NATIVE_OSMESA - #define GLFW_EXPOSE_NATIVE_OSMESA - #include -#endif - #include "linmath.h" #include @@ -151,12 +146,8 @@ int main(void) glDrawArrays(GL_TRIANGLES, 0, 3); glFinish(); -#if USE_NATIVE_OSMESA - glfwGetOSMesaColorBuffer(window, &width, &height, NULL, (void**) &buffer); -#else buffer = calloc(4, width * height); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); -#endif // Write image Y-flipped because OpenGL stbi_write_png("offscreen.png", @@ -164,11 +155,7 @@ int main(void) buffer + (width * 4 * (height - 1)), -width * 4); -#if USE_NATIVE_OSMESA - // Here is where there's nothing -#else free(buffer); -#endif glfwDestroyWindow(window); From ce85c7dcaf48be96870f2d8fec1d78b0b7d744c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 3 May 2022 19:54:08 +0200 Subject: [PATCH 87/88] Fix GLAPIENTRY not being defined for native access This is a follow-up to 535c3ce63240ee91f998219b35dc82abef545c3d. That commit made glfw3native.h undefine GLAPIENTRY whenever GLFW_EXPOSE_NATIVE_GLX or GLFW_EXPOSE_NATIVE_OSMESA was defined, on the assumption that it was glfw3.h that had defined GLAPIENTRY. If gl.h or an extension loader header has been included before glfw3.h, then it has defined inclusion guards preventing GLAPIENTRY from being defined later when the GLX or OSMesa header includes gl.h. This commit ensures GLAPIENTRY is only undefined if glfw3.h defined it. Related to #2010 --- include/GLFW/glfw3.h | 1 + include/GLFW/glfw3native.h | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 6f189274..52225c74 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -6383,6 +6383,7 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window */ #ifndef GLAPIENTRY #define GLAPIENTRY APIENTRY + #define GLFW_GLAPIENTRY_DEFINED #endif /* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ diff --git a/include/GLFW/glfw3native.h b/include/GLFW/glfw3native.h index 7f5bf9e4..6d090778 100644 --- a/include/GLFW/glfw3native.h +++ b/include/GLFW/glfw3native.h @@ -116,7 +116,10 @@ extern "C" { * default it also acts as an OpenGL header * However, glx.h will include gl.h, which will define it unconditionally */ - #undef GLAPIENTRY + #if defined(GLFW_GLAPIENTRY_DEFINED) + #undef GLAPIENTRY + #undef GLFW_GLAPIENTRY_DEFINED + #endif #include #endif #if defined(GLFW_EXPOSE_NATIVE_EGL) @@ -127,7 +130,10 @@ extern "C" { * default it also acts as an OpenGL header * However, osmesa.h will include gl.h, which will define it unconditionally */ - #undef GLAPIENTRY + #if defined(GLFW_GLAPIENTRY_DEFINED) + #undef GLAPIENTRY + #undef GLFW_GLAPIENTRY_DEFINED + #endif #include #endif From 62e175ef9fae75335575964c845a302447c012c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 5 May 2022 21:47:34 +0200 Subject: [PATCH 88/88] Win32: Add comment clarifying use of ToUnicode Fixes #2100 --- src/win32_init.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/win32_init.c b/src/win32_init.c index 9465c951..1cdc5b98 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -502,6 +502,8 @@ void _glfwUpdateKeyNamesWin32(void) if (length == -1) { + // This is a dead key, so we need a second simulated key press + // to make it output its own character (usually a diacritic) length = ToUnicode(vk, scancode, state, chars, sizeof(chars) / sizeof(WCHAR), 0);