• 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/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