From 06c3cab81c97ad295228a3a51bfd689784f448d5 Mon Sep 17 00:00:00 2001 From: RadAd Date: Mon, 15 Jul 2019 15:35:28 +1000 Subject: Enable dark mode --- DarkMode.h | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++ RadTerminal.cpp | 32 ++++++--- RadTerminal.vcxproj | 10 +-- RadTerminalFrame.cpp | 4 +- 4 files changed, 214 insertions(+), 16 deletions(-) create mode 100644 DarkMode.h diff --git a/DarkMode.h b/DarkMode.h new file mode 100644 index 0000000..207ff6a --- /dev/null +++ b/DarkMode.h @@ -0,0 +1,184 @@ +// https://github.com/ysc3839/win32-darkmode/blob/master/win32-darkmode/DarkMode.h + +#pragma once +#include +#include +//#include "IatHook.h" + +enum IMMERSIVE_HC_CACHE_MODE +{ + IHCM_USE_CACHED_VALUE, + IHCM_REFRESH +}; + +// Insider 18334 +enum PreferredAppMode +{ + Default, + AllowDark, + ForceDark, + ForceLight, + Max +}; + +using fnRtlGetNtVersionNumbers = void (WINAPI *)(LPDWORD major, LPDWORD minor, LPDWORD build); +// 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, removed since 18334 +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 + // Insider 18290 +using fnShouldSystemUseDarkMode = bool (WINAPI *)(); // ordinal 138 + // Insider 18334 +using fnSetPreferredAppMode = PreferredAppMode(WINAPI *)(PreferredAppMode appMode); // ordinal 135, since 18334 +using fnIsDarkModeAllowedForApp = bool (WINAPI *)(); // ordinal 139 + +fnShouldAppsUseDarkMode _ShouldAppsUseDarkMode = nullptr; +fnAllowDarkModeForWindow _AllowDarkModeForWindow = nullptr; +fnAllowDarkModeForApp _AllowDarkModeForApp = nullptr; +fnFlushMenuThemes _FlushMenuThemes = nullptr; +fnRefreshImmersiveColorPolicyState _RefreshImmersiveColorPolicyState = nullptr; +fnIsDarkModeAllowedForWindow _IsDarkModeAllowedForWindow = nullptr; +fnGetIsImmersiveColorUsingHighContrast _GetIsImmersiveColorUsingHighContrast = nullptr; +fnOpenNcThemeData _OpenNcThemeData = nullptr; +// Insider 18290 +fnShouldSystemUseDarkMode _ShouldSystemUseDarkMode = nullptr; +// Insider 18334 +fnSetPreferredAppMode _SetPreferredAppMode = nullptr; + +bool g_darkModeSupported = false; +bool g_darkModeEnabled = false; +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; + } + DwmSetWindowAttribute(hWnd, 19, &dark, sizeof(dark)); +} + +bool IsColorSchemeChangeMessage(LPARAM lParam) +{ + bool is = false; + if (lParam && CompareStringOrdinal(reinterpret_cast(lParam), -1, L"ImmersiveColorSet", -1, TRUE) == CSTR_EQUAL) + { + _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 ? AllowDark : Default); +} + +void FixDarkScrollBar() +{ +#if 0 + auto addr = FindDelayLoadThunkInModule(GetModuleHandleW(L"comctl32.dll"), "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"; + } + return _OpenNcThemeData(hWnd, classList); + }; + + addr->u1.Function = reinterpret_cast(static_cast(MyOpenThemeData)); + VirtualProtect(addr, sizeof(IMAGE_THUNK_DATA), oldProtect, &oldProtect); + } + } +#endif +} + +void InitDarkMode() +{ + fnRtlGetNtVersionNumbers RtlGetNtVersionNumbers = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetNtVersionNumbers")); + if (RtlGetNtVersionNumbers) + { + DWORD major, minor; + RtlGetNtVersionNumbers(&major, &minor, &g_buildNumber); + g_buildNumber &= ~0xF0000000; + if (major == 10 && minor == 0 && 17763 <= g_buildNumber && g_buildNumber <= 18362) // Windows 10 1809 10.0.17763 - 1903 10.0.18362 + { + HMODULE hUxtheme = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (hUxtheme) + { + _OpenNcThemeData = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(49))); + _RefreshImmersiveColorPolicyState = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(104))); + _GetIsImmersiveColorUsingHighContrast = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(106))); + _ShouldAppsUseDarkMode = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(132))); + _AllowDarkModeForWindow = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(133))); + + auto ord135 = GetProcAddress(hUxtheme, MAKEINTRESOURCEA(135)); + if (g_buildNumber < 18334) + _AllowDarkModeForApp = reinterpret_cast(ord135); + else + _SetPreferredAppMode = reinterpret_cast(ord135); + + //_FlushMenuThemes = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(136))); + _IsDarkModeAllowedForWindow = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(137))); + + if (_OpenNcThemeData && + _RefreshImmersiveColorPolicyState && + _ShouldAppsUseDarkMode && + _AllowDarkModeForWindow && + (_AllowDarkModeForApp || _SetPreferredAppMode) && + //_FlushMenuThemes && + _IsDarkModeAllowedForWindow) + { + g_darkModeSupported = true; + + AllowDarkModeForApp(true); + _RefreshImmersiveColorPolicyState(); + + g_darkModeEnabled = _ShouldAppsUseDarkMode() && !IsHighContrast(); + + FixDarkScrollBar(); + } + } + } + } +} diff --git a/RadTerminal.cpp b/RadTerminal.cpp index 05ebfd5..4a9a8f3 100644 --- a/RadTerminal.cpp +++ b/RadTerminal.cpp @@ -4,6 +4,7 @@ #include #include "ProcessUtils.h" #include "WinUtils.h" +#include "DarkMode.h" #include "libtsm\src\tsm\libtsm.h" #include "libtsm\external\xkbcommon\xkbcommon-keysyms.h" #include "resource.h" @@ -64,9 +65,11 @@ void ShowError(HWND hWnd, LPCTSTR msg, HRESULT hr) ShowError(hWnd, __FUNCTIONW__ TEXT(": ") TEXT(#x), 0); \ } -HWND CreateRadTerminalFrame(HINSTANCE hInstance, int nCmdShow); +HWND CreateRadTerminalFrame(HINSTANCE hInstance); LRESULT CALLBACK RadTerminalWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +HWND ActionNewWindow(HWND hWnd, bool bParseCmdLine); + ATOM RegisterRadTerminal(HINSTANCE hInstance) { WNDCLASS wc = {}; @@ -87,8 +90,6 @@ ATOM GetRadTerminalAtom(HINSTANCE hInstance) return g_atom; } -HWND ActionNewWindow(HWND hWnd, bool bParseCmdLine); - struct RadTerminalCreate { int iFontHeight; @@ -166,16 +167,18 @@ RadTerminalCreate GetDefaultTerminalCreate(bool bParseCmdLine) int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, PTSTR pCmdLine, int nCmdShow) { + InitDarkMode(); + HWND hWnd = NULL; HWND hWndMDIClient = NULL; HACCEL hAccel = NULL; - bool bMDI = false; + bool bMDI = true; CHECK(GetRadTerminalAtom(hInstance), EXIT_FAILURE); if (bMDI) { - hWnd = CreateRadTerminalFrame(hInstance, nCmdShow); + hWnd = CreateRadTerminalFrame(hInstance); CHECK(hWnd, EXIT_FAILURE); hWndMDIClient = GetMDIClient(hWnd); @@ -209,10 +212,17 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, PTSTR pCmdLine, int nCmdSho &rtc ); CHECK(hWnd, EXIT_FAILURE); + } - ShowWindow(hWnd, nCmdShow); + if (g_darkModeEnabled) + { + SetWindowTheme(hWnd, L"DarkMode_Explorer", NULL); // Needed for scrollbar + AllowDarkModeForWindow(hWnd, true); + RefreshTitleBarThemeColor(hWnd); } + ShowWindow(hWnd, nCmdShow); + MSG msg = {}; while (GetMessage(&msg, (HWND) NULL, 0, 0)) { @@ -294,9 +304,8 @@ void tsm_log(void *data, va_list args) { char buf[1024]; - sprintf_s(buf, "tsm_log: %s:%d %s %s %d\n", strrchr(file, '\\'), line, func, subs, sev); + sprintf_s(buf, "tsm_log: %d %s:%d %s %s - ", sev, strrchr(file, '\\'), line, func, subs); OutputDebugStringA(buf); - OutputDebugStringA("tsm_log: "); vsprintf_s(buf, format, args); OutputDebugStringA(buf); OutputDebugStringA("\n"); @@ -685,8 +694,13 @@ HWND ActionNewWindow(HWND hWnd, bool bParseCmdLine) CHECK(hChildWnd != NULL, NULL); SetWindowLong(hChildWnd, GWL_EXSTYLE, GetWindowExStyle(hChildWnd) | WS_EX_ACCEPTFILES); + if (false && g_darkModeEnabled) // TODO Doesn't seem to work MDI child windows + { + SetWindowTheme(hWnd, L"DarkMode_Explorer", NULL); // Needed for scrollbar + AllowDarkModeForWindow(hWnd, true); + RefreshTitleBarThemeColor(hWnd); + } - ShowWindow(hChildWnd, SW_SHOW); return hChildWnd; } diff --git a/RadTerminal.vcxproj b/RadTerminal.vcxproj index b5f092b..7efcf80 100644 --- a/RadTerminal.vcxproj +++ b/RadTerminal.vcxproj @@ -78,8 +78,8 @@ true - kernel32.lib;user32.lib;gdi32.lib;uxtheme.lib;%(AdditionalDependencies) - kernel32.lib;user32.lib;gdi32.lib;uxtheme.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;uxtheme.lib;dwmapi.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;uxtheme.lib;dwmapi.lib;%(AdditionalDependencies) @@ -93,8 +93,8 @@ true true true - kernel32.lib;user32.lib;gdi32.lib;uxtheme.lib;%(AdditionalDependencies) - kernel32.lib;user32.lib;gdi32.lib;uxtheme.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;uxtheme.lib;dwmapi.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;uxtheme.lib;dwmapi.lib;%(AdditionalDependencies) @@ -108,6 +108,8 @@ + + diff --git a/RadTerminalFrame.cpp b/RadTerminalFrame.cpp index 9ed7ebf..1d2aeff 100644 --- a/RadTerminalFrame.cpp +++ b/RadTerminalFrame.cpp @@ -8,7 +8,7 @@ HWND ActionNewWindow(HWND hWnd, bool bParseCmdLine); LRESULT CALLBACK RadTerminalMDIFrameProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -HWND CreateRadTerminalFrame(HINSTANCE hInstance, int nCmdShow) +HWND CreateRadTerminalFrame(HINSTANCE hInstance) { WNDCLASS wcMDIFrame = {}; @@ -36,8 +36,6 @@ HWND CreateRadTerminalFrame(HINSTANCE hInstance, int nCmdShow) if (hFrame == NULL) return NULL; - ShowWindow(hFrame, nCmdShow); - return hFrame; } -- cgit v1.2.3