Compare commits

..

5 Commits

Author SHA1 Message Date
Michael Jolley
713371b31e Doing that voodoo 2026-01-26 14:24:07 -06:00
Michael Jolley
21a62f0b45 Continuing reorg clean-up 2026-01-22 17:04:16 -06:00
Michael Jolley
64a2f4a037 Continuing reorg clean-up 2026-01-22 15:41:10 -06:00
Michael Jolley
18172a20ea Continuing reorg clean-up 2026-01-21 22:50:16 -06:00
Michael Jolley
713cfdb280 Beginning reorg 2026-01-21 22:35:07 -06:00
1686 changed files with 8335 additions and 17992 deletions

View File

@@ -47,7 +47,6 @@ resw
resx
srt
Stereolithography
taskmgr
terabyte
UYVY
xbf
@@ -321,10 +320,6 @@ MRUCMPPROC
MRUINFO
REGSTR
#Xaml
NVI
Storyboards
# Misc Win32 APIs and PInvokes
INVOKEIDLIST

View File

@@ -565,7 +565,7 @@ perl(?:\s+-[a-zA-Z]\w*)+
regexp?\.MustCompile\((?:`[^`]*`|".*"|'.*')\)
# regex choice
# \(\?:[^)]+\|[^)]+\)
\(\?:[^)]+\|[^)]+\)
# proto
^\s*(\w+)\s\g{-1} =

View File

@@ -104,12 +104,8 @@
^src/common/ManagedCommon/ColorFormatHelper\.cs$
^src/common/notifications/BackgroundActivatorDLL/cpp\.hint$
^src/common/sysinternals/Eula/
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherComparisonTests.cs$
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherDiacriticsTests.cs$
^src/modules/cmdpal/doc/initial-sdk-spec/list-elements-mock-002\.pdn$
^src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownImagesPage\.cs$
^src/modules/cmdpal/Microsoft\.CmdPal\.UI/Settings/InternalPage\.SampleData\.cs$
^src/modules/cmdpal/Tests/Microsoft\.CmdPal\.Core\.Common\.UnitTests/.*\.TestData\.cs$
^src/modules/colorPicker/ColorPickerUI/Shaders/GridShader\.cso$
^src/modules/launcher/Plugins/Microsoft\.PowerToys\.Run\.Plugin\.TimeDate/Properties/
^src/modules/MouseUtils/MouseJumpUI/MainForm\.resx$

View File

@@ -22,6 +22,7 @@ ADate
ADDSTRING
ADDUNDORECORD
ADifferent
adjacents
ADMINS
adml
admx
@@ -102,6 +103,7 @@ ASYNCWINDOWPLACEMENT
ASYNCWINDOWPOS
atl
ATRIOX
ATX
aumid
authenticode
AUTOBUDDY
@@ -218,11 +220,10 @@ CIELCh
cim
CImage
cla
claude
CLASSDC
classguid
classmethod
CLASSNOTAVAILABLE
claude
CLEARTYPE
clickable
clickonce
@@ -261,6 +262,7 @@ colorhistory
colorhistorylimit
COLORKEY
colorref
Convs
comctl
comdlg
comexp
@@ -281,7 +283,6 @@ CONTEXTHELP
CONTEXTMENUHANDLER
contractversion
CONTROLPARENT
Convs
copiedcolorrepresentation
coppied
copyable
@@ -297,6 +298,7 @@ cpcontrols
cph
cplusplus
CPower
cppcoreguidelines
cpptools
cppvsdbg
cppwinrt
@@ -325,7 +327,7 @@ CURRENTDIR
CURSORINFO
cursorpos
CURSORSHOWING
cursorwrap
CURSORWRAP
customaction
CUSTOMACTIONTEST
CUSTOMFORMATPLACEHOLDER
@@ -348,14 +350,12 @@ datareader
datatracker
dataversion
Dayof
dbcc
DBID
DBLCLKS
DBLEPSILON
DBPROP
DBPROPIDSET
DBPROPSET
DBT
DCBA
DCOM
DComposition
@@ -373,7 +373,8 @@ DEFAULTICON
defaultlib
DEFAULTONLY
DEFAULTSIZE
defaulttonearest
DEFAULTTONEAREST
Defaulttonearest
DEFAULTTONULL
DEFAULTTOPRIMARY
DEFERERASE
@@ -395,19 +396,14 @@ DESKTOPVERTRES
devblogs
devdocs
devenv
DEVICEINTERFACE
devicetype
DEVINTERFACE
devmgmt
DEVMODE
DEVMODEW
DEVNODES
devpal
DEVTYP
dfx
DIALOGEX
diffs
digicert
diffs
DINORMAL
DISABLEASACTIONKEY
DISABLENOSCROLL
@@ -550,6 +546,7 @@ fdx
FErase
fesf
FFFF
FInc
Figma
FILEEXPLORER
fileexploreraddons
@@ -570,7 +567,6 @@ FILESYSPATH
Filetime
FILEVERSION
FILTERMODE
FInc
findfast
findmymouse
FIXEDFILEINFO
@@ -597,7 +593,6 @@ frm
FROMTOUCH
fsanitize
fsmgmt
ftps
fuzzingtesting
fxf
FZE
@@ -647,8 +642,6 @@ GSM
gtm
guiddata
GUITHREADINFO
Gotcha
Gotchas
GValue
gwl
GWLP
@@ -675,14 +668,13 @@ HCRYPTPROV
hcursor
hcwhite
hdc
HDEVNOTIFY
hdr
hdrop
hdwwiz
Helpline
helptext
hgdiobj
HGFE
hgdiobj
hglobal
hhk
HHmmssfff
@@ -758,9 +750,9 @@ HWNDPARENT
HWNDPREV
hyjiacan
IAI
icf
ICONERROR
ICONLOCATION
icf
IDCANCEL
IDD
idk
@@ -851,15 +843,13 @@ jeli
jfif
jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi
jjw
jobject
JOBOBJECT
jobject
jpe
jpnime
Jsons
jsonval
jxr
KBSC
kdc
keybd
KEYBDDATA
KEYBDINPUT
@@ -898,7 +888,6 @@ Ldr
LEFTALIGN
LEFTSCROLLBAR
LEFTTEXT
leftclick
LError
LEVELID
LExit
@@ -941,9 +930,9 @@ LOWORD
lparam
LPBITMAPINFOHEADER
LPCFHOOKPROC
lpch
LPCITEMIDLIST
LPCLSID
lpch
lpcmi
LPCMINVOKECOMMANDINFO
LPCREATESTRUCT
@@ -959,7 +948,6 @@ LPMONITORINFO
LPOSVERSIONINFOEXW
LPQUERY
lprc
LPrivate
LPSAFEARRAY
lpstr
lpsz
@@ -969,6 +957,7 @@ lptpm
LPTR
LPTSTR
lpv
LPrivate
LPW
lpwcx
lpwndpl
@@ -1012,13 +1001,13 @@ mber
MBM
MBR
Mbuttondown
mcp
MDICHILD
MDL
mdtext
mdtxt
mdwn
meme
mcp
memicmp
MENUITEMINFO
MENUITEMINFOW
@@ -1053,8 +1042,8 @@ mmi
mmsys
mobileredirect
mockapi
MODALFRAME
modelcontextprotocol
MODALFRAME
MODESPRUNED
MONITORENUMPROC
MONITORINFO
@@ -1098,9 +1087,9 @@ MSLLHOOKSTRUCT
Mso
msrc
msstore
mstsc
msvcp
MT
mstsc
MTND
MULTIPLEUSE
multizone
@@ -1110,11 +1099,11 @@ muxxc
muxxh
MVPs
mvvm
myorg
myrepo
MVVMTK
MWBEx
MYICON
myorg
myrepo
NAMECHANGE
namespaceanddescendants
nao
@@ -1255,8 +1244,10 @@ opencode
OPENFILENAME
openrdp
opensource
openurl
openxmlformats
ollama
onnx
openurl
OPTIMIZEFORINVOKE
ORPHANEDDIALOGTITLE
ORSCANS
@@ -1270,7 +1261,6 @@ OUTOFCONTEXT
Outptr
outputtype
outsettings
outsourced
OVERLAPPEDWINDOW
Oversampling
OVERWRITEPROMPT
@@ -1335,8 +1325,7 @@ phwnd
pici
pidl
PIDLIST
pii
pinboard
PII
pinfo
pinvoke
pipename
@@ -1417,7 +1406,6 @@ projectname
PROPERTYKEY
Propset
PROPVARIANT
Prt
PRTL
prvpane
psapi
@@ -1476,6 +1464,7 @@ rbhid
Rbuttondown
rclsid
RCZOOMIT
remotedesktop
rdp
RDW
READMODE
@@ -1504,7 +1493,6 @@ remappings
REMAPSUCCESSFUL
REMAPUNSUCCESSFUL
Remotable
remotedesktop
remoteip
Removelnk
renamable
@@ -1538,9 +1526,8 @@ RIGHTSCROLLBAR
riid
RKey
RNumber
rollups
ROOTOWNER
rop
rollups
ROUNDSMALL
ROWSETEXT
rpcrt
@@ -1724,7 +1711,6 @@ srw
srwlock
sse
ssf
Ssn
sszzz
STACKFRAME
stackoverflow
@@ -1774,13 +1760,15 @@ SUBMODULEUPDATE
subresource
Superbar
sut
swe
svchost
SVGIn
SVGIO
svgz
SVSI
SWFO
swp
SWP
Swp
SWPNOSIZE
SWPNOZORDER
SWRESTORE
@@ -1799,7 +1787,8 @@ SYSKEY
syskeydown
SYSKEYUP
SYSLIB
sysmenu
SYSMENU
Sysmenu
systemai
SYSTEMAPPS
SYSTEMMODAL
@@ -1834,7 +1823,6 @@ TEXTBOXNEWLINE
textextractor
TEXTINCLUDE
tfopen
tgamma
tgz
THEMECHANGED
themeresources
@@ -1904,9 +1892,9 @@ uitests
UITo
ULONGLONG
Ultrawide
ums
UMax
UMin
ums
uncompilable
UNCPRIORITY
UNDNAME
@@ -2093,7 +2081,6 @@ WNDCLASSW
WNDPROC
wnode
wom
workerw
WORKSPACESEDITOR
WORKSPACESLAUNCHER
WORKSPACESSNAPSHOTTOOL

View File

@@ -1,5 +1,6 @@
---
agent: 'agent'
model: 'GPT-5.1-Codex-Max'
description: 'Generate an 80-character git commit title for the local diff'
---

View File

@@ -1,5 +1,6 @@
---
agent: 'agent'
model: 'GPT-5.1-Codex-Max'
description: 'Generate a PowerToys-ready pull request description from the local diff'
---

View File

@@ -1,5 +1,6 @@
---
agent: 'agent'
model: 'GPT-5.1-Codex-Max'
description: 'Execute the fix for a GitHub issue using the previously generated implementation plan'
---

View File

@@ -1,70 +0,0 @@
---
description: 'Fix active pull request comments with scoped changes'
name: 'fix-pr-active-comments'
agent: 'agent'
argument-hint: 'PR number or active PR URL'
---
# Fix Active PR Comments
## Mission
Resolve active pull request comments by applying only simple fixes. For complex refactors, write a plan instead of changing code.
## Scope & Preconditions
- You must have an active pull request context or a provided PR number.
- Only implement simple changes. Do not implement large refactors.
- If required context is missing, request it and stop.
## Inputs
- Required: ${input:pr_number:PR number or URL}
- Optional: ${input:comment_scope:files or areas to focus on}
- Optional: ${input:fixing_guidelines:additional fixing guidelines from the user}
## Workflow
1. Locate all active (unresolved) PR review comments for the given PR.
2. For each comment, classify the change scope:
- Simple change: limited edits, localized fix, low risk, no broad redesign.
- Large refactor: multi-file redesign, architecture change, or risky behavior change.
3. For each large refactor request:
- Do not modify code.
- Write a planning document to Generated Files/prReview/${input:pr_number}/fixPlan/.
4. For each simple change request:
- Implement the fix with minimal edits.
- Run quick checks if needed.
- Commit and push the change.
5. For comments that seem invalid, unclear, or not applicable (even if simple):
- Do not change code.
- Add the item to a summary table in Generated Files/prReview/${input:pr_number}/fixPlan/overview.md.
- Consult back to the end user in a friendly, polite tone.
6. Respond to each comment that you fixed:
- Reply in the active conversation.
- Use a polite or friendly tone.
- Keep the response under 200 words.
- Resolve the comment after replying.
## Output Expectations
- Simple fixes: code changes committed and pushed.
- Large refactors: a plan file saved to Generated Files/prReview/${input:pr_number}/fixPlan/.
- Invalid or unclear comments: captured in Generated Files/prReview/${input:pr_number}/fixPlan/overview.md.
- Each fixed comment has a reply under 200 words and is resolved.
## Plan File Template
Use this template for each large refactor item:
# Fix Plan: <short title>
## Context
- Comment link:
- Impacted areas:
## Overview Table Template
Use this table in Generated Files/prReview/${input:pr_number}/fixPlan/overview.md:
| Comment link | Summary | Reason not applied | Suggested follow-up |
| --- | --- | --- | --- |
| | | | |
## Quality Assurance
- Verify plan file path exists.
- Ensure no code changes were made for large refactor items.
- Confirm replies are under 200 words and comments are resolved.

View File

@@ -1,5 +1,6 @@
---
agent: 'agent'
model: 'GPT-5.1-Codex-Max'
description: 'Resolve Code scanning / check-spelling comments on the active PR'
---

View File

@@ -1,5 +1,6 @@
---
agent: 'agent'
model: 'GPT-5.1-Codex-Max'
description: 'Review a GitHub issue, score it (0-100), and generate an implementation plan'
---

View File

@@ -1,5 +1,6 @@
---
agent: 'agent'
model: 'GPT-5.1-Codex-Max'
description: 'Perform a comprehensive PR review with per-step Markdown and machine-readable outputs'
---

View File

@@ -230,12 +230,8 @@
"WinUI3Apps\\PowerToys.RegistryPreview.dll",
"WinUI3Apps\\PowerToys.RegistryPreview.exe",
"WinUI3Apps\\PowerToys.ShortcutGuide.exe",
"WinUI3Apps\\PowerToys.ShortcutGuide.dll",
"WinUI3Apps\\PowerToys.ShortcutGuideModuleInterface.dll",
"WinUI3Apps\\PowerToys.ShortcutGuide.IndexYmlGenerator.dll",
"WinUI3Apps\\PowerToys.ShortcutGuide.IndexYmlGenerator.exe",
"WinUI3Apps\\ShortcutGuide.CPPProject.dll",
"PowerToys.ShortcutGuide.exe",
"PowerToys.ShortcutGuideModuleInterface.dll",
"PowerToys.ZoomIt.exe",
"PowerToys.ZoomItModuleInterface.dll",

View File

@@ -35,9 +35,7 @@ stages:
${{ else }}:
name: SHINE-OSS-L
${{ if eq(parameters.useVSPreview, true) }}:
demands: ImageOverride -equals SHINE-VS18-Preview
${{ else }}:
demands: ImageOverride -equals SHINE-VS18-Latest
demands: ImageOverride -equals SHINE-VS17-Preview
buildPlatforms:
- ${{ parameters.platform }}
buildConfigurations: [Release]

View File

@@ -51,9 +51,7 @@ extends:
pool:
name: SHINE-INT-S
${{ if eq(parameters.useVSPreview, true) }}:
demands: ImageOverride -equals SHINE-VS18-Preview
${{ else }}:
demands: ImageOverride -equals SHINE-VS18-Latest
demands: ImageOverride -equals SHINE-VS17-Preview
os: windows
sdl:
tsa:
@@ -76,9 +74,7 @@ extends:
demands:
# Our INT agents have a large disk mounted at P:\
- ${{ if eq(parameters.useVSPreview, true) }}:
- ImageOverride -equals SHINE-VS18-Latest-Preview
- ${{ else }}:
- ImageOverride -equals SHINE-VS18-Latest
- ImageOverride -equals SHINE-VS17-Preview
os: windows
variables:
IsPipeline: 1 # The installer uses this to detect whether it should pick up localizations

View File

@@ -253,7 +253,7 @@ jobs:
displayName: Build PowerToys main project
inputs:
solution: 'PowerToys.slnx'
vsVersion: 18.0
vsVersion: 17.0
msbuildArgs: >-
-restore -graph
/p:RestorePackagesConfig=true
@@ -276,7 +276,7 @@ jobs:
condition: and(succeeded(), eq(variables['BuildPlatform'], 'arm64'))
inputs:
solution: PowerToys.slnx
vsVersion: 18.0
vsVersion: 17.0
msbuildArgs: >-
-restore
/p:Configuration=$(BuildConfiguration)
@@ -338,7 +338,7 @@ jobs:
displayName: Build BugReportTool
inputs:
solution: '**/tools/BugReportTool/BugReportTool.sln'
vsVersion: 18.0
vsVersion: 17.0
msbuildArgs: >-
-restore -graph
/p:RestorePackagesConfig=true
@@ -359,7 +359,7 @@ jobs:
displayName: Build StylesReportTool
inputs:
solution: '**/tools/StylesReportTool/StylesReportTool.sln'
vsVersion: 18.0
vsVersion: 17.0
msbuildArgs: >-
-restore -graph
/p:RestorePackagesConfig=true
@@ -381,7 +381,7 @@ jobs:
displayName: Publish ${{ project }} for Packaging
inputs:
solution: ${{ project }}
vsVersion: 18.0
vsVersion: 17.0
msbuildArgs: >-
/target:Publish
/graph

View File

@@ -82,7 +82,7 @@ jobs:
displayName: Build UI Test Projects
inputs:
solution: '**/*UITest*.csproj'
vsVersion: 18.0
vsVersion: 17.0
msbuildArgs: >-
-restore
-graph
@@ -103,7 +103,7 @@ jobs:
displayName: 'Build UI Test Module: ${{ module }}'
inputs:
solution: '**/*${{ module }}*.csproj'
vsVersion: 18.0
vsVersion: 17.0
msbuildArgs: >-
-restore
-graph

View File

@@ -49,9 +49,7 @@ stages:
${{ else }}:
name: SHINE-OSS-L
${{ if eq(parameters.useVSPreview, true) }}:
demands: ImageOverride -equals SHINE-VS18-Preview
${{ else }}:
demands: ImageOverride -equals SHINE-VS18-Latest
demands: ImageOverride -equals SHINE-VS17-Preview
buildPlatforms:
- ${{ platform }}
buildConfigurations: [Release]

View File

@@ -29,9 +29,7 @@ stages:
${{ else }}:
name: SHINE-OSS-L
${{ if eq(parameters.useVSPreview, true) }}:
demands: ImageOverride -equals SHINE-VS18-Preview
${{ else }}:
demands: ImageOverride -equals SHINE-VS18-Latest
demands: ImageOverride -equals SHINE-VS17-Preview
buildPlatforms:
- ${{ parameters.platform }}
buildConfigurations: [Release]

View File

@@ -36,7 +36,7 @@ steps:
displayName: Build Shared Support DLLs
inputs:
solution: "**/installer/PowerToysSetup.slnx"
vsVersion: 18.0
vsVersion: 17.0
msbuildArgs: >-
/t:PowerToysSetupCustomActionsVNext;SilentFilesInUseBAFunction
/p:RunBuildEvents=true;RestorePackagesConfig=true;CIBuild=true
@@ -75,7 +75,7 @@ steps:
displayName: 💻 Build VNext MSI
inputs:
solution: "**/installer/PowerToysSetup.slnx"
vsVersion: 18.0
vsVersion: 17.0
msbuildArgs: >-
-restore
/t:PowerToysInstallerVNext
@@ -92,7 +92,7 @@ steps:
displayName: 👤 Build VNext MSI
inputs:
solution: "**/installer/PowerToysSetup.slnx"
vsVersion: 18.0
vsVersion: 17.0
msbuildArgs: >-
/t:PowerToysInstallerVNext
/p:RunBuildEvents=false;PerUser=true;BuildProjectReferences=false;CIBuild=true
@@ -143,7 +143,7 @@ steps:
displayName: 💻 Build VNext Bootstrapper
inputs:
solution: "**/installer/PowerToysSetup.slnx"
vsVersion: 18.0
vsVersion: 17.0
msbuildArgs: >-
-restore
/t:PowerToysBootstrapperVNext
@@ -160,7 +160,7 @@ steps:
displayName: 👤 Build VNext Bootstrapper
inputs:
solution: "**/installer/PowerToysSetup.slnx"
vsVersion: 18.0
vsVersion: 17.0
msbuildArgs: >-
/t:PowerToysBootstrapperVNext
/p:PerUser=true;BuildProjectReferences=false;CIBuild=true

View File

@@ -1,16 +1,9 @@
# Build common vswhere base arguments
$vsWhereBaseArgs = @('-latest', '-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64')
if ($env:VCWhereExtraVersionTarget) {
# Add version target if specified (e.g., '-version [18.0,19.0)' for VS2026)
$vsWhereBaseArgs += $env:VCWhereExtraVersionTarget.Split(' ')
}
$VSInstances = ([xml](& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' @vsWhereBaseArgs -include packages -format xml))
$VSInstances = ([xml](& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -include packages -format xml))
$VSPackages = $VSInstances.instances.instance.packages.package
$LatestVCPackage = ($VSPackages | ? { $_.id -eq "Microsoft.VisualCpp.Tools.Core" })
$LatestVCToolsVersion = $LatestVCPackage.version;
$VSRoot = (& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' @vsWhereBaseArgs -property 'resolvedInstallationPath')
$VSRoot = (& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property 'resolvedInstallationPath')
$VCToolsRoot = Join-Path $VSRoot "VC\Tools\MSVC"
# We have observed a few instances where the VC tools package version actually
@@ -31,12 +24,5 @@ If ($Null -Eq (Get-Item $PackageVCToolPath -ErrorAction:Ignore)) {
}
Write-Output "Latest VCToolsVersion: $LatestVCToolsVersion"
# VS2026 (MSVC 14.50+) doesn't need explicit VCToolsVersion - let MSBuild auto-select
$MajorMinorVersion = [Version]::Parse($LatestVCToolsVersion)
If ($MajorMinorVersion.Major -eq 14 -and $MajorMinorVersion.Minor -ge 50) {
Write-Output "VS2026 detected (MSVC 14.50+). Skipping VCToolsVersion override to allow MSBuild auto-selection."
} Else {
Write-Output "Updating VCToolsVersion environment variable for job"
Write-Output "##vso[task.setvariable variable=VCToolsVersion]$LatestVCToolsVersion"
}
Write-Output "Updating VCToolsVersion environment variable for job"
Write-Output "##vso[task.setvariable variable=VCToolsVersion]$LatestVCToolsVersion"

View File

@@ -40,7 +40,7 @@ These instruction files are automatically applied when working in their respecti
### Prerequisites
- Visual Studio 2022 17.4+ or Visual Studio 2026
- Visual Studio 2022 17.4+
- Windows 10 1803+ (April 2018 Update or newer)
- Initialize submodules once: `git submodule update --init --recursive`

View File

@@ -51,7 +51,7 @@
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<WarningLevel>Level4</WarningLevel>
<DisableSpecificWarnings>4679;4706;4874;5271;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<DisableSpecificWarnings>4679;5271;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<DisableAnalyzeExternal >true</DisableAnalyzeExternal>
<ExternalWarningLevel>TurnOffAllWarnings</ExternalWarningLevel>
<ConformanceMode>false</ConformanceMode>
@@ -110,7 +110,6 @@
<!-- Props that are constant for both Debug and Release configurations -->
<PropertyGroup Label="Configuration">
<PlatformToolset>v143</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '18.0'">v145</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<DesktopCompatible>true</DesktopCompatible>
<SpectreMitigation>Spectre</SpectreMitigation>

View File

@@ -135,7 +135,6 @@
<PackageVersion Include="WinUIEx" Version="2.8.0" />
<PackageVersion Include="WPF-UI" Version="3.0.5" />
<PackageVersion Include="WyHash" Version="1.0.5" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
<PackageVersion Include="WixToolset.Heat" Version="5.0.2" />
<PackageVersion Include="WixToolset.Firewall.wixext" Version="5.0.2" />
<PackageVersion Include="WixToolset.Util.wixext" Version="5.0.2" />

View File

@@ -1570,5 +1570,4 @@ SOFTWARE.
- UTF.Unknown
- WinUIEx
- WPF-UI
- WyHash
- YamlDotNet
- WyHash

View File

@@ -197,113 +197,99 @@
<Project Path="src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj" Id="0adeb797-c8c7-4ffa-acd5-2af6cad7ecd8" />
</Folder>
<Folder Name="/modules/CommandPalette/Built-in Extensions/">
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Microsoft.CmdPal.Ext.Bookmarks.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.Bookmark/Microsoft.CmdPal.Ext.Bookmarks.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Microsoft.CmdPal.Ext.ClipboardHistory.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.ClipboardHistory/Microsoft.CmdPal.Ext.ClipboardHistory.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
<Deploy />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Microsoft.CmdPal.Ext.PowerToys.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.PowerToys/Microsoft.CmdPal.Ext.PowerToys.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
<Deploy />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Microsoft.CmdPal.Ext.Registry.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.Registry/Microsoft.CmdPal.Ext.Registry.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.RemoteDesktop/Microsoft.CmdPal.Ext.RemoteDesktop.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.RemoteDesktop/Microsoft.CmdPal.Ext.RemoteDesktop.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Microsoft.CmdPal.Ext.Shell.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.Shell/Microsoft.CmdPal.Ext.Shell.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Microsoft.CmdPal.Ext.System.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.System/Microsoft.CmdPal.Ext.System.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Microsoft.CmdPal.Ext.TimeDate.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.TimeDate/Microsoft.CmdPal.Ext.TimeDate.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
<Deploy />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Microsoft.CmdPal.Ext.WebSearch.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.WebSearch/Microsoft.CmdPal.Ext.WebSearch.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Microsoft.CmdPal.Ext.WindowsServices.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.WindowsServices/Microsoft.CmdPal.Ext.WindowsServices.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Microsoft.CmdPal.Ext.WindowsSettings.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.WindowsSettings/Microsoft.CmdPal.Ext.WindowsSettings.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Microsoft.CmdPal.Ext.WindowsTerminal.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.WindowsTerminal/Microsoft.CmdPal.Ext.WindowsTerminal.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Microsoft.CmdPal.Ext.WindowWalker.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.WindowWalker/Microsoft.CmdPal.Ext.WindowWalker.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Microsoft.CmdPal.Ext.WinGet.csproj">
<Project Path="src/modules/cmdpal/Extensions/Microsoft.CmdPal.Ext.WinGet/Microsoft.CmdPal.Ext.WinGet.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
<Deploy />
</Project>
</Folder>
<Folder Name="/modules/CommandPalette/Core/">
<Project Path="src/modules/cmdpal/Core/Microsoft.CmdPal.Core.Common/Microsoft.CmdPal.Core.Common.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Microsoft.CmdPal.Core.ViewModels.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
</Folder>
<Folder Name="/modules/CommandPalette/Extension SDK/">
<Project Path="src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj">
<Project Path="src/modules/cmdpal/SDK/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj" Id="305dd37e-c85d-4b08-aafe-7381fa890463" />
<Project Path="src/modules/cmdpal/SDK/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj" Id="305dd37e-c85d-4b08-aafe-7381fa890463" />
</Folder>
<Folder Name="/modules/CommandPalette/Sample Extensions/">
<Project Path="src/modules/cmdpal/ext/ProcessMonitorExtension/ProcessMonitorExtension.csproj">
<Project Path="src/modules/cmdpal/Extensions/ProcessMonitorExtension/ProcessMonitorExtension.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
<Deploy />
</Project>
<Project Path="src/modules/cmdpal/ext/SamplePagesExtension/SamplePagesExtension.csproj">
<Project Path="src/modules/cmdpal/Extensions/SamplePagesExtension/SamplePagesExtension.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
<Deploy />
</Project>
</Folder>
<Folder Name="/modules/CommandPalette/Tests/">
<Project Path="src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Microsoft.CmdPal.Core.Common.UnitTests.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.Apps.UnitTests/Microsoft.CmdPal.Ext.Apps.UnitTests.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
@@ -360,22 +346,26 @@
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/Tests/Microsoft.CommandPalette.Extensions.Toolkit.UnitTests/Microsoft.CommandPalette.Extensions.Toolkit.UnitTests.csproj" Id="2eca18b7-33b7-4829-88f1-439b20fd60f6">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
</Folder>
<Folder Name="/modules/CommandPalette/UI/">
<Project Path="src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Microsoft.CmdPal.UI.ViewModels.csproj">
<Project Path="src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj" Id="6515f03f-e56d-4db4-b23d-ac4fb80db36f" />
<Project Path="src/modules/cmdpal/UI/Microsoft.CmdPal.UI.Common/Microsoft.CmdPal.UI.Common.csproj" Id="b0131363-c9e5-41e5-a541-8dc60760f03f">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj">
<Project Path="src/modules/cmdpal/UI/Microsoft.CmdPal.UI.Services/Microsoft.CmdPal.UI.Services.csproj" Id="014f1320-7d89-4d9f-a247-f68737c0f46b">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/UI/Microsoft.CmdPal.UI.ViewModels/Microsoft.CmdPal.UI.ViewModels.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/cmdpal/UI/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
<Deploy />
</Project>
<Project Path="src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj" Id="6515f03f-e56d-4db4-b23d-ac4fb80db36f" />
</Folder>
<Folder Name="/modules/CropAndLock/">
<Project Path="src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj" Id="f5e1146e-b7b3-4e11-85fd-270a500bd78c" />
@@ -962,16 +952,8 @@
</Project>
</Folder>
<Folder Name="/modules/shortcutguide/">
<Project Path="src/modules/ShortcutGuide/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj" Id="2d604c07-51fc-46bb-9eb7-75aecc7f5e81" />
<Project Path="src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/ShortcutGuide.IndexYmlGenerator.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuide.Ui.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.vcxproj" Id="e487304a-b1fb-4e6b-8e70-014051af5b99" />
<Project Path="src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj" Id="2edb3eb4-fa92-4bff-b2d8-566584837231" />
<Project Path="src/modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.vcxproj" Id="2d604c07-51fc-46bb-9eb7-75aecc7f5e81" />
</Folder>
<Folder Name="/modules/Workspaces/">
<Project Path="src/modules/Workspaces/Workspaces.ModuleServices/Workspaces.ModuleServices.csproj">

View File

@@ -51,19 +51,19 @@ But to get started quickly, choose one of the installation methods below:
Go to the <a href="https://aka.ms/installPowerToys">PowerToys GitHub releases</a>, click Assets to reveal the downloads, and choose the installer that matches your architecture and install scope. For most devices, that's the x64 per-user installer.
<!-- items that need to be updated release to release -->
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.98%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.97%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.97.1/PowerToysUserSetup-0.97.1-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.97.1/PowerToysUserSetup-0.97.1-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.97.1/PowerToysSetup-0.97.1-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.97.1/PowerToysSetup-0.97.1-arm64.exe
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.97%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.96%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.97.0/PowerToysUserSetup-0.97.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.97.0/PowerToysUserSetup-0.97.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.97.0/PowerToysSetup-0.97.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.97.0/PowerToysSetup-0.97.0-arm64.exe
| Description | Filename |
|----------------|----------|
| Per user - x64 | [PowerToysUserSetup-0.97.1-x64.exe][ptUserX64] |
| Per user - ARM64 | [PowerToysUserSetup-0.97.1-arm64.exe][ptUserArm64] |
| Machine wide - x64 | [PowerToysSetup-0.97.1-x64.exe][ptMachineX64] |
| Machine wide - ARM64 | [PowerToysSetup-0.97.1-arm64.exe][ptMachineArm64] |
| Per user - x64 | [PowerToysUserSetup-0.97.0-x64.exe][ptUserX64] |
| Per user - ARM64 | [PowerToysUserSetup-0.97.0-arm64.exe][ptUserArm64] |
| Machine wide - x64 | [PowerToysSetup-0.97.0-x64.exe][ptMachineX64] |
| Machine wide - ARM64 | [PowerToysSetup-0.97.0-arm64.exe][ptMachineArm64] |
</details>
@@ -103,38 +103,18 @@ There are <a href="https://learn.microsoft.com/windows/powertoys/install#communi
</details>
## ✨ What's new
**Version 0.97.1 (January 2026)**
**Version 0.97 (January 2026)**
This patch release fixes several important stability issues identified in v0.97.0 based on incoming reports. Check out the [v0.97.0](https://github.com/microsoft/PowerToys/releases/tag/v0.97.0) notes for the full list of changes.
For an in-depth look at the latest changes, visit the [Windows Command Line blog](https://aka.ms/powertoys-releaseblog).
**Highlights**
### Advanced Paste
- #44862: Fixed Settings UI advanced paste page crash by using correct settings repository for null checking.
### Command Palette
- #44886: Fixed personalization section not appearing by using latest MSIX for installation.
- #44938: Fixed loading of icons from internet shortcuts. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- #45076: Fixed potential deadlock from lazy-loading AppListItem details. Thanks [@jiripolasek](https://github.com/jiripolasek)!
### Cursor Wrap
- #44936: Added improved multi-monitor support; Added laptop lid close detection for dynamic monitor topology updates. Thanks [@mikehall-ms](https://github.com/mikehall-ms)!
- #44936: Added new settings dropdown to constrain wrapping to horizontal-only, vertical-only, or both directions. Thanks [@mikehall-ms](https://github.com/mikehall-ms)!
### Peek
- #44995: Fixed Space key triggering Peek during file rename, search, or address bar typing.
### PowerRename
- #44944: Fixed regex `$` not working, preventing users from adding text at the end of filenames.
### Runner
- #44931: Monochrome tray icon now adapts to Windows system theme instead of app theme.
- #44982: Fixed right-click menu to dynamically update based on Quick Access enabled/disabled state.
### GPO / Enterprise
- #45028: Added CursorWrap policy definition to ADMX templates. Thanks [@htcfreek](https://github.com/htcfreek)!
For the full list of v0.97 changes, visit the [Windows Command Line blog](https://aka.ms/powertoys-releaseblog).
**Highlights**
- **Command Palette**: Major expansion with PowerToys extension (Windows 11 only), Remote Desktop built-in extension, theme customization, drag-and-drop support, fallback ranking controls, sections/separators for pages, pinyin Chinese matching, and many UX refinements.
- **Settings**: Quick Access flyout is now a standalone process for significantly faster startup, theme-adaptive tray icon, AOT serialization, and multiple UI/accessibility fixes
- **CursorWrap (New!)**: New mouse utility that lets your cursor wrap around screen edges, making multi-monitor navigation faster and more seamless.
- **Advanced Paste**: Image input for AI, color detection in clipboard history, Foundry Local improvements, Azure AI icons, and multiple bug fixes
- **CLI Support Expanded**: FancyZones, Image Resizer, and File Locksmith can now be controlled from the command line for layout management, batch image resizing, and file lock inspection.
- **LightSwitch**: Added support for automatically following Windows Night Light mode.
- **Release Experience & Quality**: Refreshed "Whats new" dialog, plus many performance improvements, stability fixes, and refinements across PowerToys.
## Advanced Paste
@@ -309,7 +289,7 @@ For the full list of v0.97 changes, visit the [Windows Command Line blog](https:
- Stabilized FancyZones UI tests with more reliable selectors and screen recordings.
## 🛣️ Roadmap
We are planning some nice new features and improvements for the next releases PowerDisplay, Command Palette improvements and a brand-new Shortcut Guide experience! Stay tuned for [v0.98][github-next-release-work]!
We are planning some nice new features and improvements for the next releases PowerDisplay, Command Palette improvements and a brand-new Shortcut Guide experience! Stay tuned for [v0.97][github-next-release-work]!
## ❤️ PowerToys Community
The PowerToys team is extremely grateful to have the [support of an amazing active community][community-link]. The work you do is incredibly important. PowerToys wouldn't be nearly what it is today without your help filing bugs, updating documentation, guiding the design, or writing features. We want to say thank you and take time to recognize your work. Your contributions and feedback improve PowerToys month after month!

View File

@@ -88,7 +88,7 @@
### Building PowerToys Locally
#### One stop script for building installer
1. Open `Developer Powershell for VS 2022` or `Developer PowerShell for VS` for VS 2026.
1. Open developer powershell for vs 2022
2. Run tools\build\build-installer.ps1
> For the first-time setup, please run the installer as an administrator. This ensures that the Wix tool can move wix.target to the desired location and trust the certificate used to sign the MSIX packages.
@@ -109,7 +109,7 @@ dotnet tool install --global wix --version 5.0.2
##### From the command line
1. From the start menu, open a `Developer Command Prompt for VS 2022` or `Developer Command Prompt for VS`
1. From the start menu, open a `Developer Command Prompt for VS 2022`
1. Ensure `nuget.exe` is in your `%path%`
1. In the repo root, run these commands:
@@ -140,7 +140,7 @@ If you prefer, you can alternatively build prerequisite projects for the install
The resulting installer will be available in the `installer\PowerToysSetupVNext\x64\Release\` folder.
To build the installer from the command line, run `Developer Command Prompt for VS 2022` or `Developer Command Prompt for VS` in admin mode and execute the following commands. The generated installer package will be located at `\installer\PowerToysSetupVNext\{platform}\Release\MachineSetup`.
To build the installer from the command line, run `Developer Command Prompt for VS 2022` in admin mode and execute the following commands. The generated installer package will be located at `\installer\PowerToysSetupVNext\{platform}\Release\MachineSetup`.
```
git clean -xfd -e *exe -- .\installer\

View File

@@ -15,7 +15,7 @@ Before you can start debugging PowerToys, you need to set up your development en
You can build the entire solution from the command line, which is sometimes faster than building within Visual Studio:
1. Open `Developer Command Prompt for VS 2022` or `Developer Command Prompt for VS`
1. Open Developer Command Prompt for VS 2022
2. Navigate to the repository root directory
3. Run the following command(don't forget to set the correct platform):
```pwsh
@@ -105,7 +105,7 @@ If you encounter build errors about missing image files (e.g., `.png`, `.ico`, o
1. **Clean the solution in Visual Studio**: Build > Clean Solution
Or from the command line (Developer Command Prompt for VS 2022 or Developer Command Prompt for VS):
Or from the command line (Developer Command Prompt for VS 2022):
```pwsh
msbuild PowerToys.slnx /t:Clean /p:Platform=x64 /p:Configuration=Debug
```

View File

@@ -15,11 +15,9 @@ VS Code extensions Needed:
---
## Building in VS Code
### Configure Developer Powershell for VS 2022 or Developer Powershell for VS for more convenient dev in vscode.
### Configure developer powershell for vs2022 for more convenient dev in vscode.
1. Configure profile in in settings, entry: "terminal.integrated.profiles.windows"
2. Add below config as entry (choose VS 2022 or VS 2026 based on your installation):
**For Visual Studio 2022:**
2. Add below config as entry:
```json
"Developer PowerShell for VS 2022": {
// Configure based on your preference
@@ -29,35 +27,16 @@ VS Code extensions Needed:
"-Command",
"& {",
"$orig = Get-Location;",
// Adjust path based on your edition (Community/Professional/Enterprise)
// Configure based on your environment
"& 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Common7\\Tools\\Launch-VsDevShell.ps1';",
"Set-Location $orig",
"}"
]
},
```
3. [Optional] Set Developer PowerShell for VS 2022 as your default profile, so that you can get a deep integration with vscode coding agent.
**For Visual Studio 2026:**
```json
"Developer PowerShell for VS": {
// Configure based on your preference
"path": "C:\\Program Files\\WindowsApps\\Microsoft.PowerShell_7.5.2.0_arm64__8wekyb3d8bbwe\\pwsh.exe",
"args": [
"-NoExit",
"-Command",
"& {",
"$orig = Get-Location;",
// Adjust path based on your edition (Community/Professional/Enterprise)
"& 'C:\\Program Files\\Microsoft Visual Studio\\18\\Enterprise\\Common7\\Tools\\Launch-VsDevShell.ps1';",
"Set-Location $orig",
"}"
]
},
```
3. [Optional] Set your Developer PowerShell profile as the default, so that you can get a deep integration with vscode coding agent.
4. Now you can build with plain `msbuild` or configure tasks.json in below section.
4. Now You can build with plain `msbuild` or configure tasks.json in below section
Or reach out to "tools\build\BUILD-GUIDELINES.md"
### Sample plain msbuild command

View File

@@ -1,311 +0,0 @@
# 🧭 Creating a new PowerToy: end-to-end developer guide
First of all, thank you for wanting to contribute to PowerToys. The work we do would not be possible without the support of community supporters like you.
This guide documents the process of building a new PowerToys utility from scratch, including architecture decisions, integration steps, and common pitfalls.
---
## 1. Overview and prerequisites
A PowerToy module is a self-contained utility integrated into the PowerToys ecosystem. It can be UI-based, service-based, or both.
### Requirements
- [Visual Studio 2026](https://visualstudio.microsoft.com/downloads/) and the following workloads/individual components:
- Desktop Development with C++
- WinUI application development
- .NET desktop development
- Windows 10 SDK (10.0.22621.0)
- Windows 11 SDK (10.0.26100.3916)
- .NET 8 SDK
- Fork the [PowerToys repository](https://github.com/microsoft/PowerToys/tree/main) locally
- [Validate that you are able to build and run](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/development/debugging.md) `PowerToys.slnx`.
Optional:
- [WiX v5 toolset](https://github.com/microsoft/PowerToys/tree/main) for the installer
> [!NOTE]
> To ensure all the correct VS Workloads are installed, use [the WinGet configuration files](https://github.com/microsoft/PowerToys/tree/e13d6a78aafbcf32a4bb5f8581d041e1d057c3f1/.config) in the project repository. (Use the one that matches your VS distribution. ie: VS Community would use `configuration.winget`)
### Folder structure
```
src/
modules/
your_module/
YourModule.sln
YourModuleInterface/
YourModuleUI/ (if needed)
YourModuleService/ (if needed)
```
---
## 2. Design and planning
### Decide the type of module
Think about how your module works and which existing modules behave similarly. You are going to want to think about the UI needed for the application, the lifecycle, whether it is a service that is always running or event based. Below are some basic scenarios with some modules to explore. You can write your application in C++ or C#.
- **UI-only:** e.g., ColorPicker
- **Background service:** e.g., LightSwitch, Awake
- **Hybrid (UI + background logic):** e.g., ShortcutGuide
- **C++/C# interop:** e.g., PowerRename
### Write your module interface
Begin by setting up the [PowerToy module template project](https://github.com/microsoft/PowerToys/tree/main/tools/project_template). This will generate boilerplate for you to begin your new module. Below are the key headers in the Module Interface (`dllmain.cpp`) and an explanation of their purpose:
1. This is where module settings are defined. These can be anything from strings, bools, ints, and even custom Enums.
```c++
struct ModuleSettings {};
```
2. This is the header for the full class. It inherits the PowerToyModuleIface
```c++
class ModuleInterface : public PowertoyModuleIface
{
private:
// the private members of the class
// Can include the enabled variable, logic for event handlers, or hotkeys.
public:
// the public members of the class
// Will include the constructor and initialization logic.
}
```
> [!NOTE]
> Many of the class functions are boilerplate and need simple string replacements with your module name. The rest of the functions below will require bigger changes.
3. GPO stands for "Group Policy Object" and allows for administrators to configure settings across a network of machines. It is required that your module is on this list of settings. You can right click the `powertoys_gpo` object to go to the definition and set up the `getConfiguredModuleEnabledValue` for your module.
```c++
virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
{
return powertoys_gpo::getConfiguredModuleEnabledValue();
}
```
4. `init_settings()` initializes the settings for the interface. Will either pull from existing settings.json or use defaults.
```c++
void ModuleInterface::init_settings()
```
5. `get_config` retrieves the settings from the settings.json file.
```c++
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
```
6. `set_config` sets the new settings to the settings.json file.
```c++
virtual void set_config(const wchar_t* config) override
```
7. `call_custom_action` allows custom actions to be called based on signals from the settings app.
```c++
void call_custom_action(const wchar_t* action) override
```
8. Lifecycle events control whether the module is enabled or not, as well as the default status of the module.
```c++
virtual void enable() // starts the module
virtual void disable() // terminates the module and performs any cleanup
virtual bool is_enabled() // returns if the module is currently enabled
virtual bool is_enabled_by_default() const override // allows the module to dictate whether it should be enabled by default in the PowerToys app.
```
9. Hotkey functions control the status of the hotkey.
```c++
// takes the hotkey from settings into a format that the interface can understand
void parse_hotkey(PowerToysSettings::PowerToyValues& settings)
// returns the hotkeys from settings
virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override
// performs logic when the hotkey event is fired
virtual bool on_hotkey(size_t hotkeyId) override
```
### Notes
- Keep module logic isolated under `/modules/<YourModule>`
- Use shared utilities from [`common`](https://github.com/microsoft/PowerToys/tree/main/src/common) instead of cross-module dependencies
- init/set/get config use preset functions to access the settings. Check out the [`settings_objects.h`](https://github.com/microsoft/PowerToys/blob/main/src/common/SettingsAPI/settings_helpers.h) in `src\common\SettingsAPI`
---
## 3. Bootstrapping your module
1. Use the [template](https://github.com/microsoft/PowerToys/tree/main/tools/project_template) to generate the module interface starter code.
2. Update all projects and namespaces with your module name.
3. Update GUIDs in `.vcxproj` and solution files.
4. Update the functions mentioned in the above section with your custom logic.
5. In order for your module to be detected by the runner you are required to add references to various lists. In order to register your module, add the corresponding module reference to the lists that can be found in the following files. (Hint: search other modules names to find the lists quicker)
- `src/runner/modules.h`
- `src/runner/modules.cpp`
- `src/runner/resource.h`
- `src/runner/settings_window.h`
- `src/runner/settings_window.cpp`
- `src/runner/main.cpp`
- `src/common/logger.h` (for logging)
6. ModuleInterface should build your `ModuleInterface.dll`. This will allow the runner to interact with your service.
> [!TIP]
> Mismatched module IDs are one of the most common causes of load failures. Keep your ID consistent across manifest, registry, and service.
---
## 4. Write your service
This is going to look different for every PowerToy. It may be easier to develop the application independently, and then link in the PowerToys settings logic later. But you have to write the service first, before connecting it to the runner.
### Notes
- This is a separate project from the Module Interface.
- You can develop this project using C# or C++.
- Set the service icon using the `.rc` file.
- Set the service name in the `.vcxproj` by setting the `<TargetName>`
```
<PropertyGroup>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\$(MSBuildProjectName)\</OutDir>
<TargetName>PowerToys.LightSwitchService</TargetName>
</PropertyGroup>
```
- To view the code of the `.vcxproj`, right click the item and select **Unload project**
- Use the following functions to interact with settings from your service
```
ModuleSettings::instance().InitFileWatcher();
ModuleSettings::instance().LoadSettings();
auto& settings = ModuleSettings::instance().settings();
```
These come from the `ModuleSettings.h` file that lives with the Service. You can copy this from another module (e.g., Light Switch) and adjust to fit your needs.
If your module has a user interface:
- Use the **WinUI Blank App** template when setting up your project
- Use [Windows design best practices](https://learn.microsoft.com/windows/apps/design/basics/)
- Use the [WinUI 3 Gallery](https://apps.microsoft.com/detail/9p3jfpwwdzrc) for help with your UI code, and additional guidance.
## 5. Settings integration
PowerToys settings are stored per-module as JSON under:
```
%LOCALAPPDATA%\Microsoft\PowerToys\<module>\settings.json
```
### Implementation steps
- In `src\settings-ui\Settings.UI.Library\` create `<module>Properties.cs` and `<module>Settings.cs`
- `<module>Properties.cs` is where you will define your defaults. Every setting needs to be represented here. This should match what was set in the Module Interface.
- `<module>Settings.cs`is where your settings.json will be built from. The structure should match the following
```cs
public ModuleSettings()
{
Name = ModuleName;
Version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
Properties = new ModuleProperties(); // settings properties you set above.
}
```
- In `src\settings-ui\Settings.UI\ViewModels` create `<module>ViewModel.cs` this is where the interaction happens between your settings page in the PowerToys app and the settings file that is stored on the device. Changes here will trigger the settings watcher via a `NotifyPropertyChanged` event.
- Create a `SettingsPage.xaml` at `src\settings-ui\Settings.UI\SettingsXAML\Views`. This will be the page where the user interacts with the settings of your module.
- Be sure to use resource strings for user facing strings so they can be localized. (`x:Uid` connects to Resources.resw)
```xaml
// LightSwitch.xaml
<ComboBoxItem
x:Uid="LightSwitch_ModeOff"
AutomationProperties.AutomationId="OffCBItem_LightSwitch"
Tag="Off" />
// Resources.resw
<data name="LightSwitch_ModeOff.Content" xml:space="preserve">
<value>Off</value>
</data>
```
> [!IMPORTANT]
> In the above example we use `.Content` to target the content of the Combobox. This can change per UI element (e.g., `.Text`, `.Header`, etc.)
> **Reminder:** Manual changes via external editors (VS Code, Notepad) do **not** trigger the settings watcher. Only changes written through PowerToys trigger reloads.
---
### Gotchas:
- Only use the WinUI 3 framework, _not_ UWP.
- Use [`DispatcherQueue`](https://learn.microsoft.com/windows/apps/develop/dispatcherqueue) when updating UI from non-UI threads.
---
## 6. Building and debugging
### Debugging steps
1. If this is your first time debugging PowerToys, be sure to follow [these steps first](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/development/debugging.md#pre-debugging-setup).
2. Set "runner" as the start up project and ensure your build configuration is set to match your system (ARM64/x64)
3. Select <kbd>F5</kbd> or the **Local Windows Debugger** button to begin debugging. This should start the PowerToys runner.
4. To set breakpoints in your service, select Ctrl+Alt+P and search for your service to attach to the runner.
5. Use logs to document changes. The logs live at `%LOCALAPPDATA%\Microsoft\PowerToys\RunnerLogs` and `%LOCALAPPDATA%\Microsoft\PowerToys\Module\Service\<version>` for the specific module.
> [!TIP]
> PowerToys caches `.nuget` artifacts aggressively. Use `git clean -xfd` when builds behave unexpectedly.
---
## 7. Installer and packaging (WiX)
### Add your module to installer
1. Install [`WixToolset.Heat`](https://www.nuget.org/packages/WixToolset.Heat/) for Wix5 via nuget
2. Inside `installer\PowerToysInstallerVNext` add a new file for your module: `Module.wxs`
3. Inside of this file you will need copy the format from another module (ie: Light Switch) and replace the strings and GUID values.
4. The key part will be `<!--ModuleNameFiles_Component_Def-->` which is a placeholder for code that will be generated by `generateFileComponents.ps1`.
5. Inside `Product.wxs` add a line item in the `<Feature Id="CoreFeature" ... >` section. It will look like a list of ` <ComponentGroupRef Id="ModuleComponentGroup" />` items.
6. Inside `generateFileComponents.ps1` you will need to add an entry to the bottom for your new module. It will follow the following format. `-fileListName <Module>Files` will match the string you set in `Module.wxs`, `<ModuleServiceName>` will match the name of your exe.
```bash
# Module Name
Generate-FileList -fileDepsJson "" -fileListName <Module>Files -wxsFilePath $PSScriptRoot\<Module>.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\<ModuleServiceName>"
Generate-FileComponents -fileListName "<Module>Files" -wxsFilePath $PSScriptRoot\<Module>.wxs -regroot $registryroot
```
---
## 8. Testing and validation
### UI tests
- Place under `/modules/<YourModule>/Tests`
- Create a new [WinUI Unit Test App](https://learn.microsoft.com/windows/apps/winui/winui3/testing/create-winui-unit-test-project)
- Write unit tests following the format from previous modules (ie: Light Switch). This can be to test your standalone UI (if you're a module like Color Picker) or to verify that the Settings UI in the PowerToys app is controlling your service.
### Manual validation
- Enable/disable in PowerToys Settings
- Check initialization in logs
- Confirm icons, tooltips, and OOBE page appear correctly
### Pro tips
1. Validate wake/sleep and elevation states. Background modules often fail silently after resume if event handles arent recreated.
2. Use Windows Sandbox to simulate clean install environments
3. To simulate a "new user" you can delete the PowerToys folder from `%LOCALAPPDATA%\Microsoft`
### Shortcut conflict detection
If your module has a shortcut, ensure that it is properly registered following [the steps listed in the documentation](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/core/settings/settings-implementation.md#shortcut-conflict-detection) for conflict detection.
---
## 9. The final touches
### Out-of-Box experience (OOBE) page
The OOBE page is a custom settings page that gives the user at a glance information about each module. This window opens before the Settings application for new users and after updates. Create `OOBE<ModuleName>.xaml` at `src\settings-ui\Settings.UI\SettingsXAML\OOBE\Views`. You will also need to add your module name to the enum at `src\settings-ui\Settings.UI\OOBE\Enums\PowerToysModules.cs`.
### Module assets
Now that your PowerToy is _done_ you can start to think about the assets that will represent your module.
- Module Icon: This will be displayed in a number of places: OOBE page, in the README, on the home screen of PowerToys, on your individual module settings page, etc.
- Module Image: This is the image you see at the top of each individual settings page.
- OOBE Image: This is the header you see on the OOBE page for each module
> [!NOTE]
> This step is something that the Design team will handle internally to ensure consistency throughout the application. If you have ideas or recommendations on what the icon or screenshots should be for your module feel free to leave it in the "Additional Comments" section of the PR and the team will take it into consideration.
### Documentation
There are two types of documentation that will be required when submitting a new PowerToy:
1. Developer documentation: This will live in the [PowerToys repo](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/modules) at `/doc/devdocs/modules/` and should tell a developer how to work on your app. It should outline the module architecture, key files, testing, and tips on debugging if necessary.
2. Microsoft Learn documentation: When your new Module is ready to be merged into the PowerToys repository, an internal team member will create Microsoft Learn documentation so that users will understand how to use your module. There is not much work on your end as the developer for this step, but keep an eye on your PR in case we need more information about your PowerToy for this step.
---
Thank you again for contributing! If you need help, feel free to [open an issue](https://github.com/microsoft/PowerToys/issues/new/choose) and use the `Needs-Team-Response` label so we know you need attention.

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -18,28 +18,13 @@ Advanced Paste is a PowerToys module that provides enhanced clipboard pasting wi
TODO: Add implementation details
### Paste with AI Preview
The "Show preview" setting (`ShowCustomPreview`) controls whether AI-generated results are displayed in a preview window before pasting. **The preview feature does not consume additional AI credits**—the preview displays the same AI response that was already generated, cached locally from a single API call.
The implementation flow:
1. User initiates "Paste with AI" action
2. A single AI API call is made via `ExecutePasteFormatAsync`
3. The result is cached in `GeneratedResponses`
4. If preview is enabled, the cached result is displayed in the preview UI
5. User can paste the cached result without any additional API calls
See the `ExecutePasteFormatAsync(PasteFormat, PasteActionSource)` method in `OptionsViewModel.cs` for the implementation.
## Debugging
TODO: Add debugging information
## Settings
| Setting | Description |
|---------|-------------|
| `ShowCustomPreview` | When enabled, shows AI-generated results in a preview window before pasting. Does not affect AI credit consumption. |
TODO: Add settings documentation
## Future Improvements

View File

@@ -152,7 +152,7 @@ FancyZones is divided into several projects:
## Development Environment Setup
### Prerequisites
- Visual Studio 2022 or 2026: Required for building and debugging
- Visual Studio 2022: Required for building and debugging
- Windows 10 SDK: Ensure the latest version is installed
- PowerToys Repository: Clone from GitHub
@@ -183,7 +183,7 @@ FancyZones is divided into several projects:
## Debugging
### Setup for Debugging
1. In Visual Studio 2022 or 2026, set FancyZonesEditor as the startup project
1. In Visual Studio 2022, set FancyZonesEditor as the startup project
2. Set breakpoints in the code where needed
3. Click Run to start debugging

View File

@@ -9,14 +9,12 @@
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-Shortcut+Guide%22+)
## Overview
Shortcut Guide is a PowerToy that displays an overlay of available keyboard shortcuts when a user-set keyboard shortcut is pressed. It helps users discover and remember keyboard shortcuts for Windows and apps.
> [!NOTE]
> The spec for the manifest files is in development and will be linked here once available.
Shortcut Guide is a PowerToy that displays an overlay of available keyboard shortcuts when the Windows key is pressed and held. It provides a visual reference for Windows key combinations, helping users discover and utilize built-in Windows shortcuts.
## Usage
- Press the user-defined hotkey to display the overlay
- Press the hotkey again or press ESC to dismiss the overlay
- Press and hold the Windows key to display the overlay of available shortcuts
- Press the hotkey again to dismiss the overlay
- The overlay displays Windows shortcuts with their corresponding actions
## Build and Debug Instructions
@@ -27,83 +25,67 @@ Shortcut Guide is a PowerToy that displays an overlay of available keyboard shor
4. The executable is named PowerToys.ShortcutGuide.exe
### Debug
1. Right-click the ShortcutGuide.Ui project and select 'Set as Startup Project'
1. Right-click the ShortcutGuide project and select 'Set as Startup Project'
2. Right-click the project again and select 'Debug'
> [!NOTE]
> When run in debug mode, the window behaves differently than in release mode. It will not automatically close when loosing focus, it will be displayed on top of all other windows, and it is not hidden from the taskbar.
## Code Structure
## Project Structure
![Diagram](../images/shortcutguide/diagram.png)
The Shortcut Guide module consists of the following 4 projects:
### Core Files
### [`ShortcutGuide.Ui`](/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuide.Ui.csproj
#### [`dllmain.cpp`](/src/modules/shortcut_guide/dllmain.cpp)
Contains DLL boilerplate code. Implements the PowertoyModuleIface, including enable/disable functionality and GPO policy handling. Captures hotkey events and starts the PowerToys.ShortcutGuide.exe process to display the shortcut guide window.
This is the main UI project for the Shortcut Guide module. Upon startup it does the following tasks:
#### [`shortcut_guide.cpp`](/src/modules/shortcut_guide/shortcut_guide.cpp)
Contains the module interface code. It initializes the settings values and the keyboard event listener. Defines the OverlayWindow class, which manages the overall logic and event handling for the PowerToys Shortcut Guide.
1. Copies the built-in manifest files to the users manifest directory (overwriting existing files).
2. Generate the `index.yml` manifest file.
3. Populate the PowerToys shortcut manifest with the user-defined shortcuts.
4. Starts the UI.
#### [`overlay_window.cpp`](/src/modules/shortcut_guide/overlay_window.cpp)
Contains the code for loading the SVGs, creating and rendering of the overlay window. Manages and displays overlay windows with SVG graphics through two main classes:
- D2DOverlaySVG: Handles loading, resizing, and manipulation of SVG graphics
- D2DOverlayWindow: Manages the display and behavior of the overlay window
### [`ShortcutGuide.CPPProject`](/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj)
#### [`keyboard_state.cpp`](/src/modules/shortcut_guide/keyboard_state.cpp)
Contains helper methods for checking the current state of the keyboard.
This project exports certain functions to be used by the Shortcut Guide module, that were not able to be implemented in C#.
#### [`target_state.cpp`](/src/modules/shortcut_guide/target_state.cpp)
State machine that handles the keyboard events. It's responsible for deciding when to show the overlay, when to suppress the Start menu (if the overlay is displayed long enough), etc. Handles state transitions and synchronization to ensure the overlay is shown or hidden appropriately based on user interactions.
#### [`excluded_app.cpp`](/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/excluded_app.cpp)
#### [`trace.cpp`](/src/modules/shortcut_guide/trace.cpp)
Contains code for telemetry.
This file contains one function with the following signature:
### Supporting Files
```cpp
__declspec(dllexport) bool IsCurrentWindowExcludedFromShortcutGuide()
```
#### [`animation.cpp`](/src/modules/shortcut_guide/animation.cpp)
Handles the timing and interpolation of animations. Calculates the current value of an animation based on elapsed time and a specified easing function.
This function checks if the current window is excluded from the Shortcut Guide overlay. It returns `true` if the current window is excluded otherwise it returns `false`.
#### [`d2d_svg.cpp`](/src/modules/shortcut_guide/d2d_svg.cpp)
Provides functionality for loading, resizing, recoloring, rendering, and manipulating SVG images using Direct2D.
#### [`tasklist_positions.cpp`](/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/tasklist_positions.cpp)
#### [`d2d_text.cpp`](/src/modules/shortcut_guide/d2d_text.cpp)
Handles creation, resizing, alignment, and rendering of text using Direct2D and DirectWrite.
This file contains helper functions to retrieve the positions of the taskbar buttons. It exports the following function:
#### [`d2d_window.cpp`](/src/modules/shortcut_guide/d2d_window.cpp)
Manages a window using Direct2D and Direct3D for rendering. Handles window creation, resizing, rendering, and destruction.
```cpp
__declspec(dllexport) TasklistButton* get_buttons(HMONITOR monitor, int* size)
```
#### [`native_event_waiter.cpp`](/src/modules/shortcut_guide/native_event_waiter.cpp)
Waits for a named event and executes a specified action when the event is triggered. Uses a separate thread to handle event waiting and action execution.
This function retrieves the positions of the taskbar buttons for a given monitor. It returns an array of `TasklistButton` structures (max 10), which contain the position and size of each button.
#### [`tasklist_positions.cpp`](/src/modules/shortcut_guide/tasklist_positions.cpp)
Handles retrieving and updating the positions and information of taskbar buttons in Windows.
`monitor` must be the monitor handle of the monitor containing the taskbar instance of which the buttons should be retrieved.
`size` will contain the resulting array size.
It determines the positions through Windows `FindWindowEx` function.
For the primary taskbar it searches for:
* A window called "Shell_TrayWnd"
* that contains a window called "ReBarWindow32"
* that contains a window called "MSTaskSwWClass"
* that contains a window called "MSTaskListWClass"
For any secondary taskbar it searches for:
* A window called "Shell_SecondaryTrayWnd"
* that contains a window called "WorkerW"
* that contains a window called "MSTaskListWClass"
It then enumerates all the button elements inside "MSTaskListWClass" while skipping such with a same name (which implies the user does not use combining taskbar buttons)
### [`ShortcutGuide.IndexYmlGenerator`](/src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/)
This application generates the `index.yml` manifest file.
It is a separate project so that its code can be easier ported to WinGet in the future.
### [`ShortcutGuideModuleInterface`](/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.vcxproj)
The module interface that handles opening and closing the user interface.
#### [`main.cpp`](/src/modules/shortcut_guide/main.cpp)
The entry point for the PowerToys Shortcut Guide application. Handles initialization, ensures single instance execution, manages parent process termination, creates and displays the overlay window, and runs the main event loop.
## Features and Limitations
- Currently the displayed shortcuts (Except the ones from PowerToys) are not localized.
- The overlay displays Windows shortcuts (Windows key combinations)
- The module supports localization, but only for the Windows controls on the left side of the overlay
- It's currently rated as a P3 (lower priority) module
## Future Development
- Implementing with WinGet to get new shortcut manifest files
- Adding localization support for the built-in manifest files
A community-contributed version 2 is in development that will support:
- Application-specific shortcuts based on the active application
- Additional shortcuts beyond Windows key combinations
- PowerToys shortcuts

View File

@@ -79,7 +79,7 @@ Once you've discussed your proposed feature/fix/etc. with a team member, and an
### Prerequisites for Compiling PowerToys
1. Windows 10 April 2018 Update (version 1803) or newer
1. Visual Studio Community/Professional/Enterprise 2022 17.4 or newer, or Visual Studio 2026
1. Visual Studio Community/Professional/Enterprise 2022 17.4 or newer
1. A local clone of the PowerToys repository
1. Enable long paths in Windows (see [Enable Long Paths](https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation#enabling-long-paths-in-windows-10-version-1607-and-later) for details)

View File

@@ -1,318 +0,0 @@
# WinGet Manifest Keyboard Shortcuts schema
## 1 What this spec is about
This spec provides an extension to the existing [WinGet manifest schema](https://github.com/microsoft/winget-pkgs/blob/master/doc/manifest/README.md) in form of an additional yaml file, that describes keyboard shortcuts the application provides.
These yaml files are saved on a per-user base and so called manifest interpreters can then display these manifests in a human-friendly version.
### 1.1 What this spec is not about
This spec does not provide a way to back up or save user-defined keyboard shortcuts.
## 2 Save location of manifests
### 2.1 WinGet
These files are saved online along with the other manifest files in the [WinGet Package repository](https://github.com/microsoft/winget-pkgs).
### 2.2 Locally
All manifests and one index file are saved locally under `%LocalAppData%/Microsoft/WinGet/KeyboardShortcuts`. All apps are allowed to add their manifest files there. In addition Package Managers (like WinGet) and manifest interpreters (like PowerToys Shortcut Guide) can control and add other manifests themselves.
#### 2.2.1 Downloading manifests
When WinGet or other package managers download a package, they should also download the corresponding keyboard shortcuts manifest file and save it in the local directory, given such a file exists in the WinGet repository.
The downloader is also responsible for updating the local `index.yaml` file, which contains all the information about the different manifest files that are saved in the same directory.
#### 2.2.2 Updating manifests
When a manifest interpreter starts, it should download the latest version of the manifests from the WinGet repository and save them in the local directory. If a manifest interpreter is not able to download the manifests or they do not exist, it should use the locally saved manifests.
The updater is also responsible for updating the local `index.yaml` file, which contains all the information about the different manifest files that are saved in the same directory.
> Note: WinGet must provide a way to update the keyboard shortcuts manifests given a package id.
### 2.3 File names
The file name of a keyboard shortcuts file is the WinGet package identifier, plus the locale of the strings of the file and at last the `.KBSC.yaml` file extension.
For example the package "test.bar" saves its manifest with `en-US` strings in `test.bar.en-US.KBSC.yaml`.
#### 2.3.1 No winget package available
If an application has no corresponding WinGet package its name starts with a plus (`+`) symbol.
### 2.4 Reserved namespaces
Every name starting with `+WindowsNT` is reserved for the Windows OS and its components.
## 3 File syntax
All relevant files are written in [YAML](https://yaml.org/spec).
> Note: A JSON schema will be provided as soon as the spec reaches a further step
### 3.1 Manifest Schema vNext Keyboard Shortcuts File
```
PackageName: # The package unique identifier
WindowFilter: # The filter of window processes to which the shortcuts apply to
BackgroundProcess: # Optionally allows applying WindowFilter to background processes
Shortcuts: # List of sections with keyboard shortcuts
- SectionName: # Name of the category of shortcuts
Properties: # List of shortcuts in the category
- Name: # Name of the shortcut
Description: # Optional description of the shortcut
AdditionalInfo: # Optional additional information about the shortcut
Recommended: # Optionally determines if the shortcut is displayed in a designated recommended area
Shortcut: # An array of shortcuts that need to be pressed
- Win: # Determines if the Windows Key is part of the shortcut
Ctrl: # Determines if the Ctrl Key is part of the shortcut
Shift: # Determines if the Shift Key is part of the shortcut
Alt: # Determines if the Alt Key is part of the shortcut
Keys: # Array of keys that need to be pressed
```
Per Application/Package one or more Keyboard manifests can be declared. Every manifest must have a different locale and the same `PackageName`, `WindowFilter` and `BackgroundProcess` fields.
<details>
<summary><b>PackageName</b> - The package unique identifier</summary>
Package identifier (see 2.1 for more information on the package identifier).
</details>
<details>
<summary><b>WindowFilter</b> - The filter of window processes to which the shortcuts apply to</summary>
This field declares for which process name the shortcuts should be showed (To rephrase: For which processes the shortcut will have an effect if pressed). You can use an asterisk to leave out a certain part. For example `*.PowerToys.*.exe` targets all PowerToys processes and `*` apply to any process.
</details>
<details>
<summary><b>BackgroundProcess</b> - Optionally allows applying WindowFilter to background processes.</summary>
**Optional field**
Defaults to `False`. Determines if WindowFilter should apply to background processes as well (Rephrased: When the process is running, the shortcuts will apply).
</details>
<details>
<summary><b>Shortcuts</b> - List of sections with keyboard shortcuts</summary>
List of different section (also called categories) of shortcuts.
</details>
<details>
<summary><b>SectionName</b> - Name of the category of shortcuts</summary>
Name of the section of shortcuts.
**Special sections**:
Special sections start with an identifier enclosed between `<` and `>`. This declares the category as a special display. If the interpreter of the manifest file can't understand the content this section should be left out.
</details>
<details>
<summary><b>Properties</b> - List of shortcuts in the category</summary>
</details>
<details>
<summary><b>Name</b> - Name of the shortcut</summary>
Name of the shortcut. This is the name that will be displayed in the interpreter.
</details>
<details>
<summary><b>Description</b> - Optional description of the shortcut</summary>
Optional description of the shortcut. This is the description that will be displayed by the interpreter.
</details>
<details>
<summary><b>AdditionalInfo</b> - Optional additional information about the shortcut</summary>
Array of additional information about the shortcut. This is the additional information that will be displayed by the interpreter and are not part of this manifest.
**Example**:
For example, if the shortcut is only available on a certain Windows version, this information could be added here.
```yaml
AdditionalInfo:
- MinWindowsVersion: "10.0.19041.0"
```
</details>
<details>
<summary><b>Shortcut</b> - An array of shortcuts that need to be pressed</summary>
An array of shortcuts that need to be pressed. This allows defining sequential shortcuts that need to be pressed in order to trigger the action.
</details>
<details>
<summary><b>Win</b> - Determines if the Windows Key is part of the shortcut</summary>
Refers to the left Windows Key on the keyboard.
</details>
<details>
<summary><b>Ctrl</b> - Determines if the Ctrl Key is part of the shortcut</summary>
Refers to the left Ctrl Key on the keyboard.
</details>
<details>
<summary><b>Shift</b> - Determines if the Shift Key is part of the shortcut</summary>
Refers to the left Shift Key on the keyboard.
</details>
<details>
<summary><b>Alt</b> - Determines if the Alt Key is part of the shortcut</summary>
Refers to the left Alt Key on the keyboard.
</details>
<details>
<summary><b>Recommended</b> - Optionally determines if the shortcut is displayed in a designated recommended area</summary>
**Optional field**
Defaults to `False`. Determines if the shortcut should be displayed in a designated recommended area. This is a visual hint for the user that this shortcut is important.
</details>
<details>
<summary><b>Keys</b> - Array of keys that need to be pressed</summary>
A string array of all the keys that need to be pressed. If a number is supplied, it should be read as a [KeyCode](https://learn.microsoft.com/windows/win32/inputdev/virtual-key-codes) and displayed accordingly (based on the Keyboard Layout of the user).
**Special keys**:
Special keys are enclosed between `<` and `>` and correspond to a key that should be displayed in a certain way. If the interpreter of the manifest file can't understand the content, the brackets should be left out.
|Name|Description|
|----|-----------|
|`<Office>`| Corresponds to the Office key on some Windows keyboards |
|`<Copilot>`| Corresponds to the Copilot key on some Windows keyboards |
|`<Left>`| Corresponds to the left arrow key |
|`<Right>`| Corresponds to the right arrow key |
|`<Up>`| Corresponds to the up arrow key |
|`<Down>`| Corresponds to the down arrow key |
|`<Enter>`| Corresponds to the Enter key |
|`<Space>`| Corresponds to the Space key |
|`<Tab>`| Corresponds to the Tab key |
|`<Backspace>`| Corresponds to the Backspace key |
|`<Delete>`| Corresponds to the Delete key |
|`<Insert>`| Corresponds to the Insert key |
|`<Home>`| Corresponds to the Home key |
|`<End>`| Corresponds to the End key |
|`<PrtScr>`| Corresponds to the Print Screen key |
|`<Pause>`| Corresponds to the pause key |
|`<PageUp>`| Corresponds to the Page Up key |
|`<PageDown>`| Corresponds to the Page Down key |
|`<Escape>`| Corresponds to the Escape key |
|`<Arrow>`| Corresponds to either the left, right, up or down arrow key |
|`<ArrowLR>`| Corresponds to either the left or right arrow key |
|`<ArrowUD>`| Corresponds to either the up or down arrow key |
|`<Underlined letter>`| Corresponds to any letter that is _underlined_ in the UI |
</details>
#### 3.2.2 Example
```yaml
PackageName: Microsoft.PowerToys
WindowFilter: "*"
BackgroundProcess: True
Shortcuts:
- SectionName: General
Properties:
- Name: Advanced Paste
Shortcut:
- Win: True
Ctrl: False
Alt: False
Shift: False
Keys:
- 86
Description: Open Advanced Paste window
- Name: Advanced Paste
Shortcut:
- Win: True
Ctrl: True
Alt: True
Shift: False
Keys:
- 86
Description: Paste as plain text directly
```
### 3.2 `index.yaml` file
The `index.yaml` file is a file that contains all the information about the different manifest files that are saved in the same directory. This file is only available locally and is not saved in the WinGet repository as it is specific to the user.
```yaml
DefaultShellName: # The package identifier of the default shell used in Windows
Index: # List of all manifest files
- WindowFilter: # The filter of window processes to which the shortcuts apply to
BackgroundProcess: # Optionally allows applying WindowFilter to background processes
Apps: # List of all manifest files for the filter
```
<details>
<summary><b>DefaultShellName</b> - The package identifier of the default shell used in Windows</summary>
This declares the package identifier of the default shell used in Windows. Most commonly it is `+WindowsNT.Shell`. Although not enforced, only the shell declared in the registry key `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell` should be used here.
</details>
<details>
<summary><b>Index</b> - List of all manifest files</summary>
</details>
<details>
<summary><b>WindowFilter</b> - The filter of window processes to which the shortcuts apply to</summary>
See the `WindowFilter` field in the manifest file for more information.
</details>
<details>
<summary><b>BackgroundProcess</b> - Optionally allows applying WindowFilter to background processes</summary>
**Optional field**
See the `BackgroundProcess` field in the manifest file for more information.
</details>
<details>
<summary><b>Apps</b> - List of all the package identifiers applying for the filter</summary>
</details>
#### 3.2.1 Example
```yaml
DefaultShellName: "+WindowsNT.Shell"
Index:
- Filter: "*"
BackgroundProcess: True
Apps: ["+WindowsNT.Shell", "Microsoft.PowerToys"]
- Filter: "explorer.exe"
Apps: ["+WindowsNT.WindowsExplorer"]
- Filter: "taskmgr.exe"
Apps: ["+WindowsNT.TaskManager"]
- Filter: "msedge.exe"
Apps: ["+WindowsNT.Edge"]
```

View File

@@ -14,13 +14,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="..\..\deps\spdlog.props" />

View File

@@ -18,7 +18,6 @@
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Module_CmdPal" Value="" KeyPath="yes" />
</RegistryKey>
<RemoveFile Id="RemoveOldCmdPalMsix" Name="Microsoft.CmdPal.UI_*.msix" On="install" />
<?if $(sys.BUILDARCH) = x64 ?>
<File Id="Microsoft.CmdPal.UI___var.CmdPalVersion_._x64.msix" Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_x64.msix" />
<?else?>

View File

@@ -1,5 +1,5 @@
<Project>
<Import Project="..\..\Directory.Build.props" />
<Import Project="..\..\src\Version.props" Condition="Exists('..\..\src\Version.props')" />
<PropertyGroup>
<!-- Set BaseIntermediateOutputPath for each project to avoid conflicts -->
<BaseIntermediateOutputPath Condition="'$(MSBuildProjectName)' == 'PowerToysInstallerVNext'">obj\Installer\</BaseIntermediateOutputPath>

View File

@@ -2,25 +2,26 @@
<?include $(sys.CURRENTDIR)\Common.wxi?>
<?define ShortcutGuideAssetsFiles=?>
<?define ShortcutGuideAssetsFilesPath=$(var.BinDir)WinUI3Apps\Assets\ShortcutGuide\?>
<?define ShortcutGuideSvgFiles=?>
<?define ShortcutGuideSvgFilesPath=$(var.BinDir)\Assets\ShortcutGuide\?>
<Fragment>
<DirectoryRef Id="WinUI3AppsAssetsFolder">
<Directory Id="ShortcutGuideAssetsFolder" Name="ShortcutGuide" />
<!-- Shortcut guide files -->
<DirectoryRef Id="BaseApplicationsAssetsFolder">
<Directory Id="ShortcutGuideSvgsInstallFolder" Name="ShortcutGuide" />
</DirectoryRef>
<DirectoryRef Id="ShortcutGuideAssetsFolder" FileSource="$(var.ShortcutGuideAssetsFilesPath)">
<DirectoryRef Id="ShortcutGuideSvgsInstallFolder" FileSource="$(var.ShortcutGuideSvgFilesPath)">
<!-- Generated by generateFileComponents.ps1 -->
<!--ShortcutGuideAssetsFiles_Component_Def-->
<!--ShortcutGuideSvgFiles_Component_Def-->
</DirectoryRef>
<!-- Shortcut guide -->
<ComponentGroup Id="ShortcutGuideComponentGroup" >
<Component Id="RemoveShortcutGuideFolder" Guid="AD1ABC55-B593-4A60-A86A-BA8C0ED493A5" Directory="ShortcutGuideAssetsFolder" >
<ComponentGroup Id="ShortcutGuideComponentGroup">
<Component Id="RemoveShortcutGuideFolder" Guid="AD1ABC55-B593-4A60-A86A-BA8C0ED493A5" Directory="ShortcutGuideSvgsInstallFolder">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="RemoveShortcutGuideFolder" Value="" KeyPath="yes"/>
<RegistryValue Type="string" Name="RemoveShortcutGuideFolder" Value="" KeyPath="yes" />
</RegistryKey>
<RemoveFolder Id="RemoveFolderShortcutGuideAssetsInstallFolder" Directory="ShortcutGuideAssetsFolder" On="uninstall"/>
<RemoveFolder Id="RemoveFolderShortcutGuideSvgsInstallFolder" Directory="ShortcutGuideSvgsInstallFolder" On="uninstall" />
</Component>
</ComponentGroup>

View File

@@ -37,14 +37,14 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
@@ -68,10 +68,11 @@
<ClCompile Include="SilentFilesInUseBAFunctions.cpp" />
<ClCompile Include="bafunctions.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="precomp.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,6 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
#include "pch.h"
#include "precomp.h"
#include "BalBaseBAFunctions.h"
#include "BalBaseBAFunctionsProc.h"
@@ -18,6 +18,7 @@ public: // IBootstrapperApplication
BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION SYSTEM ACTIVE *** Running detect begin BA function. fCached=%d, registrationType=%d, cPackages=%u, fCancel=%d", fCached, registrationType, cPackages, *pfCancel);
LExit:
return hr;
}
@@ -36,6 +37,7 @@ public: // IBAFunctions
// BalExitOnFailure(hr, "Change this message to represent real error handling.");
//-------------------------------------------------------------------------------------------------
LExit:
return hr;
}
@@ -56,7 +58,7 @@ public: // IBAFunctions
__in DWORD cFiles,
__in_ecount_z(cFiles) LPCWSTR* rgwzFiles,
__in int nRecommendation,
__in BOOTSTRAPPER_FILES_IN_USE_TYPE /* source */,
__in BOOTSTRAPPER_FILES_IN_USE_TYPE source,
__inout int* pResult
)
{

View File

@@ -1,6 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
#include "pch.h"
#include "precomp.h"
static HINSTANCE vhInstance = NULL;

View File

@@ -299,8 +299,8 @@ Generate-FileComponents -fileListName "ValueGeneratorImagesCmpFiles" -wxsFilePat
## Plugins
#ShortcutGuide
Generate-FileList -fileDepsJson "" -fileListName ShortcutGuideAssetsFiles -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\ShortcutGuide\"
Generate-FileComponents -fileListName "ShortcutGuideAssetsFiles" -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -regroot $registryroot
Generate-FileList -fileDepsJson "" -fileListName ShortcutGuideSvgFiles -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\ShortcutGuide\"
Generate-FileComponents -fileListName "ShortcutGuideSvgFiles" -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs
#Settings
Generate-FileList -fileDepsJson "" -fileListName SettingsV2AssetsFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\"

View File

@@ -12,7 +12,7 @@
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="..\..\deps\expected.props" />
<PropertyGroup>

View File

@@ -5,6 +5,6 @@
As a temporary workaround, create a .NET 8 project and use file links
to include the code that needs testing. -->
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.26100.0</TargetFramework>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -55,26 +55,26 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Utility</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Utility</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>Utility</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>Utility</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>

View File

@@ -12,7 +12,7 @@
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="..\..\deps\expected.props" />
<PropertyGroup>

View File

@@ -10,7 +10,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">

View File

@@ -40,7 +40,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>

View File

@@ -3,27 +3,9 @@
#include <iomanip>
#include <iostream>
#include <sstream>
#include <cmath>
#include <limits>
namespace ExprtkCalculator::internal
{
static double factorial(const double n)
{
// Only allow non-negative integers
if (n < 0.0 || std::floor(n) != n)
{
return std::numeric_limits<double>::quiet_NaN();
}
return std::tgamma(n + 1.0);
}
static double sign(const double n)
{
if (n > 0.0) return 1.0;
if (n < 0.0) return -1.0;
return 0.0;
}
std::wstring ToWStringFullPrecision(double value)
{
@@ -43,9 +25,6 @@ namespace ExprtkCalculator::internal
symbol_table.add_constant(name, value);
}
symbol_table.add_function("factorial", factorial);
symbol_table.add_function("sign", sign);
exprtk::expression<double> expression;
expression.register_symbol_table(symbol_table);

View File

@@ -1,6 +1,5 @@
<?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')" />
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{CABA8DFB-823B-4BF2-93AC-3F31984150D9}</ProjectGuid>
@@ -11,7 +10,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
@@ -40,18 +39,5 @@
<ClCompile Include="monitors.cpp" />
<ClCompile Include="dpi_aware.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<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>

View File

@@ -19,7 +19,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<GenerateManifest>false</GenerateManifest>
<DesktopCompatible>true</DesktopCompatible>

View File

@@ -12,7 +12,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">

View File

@@ -11,7 +11,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">

View File

@@ -11,7 +11,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">

View File

@@ -16,50 +16,13 @@ DWORD WINAPI _checkTheme(LPVOID lpParam)
void ThemeListener::AddChangedHandler(THEME_HANDLE handle)
{
std::lock_guard<std::mutex> lock(handlesMutex);
handles.push_back(handle);
}
void ThemeListener::DelChangedHandler(THEME_HANDLE handle)
{
std::lock_guard<std::mutex> lock(handlesMutex);
auto it = std::find(handles.begin(), handles.end(), handle);
if (it != handles.end())
{
handles.erase(it);
}
}
void ThemeListener::AddAppThemeChangedHandler(THEME_HANDLE handle)
{
std::lock_guard<std::mutex> lock(handlesMutex);
appThemeHandles.push_back(handle);
}
void ThemeListener::DelAppThemeChangedHandler(THEME_HANDLE handle)
{
std::lock_guard<std::mutex> lock(handlesMutex);
auto it = std::find(appThemeHandles.begin(), appThemeHandles.end(), handle);
if (it != appThemeHandles.end())
{
appThemeHandles.erase(it);
}
}
void ThemeListener::AddSystemThemeChangedHandler(THEME_HANDLE handle)
{
std::lock_guard<std::mutex> lock(handlesMutex);
systemThemeHandles.push_back(handle);
}
void ThemeListener::DelSystemThemeChangedHandler(THEME_HANDLE handle)
{
std::lock_guard<std::mutex> lock(handlesMutex);
auto it = std::find(systemThemeHandles.begin(), systemThemeHandles.end(), handle);
if (it != systemThemeHandles.end())
{
systemThemeHandles.erase(it);
}
handles.erase(it);
}
void ThemeListener::CheckTheme()
@@ -85,51 +48,13 @@ void ThemeListener::CheckTheme()
WaitForSingleObject(hEvent, INFINITE);
auto _appTheme = ThemeHelpers::GetAppTheme();
auto _systemTheme = ThemeHelpers::GetSystemTheme();
bool appThemeChanged = (AppTheme != _appTheme);
bool systemThemeChanged = (SystemTheme != _systemTheme);
if (appThemeChanged || systemThemeChanged)
auto _theme = ThemeHelpers::GetAppTheme();
if (AppTheme != _theme)
{
AppTheme = _appTheme;
SystemTheme = _systemTheme;
// Copy handlers under lock, then invoke outside lock to avoid deadlock
std::vector<THEME_HANDLE> handlesCopy;
std::vector<THEME_HANDLE> appThemeHandlesCopy;
std::vector<THEME_HANDLE> systemThemeHandlesCopy;
AppTheme = _theme;
for (int i = 0; i < handles.size(); i++)
{
std::lock_guard<std::mutex> lock(handlesMutex);
handlesCopy = handles;
if (appThemeChanged)
{
appThemeHandlesCopy = appThemeHandles;
}
if (systemThemeChanged)
{
systemThemeHandlesCopy = systemThemeHandles;
}
}
// Call generic handlers (backward compatible)
for (const auto& handler : handlesCopy)
{
handler();
}
// Call app theme specific handlers
for (const auto& handler : appThemeHandlesCopy)
{
handler();
}
// Call system theme specific handlers
for (const auto& handler : systemThemeHandlesCopy)
{
handler();
handles[i]();
}
}
}

View File

@@ -3,7 +3,6 @@
#include <windows.h>
#include <iostream>
#include <vector>
#include <mutex>
typedef void (*THEME_HANDLE)();
DWORD WINAPI _checkTheme(LPVOID lpParam);
@@ -15,7 +14,6 @@ public:
ThemeListener()
{
AppTheme = ThemeHelpers::GetAppTheme();
SystemTheme = ThemeHelpers::GetSystemTheme();
dwThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_checkTheme, this, 0, &dwThreadId);
}
~ThemeListener()
@@ -25,20 +23,12 @@ public:
}
Theme AppTheme;
Theme SystemTheme;
void ThemeListener::AddChangedHandler(THEME_HANDLE handle);
void ThemeListener::DelChangedHandler(THEME_HANDLE handle);
void ThemeListener::AddAppThemeChangedHandler(THEME_HANDLE handle);
void ThemeListener::DelAppThemeChangedHandler(THEME_HANDLE handle);
void ThemeListener::AddSystemThemeChangedHandler(THEME_HANDLE handle);
void ThemeListener::DelSystemThemeChangedHandler(THEME_HANDLE handle);
void CheckTheme();
private:
HANDLE dwThreadHandle;
DWORD dwThreadId;
std::vector<THEME_HANDLE> handles;
std::vector<THEME_HANDLE> appThemeHandles;
std::vector<THEME_HANDLE> systemThemeHandles;
mutable std::mutex handlesMutex;
};

View File

@@ -13,7 +13,7 @@
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v143</PlatformToolset>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\tests\UnitTestsCommonLib\</OutDir>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

View File

@@ -41,7 +41,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>

View File

@@ -72,10 +72,6 @@ namespace CommonSharedConstants
const wchar_t ALWAYS_ON_TOP_TERMINATE_EVENT[] = L"Local\\AlwaysOnTopTerminateEvent-cfdf1eae-791f-4953-8021-2f18f3837eae";
const wchar_t ALWAYS_ON_TOP_INCREASE_OPACITY_EVENT[] = L"Local\\AlwaysOnTopIncreaseOpacityEvent-a1b2c3d4-e5f6-7890-abcd-ef1234567890";
const wchar_t ALWAYS_ON_TOP_DECREASE_OPACITY_EVENT[] = L"Local\\AlwaysOnTopDecreaseOpacityEvent-b2c3d4e5-f6a7-8901-bcde-f12345678901";
// Path to the event used by PowerAccent
const wchar_t POWERACCENT_EXIT_EVENT[] = L"Local\\PowerToysPowerAccentExitEvent-53e93389-d19a-4fbb-9b36-1981c8965e17";

View File

@@ -36,7 +36,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<OutDir>..\..\..\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

View File

@@ -18,7 +18,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>

View File

@@ -10,7 +10,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">

View File

@@ -11,7 +11,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">

View File

@@ -12,7 +12,7 @@
<Import Project="..\..\..\deps\expected.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="..\..\..\deps\spdlog.props" />

View File

@@ -2,7 +2,6 @@
#include <Windows.h>
#include <algorithm>
#include <appxpackaging.h>
#include <exception>
#include <filesystem>
@@ -338,30 +337,6 @@ namespace package
}
}
}
// Sort by package version in descending order (newest first)
std::sort(matchedFiles.begin(), matchedFiles.end(), [](const std::wstring& a, const std::wstring& b) {
std::wstring nameA, nameB;
PACKAGE_VERSION versionA{}, versionB{};
bool gotA = GetPackageNameAndVersionFromAppx(a, nameA, versionA);
bool gotB = GetPackageNameAndVersionFromAppx(b, nameB, versionB);
// Files that failed to parse go to the end
if (!gotA)
return false;
if (!gotB)
return true;
// Compare versions: Major, Minor, Build, Revision (descending)
if (versionA.Major != versionB.Major)
return versionA.Major > versionB.Major;
if (versionA.Minor != versionB.Minor)
return versionA.Minor > versionB.Minor;
if (versionA.Build != versionB.Build)
return versionA.Build > versionB.Build;
return versionA.Revision > versionB.Revision;
});
}
catch (const std::exception& ex)
{

View File

@@ -47,7 +47,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft Corporation.
Licensed under the MIT License. -->
<policyDefinitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.19" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<policyDefinitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.18" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<policyNamespaces>
<target prefix="powertoys" namespace="Microsoft.Policies.PowerToys" />
</policyNamespaces>
<resources minRequiredRevision="1.19"/><!-- Last changed with PowerToys v0.97.0 -->
<resources minRequiredRevision="1.18"/><!-- Last changed with PowerToys v0.96.0 -->
<supportedOn>
<definitions>
<definition name="SUPPORTED_POWERTOYS_0_64_0" displayName="$(string.SUPPORTED_POWERTOYS_0_64_0)"/>
@@ -27,7 +27,6 @@
<definition name="SUPPORTED_POWERTOYS_0_89_0" displayName="$(string.SUPPORTED_POWERTOYS_0_89_0)"/>
<definition name="SUPPORTED_POWERTOYS_0_90_0" displayName="$(string.SUPPORTED_POWERTOYS_0_90_0)"/>
<definition name="SUPPORTED_POWERTOYS_0_96_0" displayName="$(string.SUPPORTED_POWERTOYS_0_96_0)"/>
<definition name="SUPPORTED_POWERTOYS_0_97_0" displayName="$(string.SUPPORTED_POWERTOYS_0_97_0)"/>
<definition name="SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1" displayName="$(string.SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1)"/>
</definitions>
</supportedOn>
@@ -339,16 +338,6 @@
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityCursorWrap" class="Both" displayName="$(string.ConfigureEnabledUtilityCursorWrap)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityCursorWrap">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_97_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityFindMyMouse" class="Both" displayName="$(string.ConfigureEnabledUtilityFindMyMouse)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityFindMyMouse">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_64_0" />

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft Corporation.
Licensed under the MIT License. -->
<policyDefinitionResources xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.19" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<policyDefinitionResources xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.18" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<displayName>PowerToys</displayName>
<description>PowerToys</description>
<resources>
@@ -34,7 +34,6 @@
<string id="SUPPORTED_POWERTOYS_0_89_0">PowerToys version 0.89.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_90_0">PowerToys version 0.90.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_96_0">PowerToys version 0.96.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_97_0">PowerToys version 0.97.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1">From PowerToys version 0.64.0 until PowerToys version 0.87.1</string>
<string id="ConfigureAllUtilityGlobalEnabledStateDescription">This policy configures the enabled state for all PowerToys utilities.
@@ -267,7 +266,6 @@ If you don't configure this policy, the user will be able to control the setting
<string id="ConfigureEnabledUtilityKeyboardManager">Keyboard Manager: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFindMyMouse">Find My Mouse: Configure enabled state</string>
<string id="ConfigureEnabledUtilityMouseHighlighter">Mouse Highlighter: Configure enabled state</string>
<string id="ConfigureEnabledUtilityCursorWrap">CursorWrap: Configure enabled state</string>
<string id="ConfigureEnabledUtilityMouseJump">Mouse Jump: Configure enabled state</string>
<string id="ConfigureEnabledUtilityMousePointerCrosshairs">Mouse Pointer Crosshairs: Configure enabled state</string>
<string id="ConfigureEnabledUtilityMouseWithoutBorders">Mouse Without Borders: Configure enabled state</string>

View File

@@ -36,7 +36,7 @@
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
<PlatformToolset>v143</PlatformToolset>
<OutDir>..\..\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

View File

@@ -15,7 +15,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">

View File

@@ -34,6 +34,10 @@
</ItemGroup>
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0'">v142</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '17.0'">v143</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '18.0'">v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>

View File

@@ -12,13 +12,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@@ -15,13 +15,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@@ -14,11 +14,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

View File

@@ -11,6 +11,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="..\..\..\..\..\deps\spdlog.props" />
<PropertyGroup Label="Configuration">
<PlatformToolset>v143</PlatformToolset>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@@ -20,13 +20,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@@ -16,13 +16,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@@ -12,13 +12,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

View File

@@ -32,7 +32,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>

View File

@@ -15,13 +15,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@@ -31,22 +31,26 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@@ -31,13 +31,13 @@
<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>

View File

@@ -12,13 +12,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@@ -13,13 +13,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
@@ -84,17 +84,14 @@
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="CursorWrapCore.h" />
<ClInclude Include="CursorWrapTests.h" />
<ClInclude Include="MonitorTopology.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="trace.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="CursorWrapCore.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="MonitorTopology.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
</ClCompile>

View File

@@ -1,268 +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.
#include "pch.h"
#include "CursorWrapCore.h"
#include "../../../common/logger/logger.h"
#include <sstream>
#include <iomanip>
#include <ctime>
CursorWrapCore::CursorWrapCore()
{
}
#ifdef _DEBUG
std::wstring CursorWrapCore::GenerateTopologyJSON() const
{
std::wostringstream json;
// Get current time
auto now = std::time(nullptr);
std::tm tm{};
localtime_s(&tm, &now);
wchar_t computerName[MAX_COMPUTERNAME_LENGTH + 1] = {0};
DWORD size = MAX_COMPUTERNAME_LENGTH + 1;
GetComputerNameW(computerName, &size);
wchar_t userName[256] = {0};
size = 256;
GetUserNameW(userName, &size);
json << L"{\n";
json << L" \"captured_at\": \"" << std::put_time(&tm, L"%Y-%m-%dT%H:%M:%S%z") << L"\",\n";
json << L" \"computer_name\": \"" << computerName << L"\",\n";
json << L" \"user_name\": \"" << userName << L"\",\n";
json << L" \"monitor_count\": " << m_monitors.size() << L",\n";
json << L" \"monitors\": [\n";
for (size_t i = 0; i < m_monitors.size(); ++i)
{
const auto& monitor = m_monitors[i];
// Get DPI for this monitor
UINT dpiX = 96, dpiY = 96;
POINT center = {
(monitor.rect.left + monitor.rect.right) / 2,
(monitor.rect.top + monitor.rect.bottom) / 2
};
HMONITOR hMon = MonitorFromPoint(center, MONITOR_DEFAULTTONEAREST);
if (hMon)
{
// Try GetDpiForMonitor (requires linking Shcore.lib)
using GetDpiForMonitorFunc = HRESULT (WINAPI *)(HMONITOR, int, UINT*, UINT*);
HMODULE shcore = LoadLibraryW(L"Shcore.dll");
if (shcore)
{
auto getDpi = reinterpret_cast<GetDpiForMonitorFunc>(GetProcAddress(shcore, "GetDpiForMonitor"));
if (getDpi)
{
getDpi(hMon, 0, &dpiX, &dpiY); // MDT_EFFECTIVE_DPI = 0
}
FreeLibrary(shcore);
}
}
int scalingPercent = static_cast<int>((dpiX / 96.0) * 100);
json << L" {\n";
json << L" \"left\": " << monitor.rect.left << L",\n";
json << L" \"top\": " << monitor.rect.top << L",\n";
json << L" \"right\": " << monitor.rect.right << L",\n";
json << L" \"bottom\": " << monitor.rect.bottom << L",\n";
json << L" \"width\": " << (monitor.rect.right - monitor.rect.left) << L",\n";
json << L" \"height\": " << (monitor.rect.bottom - monitor.rect.top) << L",\n";
json << L" \"dpi\": " << dpiX << L",\n";
json << L" \"scaling_percent\": " << scalingPercent << L",\n";
json << L" \"primary\": " << (monitor.isPrimary ? L"true" : L"false") << L",\n";
json << L" \"monitor_id\": " << monitor.monitorId << L"\n";
json << L" }";
if (i < m_monitors.size() - 1)
{
json << L",";
}
json << L"\n";
}
json << L" ]\n";
json << L"}";
return json.str();
}
#endif
void CursorWrapCore::UpdateMonitorInfo()
{
size_t previousMonitorCount = m_monitors.size();
Logger::info(L"======= UPDATE MONITOR INFO START =======");
Logger::info(L"Previous monitor count: {}", previousMonitorCount);
m_monitors.clear();
EnumDisplayMonitors(nullptr, nullptr, [](HMONITOR hMonitor, HDC, LPRECT, LPARAM lParam) -> BOOL {
auto* self = reinterpret_cast<CursorWrapCore*>(lParam);
MONITORINFO mi{};
mi.cbSize = sizeof(MONITORINFO);
if (GetMonitorInfo(hMonitor, &mi))
{
MonitorInfo info{};
info.hMonitor = hMonitor; // Store handle for direct comparison later
info.rect = mi.rcMonitor;
info.isPrimary = (mi.dwFlags & MONITORINFOF_PRIMARY) != 0;
info.monitorId = static_cast<int>(self->m_monitors.size());
self->m_monitors.push_back(info);
Logger::info(L"Enumerated monitor {}: hMonitor={}, rect=({},{},{},{}), primary={}",
info.monitorId, reinterpret_cast<uintptr_t>(hMonitor),
mi.rcMonitor.left, mi.rcMonitor.top, mi.rcMonitor.right, mi.rcMonitor.bottom,
info.isPrimary ? L"yes" : L"no");
}
return TRUE;
}, reinterpret_cast<LPARAM>(this));
if (previousMonitorCount != m_monitors.size())
{
Logger::info(L"*** MONITOR CONFIGURATION CHANGED: {} -> {} monitors ***",
previousMonitorCount, m_monitors.size());
}
m_topology.Initialize(m_monitors);
// Log monitor configuration summary
Logger::info(L"Monitor configuration updated: {} monitor(s)", m_monitors.size());
for (size_t i = 0; i < m_monitors.size(); ++i)
{
const auto& m = m_monitors[i];
int width = m.rect.right - m.rect.left;
int height = m.rect.bottom - m.rect.top;
Logger::info(L" Monitor {}: {}x{} at ({}, {}){}",
i, width, height, m.rect.left, m.rect.top,
m.isPrimary ? L" [PRIMARY]" : L"");
}
Logger::info(L" Detected {} outer edges for cursor wrapping", m_topology.GetOuterEdges().size());
// Detect and log monitor gaps
auto gaps = m_topology.DetectMonitorGaps();
if (!gaps.empty())
{
Logger::warn(L"Monitor configuration has coordinate gaps that may prevent wrapping:");
for (const auto& gap : gaps)
{
Logger::warn(L" Gap between Monitor {} and Monitor {}: {}px horizontal gap, {}px vertical overlap",
gap.monitor1Index, gap.monitor2Index, gap.horizontalGap, gap.verticalOverlap);
}
Logger::warn(L" If monitors appear snapped in Display Settings but show gaps here:");
Logger::warn(L" 1. Try dragging monitors apart and snapping them back together");
Logger::warn(L" 2. Update your GPU drivers");
}
Logger::info(L"======= UPDATE MONITOR INFO END =======");
}
POINT CursorWrapCore::HandleMouseMove(const POINT& currentPos, bool disableWrapDuringDrag, int wrapMode)
{
// Check if wrapping should be disabled during drag
if (disableWrapDuringDrag && (GetAsyncKeyState(VK_LBUTTON) & 0x8000))
{
#ifdef _DEBUG
OutputDebugStringW(L"[CursorWrap] [DRAG] Left mouse button down - skipping wrap\n");
#endif
return currentPos;
}
// Convert int wrapMode to WrapMode enum
WrapMode mode = static_cast<WrapMode>(wrapMode);
#ifdef _DEBUG
{
std::wostringstream oss;
oss << L"[CursorWrap] [MOVE] Cursor at (" << currentPos.x << L", " << currentPos.y << L")";
// Get current monitor and identify which one
HMONITOR currentMonitor = MonitorFromPoint(currentPos, MONITOR_DEFAULTTONEAREST);
RECT monitorRect;
if (m_topology.GetMonitorRect(currentMonitor, monitorRect))
{
// Find monitor ID
int monitorId = -1;
for (const auto& monitor : m_monitors)
{
if (monitor.rect.left == monitorRect.left &&
monitor.rect.top == monitorRect.top &&
monitor.rect.right == monitorRect.right &&
monitor.rect.bottom == monitorRect.bottom)
{
monitorId = monitor.monitorId;
break;
}
}
oss << L" on Monitor " << monitorId << L" [" << monitorRect.left << L".." << monitorRect.right
<< L", " << monitorRect.top << L".." << monitorRect.bottom << L"]";
}
else
{
oss << L" (beyond monitor bounds)";
}
oss << L"\n";
OutputDebugStringW(oss.str().c_str());
}
#endif
// Get current monitor
HMONITOR currentMonitor = MonitorFromPoint(currentPos, MONITOR_DEFAULTTONEAREST);
// Check if cursor is on an outer edge (filtered by wrap mode)
EdgeType edgeType;
if (!m_topology.IsOnOuterEdge(currentMonitor, currentPos, edgeType, mode))
{
#ifdef _DEBUG
static bool lastWasNotOuter = false;
if (!lastWasNotOuter)
{
OutputDebugStringW(L"[CursorWrap] [MOVE] Not on outer edge - no wrapping\n");
lastWasNotOuter = true;
}
#endif
return currentPos; // Not on an outer edge
}
#ifdef _DEBUG
{
const wchar_t* edgeStr = L"Unknown";
switch (edgeType)
{
case EdgeType::Left: edgeStr = L"Left"; break;
case EdgeType::Right: edgeStr = L"Right"; break;
case EdgeType::Top: edgeStr = L"Top"; break;
case EdgeType::Bottom: edgeStr = L"Bottom"; break;
}
std::wostringstream oss;
oss << L"[CursorWrap] [EDGE] Detected outer " << edgeStr << L" edge at (" << currentPos.x << L", " << currentPos.y << L")\n";
OutputDebugStringW(oss.str().c_str());
}
#endif
// Calculate wrap destination
POINT newPos = m_topology.GetWrapDestination(currentMonitor, currentPos, edgeType);
#ifdef _DEBUG
if (newPos.x != currentPos.x || newPos.y != currentPos.y)
{
std::wostringstream oss;
oss << L"[CursorWrap] [WRAP] Position change: (" << currentPos.x << L", " << currentPos.y
<< L") -> (" << newPos.x << L", " << newPos.y << L")\n";
oss << L"[CursorWrap] [WRAP] Delta: (" << (newPos.x - currentPos.x) << L", " << (newPos.y - currentPos.y) << L")\n";
OutputDebugStringW(oss.str().c_str());
}
else
{
OutputDebugStringW(L"[CursorWrap] [WRAP] No position change (same-monitor wrap?)\n");
}
#endif
return newPos;
}

View File

@@ -1,33 +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.
#pragma once
#include <windows.h>
#include <vector>
#include <string>
#include "MonitorTopology.h"
// Core cursor wrapping engine
class CursorWrapCore
{
public:
CursorWrapCore();
void UpdateMonitorInfo();
// Handle mouse move with wrap mode filtering
// wrapMode: 0=Both, 1=VerticalOnly, 2=HorizontalOnly
POINT HandleMouseMove(const POINT& currentPos, bool disableWrapDuringDrag, int wrapMode);
const std::vector<MonitorInfo>& GetMonitors() const { return m_monitors; }
const MonitorTopology& GetTopology() const { return m_topology; }
private:
#ifdef _DEBUG
std::wstring GenerateTopologyJSON() const;
#endif
std::vector<MonitorInfo> m_monitors;
MonitorTopology m_topology;
};

View File

@@ -1,546 +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.
#include "pch.h"
#include "MonitorTopology.h"
#include "../../../common/logger/logger.h"
#include <algorithm>
#include <cmath>
void MonitorTopology::Initialize(const std::vector<MonitorInfo>& monitors)
{
Logger::info(L"======= TOPOLOGY INITIALIZATION START =======");
Logger::info(L"Initializing edge-based topology for {} monitors", monitors.size());
m_monitors = monitors;
m_outerEdges.clear();
m_edgeMap.clear();
if (monitors.empty())
{
Logger::warn(L"No monitors provided to Initialize");
return;
}
// Log monitor details
for (size_t i = 0; i < monitors.size(); ++i)
{
const auto& m = monitors[i];
Logger::info(L"Monitor {}: hMonitor={}, rect=({},{},{},{}), primary={}",
i, reinterpret_cast<uintptr_t>(m.hMonitor),
m.rect.left, m.rect.top, m.rect.right, m.rect.bottom,
m.isPrimary ? L"yes" : L"no");
}
BuildEdgeMap();
IdentifyOuterEdges();
Logger::info(L"Found {} outer edges", m_outerEdges.size());
for (const auto& edge : m_outerEdges)
{
const wchar_t* typeStr = L"Unknown";
switch (edge.type)
{
case EdgeType::Left: typeStr = L"Left"; break;
case EdgeType::Right: typeStr = L"Right"; break;
case EdgeType::Top: typeStr = L"Top"; break;
case EdgeType::Bottom: typeStr = L"Bottom"; break;
}
Logger::info(L"Outer edge: Monitor {} {} at position {}, range [{}, {}]",
edge.monitorIndex, typeStr, edge.position, edge.start, edge.end);
}
Logger::info(L"======= TOPOLOGY INITIALIZATION COMPLETE =======");
}
void MonitorTopology::BuildEdgeMap()
{
// Create edges for each monitor using monitor index (not HMONITOR)
// This is important because HMONITOR handles can change when monitors are
// added/removed dynamically, but indices remain stable within a single
// topology configuration
for (size_t idx = 0; idx < m_monitors.size(); ++idx)
{
const auto& monitor = m_monitors[idx];
int monitorIndex = static_cast<int>(idx);
// Left edge
MonitorEdge leftEdge;
leftEdge.monitorIndex = monitorIndex;
leftEdge.type = EdgeType::Left;
leftEdge.position = monitor.rect.left;
leftEdge.start = monitor.rect.top;
leftEdge.end = monitor.rect.bottom;
leftEdge.isOuter = true; // Will be updated in IdentifyOuterEdges
m_edgeMap[{monitorIndex, EdgeType::Left}] = leftEdge;
// Right edge
MonitorEdge rightEdge;
rightEdge.monitorIndex = monitorIndex;
rightEdge.type = EdgeType::Right;
rightEdge.position = monitor.rect.right - 1;
rightEdge.start = monitor.rect.top;
rightEdge.end = monitor.rect.bottom;
rightEdge.isOuter = true;
m_edgeMap[{monitorIndex, EdgeType::Right}] = rightEdge;
// Top edge
MonitorEdge topEdge;
topEdge.monitorIndex = monitorIndex;
topEdge.type = EdgeType::Top;
topEdge.position = monitor.rect.top;
topEdge.start = monitor.rect.left;
topEdge.end = monitor.rect.right;
topEdge.isOuter = true;
m_edgeMap[{monitorIndex, EdgeType::Top}] = topEdge;
// Bottom edge
MonitorEdge bottomEdge;
bottomEdge.monitorIndex = monitorIndex;
bottomEdge.type = EdgeType::Bottom;
bottomEdge.position = monitor.rect.bottom - 1;
bottomEdge.start = monitor.rect.left;
bottomEdge.end = monitor.rect.right;
bottomEdge.isOuter = true;
m_edgeMap[{monitorIndex, EdgeType::Bottom}] = bottomEdge;
}
}
void MonitorTopology::IdentifyOuterEdges()
{
const int tolerance = 50;
// Check each edge against all other edges to find adjacent ones
for (auto& [key1, edge1] : m_edgeMap)
{
for (const auto& [key2, edge2] : m_edgeMap)
{
if (edge1.monitorIndex == edge2.monitorIndex)
{
continue; // Same monitor
}
// Check if edges are adjacent
if (EdgesAreAdjacent(edge1, edge2, tolerance))
{
edge1.isOuter = false;
break; // This edge has an adjacent monitor
}
}
if (edge1.isOuter)
{
m_outerEdges.push_back(edge1);
}
}
}
bool MonitorTopology::EdgesAreAdjacent(const MonitorEdge& edge1, const MonitorEdge& edge2, int tolerance) const
{
// Edges must be opposite types to be adjacent
bool oppositeTypes = false;
if ((edge1.type == EdgeType::Left && edge2.type == EdgeType::Right) ||
(edge1.type == EdgeType::Right && edge2.type == EdgeType::Left) ||
(edge1.type == EdgeType::Top && edge2.type == EdgeType::Bottom) ||
(edge1.type == EdgeType::Bottom && edge2.type == EdgeType::Top))
{
oppositeTypes = true;
}
if (!oppositeTypes)
{
return false;
}
// Check if positions are within tolerance
if (abs(edge1.position - edge2.position) > tolerance)
{
return false;
}
// Check if perpendicular ranges overlap
int overlapStart = max(edge1.start, edge2.start);
int overlapEnd = min(edge1.end, edge2.end);
return overlapEnd > overlapStart + tolerance;
}
bool MonitorTopology::IsOnOuterEdge(HMONITOR monitor, const POINT& cursorPos, EdgeType& outEdgeType, WrapMode wrapMode) const
{
RECT monitorRect;
if (!GetMonitorRect(monitor, monitorRect))
{
Logger::warn(L"IsOnOuterEdge: GetMonitorRect failed for monitor handle {}", reinterpret_cast<uintptr_t>(monitor));
return false;
}
// Get monitor index for edge map lookup
int monitorIndex = GetMonitorIndex(monitor);
if (monitorIndex < 0)
{
Logger::warn(L"IsOnOuterEdge: Monitor index not found for handle {} at cursor ({}, {})",
reinterpret_cast<uintptr_t>(monitor), cursorPos.x, cursorPos.y);
return false; // Monitor not found in our list
}
// Check each edge type
const int edgeThreshold = 1;
// At corners, multiple edges may match - collect all candidates and try each
// to find one with a valid wrap destination
std::vector<EdgeType> candidateEdges;
// Left edge - only if mode allows horizontal wrapping
if ((wrapMode == WrapMode::Both || wrapMode == WrapMode::HorizontalOnly) &&
cursorPos.x <= monitorRect.left + edgeThreshold)
{
auto it = m_edgeMap.find({monitorIndex, EdgeType::Left});
if (it != m_edgeMap.end() && it->second.isOuter)
{
candidateEdges.push_back(EdgeType::Left);
}
}
// Right edge - only if mode allows horizontal wrapping
if ((wrapMode == WrapMode::Both || wrapMode == WrapMode::HorizontalOnly) &&
cursorPos.x >= monitorRect.right - 1 - edgeThreshold)
{
auto it = m_edgeMap.find({monitorIndex, EdgeType::Right});
if (it != m_edgeMap.end())
{
if (it->second.isOuter)
{
candidateEdges.push_back(EdgeType::Right);
}
// Debug: Log why right edge isn't outer
else
{
Logger::trace(L"IsOnOuterEdge: Monitor {} right edge is NOT outer (inner edge)", monitorIndex);
}
}
}
// Top edge - only if mode allows vertical wrapping
if ((wrapMode == WrapMode::Both || wrapMode == WrapMode::VerticalOnly) &&
cursorPos.y <= monitorRect.top + edgeThreshold)
{
auto it = m_edgeMap.find({monitorIndex, EdgeType::Top});
if (it != m_edgeMap.end() && it->second.isOuter)
{
candidateEdges.push_back(EdgeType::Top);
}
}
// Bottom edge - only if mode allows vertical wrapping
if ((wrapMode == WrapMode::Both || wrapMode == WrapMode::VerticalOnly) &&
cursorPos.y >= monitorRect.bottom - 1 - edgeThreshold)
{
auto it = m_edgeMap.find({monitorIndex, EdgeType::Bottom});
if (it != m_edgeMap.end() && it->second.isOuter)
{
candidateEdges.push_back(EdgeType::Bottom);
}
}
if (candidateEdges.empty())
{
return false;
}
// Try each candidate edge and return first with valid wrap destination
for (EdgeType candidate : candidateEdges)
{
MonitorEdge oppositeEdge = FindOppositeOuterEdge(candidate,
(candidate == EdgeType::Left || candidate == EdgeType::Right) ? cursorPos.y : cursorPos.x);
if (oppositeEdge.monitorIndex >= 0)
{
outEdgeType = candidate;
return true;
}
}
return false;
}
POINT MonitorTopology::GetWrapDestination(HMONITOR fromMonitor, const POINT& cursorPos, EdgeType edgeType) const
{
// Get monitor index for edge map lookup
int monitorIndex = GetMonitorIndex(fromMonitor);
if (monitorIndex < 0)
{
return cursorPos; // Monitor not found
}
auto it = m_edgeMap.find({monitorIndex, edgeType});
if (it == m_edgeMap.end())
{
return cursorPos; // Edge not found
}
const MonitorEdge& fromEdge = it->second;
// Calculate relative position on current edge (0.0 to 1.0)
double relativePos = GetRelativePosition(fromEdge,
(edgeType == EdgeType::Left || edgeType == EdgeType::Right) ? cursorPos.y : cursorPos.x);
// Find opposite outer edge
MonitorEdge oppositeEdge = FindOppositeOuterEdge(edgeType,
(edgeType == EdgeType::Left || edgeType == EdgeType::Right) ? cursorPos.y : cursorPos.x);
if (oppositeEdge.monitorIndex < 0)
{
// No opposite edge found, wrap within same monitor
RECT monitorRect;
if (GetMonitorRect(fromMonitor, monitorRect))
{
POINT result = cursorPos;
switch (edgeType)
{
case EdgeType::Left:
result.x = monitorRect.right - 2;
break;
case EdgeType::Right:
result.x = monitorRect.left + 1;
break;
case EdgeType::Top:
result.y = monitorRect.bottom - 2;
break;
case EdgeType::Bottom:
result.y = monitorRect.top + 1;
break;
}
return result;
}
return cursorPos;
}
// Calculate target position on opposite edge
POINT result;
if (edgeType == EdgeType::Left || edgeType == EdgeType::Right)
{
// Horizontal edge -> vertical movement
result.x = oppositeEdge.position;
result.y = GetAbsolutePosition(oppositeEdge, relativePos);
}
else
{
// Vertical edge -> horizontal movement
result.y = oppositeEdge.position;
result.x = GetAbsolutePosition(oppositeEdge, relativePos);
}
return result;
}
MonitorEdge MonitorTopology::FindOppositeOuterEdge(EdgeType fromEdge, int relativePosition) const
{
EdgeType targetType;
bool findMax; // true = find max position, false = find min position
switch (fromEdge)
{
case EdgeType::Left:
targetType = EdgeType::Right;
findMax = true;
break;
case EdgeType::Right:
targetType = EdgeType::Left;
findMax = false;
break;
case EdgeType::Top:
targetType = EdgeType::Bottom;
findMax = true;
break;
case EdgeType::Bottom:
targetType = EdgeType::Top;
findMax = false;
break;
default:
return { .monitorIndex = -1 }; // Invalid edge type
}
MonitorEdge result = { .monitorIndex = -1 }; // -1 indicates not found
int extremePosition = findMax ? INT_MIN : INT_MAX;
for (const auto& edge : m_outerEdges)
{
if (edge.type != targetType)
{
continue;
}
// Check if this edge overlaps with the relative position
if (relativePosition >= edge.start && relativePosition <= edge.end)
{
if ((findMax && edge.position > extremePosition) ||
(!findMax && edge.position < extremePosition))
{
extremePosition = edge.position;
result = edge;
}
}
}
return result;
}
double MonitorTopology::GetRelativePosition(const MonitorEdge& edge, int coordinate) const
{
if (edge.end == edge.start)
{
return 0.5; // Avoid division by zero
}
int clamped = max(edge.start, min(coordinate, edge.end));
// Use int64_t to avoid overflow warning C26451
int64_t numerator = static_cast<int64_t>(clamped) - static_cast<int64_t>(edge.start);
int64_t denominator = static_cast<int64_t>(edge.end) - static_cast<int64_t>(edge.start);
return static_cast<double>(numerator) / static_cast<double>(denominator);
}
int MonitorTopology::GetAbsolutePosition(const MonitorEdge& edge, double relativePosition) const
{
// Use int64_t to prevent arithmetic overflow during subtraction and multiplication
int64_t range = static_cast<int64_t>(edge.end) - static_cast<int64_t>(edge.start);
int64_t offset = static_cast<int64_t>(relativePosition * static_cast<double>(range));
// Clamp result to int range before returning
int64_t result = static_cast<int64_t>(edge.start) + offset;
return static_cast<int>(result);
}
std::vector<MonitorTopology::GapInfo> MonitorTopology::DetectMonitorGaps() const
{
std::vector<GapInfo> gaps;
const int gapThreshold = 50; // Same as ADJACENCY_TOLERANCE
// Check each pair of monitors
for (size_t i = 0; i < m_monitors.size(); ++i)
{
for (size_t j = i + 1; j < m_monitors.size(); ++j)
{
const auto& m1 = m_monitors[i];
const auto& m2 = m_monitors[j];
// Check vertical overlap
int vOverlapStart = max(m1.rect.top, m2.rect.top);
int vOverlapEnd = min(m1.rect.bottom, m2.rect.bottom);
int vOverlap = vOverlapEnd - vOverlapStart;
if (vOverlap <= 0)
{
continue; // No vertical overlap, skip
}
// Check horizontal gap
int hGap = min(abs(m1.rect.right - m2.rect.left), abs(m2.rect.right - m1.rect.left));
if (hGap > gapThreshold)
{
GapInfo gap;
gap.monitor1Index = static_cast<int>(i);
gap.monitor2Index = static_cast<int>(j);
gap.horizontalGap = hGap;
gap.verticalOverlap = vOverlap;
gaps.push_back(gap);
}
}
}
return gaps;
}
HMONITOR MonitorTopology::GetMonitorFromPoint(const POINT& pt) const
{
return MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
}
bool MonitorTopology::GetMonitorRect(HMONITOR monitor, RECT& rect) const
{
// First try direct HMONITOR comparison
for (const auto& monitorInfo : m_monitors)
{
if (monitorInfo.hMonitor == monitor)
{
rect = monitorInfo.rect;
return true;
}
}
// Fallback: If direct comparison fails, try matching by current monitor info
MONITORINFO mi{};
mi.cbSize = sizeof(MONITORINFO);
if (GetMonitorInfo(monitor, &mi))
{
for (const auto& monitorInfo : m_monitors)
{
if (monitorInfo.rect.left == mi.rcMonitor.left &&
monitorInfo.rect.top == mi.rcMonitor.top &&
monitorInfo.rect.right == mi.rcMonitor.right &&
monitorInfo.rect.bottom == mi.rcMonitor.bottom)
{
rect = monitorInfo.rect;
return true;
}
}
}
return false;
}
HMONITOR MonitorTopology::GetMonitorFromRect(const RECT& rect) const
{
return MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST);
}
int MonitorTopology::GetMonitorIndex(HMONITOR monitor) const
{
// First try direct HMONITOR comparison (fast and accurate)
for (size_t i = 0; i < m_monitors.size(); ++i)
{
if (m_monitors[i].hMonitor == monitor)
{
return static_cast<int>(i);
}
}
// Fallback: If direct comparison fails (e.g., handle changed after display reconfiguration),
// try matching by position. Get the monitor's current rect and find matching stored rect.
MONITORINFO mi{};
mi.cbSize = sizeof(MONITORINFO);
if (GetMonitorInfo(monitor, &mi))
{
for (size_t i = 0; i < m_monitors.size(); ++i)
{
// Match by rect bounds
if (m_monitors[i].rect.left == mi.rcMonitor.left &&
m_monitors[i].rect.top == mi.rcMonitor.top &&
m_monitors[i].rect.right == mi.rcMonitor.right &&
m_monitors[i].rect.bottom == mi.rcMonitor.bottom)
{
Logger::trace(L"GetMonitorIndex: Found monitor {} via rect fallback (handle changed from {} to {})",
i, reinterpret_cast<uintptr_t>(m_monitors[i].hMonitor), reinterpret_cast<uintptr_t>(monitor));
return static_cast<int>(i);
}
}
// Log all stored monitors vs the requested one for debugging
Logger::warn(L"GetMonitorIndex: No match found. Requested monitor rect=({},{},{},{})",
mi.rcMonitor.left, mi.rcMonitor.top, mi.rcMonitor.right, mi.rcMonitor.bottom);
for (size_t i = 0; i < m_monitors.size(); ++i)
{
Logger::warn(L" Stored monitor {}: rect=({},{},{},{})",
i, m_monitors[i].rect.left, m_monitors[i].rect.top,
m_monitors[i].rect.right, m_monitors[i].rect.bottom);
}
}
else
{
Logger::warn(L"GetMonitorIndex: GetMonitorInfo failed for handle {}", reinterpret_cast<uintptr_t>(monitor));
}
return -1; // Not found
}

View File

@@ -1,106 +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.
#pragma once
#include <windows.h>
#include <vector>
#include <map>
// Monitor information structure
struct MonitorInfo
{
HMONITOR hMonitor; // Direct handle for accurate lookup after display changes
RECT rect;
bool isPrimary;
int monitorId;
};
// Edge type enumeration
enum class EdgeType
{
Left = 0,
Right = 1,
Top = 2,
Bottom = 3
};
// Wrap mode enumeration (matches Settings UI dropdown)
enum class WrapMode
{
Both = 0, // Wrap in both directions
VerticalOnly = 1, // Only wrap top/bottom
HorizontalOnly = 2 // Only wrap left/right
};
// Represents a single edge of a monitor
struct MonitorEdge
{
int monitorIndex; // Index into m_monitors (stable across display changes)
EdgeType type;
int start; // For vertical edges: Y start; horizontal: X start
int end; // For vertical edges: Y end; horizontal: X end
int position; // For vertical edges: X coord; horizontal: Y coord
bool isOuter; // True if no adjacent monitor touches this edge
};
// Monitor topology helper - manages edge-based monitor layout
struct MonitorTopology
{
void Initialize(const std::vector<MonitorInfo>& monitors);
// Check if cursor is on an outer edge of the given monitor
// wrapMode filters which edges are considered (Both, VerticalOnly, HorizontalOnly)
bool IsOnOuterEdge(HMONITOR monitor, const POINT& cursorPos, EdgeType& outEdgeType, WrapMode wrapMode) const;
// Get the wrap destination point for a cursor on an outer edge
POINT GetWrapDestination(HMONITOR fromMonitor, const POINT& cursorPos, EdgeType edgeType) const;
// Get monitor at point (helper)
HMONITOR GetMonitorFromPoint(const POINT& pt) const;
// Get monitor rectangle (helper)
bool GetMonitorRect(HMONITOR monitor, RECT& rect) const;
// Get outer edges collection (for debugging)
const std::vector<MonitorEdge>& GetOuterEdges() const { return m_outerEdges; }
// Detect gaps between monitors that should be snapped together
struct GapInfo {
int monitor1Index;
int monitor2Index;
int horizontalGap;
int verticalOverlap;
};
std::vector<GapInfo> DetectMonitorGaps() const;
private:
std::vector<MonitorInfo> m_monitors;
std::vector<MonitorEdge> m_outerEdges;
// Map from (monitor index, edge type) to edge info
// Using monitor index instead of HMONITOR because HMONITOR handles can change
// when monitors are added/removed dynamically
std::map<std::pair<int, EdgeType>, MonitorEdge> m_edgeMap;
// Helper to resolve HMONITOR to monitor index at runtime
int GetMonitorIndex(HMONITOR monitor) const;
// Helper to get consistent HMONITOR from RECT
HMONITOR GetMonitorFromRect(const RECT& rect) const;
void BuildEdgeMap();
void IdentifyOuterEdges();
// Check if two edges are adjacent (within tolerance)
bool EdgesAreAdjacent(const MonitorEdge& edge1, const MonitorEdge& edge2, int tolerance = 50) const;
// Find the opposite outer edge for wrapping
MonitorEdge FindOppositeOuterEdge(EdgeType fromEdge, int relativePosition) const;
// Calculate relative position along an edge (0.0 to 1.0)
double GetRelativePosition(const MonitorEdge& edge, int coordinate) const;
// Convert relative position to absolute coordinate on target edge
int GetAbsolutePosition(const MonitorEdge& edge, double relativePosition) const;
};

File diff suppressed because it is too large Load Diff

View File

@@ -33,13 +33,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@@ -12,13 +12,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@@ -143,12 +143,11 @@ public static class DrawingHelperTests
var actualPixel = actual.GetPixel(x, y);
// allow a small tolerance for rounding differences in gdi
// using a tolerance of 3 for support of minor differences in Windows Server 2025 CI
Assert.IsTrue(
(Math.Abs(expectedPixel.A - actualPixel.A) <= 3) &&
(Math.Abs(expectedPixel.R - actualPixel.R) <= 3) &&
(Math.Abs(expectedPixel.G - actualPixel.G) <= 3) &&
(Math.Abs(expectedPixel.B - actualPixel.B) <= 3),
(Math.Abs(expectedPixel.A - actualPixel.A) <= 1) &&
(Math.Abs(expectedPixel.R - actualPixel.R) <= 1) &&
(Math.Abs(expectedPixel.G - actualPixel.G) <= 1) &&
(Math.Abs(expectedPixel.B - actualPixel.B) <= 1),
$"images differ at pixel ({x}, {y}) - expected: {expectedPixel}, actual: {actualPixel}");
}
}

View File

@@ -12,13 +12,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@@ -13,13 +13,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@@ -12,7 +12,7 @@
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">

View File

@@ -14,7 +14,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">

View File

@@ -16,13 +16,13 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

Some files were not shown because too many files have changed in this diff Show More