f489e3054a
* fix: support 'dark' theme before creating windows.
180 lines
5.5 KiB
C++
180 lines
5.5 KiB
C++
// 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
|