mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-23 20:47:29 +01:00
Compare commits
4 Commits
dev/vanzue
...
user/yeela
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8d0c1b312 | ||
|
|
9c58574484 | ||
|
|
08d4689ec5 | ||
|
|
03d1dfca2d |
7
.github/actions/spell-check/expect.txt
vendored
7
.github/actions/spell-check/expect.txt
vendored
@@ -457,7 +457,6 @@ DWMWINDOWATTRIBUTE
|
||||
DWMWINDOWMAXIMIZEDCHANGE
|
||||
DWORDLONG
|
||||
dworigin
|
||||
DWPOS
|
||||
dwrite
|
||||
dxgi
|
||||
eab
|
||||
@@ -678,7 +677,6 @@ hicon
|
||||
HICONSM
|
||||
HIDEREADONLY
|
||||
HIDEWINDOW
|
||||
hif
|
||||
Hif
|
||||
HIMAGELIST
|
||||
himl
|
||||
@@ -840,7 +838,6 @@ jpe
|
||||
jpnime
|
||||
Jsons
|
||||
jsonval
|
||||
jxl
|
||||
jxr
|
||||
keybd
|
||||
KEYBDDATA
|
||||
@@ -1467,7 +1464,6 @@ recyclebin
|
||||
Redist
|
||||
Reencode
|
||||
REFCLSID
|
||||
REFGUID
|
||||
REFIID
|
||||
REGCLS
|
||||
regfile
|
||||
@@ -1574,7 +1570,6 @@ SETBUDDYINT
|
||||
SETCONTEXT
|
||||
SETCURSEL
|
||||
setcursor
|
||||
SETDESKWALLPAPER
|
||||
SETFOCUS
|
||||
SETFOREGROUND
|
||||
SETHOTKEY
|
||||
@@ -1663,7 +1658,6 @@ SKEXP
|
||||
SKIPOWNPROCESS
|
||||
sku
|
||||
SLGP
|
||||
slideshow
|
||||
sln
|
||||
slnf
|
||||
slnx
|
||||
@@ -1902,7 +1896,6 @@ unwide
|
||||
unzoom
|
||||
UOffset
|
||||
UOI
|
||||
UPDATEINIFILE
|
||||
UPDATENOW
|
||||
UPDATEREGISTRY
|
||||
updown
|
||||
|
||||
4
.github/copilot-instructions.md
vendored
4
.github/copilot-instructions.md
vendored
@@ -48,9 +48,9 @@ This is the top-level guide for AI changes. Keep edits small, follow existing pa
|
||||
- `doc/devdocs/modules/readme.md`
|
||||
|
||||
# Language style rules
|
||||
- Always enforce repo analyzers: root `.editorconfig` plus any `stylecop.json`.
|
||||
- Always enforce repo analyzers: `src/.editorconfig` plus any `stylecop.json`.
|
||||
- C# code follows StyleCop.Analyzers and Microsoft.CodeAnalysis.NetAnalyzers.
|
||||
- C++ code honors `.clang-format` plus `.clang-tidy` (modernize/cppcoreguidelines/readability).
|
||||
- C++ code honors `src/.clang-format` for formatting.
|
||||
- Markdown files wrap at 80 characters and use ATX headers with fenced code blocks that include language tags.
|
||||
- YAML files indent two spaces and add comments for complex settings while keeping keys clear.
|
||||
- PowerShell scripts use Verb-Noun names and prefer single-quoted literals while documenting parameters and satisfying PSScriptAnalyzer.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
mode: 'agent'
|
||||
model: Claude Sonnet 4.5
|
||||
agent: 'agent'
|
||||
model: GPT-5.1-Codex-Max
|
||||
description: 'Generate an 80-character git commit title for the local diff.'
|
||||
---
|
||||
|
||||
|
||||
5
.github/prompts/create-pr-summary.prompt.md
vendored
5
.github/prompts/create-pr-summary.prompt.md
vendored
@@ -1,6 +1,5 @@
|
||||
---
|
||||
mode: 'agent'
|
||||
model: Claude Sonnet 4.5
|
||||
agent: 'agent'
|
||||
model: GPT-5.1-Codex-Max
|
||||
description: 'Generate a PowerToys-ready pull request description from the local diff.'
|
||||
---
|
||||
|
||||
|
||||
7
.github/prompts/fix-issue.prompt.md
vendored
7
.github/prompts/fix-issue.prompt.md
vendored
@@ -1,7 +1,6 @@
|
||||
---
|
||||
mode: 'agent'
|
||||
model: GPT-5-Codex (Preview)
|
||||
description: " Execute the fix for a GitHub issue using the previously generated implementation plan. Apply code & tests directly in the repo. Output only a PR description (and optional manual steps)."
|
||||
agent: 'agent'
|
||||
model: GPT-5.1-Codex-Max
|
||||
description: "Execute the fix for a GitHub issue using the previously generated implementation plan. Apply code & tests directly in the repo. Output only a PR description (and optional manual steps)."
|
||||
---
|
||||
|
||||
# DEPENDENCY
|
||||
|
||||
5
.github/prompts/fix-spelling.prompt.md
vendored
5
.github/prompts/fix-spelling.prompt.md
vendored
@@ -1,6 +1,5 @@
|
||||
---
|
||||
mode: 'agent'
|
||||
model: GPT-5-Codex (Preview)
|
||||
agent: 'agent'
|
||||
model: GPT-5.1-Codex-Max
|
||||
description: 'Resolve Code scanning / check-spelling comments on the active PR.'
|
||||
---
|
||||
|
||||
|
||||
13
.github/prompts/review-issue.prompt.md
vendored
13
.github/prompts/review-issue.prompt.md
vendored
@@ -1,7 +1,6 @@
|
||||
---
|
||||
mode: 'agent'
|
||||
model: Claude Sonnet 4.5
|
||||
description: "You are github issue review and planning expertise, Score (0–100) and write one Implementation Plan. Outputs: overview.md, implementation-plan.md."
|
||||
agent: 'agent'
|
||||
model: GPT-5.1-Codex-Max
|
||||
description: "You are a GitHub issue review and planning expert; score (0-100) and write one implementation plan. Outputs: overview.md, implementation-plan.md."
|
||||
---
|
||||
|
||||
# GOAL
|
||||
@@ -10,11 +9,11 @@ For **#{{issue_number}}** produce:
|
||||
2) `Generated Files/issueReview/{{issue_number}}/implementation-plan.md`
|
||||
|
||||
## Inputs
|
||||
figure out from the prompt on the
|
||||
Figure out required inputs {{issue_number}} from the invocation context; if anything is missing, ask for the value or note it as a gap.
|
||||
|
||||
# CONTEXT (brief)
|
||||
Ground evidence using `gh issue view {{issue_number}} --json number,title,body,author,createdAt,updatedAt,state,labels,milestone,reactions,comments,linkedPullRequests`, and download the image for understand the context of the issue more.
|
||||
Locate source code in current workspace, but also free feel to use via `rg`/`git grep`. Link related issues/PRs.
|
||||
Ground evidence using `gh issue view {{issue_number}} --json number,title,body,author,createdAt,updatedAt,state,labels,milestone,reactions,comments,linkedPullRequests`, and download images to better understand the issue context.
|
||||
Locate source code in the current workspace; feel free to use `rg`/`git grep`. Link related issues and PRs.
|
||||
|
||||
# OVERVIEW.MD
|
||||
## Summary
|
||||
|
||||
5
.github/prompts/review-pr.prompt.md
vendored
5
.github/prompts/review-pr.prompt.md
vendored
@@ -1,6 +1,5 @@
|
||||
---
|
||||
mode: 'agent'
|
||||
model: Claude Sonnet 4.5
|
||||
agent: 'agent'
|
||||
model: GPT-5.1-Codex-Max
|
||||
description: "gh-driven PR review; per-step Markdown + machine-readable outputs"
|
||||
---
|
||||
|
||||
|
||||
@@ -68,14 +68,13 @@ jobs:
|
||||
|
||||
- template: .\steps-restore-nuget.yml
|
||||
|
||||
- task: NuGetCommand@2
|
||||
- task: MSBuild@1
|
||||
displayName: Restore solution-level NuGet packages
|
||||
inputs:
|
||||
command: restore
|
||||
feedsToUse: config
|
||||
configPath: nuget.config
|
||||
restoreSolution: PowerToys.slnx
|
||||
restoreDirectory: '$(Build.SourcesDirectory)\packages'
|
||||
solution: PowerToys.slnx
|
||||
msbuildArguments: '/t:restore /p:RestorePackagesConfig=true'
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
|
||||
# Build all UI test projects if no specific modules are specified
|
||||
- ${{ if eq(length(parameters.uiTestModules), 0) }}:
|
||||
|
||||
106
.vscode/tasks.json
vendored
Normal file
106
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"windows": {
|
||||
"options": {
|
||||
"shell": {
|
||||
"executable": "cmd.exe",
|
||||
"args": ["/d", "/c"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"inputs": [
|
||||
{
|
||||
"id": "config",
|
||||
"type": "pickString",
|
||||
"description": "Configuration",
|
||||
"options": ["Debug", "Release"],
|
||||
"default": "Debug"
|
||||
},
|
||||
{
|
||||
"id": "platform",
|
||||
"type": "pickString",
|
||||
"description": "Platform (leave empty to auto-detect host platform)",
|
||||
"options": ["", "X64", "ARM64"],
|
||||
"default": "X64"
|
||||
},
|
||||
{
|
||||
"id": "msbuildExtra",
|
||||
"type": "promptString",
|
||||
"description": "Extra MSBuild args (optional). Example: /p:CIBuild=true /m",
|
||||
"default": ""
|
||||
}
|
||||
],
|
||||
|
||||
"tasks": [
|
||||
{
|
||||
"label": "PT: Build (quick)",
|
||||
"type": "shell",
|
||||
"command": "\"${workspaceFolder}\\tools\\build\\build.cmd\"",
|
||||
"args": [
|
||||
"-Path",
|
||||
"${fileDirname}"
|
||||
],
|
||||
"group": { "kind": "build", "isDefault": true },
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
"clear": true
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
|
||||
{
|
||||
"label": "PT: Build (with options)",
|
||||
"type": "shell",
|
||||
"command": "\"${workspaceFolder}\\tools\\build\\build.cmd\"",
|
||||
"args": [
|
||||
"-Path",
|
||||
"${fileDirname}",
|
||||
"-Platform",
|
||||
"${input:platform}",
|
||||
"-Configuration",
|
||||
"${input:config}",
|
||||
"${input:msbuildExtra}"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
"clear": true
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
|
||||
{
|
||||
"label": "PT: Build Essentials (quick)",
|
||||
"type": "shell",
|
||||
"command": "\"${workspaceFolder}\\tools\\build\\build-essentials.cmd\"",
|
||||
"args": [],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
"clear": true
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
|
||||
{
|
||||
"label": "PT: Build Essentials (with options)",
|
||||
"type": "shell",
|
||||
"command": "\"${workspaceFolder}\\tools\\build\\build-essentials.cmd\"",
|
||||
"args": [
|
||||
"-Platform",
|
||||
"${input:platform}",
|
||||
"-Configuration",
|
||||
"${input:config}",
|
||||
"${input:msbuildExtra}"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
"clear": true
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
36
COMMUNITY.md
36
COMMUNITY.md
@@ -6,9 +6,6 @@ Names are in alphabetical order based on first name.
|
||||
|
||||
## High impact community members
|
||||
|
||||
### [@Noraa-Junker](https://github.com/Noraa-Junker) - [Noraa Junker](https://noraajunker.ch)
|
||||
Noraa has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Noraa was the primary person for helping build the File Explorer preview pane handler for developer files.
|
||||
|
||||
### [@cgaarden](https://github.com/cgaarden) - [Christian Gaarden Gaardmark](https://www.onegreatworld.com)
|
||||
Christian contributed New+ utility
|
||||
|
||||
@@ -42,6 +39,12 @@ Jay has helped triaging, discussing, creating a substantial number of issues and
|
||||
### [@jefflord](https://github.com/Jjefflord) - Jeff Lord
|
||||
Jeff added in multiple new features into Keyboard manager, such as key chord support and launching apps. He also contributed multiple features/fixes to PowerToys.
|
||||
|
||||
### [@snickler](https://github.com/snickler) - [Jeremy Sinclair](http://sinclairinat0r.com)
|
||||
Jeremy has helped drive large sums of the ARM64 support inside PowerToys
|
||||
|
||||
### [@jiripolasek](https://github.com/jiripolasek) - [Jiří Polášek](https://github.com/jiripolasek)
|
||||
Jiří has contributed a massive number of features and improvements to Command Palette, including drag & drop support, custom themes, Web Search enhancements, Remote Desktop extension fixes, and many UX improvements.
|
||||
|
||||
### [@TheJoeFin](https://github.com/TheJoeFin) - [Joe Finney](https://joefinapps.com)
|
||||
Joe has helped triaging, discussing, issues as well as fixing bugs and building features for Text Extractor.
|
||||
|
||||
@@ -57,6 +60,9 @@ Color Picker is from Martin.
|
||||
### [@mikeclayton](https://github.com/mikeclayton) - [Michael Clayton](https://michael-clayton.com)
|
||||
Michael contributed the [initial version](https://github.com/microsoft/PowerToys/issues/23216) of the Mouse Jump tool and [a number of updates](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+author%3Amikeclayton) based on his FancyMouse utility.
|
||||
|
||||
### [@Noraa-Junker](https://github.com/Noraa-Junker) - [Noraa Junker](https://noraajunker.ch)
|
||||
Noraa has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Noraa was the primary person for helping build the File Explorer preview pane handler for developer files.
|
||||
|
||||
### [@pedrolamas](https://github.com/pedrolamas/) - Pedro Lamas
|
||||
Pedro helped create the thumbnail and File Explorer previewers for 3D files like STL and GCode. If you like 3D printing, these are very helpful.
|
||||
|
||||
@@ -69,15 +75,12 @@ Rafael has helped do the [upgrade from CppWinRT 1.x to 2.0](https://github.com/m
|
||||
### [@royvou](https://github.com/royvou)
|
||||
Roy has helped out contributing multiple features to PowerToys Run
|
||||
|
||||
### [@snickler](https://github.com/snickler) - [Jeremy Sinclair](http://sinclairinat0r.com)
|
||||
Jeremy has helped drive large sums of the ARM64 support inside PowerToys
|
||||
### [@ThiefZero](https://github.com/ThiefZero)
|
||||
ThiefZero has helped out contributing a features to PowerToys Run such as the unit converter plugin
|
||||
|
||||
### [@TobiasSekan](https://github.com/TobiasSekan) - Tobias Sekan
|
||||
Tobias Sekan has helped out contributing features to PowerToys Run such as Settings plugin, Registry plugin
|
||||
|
||||
### [@ThiefZero](https://github.com/ThiefZero)
|
||||
ThiefZero has helped out contributing a features to PowerToys Run such as the unit converter plugin
|
||||
|
||||
## Open source projects
|
||||
|
||||
As PowerToys creates new utilities, some will be based off existing technology. We'll continue to do our best to contribute back to these projects but their efforts were the base of some of our projects. We want to be sure their work is directly recognized.
|
||||
@@ -187,18 +190,10 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
|
||||
- [@niels9001](https://github.com/niels9001/) - Niels Laute - Product Manager
|
||||
- [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev Lead
|
||||
- [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev Lead
|
||||
- [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev Lead
|
||||
- [@lei9444](https://github.com/lei9444) - Leilei Zhang - Dev
|
||||
- [@shuaiyuanxx](https://github.com/shuaiyuanxx) - Shawn Yuan - Dev
|
||||
- [@moooyo](https://github.com/moooyo) - Yu Leng - Dev
|
||||
- [@haoliuu](https://github.com/haoliuu) - Hao Liu - Dev
|
||||
- [@chenmy77](https://github.com/chenmy77) - Mengyuan Chen - Dev
|
||||
- [@chemwolf6922](https://github.com/chemwolf6922) - Feng Wang - Dev
|
||||
- [@yaqingmi](https://github.com/yaqingmi) - Yaqing Mi - Dev
|
||||
- [@zhaoqpcn](https://github.com/zhaoqpcn) - Qingpeng Zhao - Dev
|
||||
- [@urnotdfs](https://github.com/urnotdfs) - Xiaofeng Wang - Dev
|
||||
- [@zhaopy536](https://github.com/zhaopy536) - Peiyao Zhao - Dev
|
||||
- [@wang563681252](https://github.com/wang563681252) - Zhaopeng Wang - Dev
|
||||
- [@vanzue](https://github.com/vanzue) - Kai Tao - Dev
|
||||
- [@zadjii-msft](https://github.com/zadjii-msft) - Mike Griese - Dev
|
||||
- [@khmyznikov](https://github.com/khmyznikov) - Gleb Khmyznikov - Dev
|
||||
@@ -229,3 +224,12 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
|
||||
- [@SeraphimaZykova](https://github.com/SeraphimaZykova) - Seraphima Zykova - Dev
|
||||
- [@stefansjfw](https://github.com/stefansjfw) - Stefan Markovic - Dev
|
||||
- [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev Lead
|
||||
- [@haoliuu](https://github.com/haoliuu) - Hao Liu - Dev
|
||||
- [@chenmy77](https://github.com/chenmy77) - Mengyuan Chen - Dev
|
||||
- [@chemwolf6922](https://github.com/chemwolf6922) - Feng Wang - Dev
|
||||
- [@yaqingmi](https://github.com/yaqingmi) - Yaqing Mi - Dev
|
||||
- [@zhaoqpcn](https://github.com/zhaoqpcn) - Qingpeng Zhao - Dev
|
||||
- [@urnotdfs](https://github.com/urnotdfs) - Xiaofeng Wang - Dev
|
||||
- [@zhaopy536](https://github.com/zhaopy536) - Peiyao Zhao - Dev
|
||||
- [@wang563681252](https://github.com/wang563681252) - Zhaopeng Wang - Dev
|
||||
- [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev Lead
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#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";
|
||||
@@ -1,370 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#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,14 +166,13 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\LightSwitchCommon;..\..\..\common;..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="..\LightSwitchCommon\ThemeHelper.h" />
|
||||
<ClInclude Include="..\LightSwitchCommon\SettingsConstants.h" />
|
||||
<ClInclude Include="ThemeHelper.h" />
|
||||
<ClInclude Include="trace.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -188,12 +187,7 @@
|
||||
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<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="ThemeHelper.cpp" />
|
||||
<ClCompile Include="trace.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -216,7 +210,6 @@
|
||||
<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="..\LightSwitchCommon\ThemeHelper.cpp">
|
||||
<ClCompile Include="ThemeHelper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
@@ -24,10 +24,7 @@
|
||||
<ClInclude Include="trace.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\LightSwitchCommon\ThemeHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\LightSwitchCommon\SettingsConstants.h">
|
||||
<ClInclude Include="ThemeHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
@@ -50,7 +47,4 @@
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,106 @@
|
||||
#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
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
void SetSystemTheme(bool dark);
|
||||
void SetAppsTheme(bool dark);
|
||||
bool GetCurrentSystemTheme();
|
||||
bool GetCurrentAppsTheme();
|
||||
@@ -94,12 +94,6 @@ 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
|
||||
@@ -357,30 +351,6 @@ 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();
|
||||
}
|
||||
@@ -596,53 +566,15 @@ 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(!current_system_theme);
|
||||
SetSystemTheme(!GetCurrentSystemTheme());
|
||||
}
|
||||
if (g_settings.m_changeApps)
|
||||
{
|
||||
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);
|
||||
SetAppsTheme(!GetCurrentAppsTheme());
|
||||
}
|
||||
|
||||
if (!m_manual_override_event_handle)
|
||||
@@ -761,18 +693,6 @@ 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, bool changeWallpaper);
|
||||
void ApplyTheme(bool shouldBeLight);
|
||||
|
||||
// Entry point for the executable
|
||||
int _tmain(int argc, TCHAR* argv[])
|
||||
@@ -125,29 +125,9 @@ VOID WINAPI ServiceCtrlHandler(DWORD dwCtrl)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
void ApplyTheme(bool shouldBeLight)
|
||||
{
|
||||
const auto& s = LightSwitchSettings::settings();
|
||||
bool somethingChanged = false;
|
||||
|
||||
if (s.changeSystem)
|
||||
{
|
||||
@@ -156,7 +136,6 @@ void ApplyTheme(bool shouldBeLight, bool changeWallpaper)
|
||||
{
|
||||
SetSystemTheme(shouldBeLight);
|
||||
Logger::info(L"[LightSwitchService] Changed system theme to {}.", shouldBeLight ? L"light" : L"dark");
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,15 +146,6 @@ void ApplyTheme(bool shouldBeLight, bool changeWallpaper)
|
||||
{
|
||||
SetAppsTheme(shouldBeLight);
|
||||
Logger::info(L"[LightSwitchService] Changed apps theme to {}.", shouldBeLight ? L"light" : L"dark");
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (somethingChanged)
|
||||
{
|
||||
if (changeWallpaper)
|
||||
{
|
||||
SetWallpaper(shouldBeLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,7 +175,7 @@ static void DetectAndHandleExternalThemeChange(LightSwitchStateManager& stateMan
|
||||
if (s.scheduleMode == ScheduleMode::FollowNightLight)
|
||||
{
|
||||
shouldBeLight = !IsNightLightEnabled();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldBeLight = ShouldBeLight(nowMinutes, effectiveLight, effectiveDark);
|
||||
|
||||
@@ -53,7 +53,18 @@
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\LightSwitchCommon;.\..\;..\..\..\common;..\..\..\common\logger;..\..\..\common\utils;..\..\..\common\SettingsAPI;..\..\..\common\Telemetry;..\..\..\;..\..\..\..\deps\spdlog\include;./;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>
|
||||
./../;
|
||||
..\..\..\common;
|
||||
..\..\..\common\logger;
|
||||
..\..\..\common\utils;
|
||||
..\..\..\common\SettingsAPI;
|
||||
..\..\..\common\Telemetry;
|
||||
..\..\..\;
|
||||
..\..\..\..\deps\spdlog\include;
|
||||
./;
|
||||
%(AdditionalIncludeDirectories)
|
||||
</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
@@ -66,7 +77,8 @@
|
||||
<ClCompile Include="LightSwitchSettings.cpp" />
|
||||
<ClCompile Include="LightSwitchStateManager.cpp" />
|
||||
<ClCompile Include="NightLightRegistryObserver.cpp" />
|
||||
<ClCompile Include="..\LightSwitchCommon\ThemeHelper.cpp" />
|
||||
<ClCompile Include="SettingsConstants.cpp" />
|
||||
<ClCompile Include="ThemeHelper.cpp" />
|
||||
<ClCompile Include="ThemeScheduler.cpp" />
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<ClCompile Include="WinHookEventIDs.cpp" />
|
||||
@@ -79,9 +91,9 @@
|
||||
<ClInclude Include="LightSwitchStateManager.h" />
|
||||
<ClInclude Include="LightSwitchUtils.h" />
|
||||
<ClInclude Include="NightLightRegistryObserver.h" />
|
||||
<ClInclude Include="SettingsConstants.h" />
|
||||
<ClInclude Include="SettingsObserver.h" />
|
||||
<ClInclude Include="..\LightSwitchCommon\ThemeHelper.h" />
|
||||
<ClInclude Include="..\LightSwitchCommon\SettingsConstants.h" />
|
||||
<ClInclude Include="ThemeHelper.h" />
|
||||
<ClInclude Include="ThemeScheduler.h" />
|
||||
<ClInclude Include="trace.h" />
|
||||
<ClInclude Include="WinHookEventIDs.h" />
|
||||
|
||||
@@ -21,12 +21,15 @@
|
||||
<ClCompile Include="ThemeScheduler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\LightSwitchCommon\ThemeHelper.cpp">
|
||||
<ClCompile Include="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>
|
||||
@@ -47,10 +50,10 @@
|
||||
<ClInclude Include="ThemeHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\LightSwitchCommon\LightSwitchSettings.h">
|
||||
<ClInclude Include="LightSwitchSettings.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\LightSwitchCommon\SettingsConstants.h">
|
||||
<ClInclude Include="SettingsConstants.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SettingsObserver.h">
|
||||
|
||||
@@ -253,66 +253,6 @@ 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,13 +67,6 @@ 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,9 +4,8 @@
|
||||
#include <LightSwitchUtils.h>
|
||||
#include "ThemeScheduler.h"
|
||||
#include <ThemeHelper.h>
|
||||
#include <filesystem>
|
||||
|
||||
void ApplyTheme(bool shouldBeLight, bool changeWallpaper);
|
||||
void ApplyTheme(bool shouldBeLight);
|
||||
|
||||
// Constructor
|
||||
LightSwitchStateManager::LightSwitchStateManager()
|
||||
@@ -148,11 +147,6 @@ 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()
|
||||
{
|
||||
@@ -246,11 +240,6 @@ 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,
|
||||
@@ -271,11 +260,11 @@ void LightSwitchStateManager::EvaluateAndApplyIfNeeded()
|
||||
if (!_state.isManualOverride && (appsNeedsToChange || systemNeedsToChange))
|
||||
{
|
||||
Logger::info(L"[LightSwitchStateManager] Applying {} theme", shouldBeLight ? L"light" : L"dark");
|
||||
ApplyTheme(shouldBeLight, changeWallpaper);
|
||||
ApplyTheme(shouldBeLight);
|
||||
|
||||
_state.isSystemLightActive = GetCurrentSystemTheme();
|
||||
_state.isAppsLightActive = GetCurrentAppsTheme();
|
||||
}
|
||||
|
||||
_state.lastTickMinutes = now;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
#include "SettingsConstants.h"
|
||||
@@ -0,0 +1,17 @@
|
||||
#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";
|
||||
139
src/modules/LightSwitch/LightSwitchService/ThemeHelper.cpp
Normal file
139
src/modules/LightSwitch/LightSwitchService/ThemeHelper.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#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;
|
||||
}
|
||||
6
src/modules/LightSwitch/LightSwitchService/ThemeHelper.h
Normal file
6
src/modules/LightSwitch/LightSwitchService/ThemeHelper.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
void SetSystemTheme(bool dark);
|
||||
void SetAppsTheme(bool dark);
|
||||
bool GetCurrentSystemTheme();
|
||||
bool GetCurrentAppsTheme();
|
||||
bool IsNightLightEnabled();
|
||||
@@ -17,10 +17,6 @@ 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()
|
||||
@@ -34,12 +30,6 @@ 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);
|
||||
}
|
||||
|
||||
@@ -72,23 +62,5 @@ 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,12 +60,6 @@ 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,7 +9,6 @@
|
||||
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}"
|
||||
@@ -18,12 +17,6 @@
|
||||
<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
|
||||
@@ -218,124 +211,6 @@
|
||||
</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,13 +16,9 @@ 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
|
||||
{
|
||||
@@ -189,7 +185,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);
|
||||
|
||||
@@ -395,50 +391,5 @@ 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,54 +5456,6 @@ 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,9 +5,7 @@
|
||||
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;
|
||||
@@ -17,12 +15,9 @@ 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
|
||||
{
|
||||
@@ -51,7 +46,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
};
|
||||
|
||||
_toggleThemeHotkey = _moduleSettings.Properties.ToggleThemeHotkey.Value;
|
||||
PropertyChanged += WallpaperPath_Changed;
|
||||
}
|
||||
|
||||
public override Dictionary<string, HotkeySettings[]> GetAllHotkeySettings()
|
||||
@@ -530,11 +524,6 @@ 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")
|
||||
@@ -585,222 +574,6 @@ 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;
|
||||
@@ -808,10 +581,6 @@ 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; }
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ Target platform (e.g., 'x64', 'arm64'). If omitted the script will try to detect
|
||||
.PARAMETER Configuration
|
||||
Build configuration (e.g., 'Debug', 'Release'). Default: 'Debug'.
|
||||
|
||||
.PARAMETER Path
|
||||
Optional directory path containing projects to build. If not specified, uses the current working directory.
|
||||
|
||||
.PARAMETER RestoreOnly
|
||||
If specified, only perform package restore for local projects and skip the build steps for a solution file (i.e. .sln).
|
||||
|
||||
@@ -21,6 +24,10 @@ Any remaining, positional arguments passed to the script are forwarded to MSBuil
|
||||
.\tools\build\build.ps1
|
||||
Builds any .sln/.csproj/.vcxproj in the current working directory (auto-detects Platform).
|
||||
|
||||
.EXAMPLE
|
||||
.\tools\build\build.ps1 -Platform x64 -Configuration Release -Path "C:\MyProject\src"
|
||||
Builds local projects in the specified directory for x64 Release.
|
||||
|
||||
.EXAMPLE
|
||||
.\tools\build\build.ps1 -Platform x64 -Configuration Release
|
||||
Builds local projects for x64 Release.
|
||||
@@ -41,6 +48,7 @@ Only restores packages for local projects; ExtraArgs still forwarded to msbuild'
|
||||
param (
|
||||
[string]$Platform = '',
|
||||
[string]$Configuration = 'Debug',
|
||||
[string]$Path = '',
|
||||
[switch]$RestoreOnly,
|
||||
[Parameter(ValueFromRemainingArguments=$true)]
|
||||
[string[]]$ExtraArgs
|
||||
@@ -78,7 +86,11 @@ if (-not $Platform -or $Platform -eq '') {
|
||||
}
|
||||
}
|
||||
|
||||
$cwd = (Get-Location).ProviderPath
|
||||
$cwd = if ($Path) {
|
||||
(Resolve-Path $Path).ProviderPath
|
||||
} else {
|
||||
(Get-Location).ProviderPath
|
||||
}
|
||||
$extraArgsString = $null
|
||||
if ($ExtraArgs -and $ExtraArgs.Count -gt 0) { $extraArgsString = ($ExtraArgs -join ' ') }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user