|
@@ -1,4 +1,4 @@
|
|
|
-// Copyright (c) 2020 Microsoft Inc. All rights reserved.
|
|
|
+// Copyright (c) 2022 Microsoft Inc. All rights reserved.
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
// found in the LICENSE-CHROMIUM file.
|
|
|
|
|
@@ -6,173 +6,57 @@
|
|
|
|
|
|
#include <dwmapi.h> // DwmSetWindowAttribute()
|
|
|
|
|
|
-#include "base/files/file_path.h"
|
|
|
-#include "base/scoped_native_library.h"
|
|
|
-#include "base/win/pe_image.h"
|
|
|
-#include "base/win/win_util.h"
|
|
|
#include "base/win/windows_version.h"
|
|
|
|
|
|
+// This flag works since Win10 20H1 but is not documented until Windows 11
|
|
|
+#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
|
|
|
+
|
|
|
// This namespace contains code originally from
|
|
|
-// https://github.com/ysc3839/win32-darkmode/
|
|
|
-// governed by the MIT license and (c) Richard Yu
|
|
|
+// https://github.com/microsoft/terminal
|
|
|
+// governed by the MIT license and (c) Microsoft Corporation.
|
|
|
namespace {
|
|
|
|
|
|
-// 1903 18362
|
|
|
-enum PreferredAppMode { Default, AllowDark, ForceDark, ForceLight, Max };
|
|
|
-
|
|
|
-bool g_darkModeSupported = false;
|
|
|
-bool g_darkModeEnabled = false;
|
|
|
-DWORD g_buildNumber = 0;
|
|
|
-
|
|
|
-enum WINDOWCOMPOSITIONATTRIB {
|
|
|
- WCA_USEDARKMODECOLORS = 26 // build 18875+
|
|
|
-};
|
|
|
-struct WINDOWCOMPOSITIONATTRIBDATA {
|
|
|
- WINDOWCOMPOSITIONATTRIB Attrib;
|
|
|
- PVOID pvData;
|
|
|
- SIZE_T cbData;
|
|
|
-};
|
|
|
-
|
|
|
-using fnSetWindowCompositionAttribute =
|
|
|
- BOOL(WINAPI*)(HWND hWnd, WINDOWCOMPOSITIONATTRIBDATA*);
|
|
|
-fnSetWindowCompositionAttribute _SetWindowCompositionAttribute = nullptr;
|
|
|
-
|
|
|
-bool IsHighContrast() {
|
|
|
- HIGHCONTRASTW highContrast = {sizeof(highContrast)};
|
|
|
- if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(highContrast),
|
|
|
- &highContrast, FALSE))
|
|
|
- return highContrast.dwFlags & HCF_HIGHCONTRASTON;
|
|
|
- return false;
|
|
|
-}
|
|
|
+// https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
|
|
|
+HRESULT TrySetWindowTheme(HWND hWnd, bool dark) {
|
|
|
+ const BOOL isDarkMode = dark;
|
|
|
+ HRESULT result = DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE,
|
|
|
+ &isDarkMode, sizeof(isDarkMode));
|
|
|
|
|
|
-void RefreshTitleBarThemeColor(HWND hWnd, bool dark) {
|
|
|
- LONG ldark = dark;
|
|
|
- if (g_buildNumber >= 20161) {
|
|
|
- // DWMA_USE_IMMERSIVE_DARK_MODE = 20
|
|
|
- DwmSetWindowAttribute(hWnd, 20, &ldark, sizeof dark);
|
|
|
- return;
|
|
|
- }
|
|
|
- if (g_buildNumber >= 18363) {
|
|
|
- auto data = WINDOWCOMPOSITIONATTRIBDATA{WCA_USEDARKMODECOLORS, &ldark,
|
|
|
- sizeof ldark};
|
|
|
- _SetWindowCompositionAttribute(hWnd, &data);
|
|
|
- return;
|
|
|
- }
|
|
|
- DwmSetWindowAttribute(hWnd, 0x13, &ldark, sizeof ldark);
|
|
|
-}
|
|
|
+ if (FAILED(result))
|
|
|
+ return result;
|
|
|
|
|
|
-void InitDarkMode() {
|
|
|
- // confirm that we're running on a version of Windows
|
|
|
- // where the Dark Mode API is known
|
|
|
auto* os_info = base::win::OSInfo::GetInstance();
|
|
|
- g_buildNumber = os_info->version_number().build;
|
|
|
auto const version = os_info->version();
|
|
|
- if ((version < base::win::Version::WIN10_RS5) ||
|
|
|
- (version > base::win::Version::WIN10_20H1)) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // load "SetWindowCompositionAttribute", used in RefreshTitleBarThemeColor()
|
|
|
- _SetWindowCompositionAttribute =
|
|
|
- reinterpret_cast<decltype(_SetWindowCompositionAttribute)>(
|
|
|
- base::win::GetUser32FunctionPointer("SetWindowCompositionAttribute"));
|
|
|
- if (_SetWindowCompositionAttribute == nullptr) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // load the dark mode functions from uxtheme.dll
|
|
|
- // * RefreshImmersiveColorPolicyState()
|
|
|
- // * ShouldAppsUseDarkMode()
|
|
|
- // * AllowDarkModeForApp()
|
|
|
- // * SetPreferredAppMode()
|
|
|
- // * AllowDarkModeForApp() (build < 18362)
|
|
|
- // * SetPreferredAppMode() (build >= 18362)
|
|
|
-
|
|
|
- base::NativeLibrary uxtheme =
|
|
|
- base::PinSystemLibrary(FILE_PATH_LITERAL("uxtheme.dll"));
|
|
|
- if (!uxtheme) {
|
|
|
- return;
|
|
|
- }
|
|
|
- auto ux_pei = base::win::PEImage(uxtheme);
|
|
|
- auto get_ux_proc_from_ordinal = [&ux_pei](int ordinal, auto* setme) {
|
|
|
- FARPROC proc = ux_pei.GetProcAddress(reinterpret_cast<LPCSTR>(ordinal));
|
|
|
- *setme = reinterpret_cast<decltype(*setme)>(proc);
|
|
|
- };
|
|
|
-
|
|
|
- // ordinal 104
|
|
|
- using fnRefreshImmersiveColorPolicyState = VOID(WINAPI*)();
|
|
|
- fnRefreshImmersiveColorPolicyState _RefreshImmersiveColorPolicyState = {};
|
|
|
- get_ux_proc_from_ordinal(104, &_RefreshImmersiveColorPolicyState);
|
|
|
-
|
|
|
- // ordinal 132
|
|
|
- using fnShouldAppsUseDarkMode = BOOL(WINAPI*)();
|
|
|
- fnShouldAppsUseDarkMode _ShouldAppsUseDarkMode = {};
|
|
|
- get_ux_proc_from_ordinal(132, &_ShouldAppsUseDarkMode);
|
|
|
-
|
|
|
- // ordinal 135, in 1809
|
|
|
- using fnAllowDarkModeForApp = BOOL(WINAPI*)(BOOL allow);
|
|
|
- fnAllowDarkModeForApp _AllowDarkModeForApp = {};
|
|
|
-
|
|
|
- // ordinal 135, in 1903
|
|
|
- typedef PreferredAppMode(WINAPI *
|
|
|
- fnSetPreferredAppMode)(PreferredAppMode appMode);
|
|
|
- fnSetPreferredAppMode _SetPreferredAppMode = {};
|
|
|
-
|
|
|
- if (g_buildNumber < 18362) {
|
|
|
- get_ux_proc_from_ordinal(135, &_AllowDarkModeForApp);
|
|
|
- } else {
|
|
|
- get_ux_proc_from_ordinal(135, &_SetPreferredAppMode);
|
|
|
- }
|
|
|
|
|
|
- // dark mode is supported iff we found the functions
|
|
|
- g_darkModeSupported = _RefreshImmersiveColorPolicyState &&
|
|
|
- _ShouldAppsUseDarkMode &&
|
|
|
- (_AllowDarkModeForApp || _SetPreferredAppMode);
|
|
|
- if (!g_darkModeSupported) {
|
|
|
- return;
|
|
|
+ // Toggle the nonclient area active state to force a redraw (Win10 workaround)
|
|
|
+ if (version < base::win::Version::WIN11) {
|
|
|
+ HWND activeWindow = GetActiveWindow();
|
|
|
+ SendMessage(hWnd, WM_NCACTIVATE, hWnd != activeWindow, 0);
|
|
|
+ SendMessage(hWnd, WM_NCACTIVATE, hWnd == activeWindow, 0);
|
|
|
}
|
|
|
|
|
|
- // initial setup: allow dark mode to be used
|
|
|
- if (_AllowDarkModeForApp) {
|
|
|
- _AllowDarkModeForApp(true);
|
|
|
- } else if (_SetPreferredAppMode) {
|
|
|
- _SetPreferredAppMode(AllowDark);
|
|
|
- }
|
|
|
- _RefreshImmersiveColorPolicyState();
|
|
|
-
|
|
|
- // check to see if dark mode is currently enabled
|
|
|
- g_darkModeEnabled = _ShouldAppsUseDarkMode() && !IsHighContrast();
|
|
|
+ return S_OK;
|
|
|
}
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
namespace electron {
|
|
|
|
|
|
-void EnsureInitialized() {
|
|
|
- static bool initialized = false;
|
|
|
- if (!initialized) {
|
|
|
- initialized = true;
|
|
|
- ::InitDarkMode();
|
|
|
- }
|
|
|
-}
|
|
|
+namespace win {
|
|
|
|
|
|
-bool IsDarkPreferred(ui::NativeTheme::ThemeSource theme_source) {
|
|
|
- switch (theme_source) {
|
|
|
- case ui::NativeTheme::ThemeSource::kForcedLight:
|
|
|
- return false;
|
|
|
- case ui::NativeTheme::ThemeSource::kForcedDark:
|
|
|
- return g_darkModeSupported;
|
|
|
- case ui::NativeTheme::ThemeSource::kSystem:
|
|
|
- return g_darkModeEnabled;
|
|
|
- }
|
|
|
+bool IsDarkModeSupported() {
|
|
|
+ auto* os_info = base::win::OSInfo::GetInstance();
|
|
|
+ auto const version = os_info->version();
|
|
|
+
|
|
|
+ return version >= base::win::Version::WIN10_20H1;
|
|
|
}
|
|
|
|
|
|
-namespace win {
|
|
|
+void SetDarkModeForWindow(HWND hWnd) {
|
|
|
+ ui::NativeTheme* theme = ui::NativeTheme::GetInstanceForNativeUi();
|
|
|
+ bool dark =
|
|
|
+ theme->ShouldUseDarkColors() && !theme->UserHasContrastPreference();
|
|
|
|
|
|
-void SetDarkModeForWindow(HWND hWnd,
|
|
|
- ui::NativeTheme::ThemeSource theme_source) {
|
|
|
- EnsureInitialized();
|
|
|
- RefreshTitleBarThemeColor(hWnd, IsDarkPreferred(theme_source));
|
|
|
+ TrySetWindowTheme(hWnd, dark);
|
|
|
}
|
|
|
|
|
|
} // namespace win
|