Compare commits

...

5 Commits

Author SHA1 Message Date
Peru S
eac1e09ff9
Merge 959f13c1bd into 9352d8fe93 2026-01-14 22:26:41 -03:00
Camilla Löwy
9352d8fe93 X11: Cleanup 2026-01-14 18:26:50 +01:00
Camilla Löwy
a228a8b447 X11: Fix window made non-floating by being hidden
The previous implementation was based on the incorrect assumption that
the _NET_WM_STATE_ABOVE state is always retained on unmapped windows.
According to EWMH the WM should remove the _NET_WM_STATE property when
a window is unmapped.

This commit moves the adding of _NET_WM_STATE_ABOVE to before mapping
the window during glfwShowWindow.  The logic for removing
_NET_WM_STATE_ABOVE from hidden windows is retained, as EWMH still
allows WMs to leave the window property on unmapped window.

Fixes #2276
2026-01-12 21:26:42 +01:00
Peru S
959f13c1bd Add to CONTRIBUTORS.md and README.md 2025-07-29 12:59:07 -07:00
Peru S
2fded1aab2 Fix windowProc when SetPropW() fails silently
**Issue**

In some cases, on Windows, `SetPropW` returns `FALSE`.
glfw currently silently ignores the result.
This means that the message pump `windowProc` fails to work (does not receive keyboard events for example). Drawing is fine though as it's a different GLFW mechanism.

