diff --git a/mods/accent-color-sync.wh.cpp b/mods/accent-color-sync.wh.cpp index 4fe747640..96c081ce8 100644 --- a/mods/accent-color-sync.wh.cpp +++ b/mods/accent-color-sync.wh.cpp @@ -1,43 +1,46 @@ // ==WindhawkMod== -// @id accent-color-sync -// @name Accent Color Sync -// @description Synchronises OpenGlass and Control Panel color settings -// @version 1.2 -// @author CatmanFan / Mr._Lechkar -// @github https://github.com/CatmanFan -// @include explorer.exe -// @compilerOptions -lcomdlg32 +// @id accent-color-sync +// @name Accent Color Sync +// @description Synchronises OpenGlass and Control Panel color settings +// @description:fr-FR Synchronisation des couleurs d'OpenGlass et du Panneau de configuration +// @description:es-ES Sincroniza los colores de OpenGlass y del Panel de control +// @version 1.3 +// @author CatmanFan / Mr._Lechkar +// @github https://github.com/CatmanFan +// @include explorer.exe +// @architecture x86 +// @architecture x86-64 +// @compilerOptions -lcomdlg32 // ==/WindhawkMod== // ==WindhawkModReadme== /* -Brings back the functionality of the 'Color intensity' slider by synchronising OpenGlass's Aero settings with the slider value. +Brings back the functionality of the 'Color intensity' slider to Windows 10 using OpenGlass. The mod synchronises OpenGlass's Aero settings with the slider value. ## ⚠️ 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. To get this mod to function fully, perform the following steps: -1. Install the OpenGlass-legacy fork; this can be done by grabbing the source code from **[the official repo](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)**. +1. Install the OpenGlass-legacy fork; this can be done by compiling the source code from **[the official repo](https://github.com/ALTaleX531/OpenGlass/tree/legacy)**, or by getting an existing binary version from **[this GitHub post](https://github.com/ALTaleX531/OpenGlass/pull/14#issue-2415161314)**. * If you are updating this mod from version 1.0, it is required to disable or uninstall any other existing DWM shader software (such as regular OpenGlass or DWMBlurGlass). 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. You may need to try changing the accent color manually if changes do not automatically take effect. ## Current known bugs -* *Sync with DWM* mod setting: - * While this option is enabled, the opacity shown by the Control Panel theme preview's color icon does not always reflect the value set by the color intensity slider when it is changed, unless after the theme preview is regenerated by changing the color RGB values or the desktop background. - * The OpenGlass color balances do not update after changing the theme itself to one with a different color intensity value. This is because the feature to do so has not been implemented yet for the mod. +* **Sync with DWM** option: In the Control Panel, the theme preview's color icon's opacity does not always reflect the value set by the color intensity slider if it is changed while the *Sync with DWM* mod setting is enabled, unless after the theme preview is regenerated by changing the color RGB values or the desktop background. +* When toggling transparency, because the GlassType value used by OpenGlass is changed, the taskbar and windows visually glitch after DWM is refreshed. A hotfix is currently implemented which logs out the current session if this setting is toggled. +* Actually closing the Personalization window does not produce the same behaviour as clicking "Cancel" (i.e. the RGB color is changed but the OpenGlass opacity stays the same). * When changing the intensity slider, there is a very rare chance of DWM crashing. */ // ==/WindhawkModReadme== // ==WindhawkModSettings== /* -- transparency: TRUE - $name: Enable transparency - $description: Replicates the option found in Windows 7. At present, this only sets the OpenGlass solid color balance to 100 if the option is disabled. - syncDWM: FALSE $name: Sync with DWM + $name:fr-FR: Synchroniser avec DWM + $name:es-ES: Sincronizar con DWM $description: Writes the opacity value to DWM's color and afterglow variables. This makes it so that the opacity is also written to the theme alongside the color's RGB. Otherwise, Windows automatically sets it to remain stationary at 0xc4 (196). */ // ==/WindhawkModSettings== @@ -45,10 +48,14 @@ You may need to try changing the accent color manually if changes do not automat #if _WIN64 #define THISCALL __cdecl #define STHISCALL L"__cdecl" +#define STDCALL __cdecl +#define SSTDCALL L"__cdecl" #define STRTOID_NAME L"StrToID" #else #define THISCALL __thiscall #define STHISCALL L"__thiscall" +#define STDCALL __stdcall +#define SSTDCALL L"__stdcall" #define STRTOID_NAME L"_StrToID@4" #endif @@ -223,7 +230,7 @@ BOOL set_DWORD(std::wstring sk, std::wstring v, unsigned long data) #pragma endregion #pragma region ## Windows7 opacity ## -int getOpacityFromBlur(int bB, bool returnOutOf100 = FALSE) +int getOpacityFromBlur(int bB, bool returnOutOf100 = TRUE) { bool found = FALSE; if (bB == 28) found = TRUE; @@ -248,39 +255,36 @@ int getOpacityFromBlur(int bB, bool returnOutOf100 = FALSE) void getColorBalances(int sliderPosition, int &primaryBalance, int &secondaryBalance, int &blurBalance) { - 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 = 0.776471*x - 74.9765f + 1.5f; + else if (x == 188) pB = 71; else if (x >= 189 && x <= 217) - pB = roundNum(0.535714*x - 31.25f); + pB = 0.535714*x - 31.25f + 1.5f; //secondary if (x >= 26 && x < 102) - sB = roundNum(0.526316*x - 8.68421f); + sB = 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.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); + bB = -0.526316*x + 103.6842f; else if (x >= 102 && x < 188) - bB = roundNum(-0.255814f*x + 76.093f); + bB = -0.255814f*x + 76.093f; else if (x == 188) bB = 28; else if (x >= 189 && x <= 217) - bB = roundNum(-0.535714f*x + 131.25f); + bB = -0.535714f*x + 131.25f; primaryBalance = pB; secondaryBalance = sB; @@ -290,25 +294,48 @@ void getColorBalances(int sliderPosition, int &primaryBalance, int &secondaryBal int intensitySliderMin = 10; int intensitySliderMax = 85; -int valueTo100(int value) { return value - intensitySliderMin / (intensitySliderMax - intensitySliderMin) * 100; } -int valueFrom100(int value) { return (value / 100) * (intensitySliderMax - intensitySliderMin) + intensitySliderMin; } +int valueTo100(int x) { return x - intensitySliderMin / (intensitySliderMax - intensitySliderMin) * 100; } +int valueFrom100(int x) { return (x / 100) * (intensitySliderMax - intensitySliderMin) + intensitySliderMin; } +int valueTo255(int x) { return round(2.5497*x + 0.0449); } +int getOpacityFromColor(DWORD color, bool rounded) +{ + std::stringstream ssA; + ssA << std::hex << std::setfill('0') << std::setw(8) << color; + unsigned long x = std::stoul(ssA.str().substr(0, 2), nullptr, 16); + return rounded ? round((x - 0.0732) / 2.5488) : x; +} +/** + * Writes the OpenGlass opacity value to DWM's color and afterglow values in registry. + */ void SyncOpacity() { if (!settings.boolSyncDWM) return; + if (settings.opacity < 0 || settings.opacity > 100) + { + settings.opacity = getOpacityFromBlur(0x31); + set_DWORD(dwmKey, opacityValue, settings.opacity); + } const std::wstring value = L"ColorizationColor"; if (!exists_DWORD(dwmKey, value)) return; DWORD x = read_DWORD(dwmKey, value); + int sysOpacity = getOpacityFromColor(x, false); + + int ogOpacity = valueTo255(settings.opacity); - std::stringstream ssA; - ssA << std::hex << std::setfill('0') << std::setw(8) << x; - int sysOpacity = std::stoul(ssA.str().substr(0, 2), nullptr, 16); - - int ogOpacity = valueTo100(settings.opacity) / 100.0 * 255.0 + 3; + // Hotfixes to force match with Windows 7 accent colors, since the calculation above is janky + if (ogOpacity == 0x69 || ogOpacity == 0x6a) ogOpacity = 0x6b; + if (ogOpacity == 0xa6 || ogOpacity == 0xa7) ogOpacity = 0xa8; + if (ogOpacity == 0x67) ogOpacity = 0x66; + if (ogOpacity == 0x6e || ogOpacity == 0x6f) ogOpacity = 0x70; + if (x == 0x52fadc0e) x = 0x54fadc0e; + if (x == 0x54fadc0e) ogOpacity = 0x54; if (sysOpacity != ogOpacity) { + std::stringstream ssA; + ssA << std::hex << std::setfill('0') << std::setw(8) << x; std::stringstream ssB; ssB << std::hex << std::setfill('0') << std::setw(2) << ogOpacity; DWORD newColor = std::stoul("0x" + ssB.str() + ssA.str().substr(2), nullptr, 16); @@ -318,11 +345,12 @@ void SyncOpacity() } } +int opacity = -1; /** * Calculates the color, afterglow and blur intensity values from an integer out of 100, then writes them to the registry for use with OpenGlass. - * @param opacity An integer value ranging between 0 and 100. + * @param sync Determines whether to also sync opacity. */ -void WriteNewColor(int opacity = -1) +void WriteNewColor(bool sync) { if (!settings.boolTransparency) opacity = 100; @@ -331,9 +359,11 @@ void WriteNewColor(int opacity = -1) if (opacity < 0 || opacity > 100) opacity = settings.opacity; if (opacity < 0 || opacity > 100) + { settings.opacity = opacity = getOpacityFromBlur(0x31); + } } - set_DWORD(dwmKey, opacityValue, settings.opacity); + set_DWORD(dwmKey, opacityValue, opacity); // ********************************************* // Create Aero intensity values @@ -346,7 +376,7 @@ void WriteNewColor(int opacity = -1) { getColorBalances ( - (opacity * 0.01) * (217.0 - 26.0) + 26.0, + valueTo255(opacity), colour, afterglow, blur @@ -363,9 +393,10 @@ void WriteNewColor(int opacity = -1) set_DWORD(dwmKey, L"og_ColorizationBlurBalance", blur); // Other registry values - if (exists_DWORD(dwmKey, L"GlassOpacity")) set_DWORD(dwmKey, L"GlassOpacity", 0); + set_DWORD(dwmKey, L"GlassOpacity", settings.boolTransparency ? 0 : 100); + set_DWORD(dwmKey, L"GlassType", settings.boolTransparency ? 1 : 0); - SyncOpacity(); + if (sync) SyncOpacity(); PostMessage(FindWindow(TEXT("dwm"), nullptr), WM_DWMCOLORIZATIONCOLORCHANGED, 0, 0); } @@ -375,6 +406,8 @@ StrToId_T StrToID; // Pointers to DUI elements intptr_t intensitySlider = 0; +intptr_t okButton = 0; +intptr_t cancelButton = 0; typedef unsigned short(*THISCALL Element_GetID_T)(class Element*, void*); Element_GetID_T Element_GetID; @@ -391,13 +424,19 @@ void THISCALL Element_OnPropertyChanged_hook(class Element* This, class Property { intensitySlider = reinterpret_cast(This); } - else + else if (okButton != id && id == StrToID((unsigned const short*)L"OkButton")) + { + okButton = reinterpret_cast(This); + SyncOpacity(); + } + else if (cancelButton != id && id == StrToID((unsigned const short*)L"CancelButton")) { + cancelButton = reinterpret_cast(This); SyncOpacity(); } } -long(*THISCALL CCTrackBar_SetThumbPosition)(class CCTrackBar*, int); +long (*THISCALL CCTrackBar_SetThumbPosition)(class CCTrackBar*, int); long THISCALL CCTrackBar_SetThumbPosition_hook(class CCTrackBar* This, int value) { intptr_t current = reinterpret_cast(This); @@ -405,15 +444,38 @@ long THISCALL CCTrackBar_SetThumbPosition_hook(class CCTrackBar* This, int value // Track bar value if (current > 0 && current == intensitySlider) { - settings.opacity = value; - WriteNewColor(valueTo100(value)); + opacity = value; + WriteNewColor(false); } return CCTrackBar_SetThumbPosition(This, value); } + +typedef void (*THISCALL CCPushButton_OnSelectedPropertyChanged_T)(class CCPushButton*, void*); +CCPushButton_OnSelectedPropertyChanged_T CCPushButton_OnSelectedPropertyChanged; +void THISCALL CCPushButton_OnSelectedPropertyChanged_hook(class CCPushButton* This, void* that) +{ + intptr_t current = reinterpret_cast(This); + + // OK button + if (current > 0 && current == okButton) + { + settings.opacity = opacity; + SyncOpacity(); + } + + // Cancel button + else if (current > 0 && current == cancelButton) + { + opacity = settings.opacity; + WriteNewColor(true); + if (intensitySlider > 0) CCTrackBar_SetThumbPosition(reinterpret_cast(intensitySlider), opacity); + } + + CCPushButton_OnSelectedPropertyChanged(This, that); +} #pragma endregion -// WindhawkUtils::SYMBOL_HOOK dui70dll_hooks[] = { { {STRTOID_NAME}, @@ -435,6 +497,41 @@ WindhawkUtils::SYMBOL_HOOK dui70dll_hooks[] = { {L"public: long " STHISCALL " DirectUI::CCTrackBar::SetThumbPosition(int)"}, (void**)&CCTrackBar_SetThumbPosition, (void*)CCTrackBar_SetThumbPosition_hook + }, + + { + {L"public: virtual void " STHISCALL " DirectUI::CCPushButton::OnSelectedPropertyChanged(void)"}, + (void**)&CCPushButton_OnSelectedPropertyChanged, + (void*)&CCPushButton_OnSelectedPropertyChanged_hook + } +}; + +#pragma region ## ThemeUI hooks ## +enum DWMPGLASSATTRIBUTE : INT +{ + DWMPGA_TRANSPARENCY_ALLOWED = 0x0, + DWMPGA_TRANSPARENCY_DISALLOWED = 0x1, + DWMPGA_NO_GLASS = 0x2, + DWMPGA_LAST = 0x3, +}; + +long (*STDCALL SetDwmColorizationColor)(unsigned long, enum DWMPGLASSATTRIBUTE,int); +long STDCALL SetDwmColorizationColor_hook(unsigned long color, enum DWMPGLASSATTRIBUTE attribute,int integer) +{ + if (settings.boolSyncDWM) + { + settings.opacity = opacity = getOpacityFromColor(color, true); + WriteNewColor(false); + } + return SetDwmColorizationColor(color, attribute, integer); +} +#pragma endregion + +WindhawkUtils::SYMBOL_HOOK themeuidll_hooks[] = { + { + {L"long " SSTDCALL " SetDwmColorizationColor(unsigned long,enum DWMPGLASSATTRIBUTE,int)"}, + (void**)&SetDwmColorizationColor, + (void*)SetDwmColorizationColor_hook } }; @@ -475,7 +572,7 @@ BOOL LoadSettings() if (!exists_DWORD(dwmKey, opacityValue)) return FALSE; } - settings.boolTransparency = Wh_GetIntSetting(L"transparency"); + settings.boolTransparency = TRUE; settings.boolSyncDWM = Wh_GetIntSetting(L"syncDWM"); return TRUE; @@ -498,7 +595,7 @@ BOOL Wh_ModInit() { std::wstring username = read_SZ(L"Volatile Environment", L"USERNAME", L"???"); if (username == L"???") { - Wh_Log(L"Warning: Local username not detected, unloading"); + Wh_Log(L"Local username not detected, unloading"); return FALSE; } @@ -509,23 +606,35 @@ BOOL Wh_ModInit() { } HMODULE hDui = LoadLibraryW(L"dui70.dll"); - if (!hDui) + if (hDui) { - Wh_Log(L"Failed to load dui70.dll"); - return FALSE; + if (!WindhawkUtils::HookSymbols(hDui, dui70dll_hooks, ARRAYSIZE(dui70dll_hooks))) + { + Wh_Log(L"Failed to hook symbols from dui70.dll"); + return FALSE; + } } - if (!WindhawkUtils::HookSymbols(hDui, dui70dll_hooks, ARRAYSIZE(dui70dll_hooks))) + else { - Wh_Log(L"Failed to hook DUI symbols"); + Wh_Log(L"Failed to load dui70.dll"); return FALSE; } - if (exists_DWORD(dwmKey, L"GlassType") && read_DWORD(dwmKey, L"GlassType") == 0) + HMODULE hThemeUi = LoadLibraryW(L"themeui.dll"); + if (hThemeUi) { - Wh_Log(L"Glass type detected as Blur, automatically setting to Aero"); - set_DWORD(dwmKey, L"GlassType", 1); + if (!WindhawkUtils::HookSymbols(hThemeUi, themeuidll_hooks, ARRAYSIZE(themeuidll_hooks))) + { + Wh_Log(L"Failed to hook symbols from themeui.dll"); + return FALSE; + } + } + else + { + Wh_Log(L"Failed to load themeui.dll"); + return FALSE; } - WriteNewColor(); + WriteNewColor(true); return TRUE; } \ No newline at end of file