From a6c9b038987ae6ee5168baeb62923debaff16a63 Mon Sep 17 00:00:00 2001 From: mlaurencin Date: Tue, 5 Apr 2022 10:28:45 -0700 Subject: [PATCH 01/12] feat: add immersive dark mode --- BUILD.gn | 13 +- build/fuses/fuses.json5 | 3 +- buildflags/BUILD.gn | 1 - buildflags/buildflags.gni | 3 - filenames.gni | 2 + .../electron_desktop_window_tree_host_win.cc | 24 +-- .../electron_desktop_window_tree_host_win.h | 7 +- shell/browser/win/dark_mode.cc | 179 +++--------------- shell/browser/win/dark_mode.h | 4 +- shell/common/api/features.cc | 7 +- typings/internal-ambient.d.ts | 2 +- 11 files changed, 59 insertions(+), 186 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 60ab4cac8106c..2ce3f1887718f 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -596,7 +596,10 @@ source_set("electron_lib") { ] } if (is_win) { - libs += [ "dwmapi.lib" ] + libs += [ + "dwmapi.lib", + "uxtheme.lib", + ] deps += [ "//components/crash/core/app:crash_export_thunks", "//ui/native_theme:native_theme_browser", @@ -726,14 +729,6 @@ source_set("electron_lib") { sources += get_target_outputs(":electron_fuses") - if (is_win && enable_win_dark_mode_window_ui) { - sources += [ - "shell/browser/win/dark_mode.cc", - "shell/browser/win/dark_mode.h", - ] - libs += [ "uxtheme.lib" ] - } - if (allow_runtime_configurable_key_storage) { defines += [ "ALLOW_RUNTIME_CONFIGURABLE_KEY_STORAGE" ] } diff --git a/build/fuses/fuses.json5 b/build/fuses/fuses.json5 index f4984aa2a17b4..3703fb00a4d9e 100644 --- a/build/fuses/fuses.json5 +++ b/build/fuses/fuses.json5 @@ -7,5 +7,6 @@ "node_options": "1", "node_cli_inspect": "1", "embedded_asar_integrity_validation": "0", - "only_load_app_from_asar": "0" + "only_load_app_from_asar": "0", + "windows_immersive_dark_mode": "1" } diff --git a/buildflags/BUILD.gn b/buildflags/BUILD.gn index 9cbb8227f5f52..a37d752dba360 100644 --- a/buildflags/BUILD.gn +++ b/buildflags/BUILD.gn @@ -19,7 +19,6 @@ buildflag_header("buildflags") { "ENABLE_ELECTRON_EXTENSIONS=$enable_electron_extensions", "ENABLE_BUILTIN_SPELLCHECKER=$enable_builtin_spellchecker", "ENABLE_PICTURE_IN_PICTURE=$enable_picture_in_picture", - "ENABLE_WIN_DARK_MODE_WINDOW_UI=$enable_win_dark_mode_window_ui", "OVERRIDE_LOCATION_PROVIDER=$enable_fake_location_provider", ] } diff --git a/buildflags/buildflags.gni b/buildflags/buildflags.gni index 5adc739ef7bb1..6a6a7d59fefe3 100644 --- a/buildflags/buildflags.gni +++ b/buildflags/buildflags.gni @@ -31,7 +31,4 @@ declare_args() { # Enable Spellchecker support enable_builtin_spellchecker = true - - # Undocumented Windows dark mode API - enable_win_dark_mode_window_ui = false } diff --git a/filenames.gni b/filenames.gni index f7c6ce5dff438..6b75da667a0c9 100644 --- a/filenames.gni +++ b/filenames.gni @@ -105,6 +105,8 @@ filenames = { "shell/browser/ui/win/notify_icon.h", "shell/browser/ui/win/taskbar_host.cc", "shell/browser/ui/win/taskbar_host.h", + "shell/browser/win/dark_mode.cc", + "shell/browser/win/dark_mode.h", "shell/browser/win/scoped_hstring.cc", "shell/browser/win/scoped_hstring.h", "shell/common/api/electron_api_native_image_win.cc", diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc index 696798d9111aa..d130d13c4d3c7 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc @@ -7,13 +7,10 @@ #include "base/win/windows_version.h" #include "electron/buildflags/buildflags.h" #include "shell/browser/ui/views/win_frame_view.h" +#include "shell/browser/win/dark_mode.h" #include "ui/base/win/hwnd_metrics.h" #include "ui/base/win/shell.h" -#if BUILDFLAG(ENABLE_WIN_DARK_MODE_WINDOW_UI) -#include "shell/browser/win/dark_mode.h" -#endif - namespace electron { ElectronDesktopWindowTreeHostWin::ElectronDesktopWindowTreeHostWin( @@ -29,14 +26,14 @@ bool ElectronDesktopWindowTreeHostWin::PreHandleMSG(UINT message, WPARAM w_param, LPARAM l_param, LRESULT* result) { -#if BUILDFLAG(ENABLE_WIN_DARK_MODE_WINDOW_UI) - if (message == WM_NCCREATE) { - HWND const hwnd = GetAcceleratedWidget(); - auto const theme_source = - ui::NativeTheme::GetInstanceForNativeUi()->theme_source(); - win::SetDarkModeForWindow(hwnd, theme_source); + if (electron::fuses::IsWindowsImmersiveDarkModeEnabled() && + message == WM_NCCREATE) { + win::SetDarkModeForWindow(GetAcceleratedWidget()); + ui::NativeTheme::GetInstanceForNativeUi()->AddObserver(this); + } else if (electron::fuses::IsWindowsImmersiveDarkModeEnabled() && + message == WM_DESTROY) { + ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this); } -#endif return native_window_view_->PreHandleMSG(message, w_param, l_param, result); } @@ -99,4 +96,9 @@ bool ElectronDesktopWindowTreeHostWin::GetClientAreaInsets( return false; } +void ElectronDesktopWindowTreeHostWin::OnNativeThemeUpdated( + ui::NativeTheme* observed_theme) { + win::SetDarkModeForWindow(GetAcceleratedWidget()); +} + } // namespace electron diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.h b/shell/browser/ui/win/electron_desktop_window_tree_host_win.h index 3fd831aae1ed0..1b53f6f8e5313 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.h +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.h @@ -12,8 +12,8 @@ namespace electron { -class ElectronDesktopWindowTreeHostWin - : public views::DesktopWindowTreeHostWin { +class ElectronDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin, + public ::ui::NativeThemeObserver { public: ElectronDesktopWindowTreeHostWin( NativeWindowViews* native_window_view, @@ -37,6 +37,9 @@ class ElectronDesktopWindowTreeHostWin bool GetClientAreaInsets(gfx::Insets* insets, HMONITOR monitor) const override; + // ui::NativeThemeObserver: + void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override; + private: NativeWindowViews* native_window_view_; // weak ref }; diff --git a/shell/browser/win/dark_mode.cc b/shell/browser/win/dark_mode.cc index c85bf18786c20..5e7d8c09a453a 100644 --- a/shell/browser/win/dark_mode.cc +++ b/shell/browser/win/dark_mode.cc @@ -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,46 @@ #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" +#define DARK_MODE_STRING_NAME L"DarkMode_Explorer" +#define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19 +#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 }; +// Use private Windows API to set window theme. +HRESULT TrySetWindowTheme(HWND hWnd, bool dark) { + HRESULT result = + SetWindowTheme(hWnd, dark ? DARK_MODE_STRING_NAME : L"", nullptr); + if (FAILED(result)) + return result; -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; + const BOOL isDarkMode = dark; + if (FAILED(DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, + &isDarkMode, sizeof(isDarkMode)))) { + result = + DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, + &isDarkMode, sizeof(isDarkMode)); + if (FAILED(result)) + return result; } - // 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(); - } -} - -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)); +void SetDarkModeForWindow(HWND hWnd) { + ui::NativeTheme* theme = ui::NativeTheme::GetInstanceForNativeUi(); + bool dark = + theme->ShouldUseDarkColors() && !theme->UserHasContrastPreference(); + TrySetWindowTheme(hWnd, dark); } } // namespace win diff --git a/shell/browser/win/dark_mode.h b/shell/browser/win/dark_mode.h index c6d14f9bfa7e7..6932f695389ff 100644 --- a/shell/browser/win/dark_mode.h +++ b/shell/browser/win/dark_mode.h @@ -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. @@ -19,7 +19,7 @@ namespace electron { namespace win { -void SetDarkModeForWindow(HWND hWnd, ui::NativeTheme::ThemeSource theme_source); +void SetDarkModeForWindow(HWND hWnd); } // namespace win diff --git a/shell/common/api/features.cc b/shell/common/api/features.cc index 92208cfba036d..3b1e5bc6b72c5 100644 --- a/shell/common/api/features.cc +++ b/shell/common/api/features.cc @@ -54,8 +54,8 @@ bool IsPictureInPictureEnabled() { return BUILDFLAG(ENABLE_PICTURE_IN_PICTURE); } -bool IsWinDarkModeWindowUiEnabled() { - return BUILDFLAG(ENABLE_WIN_DARK_MODE_WINDOW_UI); +bool IsWindowsImmersiveDarkModeEnabled() { + return electron::fuses::IsWindowsImmersiveDarkModeEnabled() } bool IsComponentBuild() { @@ -84,7 +84,8 @@ void Initialize(v8::Local exports, dict.SetMethod("isPictureInPictureEnabled", &IsPictureInPictureEnabled); dict.SetMethod("isComponentBuild", &IsComponentBuild); dict.SetMethod("isExtensionsEnabled", &IsExtensionsEnabled); - dict.SetMethod("isWinDarkModeWindowUiEnabled", &IsWinDarkModeWindowUiEnabled); + dict.SetMethod("isWindowsImmersiveDarkModeEnabled", + &IsWindowsImmersiveDarkModeEnabled); } } // namespace diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts index 0eb2f3d493a61..e4e976e4913f6 100644 --- a/typings/internal-ambient.d.ts +++ b/typings/internal-ambient.d.ts @@ -27,7 +27,7 @@ declare namespace NodeJS { isPictureInPictureEnabled(): boolean; isExtensionsEnabled(): boolean; isComponentBuild(): boolean; - isWinDarkModeWindowUiEnabled(): boolean; + isWindowsImmersiveDarkModeEnabled(): boolean; } interface IpcRendererBinding { From d89374e4540dab8eb3b8d20a1861edae549989fe Mon Sep 17 00:00:00 2001 From: mlaurencin Date: Tue, 5 Apr 2022 14:47:17 -0700 Subject: [PATCH 02/12] fix syntax and add header --- shell/browser/ui/win/electron_desktop_window_tree_host_win.cc | 1 + shell/common/api/features.cc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc index d130d13c4d3c7..f922218f535e6 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc @@ -6,6 +6,7 @@ #include "base/win/windows_version.h" #include "electron/buildflags/buildflags.h" +#include "electron/fuses.h" #include "shell/browser/ui/views/win_frame_view.h" #include "shell/browser/win/dark_mode.h" #include "ui/base/win/hwnd_metrics.h" diff --git a/shell/common/api/features.cc b/shell/common/api/features.cc index 3b1e5bc6b72c5..a30ec2b254e1a 100644 --- a/shell/common/api/features.cc +++ b/shell/common/api/features.cc @@ -55,7 +55,7 @@ bool IsPictureInPictureEnabled() { } bool IsWindowsImmersiveDarkModeEnabled() { - return electron::fuses::IsWindowsImmersiveDarkModeEnabled() + return electron::fuses::IsWindowsImmersiveDarkModeEnabled(); } bool IsComponentBuild() { From 71a1580c76279efb30c03f645c84e763f58d1abd Mon Sep 17 00:00:00 2001 From: Micha Hanselmann Date: Wed, 6 Apr 2022 12:09:32 +0200 Subject: [PATCH 03/12] add me From 57ec41e24e190ced2c62032d27a0bbc61b1c8c3c Mon Sep 17 00:00:00 2001 From: Michaela Laurencin <35157522+mlaurencin@users.noreply.github.com> Date: Wed, 6 Apr 2022 11:02:27 -0700 Subject: [PATCH 04/12] Update fuses.json5 --- build/fuses/fuses.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/fuses/fuses.json5 b/build/fuses/fuses.json5 index 3703fb00a4d9e..04c4c29bd9b81 100644 --- a/build/fuses/fuses.json5 +++ b/build/fuses/fuses.json5 @@ -8,5 +8,5 @@ "node_cli_inspect": "1", "embedded_asar_integrity_validation": "0", "only_load_app_from_asar": "0", - "windows_immersive_dark_mode": "1" + "windows_immersive_dark_mode": "0" } From e326a81ccb57ebe1b4965a4030f039f5ab3b44d8 Mon Sep 17 00:00:00 2001 From: David Sanders Date: Wed, 6 Apr 2022 22:56:30 -0700 Subject: [PATCH 05/12] fix: redraw title bar on dark mode change --- shell/browser/win/dark_mode.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shell/browser/win/dark_mode.cc b/shell/browser/win/dark_mode.cc index 5e7d8c09a453a..e1e74c492757f 100644 --- a/shell/browser/win/dark_mode.cc +++ b/shell/browser/win/dark_mode.cc @@ -32,6 +32,11 @@ HRESULT TrySetWindowTheme(HWND hWnd, bool dark) { return result; } + // Toggle the nonclient area active state to force a redraw + HWND activeWindow = GetActiveWindow(); + SendMessage(hWnd, WM_NCACTIVATE, hWnd != activeWindow, 0); + SendMessage(hWnd, WM_NCACTIVATE, hWnd == activeWindow, 0); + return S_OK; } From 325f559c5250768af9233b8c18340ec79f23251c Mon Sep 17 00:00:00 2001 From: David Sanders Date: Fri, 13 May 2022 17:17:28 -0700 Subject: [PATCH 06/12] chore: SetWindowTheme doesn't seem to be needed --- BUILD.gn | 5 +---- shell/browser/win/dark_mode.cc | 8 +------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 2ce3f1887718f..796f1fb27a090 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -596,10 +596,7 @@ source_set("electron_lib") { ] } if (is_win) { - libs += [ - "dwmapi.lib", - "uxtheme.lib", - ] + libs += [ "dwmapi.lib" ] deps += [ "//components/crash/core/app:crash_export_thunks", "//ui/native_theme:native_theme_browser", diff --git a/shell/browser/win/dark_mode.cc b/shell/browser/win/dark_mode.cc index e1e74c492757f..a3636433a2683 100644 --- a/shell/browser/win/dark_mode.cc +++ b/shell/browser/win/dark_mode.cc @@ -6,7 +6,6 @@ #include // DwmSetWindowAttribute() -#define DARK_MODE_STRING_NAME L"DarkMode_Explorer" #define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19 #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 @@ -17,15 +16,10 @@ namespace { // Use private Windows API to set window theme. HRESULT TrySetWindowTheme(HWND hWnd, bool dark) { - HRESULT result = - SetWindowTheme(hWnd, dark ? DARK_MODE_STRING_NAME : L"", nullptr); - if (FAILED(result)) - return result; - const BOOL isDarkMode = dark; if (FAILED(DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &isDarkMode, sizeof(isDarkMode)))) { - result = + HRESULT result = DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, &isDarkMode, sizeof(isDarkMode)); if (FAILED(result)) From 6c31615836938f9352259e2dbcdbf4a8d2d0bc8d Mon Sep 17 00:00:00 2001 From: David Sanders Date: Fri, 13 May 2022 18:24:18 -0700 Subject: [PATCH 07/12] chore: separate out Win 10 dark mode implementation --- build/fuses/fuses.json5 | 2 +- .../electron_desktop_window_tree_host_win.cc | 8 ++--- shell/browser/win/dark_mode.cc | 34 +++++++++++++++++-- shell/browser/win/dark_mode.h | 1 + shell/common/api/features.cc | 8 ++--- typings/internal-ambient.d.ts | 2 +- 6 files changed, 41 insertions(+), 14 deletions(-) diff --git a/build/fuses/fuses.json5 b/build/fuses/fuses.json5 index 04c4c29bd9b81..abaef6c6f555f 100644 --- a/build/fuses/fuses.json5 +++ b/build/fuses/fuses.json5 @@ -8,5 +8,5 @@ "node_cli_inspect": "1", "embedded_asar_integrity_validation": "0", "only_load_app_from_asar": "0", - "windows_immersive_dark_mode": "0" + "windows_10_immersive_dark_mode": "0" } diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc index f922218f535e6..d978ce64f59a6 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc @@ -6,7 +6,6 @@ #include "base/win/windows_version.h" #include "electron/buildflags/buildflags.h" -#include "electron/fuses.h" #include "shell/browser/ui/views/win_frame_view.h" #include "shell/browser/win/dark_mode.h" #include "ui/base/win/hwnd_metrics.h" @@ -27,12 +26,11 @@ bool ElectronDesktopWindowTreeHostWin::PreHandleMSG(UINT message, WPARAM w_param, LPARAM l_param, LRESULT* result) { - if (electron::fuses::IsWindowsImmersiveDarkModeEnabled() && - message == WM_NCCREATE) { + const bool dark_mode_supported = win::IsDarkModeSupported(); + if (dark_mode_supported && message == WM_NCCREATE) { win::SetDarkModeForWindow(GetAcceleratedWidget()); ui::NativeTheme::GetInstanceForNativeUi()->AddObserver(this); - } else if (electron::fuses::IsWindowsImmersiveDarkModeEnabled() && - message == WM_DESTROY) { + } else if (dark_mode_supported && message == WM_DESTROY) { ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this); } diff --git a/shell/browser/win/dark_mode.cc b/shell/browser/win/dark_mode.cc index a3636433a2683..ab06f4a03e730 100644 --- a/shell/browser/win/dark_mode.cc +++ b/shell/browser/win/dark_mode.cc @@ -6,6 +6,9 @@ #include // DwmSetWindowAttribute() +#include "base/win/windows_version.h" +#include "electron/fuses.h" + #define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19 #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 @@ -14,8 +17,8 @@ // governed by the MIT license and (c) Microsoft Corporation. namespace { -// Use private Windows API to set window theme. -HRESULT TrySetWindowTheme(HWND hWnd, bool dark) { +// Use private Windows API to set window theme on Windows 10 +HRESULT TrySetWindowThemeOnWin10(HWND hWnd, bool dark) { const BOOL isDarkMode = dark; if (FAILED(DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &isDarkMode, sizeof(isDarkMode)))) { @@ -34,17 +37,42 @@ HRESULT TrySetWindowTheme(HWND hWnd, bool dark) { return S_OK; } +HRESULT TrySetWindowTheme(HWND hWnd, bool dark) { + const BOOL isDarkMode = dark; + HRESULT result = DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, + &isDarkMode, sizeof(isDarkMode)); + + return FAILED(result) ? result : S_OK; +} + } // namespace namespace electron { namespace win { +bool IsDarkModeSupported() { + auto* os_info = base::win::OSInfo::GetInstance(); + auto const version = os_info->version(); + + return version >= base::win::Version::WIN11 || + (version >= base::win::Version::WIN10 && + electron::fuses::IsWindows10ImmersiveDarkModeEnabled()); +} + void SetDarkModeForWindow(HWND hWnd) { ui::NativeTheme* theme = ui::NativeTheme::GetInstanceForNativeUi(); bool dark = theme->ShouldUseDarkColors() && !theme->UserHasContrastPreference(); - TrySetWindowTheme(hWnd, dark); + + auto* os_info = base::win::OSInfo::GetInstance(); + auto const version = os_info->version(); + + if (version >= base::win::Version::WIN11) { + TrySetWindowTheme(hWnd, dark); + } else { + TrySetWindowThemeOnWin10(hWnd, dark); + } } } // namespace win diff --git a/shell/browser/win/dark_mode.h b/shell/browser/win/dark_mode.h index 6932f695389ff..12f6adb1dade9 100644 --- a/shell/browser/win/dark_mode.h +++ b/shell/browser/win/dark_mode.h @@ -19,6 +19,7 @@ namespace electron { namespace win { +bool IsDarkModeSupported(); void SetDarkModeForWindow(HWND hWnd); } // namespace win diff --git a/shell/common/api/features.cc b/shell/common/api/features.cc index a30ec2b254e1a..b07994ceac7ca 100644 --- a/shell/common/api/features.cc +++ b/shell/common/api/features.cc @@ -54,8 +54,8 @@ bool IsPictureInPictureEnabled() { return BUILDFLAG(ENABLE_PICTURE_IN_PICTURE); } -bool IsWindowsImmersiveDarkModeEnabled() { - return electron::fuses::IsWindowsImmersiveDarkModeEnabled(); +bool IsWindows10ImmersiveDarkModeEnabled() { + return electron::fuses::IsWindows10ImmersiveDarkModeEnabled(); } bool IsComponentBuild() { @@ -84,8 +84,8 @@ void Initialize(v8::Local exports, dict.SetMethod("isPictureInPictureEnabled", &IsPictureInPictureEnabled); dict.SetMethod("isComponentBuild", &IsComponentBuild); dict.SetMethod("isExtensionsEnabled", &IsExtensionsEnabled); - dict.SetMethod("isWindowsImmersiveDarkModeEnabled", - &IsWindowsImmersiveDarkModeEnabled); + dict.SetMethod("isWindows10ImmersiveDarkModeEnabled", + &IsWindows10ImmersiveDarkModeEnabled); } } // namespace diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts index e4e976e4913f6..133c2335ae546 100644 --- a/typings/internal-ambient.d.ts +++ b/typings/internal-ambient.d.ts @@ -27,7 +27,7 @@ declare namespace NodeJS { isPictureInPictureEnabled(): boolean; isExtensionsEnabled(): boolean; isComponentBuild(): boolean; - isWindowsImmersiveDarkModeEnabled(): boolean; + isWindows10ImmersiveDarkModeEnabled(): boolean; } interface IpcRendererBinding { From 902a9ae72e8caf525d91c57178fc81670baaa146 Mon Sep 17 00:00:00 2001 From: Micha Hanselmann Date: Tue, 17 May 2022 15:01:42 +0200 Subject: [PATCH 08/12] final touches --- shell/browser/win/dark_mode.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/shell/browser/win/dark_mode.cc b/shell/browser/win/dark_mode.cc index ab06f4a03e730..0b6031cc70f67 100644 --- a/shell/browser/win/dark_mode.cc +++ b/shell/browser/win/dark_mode.cc @@ -17,7 +17,7 @@ // governed by the MIT license and (c) Microsoft Corporation. namespace { -// Use private Windows API to set window theme on Windows 10 +// Use undocumented flags to set window theme on Windows 10 HRESULT TrySetWindowThemeOnWin10(HWND hWnd, bool dark) { const BOOL isDarkMode = dark; if (FAILED(DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, @@ -29,7 +29,7 @@ HRESULT TrySetWindowThemeOnWin10(HWND hWnd, bool dark) { return result; } - // Toggle the nonclient area active state to force a redraw + // Toggle the nonclient area active state to force a redraw (workaround for Windows 10) HWND activeWindow = GetActiveWindow(); SendMessage(hWnd, WM_NCACTIVATE, hWnd != activeWindow, 0); SendMessage(hWnd, WM_NCACTIVATE, hWnd == activeWindow, 0); @@ -37,12 +37,10 @@ HRESULT TrySetWindowThemeOnWin10(HWND hWnd, bool dark) { return S_OK; } +// Docs: 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)); - - return FAILED(result) ? result : S_OK; + return DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &isDarkMode, sizeof(isDarkMode)); } } // namespace From 25ac75db5dace5cb507aeb7189de9bf3672c34de Mon Sep 17 00:00:00 2001 From: Micha Hanselmann Date: Tue, 17 May 2022 15:25:14 +0200 Subject: [PATCH 09/12] final touches --- shell/browser/win/dark_mode.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/shell/browser/win/dark_mode.cc b/shell/browser/win/dark_mode.cc index 0b6031cc70f67..bc4a0192dea53 100644 --- a/shell/browser/win/dark_mode.cc +++ b/shell/browser/win/dark_mode.cc @@ -29,7 +29,7 @@ HRESULT TrySetWindowThemeOnWin10(HWND hWnd, bool dark) { return result; } - // Toggle the nonclient area active state to force a redraw (workaround for Windows 10) + // Toggle the nonclient area active state to force a redraw (Win10 workaround) HWND activeWindow = GetActiveWindow(); SendMessage(hWnd, WM_NCACTIVATE, hWnd != activeWindow, 0); SendMessage(hWnd, WM_NCACTIVATE, hWnd == activeWindow, 0); @@ -37,10 +37,11 @@ HRESULT TrySetWindowThemeOnWin10(HWND hWnd, bool dark) { return S_OK; } -// Docs: https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +// https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute HRESULT TrySetWindowTheme(HWND hWnd, bool dark) { const BOOL isDarkMode = dark; - return DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &isDarkMode, sizeof(isDarkMode)); + return DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &isDarkMode, + sizeof(isDarkMode)); } } // namespace From e7a2bf8953368b9319e2630e4df93cc5041c5a19 Mon Sep 17 00:00:00 2001 From: David Sanders Date: Mon, 6 Jun 2022 21:57:20 -0700 Subject: [PATCH 10/12] chore: limit Win 10 to >= 20H1 and drop fuse --- build/fuses/fuses.json5 | 3 +- shell/browser/win/dark_mode.cc | 51 ++++++++++++---------------------- shell/common/api/features.cc | 6 ---- 3 files changed, 19 insertions(+), 41 deletions(-) diff --git a/build/fuses/fuses.json5 b/build/fuses/fuses.json5 index abaef6c6f555f..f4984aa2a17b4 100644 --- a/build/fuses/fuses.json5 +++ b/build/fuses/fuses.json5 @@ -7,6 +7,5 @@ "node_options": "1", "node_cli_inspect": "1", "embedded_asar_integrity_validation": "0", - "only_load_app_from_asar": "0", - "windows_10_immersive_dark_mode": "0" + "only_load_app_from_asar": "0" } diff --git a/shell/browser/win/dark_mode.cc b/shell/browser/win/dark_mode.cc index bc4a0192dea53..6f91960a0eb2c 100644 --- a/shell/browser/win/dark_mode.cc +++ b/shell/browser/win/dark_mode.cc @@ -7,9 +7,8 @@ #include // DwmSetWindowAttribute() #include "base/win/windows_version.h" -#include "electron/fuses.h" -#define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19 +// 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 @@ -17,33 +16,28 @@ // governed by the MIT license and (c) Microsoft Corporation. namespace { -// Use undocumented flags to set window theme on Windows 10 -HRESULT TrySetWindowThemeOnWin10(HWND hWnd, bool dark) { +// https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +HRESULT TrySetWindowTheme(HWND hWnd, bool dark) { const BOOL isDarkMode = dark; - if (FAILED(DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, - &isDarkMode, sizeof(isDarkMode)))) { - HRESULT result = - DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, - &isDarkMode, sizeof(isDarkMode)); - if (FAILED(result)) - return result; - } + HRESULT result = DwmSetWindowAttribute( + hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &isDarkMode, sizeof(isDarkMode)); + + if (FAILED(result)) + return result; + + auto* os_info = base::win::OSInfo::GetInstance(); + auto const version = os_info->version(); // Toggle the nonclient area active state to force a redraw (Win10 workaround) - HWND activeWindow = GetActiveWindow(); - SendMessage(hWnd, WM_NCACTIVATE, hWnd != activeWindow, 0); - SendMessage(hWnd, WM_NCACTIVATE, hWnd == activeWindow, 0); + if (version < base::win::Version::WIN11) { + HWND activeWindow = GetActiveWindow(); + SendMessage(hWnd, WM_NCACTIVATE, hWnd != activeWindow, 0); + SendMessage(hWnd, WM_NCACTIVATE, hWnd == activeWindow, 0); + } return S_OK; } -// https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute -HRESULT TrySetWindowTheme(HWND hWnd, bool dark) { - const BOOL isDarkMode = dark; - return DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &isDarkMode, - sizeof(isDarkMode)); -} - } // namespace namespace electron { @@ -54,9 +48,7 @@ bool IsDarkModeSupported() { auto* os_info = base::win::OSInfo::GetInstance(); auto const version = os_info->version(); - return version >= base::win::Version::WIN11 || - (version >= base::win::Version::WIN10 && - electron::fuses::IsWindows10ImmersiveDarkModeEnabled()); + return version >= base::win::Version::WIN10_20H1; } void SetDarkModeForWindow(HWND hWnd) { @@ -64,14 +56,7 @@ void SetDarkModeForWindow(HWND hWnd) { bool dark = theme->ShouldUseDarkColors() && !theme->UserHasContrastPreference(); - auto* os_info = base::win::OSInfo::GetInstance(); - auto const version = os_info->version(); - - if (version >= base::win::Version::WIN11) { - TrySetWindowTheme(hWnd, dark); - } else { - TrySetWindowThemeOnWin10(hWnd, dark); - } + TrySetWindowTheme(hWnd, dark); } } // namespace win diff --git a/shell/common/api/features.cc b/shell/common/api/features.cc index b07994ceac7ca..48d733f16a031 100644 --- a/shell/common/api/features.cc +++ b/shell/common/api/features.cc @@ -54,10 +54,6 @@ bool IsPictureInPictureEnabled() { return BUILDFLAG(ENABLE_PICTURE_IN_PICTURE); } -bool IsWindows10ImmersiveDarkModeEnabled() { - return electron::fuses::IsWindows10ImmersiveDarkModeEnabled(); -} - bool IsComponentBuild() { #if defined(COMPONENT_BUILD) return true; @@ -84,8 +80,6 @@ void Initialize(v8::Local exports, dict.SetMethod("isPictureInPictureEnabled", &IsPictureInPictureEnabled); dict.SetMethod("isComponentBuild", &IsComponentBuild); dict.SetMethod("isExtensionsEnabled", &IsExtensionsEnabled); - dict.SetMethod("isWindows10ImmersiveDarkModeEnabled", - &IsWindows10ImmersiveDarkModeEnabled); } } // namespace From bf6844fd8ca2a70df13de65eb138a14a97e738c6 Mon Sep 17 00:00:00 2001 From: Micha Hanselmann Date: Fri, 10 Jun 2022 17:40:30 +0200 Subject: [PATCH 11/12] fix types --- typings/internal-ambient.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts index 133c2335ae546..2ac177b263154 100644 --- a/typings/internal-ambient.d.ts +++ b/typings/internal-ambient.d.ts @@ -27,7 +27,6 @@ declare namespace NodeJS { isPictureInPictureEnabled(): boolean; isExtensionsEnabled(): boolean; isComponentBuild(): boolean; - isWindows10ImmersiveDarkModeEnabled(): boolean; } interface IpcRendererBinding { From 84bc419464281fd5c76bd1dafcd4b521c2dbb5bc Mon Sep 17 00:00:00 2001 From: Micha Hanselmann Date: Fri, 10 Jun 2022 17:53:12 +0200 Subject: [PATCH 12/12] fix lint --- shell/browser/win/dark_mode.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/browser/win/dark_mode.cc b/shell/browser/win/dark_mode.cc index 6f91960a0eb2c..200ba8c1f13a5 100644 --- a/shell/browser/win/dark_mode.cc +++ b/shell/browser/win/dark_mode.cc @@ -19,8 +19,8 @@ namespace { // 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)); + HRESULT result = DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, + &isDarkMode, sizeof(isDarkMode)); if (FAILED(result)) return result;