Win32: Fix GLFW_MOUSE_PASSTHROUGH dropping events

Returning HTTRANSPARENT from WM_NCHITTEST does cause the window to be
transparent for some hit-testing APIs but does not make it pass mouse
input through to whatever window is below it.

For that to work on modern Windows, the window needs to be both layered
and extended-window-style-transparent.

Additional logic changes to ensure mouse input passthrough, framebuffer
transparency and window opacity are mindful of one another when
modifying WS_EX_LAYERED.

Related to #1568.
This commit is contained in:
Camilla Löwy 2020-07-06 23:17:41 +02:00
parent 68e4261d73
commit 7da3e52c86

View File

@ -417,10 +417,15 @@ static void updateFramebufferTransparency(const _GLFWwindow* window)
else else
{ {
LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
exStyle &= ~WS_EX_LAYERED; if (exStyle & WS_EX_TRANSPARENT)
SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle); SetLayeredWindowAttributes(window->win32.handle, 0, 0, 0);
RedrawWindow(window->win32.handle, NULL, NULL, else
RDW_ERASE | RDW_INVALIDATE | RDW_FRAME); {
exStyle &= ~WS_EX_LAYERED;
SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
RedrawWindow(window->win32.handle, NULL, NULL,
RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
}
} }
} }
@ -1201,13 +1206,6 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
DragFinish(drop); DragFinish(drop);
return 0; return 0;
} }
case WM_NCHITTEST:
{
if (window->mousePassthrough)
return HTTRANSPARENT;
break;
}
} }
return DefWindowProcW(hWnd, uMsg, wParam, lParam); return DefWindowProcW(hWnd, uMsg, wParam, lParam);
@ -1863,6 +1861,33 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled) void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled)
{ {
COLORREF key = 0;
BYTE alpha = 0;
DWORD flags = 0;
DWORD exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
if (exStyle & WS_EX_LAYERED)
GetLayeredWindowAttributes(window->win32.handle, &key, &alpha, &flags);
if (enabled)
exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED);
else
{
exStyle &= ~WS_EX_TRANSPARENT;
// NOTE: Window opacity and framebuffer transparency also need to
// control the layered style so avoid stepping on their feet
if (exStyle & WS_EX_LAYERED)
{
if (!(flags & (LWA_ALPHA | LWA_COLORKEY)))
exStyle &= ~WS_EX_LAYERED;
}
}
SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
if (enabled)
SetLayeredWindowAttributes(window->win32.handle, key, alpha, flags);
window->mousePassthrough = enabled; window->mousePassthrough = enabled;
} }
@ -1883,19 +1908,22 @@ float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
{ {
if (opacity < 1.f) LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
if (opacity < 1.f || (exStyle & WS_EX_TRANSPARENT))
{ {
const BYTE alpha = (BYTE) (255 * opacity); const BYTE alpha = (BYTE) (255 * opacity);
DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); exStyle |= WS_EX_LAYERED;
style |= WS_EX_LAYERED; SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style);
SetLayeredWindowAttributes(window->win32.handle, 0, alpha, LWA_ALPHA); SetLayeredWindowAttributes(window->win32.handle, 0, alpha, LWA_ALPHA);
} }
else if (exStyle & WS_EX_TRANSPARENT)
{
SetLayeredWindowAttributes(window->win32.handle, 0, 0, 0);
}
else else
{ {
DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); exStyle &= ~WS_EX_LAYERED;
style &= ~WS_EX_LAYERED; SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style);
} }
} }