I had this failure frequently infrequently and it was frustrating to deal with (just have to rerun the program a few times and hope `SetPropW` doesn't return `FALSE`). So sending this fix in case others are facing the issue on Windows.

**Fix**

On Windows+GLFW, `windowProc` will fallback to looking up the GLFW window list (just like `PollEvents` does).
Also added the error code to `_glfwInputError` message to aid debugging in the future.

My suggestion is to get rid of `SetPropW`/`GetPropW` as it is a string-based Windows API (slow/fragile) - we already have the lightweight linked list, might as well just use it always - also it's usually just the one or two windows we deal with.

- [x] Verified on Windows - message pump is stable with this fix (once I was able to repro this issue).
2025-07-29 12:53:19 -07:00
5 changed files with 76 additions and 33 deletions

View File

@ -237,6 +237,7 @@ video tutorials.
- Jan Schürkamp
- Christian Sdunek
- Matt Sealey
- Perumaal Shanmugam
- Steve Sexton
- Arkady Shapkin
- Mingjie Shen

View File

@ -153,11 +153,13 @@ information on what to include when reporting a bug.
- [X11] Bugfix: Prevent BadWindow when creating small windows with a content scale
less than 1 (#2754)
- [X11] Bugfix: Clamp width and height to >= 1 to prevent BadValue error and app exit
- [X11] Bugfix: Floating windows silently became non-floating when hidden (#2276)
- [Linux] Bugfix: The header for `ioctl` was only implicitly included (#2778)
- [Null] Added Vulkan 'window' surface creation via `VK_EXT_headless_surface`
- [Null] Added EGL context creation on Mesa via `EGL_MESA_platform_surfaceless`
- [EGL] Allowed native access on Wayland with `GLFW_CONTEXT_CREATION_API` set to
`GLFW_NATIVE_CONTEXT_API` (#2518)
- [Win32] Bugfix: Fix `windowProc` to work when `SetPropW` fails silently
## Contact

View File

@ -493,18 +493,19 @@ void _glfwInputErrorWin32(int error, const char* description)
WCHAR buffer[_GLFW_MESSAGE_SIZE] = L"";
char message[_GLFW_MESSAGE_SIZE] = "";
DWORD lastError = GetLastError();
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL,
GetLastError() & 0xffff,
lastError & 0xffff,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buffer,
sizeof(buffer) / sizeof(WCHAR),
NULL);
WideCharToMultiByte(CP_UTF8, 0, buffer, -1, message, sizeof(message), NULL, NULL);
_glfwInputError(error, "%s: %s", description, message);
_glfwInputError(error, "%s (0x%lx / %lu): %s", description, lastError, lastError, message);
}
// Updates key names according to the current keyboard layout

View File

@ -548,9 +548,24 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
EnableNonClientDpiScaling(hWnd);
}
}
else
{
// HACK: SetPropW returns FALSE in some instances (GetLastError() = 0x8 Insufficient resources).
// We already have the global list of windows, check against it. It's not many, and we are already
// doing this in the (one of the callers) PollEvents below.
window = _glfw.windowListHead;
while (window)
{
if (window->win32.handle == hWnd) { break; }
window = window->next;
}
}
if (!window)
{
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
}
switch (uMsg)
{
@ -1401,7 +1416,13 @@ static int createNativeWindow(_GLFWwindow* window,
return GLFW_FALSE;
}
SetPropW(window->win32.handle, L"GLFW", window);
if (!SetPropW(window->win32.handle, L"GLFW", window))
{
// In some cases, SetPropW returns FALSE: GetLastError() returns 0x8 (Insufficient resources).
// The message pump fails to work because windowProc cannot look up the GLFW property.
// Instead of failing the program completely by raising an error instead, windowProc looks up
// the global window list to find the hWnd under consideration.
}
ChangeWindowMessageFilterEx(window->win32.handle, WM_DROPFILES, MSGFLT_ALLOW, NULL);
ChangeWindowMessageFilterEx(window->win32.handle, WM_COPYDATA, MSGFLT_ALLOW, NULL);

View File

@ -2438,6 +2438,38 @@ void _glfwShowWindowX11(_GLFWwindow* window)
if (_glfwWindowVisibleX11(window))
return;
if (window->floating && _glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_ABOVE)
{
Atom* states = NULL;
const unsigned long count =
_glfwGetWindowPropertyX11(window->x11.handle,
_glfw.x11.NET_WM_STATE,
XA_ATOM, (unsigned char**) &states);
// NOTE: We don't check for failure as this property may not exist yet
// and that's fine (and we'll create it implicitly with append)
unsigned long i;
for (i = 0; i < count; i++)
{
if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
break;
}
if (i == count)
{
XChangeProperty(_glfw.x11.display, window->x11.handle,
_glfw.x11.NET_WM_STATE, XA_ATOM, 32,
PropModeAppend,
(unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
1);
}
if (states)
XFree(states);
}
XMapWindow(_glfw.x11.display, window->x11.handle);
waitForVisibilityNotify(window);
}
@ -2667,6 +2699,10 @@ void _glfwSetWindowFloatingX11(_GLFWwindow* window, GLFWbool enabled)
}
else
{
// NOTE: _NET_WM_STATE_ABOVE is added when the window is shown
if (enabled)
return;
Atom* states = NULL;
const unsigned long count =
_glfwGetWindowPropertyX11(window->x11.handle,
@ -2677,8 +2713,6 @@ void _glfwSetWindowFloatingX11(_GLFWwindow* window, GLFWbool enabled)
// NOTE: We don't check for failure as this property may not exist yet
// and that's fine (and we'll create it implicitly with append)
if (enabled)
{
unsigned long i;
for (i = 0; i < count; i++)
@ -2687,28 +2721,12 @@ void _glfwSetWindowFloatingX11(_GLFWwindow* window, GLFWbool enabled)
break;
}
if (i == count)
{
XChangeProperty(_glfw.x11.display, window->x11.handle,
_glfw.x11.NET_WM_STATE, XA_ATOM, 32,
PropModeAppend,
(unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
1);
}
}
else if (states)
{
for (unsigned long i = 0; i < count; i++)
{
if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
if (i < count)
{
states[i] = states[count - 1];
XChangeProperty(_glfw.x11.display, window->x11.handle,
_glfw.x11.NET_WM_STATE, XA_ATOM, 32,
PropModeReplace, (unsigned char*) states, count - 1);
break;
}
}
}
if (states)