From 45b792a63461caa9ac8c5864b83fcf26d724e549 Mon Sep 17 00:00:00 2001 From: CatmanFan <30674270+CatmanFan@users.noreply.github.com> Date: Sun, 21 Jul 2024 14:05:06 +0100 Subject: [PATCH] Update "Accent Color Sync" (#799) * Add files via upload * Update fix * Add files via upload --- mods/accent-color-sync.wh.cpp | 466 ++++++++++++++++++---------------- 1 file changed, 249 insertions(+), 217 deletions(-) diff --git a/mods/accent-color-sync.wh.cpp b/mods/accent-color-sync.wh.cpp index abe6de744..4631b6af3 100644 --- a/mods/accent-color-sync.wh.cpp +++ b/mods/accent-color-sync.wh.cpp @@ -1,83 +1,54 @@ // ==WindhawkMod== -// @id accent-color-sync -// @name Accent color sync -// @description Syncs OpenGlass & StartIsBack++ colors with DWM -// @version 1.0 -// @author CatmanFan -// @github https://github.com/CatmanFan -// @include explorer.exe -// @compilerOptions -lcomdlg32 -ldwmapi +// @id accent-color-sync +// @name Accent Color Sync +// @description Synchronises OpenGlass and Control Panel color settings +// @version 1.1 +// @author CatmanFan / Mr._Lechkar +// @github https://github.com/CatmanFan +// @include explorer.exe +// @compilerOptions -lcomdlg32 // ==/WindhawkMod== // ==WindhawkModReadme== /* -This mod syncs accent colors used by OpenGlass and StartIsBack++ (if found) with that of the DWM every time a change to the latter is effectuated. -It does this by hooking to DefWindowProc and listening for any changes to the accent color. +Brings back the functionality of the 'Color intensity' slider by synchronising OpenGlass's Aero settings with the slider value. -It also changes the color opacity based on the value that has been set in the Mod Settings. +## ⚠️ Requirements +**This mod will not function properly without [kfh83](https://github.com/kfh83)'s OpenGlass-legacy fork installed.** This is because the mod writes values to the registry's DWM section that are used only by this specific software. This requires the GlassType value in the registry to be set to 0x01 (Aero), and it will be automatically changed as such if it isn't. -***This will not function properly without OpenGlass installed.*** +To get this mod to function fully, perform the following steps: +1. Grab the source code of OpenGlass-legacy from **[here](https://github.com/ALTaleX531/OpenGlass/tree/legacy)** and compile, or get an existing binary version from **[this GitHub post](https://github.com/ALTaleX531/OpenGlass/pull/14#issue-2415161314)**. +2. Afterwards, go to *HKCU\SOFTWARE\Microsoft\Windows\DWM* in the registry and add any one of the three DWORD values of *og_ColorizationColorBalance*, *og_ColorizationAfterglowBalance* and *og_ColorizationBlurBalance* before applying this mod. These will be handled automatically. -### Bugs so far -* When changing the mod settings, the SIB taskbar and start menu opacity may occassionally not match that of the window titlebar. This can be fixed by changing the theme color from Windows itself. +You may need to try changing the accent color manually if changes do not automatically take effect. + +## Current bugs +* When changing the slider, there is a rare chance of DWM crashing. */ // ==/WindhawkModReadme== // ==WindhawkModSettings== /* -- opacitySlider: 25 - $name: Color intensity - $description: Window titlebar color intensity. This needs to be manually set for now. -- isTransparent: true +- transparency: true $name: Enable transparency - $description: Replicates the option found in Windows 7. + $description: Replicates the option found in Windows 7. Note this may not take effect immediately until after the color has been changed. */ // ==/WindhawkModSettings== -#include -#include -#include - -#include #include #include +#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct { +struct +{ int opacity; - bool isTransparent; - bool isDWMBG; + bool boolTransparency; } settings; -#pragma region -- DWM functions -- -typedef struct COLORIZATIONPARAMS -{ - COLORREF clrColor; //ColorizationColor - COLORREF clrAftGlow; //ColorizationAfterglow - UINT nIntensity; //ColorizationColorBalance -> 0-100 - UINT clrAftGlowBal; //ColorizationAfterglowBalance - UINT clrBlurBal; //ColorizationBlurBalance - UINT clrGlassReflInt; //ColorizationGlassReflectionIntensity - BOOL fOpaque; -} DWMColor; -// Opacity is permanently fixed, as is intensity - -DWMColor tempSettings; -DWMColor newSettings; - -HRESULT (WINAPI *DwmSetColorizationParameters) (COLORIZATIONPARAMS *colorparam,UINT unknown); -HRESULT (WINAPI *DwmGetColorizationParameters) (COLORIZATIONPARAMS *colorparam); -#pragma endregion +const std::wstring dwmKey = L"SOFTWARE\\Microsoft\\Windows\\DWM"; +const std::wstring opacityValue = L"og_Opacity"; -#pragma region -- Registry functions -- +#pragma region ## Registry functions ## std::wstring read_SZ(std::wstring sk, std::wstring v, std::wstring defaultValue) { const LPCTSTR subkey = sk.c_str(); @@ -128,7 +99,29 @@ DWORD read_DWORD(std::wstring sk, std::wstring v) } } -BOOL keyExists(std::wstring sk) +BOOL exists_DWORD(std::wstring sk, std::wstring v) +{ + const LPCTSTR subkey = sk.c_str(); + const LPCTSTR value = v.c_str(); + DWORD data(0); + DWORD size(sizeof(DWORD)); + + HKEY hKey; + LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, subkey, 0, KEY_ALL_ACCESS, &hKey); + LONG setRes = RegQueryValueEx(hKey, value, 0, NULL, reinterpret_cast(&data), &size); + RegCloseKey(hKey); + + if (openRes != ERROR_SUCCESS || setRes != ERROR_SUCCESS) + { + return FALSE; + } + else + { + return TRUE; + } +} + +BOOL exists_Key(std::wstring sk) { const LPCTSTR subkey = sk.c_str(); @@ -144,7 +137,7 @@ BOOL keyExists(std::wstring sk) } } -BOOL set_DWORD(std::wstring sk, std::wstring v, DWORD data) +BOOL set_DWORD(std::wstring sk, std::wstring v, unsigned long data) { const LPCTSTR subkey = sk.c_str(); const LPCTSTR value = v.c_str(); @@ -157,7 +150,7 @@ BOOL set_DWORD(std::wstring sk, std::wstring v, DWORD data) return FALSE; } - LONG setRes = RegSetValueEx(hKey, value, 0, REG_DWORD, (const BYTE*)&data, sizeof(data)); + LONG setRes = data < 0 ? RegDeleteValue(hKey, value) : RegSetValueEx(hKey, value, 0, REG_DWORD, (const BYTE*)&data, sizeof(data)); RegCloseKey(hKey); if (setRes == ERROR_SUCCESS) @@ -172,225 +165,264 @@ BOOL set_DWORD(std::wstring sk, std::wstring v, DWORD data) } #pragma endregion -#pragma region -- Aero intensity calculation -- -typedef struct AEROINTPARAMETERS +#pragma region ## Windows7 opacity ## +int getOpacityFromBlur(int bB, bool returnOutOf100 = false) { - double color; - double afterglow; - double blur; -} AEROINT; + bool found = false; + if (bB == 28) found = true; + int x = 0; -AEROINT CalculateAeroIntensity(double value) -{ - float t = 26 + (217 * (value / 100.0f)); - - double primary = (t < 103.0) ? 5 : (t < 188.0) ? 0.776471 * t - 74.976471 : (t < 189.0) ? 71 : 0.535714 * t - 31.25; - double secondary = (t < 102.0) ? 0.526316 * t - 8.684211 : (t < 189.0) ? -0.517241 * t + 97.758621 : 0.0; - double blur = (t < 102.0) ? -0.526316 * t + 103.684211 : (t < 188.0) ? -0.255814 * t + 76.093023 : (t < 189.0) ? 28.0 : -0.535714 * t + 131.25; + x = (bB - 103.6842f) / -0.526316f; + if (x >= 26 && x < 102) found = true; - return AEROINTPARAMETERS {primary, secondary, blur}; -} -#pragma endregion + x = (bB - 76.093f) / -0.255814f; + if (x >= 102 && x < 188) found = true; -// Maximum value rendered by DWM color is 250 -// double factor = 255.0 / 250.0; + x = (bB - 131.25f) / -0.535714f; + if (x >= 189 && x <= 217) found = true; -COLORREF current; -BOOL isWorking = false; -int msgCount = 0; + if (found) + { + if (returnOutOf100) x = (x - 26.0) / (217.0 - 26.0) * 100.0; + return x; + } + return 0; +} -void RefreshDWM() +void getColorBalances(int sliderPosition, int &primaryBalance, int &secondaryBalance, int &blurBalance) { - HWND hWnd = FindWindow(TEXT("Dwm"), nullptr); - - PostMessage(hWnd, WM_THEMECHANGED, 0, 0); // refresh part of the settings related to theme - PostMessage(hWnd, WM_DWMCOLORIZATIONCOLORCHANGED, 0, 0); // refresh part of the settings related to color/backdrop - PostMessage(hWnd, WM_SYSCOLORCHANGE, 0, 0); + auto roundNum = [&](float a) -> int { + float rem = a - (int)a; + return (int)a + (rem >= 0.5f ? 1 : 0); + }; + int pB = 0, sB = 0, bB = 0; + int x = sliderPosition; + //primary + if (x >= 26 && x < 103) + pB = 5; + else if (x >= 103 && x < 188) + pB = roundNum(0.776471*x - 74.9765f); + else if (x == 188) + pB = 71; + else if (x >= 189 && x <= 217) + pB = roundNum(0.535714*x - 31.25f); + + //secondary + if (x >= 26 && x < 102) + sB = roundNum(0.526316*x - 8.68421f); + else if (x >= 102 && x < 189) + sB = roundNum(-0.517241*x + 97.7586f); + else if (x >= 189 && x <= 217) + sB = 0; + + //blur + if (x >= 26 && x < 102) + bB = roundNum(-0.526316*x + 103.6842f); + else if (x >= 102 && x < 188) + bB = roundNum(-0.255814f*x + 76.093f); + else if (x == 188) + bB = 28; + else if (x >= 189 && x <= 217) + bB = roundNum(-0.535714f*x + 131.25f); + + primaryBalance = pB; + secondaryBalance = sB; + blurBalance = bB; } +#pragma endregion -#pragma region -- Color read and write functions -- -BOOL WriteNewColor(COLORREF input) +void WriteNewColor() { - const COLORREF color = RGB(GetRValue(input), GetGValue(input), GetBValue(input)); - - const int opacityValue = settings.isTransparent ? settings.opacity : 100; - const int opacity255 = (opacityValue / 100.0) * 255.0; - const AEROINT aeroValues = CalculateAeroIntensity(opacityValue); - - std::stringstream ss1; - std::stringstream ss2; - ss1 << std::hex << std::setfill('0') << std::setw(6) << color; - ss2 << std::hex << std::setfill('0') << std::setw(2) << opacity255; - - const DWORD newColor = std::stoul("0x" + ss2.str() + ss1.str(), nullptr, 16); - - // Write to settings - newSettings = COLORIZATIONPARAMS(); - newSettings.clrAftGlow = newSettings.clrColor = newColor; - newSettings.nIntensity = static_cast(aeroValues.color); - newSettings.clrAftGlowBal = static_cast(aeroValues.afterglow); - newSettings.clrBlurBal = static_cast(aeroValues.blur); - newSettings.clrGlassReflInt = tempSettings.clrGlassReflInt; - newSettings.fOpaque = tempSettings.fOpaque; + if (settings.opacity < 0 || settings.opacity > 100) + settings.opacity = getOpacityFromBlur(0x31); + set_DWORD(dwmKey, opacityValue, settings.opacity); + + // ********************************************* + // Create Aero intensity values + // ********************************************* + int colour = 100; + int afterglow = 0; + int blur = 0; - set_DWORD(L"SOFTWARE\\Microsoft\\Windows\\DWM", L"ColorizationColorBalanceOverride", newSettings.nIntensity); - set_DWORD(L"SOFTWARE\\Microsoft\\Windows\\DWM", L"ColorizationAfterglowBalanceOverride", newSettings.clrAftGlowBal); - set_DWORD(L"SOFTWARE\\Microsoft\\Windows\\DWM", L"GlassOpacity", newSettings.clrBlurBal); - set_DWORD(L"SOFTWARE\\Microsoft\\Windows\\DWM", L"GlassType", settings.isTransparent ? 1 : 4); - set_DWORD(L"SOFTWARE\\Microsoft\\Windows\\DWM", L"GlassOverrideAccent", settings.isTransparent ? 1 : 0); - - if (keyExists(L"SOFTWARE\\StartIsBack")) + if (settings.boolTransparency) { - set_DWORD(L"SOFTWARE\\StartIsBack", L"StartMenuColor", RGB(GetBValue(newColor), GetGValue(newColor), GetRValue(newColor))); - set_DWORD(L"SOFTWARE\\StartIsBack", L"TaskbarColor", RGB(GetBValue(newColor), GetGValue(newColor), GetRValue(newColor))); - set_DWORD(L"SOFTWARE\\StartIsBack", L"StartMenuAlpha", settings.isTransparent ? newSettings.nIntensity / 100.0 * 255.0 : 255.0); - set_DWORD(L"SOFTWARE\\StartIsBack", L"TaskbarAlpha", settings.isTransparent ? newSettings.nIntensity / 100.0 * 255.0: 255.0); + int opacity = (settings.opacity * 0.01) * (217.0 - 26.0) + 26.0; + + getColorBalances + ( + opacity, + colour, + afterglow, + blur + ); + + // Changing the ColorizationColor and Afterglow also affects the intensity slider } + + // ********************************************* + // Actually do the registry editing + // ********************************************* + set_DWORD(dwmKey, L"og_ColorizationColorBalance", colour); + set_DWORD(dwmKey, L"og_ColorizationAfterglowBalance", afterglow); + set_DWORD(dwmKey, L"og_ColorizationBlurBalance", blur); - RefreshDWM(); - return TRUE; + // Other registry values + if (exists_DWORD(dwmKey, L"GlassOpacity")) set_DWORD(dwmKey, L"GlassOpacity", 0); + + PostMessage(FindWindow(TEXT("dwm"), nullptr), WM_DWMCOLORIZATIONCOLORCHANGED, 0, 0); } -BOOL isChangeable(bool force) -{ - if (isWorking && !force) return FALSE; +#pragma region ## DirectUI hooks ## +typedef ATOM WINAPI (*StrToId_T)(unsigned const short*); +StrToId_T StrToID; + +// Pointers to DUI elements +intptr_t intensitySlider = 0; + +typedef unsigned short(*__cdecl Element_GetID_T)(class Element*, void*); +Element_GetID_T Element_GetID; - current = tempSettings.clrColor; - DwmGetColorizationParameters(&tempSettings); +typedef void(* __cdecl Element_OnPropertyChanged_T)(class Element*, class PropertyInfo const *,int,class Value *,class Value *); +Element_OnPropertyChanged_T Element_OnPropertyChanged; - if (tempSettings.clrColor == current) +void __cdecl Element_OnPropertyChanged_hook(class Element* This, class PropertyInfo const *prop, int integer, class Value *valueA, class Value *valueB) +{ + Element_OnPropertyChanged(This, prop, integer, valueA, valueB); + + ATOM id = Element_GetID(This, &This); + if (intensitySlider != id && id == StrToID((unsigned const short*)L"IntensitySlider")) { - return FALSE; + intensitySlider = reinterpret_cast(This); } +} - if (current != 0 || force) - { - isWorking = true; +long(*__cdecl CCTrackBar_SetThumbPosition)(class CCTrackBar*, int); +long __cdecl CCTrackBar_SetThumbPosition_hook(class CCTrackBar* This, int value) +{ + intptr_t current = reinterpret_cast(This); - /* Each unconverted color value contains the opacity in AArrggbb */ - return TRUE; + // Track bar value + if (current > 0 && current == intensitySlider) + { + settings.opacity = (value - 10) / 75.0 * 100.0; + WriteNewColor(); } - return FALSE; + return CCTrackBar_SetThumbPosition(This, value); } +#pragma endregion -void ColorFunction(BOOL force = false) -{ - if (!isWorking) +WindhawkUtils::SYMBOL_HOOK dui70dll_hooks[] = { { - if (isChangeable(force)) - { - if (isWorking) - { - WriteNewColor(tempSettings.clrColor); + {L"StrToID"}, + (void**)&StrToID, + }, - isWorking = false; - } - } + { + {L"public: unsigned short __cdecl DirectUI::Element::GetID(void)"}, + (void**)&Element_GetID + }, + + { + {L"public: virtual void __cdecl DirectUI::Element::OnPropertyChanged(struct DirectUI::PropertyInfo const *,int,class DirectUI::Value *,class DirectUI::Value *)"}, + (void**)&Element_OnPropertyChanged, + (void*)Element_OnPropertyChanged_hook + }, + + { + {L"public: long __cdecl DirectUI::CCTrackBar::SetThumbPosition(int)"}, + (void**)&CCTrackBar_SetThumbPosition, + (void*)CCTrackBar_SetThumbPosition_hook } -} -#pragma endregion +}; -using DefWindowProc_t = decltype(&DefWindowProc); -DefWindowProc_t DefWindowProc_orig; -LRESULT CALLBACK DefWindowProc_hook(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +BOOL LoadSettings() { - switch (uMsg) + bool regSetup = false; + + if (!exists_DWORD(dwmKey, L"og_ColorizationColorBalance") + && !exists_DWORD(dwmKey, L"og_ColorizationAfterglowBalance") + && !exists_DWORD(dwmKey, L"og_ColorizationBlurBalance")) { - case WM_SYSCOLORCHANGE: - case WM_DWMCOLORIZATIONCOLORCHANGED: - if (msgCount == 0) - { - ColorFunction(); - } - else - { - return DefWindowProc_orig(NULL, WM_NULL, 0, 0); - } - msgCount++; - break; - - default: - if (msgCount > 0 && uMsg != WM_NULL) - { - msgCount = 0; - } - break; + Wh_Log(L"Did not find OpenGlass Aero balance values, cannot continue"); + return FALSE; + } + else if (exists_DWORD(dwmKey, L"og_ColorizationColorBalance") + && exists_DWORD(dwmKey, L"og_ColorizationAfterglowBalance") + && exists_DWORD(dwmKey, L"og_ColorizationBlurBalance")) + { + regSetup = false; + } + else + { + regSetup = true; } - return DefWindowProc_orig(hWnd, uMsg, wParam, lParam); -} - -BOOL LoadSettings() { - settings.opacity = fmin(Wh_GetIntSetting(L"opacitySlider"), (217.0 / 255.0) * 100.0); - settings.isTransparent = Wh_GetIntSetting(L"isTransparent"); - settings.isDWMBG = false; // !wcscmp(Wh_GetStringSetting(L"dwmSoftware"), L"DWMBlurGlass"); + if (!exists_DWORD(dwmKey, opacityValue)) regSetup = true; + else settings.opacity = read_DWORD(dwmKey, opacityValue); - if (settings.isDWMBG) + if (regSetup) { - MessageBoxW(NULL, L"DWMBlurGlass functionality not yet implemented", L"Warning", MB_ICONWARNING | MB_OK); - return FALSE; + Wh_Log(L"Setting up registry"); + set_DWORD(dwmKey, L"og_ColorizationColorBalance", 0x08); + set_DWORD(dwmKey, L"og_ColorizationAfterglowBalance", 0x2b); + set_DWORD(dwmKey, L"og_ColorizationBlurBalance", 0x31); + + settings.opacity = getOpacityFromBlur(0x31); + if (!set_DWORD(dwmKey, opacityValue, settings.opacity)) return FALSE; + if (!exists_DWORD(dwmKey, opacityValue)) return FALSE; } - // Load current color - DWMColor oldSettings; - DwmGetColorizationParameters(&oldSettings); - current = oldSettings.clrColor; + settings.boolTransparency = Wh_GetIntSetting(L"transparency"); + + return TRUE; +} + +BOOL Wh_ModSettingsChanged(BOOL* bReload) { + Wh_Log(L"Settings changed"); - ColorFunction(true); - RefreshDWM(); + *bReload = TRUE; return TRUE; } BOOL Wh_ModInit() { - std::wstring username = read_SZ(L"Volatile Environment", L"USERNAME", L"???"); - if (username == L"???") + if (!IsWindows10OrGreater()) { - Wh_Log(L"Warning: Local username not detected, unloading"); + Wh_Log(L"Cannot run on Windows 8.1 or earlier"); return FALSE; } - HMODULE dwmapi = LoadLibraryW(L"dwmapi.dll"); - if (!dwmapi) + std::wstring username = read_SZ(L"Volatile Environment", L"USERNAME", L"???"); + if (username == L"???") { - Wh_Log(L"Failed to load dwmapi.dll"); + Wh_Log(L"Warning: Local username not detected, unloading"); return FALSE; } - *(FARPROC *)&DwmGetColorizationParameters = GetProcAddress(dwmapi, (LPCSTR)127); - *(FARPROC *)&DwmSetColorizationParameters = GetProcAddress(dwmapi, (LPCSTR)131); - BOOL hasComposition; - DwmIsCompositionEnabled(&hasComposition); - if (!hasComposition) + + if (!LoadSettings()) { - Wh_Log(L"DWM composition not loaded"); + Wh_Log(L"Failed to load settings"); return FALSE; } - if (!WindhawkUtils::Wh_SetFunctionHookT(DefWindowProc, DefWindowProc_hook, &DefWindowProc_orig)) + HMODULE hDui = LoadLibraryW(L"dui70.dll"); + if (!hDui) { - Wh_Log(L"Failed to hook DefWindowProc"); + Wh_Log(L"Failed to load dui70.dll"); return FALSE; } - - if (keyExists(L"SOFTWARE\\StartIsBack")) + if (!WindhawkUtils::HookSymbols(hDui, dui70dll_hooks, ARRAYSIZE(dui70dll_hooks))) { - Wh_Log(L"Detected StartIsBack++ installation"); + Wh_Log(L"Failed to hook DUI symbols"); + return FALSE; } - - if (!LoadSettings()) + + if (exists_DWORD(dwmKey, L"GlassType") && read_DWORD(dwmKey, L"GlassType") == 0) { - Wh_Log(L"Failed to load settings"); - return FALSE; + Wh_Log(L"Glass type detected as Blur, automatically setting to Aero"); + set_DWORD(dwmKey, L"GlassType", 1); } - RefreshDWM(); - Wh_Log(L"Loaded as %s", username.c_str()); - return TRUE; -} - -BOOL Wh_ModSettingsChanged(BOOL* bReload) { - Wh_Log(L"Settings changed"); - - *bReload = TRUE; + PostMessage(FindWindow(TEXT("dwm"), nullptr), WM_DWMCOLORIZATIONCOLORCHANGED, 0, 0); return TRUE; } \ No newline at end of file