Compare commits
12 Commits
shawn/fixL
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd9f523d5d | ||
|
|
de5fee2ca6 | ||
|
|
5da2e74622 | ||
|
|
d43a23c745 | ||
|
|
ffae061135 | ||
|
|
1e7c60ec23 | ||
|
|
2a5c61ce1f | ||
|
|
fdfffdc256 | ||
|
|
f249f99694 | ||
|
|
28c0d4a420 | ||
|
|
b0f6e0ae87 | ||
|
|
8cb518b649 |
2
.github/actions/spell-check/allow/names.txt
vendored
@@ -29,6 +29,8 @@ shortcutguide
|
||||
|
||||
# 8LWXpg is user name but user folder causes a flag
|
||||
LWXpg
|
||||
# 0x6f677548 is user name but user folder causes a flag
|
||||
x6f677548
|
||||
Adoumie
|
||||
Advaith
|
||||
alekhyareddy
|
||||
|
||||
4
.github/actions/spell-check/excludes.txt
vendored
@@ -121,10 +121,6 @@
|
||||
^src/modules/MouseWithoutBorders/App/Helper/.*\.resx$
|
||||
^src/modules/MouseWithoutBorders/ModuleInterface/generateSecurityDescriptor\.h$
|
||||
^src/modules/peek/Peek.Common/NativeMethods\.txt$
|
||||
^src/modules/peek/Peek.UITests/TestAssets/4\.qoi$
|
||||
^src/modules/powerrename/PowerRenameUITest/testItems/folder1/testCase2\.txt$
|
||||
^src/modules/powerrename/PowerRenameUITest/testItems/folder2/SpecialCase\.txt$
|
||||
^src/modules/powerrename/PowerRenameUITest/testItems/testCase1\.txt$
|
||||
^src/modules/previewpane/SvgPreviewHandler/SvgHTMLPreviewGenerator\.cs$
|
||||
^src/modules/previewpane/UnitTests-MarkdownPreviewHandler/HelperFiles/MarkdownWithHTMLImageTag\.txt$
|
||||
^src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/.*$
|
||||
|
||||
67
.github/actions/spell-check/expect.txt
vendored
@@ -3,7 +3,6 @@ abcdefghjkmnpqrstuvxyz
|
||||
abgr
|
||||
ABlocked
|
||||
ABOUTBOX
|
||||
ABORTIFHUNG
|
||||
Abug
|
||||
Acceleratorkeys
|
||||
ACCEPTFILES
|
||||
@@ -26,6 +25,8 @@ ADMINS
|
||||
adml
|
||||
admx
|
||||
advancedpaste
|
||||
advancedpasteui
|
||||
advancedpasteuishortcut
|
||||
advapi
|
||||
advfirewall
|
||||
AFeature
|
||||
@@ -43,6 +44,7 @@ ALLINPUT
|
||||
Allman
|
||||
Allmodule
|
||||
ALLOWUNDO
|
||||
allpc
|
||||
ALLVIEW
|
||||
ALPHATYPE
|
||||
AModifier
|
||||
@@ -75,7 +77,6 @@ appwiz
|
||||
appxpackage
|
||||
APSTUDIO
|
||||
AQS
|
||||
Aquadrant
|
||||
ARandom
|
||||
ARCHITEW
|
||||
ARemapped
|
||||
@@ -133,6 +134,7 @@ bla
|
||||
BLACKFRAME
|
||||
BLENDFUNCTION
|
||||
Blockquotes
|
||||
blogs
|
||||
Blt
|
||||
BLURBEHIND
|
||||
BLURREGION
|
||||
@@ -364,7 +366,6 @@ desktopshorcutinstalled
|
||||
DESKTOPVERTRES
|
||||
devblogs
|
||||
devdocs
|
||||
devenv
|
||||
devmgmt
|
||||
DEVMODE
|
||||
DEVMODEW
|
||||
@@ -453,7 +454,6 @@ encryptor
|
||||
ENDSESSION
|
||||
ENSUREVISIBLE
|
||||
ENTERSIZEMOVE
|
||||
ENTRYW
|
||||
ENU
|
||||
environmentvariables
|
||||
EOAC
|
||||
@@ -508,6 +508,7 @@ FANCYZONESDRAWLAYOUTTEST
|
||||
FANCYZONESEDITOR
|
||||
FARPROC
|
||||
fesf
|
||||
fff
|
||||
FFFF
|
||||
FILEEXPLORER
|
||||
fileexploreraddons
|
||||
@@ -570,7 +571,6 @@ GETDESKWALLPAPER
|
||||
GETDLGCODE
|
||||
GETDPISCALEDSIZE
|
||||
getfilesiginforedist
|
||||
geolocator
|
||||
GETHOTKEY
|
||||
GETICON
|
||||
GETMINMAXINFO
|
||||
@@ -580,7 +580,6 @@ GETSCREENSAVERRUNNING
|
||||
GETSECKEY
|
||||
GETSTICKYKEYS
|
||||
GETTEXTLENGTH
|
||||
gitmodules
|
||||
GHND
|
||||
GMEM
|
||||
GNumber
|
||||
@@ -668,7 +667,11 @@ Hostx
|
||||
hotfixes
|
||||
hotkeycontrol
|
||||
HOTKEYF
|
||||
hotkeylockmachine
|
||||
hotkeyreconnect
|
||||
hotkeys
|
||||
hotkeyswitch
|
||||
hotkeytoggleeasymouse
|
||||
hotlight
|
||||
hotspot
|
||||
HPAINTBUFFER
|
||||
@@ -727,6 +730,8 @@ IMAGERESIZERCONTEXTMENU
|
||||
IMAGERESIZEREXT
|
||||
imageresizerinput
|
||||
imageresizersettings
|
||||
imagetotext
|
||||
imagetotextshortcut
|
||||
imagingdevices
|
||||
ime
|
||||
imgflip
|
||||
@@ -817,12 +822,10 @@ killrunner
|
||||
kmph
|
||||
kvp
|
||||
Kybd
|
||||
LARGEICON
|
||||
lastcodeanalysissucceeded
|
||||
LASTEXITCODE
|
||||
LAYOUTRTL
|
||||
LCh
|
||||
lbl
|
||||
lcid
|
||||
LCIDTo
|
||||
lcl
|
||||
@@ -841,12 +844,10 @@ LIBID
|
||||
LIMITSIZE
|
||||
LIMITTEXT
|
||||
lindex
|
||||
lightswitch
|
||||
linkid
|
||||
LINKOVERLAY
|
||||
LINQTo
|
||||
listview
|
||||
LIVEDRAW
|
||||
LIVEZOOM
|
||||
LLKH
|
||||
llkhf
|
||||
@@ -858,6 +859,7 @@ localappdata
|
||||
localpackage
|
||||
LOCALSYSTEM
|
||||
LOCATIONCHANGE
|
||||
LOCKMACHINE
|
||||
LOCKTYPE
|
||||
LOGFONT
|
||||
LOGFONTW
|
||||
@@ -865,12 +867,10 @@ logon
|
||||
LOGMSG
|
||||
LOGPIXELSX
|
||||
LOGPIXELSY
|
||||
lng
|
||||
lon
|
||||
LOn
|
||||
longdate
|
||||
LONGNAMES
|
||||
lowlevel
|
||||
lquadrant
|
||||
LOWORD
|
||||
lparam
|
||||
LPBITMAPINFOHEADER
|
||||
@@ -917,10 +917,12 @@ luid
|
||||
LUMA
|
||||
lusrmgr
|
||||
LVal
|
||||
lvm
|
||||
LWA
|
||||
lwin
|
||||
LZero
|
||||
MAGTRANSFORM
|
||||
MAJORMINOR
|
||||
MAKEINTRESOURCE
|
||||
MAKEINTRESOURCEA
|
||||
MAKEINTRESOURCEW
|
||||
@@ -945,6 +947,7 @@ MDL
|
||||
mdtext
|
||||
mdtxt
|
||||
mdwn
|
||||
measuretool
|
||||
meme
|
||||
memicmp
|
||||
MENUITEMINFO
|
||||
@@ -994,6 +997,7 @@ MOUSEHWHEEL
|
||||
MOUSEINPUT
|
||||
mousejump
|
||||
mousepointer
|
||||
mousepointercrosshairs
|
||||
mouseutils
|
||||
MOVESIZEEND
|
||||
MOVESIZESTART
|
||||
@@ -1038,6 +1042,7 @@ MWBEx
|
||||
MYICON
|
||||
NAMECHANGE
|
||||
namespaceanddescendants
|
||||
Namotion
|
||||
nao
|
||||
NCACTIVATE
|
||||
ncc
|
||||
@@ -1075,6 +1080,7 @@ NEWPLUSSHELLEXTENSIONWIN
|
||||
newrow
|
||||
nicksnettravels
|
||||
NIF
|
||||
NJson
|
||||
NLog
|
||||
NLSTEXT
|
||||
NMAKE
|
||||
@@ -1194,13 +1200,23 @@ PACL
|
||||
PAINTSTRUCT
|
||||
PALETTEWINDOW
|
||||
PARENTNOTIFY
|
||||
PARENTRELATIVE
|
||||
PARENTRELATIVEEDITING
|
||||
PARENTRELATIVEFORADDRESSBAR
|
||||
PARENTRELATIVEFORUI
|
||||
PARENTRELATIVEPARSING
|
||||
parray
|
||||
PARTIALCONFIRMATIONDIALOGTITLE
|
||||
pasteashtmlfile
|
||||
pasteashtmlfileshortcut
|
||||
pasteasjson
|
||||
pasteasjsonshortcut
|
||||
pasteasmarkdown
|
||||
pasteasmarkdownshortcut
|
||||
pasteasplaintext
|
||||
pasteasplaintextshortcut
|
||||
pasteaspngfile
|
||||
pasteaspngfileshortcut
|
||||
pasteastxtfile
|
||||
pasteastxtfileshortcut
|
||||
PATCOPY
|
||||
PATHMUSTEXIST
|
||||
PATINVERT
|
||||
@@ -1208,7 +1224,6 @@ PATPAINT
|
||||
pbc
|
||||
pbi
|
||||
PBlob
|
||||
pbrush
|
||||
pcb
|
||||
pcch
|
||||
pcelt
|
||||
@@ -1242,7 +1257,6 @@ pgp
|
||||
pguid
|
||||
phbm
|
||||
phbmp
|
||||
phicon
|
||||
phwnd
|
||||
pici
|
||||
pidl
|
||||
@@ -1251,7 +1265,6 @@ pinfo
|
||||
pinvoke
|
||||
pipename
|
||||
PKBDLLHOOKSTRUCT
|
||||
pkgfamily
|
||||
plib
|
||||
ploc
|
||||
ploca
|
||||
@@ -1271,6 +1284,7 @@ Pomodoro
|
||||
Popups
|
||||
POPUPWINDOW
|
||||
POSITIONITEM
|
||||
powerocr
|
||||
POWERRENAMECONTEXTMENU
|
||||
powerrenameinput
|
||||
POWERRENAMETEST
|
||||
@@ -1321,6 +1335,7 @@ PRODUCTVERSION
|
||||
Progman
|
||||
programdata
|
||||
projectname
|
||||
projitems
|
||||
PROPERTYKEY
|
||||
Propset
|
||||
PROPVARIANT
|
||||
@@ -1328,7 +1343,6 @@ PRTL
|
||||
prvpane
|
||||
psapi
|
||||
pscid
|
||||
pscustomobject
|
||||
PSECURITY
|
||||
psfgao
|
||||
psfi
|
||||
@@ -1369,7 +1383,6 @@ quickaccent
|
||||
QUNS
|
||||
RAII
|
||||
RAlt
|
||||
RAquadrant
|
||||
randi
|
||||
rasterization
|
||||
Rasterize
|
||||
@@ -1414,6 +1427,7 @@ Removelnk
|
||||
renamable
|
||||
RENAMEONCOLLISION
|
||||
reparented
|
||||
reparenthotkey
|
||||
reparenting
|
||||
reportfileaccesses
|
||||
requery
|
||||
@@ -1439,6 +1453,7 @@ RIDEV
|
||||
RIGHTSCROLLBAR
|
||||
riid
|
||||
RKey
|
||||
Rns
|
||||
RNumber
|
||||
rop
|
||||
ROUNDSMALL
|
||||
@@ -1662,10 +1677,10 @@ STYLECHANGED
|
||||
STYLECHANGING
|
||||
subkeys
|
||||
sublang
|
||||
Subdomain
|
||||
SUBMODULEUPDATE
|
||||
subresource
|
||||
Superbar
|
||||
suntimes
|
||||
sut
|
||||
svchost
|
||||
SVGIn
|
||||
@@ -1731,9 +1746,9 @@ tgz
|
||||
themeresources
|
||||
THH
|
||||
THICKFRAME
|
||||
THEMECHANGED
|
||||
THISCOMPONENT
|
||||
throughs
|
||||
thumbnailhotkey
|
||||
TILEDWINDOW
|
||||
TILLSON
|
||||
timedate
|
||||
@@ -1747,9 +1762,10 @@ tkconverters
|
||||
tlb
|
||||
tlbimp
|
||||
tlc
|
||||
tmain
|
||||
TNP
|
||||
TOGGLEEASYMOUSE
|
||||
Toolhelp
|
||||
toolkitconverters
|
||||
toolwindow
|
||||
TOPDOWNDIB
|
||||
TOUCHEVENTF
|
||||
@@ -1761,9 +1777,11 @@ tracelogging
|
||||
tracerpt
|
||||
trackbar
|
||||
trafficmanager
|
||||
transcodetomp
|
||||
transicc
|
||||
TRAYMOUSEMESSAGE
|
||||
triaging
|
||||
Tru
|
||||
trl
|
||||
trx
|
||||
tsa
|
||||
@@ -1799,6 +1817,7 @@ ULONGLONG
|
||||
ums
|
||||
uncompilable
|
||||
UNCPRIORITY
|
||||
undefining
|
||||
UNDNAME
|
||||
UNICODETEXT
|
||||
unins
|
||||
@@ -1965,7 +1984,6 @@ WMI
|
||||
WMICIM
|
||||
wmimgmt
|
||||
wmp
|
||||
wmsg
|
||||
WMSYSCOMMAND
|
||||
wnd
|
||||
WNDCLASS
|
||||
@@ -1979,7 +1997,6 @@ WORKSPACESEDITOR
|
||||
WORKSPACESLAUNCHER
|
||||
WORKSPACESSNAPSHOTTOOL
|
||||
WORKSPACESWINDOWARRANGER
|
||||
Worktree
|
||||
wox
|
||||
wparam
|
||||
wpf
|
||||
|
||||
9
.github/actions/spell-check/patterns.txt
vendored
@@ -1,10 +1,5 @@
|
||||
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns
|
||||
|
||||
# marker to ignore all code on line
|
||||
^.*/\* #no-spell-check-line \*/.*$
|
||||
# marker for ignoring a comment to the end of the line
|
||||
// #no-spell-check.*$
|
||||
|
||||
# Gaelic
|
||||
Gàidhlig
|
||||
|
||||
@@ -269,7 +264,3 @@ St&yle
|
||||
# This matches a relative clause where the relative pronoun "that" is omitted.
|
||||
# Example: "Gets or sets the window the TitleBar should configure."
|
||||
\bthe\s+\w+\s+the\b
|
||||
|
||||
# Usernames with numbers
|
||||
# 0x6f677548 is user name but user folder causes a flag
|
||||
\bx6f677548\b
|
||||
|
||||
@@ -133,9 +133,6 @@
|
||||
"PowerToys.ImageResizerContextMenu.dll",
|
||||
"ImageResizerContextMenuPackage.msix",
|
||||
|
||||
"PowerToys.LightSwitchModuleInterface.dll",
|
||||
"LightSwitchService\\PowerToys.LightSwitchService.exe",
|
||||
|
||||
"PowerToys.KeyboardManager.dll",
|
||||
"KeyboardManagerEditor\\PowerToys.KeyboardManagerEditor.exe",
|
||||
"KeyboardManagerEngine\\PowerToys.KeyboardManagerEngine.exe",
|
||||
|
||||
@@ -29,8 +29,8 @@ steps:
|
||||
displayName: 'Touchdown Build - 37400, PRODEXT'
|
||||
inputs:
|
||||
teamId: 37400
|
||||
FederatedIdentityTDBuildServiceConnection: $(TouchdownServiceConnection)
|
||||
authType: FederatedIdentityTDBuild
|
||||
TDBuildServiceConnection: $(TouchdownServiceConnection)
|
||||
authType: SubjectNameIssuer
|
||||
resourceFilePath: |
|
||||
src\**\Resources.resx
|
||||
src\**\Resource.resx
|
||||
|
||||
@@ -32,7 +32,7 @@ parameters:
|
||||
- name: enableMsBuildCaching
|
||||
type: boolean
|
||||
displayName: "Enable MSBuild Caching"
|
||||
default: true
|
||||
default: false
|
||||
- name: runTests
|
||||
type: boolean
|
||||
displayName: "Run Tests"
|
||||
|
||||
@@ -8,8 +8,8 @@ steps:
|
||||
displayName: 'Download Localization Files -- PowerToys 37400'
|
||||
inputs:
|
||||
teamId: 37400
|
||||
FederatedIdentityTDBuildServiceConnection: $(TouchdownServiceConnection)
|
||||
authType: FederatedIdentityTDBuild
|
||||
TDBuildServiceConnection: $(TouchdownServiceConnection)
|
||||
authType: SubjectNameIssuer
|
||||
resourceFilePath: |
|
||||
**\Resources.resx
|
||||
**\Resource.resx
|
||||
|
||||
@@ -44,9 +44,6 @@ foreach ($csprojFile in $csprojFilesArray) {
|
||||
if ($csprojFile -like '*Microsoft.CmdPal.Core.*.csproj') {
|
||||
continue
|
||||
}
|
||||
if ($csprojFile -like '*Microsoft.CmdPal.Ext.Shell.csproj') {
|
||||
continue
|
||||
}
|
||||
|
||||
$importExists = Test-ImportSharedCsWinRTProps -filePath $csprojFile
|
||||
if (!$importExists) {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" Version="7.1.2" />
|
||||
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock" Version="0.1.251002-build.2316" />
|
||||
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock" Version="0.1.250910-build.2249" />
|
||||
<PackageVersion Include="ControlzEx" Version="6.0.0" />
|
||||
<PackageVersion Include="HelixToolkit" Version="2.24.0" />
|
||||
<PackageVersion Include="HelixToolkit.Core.Wpf" Version="2.24.0" />
|
||||
|
||||
@@ -5,13 +5,11 @@ MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runner", "src\runner\runner.vcxproj", "{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73} = {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}
|
||||
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC} = {08E71C67-6A7E-4CA1-B04E-2FB336410BAC}
|
||||
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B} = {0B43679E-EDFA-4DA0-AD30-F4628B308B1B}
|
||||
{0B593A6C-4143-4337-860E-DB5710FB87DB} = {0B593A6C-4143-4337-860E-DB5710FB87DB}
|
||||
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {17DA04DF-E393-4397-9CF0-84DABE11032E}
|
||||
{217DF501-135C-4E38-BFC8-99D4821032EA} = {217DF501-135C-4E38-BFC8-99D4821032EA}
|
||||
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34} = {2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}
|
||||
{38177D56-6AD1-4ADF-88C9-2843A7932166} = {38177D56-6AD1-4ADF-88C9-2843A7932166}
|
||||
{48804216-2A0E-4168-A6D8-9CD068D14227} = {48804216-2A0E-4168-A6D8-9CD068D14227}
|
||||
{51920F1F-C28C-4ADF-8660-4238766796C2} = {51920F1F-C28C-4ADF-8660-4238766796C2}
|
||||
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481} = {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}
|
||||
@@ -795,12 +793,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.Window
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.UnitTestBase", "src\modules\cmdpal\Tests\Microsoft.CmdPal.Ext.UnitTestsBase\Microsoft.CmdPal.Ext.UnitTestBase.csproj", "{00D8659C-2068-40B6-8B86-759CD6284BBB}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LightSwitch", "LightSwitch", "{5B201255-53C8-490B-A34F-01F05D48A477}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LightSwitchModuleInterface", "src\modules\LightSwitch\LightSwitchModuleInterface\LightSwitchModuleInterface.vcxproj", "{38177D56-6AD1-4ADF-88C9-2843A7932166}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LightSwitchService", "src\modules\LightSwitch\LightSwitchService\LightSwitchService.vcxproj", "{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{E11826E1-76DF-42AC-985C-164CC2EE57A1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScreenRuler.UITests", "src\modules\MeasureTool\Tests\ScreenRuler.UITests\ScreenRuler.UITests.csproj", "{66C069F8-C548-4CA6-8CDE-239104D68E88}"
|
||||
@@ -819,14 +811,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.WebSea
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.Shell.UnitTests", "src\modules\cmdpal\Tests\Microsoft.CmdPal.Ext.Shell.UnitTests\Microsoft.CmdPal.Ext.Shell.UnitTests.csproj", "{E816D7B4-4688-4ECB-97CC-3D8E798F3833}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{3DCCD936-D085-4869-A1DE-CA6A64152C94}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightSwitch.UITests", "src\modules\LightSwitch\Tests\LightSwitch.UITests\LightSwitch.UITests.csproj", "{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests", "src\modules\cmdpal\Tests\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests.csproj", "{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.UI.ViewModels.UnitTests", "src\modules\cmdpal\Tests\Microsoft.CmdPal.UI.ViewModels.UnitTests\Microsoft.CmdPal.UI.ViewModels.UnitTests.csproj", "{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
@@ -2905,22 +2889,6 @@ Global
|
||||
{00D8659C-2068-40B6-8B86-759CD6284BBB}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{00D8659C-2068-40B6-8B86-759CD6284BBB}.Release|x64.ActiveCfg = Release|x64
|
||||
{00D8659C-2068-40B6-8B86-759CD6284BBB}.Release|x64.Build.0 = Release|x64
|
||||
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Debug|x64.Build.0 = Debug|x64
|
||||
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Release|x64.ActiveCfg = Release|x64
|
||||
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Release|x64.Build.0 = Release|x64
|
||||
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Debug|x64.Build.0 = Debug|x64
|
||||
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Release|x64.ActiveCfg = Release|x64
|
||||
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Release|x64.Build.0 = Release|x64
|
||||
{66C069F8-C548-4CA6-8CDE-239104D68E88}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{66C069F8-C548-4CA6-8CDE-239104D68E88}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{66C069F8-C548-4CA6-8CDE-239104D68E88}.Debug|x64.ActiveCfg = Debug|x64
|
||||
@@ -2977,34 +2945,6 @@ Global
|
||||
{E816D7B4-4688-4ECB-97CC-3D8E798F3833}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{E816D7B4-4688-4ECB-97CC-3D8E798F3833}.Release|x64.ActiveCfg = Release|x64
|
||||
{E816D7B4-4688-4ECB-97CC-3D8E798F3833}.Release|x64.Build.0 = Release|x64
|
||||
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Debug|ARM64.Deploy.0 = Debug|ARM64
|
||||
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Debug|x64.Build.0 = Debug|x64
|
||||
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Release|ARM64.Deploy.0 = Release|ARM64
|
||||
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Release|x64.ActiveCfg = Release|x64
|
||||
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Release|x64.Build.0 = Release|x64
|
||||
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Release|x64.Deploy.0 = Release|x64
|
||||
{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA}.Debug|x64.Build.0 = Debug|x64
|
||||
{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA}.Release|x64.ActiveCfg = Release|x64
|
||||
{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA}.Release|x64.Build.0 = Release|x64
|
||||
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C}.Debug|x64.Build.0 = Debug|x64
|
||||
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C}.Release|x64.ActiveCfg = Release|x64
|
||||
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -3318,9 +3258,6 @@ Global
|
||||
{E816D7AF-4688-4ECB-97CC-3D8E798F3828} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
|
||||
{E816D7B0-4688-4ECB-97CC-3D8E798F3829} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
|
||||
{00D8659C-2068-40B6-8B86-759CD6284BBB} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
|
||||
{5B201255-53C8-490B-A34F-01F05D48A477} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{38177D56-6AD1-4ADF-88C9-2843A7932166} = {5B201255-53C8-490B-A34F-01F05D48A477}
|
||||
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC} = {5B201255-53C8-490B-A34F-01F05D48A477}
|
||||
{E11826E1-76DF-42AC-985C-164CC2EE57A1} = {7AC943C9-52E8-44CF-9083-744D8049667B}
|
||||
{66C069F8-C548-4CA6-8CDE-239104D68E88} = {E11826E1-76DF-42AC-985C-164CC2EE57A1}
|
||||
{9605B84E-FAC4-477B-B9EC-0753177EE6A8} = {557C4636-D7E1-4838-A504-7D19B725EE95}
|
||||
@@ -3330,10 +3267,6 @@ Global
|
||||
{E816D7B3-4688-4ECB-97CC-3D8E798F3832} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
|
||||
{E816D7B2-4688-4ECB-97CC-3D8E798F3831} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
|
||||
{E816D7B4-4688-4ECB-97CC-3D8E798F3833} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
|
||||
{3DCCD936-D085-4869-A1DE-CA6A64152C94} = {5B201255-53C8-490B-A34F-01F05D48A477}
|
||||
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F} = {3DCCD936-D085-4869-A1DE-CA6A64152C94}
|
||||
{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
|
||||
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
# Light Switch
|
||||
|
||||
[Public Overview – Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/light-switch)
|
||||
|
||||
## Quick Links
|
||||
|
||||
* [All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aissue%20state%3Aopen%20label%3AProduct-LightSwitch)
|
||||
* [Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aissue%20state%3Aopen%20label%3AProduct-LightSwitch%20label%3AIssue-Bug)
|
||||
* [Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3AProduct-LightSwitch)
|
||||
|
||||
## Overview
|
||||
|
||||
The **Light Switch** module lets users automatically transition between light and dark mode using a timed schedule or a keyboard shortcut.
|
||||
|
||||
## Features
|
||||
|
||||
* Set custom times to start and stop dark mode.
|
||||
* Use geolocation to determine local sunrise and sunset times.
|
||||
* Apply offsets in sunrise mode (e.g., 15 minutes before sunset).
|
||||
* Quickly toggle between modes with a keyboard shortcut (`Ctrl+Shift+Win+D` by default).
|
||||
* Choose whether theme changes apply to:
|
||||
|
||||
* Apps only
|
||||
* System only
|
||||
* Both apps and system
|
||||
|
||||
## Architecture
|
||||
|
||||
### Main Components
|
||||
|
||||
* **Shortcut/Hotkey**
|
||||
Listens for a hotkey event. Calling `onHotkey()` flips the theme flags.
|
||||
|
||||
> **Note:** Using the shortcut overrides the current schedule until the next transition event.
|
||||
|
||||
* **LightSwitchService**
|
||||
Reads settings and applies theming. Runs a check every minute to ensure the state is correct.
|
||||
|
||||
* **SettingsXAML/LightSwitch**
|
||||
Provides the settings UI for configuring schedules, syncing location, and customizing shortcuts.
|
||||
|
||||
* **Settings.UI/ViewModels/LightSwitchViewModel.cs**
|
||||
Handles updates to the settings file and communicates changes to the front end.
|
||||
|
||||
* **modules/LightSwitch/Tests**
|
||||
Contains UI tests that verify interactions between the settings UI, system state, and `settings.json`.
|
||||
|
||||
### Data Flow
|
||||
|
||||
1. User configures settings in the UI (default: manual mode, light mode from 06:00–18:00).
|
||||
2. Every minute, the service checks the time.
|
||||
|
||||
* If it’s not a threshold, the service sleeps until the next minute.
|
||||
* If it matches a threshold, the service applies the theme based on settings and returns to sleep.
|
||||
3. At **midnight**, when in *Sunrise to Sunset* mode, the service updates daily sunrise and sunset times.
|
||||
4. If the machine was asleep during a scheduled event, the service applies the correct settings at the next check.
|
||||
|
||||
## User Interface
|
||||
|
||||
The module’s settings are exposed in the PowerToys Settings UI. Options include:
|
||||
|
||||
* Shortcut customization
|
||||
* Mode selection (Manual or Sunrise to Sunset)
|
||||
* Manual start/stop times (manual mode only)
|
||||
* Automatic sunrise/sunset calculation (location-based)
|
||||
* Time offsets (sunrise mode)
|
||||
* Target scope (system, apps, or both)
|
||||
|
||||
## Development Environment Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
* Visual Studio 2019 or later
|
||||
* Windows 10 SDK
|
||||
* PowerToys repository cloned from GitHub
|
||||
|
||||
### Building and Testing
|
||||
|
||||
1. Clone the repo:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/microsoft/PowerToys.git
|
||||
```
|
||||
2. Initialize submodules:
|
||||
|
||||
```sh
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
3. Build the solution:
|
||||
|
||||
```sh
|
||||
msbuild -restore -p:RestorePackagesConfig=true -p:Platform=ARM64 -m PowerToys.sln
|
||||
```
|
||||
|
||||
> Note: This may take some time.
|
||||
4. Set `runner` as the startup project and press **F5**.
|
||||
5. Enable Light Switch in PowerToys Settings.
|
||||
6. To debug the service:
|
||||
|
||||
* Press `Ctrl+Alt+P` or go to **Debug > Attach to Process**.
|
||||
* Select `LightSwitchService.exe` and click **Attach**.
|
||||
* You can now set breakpoints in the service files.
|
||||
7. To debug the Settings UI:
|
||||
|
||||
* Set the startup project to `PowerToys.Settings` and press **F5**.
|
||||
* Note: Light Switch settings will not persist in this mode (they depend on the service executable).
|
||||
* Alternatively, you can attach `PowerToys.Settings.exe` to the debugger while `runner` is running to test the full flow with breakpoints.
|
||||
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 124 KiB |
@@ -1283,7 +1283,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
|
||||
}
|
||||
processes.resize(bytes / sizeof(processes[0]));
|
||||
|
||||
std::array<std::wstring_view, 42> processesToTerminate = {
|
||||
std::array<std::wstring_view, 41> processesToTerminate = {
|
||||
L"PowerToys.PowerLauncher.exe",
|
||||
L"PowerToys.Settings.exe",
|
||||
L"PowerToys.AdvancedPaste.exe",
|
||||
@@ -1298,7 +1298,6 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
|
||||
L"PowerToys.Hosts.exe",
|
||||
L"PowerToys.PowerRename.exe",
|
||||
L"PowerToys.ImageResizer.exe",
|
||||
L"PowerToys.LightSwitchService.exe",
|
||||
L"PowerToys.GcodeThumbnailProvider.exe",
|
||||
L"PowerToys.BgcodeThumbnailProvider.exe",
|
||||
L"PowerToys.PdfThumbnailProvider.exe",
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\Hosts.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\Hosts.wxs.bk""""
|
||||
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\ImageResizer.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\ImageResizer.wxs.bk""""
|
||||
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\KeyboardManager.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\KeyboardManager.wxs.bk""""
|
||||
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\LightSwitch.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\LightSwitch.wxs.bk""""
|
||||
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\MouseWithoutBorders.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\MouseWithoutBorders.wxs.bk""""
|
||||
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\NewPlus.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\NewPlus.wxs.bk""""
|
||||
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\Peek.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\Peek.wxs.bk""""
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
||||
|
||||
<?include $(sys.CURRENTDIR)\Common.wxi?>
|
||||
|
||||
<?define LightSwitchFiles=?>
|
||||
<?define LightSwitchFilesPath=$(var.BinDir)\LightSwitchService\?>
|
||||
|
||||
<Fragment>
|
||||
<!-- Light Switch background service -->
|
||||
|
||||
<!-- Create a directory for the service binaries -->
|
||||
<DirectoryRef Id="INSTALLFOLDER">
|
||||
<Directory Id="LightSwitchServiceFolder" Name="LightSwitchService" />
|
||||
</DirectoryRef>
|
||||
|
||||
<!-- File components generated by generateAllFileComponents.ps1 -->
|
||||
<DirectoryRef Id="LightSwitchServiceFolder" FileSource="$(var.LightSwitchFilesPath)">
|
||||
<!-- Generated by generateFileComponents.ps1 -->
|
||||
<!--LightSwitchFiles_Component_Def-->
|
||||
</DirectoryRef>
|
||||
|
||||
<!-- Group to include the service + cleanup on uninstall -->
|
||||
<ComponentGroup Id="LightSwitchComponentGroup">
|
||||
|
||||
<!-- Ensures folder removal on uninstall -->
|
||||
<Component Id="RemoveLightSwitchServiceFolder" Guid="C1E2F2ED-34A2-4EB0-8E17-DC0535F50F9D" Directory="INSTALLFOLDER">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="RemoveLightSwitchServiceFolder" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<RemoveFolder Id="RemoveFolderLightSwitchServiceFolder" Directory="LightSwitchServiceFolder" On="uninstall" />
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
@@ -41,7 +41,6 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
|
||||
call move /Y ..\..\..\FileExplorerPreview.wxs.bk ..\..\..\FileExplorerPreview.wxs
|
||||
call move /Y ..\..\..\FileLocksmith.wxs.bk ..\..\..\FileLocksmith.wxs
|
||||
call move /Y ..\..\..\Hosts.wxs.bk ..\..\..\Hosts.wxs
|
||||
call move /Y ..\..\..\LightSwitch.wxs.bk ..\..\..\LightSwitch.wxs
|
||||
call move /Y ..\..\..\ImageResizer.wxs.bk ..\..\..\ImageResizer.wxs
|
||||
call move /Y ..\..\..\KeyboardManager.wxs.bk ..\..\..\KeyboardManager.wxs
|
||||
call move /Y ..\..\..\MouseWithoutBorders.wxs.bk ..\..\..\MouseWithoutBorders.wxs
|
||||
@@ -115,7 +114,6 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
|
||||
<Compile Include="FileLocksmith.wxs" />
|
||||
<Compile Include="Hosts.wxs" />
|
||||
<Compile Include="ImageResizer.wxs" />
|
||||
<Compile Include="LightSwitch.wxs" />
|
||||
<Compile Include="KeyboardManager.wxs" />
|
||||
<Compile Include="Peek.wxs" />
|
||||
<Compile Include="PowerRename.wxs" />
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
<ComponentGroupRef Id="HostsComponentGroup" />
|
||||
<ComponentGroupRef Id="ImageResizerComponentGroup" />
|
||||
<ComponentGroupRef Id="KeyboardManagerComponentGroup" />
|
||||
<ComponentGroupRef Id="LightSwitchComponentGroup" />
|
||||
<ComponentGroupRef Id="PeekComponentGroup" />
|
||||
<ComponentGroupRef Id="PowerRenameComponentGroup" />
|
||||
<ComponentGroupRef Id="RegistryPreviewComponentGroup" />
|
||||
|
||||
@@ -182,10 +182,6 @@ Generate-FileComponents -fileListName "HostsAssetsFiles" -wxsFilePath $PSScriptR
|
||||
Generate-FileList -fileDepsJson "" -fileListName ImageResizerAssetsFiles -wxsFilePath $PSScriptRoot\ImageResizer.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\ImageResizer"
|
||||
Generate-FileComponents -fileListName "ImageResizerAssetsFiles" -wxsFilePath $PSScriptRoot\ImageResizer.wxs -regroot $registryroot
|
||||
|
||||
# Light Switch Service
|
||||
Generate-FileList -fileDepsJson "" -fileListName LightSwitchFiles -wxsFilePath $PSScriptRoot\LightSwitch.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\LightSwitchService"
|
||||
Generate-FileComponents -fileListName "LightSwitchFiles" -wxsFilePath $PSScriptRoot\LightSwitch.wxs -regroot $registryroot
|
||||
|
||||
#New+
|
||||
Generate-FileList -fileDepsJson "" -fileListName NewPlusAssetsFiles -wxsFilePath $PSScriptRoot\NewPlus.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\NewPlus"
|
||||
Generate-FileComponents -fileListName "NewPlusAssetsFiles" -wxsFilePath $PSScriptRoot\NewPlus.wxs -regroot $registryroot
|
||||
|
||||
@@ -17,7 +17,6 @@ namespace Common.UI
|
||||
Awake,
|
||||
ColorPicker,
|
||||
CmdNotFound,
|
||||
LightSwitch,
|
||||
FancyZones,
|
||||
FileLocksmith,
|
||||
Run,
|
||||
@@ -61,8 +60,6 @@ namespace Common.UI
|
||||
return "ColorPicker";
|
||||
case SettingsWindow.CmdNotFound:
|
||||
return "CmdNotFound";
|
||||
case SettingsWindow.LightSwitch:
|
||||
return "LightSwitch";
|
||||
case SettingsWindow.FancyZones:
|
||||
return "FancyZones";
|
||||
case SettingsWindow.FileLocksmith:
|
||||
|
||||
@@ -28,10 +28,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredCropAndLockEnabledValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredLightSwitchEnabledValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredLightSwitchEnabledValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredFancyZonesEnabledValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredFancyZonesEnabledValue());
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
|
||||
static GpoRuleConfigured GetConfiguredCmdPalEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredColorPickerEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredFileLocksmithEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue();
|
||||
|
||||
@@ -17,7 +17,6 @@ namespace PowerToys
|
||||
static GpoRuleConfigured GetConfiguredCmdPalEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredColorPickerEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredFileLocksmithEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue();
|
||||
|
||||
@@ -19,7 +19,6 @@ namespace ManagedCommon
|
||||
Hosts,
|
||||
ImageResizer,
|
||||
KeyboardManager,
|
||||
LightSwitch,
|
||||
MouseHighlighter,
|
||||
MouseJump,
|
||||
MousePointerCrosshairs,
|
||||
|
||||
@@ -81,14 +81,6 @@ namespace Microsoft.PowerToys.UITest
|
||||
get { return this.windowsElement?.Selected ?? false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the UI element is visible to the user.
|
||||
/// </summary>
|
||||
public bool Displayed
|
||||
{
|
||||
get { return this.windowsElement?.Displayed ?? false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Rect of the UI element.
|
||||
/// </summary>
|
||||
@@ -337,7 +329,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// Send Key of the element.
|
||||
/// </summary>
|
||||
/// <param name="key">The Key to Send.</param>
|
||||
public void SendKeys(string key)
|
||||
protected void SendKeys(string key)
|
||||
{
|
||||
PerformAction((actions, windowElement) =>
|
||||
{
|
||||
@@ -377,19 +369,5 @@ namespace Microsoft.PowerToys.UITest
|
||||
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method SaveToPngFile with parameter: path = {path}");
|
||||
this.windowsElement.GetScreenshot().SaveAsFile(path);
|
||||
}
|
||||
|
||||
public void EnsureVisible(Element scrollViewer, int maxScrolls = 10)
|
||||
{
|
||||
int count = 0;
|
||||
if (scrollViewer.WindowsElement != null)
|
||||
{
|
||||
while (!this.windowsElement!.Displayed && count < maxScrolls)
|
||||
{
|
||||
scrollViewer.WindowsElement.SendKeys(OpenQA.Selenium.Keys.PageDown);
|
||||
Task.Delay(250).Wait();
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ namespace Microsoft.PowerToys.UITest
|
||||
PowerRename,
|
||||
CommandPalette,
|
||||
ScreenRuler,
|
||||
LightSwitch,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -107,7 +106,6 @@ namespace Microsoft.PowerToys.UITest
|
||||
[PowerToysModule.PowerRename] = new ModuleInfo("PowerToys.PowerRename.exe", "PowerRename", "WinUI3Apps"),
|
||||
[PowerToysModule.CommandPalette] = new ModuleInfo("Microsoft.CmdPal.UI.exe", "PowerToys Command Palette", "WinUI3Apps\\CmdPal"),
|
||||
[PowerToysModule.ScreenRuler] = new ModuleInfo("PowerToys.MeasureToolUI.exe", "PowerToys.ScreenRuler", "WinUI3Apps"),
|
||||
[PowerToysModule.LightSwitch] = new ModuleInfo("PowerToys.LightSwitch.exe", "PowerToys.LightSwitch", "LightSwitchService"),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,6 @@ struct LogSettings
|
||||
inline const static std::string workspacesSnapshotToolLoggerName = "workspaces-snapshot-tool";
|
||||
inline const static std::wstring workspacesSnapshotToolLogPath = L"workspaces-snapshot-tool-log.log";
|
||||
inline const static std::string zoomItLoggerName = "zoom-it";
|
||||
inline const static std::string lightSwitchLoggerName = "light-switch";
|
||||
inline const static int retention = 30;
|
||||
std::wstring logLevel;
|
||||
LogSettings();
|
||||
|
||||
@@ -257,9 +257,7 @@ inline HANDLE run_elevated(const std::wstring& file, const std::wstring& params,
|
||||
exec_info.nShow = SW_HIDE;
|
||||
}
|
||||
|
||||
BOOL result = ShellExecuteExW(&exec_info);
|
||||
|
||||
return result ? exec_info.hProcess : nullptr;
|
||||
return ShellExecuteExW(&exec_info) ? exec_info.hProcess : nullptr;
|
||||
}
|
||||
|
||||
// Run command as non-elevated user, returns true if succeeded, puts the process id into returnPid if returnPid != NULL
|
||||
|
||||
@@ -30,7 +30,6 @@ namespace powertoys_gpo
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_CMD_NOT_FOUND = L"ConfigureEnabledUtilityCmdNotFound";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_COLOR_PICKER = L"ConfigureEnabledUtilityColorPicker";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_CROP_AND_LOCK = L"ConfigureEnabledUtilityCropAndLock";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_LIGHT_SWITCH = L"ConfigureEnabledUtilityLightSwitch";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_FANCYZONES = L"ConfigureEnabledUtilityFancyZones";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_FILE_LOCKSMITH = L"ConfigureEnabledUtilityFileLocksmith";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_SVG_PREVIEW = L"ConfigureEnabledUtilityFileExplorerSVGPreview";
|
||||
@@ -296,11 +295,6 @@ namespace powertoys_gpo
|
||||
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_CROP_AND_LOCK);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredLightSwitchEnabledValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_LIGHT_SWITCH);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredFancyZonesEnabledValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_FANCYZONES);
|
||||
|
||||
@@ -137,16 +137,6 @@
|
||||
<decimal value="0" />
|
||||
</disabledValue>
|
||||
</policy>
|
||||
<policy name="ConfigureEnabledUtilityLightSwitch" class="Both" displayName="$(string.ConfigureEnabledUtilityLightSwitch)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityLightSwitch">
|
||||
<parentCategory ref="PowerToys" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_95_0" />
|
||||
<enabledValue>
|
||||
<decimal value="1" />
|
||||
</enabledValue>
|
||||
<disabledValue>
|
||||
<decimal value="0" />
|
||||
</disabledValue>
|
||||
</policy>
|
||||
<policy name="ConfigureEnabledUtilityEnvironmentVariables" class="Both" displayName="$(string.ConfigureEnabledUtilityEnvironmentVariables)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityEnvironmentVariables">
|
||||
<parentCategory ref="PowerToys" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_75_0" />
|
||||
|
||||
@@ -245,7 +245,6 @@ If you don't configure this policy, the user will be able to control the setting
|
||||
<string id="ConfigureEnabledUtilityCmdNotFound">Command Not Found: Configure enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityCmdPal">CmdPal: Configure enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityCropAndLock">Crop And Lock: Configure enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityLightSwitch">Light Switch: Configure enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityEnvironmentVariables">Environment Variables: Configure enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityFancyZones">FancyZones: Configure enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityFileLocksmith">File Locksmith: Configure enabled state</string>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#include <windows.h>
|
||||
#include "resource.h"
|
||||
#include "../../../common/version/version.h"
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_DLL
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
|
||||
BEGIN
|
||||
VALUE "CompanyName", COMPANY_NAME
|
||||
VALUE "FileDescription", FILE_DESCRIPTION
|
||||
VALUE "FileVersion", FILE_VERSION_STRING
|
||||
VALUE "InternalName", INTERNAL_NAME
|
||||
VALUE "LegalCopyright", COPYRIGHT_NOTE
|
||||
VALUE "OriginalFilename", ORIGINAL_FILENAME
|
||||
VALUE "ProductName", PRODUCT_NAME
|
||||
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
|
||||
END
|
||||
END
|
||||
@@ -1,225 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{38177d56-6ad1-4adf-88c9-2843a7932166}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>LightSwitchModuleInterface</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>LightSwitchModuleInterface</ProjectName>
|
||||
<TargetName>PowerToys.LightSwitchModuleInterface</TargetName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(CoreLibraryDependencies);%(AdditionalDependencies);advapi32.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="ThemeHelper.h" />
|
||||
<ClInclude Include="trace.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">pch.h</PrecompiledHeaderFile>
|
||||
<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="trace.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="LightSwitchModuleInterface.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
|
||||
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj">
|
||||
<Project>{4aed67b6-55fd-486f-b917-e543dee2cb3c}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<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">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,50 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="trace.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ThemeHelper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="trace.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ThemeHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{bbf22ac8-46f8-4206-b44b-9c3897e99ce5}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{530ed784-9a70-46a0-8fb6-20d5dee4f7d3}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{da1cb871-86d3-414c-adf5-a7e9f2077d2f}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="LightSwitchModuleInterface.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,81 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include <windows.h>
|
||||
#include "ThemeHelper.h"
|
||||
|
||||
// Controls changing the themes.
|
||||
|
||||
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);
|
||||
|
||||
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();
|
||||
@@ -1,570 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include <interface/powertoy_module_interface.h>
|
||||
#include "trace.h"
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <common/utils/logger_helper.h>
|
||||
#include "ThemeHelper.h"
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
namespace
|
||||
{
|
||||
const wchar_t JSON_KEY_PROPERTIES[] = L"properties";
|
||||
const wchar_t JSON_KEY_WIN[] = L"win";
|
||||
const wchar_t JSON_KEY_ALT[] = L"alt";
|
||||
const wchar_t JSON_KEY_CTRL[] = L"ctrl";
|
||||
const wchar_t JSON_KEY_SHIFT[] = L"shift";
|
||||
const wchar_t JSON_KEY_CODE[] = L"code";
|
||||
const wchar_t JSON_KEY_TOGGLE_THEME_HOTKEY[] = L"toggle-theme-hotkey";
|
||||
const wchar_t JSON_KEY_VALUE[] = L"value";
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
Trace::RegisterProvider();
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
Trace::UnregisterProvider();
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// The PowerToy name that will be shown in the settings.
|
||||
const static wchar_t* MODULE_NAME = L"LightSwitch";
|
||||
// Add a description that will we shown in the module settings page.
|
||||
const static wchar_t* MODULE_DESC = L"This is a module that allows you to control light/dark theming via set times, sun rise, or directly invoking the change.";
|
||||
|
||||
enum class ScheduleMode
|
||||
{
|
||||
FixedHours,
|
||||
SunsetToSunrise,
|
||||
// add more later
|
||||
};
|
||||
|
||||
inline std::wstring ToString(ScheduleMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ScheduleMode::SunsetToSunrise:
|
||||
return L"SunsetToSunrise";
|
||||
case ScheduleMode::FixedHours:
|
||||
default:
|
||||
return L"FixedHours";
|
||||
}
|
||||
}
|
||||
|
||||
inline ScheduleMode FromString(const std::wstring& str)
|
||||
{
|
||||
if (str == L"SunsetToSunrise")
|
||||
return ScheduleMode::SunsetToSunrise;
|
||||
return ScheduleMode::FixedHours;
|
||||
}
|
||||
|
||||
// These are the properties shown in the Settings page.
|
||||
struct ModuleSettings
|
||||
{
|
||||
bool m_changeSystem = true;
|
||||
bool m_changeApps = true;
|
||||
ScheduleMode m_scheduleMode = ScheduleMode::FixedHours;
|
||||
int m_lightTime = 480;
|
||||
int m_darkTime = 1200;
|
||||
int m_sunrise_offset = 0;
|
||||
int m_sunset_offset = 0;
|
||||
std::wstring m_latitude = L"0.0";
|
||||
std::wstring m_longitude = L"0.0";
|
||||
} g_settings;
|
||||
|
||||
class LightSwitchInterface : public PowertoyModuleIface
|
||||
{
|
||||
private:
|
||||
bool m_enabled = false;
|
||||
|
||||
HANDLE m_process{ nullptr };
|
||||
HANDLE m_force_light_event_handle;
|
||||
HANDLE m_force_dark_event_handle;
|
||||
HANDLE m_manual_override_event_handle;
|
||||
|
||||
static const constexpr int NUM_DEFAULT_HOTKEYS = 4;
|
||||
|
||||
Hotkey m_toggle_theme_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'D' };
|
||||
|
||||
void init_settings();
|
||||
|
||||
public:
|
||||
LightSwitchInterface()
|
||||
{
|
||||
LoggerHelpers::init_logger(L"LightSwitch", L"ModuleInterface", LogSettings::lightSwitchLoggerName);
|
||||
|
||||
m_force_light_event_handle = CreateDefaultEvent(L"POWERTOYS_LIGHTSWITCH_FORCE_LIGHT");
|
||||
m_force_dark_event_handle = CreateDefaultEvent(L"POWERTOYS_LIGHTSWITCH_FORCE_DARK");
|
||||
m_manual_override_event_handle = CreateEventW(nullptr, TRUE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
|
||||
|
||||
init_settings();
|
||||
};
|
||||
|
||||
virtual const wchar_t* get_key() override
|
||||
{
|
||||
return L"LightSwitch";
|
||||
}
|
||||
|
||||
// Destroy the powertoy and free memory
|
||||
virtual void destroy() override
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
// Return the display name of the powertoy, this will be cached by the runner
|
||||
virtual const wchar_t* get_name() override
|
||||
{
|
||||
return MODULE_NAME;
|
||||
}
|
||||
|
||||
// Return the configured status for the gpo policy for the module
|
||||
virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
|
||||
{
|
||||
return powertoys_gpo::getConfiguredLightSwitchEnabledValue();
|
||||
}
|
||||
|
||||
// Return JSON with the configuration options.
|
||||
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
|
||||
{
|
||||
HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
|
||||
|
||||
// Create a Settings object with your module name
|
||||
PowerToysSettings::Settings settings(hinstance, get_name());
|
||||
settings.set_description(MODULE_DESC);
|
||||
settings.set_overview_link(L"https://aka.ms/powertoys");
|
||||
|
||||
// Boolean toggles
|
||||
settings.add_bool_toggle(
|
||||
L"changeSystem",
|
||||
L"Change System Theme",
|
||||
g_settings.m_changeSystem);
|
||||
|
||||
settings.add_bool_toggle(
|
||||
L"changeApps",
|
||||
L"Change Apps Theme",
|
||||
g_settings.m_changeApps);
|
||||
|
||||
settings.add_choice_group(
|
||||
L"scheduleMode",
|
||||
L"Theme schedule mode",
|
||||
ToString(g_settings.m_scheduleMode),
|
||||
{ { L"FixedHours", L"Set hours manually" },
|
||||
{ L"SunsetToSunrise", L"Use sunrise/sunset times" } });
|
||||
|
||||
// Integer spinners
|
||||
settings.add_int_spinner(
|
||||
L"lightTime",
|
||||
L"Time to switch to light theme (minutes after midnight).",
|
||||
g_settings.m_lightTime,
|
||||
0,
|
||||
1439,
|
||||
1);
|
||||
|
||||
settings.add_int_spinner(
|
||||
L"darkTime",
|
||||
L"Time to switch to dark theme (minutes after midnight).",
|
||||
g_settings.m_darkTime,
|
||||
0,
|
||||
1439,
|
||||
1);
|
||||
|
||||
settings.add_int_spinner(
|
||||
L"sunrise_offset",
|
||||
L"Time to offset turning on your light theme.",
|
||||
g_settings.m_sunrise_offset,
|
||||
0,
|
||||
1439,
|
||||
1);
|
||||
|
||||
settings.add_int_spinner(
|
||||
L"sunset_offset",
|
||||
L"Time to offset turning on your dark theme.",
|
||||
g_settings.m_sunset_offset,
|
||||
0,
|
||||
1439,
|
||||
1);
|
||||
|
||||
// Strings for latitude and longitude
|
||||
settings.add_string(
|
||||
L"latitude",
|
||||
L"Your latitude in decimal degrees (e.g. 39.95).",
|
||||
g_settings.m_latitude);
|
||||
|
||||
settings.add_string(
|
||||
L"longitude",
|
||||
L"Your longitude in decimal degrees (e.g. -75.16).",
|
||||
g_settings.m_longitude);
|
||||
|
||||
// One-shot actions (buttons)
|
||||
settings.add_custom_action(
|
||||
L"forceLight",
|
||||
L"Switch immediately to light theme",
|
||||
L"Force Light",
|
||||
L"{}");
|
||||
|
||||
settings.add_custom_action(
|
||||
L"forceDark",
|
||||
L"Switch immediately to dark theme",
|
||||
L"Force Dark",
|
||||
L"{}");
|
||||
|
||||
// Hotkeys
|
||||
PowerToysSettings::HotkeyObject dm_hk = PowerToysSettings::HotkeyObject::from_settings(
|
||||
m_toggle_theme_hotkey.win,
|
||||
m_toggle_theme_hotkey.ctrl,
|
||||
m_toggle_theme_hotkey.alt,
|
||||
m_toggle_theme_hotkey.shift,
|
||||
m_toggle_theme_hotkey.key);
|
||||
|
||||
settings.add_hotkey(
|
||||
L"toggle-theme-hotkey",
|
||||
L"Shortcut to toggle theme immediately",
|
||||
dm_hk);
|
||||
|
||||
// Serialize to buffer for the PowerToys runner
|
||||
return settings.serialize_to_buffer(buffer, buffer_size);
|
||||
}
|
||||
|
||||
// Signal from the Settings editor to call a custom action.
|
||||
// This can be used to spawn more complex editors.
|
||||
void call_custom_action(const wchar_t* action) override
|
||||
{
|
||||
try
|
||||
{
|
||||
auto action_object = PowerToysSettings::CustomActionObject::from_json_string(action);
|
||||
|
||||
if (action_object.get_name() == L"forceLight")
|
||||
{
|
||||
Logger::info(L"[Light Switch] Custom action triggered: Force Light");
|
||||
SetSystemTheme(true);
|
||||
SetAppsTheme(true);
|
||||
}
|
||||
else if (action_object.get_name() == L"forceDark")
|
||||
{
|
||||
Logger::info(L"[Light Switch] Custom action triggered: Force Dark");
|
||||
SetSystemTheme(false);
|
||||
SetAppsTheme(false);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::error(L"[Light Switch] Invalid custom action JSON");
|
||||
}
|
||||
}
|
||||
|
||||
// Called by the runner to pass the updated settings values as a serialized JSON.
|
||||
virtual void set_config(const wchar_t* config) override
|
||||
{
|
||||
try
|
||||
{
|
||||
auto values = PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
|
||||
|
||||
parse_hotkey(values);
|
||||
|
||||
if (auto v = values.get_bool_value(L"changeSystem"))
|
||||
{
|
||||
g_settings.m_changeSystem = *v;
|
||||
}
|
||||
|
||||
if (auto v = values.get_bool_value(L"changeApps"))
|
||||
{
|
||||
g_settings.m_changeApps = *v;
|
||||
}
|
||||
|
||||
if (auto v = values.get_string_value(L"scheduleMode"))
|
||||
{
|
||||
g_settings.m_scheduleMode = FromString(*v);
|
||||
}
|
||||
|
||||
if (auto v = values.get_int_value(L"lightTime"))
|
||||
{
|
||||
g_settings.m_lightTime = *v;
|
||||
}
|
||||
|
||||
if (auto v = values.get_int_value(L"darkTime"))
|
||||
{
|
||||
g_settings.m_darkTime = *v;
|
||||
}
|
||||
|
||||
if (auto v = values.get_int_value(L"sunrise_offset"))
|
||||
{
|
||||
g_settings.m_sunrise_offset = *v;
|
||||
}
|
||||
|
||||
if (auto v = values.get_int_value(L"m_sunset_offset"))
|
||||
{
|
||||
g_settings.m_sunset_offset = *v;
|
||||
}
|
||||
|
||||
if (auto v = values.get_string_value(L"latitude"))
|
||||
{
|
||||
g_settings.m_latitude = *v;
|
||||
}
|
||||
if (auto v = values.get_string_value(L"longitude"))
|
||||
{
|
||||
g_settings.m_longitude = *v;
|
||||
}
|
||||
|
||||
values.save_to_settings_file();
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
Logger::error("[Light Switch] set_config: Failed to parse or apply config.");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void enable()
|
||||
{
|
||||
m_enabled = true;
|
||||
Logger::info(L"Enabling Light Switch module...");
|
||||
|
||||
unsigned long powertoys_pid = GetCurrentProcessId();
|
||||
std::wstring args = L"--pid " + std::to_wstring(powertoys_pid);
|
||||
std::wstring exe_name = L"LightSwitchService\\PowerToys.LightSwitchService.exe";
|
||||
|
||||
std::wstring resolved_path(MAX_PATH, L'\0');
|
||||
DWORD result = SearchPathW(
|
||||
nullptr,
|
||||
exe_name.c_str(),
|
||||
nullptr,
|
||||
static_cast<DWORD>(resolved_path.size()),
|
||||
resolved_path.data(),
|
||||
nullptr);
|
||||
|
||||
if (result == 0 || result >= resolved_path.size())
|
||||
{
|
||||
Logger::error(
|
||||
L"Failed to locate Light Switch executable named '{}' at location '{}'",
|
||||
exe_name,
|
||||
resolved_path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
resolved_path.resize(result);
|
||||
Logger::debug(L"Resolved executable path: {}", resolved_path);
|
||||
|
||||
std::wstring command_line = L"\"" + resolved_path + L"\" " + args;
|
||||
|
||||
STARTUPINFO si = { sizeof(si) };
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
if (!CreateProcessW(
|
||||
resolved_path.c_str(),
|
||||
command_line.data(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
TRUE,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&si,
|
||||
&pi))
|
||||
{
|
||||
Logger::error(L"Failed to launch Light Switch process. {}", get_last_error_or_default(GetLastError()));
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::info(L"Light Switch process launched successfully (PID: {}).", pi.dwProcessId);
|
||||
m_process = pi.hProcess;
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
|
||||
// Disable the powertoy
|
||||
virtual void disable()
|
||||
{
|
||||
Logger::info("Light Switch disabling");
|
||||
m_enabled = false;
|
||||
|
||||
if (m_process)
|
||||
{
|
||||
constexpr DWORD timeout_ms = 1500;
|
||||
DWORD result = WaitForSingleObject(m_process, timeout_ms);
|
||||
|
||||
if (result == WAIT_TIMEOUT)
|
||||
{
|
||||
Logger::warn("Light Switch: Process didn't exit in time. Forcing termination.");
|
||||
TerminateProcess(m_process, 0);
|
||||
}
|
||||
|
||||
CloseHandle(m_manual_override_event_handle);
|
||||
m_manual_override_event_handle = nullptr;
|
||||
|
||||
CloseHandle(m_process);
|
||||
m_process = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns if the powertoys is enabled
|
||||
virtual bool is_enabled() override
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
void parse_hotkey(PowerToysSettings::PowerToyValues& settings)
|
||||
{
|
||||
auto settingsObject = settings.get_raw_json();
|
||||
if (settingsObject.GetView().Size())
|
||||
{
|
||||
try
|
||||
{
|
||||
Hotkey _temp_toggle_theme;
|
||||
auto jsonHotkeyObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_TOGGLE_THEME_HOTKEY).GetNamedObject(JSON_KEY_VALUE);
|
||||
_temp_toggle_theme.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN);
|
||||
_temp_toggle_theme.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT);
|
||||
_temp_toggle_theme.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
|
||||
_temp_toggle_theme.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
|
||||
_temp_toggle_theme.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
|
||||
m_toggle_theme_hotkey = _temp_toggle_theme;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::error("Failed to initialize Light Switch force dark mode shortcut from settings. Value will keep unchanged.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::info("Light Switch settings are empty");
|
||||
}
|
||||
}
|
||||
|
||||
virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override
|
||||
{
|
||||
if (hotkeys && buffer_size >= 1)
|
||||
{
|
||||
hotkeys[0] = m_toggle_theme_hotkey;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual bool on_hotkey(size_t hotkeyId) override
|
||||
{
|
||||
if (m_enabled)
|
||||
{
|
||||
Logger::trace(L"Light Switch hotkey pressed");
|
||||
if (!is_process_running())
|
||||
{
|
||||
enable();
|
||||
}
|
||||
else if (hotkeyId == 0)
|
||||
{
|
||||
// get current will return true if in light mode; otherwise false
|
||||
Logger::info(L"[Light Switch] Hotkey triggered: Toggle Theme");
|
||||
if (g_settings.m_changeSystem)
|
||||
{
|
||||
SetSystemTheme(!GetCurrentSystemTheme());
|
||||
}
|
||||
if (g_settings.m_changeApps)
|
||||
{
|
||||
SetAppsTheme(!GetCurrentAppsTheme());
|
||||
}
|
||||
|
||||
if (m_manual_override_event_handle)
|
||||
{
|
||||
SetEvent(m_manual_override_event_handle);
|
||||
Logger::debug(L"[Light Switch] Manual override event set");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_process_running()
|
||||
{
|
||||
return WaitForSingleObject(m_process, 0) == WAIT_TIMEOUT;
|
||||
}
|
||||
};
|
||||
|
||||
std::wstring utf8_to_wstring(const std::string& str)
|
||||
{
|
||||
if (str.empty())
|
||||
return std::wstring();
|
||||
|
||||
int size_needed = MultiByteToWideChar(
|
||||
CP_UTF8,
|
||||
0,
|
||||
str.c_str(),
|
||||
static_cast<int>(str.size()),
|
||||
nullptr,
|
||||
0);
|
||||
|
||||
std::wstring wstr(size_needed, 0);
|
||||
|
||||
MultiByteToWideChar(
|
||||
CP_UTF8,
|
||||
0,
|
||||
str.c_str(),
|
||||
static_cast<int>(str.size()),
|
||||
&wstr[0],
|
||||
size_needed);
|
||||
|
||||
return wstr;
|
||||
}
|
||||
|
||||
// Load the settings file.
|
||||
void LightSwitchInterface::init_settings()
|
||||
{
|
||||
Logger::info(L"[Light Switch] init_settings: starting to load settings for module");
|
||||
|
||||
try
|
||||
{
|
||||
PowerToysSettings::PowerToyValues settings =
|
||||
PowerToysSettings::PowerToyValues::load_from_settings_file(get_name());
|
||||
|
||||
parse_hotkey(settings);
|
||||
|
||||
if (auto v = settings.get_bool_value(L"changeSystem"))
|
||||
g_settings.m_changeSystem = *v;
|
||||
if (auto v = settings.get_bool_value(L"changeApps"))
|
||||
g_settings.m_changeApps = *v;
|
||||
if (auto v = settings.get_string_value(L"scheduleMode"))
|
||||
g_settings.m_scheduleMode = FromString(*v);
|
||||
if (auto v = settings.get_int_value(L"lightTime"))
|
||||
g_settings.m_lightTime = *v;
|
||||
if (auto v = settings.get_int_value(L"darkTime"))
|
||||
g_settings.m_darkTime = *v;
|
||||
if (auto v = settings.get_int_value(L"sunrise_offset"))
|
||||
g_settings.m_sunrise_offset = *v;
|
||||
if (auto v = settings.get_int_value(L"sunset_offset"))
|
||||
g_settings.m_sunset_offset = *v;
|
||||
if (auto v = settings.get_string_value(L"latitude"))
|
||||
g_settings.m_latitude = *v;
|
||||
if (auto v = settings.get_string_value(L"longitude"))
|
||||
g_settings.m_longitude = *v;
|
||||
|
||||
Logger::info(L"[Light Switch] init_settings: loaded successfully");
|
||||
}
|
||||
catch (const winrt::hresult_error& e)
|
||||
{
|
||||
Logger::error(L"[Light Switch] init_settings: hresult_error 0x{:08X} - {}", e.code(), e.message().c_str());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::wstring whatStr = utf8_to_wstring(e.what());
|
||||
Logger::error(L"[Light Switch] init_settings: std::exception - {}", whatStr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::error(L"[Light Switch] init_settings: unknown exception while loading settings");
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
||||
{
|
||||
return new LightSwitchInterface();
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
#include "pch.h"
|
||||
#pragma comment(lib, "windowsapp")
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
#include <common/utils/gpo.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
#include <shlwapi.h>
|
||||
#include <shellapi.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.System.h>
|
||||
#include <winrt/Windows.Globalization.h>
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
#include <winrt/Windows.ApplicationModel.Core.h>
|
||||
@@ -1,13 +0,0 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by CalculatorEngineCommon.rc
|
||||
|
||||
//////////////////////////////
|
||||
// Non-localizable
|
||||
|
||||
#define FILE_DESCRIPTION "Light Switch Module"
|
||||
#define INTERNAL_NAME "Light Switch"
|
||||
#define ORIGINAL_FILENAME "PowerToys.LightSwitchModuleInterface.dll"
|
||||
|
||||
// Non-localizable
|
||||
//////////////////////////////
|
||||
@@ -1,30 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "trace.h"
|
||||
#include <TraceLoggingProvider.h>
|
||||
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_hProvider,
|
||||
"Microsoft.PowerToys",
|
||||
// {38e8889b-9731-53f5-e901-e8a7c1753074}
|
||||
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
|
||||
TraceLoggingOptionProjectTelemetry());
|
||||
|
||||
void Trace::RegisterProvider()
|
||||
{
|
||||
TraceLoggingRegister(g_hProvider);
|
||||
}
|
||||
|
||||
void Trace::UnregisterProvider()
|
||||
{
|
||||
TraceLoggingUnregister(g_hProvider);
|
||||
}
|
||||
|
||||
void Trace::MyEvent()
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"PowerToyName_MyEvent",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <TraceLoggingActivity.h>
|
||||
#include <common/telemetry/ProjectTelemetry.h>
|
||||
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_hProvider);
|
||||
|
||||
class Trace
|
||||
{
|
||||
public:
|
||||
static void RegisterProvider();
|
||||
static void UnregisterProvider();
|
||||
static void MyEvent();
|
||||
};
|
||||
|
Before Width: | Height: | Size: 88 KiB |
@@ -1,317 +0,0 @@
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
#include "ThemeScheduler.h"
|
||||
#include "ThemeHelper.h"
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <LightSwitchSettings.h>
|
||||
#include <common/utils/gpo.h>
|
||||
#include <logger/logger_settings.h>
|
||||
#include <logger/logger.h>
|
||||
#include <utils/logger_helper.h>
|
||||
|
||||
SERVICE_STATUS g_ServiceStatus = {};
|
||||
SERVICE_STATUS_HANDLE g_StatusHandle = nullptr;
|
||||
HANDLE g_ServiceStopEvent = nullptr;
|
||||
static int g_lastUpdatedDay = -1;
|
||||
|
||||
VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv);
|
||||
VOID WINAPI ServiceCtrlHandler(DWORD dwCtrl);
|
||||
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam);
|
||||
|
||||
// Entry point for the executable
|
||||
int _tmain(int argc, TCHAR* argv[])
|
||||
{
|
||||
DWORD parentPid = 0;
|
||||
bool debug = false;
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
if (_tcscmp(argv[i], _T("--debug")) == 0)
|
||||
debug = true;
|
||||
else if (_tcscmp(argv[i], _T("--pid")) == 0 && i + 1 < argc)
|
||||
parentPid = _tstoi(argv[++i]);
|
||||
}
|
||||
|
||||
// Try to connect to SCM
|
||||
wchar_t serviceName[] = L"LightSwitchService";
|
||||
SERVICE_TABLE_ENTRYW table[] = { { serviceName, ServiceMain }, { nullptr, nullptr } };
|
||||
|
||||
LoggerHelpers::init_logger(L"LightSwitch", L"Service", LogSettings::lightSwitchLoggerName);
|
||||
|
||||
if (!StartServiceCtrlDispatcherW(table))
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
if (err == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) // not launched by SCM
|
||||
{
|
||||
g_ServiceStopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
HANDLE hThread = CreateThread(
|
||||
nullptr, 0, ServiceWorkerThread, reinterpret_cast<void*>(static_cast<ULONG_PTR>(parentPid)), 0, nullptr);
|
||||
|
||||
// Wait so the process stays alive
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
CloseHandle(hThread);
|
||||
CloseHandle(g_ServiceStopEvent);
|
||||
return 0;
|
||||
}
|
||||
return static_cast<int>(err);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Called when the service is launched by Windows
|
||||
VOID WINAPI ServiceMain(DWORD, LPTSTR*)
|
||||
{
|
||||
g_StatusHandle = RegisterServiceCtrlHandler(_T("LightSwitchService"), ServiceCtrlHandler);
|
||||
if (!g_StatusHandle)
|
||||
return;
|
||||
|
||||
g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
||||
g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
|
||||
SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
|
||||
|
||||
g_ServiceStopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
if (!g_ServiceStopEvent)
|
||||
{
|
||||
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
||||
g_ServiceStatus.dwWin32ExitCode = GetLastError();
|
||||
SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
SECURITY_ATTRIBUTES sa{ sizeof(sa) };
|
||||
sa.bInheritHandle = FALSE;
|
||||
sa.lpSecurityDescriptor = nullptr;
|
||||
|
||||
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
|
||||
SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
|
||||
|
||||
HANDLE hThread = CreateThread(nullptr, 0, ServiceWorkerThread, nullptr, 0, nullptr);
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
CloseHandle(hThread);
|
||||
|
||||
CloseHandle(g_ServiceStopEvent);
|
||||
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
||||
g_ServiceStatus.dwWin32ExitCode = 0;
|
||||
SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
|
||||
}
|
||||
|
||||
VOID WINAPI ServiceCtrlHandler(DWORD dwCtrl)
|
||||
{
|
||||
switch (dwCtrl)
|
||||
{
|
||||
case SERVICE_CONTROL_STOP:
|
||||
if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
|
||||
break;
|
||||
|
||||
g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||||
SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
|
||||
|
||||
// Signal the service to stop
|
||||
Logger::info(L"[LightSwitchService] Stop requested, signaling worker thread to exit.");
|
||||
SetEvent(g_ServiceStopEvent);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void update_sun_times(auto& settings)
|
||||
{
|
||||
double latitude = std::stod(settings.latitude);
|
||||
double longitude = std::stod(settings.longitude);
|
||||
|
||||
SYSTEMTIME st;
|
||||
GetLocalTime(&st);
|
||||
|
||||
SunTimes newTimes = CalculateSunriseSunset(latitude, longitude, st.wYear, st.wMonth, st.wDay);
|
||||
|
||||
int newLightTime = newTimes.sunriseHour * 60 + newTimes.sunriseMinute;
|
||||
int newDarkTime = newTimes.sunsetHour * 60 + newTimes.sunsetMinute;
|
||||
try
|
||||
{
|
||||
auto values = PowerToysSettings::PowerToyValues::load_from_settings_file(L"LightSwitch");
|
||||
values.add_property(L"lightTime", newLightTime);
|
||||
values.add_property(L"darkTime", newDarkTime);
|
||||
values.save_to_settings_file();
|
||||
|
||||
Logger::info(L"[LightSwitchService] Updated sun times and saved to config.");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::wstring wmsg(e.what(), e.what() + strlen(e.what()));
|
||||
Logger::error(L"[LightSwitchService] Exception during sun time update: {}", wmsg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
|
||||
{
|
||||
DWORD parentPid = static_cast<DWORD>(reinterpret_cast<ULONG_PTR>(lpParam));
|
||||
HANDLE hParent = nullptr;
|
||||
if (parentPid)
|
||||
hParent = OpenProcess(SYNCHRONIZE, FALSE, parentPid);
|
||||
|
||||
Logger::info(L"[LightSwitchService] Worker thread starting...");
|
||||
Logger::info(L"[LightSwitchService] Parent PID: {}", parentPid);
|
||||
|
||||
// Initialize settings system
|
||||
LightSwitchSettings::instance().InitFileWatcher();
|
||||
|
||||
// Open the manual override event created by the module interface
|
||||
HANDLE hManualOverride = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
|
||||
|
||||
auto applyTheme = [](int nowMinutes, int lightMinutes, int darkMinutes, const auto& settings) {
|
||||
bool isLightActive = false;
|
||||
|
||||
if (lightMinutes < darkMinutes)
|
||||
{
|
||||
// Normal case: sunrise < sunset
|
||||
isLightActive = (nowMinutes >= lightMinutes && nowMinutes < darkMinutes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wraparound case: e.g. light at 21:00, dark at 06:00
|
||||
isLightActive = (nowMinutes >= lightMinutes || nowMinutes < darkMinutes);
|
||||
}
|
||||
|
||||
bool isSystemCurrentlyLight = GetCurrentSystemTheme();
|
||||
bool isAppsCurrentlyLight = GetCurrentAppsTheme();
|
||||
|
||||
if (isLightActive)
|
||||
{
|
||||
if (settings.changeSystem && !isSystemCurrentlyLight)
|
||||
SetSystemTheme(true);
|
||||
if (settings.changeApps && !isAppsCurrentlyLight)
|
||||
SetAppsTheme(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (settings.changeSystem && isSystemCurrentlyLight)
|
||||
SetSystemTheme(false);
|
||||
if (settings.changeApps && isAppsCurrentlyLight)
|
||||
SetAppsTheme(false);
|
||||
}
|
||||
};
|
||||
|
||||
// --- At service start: immediately honor the schedule ---
|
||||
{
|
||||
SYSTEMTIME st;
|
||||
GetLocalTime(&st);
|
||||
int nowMinutes = st.wHour * 60 + st.wMinute;
|
||||
|
||||
LightSwitchSettings::instance().LoadSettings();
|
||||
const auto& settings = LightSwitchSettings::instance().settings();
|
||||
|
||||
applyTheme(nowMinutes, settings.lightTime + settings.sunrise_offset, settings.darkTime + settings.sunset_offset, settings);
|
||||
}
|
||||
|
||||
// --- Main loop: wakes once per minute or stop/parent death ---
|
||||
for (;;)
|
||||
{
|
||||
HANDLE waits[2] = { g_ServiceStopEvent, hParent };
|
||||
DWORD count = hParent ? 2 : 1;
|
||||
|
||||
SYSTEMTIME st;
|
||||
GetLocalTime(&st);
|
||||
int nowMinutes = st.wHour * 60 + st.wMinute;
|
||||
|
||||
LightSwitchSettings::instance().LoadSettings();
|
||||
const auto& settings = LightSwitchSettings::instance().settings();
|
||||
|
||||
// Refresh suntimes at day boundary
|
||||
if (g_lastUpdatedDay != st.wDay)
|
||||
{
|
||||
update_sun_times(settings);
|
||||
g_lastUpdatedDay = st.wDay;
|
||||
|
||||
Logger::info(L"[LightSwitchService] Recalculated sun times at new day boundary.");
|
||||
}
|
||||
|
||||
wchar_t msg[160];
|
||||
swprintf_s(msg,
|
||||
L"[LightSwitchService] now=%02d:%02d | light=%02d:%02d | dark=%02d:%02d",
|
||||
st.wHour,
|
||||
st.wMinute,
|
||||
settings.lightTime / 60,
|
||||
settings.lightTime % 60,
|
||||
settings.darkTime / 60,
|
||||
settings.darkTime % 60);
|
||||
Logger::info(msg);
|
||||
|
||||
// --- Manual override check ---
|
||||
bool manualOverrideActive = false;
|
||||
if (hManualOverride)
|
||||
{
|
||||
manualOverrideActive = (WaitForSingleObject(hManualOverride, 0) == WAIT_OBJECT_0);
|
||||
}
|
||||
|
||||
if (manualOverrideActive)
|
||||
{
|
||||
// Did we hit a scheduled boundary? (reset override at boundary)
|
||||
if (nowMinutes == (settings.lightTime + settings.sunrise_offset) % 1440 ||
|
||||
nowMinutes == (settings.darkTime + settings.sunset_offset) % 1440)
|
||||
{
|
||||
ResetEvent(hManualOverride);
|
||||
Logger::info(L"[LightSwitchService] Manual override cleared at boundary\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::info(L"[LightSwitchService] Skipping schedule due to manual override\n");
|
||||
goto sleep_until_next_minute;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply theme logic (only runs if no manual override or override just cleared)
|
||||
applyTheme(nowMinutes, settings.lightTime + settings.sunrise_offset, settings.darkTime + settings.sunset_offset, settings);
|
||||
|
||||
sleep_until_next_minute:
|
||||
GetLocalTime(&st);
|
||||
int msToNextMinute = (60 - st.wSecond) * 1000 - st.wMilliseconds;
|
||||
if (msToNextMinute < 50)
|
||||
msToNextMinute = 50;
|
||||
|
||||
DWORD wait = WaitForMultipleObjects(count, waits, FALSE, msToNextMinute);
|
||||
if (wait == WAIT_OBJECT_0)
|
||||
{
|
||||
Logger::info(L"[LightSwitchService] Stop event triggered <20> exiting worker loop.");
|
||||
break;
|
||||
}
|
||||
if (hParent && wait == WAIT_OBJECT_0 + 1) // parent process exited
|
||||
{
|
||||
Logger::info(L"[LightSwitchService] Parent process exited <20> stopping service.");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (hManualOverride)
|
||||
CloseHandle(hManualOverride);
|
||||
if (hParent)
|
||||
CloseHandle(hParent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
|
||||
{
|
||||
if (powertoys_gpo::getConfiguredLightSwitchEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled)
|
||||
{
|
||||
wchar_t msg[160];
|
||||
swprintf_s(
|
||||
msg,
|
||||
L"Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
|
||||
Logger::info(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int argc = 0;
|
||||
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||
int rc = _tmain(argc, argv); // reuse your existing logic
|
||||
LocalFree(argv);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{08e71c67-6a7e-4ca1-b04e-2fb336410bac}</ProjectGuid>
|
||||
<RootNamespace>LightSwitchService</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>LightSwitchService</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<PropertyGroup>
|
||||
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\$(MSBuildProjectName)\</OutDir>
|
||||
<TargetName>PowerToys.LightSwitchService</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>
|
||||
./../;
|
||||
..\..\..\common;
|
||||
..\..\..\common\logger;
|
||||
..\..\..\common\utils;
|
||||
..\..\..\common\SettingsAPI;
|
||||
..\..\..\common\Telemetry;
|
||||
..\..\..\;
|
||||
..\..\..\..\deps\spdlog\include;
|
||||
./;
|
||||
%(AdditionalIncludeDirectories)
|
||||
</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="LightSwitchService.cpp" />
|
||||
<ClCompile Include="LightSwitchSettings.cpp" />
|
||||
<ClCompile Include="SettingsConstants.cpp" />
|
||||
<ClCompile Include="ThemeHelper.cpp" />
|
||||
<ClCompile Include="ThemeScheduler.cpp" />
|
||||
<ClCompile Include="WinHookEventIDs.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="LightSwitchService.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="LightSwitchSettings.h" />
|
||||
<ClInclude Include="SettingsConstants.h" />
|
||||
<ClInclude Include="SettingsObserver.h" />
|
||||
<ClInclude Include="ThemeHelper.h" />
|
||||
<ClInclude Include="ThemeScheduler.h" />
|
||||
<ClInclude Include="WinHookEventIDs.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj">
|
||||
<Project>{4aed67b6-55fd-486f-b917-e543dee2cb3c}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\notifications\notifications.vcxproj">
|
||||
<Project>{1d5be09d-78c0-4fd7-af00-ae7c1af7c525}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\Telemetry\EtwTrace\EtwTrace.vcxproj">
|
||||
<Project>{8f021b46-362b-485c-bfba-ccf83e820cbd}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<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>
|
||||
</Project>
|
||||
@@ -1,65 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="LightSwitchService.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ThemeScheduler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<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>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ThemeScheduler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ThemeHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LightSwitchSettings.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SettingsConstants.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SettingsObserver.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WinHookEventIDs.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="LightSwitchService.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,167 +0,0 @@
|
||||
#include "LightSwitchSettings.h"
|
||||
#include <common/utils/json.h>
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
#include "SettingsObserver.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <WinHookEventIDs.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
LightSwitchSettings& LightSwitchSettings::instance()
|
||||
{
|
||||
static LightSwitchSettings inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
LightSwitchSettings::LightSwitchSettings()
|
||||
{
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
std::wstring LightSwitchSettings::GetSettingsFileName()
|
||||
{
|
||||
return PTSettingsHelper::get_module_save_file_location(L"LightSwitch");
|
||||
}
|
||||
|
||||
void LightSwitchSettings::InitFileWatcher()
|
||||
{
|
||||
const std::wstring& settingsFileName = GetSettingsFileName();
|
||||
m_settingsFileWatcher = std::make_unique<FileWatcher>(settingsFileName, [&]() {
|
||||
PostMessageW(HWND_BROADCAST, WM_PRIV_SETTINGS_CHANGED, NULL, NULL);
|
||||
});
|
||||
}
|
||||
|
||||
void LightSwitchSettings::AddObserver(SettingsObserver& observer)
|
||||
{
|
||||
m_observers.insert(&observer);
|
||||
}
|
||||
|
||||
void LightSwitchSettings::RemoveObserver(SettingsObserver& observer)
|
||||
{
|
||||
m_observers.erase(&observer);
|
||||
}
|
||||
|
||||
void LightSwitchSettings::NotifyObservers(SettingId id) const
|
||||
{
|
||||
for (auto observer : m_observers)
|
||||
{
|
||||
if (observer->WantsToBeNotified(id))
|
||||
{
|
||||
observer->SettingsUpdate(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LightSwitchSettings::LoadSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
PowerToysSettings::PowerToyValues values =
|
||||
PowerToysSettings::PowerToyValues::load_from_settings_file(L"LightSwitch");
|
||||
|
||||
|
||||
if (const auto jsonVal = values.get_string_value(L"scheduleMode"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
auto newMode = FromString(val);
|
||||
if (m_settings.scheduleMode != newMode)
|
||||
{
|
||||
m_settings.scheduleMode = newMode;
|
||||
NotifyObservers(SettingId::ScheduleMode);
|
||||
}
|
||||
}
|
||||
|
||||
// Latitude
|
||||
if (const auto jsonVal = values.get_string_value(L"latitude"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
if (m_settings.latitude != val)
|
||||
{
|
||||
m_settings.latitude = val;
|
||||
NotifyObservers(SettingId::Latitude);
|
||||
}
|
||||
}
|
||||
|
||||
// Longitude
|
||||
if (const auto jsonVal = values.get_string_value(L"longitude"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
if (m_settings.longitude != val)
|
||||
{
|
||||
m_settings.longitude = val;
|
||||
NotifyObservers(SettingId::Longitude);
|
||||
}
|
||||
}
|
||||
|
||||
// LightTime
|
||||
if (const auto jsonVal = values.get_int_value(L"lightTime"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
if (m_settings.lightTime != val)
|
||||
{
|
||||
m_settings.lightTime = val;
|
||||
NotifyObservers(SettingId::LightTime);
|
||||
}
|
||||
}
|
||||
|
||||
// DarkTime
|
||||
if (const auto jsonVal = values.get_int_value(L"darkTime"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
if (m_settings.darkTime != val)
|
||||
{
|
||||
m_settings.darkTime = val;
|
||||
NotifyObservers(SettingId::DarkTime);
|
||||
}
|
||||
}
|
||||
|
||||
// Offset
|
||||
if (const auto jsonVal = values.get_int_value(L"sunrise_offset"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
if (m_settings.sunrise_offset != val)
|
||||
{
|
||||
m_settings.sunrise_offset = val;
|
||||
NotifyObservers(SettingId::Sunrise_Offset);
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto jsonVal = values.get_int_value(L"sunset_offset"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
if (m_settings.sunset_offset != val)
|
||||
{
|
||||
m_settings.sunset_offset = val;
|
||||
NotifyObservers(SettingId::Sunset_Offset);
|
||||
}
|
||||
}
|
||||
|
||||
// ChangeSystem
|
||||
if (const auto jsonVal = values.get_bool_value(L"changeSystem"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
if (m_settings.changeSystem != val)
|
||||
{
|
||||
m_settings.changeSystem = val;
|
||||
NotifyObservers(SettingId::ChangeSystem);
|
||||
}
|
||||
}
|
||||
|
||||
// ChangeApps
|
||||
if (const auto jsonVal = values.get_bool_value(L"changeApps"))
|
||||
{
|
||||
auto val = *jsonVal;
|
||||
if (m_settings.changeApps != val)
|
||||
{
|
||||
m_settings.changeApps = val;
|
||||
NotifyObservers(SettingId::ChangeApps);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Keeps defaults if load fails
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <windows.h>
|
||||
|
||||
#include <common/SettingsAPI/FileWatcher.h>
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
#include <SettingsConstants.h>
|
||||
|
||||
class SettingsObserver;
|
||||
|
||||
enum class ScheduleMode
|
||||
{
|
||||
FixedHours,
|
||||
SunsetToSunrise
|
||||
// Add more in the future
|
||||
};
|
||||
|
||||
inline std::wstring ToString(ScheduleMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ScheduleMode::FixedHours:
|
||||
return L"FixedHours";
|
||||
case ScheduleMode::SunsetToSunrise:
|
||||
return L"SunsetToSunrise";
|
||||
default:
|
||||
return L"FixedHours";
|
||||
}
|
||||
}
|
||||
|
||||
inline ScheduleMode FromString(const std::wstring& str)
|
||||
{
|
||||
if (str == L"SunsetToSunrise")
|
||||
return ScheduleMode::SunsetToSunrise;
|
||||
else
|
||||
return ScheduleMode::FixedHours;
|
||||
}
|
||||
|
||||
struct LightSwitchConfig
|
||||
{
|
||||
ScheduleMode scheduleMode = ScheduleMode::FixedHours;
|
||||
|
||||
std::wstring latitude = L"0.0";
|
||||
std::wstring longitude = L"0.0";
|
||||
|
||||
// Stored as minutes since midnight
|
||||
int lightTime = 8 * 60; // 08:00 default
|
||||
int darkTime = 20 * 60; // 20:00 default
|
||||
|
||||
int sunrise_offset = 0;
|
||||
int sunset_offset = 0;
|
||||
|
||||
bool changeSystem = false;
|
||||
bool changeApps = false;
|
||||
};
|
||||
|
||||
class LightSwitchSettings
|
||||
{
|
||||
public:
|
||||
static LightSwitchSettings& instance();
|
||||
|
||||
static inline const LightSwitchConfig& settings()
|
||||
{
|
||||
return instance().m_settings;
|
||||
}
|
||||
|
||||
void InitFileWatcher();
|
||||
static std::wstring GetSettingsFileName();
|
||||
|
||||
void AddObserver(SettingsObserver& observer);
|
||||
void RemoveObserver(SettingsObserver& observer);
|
||||
|
||||
void LoadSettings();
|
||||
|
||||
private:
|
||||
LightSwitchSettings();
|
||||
~LightSwitchSettings() = default;
|
||||
|
||||
LightSwitchConfig m_settings;
|
||||
std::unique_ptr<FileWatcher> m_settingsFileWatcher;
|
||||
std::unordered_set<SettingsObserver*> m_observers;
|
||||
|
||||
void NotifyObservers(SettingId id) const;
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
#include "SettingsConstants.h"
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
enum class SettingId
|
||||
{
|
||||
ScheduleMode = 0,
|
||||
Latitude,
|
||||
Longitude,
|
||||
LightTime,
|
||||
DarkTime,
|
||||
Sunrise_Offset,
|
||||
Sunset_Offset,
|
||||
ChangeSystem,
|
||||
ChangeApps
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_set>
|
||||
#include "SettingsConstants.h"
|
||||
|
||||
class LightSwitchSettings;
|
||||
|
||||
class SettingsObserver
|
||||
{
|
||||
public:
|
||||
SettingsObserver(std::unordered_set<SettingId> observedSettings) :
|
||||
m_observedSettings(std::move(observedSettings))
|
||||
{
|
||||
LightSwitchSettings::instance().AddObserver(*this);
|
||||
}
|
||||
|
||||
virtual ~SettingsObserver()
|
||||
{
|
||||
LightSwitchSettings::instance().RemoveObserver(*this);
|
||||
}
|
||||
|
||||
// Override this in your class to respond to updates
|
||||
virtual void SettingsUpdate(SettingId type) {}
|
||||
|
||||
bool WantsToBeNotified(SettingId type) const noexcept
|
||||
{
|
||||
return m_observedSettings.contains(type);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unordered_set<SettingId> m_observedSettings;
|
||||
};
|
||||
@@ -1,81 +0,0 @@
|
||||
#include <windows.h>
|
||||
#include "ThemeHelper.h"
|
||||
|
||||
// Controls changing the themes.
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
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();
|
||||
@@ -1,89 +0,0 @@
|
||||
#include "ThemeScheduler.h"
|
||||
#include <utility>
|
||||
|
||||
SunTimes CalculateSunriseSunset(double latitude, double longitude, int year, int month, int day)
|
||||
{
|
||||
double zenith = 90.833;
|
||||
int N1 = static_cast<int>(floor(275.0 * month / 9.0));
|
||||
int N2 = static_cast<int>(floor((static_cast<double>(month) + 9) / 12.0));
|
||||
int N3 = static_cast<int>(floor((1.0 + floor((year - 4.0 * floor(year / 4.0) + 2.0) / 3.0))));
|
||||
int N = N1 - (N2 * N3) + day - 30;
|
||||
|
||||
auto calcTime = [&](bool sunrise) -> double {
|
||||
double lngHour = longitude / 15.0;
|
||||
double t = sunrise ? N + ((6 - lngHour) / 24) : N + ((18 - lngHour) / 24);
|
||||
|
||||
double M = (0.9856 * t) - 3.289;
|
||||
double L = M + (1.916 * sin(deg2rad(M))) + (0.020 * sin(2 * deg2rad(M))) + 282.634;
|
||||
if (L < 0)
|
||||
L += 360;
|
||||
if (L > 360)
|
||||
L -= 360;
|
||||
|
||||
double RA = rad2deg(atan(0.91764 * tan(deg2rad(L))));
|
||||
if (RA < 0)
|
||||
RA += 360;
|
||||
if (RA > 360)
|
||||
RA -= 360;
|
||||
|
||||
double Lquadrant = floor(L / 90) * 90;
|
||||
double RAquadrant = floor(RA / 90) * 90;
|
||||
RA = RA + (Lquadrant - RAquadrant);
|
||||
RA /= 15;
|
||||
|
||||
double sinDec = 0.39782 * sin(deg2rad(L));
|
||||
double cosDec = cos(asin(sinDec));
|
||||
|
||||
double cosH = (cos(deg2rad(zenith)) - (sinDec * sin(deg2rad(latitude)))) / (cosDec * cos(deg2rad(latitude)));
|
||||
if (cosH > 1 || cosH < -1)
|
||||
return -1;
|
||||
|
||||
double H = sunrise ? 360 - rad2deg(acos(cosH)) : rad2deg(acos(cosH));
|
||||
H /= 15;
|
||||
|
||||
double T = H + RA - (0.06571 * t) - 6.622;
|
||||
double UT = T - lngHour;
|
||||
while (UT < 0)
|
||||
UT += 24;
|
||||
while (UT >= 24)
|
||||
UT -= 24;
|
||||
|
||||
return UT;
|
||||
};
|
||||
|
||||
double riseUT = calcTime(true);
|
||||
double setUT = calcTime(false);
|
||||
|
||||
auto toLocal = [](double UT) {
|
||||
TIME_ZONE_INFORMATION tz;
|
||||
DWORD state = GetTimeZoneInformation(&tz);
|
||||
double totalBias = tz.Bias;
|
||||
|
||||
if (state == TIME_ZONE_ID_DAYLIGHT)
|
||||
totalBias += tz.DaylightBias;
|
||||
else if (state == TIME_ZONE_ID_STANDARD)
|
||||
totalBias += tz.StandardBias;
|
||||
|
||||
double biasHours = -(totalBias / 60.0);
|
||||
double localTime = UT + biasHours;
|
||||
|
||||
while (localTime < 0)
|
||||
localTime += 24;
|
||||
while (localTime >= 24)
|
||||
localTime -= 24;
|
||||
|
||||
int hour = static_cast<int>(localTime);
|
||||
int minute = static_cast<int>((localTime - hour) * 60);
|
||||
return std::pair<int, int>{ hour, minute };
|
||||
};
|
||||
|
||||
auto [riseHour, riseMinute] = toLocal(riseUT);
|
||||
auto [setHour, setMinute] = toLocal(setUT);
|
||||
|
||||
SunTimes result;
|
||||
result.sunriseHour = riseHour;
|
||||
result.sunriseMinute = riseMinute;
|
||||
result.sunsetHour = setHour;
|
||||
result.sunsetMinute = setMinute;
|
||||
return result;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <windows.h>
|
||||
|
||||
// Struct to hold calculated sunrise/sunset times
|
||||
struct SunTimes
|
||||
{
|
||||
int sunriseHour;
|
||||
int sunriseMinute;
|
||||
int sunsetHour;
|
||||
int sunsetMinute;
|
||||
};
|
||||
|
||||
constexpr double PI = 3.14159265358979323846;
|
||||
constexpr double deg2rad(double deg)
|
||||
{
|
||||
return deg * PI / 180.0;
|
||||
}
|
||||
constexpr double rad2deg(double rad)
|
||||
{
|
||||
return rad * 180.0 / PI;
|
||||
}
|
||||
|
||||
SunTimes CalculateSunriseSunset(double latitude, double longitude, int year, int month, int day);
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
#include "WinHookEventIDs.h"
|
||||
#include <wtypes.h>
|
||||
#include <mutex>
|
||||
|
||||
UINT WM_PRIV_SETTINGS_CHANGED = 0;
|
||||
|
||||
std::once_flag init_flag;
|
||||
|
||||
void InitializeWinhookEventIds()
|
||||
{
|
||||
std::call_once(init_flag, [&] {
|
||||
WM_PRIV_SETTINGS_CHANGED = RegisterWindowMessage(L"{11978F7B-221A-4E65-B9A9-693F7D6E4B25}");
|
||||
});
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
|
||||
extern UINT WM_PRIV_SETTINGS_CHANGED; // Scheduled when a watched settings file is updated
|
||||
|
||||
void InitializeWinhookEventIds();
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -1,16 +0,0 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by LightSwitchService.rc
|
||||
//
|
||||
#define IDI_ICON1 101
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
|
Before Width: | Height: | Size: 432 B |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 637 B |
|
Before Width: | Height: | Size: 283 B |
|
Before Width: | Height: | Size: 456 B |
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1,22 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<PropertyGroup>
|
||||
<RootNamespace>PowerToys.LightSwitch.UITests</RootNamespace>
|
||||
<AssemblyName>LightSwitch.UITests</AssemblyName>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>Library</OutputType>
|
||||
|
||||
<!-- This is a UI test, so don't run as part of MSBuild -->
|
||||
<RunVSTest>false</RunVSTest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\tests\LightSwitch.UITests\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MSTest" />
|
||||
<ProjectReference Include="..\..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,51 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap rescap">
|
||||
|
||||
<Identity
|
||||
Name="ad22010c-e33e-4459-848e-a1ed976bfd3b"
|
||||
Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
|
||||
Version="1.0.0.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="ad22010c-e33e-4459-848e-a1ed976bfd3b" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
<Properties>
|
||||
<DisplayName>LightSwitch.UITests</DisplayName>
|
||||
<PublisherDisplayName>Microsoft</PublisherDisplayName>
|
||||
<Logo>Assets\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="$targetentrypoint$">
|
||||
<uap:VisualElements
|
||||
DisplayName="LightSwitch.UITests"
|
||||
Description="LightSwitch.UITests"
|
||||
BackgroundColor="transparent"
|
||||
Square150x150Logo="Assets\Square150x150Logo.png"
|
||||
Square44x44Logo="Assets\Square44x44Logo.png">
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
</Capabilities>
|
||||
</Package>
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.PowerToys.UITest;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace LightSwitch.UITests
|
||||
{
|
||||
[TestClass]
|
||||
public class TestGeolocation : UITestBase
|
||||
{
|
||||
public TestGeolocation()
|
||||
: base(PowerToysModule.PowerToysSettings, WindowSize.Large)
|
||||
{
|
||||
}
|
||||
|
||||
[TestMethod("LightSwitch.Geolocation")]
|
||||
[TestCategory("Location")]
|
||||
public void TestGeolocationUpdate()
|
||||
{
|
||||
TestHelper.InitializeTest(this, "geolocation test");
|
||||
TestHelper.PerformGeolocationTest(this);
|
||||
TestHelper.CleanupTest(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,439 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.PowerToys.UITest;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace LightSwitch.UITests
|
||||
{
|
||||
internal sealed class TestHelper
|
||||
{
|
||||
private static readonly string[] ShortcutSeparators = { " + ", "+", " " };
|
||||
|
||||
/// <summary>
|
||||
/// Performs common test initialization: navigate to settings, enable toggle, verify shortcut
|
||||
/// </summary>
|
||||
/// <param name="testBase">The test base instance</param>
|
||||
/// <param name="testName">Name of the test for assertions</param>
|
||||
/// <returns>The activation keys for the test</returns>
|
||||
public static Key[] InitializeTest(UITestBase testBase, string testName)
|
||||
{
|
||||
LaunchFromSetting(testBase);
|
||||
|
||||
var toggleSwitch = SetLightSwitchToggle(testBase, enable: true);
|
||||
Assert.IsTrue(
|
||||
toggleSwitch.IsOn,
|
||||
$"Light Switch toggle switch should be ON for {testName}");
|
||||
|
||||
var activationKeys = ReadActivationShortcut(testBase);
|
||||
Assert.IsNotNull(activationKeys, "Should be able to read activation shortcut");
|
||||
Assert.IsTrue(activationKeys.Length > 0, "Activation shortcut should contain at least one key");
|
||||
|
||||
return activationKeys;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate to the Light Switch settings page
|
||||
/// </summary>
|
||||
public static void LaunchFromSetting(UITestBase testBase)
|
||||
{
|
||||
var lightSwitch = testBase.Session.FindAll<NavigationViewItem>(By.AccessibilityId("LightSwitchNavItem"));
|
||||
|
||||
if (lightSwitch.Count == 0)
|
||||
{
|
||||
testBase.Session.Find<NavigationViewItem>(By.AccessibilityId("SystemToolsNavItem"), 5000).Click(msPostAction: 500);
|
||||
}
|
||||
|
||||
testBase.Session.Find<NavigationViewItem>(By.AccessibilityId("LightSwitchNavItem"), 5000).Click(msPostAction: 500);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the Light Switch enable toggle switch to the specified state
|
||||
/// </summary>
|
||||
public static ToggleSwitch SetLightSwitchToggle(UITestBase testBase, bool enable)
|
||||
{
|
||||
var toggleSwitch = testBase.Session.Find<ToggleSwitch>(By.AccessibilityId("Toggle_LightSwitch"), 5000);
|
||||
|
||||
if (toggleSwitch.IsOn != enable)
|
||||
{
|
||||
toggleSwitch.Click(msPreAction: 1000, msPostAction: 2000);
|
||||
}
|
||||
|
||||
if (toggleSwitch.IsOn != enable)
|
||||
{
|
||||
testBase.Session.SendKey(Key.Space, msPreAction: 0, msPostAction: 2000);
|
||||
}
|
||||
|
||||
return toggleSwitch;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the current activation shortcut from the ShortcutControl
|
||||
/// </summary>
|
||||
public static Key[] ReadActivationShortcut(UITestBase testBase)
|
||||
{
|
||||
var shortcutCard = testBase.Session.Find<Element>(By.AccessibilityId("Shortcut_LightSwitch"), 5000);
|
||||
var shortcutButton = shortcutCard.Find<Element>(By.AccessibilityId("EditButton"), 5000);
|
||||
return ParseShortcutText(shortcutButton.HelpText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse shortcut text like "Win + Ctrl + Shift + M" into Key array
|
||||
/// </summary>
|
||||
private static Key[] ParseShortcutText(string shortcutText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(shortcutText))
|
||||
{
|
||||
return new Key[] { Key.Win, Key.Ctrl, Key.Shift, Key.D };
|
||||
}
|
||||
|
||||
var keys = new List<Key>();
|
||||
var parts = shortcutText.Split(ShortcutSeparators, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
foreach (var part in parts)
|
||||
{
|
||||
var cleanPart = part.Trim().ToLowerInvariant();
|
||||
var key = cleanPart switch
|
||||
{
|
||||
"win" or "windows" => Key.Win,
|
||||
"ctrl" or "control" => Key.Ctrl,
|
||||
"shift" => Key.Shift,
|
||||
"alt" => Key.Alt,
|
||||
_ when cleanPart.Length == 1 && char.IsLetter(cleanPart[0]) &&
|
||||
cleanPart[0] >= 'a' && cleanPart[0] <= 'z' =>
|
||||
(Key)Enum.Parse(typeof(Key), cleanPart.ToUpperInvariant()),
|
||||
_ => (Key?)null,
|
||||
};
|
||||
|
||||
if (key.HasValue)
|
||||
{
|
||||
keys.Add(key.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return keys.Count > 0 ? keys.ToArray() : new Key[] { Key.Win, Key.Ctrl, Key.Shift, Key.D };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs common test cleanup: close LightSwitch task
|
||||
/// </summary>
|
||||
/// <param name="testBase">The test base instance</param>
|
||||
public static void CleanupTest(UITestBase testBase)
|
||||
{
|
||||
// TODO: Make sure the task kills?
|
||||
// CloseLightSwitch(testBase);
|
||||
|
||||
// Ensure we're attached to settings after cleanup
|
||||
try
|
||||
{
|
||||
testBase.Session.Attach(PowerToysModule.PowerToysSettings);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore attachment errors - this is just cleanup
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a update time test operation
|
||||
/// </summary>
|
||||
public static void PerformUpdateTimeTest(UITestBase testBase)
|
||||
{
|
||||
// Make sure in manual mode
|
||||
var modeCombobox = testBase.Session.Find<Element>(By.AccessibilityId("ModeSelection_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(modeCombobox, "Mode combobox not found.");
|
||||
|
||||
var neededTabs = 6;
|
||||
|
||||
if (modeCombobox.Text != "Manual")
|
||||
{
|
||||
modeCombobox.Click();
|
||||
var manualListItem = testBase.Session.Find<Element>(By.AccessibilityId("ManualCBItem_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(manualListItem, "Manual combobox item not found.");
|
||||
manualListItem.Click();
|
||||
neededTabs = 1;
|
||||
}
|
||||
|
||||
Assert.AreEqual("Manual", modeCombobox.Text, "Mode combobox should be set to Manual.");
|
||||
|
||||
var timeline = testBase.Session.Find<Element>(By.AccessibilityId("Timeline_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(timeline, "Timeline not found.");
|
||||
|
||||
var helpText = timeline.GetAttribute("HelpText");
|
||||
string originalEndValue = GetHelpTextValue(helpText, "End");
|
||||
|
||||
for (int i = 0; i < neededTabs; i++)
|
||||
{
|
||||
testBase.Session.SendKeys(Key.Tab);
|
||||
}
|
||||
|
||||
testBase.Session.SendKeys(Key.Enter);
|
||||
testBase.Session.SendKeys(Key.Up);
|
||||
testBase.Session.SendKeys(Key.Enter);
|
||||
|
||||
helpText = timeline.GetAttribute("HelpText");
|
||||
string updatedEndValue = GetHelpTextValue(helpText, "End");
|
||||
|
||||
Assert.AreNotEqual(originalEndValue, updatedEndValue, "Timeline end time should have been updated.");
|
||||
|
||||
helpText = timeline.GetAttribute("HelpText");
|
||||
string originalStartValue = GetHelpTextValue(helpText, "Start");
|
||||
|
||||
testBase.Session.SendKeys(Key.Tab);
|
||||
testBase.Session.SendKeys(Key.Enter);
|
||||
testBase.Session.SendKeys(Key.Up);
|
||||
testBase.Session.SendKeys(Key.Enter);
|
||||
|
||||
helpText = timeline.GetAttribute("HelpText");
|
||||
string updatedStartValue = GetHelpTextValue(helpText, "Start");
|
||||
|
||||
Assert.AreNotEqual(originalStartValue, updatedStartValue, "Timeline start time should have been updated.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a update geolocation test operation
|
||||
/// </summary>
|
||||
public static void PerformUserSelectedLocationTest(UITestBase testBase)
|
||||
{
|
||||
// Make sure in sun time mode
|
||||
var modeCombobox = testBase.Session.Find<Element>(By.AccessibilityId("ModeSelection_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(modeCombobox, "Mode combobox not found.");
|
||||
|
||||
if (modeCombobox.Text != "Sunset to sunrise")
|
||||
{
|
||||
modeCombobox.Click();
|
||||
var sunriseListItem = testBase.Session.Find<Element>(By.AccessibilityId("SunCBItem_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(sunriseListItem, "Sunrise combobox item not found.");
|
||||
sunriseListItem.Click();
|
||||
}
|
||||
|
||||
Assert.AreEqual("Sunset to sunrise", modeCombobox.Text, "Mode combobox should be set to Sunset to sunrise.");
|
||||
|
||||
var setLocationButton = testBase.Session.Find<Element>(By.AccessibilityId("SetLocationButton_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(setLocationButton, "Set location button not found.");
|
||||
setLocationButton.Click();
|
||||
|
||||
var autoSuggestTextbox = testBase.Session.Find<Element>(By.AccessibilityId("CitySearchBox_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(autoSuggestTextbox, "City search box not found.");
|
||||
autoSuggestTextbox.Click();
|
||||
autoSuggestTextbox.SendKeys("Seattle");
|
||||
autoSuggestTextbox.SendKeys(OpenQA.Selenium.Keys.Down);
|
||||
autoSuggestTextbox.SendKeys(OpenQA.Selenium.Keys.Enter);
|
||||
|
||||
var latLong = testBase.Session.Find<Element>(By.AccessibilityId("LocationResultText_LightSwitch"), 5000);
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(latLong.Text));
|
||||
|
||||
var sunrise = testBase.Session.Find<Element>(By.AccessibilityId("SunriseText_LightSwitch"), 5000);
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(sunrise.Text));
|
||||
|
||||
var sunset = testBase.Session.Find<Element>(By.AccessibilityId("SunsetText_LightSwitch"), 5000);
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(sunset.Text));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a update geolocation test operation
|
||||
/// </summary>
|
||||
public static void PerformGeolocationTest(UITestBase testBase)
|
||||
{
|
||||
// Make sure in sun time mode
|
||||
var modeCombobox = testBase.Session.Find<Element>(By.AccessibilityId("ModeSelection_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(modeCombobox, "Mode combobox not found.");
|
||||
|
||||
if (modeCombobox.Text != "Sunset to sunrise")
|
||||
{
|
||||
modeCombobox.Click();
|
||||
var sunriseListItem = testBase.Session.Find<Element>(By.AccessibilityId("SunCBItem_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(sunriseListItem, "Sunrise combobox item not found.");
|
||||
sunriseListItem.Click();
|
||||
}
|
||||
|
||||
Assert.AreEqual("Sunset to sunrise", modeCombobox.Text, "Mode combobox should be set to Sunset to sunrise.");
|
||||
|
||||
// Click the select city button
|
||||
var setLocationButton = testBase.Session.Find<Element>(By.AccessibilityId("SetLocationButton_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(setLocationButton, "Set location button not found.");
|
||||
setLocationButton.Click(msPostAction: 8000);
|
||||
|
||||
var latLong = testBase.Session.Find<Element>(By.AccessibilityId("LocationResultText_LightSwitch"), 5000);
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(latLong.Text));
|
||||
|
||||
var sunrise = testBase.Session.Find<Element>(By.AccessibilityId("SunriseText_LightSwitch"), 5000);
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(sunrise.Text));
|
||||
|
||||
var sunset = testBase.Session.Find<Element>(By.AccessibilityId("SunsetText_LightSwitch"), 5000);
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(sunset.Text));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a update time test operation
|
||||
/// </summary>
|
||||
public static void PerformOffsetTest(UITestBase testBase)
|
||||
{
|
||||
// Make sure in sun time mode
|
||||
var modeCombobox = testBase.Session.Find<Element>(By.AccessibilityId("ModeSelection_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(modeCombobox, "Mode combobox not found.");
|
||||
|
||||
if (modeCombobox.Text != "Sunset to sunrise")
|
||||
{
|
||||
modeCombobox.Click();
|
||||
var sunriseListItem = testBase.Session.Find<Element>(By.AccessibilityId("SunCBItem_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(sunriseListItem, "Sunrise combobox item not found.");
|
||||
sunriseListItem.Click();
|
||||
}
|
||||
|
||||
Assert.AreEqual("Sunset to sunrise", modeCombobox.Text, "Mode combobox should be set to Sunset to sunrise.");
|
||||
|
||||
// Testing sunrise offset
|
||||
var sunriseOffset = testBase.Session.Find<Element>(By.AccessibilityId("SunriseOffset_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(sunriseOffset, "Sunrise offset number box not found.");
|
||||
|
||||
var timeline = testBase.Session.Find<Element>(By.AccessibilityId("Timeline_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(timeline, "Timeline not found.");
|
||||
|
||||
var helpText = timeline.GetAttribute("HelpText");
|
||||
string originalStartValue = GetHelpTextValue(helpText, "Start");
|
||||
|
||||
sunriseOffset.Click();
|
||||
testBase.Session.SendKeys(Key.Up);
|
||||
|
||||
helpText = timeline.GetAttribute("HelpText");
|
||||
string updatedStartValue = GetHelpTextValue(helpText, "Start");
|
||||
|
||||
Assert.AreNotEqual(originalStartValue, updatedStartValue, "Timeline start time should have been updated.");
|
||||
|
||||
// Testing sunset offset
|
||||
var sunsetOffset = testBase.Session.Find<Element>(By.AccessibilityId("SunsetOffset_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(sunsetOffset, "Sunrise offset number box not found.");
|
||||
|
||||
helpText = timeline.GetAttribute("HelpText");
|
||||
string originalEndValue = GetHelpTextValue(helpText, "End");
|
||||
|
||||
sunsetOffset.Click();
|
||||
testBase.Session.SendKeys(Key.Up);
|
||||
|
||||
helpText = timeline.GetAttribute("HelpText");
|
||||
string updatedEndValue = GetHelpTextValue(helpText, "End");
|
||||
|
||||
Assert.AreNotEqual(originalEndValue, updatedEndValue, "Timeline end time should have been updated.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a test for shortcut changing themes
|
||||
/// </summary>
|
||||
public static void PerformShortcutTest(UITestBase testBase, Key[] activationKeys)
|
||||
{
|
||||
// Test when both are checked
|
||||
var systemCheckbox = testBase.Session.Find<Element>(By.AccessibilityId("ChangeSystemCheckbox_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(systemCheckbox, "System checkbox not found.");
|
||||
|
||||
var scrollViewer = testBase.Session.Find<Element>(By.AccessibilityId("PageScrollViewer"));
|
||||
systemCheckbox.EnsureVisible(scrollViewer);
|
||||
|
||||
int neededTabs = 10;
|
||||
|
||||
if (!systemCheckbox.Selected)
|
||||
{
|
||||
for (int i = 0; i < neededTabs; i++)
|
||||
{
|
||||
testBase.Session.SendKeys(Key.Tab);
|
||||
}
|
||||
|
||||
systemCheckbox.Click();
|
||||
}
|
||||
|
||||
Assert.IsTrue(systemCheckbox.Selected, "System checkbox should be checked.");
|
||||
|
||||
var appsCheckbox = testBase.Session.Find<Element>(By.AccessibilityId("ChangeAppsCheckbox_LightSwitch"), 5000);
|
||||
Assert.IsNotNull(appsCheckbox, "Apps checkbox not found.");
|
||||
|
||||
if (!appsCheckbox.Selected)
|
||||
{
|
||||
appsCheckbox.Click();
|
||||
}
|
||||
|
||||
Assert.IsTrue(appsCheckbox.Selected, "Apps checkbox should be checked.");
|
||||
|
||||
var systemBeforeValue = GetSystemTheme();
|
||||
var appsBeforeValue = GetAppsTheme();
|
||||
|
||||
testBase.Session.SendKeys(activationKeys);
|
||||
Task.Delay(5000).Wait();
|
||||
|
||||
var systemAfterValue = GetSystemTheme();
|
||||
var appsAfterValue = GetAppsTheme();
|
||||
|
||||
Assert.AreNotEqual(systemBeforeValue, systemAfterValue, "System theme should have changed.");
|
||||
Assert.AreNotEqual(appsBeforeValue, appsAfterValue, "Apps theme should have changed.");
|
||||
|
||||
// Test with nothing checked
|
||||
if (systemCheckbox.Selected)
|
||||
{
|
||||
systemCheckbox.Click();
|
||||
}
|
||||
|
||||
if (appsCheckbox.Selected)
|
||||
{
|
||||
appsCheckbox.Click();
|
||||
}
|
||||
|
||||
Assert.IsFalse(systemCheckbox.Selected, "System checkbox should be unchecked.");
|
||||
Assert.IsFalse(appsCheckbox.Selected, "Apps checkbox should be unchecked.");
|
||||
|
||||
var noneSystemBeforeValue = GetSystemTheme();
|
||||
var noneAppsBeforeValue = GetAppsTheme();
|
||||
|
||||
testBase.Session.SendKeys(activationKeys);
|
||||
Task.Delay(5000).Wait();
|
||||
|
||||
var noneSystemAfterValue = GetSystemTheme();
|
||||
var noneAppsAfterValue = GetAppsTheme();
|
||||
|
||||
Assert.AreEqual(noneSystemBeforeValue, noneSystemAfterValue, "System theme should not have changed.");
|
||||
Assert.AreEqual(noneAppsBeforeValue, noneAppsAfterValue, "Apps theme should not have changed.");
|
||||
}
|
||||
|
||||
/* Helpers */
|
||||
private static int GetSystemTheme()
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
|
||||
if (key is null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (int)key.GetValue("SystemUsesLightTheme", 1);
|
||||
}
|
||||
|
||||
private static int GetAppsTheme()
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
|
||||
if (key is null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (int)key.GetValue("AppsUseLightTheme", 1);
|
||||
}
|
||||
|
||||
private static string GetHelpTextValue(string helpText, string key)
|
||||
{
|
||||
foreach (var part in helpText.Split(';'))
|
||||
{
|
||||
var kv = part.Split('=');
|
||||
if (kv.Length == 2 && kv[0] == key)
|
||||
{
|
||||
return kv[1];
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.PowerToys.UITest;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace LightSwitch.UITests
|
||||
{
|
||||
[TestClass]
|
||||
public class TestOffset : UITestBase
|
||||
{
|
||||
public TestOffset()
|
||||
: base(PowerToysModule.PowerToysSettings, WindowSize.Large)
|
||||
{
|
||||
}
|
||||
|
||||
[TestMethod("LightSwitch.Offset")]
|
||||
[TestCategory("Time")]
|
||||
public void TestTimeOffset()
|
||||
{
|
||||
TestHelper.InitializeTest(this, "offset test");
|
||||
TestHelper.PerformOffsetTest(this);
|
||||
TestHelper.CleanupTest(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.PowerToys.UITest;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace LightSwitch.UITests
|
||||
{
|
||||
[TestClass]
|
||||
public class TestShortcut : UITestBase
|
||||
{
|
||||
public TestShortcut()
|
||||
: base(PowerToysModule.PowerToysSettings, WindowSize.Large)
|
||||
{
|
||||
}
|
||||
|
||||
[TestMethod("LightSwitch.TestShortcut")]
|
||||
[TestCategory("Shortcut")]
|
||||
public void TestLightSwitchShortcut()
|
||||
{
|
||||
var activationKeys = TestHelper.InitializeTest(this, "light switch shortcut test");
|
||||
TestHelper.PerformShortcutTest(this, activationKeys);
|
||||
TestHelper.CleanupTest(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.PowerToys.UITest;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace LightSwitch.UITests
|
||||
{
|
||||
[TestClass]
|
||||
public class TestUpdateManualTime : UITestBase
|
||||
{
|
||||
public TestUpdateManualTime()
|
||||
: base(PowerToysModule.PowerToysSettings, WindowSize.Large)
|
||||
{
|
||||
}
|
||||
|
||||
[TestMethod("LightSwitch.UpdateManualTime")]
|
||||
[TestCategory("Time")]
|
||||
public void TestUpdateTime()
|
||||
{
|
||||
TestHelper.InitializeTest(this, "update manual time test");
|
||||
TestHelper.PerformUpdateTimeTest(this);
|
||||
TestHelper.CleanupTest(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.PowerToys.UITest;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace LightSwitch.UITests
|
||||
{
|
||||
[TestClass]
|
||||
public class TestUserSelectedLocation : UITestBase
|
||||
{
|
||||
public TestUserSelectedLocation()
|
||||
: base(PowerToysModule.PowerToysSettings, WindowSize.Large)
|
||||
{
|
||||
}
|
||||
|
||||
[TestMethod("LightSwitch.UserSelectedLocation")]
|
||||
[TestCategory("Location")]
|
||||
public void TestUserSelectedLocationUpdate()
|
||||
{
|
||||
TestHelper.InitializeTest(this, "user selected location test");
|
||||
TestHelper.PerformUserSelectedLocationTest(this);
|
||||
TestHelper.CleanupTest(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="LightSwitch.UITests.app"/>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- The ID below informs the system that this application is compatible with OS features first introduced in Windows 10.
|
||||
It is necessary to support features in unpackaged applications, for example the custom titlebar implementation.
|
||||
For more info see https://docs.microsoft.com/windows/apps/windows-app-sdk/use-windows-app-sdk-run-time#declare-os-compatibility-in-your-application-manifest -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
@@ -12,8 +12,8 @@ enum struct FindMyMouseActivationMethod : int
|
||||
|
||||
constexpr bool FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE = true;
|
||||
// Default colors now include full alpha. Opacity is encoded directly in color alpha (legacy overlay_opacity migrated into A channel)
|
||||
const winrt::Windows::UI::Color FIND_MY_MOUSE_DEFAULT_BACKGROUND_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(128, 0, 0, 0);
|
||||
const winrt::Windows::UI::Color FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(128, 255, 255, 255);
|
||||
const winrt::Windows::UI::Color FIND_MY_MOUSE_DEFAULT_BACKGROUND_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(255, 0, 0, 0);
|
||||
const winrt::Windows::UI::Color FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(255, 255, 255, 255);
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_RADIUS = 100;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS = 500;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM = 9;
|
||||
@@ -43,4 +43,4 @@ int FindMyMouseMain(HINSTANCE hinst, const FindMyMouseSettings& settings);
|
||||
void FindMyMouseDisable();
|
||||
bool FindMyMouseIsEnabled();
|
||||
void FindMyMouseApplySettings(const FindMyMouseSettings& settings);
|
||||
HWND GetSonarHwnd() noexcept;
|
||||
HWND GetSonarHwnd() noexcept;
|
||||
@@ -1055,13 +1055,8 @@ namespace MouseWithoutBorders.Class
|
||||
|
||||
if (machineId == 0)
|
||||
{
|
||||
var newMachineId = Common.Ran.Next();
|
||||
_properties.MachineID.Value = newMachineId;
|
||||
machineId = newMachineId;
|
||||
if (!PauseInstantSaving)
|
||||
{
|
||||
SaveSettings();
|
||||
}
|
||||
_properties.MachineID.Value = Common.Ran.Next();
|
||||
machineId = _properties.MachineID.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1073,11 +1068,6 @@ namespace MouseWithoutBorders.Class
|
||||
lock (_loadingSettingsLock)
|
||||
{
|
||||
_properties.MachineID.Value = value;
|
||||
machineId = value;
|
||||
if (!PauseInstantSaving)
|
||||
{
|
||||
SaveSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,10 +96,7 @@ typedef struct {
|
||||
#define SHALLOW_DESTROY 2
|
||||
#define LIVE_DRAW_ZOOM 3
|
||||
|
||||
#define PEN_COLOR_HIGHLIGHT(Pencolor) ((Pencolor >> 24) != 0xFF)
|
||||
#define PEN_COLOR_BLUR(Pencolor) ((Pencolor & 0x00FFFFFF) == COLOR_BLUR)
|
||||
|
||||
#define CURSOR_SAVE_MARGIN 4
|
||||
#define PEN_COLOR_HIGHLIGHT(Pencolor) (Pencolor >> 24) != 0xFF
|
||||
|
||||
|
||||
typedef BOOL (__stdcall *type_pGetMonitorInfo)(
|
||||
@@ -146,14 +143,7 @@ typedef BOOL(__stdcall *type_pMagSetWindowFilterList)(
|
||||
int count,
|
||||
HWND* pHWND
|
||||
);
|
||||
typedef BOOL(__stdcall* type_pMagSetLensUseBitmapSmoothing)(
|
||||
_In_ HWND,
|
||||
_In_ BOOL
|
||||
);
|
||||
typedef BOOL(__stdcall* type_MagSetFullscreenUseBitmapSmoothing)(
|
||||
BOOL fUseBitmapSmoothing
|
||||
);
|
||||
typedef BOOL(__stdcall* type_pMagInitialize)(VOID);
|
||||
typedef BOOL (__stdcall *type_pMagInitialize)(VOID);
|
||||
|
||||
typedef BOOL(__stdcall *type_pGetPointerType)(
|
||||
_In_ UINT32 pointerId,
|
||||
|
||||
@@ -121,8 +121,8 @@ FONT 8, "MS Shell Dlg", 0, 0, 0x0
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK",IDOK,166,306,50,14
|
||||
PUSHBUTTON "Cancel",IDCANCEL,223,306,50,14
|
||||
LTEXT "ZoomIt v9.10",IDC_VERSION,42,7,73,10
|
||||
LTEXT "Copyright © 2006-2025 Mark Russinovich",IDC_COPYRIGHT,42,17,231,8
|
||||
LTEXT "ZoomIt v9.01",IDC_VERSION,42,7,73,10
|
||||
LTEXT "Copyright © 2006-2025 Mark Russinovich",IDC_COPYRIGHT,42,17,166,8
|
||||
CONTROL "<a HREF=""https://www.sysinternals.com"">Sysinternals - www.sysinternals.com</a>",IDC_LINK,
|
||||
"SysLink",WS_TABSTOP,42,26,150,9
|
||||
ICON "APPICON",IDC_STATIC,12,9,20,20
|
||||
@@ -149,8 +149,7 @@ BEGIN
|
||||
CONTROL "",IDC_TIMER_POS7,"Button",BS_AUTORADIOBUTTON,63,108,10,10
|
||||
CONTROL "",IDC_TIMER_POS8,"Button",BS_AUTORADIOBUTTON,79,108,10,10
|
||||
CONTROL "",IDC_TIMER_POS9,"Button",BS_AUTORADIOBUTTON,97,108,10,10
|
||||
CONTROL "Show background bitmap:",IDC_CHECK_BACKGROUND_FILE,
|
||||
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,122,99,10,WS_EX_RIGHT
|
||||
CONTROL "Show background bitmap:",IDC_CHECK_BACKGROUND_FILE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,122,99,10,WS_EX_RIGHT
|
||||
CONTROL "Use faded desktop as background",IDC_STATIC_DESKTOP_BACKGROUND,
|
||||
"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,46,135,125,10
|
||||
CONTROL "Use image file as background",IDC_STATIC_BACKGROUND_FILE,
|
||||
@@ -166,25 +165,23 @@ BEGIN
|
||||
CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME | SS_SUNKEN,7,196,193,1,WS_EX_CLIENTEDGE
|
||||
END
|
||||
|
||||
ZOOM DIALOGEX 0, 0, 260, 170
|
||||
ZOOM DIALOGEX 0, 0, 260, 158
|
||||
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
CONTROL "",IDC_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,59,57,80,12
|
||||
LTEXT "After toggling ZoomIt you can zoom in with the mouse wheel or up and down arrow keys. Exit zoom mode with Escape or by pressing the right mouse button.",IDC_STATIC,7,6,246,26
|
||||
LTEXT "Zoom Toggle:",IDC_STATIC,7,59,51,8
|
||||
CONTROL "",IDC_ZOOM_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,53,118,150,15,WS_EX_TRANSPARENT
|
||||
LTEXT "Specify the initial level of magnification when zooming in:",IDC_STATIC,7,105,215,10
|
||||
LTEXT "1.25",IDC_STATIC,52,136,16,8
|
||||
LTEXT "1.5",IDC_STATIC,82,136,12,8
|
||||
LTEXT "1.75",IDC_STATIC,108,136,16,8
|
||||
LTEXT "2.0",IDC_STATIC,138,136,12,8
|
||||
LTEXT "3.0",IDC_STATIC,164,136,12,8
|
||||
LTEXT "4.0",IDC_STATIC,190,136,12,8
|
||||
CONTROL "",IDC_ZOOM_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,53,104,150,15,WS_EX_TRANSPARENT
|
||||
LTEXT "Specify the initial level of magnification when zooming in:",IDC_STATIC,7,91,215,10
|
||||
LTEXT "1.25",IDC_STATIC,52,122,16,8
|
||||
LTEXT "1.5",IDC_STATIC,82,122,12,8
|
||||
LTEXT "1.75",IDC_STATIC,108,122,16,8
|
||||
LTEXT "2.0",IDC_STATIC,138,122,12,8
|
||||
LTEXT "3.0",IDC_STATIC,164,122,12,8
|
||||
LTEXT "4.0",IDC_STATIC,190,122,12,8
|
||||
CONTROL "Animate zoom in and zoom out:",IDC_ANIMATE_ZOOM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,74,116,10
|
||||
CONTROL "Smooth zoomed image:",IDC_SMOOTH_IMAGE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,88,116,10
|
||||
LTEXT "Copy a zoomed screen with Ctrl+C or save it by typing Ctrl+S. Crop the copy or save region by entering Ctrl+Shift instead of Ctrl.",IDC_STATIC,7,148,246,17
|
||||
LTEXT "Copy a zoomed screen with Ctrl+C or save it by typing Ctrl+S. Crop the copy or save region by entering Ctrl+Shift instead of Ctrl.",IDC_STATIC,6,34,246,18
|
||||
LTEXT "Copy a zoomed screen with Ctrl+C or save it by typing Ctrl+S. Crop the copy or save region by entering Ctrl+Shift instead of Ctrl.",IDC_STATIC,7,34,246,17
|
||||
END
|
||||
|
||||
DRAW DIALOGEX 0, 0, 260, 228
|
||||
@@ -298,8 +295,7 @@ BEGIN
|
||||
LTEXT "DemoType toggle:",IDC_STATIC,7,157,63,8
|
||||
PUSHBUTTON "&...",IDC_DEMOTYPE_BROWSE,231,137,16,13
|
||||
CONTROL "",IDC_DEMOTYPE_SPEED_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,52,202,150,11,WS_EX_TRANSPARENT
|
||||
CONTROL "Drive input with typing:",IDC_DEMOTYPE_USER_DRIVEN,
|
||||
"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,173,88,10
|
||||
CONTROL "Drive input with typing:",IDC_DEMOTYPE_USER_DRIVEN,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,173,88,10
|
||||
LTEXT "DemoType typing speed:",IDC_STATIC,7,189,215,10
|
||||
LTEXT "Slow",IDC_DEMOTYPE_STATIC1,51,213,18,8
|
||||
LTEXT "Fast",IDC_DEMOTYPE_STATIC2,186,213,17,8
|
||||
@@ -417,8 +413,8 @@ ACCELERATORS ACCELERATORS
|
||||
BEGIN
|
||||
"C", IDC_COPY, VIRTKEY, CONTROL, NOINVERT
|
||||
"S", IDC_SAVE, VIRTKEY, CONTROL, NOINVERT
|
||||
"C", IDC_COPY_CROP, VIRTKEY, SHIFT, CONTROL, NOINVERT
|
||||
"S", IDC_SAVE_CROP, VIRTKEY, SHIFT, CONTROL, NOINVERT
|
||||
"C", IDC_COPY_CROP, VIRTKEY, SHIFT, CONTROL, NOINVERT
|
||||
"S", IDC_SAVE_CROP, VIRTKEY, SHIFT, CONTROL, NOINVERT
|
||||
END
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ DWORD g_SnipToggleKey = ((HOTKEYF_CONTROL) << 8) | '6';
|
||||
DWORD g_ShowExpiredTime = 1;
|
||||
DWORD g_SliderZoomLevel = 3;
|
||||
BOOLEAN g_AnimateZoom = TRUE;
|
||||
BOOLEAN g_SmoothImage = TRUE;
|
||||
DWORD g_PenColor = COLOR_RED;
|
||||
DWORD g_BreakPenColor = COLOR_RED;
|
||||
DWORD g_RootPenWidth = PEN_WIDTH;
|
||||
@@ -73,7 +72,6 @@ REG_SETTING RegSettings[] = {
|
||||
{ L"ShowTrayIcon", SETTING_TYPE_BOOLEAN, 0, &g_ShowTrayIcon, static_cast<DOUBLE>(g_ShowTrayIcon) },
|
||||
// NOTE: AnimateZoom is misspelled, but since it is a user setting stored in the registry we must continue to misspell it.
|
||||
{ L"AnimnateZoom", SETTING_TYPE_BOOLEAN, 0, &g_AnimateZoom, static_cast<DOUBLE>(g_AnimateZoom) },
|
||||
{ L"SmoothImage", SETTING_TYPE_BOOLEAN, 0, &g_SmoothImage, static_cast<DOUBLE>(g_SmoothImage) },
|
||||
{ L"TelescopeZoomOut", SETTING_TYPE_BOOLEAN, 0, &g_TelescopeZoomOut, static_cast<DOUBLE>(g_TelescopeZoomOut) },
|
||||
{ L"SnapToGrid", SETTING_TYPE_BOOLEAN, 0, &g_SnapToGrid, static_cast<DOUBLE>(g_SnapToGrid) },
|
||||
{ L"ZoominSliderLevel", SETTING_TYPE_DWORD, 0, &g_SliderZoomLevel, static_cast<DOUBLE>(g_SliderZoomLevel) },
|
||||
|
||||
@@ -170,8 +170,6 @@ type_pMagSetFullscreenTransform pMagSetFullscreenTransform;
|
||||
type_pMagSetInputTransform pMagSetInputTransform;
|
||||
type_pMagShowSystemCursor pMagShowSystemCursor;
|
||||
type_pMagSetWindowFilterList pMagSetWindowFilterList;
|
||||
type_MagSetFullscreenUseBitmapSmoothing pMagSetFullscreenUseBitmapSmoothing;
|
||||
type_pMagSetLensUseBitmapSmoothing pMagSetLensUseBitmapSmoothing;
|
||||
type_pMagInitialize pMagInitialize;
|
||||
type_pDwmIsCompositionEnabled pDwmIsCompositionEnabled;
|
||||
type_pGetPointerType pGetPointerType;
|
||||
@@ -1101,8 +1099,6 @@ void DrawHighlightedShape( DWORD Shape, HDC hdcScreenCompat, Gdiplus::Brush *pBr
|
||||
// Create a new bitmap that's the size of the area covered by the line + 2 * g_PenWidth
|
||||
Gdiplus::Rect lineBounds(min(x1, x2), min(y1, y2), abs(x2 - x1), abs(y2 - y1));
|
||||
|
||||
OutputDebug(L"DrawHighlightedShape\n");
|
||||
|
||||
// Expand for line drawing
|
||||
if (Shape == DRAW_LINE)
|
||||
lineBounds.Inflate(static_cast<int>(g_PenWidth / 2), static_cast<int>(g_PenWidth / 2));
|
||||
@@ -1190,7 +1186,7 @@ void DrawHighlightedShape( DWORD Shape, HDC hdcScreenCompat, Gdiplus::Brush *pBr
|
||||
DeleteDC(hdcDIBOrig);
|
||||
|
||||
// Invalidate the updated rectangle
|
||||
//InvalidateGdiplusRect(hWnd, lineBounds);
|
||||
// InvalidateGdiplusRect(hWnd, lineBounds);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -1288,12 +1284,7 @@ void ScaleImage( HDC hdcDst, float xDst, float yDst, float wDst, float hDst,
|
||||
{
|
||||
Gdiplus::Bitmap srcBitmap( bmSrc, NULL );
|
||||
|
||||
// Use high quality interpolation when smooth image is enabled
|
||||
if (g_SmoothImage) {
|
||||
dstGraphics.SetInterpolationMode( Gdiplus::InterpolationModeHighQuality );
|
||||
} else {
|
||||
dstGraphics.SetInterpolationMode( Gdiplus::InterpolationModeLowQuality );
|
||||
}
|
||||
dstGraphics.SetInterpolationMode( Gdiplus::InterpolationModeLowQuality );
|
||||
dstGraphics.SetPixelOffsetMode( Gdiplus::PixelOffsetModeHalf );
|
||||
|
||||
dstGraphics.DrawImage( &srcBitmap, Gdiplus::RectF(xDst,yDst,wDst,hDst), xSrc, ySrc, wSrc, hSrc, Gdiplus::UnitPixel );
|
||||
@@ -2080,8 +2071,6 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
|
||||
IsAutostartConfigured() ? BST_CHECKED: BST_UNCHECKED );
|
||||
CheckDlgButton( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ANIMATE_ZOOM,
|
||||
g_AnimateZoom ? BST_CHECKED: BST_UNCHECKED );
|
||||
CheckDlgButton( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_SMOOTH_IMAGE,
|
||||
g_SmoothImage ? BST_CHECKED: BST_UNCHECKED );
|
||||
|
||||
SendMessage( GetDlgItem(g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ZOOM_SLIDER), TBM_SETRANGE, false, MAKELONG(0,_countof(g_ZoomLevels)-1) );
|
||||
SendMessage( GetDlgItem(g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ZOOM_SLIDER), TBM_SETPOS, true, g_SliderZoomLevel );
|
||||
@@ -2221,7 +2210,6 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
|
||||
}
|
||||
g_ShowTrayIcon = IsDlgButtonChecked( hDlg, IDC_SHOW_TRAY_ICON ) == BST_CHECKED;
|
||||
g_AnimateZoom = IsDlgButtonChecked( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ANIMATE_ZOOM ) == BST_CHECKED;
|
||||
g_SmoothImage = IsDlgButtonChecked( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_SMOOTH_IMAGE ) == BST_CHECKED;
|
||||
g_DemoTypeUserDriven = IsDlgButtonChecked( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_USER_DRIVEN ) == BST_CHECKED;
|
||||
|
||||
newToggleKey = static_cast<DWORD>(SendMessage( GetDlgItem( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_HOTKEY), HKM_GETHOTKEY, 0, 0 ));
|
||||
@@ -2735,6 +2723,7 @@ VOID DrawShape( DWORD Shape, HDC hDc, RECT *Rect, bool UseGdiPlus = false )
|
||||
bool isBlur = false;
|
||||
|
||||
Gdiplus::Graphics dstGraphics(hDc);
|
||||
|
||||
if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 )
|
||||
{
|
||||
dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
|
||||
@@ -2757,7 +2746,6 @@ VOID DrawShape( DWORD Shape, HDC hDc, RECT *Rect, bool UseGdiPlus = false )
|
||||
InflateRect(Rect, g_PenWidth / 2, g_PenWidth / 2);
|
||||
isBlur = true;
|
||||
}
|
||||
OutputDebug(L"Draw shape: highlight: %d pbrush: %d\n", PEN_COLOR_HIGHLIGHT(g_PenColor), pBrush != NULL);
|
||||
|
||||
switch (Shape) {
|
||||
case DRAW_RECTANGLE:
|
||||
@@ -2932,7 +2920,7 @@ void InvalidateCursorMoveArea( HWND hWnd, float zoomLevel, int width, int height
|
||||
{
|
||||
int x, y;
|
||||
RECT rc;
|
||||
int invWidth = g_PenWidth + CURSOR_SAVE_MARGIN;
|
||||
int invWidth = g_PenWidth;
|
||||
|
||||
if( DrawHighlightedCursor( zoomLevel, width, height ) ) {
|
||||
|
||||
@@ -2957,7 +2945,7 @@ void InvalidateCursorMoveArea( HWND hWnd, float zoomLevel, int width, int height
|
||||
void SaveCursorArea( HDC hDcTarget, HDC hDcSource, POINT pt )
|
||||
{
|
||||
OutputDebug( L"SaveCursorArea\n");
|
||||
int penWidth = g_PenWidth + CURSOR_SAVE_MARGIN;
|
||||
int penWidth = g_PenWidth + 2;
|
||||
BitBlt( hDcTarget, 0, 0, penWidth +CURSOR_ARM_LENGTH*2, penWidth +CURSOR_ARM_LENGTH*2,
|
||||
hDcSource, static_cast<INT> (pt.x- penWidth /2)-CURSOR_ARM_LENGTH,
|
||||
static_cast<INT>(pt.y- penWidth /2)-CURSOR_ARM_LENGTH, SRCCOPY|CAPTUREBLT );
|
||||
@@ -2971,7 +2959,7 @@ void SaveCursorArea( HDC hDcTarget, HDC hDcSource, POINT pt )
|
||||
void RestoreCursorArea( HDC hDcTarget, HDC hDcSource, POINT pt )
|
||||
{
|
||||
OutputDebug( L"RestoreCursorArea\n");
|
||||
int penWidth = g_PenWidth + CURSOR_SAVE_MARGIN;
|
||||
int penWidth = g_PenWidth + 2;
|
||||
BitBlt( hDcTarget, static_cast<INT>(pt.x- penWidth /2)-CURSOR_ARM_LENGTH,
|
||||
static_cast<INT>(pt.y- penWidth /2)-CURSOR_ARM_LENGTH, penWidth +CURSOR_ARM_LENGTH*2,
|
||||
penWidth + CURSOR_ARM_LENGTH*2, hDcSource, 0, 0, SRCCOPY|CAPTUREBLT );
|
||||
@@ -4190,11 +4178,6 @@ LRESULT APIENTRY MainWndProc(
|
||||
}
|
||||
#endif
|
||||
}
|
||||
OutputDebug(L"LIVEDRAW SMOOTHING: %d\n", g_SmoothImage);
|
||||
if (!pMagSetLensUseBitmapSmoothing(g_hWndLiveZoomMag, g_SmoothImage))
|
||||
{
|
||||
OutputDebug(L"MagSetLensUseBitmapSmoothing failed: %d\n", GetLastError());
|
||||
}
|
||||
|
||||
if ( g_RecordToggle )
|
||||
{
|
||||
@@ -5313,8 +5296,6 @@ LRESULT APIENTRY MainWndProc(
|
||||
|
||||
if( g_Drawing ) {
|
||||
|
||||
OutputDebug(L"Mousemove: Drawing\n");
|
||||
|
||||
POINT currentPt;
|
||||
|
||||
// Are we in pen mode on a tablet?
|
||||
@@ -5353,15 +5334,7 @@ LRESULT APIENTRY MainWndProc(
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PEN_COLOR_HIGHLIGHT(g_PenColor))
|
||||
{
|
||||
// copy original bitmap to screen bitmap to erase previous highlight
|
||||
BitBlt(hdcScreenCompat, 0, 0, bmp.bmWidth, bmp.bmHeight, drawUndoList->hDc, 0, 0, SRCCOPY | CAPTUREBLT);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawShape(g_DrawingShape, hdcScreenCompat, &g_rcRectangle, PEN_COLOR_HIGHLIGHT(g_PenColor));
|
||||
}
|
||||
DrawShape( g_DrawingShape, hdcScreenCompat, &g_rcRectangle );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5407,7 +5380,7 @@ LRESULT APIENTRY MainWndProc(
|
||||
g_rcRectangle.top != g_rcRectangle.bottom) {
|
||||
|
||||
// Draw the new target rectangle.
|
||||
DrawShape(g_DrawingShape, hdcScreenCompat, &g_rcRectangle, PEN_COLOR_HIGHLIGHT(g_PenColor));
|
||||
DrawShape(g_DrawingShape, hdcScreenCompat, &g_rcRectangle);
|
||||
OutputDebug(L"SHAPE: (%d, %d) - (%d, %d)\n", g_rcRectangle.left, g_rcRectangle.top,
|
||||
g_rcRectangle.right, g_rcRectangle.bottom);
|
||||
}
|
||||
@@ -5445,6 +5418,9 @@ LRESULT APIENTRY MainWndProc(
|
||||
Gdiplus::BitmapData* lineData = LockGdiPlusBitmap(lineBitmap);
|
||||
BYTE* pPixels = static_cast<BYTE*>(lineData->Scan0);
|
||||
|
||||
// Copy the contents of the screen bitmap to the temporary bitmap
|
||||
GetOldestUndo(drawUndoList);
|
||||
|
||||
// Create a GDI bitmap that's the size of the lineBounds rectangle
|
||||
Gdiplus::Bitmap *blurBitmap = CreateGdiplusBitmap( hdcScreenCompat, // oldestUndo->hDc,
|
||||
lineBounds.X, lineBounds.Y, lineBounds.Width, lineBounds.Height);
|
||||
@@ -5469,8 +5445,6 @@ LRESULT APIENTRY MainWndProc(
|
||||
}
|
||||
else if(PEN_COLOR_HIGHLIGHT(g_PenColor)) {
|
||||
|
||||
OutputDebug(L"HIGHLIGHT\n");
|
||||
|
||||
// This is a highlighting pen color
|
||||
Gdiplus::Rect lineBounds = GetLineBounds(prevPt, currentPt, g_PenWidth);
|
||||
Gdiplus::Bitmap* lineBitmap = DrawBitmapLine(lineBounds, prevPt, currentPt, &pen);
|
||||
@@ -5810,30 +5784,26 @@ LRESULT APIENTRY MainWndProc(
|
||||
if( !g_DrawingShape ) {
|
||||
|
||||
// If the point has changed, draw a line to it
|
||||
if (!PEN_COLOR_HIGHLIGHT(g_PenColor))
|
||||
{
|
||||
if (prevPt.x != LOWORD(lParam) || prevPt.y != HIWORD(lParam))
|
||||
if (prevPt.x != LOWORD(lParam) || prevPt.y != HIWORD(lParam)) {
|
||||
Gdiplus::Graphics dstGraphics(hdcScreenCompat);
|
||||
if ((GetWindowLong(g_hWndMain, GWL_EXSTYLE) & WS_EX_LAYERED) == 0)
|
||||
{
|
||||
Gdiplus::Graphics dstGraphics(hdcScreenCompat);
|
||||
if ((GetWindowLong(g_hWndMain, GWL_EXSTYLE) & WS_EX_LAYERED) == 0)
|
||||
{
|
||||
dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
|
||||
}
|
||||
Gdiplus::Color color = ColorFromColorRef(g_PenColor);
|
||||
Gdiplus::Pen pen(color, static_cast<Gdiplus::REAL>(g_PenWidth));
|
||||
Gdiplus::GraphicsPath path;
|
||||
pen.SetLineJoin(Gdiplus::LineJoinRound);
|
||||
path.AddLine(prevPt.x, prevPt.y, LOWORD(lParam), HIWORD(lParam));
|
||||
dstGraphics.DrawPath(&pen, &path);
|
||||
}
|
||||
// Draw a dot at the current point, if the point hasn't changed
|
||||
else
|
||||
{
|
||||
MoveToEx(hdcScreenCompat, prevPt.x, prevPt.y, NULL);
|
||||
LineTo(hdcScreenCompat, LOWORD(lParam), HIWORD(lParam));
|
||||
InvalidateRect(hWnd, NULL, FALSE);
|
||||
dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
|
||||
}
|
||||
Gdiplus::Color color = ColorFromColorRef(g_PenColor);
|
||||
Gdiplus::Pen pen(color, static_cast<Gdiplus::REAL>(g_PenWidth));
|
||||
Gdiplus::GraphicsPath path;
|
||||
pen.SetLineJoin(Gdiplus::LineJoinRound);
|
||||
path.AddLine(prevPt.x, prevPt.y, LOWORD(lParam), HIWORD(lParam));
|
||||
dstGraphics.DrawPath(&pen, &path);
|
||||
}
|
||||
// Draw a dot at the current point, if the point hasn't changed
|
||||
else {
|
||||
MoveToEx(hdcScreenCompat, prevPt.x, prevPt.y, NULL);
|
||||
LineTo(hdcScreenCompat, LOWORD(lParam), HIWORD(lParam));
|
||||
InvalidateRect(hWnd, NULL, FALSE);
|
||||
}
|
||||
|
||||
prevPt.x = LOWORD( lParam );
|
||||
prevPt.y = HIWORD( lParam );
|
||||
|
||||
@@ -5848,11 +5818,8 @@ LRESULT APIENTRY MainWndProc(
|
||||
g_rcRectangle.left != g_rcRectangle.right ) {
|
||||
|
||||
// erase previous
|
||||
if (!PEN_COLOR_HIGHLIGHT(g_PenColor))
|
||||
{
|
||||
SetROP2(hdcScreenCompat, R2_NOTXORPEN);
|
||||
DrawShape(g_DrawingShape, hdcScreenCompat, &g_rcRectangle);
|
||||
}
|
||||
SetROP2(hdcScreenCompat, R2_NOTXORPEN);
|
||||
DrawShape( g_DrawingShape, hdcScreenCompat, &g_rcRectangle );
|
||||
|
||||
// Draw the final shape
|
||||
HBRUSH hBrush = static_cast<HBRUSH>(GetStockObject( NULL_BRUSH ));
|
||||
@@ -6218,14 +6185,8 @@ LRESULT APIENTRY MainWndProc(
|
||||
SetStretchBltMode( hInterimSaveDc, HALFTONE );
|
||||
SetStretchBltMode( hSaveDc, HALFTONE );
|
||||
#else
|
||||
// Use HALFTONE for better quality when smooth image is enabled
|
||||
if (g_SmoothImage) {
|
||||
SetStretchBltMode( hInterimSaveDc, HALFTONE );
|
||||
SetStretchBltMode( hSaveDc, HALFTONE );
|
||||
} else {
|
||||
SetStretchBltMode( hInterimSaveDc, COLORONCOLOR );
|
||||
SetStretchBltMode( hSaveDc, COLORONCOLOR );
|
||||
}
|
||||
SetStretchBltMode( hInterimSaveDc, COLORONCOLOR );
|
||||
SetStretchBltMode( hSaveDc, COLORONCOLOR );
|
||||
#endif
|
||||
StretchBlt( hInterimSaveDc,
|
||||
0, 0,
|
||||
@@ -6348,12 +6309,7 @@ LRESULT APIENTRY MainWndProc(
|
||||
#if SCALE_HALFTONE
|
||||
SetStretchBltMode( hSaveDc, HALFTONE );
|
||||
#else
|
||||
// Use HALFTONE for better quality when smooth image is enabled
|
||||
if (g_SmoothImage) {
|
||||
SetStretchBltMode( hSaveDc, HALFTONE );
|
||||
} else {
|
||||
SetStretchBltMode( hSaveDc, COLORONCOLOR );
|
||||
}
|
||||
SetStretchBltMode( hSaveDc, COLORONCOLOR );
|
||||
#endif
|
||||
StretchBlt( hSaveDc,
|
||||
0, 0,
|
||||
@@ -6690,8 +6646,8 @@ LRESULT APIENTRY MainWndProc(
|
||||
(float)x, (float)y,
|
||||
width/zoomLevel, height/zoomLevel );
|
||||
} else {
|
||||
// do a fast, less accurate render (but use smooth if enabled)
|
||||
SetStretchBltMode( hDc, g_SmoothImage ? HALFTONE : COLORONCOLOR );
|
||||
// do a fast, less accurate render
|
||||
SetStretchBltMode( hDc, HALFTONE );
|
||||
StretchBlt( ps.hdc,
|
||||
0, 0,
|
||||
bmp.bmWidth, bmp.bmHeight,
|
||||
@@ -6704,12 +6660,7 @@ LRESULT APIENTRY MainWndProc(
|
||||
#if SCALE_HALFTONE
|
||||
SetStretchBltMode( hDc, zoomLevel == zoomTelescopeTarget ? HALFTONE : COLORONCOLOR );
|
||||
#else
|
||||
// Use HALFTONE for better quality when smooth image is enabled
|
||||
if (g_SmoothImage) {
|
||||
SetStretchBltMode( hDc, HALFTONE );
|
||||
} else {
|
||||
SetStretchBltMode( hDc, COLORONCOLOR );
|
||||
}
|
||||
SetStretchBltMode( hDc, COLORONCOLOR );
|
||||
#endif
|
||||
StretchBlt( ps.hdc,
|
||||
0, 0,
|
||||
@@ -6732,7 +6683,7 @@ LRESULT APIENTRY MainWndProc(
|
||||
|
||||
BITMAP local_bmp;
|
||||
GetObject(g_hBackgroundBmp, sizeof(local_bmp), &local_bmp);
|
||||
SetStretchBltMode( hdcScreenCompat, g_SmoothImage ? HALFTONE : COLORONCOLOR );
|
||||
SetStretchBltMode( hdcScreenCompat, HALFTONE );
|
||||
if( g_BreakBackgroundStretch ) {
|
||||
StretchBlt( hdcScreenCompat, 0, 0, width, height,
|
||||
g_hDcBackgroundFile, 0, 0, local_bmp.bmWidth, local_bmp.bmHeight, SRCCOPY|CAPTUREBLT );
|
||||
@@ -6891,6 +6842,7 @@ LRESULT CALLBACK LiveZoomWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
|
||||
WS_CHILD | MS_SHOWMAGNIFIEDCURSOR | WS_VISIBLE,
|
||||
0, 0, 0, 0, hWnd, NULL, g_hInstance, NULL );
|
||||
}
|
||||
|
||||
ShowWindow( hWnd, SW_SHOW );
|
||||
InvalidateRect( g_hWndLiveZoomMag, NULL, TRUE );
|
||||
|
||||
@@ -7603,10 +7555,6 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
||||
"MagSetWindowTransform" );
|
||||
pMagSetFullscreenTransform = (type_pMagSetFullscreenTransform)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM),
|
||||
"MagSetFullscreenTransform");
|
||||
pMagSetFullscreenUseBitmapSmoothing = (type_MagSetFullscreenUseBitmapSmoothing)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM),
|
||||
"MagSetFullscreenUseBitmapSmoothing");
|
||||
pMagSetLensUseBitmapSmoothing = (type_pMagSetLensUseBitmapSmoothing)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM),
|
||||
"MagSetLensUseBitmapSmoothing");
|
||||
pMagSetInputTransform = (type_pMagSetInputTransform)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM),
|
||||
"MagSetInputTransform");
|
||||
pMagShowSystemCursor = (type_pMagShowSystemCursor)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM),
|
||||
|
||||
@@ -95,7 +95,6 @@
|
||||
#define IDC_COPYRIGHT 1075
|
||||
#define IDC_PEN_WIDTH 1105
|
||||
#define IDC_TIMER 1106
|
||||
#define IDC_SMOOTH_IMAGE 1107
|
||||
#define IDC_SAVE 40002
|
||||
#define IDC_COPY 40004
|
||||
#define IDC_RECORD 40006
|
||||
@@ -110,7 +109,7 @@
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 118
|
||||
#define _APS_NEXT_COMMAND_VALUE 40013
|
||||
#define _APS_NEXT_CONTROL_VALUE 1078
|
||||
#define _APS_NEXT_CONTROL_VALUE 1076
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Microsoft.CmdPal.Core.Common.Helpers;
|
||||
/// If ExecuteAsync is called while already executing, it cancels the current execution
|
||||
/// and starts the operation again (superseding behavior).
|
||||
/// </summary>
|
||||
public sealed partial class SupersedingAsyncGate : IDisposable
|
||||
public partial class SupersedingAsyncGate : IDisposable
|
||||
{
|
||||
private readonly Func<CancellationToken, Task> _action;
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// An async gate that ensures only one value computation runs at a time.
|
||||
/// If ExecuteAsync is called while already executing, it cancels the current computation
|
||||
/// and starts the operation again (superseding behavior).
|
||||
/// Once a value is successfully computed, it is applied (via the provided <see cref="Action{T}"/>).
|
||||
/// The apply step uses its own lock so that long-running apply logic does not block the
|
||||
/// computation / superseding pipeline, while still remaining serialized with respect to
|
||||
/// other apply calls.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the computed value.</typeparam>
|
||||
public sealed partial class SupersedingAsyncValueGate<T> : IDisposable
|
||||
{
|
||||
private readonly Func<CancellationToken, Task<T>> _valueFactory;
|
||||
private readonly Action<T> _apply;
|
||||
private readonly Lock _lock = new(); // Controls scheduling / superseding
|
||||
private readonly Lock _applyLock = new(); // Serializes application of results
|
||||
private int _callId;
|
||||
private TaskCompletionSource<T>? _currentTcs;
|
||||
private CancellationTokenSource? _currentCancellationSource;
|
||||
private Task? _executingTask;
|
||||
|
||||
public SupersedingAsyncValueGate(
|
||||
Func<CancellationToken, Task<T>> valueFactory,
|
||||
Action<T> apply)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(valueFactory);
|
||||
ArgumentNullException.ThrowIfNull(apply);
|
||||
_valueFactory = valueFactory;
|
||||
_apply = apply;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the configured value computation. If another execution is running, this call will
|
||||
/// cancel the current execution and restart the computation. The returned task completes when
|
||||
/// (and only if) the computation associated with this invocation completes (or is canceled / superseded).
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Optional external cancellation token.</param>
|
||||
/// <returns>The computed value for this invocation.</returns>
|
||||
public async Task<T> ExecuteAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
TaskCompletionSource<T> tcs;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
// Supersede any in-flight computation.
|
||||
_currentCancellationSource?.Cancel();
|
||||
_currentTcs?.TrySetException(new OperationCanceledException("Superseded by newer call"));
|
||||
|
||||
tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
_currentTcs = tcs;
|
||||
_callId++;
|
||||
|
||||
if (_executingTask is null)
|
||||
{
|
||||
_executingTask = Task.Run(ExecuteLoop, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
using var ctr = cancellationToken.Register(state => ((TaskCompletionSource<T>)state!).TrySetCanceled(cancellationToken), tcs);
|
||||
return await tcs.Task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task ExecuteLoop()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
TaskCompletionSource<T>? currentTcs;
|
||||
CancellationTokenSource? currentCts;
|
||||
int currentCallId;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
currentTcs = _currentTcs;
|
||||
currentCallId = _callId;
|
||||
|
||||
if (currentTcs is null)
|
||||
{
|
||||
break; // Nothing pending.
|
||||
}
|
||||
|
||||
_currentCancellationSource?.Dispose();
|
||||
_currentCancellationSource = new();
|
||||
currentCts = _currentCancellationSource;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var value = await _valueFactory(currentCts.Token).ConfigureAwait(false);
|
||||
CompleteSuccessIfCurrent(currentTcs, currentCallId, value);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
CompleteIfCurrent(currentTcs, currentCallId, t => t.TrySetCanceled(currentCts.Token));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CompleteIfCurrent(currentTcs, currentCallId, t => t.TrySetException(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_currentTcs = null;
|
||||
_currentCancellationSource?.Dispose();
|
||||
_currentCancellationSource = null;
|
||||
_executingTask = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CompleteSuccessIfCurrent(TaskCompletionSource<T> candidate, int id, T value)
|
||||
{
|
||||
var shouldApply = false;
|
||||
lock (_lock)
|
||||
{
|
||||
if (_currentTcs == candidate && _callId == id)
|
||||
{
|
||||
// Mark as consumed so a new computation can start immediately.
|
||||
_currentTcs = null;
|
||||
shouldApply = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldApply)
|
||||
{
|
||||
return; // Superseded meanwhile.
|
||||
}
|
||||
|
||||
Exception? applyException = null;
|
||||
try
|
||||
{
|
||||
lock (_applyLock)
|
||||
{
|
||||
_apply(value);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
applyException = ex;
|
||||
}
|
||||
|
||||
if (applyException is null)
|
||||
{
|
||||
candidate.TrySetResult(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
candidate.TrySetException(applyException);
|
||||
}
|
||||
}
|
||||
|
||||
private void CompleteIfCurrent(
|
||||
TaskCompletionSource<T> candidate,
|
||||
int id,
|
||||
Action<TaskCompletionSource<T>> complete)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_currentTcs == candidate && _callId == id)
|
||||
{
|
||||
complete(candidate);
|
||||
_currentTcs = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_currentCancellationSource?.Cancel();
|
||||
_currentCancellationSource?.Dispose();
|
||||
_currentTcs?.TrySetException(new ObjectDisposedException(nameof(SupersedingAsyncValueGate<T>)));
|
||||
_currentTcs = null;
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates a navigation request within Command Palette view models.
|
||||
/// </summary>
|
||||
/// <param name="TargetViewModel">A view model that should be navigated to.</param>
|
||||
/// <param name="NavigationToken"> A <see cref="CancellationToken"/> that can be used to cancel the pending navigation.</param>
|
||||
public record AsyncNavigationRequest(object? TargetViewModel, CancellationToken NavigationToken);
|
||||
@@ -17,7 +17,7 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
public ExtensionObject<ICommandItem> Model => _commandItemModel;
|
||||
|
||||
private readonly ExtensionObject<ICommandItem> _commandItemModel = new(null);
|
||||
private CommandContextItemViewModel? _defaultCommandContextItemViewModel;
|
||||
private CommandContextItemViewModel? _defaultCommandContextItem;
|
||||
|
||||
internal InitializedState Initialized { get; private set; } = InitializedState.Uninitialized;
|
||||
|
||||
@@ -43,9 +43,9 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
|
||||
public string Subtitle { get; private set; } = string.Empty;
|
||||
|
||||
private IconInfoViewModel _icon = new(null);
|
||||
private IconInfoViewModel _listItemIcon = new(null);
|
||||
|
||||
public IconInfoViewModel Icon => _icon.IsSet ? _icon : Command.Icon;
|
||||
public IconInfoViewModel Icon => _listItemIcon.IsSet ? _listItemIcon : Command.Icon;
|
||||
|
||||
public CommandViewModel Command { get; private set; }
|
||||
|
||||
@@ -69,9 +69,9 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
{
|
||||
get
|
||||
{
|
||||
List<IContextItemViewModel> l = _defaultCommandContextItemViewModel is null ?
|
||||
List<IContextItemViewModel> l = _defaultCommandContextItem is null ?
|
||||
new() :
|
||||
[_defaultCommandContextItemViewModel];
|
||||
[_defaultCommandContextItem];
|
||||
|
||||
l.AddRange(MoreCommands);
|
||||
return l;
|
||||
@@ -136,11 +136,11 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
|
||||
Command.InitializeProperties();
|
||||
|
||||
var icon = model.Icon;
|
||||
if (icon is not null)
|
||||
var listIcon = model.Icon;
|
||||
if (listIcon is not null)
|
||||
{
|
||||
_icon = new(icon);
|
||||
_icon.InitializeProperties();
|
||||
_listItemIcon = new(listIcon);
|
||||
_listItemIcon.InitializeProperties();
|
||||
}
|
||||
|
||||
// TODO: Do these need to go into FastInit?
|
||||
@@ -201,19 +201,21 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
|
||||
if (!string.IsNullOrEmpty(model.Command?.Name))
|
||||
{
|
||||
_defaultCommandContextItemViewModel = new CommandContextItemViewModel(new CommandContextItem(model.Command!), PageContext)
|
||||
_defaultCommandContextItem = new(new CommandContextItem(model.Command!), PageContext)
|
||||
{
|
||||
_itemTitle = Name,
|
||||
Subtitle = Subtitle,
|
||||
Command = Command,
|
||||
|
||||
// TODO this probably should just be a CommandContextItemViewModel(CommandItemViewModel) ctor, or a copy ctor or whatever
|
||||
// Anything we set manually here must stay in sync with the corresponding properties on CommandItemViewModel.
|
||||
};
|
||||
|
||||
// Only set the icon on the context item for us if our command didn't
|
||||
// have its own icon
|
||||
UpdateDefaultContextItemIcon();
|
||||
if (!Command.HasIcon)
|
||||
{
|
||||
_defaultCommandContextItem._listItemIcon = _listItemIcon;
|
||||
}
|
||||
}
|
||||
|
||||
Initialized |= InitializedState.SelectionInitialized;
|
||||
@@ -236,7 +238,7 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
_itemTitle = "Error";
|
||||
Subtitle = "Item failed to load";
|
||||
MoreCommands = [];
|
||||
_icon = _errorIcon;
|
||||
_listItemIcon = _errorIcon;
|
||||
Initialized |= InitializedState.Error;
|
||||
}
|
||||
|
||||
@@ -273,7 +275,7 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
_itemTitle = "Error";
|
||||
Subtitle = "Item failed to load";
|
||||
MoreCommands = [];
|
||||
_icon = _errorIcon;
|
||||
_listItemIcon = _errorIcon;
|
||||
Initialized |= InitializedState.Error;
|
||||
}
|
||||
|
||||
@@ -303,18 +305,17 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
switch (propertyName)
|
||||
{
|
||||
case nameof(Command):
|
||||
Command.PropertyChanged -= Command_PropertyChanged;
|
||||
if (Command is not null)
|
||||
{
|
||||
Command.PropertyChanged -= Command_PropertyChanged;
|
||||
}
|
||||
|
||||
Command = new(model.Command, PageContext);
|
||||
Command.InitializeProperties();
|
||||
|
||||
// Extensions based on Command Palette SDK < 0.3 CommandItem class won't notify when Title changes because Command
|
||||
// or Command.Name change. This is a workaround to ensure that the Title is always up-to-date for extensions with old SDK.
|
||||
_itemTitle = model.Title;
|
||||
|
||||
_defaultCommandContextItemViewModel?.Command = Command;
|
||||
_defaultCommandContextItemViewModel?.UpdateTitle(_itemTitle);
|
||||
UpdateDefaultContextItemIcon();
|
||||
|
||||
UpdateProperty(nameof(Name));
|
||||
UpdateProperty(nameof(Title));
|
||||
UpdateProperty(nameof(Icon));
|
||||
@@ -325,22 +326,12 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
break;
|
||||
|
||||
case nameof(Subtitle):
|
||||
var modelSubtitle = model.Subtitle;
|
||||
this.Subtitle = modelSubtitle;
|
||||
_defaultCommandContextItemViewModel?.Subtitle = modelSubtitle;
|
||||
this.Subtitle = model.Subtitle;
|
||||
break;
|
||||
|
||||
case nameof(Icon):
|
||||
var oldIcon = _icon;
|
||||
_icon = new(model.Icon);
|
||||
_icon.InitializeProperties();
|
||||
if (oldIcon.IsSet || _icon.IsSet)
|
||||
{
|
||||
UpdateProperty(nameof(Icon));
|
||||
}
|
||||
|
||||
UpdateDefaultContextItemIcon();
|
||||
|
||||
_listItemIcon = new(model.Icon);
|
||||
_listItemIcon.InitializeProperties();
|
||||
break;
|
||||
|
||||
case nameof(model.MoreCommands):
|
||||
@@ -387,49 +378,26 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
private void Command_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
var propertyName = e.PropertyName;
|
||||
var model = _commandItemModel.Unsafe;
|
||||
if (model is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (propertyName)
|
||||
{
|
||||
case nameof(Command.Name):
|
||||
// Extensions based on Command Palette SDK < 0.3 CommandItem class won't notify when Title changes because Command
|
||||
// or Command.Name change. This is a workaround to ensure that the Title is always up-to-date for extensions with old SDK.
|
||||
_itemTitle = model.Title;
|
||||
UpdateProperty(nameof(Title), nameof(Name));
|
||||
var model = _commandItemModel.Unsafe;
|
||||
if (model is not null)
|
||||
{
|
||||
_itemTitle = model.Title;
|
||||
}
|
||||
|
||||
_defaultCommandContextItemViewModel?.UpdateTitle(model.Command.Name);
|
||||
UpdateProperty(nameof(Title));
|
||||
UpdateProperty(nameof(Name));
|
||||
break;
|
||||
|
||||
case nameof(Command.Icon):
|
||||
UpdateDefaultContextItemIcon();
|
||||
UpdateProperty(nameof(Icon));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateDefaultContextItemIcon()
|
||||
{
|
||||
// Command icon takes precedence over our icon on the primary command
|
||||
_defaultCommandContextItemViewModel?.UpdateIcon(Command.Icon.IsSet ? Command.Icon : _icon);
|
||||
}
|
||||
|
||||
private void UpdateTitle(string? title)
|
||||
{
|
||||
_itemTitle = title ?? string.Empty;
|
||||
UpdateProperty(nameof(Title));
|
||||
}
|
||||
|
||||
private void UpdateIcon(IIconInfo? iconInfo)
|
||||
{
|
||||
_icon = new(iconInfo);
|
||||
_icon.InitializeProperties();
|
||||
UpdateProperty(nameof(Icon));
|
||||
}
|
||||
|
||||
protected override void UnsafeCleanup()
|
||||
{
|
||||
base.UnsafeCleanup();
|
||||
@@ -443,10 +411,10 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
}
|
||||
|
||||
// _listItemIcon.SafeCleanup();
|
||||
_icon = new(null); // necessary?
|
||||
_listItemIcon = new(null); // necessary?
|
||||
|
||||
_defaultCommandContextItemViewModel?.SafeCleanup();
|
||||
_defaultCommandContextItemViewModel = null;
|
||||
_defaultCommandContextItem?.SafeCleanup();
|
||||
_defaultCommandContextItem = null;
|
||||
|
||||
Command.PropertyChanged -= Command_PropertyChanged;
|
||||
Command.SafeCleanup();
|
||||
|
||||
@@ -4,4 +4,6 @@
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
|
||||
public record NavigateToPageMessage(PageViewModel Page, bool WithAnimation, CancellationToken CancellationToken);
|
||||
public record NavigateToPageMessage(PageViewModel Page, bool WithAnimation)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels;
|
||||
|
||||
internal sealed partial class NullPageViewModel(TaskScheduler scheduler, AppExtensionHost extensionHost)
|
||||
: PageViewModel(null, scheduler, extensionHost);
|
||||
@@ -23,9 +23,6 @@ public partial class ShellViewModel : ObservableObject,
|
||||
private readonly Lock _invokeLock = new();
|
||||
private Task? _handleInvokeTask;
|
||||
|
||||
// Cancellation token source for page loading/navigation operations
|
||||
private CancellationTokenSource? _navigationCts;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsLoaded { get; set; } = false;
|
||||
|
||||
@@ -69,8 +66,6 @@ public partial class ShellViewModel : ObservableObject,
|
||||
|
||||
public bool IsNested => _isNested;
|
||||
|
||||
public PageViewModel NullPage { get; private set; }
|
||||
|
||||
public ShellViewModel(
|
||||
TaskScheduler scheduler,
|
||||
IRootPageService rootPageService,
|
||||
@@ -82,7 +77,6 @@ public partial class ShellViewModel : ObservableObject,
|
||||
_rootPageService = rootPageService;
|
||||
_appHostService = appHostService;
|
||||
|
||||
NullPage = new NullPageViewModel(_scheduler, appHostService.GetDefaultHost());
|
||||
_currentPage = new LoadingPageViewModel(null, _scheduler, appHostService.GetDefaultHost());
|
||||
|
||||
// Register to receive messages
|
||||
@@ -119,7 +113,7 @@ public partial class ShellViewModel : ObservableObject,
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task LoadPageViewModelAsync(PageViewModel viewModel, CancellationToken cancellationToken = default)
|
||||
public async Task LoadPageViewModelAsync(PageViewModel viewModel)
|
||||
{
|
||||
// Note: We removed the general loading state, extensions sometimes use their `IsLoading`, but it's inconsistently implemented it seems.
|
||||
// IsInitialized is our main indicator of the general overall state of loading props/items from a page we use for the progress bar
|
||||
@@ -131,80 +125,44 @@ public partial class ShellViewModel : ObservableObject,
|
||||
if (!viewModel.IsInitialized
|
||||
&& viewModel.InitializeCommand is not null)
|
||||
{
|
||||
var outer = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
// You know, this creates the situation where we wait for
|
||||
// both loading page properties, AND the items, before we
|
||||
// display anything.
|
||||
//
|
||||
// We almost need to do an async await on initialize, then
|
||||
// just a fire-and-forget on FetchItems.
|
||||
// RE: We do set the CurrentPage in ShellPage.xaml.cs as well, so, we kind of are doing two different things here.
|
||||
// Definitely some more clean-up to do, but at least its centralized to one spot now.
|
||||
viewModel.InitializeCommand.Execute(null);
|
||||
|
||||
await viewModel.InitializeCommand.ExecutionTask!;
|
||||
|
||||
if (viewModel.InitializeCommand.ExecutionTask.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
if (viewModel.InitializeCommand.ExecutionTask.Exception is AggregateException ex)
|
||||
{
|
||||
CoreLogger.LogError(ex.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var t = Task.Factory.StartNew(
|
||||
() =>
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
if (viewModel is IDisposable disposable)
|
||||
{
|
||||
try
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CoreLogger.LogError(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentPage = viewModel;
|
||||
},
|
||||
cancellationToken,
|
||||
TaskCreationOptions.None,
|
||||
_scheduler);
|
||||
await t;
|
||||
}
|
||||
},
|
||||
cancellationToken);
|
||||
await outer;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
var outer = Task.Run(async () =>
|
||||
{
|
||||
if (viewModel is IDisposable disposable)
|
||||
// You know, this creates the situation where we wait for
|
||||
// both loading page properties, AND the items, before we
|
||||
// display anything.
|
||||
//
|
||||
// We almost need to do an async await on initialize, then
|
||||
// just a fire-and-forget on FetchItems.
|
||||
// RE: We do set the CurrentPage in ShellPage.xaml.cs as well, so, we kind of are doing two different things here.
|
||||
// Definitely some more clean-up to do, but at least its centralized to one spot now.
|
||||
viewModel.InitializeCommand.Execute(null);
|
||||
|
||||
await viewModel.InitializeCommand.ExecutionTask!;
|
||||
|
||||
if (viewModel.InitializeCommand.ExecutionTask.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
try
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
if (viewModel.InitializeCommand.ExecutionTask.Exception is AggregateException ex)
|
||||
{
|
||||
CoreLogger.LogError(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var t = Task.Factory.StartNew(
|
||||
() =>
|
||||
{
|
||||
CurrentPage = viewModel;
|
||||
},
|
||||
CancellationToken.None,
|
||||
TaskCreationOptions.None,
|
||||
_scheduler);
|
||||
await t;
|
||||
}
|
||||
});
|
||||
await outer;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentPage = viewModel;
|
||||
}
|
||||
}
|
||||
@@ -216,28 +174,6 @@ public partial class ShellViewModel : ObservableObject,
|
||||
|
||||
private void PerformCommand(PerformCommandMessage message)
|
||||
{
|
||||
// Create/replace the navigation cancellation token.
|
||||
// If one already exists, cancel and dispose it first.
|
||||
var newCts = new CancellationTokenSource();
|
||||
var oldCts = Interlocked.Exchange(ref _navigationCts, newCts);
|
||||
if (oldCts is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
oldCts.Cancel();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CoreLogger.LogError(ex.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
oldCts.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
var navigationToken = newCts.Token;
|
||||
|
||||
var command = message.Command.Unsafe;
|
||||
if (command is null)
|
||||
{
|
||||
@@ -266,26 +202,15 @@ public partial class ShellViewModel : ObservableObject,
|
||||
}
|
||||
|
||||
// Kick off async loading of our ViewModel
|
||||
LoadPageViewModelAsync(pageViewModel, navigationToken)
|
||||
LoadPageViewModelAsync(pageViewModel)
|
||||
.ContinueWith(
|
||||
(Task t) =>
|
||||
{
|
||||
// clean up the navigation token if it's still ours
|
||||
if (Interlocked.CompareExchange(ref _navigationCts, null, newCts) == newCts)
|
||||
{
|
||||
newCts.Dispose();
|
||||
}
|
||||
|
||||
// When we're done loading the page, then update the command bar to match
|
||||
WeakReferenceMessenger.Default.Send<UpdateCommandBarMessage>(new(null));
|
||||
OnUIThread(() => { WeakReferenceMessenger.Default.Send<UpdateCommandBarMessage>(new(null)); });
|
||||
WeakReferenceMessenger.Default.Send<NavigateToPageMessage>(new(pageViewModel, message.WithAnimation));
|
||||
},
|
||||
navigationToken,
|
||||
TaskContinuationOptions.None,
|
||||
_scheduler);
|
||||
|
||||
// While we're loading in the background, immediately move to the next page.
|
||||
WeakReferenceMessenger.Default.Send<NavigateToPageMessage>(new(pageViewModel, message.WithAnimation, navigationToken));
|
||||
|
||||
// Note: Originally we set our page back in the ViewModel here, but that now happens in response to the Frame navigating triggered from the above
|
||||
// See RootFrame_Navigated event handler.
|
||||
}
|
||||
@@ -443,9 +368,4 @@ public partial class ShellViewModel : ObservableObject,
|
||||
TaskCreationOptions.None,
|
||||
_scheduler);
|
||||
}
|
||||
|
||||
public void CancelNavigation()
|
||||
{
|
||||
_navigationCts?.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ using ManagedCommon;
|
||||
using Microsoft.CmdPal.Core.Common.Helpers;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.Ext.Apps;
|
||||
using Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
using Microsoft.CmdPal.Ext.Apps.State;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Properties;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
@@ -38,7 +36,6 @@ public partial class MainListPage : DynamicListPage,
|
||||
private List<Scored<IListItem>>? _filteredItems;
|
||||
private List<Scored<IListItem>>? _filteredApps;
|
||||
private List<Scored<IListItem>>? _fallbackItems;
|
||||
private IEnumerable<Scored<IListItem>>? _scoredFallbackItems;
|
||||
private bool _includeApps;
|
||||
private bool _filteredItemsIncludesApps;
|
||||
private int _appResultLimit = 10;
|
||||
@@ -163,7 +160,7 @@ public partial class MainListPage : DynamicListPage,
|
||||
{
|
||||
lock (_tlcManager.TopLevelCommands)
|
||||
{
|
||||
var limitedApps = new List<Scored<IListItem>>();
|
||||
List<Scored<IListItem>> limitedApps = new List<Scored<IListItem>>();
|
||||
|
||||
// Fuzzy matching can produce a lot of results, so we want to limit the
|
||||
// number of apps we show at once if it's a large set.
|
||||
@@ -174,7 +171,6 @@ public partial class MainListPage : DynamicListPage,
|
||||
|
||||
var items = Enumerable.Empty<Scored<IListItem>>()
|
||||
.Concat(_filteredItems is not null ? _filteredItems : [])
|
||||
.Concat(_scoredFallbackItems is not null ? _scoredFallbackItems : [])
|
||||
.Concat(limitedApps)
|
||||
.OrderByDescending(o => o.Score)
|
||||
|
||||
@@ -188,14 +184,6 @@ public partial class MainListPage : DynamicListPage,
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearResults()
|
||||
{
|
||||
_filteredItems = null;
|
||||
_filteredApps = null;
|
||||
_fallbackItems = null;
|
||||
_scoredFallbackItems = null;
|
||||
}
|
||||
|
||||
public override void UpdateSearchText(string oldSearch, string newSearch)
|
||||
{
|
||||
var timer = new Stopwatch();
|
||||
@@ -228,7 +216,8 @@ public partial class MainListPage : DynamicListPage,
|
||||
lock (_tlcManager.TopLevelCommands)
|
||||
{
|
||||
_filteredItemsIncludesApps = _includeApps;
|
||||
ClearResults();
|
||||
_filteredItems = null;
|
||||
_filteredApps = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,36 +233,7 @@ public partial class MainListPage : DynamicListPage,
|
||||
var commands = _tlcManager.TopLevelCommands;
|
||||
lock (commands)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// prefilter fallbacks
|
||||
var specialFallbacks = new List<TopLevelViewModel>(_specialFallbacks.Length);
|
||||
var commonFallbacks = new List<TopLevelViewModel>();
|
||||
|
||||
foreach (var s in commands)
|
||||
{
|
||||
if (!s.IsFallback)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_specialFallbacks.Contains(s.CommandProviderId))
|
||||
{
|
||||
specialFallbacks.Add(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
commonFallbacks.Add(s);
|
||||
}
|
||||
}
|
||||
|
||||
// start update of fallbacks; update special fallbacks separately,
|
||||
// so they can finish faster
|
||||
UpdateFallbacks(SearchText, specialFallbacks, token);
|
||||
UpdateFallbacks(SearchText, commonFallbacks, token);
|
||||
UpdateFallbacks(SearchText, commands.ToImmutableArray(), token);
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
@@ -284,7 +244,9 @@ public partial class MainListPage : DynamicListPage,
|
||||
if (string.IsNullOrEmpty(newSearch))
|
||||
{
|
||||
_filteredItemsIncludesApps = _includeApps;
|
||||
ClearResults();
|
||||
_filteredItems = null;
|
||||
_filteredApps = null;
|
||||
_fallbackItems = null;
|
||||
RaiseItemsChanged(commands.Count);
|
||||
return;
|
||||
}
|
||||
@@ -293,13 +255,17 @@ public partial class MainListPage : DynamicListPage,
|
||||
// re-use previous results. Reset _filteredItems, and keep er moving.
|
||||
if (!newSearch.StartsWith(oldSearch, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
ClearResults();
|
||||
_filteredItems = null;
|
||||
_filteredApps = null;
|
||||
_fallbackItems = null;
|
||||
}
|
||||
|
||||
// If the internal state has changed, reset _filteredItems to reset the list.
|
||||
if (_filteredItemsIncludesApps != _includeApps)
|
||||
{
|
||||
ClearResults();
|
||||
_filteredItems = null;
|
||||
_filteredApps = null;
|
||||
_fallbackItems = null;
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
@@ -307,9 +273,9 @@ public partial class MainListPage : DynamicListPage,
|
||||
return;
|
||||
}
|
||||
|
||||
var newFilteredItems = Enumerable.Empty<IListItem>();
|
||||
var newFallbacks = Enumerable.Empty<IListItem>();
|
||||
var newApps = Enumerable.Empty<IListItem>();
|
||||
IEnumerable<IListItem> newFilteredItems = Enumerable.Empty<IListItem>();
|
||||
IEnumerable<IListItem> newFallbacks = Enumerable.Empty<IListItem>();
|
||||
IEnumerable<IListItem> newApps = Enumerable.Empty<IListItem>();
|
||||
|
||||
if (_filteredItems is not null)
|
||||
{
|
||||
@@ -345,12 +311,15 @@ public partial class MainListPage : DynamicListPage,
|
||||
// with a list of all our commands & apps.
|
||||
if (!newFilteredItems.Any() && !newApps.Any())
|
||||
{
|
||||
newFilteredItems = commands.Where(s => !s.IsFallback);
|
||||
// We're going to start over with our fallbacks
|
||||
newFallbacks = Enumerable.Empty<IListItem>();
|
||||
|
||||
newFilteredItems = commands.Where(s => !s.IsFallback || _specialFallbacks.Contains(s.CommandProviderId));
|
||||
|
||||
// Fallbacks are always included in the list, even if they
|
||||
// don't match the search text. But we don't want to
|
||||
// consider them when filtering the list.
|
||||
newFallbacks = commonFallbacks;
|
||||
newFallbacks = commands.Where(s => s.IsFallback && !_specialFallbacks.Contains(s.CommandProviderId));
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
@@ -361,20 +330,7 @@ public partial class MainListPage : DynamicListPage,
|
||||
|
||||
if (_includeApps)
|
||||
{
|
||||
var allNewApps = AllAppsCommandProvider.Page.GetItems().ToList();
|
||||
|
||||
// We need to remove pinned apps from allNewApps so they don't show twice.
|
||||
var pinnedApps = PinnedAppsManager.Instance.GetPinnedAppIdentifiers();
|
||||
|
||||
if (pinnedApps.Length > 0)
|
||||
{
|
||||
newApps = allNewApps.Where(w =>
|
||||
pinnedApps.IndexOf(((AppListItem)w).AppIdentifier) < 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
newApps = allNewApps;
|
||||
}
|
||||
newApps = AllAppsCommandProvider.Page.GetItems().ToList();
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
@@ -383,25 +339,8 @@ public partial class MainListPage : DynamicListPage,
|
||||
}
|
||||
}
|
||||
|
||||
var history = _serviceProvider.GetService<AppStateModel>()!.RecentCommands!;
|
||||
Func<string, IListItem, int> scoreItem = (a, b) => { return ScoreTopLevelItem(a, b, history); };
|
||||
|
||||
// Produce a list of everything that matches the current filter.
|
||||
_filteredItems = [.. ListHelpers.FilterListWithScores<IListItem>(newFilteredItems ?? [], SearchText, scoreItem)];
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumerable<IListItem> newFallbacksForScoring = commands.Where(s => s.IsFallback && _specialFallbacks.Contains(s.CommandProviderId));
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_scoredFallbackItems = ListHelpers.FilterListWithScores<IListItem>(newFallbacksForScoring ?? [], SearchText, scoreItem);
|
||||
_filteredItems = [.. ListHelpers.FilterListWithScores<IListItem>(newFilteredItems ?? [], SearchText, ScoreTopLevelItem)];
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
@@ -419,7 +358,7 @@ public partial class MainListPage : DynamicListPage,
|
||||
// Produce a list of filtered apps with the appropriate limit
|
||||
if (newApps.Any())
|
||||
{
|
||||
var scoredApps = ListHelpers.FilterListWithScores<IListItem>(newApps, SearchText, scoreItem);
|
||||
var scoredApps = ListHelpers.FilterListWithScores<IListItem>(newApps, SearchText, ScoreTopLevelItem);
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
@@ -486,7 +425,7 @@ public partial class MainListPage : DynamicListPage,
|
||||
// Almost verbatim ListHelpers.ScoreListItem, but also accounting for the
|
||||
// fact that we want fallback handlers down-weighted, so that they don't
|
||||
// _always_ show up first.
|
||||
internal static int ScoreTopLevelItem(string query, IListItem topLevelOrAppItem, IRecentCommandsManager history)
|
||||
private int ScoreTopLevelItem(string query, IListItem topLevelOrAppItem)
|
||||
{
|
||||
var title = topLevelOrAppItem.Title;
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
@@ -562,9 +501,10 @@ public partial class MainListPage : DynamicListPage,
|
||||
// here we add the recent command weight boost
|
||||
//
|
||||
// Otherwise something like `x` will still match everything you've run before
|
||||
var finalScore = matchSomething * 10;
|
||||
var finalScore = matchSomething;
|
||||
if (matchSomething > 0)
|
||||
{
|
||||
var history = _serviceProvider.GetService<AppStateModel>()!.RecentCommands;
|
||||
var recentWeightBoost = history.GetCommandHistoryWeight(id);
|
||||
finalScore += recentWeightBoost;
|
||||
}
|
||||
@@ -581,7 +521,7 @@ public partial class MainListPage : DynamicListPage,
|
||||
AppStateModel.SaveState(state);
|
||||
}
|
||||
|
||||
private static string IdForTopLevelOrAppItem(IListItem topLevelOrAppItem)
|
||||
private string IdForTopLevelOrAppItem(IListItem topLevelOrAppItem)
|
||||
{
|
||||
if (topLevelOrAppItem is TopLevelViewModel topLevel)
|
||||
{
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.CmdPal.UI.ViewModels.UnitTests")]
|
||||
@@ -7,7 +7,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class RecentCommandsManager : ObservableObject, IRecentCommandsManager
|
||||
public partial class RecentCommandsManager : ObservableObject
|
||||
{
|
||||
[JsonInclude]
|
||||
internal List<HistoryItem> History { get; set; } = [];
|
||||
@@ -80,10 +80,3 @@ public partial class RecentCommandsManager : ObservableObject, IRecentCommandsMa
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface IRecentCommandsManager
|
||||
{
|
||||
int GetCommandHistoryWeight(string commandId);
|
||||
|
||||
void AddHistoryItem(string commandId);
|
||||
}
|
||||
|
||||
@@ -52,8 +52,6 @@ public partial class SettingsModel : ObservableObject
|
||||
|
||||
public MonitorBehavior SummonOn { get; set; } = MonitorBehavior.ToMouse;
|
||||
|
||||
public bool DisableAnimations { get; set; } = true;
|
||||
|
||||
// END SETTINGS
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -128,16 +128,6 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
}
|
||||
}
|
||||
|
||||
public bool DisableAnimations
|
||||
{
|
||||
get => _settings.DisableAnimations;
|
||||
set
|
||||
{
|
||||
_settings.DisableAnimations = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<ProviderSettingsViewModel> CommandProviders { get; } = [];
|
||||
|
||||
public SettingsViewModel(SettingsModel settings, IServiceProvider serviceProvider, TaskScheduler scheduler)
|
||||
|
||||
@@ -114,7 +114,7 @@ public partial class App : Application
|
||||
services.AddSingleton<ICommandProvider, ShellCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, CalculatorCommandProvider>();
|
||||
services.AddSingleton<ICommandProvider>(files);
|
||||
services.AddSingleton<ICommandProvider, BookmarksCommandProvider>(_ => BookmarksCommandProvider.CreateWithDefaultStore());
|
||||
services.AddSingleton<ICommandProvider, BookmarksCommandProvider>();
|
||||
|
||||
services.AddSingleton<ICommandProvider, WindowWalkerCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, WebSearchCommandsProvider>();
|
||||
|
||||