• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/win/dark_mode_support.h"
6 
7 #include <windows.h>
8 
9 #include "base/native_library.h"
10 #include "base/win/windows_version.h"
11 
12 namespace {
13 
14 // APIs for controlling how an app and window respond to system-level
15 // dark/light modes.
16 
17 // Available on Wwindows build base::win::Version::WIN10_19H1 and up.
18 enum class PreferredAppMode {
19   kDefault,
20   kAllowDark,
21   kForceDark,
22   kForceLight,
23   kMax
24 };
25 
26 // The following APIs and code was based on information from here:
27 // https://github.com/ysc3839/win32-darkmode
28 
29 // Only available on Windows build base::win::Version::WIN10_RS5.
30 // NOLINTNEXTLINE(readability/casting)
31 using UxThemeAllowDarkModeForAppFunc = bool(WINAPI*)(bool allow);
32 
33 // Available on Windows build base::win::Version::WIN10_19H1 and up.
34 using UxThemeSetPreferredAppModeFunc =
35     // NOLINTNEXTLINE(readability/casting)
36     PreferredAppMode(WINAPI*)(PreferredAppMode app_mode);
37 
38 // Available on Windows build base::win::Version::WIN10_RS5 and up.
39 // NOLINTNEXTLINE(readability/casting)
40 using UxThemeAllowDarkModeForWindowFunc = bool(WINAPI*)(HWND hwnd, bool allow);
41 
42 // The following two ordinals are mutually exclusive and represent a difference
43 // between base::win::Version::WIN10_RS5 and base::win::Version::WIN10_19H1.
44 constexpr WORD kUxThemeAllowDarkModeForAppOrdinal = 135;
45 constexpr WORD kUxThemeSetPreferredAppModeOrdinal = 135;
46 constexpr WORD kUxThemeAllowDarkModeForWindowOrdinal = 133;
47 
48 struct DarkModeSupport {
49   UxThemeAllowDarkModeForAppFunc allow_dark_mode_for_app = nullptr;
50   UxThemeSetPreferredAppModeFunc set_preferred_app_mode = nullptr;
51   UxThemeAllowDarkModeForWindowFunc allow_dark_mode_for_window = nullptr;
52 };
53 
GetDarkModeSupport()54 const DarkModeSupport& GetDarkModeSupport() {
55   static const DarkModeSupport dark_mode_support =
56       [] {
57         DarkModeSupport dark_mode_support;
58         auto* os_info = base::win::OSInfo::GetInstance();
59         // Dark mode only works on WIN10_RS5 and up.
60         if (os_info->version() >= base::win::Version::WIN10_RS5) {
61           base::NativeLibraryLoadError error;
62           HMODULE ux_theme_lib = base::PinSystemLibrary(L"uxtheme.dll", &error);
63           DCHECK(!error.code);
64           if (os_info->version() >= base::win::Version::WIN10_19H1) {
65             dark_mode_support.set_preferred_app_mode =
66                 reinterpret_cast<UxThemeSetPreferredAppModeFunc>(
67                     ::GetProcAddress(
68                         ux_theme_lib,
69                         MAKEINTRESOURCEA(kUxThemeSetPreferredAppModeOrdinal)));
70           } else {
71             dark_mode_support.allow_dark_mode_for_app =
72                 reinterpret_cast<UxThemeAllowDarkModeForAppFunc>(
73                     ::GetProcAddress(
74                         ux_theme_lib,
75                         MAKEINTRESOURCEA(kUxThemeAllowDarkModeForAppOrdinal)));
76           }
77           dark_mode_support.allow_dark_mode_for_window =
78               reinterpret_cast<UxThemeAllowDarkModeForWindowFunc>(
79                   ::GetProcAddress(
80                       ux_theme_lib,
81                       MAKEINTRESOURCEA(kUxThemeAllowDarkModeForWindowOrdinal)));
82         }
83         return dark_mode_support;
84       }();
85   return dark_mode_support;
86 }
87 
88 }  // namespace
89 
90 namespace base::win {
91 
IsDarkModeAvailable()92 bool IsDarkModeAvailable() {
93   auto& dark_mode_support = GetDarkModeSupport();
94   return (dark_mode_support.allow_dark_mode_for_app ||
95           dark_mode_support.set_preferred_app_mode) &&
96          dark_mode_support.allow_dark_mode_for_window;
97 }
98 
AllowDarkModeForApp(bool allow)99 void AllowDarkModeForApp(bool allow) {
100   if (!IsDarkModeAvailable())
101     return;
102   auto& dark_mode_support = GetDarkModeSupport();
103   if (dark_mode_support.set_preferred_app_mode) {
104     dark_mode_support.set_preferred_app_mode(
105         allow ? PreferredAppMode::kAllowDark : PreferredAppMode::kDefault);
106   } else {
107     dark_mode_support.allow_dark_mode_for_app(allow);
108   }
109 }
110 
AllowDarkModeForWindow(HWND hwnd,bool allow)111 bool AllowDarkModeForWindow(HWND hwnd, bool allow) {
112   if (!IsDarkModeAvailable())
113     return false;
114   return GetDarkModeSupport().allow_dark_mode_for_window(hwnd, allow);
115 }
116 
117 }  // namespace base::win
118