dark_mode.cc 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // Copyright (c) 2020 Microsoft Inc. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE-CHROMIUM file.
  4. #include "shell/browser/win/dark_mode.h"
  5. #include <dwmapi.h> // DwmSetWindowAttribute()
  6. #include "base/files/file_path.h"
  7. #include "base/scoped_native_library.h"
  8. #include "base/win/pe_image.h"
  9. #include "base/win/win_util.h"
  10. #include "base/win/windows_version.h"
  11. // This namespace contains code originally from
  12. // https://github.com/ysc3839/win32-darkmode/
  13. // governed by the MIT license and (c) Richard Yu
  14. namespace {
  15. // 1903 18362
  16. enum PreferredAppMode { Default, AllowDark, ForceDark, ForceLight, Max };
  17. bool g_darkModeSupported = false;
  18. bool g_darkModeEnabled = false;
  19. DWORD g_buildNumber = 0;
  20. enum WINDOWCOMPOSITIONATTRIB {
  21. WCA_USEDARKMODECOLORS = 26 // build 18875+
  22. };
  23. struct WINDOWCOMPOSITIONATTRIBDATA {
  24. WINDOWCOMPOSITIONATTRIB Attrib;
  25. PVOID pvData;
  26. SIZE_T cbData;
  27. };
  28. using fnSetWindowCompositionAttribute =
  29. BOOL(WINAPI*)(HWND hWnd, WINDOWCOMPOSITIONATTRIBDATA*);
  30. fnSetWindowCompositionAttribute _SetWindowCompositionAttribute = nullptr;
  31. bool IsHighContrast() {
  32. HIGHCONTRASTW highContrast = {sizeof(highContrast)};
  33. if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(highContrast),
  34. &highContrast, FALSE))
  35. return highContrast.dwFlags & HCF_HIGHCONTRASTON;
  36. return false;
  37. }
  38. void RefreshTitleBarThemeColor(HWND hWnd, bool dark) {
  39. LONG ldark = dark;
  40. if (g_buildNumber >= 20161) {
  41. // DWMA_USE_IMMERSIVE_DARK_MODE = 20
  42. DwmSetWindowAttribute(hWnd, 20, &ldark, sizeof dark);
  43. return;
  44. }
  45. if (g_buildNumber >= 18363) {
  46. auto data = WINDOWCOMPOSITIONATTRIBDATA{WCA_USEDARKMODECOLORS, &ldark,
  47. sizeof ldark};
  48. _SetWindowCompositionAttribute(hWnd, &data);
  49. return;
  50. }
  51. DwmSetWindowAttribute(hWnd, 0x13, &ldark, sizeof ldark);
  52. }
  53. void InitDarkMode() {
  54. // confirm that we're running on a version of Windows
  55. // where the Dark Mode API is known
  56. auto* os_info = base::win::OSInfo::GetInstance();
  57. g_buildNumber = os_info->version_number().build;
  58. auto const version = os_info->version();
  59. if ((version < base::win::Version::WIN10_RS5) ||
  60. (version > base::win::Version::WIN10_20H1)) {
  61. return;
  62. }
  63. // load "SetWindowCompositionAttribute", used in RefreshTitleBarThemeColor()
  64. _SetWindowCompositionAttribute =
  65. reinterpret_cast<decltype(_SetWindowCompositionAttribute)>(
  66. base::win::GetUser32FunctionPointer("SetWindowCompositionAttribute"));
  67. if (_SetWindowCompositionAttribute == nullptr) {
  68. return;
  69. }
  70. // load the dark mode functions from uxtheme.dll
  71. // * RefreshImmersiveColorPolicyState()
  72. // * ShouldAppsUseDarkMode()
  73. // * AllowDarkModeForApp()
  74. // * SetPreferredAppMode()
  75. // * AllowDarkModeForApp() (build < 18362)
  76. // * SetPreferredAppMode() (build >= 18362)
  77. base::NativeLibrary uxtheme =
  78. base::PinSystemLibrary(FILE_PATH_LITERAL("uxtheme.dll"));
  79. if (!uxtheme) {
  80. return;
  81. }
  82. auto ux_pei = base::win::PEImage(uxtheme);
  83. auto get_ux_proc_from_ordinal = [&ux_pei](int ordinal, auto* setme) {
  84. FARPROC proc = ux_pei.GetProcAddress(reinterpret_cast<LPCSTR>(ordinal));
  85. *setme = reinterpret_cast<decltype(*setme)>(proc);
  86. };
  87. // ordinal 104
  88. using fnRefreshImmersiveColorPolicyState = VOID(WINAPI*)();
  89. fnRefreshImmersiveColorPolicyState _RefreshImmersiveColorPolicyState = {};
  90. get_ux_proc_from_ordinal(104, &_RefreshImmersiveColorPolicyState);
  91. // ordinal 132
  92. using fnShouldAppsUseDarkMode = BOOL(WINAPI*)();
  93. fnShouldAppsUseDarkMode _ShouldAppsUseDarkMode = {};
  94. get_ux_proc_from_ordinal(132, &_ShouldAppsUseDarkMode);
  95. // ordinal 135, in 1809
  96. using fnAllowDarkModeForApp = BOOL(WINAPI*)(BOOL allow);
  97. fnAllowDarkModeForApp _AllowDarkModeForApp = {};
  98. // ordinal 135, in 1903
  99. typedef PreferredAppMode(WINAPI *
  100. fnSetPreferredAppMode)(PreferredAppMode appMode);
  101. fnSetPreferredAppMode _SetPreferredAppMode = {};
  102. if (g_buildNumber < 18362) {
  103. get_ux_proc_from_ordinal(135, &_AllowDarkModeForApp);
  104. } else {
  105. get_ux_proc_from_ordinal(135, &_SetPreferredAppMode);
  106. }
  107. // dark mode is supported iff we found the functions
  108. g_darkModeSupported = _RefreshImmersiveColorPolicyState &&
  109. _ShouldAppsUseDarkMode &&
  110. (_AllowDarkModeForApp || _SetPreferredAppMode);
  111. if (!g_darkModeSupported) {
  112. return;
  113. }
  114. // initial setup: allow dark mode to be used
  115. if (_AllowDarkModeForApp) {
  116. _AllowDarkModeForApp(true);
  117. } else if (_SetPreferredAppMode) {
  118. _SetPreferredAppMode(AllowDark);
  119. }
  120. _RefreshImmersiveColorPolicyState();
  121. // check to see if dark mode is currently enabled
  122. g_darkModeEnabled = _ShouldAppsUseDarkMode() && !IsHighContrast();
  123. }
  124. } // namespace
  125. namespace electron {
  126. void EnsureInitialized() {
  127. static bool initialized = false;
  128. if (!initialized) {
  129. initialized = true;
  130. ::InitDarkMode();
  131. }
  132. }
  133. bool IsDarkPreferred(ui::NativeTheme::ThemeSource theme_source) {
  134. switch (theme_source) {
  135. case ui::NativeTheme::ThemeSource::kForcedLight:
  136. return false;
  137. case ui::NativeTheme::ThemeSource::kForcedDark:
  138. return g_darkModeSupported;
  139. case ui::NativeTheme::ThemeSource::kSystem:
  140. return g_darkModeEnabled;
  141. }
  142. }
  143. namespace win {
  144. void SetDarkModeForWindow(HWND hWnd,
  145. ui::NativeTheme::ThemeSource theme_source) {
  146. EnsureInitialized();
  147. RefreshTitleBarThemeColor(hWnd, IsDarkPreferred(theme_source));
  148. }
  149. } // namespace win
  150. } // namespace electron