mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-20 10:10:10 +01:00
Compare commits
1 Commits
async-cpp-
...
revert-445
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
943002bdeb |
7
.github/actions/spell-check/expect.txt
vendored
7
.github/actions/spell-check/expect.txt
vendored
@@ -457,6 +457,7 @@ DWMWINDOWATTRIBUTE
|
||||
DWMWINDOWMAXIMIZEDCHANGE
|
||||
DWORDLONG
|
||||
dworigin
|
||||
DWPOS
|
||||
dwrite
|
||||
dxgi
|
||||
eab
|
||||
@@ -677,6 +678,7 @@ hicon
|
||||
HICONSM
|
||||
HIDEREADONLY
|
||||
HIDEWINDOW
|
||||
hif
|
||||
Hif
|
||||
HIMAGELIST
|
||||
himl
|
||||
@@ -838,6 +840,7 @@ jpe
|
||||
jpnime
|
||||
Jsons
|
||||
jsonval
|
||||
jxl
|
||||
jxr
|
||||
keybd
|
||||
KEYBDDATA
|
||||
@@ -1464,6 +1467,7 @@ recyclebin
|
||||
Redist
|
||||
Reencode
|
||||
REFCLSID
|
||||
REFGUID
|
||||
REFIID
|
||||
REGCLS
|
||||
regfile
|
||||
@@ -1570,6 +1574,7 @@ SETBUDDYINT
|
||||
SETCONTEXT
|
||||
SETCURSEL
|
||||
setcursor
|
||||
SETDESKWALLPAPER
|
||||
SETFOCUS
|
||||
SETFOREGROUND
|
||||
SETHOTKEY
|
||||
@@ -1658,6 +1663,7 @@ SKEXP
|
||||
SKIPOWNPROCESS
|
||||
sku
|
||||
SLGP
|
||||
slideshow
|
||||
sln
|
||||
slnf
|
||||
slnx
|
||||
@@ -1896,6 +1902,7 @@ unwide
|
||||
unzoom
|
||||
UOffset
|
||||
UOI
|
||||
UPDATEINIFILE
|
||||
UPDATENOW
|
||||
UPDATEREGISTRY
|
||||
updown
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
enum class SettingId
|
||||
{
|
||||
ScheduleMode = 0,
|
||||
Latitude,
|
||||
Longitude,
|
||||
LightTime,
|
||||
DarkTime,
|
||||
Sunrise_Offset,
|
||||
Sunset_Offset,
|
||||
ChangeSystem,
|
||||
ChangeApps,
|
||||
WallpaperEnabled,
|
||||
WallpaperVirtualDesktopEnabled,
|
||||
WallpaperStyleLight,
|
||||
WallpaperStyleDark,
|
||||
WallpaperPathLight,
|
||||
WallpaperPathDark
|
||||
};
|
||||
|
||||
inline constexpr wchar_t PERSONALIZATION_REGISTRY_PATH[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
|
||||
inline constexpr wchar_t NIGHT_LIGHT_REGISTRY_PATH[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\CloudStore\\Store\\DefaultAccount\\Current\\default$windows.data.bluelightreduction.bluelightreductionstate\\windows.data.bluelightreduction.bluelightreductionstate";
|
||||
370
src/modules/LightSwitch/LightSwitchCommon/ThemeHelper.cpp
Normal file
370
src/modules/LightSwitch/LightSwitchCommon/ThemeHelper.cpp
Normal file
@@ -0,0 +1,370 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <logger/logger_settings.h>
|
||||
#include <logger/logger.h>
|
||||
#include <utils/logger_helper.h>
|
||||
#include "ThemeHelper.h"
|
||||
#include <wil/resource.h>
|
||||
#include "SettingsConstants.h"
|
||||
|
||||
static auto RegKeyGuard(HKEY& hKey) noexcept
|
||||
{
|
||||
return wil::scope_exit([&hKey]() {
|
||||
if (hKey == nullptr)
|
||||
return;
|
||||
if (RegCloseKey(hKey) != ERROR_SUCCESS)
|
||||
std::terminate();
|
||||
});
|
||||
}
|
||||
|
||||
// Controls changing the themes.
|
||||
static void ResetColorPrevalence() noexcept
|
||||
{
|
||||
HKEY hKey{};
|
||||
auto closeKey = RegKeyGuard(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));
|
||||
|
||||
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) noexcept
|
||||
{
|
||||
HKEY hKey{};
|
||||
auto closeKey = RegKeyGuard(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));
|
||||
|
||||
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) noexcept
|
||||
{
|
||||
HKEY hKey{};
|
||||
auto closeKey = RegKeyGuard(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));
|
||||
|
||||
if (mode) // if are changing to light mode
|
||||
{
|
||||
ResetColorPrevalence();
|
||||
Logger::info(L"[LightSwitchService] 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() noexcept
|
||||
{
|
||||
HKEY hKey{};
|
||||
auto closeKey = RegKeyGuard(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);
|
||||
}
|
||||
|
||||
return value == 1; // true = light, false = dark
|
||||
}
|
||||
|
||||
bool GetCurrentAppsTheme() noexcept
|
||||
{
|
||||
HKEY hKey{};
|
||||
auto closeKey = RegKeyGuard(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);
|
||||
}
|
||||
|
||||
return value == 1; // true = light, false = dark
|
||||
}
|
||||
|
||||
bool IsNightLightEnabled() noexcept
|
||||
{
|
||||
HKEY hKey{};
|
||||
auto closeKey = RegKeyGuard(hKey);
|
||||
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, NIGHT_LIGHT_REGISTRY_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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<BYTE> data(size);
|
||||
if (RegGetValueW(hKey, nullptr, L"Data", RRF_RT_REG_BINARY, nullptr, data.data(), &size) != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return data[23] == 0x10 && data[24] == 0x00;
|
||||
}
|
||||
|
||||
#include <atomic>
|
||||
#include <charconv>
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
static bool GetWindowsVersionFromRegistryInternal(int& build, int& revision) noexcept
|
||||
{
|
||||
HKEY hKey{};
|
||||
auto closeKey = RegKeyGuard(hKey);
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
wchar_t buffer[11]{};
|
||||
DWORD bufferSize{ sizeof(buffer) };
|
||||
if (RegGetValueW(hKey, nullptr, L"CurrentBuildNumber", RRF_RT_REG_SZ, nullptr, static_cast<void*>(buffer), &bufferSize))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
char bufferA[11]{};
|
||||
std::transform(std::begin(buffer), std::end(buffer), std::begin(bufferA), [](auto c) { return static_cast<char>(c); });
|
||||
int bld{};
|
||||
if (std::from_chars(bufferA, bufferA + sizeof(bufferA), bld).ec != std::errc{})
|
||||
{
|
||||
return false;
|
||||
}
|
||||
DWORD rev{};
|
||||
DWORD revSize{ sizeof(rev) };
|
||||
if (RegGetValueW(hKey, nullptr, L"UBR", RRF_RT_DWORD, nullptr, &rev, &revSize) != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
revision = static_cast<int>(rev);
|
||||
build = static_cast<int>(bld);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool GetWindowsVersionFromRegistry(int& build, int& revision) noexcept
|
||||
{
|
||||
static std::atomic<int> build_cache{};
|
||||
static std::atomic<int> rev_cache{};
|
||||
|
||||
if (auto bld = build_cache.load(); bld != 0)
|
||||
{
|
||||
build = bld;
|
||||
revision = rev_cache.load();
|
||||
return true;
|
||||
}
|
||||
|
||||
int bld{};
|
||||
int rev{};
|
||||
if (auto e = GetWindowsVersionFromRegistryInternal(bld, rev); e == false)
|
||||
{
|
||||
return e;
|
||||
}
|
||||
build = bld;
|
||||
revision = rev;
|
||||
rev_cache.store(rev);
|
||||
// Write after rev_cache for condition
|
||||
build_cache.store(bld);
|
||||
return true;
|
||||
}
|
||||
|
||||
// This function will supplement the wallpaper path setting. It does not cause the wallpaper to change, but for consistency, it is better to set it
|
||||
static int SetRemainWallpaperPathRegistry(std::wstring const& wallpaperPath) noexcept
|
||||
{
|
||||
HKEY hKey{};
|
||||
auto closeKey = RegKeyGuard(hKey);
|
||||
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Wallpapers", 0, KEY_WRITE, &hKey) != ERROR_SUCCESS)
|
||||
{
|
||||
// The key may not exist after updating Windows, so it is not an error
|
||||
// The key will be created by the Settings app
|
||||
return 0;
|
||||
}
|
||||
if (RegSetValueExW(hKey, L"CurrentWallpaperPath", 0, REG_SZ, reinterpret_cast<const BYTE*>(wallpaperPath.data()), static_cast<DWORD>((wallpaperPath.size() + 1u) * sizeof(wchar_t))) != ERROR_SUCCESS)
|
||||
{
|
||||
return 0x301;
|
||||
}
|
||||
DWORD backgroundType = 0; // 0 = picture, 1 = solid color, 2 = slideshow
|
||||
if (RegSetValueExW(hKey, L"BackgroundType", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&backgroundType), static_cast<DWORD>(sizeof(DWORD))) != ERROR_SUCCESS)
|
||||
{
|
||||
return 0x302;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define COM_NO_WINDOWS_H
|
||||
#include <string>
|
||||
#include <unknwn.h>
|
||||
#include <inspectable.h>
|
||||
#include <restrictederrorinfo.h>
|
||||
#include "Shobjidl.h"
|
||||
#include <hstring.h>
|
||||
#include <winrt/base.h>
|
||||
|
||||
#pragma comment(lib, "runtimeobject.lib")
|
||||
|
||||
// COM interface definition from https://github.com/MScholtes/VirtualDesktop
|
||||
|
||||
inline constexpr GUID CLSID_ImmersiveShell{ 0xC2F03A33, 0x21F5, 0x47FA, { 0xB4, 0xBB, 0x15, 0x63, 0x62, 0xA2, 0xF2, 0x39 } };
|
||||
inline constexpr GUID CLSID_VirtualDesktopManagerInternal{ 0xC5E0CDCA, 0x7B6E, 0x41B2, { 0x9F, 0xC4, 0xD9, 0x39, 0x75, 0xCC, 0x46, 0x7B } };
|
||||
|
||||
struct __declspec(novtable) __declspec(uuid("6D5140C1-7436-11CE-8034-00AA006009FA")) IServiceProvider10 : public IUnknown
|
||||
{
|
||||
virtual HRESULT __stdcall QueryService(REFGUID service, REFIID riid, void** obj) = 0;
|
||||
};
|
||||
|
||||
#undef CreateDesktop
|
||||
|
||||
struct __declspec(novtable) __declspec(uuid("53F5CA0B-158F-4124-900C-057158060B27")) IVirtualDesktopManagerInternal24H2 : public IUnknown
|
||||
{
|
||||
virtual HRESULT __stdcall GetCount(int* count) = 0;
|
||||
virtual HRESULT __stdcall MoveViewToDesktop(IInspectable* view, IUnknown* desktop) = 0;
|
||||
virtual HRESULT __stdcall CanViewMoveDesktops(IInspectable* view, bool* result) = 0;
|
||||
virtual HRESULT __stdcall GetCurrentDesktop(IUnknown** desktop) = 0;
|
||||
virtual HRESULT __stdcall GetDesktops(IObjectArray** desktops) = 0;
|
||||
virtual HRESULT __stdcall GetAdjacentDesktop(IUnknown* from, int direction, IUnknown** desktop) = 0;
|
||||
virtual HRESULT __stdcall SwitchDesktop(IUnknown* desktop) = 0;
|
||||
virtual HRESULT __stdcall SwitchDesktopAndMoveForegroundView(IUnknown* desktop) = 0;
|
||||
virtual HRESULT __stdcall CreateDesktop(IUnknown** desktop) = 0;
|
||||
virtual HRESULT __stdcall MoveDesktop(IUnknown* desktop, int nIndex) = 0;
|
||||
virtual HRESULT __stdcall RemoveDesktop(IUnknown* desktop, IUnknown* fallback) = 0;
|
||||
virtual HRESULT __stdcall FindDesktop(const GUID* desktopId, IUnknown** desktop) = 0;
|
||||
virtual HRESULT __stdcall GetDesktopSwitchIncludeExcludeViews(IUnknown* desktop, IObjectArray** unknown1, IObjectArray** unknown2) = 0;
|
||||
virtual HRESULT __stdcall SetDesktopName(IUnknown* desktop, HSTRING name) = 0;
|
||||
virtual HRESULT __stdcall SetDesktopWallpaper(IUnknown* desktop, HSTRING path) = 0;
|
||||
virtual HRESULT __stdcall UpdateWallpaperPathForAllDesktops(HSTRING path) = 0;
|
||||
virtual HRESULT __stdcall CopyDesktopState(IInspectable* pView0, IInspectable* pView1) = 0;
|
||||
virtual HRESULT __stdcall CreateRemoteDesktop(HSTRING path, IUnknown** desktop) = 0;
|
||||
virtual HRESULT __stdcall SwitchRemoteDesktop(IUnknown* desktop, void* switchType) = 0;
|
||||
virtual HRESULT __stdcall SwitchDesktopWithAnimation(IUnknown* desktop) = 0;
|
||||
virtual HRESULT __stdcall GetLastActiveDesktop(IUnknown** desktop) = 0;
|
||||
virtual HRESULT __stdcall WaitForAnimationToComplete() = 0;
|
||||
};
|
||||
|
||||
// Using this method to set the wallpaper works across virtual desktops, but it does not provide the functionality to set the style
|
||||
static int SetWallpaperViaIVirtualDesktopManagerInternal(const std::wstring& path) noexcept
|
||||
{
|
||||
int build{};
|
||||
int revision{};
|
||||
if (!GetWindowsVersionFromRegistry(build, revision))
|
||||
{
|
||||
return 0x201;
|
||||
}
|
||||
// Unstable Windows internal API, at least 24H2 required
|
||||
if (build < 26100)
|
||||
{
|
||||
return 0x202;
|
||||
}
|
||||
auto shell = winrt::try_create_instance<IServiceProvider10>(CLSID_ImmersiveShell, CLSCTX_LOCAL_SERVER);
|
||||
if (!shell)
|
||||
{
|
||||
return 0x203;
|
||||
}
|
||||
winrt::com_ptr<IVirtualDesktopManagerInternal24H2> virtualDesktopManagerInternal;
|
||||
if (shell->QueryService(
|
||||
CLSID_VirtualDesktopManagerInternal,
|
||||
__uuidof(IVirtualDesktopManagerInternal24H2),
|
||||
virtualDesktopManagerInternal.put_void()) != S_OK)
|
||||
{
|
||||
return 0x204;
|
||||
}
|
||||
if (virtualDesktopManagerInternal->UpdateWallpaperPathForAllDesktops(static_cast<HSTRING>(winrt::detach_abi(path))) != S_OK)
|
||||
{
|
||||
return 0x205;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// After setting the wallpaper using this method, switching to other virtual desktops will cause the wallpaper to be restored
|
||||
static int SetWallpaperViaIDesktopWallpaper(const std::wstring& path, int style) noexcept
|
||||
{
|
||||
auto pos = static_cast<DESKTOP_WALLPAPER_POSITION>(style);
|
||||
switch (pos)
|
||||
{
|
||||
case DWPOS_CENTER:
|
||||
case DWPOS_TILE:
|
||||
case DWPOS_STRETCH:
|
||||
case DWPOS_FIT:
|
||||
case DWPOS_FILL:
|
||||
case DWPOS_SPAN:
|
||||
break;
|
||||
default:
|
||||
std::terminate();
|
||||
}
|
||||
auto desktopWallpaper = winrt::try_create_instance<IDesktopWallpaper>(__uuidof(DesktopWallpaper), CLSCTX_LOCAL_SERVER);
|
||||
if (!desktopWallpaper)
|
||||
{
|
||||
return 0x301;
|
||||
}
|
||||
if (desktopWallpaper->SetPosition(pos) != S_OK)
|
||||
{
|
||||
return 0x302;
|
||||
}
|
||||
if (desktopWallpaper->SetWallpaper(nullptr, path.c_str()) != S_OK)
|
||||
{
|
||||
return 0x303;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SetDesktopWallpaper(const std::wstring& path, int style, bool virtualDesktop) noexcept
|
||||
{
|
||||
if (virtualDesktop)
|
||||
{
|
||||
if (auto e = SetWallpaperViaIVirtualDesktopManagerInternal(path); e != 0)
|
||||
{
|
||||
return e;
|
||||
}
|
||||
}
|
||||
if (auto e = SetWallpaperViaIDesktopWallpaper(path, style); e != 0)
|
||||
{
|
||||
return e;
|
||||
}
|
||||
if (auto e = SetRemainWallpaperPathRegistry(path); e != 0)
|
||||
{
|
||||
return e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
9
src/modules/LightSwitch/LightSwitchCommon/ThemeHelper.h
Normal file
9
src/modules/LightSwitch/LightSwitchCommon/ThemeHelper.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
void SetSystemTheme(bool dark) noexcept;
|
||||
void SetAppsTheme(bool dark) noexcept;
|
||||
bool GetCurrentSystemTheme() noexcept;
|
||||
bool GetCurrentAppsTheme() noexcept;
|
||||
bool IsNightLightEnabled() noexcept;
|
||||
// Returned 0 indicates success; otherwise, the reason is returned, see definition
|
||||
int SetDesktopWallpaper(std::wstring const& wallpaperPath, int style, bool virtualDesktop) noexcept;
|
||||
@@ -166,13 +166,14 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\LightSwitchCommon;..\..\..\common;..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="ThemeHelper.h" />
|
||||
<ClInclude Include="..\LightSwitchCommon\ThemeHelper.h" />
|
||||
<ClInclude Include="..\LightSwitchCommon\SettingsConstants.h" />
|
||||
<ClInclude Include="trace.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -187,7 +188,12 @@
|
||||
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ThemeHelper.cpp" />
|
||||
<ClCompile Include="..\LightSwitchCommon\ThemeHelper.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="trace.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -210,6 +216,7 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<ClCompile Include="trace.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ThemeHelper.cpp">
|
||||
<ClCompile Include="..\LightSwitchCommon\ThemeHelper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
@@ -24,7 +24,10 @@
|
||||
<ClInclude Include="trace.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ThemeHelper.h">
|
||||
<ClInclude Include="..\LightSwitchCommon\ThemeHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\LightSwitchCommon\SettingsConstants.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
@@ -47,4 +50,7 @@
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,106 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include <windows.h>
|
||||
#include "ThemeHelper.h"
|
||||
|
||||
// Controls changing the themes.
|
||||
static void ResetColorPrevalence()
|
||||
{
|
||||
HKEY hKey;
|
||||
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
||||
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
|
||||
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,
|
||||
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
|
||||
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,
|
||||
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
bool GetCurrentSystemTheme()
|
||||
{
|
||||
HKEY hKey;
|
||||
DWORD value = 1; // default = light
|
||||
DWORD size = sizeof(value);
|
||||
|
||||
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
||||
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
|
||||
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,
|
||||
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
|
||||
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
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
void SetSystemTheme(bool dark);
|
||||
void SetAppsTheme(bool dark);
|
||||
bool GetCurrentSystemTheme();
|
||||
bool GetCurrentAppsTheme();
|
||||
@@ -94,6 +94,12 @@ struct ModuleSettings
|
||||
int m_sunset_offset = 0;
|
||||
std::wstring m_latitude = L"0.0";
|
||||
std::wstring m_longitude = L"0.0";
|
||||
bool m_wallpaper = false;
|
||||
bool m_wallpaper_virtual_desktop = false;
|
||||
int m_wallpaper_style_light = 0;
|
||||
int m_wallpaper_style_dark = 0;
|
||||
std::wstring m_wallpaper_path_light;
|
||||
std::wstring m_wallpaper_path_dark;
|
||||
} g_settings;
|
||||
|
||||
class LightSwitchInterface : public PowertoyModuleIface
|
||||
@@ -351,6 +357,30 @@ public:
|
||||
{
|
||||
g_settings.m_longitude = *v;
|
||||
}
|
||||
if (auto v = values.get_bool_value(L"wallpaperEnabled"))
|
||||
{
|
||||
g_settings.m_wallpaper = *v;
|
||||
}
|
||||
if (auto v = values.get_bool_value(L"wallpaperVirtualDesktopEnabled"))
|
||||
{
|
||||
g_settings.m_wallpaper_virtual_desktop = *v;
|
||||
}
|
||||
if (auto v = values.get_int_value(L"wallpaperStyleLight"))
|
||||
{
|
||||
g_settings.m_wallpaper_style_light = *v;
|
||||
}
|
||||
if (auto v = values.get_int_value(L"wallpaperStyleDark"))
|
||||
{
|
||||
g_settings.m_wallpaper_style_dark = *v;
|
||||
}
|
||||
if (auto v = values.get_string_value(L"wallpaperPathLight"))
|
||||
{
|
||||
g_settings.m_wallpaper_path_light = *v;
|
||||
}
|
||||
if (auto v = values.get_string_value(L"wallpaperPathDark"))
|
||||
{
|
||||
g_settings.m_wallpaper_path_dark = *v;
|
||||
}
|
||||
|
||||
values.save_to_settings_file();
|
||||
}
|
||||
@@ -566,15 +596,53 @@ public:
|
||||
|
||||
};
|
||||
|
||||
static bool IsValidPath(const std::wstring& path)
|
||||
{
|
||||
std::error_code ec;
|
||||
return !path.empty() && std::filesystem::exists(path, ec);
|
||||
}
|
||||
|
||||
void LightSwitchInterface::ToggleTheme()
|
||||
{
|
||||
bool current_system_theme = GetCurrentSystemTheme();
|
||||
bool current_apps_theme = GetCurrentAppsTheme();
|
||||
|
||||
if (g_settings.m_changeSystem)
|
||||
{
|
||||
SetSystemTheme(!GetCurrentSystemTheme());
|
||||
SetSystemTheme(!current_system_theme);
|
||||
}
|
||||
if (g_settings.m_changeApps)
|
||||
{
|
||||
SetAppsTheme(!GetCurrentAppsTheme());
|
||||
SetAppsTheme(!current_apps_theme);
|
||||
}
|
||||
|
||||
bool new_system_theme = GetCurrentSystemTheme();
|
||||
bool new_apps_theme = GetCurrentAppsTheme();
|
||||
|
||||
bool changeWallpaper =
|
||||
g_settings.m_wallpaper &&
|
||||
IsValidPath(g_settings.m_wallpaper_path_light) &&
|
||||
IsValidPath(g_settings.m_wallpaper_path_dark);
|
||||
|
||||
// if something changed and wallpaper change is enabled and paths are valid
|
||||
if ((new_system_theme != current_system_theme || new_apps_theme != current_apps_theme) && changeWallpaper)
|
||||
{
|
||||
bool shouldBeLight;
|
||||
|
||||
if (g_settings.m_changeSystem && new_system_theme != current_system_theme)
|
||||
{
|
||||
shouldBeLight = new_system_theme;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Either apps-only changed, or system didn't move
|
||||
shouldBeLight = new_apps_theme;
|
||||
}
|
||||
|
||||
auto&& wallpaperPath = shouldBeLight ? g_settings.m_wallpaper_path_light : g_settings.m_wallpaper_path_dark;
|
||||
auto style = shouldBeLight ? g_settings.m_wallpaper_style_light : g_settings.m_wallpaper_style_dark;
|
||||
|
||||
SetDesktopWallpaper(wallpaperPath, style, g_settings.m_wallpaper_virtual_desktop);
|
||||
}
|
||||
|
||||
if (!m_manual_override_event_handle)
|
||||
@@ -693,6 +761,18 @@ void LightSwitchInterface::init_settings()
|
||||
g_settings.m_latitude = *v;
|
||||
if (auto v = settings.get_string_value(L"longitude"))
|
||||
g_settings.m_longitude = *v;
|
||||
if (auto v = settings.get_bool_value(L"wallpaperEnabled"))
|
||||
g_settings.m_wallpaper = *v;
|
||||
if (auto v = settings.get_bool_value(L"wallpaperVirtualDesktopEnabled"))
|
||||
g_settings.m_wallpaper_virtual_desktop = *v;
|
||||
if (auto v = settings.get_int_value(L"wallpaperStyleLight"))
|
||||
g_settings.m_wallpaper_style_light = *v;
|
||||
if (auto v = settings.get_int_value(L"wallpaperStyleDark"))
|
||||
g_settings.m_wallpaper_style_dark = *v;
|
||||
if (auto v = settings.get_string_value(L"wallpaperPathLight"))
|
||||
g_settings.m_wallpaper_path_light = *v;
|
||||
if (auto v = settings.get_string_value(L"wallpaperPathDark"))
|
||||
g_settings.m_wallpaper_path_dark = *v;
|
||||
|
||||
Logger::info(L"[Light Switch] init_settings: loaded successfully");
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ static LightSwitchStateManager* g_stateManagerPtr = nullptr;
|
||||
VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv);
|
||||
VOID WINAPI ServiceCtrlHandler(DWORD dwCtrl);
|
||||
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam);
|
||||
void ApplyTheme(bool shouldBeLight);
|
||||
void ApplyTheme(bool shouldBeLight, bool changeWallpaper);
|
||||
|
||||
// Entry point for the executable
|
||||
int _tmain(int argc, TCHAR* argv[])
|
||||
@@ -125,9 +125,29 @@ VOID WINAPI ServiceCtrlHandler(DWORD dwCtrl)
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyTheme(bool shouldBeLight)
|
||||
void SetWallpaper(bool shouldBeLight)
|
||||
{
|
||||
const auto& settings = LightSwitchSettings::settings();
|
||||
|
||||
if (settings.wallpaperEnabled)
|
||||
{
|
||||
std::wstring const& wallpaperPath = shouldBeLight ? settings.wallpaperPathLight : settings.wallpaperPathDark;
|
||||
auto style = shouldBeLight ? settings.wallpaperStyleLight : settings.wallpaperStyleDark;
|
||||
if (auto e = SetDesktopWallpaper(wallpaperPath, style, settings.wallpaperVirtualDesktop) == 0)
|
||||
{
|
||||
Logger::info(L"[LightSwitchService] Wallpaper is changed to {}.", wallpaperPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::error(L"[LightSwitchService] Failed to set wallpaper, error: {}.", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void ApplyTheme(bool shouldBeLight, bool changeWallpaper)
|
||||
{
|
||||
const auto& s = LightSwitchSettings::settings();
|
||||
bool somethingChanged = false;
|
||||
|
||||
if (s.changeSystem)
|
||||
{
|
||||
@@ -136,6 +156,7 @@ void ApplyTheme(bool shouldBeLight)
|
||||
{
|
||||
SetSystemTheme(shouldBeLight);
|
||||
Logger::info(L"[LightSwitchService] Changed system theme to {}.", shouldBeLight ? L"light" : L"dark");
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +167,15 @@ void ApplyTheme(bool shouldBeLight)
|
||||
{
|
||||
SetAppsTheme(shouldBeLight);
|
||||
Logger::info(L"[LightSwitchService] Changed apps theme to {}.", shouldBeLight ? L"light" : L"dark");
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (somethingChanged)
|
||||
{
|
||||
if (changeWallpaper)
|
||||
{
|
||||
SetWallpaper(shouldBeLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,7 +205,7 @@ static void DetectAndHandleExternalThemeChange(LightSwitchStateManager& stateMan
|
||||
if (s.scheduleMode == ScheduleMode::FollowNightLight)
|
||||
{
|
||||
shouldBeLight = !IsNightLightEnabled();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldBeLight = ShouldBeLight(nowMinutes, effectiveLight, effectiveDark);
|
||||
|
||||
@@ -53,18 +53,7 @@
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>
|
||||
./../;
|
||||
..\..\..\common;
|
||||
..\..\..\common\logger;
|
||||
..\..\..\common\utils;
|
||||
..\..\..\common\SettingsAPI;
|
||||
..\..\..\common\Telemetry;
|
||||
..\..\..\;
|
||||
..\..\..\..\deps\spdlog\include;
|
||||
./;
|
||||
%(AdditionalIncludeDirectories)
|
||||
</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\LightSwitchCommon;.\..\;..\..\..\common;..\..\..\common\logger;..\..\..\common\utils;..\..\..\common\SettingsAPI;..\..\..\common\Telemetry;..\..\..\;..\..\..\..\deps\spdlog\include;./;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
@@ -77,8 +66,7 @@
|
||||
<ClCompile Include="LightSwitchSettings.cpp" />
|
||||
<ClCompile Include="LightSwitchStateManager.cpp" />
|
||||
<ClCompile Include="NightLightRegistryObserver.cpp" />
|
||||
<ClCompile Include="SettingsConstants.cpp" />
|
||||
<ClCompile Include="ThemeHelper.cpp" />
|
||||
<ClCompile Include="..\LightSwitchCommon\ThemeHelper.cpp" />
|
||||
<ClCompile Include="ThemeScheduler.cpp" />
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<ClCompile Include="WinHookEventIDs.cpp" />
|
||||
@@ -91,9 +79,9 @@
|
||||
<ClInclude Include="LightSwitchStateManager.h" />
|
||||
<ClInclude Include="LightSwitchUtils.h" />
|
||||
<ClInclude Include="NightLightRegistryObserver.h" />
|
||||
<ClInclude Include="SettingsConstants.h" />
|
||||
<ClInclude Include="SettingsObserver.h" />
|
||||
<ClInclude Include="ThemeHelper.h" />
|
||||
<ClInclude Include="..\LightSwitchCommon\ThemeHelper.h" />
|
||||
<ClInclude Include="..\LightSwitchCommon\SettingsConstants.h" />
|
||||
<ClInclude Include="ThemeScheduler.h" />
|
||||
<ClInclude Include="trace.h" />
|
||||
<ClInclude Include="WinHookEventIDs.h" />
|
||||
|
||||
@@ -21,15 +21,12 @@
|
||||
<ClCompile Include="ThemeScheduler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ThemeHelper.cpp">
|
||||
<ClCompile Include="..\LightSwitchCommon\ThemeHelper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LightSwitchSettings.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SettingsConstants.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WinHookEventIDs.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -50,10 +47,10 @@
|
||||
<ClInclude Include="ThemeHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LightSwitchSettings.h">
|
||||
<ClInclude Include="..\LightSwitchCommon\LightSwitchSettings.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SettingsConstants.h">
|
||||
<ClInclude Include="..\LightSwitchCommon\SettingsConstants.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SettingsObserver.h">
|
||||
|
||||
@@ -253,6 +253,66 @@ void LightSwitchSettings::LoadSettings()
|
||||
{
|
||||
Trace::LightSwitch::ThemeTargetChanged(m_settings.changeApps, m_settings.changeSystem);
|
||||
}
|
||||
|
||||
if (const auto jsonVal = values.get_bool_value(L"wallpaperEnabled"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
if (m_settings.wallpaperEnabled != val)
|
||||
{
|
||||
m_settings.wallpaperEnabled = val;
|
||||
NotifyObservers(SettingId::WallpaperEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto jsonVal = values.get_bool_value(L"wallpaperVirtualDesktopEnabled"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
if (m_settings.wallpaperVirtualDesktop != val)
|
||||
{
|
||||
m_settings.wallpaperVirtualDesktop = val;
|
||||
NotifyObservers(SettingId::WallpaperVirtualDesktopEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto jsonVal = values.get_int_value(L"wallpaperStyleLight"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
if (m_settings.wallpaperStyleLight != val)
|
||||
{
|
||||
m_settings.wallpaperStyleLight = val;
|
||||
NotifyObservers(SettingId::WallpaperStyleLight);
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto jsonVal = values.get_int_value(L"wallpaperStyleDark"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
if (m_settings.wallpaperStyleDark != val)
|
||||
{
|
||||
m_settings.wallpaperStyleDark = val;
|
||||
NotifyObservers(SettingId::WallpaperStyleDark);
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto jsonVal = values.get_string_value(L"wallpaperPathLight"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
if (m_settings.wallpaperPathLight != val)
|
||||
{
|
||||
m_settings.wallpaperPathLight = val;
|
||||
NotifyObservers(SettingId::WallpaperPathLight);
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto jsonVal = values.get_string_value(L"wallpaperPathDark"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
if (m_settings.wallpaperPathDark != val)
|
||||
{
|
||||
m_settings.wallpaperPathDark = val;
|
||||
NotifyObservers(SettingId::WallpaperPathDark);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
||||
@@ -67,6 +67,13 @@ struct LightSwitchConfig
|
||||
|
||||
bool changeSystem = false;
|
||||
bool changeApps = false;
|
||||
|
||||
bool wallpaperEnabled = false;
|
||||
bool wallpaperVirtualDesktop = false;
|
||||
int wallpaperStyleLight = 0;
|
||||
int wallpaperStyleDark = 0;
|
||||
std::wstring wallpaperPathLight;
|
||||
std::wstring wallpaperPathDark;
|
||||
};
|
||||
|
||||
class LightSwitchSettings
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
#include <LightSwitchUtils.h>
|
||||
#include "ThemeScheduler.h"
|
||||
#include <ThemeHelper.h>
|
||||
#include <filesystem>
|
||||
|
||||
void ApplyTheme(bool shouldBeLight);
|
||||
void ApplyTheme(bool shouldBeLight, bool changeWallpaper);
|
||||
|
||||
// Constructor
|
||||
LightSwitchStateManager::LightSwitchStateManager()
|
||||
@@ -147,6 +148,11 @@ static std::pair<int, int> update_sun_times(auto& settings)
|
||||
return { newLightTime, newDarkTime };
|
||||
}
|
||||
|
||||
static bool IsValidPath(const std::wstring& path)
|
||||
{
|
||||
return !path.empty() && std::filesystem::exists(path);
|
||||
}
|
||||
|
||||
// Internal: decide what should happen now
|
||||
void LightSwitchStateManager::EvaluateAndApplyIfNeeded()
|
||||
{
|
||||
@@ -240,6 +246,11 @@ void LightSwitchStateManager::EvaluateAndApplyIfNeeded()
|
||||
bool appsNeedsToChange = _currentSettings.changeApps && (_state.isAppsLightActive != shouldBeLight);
|
||||
bool systemNeedsToChange = _currentSettings.changeSystem && (_state.isSystemLightActive != shouldBeLight);
|
||||
|
||||
bool changeWallpaper =
|
||||
_currentSettings.wallpaperEnabled &&
|
||||
IsValidPath(_currentSettings.wallpaperPathDark) &&
|
||||
IsValidPath(_currentSettings.wallpaperPathLight);
|
||||
|
||||
/* Logger::debug(
|
||||
L"[LightSwitchStateManager] now = {:02d}:{:02d}, light boundary = {:02d}:{:02d} ({}), dark boundary = {:02d}:{:02d} ({})",
|
||||
now / 60,
|
||||
@@ -260,11 +271,11 @@ void LightSwitchStateManager::EvaluateAndApplyIfNeeded()
|
||||
if (!_state.isManualOverride && (appsNeedsToChange || systemNeedsToChange))
|
||||
{
|
||||
Logger::info(L"[LightSwitchStateManager] Applying {} theme", shouldBeLight ? L"light" : L"dark");
|
||||
ApplyTheme(shouldBeLight);
|
||||
ApplyTheme(shouldBeLight, changeWallpaper);
|
||||
|
||||
_state.isSystemLightActive = GetCurrentSystemTheme();
|
||||
_state.isAppsLightActive = GetCurrentAppsTheme();
|
||||
}
|
||||
|
||||
_state.lastTickMinutes = now;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
#include "SettingsConstants.h"
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
enum class SettingId
|
||||
{
|
||||
ScheduleMode = 0,
|
||||
Latitude,
|
||||
Longitude,
|
||||
LightTime,
|
||||
DarkTime,
|
||||
Sunrise_Offset,
|
||||
Sunset_Offset,
|
||||
ChangeSystem,
|
||||
ChangeApps
|
||||
};
|
||||
|
||||
constexpr wchar_t PERSONALIZATION_REGISTRY_PATH[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
|
||||
constexpr wchar_t NIGHT_LIGHT_REGISTRY_PATH[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\CloudStore\\Store\\DefaultAccount\\Current\\default$windows.data.bluelightreduction.bluelightreductionstate\\windows.data.bluelightreduction.bluelightreductionstate";
|
||||
@@ -1,139 +0,0 @@
|
||||
#include <windows.h>
|
||||
#include <logger/logger_settings.h>
|
||||
#include <logger/logger.h>
|
||||
#include <utils/logger_helper.h>
|
||||
#include "ThemeHelper.h"
|
||||
#include <SettingsConstants.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"[LightSwitchService] 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;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
void SetSystemTheme(bool dark);
|
||||
void SetAppsTheme(bool dark);
|
||||
bool GetCurrentSystemTheme();
|
||||
bool GetCurrentAppsTheme();
|
||||
bool IsNightLightEnabled();
|
||||
@@ -17,6 +17,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public const string DefaultLatitude = "0.0";
|
||||
public const string DefaultLongitude = "0.0";
|
||||
public const string DefaultScheduleMode = "Off";
|
||||
public const bool DefaultWallpaperEnabled = false;
|
||||
public const bool DefaultWallpaperVirtualDesktopEnabled = false;
|
||||
public const int DefaultWallpaperStyle = 0;
|
||||
public const string DefaultWallpaperPath = "";
|
||||
public static readonly HotkeySettings DefaultToggleThemeHotkey = new HotkeySettings(true, true, false, true, 0x44); // Ctrl+Win+Shift+D
|
||||
|
||||
public LightSwitchProperties()
|
||||
@@ -30,6 +34,12 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
SunriseOffset = new IntProperty(DefaultSunriseOffset);
|
||||
SunsetOffset = new IntProperty(DefaultSunsetOffset);
|
||||
ScheduleMode = new StringProperty(DefaultScheduleMode);
|
||||
WallpaperEnabled = new BoolProperty(DefaultWallpaperEnabled);
|
||||
WallpaperVirtualDesktopEnabled = new BoolProperty(DefaultWallpaperVirtualDesktopEnabled);
|
||||
WallpaperStyleLight = new IntProperty(DefaultWallpaperStyle);
|
||||
WallpaperStyleDark = new IntProperty(DefaultWallpaperStyle);
|
||||
WallpaperPathLight = new StringProperty(DefaultWallpaperPath);
|
||||
WallpaperPathDark = new StringProperty(DefaultWallpaperPath);
|
||||
ToggleThemeHotkey = new KeyboardKeysProperty(DefaultToggleThemeHotkey);
|
||||
}
|
||||
|
||||
@@ -62,5 +72,23 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
|
||||
[JsonPropertyName("toggle-theme-hotkey")]
|
||||
public KeyboardKeysProperty ToggleThemeHotkey { get; set; }
|
||||
|
||||
[JsonPropertyName("wallpaperEnabled")]
|
||||
public BoolProperty WallpaperEnabled { get; set; }
|
||||
|
||||
[JsonPropertyName("wallpaperVirtualDesktopEnabled")]
|
||||
public BoolProperty WallpaperVirtualDesktopEnabled { get; set; }
|
||||
|
||||
[JsonPropertyName("wallpaperStyleLight")]
|
||||
public IntProperty WallpaperStyleLight { get; set; }
|
||||
|
||||
[JsonPropertyName("wallpaperStyleDark")]
|
||||
public IntProperty WallpaperStyleDark { get; set; }
|
||||
|
||||
[JsonPropertyName("wallpaperPathLight")]
|
||||
public StringProperty WallpaperPathLight { get; set; }
|
||||
|
||||
[JsonPropertyName("wallpaperPathDark")]
|
||||
public StringProperty WallpaperPathDark { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,12 @@ namespace Settings.UI.Library
|
||||
Latitude = new StringProperty(Properties.Latitude.Value),
|
||||
Longitude = new StringProperty(Properties.Longitude.Value),
|
||||
ToggleThemeHotkey = new KeyboardKeysProperty(Properties.ToggleThemeHotkey.Value),
|
||||
WallpaperEnabled = new BoolProperty(Properties.WallpaperEnabled.Value),
|
||||
WallpaperVirtualDesktopEnabled = new BoolProperty(Properties.WallpaperVirtualDesktopEnabled.Value),
|
||||
WallpaperStyleLight = new IntProperty((int)Properties.WallpaperStyleLight.Value),
|
||||
WallpaperStyleDark = new IntProperty((int)Properties.WallpaperStyleDark.Value),
|
||||
WallpaperPathLight = new StringProperty(Properties.WallpaperPathLight.Value),
|
||||
WallpaperPathDark = new StringProperty(Properties.WallpaperPathDark.Value),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
xmlns:local="using:Microsoft.PowerToys.Settings.UI.Helpers"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
xmlns:viewModels="using:Microsoft.PowerToys.Settings.UI.ViewModels"
|
||||
d:DataContext="{d:DesignInstance Type=viewModels:LightSwitchViewModel}"
|
||||
@@ -17,6 +18,12 @@
|
||||
<local:NavigablePage.Resources>
|
||||
<converters:TimeSpanToFriendlyTimeConverter x:Key="TimeSpanToFriendlyTimeConverter" />
|
||||
<converters:StringToDoubleConverter x:Key="StringToDoubleConverter" />
|
||||
<tkconverters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
||||
<tkconverters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||
<tkconverters:BoolToVisibilityConverter
|
||||
x:Key="BoolToInvertedVisibilityConverter"
|
||||
FalseValue="Visible"
|
||||
TrueValue="Collapsed" />
|
||||
</local:NavigablePage.Resources>
|
||||
<Grid>
|
||||
<controls:SettingsPageControl
|
||||
@@ -211,6 +218,124 @@
|
||||
</tkcontrols:SettingsCard>
|
||||
</tkcontrols:SettingsExpander.Items>
|
||||
</tkcontrols:SettingsExpander>
|
||||
<tkcontrols:SettingsExpander
|
||||
x:Uid="LightSwitch_WallpaperExpander"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||
IsExpanded="True">
|
||||
<ToggleSwitch AutomationProperties.AutomationId="Toggle_WallpaperSwitch" IsOn="{x:Bind ViewModel.IsWallpaperEnabled, Mode=TwoWay}" />
|
||||
<tkcontrols:SettingsExpander.Items>
|
||||
<tkcontrols:SettingsCard HorizontalContentAlignment="Stretch" ContentAlignment="Left">
|
||||
<Grid HorizontalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="20px" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
x:Uid="LightSwitch_WallpaperImageUnavailable"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{x:Bind ViewModel.IsLightWallpaperValid, Converter={StaticResource BoolToInvertedVisibilityConverter}, Mode=OneWay}" />
|
||||
<Image
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Source="{x:Bind ViewModel.WallpaperSourceLight, Mode=OneWay}"
|
||||
Tag="Light" />
|
||||
<TextBlock
|
||||
x:Uid="LightSwitch_WallpaperImageUnavailable"
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{x:Bind ViewModel.IsDarkWallpaperValid, Converter={StaticResource BoolToInvertedVisibilityConverter}, Mode=OneWay}" />
|
||||
<Image
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Source="{x:Bind ViewModel.WallpaperSourceDark, Mode=OneWay}"
|
||||
Tag="Dark" />
|
||||
</Grid>
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Uid="LightSwitch_WallpaperSelect">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<TextBlock
|
||||
x:Uid="LightSwitch_WallpaperLight"
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<Button
|
||||
x:Uid="LightSwitch_WallpaperBrowse"
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
AutomationProperties.AutomationId="Pick_ButtonLight"
|
||||
Click="PickWallpaper_Click"
|
||||
Tag="Light" />
|
||||
<TextBlock
|
||||
x:Uid="LightSwitch_WallpaperDark"
|
||||
Grid.Row="2"
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<Button
|
||||
x:Uid="LightSwitch_WallpaperBrowse"
|
||||
Grid.Row="2"
|
||||
Grid.Column="2"
|
||||
AutomationProperties.AutomationId="Pick_ButtonDark"
|
||||
Click="PickWallpaper_Click"
|
||||
Tag="Dark" />
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Uid="LightSwitch_WallpaperStyle">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<TextBlock
|
||||
x:Uid="LightSwitch_WallpaperLight"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<ComboBox AutomationProperties.AutomationId="Toggle_WallpaperStyleSwitchLight" SelectedIndex="{x:Bind ViewModel.WallpaperStyleLight, Mode=TwoWay}">
|
||||
<ComboBoxItem x:Uid="LightSwitch_StyleCenter" AutomationProperties.AutomationId="CenterCBItem_StyleSwitch" />
|
||||
<ComboBoxItem x:Uid="LightSwitch_StyleTile" AutomationProperties.AutomationId="TileCBItem_StyleSwitch" />
|
||||
<ComboBoxItem x:Uid="LightSwitch_StyleStretch" AutomationProperties.AutomationId="StretchCBItem_StyleSwitch" />
|
||||
<ComboBoxItem x:Uid="LightSwitch_StyleFit" AutomationProperties.AutomationId="FitCBItem_StyleSwitch" />
|
||||
<ComboBoxItem x:Uid="LightSwitch_StyleFill" AutomationProperties.AutomationId="FillCBItem_StyleSwitch" />
|
||||
<ComboBoxItem x:Uid="LightSwitch_StyleSpan" AutomationProperties.AutomationId="SpanCBItem_StyleSwitch" />
|
||||
</ComboBox>
|
||||
<TextBlock
|
||||
x:Uid="LightSwitch_WallpaperDark"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<ComboBox AutomationProperties.AutomationId="Toggle_WallpaperStyleSwitchDark" SelectedIndex="{x:Bind ViewModel.WallpaperStyleDark, Mode=TwoWay}">
|
||||
<ComboBoxItem x:Uid="LightSwitch_StyleCenter" AutomationProperties.AutomationId="CenterCBItem_StyleSwitch" />
|
||||
<ComboBoxItem x:Uid="LightSwitch_StyleTile" AutomationProperties.AutomationId="TileCBItem_StyleSwitch" />
|
||||
<ComboBoxItem x:Uid="LightSwitch_StyleStretch" AutomationProperties.AutomationId="StretchCBItem_StyleSwitch" />
|
||||
<ComboBoxItem x:Uid="LightSwitch_StyleFit" AutomationProperties.AutomationId="FitCBItem_StyleSwitch" />
|
||||
<ComboBoxItem x:Uid="LightSwitch_StyleFill" AutomationProperties.AutomationId="FillCBItem_StyleSwitch" />
|
||||
<ComboBoxItem x:Uid="LightSwitch_StyleSpan" AutomationProperties.AutomationId="SpanCBItem_StyleSwitch" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Uid="LightSwitch_WallpaperVirtualDesktop" Visibility="{x:Bind ViewModel.Is24H2OrLater, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneTime}">
|
||||
<ToggleSwitch AutomationProperties.AutomationId="Toggle_WallpaperVirtualDesktopSwitch" IsOn="{x:Bind ViewModel.IsVirtualDesktopEnabled, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard Background="{ThemeResource SystemFillColorSuccessBackgroundBrush}" Visibility="{x:Bind ViewModel.Is24H2OrLater, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneTime}">
|
||||
<tkcontrols:SettingsCard.Header>
|
||||
<TextBlock x:Uid="LightSwitch_VirtualDesktopInfo" TextWrapping="Wrap" />
|
||||
</tkcontrols:SettingsCard.Header>
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard Background="{ThemeResource SystemFillColorSuccessBackgroundBrush}" Visibility="{x:Bind ViewModel.Is24H2OrLater, Converter={StaticResource BoolToInvertedVisibilityConverter}, Mode=OneTime}">
|
||||
<tkcontrols:SettingsCard.Header>
|
||||
<TextBlock x:Uid="LightSwitch_DetectWindows24H2Info" TextWrapping="Wrap" />
|
||||
</tkcontrols:SettingsCard.Header>
|
||||
</tkcontrols:SettingsCard>
|
||||
</tkcontrols:SettingsExpander.Items>
|
||||
</tkcontrols:SettingsExpander>
|
||||
</controls:SettingsGroup>
|
||||
<!-- Force mode buttons -->
|
||||
<!--<tkcontrols:SettingsCard
|
||||
|
||||
@@ -16,9 +16,13 @@ using Microsoft.PowerToys.Settings.UI.ViewModels;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Microsoft.Windows.Storage.Pickers;
|
||||
using PowerToys.GPOWrapper;
|
||||
using Settings.UI.Library;
|
||||
using Windows.Devices.Geolocation;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
{
|
||||
@@ -185,7 +189,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
// need to save the values
|
||||
this.ViewModel.Latitude = latitude.ToString(CultureInfo.InvariantCulture);
|
||||
this.ViewModel.Longitude = longitude.ToString(CultureInfo.InvariantCulture);
|
||||
this.ViewModel.SyncButtonInformation = $"{this.ViewModel.Latitude}<7D>, {this.ViewModel.Longitude}<7D>";
|
||||
this.ViewModel.SyncButtonInformation = $"{this.ViewModel.Latitude}<7D>, {this.ViewModel.Longitude}<7D>";
|
||||
|
||||
var result = SunCalc.CalculateSunriseSunset(latitude, longitude, DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
|
||||
|
||||
@@ -391,5 +395,50 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
this.LocationWarningBar.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
private async void PickWallpaper_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var tag = (sender as Button).Tag as string;
|
||||
|
||||
var fileOpenPicker = new FileOpenPicker((sender as Button).XamlRoot.ContentIslandEnvironment.AppWindowId);
|
||||
string[] extensions = { ".jpg", ".jpeg", ".bmp", ".dib", ".png", ".jfif", ".jpe", ".gif", ".tif", ".tiff", ".wdp", ".heic", ".heif", ".heics", ".heifs", ".hif", ".avci", ".avcs", ".avif", ".avifs", ".jxr", ".jxl", ".webp" };
|
||||
foreach (var ext in extensions)
|
||||
{
|
||||
fileOpenPicker.FileTypeFilter.Add(ext);
|
||||
}
|
||||
|
||||
fileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
|
||||
var selectedFile = await fileOpenPicker.PickSingleFileAsync();
|
||||
|
||||
if (selectedFile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ViewModel.WallpaperPathLight) && tag == "Light")
|
||||
{
|
||||
LightSwitchViewModel.DeleteFile(ViewModel.WallpaperPathLight);
|
||||
ViewModel.WallpaperPathLight = string.Empty;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(ViewModel.WallpaperPathDark) && tag == "Dark")
|
||||
{
|
||||
LightSwitchViewModel.DeleteFile(ViewModel.WallpaperPathDark);
|
||||
ViewModel.WallpaperPathDark = string.Empty;
|
||||
}
|
||||
|
||||
var srcFile = await StorageFile.GetFileFromPathAsync(selectedFile.Path);
|
||||
var settingsFolder = await StorageFolder.GetFolderFromPathAsync(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Microsoft\\PowerToys\\LightSwitch");
|
||||
var dstFile = await settingsFolder.CreateFileAsync($"{tag}{DateTime.Now.ToFileTime()}{srcFile.FileType}", CreationCollisionOption.ReplaceExisting);
|
||||
await FileIO.WriteBufferAsync(dstFile, await FileIO.ReadBufferAsync(srcFile));
|
||||
|
||||
if (tag == "Light")
|
||||
{
|
||||
ViewModel.WallpaperPathLight = dstFile.Path;
|
||||
}
|
||||
else if (tag == "Dark")
|
||||
{
|
||||
ViewModel.WallpaperPathDark = dstFile.Path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5456,6 +5456,54 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
|
||||
<data name="LightSwitch_SunsetTooltip.Text" xml:space="preserve">
|
||||
<value>Sunset</value>
|
||||
</data>
|
||||
<data name="LightSwitch_WallpaperExpander.Header" xml:space="preserve">
|
||||
<value>Wallpaper changes with the color mode</value>
|
||||
</data>
|
||||
<data name="LightSwitch_StyleFill.Content" xml:space="preserve">
|
||||
<value>Fill</value>
|
||||
</data>
|
||||
<data name="LightSwitch_StyleFit.Content" xml:space="preserve">
|
||||
<value>Fit</value>
|
||||
</data>
|
||||
<data name="LightSwitch_StyleStretch.Content" xml:space="preserve">
|
||||
<value>Stretch</value>
|
||||
</data>
|
||||
<data name="LightSwitch_StyleTile.Content" xml:space="preserve">
|
||||
<value>Tile</value>
|
||||
</data>
|
||||
<data name="LightSwitch_StyleCenter.Content" xml:space="preserve">
|
||||
<value>Center</value>
|
||||
</data>
|
||||
<data name="LightSwitch_StyleSpan.Content" xml:space="preserve">
|
||||
<value>Span</value>
|
||||
</data>
|
||||
<data name="LightSwitch_WallpaperBrowse.Content" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
</data>
|
||||
<data name="LightSwitch_WallpaperLight.Text" xml:space="preserve">
|
||||
<value>Light:</value>
|
||||
</data>
|
||||
<data name="LightSwitch_WallpaperDark.Text" xml:space="preserve">
|
||||
<value>Dark:</value>
|
||||
</data>
|
||||
<data name="LightSwitch_WallpaperImageUnavailable.Text" xml:space="preserve">
|
||||
<value>Image preview unavailable, may not exist or is corrupted</value>
|
||||
</data>
|
||||
<data name="LightSwitch_WallpaperSelect.Header" xml:space="preserve">
|
||||
<value>Choose images for different modes</value>
|
||||
</data>
|
||||
<data name="LightSwitch_WallpaperStyle.Header" xml:space="preserve">
|
||||
<value>Choose fits for your desktop images</value>
|
||||
</data>
|
||||
<data name="LightSwitch_WallpaperVirtualDesktop.Header" xml:space="preserve">
|
||||
<value>Apply wallpaper to all virtual desktops</value>
|
||||
</data>
|
||||
<data name="LightSwitch_VirtualDesktopInfo.Text" xml:space="preserve">
|
||||
<value>Apply wallpaper to all virtual desktops is an experimental feature, only supported on Windows 24H2 and later versions. It may become unavailable or cause errors due to Windows updates. If you find that the LightSwitch service is not running properly after enabling this feature, please disable the feature and file a bug report with your Windows version.</value>
|
||||
</data>
|
||||
<data name="LightSwitch_DetectWindows24H2Info.Text" xml:space="preserve">
|
||||
<value>It has been detected that your Windows version is earlier than 24H2. On such versions, switching to another virtual desktop may cause the wallpaper to revert. If you are using virtual desktops, it is not recommended to enable this feature. Alternatively, upgrade your Windows version to 24H2 or later for the best experience.</value>
|
||||
</data>
|
||||
<data name="Close_NavViewItem.Content" xml:space="preserve">
|
||||
<value>Close PowerToys</value>
|
||||
<comment>Don't loc "PowerToys"</comment>
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
@@ -15,9 +17,12 @@ using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.SerializationContext;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Settings.UI.Library;
|
||||
using Settings.UI.Library.Helpers;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
@@ -46,6 +51,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
};
|
||||
|
||||
_toggleThemeHotkey = _moduleSettings.Properties.ToggleThemeHotkey.Value;
|
||||
PropertyChanged += WallpaperPath_Changed;
|
||||
}
|
||||
|
||||
public override Dictionary<string, HotkeySettings[]> GetAllHotkeySettings()
|
||||
@@ -524,6 +530,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
OnPropertyChanged(nameof(Latitude));
|
||||
OnPropertyChanged(nameof(Longitude));
|
||||
OnPropertyChanged(nameof(ScheduleMode));
|
||||
OnPropertyChanged(nameof(IsWallpaperEnabled));
|
||||
OnPropertyChanged(nameof(WallpaperPathLight));
|
||||
OnPropertyChanged(nameof(WallpaperPathDark));
|
||||
OnPropertyChanged(nameof(WallpaperStyleLight));
|
||||
OnPropertyChanged(nameof(WallpaperStyleDark));
|
||||
}
|
||||
|
||||
private void UpdateSunTimes(double latitude, double longitude, string city = "n/a")
|
||||
@@ -574,6 +585,222 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsWallpaperEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return ModuleSettings.Properties.WallpaperEnabled.Value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (ModuleSettings.Properties.WallpaperEnabled.Value != value)
|
||||
{
|
||||
ModuleSettings.Properties.WallpaperEnabled.Value = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsVirtualDesktopEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return ModuleSettings.Properties.WallpaperVirtualDesktopEnabled.Value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (ModuleSettings.Properties.WallpaperVirtualDesktopEnabled.Value != value)
|
||||
{
|
||||
ModuleSettings.Properties.WallpaperVirtualDesktopEnabled.Value = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string WallpaperPathLight
|
||||
{
|
||||
get
|
||||
{
|
||||
return ModuleSettings.Properties.WallpaperPathLight.Value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (ModuleSettings.Properties.WallpaperPathLight.Value != value)
|
||||
{
|
||||
ModuleSettings.Properties.WallpaperPathLight.Value = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string WallpaperPathDark
|
||||
{
|
||||
get
|
||||
{
|
||||
return ModuleSettings.Properties.WallpaperPathDark.Value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (ModuleSettings.Properties.WallpaperPathDark.Value != value)
|
||||
{
|
||||
ModuleSettings.Properties.WallpaperPathDark.Value = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLightWallpaperValid
|
||||
{
|
||||
get => _isLightWallpaperValid;
|
||||
|
||||
set
|
||||
{
|
||||
if (_isLightWallpaperValid != value)
|
||||
{
|
||||
_isLightWallpaperValid = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsDarkWallpaperValid
|
||||
{
|
||||
get => _isDarkWallpaperValid;
|
||||
set
|
||||
{
|
||||
if (_isDarkWallpaperValid != value)
|
||||
{
|
||||
_isDarkWallpaperValid = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ImageSource WallpaperSourceLight
|
||||
{
|
||||
get => _wallpaperSourceLight;
|
||||
set
|
||||
{
|
||||
if (_wallpaperSourceLight != value)
|
||||
{
|
||||
_wallpaperSourceLight = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ImageSource WallpaperSourceDark
|
||||
{
|
||||
get => _wallpaperSourceDark;
|
||||
set
|
||||
{
|
||||
if (_wallpaperSourceDark != value)
|
||||
{
|
||||
_wallpaperSourceDark = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int WallpaperStyleLight
|
||||
{
|
||||
get => ModuleSettings.Properties.WallpaperStyleLight.Value;
|
||||
set
|
||||
{
|
||||
if (ModuleSettings.Properties.WallpaperStyleLight.Value != value)
|
||||
{
|
||||
ModuleSettings.Properties.WallpaperStyleLight.Value = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int WallpaperStyleDark
|
||||
{
|
||||
get => ModuleSettings.Properties.WallpaperStyleDark.Value;
|
||||
set
|
||||
{
|
||||
if (ModuleSettings.Properties.WallpaperStyleDark.Value != value)
|
||||
{
|
||||
ModuleSettings.Properties.WallpaperStyleDark.Value = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeleteFile(string path)
|
||||
{
|
||||
// Prevent attackers from damaging files through specially crafted JSON
|
||||
var dataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Microsoft\\PowerToys\\LightSwitch";
|
||||
if (!string.IsNullOrEmpty(path) && path.StartsWith(dataPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void WallpaperPath_Changed(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(WallpaperPathLight))
|
||||
{
|
||||
var lightImage = new BitmapImage();
|
||||
try
|
||||
{
|
||||
var lightFile = await StorageFile.GetFileFromPathAsync(WallpaperPathLight);
|
||||
await lightImage.SetSourceAsync(await lightFile.OpenReadAsync()); // thrown here when the image is invalid
|
||||
WallpaperSourceLight = lightImage;
|
||||
IsLightWallpaperValid = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
DeleteFile(WallpaperPathLight);
|
||||
WallpaperPathLight = null;
|
||||
IsLightWallpaperValid = false;
|
||||
WallpaperSourceLight = null;
|
||||
IsWallpaperEnabled = false;
|
||||
}
|
||||
}
|
||||
else if (e.PropertyName == nameof(WallpaperPathDark))
|
||||
{
|
||||
var darkImage = new BitmapImage();
|
||||
try
|
||||
{
|
||||
var darkFile = await StorageFile.GetFileFromPathAsync(WallpaperPathDark);
|
||||
await darkImage.SetSourceAsync(await darkFile.OpenReadAsync());
|
||||
WallpaperSourceDark = darkImage;
|
||||
IsDarkWallpaperValid = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
DeleteFile(WallpaperPathDark);
|
||||
WallpaperPathDark = null;
|
||||
IsDarkWallpaperValid = false;
|
||||
WallpaperSourceDark = null;
|
||||
IsWallpaperEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int GetRegistryBuildNumber()
|
||||
{
|
||||
var value = Win32.Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "CurrentBuildNumber", string.Empty);
|
||||
#pragma warning disable CA1305
|
||||
return int.Parse(value as string);
|
||||
#pragma warning restore CA1305
|
||||
}
|
||||
|
||||
public bool Is24H2OrLater
|
||||
{
|
||||
get => GetRegistryBuildNumber() > 26100;
|
||||
}
|
||||
|
||||
private bool _enabledStateIsGPOConfigured;
|
||||
private bool _enabledGPOConfiguration;
|
||||
private LightSwitchSettings _moduleSettings;
|
||||
@@ -581,6 +808,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
private HotkeySettings _toggleThemeHotkey;
|
||||
private TimeSpan? _sunriseTimeSpan;
|
||||
private TimeSpan? _sunsetTimeSpan;
|
||||
private bool _isLightWallpaperValid;
|
||||
private bool _isDarkWallpaperValid;
|
||||
private ImageSource _wallpaperSourceLight;
|
||||
private ImageSource _wallpaperSourceDark;
|
||||
|
||||
public ICommand ForceLightCommand { get; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user