Skip to content

Commit 3dadc7a

Browse files
committed
feat: Windows深色模式
1 parent ea5f5a3 commit 3dadc7a

File tree

4 files changed

+149
-0
lines changed

4 files changed

+149
-0
lines changed

cmake/compile_definitions/windows.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ set(PLATFORM_TARGET_FILES
6767
"${CMAKE_SOURCE_DIR}/src/platform/windows/ftime_compat.cpp"
6868
"${CMAKE_SOURCE_DIR}/src/platform/windows/misc.h"
6969
"${CMAKE_SOURCE_DIR}/src/platform/windows/misc.cpp"
70+
"${CMAKE_SOURCE_DIR}/src/platform/windows/win_dark_mode.h"
71+
"${CMAKE_SOURCE_DIR}/src/platform/windows/win_dark_mode.cpp"
7072
"${CMAKE_SOURCE_DIR}/src/platform/windows/input.cpp"
7173
"${CMAKE_SOURCE_DIR}/src/platform/windows/dsu_server.h"
7274
"${CMAKE_SOURCE_DIR}/src/platform/windows/dsu_server.cpp"

src/main.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
#include "version.h"
2424
#include "video.h"
2525

26+
#ifdef _WIN32
27+
#include "platform/windows/win_dark_mode.h"
28+
#endif
29+
2630
extern "C" {
2731
#include "rswrapper.h"
2832
}
@@ -122,6 +126,10 @@ main(int argc, char *argv[]) {
122126
// by placing a user-writable directory in the system-wide PATH variable.
123127
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32);
124128

129+
// Enable dark mode for the entire process before creating any windows
130+
// This must be called early, before any windows or system tray icons are created
131+
win_dark_mode::enable_process_dark_mode();
132+
125133
// Set locale to UTF-8 instead of C locale
126134
setlocale(LC_ALL, ".UTF-8");
127135
#endif
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
* @file src/platform/windows/win_dark_mode.cpp
3+
* @brief Implementation of Windows dark mode support
4+
*/
5+
6+
#ifdef _WIN32
7+
8+
#include "win_dark_mode.h"
9+
10+
#include <dwmapi.h>
11+
#include <mutex>
12+
13+
namespace win_dark_mode {
14+
15+
// Undocumented PreferredAppMode enum from uxtheme.dll
16+
enum class PreferredAppMode {
17+
Default = 0, // Use system default (usually light)
18+
AllowDark = 1, // Allow dark mode (follow system setting)
19+
ForceDark = 2, // Force dark mode regardless of system setting
20+
ForceLight = 3, // Force light mode regardless of system setting
21+
Max = 4,
22+
};
23+
24+
// Function pointer types for undocumented uxtheme.dll APIs
25+
// Windows 10 1903+ uses SetPreferredAppMode (ordinal 135)
26+
// Windows 10 1809-1903 uses AllowDarkModeForApp (ordinal 135) - same ordinal, different signature
27+
// We use SetPreferredAppMode as it works on all modern Windows versions
28+
using SetPreferredAppModeFn = PreferredAppMode(WINAPI *)(PreferredAppMode);
29+
30+
// Global function pointer (initialized once)
31+
static SetPreferredAppModeFn g_SetPreferredAppMode = nullptr;
32+
static std::once_flag g_init_flag;
33+
34+
/**
35+
* @brief Initialize the dark mode API function pointers
36+
*
37+
* This function loads uxtheme.dll and retrieves the undocumented function pointers.
38+
* It only runs once and caches the results using std::call_once for thread safety.
39+
*/
40+
static void
41+
init_dark_mode_apis() {
42+
// Load uxtheme.dll
43+
HMODULE hUxTheme = LoadLibraryW(L"uxtheme.dll");
44+
if (!hUxTheme) {
45+
return;
46+
}
47+
48+
// Get SetPreferredAppMode (ordinal 135)
49+
// This works on Windows 10 1809+ (build 17763+)
50+
// On older versions, the function will exist but may not have the expected effect
51+
g_SetPreferredAppMode =
52+
reinterpret_cast<SetPreferredAppModeFn>(
53+
GetProcAddress(hUxTheme, MAKEINTRESOURCEA(135)));
54+
55+
// Note: We intentionally don't call FreeLibrary(hUxTheme) because we need
56+
// the function pointers to remain valid for the lifetime of the process
57+
}
58+
59+
void
60+
enable_process_dark_mode() {
61+
// Thread-safe one-time initialization
62+
std::call_once(g_init_flag, init_dark_mode_apis);
63+
64+
// Call the function if available
65+
if (g_SetPreferredAppMode) {
66+
// Use AllowDark to follow the system's dark/light mode preference
67+
g_SetPreferredAppMode(PreferredAppMode::AllowDark);
68+
}
69+
// If API is not available, dark mode is not supported on this Windows version
70+
// (Windows 10 < 1809 or earlier). We silently do nothing in this case.
71+
}
72+
73+
void
74+
apply_window_dark_title_bar(HWND hwnd, bool enable) {
75+
if (!hwnd) {
76+
return;
77+
}
78+
79+
// DWMWA_USE_IMMERSIVE_DARK_MODE is documented for Windows 11
80+
// but also works on Windows 10 20H1+
81+
constexpr DWORD DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
82+
83+
BOOL useDark = enable ? TRUE : FALSE;
84+
DwmSetWindowAttribute(
85+
hwnd,
86+
DWMWA_USE_IMMERSIVE_DARK_MODE,
87+
&useDark,
88+
sizeof(useDark));
89+
}
90+
91+
} // namespace win_dark_mode
92+
93+
#endif // _WIN32
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* @file src/platform/windows/win_dark_mode.h
3+
* @brief Windows dark mode support for the entire process
4+
*
5+
* This module provides process-wide dark mode support for Windows 10 1809+ and Windows 11.
6+
* It handles the undocumented Windows APIs for enabling dark mode for menus, dialogs, and windows.
7+
*
8+
* Note: This should be called early in program initialization, before creating any windows or menus.
9+
*/
10+
11+
#pragma once
12+
13+
#ifdef _WIN32
14+
15+
#include <windows.h>
16+
17+
namespace win_dark_mode {
18+
19+
/**
20+
* @brief Enable dark mode support for the entire process
21+
*
22+
* This function enables dark mode support by calling SetPreferredAppMode (or AllowDarkModeForApp
23+
* on older Windows versions). It should be called once during application initialization,
24+
* before creating any windows or system tray icons.
25+
*
26+
* The function will:
27+
* - Load the necessary APIs from uxtheme.dll
28+
* - Set the process to allow dark mode (follows system setting)
29+
* - This affects all menus, dialogs, and windows created after this call
30+
*/
31+
void enable_process_dark_mode();
32+
33+
/**
34+
* @brief Apply dark mode to a specific window's title bar
35+
*
36+
* This function applies the immersive dark mode to a window's title bar using
37+
* DwmSetWindowAttribute with DWMWA_USE_IMMERSIVE_DARK_MODE.
38+
*
39+
* @param hwnd Window handle to apply dark mode to
40+
* @param enable Whether to enable (true) or disable (false) dark mode for the title bar
41+
*/
42+
void apply_window_dark_title_bar(HWND hwnd, bool enable);
43+
44+
} // namespace win_dark_mode
45+
46+
#endif // _WIN32

0 commit comments

Comments
 (0)