Compare commits
16 Commits
autoUpgrad
...
niels9001/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d628c243d9 | ||
|
|
088da21a70 | ||
|
|
befb5c672e | ||
|
|
578554d157 | ||
|
|
e4f98897ce | ||
|
|
be1e749574 | ||
|
|
dc57d22754 | ||
|
|
99ef5948b0 | ||
|
|
218a01c1a9 | ||
|
|
1c7f3d832c | ||
|
|
ff87dce4a4 | ||
|
|
758a60103c | ||
|
|
f8cadbf7f0 | ||
|
|
0819a6268b | ||
|
|
a31f82fcbd | ||
|
|
daeb2e1ef4 |
2
.github/actions/spell-check/excludes.txt
vendored
@@ -107,7 +107,7 @@
|
||||
^src/common/sysinternals/Eula/
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherComparisonTests.cs$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherDiacriticsTests.cs$
|
||||
^src/modules/cmdpal/doc/initial-sdk-spec/list-elements-mock-002\.pdn$
|
||||
^doc/devdocs/modules/cmdpal/initial-sdk-spec/list-elements-mock-002\.pdn$
|
||||
^src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownImagesPage\.cs$
|
||||
^src/modules/cmdpal/Microsoft\.CmdPal\.UI/Settings/InternalPage\.SampleData\.cs$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CmdPal\.Common\.UnitTests/.*\.TestData\.cs$
|
||||
|
||||
8
.github/actions/spell-check/expect.txt
vendored
@@ -1641,6 +1641,7 @@ tracelogging
|
||||
tracerpt
|
||||
trackbar
|
||||
trafficmanager
|
||||
traies
|
||||
transicc
|
||||
TRAYMOUSEMESSAGE
|
||||
triaging
|
||||
@@ -1661,6 +1662,7 @@ UBR
|
||||
UCallback
|
||||
ucrt
|
||||
ucrtd
|
||||
udit
|
||||
uefi
|
||||
uesc
|
||||
UFlags
|
||||
@@ -2341,3 +2343,9 @@ YTimer
|
||||
zamora
|
||||
zonability
|
||||
Zorder
|
||||
LLMHF
|
||||
RIGHTBUTTON
|
||||
SIZEALL
|
||||
grabandmove
|
||||
GRABANDMOVEMODULEINTERFACE
|
||||
INITCOMMONCONTROLSEX
|
||||
|
||||
@@ -250,6 +250,9 @@
|
||||
"PowerToys.ZoomItModuleInterface.dll",
|
||||
"PowerToys.ZoomItSettingsInterop.dll",
|
||||
|
||||
"PowerToys.GrabAndMove.exe",
|
||||
"PowerToys.GrabAndMoveModuleInterface.dll",
|
||||
|
||||
"WinUI3Apps\\PowerToys.Settings.dll",
|
||||
"WinUI3Apps\\PowerToys.Settings.exe",
|
||||
|
||||
|
||||
@@ -108,6 +108,7 @@ Thank you for using PowerToys!
|
||||
| Microsoft.PowerToys.AwakeIndefinitelyKeepAwakeEvent | Triggered when the system is set to stay awake indefinitely. |
|
||||
| Microsoft.PowerToys.AwakeNoKeepAwakeEvent | Occurs when Awake is turned off, allowing the computer to enter sleep mode. |
|
||||
| Microsoft.PowerToys.AwakeTimedKeepAwakeEvent | Triggered when the system is kept awake for a specified time duration. |
|
||||
| Microsoft.PowerToys.Awake_CLICommand | Triggered when an Awake CLI command is executed, logging the command name and success status. |
|
||||
|
||||
### Color Picker
|
||||
|
||||
@@ -204,6 +205,7 @@ Thank you for using PowerToys!
|
||||
| Microsoft.PowerToys.FileLocksmith_Invoked | Occurs when File Locksmith is invoked. |
|
||||
| Microsoft.PowerToys.FileLocksmith_InvokedRet | Triggered when File Locksmith invocation returns a result. |
|
||||
| Microsoft.PowerToys.FileLocksmith_QueryContextMenuError | Occurs when there is an error querying the context menu for File Locksmith. |
|
||||
| Microsoft.PowerToys.FileLocksmith_CLICommand | Triggered when a File Locksmith CLI command is executed, logging the operation mode (query, kill, query-wait, query-json, or help) and success status. |
|
||||
|
||||
### FileExplorerAddOns
|
||||
|
||||
@@ -258,6 +260,7 @@ Thank you for using PowerToys!
|
||||
| Microsoft.PowerToys.ImageResizer_Invoked | Occurs when Image Resizer is invoked by the user. |
|
||||
| Microsoft.PowerToys.ImageResizer_InvokedRet | Fires when the Image Resizer operation is completed and returns a result. |
|
||||
| Microsoft.PowerToys.ImageResizer_QueryContextMenuError | Triggered when there is an error querying the context menu for Image Resizer. |
|
||||
| Microsoft.PowerToys.ImageResizer_CLICommand | Triggered when an Image Resizer CLI command is executed, logging the command name and success status. |
|
||||
|
||||
### Keyboard Manager
|
||||
|
||||
|
||||
@@ -1036,6 +1036,15 @@
|
||||
<Project Path="src/modules/ZoomIt/ZoomItModuleInterface/ZoomItModuleInterface.vcxproj" Id="e4585179-2ac1-4d5f-a3ff-cfc5392f694c" />
|
||||
<Project Path="src/modules/ZoomIt/ZoomItSettingsInterop/ZoomItSettingsInterop.vcxproj" Id="ca7d8106-30b9-4aec-9d05-b69b31b8c461" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/GrabAndMove/">
|
||||
<Project Path="src/modules/GrabAndMove/GrabAndMove/GrabAndMove.vcxproj" Id="568c4c30-2e3c-4c2c-a691-007362073765" />
|
||||
<Project Path="src/modules/GrabAndMove/GrabAndMoveModuleInterface/GrabAndMoveModuleInterface.vcxproj" Id="2c3f7770-4e57-46b7-8dc1-7428a383d0db" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/ShowDesktop/">
|
||||
<Project Path="src/modules/ShowDesktop/ShowDesktopModuleInterface/ShowDesktopModuleInterface.vcxproj" Id="a1b2c3d4-e5f6-7890-abcd-ef1234567890" />
|
||||
<Project Path="src/modules/ShowDesktop/ShowDesktop/ShowDesktop.csproj" Id="b2c3d4e5-f6a7-8901-bcde-f12345678901" />
|
||||
<Project Path="src/modules/ShowDesktop/ShowDesktop.UnitTests/ShowDesktop.UnitTests.csproj" Id="c3d4e5f6-a7b8-9012-cdef-123456789012" />
|
||||
</Folder>
|
||||
<Folder Name="/settings-ui/">
|
||||
<Project Path="src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
@@ -1100,6 +1109,9 @@
|
||||
<BuildDependency Project="src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj" />
|
||||
<BuildDependency Project="src/modules/LightSwitch/LightSwitchModuleInterface/LightSwitchModuleInterface.vcxproj" />
|
||||
<BuildDependency Project="src/modules/LightSwitch/LightSwitchService/LightSwitchService.vcxproj" />
|
||||
<BuildDependency Project="src/modules/GrabAndMove/GrabAndMoveModuleInterface/GrabAndMoveModuleInterface.vcxproj" />
|
||||
<BuildDependency Project="src/modules/GrabAndMove/GrabAndMove/GrabAndMove.vcxproj" />
|
||||
<BuildDependency Project="src/modules/ShowDesktop/ShowDesktopModuleInterface/ShowDesktopModuleInterface.vcxproj" />
|
||||
<BuildDependency Project="src/modules/powerrename/dll/PowerRenameExt.vcxproj" />
|
||||
<BuildDependency Project="src/modules/powerrename/lib/PowerRenameLib.vcxproj" />
|
||||
<BuildDependency Project="src/modules/previewpane/Common/PreviewHandlerCommon.csproj" />
|
||||
|
||||
|
Before Width: | Height: | Size: 2.3 MiB After Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 5.3 MiB After Width: | Height: | Size: 5.3 MiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 3.5 MiB After Width: | Height: | Size: 3.5 MiB |
|
Before Width: | Height: | Size: 497 KiB After Width: | Height: | Size: 497 KiB |
|
Before Width: | Height: | Size: 276 KiB After Width: | Height: | Size: 276 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 269 KiB After Width: | Height: | Size: 269 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 228 KiB After Width: | Height: | Size: 228 KiB |
|
Before Width: | Height: | Size: 491 KiB After Width: | Height: | Size: 491 KiB |
|
Before Width: | Height: | Size: 706 KiB After Width: | Height: | Size: 706 KiB |
|
Before Width: | Height: | Size: 408 KiB After Width: | Height: | Size: 408 KiB |
|
Before Width: | Height: | Size: 384 KiB After Width: | Height: | Size: 384 KiB |
|
Before Width: | Height: | Size: 493 KiB After Width: | Height: | Size: 493 KiB |
|
Before Width: | Height: | Size: 492 KiB After Width: | Height: | Size: 492 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 311 KiB After Width: | Height: | Size: 311 KiB |
|
Before Width: | Height: | Size: 297 KiB After Width: | Height: | Size: 297 KiB |
|
Before Width: | Height: | Size: 284 KiB After Width: | Height: | Size: 284 KiB |
|
Before Width: | Height: | Size: 225 KiB After Width: | Height: | Size: 225 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 890 KiB After Width: | Height: | Size: 890 KiB |
|
Before Width: | Height: | Size: 858 KiB After Width: | Height: | Size: 858 KiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
66
doc/devdocs/modules/show-desktop.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# ShowDesktop Module
|
||||
|
||||
## Overview
|
||||
|
||||
ShowDesktop brings macOS Sonoma's "click wallpaper to reveal desktop" feature to Windows. When enabled, clicking on empty desktop wallpaper minimizes all windows to reveal the desktop. Clicking again (or clicking any app/taskbar) restores all windows to their previous positions.
|
||||
|
||||
## Architecture
|
||||
|
||||
ShowDesktop follows the **External Application Launcher** pattern:
|
||||
|
||||
```
|
||||
Runner
|
||||
└─ ShowDesktopModuleInterface.dll (C++ interface DLL)
|
||||
└─ PowerToys.ShowDesktop.exe (C# application)
|
||||
```
|
||||
|
||||
### Components
|
||||
|
||||
| Component | Description |
|
||||
|-----------|-------------|
|
||||
| **ShowDesktopModuleInterface** | C++ DLL implementing `PowertoyModuleIface`. Handles module lifecycle (enable/disable) and spawns the C# app. |
|
||||
| **PowerToys.ShowDesktop.exe** | C# application containing the core desktop-peek logic. Runs a Win32 message loop for the low-level mouse hook. |
|
||||
|
||||
### Core C# Classes
|
||||
|
||||
| Class | Purpose |
|
||||
|-------|---------|
|
||||
| `DesktopPeek` | Core state machine (Idle ↔ Peeking). Orchestrates click detection, window tracking, and restore. |
|
||||
| `MouseHook` | Low-level mouse hook (`WH_MOUSE_LL`) for detecting clicks on desktop wallpaper. |
|
||||
| `FocusWatcher` | `WinEventHook(EVENT_SYSTEM_FOREGROUND)` for monitoring foreground window changes. |
|
||||
| `WindowTracker` | Enumerates, captures, minimizes, restores, and animates windows. |
|
||||
| `DesktopDetector` | Identifies whether a click landed on desktop wallpaper, desktop icons, or the taskbar. |
|
||||
| `VirtualDesktopService` | Virtual desktop management via undocumented COM interfaces. |
|
||||
| `NativeMethods` | P/Invoke declarations for Win32 APIs. |
|
||||
|
||||
## Settings
|
||||
|
||||
Settings are stored in `%LOCALAPPDATA%\Microsoft\PowerToys\ShowDesktop\settings.json`.
|
||||
|
||||
| Setting | Type | Default | Description |
|
||||
|---------|------|---------|-------------|
|
||||
| `peek-mode` | int | 0 | 0=Native Show Desktop, 1=Minimize, 2=Fly Away |
|
||||
| `require-double-click` | bool | false | Require double-click on wallpaper to activate |
|
||||
| `enable-taskbar-peek` | bool | false | Also trigger peek from empty taskbar space |
|
||||
| `enable-gaming-detection` | bool | true | Auto-pause during fullscreen gaming |
|
||||
| `fly-away-animation-duration-ms` | int | 300 | Animation duration for Fly Away mode (ms) |
|
||||
|
||||
## Peek Modes
|
||||
|
||||
- **Native Show Desktop (0)**: Uses the built-in Win+D mechanism via `SendInput`.
|
||||
- **Minimize (1)**: Minimizes each window individually, preserving exact `WINDOWPLACEMENT` state for restore.
|
||||
- **Fly Away (2)**: Animated offscreen movement of windows, then restore with reverse animation.
|
||||
|
||||
## IPC
|
||||
|
||||
Communication between the interface DLL and the C# app uses Windows named events:
|
||||
|
||||
- `SHOW_DESKTOP_TERMINATE_EVENT`: Signaled by the interface DLL to terminate the C# app on disable.
|
||||
|
||||
## GPO Policy
|
||||
|
||||
Registry value: `ConfigureEnabledUtilityShowDesktop` under `SOFTWARE\Policies\PowerToys`
|
||||
|
||||
## Credits
|
||||
|
||||
Based on [PeekDesktop](https://github.com/shanselman/PeekDesktop) by Scott Hanselman.
|
||||
@@ -18,6 +18,7 @@
|
||||
<?define RegistryPreviewProjectName="RegistryPreview"?>
|
||||
<?define PeekProjectName="Peek"?>
|
||||
<?define WorkspacesProjectName="Workspaces"?>
|
||||
<?define ShowDesktopProjectName="ShowDesktop"?>
|
||||
|
||||
<?define RepoDir="$(var.ProjectDir)..\..\" ?>
|
||||
<?if $(var.Platform) = x64?>
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
<ComponentGroupRef Id="ToolComponentGroup" />
|
||||
<ComponentGroupRef Id="MonacoSRCHeatGenerated" />
|
||||
<ComponentGroupRef Id="WorkspacesComponentGroup" />
|
||||
<ComponentGroupRef Id="ShowDesktopComponentGroup" />
|
||||
<ComponentGroupRef Id="CmdPalComponentGroup" />
|
||||
</Feature>
|
||||
|
||||
|
||||
30
installer/PowerToysSetupVNext/ShowDesktop.wxs
Normal file
@@ -0,0 +1,30 @@
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
||||
|
||||
<?include $(sys.CURRENTDIR)\Common.wxi?>
|
||||
|
||||
<?define ShowDesktopImagesFiles=?>
|
||||
<?define ShowDesktopImagesFilesPath=$(var.BinDir)\Assets\ShowDesktop\?>
|
||||
|
||||
<Fragment>
|
||||
<!-- ShowDesktop images -->
|
||||
|
||||
<DirectoryRef Id="BaseApplicationsAssetsFolder">
|
||||
<Directory Id="ShowDesktopImagesFolder" Name="ShowDesktop" />
|
||||
</DirectoryRef>
|
||||
|
||||
<DirectoryRef Id="ShowDesktopImagesFolder" FileSource="$(var.ShowDesktopImagesFilesPath)">
|
||||
<!-- Generated by generateFileComponents.ps1 -->
|
||||
<!--ShowDesktopImagesFiles_Component_Def-->
|
||||
</DirectoryRef>
|
||||
|
||||
<ComponentGroup Id="ShowDesktopComponentGroup">
|
||||
<Component Id="RemoveShowDesktopFolder" Guid="7B3A9C1E-2D4F-5A6B-8E7C-9F0D1A2B3C4E" Directory="INSTALLFOLDER">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="RemoveShowDesktopFolder" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<RemoveFolder Id="RemoveFolderShowDesktopImagesFolder" Directory="ShowDesktopImagesFolder" On="uninstall" />
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
|
||||
</Fragment>
|
||||
</Wix>
|
||||
@@ -28,6 +28,7 @@ namespace ExprtkCalculator::internal
|
||||
std::wstring ToWStringFullPrecision(double value)
|
||||
{
|
||||
std::wostringstream oss;
|
||||
oss.imbue(std::locale::classic());
|
||||
oss << std::fixed << std::setprecision(15) << value;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ namespace winrt::PowerToys::GPOWrapper::implementation
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredPowerDisplayEnabledValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredGrabAndMoveEnabledValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredGrabAndMoveEnabledValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredFancyZonesEnabledValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredFancyZonesEnabledValue());
|
||||
@@ -232,6 +236,10 @@ namespace winrt::PowerToys::GPOWrapper::implementation
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredNewPlusEnabledValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredShowDesktopEnabledValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredShowDesktopEnabledValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredWorkspacesEnabledValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredWorkspacesEnabledValue());
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation
|
||||
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredPowerDisplayEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredGrabAndMoveEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredFileLocksmithEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue();
|
||||
@@ -64,6 +65,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteOllamaValue();
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteFoundryLocalValue();
|
||||
static GpoRuleConfigured GetConfiguredNewPlusEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredShowDesktopEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredWorkspacesEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbClipboardSharingEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbFileTransferEnabledValue();
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace PowerToys
|
||||
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredPowerDisplayEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredGrabAndMoveEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredFileLocksmithEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue();
|
||||
@@ -68,6 +69,7 @@ namespace PowerToys
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteOllamaValue();
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteFoundryLocalValue();
|
||||
static GpoRuleConfigured GetConfiguredNewPlusEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredShowDesktopEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredWorkspacesEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbClipboardSharingEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbFileTransferEnabledValue();
|
||||
|
||||
@@ -71,5 +71,15 @@ namespace PowerToys.GPOWrapperProjection
|
||||
{
|
||||
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredLightSwitchEnabledValue();
|
||||
}
|
||||
|
||||
public static GpoRuleConfigured GetConfiguredGrabAndMoveEnabledValue()
|
||||
{
|
||||
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredGrabAndMoveEnabledValue();
|
||||
}
|
||||
|
||||
public static GpoRuleConfigured GetConfiguredShowDesktopEnabledValue()
|
||||
{
|
||||
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredShowDesktopEnabledValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ namespace ManagedCommon
|
||||
ShortcutGuide,
|
||||
PowerOCR,
|
||||
Workspaces,
|
||||
GrabAndMove,
|
||||
ShowDesktop,
|
||||
ZoomIt,
|
||||
GeneralSettings,
|
||||
}
|
||||
|
||||
@@ -145,6 +145,10 @@ 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";
|
||||
|
||||
// Path to the events used by GrabAndMove
|
||||
const wchar_t GRABANDMOVE_REFRESH_SETTINGS_EVENT[] = L"Local\\PowerToysGrabAndMove-RefreshSettingsEvent-a7b3c1d2-4e5f-6a7b-8c9d-0e1f2a3b4c5d";
|
||||
const wchar_t GRABANDMOVE_EXIT_EVENT[] = L"Local\\PowerToysGrabAndMove-ExitEvent-b8c4d2e3-5f6a-7b8c-9d0e-1f2a3b4c5d6e";
|
||||
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";
|
||||
@@ -183,6 +187,9 @@ namespace CommonSharedConstants
|
||||
const wchar_t MWB_TOGGLE_EASY_MOUSE_EVENT[] = L"Local\\PowerToysMWB-ToggleEasyMouseEvent-a9c8d7b6-e5f4-3c2a-1b0d-9e8f7a6b5c4d";
|
||||
const wchar_t MWB_RECONNECT_EVENT[] = L"Local\\PowerToysMWB-ReconnectEvent-b8d7c6a5-f4e3-2b1c-0a9d-8e7f6a5b4c3d";
|
||||
|
||||
// Path to the event used by ShowDesktop
|
||||
const wchar_t SHOW_DESKTOP_TERMINATE_EVENT[] = L"Local\\PowerToysShowDesktop-TerminateEvent-7b3a9c1e-2d4f-5a6b-8e7c-9f0d1a2b3c4e";
|
||||
|
||||
// Max DWORD for key code to disable keys.
|
||||
const DWORD VK_DISABLED = 0x100;
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ struct LogSettings
|
||||
inline const static std::string zoomItLoggerName = "zoom-it";
|
||||
inline const static std::string lightSwitchLoggerName = "light-switch";
|
||||
inline const static std::string powerDisplayLoggerName = "powerdisplay";
|
||||
inline const static std::string grabAndMoveLoggerName = "grabandmove";
|
||||
inline const static int retention = 30;
|
||||
std::wstring logLevel;
|
||||
LogSettings();
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace powertoys_gpo
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_CROP_AND_LOCK = L"ConfigureEnabledUtilityCropAndLock";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_LIGHT_SWITCH = L"ConfigureEnabledUtilityLightSwitch";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_POWER_DISPLAY = L"ConfigureEnabledUtilityPowerDisplay";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_GRAB_AND_MOVE = L"ConfigureEnabledUtilityGrabAndMove";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_FANCYZONES = L"ConfigureEnabledUtilityFancyZones";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_FILE_LOCKSMITH = L"ConfigureEnabledUtilityFileLocksmith";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_SVG_PREVIEW = L"ConfigureEnabledUtilityFileExplorerSVGPreview";
|
||||
@@ -70,6 +71,7 @@ namespace powertoys_gpo
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_QOI_PREVIEW = L"ConfigureEnabledUtilityFileExplorerQOIPreview";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_QOI_THUMBNAILS = L"ConfigureEnabledUtilityFileExplorerQOIThumbnails";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_NEWPLUS = L"ConfigureEnabledUtilityNewPlus";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_SHOW_DESKTOP = L"ConfigureEnabledUtilityShowDesktop";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_WORKSPACES = L"ConfigureEnabledUtilityWorkspaces";
|
||||
|
||||
// The registry value names for PowerToys installer and update policies.
|
||||
@@ -317,6 +319,11 @@ namespace powertoys_gpo
|
||||
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_POWER_DISPLAY);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredGrabAndMoveEnabledValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_GRAB_AND_MOVE);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredFancyZonesEnabledValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_FANCYZONES);
|
||||
@@ -506,6 +513,11 @@ namespace powertoys_gpo
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_NEWPLUS);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredShowDesktopEnabledValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_SHOW_DESKTOP);
|
||||
}
|
||||
#pragma endregion UtilityEnabledStatePolicies
|
||||
|
||||
// Individual module setting policies
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<definition name="SUPPORTED_POWERTOYS_0_96_0" displayName="$(string.SUPPORTED_POWERTOYS_0_96_0)"/>
|
||||
<definition name="SUPPORTED_POWERTOYS_0_97_0" displayName="$(string.SUPPORTED_POWERTOYS_0_97_0)"/>
|
||||
<definition name="SUPPORTED_POWERTOYS_0_98_0" displayName="$(string.SUPPORTED_POWERTOYS_0_98_0)"/>
|
||||
<definition name="SUPPORTED_POWERTOYS_0_99_0" displayName="$(string.SUPPORTED_POWERTOYS_0_99_0)"/>
|
||||
<definition name="SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1" displayName="$(string.SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1)"/>
|
||||
</definitions>
|
||||
</supportedOn>
|
||||
@@ -152,7 +153,7 @@
|
||||
</policy>
|
||||
<policy name="ConfigureEnabledUtilityPowerDisplay" class="Both" displayName="$(string.ConfigureEnabledUtilityPowerDisplay)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityPowerDisplay">
|
||||
<parentCategory ref="PowerToys" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_95_0" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_99_0" />
|
||||
<enabledValue>
|
||||
<decimal value="1" />
|
||||
</enabledValue>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<string id="SUPPORTED_POWERTOYS_0_96_0">PowerToys version 0.96.0 or later</string>
|
||||
<string id="SUPPORTED_POWERTOYS_0_97_0">PowerToys version 0.97.0 or later</string>
|
||||
<string id="SUPPORTED_POWERTOYS_0_98_0">PowerToys version 0.98.0 or later</string>
|
||||
<string id="SUPPORTED_POWERTOYS_0_99_0">PowerToys version 0.99.0 or later</string>
|
||||
<string id="SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1">From PowerToys version 0.64.0 until PowerToys version 0.87.1</string>
|
||||
|
||||
<string id="ConfigureAllUtilityGlobalEnabledStateDescription">This policy configures the enabled state for all PowerToys utilities.
|
||||
|
||||
@@ -496,23 +496,119 @@ private:
|
||||
|
||||
if (!GetGUIThreadInfo(0, &gui_info))
|
||||
{
|
||||
Logger::warn(L"Auto-copy: GetGUIThreadInfo failed (error={})", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
HWND target = gui_info.hwndFocus ? gui_info.hwndFocus : gui_info.hwndActive;
|
||||
if (!target)
|
||||
{
|
||||
Logger::warn(L"Auto-copy: no focused or active window found");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD_PTR result = 0;
|
||||
return SendMessageTimeout(target,
|
||||
WM_COPY,
|
||||
0,
|
||||
0,
|
||||
SMTO_ABORTIFHUNG | SMTO_BLOCK,
|
||||
50,
|
||||
&result) != 0;
|
||||
auto sendResult = SendMessageTimeout(target, WM_COPY, 0, 0, SMTO_ABORTIFHUNG | SMTO_BLOCK, 50, &result);
|
||||
return sendResult != 0;
|
||||
}
|
||||
|
||||
// Helper: poll clipboard sequence number for a change from initial_sequence.
|
||||
// Returns true if the sequence number changed within the given number of polls.
|
||||
bool poll_clipboard_sequence(DWORD initial_sequence, int poll_attempts, std::chrono::milliseconds poll_delay)
|
||||
{
|
||||
for (int poll = 0; poll < poll_attempts; ++poll)
|
||||
{
|
||||
if (GetClipboardSequenceNumber() != initial_sequence)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
std::this_thread::sleep_for(poll_delay);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper: send Ctrl+C via SendInput, releasing any held modifier keys first
|
||||
// (the hotkey combination may still have modifiers physically pressed).
|
||||
bool send_ctrl_c_input()
|
||||
{
|
||||
std::vector<INPUT> inputs;
|
||||
|
||||
// Release all modifier keys that are currently held down from the hotkey.
|
||||
// Without this, the target app sees e.g. Win+Shift+Ctrl+C instead of just Ctrl+C.
|
||||
try_inject_modifier_key_up(inputs, VK_LCONTROL);
|
||||
try_inject_modifier_key_up(inputs, VK_RCONTROL);
|
||||
try_inject_modifier_key_up(inputs, VK_LWIN);
|
||||
try_inject_modifier_key_up(inputs, VK_RWIN);
|
||||
try_inject_modifier_key_up(inputs, VK_LSHIFT);
|
||||
try_inject_modifier_key_up(inputs, VK_RSHIFT);
|
||||
try_inject_modifier_key_up(inputs, VK_LMENU);
|
||||
try_inject_modifier_key_up(inputs, VK_RMENU);
|
||||
|
||||
// Ctrl down
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = VK_CONTROL;
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
// C down
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = 0x43; // C
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
// C up
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = 0x43; // C
|
||||
input_event.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
// Ctrl up
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = VK_CONTROL;
|
||||
input_event.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
// Restore modifiers that were held down
|
||||
try_inject_modifier_key_restore(inputs, VK_LCONTROL);
|
||||
try_inject_modifier_key_restore(inputs, VK_RCONTROL);
|
||||
try_inject_modifier_key_restore(inputs, VK_LWIN);
|
||||
try_inject_modifier_key_restore(inputs, VK_RWIN);
|
||||
try_inject_modifier_key_restore(inputs, VK_LSHIFT);
|
||||
try_inject_modifier_key_restore(inputs, VK_RSHIFT);
|
||||
try_inject_modifier_key_restore(inputs, VK_LMENU);
|
||||
try_inject_modifier_key_restore(inputs, VK_RMENU);
|
||||
|
||||
// Prevent Start Menu from activating after Win key release/restore
|
||||
INPUT dummyEvent = {};
|
||||
dummyEvent.type = INPUT_KEYBOARD;
|
||||
dummyEvent.ki.wVk = 0xFF;
|
||||
dummyEvent.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
inputs.push_back(dummyEvent);
|
||||
|
||||
auto uSent = SendInput(static_cast<UINT>(inputs.size()), inputs.data(), sizeof(INPUT));
|
||||
if (uSent != inputs.size())
|
||||
{
|
||||
DWORD errorCode = GetLastError();
|
||||
auto errorMessage = get_last_error_message(errorCode);
|
||||
Logger::error(L"SendInput failed for Ctrl+C. Expected to send {} inputs and sent only {}. {}", inputs.size(), uSent, errorMessage.has_value() ? errorMessage.value() : L"");
|
||||
Trace::AdvancedPaste_Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"input.SendInput");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool send_copy_selection()
|
||||
@@ -526,78 +622,30 @@ private:
|
||||
for (int attempt = 0; attempt < copy_attempts; ++attempt)
|
||||
{
|
||||
const auto initial_sequence = GetClipboardSequenceNumber();
|
||||
copy_succeeded = try_send_copy_message();
|
||||
|
||||
if (!copy_succeeded)
|
||||
// Strategy 1: Try WM_COPY message (works for standard Win32 controls)
|
||||
bool wm_copy_sent = try_send_copy_message();
|
||||
|
||||
if (wm_copy_sent)
|
||||
{
|
||||
std::vector<INPUT> inputs;
|
||||
|
||||
// send Ctrl+C (key downs and key ups)
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = VK_CONTROL;
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = 0x43; // C
|
||||
// Avoid triggering detection by the centralized keyboard hook.
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = 0x43; // C
|
||||
input_event.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
// Avoid triggering detection by the centralized keyboard hook.
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = VK_CONTROL;
|
||||
input_event.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
auto uSent = SendInput(static_cast<UINT>(inputs.size()), inputs.data(), sizeof(INPUT));
|
||||
if (uSent != inputs.size())
|
||||
{
|
||||
DWORD errorCode = GetLastError();
|
||||
auto errorMessage = get_last_error_message(errorCode);
|
||||
Logger::error(L"SendInput failed for Ctrl+C. Expected to send {} inputs and sent only {}. {}", inputs.size(), uSent, errorMessage.has_value() ? errorMessage.value() : L"");
|
||||
Trace::AdvancedPaste_Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"input.SendInput");
|
||||
}
|
||||
else
|
||||
if (poll_clipboard_sequence(initial_sequence, clipboard_poll_attempts, clipboard_poll_delay))
|
||||
{
|
||||
copy_succeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (copy_succeeded)
|
||||
// Strategy 2: If WM_COPY didn't work, try SendInput Ctrl+C (works for Electron, browsers, etc.)
|
||||
if (!copy_succeeded)
|
||||
{
|
||||
bool sequence_changed = false;
|
||||
for (int poll_attempt = 0; poll_attempt < clipboard_poll_attempts; ++poll_attempt)
|
||||
const auto sequence_before_ctrl_c = GetClipboardSequenceNumber();
|
||||
|
||||
if (send_ctrl_c_input())
|
||||
{
|
||||
if (GetClipboardSequenceNumber() != initial_sequence)
|
||||
if (poll_clipboard_sequence(sequence_before_ctrl_c, clipboard_poll_attempts, clipboard_poll_delay))
|
||||
{
|
||||
sequence_changed = true;
|
||||
break;
|
||||
copy_succeeded = true;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(clipboard_poll_delay);
|
||||
}
|
||||
|
||||
copy_succeeded = sequence_changed;
|
||||
}
|
||||
|
||||
if (copy_succeeded)
|
||||
@@ -611,6 +659,11 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
if (!copy_succeeded)
|
||||
{
|
||||
Logger::warn(L"Auto-copy: all {} copy attempts failed — the target application did not update the clipboard after WM_COPY and Ctrl+C", copy_attempts);
|
||||
}
|
||||
|
||||
return copy_succeeded;
|
||||
}
|
||||
|
||||
@@ -977,6 +1030,7 @@ public:
|
||||
{
|
||||
if (!send_copy_selection())
|
||||
{
|
||||
Logger::warn(L"Auto-copy: failed to copy selection for custom action index {} — aborting action", custom_action_index);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
|
||||
if (argc < 2)
|
||||
{
|
||||
Logger::warn("No arguments provided");
|
||||
return { 1, get_usage(strings) };
|
||||
return { 1, get_usage(strings), L"help" };
|
||||
}
|
||||
|
||||
bool json_output = false;
|
||||
@@ -156,18 +156,18 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
|
||||
catch (...)
|
||||
{
|
||||
Logger::error("Invalid timeout value");
|
||||
return { 1, strings.GetString(IDS_ERROR_INVALID_TIMEOUT) };
|
||||
return { 1, strings.GetString(IDS_ERROR_INVALID_TIMEOUT), L"query-wait" };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::error("Timeout argument missing");
|
||||
return { 1, strings.GetString(IDS_ERROR_TIMEOUT_ARG) };
|
||||
return { 1, strings.GetString(IDS_ERROR_TIMEOUT_ARG), L"query-wait" };
|
||||
}
|
||||
}
|
||||
else if (arg == L"--help")
|
||||
{
|
||||
return { 0, get_usage(strings) };
|
||||
return { 0, get_usage(strings), L"help" };
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -178,7 +178,7 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
|
||||
if (paths.empty())
|
||||
{
|
||||
Logger::error("No paths specified");
|
||||
return { 1, strings.GetString(IDS_ERROR_NO_PATHS) };
|
||||
return { 1, strings.GetString(IDS_ERROR_NO_PATHS), L"query" };
|
||||
}
|
||||
|
||||
Logger::info("Processing {} paths", paths.size());
|
||||
@@ -213,13 +213,13 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
|
||||
{
|
||||
Logger::warn("Timeout waiting for files to be unlocked");
|
||||
ss << strings.GetString(IDS_TIMEOUT);
|
||||
return { 1, ss.str() };
|
||||
return { 1, ss.str(), L"query-wait" };
|
||||
}
|
||||
}
|
||||
|
||||
Sleep(200);
|
||||
}
|
||||
return { 0, ss.str() };
|
||||
return { 0, ss.str(), L"query-wait" };
|
||||
}
|
||||
|
||||
auto results = finder.find(paths);
|
||||
@@ -244,5 +244,6 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
|
||||
output_ss << get_text(results, strings);
|
||||
}
|
||||
|
||||
return { 0, output_ss.str() };
|
||||
std::wstring cmd_name = kill ? L"kill" : (json_output ? L"query-json" : L"query");
|
||||
return { 0, output_ss.str(), cmd_name };
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ struct CommandResult
|
||||
{
|
||||
int exit_code;
|
||||
std::wstring output;
|
||||
std::wstring command_name;
|
||||
};
|
||||
|
||||
struct IProcessFinder
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "pch.h"
|
||||
#include "CLILogic.h"
|
||||
#include "FileLocksmithLib/FileLocksmith.h"
|
||||
#include "FileLocksmithLib/Trace.h"
|
||||
#include <iostream>
|
||||
#include "resource.h"
|
||||
#include <common/logger/logger.h>
|
||||
@@ -47,6 +48,7 @@ struct RealStringProvider : IStringProvider
|
||||
int wmain(int argc, wchar_t* argv[])
|
||||
{
|
||||
winrt::init_apartment();
|
||||
Trace::RegisterProvider();
|
||||
LoggerHelpers::init_logger(L"FileLocksmithCLI", L"", LogSettings::fileLocksmithLoggerName);
|
||||
Logger::info("FileLocksmithCLI started");
|
||||
|
||||
@@ -65,7 +67,10 @@ int wmain(int argc, wchar_t* argv[])
|
||||
Logger::info("Command succeeded");
|
||||
}
|
||||
|
||||
Trace::CLICommand(result.command_name.c_str(), result.exit_code == 0);
|
||||
|
||||
std::wcout << result.output;
|
||||
Trace::UnregisterProvider();
|
||||
return result.exit_code;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace FileLocksmithCLIUnitTests
|
||||
auto result = run_command(1, argv, finder, terminator, strings);
|
||||
|
||||
Assert::AreEqual(1, result.exit_code);
|
||||
Assert::AreEqual(std::wstring(L"help"), result.command_name);
|
||||
}
|
||||
|
||||
TEST_METHOD(TestHelp)
|
||||
@@ -64,6 +65,7 @@ namespace FileLocksmithCLIUnitTests
|
||||
auto result = run_command(2, argv, finder, terminator, strings);
|
||||
|
||||
Assert::AreEqual(0, result.exit_code);
|
||||
Assert::AreEqual(std::wstring(L"help"), result.command_name);
|
||||
}
|
||||
|
||||
TEST_METHOD(TestFindProcesses)
|
||||
@@ -77,6 +79,7 @@ namespace FileLocksmithCLIUnitTests
|
||||
auto result = run_command(2, argv, finder, terminator, strings);
|
||||
|
||||
Assert::AreEqual(0, result.exit_code);
|
||||
Assert::AreEqual(std::wstring(L"query"), result.command_name);
|
||||
Assert::IsTrue(result.output.find(L"123") != std::wstring::npos);
|
||||
Assert::IsTrue(result.output.find(L"process") != std::wstring::npos);
|
||||
}
|
||||
@@ -94,6 +97,7 @@ namespace FileLocksmithCLIUnitTests
|
||||
Microsoft::VisualStudio::CppUnitTestFramework::Logger::WriteMessage(result.output.c_str());
|
||||
|
||||
Assert::AreEqual(0, result.exit_code);
|
||||
Assert::AreEqual(std::wstring(L"query-json"), result.command_name);
|
||||
Assert::IsTrue(result.output.find(L"\"pid\"") != std::wstring::npos);
|
||||
Assert::IsTrue(result.output.find(L"123") != std::wstring::npos);
|
||||
}
|
||||
@@ -109,6 +113,7 @@ namespace FileLocksmithCLIUnitTests
|
||||
auto result = run_command(3, argv, finder, terminator, strings);
|
||||
|
||||
Assert::AreEqual(0, result.exit_code);
|
||||
Assert::AreEqual(std::wstring(L"kill"), result.command_name);
|
||||
Assert::AreEqual((size_t)1, terminator.terminatedPids.size());
|
||||
Assert::AreEqual((DWORD)123, terminator.terminatedPids[0]);
|
||||
}
|
||||
@@ -125,6 +130,7 @@ namespace FileLocksmithCLIUnitTests
|
||||
auto result = run_command(5, argv, finder, terminator, strings);
|
||||
|
||||
Assert::AreEqual(1, result.exit_code);
|
||||
Assert::AreEqual(std::wstring(L"query-wait"), result.command_name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -49,3 +49,14 @@ void Trace::QueryContextMenuError(_In_ HRESULT hr) noexcept
|
||||
TraceLoggingHResult(hr),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
void Trace::CLICommand(_In_ PCWSTR commandName, _In_ bool successful) noexcept
|
||||
{
|
||||
TraceLoggingWriteWrapper(
|
||||
g_hProvider,
|
||||
"FileLocksmith_CLICommand",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingWideString(commandName, "CommandName"),
|
||||
TraceLoggingBoolean(successful, "Successful"));
|
||||
}
|
||||
|
||||
@@ -11,4 +11,5 @@ public:
|
||||
static void Invoked() noexcept;
|
||||
static void InvokedRet(_In_ HRESULT hr) noexcept;
|
||||
static void QueryContextMenuError(_In_ HRESULT hr) noexcept;
|
||||
static void CLICommand(_In_ PCWSTR commandName, _In_ bool successful) noexcept;
|
||||
};
|
||||
|
||||
BIN
src/modules/GrabAndMove/GrabAndMove/GrabAndMove.ico
Normal file
|
After Width: | Height: | Size: 766 B |
10
src/modules/GrabAndMove/GrabAndMove/GrabAndMove.manifest
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"
|
||||
xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<asmv3:application>
|
||||
<asmv3:windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
</assembly>
|
||||
102
src/modules/GrabAndMove/GrabAndMove/GrabAndMove.rc
Normal file
@@ -0,0 +1,102 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
#include "../../../common/version/version.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (United States) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
IDI_APP_ICON ICON "GrabAndMove.ico"
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
|
||||
BEGIN
|
||||
VALUE "CompanyName", COMPANY_NAME
|
||||
VALUE "FileDescription", FILE_DESCRIPTION
|
||||
VALUE "FileVersion", FILE_VERSION_STRING
|
||||
VALUE "InternalName", INTERNAL_NAME
|
||||
VALUE "LegalCopyright", COPYRIGHT_NOTE
|
||||
VALUE "OriginalFilename", ORIGINAL_FILENAME
|
||||
VALUE "ProductName", PRODUCT_NAME
|
||||
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
|
||||
END
|
||||
END
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
197
src/modules/GrabAndMove/GrabAndMove/GrabAndMove.vcxproj
Normal file
@@ -0,0 +1,197 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{568c4c30-2e3c-4c2c-a691-007362073765}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>GrabAndMove</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>GrabAndMove</ProjectName>
|
||||
<TargetName>PowerToys.GrabAndMove</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<OutDir>$(RepoRoot)$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>
|
||||
$(RepoRoot)src\common;
|
||||
$(RepoRoot)src\common\SettingsAPI;
|
||||
$(RepoRoot)src\;
|
||||
%(AdditionalIncludeDirectories)
|
||||
</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;UNICODE;_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EntryPointSymbol>wWinMainCRTStartup</EntryPointSymbol>
|
||||
<AdditionalDependencies>comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;UNICODE;_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EntryPointSymbol>wWinMainCRTStartup</EntryPointSymbol>
|
||||
<AdditionalDependencies>comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;UNICODE;_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EntryPointSymbol>wWinMainCRTStartup</EntryPointSymbol>
|
||||
<AdditionalDependencies>comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;UNICODE;_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EntryPointSymbol>wWinMainCRTStartup</EntryPointSymbol>
|
||||
<AdditionalDependencies>comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="GrabAndMove.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)src\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||
<Project>{6955446D-23F7-4023-9BB3-8657F904AF99}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="GrabAndMove.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="GrabAndMove.manifest" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{195243ad-53ca-40c9-8879-b9efef4fc26d}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{9bdf974b-a58f-4af8-aed8-4882381f7172}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{0e19fd51-9939-4511-b8cb-d144c0e2a670}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="GrabAndMove.ico">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="GrabAndMove.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
1151
src/modules/GrabAndMove/GrabAndMove/main.cpp
Normal file
1
src/modules/GrabAndMove/GrabAndMove/pch.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
||||
14
src/modules/GrabAndMove/GrabAndMove/pch.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
#include <commctrl.h>
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
21
src/modules/GrabAndMove/GrabAndMove/resource.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by GrabAndMove.rc
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#define IDI_APP_ICON 101
|
||||
#define IDR_TRAY_MENU 102
|
||||
#define IDM_EXIT 1001
|
||||
|
||||
#define WM_TRAY_ICON (WM_USER + 1)
|
||||
|
||||
//////////////////////////////
|
||||
// Non-localizable
|
||||
|
||||
#define FILE_DESCRIPTION "PowerToys GrabAndMove"
|
||||
#define INTERNAL_NAME "PowerToys.GrabAndMove"
|
||||
#define ORIGINAL_FILENAME "PowerToys.GrabAndMove.exe"
|
||||
|
||||
// Non-localizable
|
||||
//////////////////////////////
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <windows.h>
|
||||
#include "resource.h"
|
||||
#include "../../../common/version/version.h"
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_DLL
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
|
||||
BEGIN
|
||||
VALUE "CompanyName", COMPANY_NAME
|
||||
VALUE "FileDescription", FILE_DESCRIPTION
|
||||
VALUE "FileVersion", FILE_VERSION_STRING
|
||||
VALUE "InternalName", INTERNAL_NAME
|
||||
VALUE "LegalCopyright", COPYRIGHT_NOTE
|
||||
VALUE "OriginalFilename", ORIGINAL_FILENAME
|
||||
VALUE "ProductName", PRODUCT_NAME
|
||||
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
|
||||
END
|
||||
END
|
||||
@@ -0,0 +1,208 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{2c3f7770-4e57-46b7-8dc1-7428a383d0db}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>GrabAndMoveModuleInterface</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>GrabAndMoveModuleInterface</ProjectName>
|
||||
<TargetName>PowerToys.GrabAndMoveModuleInterface</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<OutDir>$(RepoRoot)$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;GRABANDMOVEMODULEINTERFACE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;GRABANDMOVEMODULEINTERFACE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;GRABANDMOVEMODULEINTERFACE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;GRABANDMOVEMODULEINTERFACE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(RepoRoot)src\common\inc;$(RepoRoot)src\common\Telemetry;..\..\;$(RepoRoot)src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="trace.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<ClCompile Include="trace.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="GrabAndMoveModuleInterface.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)src\common\logger\logger.vcxproj">
|
||||
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(RepoRoot)src\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="$(RepoRoot)deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
216
src/modules/GrabAndMove/GrabAndMoveModuleInterface/dllmain.cpp
Normal file
@@ -0,0 +1,216 @@
|
||||
#include "pch.h"
|
||||
#include <interface/powertoy_module_interface.h>
|
||||
#include "trace.h"
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
#include <common/utils/logger_helper.h>
|
||||
#include <common/interop/shared_constants.h>
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
Trace::RegisterProvider();
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
Trace::UnregisterProvider();
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// The PowerToy name that will be shown in the settings.
|
||||
const static wchar_t* MODULE_NAME = L"GrabAndMove";
|
||||
// Add a description that will be shown in the module settings page.
|
||||
const static wchar_t* MODULE_DESC = L"Move and resize windows with Alt+Drag (left button to move, right button to resize).";
|
||||
|
||||
class GrabAndMoveInterface : public PowertoyModuleIface
|
||||
{
|
||||
private:
|
||||
bool m_enabled = false;
|
||||
HANDLE m_process{ nullptr };
|
||||
HANDLE m_reload_settings_event_handle{ nullptr };
|
||||
HANDLE m_exit_event_handle{ nullptr };
|
||||
|
||||
public:
|
||||
GrabAndMoveInterface()
|
||||
{
|
||||
LoggerHelpers::init_logger(L"GrabAndMove", L"ModuleInterface", LogSettings::grabAndMoveLoggerName);
|
||||
m_reload_settings_event_handle = CreateDefaultEvent(CommonSharedConstants::GRABANDMOVE_REFRESH_SETTINGS_EVENT);
|
||||
m_exit_event_handle = CreateDefaultEvent(CommonSharedConstants::GRABANDMOVE_EXIT_EVENT);
|
||||
}
|
||||
|
||||
virtual const wchar_t* get_key() override
|
||||
{
|
||||
return L"GrabAndMove";
|
||||
}
|
||||
|
||||
virtual void destroy() override
|
||||
{
|
||||
disable();
|
||||
delete this;
|
||||
}
|
||||
|
||||
virtual const wchar_t* get_name() override
|
||||
{
|
||||
return MODULE_NAME;
|
||||
}
|
||||
|
||||
virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
|
||||
{
|
||||
return powertoys_gpo::getConfiguredGrabAndMoveEnabledValue();
|
||||
}
|
||||
|
||||
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
|
||||
{
|
||||
HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
|
||||
|
||||
PowerToysSettings::Settings settings(hinstance, get_name());
|
||||
settings.set_description(MODULE_DESC);
|
||||
settings.set_overview_link(L"https://aka.ms/powertoys");
|
||||
|
||||
return settings.serialize_to_buffer(buffer, buffer_size);
|
||||
}
|
||||
|
||||
virtual void set_config(const wchar_t* config) override
|
||||
{
|
||||
try
|
||||
{
|
||||
auto values = PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
|
||||
values.save_to_settings_file();
|
||||
|
||||
// Signal the GrabAndMove process to reload settings
|
||||
if (m_reload_settings_event_handle)
|
||||
{
|
||||
SetEvent(m_reload_settings_event_handle);
|
||||
}
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
Logger::error("[GrabAndMove] set_config: Failed to parse or apply config.");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void enable()
|
||||
{
|
||||
Logger::info(L"Enabling GrabAndMove module...");
|
||||
|
||||
if (m_process && WaitForSingleObject(m_process, 0) == WAIT_TIMEOUT)
|
||||
{
|
||||
m_enabled = true;
|
||||
Trace::Enable(true);
|
||||
Logger::debug(L"GrabAndMove process already running.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_process)
|
||||
{
|
||||
CloseHandle(m_process);
|
||||
m_process = nullptr;
|
||||
}
|
||||
|
||||
m_enabled = false;
|
||||
|
||||
unsigned long powertoys_pid = GetCurrentProcessId();
|
||||
std::wstring args = std::to_wstring(powertoys_pid);
|
||||
std::wstring exe_name = L"PowerToys.GrabAndMove.exe";
|
||||
|
||||
std::wstring resolved_path(MAX_PATH, L'\0');
|
||||
DWORD result = SearchPathW(
|
||||
nullptr,
|
||||
exe_name.c_str(),
|
||||
nullptr,
|
||||
static_cast<DWORD>(resolved_path.size()),
|
||||
resolved_path.data(),
|
||||
nullptr);
|
||||
|
||||
if (result == 0 || result >= resolved_path.size())
|
||||
{
|
||||
Logger::error(
|
||||
L"Failed to locate GrabAndMove executable named '{}' at location '{}'",
|
||||
exe_name,
|
||||
resolved_path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
resolved_path.resize(result);
|
||||
Logger::debug(L"Resolved executable path: {}", resolved_path);
|
||||
|
||||
std::wstring command_line = L"\"" + resolved_path + L"\" " + args;
|
||||
|
||||
STARTUPINFO si = { sizeof(si) };
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
if (!CreateProcessW(
|
||||
resolved_path.c_str(),
|
||||
command_line.data(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
TRUE,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&si,
|
||||
&pi))
|
||||
{
|
||||
Logger::error(L"Failed to launch GrabAndMove process. {}", get_last_error_or_default(GetLastError()));
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::info(L"GrabAndMove process launched successfully (PID: {}).", pi.dwProcessId);
|
||||
m_process = pi.hProcess;
|
||||
m_enabled = true;
|
||||
Trace::Enable(true);
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
|
||||
virtual void disable()
|
||||
{
|
||||
Logger::info("GrabAndMove disabling");
|
||||
m_enabled = false;
|
||||
|
||||
if (m_exit_event_handle)
|
||||
{
|
||||
SetEvent(m_exit_event_handle);
|
||||
}
|
||||
|
||||
if (m_process)
|
||||
{
|
||||
constexpr DWORD timeout_ms = 1500;
|
||||
DWORD result = WaitForSingleObject(m_process, timeout_ms);
|
||||
|
||||
if (result == WAIT_TIMEOUT)
|
||||
{
|
||||
Logger::warn("GrabAndMove: Process didn't exit in time. Forcing termination.");
|
||||
TerminateProcess(m_process, 0);
|
||||
}
|
||||
|
||||
CloseHandle(m_process);
|
||||
m_process = nullptr;
|
||||
}
|
||||
|
||||
Trace::Enable(false);
|
||||
}
|
||||
|
||||
virtual bool is_enabled() override
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
virtual bool is_enabled_by_default() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
||||
{
|
||||
return new GrabAndMoveInterface();
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
||||
9
src/modules/GrabAndMove/GrabAndMoveModuleInterface/pch.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
#include <common/utils/gpo.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
#include <shlwapi.h>
|
||||
#include <shellapi.h>
|
||||
@@ -0,0 +1,12 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
|
||||
//////////////////////////////
|
||||
// Non-localizable
|
||||
|
||||
#define FILE_DESCRIPTION "GrabAndMove Module"
|
||||
#define INTERNAL_NAME "GrabAndMove"
|
||||
#define ORIGINAL_FILENAME "PowerToys.GrabAndMoveModuleInterface.dll"
|
||||
|
||||
// Non-localizable
|
||||
//////////////////////////////
|
||||
30
src/modules/GrabAndMove/GrabAndMoveModuleInterface/trace.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "pch.h"
|
||||
#include "trace.h"
|
||||
#include <TraceLoggingProvider.h>
|
||||
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_hProvider,
|
||||
"Microsoft.PowerToys",
|
||||
// {38e8889b-9731-53f5-e901-e8a7c1753074}
|
||||
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
|
||||
TraceLoggingOptionProjectTelemetry());
|
||||
|
||||
void Trace::RegisterProvider()
|
||||
{
|
||||
TraceLoggingRegister(g_hProvider);
|
||||
}
|
||||
|
||||
void Trace::UnregisterProvider()
|
||||
{
|
||||
TraceLoggingUnregister(g_hProvider);
|
||||
}
|
||||
|
||||
void Trace::Enable(bool enabled) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"GrabAndMove_EnableGrabAndMove",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingBoolean(enabled, "Enabled"));
|
||||
}
|
||||
15
src/modules/GrabAndMove/GrabAndMoveModuleInterface/trace.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <TraceLoggingActivity.h>
|
||||
#include <common/telemetry/ProjectTelemetry.h>
|
||||
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_hProvider);
|
||||
|
||||
class Trace
|
||||
{
|
||||
public:
|
||||
static void RegisterProvider();
|
||||
static void UnregisterProvider();
|
||||
static void Enable(bool enabled) noexcept;
|
||||
};
|
||||
@@ -3,16 +3,101 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:PowerOCR"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
Exit="Application_Exit"
|
||||
ShutdownMode="OnExplicitShutdown"
|
||||
Startup="Application_Startup">
|
||||
Startup="Application_Startup"
|
||||
ThemeMode="System">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ui:ThemesDictionary Theme="Dark" />
|
||||
<ui:ControlsDictionary />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<FontFamily x:Key="SymbolThemeFontFamily">Segoe Fluent Icons, Segoe MDL2 Assets</FontFamily>
|
||||
<Style
|
||||
x:Key="SubtleButtonStyle"
|
||||
BasedOn="{StaticResource {x:Type Button}}"
|
||||
TargetType="Button">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Focusable="False"
|
||||
RecognizesAccessKey="True"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="{DynamicResource SubtleFillColorSecondaryBrush}" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource SubtleFillColorSecondaryBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="{DynamicResource SubtleFillColorTertiaryBrush}" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource SubtleFillColorTertiaryBrush}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorDisabledBrush}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="SubtleToggleButtonStyle"
|
||||
BasedOn="{StaticResource {x:Type ToggleButton}}"
|
||||
TargetType="ToggleButton">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Focusable="False"
|
||||
RecognizesAccessKey="True"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="{DynamicResource SubtleFillColorSecondaryBrush}" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource SubtleFillColorSecondaryBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="{DynamicResource SubtleFillColorTertiaryBrush}" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource SubtleFillColorTertiaryBrush}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="{DynamicResource AccentFillColorDefaultBrush}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextOnAccentFillColorPrimaryBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorDisabledBrush}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
<Window
|
||||
<Window
|
||||
x:Class="PowerOCR.OCROverlay"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:p="clr-namespace:PowerOCR.Properties"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
x:Name="TextExtractorWindow"
|
||||
Title="TextExtractor"
|
||||
ui:Design.Background="Transparent"
|
||||
AllowsTransparency="True"
|
||||
Background="Transparent"
|
||||
Loaded="Window_Loaded"
|
||||
@@ -22,26 +20,6 @@
|
||||
WindowStyle="None"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Window.Resources>
|
||||
<Style BasedOn="{StaticResource DefaultToggleButtonStyle}" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Margin" Value="4,0" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Width" Value="32" />
|
||||
<Setter Property="Height" Value="32" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
|
||||
<Style BasedOn="{StaticResource DefaultButtonStyle}" TargetType="{x:Type Button}">
|
||||
<Setter Property="Margin" Value="4,0" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="Width" Value="32" />
|
||||
<Setter Property="Height" Value="32" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Viewbox>
|
||||
<Image x:Name="BackgroundImage" Stretch="UniformToFill" />
|
||||
@@ -101,17 +79,11 @@
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
d:Visibility="Visible"
|
||||
Background="{DynamicResource ApplicationBackgroundBrush}"
|
||||
Background="{DynamicResource SolidBackgroundFillColorBaseBrush}"
|
||||
BorderBrush="{DynamicResource SurfaceStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Visibility="Collapsed">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect
|
||||
BlurRadius="32"
|
||||
Opacity="0.28"
|
||||
RenderingBias="Performance"
|
||||
ShadowDepth="1" />
|
||||
</Border.Effect>
|
||||
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
@@ -133,35 +105,67 @@
|
||||
</ComboBox>
|
||||
<ToggleButton
|
||||
x:Name="SingleLineToggleButton"
|
||||
Width="32"
|
||||
Height="32"
|
||||
Margin="4,0"
|
||||
Padding="0"
|
||||
d:IsChecked="True"
|
||||
AutomationProperties.Name="{x:Static p:Resources.ResultTextSingleLine}"
|
||||
Click="SingleLineMenuItem_Click"
|
||||
IsChecked="{Binding IsChecked, ElementName=SingleLineMenuItem, Mode=TwoWay}"
|
||||
Style="{DynamicResource SubtleToggleButtonStyle}"
|
||||
ToolTip="{x:Static p:Resources.ResultTextSingleLineShortcut}">
|
||||
<ui:SymbolIcon FontSize="18" Symbol="SubtractSquare24" />
|
||||
<TextBlock
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Text="" />
|
||||
</ToggleButton>
|
||||
<ToggleButton
|
||||
x:Name="TableToggleButton"
|
||||
Width="32"
|
||||
Height="32"
|
||||
Margin="4,0"
|
||||
Padding="0"
|
||||
d:IsChecked="True"
|
||||
AutomationProperties.Name="{x:Static p:Resources.ResultTextTable}"
|
||||
Click="TableToggleButton_Click"
|
||||
IsChecked="{Binding IsChecked, ElementName=TableMenuItem, Mode=TwoWay}"
|
||||
Style="{DynamicResource SubtleToggleButtonStyle}"
|
||||
ToolTip="{x:Static p:Resources.ResultTextTableShortcut}">
|
||||
<ui:SymbolIcon FontSize="18" Symbol="Table24" />
|
||||
<TextBlock
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Text="" />
|
||||
</ToggleButton>
|
||||
<Button
|
||||
x:Name="SettingsButton"
|
||||
Width="32"
|
||||
Height="32"
|
||||
Margin="4,0"
|
||||
Padding="0"
|
||||
AutomationProperties.Name="{x:Static p:Resources.Settings}"
|
||||
Click="SettingsMenuItem_Click"
|
||||
Style="{DynamicResource SubtleButtonStyle}"
|
||||
ToolTip="{x:Static p:Resources.Settings}">
|
||||
<ui:SymbolIcon FontSize="18" Symbol="Settings24" />
|
||||
<TextBlock
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Text="" />
|
||||
</Button>
|
||||
<Button
|
||||
x:Name="CancelButton"
|
||||
Width="32"
|
||||
Height="32"
|
||||
Margin="4,0"
|
||||
Padding="0"
|
||||
AutomationProperties.Name="{x:Static p:Resources.Cancel}"
|
||||
Click="CancelMenuItem_Click"
|
||||
Style="{DynamicResource SubtleButtonStyle}"
|
||||
ToolTip="{x:Static p:Resources.CancelShortcut}">
|
||||
<ui:SymbolIcon FontSize="18" Symbol="Dismiss24" />
|
||||
<TextBlock
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Text="" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
@@ -64,8 +64,6 @@ public partial class OCROverlay : Window
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this, Wpf.Ui.Controls.WindowBackdropType.None);
|
||||
|
||||
PopulateLanguageMenu();
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.ComponentModel.Composition" />
|
||||
<PackageReference Include="System.Drawing.Common" />
|
||||
<PackageReference Include="WPF-UI" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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 Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace ShowDesktop.UnitTests
|
||||
{
|
||||
[TestClass]
|
||||
public class DesktopDetectorTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void IsDesktopWindow_ZeroHandle_ReturnsFalse()
|
||||
{
|
||||
bool result = DesktopDetector.IsDesktopWindow(IntPtr.Zero);
|
||||
Assert.IsFalse(result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsDesktopWindow_InvalidHandle_ReturnsFalse()
|
||||
{
|
||||
// An invalid, non-zero handle should not be detected as a desktop window
|
||||
// since GetClassName will return empty for invalid handles
|
||||
bool result = DesktopDetector.IsDesktopWindow(new IntPtr(0x7FFFFFFF));
|
||||
Assert.IsFalse(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// 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 Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace ShowDesktop.UnitTests
|
||||
{
|
||||
[TestClass]
|
||||
public class DesktopPeekTests
|
||||
{
|
||||
private static ShowDesktopSettings CreateSettings(
|
||||
int peekMode = 0,
|
||||
bool requireDoubleClick = false,
|
||||
bool enableTaskbarPeek = false,
|
||||
bool enableGamingDetection = true)
|
||||
{
|
||||
var settings = new ShowDesktopSettings();
|
||||
settings.Properties.PeekMode = new IntProperty(peekMode);
|
||||
settings.Properties.RequireDoubleClick = new BoolProperty(requireDoubleClick);
|
||||
settings.Properties.EnableTaskbarPeek = new BoolProperty(enableTaskbarPeek);
|
||||
settings.Properties.EnableGamingDetection = new BoolProperty(enableGamingDetection);
|
||||
return settings;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Constructor_WithDefaultSettings_DoesNotThrow()
|
||||
{
|
||||
var settings = CreateSettings();
|
||||
using var peek = new DesktopPeek(settings);
|
||||
|
||||
// No exception means construction succeeded
|
||||
Assert.IsNotNull(peek);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Constructor_WithNativeMode_DoesNotThrow()
|
||||
{
|
||||
var settings = CreateSettings(peekMode: (int)PeekMode.Native);
|
||||
using var peek = new DesktopPeek(settings);
|
||||
Assert.IsNotNull(peek);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Constructor_WithMinimizeMode_DoesNotThrow()
|
||||
{
|
||||
var settings = CreateSettings(peekMode: (int)PeekMode.Minimize);
|
||||
using var peek = new DesktopPeek(settings);
|
||||
Assert.IsNotNull(peek);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Constructor_WithFlyAwayMode_DoesNotThrow()
|
||||
{
|
||||
var settings = CreateSettings(peekMode: (int)PeekMode.FlyAway);
|
||||
using var peek = new DesktopPeek(settings);
|
||||
Assert.IsNotNull(peek);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UpdateSettings_AppliesNewSettings()
|
||||
{
|
||||
var settings = CreateSettings(peekMode: (int)PeekMode.Native);
|
||||
using var peek = new DesktopPeek(settings);
|
||||
|
||||
var newSettings = CreateSettings(
|
||||
peekMode: (int)PeekMode.Minimize,
|
||||
requireDoubleClick: true,
|
||||
enableTaskbarPeek: true,
|
||||
enableGamingDetection: false);
|
||||
|
||||
// Should not throw
|
||||
peek.UpdateSettings(newSettings);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Dispose_CalledTwice_DoesNotThrow()
|
||||
{
|
||||
var settings = CreateSettings();
|
||||
var peek = new DesktopPeek(settings);
|
||||
peek.Dispose();
|
||||
peek.Dispose(); // second dispose should be safe
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Constructor_WithDoubleClickEnabled_DoesNotThrow()
|
||||
{
|
||||
var settings = CreateSettings(requireDoubleClick: true);
|
||||
using var peek = new DesktopPeek(settings);
|
||||
Assert.IsNotNull(peek);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Constructor_WithTaskbarPeekEnabled_DoesNotThrow()
|
||||
{
|
||||
var settings = CreateSettings(enableTaskbarPeek: true);
|
||||
using var peek = new DesktopPeek(settings);
|
||||
Assert.IsNotNull(peek);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Constructor_WithGamingDetectionDisabled_DoesNotThrow()
|
||||
{
|
||||
var settings = CreateSettings(enableGamingDetection: false);
|
||||
using var peek = new DesktopPeek(settings);
|
||||
Assert.IsNotNull(peek);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Constructor_AllSettingsCombined_DoesNotThrow()
|
||||
{
|
||||
var settings = CreateSettings(
|
||||
peekMode: (int)PeekMode.FlyAway,
|
||||
requireDoubleClick: true,
|
||||
enableTaskbarPeek: true,
|
||||
enableGamingDetection: false);
|
||||
using var peek = new DesktopPeek(settings);
|
||||
Assert.IsNotNull(peek);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// 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 Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace ShowDesktop.UnitTests
|
||||
{
|
||||
[TestClass]
|
||||
public class MouseHookEventArgsTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Properties_DefaultValues_AreCorrect()
|
||||
{
|
||||
var args = new MouseHookEventArgs();
|
||||
|
||||
Assert.AreEqual(0, args.X);
|
||||
Assert.AreEqual(0, args.Y);
|
||||
Assert.IsFalse(args.IsDoubleClick);
|
||||
Assert.IsFalse(args.IsTaskbar);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Properties_WhenInitialized_ReturnSetValues()
|
||||
{
|
||||
var args = new MouseHookEventArgs
|
||||
{
|
||||
X = 100,
|
||||
Y = 200,
|
||||
IsDoubleClick = true,
|
||||
IsTaskbar = false,
|
||||
};
|
||||
|
||||
Assert.AreEqual(100, args.X);
|
||||
Assert.AreEqual(200, args.Y);
|
||||
Assert.IsTrue(args.IsDoubleClick);
|
||||
Assert.IsFalse(args.IsTaskbar);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Properties_TaskbarClick_SetsCorrectly()
|
||||
{
|
||||
var args = new MouseHookEventArgs
|
||||
{
|
||||
X = 500,
|
||||
Y = 1060,
|
||||
IsDoubleClick = false,
|
||||
IsTaskbar = true,
|
||||
};
|
||||
|
||||
Assert.AreEqual(500, args.X);
|
||||
Assert.AreEqual(1060, args.Y);
|
||||
Assert.IsFalse(args.IsDoubleClick);
|
||||
Assert.IsTrue(args.IsTaskbar);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Properties_NegativeCoordinates_AreAllowed()
|
||||
{
|
||||
var args = new MouseHookEventArgs
|
||||
{
|
||||
X = -100,
|
||||
Y = -200,
|
||||
};
|
||||
|
||||
Assert.AreEqual(-100, args.X);
|
||||
Assert.AreEqual(-200, args.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace ShowDesktop.UnitTests
|
||||
{
|
||||
[TestClass]
|
||||
public class PeekModeTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Native_HasValue_Zero()
|
||||
{
|
||||
Assert.AreEqual(0, (int)PeekMode.Native);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Minimize_HasValue_One()
|
||||
{
|
||||
Assert.AreEqual(1, (int)PeekMode.Minimize);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FlyAway_HasValue_Two()
|
||||
{
|
||||
Assert.AreEqual(2, (int)PeekMode.FlyAway);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PeekMode_HasExactlyThreeValues()
|
||||
{
|
||||
var values = System.Enum.GetValues<PeekMode>();
|
||||
Assert.AreEqual(3, values.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(0, PeekMode.Native)]
|
||||
[DataRow(1, PeekMode.Minimize)]
|
||||
[DataRow(2, PeekMode.FlyAway)]
|
||||
public void PeekMode_CastsFromInt_Correctly(int intValue, PeekMode expected)
|
||||
{
|
||||
Assert.AreEqual(expected, (PeekMode)intValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="$(RepoRoot)src\Common.Dotnet.CsWinRT.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<SelfContained>true</SelfContained>
|
||||
<RuntimeIdentifier Condition="'$(Platform)' == 'x64'">win-x64</RuntimeIdentifier>
|
||||
<RuntimeIdentifier Condition="'$(Platform)' == 'ARM64'">win-arm64</RuntimeIdentifier>
|
||||
<OutputType>Exe</OutputType>
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\tests\ShowDesktop.UnitTests\</OutputPath>
|
||||
<Nullable>enable</Nullable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MSTest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ShowDesktop\ShowDesktop.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,109 @@
|
||||
// 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 Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace ShowDesktop.UnitTests
|
||||
{
|
||||
[TestClass]
|
||||
public class ShowDesktopSettingsTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void DefaultSettings_PeekMode_IsNative()
|
||||
{
|
||||
var settings = new ShowDesktopSettings();
|
||||
Assert.AreEqual(ShowDesktopProperties.DefaultPeekMode, settings.Properties.PeekMode.Value);
|
||||
Assert.AreEqual(0, settings.Properties.PeekMode.Value);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DefaultSettings_RequireDoubleClick_IsFalse()
|
||||
{
|
||||
var settings = new ShowDesktopSettings();
|
||||
Assert.AreEqual(ShowDesktopProperties.DefaultRequireDoubleClick, settings.Properties.RequireDoubleClick.Value);
|
||||
Assert.IsFalse(settings.Properties.RequireDoubleClick.Value);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DefaultSettings_EnableTaskbarPeek_IsFalse()
|
||||
{
|
||||
var settings = new ShowDesktopSettings();
|
||||
Assert.AreEqual(ShowDesktopProperties.DefaultEnableTaskbarPeek, settings.Properties.EnableTaskbarPeek.Value);
|
||||
Assert.IsFalse(settings.Properties.EnableTaskbarPeek.Value);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DefaultSettings_EnableGamingDetection_IsTrue()
|
||||
{
|
||||
var settings = new ShowDesktopSettings();
|
||||
Assert.AreEqual(ShowDesktopProperties.DefaultEnableGamingDetection, settings.Properties.EnableGamingDetection.Value);
|
||||
Assert.IsTrue(settings.Properties.EnableGamingDetection.Value);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DefaultSettings_FlyAwayDuration_Is300()
|
||||
{
|
||||
var settings = new ShowDesktopSettings();
|
||||
Assert.AreEqual(ShowDesktopProperties.DefaultFlyAwayAnimationDurationMs, settings.Properties.FlyAwayAnimationDurationMs.Value);
|
||||
Assert.AreEqual(300, settings.Properties.FlyAwayAnimationDurationMs.Value);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetModuleName_ReturnsShowDesktop()
|
||||
{
|
||||
var settings = new ShowDesktopSettings();
|
||||
Assert.AreEqual("ShowDesktop", settings.GetModuleName());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ModuleName_Constant_IsShowDesktop()
|
||||
{
|
||||
Assert.AreEqual("ShowDesktop", ShowDesktopSettings.ModuleName);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Properties_PeekMode_CanBeSet()
|
||||
{
|
||||
var settings = new ShowDesktopSettings();
|
||||
settings.Properties.PeekMode = new IntProperty((int)PeekMode.Minimize);
|
||||
Assert.AreEqual((int)PeekMode.Minimize, settings.Properties.PeekMode.Value);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Properties_ToJsonString_IsNotEmpty()
|
||||
{
|
||||
var properties = new ShowDesktopProperties();
|
||||
string json = properties.ToJsonString();
|
||||
Assert.IsFalse(string.IsNullOrEmpty(json));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Properties_ToJsonString_ContainsPeekMode()
|
||||
{
|
||||
var properties = new ShowDesktopProperties();
|
||||
string json = properties.ToJsonString();
|
||||
Assert.IsTrue(json.Contains("peek-mode"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Properties_ToJsonString_ContainsAllProperties()
|
||||
{
|
||||
var properties = new ShowDesktopProperties();
|
||||
string json = properties.ToJsonString();
|
||||
Assert.IsTrue(json.Contains("peek-mode"));
|
||||
Assert.IsTrue(json.Contains("require-double-click"));
|
||||
Assert.IsTrue(json.Contains("enable-taskbar-peek"));
|
||||
Assert.IsTrue(json.Contains("enable-gaming-detection"));
|
||||
Assert.IsTrue(json.Contains("fly-away-animation-duration-ms"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UpgradeSettingsConfiguration_ReturnsFalse()
|
||||
{
|
||||
var settings = new ShowDesktopSettings();
|
||||
Assert.IsFalse(settings.UpgradeSettingsConfiguration());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace ShowDesktop.UnitTests
|
||||
{
|
||||
[TestClass]
|
||||
public class WindowInfoTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Constructor_StoresHwnd()
|
||||
{
|
||||
IntPtr hwnd = new IntPtr(12345);
|
||||
var placement = NativeMethods.WINDOWPLACEMENT.Default;
|
||||
|
||||
var info = new WindowInfo(hwnd, placement);
|
||||
|
||||
Assert.AreEqual(hwnd, info.Hwnd);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Constructor_StoresPlacement()
|
||||
{
|
||||
IntPtr hwnd = new IntPtr(42);
|
||||
var placement = NativeMethods.WINDOWPLACEMENT.Default;
|
||||
placement.showCmd = NativeMethods.SW_SHOWMAXIMIZED;
|
||||
|
||||
var info = new WindowInfo(hwnd, placement);
|
||||
|
||||
Assert.AreEqual((uint)NativeMethods.SW_SHOWMAXIMIZED, info.Placement.showCmd);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Constructor_ZeroHwnd_IsAllowed()
|
||||
{
|
||||
var placement = NativeMethods.WINDOWPLACEMENT.Default;
|
||||
var info = new WindowInfo(IntPtr.Zero, placement);
|
||||
|
||||
Assert.AreEqual(IntPtr.Zero, info.Hwnd);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Placement_PreservesNormalPosition()
|
||||
{
|
||||
var placement = NativeMethods.WINDOWPLACEMENT.Default;
|
||||
placement.rcNormalPosition = new NativeMethods.RECT
|
||||
{
|
||||
Left = 10,
|
||||
Top = 20,
|
||||
Right = 810,
|
||||
Bottom = 620,
|
||||
};
|
||||
|
||||
var info = new WindowInfo(new IntPtr(1), placement);
|
||||
|
||||
Assert.AreEqual(10, info.Placement.rcNormalPosition.Left);
|
||||
Assert.AreEqual(20, info.Placement.rcNormalPosition.Top);
|
||||
Assert.AreEqual(810, info.Placement.rcNormalPosition.Right);
|
||||
Assert.AreEqual(620, info.Placement.rcNormalPosition.Bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace ShowDesktop.UnitTests
|
||||
{
|
||||
[TestClass]
|
||||
public class WindowTrackerTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void SendShowDesktop_GeneratesCorrectInputStructures()
|
||||
{
|
||||
// Verify the INPUT array structure by recreating the same logic
|
||||
var inputs = new NativeMethods.INPUT[4];
|
||||
|
||||
// Win key down
|
||||
inputs[0].type = NativeMethods.INPUT_KEYBOARD;
|
||||
inputs[0].u.ki.wVk = NativeMethods.VK_LWIN;
|
||||
inputs[0].u.ki.dwFlags = 0;
|
||||
|
||||
// D key down
|
||||
inputs[1].type = NativeMethods.INPUT_KEYBOARD;
|
||||
inputs[1].u.ki.wVk = NativeMethods.VK_D;
|
||||
inputs[1].u.ki.dwFlags = 0;
|
||||
|
||||
// D key up
|
||||
inputs[2].type = NativeMethods.INPUT_KEYBOARD;
|
||||
inputs[2].u.ki.wVk = NativeMethods.VK_D;
|
||||
inputs[2].u.ki.dwFlags = NativeMethods.KEYEVENTF_KEYUP;
|
||||
|
||||
// Win key up
|
||||
inputs[3].type = NativeMethods.INPUT_KEYBOARD;
|
||||
inputs[3].u.ki.wVk = NativeMethods.VK_LWIN;
|
||||
inputs[3].u.ki.dwFlags = NativeMethods.KEYEVENTF_KEYUP;
|
||||
|
||||
// Validate all four inputs are keyboard type
|
||||
Assert.AreEqual(NativeMethods.INPUT_KEYBOARD, inputs[0].type);
|
||||
Assert.AreEqual(NativeMethods.INPUT_KEYBOARD, inputs[1].type);
|
||||
Assert.AreEqual(NativeMethods.INPUT_KEYBOARD, inputs[2].type);
|
||||
Assert.AreEqual(NativeMethods.INPUT_KEYBOARD, inputs[3].type);
|
||||
|
||||
// Validate key sequence: Win↓, D↓, D↑, Win↑
|
||||
Assert.AreEqual(NativeMethods.VK_LWIN, inputs[0].u.ki.wVk);
|
||||
Assert.AreEqual(0u, inputs[0].u.ki.dwFlags);
|
||||
|
||||
Assert.AreEqual(NativeMethods.VK_D, inputs[1].u.ki.wVk);
|
||||
Assert.AreEqual(0u, inputs[1].u.ki.dwFlags);
|
||||
|
||||
Assert.AreEqual(NativeMethods.VK_D, inputs[2].u.ki.wVk);
|
||||
Assert.AreEqual(NativeMethods.KEYEVENTF_KEYUP, inputs[2].u.ki.dwFlags);
|
||||
|
||||
Assert.AreEqual(NativeMethods.VK_LWIN, inputs[3].u.ki.wVk);
|
||||
Assert.AreEqual(NativeMethods.KEYEVENTF_KEYUP, inputs[3].u.ki.dwFlags);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void HasSavedWindows_InitialState_IsFalse()
|
||||
{
|
||||
var tracker = new WindowTracker();
|
||||
Assert.IsFalse(tracker.HasSavedWindows);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void InputStructSize_IsConsistent()
|
||||
{
|
||||
// Verify that Marshal.SizeOf<INPUT>() returns a consistent value
|
||||
int size = Marshal.SizeOf<NativeMethods.INPUT>();
|
||||
Assert.IsTrue(size > 0, "INPUT struct size should be positive");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NativeConstants_HaveExpectedValues()
|
||||
{
|
||||
// Validate key constants used by SendShowDesktop
|
||||
Assert.AreEqual(1, NativeMethods.INPUT_KEYBOARD);
|
||||
Assert.AreEqual(0x0002u, NativeMethods.KEYEVENTF_KEYUP);
|
||||
Assert.AreEqual((ushort)0x5B, NativeMethods.VK_LWIN);
|
||||
Assert.AreEqual((ushort)0x44, NativeMethods.VK_D);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WindowPlacement_Default_HasCorrectLength()
|
||||
{
|
||||
var placement = NativeMethods.WINDOWPLACEMENT.Default;
|
||||
Assert.AreEqual((uint)Marshal.SizeOf<NativeMethods.WINDOWPLACEMENT>(), placement.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
217
src/modules/ShowDesktop/ShowDesktop/DesktopDetector.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
// 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 ManagedCommon;
|
||||
|
||||
namespace ShowDesktop
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether a screen point corresponds to the desktop wallpaper
|
||||
/// (not desktop icons, taskbar, or other windows).
|
||||
/// </summary>
|
||||
internal static class DesktopDetector
|
||||
{
|
||||
private const string ProgmanClassName = "Progman";
|
||||
private const string WorkerWClassName = "WorkerW";
|
||||
private const string ShellDllDefViewClassName = "SHELLDLL_DefView";
|
||||
private const string SysListView32ClassName = "SysListView32";
|
||||
private const string TaskbarClassName = "Shell_TrayWnd";
|
||||
private const string SecondaryTaskbarClassName = "Shell_SecondaryTrayWnd";
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the click at (x, y) lands on the desktop wallpaper surface.
|
||||
/// </summary>
|
||||
public static bool IsDesktopClick(int x, int y)
|
||||
{
|
||||
try
|
||||
{
|
||||
var point = new NativeMethods.POINT(x, y);
|
||||
IntPtr hwnd = NativeMethods.WindowFromPoint(point);
|
||||
|
||||
if (hwnd == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsDesktopWindow(hwnd);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Error detecting desktop click: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the click at (x, y) lands on empty taskbar space.
|
||||
/// </summary>
|
||||
public static bool IsTaskbarClick(int x, int y)
|
||||
{
|
||||
try
|
||||
{
|
||||
var point = new NativeMethods.POINT(x, y);
|
||||
IntPtr hwnd = NativeMethods.WindowFromPoint(point);
|
||||
|
||||
if (hwnd == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string className = GetClassName(hwnd);
|
||||
|
||||
// Direct hit on the taskbar window or secondary taskbar
|
||||
if (className == TaskbarClassName || className == SecondaryTaskbarClassName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Walk parents to check if any ancestor is the taskbar
|
||||
IntPtr parent = hwnd;
|
||||
while (parent != IntPtr.Zero)
|
||||
{
|
||||
string parentClass = GetClassName(parent);
|
||||
if (parentClass == TaskbarClassName || parentClass == SecondaryTaskbarClassName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
IntPtr nextParent = NativeMethods.GetParent(parent);
|
||||
if (nextParent == parent)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
parent = nextParent;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Error detecting taskbar click: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given window handle is part of the desktop surface.
|
||||
/// The desktop can be either Progman or a WorkerW that hosts SHELLDLL_DefView.
|
||||
/// </summary>
|
||||
public static bool IsDesktopWindow(IntPtr hwnd)
|
||||
{
|
||||
if (hwnd == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string className = GetClassName(hwnd);
|
||||
|
||||
// Direct Progman hit (the base desktop window)
|
||||
if (className == ProgmanClassName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// WorkerW hosts the desktop when wallpaper slideshow or certain effects are active
|
||||
if (className == WorkerWClassName)
|
||||
{
|
||||
// Check if this WorkerW contains SHELLDLL_DefView (the desktop icon container)
|
||||
IntPtr shellView = NativeMethods.FindWindowEx(hwnd, IntPtr.Zero, ShellDllDefViewClassName, null);
|
||||
if (shellView != IntPtr.Zero)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Also check if it's a sibling WorkerW to the one hosting SHELLDLL_DefView
|
||||
// (Windows creates multiple WorkerW windows for the desktop)
|
||||
return HasDesktopSiblingWorkerW(hwnd);
|
||||
}
|
||||
|
||||
// SHELLDLL_DefView is the icon container on the desktop
|
||||
if (className == ShellDllDefViewClassName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// SysListView32 hosts the actual desktop icons inside SHELLDLL_DefView.
|
||||
// We consider this a desktop click even though it's on the icon area,
|
||||
// because the user clicked the desktop surface behind the icons.
|
||||
if (className == SysListView32ClassName)
|
||||
{
|
||||
IntPtr parent = NativeMethods.GetParent(hwnd);
|
||||
if (parent != IntPtr.Zero && GetClassName(parent) == ShellDllDefViewClassName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Walk up the parent chain
|
||||
IntPtr current = NativeMethods.GetParent(hwnd);
|
||||
while (current != IntPtr.Zero)
|
||||
{
|
||||
string parentClass = GetClassName(current);
|
||||
if (parentClass == ProgmanClassName || parentClass == WorkerWClassName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
IntPtr next = NativeMethods.GetParent(current);
|
||||
if (next == current)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When wallpaper slideshow is active, Windows creates the desktop using WorkerW windows.
|
||||
/// One WorkerW has SHELLDLL_DefView, and a sibling WorkerW renders behind it.
|
||||
/// This checks if any sibling WorkerW of <paramref name="hwnd"/> hosts the desktop.
|
||||
/// </summary>
|
||||
private static bool HasDesktopSiblingWorkerW(IntPtr hwnd)
|
||||
{
|
||||
// Find Progman first
|
||||
IntPtr progman = NativeMethods.FindWindow(ProgmanClassName, null);
|
||||
if (progman == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enumerate top-level WorkerW windows looking for one that has SHELLDLL_DefView
|
||||
bool foundDesktopWorkerW = false;
|
||||
NativeMethods.EnumWindows(
|
||||
(enumHwnd, _) =>
|
||||
{
|
||||
if (GetClassName(enumHwnd) == WorkerWClassName)
|
||||
{
|
||||
IntPtr shellView = NativeMethods.FindWindowEx(enumHwnd, IntPtr.Zero, ShellDllDefViewClassName, null);
|
||||
if (shellView != IntPtr.Zero)
|
||||
{
|
||||
// Found the WorkerW that hosts the desktop icons.
|
||||
// The clicked WorkerW is a sibling used for the wallpaper surface.
|
||||
foundDesktopWorkerW = true;
|
||||
return false; // stop enumeration
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
IntPtr.Zero);
|
||||
|
||||
return foundDesktopWorkerW;
|
||||
}
|
||||
|
||||
private static string GetClassName(IntPtr hwnd)
|
||||
{
|
||||
var buffer = new char[256];
|
||||
int length = NativeMethods.GetClassName(hwnd, buffer, buffer.Length);
|
||||
return length > 0 ? new string(buffer, 0, length) : string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
195
src/modules/ShowDesktop/ShowDesktop/DesktopPeek.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
// 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 ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
|
||||
namespace ShowDesktop
|
||||
{
|
||||
internal sealed class DesktopPeek : IDisposable
|
||||
{
|
||||
private enum State
|
||||
{
|
||||
Idle,
|
||||
Peeking,
|
||||
}
|
||||
|
||||
private readonly MouseHook _mouseHook;
|
||||
private readonly FocusWatcher _focusWatcher;
|
||||
private readonly WindowTracker _windowTracker;
|
||||
private State _state = State.Idle;
|
||||
private ShowDesktopSettings _settings;
|
||||
private bool _disposed;
|
||||
|
||||
// Cached settings values
|
||||
private PeekMode _peekMode;
|
||||
private bool _requireDoubleClick;
|
||||
private bool _enableTaskbarPeek;
|
||||
private bool _enableGamingDetection;
|
||||
|
||||
public DesktopPeek(ShowDesktopSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
ApplySettings();
|
||||
_mouseHook = new MouseHook();
|
||||
_focusWatcher = new FocusWatcher();
|
||||
_windowTracker = new WindowTracker();
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_mouseHook.DesktopClicked += OnDesktopClicked;
|
||||
_mouseHook.Install();
|
||||
_focusWatcher.ForegroundWindowChanged += OnForegroundChanged;
|
||||
_focusWatcher.Start();
|
||||
Logger.LogInfo("DesktopPeek started.");
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_mouseHook.DesktopClicked -= OnDesktopClicked;
|
||||
_mouseHook.Uninstall();
|
||||
_focusWatcher.ForegroundWindowChanged -= OnForegroundChanged;
|
||||
_focusWatcher.Stop();
|
||||
|
||||
// Restore windows if we're currently peeking
|
||||
if (_state == State.Peeking && _peekMode != PeekMode.Native)
|
||||
{
|
||||
_windowTracker.Restore(_peekMode);
|
||||
}
|
||||
|
||||
_state = State.Idle;
|
||||
Logger.LogInfo("DesktopPeek stopped.");
|
||||
}
|
||||
|
||||
public void UpdateSettings(ShowDesktopSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
ApplySettings();
|
||||
Logger.LogInfo("DesktopPeek settings updated.");
|
||||
}
|
||||
|
||||
private void ApplySettings()
|
||||
{
|
||||
_peekMode = (PeekMode)_settings.Properties.PeekMode.Value;
|
||||
_requireDoubleClick = _settings.Properties.RequireDoubleClick.Value;
|
||||
_enableTaskbarPeek = _settings.Properties.EnableTaskbarPeek.Value;
|
||||
_enableGamingDetection = _settings.Properties.EnableGamingDetection.Value;
|
||||
}
|
||||
|
||||
private void OnDesktopClicked(MouseHookEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Ignore taskbar clicks unless explicitly enabled
|
||||
if (args.IsTaskbar && !_enableTaskbarPeek)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Gaming / fullscreen detection
|
||||
if (_enableGamingDetection && IsFullscreenAppRunning())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Double-click requirement
|
||||
if (_requireDoubleClick && !args.IsDoubleClick)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_state == State.Idle)
|
||||
{
|
||||
Logger.LogInfo($"Desktop clicked — peeking (mode={_peekMode}).");
|
||||
|
||||
if (_peekMode == PeekMode.Native)
|
||||
{
|
||||
WindowTracker.SendShowDesktop();
|
||||
}
|
||||
else
|
||||
{
|
||||
_windowTracker.CaptureAndMinimize(_peekMode);
|
||||
}
|
||||
|
||||
_state = State.Peeking;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInfo("Desktop clicked — restoring.");
|
||||
|
||||
if (_peekMode == PeekMode.Native)
|
||||
{
|
||||
WindowTracker.SendShowDesktop(); // toggles back
|
||||
}
|
||||
else
|
||||
{
|
||||
_windowTracker.Restore(_peekMode);
|
||||
}
|
||||
|
||||
_state = State.Idle;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Error handling desktop click: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnForegroundChanged()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_state != State.Peeking)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the user activated a regular window (not the desktop), cancel peek
|
||||
IntPtr fg = NativeMethods.GetForegroundWindow();
|
||||
if (fg == IntPtr.Zero || DesktopDetector.IsDesktopWindow(fg))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogInfo("Foreground changed to non-desktop window — cancelling peek.");
|
||||
|
||||
if (_peekMode != PeekMode.Native)
|
||||
{
|
||||
_windowTracker.Restore(_peekMode);
|
||||
}
|
||||
|
||||
_state = State.Idle;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Error handling foreground change: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsFullscreenAppRunning()
|
||||
{
|
||||
int hr = NativeMethods.SHQueryUserNotificationState(out var state);
|
||||
if (hr != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return state == NativeMethods.QUERY_USER_NOTIFICATION_STATE.QUNS_RUNNING_D3D_FULL_SCREEN
|
||||
|| state == NativeMethods.QUERY_USER_NOTIFICATION_STATE.QUNS_BUSY;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
Stop();
|
||||
_mouseHook.Dispose();
|
||||
_focusWatcher.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
71
src/modules/ShowDesktop/ShowDesktop/FocusWatcher.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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 ManagedCommon;
|
||||
|
||||
namespace ShowDesktop
|
||||
{
|
||||
internal sealed class FocusWatcher : IDisposable
|
||||
{
|
||||
private IntPtr _hookId;
|
||||
private NativeMethods.WinEventDelegate? _proc;
|
||||
private bool _disposed;
|
||||
|
||||
public event Action? ForegroundWindowChanged;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_proc = WinEventCallback;
|
||||
_hookId = NativeMethods.SetWinEventHook(
|
||||
NativeMethods.EVENT_SYSTEM_FOREGROUND,
|
||||
NativeMethods.EVENT_SYSTEM_FOREGROUND,
|
||||
IntPtr.Zero,
|
||||
_proc,
|
||||
0,
|
||||
0,
|
||||
NativeMethods.WINEVENT_OUTOFCONTEXT);
|
||||
|
||||
if (_hookId == IntPtr.Zero)
|
||||
{
|
||||
Logger.LogError("Failed to install WinEvent hook for foreground tracking.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInfo("FocusWatcher started.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (_hookId != IntPtr.Zero)
|
||||
{
|
||||
NativeMethods.UnhookWinEvent(_hookId);
|
||||
_hookId = IntPtr.Zero;
|
||||
Logger.LogInfo("FocusWatcher stopped.");
|
||||
}
|
||||
}
|
||||
|
||||
private void WinEventCallback(
|
||||
IntPtr hWinEventHook,
|
||||
uint eventType,
|
||||
IntPtr hwnd,
|
||||
int idObject,
|
||||
int idChild,
|
||||
uint dwEventThread,
|
||||
uint dwmsEventTime)
|
||||
{
|
||||
ForegroundWindowChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
Stop();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
104
src/modules/ShowDesktop/ShowDesktop/MouseHook.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using ManagedCommon;
|
||||
|
||||
namespace ShowDesktop
|
||||
{
|
||||
internal sealed class MouseHook : IDisposable
|
||||
{
|
||||
private const int DoubleClickTimeMs = 500;
|
||||
|
||||
private IntPtr _hookId;
|
||||
private NativeMethods.LowLevelMouseProc? _proc;
|
||||
private DateTime _lastClickTime = DateTime.MinValue;
|
||||
private bool _disposed;
|
||||
|
||||
public event Action<MouseHookEventArgs>? DesktopClicked;
|
||||
|
||||
public void Install()
|
||||
{
|
||||
_proc = HookCallback;
|
||||
_hookId = NativeMethods.SetWindowsHookEx(
|
||||
NativeMethods.WH_MOUSE_LL,
|
||||
_proc,
|
||||
IntPtr.Zero,
|
||||
0);
|
||||
|
||||
if (_hookId == IntPtr.Zero)
|
||||
{
|
||||
Logger.LogError($"Failed to install mouse hook. Error: {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInfo("Mouse hook installed successfully.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Uninstall()
|
||||
{
|
||||
if (_hookId != IntPtr.Zero)
|
||||
{
|
||||
NativeMethods.UnhookWindowsHookEx(_hookId);
|
||||
_hookId = IntPtr.Zero;
|
||||
Logger.LogInfo("Mouse hook uninstalled.");
|
||||
}
|
||||
}
|
||||
|
||||
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
if (nCode >= 0)
|
||||
{
|
||||
int msg = wParam.ToInt32();
|
||||
if (msg == NativeMethods.WM_LBUTTONDOWN || msg == NativeMethods.WM_LBUTTONDBLCLK)
|
||||
{
|
||||
var hookStruct = Marshal.PtrToStructure<NativeMethods.MSLLHOOKSTRUCT>(lParam);
|
||||
int x = hookStruct.pt.X;
|
||||
int y = hookStruct.pt.Y;
|
||||
|
||||
bool isDoubleClick = msg == NativeMethods.WM_LBUTTONDBLCLK;
|
||||
if (!isDoubleClick)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
if ((now - _lastClickTime).TotalMilliseconds <= DoubleClickTimeMs)
|
||||
{
|
||||
isDoubleClick = true;
|
||||
}
|
||||
|
||||
_lastClickTime = now;
|
||||
}
|
||||
|
||||
bool isDesktop = DesktopDetector.IsDesktopClick(x, y);
|
||||
bool isTaskbar = !isDesktop && DesktopDetector.IsTaskbarClick(x, y);
|
||||
|
||||
if (isDesktop || isTaskbar)
|
||||
{
|
||||
var args = new MouseHookEventArgs
|
||||
{
|
||||
X = x,
|
||||
Y = y,
|
||||
IsDoubleClick = isDoubleClick,
|
||||
IsTaskbar = isTaskbar,
|
||||
};
|
||||
|
||||
DesktopClicked?.Invoke(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NativeMethods.CallNextHookEx(_hookId, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
Uninstall();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/modules/ShowDesktop/ShowDesktop/MouseHookEventArgs.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 ShowDesktop
|
||||
{
|
||||
internal sealed class MouseHookEventArgs
|
||||
{
|
||||
public int X { get; init; }
|
||||
|
||||
public int Y { get; init; }
|
||||
|
||||
public bool IsDoubleClick { get; init; }
|
||||
|
||||
public bool IsTaskbar { get; init; }
|
||||
}
|
||||
}
|
||||
346
src/modules/ShowDesktop/ShowDesktop/NativeMethods.cs
Normal file
@@ -0,0 +1,346 @@
|
||||
// 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.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ShowDesktop
|
||||
{
|
||||
internal static partial class NativeMethods
|
||||
{
|
||||
// Hook constants
|
||||
public const int WH_MOUSE_LL = 14;
|
||||
|
||||
// Mouse messages
|
||||
public const int WM_LBUTTONDOWN = 0x0201;
|
||||
public const int WM_LBUTTONUP = 0x0202;
|
||||
public const int WM_LBUTTONDBLCLK = 0x0203;
|
||||
public const int WM_QUIT = 0x0012;
|
||||
|
||||
// ShowWindow constants
|
||||
public const int SW_HIDE = 0;
|
||||
public const int SW_SHOWNORMAL = 1;
|
||||
public const int SW_SHOWMINIMIZED = 2;
|
||||
public const int SW_SHOWMAXIMIZED = 3;
|
||||
public const int SW_MINIMIZE = 6;
|
||||
public const int SW_RESTORE = 9;
|
||||
|
||||
// WinEvent constants
|
||||
public const uint EVENT_SYSTEM_FOREGROUND = 0x0003;
|
||||
public const uint WINEVENT_OUTOFCONTEXT = 0x0000;
|
||||
|
||||
// GetWindow constants
|
||||
public const uint GW_OWNER = 4;
|
||||
|
||||
// GetWindowLong constants
|
||||
public const int GWL_STYLE = -16;
|
||||
public const int GWL_EXSTYLE = -20;
|
||||
|
||||
// Window styles
|
||||
public const long WS_VISIBLE = 0x10000000L;
|
||||
public const long WS_EX_TOOLWINDOW = 0x00000080L;
|
||||
public const long WS_EX_APPWINDOW = 0x00040000L;
|
||||
public const long WS_EX_NOACTIVATE = 0x08000000L;
|
||||
|
||||
// DWM constants
|
||||
public const int DWMWA_CLOAKED = 14;
|
||||
|
||||
// SendInput constants
|
||||
public const int INPUT_KEYBOARD = 1;
|
||||
public const uint KEYEVENTF_KEYUP = 0x0002;
|
||||
public const ushort VK_LWIN = 0x5B;
|
||||
public const ushort VK_D = 0x44;
|
||||
|
||||
// ChildWindowFromPointEx flags
|
||||
public const uint CWP_SKIPINVISIBLE = 0x0001;
|
||||
public const uint CWP_SKIPDISABLED = 0x0002;
|
||||
public const uint CWP_SKIPTRANSPARENT = 0x0004;
|
||||
public const uint CWP_ALL = CWP_SKIPINVISIBLE | CWP_SKIPDISABLED | CWP_SKIPTRANSPARENT;
|
||||
|
||||
// Notification state
|
||||
public enum QUERY_USER_NOTIFICATION_STATE
|
||||
{
|
||||
QUNS_NOT_PRESENT = 1,
|
||||
QUNS_BUSY = 2,
|
||||
QUNS_RUNNING_D3D_FULL_SCREEN = 3,
|
||||
QUNS_PRESENTATION_MODE = 4,
|
||||
QUNS_ACCEPTS_NOTIFICATIONS = 5,
|
||||
QUNS_QUIET_TIME = 6,
|
||||
QUNS_APP = 7,
|
||||
}
|
||||
|
||||
// Delegates
|
||||
public delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
public delegate void WinEventDelegate(
|
||||
IntPtr hWinEventHook,
|
||||
uint eventType,
|
||||
IntPtr hwnd,
|
||||
int idObject,
|
||||
int idChild,
|
||||
uint dwEventThread,
|
||||
uint dwmsEventTime);
|
||||
|
||||
public delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
|
||||
|
||||
// Structs
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct POINT
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
|
||||
public POINT(int x, int y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MSLLHOOKSTRUCT
|
||||
{
|
||||
public POINT pt;
|
||||
public uint mouseData;
|
||||
public uint flags;
|
||||
public uint time;
|
||||
public IntPtr dwExtraInfo;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MSG
|
||||
{
|
||||
public IntPtr hwnd;
|
||||
public uint message;
|
||||
public IntPtr wParam;
|
||||
public IntPtr lParam;
|
||||
public uint time;
|
||||
public POINT pt;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RECT
|
||||
{
|
||||
public int Left;
|
||||
public int Top;
|
||||
public int Right;
|
||||
public int Bottom;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct WINDOWPLACEMENT
|
||||
{
|
||||
public uint length;
|
||||
public uint flags;
|
||||
public uint showCmd;
|
||||
public POINT ptMinPosition;
|
||||
public POINT ptMaxPosition;
|
||||
public RECT rcNormalPosition;
|
||||
|
||||
public static WINDOWPLACEMENT Default
|
||||
{
|
||||
get
|
||||
{
|
||||
var wp = default(WINDOWPLACEMENT);
|
||||
wp.length = (uint)Marshal.SizeOf<WINDOWPLACEMENT>();
|
||||
return wp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct INPUT
|
||||
{
|
||||
public int type;
|
||||
public INPUTUNION u;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct INPUTUNION
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public MOUSEINPUT mi;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public KEYBDINPUT ki;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public HARDWAREINPUT hi;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MOUSEINPUT
|
||||
{
|
||||
public int dx;
|
||||
public int dy;
|
||||
public uint mouseData;
|
||||
public uint dwFlags;
|
||||
public uint time;
|
||||
public IntPtr dwExtraInfo;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct KEYBDINPUT
|
||||
{
|
||||
public ushort wVk;
|
||||
public ushort wScan;
|
||||
public uint dwFlags;
|
||||
public uint time;
|
||||
public IntPtr dwExtraInfo;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct HARDWAREINPUT
|
||||
{
|
||||
public uint uMsg;
|
||||
public ushort wParamL;
|
||||
public ushort wParamH;
|
||||
}
|
||||
|
||||
// Mouse hook
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool UnhookWindowsHookEx(IntPtr hhk);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
// Message loop
|
||||
[DllImport("user32.dll")]
|
||||
public static extern int GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool TranslateMessage(ref MSG lpMsg);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr DispatchMessage(ref MSG lpMsg);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
public static partial void PostQuitMessage(int nExitCode);
|
||||
|
||||
// Window enumeration and management
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool SetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool IsWindowVisible(IntPtr hWnd);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool IsIconic(IntPtr hWnd);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
public static partial IntPtr GetWindow(IntPtr hWnd, uint uCmd);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
public static partial IntPtr GetForegroundWindow();
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
public static partial long GetWindowLongA(IntPtr hWnd, int nIndex);
|
||||
|
||||
// WinEvent hook
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr SetWinEventHook(
|
||||
uint eventMin,
|
||||
uint eventMax,
|
||||
IntPtr hmodWinEventProc,
|
||||
WinEventDelegate lpfnWinEventProc,
|
||||
uint idProcess,
|
||||
uint idThread,
|
||||
uint dwFlags);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool UnhookWinEvent(IntPtr hWinEventHook);
|
||||
|
||||
// Window identification
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
public static extern IntPtr FindWindow(string? lpClassName, string? lpWindowName);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
public static extern int GetClassName(IntPtr hWnd, [Out] char[] lpClassName, int nMaxCount);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
public static partial IntPtr GetParent(IntPtr hWnd);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
public static partial IntPtr GetDesktopWindow();
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
public static partial IntPtr GetShellWindow();
|
||||
|
||||
// Hit-testing
|
||||
[LibraryImport("user32.dll")]
|
||||
public static partial IntPtr WindowFromPoint(POINT point);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
public static partial IntPtr ChildWindowFromPointEx(IntPtr hwndParent, POINT point, uint uFlags);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool GetCursorPos(out POINT lpPoint);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
||||
|
||||
// Window text
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
public static extern int GetWindowText(IntPtr hWnd, [Out] char[] lpString, int nMaxCount);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
public static partial int GetWindowTextLengthA(IntPtr hWnd);
|
||||
|
||||
// DWM
|
||||
[LibraryImport("dwmapi.dll")]
|
||||
public static partial int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out int pvAttribute, int cbAttribute);
|
||||
|
||||
// SendInput for Win+D
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
|
||||
|
||||
// Process info
|
||||
[LibraryImport("user32.dll")]
|
||||
public static partial uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||||
|
||||
// Notification state for gaming detection
|
||||
[LibraryImport("shell32.dll")]
|
||||
public static partial int SHQueryUserNotificationState(out QUERY_USER_NOTIFICATION_STATE pquns);
|
||||
|
||||
// EnumChildWindows for desktop icon detection
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string? lpszClass, string? lpszWindow);
|
||||
}
|
||||
}
|
||||