diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 020bb26e..ce7b2e9d 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -33,6 +33,7 @@ video tutorials. - David Carlier - Arturo Castro - Chi-kwan Chan + - Haoyun Chen - TheChocolateOre - Ali Chraghi - Joseph Chua diff --git a/README.md b/README.md index e3d18ec8..f8d6b415 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,7 @@ information on what to include when reporting a bug. - Bugfix: `glfwMakeContextCurrent` would access TLS slot before initialization - Bugfix: `glfwSetGammaRamp` could emit `GLFW_INVALID_VALUE` before initialization - Bugfix: `glfwGetJoystickUserPointer` returned `NULL` during disconnection (#2092) + - [Win32] Added support for dark title bar (#2228) - [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/win32_init.c b/src/win32_init.c index ef2615f1..a6169583 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -151,6 +151,8 @@ static GLFWbool loadLibraries(void) _glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); _glfw.win32.dwmapi.GetColorizationColor = (PFN_DwmGetColorizationColor) _glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmGetColorizationColor"); + _glfw.win32.dwmapi.SetWindowAttribute = (PFN_DwmSetWindowAttribute) + _glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmSetWindowAttribute"); } _glfw.win32.shcore.instance = _glfwPlatformLoadModule("shcore.dll"); @@ -169,6 +171,18 @@ static GLFWbool loadLibraries(void) _glfwPlatformGetModuleSymbol(_glfw.win32.ntdll.instance, "RtlVerifyVersionInfo"); } + _glfw.win32.uxtheme.instance = _glfwPlatformLoadModule("uxtheme.dll"); + if (_glfw.win32.uxtheme.instance) + { + _glfw.win32.uxtheme.ShouldAppsUseDarkMode = (ShouldAppsUseDarkModePtr)_glfwPlatformGetModuleSymbol(_glfw.win32.uxtheme.instance, MAKEINTRESOURCEA(132)); + _glfw.win32.uxtheme.GetImmersiveColorFromColorSetEx = (GetImmersiveColorFromColorSetExPtr)_glfwPlatformGetModuleSymbol(_glfw.win32.uxtheme.instance, MAKEINTRESOURCEA(95)); + _glfw.win32.uxtheme.GetImmersiveColorTypeFromName = (GetImmersiveColorTypeFromNamePtr)_glfwPlatformGetModuleSymbol(_glfw.win32.uxtheme.instance, MAKEINTRESOURCEA(96)); + _glfw.win32.uxtheme.GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)_glfwPlatformGetModuleSymbol(_glfw.win32.uxtheme.instance, MAKEINTRESOURCEA(98)); + + _glfw.win32.uxtheme.uxThemeAvailable = _glfw.win32.uxtheme.ShouldAppsUseDarkMode && _glfw.win32.uxtheme.GetImmersiveColorFromColorSetEx && _glfw.win32.uxtheme.GetImmersiveColorTypeFromName && _glfw.win32.uxtheme.GetImmersiveUserColorSetPreference; + _glfw.win32.uxtheme.darkTitleAvailable = _glfwIsWindows10BuildOrGreaterWin32(22000); + } + return GLFW_TRUE; } @@ -193,6 +207,9 @@ static void freeLibraries(void) if (_glfw.win32.ntdll.instance) _glfwPlatformFreeModule(_glfw.win32.ntdll.instance); + + if (_glfw.win32.uxtheme.instance) + _glfwPlatformFreeModule(_glfw.win32.uxtheme.instance); } // Create key code translation tables diff --git a/src/win32_platform.h b/src/win32_platform.h index 82b34bb9..b5f9ee1f 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -299,12 +299,15 @@ typedef int (WINAPI * PFN_GetSystemMetricsForDpi)(int,UINT); // dwmapi.dll function pointer typedefs 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_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*); typedef HRESULT (WINAPI * PFN_DwmGetColorizationColor)(DWORD*,BOOL*); +typedef HRESULT (WINAPI * PFN_DwmSetWindowAttribute)(HWND,DWORD,LPCVOID,DWORD); + #define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled #define DwmFlush _glfw.win32.dwmapi.Flush #define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow #define DwmGetColorizationColor _glfw.win32.dwmapi.GetColorizationColor +#define DwmSetWindowAttribute _glfw.win32.dwmapi.SetWindowAttribute // shcore.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); @@ -366,6 +369,10 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)( #define GLFW_WGL_CONTEXT_STATE _GLFWcontextWGL wgl; #define GLFW_WGL_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl; +typedef BOOL (WINAPI * ShouldAppsUseDarkModePtr)(); +typedef DWORD (WINAPI * GetImmersiveColorFromColorSetExPtr)(UINT,UINT,BOOL,UINT); +typedef int (WINAPI * GetImmersiveColorTypeFromNamePtr)(const WCHAR*); +typedef int (WINAPI * GetImmersiveUserColorSetPreferencePtr)(BOOL,BOOL); // WGL-specific per-context data // @@ -487,6 +494,7 @@ typedef struct _GLFWlibraryWin32 PFN_DwmFlush Flush; PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow; PFN_DwmGetColorizationColor GetColorizationColor; + PFN_DwmSetWindowAttribute SetWindowAttribute; } dwmapi; struct { @@ -499,6 +507,16 @@ typedef struct _GLFWlibraryWin32 HINSTANCE instance; PFN_RtlVerifyVersionInfo RtlVerifyVersionInfo_; } ntdll; + + struct { + HINSTANCE instance; + GLFWbool uxThemeAvailable; + GLFWbool darkTitleAvailable; + ShouldAppsUseDarkModePtr ShouldAppsUseDarkMode; + GetImmersiveColorFromColorSetExPtr GetImmersiveColorFromColorSetEx; + GetImmersiveColorTypeFromNamePtr GetImmersiveColorTypeFromName; + GetImmersiveUserColorSetPreferencePtr GetImmersiveUserColorSetPreference; + } uxtheme; } _GLFWlibraryWin32; // Win32-specific per-monitor data diff --git a/src/win32_window.c b/src/win32_window.c index 676640bf..1235eec6 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -37,6 +37,25 @@ #include #include +// Ref: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +// Update window theme (light/dark) +// +static void updateTheme(HWND hWnd) +{ + if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) + { + BOOL value = _glfw.win32.uxtheme.ShouldAppsUseDarkMode() & 0x1; + DwmSetWindowAttribute(hWnd, + DWMWA_USE_IMMERSIVE_DARK_MODE, + &value, + sizeof(value)); + } +} + // Returns the window style for the specified window // static DWORD getWindowStyle(const _GLFWwindow* window) @@ -1146,6 +1165,13 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l return 0; } + case WM_THEMECHANGED: + case WM_SETTINGCHANGE: + { + updateTheme(window->win32.handle); + break; + } + case WM_GETDPISCALEDSIZE: { if (window->win32.scaleToMonitor) @@ -1436,6 +1462,8 @@ static int createNativeWindow(_GLFWwindow* window, _glfwGetWindowSizeWin32(window, &window->win32.width, &window->win32.height); + updateTheme(window->win32.handle); + return GLFW_TRUE; }