-
Notifications
You must be signed in to change notification settings - Fork 16
Dark Mode changes to official API integration #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
RonGeorge
wants to merge
2
commits into
ffes:master
Choose a base branch
from
RonGeorge:dark-mode
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,338 @@ | ||
| #include <windows.h> | ||
|
|
||
| #include "DarkMode.h" | ||
| #include "IatHook.h" | ||
|
|
||
| #include <uxtheme.h> | ||
| #include <vssym32.h> | ||
|
|
||
| #include <unordered_set> | ||
| #include <mutex> | ||
|
|
||
| #if defined(__GNUC__) && __GNUC__ > 8 | ||
| #define WINAPI_LAMBDA_RETURN(return_t) -> return_t WINAPI | ||
| #elif defined(__GNUC__) | ||
| #define WINAPI_LAMBDA_RETURN(return_t) WINAPI -> return_t | ||
| #else | ||
| #define WINAPI_LAMBDA_RETURN(return_t) -> return_t | ||
| #endif | ||
|
|
||
| enum IMMERSIVE_HC_CACHE_MODE | ||
| { | ||
| IHCM_USE_CACHED_VALUE, | ||
| IHCM_REFRESH | ||
| }; | ||
|
|
||
| // 1903 18362 | ||
| enum class 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 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 | ||
| using fnSetPreferredAppMode = PreferredAppMode (WINAPI *)(PreferredAppMode appMode); // ordinal 135, in 1903 | ||
|
|
||
| fnSetWindowCompositionAttribute _SetWindowCompositionAttribute = 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; | ||
| fnSetPreferredAppMode _SetPreferredAppMode = nullptr; | ||
|
|
||
| bool g_darkModeSupported = false; | ||
| bool g_darkModeEnabled = false; | ||
| DWORD g_buildNumber = 0; | ||
|
|
||
| bool ShouldAppsUseDarkMode() | ||
| { | ||
| if (!_ShouldAppsUseDarkMode) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return _ShouldAppsUseDarkMode(); | ||
| } | ||
|
|
||
| bool AllowDarkModeForWindow(HWND hWnd, bool allow) | ||
| { | ||
| if (g_darkModeSupported && _AllowDarkModeForWindow) | ||
| return _AllowDarkModeForWindow(hWnd, allow); | ||
| return false; | ||
| } | ||
|
|
||
| bool IsHighContrast() | ||
| { | ||
| HIGHCONTRASTW highContrast{}; | ||
| highContrast.cbSize = sizeof(HIGHCONTRASTW); | ||
| if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(HIGHCONTRASTW), &highContrast, FALSE)) | ||
| return (highContrast.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON; | ||
| return false; | ||
| } | ||
|
|
||
| void SetTitleBarThemeColor(HWND hWnd, BOOL dark) | ||
| { | ||
| if (g_buildNumber < 18362) | ||
| SetPropW(hWnd, L"UseImmersiveDarkModeColors", reinterpret_cast<HANDLE>(static_cast<intptr_t>(dark))); | ||
| else if (_SetWindowCompositionAttribute) | ||
| { | ||
| WINDOWCOMPOSITIONATTRIBDATA data = { WCA_USEDARKMODECOLORS, &dark, sizeof(dark) }; | ||
| _SetWindowCompositionAttribute(hWnd, &data); | ||
| } | ||
| } | ||
|
|
||
| void RefreshTitleBarThemeColor(HWND hWnd) | ||
| { | ||
| BOOL dark = FALSE; | ||
| if (_IsDarkModeAllowedForWindow && _ShouldAppsUseDarkMode) | ||
| { | ||
| if (_IsDarkModeAllowedForWindow(hWnd) && _ShouldAppsUseDarkMode() && !IsHighContrast()) | ||
| { | ||
| dark = TRUE; | ||
| } | ||
| } | ||
|
|
||
| SetTitleBarThemeColor(hWnd, dark); | ||
| } | ||
|
|
||
| bool IsColorSchemeChangeMessage(LPARAM lParam) | ||
| { | ||
| bool is = false; | ||
| if (lParam && (0 == lstrcmpi(reinterpret_cast<LPCWCH>(lParam), L"ImmersiveColorSet")) && _RefreshImmersiveColorPolicyState) | ||
| { | ||
| _RefreshImmersiveColorPolicyState(); | ||
| is = true; | ||
| } | ||
| if (_GetIsImmersiveColorUsingHighContrast) | ||
| _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 ? PreferredAppMode::ForceDark : PreferredAppMode::Default); | ||
| } | ||
|
|
||
| void FlushMenuThemes() | ||
| { | ||
| if (_FlushMenuThemes) | ||
| { | ||
| _FlushMenuThemes(); | ||
| } | ||
| } | ||
|
|
||
| // 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 = LoadLibraryEx(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) && _OpenNcThemeData) | ||
| { | ||
| auto MyOpenThemeData = [](HWND hWnd, LPCWSTR classList) WINAPI_LAMBDA_RETURN(HTHEME) { | ||
| if (wcscmp(classList, L"ScrollBar") == 0) | ||
| { | ||
| if (IsWindowOrParentUsingDarkScrollBar(hWnd)) { | ||
| hWnd = nullptr; | ||
| classList = L"Explorer::ScrollBar"; | ||
| } | ||
| } | ||
| 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 || // 2004 | ||
| buildNumber == 19042 || // 20H2 | ||
| buildNumber == 19043 || // 21H1 | ||
| buildNumber == 19044 || // 21H2 | ||
| (buildNumber > 19044 && buildNumber < 22000) || // Windows 10 any version > 21H2 | ||
| buildNumber >= 22000); // Windows 11 builds | ||
| } | ||
|
|
||
| bool IsWindows10() // or later OS version | ||
| { | ||
| return (g_buildNumber >= 17763); | ||
| } | ||
|
|
||
| bool IsWindows11() // or later OS version | ||
| { | ||
| return (g_buildNumber >= 22000); | ||
| } | ||
|
|
||
| DWORD GetWindowsBuildNumber() | ||
| { | ||
| return g_buildNumber; | ||
| } | ||
|
|
||
| void InitDarkMode() | ||
| { | ||
| fnRtlGetNtVersionNumbers RtlGetNtVersionNumbers = nullptr; | ||
| HMODULE hNtdllModule = GetModuleHandle(L"ntdll.dll"); | ||
| if (hNtdllModule) | ||
| { | ||
| RtlGetNtVersionNumbers = reinterpret_cast<fnRtlGetNtVersionNumbers>(GetProcAddress(hNtdllModule, "RtlGetNtVersionNumbers")); | ||
| } | ||
|
|
||
| if (RtlGetNtVersionNumbers) | ||
| { | ||
| DWORD major, minor; | ||
| RtlGetNtVersionNumbers(&major, &minor, &g_buildNumber); | ||
| g_buildNumber &= ~0xF0000000; | ||
| if (major == 10 && minor == 0 && CheckBuildNumber(g_buildNumber)) | ||
| { | ||
| HMODULE hUxtheme = LoadLibraryEx(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))); | ||
|
|
||
| HMODULE hUser32Module = GetModuleHandleW(L"user32.dll"); | ||
| if (hUser32Module) | ||
| { | ||
| _SetWindowCompositionAttribute = reinterpret_cast<fnSetWindowCompositionAttribute>(GetProcAddress(hUser32Module, "SetWindowCompositionAttribute")); | ||
| } | ||
|
|
||
| if (_OpenNcThemeData && | ||
| _RefreshImmersiveColorPolicyState && | ||
| _ShouldAppsUseDarkMode && | ||
| _AllowDarkModeForWindow && | ||
| (_AllowDarkModeForApp || _SetPreferredAppMode) && | ||
| _FlushMenuThemes && | ||
| _IsDarkModeAllowedForWindow) | ||
| { | ||
| g_darkModeSupported = true; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void SetDarkMode(bool useDark, bool fixDarkScrollbar) | ||
| { | ||
| if (g_darkModeSupported) | ||
| { | ||
| AllowDarkModeForApp(useDark); | ||
| FlushMenuThemes(); | ||
| if (fixDarkScrollbar) | ||
| { | ||
| FixDarkScrollBar(); | ||
| } | ||
| g_darkModeEnabled = ShouldAppsUseDarkMode() && !IsHighContrast(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| #pragma once | ||
|
|
||
| extern bool g_darkModeSupported; | ||
| extern bool g_darkModeEnabled; | ||
|
|
||
| bool ShouldAppsUseDarkMode(); | ||
| bool AllowDarkModeForWindow(HWND hWnd, bool allow); | ||
| bool IsHighContrast(); | ||
| void RefreshTitleBarThemeColor(HWND hWnd); | ||
| void SetTitleBarThemeColor(HWND hWnd, BOOL dark); | ||
| bool IsColorSchemeChangeMessage(LPARAM lParam); | ||
| bool IsColorSchemeChangeMessage(UINT message, LPARAM lParam); | ||
| void AllowDarkModeForApp(bool allow); | ||
| void EnableDarkScrollBarForWindowAndChildren(HWND hwnd); | ||
| void InitDarkMode(); | ||
| void SetDarkMode(bool useDarkMode, bool fixDarkScrollbar); | ||
| bool IsWindows10(); | ||
| bool IsWindows11(); | ||
| DWORD GetWindowsBuildNumber(); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, you should restore the previous version of the
.gitignorefile. The changes here seem to be committed by accident.Additionally, you should not add the x64 binary files to the repo.