mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-30 08:56:33 +01:00
Compare commits
11 Commits
main
...
shawn/APIm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2128505de8 | ||
|
|
0f4ead7069 | ||
|
|
3749f3e87d | ||
|
|
d341bd2ca6 | ||
|
|
20dcb6fb47 | ||
|
|
72f84f9652 | ||
|
|
64dafff7c4 | ||
|
|
927d190cf2 | ||
|
|
667800eb86 | ||
|
|
35cab47465 | ||
|
|
c1603b189f |
3
.github/actions/spell-check/allow/code.txt
vendored
3
.github/actions/spell-check/allow/code.txt
vendored
@@ -330,9 +330,6 @@ HHH
|
||||
riday
|
||||
YYY
|
||||
|
||||
# Unicode
|
||||
precomposed
|
||||
|
||||
# GitHub issue/PR commands
|
||||
azp
|
||||
feedbackhub
|
||||
|
||||
19
.github/actions/spell-check/expect.txt
vendored
19
.github/actions/spell-check/expect.txt
vendored
@@ -216,7 +216,6 @@ CImage
|
||||
cla
|
||||
CLASSDC
|
||||
CLASSNOTAVAILABLE
|
||||
CLEARTYPE
|
||||
clickable
|
||||
clickonce
|
||||
CLIENTEDGE
|
||||
@@ -254,7 +253,6 @@ colorhistory
|
||||
colorhistorylimit
|
||||
COLORKEY
|
||||
colorref
|
||||
Convs
|
||||
comctl
|
||||
comdlg
|
||||
comexp
|
||||
@@ -531,12 +529,9 @@ eyetracker
|
||||
FANCYZONESDRAWLAYOUTTEST
|
||||
FANCYZONESEDITOR
|
||||
FARPROC
|
||||
fdw
|
||||
fdx
|
||||
FErase
|
||||
fesf
|
||||
FFFF
|
||||
FInc
|
||||
Figma
|
||||
FILEEXPLORER
|
||||
fileexploreraddons
|
||||
@@ -578,7 +573,6 @@ formatetc
|
||||
FORPARSING
|
||||
foundrylocal
|
||||
FRAMECHANGED
|
||||
FRestore
|
||||
frm
|
||||
FROMTOUCH
|
||||
fsanitize
|
||||
@@ -613,7 +607,6 @@ GETSCREENSAVERRUNNING
|
||||
GETSECKEY
|
||||
GETSTICKYKEYS
|
||||
GETTEXTLENGTH
|
||||
gfx
|
||||
GHND
|
||||
gitmodules
|
||||
GMEM
|
||||
@@ -664,7 +657,6 @@ hdwwiz
|
||||
Helpline
|
||||
helptext
|
||||
HGFE
|
||||
hgdiobj
|
||||
hglobal
|
||||
hhk
|
||||
HHmmssfff
|
||||
@@ -712,7 +704,7 @@ hotlight
|
||||
hotspot
|
||||
HPAINTBUFFER
|
||||
HRAWINPUT
|
||||
hredraw
|
||||
HREDRAW
|
||||
hres
|
||||
hresult
|
||||
hrgn
|
||||
@@ -890,7 +882,7 @@ LINKOVERLAY
|
||||
LINQTo
|
||||
listview
|
||||
LIVEDRAW
|
||||
livezoom
|
||||
LIVEZOOM
|
||||
LLKH
|
||||
llkhf
|
||||
LMEM
|
||||
@@ -919,7 +911,6 @@ LPBITMAPINFOHEADER
|
||||
LPCFHOOKPROC
|
||||
LPCITEMIDLIST
|
||||
LPCLSID
|
||||
lpch
|
||||
lpcmi
|
||||
LPCMINVOKECOMMANDINFO
|
||||
LPCREATESTRUCT
|
||||
@@ -944,7 +935,6 @@ lptpm
|
||||
LPTR
|
||||
LPTSTR
|
||||
lpv
|
||||
LPrivate
|
||||
LPW
|
||||
lpwcx
|
||||
lpwndpl
|
||||
@@ -1359,7 +1349,6 @@ ppv
|
||||
ppwsz
|
||||
prc
|
||||
Prefixer
|
||||
Premul
|
||||
prependpath
|
||||
prepopulate
|
||||
prevhost
|
||||
@@ -1915,7 +1904,7 @@ valuegenerator
|
||||
variantassignment
|
||||
VARTYPE
|
||||
vcamp
|
||||
vcenter
|
||||
VCENTER
|
||||
vcgtq
|
||||
VCINSTALLDIR
|
||||
Vcpkg
|
||||
@@ -1947,7 +1936,7 @@ vorrq
|
||||
VOS
|
||||
vpaddlq
|
||||
vqsubq
|
||||
vredraw
|
||||
VREDRAW
|
||||
vreinterpretq
|
||||
VSC
|
||||
VSCBD
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -358,4 +358,4 @@ src/common/Telemetry/*.etl
|
||||
/src/settings-ui/Settings.UI/Assets/Settings/search.index.json
|
||||
|
||||
# PowerToysInstaller Build Temp Files
|
||||
installer/*/*.wxs.bk
|
||||
installer/*/*.wxs.bk
|
||||
@@ -131,8 +131,6 @@
|
||||
|
||||
"PowerToys.ImageResizer.exe",
|
||||
"PowerToys.ImageResizer.dll",
|
||||
"WinUI3Apps\\PowerToys.ImageResizerCLI.exe",
|
||||
"WinUI3Apps\\PowerToys.ImageResizerCLI.dll",
|
||||
"PowerToys.ImageResizerExt.dll",
|
||||
"PowerToys.ImageResizerContextMenu.dll",
|
||||
"ImageResizerContextMenuPackage.msix",
|
||||
@@ -237,14 +235,6 @@
|
||||
|
||||
"PowerToys.CmdPalModuleInterface.dll",
|
||||
"CmdPalKeyboardService.dll",
|
||||
"PowerToys.ModuleContracts.dll",
|
||||
"Awake.ModuleServices.dll",
|
||||
"ColorPicker.ModuleServices.dll",
|
||||
"Workspaces.ModuleServices.dll",
|
||||
"Microsoft.CommandPalette.Extensions.dll",
|
||||
"Microsoft.CommandPalette.Extensions.Toolkit.dll",
|
||||
"Microsoft.CmdPal.Ext.PowerToys.dll",
|
||||
"Microsoft.CmdPal.Ext.PowerToys.exe",
|
||||
"*Microsoft.CmdPal.UI_*.msix",
|
||||
|
||||
"PowerToys.DSC.dll",
|
||||
@@ -368,13 +358,9 @@
|
||||
"boost_regex-vc143-mt-x32-1_87.dll",
|
||||
"boost_regex-vc143-mt-x64-1_87.dll",
|
||||
|
||||
"Microsoft.ML.OnnxRuntime.dll",
|
||||
|
||||
"UnitsNet.dll",
|
||||
"UtfUnknown.dll",
|
||||
"Wpf.Ui.dll",
|
||||
"Shmuelie.WinRTServer.dll",
|
||||
"ToolGood.Words.Pinyin.dll"
|
||||
"Wpf.Ui.dll"
|
||||
],
|
||||
"SigningInfo": {
|
||||
"Operations": [
|
||||
|
||||
@@ -624,4 +624,4 @@ jobs:
|
||||
- publish: $(JobOutputDirectory)
|
||||
artifact: $(JobOutputArtifactName)-failure-$(System.JobAttempt)
|
||||
displayName: Publish failure logs
|
||||
condition: or(failed(), canceled())
|
||||
condition: or(failed(), canceled())
|
||||
|
||||
@@ -104,4 +104,4 @@ if ($totalFailure -gt 0) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
exit 0
|
||||
exit 0
|
||||
|
||||
@@ -444,10 +444,6 @@ _If you want to find diagnostic data events in the source code, these two links
|
||||
<td>Microsoft.PowerToys.FancyZones_ZoneWindowKeyUp</td>
|
||||
<td>Occurs when a key is released while interacting with zones.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.FancyZones_CLICommand</td>
|
||||
<td>Triggered when a FancyZones CLI command is executed, logging the command name and success status.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### FileExplorerAddOns
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
|
||||
<PackageVersion Include="MessagePack" Version="3.1.3" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.CommandPalette.Extensions" Version="0.5.250829002" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.10" />
|
||||
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.10" />
|
||||
@@ -95,7 +94,6 @@
|
||||
<PackageVersion Include="RtfPipe" Version="2.0.7677.4303" />
|
||||
<PackageVersion Include="ScipBe.Common.Office.OneNote" Version="3.0.1" />
|
||||
<PackageVersion Include="SharpCompress" Version="0.37.2" />
|
||||
<PackageVersion Include="Shmuelie.WinRTServer" Version="2.1.1" />
|
||||
<!-- Don't update SkiaSharp.Views.WinUI to version 3.* branch as this brakes the HexBox control in Registry Preview. -->
|
||||
<PackageVersion Include="SkiaSharp.Views.WinUI" Version="2.88.9" />
|
||||
<PackageVersion Include="StreamJsonRpc" Version="2.21.69" />
|
||||
@@ -147,4 +145,4 @@
|
||||
<PackageVersion Include="Microsoft.VariantAssignment.Client" Version="2.4.17140001" />
|
||||
<PackageVersion Include="Microsoft.VariantAssignment.Contract" Version="3.0.16990001" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -1560,7 +1560,6 @@ SOFTWARE.
|
||||
- ReverseMarkdown
|
||||
- ScipBe.Common.Office.OneNote
|
||||
- SharpCompress
|
||||
- Shmuelie.WinRTServer
|
||||
- SkiaSharp.Views.WinUI
|
||||
- StreamJsonRpc
|
||||
- StyleCop.Analyzers
|
||||
|
||||
@@ -44,10 +44,6 @@
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/common/PowerToys.ModuleContracts/PowerToys.ModuleContracts.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/common/SettingsAPI/SettingsAPI.vcxproj" Id="6955446d-23f7-4023-9bb3-8657f904af99" />
|
||||
<Project Path="src/common/Themes/Themes.vcxproj" Id="98537082-0fdb-40de-abd8-0dc5a4269bab" />
|
||||
<Project Path="src/common/UITestAutomation/UITestAutomation.csproj">
|
||||
@@ -160,10 +156,6 @@
|
||||
<Project Path="src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj" Id="48a0a19e-a0be-4256-acf8-cc3b80291af9" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/awake/">
|
||||
<Project Path="src/modules/awake/Awake.ModuleServices/Awake.ModuleServices.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/awake/Awake/Awake.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
@@ -174,10 +166,6 @@
|
||||
<Project Path="src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.vcxproj" Id="0014d652-901f-4456-8d65-06fc5f997fb0" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/colorpicker/">
|
||||
<Project Path="src/modules/colorPicker/ColorPicker.ModuleServices/ColorPicker.ModuleServices.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/colorPicker/ColorPicker/ColorPicker.vcxproj" Id="655c9af2-18d3-4da6-80e4-85504a7722ba">
|
||||
<BuildDependency Project="src/common/logger/logger.vcxproj" />
|
||||
</Project>
|
||||
@@ -218,11 +206,6 @@
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
<Deploy />
|
||||
</Project>
|
||||
<Project Path="src/modules/cmdpal/ext/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">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
@@ -459,10 +442,6 @@
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/imageresizer/ImageResizerCLI/ImageResizerCLI.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/imageresizer/Tests/">
|
||||
<Project Path="src/modules/imageresizer/tests/ImageResizer.UnitTests.csproj">
|
||||
@@ -953,10 +932,6 @@
|
||||
<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">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/Workspaces/WorkspacesCsharpLibrary/WorkspacesCsharpLibrary.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
# CLI Conventions
|
||||
|
||||
This document describes the conventions for implementing command-line interfaces (CLI) in PowerToys modules.
|
||||
|
||||
## Library
|
||||
|
||||
Use the **System.CommandLine** library for CLI argument parsing. This is already defined in `Directory.Packages.props`:
|
||||
|
||||
```xml
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
```
|
||||
|
||||
Add the reference to your project:
|
||||
|
||||
```xml
|
||||
<PackageReference Include="System.CommandLine" />
|
||||
```
|
||||
|
||||
## Option Naming and Definition
|
||||
|
||||
- Use `--kebab-case` for long form (e.g., `--shrink-only`).
|
||||
- Use single `-x` for short form (e.g., `-s`, `-w`).
|
||||
- Define aliases as static readonly arrays: `["--silent", "-s"]`.
|
||||
- Create options using `Option<T>` with descriptive help text.
|
||||
- Add validators for options that require range or format checking.
|
||||
|
||||
## RootCommand Setup
|
||||
|
||||
- Create a `RootCommand` with a brief description.
|
||||
- Add all options and arguments to the command.
|
||||
|
||||
## Parsing
|
||||
|
||||
- Use `Parser(rootCommand).Parse(args)` to parse CLI arguments.
|
||||
- Extract option values using `parseResult.GetValueForOption()`.
|
||||
- Note: Use `Parser` directly; `RootCommand.Parse()` may not be available with the pinned System.CommandLine version.
|
||||
|
||||
### Parse/Validation Errors
|
||||
|
||||
- On parse/validation errors, print error messages and usage, then exit with non-zero code.
|
||||
|
||||
## Examples
|
||||
|
||||
Reference implementations:
|
||||
- Awake: `src/modules/Awake/Awake/Program.cs`
|
||||
- ImageResizer: `src/modules/imageresizer/ui/Cli/`
|
||||
|
||||
## Help Output
|
||||
|
||||
- Provide a `PrintUsage()` method for custom help formatting if needed.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Consistency**: Follow existing module patterns.
|
||||
2. **Documentation**: Always provide help text for each option.
|
||||
3. **Validation**: Validate input and provide clear error messages.
|
||||
4. **Atomicity**: Make one logical change per PR; avoid drive-by refactors.
|
||||
5. **Build/Test Discipline**: Build and test synchronously, one terminal per operation.
|
||||
6. **Style**: Follow repo analyzers (`.editorconfig`, StyleCop) and formatting rules.
|
||||
|
||||
## Logging Requirements
|
||||
|
||||
- Use `ManagedCommon.Logger` for consistent logging.
|
||||
- Initialize logging early in `Main()`.
|
||||
- Use dual output (console + log file) for errors and warnings to ensure visibility.
|
||||
- Reference: `src/modules/imageresizer/ui/Cli/CliLogger.cs`
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Exit Codes
|
||||
|
||||
- `0`: Success
|
||||
- `1`: General error (parsing, validation, runtime)
|
||||
- `2`: Invalid arguments (optional)
|
||||
|
||||
### Exception Handling
|
||||
|
||||
- Always wrap `Main()` in try-catch for unhandled exceptions.
|
||||
- Log exceptions before exiting with non-zero code.
|
||||
- Display user-friendly error messages to stderr.
|
||||
- Preserve detailed stack traces in log files only.
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
- Include tests for argument parsing, validation, and edge cases.
|
||||
- Place CLI tests in module-specific test projects (e.g., `src/modules/[module]/tests/*CliTests.cs`).
|
||||
|
||||
## Signing and Deployment
|
||||
|
||||
- CLI executables are signed automatically in CI/CD.
|
||||
- **New CLI tools**: Add your executable and dll to `.pipelines/ESRPSigning_core.json` in the signing list.
|
||||
- CLI executables are deployed alongside their parent module (e.g., `C:\Program Files\PowerToys\modules\[ModuleName]\`).
|
||||
- Use self-contained deployment (import `Common.SelfContained.props`).
|
||||
@@ -7,18 +7,11 @@
|
||||
|
||||
<Fragment>
|
||||
<DirectoryRef Id="INSTALLFOLDER">
|
||||
<Component Id="Microsoft_CommandPalette_Extensions_winmd" Guid="304AD25A-A986-4058-940E-61DB79EBD78C" Bitness="always64">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="Microsoft_CommandPalette_Extensions_winmd" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<File Id="Microsoft.CommandPalette.Extensions.winmd" Source="$(var.BinDir)Microsoft.CommandPalette.Extensions.winmd" />
|
||||
</Component>
|
||||
<!-- Generated by generateFileComponents.ps1 -->
|
||||
<!--BaseApplicationsFiles_Component_Def-->
|
||||
</DirectoryRef>
|
||||
|
||||
<ComponentGroup Id="BaseApplicationsComponentGroup">
|
||||
<ComponentRef Id="Microsoft_CommandPalette_Extensions_winmd" />
|
||||
</ComponentGroup>
|
||||
|
||||
</Fragment>
|
||||
|
||||
@@ -173,4 +173,4 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
<Target Name="Restore" />
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -10,8 +10,7 @@
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
|
||||
xmlns:systemai="http://schemas.microsoft.com/appx/manifest/systemai/windows10"
|
||||
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
|
||||
IgnorableNamespaces="uap uap2 uap3 rescap desktop uap10 systemai com">
|
||||
IgnorableNamespaces="uap uap2 uap3 rescap desktop uap10 systemai">
|
||||
<Identity
|
||||
Name="Microsoft.PowerToys.SparseApp"
|
||||
Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
|
||||
@@ -31,7 +30,6 @@
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19000.0" MaxVersionTested="10.0.26226.0" />
|
||||
</Dependencies>
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
<systemai:Capability Name="systemAIModels"/>
|
||||
<rescap:Capability Name="unvirtualizedResources"/>
|
||||
@@ -68,42 +66,5 @@
|
||||
AppListEntry="none">
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
<Application Id="PowerToys.CmdPalExtension" Executable="Microsoft.CmdPal.Ext.PowerToys.exe" EntryPoint="Windows.FullTrustApplication">
|
||||
<uap:VisualElements
|
||||
DisplayName="PowerToys.CommandPaletteExtension"
|
||||
Description="PowerToys Command Palette Extension"
|
||||
BackgroundColor="transparent"
|
||||
Square150x150Logo="Images\Square150x150Logo.png"
|
||||
Square44x44Logo="Images\Square44x44Logo.png"
|
||||
AppListEntry="none">
|
||||
</uap:VisualElements>
|
||||
<Extensions>
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
<com:ExeServer Executable="Microsoft.CmdPal.Ext.PowerToys.exe" Arguments="-RegisterProcessAsComServer" DisplayName="PowerToys Command Palette Extension">
|
||||
<com:Class Id="7EC02C7D-8F98-4A2E-9F23-B58C2C2F2B17" DisplayName="PowerToys Command Palette Extension" />
|
||||
</com:ExeServer>
|
||||
</com:ComServer>
|
||||
</com:Extension>
|
||||
<uap3:Extension Category="windows.appExtension">
|
||||
<uap3:AppExtension Name="com.microsoft.commandpalette"
|
||||
Id="PowerToys"
|
||||
PublicFolder="Public"
|
||||
DisplayName="PowerToys"
|
||||
Description="Surface PowerToys commands inside Command Palette">
|
||||
<uap3:Properties>
|
||||
<CmdPalProvider>
|
||||
<Activation>
|
||||
<CreateInstance ClassId="7EC02C7D-8F98-4A2E-9F23-B58C2C2F2B17" />
|
||||
</Activation>
|
||||
<SupportedInterfaces>
|
||||
<Commands/>
|
||||
</SupportedInterfaces>
|
||||
</CmdPalProvider>
|
||||
</uap3:Properties>
|
||||
</uap3:AppExtension>
|
||||
</uap3:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
</Package>
|
||||
</Package>
|
||||
@@ -2,10 +2,8 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using ManagedCommon;
|
||||
|
||||
namespace Common.UI
|
||||
{
|
||||
@@ -122,33 +120,28 @@ namespace Common.UI
|
||||
}
|
||||
}
|
||||
|
||||
// What about debug build? Should also consider debug build, maybe tray window message?
|
||||
public static void OpenSettings(SettingsWindow window)
|
||||
public static void OpenSettings(SettingsWindow window, bool mainExecutableIsOnTheParentFolder)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exePath = Path.Combine(
|
||||
PowerToysPathResolver.GetPowerToysInstallPath(),
|
||||
"PowerToys.exe");
|
||||
|
||||
if (exePath == null || !File.Exists(exePath))
|
||||
var directoryPath = System.AppContext.BaseDirectory;
|
||||
if (mainExecutableIsOnTheParentFolder)
|
||||
{
|
||||
Logger.LogError($"Failed to find powertoys exe path, {exePath}");
|
||||
return;
|
||||
// Need to go into parent folder for PowerToys.exe. Likely a WinUI3 App SDK application.
|
||||
directoryPath = Path.Combine(directoryPath, "..");
|
||||
directoryPath = Path.Combine(directoryPath, "PowerToys.exe");
|
||||
}
|
||||
else
|
||||
{
|
||||
// PowerToys.exe is in the same path as the application.
|
||||
directoryPath = Path.Combine(directoryPath, "PowerToys.exe");
|
||||
}
|
||||
|
||||
var args = "--open-settings=" + SettingsWindowNameToString(window);
|
||||
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = exePath,
|
||||
Arguments = args,
|
||||
UseShellExecute = false,
|
||||
});
|
||||
Process.Start(new ProcessStartInfo(directoryPath) { Arguments = "--open-settings=" + SettingsWindowNameToString(window) });
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch
|
||||
{
|
||||
Logger.LogError(ex.Message);
|
||||
// TODO(stefan): Log exception once unified logging is implemented
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Microsoft.PowerToys.FilePreviewCommon
|
||||
var softlineBreak = new Markdig.Extensions.Hardlines.SoftlineBreakAsHardlineExtension();
|
||||
|
||||
MarkdownPipelineBuilder pipelineBuilder;
|
||||
pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseEmojiAndSmiley().UseYamlFrontMatter().UseMathematics().DisableHtml();
|
||||
pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseEmojiAndSmiley().UseYamlFrontMatter().UseMathematics();
|
||||
pipelineBuilder.Extensions.Add(extension);
|
||||
pipelineBuilder.Extensions.Add(softlineBreak);
|
||||
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.Versioning;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace ManagedCommon
|
||||
{
|
||||
[SupportedOSPlatform("windows")]
|
||||
public class PowerToysPathResolver
|
||||
{
|
||||
private const string PowerToysRegistryKey = @"Software\Classes\powertoys";
|
||||
private const string PowerToysExe = "PowerToys.exe";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the PowerToys installation path by checking registry entries
|
||||
/// </summary>
|
||||
/// <returns>The path to PowerToys installation directory, or null if not found</returns>
|
||||
public static string GetPowerToysInstallPath()
|
||||
{
|
||||
#if DEBUG
|
||||
// In debug builds, resolve directly from the running process (no installer/registry involved).
|
||||
return GetPathFromCurrentProcess();
|
||||
#else
|
||||
// Try to get path from Per-User installation first
|
||||
string path = GetPathFromRegistry(RegistryHive.CurrentUser);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
// Fall back to Per-Machine installation
|
||||
path = GetPathFromRegistry(RegistryHive.LocalMachine);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
private static string GetPathFromRegistry(RegistryHive hive)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var baseKey = RegistryKey.OpenBaseKey(hive, RegistryView.Registry64);
|
||||
|
||||
// First try to get path from the powertoys protocol registration
|
||||
string path = GetPathFromProtocolRegistration(baseKey);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore registry access errors
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string GetPathFromProtocolRegistration(RegistryKey baseKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var key = baseKey.OpenSubKey($@"{PowerToysRegistryKey}\shell\open\command");
|
||||
|
||||
if (key != null)
|
||||
{
|
||||
string command = key.GetValue(string.Empty)?.ToString();
|
||||
if (!string.IsNullOrEmpty(command))
|
||||
{
|
||||
// Parse command like: "C:\Program Files\PowerToys\PowerToys.exe" "%1"
|
||||
return ExtractPathFromCommand(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore registry access errors
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string GetPathFromCurrentProcess()
|
||||
{
|
||||
try
|
||||
{
|
||||
// If we're running inside PowerToys.exe (dev/debug builds), use the executable location.
|
||||
var processPath = Process.GetCurrentProcess().MainModule?.FileName;
|
||||
if (!string.IsNullOrEmpty(processPath))
|
||||
{
|
||||
var processDir = Path.GetDirectoryName(processPath);
|
||||
if (!string.IsNullOrEmpty(processDir) && File.Exists(Path.Combine(processDir, PowerToysExe)))
|
||||
{
|
||||
return processDir;
|
||||
}
|
||||
}
|
||||
|
||||
// As a fallback, walk up from AppContext.BaseDirectory to find PowerToys.exe.
|
||||
var directory = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
while (directory != null)
|
||||
{
|
||||
var candidate = Path.Combine(directory.FullName, PowerToysExe);
|
||||
if (File.Exists(candidate))
|
||||
{
|
||||
return directory.FullName;
|
||||
}
|
||||
|
||||
directory = directory.Parent;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore reflection/process permission errors; caller will see null and handle accordingly.
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string ExtractPathFromCommand(string command)
|
||||
{
|
||||
if (string.IsNullOrEmpty(command))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Handle quoted paths: "C:\Program Files\PowerToys\PowerToys.exe" "%1"
|
||||
if (command.StartsWith('\"'))
|
||||
{
|
||||
int endQuote = command.IndexOf('\"', 1);
|
||||
if (endQuote > 1)
|
||||
{
|
||||
string exePath = command.Substring(1, endQuote - 1);
|
||||
if (File.Exists(exePath))
|
||||
{
|
||||
return Path.GetDirectoryName(exePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle unquoted paths (less common)
|
||||
string[] parts = command.Split(' ');
|
||||
if (parts.Length > 0 && File.Exists(parts[0]))
|
||||
{
|
||||
return Path.GetDirectoryName(parts[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore path parsing errors
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Common.UI;
|
||||
|
||||
namespace PowerToys.ModuleContracts;
|
||||
|
||||
/// <summary>
|
||||
/// Base contract for PowerToys modules exposed to the Command Palette.
|
||||
/// </summary>
|
||||
public interface IModuleService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets module identifier (e.g., Workspaces, Awake).
|
||||
/// </summary>
|
||||
string Key { get; }
|
||||
|
||||
Task<OperationResult> LaunchAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
Task<OperationResult> OpenSettingsAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper base to reduce duplication for simple modules.
|
||||
/// </summary>
|
||||
public abstract class ModuleServiceBase : IModuleService
|
||||
{
|
||||
public abstract string Key { get; }
|
||||
|
||||
protected abstract SettingsDeepLink.SettingsWindow SettingsWindow { get; }
|
||||
|
||||
public abstract Task<OperationResult> LaunchAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
public virtual Task<OperationResult> OpenSettingsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
SettingsDeepLink.OpenSettings(SettingsWindow);
|
||||
return Task.FromResult(OperationResult.Ok());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(OperationResult.Fail($"Failed to open settings for {Key}: {ex.Message}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace PowerToys.ModuleContracts;
|
||||
|
||||
/// <summary>
|
||||
/// Lightweight result type for module operations.
|
||||
/// </summary>
|
||||
public readonly record struct OperationResult(bool Success, string? Error = null)
|
||||
{
|
||||
public static OperationResult Ok() => new(true, null);
|
||||
|
||||
public static OperationResult Fail(string error) => new(false, error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result type with a payload.
|
||||
/// </summary>
|
||||
public readonly record struct OperationResult<T>(bool Success, T? Value, string? Error = null);
|
||||
|
||||
/// <summary>
|
||||
/// Factory helpers for creating operation results.
|
||||
/// </summary>
|
||||
public static class OperationResults
|
||||
{
|
||||
public static OperationResult<T> Ok<T>(T value) => new(true, value, null);
|
||||
|
||||
public static OperationResult<T> Fail<T>(string error) => new(false, default, error);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Common.UI\Common.UI.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -75,62 +75,10 @@ namespace winrt::PowerToys::Interop::implementation
|
||||
{
|
||||
return CommonSharedConstants::ADVANCED_PASTE_CUSTOM_ACTION_MESSAGE;
|
||||
}
|
||||
hstring Constants::AdvancedPasteShowUIEvent()
|
||||
{
|
||||
return CommonSharedConstants::ADVANCED_PASTE_SHOW_UI_EVENT;
|
||||
}
|
||||
hstring Constants::AdvancedPasteTerminateAppMessage()
|
||||
{
|
||||
return CommonSharedConstants::ADVANCED_PASTE_TERMINATE_APP_MESSAGE;
|
||||
}
|
||||
hstring Constants::AlwaysOnTopPinEvent()
|
||||
{
|
||||
return CommonSharedConstants::ALWAYS_ON_TOP_PIN_EVENT;
|
||||
}
|
||||
hstring Constants::FindMyMouseTriggerEvent()
|
||||
{
|
||||
return CommonSharedConstants::FIND_MY_MOUSE_TRIGGER_EVENT;
|
||||
}
|
||||
hstring Constants::MouseHighlighterTriggerEvent()
|
||||
{
|
||||
return CommonSharedConstants::MOUSE_HIGHLIGHTER_TRIGGER_EVENT;
|
||||
}
|
||||
hstring Constants::MouseCrosshairsTriggerEvent()
|
||||
{
|
||||
return CommonSharedConstants::MOUSE_CROSSHAIRS_TRIGGER_EVENT;
|
||||
}
|
||||
hstring Constants::CursorWrapTriggerEvent()
|
||||
{
|
||||
return CommonSharedConstants::CURSOR_WRAP_TRIGGER_EVENT;
|
||||
}
|
||||
hstring Constants::LightSwitchToggleEvent()
|
||||
{
|
||||
return CommonSharedConstants::LIGHTSWITCH_TOGGLE_EVENT;
|
||||
}
|
||||
hstring Constants::ZoomItZoomEvent()
|
||||
{
|
||||
return CommonSharedConstants::ZOOMIT_ZOOM_EVENT;
|
||||
}
|
||||
hstring Constants::ZoomItDrawEvent()
|
||||
{
|
||||
return CommonSharedConstants::ZOOMIT_DRAW_EVENT;
|
||||
}
|
||||
hstring Constants::ZoomItBreakEvent()
|
||||
{
|
||||
return CommonSharedConstants::ZOOMIT_BREAK_EVENT;
|
||||
}
|
||||
hstring Constants::ZoomItLiveZoomEvent()
|
||||
{
|
||||
return CommonSharedConstants::ZOOMIT_LIVEZOOM_EVENT;
|
||||
}
|
||||
hstring Constants::ZoomItSnipEvent()
|
||||
{
|
||||
return CommonSharedConstants::ZOOMIT_SNIP_EVENT;
|
||||
}
|
||||
hstring Constants::ZoomItRecordEvent()
|
||||
{
|
||||
return CommonSharedConstants::ZOOMIT_RECORD_EVENT;
|
||||
}
|
||||
hstring Constants::ShowPowerOCRSharedEvent()
|
||||
{
|
||||
return CommonSharedConstants::SHOW_POWEROCR_SHARED_EVENT;
|
||||
|
||||
@@ -23,20 +23,6 @@ namespace winrt::PowerToys::Interop::implementation
|
||||
static hstring AdvancedPasteAdditionalActionMessage();
|
||||
static hstring AdvancedPasteCustomActionMessage();
|
||||
static hstring AdvancedPasteTerminateAppMessage();
|
||||
static hstring AdvancedPasteShowUIEvent();
|
||||
static hstring AlwaysOnTopPinEvent();
|
||||
static hstring MeasureToolTriggerEvent();
|
||||
static hstring FindMyMouseTriggerEvent();
|
||||
static hstring MouseHighlighterTriggerEvent();
|
||||
static hstring MouseCrosshairsTriggerEvent();
|
||||
static hstring CursorWrapTriggerEvent();
|
||||
static hstring LightSwitchToggleEvent();
|
||||
static hstring ZoomItZoomEvent();
|
||||
static hstring ZoomItDrawEvent();
|
||||
static hstring ZoomItBreakEvent();
|
||||
static hstring ZoomItLiveZoomEvent();
|
||||
static hstring ZoomItSnipEvent();
|
||||
static hstring ZoomItRecordEvent();
|
||||
static hstring ShowPowerOCRSharedEvent();
|
||||
static hstring TerminatePowerOCRSharedEvent();
|
||||
static hstring MouseJumpShowPreviewEvent();
|
||||
@@ -47,6 +33,7 @@ namespace winrt::PowerToys::Interop::implementation
|
||||
static hstring PowerAccentExitEvent();
|
||||
static hstring ShortcutGuideTriggerEvent();
|
||||
static hstring RegistryPreviewTriggerEvent();
|
||||
static hstring MeasureToolTriggerEvent();
|
||||
static hstring GcodePreviewResizeEvent();
|
||||
static hstring BgcodePreviewResizeEvent();
|
||||
static hstring QoiPreviewResizeEvent();
|
||||
|
||||
@@ -20,19 +20,6 @@ namespace PowerToys
|
||||
static String AdvancedPasteAdditionalActionMessage();
|
||||
static String AdvancedPasteCustomActionMessage();
|
||||
static String AdvancedPasteTerminateAppMessage();
|
||||
static String AdvancedPasteShowUIEvent();
|
||||
static String AlwaysOnTopPinEvent();
|
||||
static String FindMyMouseTriggerEvent();
|
||||
static String MouseHighlighterTriggerEvent();
|
||||
static String MouseCrosshairsTriggerEvent();
|
||||
static String CursorWrapTriggerEvent();
|
||||
static String LightSwitchToggleEvent();
|
||||
static String ZoomItZoomEvent();
|
||||
static String ZoomItDrawEvent();
|
||||
static String ZoomItBreakEvent();
|
||||
static String ZoomItLiveZoomEvent();
|
||||
static String ZoomItSnipEvent();
|
||||
static String ZoomItRecordEvent();
|
||||
static String ShowPowerOCRSharedEvent();
|
||||
static String TerminatePowerOCRSharedEvent();
|
||||
static String MouseJumpShowPreviewEvent();
|
||||
@@ -64,4 +51,4 @@ namespace PowerToys
|
||||
static String ShowCmdPalEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,8 +40,6 @@ namespace CommonSharedConstants
|
||||
const wchar_t ADVANCED_PASTE_CUSTOM_ACTION_MESSAGE[] = L"CustomAction";
|
||||
|
||||
const wchar_t ADVANCED_PASTE_TERMINATE_APP_MESSAGE[] = L"TerminateApp";
|
||||
|
||||
const wchar_t ADVANCED_PASTE_SHOW_UI_EVENT[] = L"Local\\PowerToys_AdvancedPaste_ShowUI";
|
||||
|
||||
// Path to the event used to show Color Picker
|
||||
const wchar_t SHOW_COLOR_PICKER_SHARED_EVENT[] = L"Local\\ShowColorPickerEvent-8c46be2a-3e05-4186-b56b-4ae986ef2525";
|
||||
@@ -85,21 +83,12 @@ namespace CommonSharedConstants
|
||||
|
||||
const wchar_t TERMINATE_MOUSE_JUMP_SHARED_EVENT[] = L"Local\\TerminateMouseJumpEvent-252fa337-317f-4c37-a61f-99464c3f9728";
|
||||
|
||||
// Paths to the events used by other Mouse Utilities
|
||||
const wchar_t FIND_MY_MOUSE_TRIGGER_EVENT[] = L"Local\\FindMyMouseTriggerEvent-5a9dc5f4-1c74-4f2f-a66f-1b9b6a2f9b23";
|
||||
const wchar_t MOUSE_HIGHLIGHTER_TRIGGER_EVENT[] = L"Local\\MouseHighlighterTriggerEvent-1e3c9c3d-3fdf-4f9a-9a52-31c9b3c3a8f4";
|
||||
const wchar_t MOUSE_CROSSHAIRS_TRIGGER_EVENT[] = L"Local\\MouseCrosshairsTriggerEvent-0d4c7f92-0a5c-4f5c-b64b-8a2a2f7e0b21";
|
||||
const wchar_t CURSOR_WRAP_TRIGGER_EVENT[] = L"Local\\CursorWrapTriggerEvent-1f8452b5-4e6e-45b3-8b09-13f14a5900c9";
|
||||
|
||||
// Path to the event used by RegistryPreview
|
||||
const wchar_t REGISTRY_PREVIEW_TRIGGER_EVENT[] = L"Local\\RegistryPreviewEvent-4C559468-F75A-4E7F-BC4F-9C9688316687";
|
||||
|
||||
// Path to the event used by MeasureTool
|
||||
const wchar_t MEASURE_TOOL_TRIGGER_EVENT[] = L"Local\\MeasureToolEvent-3d46745f-09b3-4671-a577-236be7abd199";
|
||||
|
||||
// Path to the event used by LightSwitch
|
||||
const wchar_t LIGHTSWITCH_TOGGLE_EVENT[] = L"Local\\PowerToys-LightSwitch-ToggleEvent-d8dc2f29-8c94-4ca1-8c5f-3e2b1e3c4f5a";
|
||||
|
||||
// Path to the event used by GcodePreviewHandler
|
||||
const wchar_t GCODE_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysGcodePreviewResizeEvent-6ff1f9bd-ccbd-4b24-a79f-40a34fb0317d";
|
||||
|
||||
@@ -141,12 +130,6 @@ namespace CommonSharedConstants
|
||||
// Path to the events used by ZoomIt
|
||||
const wchar_t ZOOMIT_REFRESH_SETTINGS_EVENT[] = L"Local\\PowerToysZoomIt-RefreshSettingsEvent-f053a563-d519-4b0d-8152-a54489c13324";
|
||||
const wchar_t ZOOMIT_EXIT_EVENT[] = L"Local\\PowerToysZoomIt-ExitEvent-36641ce6-df02-4eac-abea-a3fbf9138220";
|
||||
const wchar_t ZOOMIT_ZOOM_EVENT[] = L"Local\\PowerToysZoomIt-ZoomEvent-1e4190d7-94bc-4ad5-adc0-9a8fd07cb393";
|
||||
const wchar_t ZOOMIT_DRAW_EVENT[] = L"Local\\PowerToysZoomIt-DrawEvent-56338997-404d-4549-bd9a-d132b6766975";
|
||||
const wchar_t ZOOMIT_BREAK_EVENT[] = L"Local\\PowerToysZoomIt-BreakEvent-17f2e63c-4c56-41dd-90a0-2d12f9f50c6b";
|
||||
const wchar_t ZOOMIT_LIVEZOOM_EVENT[] = L"Local\\PowerToysZoomIt-LiveZoomEvent-390bf0c7-616f-47dc-bafe-a2d228add20d";
|
||||
const wchar_t ZOOMIT_SNIP_EVENT[] = L"Local\\PowerToysZoomIt-SnipEvent-2fd9c211-436d-4f17-a902-2528aaae3e30";
|
||||
const wchar_t ZOOMIT_RECORD_EVENT[] = L"Local\\PowerToysZoomIt-RecordEvent-74539344-eaad-4711-8e83-23946e424512";
|
||||
|
||||
// used from quick access window
|
||||
const wchar_t CMDPAL_SHOW_EVENT[] = L"Local\\PowerToysCmdPal-ShowEvent-62336fcd-8611-4023-9b30-091a6af4cc5a";
|
||||
|
||||
@@ -3,128 +3,78 @@
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <windows.h>
|
||||
|
||||
/// <summary>
|
||||
/// A reusable utility class that listens for a named Windows event and invokes a callback when triggered.
|
||||
/// Provides RAII-based resource management for event handles and the listener thread.
|
||||
/// The thread is properly joined on destruction to ensure clean shutdown.
|
||||
/// </summary>
|
||||
class EventWaiter
|
||||
{
|
||||
public:
|
||||
EventWaiter() = default;
|
||||
|
||||
EventWaiter(const EventWaiter&) = delete;
|
||||
EventWaiter& operator=(const EventWaiter&) = delete;
|
||||
EventWaiter(EventWaiter&&) = delete;
|
||||
EventWaiter& operator=(EventWaiter&&) = delete;
|
||||
|
||||
~EventWaiter()
|
||||
EventWaiter() {}
|
||||
EventWaiter(const std::wstring& name, std::function<void(DWORD)> callback)
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts listening for the specified named event. When the event is signaled, the callback is invoked.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the Windows event to listen for.</param>
|
||||
/// <param name="callback">The callback function to invoke when the event is triggered. Receives ERROR_SUCCESS on success.</param>
|
||||
/// <returns>true if listening started successfully, false otherwise.</returns>
|
||||
bool start(const std::wstring& name, std::function<void(DWORD)> callback)
|
||||
{
|
||||
if (m_listening)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_exitThreadEvent = CreateEventW(nullptr, false, false, nullptr);
|
||||
m_waitingEvent = CreateEventW(nullptr, false, false, name.c_str());
|
||||
|
||||
if (!m_exitThreadEvent || !m_waitingEvent)
|
||||
{
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_listening = true;
|
||||
m_eventThread = std::thread([this, cb = std::move(callback)]() {
|
||||
HANDLE events[2] = { m_waitingEvent, m_exitThreadEvent };
|
||||
while (m_listening)
|
||||
// Create localExitThreadEvent and localWaitingEvent for capturing. We cannot capture 'this' as we implement move constructor.
|
||||
auto localExitThreadEvent = exitThreadEvent = CreateEvent(nullptr, false, false, nullptr);
|
||||
HANDLE localWaitingEvent = waitingEvent = CreateEvent(nullptr, false, false, name.c_str());
|
||||
std::thread([=]() {
|
||||
HANDLE events[2] = { localWaitingEvent, localExitThreadEvent };
|
||||
while (true)
|
||||
{
|
||||
auto waitResult = WaitForMultipleObjects(2, events, false, INFINITE);
|
||||
if (!m_listening)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (waitResult == WAIT_OBJECT_0 + 1)
|
||||
{
|
||||
// Exit event signaled
|
||||
break;
|
||||
}
|
||||
|
||||
if (waitResult == WAIT_FAILED)
|
||||
{
|
||||
cb(GetLastError());
|
||||
callback(GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (waitResult == WAIT_OBJECT_0)
|
||||
{
|
||||
cb(ERROR_SUCCESS);
|
||||
callback(ERROR_SUCCESS);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}).detach();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops listening for the event and cleans up resources.
|
||||
/// Waits for the listener thread to finish before returning.
|
||||
/// Safe to call multiple times.
|
||||
/// </summary>
|
||||
void stop()
|
||||
EventWaiter(EventWaiter&) = delete;
|
||||
EventWaiter& operator=(EventWaiter&) = delete;
|
||||
|
||||
EventWaiter(EventWaiter&& a) noexcept
|
||||
{
|
||||
m_listening = false;
|
||||
if (m_exitThreadEvent)
|
||||
{
|
||||
SetEvent(m_exitThreadEvent);
|
||||
}
|
||||
if (m_eventThread.joinable())
|
||||
{
|
||||
m_eventThread.join();
|
||||
}
|
||||
cleanup();
|
||||
this->exitThreadEvent = a.exitThreadEvent;
|
||||
this->waitingEvent = a.waitingEvent;
|
||||
|
||||
a.exitThreadEvent = nullptr;
|
||||
a.waitingEvent = nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the listener is currently active.
|
||||
/// </summary>
|
||||
bool is_listening() const
|
||||
EventWaiter& operator=(EventWaiter&& a) noexcept
|
||||
{
|
||||
return m_listening;
|
||||
this->exitThreadEvent = a.exitThreadEvent;
|
||||
this->waitingEvent = a.waitingEvent;
|
||||
|
||||
a.exitThreadEvent = nullptr;
|
||||
a.waitingEvent = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~EventWaiter()
|
||||
{
|
||||
if (exitThreadEvent)
|
||||
{
|
||||
SetEvent(exitThreadEvent);
|
||||
CloseHandle(exitThreadEvent);
|
||||
}
|
||||
|
||||
if (waitingEvent)
|
||||
{
|
||||
CloseHandle(waitingEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void cleanup()
|
||||
{
|
||||
if (m_exitThreadEvent)
|
||||
{
|
||||
CloseHandle(m_exitThreadEvent);
|
||||
m_exitThreadEvent = nullptr;
|
||||
}
|
||||
if (m_waitingEvent)
|
||||
{
|
||||
CloseHandle(m_waitingEvent);
|
||||
m_waitingEvent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE m_exitThreadEvent = nullptr;
|
||||
HANDLE m_waitingEvent = nullptr;
|
||||
std::thread m_eventThread;
|
||||
std::atomic_bool m_listening{ false };
|
||||
HANDLE exitThreadEvent = nullptr;
|
||||
HANDLE waitingEvent = nullptr;
|
||||
};
|
||||
@@ -45,7 +45,6 @@
|
||||
<Target Name="GenerateDscResourceJsonFiles" AfterTargets="Build" Condition="'$(CIBuild)' != 'true'">
|
||||
<Message Text="Generating DSC resource JSON files to DSCModules subfolder..." Importance="high" />
|
||||
<MakeDir Directories="$(TargetDir)DSCModules" />
|
||||
|
||||
<Exec Command="dotnet "$(TargetPath)" manifest --resource settings --outputDir "$(TargetDir)DSCModules"" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -335,6 +335,7 @@
|
||||
<converters:CountToVisibilityConverter x:Key="CountToVisibilityConverter" />
|
||||
<converters:CountToInvertedVisibilityConverter x:Key="CountToInvertedVisibilityConverter" />
|
||||
<converters:ServiceTypeToIconConverter x:Key="ServiceTypeToIconConverter" />
|
||||
<converters:PasteAIUsageToStringConverter x:Key="PasteAIUsageToStringConverter" />
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid x:Name="PromptBoxGrid" Loaded="Grid_Loaded">
|
||||
@@ -430,12 +431,20 @@
|
||||
Grid.Row="1"
|
||||
MinHeight="104"
|
||||
MaxHeight="320">
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.CustomFormatResult, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.CustomFormatResult, Mode=OneWay}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{x:Bind ViewModel.HasCustomFormatText, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<Image
|
||||
HorizontalAlignment="Left"
|
||||
Source="{x:Bind ViewModel.CustomFormatImageResult, Mode=OneWay}"
|
||||
Stretch="Uniform"
|
||||
Visibility="{x:Bind ViewModel.HasCustomFormatImage, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
<Rectangle
|
||||
@@ -602,20 +611,37 @@
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ServiceType, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<Border
|
||||
<StackPanel
|
||||
Grid.Column="2"
|
||||
Padding="2,0,2,0"
|
||||
VerticalAlignment="Center"
|
||||
BorderBrush="{ThemeResource ControlStrokeColorSecondary}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{StaticResource ControlCornerRadius}"
|
||||
Visibility="{x:Bind IsLocalModel, Mode=OneWay}">
|
||||
<TextBlock
|
||||
x:Uid="LocalModelBadge"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="10"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</Border>
|
||||
Orientation="Horizontal"
|
||||
Spacing="4">
|
||||
<Border
|
||||
Padding="2,0,2,0"
|
||||
VerticalAlignment="Center"
|
||||
BorderBrush="{ThemeResource ControlStrokeColorSecondary}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{StaticResource ControlCornerRadius}">
|
||||
<TextBlock
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="10"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind Usage, Mode=OneWay, Converter={StaticResource PasteAIUsageToStringConverter}}" />
|
||||
</Border>
|
||||
<Border
|
||||
Padding="2,0,2,0"
|
||||
VerticalAlignment="Center"
|
||||
BorderBrush="{ThemeResource ControlStrokeColorSecondary}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{StaticResource ControlCornerRadius}"
|
||||
Visibility="{x:Bind IsLocalModel, Mode=OneWay}">
|
||||
<TextBlock
|
||||
x:Uid="LocalModelBadge"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="10"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
<!--<Border
|
||||
Grid.Column="2"
|
||||
Padding="2,0,2,0"
|
||||
|
||||
@@ -164,7 +164,7 @@ namespace AdvancedPaste.Controls
|
||||
return;
|
||||
}
|
||||
|
||||
var flyout = FlyoutBase.GetAttachedFlyout(AIProviderButton);
|
||||
var flyout = AIProviderButton.Flyout;
|
||||
|
||||
if (AIProviderListView.SelectedItem is not PasteAIProviderDefinition provider)
|
||||
{
|
||||
@@ -180,7 +180,6 @@ namespace AdvancedPaste.Controls
|
||||
if (ViewModel.SetActiveProviderCommand.CanExecute(provider))
|
||||
{
|
||||
await ViewModel.SetActiveProviderCommand.ExecuteAsync(provider);
|
||||
SyncProviderSelection();
|
||||
}
|
||||
|
||||
flyout?.Hide();
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using AdvancedPaste.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace AdvancedPaste.Converters;
|
||||
|
||||
public sealed partial class PasteAIUsageToStringConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
var usage = value switch
|
||||
{
|
||||
string s => PasteAIUsageExtensions.FromConfigString(s),
|
||||
PasteAIUsage u => u,
|
||||
_ => PasteAIUsage.ChatCompletion,
|
||||
};
|
||||
|
||||
return ResourceLoaderInstance.ResourceLoader.GetString($"PasteAIUsage_{usage}");
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,13 @@ internal static class DataPackageHelpers
|
||||
return dataPackage;
|
||||
}
|
||||
|
||||
internal static DataPackage CreateFromImage(RandomAccessStreamReference imageStreamRef)
|
||||
{
|
||||
DataPackage dataPackage = new();
|
||||
dataPackage.SetBitmap(imageStreamRef);
|
||||
return dataPackage;
|
||||
}
|
||||
|
||||
internal static async Task<DataPackage> CreateFromFileAsync(string fileName)
|
||||
{
|
||||
var storageFile = await StorageFile.GetFileFromPathAsync(fileName);
|
||||
|
||||
@@ -168,6 +168,9 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
ModelPath = provider.ModelPath,
|
||||
SystemPrompt = systemPrompt,
|
||||
ModerationEnabled = provider.ModerationEnabled,
|
||||
Usage = provider.UsageKind,
|
||||
ImageWidth = provider.ImageWidth,
|
||||
ImageHeight = provider.ImageHeight,
|
||||
};
|
||||
|
||||
return providerConfig;
|
||||
|
||||
@@ -28,5 +28,11 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
public string SystemPrompt { get; set; }
|
||||
|
||||
public bool ModerationEnabled { get; set; }
|
||||
|
||||
public PasteAIUsage Usage { get; set; }
|
||||
|
||||
public int ImageWidth { get; set; }
|
||||
|
||||
public int ImageHeight { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AdvancedPaste.Helpers;
|
||||
@@ -16,6 +17,7 @@ using Microsoft.SemanticKernel.Connectors.Google;
|
||||
using Microsoft.SemanticKernel.Connectors.MistralAI;
|
||||
using Microsoft.SemanticKernel.Connectors.Ollama;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using Microsoft.SemanticKernel.TextToImage;
|
||||
|
||||
namespace AdvancedPaste.Services.CustomActions
|
||||
{
|
||||
@@ -73,6 +75,69 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
|
||||
var executionSettings = CreateExecutionSettings();
|
||||
var kernel = CreateKernel();
|
||||
|
||||
switch (_config.Usage)
|
||||
{
|
||||
case PasteAIUsage.TextToImage:
|
||||
var imageDescription = string.IsNullOrWhiteSpace(prompt) ? inputText : $"{inputText}. {prompt}";
|
||||
return await ProcessTextToImageAsync(kernel, imageDescription, cancellationToken);
|
||||
case PasteAIUsage.ChatCompletion:
|
||||
default:
|
||||
var userMessageContent = $"""
|
||||
User instructions:
|
||||
{prompt}
|
||||
|
||||
Clipboard Content:
|
||||
{inputText}
|
||||
|
||||
Output:
|
||||
""";
|
||||
return await ProcessChatCompletionAsync(kernel, request, userMessageContent, systemPrompt, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> ProcessTextToImageAsync(Kernel kernel, string userMessageContent, CancellationToken cancellationToken)
|
||||
{
|
||||
#pragma warning disable SKEXP0001
|
||||
var imageService = kernel.GetRequiredService<ITextToImageService>();
|
||||
var width = _config.ImageWidth > 0 ? _config.ImageWidth : 1024;
|
||||
var height = _config.ImageHeight > 0 ? _config.ImageHeight : 1024;
|
||||
var settings = new OpenAITextToImageExecutionSettings
|
||||
{
|
||||
Size = (width, height),
|
||||
};
|
||||
|
||||
var generatedImages = await imageService.GetImageContentsAsync(new TextContent(userMessageContent), settings, cancellationToken: cancellationToken);
|
||||
|
||||
if (generatedImages.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("No image generated.");
|
||||
}
|
||||
|
||||
var imageContent = generatedImages[0];
|
||||
|
||||
if (imageContent.Data.HasValue)
|
||||
{
|
||||
var base64 = Convert.ToBase64String(imageContent.Data.Value.ToArray());
|
||||
return $"data:{imageContent.MimeType ?? "image/png"};base64,{base64}";
|
||||
}
|
||||
else if (imageContent.Uri != null)
|
||||
{
|
||||
using var client = new HttpClient();
|
||||
var imageBytes = await client.GetByteArrayAsync(imageContent.Uri, cancellationToken);
|
||||
var base64 = Convert.ToBase64String(imageBytes);
|
||||
return $"data:image/png;base64,{base64}";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Generated image contains no data.");
|
||||
}
|
||||
#pragma warning restore SKEXP0001
|
||||
}
|
||||
|
||||
private async Task<string> ProcessChatCompletionAsync(Kernel kernel, PasteAIRequest request, string userMessageContent, string systemPrompt, CancellationToken cancellationToken)
|
||||
{
|
||||
var executionSettings = CreateExecutionSettings();
|
||||
var modelId = _config.Model;
|
||||
|
||||
IChatCompletionService chatService;
|
||||
@@ -95,29 +160,20 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
var chatHistory = new ChatHistory();
|
||||
chatHistory.AddSystemMessage(systemPrompt);
|
||||
|
||||
if (imageBytes != null)
|
||||
if (request.ImageBytes != null)
|
||||
{
|
||||
var collection = new ChatMessageContentItemCollection();
|
||||
if (!string.IsNullOrWhiteSpace(inputText))
|
||||
if (!string.IsNullOrWhiteSpace(request.InputText))
|
||||
{
|
||||
collection.Add(new TextContent($"Clipboard Content:\n{inputText}"));
|
||||
collection.Add(new TextContent($"Clipboard Content:\n{request.InputText}"));
|
||||
}
|
||||
|
||||
collection.Add(new ImageContent(imageBytes, request.ImageMimeType ?? "image/png"));
|
||||
collection.Add(new TextContent($"User instructions:\n{prompt}\n\nOutput:"));
|
||||
collection.Add(new ImageContent(request.ImageBytes, request.ImageMimeType ?? "image/png"));
|
||||
collection.Add(new TextContent($"User instructions:\n{request.Prompt}\n\nOutput:"));
|
||||
chatHistory.AddUserMessage(collection);
|
||||
}
|
||||
else
|
||||
{
|
||||
var userMessageContent = $"""
|
||||
User instructions:
|
||||
{prompt}
|
||||
|
||||
Clipboard Content:
|
||||
{inputText}
|
||||
|
||||
Output:
|
||||
""";
|
||||
chatHistory.AddUserMessage(userMessageContent);
|
||||
}
|
||||
|
||||
@@ -142,11 +198,31 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
switch (_serviceType)
|
||||
{
|
||||
case AIServiceType.OpenAI:
|
||||
kernelBuilder.AddOpenAIChatCompletion(_config.Model, apiKey, serviceId: _config.Model);
|
||||
if (_config.Usage == PasteAIUsage.TextToImage)
|
||||
{
|
||||
#pragma warning disable SKEXP0010
|
||||
kernelBuilder.AddOpenAITextToImage(apiKey, modelId: _config.Model);
|
||||
#pragma warning restore SKEXP0010
|
||||
}
|
||||
else
|
||||
{
|
||||
kernelBuilder.AddOpenAIChatCompletion(_config.Model, apiKey, serviceId: _config.Model);
|
||||
}
|
||||
|
||||
break;
|
||||
case AIServiceType.AzureOpenAI:
|
||||
var deploymentName = string.IsNullOrWhiteSpace(_config.DeploymentName) ? _config.Model : _config.DeploymentName;
|
||||
kernelBuilder.AddAzureOpenAIChatCompletion(deploymentName, RequireEndpoint(endpoint, _serviceType), apiKey, serviceId: _config.Model);
|
||||
if (_config.Usage == PasteAIUsage.TextToImage)
|
||||
{
|
||||
#pragma warning disable SKEXP0010
|
||||
kernelBuilder.AddAzureOpenAITextToImage(deploymentName, RequireEndpoint(endpoint, _serviceType), apiKey);
|
||||
#pragma warning restore SKEXP0010
|
||||
}
|
||||
else
|
||||
{
|
||||
kernelBuilder.AddAzureOpenAIChatCompletion(deploymentName, RequireEndpoint(endpoint, _serviceType), apiKey, serviceId: _config.Model);
|
||||
}
|
||||
|
||||
break;
|
||||
case AIServiceType.Mistral:
|
||||
kernelBuilder.AddMistralChatCompletion(_config.Model, apiKey: apiKey);
|
||||
|
||||
@@ -372,4 +372,10 @@
|
||||
<value>Unable to load Foundry Local model: {0}</value>
|
||||
<comment>{0} is the model identifier. Do not translate {0}.</comment>
|
||||
</data>
|
||||
<data name="PasteAIUsage_ChatCompletion" xml:space="preserve">
|
||||
<value>Chat completion</value>
|
||||
</data>
|
||||
<data name="PasteAIUsage_TextToImage" xml:space="preserve">
|
||||
<value>Text to image</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -614,8 +615,40 @@ namespace AdvancedPaste.ViewModels
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(HasCustomFormatImage))]
|
||||
[NotifyPropertyChangedFor(nameof(HasCustomFormatText))]
|
||||
[NotifyPropertyChangedFor(nameof(CustomFormatImageResult))]
|
||||
private string _customFormatResult;
|
||||
|
||||
public bool HasCustomFormatImage => CustomFormatResult?.StartsWith("data:image", StringComparison.OrdinalIgnoreCase) ?? false;
|
||||
|
||||
public bool HasCustomFormatText => !HasCustomFormatImage;
|
||||
|
||||
public ImageSource CustomFormatImageResult
|
||||
{
|
||||
get
|
||||
{
|
||||
if (HasCustomFormatImage && !string.IsNullOrEmpty(CustomFormatResult))
|
||||
{
|
||||
try
|
||||
{
|
||||
var base64Data = CustomFormatResult.Split(',')[1];
|
||||
var bytes = Convert.FromBase64String(base64Data);
|
||||
var stream = new System.IO.MemoryStream(bytes);
|
||||
var image = new BitmapImage();
|
||||
image.SetSource(stream.AsRandomAccessStream());
|
||||
return image;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Failed to create image source from data URI", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public async Task PasteCustomAsync()
|
||||
{
|
||||
@@ -623,7 +656,25 @@ namespace AdvancedPaste.ViewModels
|
||||
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
await CopyPasteAndHideAsync(DataPackageHelpers.CreateFromText(text));
|
||||
if (text.StartsWith("data:image", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
try
|
||||
{
|
||||
var base64Data = text.Split(',')[1];
|
||||
var bytes = Convert.FromBase64String(base64Data);
|
||||
var stream = new System.IO.MemoryStream(bytes);
|
||||
var dataPackage = DataPackageHelpers.CreateFromImage(Windows.Storage.Streams.RandomAccessStreamReference.CreateFromStream(stream.AsRandomAccessStream()));
|
||||
await CopyPasteAndHideAsync(dataPackage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Failed to paste image from data URI", ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await CopyPasteAndHideAsync(DataPackageHelpers.CreateFromText(text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,7 +712,7 @@ namespace AdvancedPaste.ViewModels
|
||||
[RelayCommand]
|
||||
public void OpenSettings()
|
||||
{
|
||||
SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.AdvancedPaste);
|
||||
SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.AdvancedPaste, true);
|
||||
GetMainWindow()?.Close();
|
||||
}
|
||||
|
||||
@@ -895,11 +946,6 @@ namespace AdvancedPaste.ViewModels
|
||||
Logger.LogError("Failed to activate AI provider", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateAIProviderActiveFlags();
|
||||
OnPropertyChanged(nameof(AIProviders));
|
||||
NotifyActiveProviderChanged();
|
||||
EnqueueRefreshPasteFormats();
|
||||
}
|
||||
|
||||
public async Task CancelPasteActionAsync()
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
#include <common/utils/logger_helper.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
#include <common/utils/gpo.h>
|
||||
#include <common/utils/EventWaiter.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cwctype>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/)
|
||||
@@ -103,9 +101,6 @@ private:
|
||||
bool m_is_advanced_ai_enabled = false;
|
||||
bool m_preview_custom_format_output = true;
|
||||
|
||||
// Event listening for external triggers (e.g., from CmdPal extension)
|
||||
EventWaiter m_triggerEventWaiter;
|
||||
|
||||
Hotkey parse_single_hotkey(const wchar_t* keyName, const winrt::Windows::Data::Json::JsonObject& settingsObject)
|
||||
{
|
||||
try
|
||||
@@ -784,17 +779,6 @@ public:
|
||||
Trace::AdvancedPaste_Enable(true);
|
||||
m_enabled = true;
|
||||
m_process_manager.start();
|
||||
|
||||
// Start listening for external trigger event so we can invoke the same logic as the hotkey.
|
||||
// Note: Use start() directly instead of constructor + move assignment to avoid dangling this pointer in the thread.
|
||||
m_triggerEventWaiter.start(CommonSharedConstants::ADVANCED_PASTE_SHOW_UI_EVENT, [this](DWORD) {
|
||||
// Same logic as hotkeyId == 1 (m_advanced_paste_ui_hotkey)
|
||||
Logger::trace(L"AdvancedPaste ShowUI event triggered");
|
||||
m_process_manager.start();
|
||||
m_process_manager.bring_to_front();
|
||||
m_process_manager.send_message(CommonSharedConstants::ADVANCED_PASTE_SHOW_UI_MESSAGE);
|
||||
Trace::AdvancedPaste_Invoked(L"AdvancedPasteUIEvent");
|
||||
});
|
||||
};
|
||||
|
||||
void Disable(bool traceEvent)
|
||||
@@ -803,9 +787,6 @@ public:
|
||||
{
|
||||
m_process_manager.stop();
|
||||
|
||||
// Stop event listening
|
||||
m_triggerEventWaiter.stop();
|
||||
|
||||
if (traceEvent)
|
||||
{
|
||||
Trace::AdvancedPaste_Enable(false);
|
||||
|
||||
@@ -146,7 +146,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
m_showEventWaiter.start(CommonSharedConstants::SHOW_ENVIRONMENT_VARIABLES_EVENT, [&](DWORD err) {
|
||||
m_showEventWaiter = EventWaiter(CommonSharedConstants::SHOW_ENVIRONMENT_VARIABLES_EVENT, [&](int err) {
|
||||
if (m_enabled && err == ERROR_SUCCESS)
|
||||
{
|
||||
Logger::trace(L"{} event was signaled", CommonSharedConstants::SHOW_ENVIRONMENT_VARIABLES_EVENT);
|
||||
@@ -164,7 +164,7 @@ public:
|
||||
}
|
||||
});
|
||||
|
||||
m_showAdminEventWaiter.start(CommonSharedConstants::SHOW_ENVIRONMENT_VARIABLES_ADMIN_EVENT, [&](DWORD err) {
|
||||
m_showAdminEventWaiter = EventWaiter(CommonSharedConstants::SHOW_ENVIRONMENT_VARIABLES_ADMIN_EVENT, [&](int err) {
|
||||
if (m_enabled && err == ERROR_SUCCESS)
|
||||
{
|
||||
Logger::trace(L"{} event was signaled", CommonSharedConstants::SHOW_ENVIRONMENT_VARIABLES_ADMIN_EVENT);
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Hosts
|
||||
services.AddSingleton<IElevationHelper, ElevationHelper>();
|
||||
services.AddSingleton<OpenSettingsFunction>(() =>
|
||||
{
|
||||
SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.Hosts);
|
||||
SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.Hosts, true);
|
||||
});
|
||||
|
||||
services.AddSingleton<MainViewModel, MainViewModel>();
|
||||
|
||||
@@ -155,7 +155,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
m_showEventWaiter.start(CommonSharedConstants::SHOW_HOSTS_EVENT, [&](DWORD err)
|
||||
m_showEventWaiter = EventWaiter(CommonSharedConstants::SHOW_HOSTS_EVENT, [&](int err)
|
||||
{
|
||||
if (m_enabled && err == ERROR_SUCCESS)
|
||||
{
|
||||
@@ -174,7 +174,7 @@ public:
|
||||
}
|
||||
});
|
||||
|
||||
m_showAdminEventWaiter.start(CommonSharedConstants::SHOW_HOSTS_ADMIN_EVENT, [&](DWORD err)
|
||||
m_showAdminEventWaiter = EventWaiter(CommonSharedConstants::SHOW_HOSTS_ADMIN_EVENT, [&](int err)
|
||||
{
|
||||
if (m_enabled && err == ERROR_SUCCESS)
|
||||
{
|
||||
|
||||
@@ -168,6 +168,9 @@
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(CoreLibraryDependencies);%(AdditionalDependencies);advapi32.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
@@ -219,4 +222,4 @@
|
||||
<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>
|
||||
</Project>
|
||||
@@ -8,8 +8,6 @@
|
||||
#include <codecvt>
|
||||
#include <common/utils/logger_helper.h>
|
||||
#include "ThemeHelper.h"
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
@@ -105,18 +103,12 @@ private:
|
||||
HANDLE m_force_light_event_handle;
|
||||
HANDLE m_force_dark_event_handle;
|
||||
HANDLE m_manual_override_event_handle;
|
||||
HANDLE m_toggle_event_handle{ nullptr };
|
||||
std::thread m_toggle_thread;
|
||||
std::atomic<bool> m_toggle_thread_running{ false };
|
||||
|
||||
static const constexpr int NUM_DEFAULT_HOTKEYS = 4;
|
||||
|
||||
Hotkey m_toggle_theme_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'D' };
|
||||
|
||||
void init_settings();
|
||||
void ToggleTheme();
|
||||
void StartToggleListener();
|
||||
void StopToggleListener();
|
||||
|
||||
public:
|
||||
LightSwitchInterface()
|
||||
@@ -126,7 +118,6 @@ public:
|
||||
m_force_light_event_handle = CreateDefaultEvent(L"POWERTOYS_LIGHTSWITCH_FORCE_LIGHT");
|
||||
m_force_dark_event_handle = CreateDefaultEvent(L"POWERTOYS_LIGHTSWITCH_FORCE_DARK");
|
||||
m_manual_override_event_handle = CreateEventW(nullptr, TRUE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
|
||||
m_toggle_event_handle = CreateDefaultEvent(L"Local\\PowerToys-LightSwitch-ToggleEvent-d8dc2f29-8c94-4ca1-8c5f-3e2b1e3c4f5a");
|
||||
|
||||
init_settings();
|
||||
};
|
||||
@@ -139,8 +130,6 @@ public:
|
||||
// Destroy the powertoy and free memory
|
||||
virtual void destroy() override
|
||||
{
|
||||
// Ensure worker threads/process handles are cleaned up before destruction
|
||||
disable();
|
||||
delete this;
|
||||
}
|
||||
|
||||
@@ -455,8 +444,6 @@ public:
|
||||
Logger::info(L"Light Switch process launched successfully (PID: {}).", pi.dwProcessId);
|
||||
m_process = pi.hProcess;
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
StartToggleListener();
|
||||
}
|
||||
|
||||
// Disable the powertoy
|
||||
@@ -482,8 +469,6 @@ public:
|
||||
CloseHandle(m_process);
|
||||
m_process = nullptr;
|
||||
}
|
||||
|
||||
StopToggleListener();
|
||||
}
|
||||
|
||||
// Returns if the powertoys is enabled
|
||||
@@ -545,8 +530,31 @@ public:
|
||||
}
|
||||
else if (hotkeyId == 0)
|
||||
{
|
||||
// get current will return true if in light mode; otherwise false
|
||||
Logger::info(L"[Light Switch] Hotkey triggered: Toggle Theme");
|
||||
ToggleTheme();
|
||||
if (g_settings.m_changeSystem)
|
||||
{
|
||||
SetSystemTheme(!GetCurrentSystemTheme());
|
||||
}
|
||||
if (g_settings.m_changeApps)
|
||||
{
|
||||
SetAppsTheme(!GetCurrentAppsTheme());
|
||||
}
|
||||
|
||||
if (!m_manual_override_event_handle)
|
||||
{
|
||||
m_manual_override_event_handle = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
|
||||
if (!m_manual_override_event_handle)
|
||||
{
|
||||
m_manual_override_event_handle = CreateEventW(nullptr, TRUE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
|
||||
}
|
||||
}
|
||||
|
||||
if (m_manual_override_event_handle)
|
||||
{
|
||||
SetEvent(m_manual_override_event_handle);
|
||||
Logger::debug(L"[Light Switch] Manual override event set");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -559,80 +567,8 @@ public:
|
||||
{
|
||||
return WaitForSingleObject(m_process, 0) == WAIT_TIMEOUT;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void LightSwitchInterface::ToggleTheme()
|
||||
{
|
||||
if (g_settings.m_changeSystem)
|
||||
{
|
||||
SetSystemTheme(!GetCurrentSystemTheme());
|
||||
}
|
||||
if (g_settings.m_changeApps)
|
||||
{
|
||||
SetAppsTheme(!GetCurrentAppsTheme());
|
||||
}
|
||||
|
||||
if (!m_manual_override_event_handle)
|
||||
{
|
||||
m_manual_override_event_handle = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
|
||||
if (!m_manual_override_event_handle)
|
||||
{
|
||||
m_manual_override_event_handle = CreateEventW(nullptr, TRUE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
|
||||
}
|
||||
}
|
||||
|
||||
if (m_manual_override_event_handle)
|
||||
{
|
||||
SetEvent(m_manual_override_event_handle);
|
||||
Logger::debug(L"[Light Switch] Manual override event set");
|
||||
}
|
||||
}
|
||||
|
||||
void LightSwitchInterface::StartToggleListener()
|
||||
{
|
||||
if (m_toggle_thread_running || !m_toggle_event_handle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_toggle_thread_running = true;
|
||||
m_toggle_thread = std::thread([this]() {
|
||||
while (m_toggle_thread_running)
|
||||
{
|
||||
const DWORD wait_result = WaitForSingleObject(m_toggle_event_handle, 500);
|
||||
if (!m_toggle_thread_running)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (wait_result == WAIT_OBJECT_0)
|
||||
{
|
||||
ToggleTheme();
|
||||
ResetEvent(m_toggle_event_handle);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void LightSwitchInterface::StopToggleListener()
|
||||
{
|
||||
if (!m_toggle_thread_running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_toggle_thread_running = false;
|
||||
if (m_toggle_event_handle)
|
||||
{
|
||||
SetEvent(m_toggle_event_handle);
|
||||
}
|
||||
if (m_toggle_thread.joinable())
|
||||
{
|
||||
m_toggle_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring utf8_to_wstring(const std::string& str)
|
||||
{
|
||||
if (str.empty())
|
||||
@@ -710,4 +646,4 @@ void LightSwitchInterface::init_settings()
|
||||
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
||||
{
|
||||
return new LightSwitchInterface();
|
||||
}
|
||||
}
|
||||
@@ -149,7 +149,7 @@ public:
|
||||
init_settings();
|
||||
|
||||
triggerEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::MEASURE_TOOL_TRIGGER_EVENT);
|
||||
triggerEventWaiter.start(CommonSharedConstants::MEASURE_TOOL_TRIGGER_EVENT, [this](DWORD) {
|
||||
triggerEventWaiter = EventWaiter(CommonSharedConstants::MEASURE_TOOL_TRIGGER_EVENT, [this](int) {
|
||||
on_hotkey(0);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "../../../common/utils/resources.h"
|
||||
#include "../../../common/logger/logger.h"
|
||||
#include "../../../common/utils/logger_helper.h"
|
||||
#include "../../../common/interop/shared_constants.h"
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
@@ -109,12 +108,6 @@ private:
|
||||
// Hotkey
|
||||
Hotkey m_activationHotkey{};
|
||||
|
||||
// Event-driven trigger support (for CmdPal/automation)
|
||||
HANDLE m_triggerEventHandle = nullptr;
|
||||
HANDLE m_terminateEventHandle = nullptr;
|
||||
std::thread m_eventThread;
|
||||
std::atomic_bool m_listening{ false };
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
CursorWrap()
|
||||
@@ -128,8 +121,7 @@ public:
|
||||
// Destroy the powertoy and free memory
|
||||
virtual void destroy() override
|
||||
{
|
||||
// Ensure hooks/threads/handles are torn down before deletion
|
||||
disable();
|
||||
StopMouseHook();
|
||||
g_cursorWrapInstance = nullptr; // Clear global instance pointer
|
||||
delete this;
|
||||
}
|
||||
@@ -203,54 +195,11 @@ public:
|
||||
{
|
||||
m_enabled = true;
|
||||
Trace::EnableCursorWrap(true);
|
||||
|
||||
// Start listening for external trigger event so we can invoke the same logic as the activation hotkey.
|
||||
m_triggerEventHandle = CreateEventW(nullptr, false, false, CommonSharedConstants::CURSOR_WRAP_TRIGGER_EVENT);
|
||||
m_terminateEventHandle = CreateEventW(nullptr, false, false, nullptr);
|
||||
if (m_triggerEventHandle && m_terminateEventHandle)
|
||||
{
|
||||
m_listening = true;
|
||||
m_eventThread = std::thread([this]() {
|
||||
HANDLE handles[2] = { m_triggerEventHandle, m_terminateEventHandle };
|
||||
|
||||
// WH_MOUSE_LL callbacks are delivered to the thread that installed the hook.
|
||||
// Ensure this thread has a message queue and pumps messages while the hook is active.
|
||||
MSG msg;
|
||||
PeekMessage(&msg, nullptr, WM_USER, WM_USER, PM_NOREMOVE);
|
||||
|
||||
StartMouseHook();
|
||||
Logger::info("CursorWrap enabled - mouse hook started");
|
||||
|
||||
while (m_listening)
|
||||
{
|
||||
auto res = MsgWaitForMultipleObjects(2, handles, false, INFINITE, QS_ALLINPUT);
|
||||
if (!m_listening)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (res == WAIT_OBJECT_0)
|
||||
{
|
||||
ToggleMouseHook();
|
||||
}
|
||||
else if (res == WAIT_OBJECT_0 + 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StopMouseHook();
|
||||
Logger::info("CursorWrap event listener stopped");
|
||||
});
|
||||
}
|
||||
|
||||
// Always start the mouse hook when the module is enabled
|
||||
// This ensures cursor wrapping is active immediately after enabling
|
||||
StartMouseHook();
|
||||
Logger::info("CursorWrap enabled - mouse hook started");
|
||||
}
|
||||
|
||||
// Disable the powertoy
|
||||
@@ -258,26 +207,8 @@ public:
|
||||
{
|
||||
m_enabled = false;
|
||||
Trace::EnableCursorWrap(false);
|
||||
|
||||
m_listening = false;
|
||||
if (m_terminateEventHandle)
|
||||
{
|
||||
SetEvent(m_terminateEventHandle);
|
||||
}
|
||||
if (m_eventThread.joinable())
|
||||
{
|
||||
m_eventThread.join();
|
||||
}
|
||||
if (m_triggerEventHandle)
|
||||
{
|
||||
CloseHandle(m_triggerEventHandle);
|
||||
m_triggerEventHandle = nullptr;
|
||||
}
|
||||
if (m_terminateEventHandle)
|
||||
{
|
||||
CloseHandle(m_terminateEventHandle);
|
||||
m_terminateEventHandle = nullptr;
|
||||
}
|
||||
StopMouseHook();
|
||||
Logger::info("CursorWrap disabled - mouse hook stopped");
|
||||
}
|
||||
|
||||
// Returns if the powertoys is enabled
|
||||
@@ -309,19 +240,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Toggle on the thread that owns the WH_MOUSE_LL hook (the event listener thread).
|
||||
if (m_triggerEventHandle)
|
||||
{
|
||||
return SetEvent(m_triggerEventHandle);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
void ToggleMouseHook()
|
||||
{
|
||||
// Toggle cursor wrapping.
|
||||
// Toggle cursor wrapping
|
||||
if (m_hookActive)
|
||||
{
|
||||
StopMouseHook();
|
||||
@@ -334,8 +253,11 @@ public:
|
||||
RunComprehensiveTests();
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Load the settings file.
|
||||
void init_settings()
|
||||
{
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#include <common/utils/logger_helper.h>
|
||||
#include <common/utils/color.h>
|
||||
#include <common/utils/string_utils.h>
|
||||
#include <common/utils/EventWaiter.h>
|
||||
#include <common/interop/shared_constants.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -71,9 +69,6 @@ private:
|
||||
// Find My Mouse specific settings
|
||||
FindMyMouseSettings m_findMyMouseSettings;
|
||||
|
||||
// Event-driven trigger support
|
||||
EventWaiter m_triggerEventWaiter;
|
||||
|
||||
// Load initial settings from the persisted values.
|
||||
void init_settings();
|
||||
|
||||
@@ -91,8 +86,6 @@ public:
|
||||
// Destroy the powertoy and free memory
|
||||
virtual void destroy() override
|
||||
{
|
||||
// Ensure threads/handles are cleaned up before destruction
|
||||
disable();
|
||||
delete this;
|
||||
}
|
||||
|
||||
@@ -157,11 +150,6 @@ public:
|
||||
m_enabled = true;
|
||||
Trace::EnableFindMyMouse(true);
|
||||
std::thread([=]() { FindMyMouseMain(m_hModule, m_findMyMouseSettings); }).detach();
|
||||
|
||||
// Start listening for external trigger event so we can invoke the same logic as the hotkey.
|
||||
m_triggerEventWaiter.start(CommonSharedConstants::FIND_MY_MOUSE_TRIGGER_EVENT, [this](DWORD) {
|
||||
OnHotkeyEx();
|
||||
});
|
||||
}
|
||||
|
||||
// Disable the powertoy
|
||||
@@ -170,8 +158,6 @@ public:
|
||||
m_enabled = false;
|
||||
Trace::EnableFindMyMouse(false);
|
||||
FindMyMouseDisable();
|
||||
|
||||
m_triggerEventWaiter.stop();
|
||||
}
|
||||
|
||||
// Returns if the powertoys is enabled
|
||||
@@ -230,7 +216,7 @@ inline static uint8_t LegacyOpacityToAlpha(int overlayOpacityPercent)
|
||||
overlayOpacityPercent = 100;
|
||||
}
|
||||
|
||||
// Round to nearest integer (0–255)
|
||||
// Round to nearest integer (0<EFBFBD>255)
|
||||
return static_cast<uint8_t>((overlayOpacityPercent * 255 + 50) / 100);
|
||||
}
|
||||
|
||||
@@ -546,4 +532,4 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings)
|
||||
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
||||
{
|
||||
return new FindMyMouse();
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,6 @@
|
||||
#include "trace.h"
|
||||
#include "MouseHighlighter.h"
|
||||
#include "common/utils/color.h"
|
||||
#include <common/utils/EventWaiter.h>
|
||||
#include <common/interop/shared_constants.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -63,9 +61,6 @@ private:
|
||||
// Mouse Highlighter specific settings
|
||||
MouseHighlighterSettings m_highlightSettings;
|
||||
|
||||
// Event-driven trigger support
|
||||
EventWaiter m_triggerEventWaiter;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
MouseHighlighter()
|
||||
@@ -77,8 +72,6 @@ public:
|
||||
// Destroy the powertoy and free memory
|
||||
virtual void destroy() override
|
||||
{
|
||||
// Tear down threads/handles before deletion to avoid abort() on joinable threads during shutdown
|
||||
disable();
|
||||
delete this;
|
||||
}
|
||||
|
||||
@@ -139,11 +132,6 @@ public:
|
||||
m_enabled = true;
|
||||
Trace::EnableMouseHighlighter(true);
|
||||
std::thread([=]() { MouseHighlighterMain(m_hModule, m_highlightSettings); }).detach();
|
||||
|
||||
// Start listening for external trigger event so we can invoke the same logic as the hotkey.
|
||||
m_triggerEventWaiter.start(CommonSharedConstants::MOUSE_HIGHLIGHTER_TRIGGER_EVENT, [this](DWORD) {
|
||||
OnHotkeyEx();
|
||||
});
|
||||
}
|
||||
|
||||
// Disable the powertoy
|
||||
@@ -152,8 +140,6 @@ public:
|
||||
m_enabled = false;
|
||||
Trace::EnableMouseHighlighter(false);
|
||||
MouseHighlighterDisable();
|
||||
|
||||
m_triggerEventWaiter.stop();
|
||||
}
|
||||
|
||||
// Returns if the powertoys is enabled
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "trace.h"
|
||||
#include "InclusiveCrosshairs.h"
|
||||
#include "common/utils/color.h"
|
||||
#include <common/utils/EventWaiter.h>
|
||||
#include <common/interop/shared_constants.h>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
@@ -125,9 +124,6 @@ private:
|
||||
// Mouse Pointer Crosshairs specific settings
|
||||
InclusiveCrosshairsSettings m_inclusiveCrosshairsSettings;
|
||||
|
||||
// Event-driven trigger support
|
||||
EventWaiter m_triggerEventWaiter;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
MousePointerCrosshairs()
|
||||
@@ -141,9 +137,11 @@ public:
|
||||
// Destroy the powertoy and free memory
|
||||
virtual void destroy() override
|
||||
{
|
||||
// Ensure all background threads/handles are torn down before destruction to avoid std::terminate/abort on joinable threads
|
||||
disable();
|
||||
UninstallKeyboardHook();
|
||||
StopXTimer();
|
||||
StopYTimer();
|
||||
g_instance.store(nullptr, std::memory_order_release);
|
||||
// Release shared state so worker threads (if any) exit when weak_ptr lock fails
|
||||
m_state.reset();
|
||||
delete this;
|
||||
}
|
||||
@@ -205,11 +203,6 @@ public:
|
||||
m_enabled = true;
|
||||
Trace::EnableMousePointerCrosshairs(true);
|
||||
std::thread([=]() { InclusiveCrosshairsMain(m_hModule, m_inclusiveCrosshairsSettings); }).detach();
|
||||
|
||||
// Start listening for external trigger event so we can invoke the same logic as the activation hotkey.
|
||||
m_triggerEventWaiter.start(CommonSharedConstants::MOUSE_CROSSHAIRS_TRIGGER_EVENT, [this](DWORD) {
|
||||
on_hotkey(0); // activation hotkey
|
||||
});
|
||||
}
|
||||
|
||||
// Disable the powertoy
|
||||
@@ -222,8 +215,6 @@ public:
|
||||
StopYTimer();
|
||||
m_glideState = 0;
|
||||
InclusiveCrosshairsDisable();
|
||||
|
||||
m_triggerEventWaiter.stop();
|
||||
}
|
||||
|
||||
// Returns if the powertoys is enabled
|
||||
@@ -910,4 +901,4 @@ private:
|
||||
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
||||
{
|
||||
return new MousePointerCrosshairs();
|
||||
}
|
||||
}
|
||||
@@ -426,7 +426,7 @@ public partial class OCROverlay : Window
|
||||
private void SettingsMenuItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowUtilities.CloseAllOCROverlays();
|
||||
SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.PowerOCR);
|
||||
SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.PowerOCR, false);
|
||||
}
|
||||
|
||||
private static bool CheckIfCheckingOrUnchecking(object? sender)
|
||||
|
||||
@@ -121,7 +121,7 @@ int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, _In_opt_ HINSTANCE /*hPrevInst
|
||||
else
|
||||
{
|
||||
auto mainThreadId = GetCurrentThreadId();
|
||||
exitEventWaiter.start(CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT, [mainThreadId, &window](DWORD err) {
|
||||
exitEventWaiter = EventWaiter(CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT, [mainThreadId, &window](int err) {
|
||||
if (err != ERROR_SUCCESS)
|
||||
{
|
||||
Logger::error(L"Failed to wait for {} event. {}", CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT, get_last_error_or_default(err));
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
}
|
||||
|
||||
triggerEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::SHORTCUT_GUIDE_TRIGGER_EVENT);
|
||||
triggerEventWaiter.start(CommonSharedConstants::SHORTCUT_GUIDE_TRIGGER_EVENT, [this](DWORD) {
|
||||
triggerEventWaiter = EventWaiter(CommonSharedConstants::SHORTCUT_GUIDE_TRIGGER_EVENT, [this](int) {
|
||||
OnHotkeyEx();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using PowerToys.ModuleContracts;
|
||||
using WorkspacesCsharpLibrary.Data;
|
||||
|
||||
namespace Workspaces.ModuleServices;
|
||||
|
||||
/// <summary>
|
||||
/// Workspaces-specific operations.
|
||||
/// </summary>
|
||||
public interface IWorkspaceService : IModuleService
|
||||
{
|
||||
Task<OperationResult> LaunchWorkspaceAsync(string workspaceId, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<OperationResult> LaunchEditorAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
Task<OperationResult> SnapshotAsync(string? targetPath = null, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<OperationResult<IReadOnlyList<ProjectWrapper>>> GetWorkspacesAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using Common.UI;
|
||||
using ManagedCommon;
|
||||
using PowerToys.Interop;
|
||||
using PowerToys.ModuleContracts;
|
||||
using WorkspacesCsharpLibrary.Data;
|
||||
|
||||
namespace Workspaces.ModuleServices;
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of workspace actions for reuse across hosts.
|
||||
/// </summary>
|
||||
public sealed class WorkspaceService : ModuleServiceBase, IWorkspaceService
|
||||
{
|
||||
public static WorkspaceService Instance { get; } = new();
|
||||
|
||||
public override string Key => SettingsDeepLink.SettingsWindow.Workspaces.ToString();
|
||||
|
||||
protected override SettingsDeepLink.SettingsWindow SettingsWindow => SettingsDeepLink.SettingsWindow.Workspaces;
|
||||
|
||||
public override Task<OperationResult> LaunchAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Treat launch as invoking the Workspaces editor.
|
||||
return LaunchEditorAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Task<OperationResult> LaunchEditorAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.WorkspacesLaunchEditorEvent());
|
||||
eventHandle.Set();
|
||||
return Task.FromResult(OperationResult.Ok());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(OperationResult.Fail($"Failed to launch Workspaces editor: {ex.Message}"));
|
||||
}
|
||||
}
|
||||
|
||||
public Task<OperationResult> LaunchWorkspaceAsync(string workspaceId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(workspaceId))
|
||||
{
|
||||
return Task.FromResult(OperationResult.Fail("Workspace id is required."));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var powertoysBaseDir = PowerToysPathResolver.GetPowerToysInstallPath();
|
||||
if (string.IsNullOrEmpty(powertoysBaseDir))
|
||||
{
|
||||
return Task.FromResult(OperationResult.Fail("PowerToys installation path not found."));
|
||||
}
|
||||
|
||||
var launcherPath = Path.Combine(powertoysBaseDir, "PowerToys.WorkspacesLauncher.exe");
|
||||
var startInfo = new ProcessStartInfo(launcherPath)
|
||||
{
|
||||
Arguments = workspaceId,
|
||||
UseShellExecute = true,
|
||||
};
|
||||
|
||||
Process.Start(startInfo);
|
||||
return Task.FromResult(OperationResult.Ok());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(OperationResult.Fail($"Failed to launch workspace: {ex.Message}"));
|
||||
}
|
||||
}
|
||||
|
||||
public Task<OperationResult> SnapshotAsync(string? targetPath = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Snapshot orchestration is not yet exposed via events; provide a clear failure for now.
|
||||
return Task.FromResult(OperationResult.Fail("Snapshot is not implemented for Workspaces."));
|
||||
}
|
||||
|
||||
public Task<OperationResult<IReadOnlyList<ProjectWrapper>>> GetWorkspacesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var items = WorkspacesStorage.Load();
|
||||
|
||||
return Task.FromResult(OperationResults.Ok<IReadOnlyList<ProjectWrapper>>(items));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(OperationResults.Fail<IReadOnlyList<ProjectWrapper>>($"Failed to read workspaces: {ex.Message}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\PowerToys.ModuleContracts\PowerToys.ModuleContracts.csproj" />
|
||||
<ProjectReference Include="..\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Data;
|
||||
|
||||
public struct ApplicationWrapper
|
||||
{
|
||||
public struct WindowPositionWrapper
|
||||
{
|
||||
[JsonPropertyName("x")]
|
||||
public int X { get; set; }
|
||||
|
||||
[JsonPropertyName("y")]
|
||||
public int Y { get; set; }
|
||||
|
||||
[JsonPropertyName("width")]
|
||||
public int Width { get; set; }
|
||||
|
||||
[JsonPropertyName("height")]
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
[JsonPropertyName("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonPropertyName("application")]
|
||||
public string Application { get; set; }
|
||||
|
||||
[JsonPropertyName("application-path")]
|
||||
public string ApplicationPath { get; set; }
|
||||
|
||||
[JsonPropertyName("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[JsonPropertyName("package-full-name")]
|
||||
public string PackageFullName { get; set; }
|
||||
|
||||
[JsonPropertyName("app-user-model-id")]
|
||||
public string AppUserModelId { get; set; }
|
||||
|
||||
[JsonPropertyName("pwa-app-id")]
|
||||
public string PwaAppId { get; set; }
|
||||
|
||||
[JsonPropertyName("command-line-arguments")]
|
||||
public string CommandLineArguments { get; set; }
|
||||
|
||||
[JsonPropertyName("is-elevated")]
|
||||
public bool IsElevated { get; set; }
|
||||
|
||||
[JsonPropertyName("can-launch-elevated")]
|
||||
public bool CanLaunchElevated { get; set; }
|
||||
|
||||
[JsonPropertyName("minimized")]
|
||||
public bool Minimized { get; set; }
|
||||
|
||||
[JsonPropertyName("maximized")]
|
||||
public bool Maximized { get; set; }
|
||||
|
||||
[JsonPropertyName("position")]
|
||||
public WindowPositionWrapper Position { get; set; }
|
||||
|
||||
[JsonPropertyName("monitor")]
|
||||
public int Monitor { get; set; }
|
||||
|
||||
[JsonPropertyName("version")]
|
||||
public string Version { get; set; }
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Data;
|
||||
|
||||
public struct MonitorConfigurationWrapper
|
||||
{
|
||||
public struct MonitorRectWrapper
|
||||
{
|
||||
[JsonPropertyName("top")]
|
||||
public int Top { get; set; }
|
||||
|
||||
[JsonPropertyName("left")]
|
||||
public int Left { get; set; }
|
||||
|
||||
[JsonPropertyName("width")]
|
||||
public int Width { get; set; }
|
||||
|
||||
[JsonPropertyName("height")]
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
[JsonPropertyName("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonPropertyName("instance-id")]
|
||||
public string InstanceId { get; set; }
|
||||
|
||||
[JsonPropertyName("monitor-number")]
|
||||
public int MonitorNumber { get; set; }
|
||||
|
||||
[JsonPropertyName("dpi")]
|
||||
public int Dpi { get; set; }
|
||||
|
||||
[JsonPropertyName("monitor-rect-dpi-aware")]
|
||||
public MonitorRectWrapper MonitorRectDpiAware { get; set; }
|
||||
|
||||
[JsonPropertyName("monitor-rect-dpi-unaware")]
|
||||
public MonitorRectWrapper MonitorRectDpiUnaware { get; set; }
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using WorkspacesCsharpLibrary.Data;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Data;
|
||||
|
||||
public class ProjectData : WorkspacesEditorData<ProjectWrapper>
|
||||
{
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Data;
|
||||
|
||||
public struct ProjectWrapper
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public long CreationTime { get; set; }
|
||||
|
||||
public long LastLaunchedTime { get; set; }
|
||||
|
||||
public bool IsShortcutNeeded { get; set; }
|
||||
|
||||
public bool MoveExistingWindows { get; set; }
|
||||
|
||||
public List<MonitorConfigurationWrapper> MonitorConfiguration { get; set; }
|
||||
|
||||
public List<ApplicationWrapper> Applications { get; set; }
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using WorkspacesCsharpLibrary.Utils;
|
||||
using static WorkspacesCsharpLibrary.Data.WorkspacesData;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Data;
|
||||
|
||||
public class WorkspacesData : WorkspacesEditorData<WorkspacesListWrapper>
|
||||
{
|
||||
public string File => FolderUtils.DataFolder() + "\\workspaces.json";
|
||||
|
||||
public struct WorkspacesListWrapper
|
||||
{
|
||||
public List<ProjectWrapper> Workspaces { get; set; }
|
||||
}
|
||||
|
||||
public enum OrderBy
|
||||
{
|
||||
LastViewed = 0,
|
||||
Created = 1,
|
||||
Name = 2,
|
||||
Unknown = 3,
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
using WorkspacesCsharpLibrary.Utils;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Shared JSON serializer helper for Workspaces payloads.
|
||||
/// </summary>
|
||||
public class WorkspacesEditorData<T>
|
||||
{
|
||||
[RequiresUnreferencedCode("JSON serialization uses reflection-based serializer.")]
|
||||
[RequiresDynamicCode("JSON serialization uses reflection-based serializer.")]
|
||||
public T Read(string file)
|
||||
{
|
||||
IOUtils ioUtils = new();
|
||||
string data = ioUtils.ReadFile(file);
|
||||
return JsonSerializer.Deserialize<T>(data, WorkspacesJsonOptions.EditorOptions)!;
|
||||
}
|
||||
|
||||
[RequiresUnreferencedCode("JSON serialization uses reflection-based serializer.")]
|
||||
[RequiresDynamicCode("JSON serialization uses reflection-based serializer.")]
|
||||
public string Serialize(T data)
|
||||
{
|
||||
return JsonSerializer.Serialize(data, WorkspacesJsonOptions.EditorOptions);
|
||||
}
|
||||
|
||||
[RequiresUnreferencedCode("JSON serialization uses reflection-based serializer.")]
|
||||
[RequiresDynamicCode("JSON serialization uses reflection-based serializer.")]
|
||||
public T Deserialize(string json)
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(json, WorkspacesJsonOptions.EditorOptions)!;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json;
|
||||
using WorkspacesCsharpLibrary.Utils;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Data;
|
||||
|
||||
internal static class WorkspacesJsonOptions
|
||||
{
|
||||
internal static readonly JsonSerializerOptions EditorOptions = new()
|
||||
{
|
||||
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
||||
WriteIndented = true,
|
||||
};
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Lightweight reader for persisted workspaces.
|
||||
/// </summary>
|
||||
public static class WorkspacesStorage
|
||||
{
|
||||
public static IReadOnlyList<ProjectWrapper> Load()
|
||||
{
|
||||
var filePath = GetDefaultFilePath();
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(filePath);
|
||||
var data = JsonSerializer.Deserialize(json, WorkspacesStorageJsonContext.Default.WorkspacesFile);
|
||||
|
||||
if (data?.Workspaces == null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
return data.Workspaces
|
||||
.Where(ws => !string.IsNullOrWhiteSpace(ws.Id) && !string.IsNullOrWhiteSpace(ws.Name))
|
||||
.Select(ws => new ProjectWrapper
|
||||
{
|
||||
Id = ws.Id!,
|
||||
Name = ws.Name!,
|
||||
Applications = ws.Applications ?? new List<ApplicationWrapper>(),
|
||||
CreationTime = ws.CreationTime,
|
||||
LastLaunchedTime = ws.LastLaunchedTime,
|
||||
IsShortcutNeeded = ws.IsShortcutNeeded,
|
||||
MoveExistingWindows = ws.MoveExistingWindows,
|
||||
MonitorConfiguration = ws.MonitorConfiguration ?? new List<MonitorConfigurationWrapper>(),
|
||||
})
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Array.Empty<ProjectWrapper>();
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetDefaultFilePath()
|
||||
{
|
||||
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
return Path.Combine(localAppData, "Microsoft", "PowerToys", "Workspaces", "workspaces.json");
|
||||
}
|
||||
|
||||
internal sealed class WorkspacesFile
|
||||
{
|
||||
public List<WorkspaceProject> Workspaces { get; set; } = new();
|
||||
}
|
||||
|
||||
internal sealed class WorkspaceProject
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("applications")]
|
||||
public List<ApplicationWrapper> Applications { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("monitor-configuration")]
|
||||
public List<MonitorConfigurationWrapper> MonitorConfiguration { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("creation-time")]
|
||||
public long CreationTime { get; set; }
|
||||
|
||||
[JsonPropertyName("last-launched-time")]
|
||||
public long LastLaunchedTime { get; set; }
|
||||
|
||||
[JsonPropertyName("is-shortcut-needed")]
|
||||
public bool IsShortcutNeeded { get; set; }
|
||||
|
||||
[JsonPropertyName("move-existing-windows")]
|
||||
public bool MoveExistingWindows { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Data;
|
||||
|
||||
[JsonSourceGenerationOptions(PropertyNameCaseInsensitive = true)]
|
||||
[JsonSerializable(typeof(WorkspacesStorage.WorkspacesFile))]
|
||||
[JsonSerializable(typeof(WorkspacesStorage.WorkspaceProject))]
|
||||
[JsonSerializable(typeof(ApplicationWrapper))]
|
||||
[JsonSerializable(typeof(ApplicationWrapper.WindowPositionWrapper))]
|
||||
[JsonSerializable(typeof(MonitorConfigurationWrapper))]
|
||||
[JsonSerializable(typeof(MonitorConfigurationWrapper.MonitorRectWrapper))]
|
||||
internal sealed partial class WorkspacesStorageJsonContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
@@ -16,7 +16,7 @@ using Windows.Management.Deployment;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Models
|
||||
{
|
||||
public partial class BaseApplication : INotifyPropertyChanged, IDisposable
|
||||
public class BaseApplication : INotifyPropertyChanged, IDisposable
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json;
|
||||
using WorkspacesCsharpLibrary.Utils;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Utils;
|
||||
|
||||
public class DashCaseNamingPolicy : JsonNamingPolicy
|
||||
{
|
||||
public static DashCaseNamingPolicy Instance { get; } = new DashCaseNamingPolicy();
|
||||
|
||||
public override string ConvertName(string name)
|
||||
{
|
||||
return name.UpperCamelCaseToDashCase();
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Utils;
|
||||
|
||||
public class FolderUtils
|
||||
{
|
||||
public static string Desktop()
|
||||
{
|
||||
return Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||
}
|
||||
|
||||
public static string Temp()
|
||||
{
|
||||
return Path.GetTempPath();
|
||||
}
|
||||
|
||||
// Note: the same path should be used in SnapshotTool and Launcher
|
||||
public static string DataFolder()
|
||||
{
|
||||
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Microsoft\\PowerToys\\Workspaces";
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Utils;
|
||||
|
||||
public class IOUtils
|
||||
{
|
||||
private readonly IFileSystem _fileSystem = new FileSystem();
|
||||
|
||||
public void WriteFile(string fileName, string data)
|
||||
{
|
||||
_fileSystem.File.WriteAllText(fileName, data);
|
||||
}
|
||||
|
||||
public string ReadFile(string fileName)
|
||||
{
|
||||
if (_fileSystem.File.Exists(fileName))
|
||||
{
|
||||
int attempts = 0;
|
||||
while (attempts < 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
using FileSystemStream inputStream = _fileSystem.File.Open(fileName, FileMode.Open);
|
||||
using StreamReader reader = new(inputStream);
|
||||
string data = reader.ReadToEnd();
|
||||
inputStream.Close();
|
||||
return data;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Task.Delay(10).Wait();
|
||||
}
|
||||
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Linq;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Utils;
|
||||
|
||||
public static class StringUtils
|
||||
{
|
||||
public static string UpperCamelCaseToDashCase(this string str)
|
||||
{
|
||||
// If it's a single letter variable, leave it as it is
|
||||
return str.Length == 1
|
||||
? str
|
||||
: string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "-" + x : x.ToString())).ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyTitle>PowerToys.WorkspacesCsharpLibrary</AssemblyTitle>
|
||||
<AssemblyDescription>PowerToys Workspaces Csharp Library</AssemblyDescription>
|
||||
<Description>PowerToys Workspaces Csharp Library</Description>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
@@ -17,8 +15,4 @@
|
||||
<AssemblyName>PowerToys.WorkspacesCsharpLibrary</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -2,12 +2,13 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Data;
|
||||
|
||||
public enum InvokePoint
|
||||
namespace WorkspacesEditor.Data
|
||||
{
|
||||
EditorButton = 0,
|
||||
Shortcut,
|
||||
LaunchAndEdit,
|
||||
CommandPaletteExtension,
|
||||
/* sync with workspaces-common */
|
||||
public enum InvokePoint
|
||||
{
|
||||
EditorButton = 0,
|
||||
Shortcut,
|
||||
LaunchAndEdit,
|
||||
}
|
||||
}
|
||||
101
src/modules/Workspaces/WorkspacesEditor/Data/ProjectData.cs
Normal file
101
src/modules/Workspaces/WorkspacesEditor/Data/ProjectData.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using static WorkspacesEditor.Data.ProjectData;
|
||||
|
||||
namespace WorkspacesEditor.Data
|
||||
{
|
||||
public class ProjectData : WorkspacesEditorData<ProjectWrapper>
|
||||
{
|
||||
public struct ApplicationWrapper
|
||||
{
|
||||
public struct WindowPositionWrapper
|
||||
{
|
||||
public int X { get; set; }
|
||||
|
||||
public int Y { get; set; }
|
||||
|
||||
public int Width { get; set; }
|
||||
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Application { get; set; }
|
||||
|
||||
public string ApplicationPath { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public string PackageFullName { get; set; }
|
||||
|
||||
public string AppUserModelId { get; set; }
|
||||
|
||||
public string PwaAppId { get; set; }
|
||||
|
||||
public string CommandLineArguments { get; set; }
|
||||
|
||||
public bool IsElevated { get; set; }
|
||||
|
||||
public bool CanLaunchElevated { get; set; }
|
||||
|
||||
public bool Minimized { get; set; }
|
||||
|
||||
public bool Maximized { get; set; }
|
||||
|
||||
public WindowPositionWrapper Position { get; set; }
|
||||
|
||||
public int Monitor { get; set; }
|
||||
|
||||
public string Version { get; set; }
|
||||
}
|
||||
|
||||
public struct MonitorConfigurationWrapper
|
||||
{
|
||||
public struct MonitorRectWrapper
|
||||
{
|
||||
public int Top { get; set; }
|
||||
|
||||
public int Left { get; set; }
|
||||
|
||||
public int Width { get; set; }
|
||||
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
public string Id { get; set; }
|
||||
|
||||
public string InstanceId { get; set; }
|
||||
|
||||
public int MonitorNumber { get; set; }
|
||||
|
||||
public int Dpi { get; set; }
|
||||
|
||||
public MonitorRectWrapper MonitorRectDpiAware { get; set; }
|
||||
|
||||
public MonitorRectWrapper MonitorRectDpiUnaware { get; set; }
|
||||
}
|
||||
|
||||
public struct ProjectWrapper
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public long CreationTime { get; set; }
|
||||
|
||||
public long LastLaunchedTime { get; set; }
|
||||
|
||||
public bool IsShortcutNeeded { get; set; }
|
||||
|
||||
public bool MoveExistingWindows { get; set; }
|
||||
|
||||
public List<MonitorConfigurationWrapper> MonitorConfiguration { get; set; }
|
||||
|
||||
public List<ApplicationWrapper> Applications { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,9 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using WorkspacesCsharpLibrary.Data;
|
||||
using WorkspacesCsharpLibrary.Utils;
|
||||
using WorkspacesEditor.Utils;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Data
|
||||
namespace WorkspacesEditor.Data
|
||||
{
|
||||
public class TempProjectData : ProjectData
|
||||
{
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using WorkspacesEditor.Utils;
|
||||
|
||||
using static WorkspacesEditor.Data.ProjectData;
|
||||
using static WorkspacesEditor.Data.WorkspacesData;
|
||||
|
||||
namespace WorkspacesEditor.Data
|
||||
{
|
||||
public class WorkspacesData : WorkspacesEditorData<WorkspacesListWrapper>
|
||||
{
|
||||
public string File => FolderUtils.DataFolder() + "\\workspaces.json";
|
||||
|
||||
public struct WorkspacesListWrapper
|
||||
{
|
||||
public List<ProjectWrapper> Workspaces { get; set; }
|
||||
}
|
||||
|
||||
public enum OrderBy
|
||||
{
|
||||
LastViewed = 0,
|
||||
Created = 1,
|
||||
Name = 2,
|
||||
Unknown = 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json;
|
||||
using WorkspacesEditor.Utils;
|
||||
|
||||
namespace WorkspacesEditor.Data
|
||||
{
|
||||
public class WorkspacesEditorData<T>
|
||||
{
|
||||
protected JsonSerializerOptions JsonOptions
|
||||
{
|
||||
get => new()
|
||||
{
|
||||
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
||||
WriteIndented = true,
|
||||
};
|
||||
}
|
||||
|
||||
public T Read(string file)
|
||||
{
|
||||
IOUtils ioUtils = new();
|
||||
string data = ioUtils.ReadFile(file);
|
||||
return JsonSerializer.Deserialize<T>(data, JsonOptions);
|
||||
}
|
||||
|
||||
public string Serialize(T data)
|
||||
{
|
||||
return JsonSerializer.Serialize(data, JsonOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ using System.Threading.Tasks;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
using ManagedCommon;
|
||||
using WorkspacesCsharpLibrary.Data;
|
||||
using WorkspacesEditor.Data;
|
||||
using WorkspacesEditor.Utils;
|
||||
|
||||
namespace WorkspacesEditor.Models
|
||||
@@ -226,7 +226,7 @@ namespace WorkspacesEditor.Models
|
||||
}
|
||||
}
|
||||
|
||||
public Project(ProjectWrapper project)
|
||||
public Project(ProjectData.ProjectWrapper project)
|
||||
{
|
||||
Id = project.Id;
|
||||
Name = project.Name;
|
||||
@@ -237,7 +237,7 @@ namespace WorkspacesEditor.Models
|
||||
Monitors = [];
|
||||
Applications = [];
|
||||
|
||||
foreach (ApplicationWrapper app in project.Applications)
|
||||
foreach (ProjectData.ApplicationWrapper app in project.Applications)
|
||||
{
|
||||
Models.Application newApp = new()
|
||||
{
|
||||
@@ -269,7 +269,7 @@ namespace WorkspacesEditor.Models
|
||||
Applications.Add(newApp);
|
||||
}
|
||||
|
||||
foreach (MonitorConfigurationWrapper monitor in project.MonitorConfiguration)
|
||||
foreach (ProjectData.MonitorConfigurationWrapper monitor in project.MonitorConfiguration)
|
||||
{
|
||||
System.Windows.Rect dpiAware = new(monitor.MonitorRectDpiAware.Left, monitor.MonitorRectDpiAware.Top, monitor.MonitorRectDpiAware.Width, monitor.MonitorRectDpiAware.Height);
|
||||
System.Windows.Rect dpiUnaware = new(monitor.MonitorRectDpiUnaware.Left, monitor.MonitorRectDpiUnaware.Top, monitor.MonitorRectDpiUnaware.Width, monitor.MonitorRectDpiUnaware.Height);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json;
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public class DashCaseNamingPolicy : JsonNamingPolicy
|
||||
{
|
||||
public static DashCaseNamingPolicy Instance { get; } = new DashCaseNamingPolicy();
|
||||
|
||||
public override string ConvertName(string name)
|
||||
{
|
||||
return name.UpperCamelCaseToDashCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/modules/Workspaces/WorkspacesEditor/Utils/FolderUtils.cs
Normal file
28
src/modules/Workspaces/WorkspacesEditor/Utils/FolderUtils.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public class FolderUtils
|
||||
{
|
||||
public static string Desktop()
|
||||
{
|
||||
return Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||
}
|
||||
|
||||
public static string Temp()
|
||||
{
|
||||
return Path.GetTempPath();
|
||||
}
|
||||
|
||||
// Note: the same path should be used in SnapshotTool and Launcher
|
||||
public static string DataFolder()
|
||||
{
|
||||
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Microsoft\\PowerToys\\Workspaces";
|
||||
}
|
||||
}
|
||||
}
|
||||
52
src/modules/Workspaces/WorkspacesEditor/Utils/IOUtils.cs
Normal file
52
src/modules/Workspaces/WorkspacesEditor/Utils/IOUtils.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public class IOUtils
|
||||
{
|
||||
private readonly IFileSystem _fileSystem = new FileSystem();
|
||||
|
||||
public IOUtils()
|
||||
{
|
||||
}
|
||||
|
||||
public void WriteFile(string fileName, string data)
|
||||
{
|
||||
_fileSystem.File.WriteAllText(fileName, data);
|
||||
}
|
||||
|
||||
public string ReadFile(string fileName)
|
||||
{
|
||||
if (_fileSystem.File.Exists(fileName))
|
||||
{
|
||||
int attempts = 0;
|
||||
while (attempts < 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
using FileSystemStream inputStream = _fileSystem.File.Open(fileName, FileMode.Open);
|
||||
using StreamReader reader = new(inputStream);
|
||||
string data = reader.ReadToEnd();
|
||||
inputStream.Close();
|
||||
return data;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Task.Delay(10).Wait();
|
||||
}
|
||||
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using ManagedCommon;
|
||||
using WorkspacesCsharpLibrary.Data;
|
||||
using WorkspacesCsharpLibrary.Utils;
|
||||
using WorkspacesEditor.Data;
|
||||
using WorkspacesEditor.Models;
|
||||
using WorkspacesEditor.ViewModels;
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace WorkspacesEditor.Utils
|
||||
|
||||
foreach (Project project in workspaces)
|
||||
{
|
||||
ProjectWrapper wrapper = new()
|
||||
ProjectData.ProjectWrapper wrapper = new()
|
||||
{
|
||||
Id = project.Id,
|
||||
Name = project.Name,
|
||||
@@ -95,7 +95,7 @@ namespace WorkspacesEditor.Utils
|
||||
|
||||
foreach (Application app in project.Applications.Where(x => x.IsIncluded))
|
||||
{
|
||||
wrapper.Applications.Add(new ApplicationWrapper
|
||||
wrapper.Applications.Add(new ProjectData.ApplicationWrapper
|
||||
{
|
||||
Id = app.Id,
|
||||
Application = app.AppName,
|
||||
@@ -110,7 +110,7 @@ namespace WorkspacesEditor.Utils
|
||||
Version = app.Version,
|
||||
Maximized = app.Maximized,
|
||||
Minimized = app.Minimized,
|
||||
Position = new ApplicationWrapper.WindowPositionWrapper
|
||||
Position = new ProjectData.ApplicationWrapper.WindowPositionWrapper
|
||||
{
|
||||
X = app.Position.X,
|
||||
Y = app.Position.Y,
|
||||
@@ -123,20 +123,20 @@ namespace WorkspacesEditor.Utils
|
||||
|
||||
foreach (MonitorSetup monitor in project.Monitors)
|
||||
{
|
||||
wrapper.MonitorConfiguration.Add(new MonitorConfigurationWrapper
|
||||
wrapper.MonitorConfiguration.Add(new ProjectData.MonitorConfigurationWrapper
|
||||
{
|
||||
Id = monitor.MonitorName,
|
||||
InstanceId = monitor.MonitorInstanceId,
|
||||
MonitorNumber = monitor.MonitorNumber,
|
||||
Dpi = monitor.Dpi,
|
||||
MonitorRectDpiAware = new MonitorConfigurationWrapper.MonitorRectWrapper
|
||||
MonitorRectDpiAware = new ProjectData.MonitorConfigurationWrapper.MonitorRectWrapper
|
||||
{
|
||||
Left = (int)monitor.MonitorDpiAwareBounds.Left,
|
||||
Top = (int)monitor.MonitorDpiAwareBounds.Top,
|
||||
Width = (int)monitor.MonitorDpiAwareBounds.Width,
|
||||
Height = (int)monitor.MonitorDpiAwareBounds.Height,
|
||||
},
|
||||
MonitorRectDpiUnaware = new MonitorConfigurationWrapper.MonitorRectWrapper
|
||||
MonitorRectDpiUnaware = new ProjectData.MonitorConfigurationWrapper.MonitorRectWrapper
|
||||
{
|
||||
Left = (int)monitor.MonitorDpiUnawareBounds.Left,
|
||||
Top = (int)monitor.MonitorDpiUnawareBounds.Top,
|
||||
@@ -163,7 +163,7 @@ namespace WorkspacesEditor.Utils
|
||||
|
||||
private bool AddWorkspaces(MainViewModel mainViewModel, WorkspacesData.WorkspacesListWrapper workspaces)
|
||||
{
|
||||
foreach (ProjectWrapper project in workspaces.Workspaces)
|
||||
foreach (ProjectData.ProjectWrapper project in workspaces.Workspaces)
|
||||
{
|
||||
mainViewModel.Workspaces.Add(new Project(project));
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using WorkspacesCsharpLibrary;
|
||||
using WorkspacesCsharpLibrary.Data;
|
||||
using WorkspacesCsharpLibrary.Utils;
|
||||
using WorkspacesEditor.Data;
|
||||
using WorkspacesEditor.Models;
|
||||
using WorkspacesEditor.Telemetry;
|
||||
using WorkspacesEditor.Utils;
|
||||
using static WorkspacesCsharpLibrary.Data.WorkspacesData;
|
||||
|
||||
using static WorkspacesEditor.Data.WorkspacesData;
|
||||
|
||||
namespace WorkspacesEditor.ViewModels
|
||||
{
|
||||
|
||||
@@ -78,15 +78,6 @@
|
||||
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
<ProjectReference Include="..\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Data\WorkspacesData.cs" />
|
||||
<Compile Remove="Data\ProjectData.cs" />
|
||||
<Compile Remove="Data\WorkspacesEditorData`1.cs" />
|
||||
<Compile Remove="Utils\IOUtils.cs" />
|
||||
<Compile Remove="Utils\FolderUtils.cs" />
|
||||
<Compile Remove="Utils\DashCaseNamingPolicy.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
@@ -105,4 +96,4 @@
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -9,7 +9,7 @@ using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
using WorkspacesCsharpLibrary.Data;
|
||||
using WorkspacesEditor.Data;
|
||||
using WorkspacesEditor.Models;
|
||||
using WorkspacesEditor.ViewModels;
|
||||
|
||||
|
||||
@@ -3,12 +3,15 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using Workspaces.Data;
|
||||
|
||||
using static WorkspacesLauncherUI.Data.AppLaunchData;
|
||||
using static WorkspacesLauncherUI.Data.AppLaunchInfosData;
|
||||
|
||||
namespace WorkspacesLauncherUI.Data
|
||||
{
|
||||
public class AppLaunchData : WorkspacesCsharpLibrary.Data.WorkspacesEditorData<AppLaunchDataWrapper>
|
||||
public class AppLaunchData : WorkspacesUIData<AppLaunchDataWrapper>
|
||||
{
|
||||
public struct AppLaunchDataWrapper
|
||||
{
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using Workspaces.Data;
|
||||
|
||||
using static WorkspacesLauncherUI.Data.AppLaunchInfoData;
|
||||
|
||||
namespace WorkspacesLauncherUI.Data
|
||||
{
|
||||
public class AppLaunchInfoData : WorkspacesCsharpLibrary.Data.WorkspacesEditorData<AppLaunchInfoWrapper>
|
||||
public class AppLaunchInfoData : WorkspacesUIData<AppLaunchInfoWrapper>
|
||||
{
|
||||
public struct AppLaunchInfoWrapper
|
||||
{
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using Workspaces.Data;
|
||||
|
||||
using static WorkspacesLauncherUI.Data.AppLaunchInfoData;
|
||||
using static WorkspacesLauncherUI.Data.AppLaunchInfosData;
|
||||
|
||||
namespace WorkspacesLauncherUI.Data
|
||||
{
|
||||
public class AppLaunchInfosData : WorkspacesCsharpLibrary.Data.WorkspacesEditorData<AppLaunchInfoListWrapper>
|
||||
public class AppLaunchInfosData : WorkspacesUIData<AppLaunchInfoListWrapper>
|
||||
{
|
||||
public struct AppLaunchInfoListWrapper
|
||||
{
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json;
|
||||
|
||||
using WorkspacesLauncherUI.Utils;
|
||||
|
||||
namespace Workspaces.Data
|
||||
{
|
||||
public class WorkspacesUIData<T>
|
||||
{
|
||||
protected JsonSerializerOptions JsonOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
return new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
||||
WriteIndented = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public T Deserialize(string data)
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(data, JsonOptions);
|
||||
}
|
||||
|
||||
public string Serialize(T data)
|
||||
{
|
||||
return JsonSerializer.Serialize(data, JsonOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,13 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
using ManagedCommon;
|
||||
using WorkspacesCsharpLibrary;
|
||||
using WorkspacesLauncherUI.Data;
|
||||
using WorkspacesLauncherUI.Models;
|
||||
using WorkspacesLauncherUI.Utils;
|
||||
|
||||
namespace WorkspacesLauncherUI.ViewModels
|
||||
{
|
||||
|
||||
@@ -1,102 +1,102 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\Common.SelfContained.props" />
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\Common.SelfContained.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyTitle>PowerToys.WorkspacesLauncherUI</AssemblyTitle>
|
||||
<AssemblyDescription>PowerToys Workspaces Editor</AssemblyDescription>
|
||||
<Description>PowerToys Workspaces Editor</Description>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{9C53CC25-0623-4569-95BC-B05410675EE3}</ProjectGuid>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyTitle>PowerToys.WorkspacesLauncherUI</AssemblyTitle>
|
||||
<AssemblyDescription>PowerToys Workspaces Launcher UI</AssemblyDescription>
|
||||
<Description>PowerToys Workspaces Launcher UI</Description>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>..\Assets\Workspaces\Workspaces.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<AssemblyName>PowerToys.WorkspacesLauncherUI</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\Assets\**\*.*">
|
||||
<Link>Assets\Workspaces\%(Filename)%(Extension)</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{9C53CC25-0623-4569-95BC-B05410675EE3}</ProjectGuid>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<COMReference Include="IWshRuntimeLibrary">
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<Guid>f935dc20-1cf0-11d0-adb9-00c04fd58a0b</Guid>
|
||||
<Lcid>0</Lcid>
|
||||
<Isolated>false</Isolated>
|
||||
<EmbedInteropTypes>true</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="Shell32">
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<Guid>50a7e9b0-70ef-11d1-b75a-00a0c90564fe</Guid>
|
||||
<Lcid>0</Lcid>
|
||||
<Isolated>false</Isolated>
|
||||
<EmbedInteropTypes>true</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="app.manifest" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>..\Assets\Workspaces\Workspaces.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<AssemblyName>PowerToys.WorkspacesLauncherUI</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ControlzEx" />
|
||||
<PackageReference Include="ModernWpfUI" />
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\Assets\**\*.*">
|
||||
<Link>Assets\Workspaces\%(Filename)%(Extension)</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<COMReference Include="IWshRuntimeLibrary">
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<Guid>f935dc20-1cf0-11d0-adb9-00c04fd58a0b</Guid>
|
||||
<Lcid>0</Lcid>
|
||||
<Isolated>false</Isolated>
|
||||
<EmbedInteropTypes>true</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="Shell32">
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<Guid>50a7e9b0-70ef-11d1-b75a-00a0c90564fe</Guid>
|
||||
<Lcid>0</Lcid>
|
||||
<Isolated>false</Isolated>
|
||||
<EmbedInteropTypes>true</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="app.manifest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ControlzEx" />
|
||||
<PackageReference Include="ModernWpfUI" />
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\GPOWrapperProjection\GPOWrapperProjection.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
|
||||
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
<ProjectReference Include="..\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Properties\Settings.Designer.cs">
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\GPOWrapperProjection\GPOWrapperProjection.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
|
||||
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
<ProjectReference Include="..\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Properties\Settings.Designer.cs">
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -201,7 +201,7 @@ public:
|
||||
Logger::error(message.value());
|
||||
}
|
||||
}
|
||||
m_toggleEditorEventWaiter.start(CommonSharedConstants::WORKSPACES_LAUNCH_EDITOR_EVENT, [&](DWORD err) {
|
||||
m_toggleEditorEventWaiter = EventWaiter(CommonSharedConstants::WORKSPACES_LAUNCH_EDITOR_EVENT, [&](int err) {
|
||||
if (err == ERROR_SUCCESS)
|
||||
{
|
||||
Logger::trace(L"{} event was signaled", CommonSharedConstants::WORKSPACES_LAUNCH_EDITOR_EVENT);
|
||||
|
||||
@@ -369,4 +369,4 @@
|
||||
<Import Project="..\..\..\..\packages\robmikh.common.0.0.23-beta\build\native\robmikh.common.targets" Condition="Exists('..\..\..\..\packages\robmikh.common.0.0.23-beta\build\native\robmikh.common.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -28,20 +28,6 @@
|
||||
#include <common/utils/logger_helper.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
#include <common/utils/gpo.h>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#endif // __ZOOMIT_POWERTOYS__
|
||||
|
||||
#ifdef __ZOOMIT_POWERTOYS__
|
||||
enum class ZoomItCommand
|
||||
{
|
||||
Zoom,
|
||||
Draw,
|
||||
Break,
|
||||
LiveZoom,
|
||||
Snip,
|
||||
Record,
|
||||
};
|
||||
#endif // __ZOOMIT_POWERTOYS__
|
||||
|
||||
namespace winrt
|
||||
@@ -186,6 +172,7 @@ std::wstring g_RecordingSaveLocationGIF;
|
||||
winrt::IDirect3DDevice g_RecordDevice{ nullptr };
|
||||
std::shared_ptr<VideoRecordingSession> g_RecordingSession = nullptr;
|
||||
std::shared_ptr<GifRecordingSession> g_GifRecordingSession = nullptr;
|
||||
|
||||
type_pGetMonitorInfo pGetMonitorInfo;
|
||||
type_MonitorFromPoint pMonitorFromPoint;
|
||||
type_pSHAutoComplete pSHAutoComplete;
|
||||
@@ -7725,53 +7712,6 @@ HWND InitInstance( HINSTANCE hInstance, int nCmdShow )
|
||||
|
||||
}
|
||||
|
||||
// Dispatch commands coming from the PowerToys IPC channel.
|
||||
#ifdef __ZOOMIT_POWERTOYS__
|
||||
void ZoomIt_DispatchCommand(ZoomItCommand cmd)
|
||||
{
|
||||
auto post_hotkey = [](WPARAM id)
|
||||
{
|
||||
if (g_hWndMain != nullptr)
|
||||
{
|
||||
PostMessage(g_hWndMain, WM_HOTKEY, id, 0);
|
||||
}
|
||||
};
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case ZoomItCommand::Zoom:
|
||||
if (g_hWndMain != nullptr)
|
||||
{
|
||||
PostMessage(g_hWndMain, WM_COMMAND, IDC_ZOOM, 0);
|
||||
}
|
||||
Trace::ZoomItActivateZoom();
|
||||
break;
|
||||
case ZoomItCommand::Draw:
|
||||
post_hotkey(DRAW_HOTKEY);
|
||||
Trace::ZoomItActivateDraw();
|
||||
break;
|
||||
case ZoomItCommand::Break:
|
||||
post_hotkey(BREAK_HOTKEY);
|
||||
Trace::ZoomItActivateBreak();
|
||||
break;
|
||||
case ZoomItCommand::LiveZoom:
|
||||
post_hotkey(LIVE_HOTKEY);
|
||||
Trace::ZoomItActivateLiveZoom();
|
||||
break;
|
||||
case ZoomItCommand::Snip:
|
||||
post_hotkey(SNIP_HOTKEY);
|
||||
Trace::ZoomItActivateSnip();
|
||||
break;
|
||||
case ZoomItCommand::Record:
|
||||
post_hotkey(RECORD_HOTKEY);
|
||||
Trace::ZoomItActivateRecord();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// WinMain
|
||||
@@ -7806,6 +7746,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
||||
|
||||
// Initialize logger
|
||||
LoggerHelpers::init_logger(L"ZoomIt", L"", LogSettings::zoomItLoggerName);
|
||||
|
||||
ProcessWaiter::OnProcessTerminate(pid, [mainThreadId](int err) {
|
||||
if (err != ERROR_SUCCESS)
|
||||
{
|
||||
@@ -7964,63 +7905,27 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
||||
#ifdef __ZOOMIT_POWERTOYS__
|
||||
HANDLE m_reload_settings_event_handle = NULL;
|
||||
HANDLE m_exit_event_handle = NULL;
|
||||
HANDLE m_zoom_event_handle = NULL;
|
||||
HANDLE m_draw_event_handle = NULL;
|
||||
HANDLE m_break_event_handle = NULL;
|
||||
HANDLE m_live_zoom_event_handle = NULL;
|
||||
HANDLE m_snip_event_handle = NULL;
|
||||
HANDLE m_record_event_handle = NULL;
|
||||
std::thread m_event_triggers_thread;
|
||||
|
||||
if( g_StartedByPowerToys ) {
|
||||
// Start a thread to listen to PowerToys Events.
|
||||
m_reload_settings_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_REFRESH_SETTINGS_EVENT);
|
||||
m_exit_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_EXIT_EVENT);
|
||||
m_zoom_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_ZOOM_EVENT);
|
||||
m_draw_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_DRAW_EVENT);
|
||||
m_break_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_BREAK_EVENT);
|
||||
m_live_zoom_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_LIVEZOOM_EVENT);
|
||||
m_snip_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_SNIP_EVENT);
|
||||
m_record_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_RECORD_EVENT);
|
||||
if (!m_reload_settings_event_handle || !m_exit_event_handle || !m_zoom_event_handle || !m_draw_event_handle || !m_break_event_handle || !m_live_zoom_event_handle || !m_snip_event_handle || !m_record_event_handle)
|
||||
if (!m_reload_settings_event_handle || !m_exit_event_handle)
|
||||
{
|
||||
Logger::warn(L"Failed to create events. {}", get_last_error_or_default(GetLastError()));
|
||||
return 1;
|
||||
}
|
||||
const std::array<HANDLE, 8> event_handles{
|
||||
m_reload_settings_event_handle,
|
||||
m_exit_event_handle,
|
||||
m_zoom_event_handle,
|
||||
m_draw_event_handle,
|
||||
m_break_event_handle,
|
||||
m_live_zoom_event_handle,
|
||||
m_snip_event_handle,
|
||||
m_record_event_handle,
|
||||
};
|
||||
const DWORD handle_count = static_cast<DWORD>(event_handles.size());
|
||||
m_event_triggers_thread = std::thread([event_handles, handle_count]() {
|
||||
m_event_triggers_thread = std::thread([&]() {
|
||||
MSG msg;
|
||||
HANDLE event_handles[2] = {m_reload_settings_event_handle, m_exit_event_handle};
|
||||
while (g_running)
|
||||
{
|
||||
DWORD dwEvt = MsgWaitForMultipleObjects(handle_count, event_handles.data(), false, INFINITE, QS_ALLINPUT);
|
||||
if (dwEvt == WAIT_FAILED)
|
||||
{
|
||||
Logger::error(L"ZoomIt event wait failed. {}", get_last_error_or_default(GetLastError()));
|
||||
break;
|
||||
}
|
||||
DWORD dwEvt = MsgWaitForMultipleObjects(2, event_handles, false, INFINITE, QS_ALLINPUT);
|
||||
if (!g_running)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (dwEvt == WAIT_OBJECT_0 + handle_count)
|
||||
{
|
||||
if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
switch (dwEvt)
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
@@ -8033,28 +7938,19 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
{
|
||||
// Exit Event
|
||||
Logger::trace(L"Received an exit event.");
|
||||
PostMessage(g_hWndMain, WM_QUIT, 0, 0);
|
||||
break;
|
||||
}
|
||||
case WAIT_OBJECT_0 + 2:
|
||||
ZoomIt_DispatchCommand(ZoomItCommand::Zoom);
|
||||
if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 3:
|
||||
ZoomIt_DispatchCommand(ZoomItCommand::Draw);
|
||||
default:
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 4:
|
||||
ZoomIt_DispatchCommand(ZoomItCommand::Break);
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 5:
|
||||
ZoomIt_DispatchCommand(ZoomItCommand::LiveZoom);
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 6:
|
||||
ZoomIt_DispatchCommand(ZoomItCommand::Snip);
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 7:
|
||||
ZoomIt_DispatchCommand(ZoomItCommand::Record);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -8084,12 +7980,6 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
|
||||
SetEvent(m_reload_settings_event_handle);
|
||||
CloseHandle(m_reload_settings_event_handle);
|
||||
CloseHandle(m_exit_event_handle);
|
||||
CloseHandle(m_zoom_event_handle);
|
||||
CloseHandle(m_draw_event_handle);
|
||||
CloseHandle(m_break_event_handle);
|
||||
CloseHandle(m_live_zoom_event_handle);
|
||||
CloseHandle(m_snip_event_handle);
|
||||
CloseHandle(m_record_event_handle);
|
||||
m_event_triggers_thread.join();
|
||||
}
|
||||
#endif // __ZOOMIT_POWERTOYS__
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <common/utils/logger_helper.h>
|
||||
#include <common/utils/resources.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
#include <common/interop/shared_constants.h>
|
||||
|
||||
#include <shellapi.h>
|
||||
#include <common/interop/shared_constants.h>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\PowerToys.ModuleContracts\PowerToys.ModuleContracts.csproj" />
|
||||
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,151 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using Common.UI;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.ModuleContracts;
|
||||
|
||||
namespace Awake.ModuleServices;
|
||||
|
||||
/// <summary>
|
||||
/// Provides CLI-based Awake control for reuse across hosts.
|
||||
/// </summary>
|
||||
public sealed class AwakeService : ModuleServiceBase, IAwakeService
|
||||
{
|
||||
public static AwakeService Instance { get; } = new();
|
||||
|
||||
public override string Key => SettingsDeepLink.SettingsWindow.Awake.ToString();
|
||||
|
||||
protected override SettingsDeepLink.SettingsWindow SettingsWindow => SettingsDeepLink.SettingsWindow.Awake;
|
||||
|
||||
public override Task<OperationResult> LaunchAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Default launch -> indefinite, honoring Awake's own settings for display behavior.
|
||||
return SetIndefiniteAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public AwakeState GetCurrentState()
|
||||
{
|
||||
var isRunning = IsAwakeProcessRunning();
|
||||
var settings = ReadSettings();
|
||||
|
||||
if (settings is null)
|
||||
{
|
||||
return new AwakeState(isRunning, AwakeStateMode.Passive, false, null, null);
|
||||
}
|
||||
|
||||
var mode = settings.Properties.Mode switch
|
||||
{
|
||||
AwakeMode.PASSIVE => AwakeStateMode.Passive,
|
||||
AwakeMode.INDEFINITE => AwakeStateMode.Indefinite,
|
||||
AwakeMode.TIMED => AwakeStateMode.Timed,
|
||||
AwakeMode.EXPIRABLE => AwakeStateMode.Expirable,
|
||||
_ => AwakeStateMode.Passive,
|
||||
};
|
||||
|
||||
TimeSpan? duration = null;
|
||||
DateTimeOffset? expiration = null;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case AwakeStateMode.Timed:
|
||||
duration = TimeSpan.FromHours(settings.Properties.IntervalHours) + TimeSpan.FromMinutes(settings.Properties.IntervalMinutes);
|
||||
break;
|
||||
case AwakeStateMode.Expirable:
|
||||
expiration = settings.Properties.ExpirationDateTime;
|
||||
break;
|
||||
}
|
||||
|
||||
return new AwakeState(isRunning, mode, settings.Properties.KeepDisplayOn, duration, expiration);
|
||||
}
|
||||
|
||||
public Task<OperationResult> SetIndefiniteAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return UpdateSettingsAsync(
|
||||
settings =>
|
||||
{
|
||||
settings.Properties.Mode = AwakeMode.INDEFINITE;
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
public Task<OperationResult> SetTimedAsync(int minutes, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (minutes <= 0)
|
||||
{
|
||||
return Task.FromResult(OperationResult.Fail("Minutes must be greater than zero."));
|
||||
}
|
||||
|
||||
return UpdateSettingsAsync(
|
||||
settings =>
|
||||
{
|
||||
var totalMinutes = Math.Min(minutes, int.MaxValue);
|
||||
settings.Properties.Mode = AwakeMode.TIMED;
|
||||
settings.Properties.IntervalHours = (uint)(totalMinutes / 60);
|
||||
settings.Properties.IntervalMinutes = (uint)(totalMinutes % 60);
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
public Task<OperationResult> SetOffAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return UpdateSettingsAsync(
|
||||
settings =>
|
||||
{
|
||||
settings.Properties.Mode = AwakeMode.PASSIVE;
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
private static Task<OperationResult> UpdateSettingsAsync(Action<AwakeSettings> mutateSettings, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var settingsUtils = SettingsUtils.Default;
|
||||
var settings = settingsUtils.GetSettingsOrDefault<AwakeSettings>(AwakeSettings.ModuleName);
|
||||
|
||||
mutateSettings(settings);
|
||||
|
||||
settingsUtils.SaveSettings(JsonSerializer.Serialize(settings, AwakeServiceJsonContext.Default.AwakeSettings), AwakeSettings.ModuleName);
|
||||
return Task.FromResult(OperationResult.Ok());
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return Task.FromResult(OperationResult.Fail("Awake update was cancelled."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(OperationResult.Fail($"Failed to update Awake settings: {ex.Message}"));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAwakeProcessRunning()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Process.GetProcessesByName("PowerToys.Awake").Length > 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static AwakeSettings? ReadSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settingsUtils = SettingsUtils.Default;
|
||||
return settingsUtils.GetSettingsOrDefault<AwakeSettings>(AwakeSettings.ModuleName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
|
||||
namespace Awake.ModuleServices;
|
||||
|
||||
[JsonSerializable(typeof(AwakeSettings))]
|
||||
internal sealed partial class AwakeServiceJsonContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Awake.ModuleServices;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the current state of the Awake module.
|
||||
/// </summary>
|
||||
/// <param name="IsRunning">Whether the Awake process is currently running.</param>
|
||||
/// <param name="Mode">The current Awake mode (Passive, Indefinite, Timed, Expirable).</param>
|
||||
/// <param name="KeepDisplayOn">Whether the display is kept on.</param>
|
||||
/// <param name="Duration">For timed mode, the configured duration.</param>
|
||||
/// <param name="Expiration">For expirable mode, the expiration date/time.</param>
|
||||
public readonly record struct AwakeState(bool IsRunning, AwakeStateMode Mode, bool KeepDisplayOn, TimeSpan? Duration, DateTimeOffset? Expiration);
|
||||
|
||||
/// <summary>
|
||||
/// The mode of the Awake module.
|
||||
/// </summary>
|
||||
public enum AwakeStateMode
|
||||
{
|
||||
Passive = 0,
|
||||
Indefinite = 1,
|
||||
Timed = 2,
|
||||
Expirable = 3,
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using PowerToys.ModuleContracts;
|
||||
|
||||
namespace Awake.ModuleServices;
|
||||
|
||||
public interface IAwakeService : IModuleService
|
||||
{
|
||||
Task<OperationResult> SetIndefiniteAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
Task<OperationResult> SetTimedAsync(int minutes, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<OperationResult> SetOffAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current state of the Awake module.
|
||||
/// </summary>
|
||||
AwakeState GetCurrentState();
|
||||
}
|
||||
@@ -28,13 +28,6 @@ namespace Awake.Core.Native
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool AllocConsole();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool AttachConsole(int dwProcessId);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern void FreeConsole();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle);
|
||||
|
||||
|
||||
@@ -49,8 +49,5 @@ namespace Awake.Core.Native
|
||||
// Menu Item Info Flags
|
||||
internal const uint MNS_AUTO_DISMISS = 0x10000000;
|
||||
internal const uint MIM_STYLE = 0x00000010;
|
||||
|
||||
// Attach Console
|
||||
internal const int ATTACH_PARENT_PROCESS = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,24 +51,6 @@ namespace Awake
|
||||
|
||||
private static async Task<int> Main(string[] args)
|
||||
{
|
||||
var rootCommand = BuildRootCommand();
|
||||
|
||||
Bridge.AttachConsole(Core.Native.Constants.ATTACH_PARENT_PROCESS);
|
||||
|
||||
var parseResult = rootCommand.Parse(args);
|
||||
|
||||
if (parseResult.Tokens.Any(t => t.Value.ToLowerInvariant() is "--help" or "-h" or "-?"))
|
||||
{
|
||||
// Print help and exit.
|
||||
return rootCommand.Invoke(args);
|
||||
}
|
||||
|
||||
if (parseResult.Errors.Count > 0)
|
||||
{
|
||||
// Shows errors and returns non-zero.
|
||||
return rootCommand.Invoke(args);
|
||||
}
|
||||
|
||||
_settingsUtils = SettingsUtils.Default;
|
||||
|
||||
LockMutex = new Mutex(true, Core.Constants.AppName, out bool instantiated);
|
||||
@@ -125,97 +107,116 @@ namespace Awake
|
||||
Bridge.GetPwrCapabilities(out _powerCapabilities);
|
||||
Logger.LogInfo(JsonSerializer.Serialize(_powerCapabilities, _serializerOptions));
|
||||
|
||||
return await rootCommand.InvokeAsync(args);
|
||||
Logger.LogInfo("Parsing parameters...");
|
||||
|
||||
Option<bool> configOption = new(_aliasesConfigOption, () => false, Resources.AWAKE_CMD_HELP_CONFIG_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<bool> displayOption = new(_aliasesDisplayOption, () => true, Resources.AWAKE_CMD_HELP_DISPLAY_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<uint> timeOption = new(_aliasesTimeOption, () => 0, Resources.AWAKE_CMD_HELP_TIME_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ExactlyOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<int> pidOption = new(_aliasesPidOption, () => 0, Resources.AWAKE_CMD_HELP_PID_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<string> expireAtOption = new(_aliasesExpireAtOption, () => string.Empty, Resources.AWAKE_CMD_HELP_EXPIRE_AT_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<bool> parentPidOption = new(_aliasesParentPidOption, () => false, Resources.AWAKE_CMD_PARENT_PID_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
timeOption.AddValidator(result =>
|
||||
{
|
||||
if (result.Tokens.Count != 0 && !uint.TryParse(result.Tokens[0].Value, out _))
|
||||
{
|
||||
string errorMessage = $"Interval in --time-limit could not be parsed correctly. Check that the value is valid and doesn't exceed 4,294,967,295. Value used: {result.Tokens[0].Value}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
}
|
||||
});
|
||||
|
||||
pidOption.AddValidator(result =>
|
||||
{
|
||||
if (result.Tokens.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string tokenValue = result.Tokens[0].Value;
|
||||
|
||||
if (!int.TryParse(tokenValue, out int parsed))
|
||||
{
|
||||
string errorMessage = $"PID value in --pid could not be parsed correctly. Check that the value is valid and falls within the boundaries of Windows PID process limits. Value used: {tokenValue}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
return;
|
||||
}
|
||||
|
||||
if (parsed <= 0)
|
||||
{
|
||||
string errorMessage = $"PID value in --pid must be a positive integer. Value used: {parsed}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
return;
|
||||
}
|
||||
|
||||
// Process existence check. (We also re-validate just before binding.)
|
||||
if (!ProcessExists(parsed))
|
||||
{
|
||||
string errorMessage = $"No running process found with an ID of {parsed}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
}
|
||||
});
|
||||
|
||||
expireAtOption.AddValidator(result =>
|
||||
{
|
||||
if (result.Tokens.Count != 0 && !DateTimeOffset.TryParse(result.Tokens[0].Value, out _))
|
||||
{
|
||||
string errorMessage = $"Date and time value in --expire-at could not be parsed correctly. Check that the value is valid date and time. Refer to https://aka.ms/powertoys/awake for format examples. Value used: {result.Tokens[0].Value}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
}
|
||||
});
|
||||
|
||||
RootCommand? rootCommand =
|
||||
[
|
||||
configOption,
|
||||
displayOption,
|
||||
timeOption,
|
||||
pidOption,
|
||||
expireAtOption,
|
||||
parentPidOption,
|
||||
];
|
||||
|
||||
rootCommand.Description = Core.Constants.AppName;
|
||||
rootCommand.SetHandler(HandleCommandLineArguments, configOption, displayOption, timeOption, pidOption, expireAtOption, parentPidOption);
|
||||
|
||||
return rootCommand.InvokeAsync(args).Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static RootCommand BuildRootCommand()
|
||||
{
|
||||
Logger.LogInfo("Parsing parameters...");
|
||||
|
||||
Option<bool> configOption = new(_aliasesConfigOption, () => false, Resources.AWAKE_CMD_HELP_CONFIG_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<bool> displayOption = new(_aliasesDisplayOption, () => true, Resources.AWAKE_CMD_HELP_DISPLAY_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<uint> timeOption = new(_aliasesTimeOption, () => 0, Resources.AWAKE_CMD_HELP_TIME_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ExactlyOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<int> pidOption = new(_aliasesPidOption, () => 0, Resources.AWAKE_CMD_HELP_PID_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<string> expireAtOption = new(_aliasesExpireAtOption, () => string.Empty, Resources.AWAKE_CMD_HELP_EXPIRE_AT_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<bool> parentPidOption = new(_aliasesParentPidOption, () => false, Resources.AWAKE_CMD_PARENT_PID_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
timeOption.AddValidator(result =>
|
||||
{
|
||||
if (result.Tokens.Count != 0 && !uint.TryParse(result.Tokens[0].Value, out _))
|
||||
{
|
||||
string errorMessage = $"Interval in --time-limit could not be parsed correctly. Check that the value is valid and doesn't exceed 4,294,967,295. Value used: {result.Tokens[0].Value}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
}
|
||||
});
|
||||
|
||||
pidOption.AddValidator(result =>
|
||||
{
|
||||
if (result.Tokens.Count != 0 && !int.TryParse(result.Tokens[0].Value, out _))
|
||||
{
|
||||
string errorMessage = $"PID value in --pid could not be parsed correctly. Check that the value is valid and falls within the boundaries of Windows PID process limits. Value used: {result.Tokens[0].Value}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
}
|
||||
});
|
||||
|
||||
expireAtOption.AddValidator(result =>
|
||||
{
|
||||
if (result.Tokens.Count != 0 && !DateTimeOffset.TryParse(result.Tokens[0].Value, out _))
|
||||
{
|
||||
string errorMessage = $"Date and time value in --expire-at could not be parsed correctly. Check that the value is valid date and time. Refer to https://aka.ms/powertoys/awake for format examples. Value used: {result.Tokens[0].Value}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
}
|
||||
});
|
||||
|
||||
RootCommand? rootCommand =
|
||||
[
|
||||
configOption,
|
||||
displayOption,
|
||||
timeOption,
|
||||
pidOption,
|
||||
expireAtOption,
|
||||
parentPidOption,
|
||||
];
|
||||
|
||||
rootCommand.Description = Core.Constants.AppName;
|
||||
rootCommand.SetHandler(HandleCommandLineArguments, configOption, displayOption, timeOption, pidOption, expireAtOption, parentPidOption);
|
||||
|
||||
return rootCommand;
|
||||
}
|
||||
|
||||
private static void AwakeUnhandledExceptionCatcher(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
if (e.ExceptionObject is Exception exception)
|
||||
@@ -263,7 +264,6 @@ namespace Awake
|
||||
if (pid == 0 && !useParentPid)
|
||||
{
|
||||
Logger.LogInfo("No PID specified. Allocating console...");
|
||||
Bridge.FreeConsole();
|
||||
AllocateLocalConsole();
|
||||
}
|
||||
else
|
||||
|
||||
@@ -12,4 +12,4 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>False</PublishSingleFile>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -12,4 +12,4 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>False</PublishSingleFile>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user