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