From 05dd2fa2986c37e7aee3cba917b1d02a3a189390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 28 Jul 2020 00:12:45 +0200 Subject: [PATCH] Win32: Disable fb transparency when it is broken On Windows 7, when GLFW framebuffer transparency and the DWM are enabled but DWM transparency is disabled (i.e. when the Transparency setting is disabled under Personalization > Color), the contents of the framebuffer is combined with the last frame using additive blending instead of replacing the previous contents. This commit limits GLFW framebuffer transparency on Windows 7 to when DWM transparency is enabled, removing the previous workaround of setting a layered window color key that led to rendering artifacts. Fixes #1512. --- README.md | 2 ++ docs/news.dox | 7 +++++ docs/window.dox | 7 ----- src/win32_init.c | 2 ++ src/win32_platform.h | 6 ++++ src/win32_window.c | 74 +++++++++++++++++++++----------------------- 6 files changed, 52 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index dfbd5839..8c3ec378 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,8 @@ information on what to include when reporting a bug. - [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 + - [Win32] Disabled framebuffer transparency on Windows 7 when DWM windows are + opaque (#1512) - [Win32] Bugfix: `GLFW_INCLUDE_VULKAN` plus `VK_USE_PLATFORM_WIN32_KHR` caused symbol redefinition (#1524) - [Win32] Bugfix: The cursor position event was emitted before its cursor enter diff --git a/docs/news.dox b/docs/news.dox index 0fa3a632..19df848a 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -97,6 +97,13 @@ GLFW no longer depends on the CoreVideo framework on macOS and it no longer needs to be specified during compilation or linking. +@subsubsection caveat_fbtransparency_34 Framebuffer transparency requires DWM transparency + +GLFW no longer supports framebuffer transparency enabled via @ref +GLFW_TRANSPARENT_FRAMEBUFFER on Windows 7 if DWM transparency is off +(the Transparency setting under Personalization > Window Color). + + @subsection deprecations_34 Deprecations in version 3.4 @subsection removals_34 Removals in 3.4 diff --git a/docs/window.dox b/docs/window.dox index 1132b47b..3cc20eb1 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -234,13 +234,6 @@ alpha channel will be used to combine the framebuffer with the background. This does not affect window decorations. Possible values are `GLFW_TRUE` and `GLFW_FALSE`. -@par -@win32 GLFW sets a color key for the window to work around repainting issues -with a transparent framebuffer. The chosen color value is RGB 255,0,255 -(magenta). This will make pixels with that exact color fully transparent -regardless of their alpha values. If this is a problem, make these pixels any -other color before buffer swap. - @anchor GLFW_FOCUS_ON_SHOW_hint __GLFW_FOCUS_ON_SHOW__ specifies whether the window will be given input focus when @ref glfwShowWindow is called. Possible values are `GLFW_TRUE` and `GLFW_FALSE`. diff --git a/src/win32_init.c b/src/win32_init.c index cbc0fd83..f6995e51 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -143,6 +143,8 @@ static GLFWbool loadLibraries(void) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmFlush"); _glfw.win32.dwmapi.EnableBlurBehindWindow = (PFN_DwmEnableBlurBehindWindow) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); + _glfw.win32.dwmapi.GetColorizationColor = (PFN_DwmGetColorizationColor) + GetProcAddress(_glfw.win32.dwmapi.instance, "DwmGetColorizationColor"); } _glfw.win32.shcore.instance = LoadLibraryA("shcore.dll"); diff --git a/src/win32_platform.h b/src/win32_platform.h index 76ea609a..260a77ff 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -77,6 +77,9 @@ #ifndef WM_DWMCOMPOSITIONCHANGED #define WM_DWMCOMPOSITIONCHANGED 0x031E #endif +#ifndef WM_DWMCOLORIZATIONCOLORCHANGED + #define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320 +#endif #ifndef WM_COPYGLOBALDATA #define WM_COPYGLOBALDATA 0x0049 #endif @@ -244,9 +247,11 @@ typedef BOOL (WINAPI * PFN_AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UIN typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*); +typedef HRESULT (WINAPI * PFN_DwmGetColorizationColor)(DWORD*,BOOL*); #define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled #define DwmFlush _glfw.win32.dwmapi.Flush #define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow +#define DwmGetColorizationColor _glfw.win32.dwmapi.GetColorizationColor // shcore.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); @@ -368,6 +373,7 @@ typedef struct _GLFWlibraryWin32 PFN_DwmIsCompositionEnabled IsCompositionEnabled; PFN_DwmFlush Flush; PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow; + PFN_DwmGetColorizationColor GetColorizationColor; } dwmapi; struct { diff --git a/src/win32_window.c b/src/win32_window.c index 486c102e..8cb50500 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -377,12 +377,17 @@ static void updateWindowStyles(const _GLFWwindow* window) // static void updateFramebufferTransparency(const _GLFWwindow* window) { - BOOL enabled; + BOOL composition, opaque; + DWORD color; if (!IsWindowsVistaOrGreater()) return; - if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled) + if (FAILED(DwmIsCompositionEnabled(&composition)) || !composition) + return; + + if (IsWindows8OrGreater() || + (SUCCEEDED(DwmGetColorizationColor(&color, &opaque)) && !opaque)) { HRGN region = CreateRectRgn(0, 0, -1, -1); DWM_BLURBEHIND bb = {0}; @@ -390,42 +395,18 @@ static void updateFramebufferTransparency(const _GLFWwindow* window) bb.hRgnBlur = region; bb.fEnable = TRUE; - if (SUCCEEDED(DwmEnableBlurBehindWindow(window->win32.handle, &bb))) - { - // Decorated windows don't repaint the transparent background - // leaving a trail behind animations - // HACK: Making the window layered with a transparency color key - // seems to fix this. Normally, when specifying - // a transparency color key to be used when composing the - // layered window, all pixels painted by the window in this - // color will be transparent. That doesn't seem to be the - // case anymore, at least when used with blur behind window - // plus negative region. - LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); - exStyle |= WS_EX_LAYERED; - SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle); - - // Using a color key not equal to black to fix the trailing - // issue. When set to black, something is making the hit test - // not resize with the window frame. - SetLayeredWindowAttributes(window->win32.handle, - RGB(255, 0, 255), 255, LWA_COLORKEY); - } - + DwmEnableBlurBehindWindow(window->win32.handle, &bb); DeleteObject(region); } else { - LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); - if (exStyle & WS_EX_TRANSPARENT) - SetLayeredWindowAttributes(window->win32.handle, 0, 0, 0); - else - { - exStyle &= ~WS_EX_LAYERED; - SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle); - RedrawWindow(window->win32.handle, NULL, NULL, - RDW_ERASE | RDW_INVALIDATE | RDW_FRAME); - } + // HACK: Disable framebuffer transparency on Windows 7 when the + // colorization color is opaque, because otherwise the window + // contents is blended additively with the previous frame instead + // of replacing it + DWM_BLURBEHIND bb = {0}; + bb.dwFlags = DWM_BB_ENABLE; + DwmEnableBlurBehindWindow(window->win32.handle, &bb); } } @@ -1109,6 +1090,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, } case WM_DWMCOMPOSITIONCHANGED: + case WM_DWMCOLORIZATIONCOLORCHANGED: { if (window->win32.transparent) updateFramebufferTransparency(window); @@ -1834,7 +1816,8 @@ int _glfwPlatformWindowHovered(_GLFWwindow* window) int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { - BOOL enabled; + BOOL composition, opaque; + DWORD color; if (!window->win32.transparent) return GLFW_FALSE; @@ -1842,7 +1825,20 @@ int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) if (!IsWindowsVistaOrGreater()) return GLFW_FALSE; - return SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled; + if (FAILED(DwmIsCompositionEnabled(&composition)) || !composition) + return GLFW_FALSE; + + if (!IsWindows8OrGreater()) + { + // HACK: Disable framebuffer transparency on Windows 7 when the + // colorization color is opaque, because otherwise the window + // contents is blended additively with the previous frame instead + // of replacing it + if (FAILED(DwmGetColorizationColor(&color, &opaque)) || opaque) + return GLFW_FALSE; + } + + return GLFW_TRUE; } void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) @@ -1877,11 +1873,11 @@ void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enable else { exStyle &= ~WS_EX_TRANSPARENT; - // NOTE: Window opacity and framebuffer transparency also need to - // control the layered style so avoid stepping on their feet + // NOTE: Window opacity also needs the layered window style so do not + // remove it if the window is alpha blended if (exStyle & WS_EX_LAYERED) { - if (!(flags & (LWA_ALPHA | LWA_COLORKEY))) + if (!(flags & LWA_ALPHA)) exStyle &= ~WS_EX_LAYERED; } }