From 43ea8967a36af464e22a6a0178ad29b63d5bbf18 Mon Sep 17 00:00:00 2001 From: floppyhammer Date: Sat, 11 Feb 2023 10:56:43 +0800 Subject: [PATCH] Add win32 theming implementation --- src/win32_init.c | 29 +++++++++++++++++++++--- src/win32_platform.h | 21 +++++++++++++++++- src/win32_window.c | 52 ++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/src/win32_init.c b/src/win32_init.c index 3bba1f65..e110af85 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; } @@ -739,9 +753,18 @@ void _glfwTerminateWin32(void) _GLFWtheme* _glfwGetSystemDefaultThemeWin32(void) { - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); - return NULL; // TODO: implement + _GLFWtheme* theme = &_glfw.theme; + + theme->variation = GLFW_THEME_LIGHT; + theme->flags = 0; + + if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) { + if (_glfw.win32.uxtheme.ShouldAppsUseDarkMode() & 0x1) { + theme->variation = GLFW_THEME_DARK; + } + } + + return theme; } #endif // _GLFW_WIN32 - diff --git a/src/win32_platform.h b/src/win32_platform.h index fd7624f0..b2851273 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,11 @@ 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 +495,7 @@ typedef struct _GLFWlibraryWin32 PFN_DwmFlush Flush; PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow; PFN_DwmGetColorizationColor GetColorizationColor; + PFN_DwmSetWindowAttribute SetWindowAttribute; } dwmapi; struct { @@ -499,6 +508,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 0a73f36b..819dbc90 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -37,6 +37,21 @@ #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 + +static void applySystemTheme(HWND handle) { + if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) { + GLFWbool value = _glfw.win32.uxtheme.ShouldAppsUseDarkMode() & 0x1; + DwmSetWindowAttribute(handle, + DWMWA_USE_IMMERSIVE_DARK_MODE, + &value, + sizeof(value)); + } +} + // Returns the window style for the specified window // static DWORD getWindowStyle(const _GLFWwindow* window) @@ -1146,6 +1161,13 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l return 0; } + case WM_THEMECHANGED: + case WM_SETTINGCHANGE: { + if (window->theme.variation == GLFW_THEME_DEFAULT) { + applySystemTheme(window->win32.handle); + } + } break; + case WM_GETDPISCALEDSIZE: { if (window->win32.scaleToMonitor) @@ -1436,6 +1458,10 @@ static int createNativeWindow(_GLFWwindow* window, _glfwGetWindowSizeWin32(window, &window->win32.width, &window->win32.height); + if (window->theme.variation == GLFW_THEME_DEFAULT) { + applySystemTheme(window->win32.handle); + } + return GLFW_TRUE; } @@ -2375,13 +2401,32 @@ const char* _glfwGetClipboardStringWin32(void) void _glfwSetThemeWin32(_GLFWwindow* window, _GLFWtheme* theme) { - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); + if (!theme || theme->variation == GLFW_THEME_DEFAULT) + { + applySystemTheme(window->win32.handle); + return; + } + + GLFWbool darkMode = theme->variation == GLFW_THEME_DARK; + + DwmSetWindowAttribute(window->win32.handle, + DWMWA_USE_IMMERSIVE_DARK_MODE, + &darkMode, + sizeof(darkMode)); } _GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window) { - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); - return NULL; // TODO: implement + _GLFWtheme* theme = &window->theme; + + theme->variation = GLFW_THEME_LIGHT; + theme->flags = 0; + + if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) { + theme->variation = GLFW_THEME_DARK; + } + + return theme; } EGLenum _glfwGetEGLPlatformWin32(EGLint** attribs) @@ -2512,4 +2557,3 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle) } #endif // _GLFW_WIN32 -