276 lines
9.0 KiB
C++
276 lines
9.0 KiB
C++
#pragma once
|
|
#include "IatHook.hpp"
|
|
|
|
#include <Windows.h>
|
|
|
|
#include <Uxtheme.h>
|
|
#include <Vssym32.h>
|
|
|
|
#include <unordered_set>
|
|
#include <mutex>
|
|
|
|
enum IMMERSIVE_HC_CACHE_MODE
|
|
{
|
|
IHCM_USE_CACHED_VALUE,
|
|
IHCM_REFRESH
|
|
};
|
|
|
|
// 1903 18362
|
|
enum PreferredAppMode
|
|
{
|
|
Default,
|
|
AllowDark,
|
|
ForceDark,
|
|
ForceLight,
|
|
Max
|
|
};
|
|
|
|
enum WINDOWCOMPOSITIONATTRIB
|
|
{
|
|
WCA_UNDEFINED = 0,
|
|
WCA_NCRENDERING_ENABLED = 1,
|
|
WCA_NCRENDERING_POLICY = 2,
|
|
WCA_TRANSITIONS_FORCEDISABLED = 3,
|
|
WCA_ALLOW_NCPAINT = 4,
|
|
WCA_CAPTION_BUTTON_BOUNDS = 5,
|
|
WCA_NONCLIENT_RTL_LAYOUT = 6,
|
|
WCA_FORCE_ICONIC_REPRESENTATION = 7,
|
|
WCA_EXTENDED_FRAME_BOUNDS = 8,
|
|
WCA_HAS_ICONIC_BITMAP = 9,
|
|
WCA_THEME_ATTRIBUTES = 10,
|
|
WCA_NCRENDERING_EXILED = 11,
|
|
WCA_NCADORNMENTINFO = 12,
|
|
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
|
|
WCA_VIDEO_OVERLAY_ACTIVE = 14,
|
|
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
|
|
WCA_DISALLOW_PEEK = 16,
|
|
WCA_CLOAK = 17,
|
|
WCA_CLOAKED = 18,
|
|
WCA_ACCENT_POLICY = 19,
|
|
WCA_FREEZE_REPRESENTATION = 20,
|
|
WCA_EVER_UNCLOAKED = 21,
|
|
WCA_VISUAL_OWNER = 22,
|
|
WCA_HOLOGRAPHIC = 23,
|
|
WCA_EXCLUDED_FROM_DDA = 24,
|
|
WCA_PASSIVEUPDATEMODE = 25,
|
|
WCA_USEDARKMODECOLORS = 26,
|
|
WCA_LAST = 27
|
|
};
|
|
|
|
struct WINDOWCOMPOSITIONATTRIBDATA
|
|
{
|
|
WINDOWCOMPOSITIONATTRIB Attrib;
|
|
PVOID pvData;
|
|
SIZE_T cbData;
|
|
};
|
|
|
|
using fnRtlGetNtVersionNumbers = void (WINAPI *)(LPDWORD major, LPDWORD minor, LPDWORD build);
|
|
using fnSetWindowCompositionAttribute = BOOL (WINAPI *)(HWND hWnd, WINDOWCOMPOSITIONATTRIBDATA*);
|
|
using fnGetDpiForWindow = UINT (WINAPI*)(_In_ HWND hwnd);
|
|
using fnGetDpiForSystem = UINT(WINAPI*)();
|
|
// 1809 17763
|
|
using fnShouldAppsUseDarkMode = bool (WINAPI *)(); // ordinal 132
|
|
using fnAllowDarkModeForWindow = bool (WINAPI *)(HWND hWnd, bool allow); // ordinal 133
|
|
using fnAllowDarkModeForApp = bool (WINAPI *)(bool allow); // ordinal 135, in 1809
|
|
using fnFlushMenuThemes = void (WINAPI *)(); // ordinal 136
|
|
using fnRefreshImmersiveColorPolicyState = void (WINAPI *)(); // ordinal 104
|
|
using fnIsDarkModeAllowedForWindow = bool (WINAPI *)(HWND hWnd); // ordinal 137
|
|
using fnGetIsImmersiveColorUsingHighContrast = bool (WINAPI *)(IMMERSIVE_HC_CACHE_MODE mode); // ordinal 106
|
|
using fnOpenNcThemeData = HTHEME(WINAPI *)(HWND hWnd, LPCWSTR pszClassList); // ordinal 49
|
|
// 1903 18362
|
|
using fnShouldSystemUseDarkMode = bool (WINAPI *)(); // ordinal 138
|
|
using fnSetPreferredAppMode = PreferredAppMode (WINAPI *)(PreferredAppMode appMode); // ordinal 135, in 1903
|
|
using fnIsDarkModeAllowedForApp = bool (WINAPI *)(); // ordinal 139
|
|
|
|
fnSetWindowCompositionAttribute _SetWindowCompositionAttribute = nullptr;
|
|
fnGetDpiForWindow _GetDpiForWindow = nullptr;
|
|
fnGetDpiForSystem _GetDpiForSystem = nullptr;
|
|
fnShouldAppsUseDarkMode _ShouldAppsUseDarkMode = nullptr;
|
|
fnAllowDarkModeForWindow _AllowDarkModeForWindow = nullptr;
|
|
fnAllowDarkModeForApp _AllowDarkModeForApp = nullptr;
|
|
fnFlushMenuThemes _FlushMenuThemes = nullptr;
|
|
fnRefreshImmersiveColorPolicyState _RefreshImmersiveColorPolicyState = nullptr;
|
|
fnIsDarkModeAllowedForWindow _IsDarkModeAllowedForWindow = nullptr;
|
|
fnGetIsImmersiveColorUsingHighContrast _GetIsImmersiveColorUsingHighContrast = nullptr;
|
|
fnOpenNcThemeData _OpenNcThemeData = nullptr;
|
|
// 1903 18362
|
|
fnShouldSystemUseDarkMode _ShouldSystemUseDarkMode = nullptr;
|
|
fnSetPreferredAppMode _SetPreferredAppMode = nullptr;
|
|
|
|
bool g_darkModeSupported = false;
|
|
bool g_darkModeEnabled = false;
|
|
bool g_SystemMenuEnabled = true;
|
|
DWORD g_buildNumber = 0;
|
|
|
|
bool AllowDarkModeForWindow(HWND hWnd, bool allow)
|
|
{
|
|
if (g_darkModeSupported)
|
|
return _AllowDarkModeForWindow(hWnd, allow);
|
|
return false;
|
|
}
|
|
|
|
bool IsHighContrast()
|
|
{
|
|
HIGHCONTRASTW highContrast = { sizeof(highContrast) };
|
|
if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(highContrast), &highContrast, FALSE))
|
|
return highContrast.dwFlags & HCF_HIGHCONTRASTON;
|
|
return false;
|
|
}
|
|
|
|
void RefreshTitleBarThemeColor(HWND hWnd)
|
|
{
|
|
BOOL dark = FALSE;
|
|
if (_IsDarkModeAllowedForWindow(hWnd) &&
|
|
_ShouldAppsUseDarkMode() &&
|
|
!IsHighContrast())
|
|
{
|
|
dark = TRUE;
|
|
}
|
|
if (g_buildNumber < 18362)
|
|
SetPropW(hWnd, L"UseImmersiveDarkModeColors", reinterpret_cast<HANDLE>(static_cast<INT_PTR>(dark)));
|
|
else if (_SetWindowCompositionAttribute)
|
|
{
|
|
WINDOWCOMPOSITIONATTRIBDATA data = { WCA_USEDARKMODECOLORS, &dark, sizeof(dark) };
|
|
_SetWindowCompositionAttribute(hWnd, &data);
|
|
}
|
|
}
|
|
|
|
bool IsColorSchemeChangeMessage(LPARAM lParam)
|
|
{
|
|
bool is = false;
|
|
if (lParam && (0 == lstrcmpi(reinterpret_cast<LPCWCH>(lParam), L"ImmersiveColorSet")))
|
|
{
|
|
_RefreshImmersiveColorPolicyState();
|
|
is = true;
|
|
}
|
|
_GetIsImmersiveColorUsingHighContrast(IHCM_REFRESH);
|
|
return is;
|
|
}
|
|
|
|
bool IsColorSchemeChangeMessage(UINT message, LPARAM lParam)
|
|
{
|
|
if (message == WM_SETTINGCHANGE)
|
|
return IsColorSchemeChangeMessage(lParam);
|
|
return false;
|
|
}
|
|
|
|
void AllowDarkModeForApp(bool allow)
|
|
{
|
|
if (_AllowDarkModeForApp)
|
|
_AllowDarkModeForApp(allow);
|
|
else if (_SetPreferredAppMode)
|
|
_SetPreferredAppMode(allow ? ForceDark : ForceLight);
|
|
}
|
|
|
|
// limit dark scroll bar to specific windows and their children
|
|
|
|
std::unordered_set<HWND> g_darkScrollBarWindows;
|
|
std::mutex g_darkScrollBarMutex;
|
|
|
|
void EnableDarkScrollBarForWindowAndChildren(HWND hwnd)
|
|
{
|
|
std::lock_guard<std::mutex> lock(g_darkScrollBarMutex);
|
|
g_darkScrollBarWindows.insert(hwnd);
|
|
}
|
|
|
|
bool IsWindowOrParentUsingDarkScrollBar(HWND hwnd)
|
|
{
|
|
HWND hwndRoot = GetAncestor(hwnd, GA_ROOT);
|
|
|
|
std::lock_guard<std::mutex> lock(g_darkScrollBarMutex);
|
|
if (g_darkScrollBarWindows.count(hwnd)) {
|
|
return true;
|
|
}
|
|
if (hwnd != hwndRoot && g_darkScrollBarWindows.count(hwndRoot)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FixDarkScrollBar()
|
|
{
|
|
HMODULE hComctl = LoadLibraryExW(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
|
if (hComctl)
|
|
{
|
|
auto addr = FindDelayLoadThunkInModule(hComctl, "uxtheme.dll", 49); // OpenNcThemeData
|
|
if (addr)
|
|
{
|
|
DWORD oldProtect;
|
|
if (VirtualProtect(addr, sizeof(IMAGE_THUNK_DATA), PAGE_READWRITE, &oldProtect))
|
|
{
|
|
auto MyOpenThemeData = [](HWND hWnd, LPCWSTR classList) -> HTHEME {
|
|
if (wcscmp(classList, L"ScrollBar") == 0) {
|
|
hWnd = nullptr;
|
|
classList = L"Explorer::ScrollBar";
|
|
}
|
|
if (wcscmp(classList, L"MENU") == 0) {
|
|
hWnd = nullptr;
|
|
classList = L"Explorer::Menu";
|
|
}
|
|
return _OpenNcThemeData(hWnd, classList);
|
|
};
|
|
|
|
addr->u1.Function = reinterpret_cast<ULONG_PTR>(static_cast<fnOpenNcThemeData>(MyOpenThemeData));
|
|
VirtualProtect(addr, sizeof(IMAGE_THUNK_DATA), oldProtect, &oldProtect);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
constexpr bool CheckBuildNumber(DWORD buildNumber)
|
|
{
|
|
return (buildNumber == 17763 || // 1809
|
|
buildNumber == 18362 || // 1903
|
|
buildNumber == 18363 || // 1909
|
|
buildNumber >= 19041); // Windows 10 any version > 2004 and newer
|
|
}
|
|
|
|
void InitDarkMode()
|
|
{
|
|
auto RtlGetNtVersionNumbers = reinterpret_cast<fnRtlGetNtVersionNumbers>(GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetNtVersionNumbers"));
|
|
if (RtlGetNtVersionNumbers)
|
|
{
|
|
DWORD major, minor;
|
|
RtlGetNtVersionNumbers(&major, &minor, &g_buildNumber);
|
|
g_buildNumber &= ~0xF0000000;
|
|
if (major == 10 && minor == 0 && CheckBuildNumber(g_buildNumber))
|
|
{
|
|
HMODULE hUxtheme = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
|
if (hUxtheme)
|
|
{
|
|
_OpenNcThemeData = reinterpret_cast<fnOpenNcThemeData>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(49)));
|
|
_RefreshImmersiveColorPolicyState = reinterpret_cast<fnRefreshImmersiveColorPolicyState>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(104)));
|
|
_GetIsImmersiveColorUsingHighContrast = reinterpret_cast<fnGetIsImmersiveColorUsingHighContrast>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(106)));
|
|
_ShouldAppsUseDarkMode = reinterpret_cast<fnShouldAppsUseDarkMode>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(132)));
|
|
_AllowDarkModeForWindow = reinterpret_cast<fnAllowDarkModeForWindow>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(133)));
|
|
|
|
auto ord135 = GetProcAddress(hUxtheme, MAKEINTRESOURCEA(135));
|
|
if (g_buildNumber < 18362)
|
|
_AllowDarkModeForApp = reinterpret_cast<fnAllowDarkModeForApp>(ord135);
|
|
else
|
|
_SetPreferredAppMode = reinterpret_cast<fnSetPreferredAppMode>(ord135);
|
|
|
|
_FlushMenuThemes = reinterpret_cast<fnFlushMenuThemes>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(136)));
|
|
_IsDarkModeAllowedForWindow = reinterpret_cast<fnIsDarkModeAllowedForWindow>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(137)));
|
|
|
|
_SetWindowCompositionAttribute = reinterpret_cast<fnSetWindowCompositionAttribute>(GetProcAddress(GetModuleHandleW(L"user32.dll"), "SetWindowCompositionAttribute"));
|
|
|
|
_GetDpiForWindow = reinterpret_cast<fnGetDpiForWindow>(GetProcAddress(GetModuleHandleW(L"user32.dll"), "GetDpiForWindow"));
|
|
_GetDpiForSystem = reinterpret_cast<fnGetDpiForSystem>(GetProcAddress(GetModuleHandleW(L"user32.dll"), "GetDpiForSystem"));
|
|
|
|
if (_OpenNcThemeData &&
|
|
_RefreshImmersiveColorPolicyState &&
|
|
_ShouldAppsUseDarkMode &&
|
|
_AllowDarkModeForWindow &&
|
|
(_AllowDarkModeForApp || _SetPreferredAppMode) &&
|
|
_FlushMenuThemes &&
|
|
_IsDarkModeAllowedForWindow)
|
|
{
|
|
g_darkModeSupported = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|