From 43ea8967a36af464e22a6a0178ad29b63d5bbf18 Mon Sep 17 00:00:00 2001 From: floppyhammer Date: Sat, 11 Feb 2023 10:56:43 +0800 Subject: [PATCH 1/4] 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 - From adc9f9d67641aad4403c7d427a31d5dc72967ee5 Mon Sep 17 00:00:00 2001 From: floppyhammer Date: Sun, 19 Feb 2023 22:23:35 +0800 Subject: [PATCH 2/4] Free uxtheme properly --- src/win32_init.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/win32_init.c b/src/win32_init.c index e110af85..2a91bd83 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -207,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 From c6b0894c577a49b0f9f958cb76f034fda2a4e33f Mon Sep 17 00:00:00 2001 From: floppyhammer Date: Sun, 19 Feb 2023 22:29:53 +0800 Subject: [PATCH 3/4] Code format --- src/win32_platform.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/win32_platform.h b/src/win32_platform.h index b2851273..f60c70e4 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -370,7 +370,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)( #define GLFW_WGL_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl; typedef BOOL (WINAPI * ShouldAppsUseDarkModePtr)(); -typedef DWORD(WINAPI * GetImmersiveColorFromColorSetExPtr)(UINT,UINT,BOOL,UINT); +typedef DWORD (WINAPI * GetImmersiveColorFromColorSetExPtr)(UINT,UINT,BOOL,UINT); typedef int (WINAPI * GetImmersiveColorTypeFromNamePtr)(const WCHAR*); typedef int (WINAPI * GetImmersiveUserColorSetPreferencePtr)(BOOL,BOOL); From 14d3920f6a7b199e751cd1bad57499d8e3a96473 Mon Sep 17 00:00:00 2001 From: floppyhammer Date: Wed, 22 Feb 2023 14:31:48 +0800 Subject: [PATCH 4/4] Add a function to get the system accent color --- src/win32_window.c | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/win32_window.c b/src/win32_window.c index 819dbc90..fcc822c3 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -42,8 +42,12 @@ #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 #endif -static void applySystemTheme(HWND handle) { - if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) { +// Apply the system default theme +// +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, @@ -52,6 +56,27 @@ static void applySystemTheme(HWND handle) { } } +static void getAccentColor(float color[4]) +{ + if (!_glfw.win32.uxtheme.uxThemeAvailable) + { + return; + } + + UINT dwImmersiveColorType = _glfw.win32.uxtheme.GetImmersiveColorTypeFromName(L"ImmersiveSystemAccent"); + UINT dwImmersiveColorSet = _glfw.win32.uxtheme.GetImmersiveUserColorSetPreference(FALSE, FALSE); + + UINT rgba = _glfw.win32.uxtheme.GetImmersiveColorFromColorSetEx(dwImmersiveColorSet, + dwImmersiveColorType, + FALSE, + 0); + + color[0] = (0xFF & rgba); + color[1] = ((0xFF00 & rgba) >> 8) ; + color[2] = ((0xFF0000 & rgba) >> 16); + color[3] = ((0xFF000000 & rgba) >> 24); +} + // Returns the window style for the specified window // static DWORD getWindowStyle(const _GLFWwindow* window) @@ -1458,9 +1483,8 @@ static int createNativeWindow(_GLFWwindow* window, _glfwGetWindowSizeWin32(window, &window->win32.width, &window->win32.height); - if (window->theme.variation == GLFW_THEME_DEFAULT) { - applySystemTheme(window->win32.handle); - } + // Use the system default when creating a window + applySystemTheme(window->win32.handle); return GLFW_TRUE; } @@ -2422,10 +2446,14 @@ _GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window) theme->variation = GLFW_THEME_LIGHT; theme->flags = 0; - if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) { + if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) + { theme->variation = GLFW_THEME_DARK; } + memset(theme->color, 0, sizeof(float) * 4); + getAccentColor(theme->color); + return theme; }