|
@@ -0,0 +1,180 @@
|
|
|
+// Copyright (c) 2020 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.
|
|
|
+
|
|
|
+#include "shell/browser/win/dark_mode.h"
|
|
|
+
|
|
|
+#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 namespace contains code originally from
|
|
|
+// https://github.com/ysc3839/win32-darkmode/
|
|
|
+// governed by the MIT license and (c) Richard Yu
|
|
|
+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;
|
|
|
+}
|
|
|
+
|
|
|
+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);
|
|
|
+}
|
|
|
+
|
|
|
+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;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 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();
|
|
|
+}
|
|
|
+
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace electron {
|
|
|
+
|
|
|
+void EnsureInitialized() {
|
|
|
+ static bool initialized = false;
|
|
|
+ if (!initialized) {
|
|
|
+ initialized = true;
|
|
|
+ ::InitDarkMode();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+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;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+namespace win {
|
|
|
+
|
|
|
+void SetDarkModeForWindow(HWND hWnd,
|
|
|
+ ui::NativeTheme::ThemeSource theme_source) {
|
|
|
+ EnsureInitialized();
|
|
|
+ RefreshTitleBarThemeColor(hWnd, IsDarkPreferred(theme_source));
|
|
|
+}
|
|
|
+
|
|
|
+} // namespace win
|
|
|
+
|
|
|
+} // namespace electron
|