Files
PowerToys/src/modules/LightSwitch/LightSwitchLib/ThemeHelper.cpp
Gleb Khmyznikov 53c5e66cce [UI Tests][Light Switch] Fix tests: LS, Hosts, Worspaces, Mouse (#44754)
This pull request refactors the LightSwitch module to introduce a new
shared static library, `LightSwitchLib`, which centralizes theme
management logic and enables code sharing between the service and module
interface. The changes move theme-related code from the service and
module interface into this new library, update project references, and
clean up now-redundant files and includes.

**LightSwitchLib introduction and code deduplication:**

- Added a new static library project, `LightSwitchLib`, containing all
theme management logic (`SetSystemTheme`, `SetAppsTheme`,
`GetCurrentSystemTheme`, `GetCurrentAppsTheme`, `IsNightLightEnabled`)
and related files (`ThemeHelper.cpp`, `ThemeHelper.h`, `pch.h`,
`pch.cpp`). This code was previously duplicated in both the service and
module interface.
(`src/modules/LightSwitch/LightSwitchLib/LightSwitchLib.vcxproj`
[[1]](diffhunk://#diff-c38e95060ad294c9ed5c2bb769616bb52032a4330af7e268ad63d81a99dc1cadR1-R123)
`LightSwitchLib.vcxproj.filters`
[[2]](diffhunk://#diff-fcfc49f1628c274cd9a40aca385e03a1937f9e42958298f36155ad16a267ba9aR1-R33)
`ThemeHelper.cpp`
[[3]](diffhunk://#diff-f5ab83c022406172501172ee88e21294c7aba2a87fb30334d7c4d4fc9d736a56L1-R3)
`ThemeHelper.h`
[[4]](diffhunk://#diff-6609a7fc7abc61d4d0029f0fb605a9f4732511642af6e12851e86c234108169aR1-R10)
`pch.h`
[[5]](diffhunk://#diff-57e4d6ddad1d356a24555bce4d6cbb0d6a93386515254abf95573324454c94c2R1-R5)
`pch.cpp`
[[6]](diffhunk://#diff-87fbf215a559e7833ec06ff32aa7f8109fdf86d92b360fe44302fc16f1784d52R1)

- Updated the solution file and relevant project files to add and
reference `LightSwitchLib` from both `LightSwitchService` and
`LightSwitchModuleInterface`, ensuring both components use the shared
implementation. (`PowerToys.slnx`
[[1]](diffhunk://#diff-40c552fef4118125c3ccd6b156db518acec74b11150b193b31f18a2cc17a531eR668)
`LightSwitchService.vcxproj`
[[2]](diffhunk://#diff-51f54bd015aa96b38ddf4e96134ea542fac4b648566a23c2c86fe91a2b5a6bdaR58)
[[3]](diffhunk://#diff-51f54bd015aa96b38ddf4e96134ea542fac4b648566a23c2c86fe91a2b5a6bdaR113-R115)
`LightSwitchModuleInterface.vcxproj`
[[4]](diffhunk://#diff-72e859ee44b3f0087018e55708e850fb5040c5b8f72449d1cac30e8efb28e2c2R205-R207)
[[5]](diffhunk://#diff-72e859ee44b3f0087018e55708e850fb5040c5b8f72449d1cac30e8efb28e2c2L169-R179)

**Cleanup and removal of redundant code:**

- Removed old theme management code and headers from
`LightSwitchService` and `LightSwitchModuleInterface` now that logic
resides in `LightSwitchLib`. (`ThemeHelper.cpp`
[[1]](diffhunk://#diff-3e2766504c1cf989390508c613b2177cd5de14fb9de46df3b416f95f955338bfL1-L106)
`ThemeHelper.h`
[[2]](diffhunk://#diff-0e8540cace398ec3eebca416ca38d81262b689eca76a004584e686a605b7a242L1-L5)
`LightSwitchService.vcxproj`
[[3]](diffhunk://#diff-51f54bd015aa96b38ddf4e96134ea542fac4b648566a23c2c86fe91a2b5a6bdaL81)
[[4]](diffhunk://#diff-51f54bd015aa96b38ddf4e96134ea542fac4b648566a23c2c86fe91a2b5a6bdaL96)
`LightSwitchModuleInterface.vcxproj`
[[5]](diffhunk://#diff-72e859ee44b3f0087018e55708e850fb5040c5b8f72449d1cac30e8efb28e2c2L190)

- Removed duplicated registry path constants from `SettingsConstants.h`,
as they are now defined in the shared header. (`SettingsConstants.h`
[src/modules/LightSwitch/LightSwitchService/SettingsConstants.hL15-L17](diffhunk://#diff-e74db005ffb8b881a08c4dae1c1ead9dc732928a69cafb4c9e0bae8b86d4e24aL15-L17))

**Module interface improvements:**

- Added `ExportedFunctions.cpp` to the module interface, exposing theme
management functions as exports and using the shared library
implementation. (`ExportedFunctions.cpp`
[[1]](diffhunk://#diff-48acf3b77a8b6ac6fd1129afe1a677b34447ce39454e86ea04f1a1181a23b546R1-R22)
`LightSwitchModuleInterface.vcxproj`
[[2]](diffhunk://#diff-72e859ee44b3f0087018e55708e850fb5040c5b8f72449d1cac30e8efb28e2c2L169-R179)

**Minor test and logging adjustments:**

- Fixed a UI test to use the correct toggle name for the Hosts File
Editor. (`HostsSettingTests.cs`
[src/modules/Hosts/Hosts.UITests/HostsSettingTests.csL116-R116](diffhunk://#diff-3782109c99cd66a2c1b870a83d1f9d9807422479c89e03799b311ef5f13a2098L116-R116))
- Updated a log message to refer to `LightSwitchLib` instead of
`LightSwitchService` for clarity. (`ThemeHelper.cpp`
[src/modules/LightSwitch/LightSwitchLib/ThemeHelper.cppL66-R63](diffhunk://#diff-f5ab83c022406172501172ee88e21294c7aba2a87fb30334d7c4d4fc9d736a56L66-R63))
2026-01-16 13:58:51 -08:00

137 lines
4.3 KiB
C++

#include "pch.h"
#include "ThemeHelper.h"
#include <logger/logger.h>
// Controls changing the themes.
static void ResetColorPrevalence()
{
HKEY hKey;
if (RegOpenKeyEx(HKEY_CURRENT_USER,
PERSONALIZATION_REGISTRY_PATH,
0,
KEY_SET_VALUE,
&hKey) == ERROR_SUCCESS)
{
DWORD value = 0; // back to default value
RegSetValueEx(hKey, L"ColorPrevalence", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value));
RegCloseKey(hKey);
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast<LPARAM>(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr);
SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr);
SendMessageTimeout(HWND_BROADCAST, WM_DWMCOLORIZATIONCOLORCHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr);
}
}
void SetAppsTheme(bool mode)
{
HKEY hKey;
if (RegOpenKeyEx(HKEY_CURRENT_USER,
PERSONALIZATION_REGISTRY_PATH,
0,
KEY_SET_VALUE,
&hKey) == ERROR_SUCCESS)
{
DWORD value = mode;
RegSetValueEx(hKey, L"AppsUseLightTheme", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value));
RegCloseKey(hKey);
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast<LPARAM>(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr);
SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr);
}
}
void SetSystemTheme(bool mode)
{
HKEY hKey;
if (RegOpenKeyEx(HKEY_CURRENT_USER,
PERSONALIZATION_REGISTRY_PATH,
0,
KEY_SET_VALUE,
&hKey) == ERROR_SUCCESS)
{
DWORD value = mode;
RegSetValueEx(hKey, L"SystemUsesLightTheme", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value));
RegCloseKey(hKey);
if (mode) // if are changing to light mode
{
ResetColorPrevalence();
Logger::info(L"[LightSwitchLib] Reset ColorPrevalence to default when switching to light mode.");
}
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast<LPARAM>(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr);
SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr);
}
}
// Can think of this as "is the current theme light?"
bool GetCurrentSystemTheme()
{
HKEY hKey;
DWORD value = 1; // default = light
DWORD size = sizeof(value);
if (RegOpenKeyEx(HKEY_CURRENT_USER,
PERSONALIZATION_REGISTRY_PATH,
0,
KEY_READ,
&hKey) == ERROR_SUCCESS)
{
RegQueryValueEx(hKey, L"SystemUsesLightTheme", nullptr, nullptr, reinterpret_cast<LPBYTE>(&value), &size);
RegCloseKey(hKey);
}
return value == 1; // true = light, false = dark
}
bool GetCurrentAppsTheme()
{
HKEY hKey;
DWORD value = 1;
DWORD size = sizeof(value);
if (RegOpenKeyEx(HKEY_CURRENT_USER,
PERSONALIZATION_REGISTRY_PATH,
0,
KEY_READ,
&hKey) == ERROR_SUCCESS)
{
RegQueryValueEx(hKey, L"AppsUseLightTheme", nullptr, nullptr, reinterpret_cast<LPBYTE>(&value), &size);
RegCloseKey(hKey);
}
return value == 1; // true = light, false = dark
}
bool IsNightLightEnabled()
{
HKEY hKey;
const wchar_t* path = NIGHT_LIGHT_REGISTRY_PATH;
if (RegOpenKeyExW(HKEY_CURRENT_USER, path, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
return false;
// RegGetValueW will set size to the size of the data and we expect that to be at least 25 bytes (we need to access bytes 23 and 24)
DWORD size = 0;
if (RegGetValueW(hKey, nullptr, L"Data", RRF_RT_REG_BINARY, nullptr, nullptr, &size) != ERROR_SUCCESS || size < 25)
{
RegCloseKey(hKey);
return false;
}
std::vector<BYTE> data(size);
if (RegGetValueW(hKey, nullptr, L"Data", RRF_RT_REG_BINARY, nullptr, data.data(), &size) != ERROR_SUCCESS)
{
RegCloseKey(hKey);
return false;
}
RegCloseKey(hKey);
return data[23] == 0x10 && data[24] == 0x00;
}