// 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 // 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( 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(ordinal)); *setme = reinterpret_cast(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