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