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