Compare commits

...

26 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
d6d5967b69 Use $conditionalImport variable consistently in EnableModule.ps1 Add-Content
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/95c97d85-c99b-4437-95df-c971b16491cd

Co-authored-by: niels9001 <9866362+niels9001@users.noreply.github.com>
2026-04-18 17:44:33 +00:00
copilot-swe-agent[bot]
129f0a33a0 Fix Command Not Found crash in non-interactive PowerShell sessions (e.g. VS Code tasks)
Only import the Microsoft.WinGet.CommandNotFound module in interactive
PowerShell sessions. The module's WarmUp background task can crash with
PipelineStoppedException when PowerShell exits quickly (e.g. when running
VS Code shell tasks with -Command). Since Command Not Found is a feedback
provider only useful in interactive sessions, skip loading it when
PowerShell is invoked with -Command, -File, or -NonInteractive flags.

Also auto-upgrade existing unconditional imports when the Settings page
checks requirements.

Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/95c97d85-c99b-4437-95df-c971b16491cd

Co-authored-by: niels9001 <9866362+niels9001@users.noreply.github.com>
2026-04-18 17:43:42 +00:00
copilot-swe-agent[bot]
2cea99965b Initial plan 2026-04-18 17:32:34 +00:00
moooyo
5520ae4cfa [PowerDisplay] Fix startup restore, volume init, and identify window lifecycle (#47051)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
- **Volume initialization**: Read VCP 0x62 on monitor discovery so
`CurrentVolume` reflects actual hardware state instead of staying at the
50% default.
- **Brightness capability check**: Guard brightness init behind
`SupportsBrightness` flag, consistent with contrast/volume handling.
- **IdentifyWindow lifecycle**: Replace fire-and-forget `Task.Delay`
with `DispatcherQueueTimer` (UI-thread-safe, stoppable on dispose). Swap
`Activate`/`PositionOnDisplay` order to eliminate first-show flicker.
- **Startup restore fix**: Change `MonitorStateEntry` fields to `int?`
so unset values (`null`) aren't confused with zero — prevents writing
default 0% brightness/volume to hardware on startup.
- **Restore/profile apply refactor**: Push value validation down to
`Set*Async` (continuous → `Math.Clamp`, discrete → capabilities check),
extract unified `TryRestore` helper, remove redundant `IsValueInRange`
and `> 0` checks.
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 08:41:14 +00:00
moooyo
beddc3b065 [ImageResizer] Fix JsonPropertyName forwarding in ObservableProperty generator (#47056)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This issue introduced by our recent WinUI 3 migration

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #47055
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Co-authored-by: Yu Leng <yuleng@microsoft.com>
Co-authored-by: Claude Opus 4 <noreply@anthropic.com>
2026-04-17 15:20:45 +08:00
Niels Laute
088da21a70 Update default module states (#47027)
## Summary

- **Disable 7 modules by default** for new users: PowerToys Run, Crop
and Lock, Advanced Paste, Hosts File Editor, Registry Preview,
Environment Variables, Workspaces
- **Swap default hotkeys**: Command Palette now defaults to \Alt+Space\,
PowerToys Run now defaults to \Win+Alt+Space\
- Update unit test to reflect PowerLauncher default-off state

## Changes

| File | Change |
|------|--------|
| \EnabledModules.cs\ | Set 7 module defaults to off |
| \PowerLauncherProperties.cs\ | Default hotkey → \Win+Alt+Space\ |
| \SettingsModel.cs\ (CmdPal) | Default hotkey → \Alt+Space\ |
| \General.cs\ (test) | Assert PowerLauncher is false |

## Validation

- Existing unit test updated to match new defaults
- No ABI or IPC contract changes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-16 08:44:25 +02:00
Gordon Lam
befb5c672e Fix AdvancedPaste auto-copy failing on Electron/Chromium apps (#46486)
## Summary

Fixes #46485

AdvancedPaste's auto-copy feature fails on Electron/Chromium-based apps
(e.g. Microsoft Teams, VS Code, browsers) because `WM_COPY` is delivered
successfully but silently ignored by these apps.

## Problem

The auto-copy code sends `WM_COPY` via `SendMessageTimeout`. For
standard Win32 controls this works, but Electron apps accept the message
delivery without actually copying to clipboard. The code treated
successful delivery as success and **never fell back to `SendInput`
Ctrl+C**.

## Changes


**`src/modules/AdvancedPaste/AdvancedPasteModuleInterface/dllmain.cpp`**:

- **Changed retry logic**: Each attempt now tries both `WM_COPY` and
`SendInput` Ctrl+C. If `WM_COPY` is delivered but clipboard is
unchanged, it falls through to Ctrl+C instead of giving up.
- **Extracted `poll_clipboard_sequence()` helper**: Reusable clipboard
polling logic (checks `GetClipboardSequenceNumber` over N polls with
configurable delay).
- **Extracted `send_ctrl_c_input()` helper**: Sends Ctrl+C via
`SendInput` with `CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG`.
- **Improved logging**: Each strategy logs clearly whether it succeeded
or fell through, making future debugging easier.

## Validation

- [x] Manual testing with Microsoft Teams (Electron): auto-copy now
works for selected text
- [x] Standard Win32 apps (Notepad, etc.): `WM_COPY` still works on
first try, no regression
- [x] No new warnings or errors in build

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-16 10:49:40 +08:00
Niels Laute
578554d157 [Settings] Format last update check date with friendly relative dates (#46923)
## Summary

Formats the Last checked date on the General and Dashboard pages with
friendly relative strings instead of raw date/time output.

**Before:** Last checked: 4/12/2026 1:22:00 PM
**After:** Last checked: Today at 1:22 PM / Yesterday at 3:45 PM

### Changes

- Add LastCheckedDateTime property to UpdatingSettings exposing the
parsed DateTime
- Create FriendlyDateHelper in Settings.UI that formats Today/Yesterday
with localized resource strings, falling back to the full
culture-specific format for older dates
- Update GeneralViewModel and CheckUpdateControl to use the friendly
format
- Add localized resource strings General_LastCheckedDate_TodayAt and
General_LastCheckedDate_YesterdayAt

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-15 10:06:10 +02:00
Gordon Lam
e4f98897ce Add window positioning and sizing with Alt+mouse button (#47024)
Re-creation of #46817 from an internal branch to work around stale
code-scanning merge protection.

## Original PR
See #46817 for full context, discussion, and review history.

## Summary
This adds a new toy, GrabAndMove (previously WinPos), that allows
dragging (left click) or resizing (right click) of windows while the Alt
key is pressed.

Closes: #269

## PR Checklist
- [x] Communication: discussed with core contributors
- [ ] Tests: Added/updated and all pass
- [ ] Localization: All end-user-facing strings can be localized
- [ ] Dev docs: Added/updated

---------

Co-authored-by: foxmsft <foxmsft@hotmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Muyuan Li (from Dev Box) <muyuanli@microsoft.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Alex Mihaiuc <amihaiuc@microsoft.com>
2026-04-15 09:13:56 +02:00
Muyuan Li
be1e749574 Add telemetry support for CLI modules (#46872)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This PR adds comprehensive localization and telemetry support to the
modules with CLI (FileLocksmith, Awake, ImageResizer), improving user
experience for international users and enabling usage tracking for
product insights.
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2026-04-15 14:14:12 +08:00
Niels Laute
dc57d22754 [Settings] Fix Dashboard layout and 1px alignment offset (#46922)
## Summary

Fixes layout issues on the Settings Dashboard page:

- **Scroll area fix**: The scroll area on the Home page extended far
beyond the content, leaving a large empty space below the modules list.
(By wrapping the quick launch / shortcuts cards into a `StackPanel` vs
separate `Grid.Rows`
- **Resizing fix**: On main, resizing states are not applied when making
the window smaller. This is now fixed.
- **1px alignment fix**: Fixed a 1-pixel vertical alignment mismatch on
the Dashboard shortcut conflict control.

Closes #45925
Closes #41523
2026-04-14 15:30:29 +02:00
moooyo
99ef5948b0 [PD] Fix thread safety, color temperature guard, and log accuracy (#47008)
Mark _disposed and _isDirty as volatile for correct cross-thread
visibility. Guard color temperature apply/restore behind
ShowColorTemperature to avoid writing unsupported VCP codes. Fix
misleading log message in Settings UI profile apply path.

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:49:54 +00:00
Niels Laute
218a01c1a9 Fix for back up folder path being clipped (#46920)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

Before:
<img width="661" height="530" alt="image"
src="https://github.com/user-attachments/assets/b77c12b2-481f-4f77-8f74-fa679331a604"
/>

After:
<img width="678" height="365" alt="image"
src="https://github.com/user-attachments/assets/ea997ab6-f1f5-4191-ac24-15885b2e19d3"
/>

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #27366
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-14 10:23:58 +02:00
Niels Laute
1c7f3d832c [Text Extractor] Remove WPF-UI in favor of Fluent theming in WPF (#46218)
Replaces the `WPF-UI` dependency in Text Extractor with native WPF
Fluent theming (`ThemeMode="System"`), custom
`SubtleButtonStyle`/`SubtleToggleButtonStyle` control templates, and
Segoe Fluent Icons font — eliminating the third-party library while
retaining light/dark theme support.

## Summary of the Pull Request

- Drops `xmlns:ui` (WPF-UI) from `App.xaml` and `OCROverlay.xaml`
- Removes `Wpf.Ui.Appearance.SystemThemeWatcher.Watch()` call; replaced
by `ThemeMode="System"` on `<Application>`
- Defines inline `SubtleButtonStyle` and `SubtleToggleButtonStyle` using
WinUI-aligned resource brush names (`SubtleFillColorSecondaryBrush`,
`AccentFillColorDefaultBrush`, etc.)
- Replaces `<ui:SymbolIcon>` with `<TextBlock
FontFamily="{DynamicResource SymbolThemeFontFamily}">` using Unicode
glyph codes
- Background uses `SolidBackgroundFillColorBaseBrush` instead of
`ApplicationBackgroundBrush`

| Light | Dark |
|-------|------|
|
![light](https://github.com/user-attachments/assets/dc03fdb3-3ef4-4f18-8352-c58fbdd19dd5)
|
![dark](https://github.com/user-attachments/assets/38fdbd6a-53cb-410d-8486-92927019be2a)
|

## PR Checklist

- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

## Detailed Description of the Pull Request / Additional comments

Related: #46220

## Validation Steps Performed

Verified light and dark themes render correctly with proper accent
highlight on the active toggle button.

---------

Co-authored-by: Joe Finney <josephfinney@LIVE.COM>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: niels9001 <9866362+niels9001@users.noreply.github.com>
2026-04-14 14:37:27 +08:00
moooyo
ff87dce4a4 [PD] Clean up PowerDisplay: fix resource leaks, remove dead code, and fix some bugs (#46979)
- Replace custom GetLastError P/Invoke with Marshal.GetLastWin32Error
and add SetLastError=true
- Fix resource leaks: dispose DpiSuppressor on window close, dispose
MonitorViewModels on refresh, destroy unused small icon handle, wrap WMI
outParams in using block
- Remove unused code: IProfileService, ColorTemperatureHelper,
ProfileHelper (Lib), CustomVcpValueMappingExtensions, IPCMessageAction,
UpdatePropertySilently, LocalizedCodeNameProvider, bring_to_front,
Constants.h
- Convert recursive MccsCapabilitiesParser.TryParseEntry to iterative
loop
- Simplify ProfileService to static class
- Use std::atomic for m_enabled flag in module interface
- Change default activation shortcut to Win+Ctrl+Shift+P
- Add null-coalescing fallback for ActivationShortcut property
- Add PowerDisplay to ModuleHelper name mapping
- Update GPO policy to target PowerToys 0.99.0
- Fix NamedPipeProcessor to break on pipe close and reduce log verbosity
- Add ShortcutControl workaround in Settings UI

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Co-authored-by: Yu Leng <yuleng@microsoft.com>
Co-authored-by: Claude Opus 4 <noreply@anthropic.com>
2026-04-13 18:00:04 +00:00
Jiří Polášek
758a60103c CmdPal: Include new transitive dependencies in SLNF (#46896)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

This PR adds new transitive dependencies to Command Palette’s SLNF (UI
tests -> UITestAutomation -> ...), and introduces a new SLNF that
excludes UI tests entirely, making it leaner.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2026-04-13 11:47:25 -05:00
Copilot
f8cadbf7f0 Fix AlwaysOnTop sound playing when pin/unpin fails (#46910)
## Summary of the Pull Request

`ProcessCommand` played the pin/unpin sound unconditionally, regardless
of whether `SetWindowPos` succeeded. This caused spurious audio feedback
when targeting desktop, taskbar, task view, start menu, or elevated
windows from a non-elevated process.

Gate sound playback on actual state change:

```cpp
bool stateChanged = false;
// ...
if (UnpinTopmostWindow(window)) { stateChanged = true; /* ... */ }
// ...
if (PinTopmostWindow(window))   { stateChanged = true; /* ... */ }
// ...
if (stateChanged && AlwaysOnTopSettings::settings()->enableSound)
    m_sound.Play(soundType);
```

## PR Checklist

- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

## Detailed Description of the Pull Request / Additional comments

`PinTopmostWindow` and `UnpinTopmostWindow` already return `bool`
indicating success, and the existing code already branches on those
return values for bookkeeping and telemetry — but the sound playback at
the end of `ProcessCommand` ignored the result. Added a `stateChanged`
flag set only inside the success branches, then checked before calling
`m_sound.Play()`.

## Validation Steps Performed

- Verified that the `soundType` / `stateChanged` logic covers all four
paths: pin success, pin failure, unpin success, unpin failure.
- Code review passed with no comments.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: niels9001 <9866362+niels9001@users.noreply.github.com>
2026-04-13 12:53:06 +02:00
Niels Laute
0819a6268b [CmdPal] Move dev docs to doc/devdocs/modules/cmdpal (#46926)
## Summary

Move Command Palette developer documentation from
\src/modules/cmdpal/doc\ to \doc/devdocs/modules/cmdpal\, consistent
with the location of other module dev docs.

Also updates the spell-check exclude path for the moved \.pdn\ file.

Points 2 and 3 from the issue (extension settings how-to and details
pane markdown documentation) are addressed in the windows-dev-docs-pr
repo.

Closes #38107

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-12 12:33:30 -05:00
Niels Laute
a31f82fcbd [CmdPal] Fix Window Walker 'Not Responding' tag illegible in dark mode (#46924)
## Summary

The 'Not Responding' tag in Window Walker used a hardcoded crimson
foreground color (rgb 220,20,60) that was illegible against the dark tag
background in dark mode.

**Fix:** Remove the hardcoded color override so the tag uses the default
theme-aware TagForeground brush, which is legible in both light and dark
modes.

Closes #40219

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-12 12:32:49 -05:00
Copilot
daeb2e1ef4 Fix CmdPal Calc extension unit test failure in non-English cultures (#46911)
## Summary of the Pull Request

`TrigModeSettingsTest` fails under cultures using `,` as decimal
separator (e.g., `de-DE`, `fr-FR`). Two root causes: the C++ calculator
engine's `ToWStringFullPrecision` doesn't pin the stream locale, and the
test classes don't set a deterministic thread culture.

## PR Checklist

- [x] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated

## Detailed Description of the Pull Request / Additional comments

**C++ locale fix** — `ExprtkEvaluator.cpp`: `std::wostringstream`
defaults to the global C++ locale, which can be changed to the system
locale by the runtime. Pin it to `std::locale::classic()` so the decimal
separator is always `.` across the WinRT boundary:

```cpp
std::wostringstream oss;
oss.imbue(std::locale::classic());
oss << std::fixed << std::setprecision(15) << value;
```

**Test culture setup** — `QueryTests.cs`, `QueryHelperTests.cs`: Added
`TestInitialize`/`TestCleanup` to set thread culture to `en-US`,
matching the existing pattern across all TimeDate test classes.

**Non-English culture test cases** — New
`TrigModeSettingsTest_NonEnglishCulture` parameterized over `de-DE` and
`fr-FR` verifies `outputUseEnglishFormat: true` produces `.`-separated
output regardless of `CurrentCulture`.

## Validation Steps Performed

- Code review passed with no actionable findings (naming convention
matches existing TimeDate test pattern across 7+ files)
- New `TrigModeSettingsTest_NonEnglishCulture` test exercises the exact
failure scenario from the issue

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: niels9001 <9866362+niels9001@users.noreply.github.com>
2026-04-12 14:12:34 +02:00
moooyo
0089de33bd [PD] Re-enable PowerDisplay (#46489)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
1. Re-enable PowerDisplay for PowerToys.
2. Add PowerDisplay back into installer.
3. Use new PowerDisplay icon and logo.
4. Fix some DPI related issue.
5. UI/UX improvement.


<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #1052
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Pull new code from this branch. Set up PowerDisplay.UI as startup
project. Click run in VS.

Or, build whole solution, set up runner as startup project. Click run to
test full experience.

---------

Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
2026-04-10 15:14:41 +08:00
Copilot
3e2914a0b2 Add unit tests for Hosts ValidationHelper and ColorPicker format conversions (#46679)
## Summary of the Pull Request

Adds comprehensive unit tests for two previously untested areas to
improve test coverage and prevent regressions:

1. **Hosts ValidationHelper** (`ValidationHelperTest.cs`) — 25+ test
cases covering `ValidIPv4`, `ValidIPv6`, and `ValidHosts` methods
2. **ColorPicker ColorFormatHelper conversions**
(`ColorFormatConversionTest.cs`) — 50+ test cases covering CMYK, HSB,
HSI, HWB, CIE XYZ, CIE LAB, Oklab, Oklch, sRGB-to-linear, NCol
conversions, plus expanded `GetStringRepresentation` tests for Red,
White, Green, and Blue colors across all supported formats

## PR Checklist

- [x] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized —
N/A (test-only changes)
- [ ] **Dev docs:** Added/updated — N/A (test-only changes)
- [ ] **New binaries:** Added on the required places — N/A (no new
binaries)

## Detailed Description of the Pull Request / Additional comments

### Hosts ValidationHelper
(`src/modules/Hosts/Hosts.Tests/ValidationHelperTest.cs`)
- Tests `ValidIPv4` with valid addresses (loopback, private ranges,
broadcast), invalid addresses (out of range octets, wrong format, CIDR
notation), and null/whitespace inputs
- Tests `ValidIPv6` with valid addresses (loopback, full/compressed
notation, link-local, IPv4-mapped), invalid addresses (extra groups,
invalid hex digits), and null/whitespace inputs
- Tests `ValidHosts` with valid hostnames, FQDNs, max host count
boundary (using `Consts.MaxHostsCount` dynamically for both
exact-boundary and exceeds-boundary tests), and invalid hostnames

### ColorPicker Format Conversions
(`src/modules/colorPicker/ColorPickerUI.UnitTests/Helpers/ColorFormatConversionTest.cs`)
- Tests `ConvertToCMYKColor` for Black, White, Red, Green, Blue, and Mid
Gray
- Tests `ConvertToHSBColor`, `ConvertToHSIColor`, `ConvertToHWBColor`
for primary colors
- Tests `ConvertToCIEXYZColor` and `ConvertToCIELABColor` including D65
illuminant verification and negative b* assertion for Blue
- Tests `ConvertToOklabColor` and `ConvertToOklchColor` including chroma
non-negativity
- Tests `ConvertSRGBToLinearRGB` for linear and gamma paths
- Tests `ConvertToNaturalColor` for hue letter mapping (R exact match, G
and B prefix assertions)
- Tests `GetStringRepresentation` with Red, White, Green, Blue across
all 11+ format types (Decimal values use BGR byte order via `%Dv`
format: Red → "255", Blue → "16711680")
- Tests `GetDefaultFormat` returns non-empty strings for all known
format names
- Tests edge cases: empty/null format strings defaulting to hex output

### Spell-check allow list
- Added `SRGBTo` to `.github/actions/spell-check/allow/code.txt` to
resolve unrecognized-spelling alerts

### StyleCop / code analysis fixes
- Resolved SA1512, SA1515, CA1866, and CA1310 analyzer warnings to
comply with repo coding standards

### Code review fixes
- Renamed `ConvertToCIELAB_Blue_HasNegativeA` →
`ConvertToCIELAB_Blue_HasNegativeB` with corrected comment to match the
actual b* axis assertion
- Replaced hardcoded 12-host string with dynamic `Consts.MaxHostsCount +
1` in the exceeds-max-count boundary test
- Renamed `ConvertToNaturalColor_Green_ReturnsG0` →
`ConvertToNaturalColor_Green_HueStartsWithG` and
`ConvertToNaturalColor_Blue_ReturnsB0` →
`ConvertToNaturalColor_Blue_HueStartsWithB` to accurately reflect
prefix-only assertions

## Validation Steps Performed

- Verified test files follow existing MSTest patterns (`[TestClass]`,
`[TestMethod]`, `[DataTestMethod]`, `[DataRow]`)
- Verified all referenced classes and methods exist and are accessible
(correct namespaces and visibility)
- Verified namespace consistency with existing test files in each
project
- Used dynamic `Consts.MaxHostsCount` rather than hardcoded values for
all boundary tests
- Verified Decimal format expected values match the `%Dv` (BGR order)
implementation: `R + G*256 + B*65536`
- Verified test method names accurately describe their assertions

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: crutkas <1462282+crutkas@users.noreply.github.com>
2026-04-10 09:07:48 +02:00
Dustin L. Howett
3554f0884b spelling: move to v0.0.26 (#46851)
This fixes, among other things, the issue with fork PRs.

---------

Co-authored-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2026-04-10 06:43:27 +02:00
Jaylyn Barbee
ad60090096 [KBM] Fixes to text replacement issues (#46794)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This PR attempts to fix some of the issues that were introduced in
0.98.0 with text replacement

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #46498
- [x] Closes: #46440
- [x] Closes: #46366

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
In 0.98.0 I made a change to support multiline text replacement using
Ctrl + V. This was very inconsistent so I have reverted back to
_basically_ the same approach we were using before except now when we
encounter a newline indicator we send "Shift + Enter" so that this works
in chat boxes and plan editors.

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Manual testing with single line and multiline replacements in Teams,
Terminal, Git bash, Edge, etc
2026-04-09 09:58:10 -05:00
Niels Laute
de6a609d16 [KBM] Manual key selection — code review fixes (#46377)
Addresses code review feedback on the KBM manual key selection feature.
No new user-facing behavior; all changes are correctness, robustness,
and maintainability fixes.

## Summary of the Pull Request

- **Localization:** All hard-coded `RemappingDialog.Title` assignments
replaced with `ResourceHelper.GetString()`; added
`RemappingDialog_TitleEdit` resource key
- **VK_DISABLED centralization:** Replaced scattered `0x100`/`"256"`
literals and local `const string vkDisabledCode` with `private const int
VkDisabled = 0x100` / `private const string VkDisabledString = "256"` on
`MainPage`
- **Disable action validation:** Added
`ValidationHelper.ValidateDisableMapping()` — same trigger-key rules as
other action types (empty keys, modifier-only, illegal shortcuts,
duplicates, conflicting modifier variants); wired into
`ValidateMapping()` switch
- **Binding-safe dropdown revert:** `TriggerKeyDropDown_KeyChanged` /
`ActionKeyDropDown_KeyChanged` no longer set `dropDown.KeyName =
e.OldKeyName` on failure (breaks `{Binding}` expression); now use
`RevertKeySelection(keys, index)` which does
`ObservableCollection.RemoveAt` + `Insert` to force a binding-tracked
refresh without touching the DP directly. `NewKeyCode == 0` ("None") is
rejected via the same path
- **Dropdown validation:** `ValidateDropDownSelection` skips
`string.IsNullOrEmpty` placeholder slots (added by
`HandleAutoGrowShrink`) when checking repeated-modifier and max-size
rules
- **`SetActionType`:** Replaced hard-coded `SelectedIndex` with
tag-matching iteration over `ActionTypeComboBox.Items`; immune to XAML
item reorder
- **`ServiceStatusHelper`:** Dispose all `Process` objects returned by
`GetProcessesByName` before returning; prevents handle accumulation on
the 3-second polling timer
- **`KeyDropDownButton.GetKeyList()`:** Filter out `KeyCode == 0`
entries (native "None" sentinel for shortcut lists) before caching
- **`SettingsManager`:** `_mappingService!` used consistently in
`CreateSettingsFromKeyboardManagerService`
- **`KeyboardHookHelper`:** Constructor catch broadened from
`DllNotFoundException or InvalidOperationException` to `Exception`

## PR Checklist

- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

## Detailed Description of the Pull Request / Additional comments

The `RevertKeySelection` pattern: in WinUI, assigning directly to a
bound `DependencyProperty` overwrites the binding expression. Using
`ObservableCollection.RemoveAt` + `Insert` instead raises
`CollectionChanged(Replace)`, causing the binding to re-read from the
source without clearing the expression.

```csharp
private static void RevertKeySelection(ObservableCollection<string> keys, int index)
{
    string current = keys[index];
    keys.RemoveAt(index);
    keys.Insert(index, current);
}
```

## Validation Steps Performed

Manually verified: dropdown key selection and revert on invalid
selection, Disable mapping save/load with the new validation, "None"
absent from key picker flyout, `SetActionType` correctly selects items
with preserved XAML order.

---------

Co-authored-by: Zach Teutsch <88554871+zateutsch@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-04-09 10:45:25 -04:00
Rin
243255ecea Fix quote breakout in launcher shell plugin commands (#45554)
## Summary of the Pull Request
Fixes a command breakout vulnerability in the Shell plugin where user
input containing double quotes could be manipulated to execute arbitrary
sub-processes. By escaping double quotes, inputs like `test" & calc.exe`
are treated as literal strings rather than shell command separators.

## PR Checklist
- [ ] Closes: #xxx
- [x] **Communication:** Proactive fix for command execution logic.
- [x] **Tests:** Verified locally.
- [x] **Localization:** N/A
- [x] **Dev docs:** N/A
- [x] **New binaries:** N/A
- [x] **Documentation updated:** N/A

## Detailed Description of the Pull Request / Additional comments
Escaping double quotes in the command string before it is passed to the
shell prevents the breakout vector I identified while still allowing
environment variables to expand as expected. I also removed some
redundant property assignments in the shell helpers to keep the logic
focused on the core fix.

## Validation Steps Performed
Standard diagnostic commands like `ping` and `ipconfig` continue to
function as expected via PowerToys Run. Malicious strings designed to
break out of quotes now fail to execute sub-commands, as confirmed
during local verification.

---------

Co-authored-by: LegendaryBlair <legendaryblair@icloud.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-09 16:31:24 +08:00
236 changed files with 8015 additions and 3527 deletions

View File

@@ -19,6 +19,7 @@ OLIVEGREEN
PALEBLUE
PArgb
Pbgra
SRGBTo
WHITEONBLACK
@@ -331,6 +332,7 @@ REGSTR
INVOKEIDLIST
MEMORYSTATUSEX
ABE
Mdt
HTCAPTION
POSCHANGED
QUERYPOS

View File

@@ -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$
@@ -140,8 +140,6 @@
^tools/project_template/ModuleTemplate/resource\.h$
^tools/Verification scripts/Check preview handler registration\.ps1$
ignore$
^src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/.*$
^src/common/CalculatorEngineCommon/exprtk\.hpp$
src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownImagesPage.cs
^src/modules/powerrename/unittests/testdata/avif_test\.avif$
^src/modules/powerrename/unittests/testdata/heif_test\.heic$

View File

@@ -1037,9 +1037,7 @@ NORMALDISPLAY
NORMALUSER
NOSEARCH
NOSENDCHANGING
NOSIZE
notdefault
Nosize
NOTHOUSANDS
NOTICKS
NOTIFICATIONSDLL
@@ -1643,6 +1641,7 @@ tracelogging
tracerpt
trackbar
trafficmanager
traies
transicc
TRAYMOUSEMESSAGE
triaging
@@ -1663,6 +1662,7 @@ UBR
UCallback
ucrt
ucrtd
udit
uefi
uesc
UFlags
@@ -1694,6 +1694,7 @@ UOI
UPDATENOW
updown
UPGRADINGPRODUCTCODE
upserts
Uptool
urld
Usb
@@ -1778,7 +1779,6 @@ webpage
websites
wekyb
wgpocpl
WIC
wic
wifi
winapi
@@ -2176,7 +2176,7 @@ nodiscard
nologo
nomove
nosize
notopmost
NOTOPMOST
Notupdated
notwindows
nowarn
@@ -2343,3 +2343,9 @@ YTimer
zamora
zonability
Zorder
LLMHF
RIGHTBUTTON
SIZEALL
grabandmove
GRABANDMOVEMODULEINTERFACE
INITCOMMONCONTROLSEX

View File

@@ -191,15 +191,6 @@ aka\.ms/[a-zA-Z0-9]+
# #pragma lib
^\s*#pragma comment\(lib, ".*?"\)
# UnitTests
\[DataRow\(.*\)\]
# AdditionalDependencies
<AdditionalDependencies>.*<
# the last line of mimetype="application/x-microsoft.net.object.bytearray.base64" things in .resx files
^\s*[-a-zA-Z=;:/0-9+]*[-a-zA-Z;:/0-9+][-a-zA-Z=;:/0-9+]*=$
RegExp\(@?([`'"]).*?\g{-1}\)|(?:escapes|regEx):\s*(?:/.*/|([`'"]).*?\g{-1})|return/.*?/
# Questionably acceptable forms of `in to`

View File

@@ -93,7 +93,7 @@ jobs:
steps:
- name: check-spelling
id: spelling
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
uses: check-spelling/check-spelling@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
with:
config: .github/actions/spell-check
suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }}
@@ -135,6 +135,7 @@ jobs:
cspell:cpp/compiler-msvc.txt
cspell:python/common/extra.txt
cspell:scala/scala.txt
ignored: ignored-expect-variant
comment-push:
name: Report (Push)
@@ -147,10 +148,8 @@ jobs:
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
steps:
- name: comment
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
uses: check-spelling/check-spelling@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
with:
config: .github/actions/spell-check
checkout: true
spell_check_this: microsoft/PowerToys@main
task: ${{ needs.spelling.outputs.followup }}
@@ -166,10 +165,8 @@ jobs:
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
steps:
- name: comment
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
uses: check-spelling/check-spelling@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
with:
config: .github/actions/spell-check
checkout: true
spell_check_this: check-spelling/spell-check-this@prerelease
task: ${{ needs.spelling.outputs.followup }}
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
@@ -193,7 +190,7 @@ jobs:
cancel-in-progress: false
steps:
- name: apply spelling updates
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
uses: check-spelling/check-spelling@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
with:
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
checkout: true

View File

@@ -217,7 +217,11 @@
"PowerToys.PowerAccentModuleInterface.dll",
"PowerToys.PowerAccentKeyboardService.dll",
"PowerToys.PowerDisplayModuleInterface.dll",
"WinUI3Apps\\PowerToys.PowerDisplay.dll",
"WinUI3Apps\\PowerToys.PowerDisplay.exe",
"PowerDisplay.Lib.dll",
"PowerDisplay.Models.dll",
"WinUI3Apps\\PowerToys.PowerRenameExt.dll",
"WinUI3Apps\\PowerToys.PowerRename.exe",
@@ -246,6 +250,9 @@
"PowerToys.ZoomItModuleInterface.dll",
"PowerToys.ZoomItSettingsInterop.dll",
"PowerToys.GrabAndMove.exe",
"PowerToys.GrabAndMoveModuleInterface.dll",
"WinUI3Apps\\PowerToys.Settings.dll",
"WinUI3Apps\\PowerToys.Settings.exe",

View File

@@ -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

View File

@@ -709,17 +709,19 @@
</Project>
</Folder>
<Folder Name="/modules/PowerDisplay/">
<Project Path="src/modules/powerdisplay/PowerDisplay.Models/PowerDisplay.Models.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/powerdisplay/PowerDisplay.Lib/PowerDisplay.Lib.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<!-- TEMPORARILY_DISABLED: PowerDisplay
<Project Path="src/modules/powerdisplay/PowerDisplay/PowerDisplay.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/powerdisplay/PowerDisplayModuleInterface/PowerDisplayModuleInterface.vcxproj" Id="d1234567-8901-2345-6789-abcdef012345" />
-->
</Folder>
<Folder Name="/modules/PowerDisplay/Tests/">
<Project Path="src/modules/powerdisplay/PowerDisplay.Lib.UnitTests/PowerDisplay.Lib.UnitTests.csproj">
@@ -1034,6 +1036,10 @@
<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="/settings-ui/">
<Project Path="src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
@@ -1098,6 +1104,8 @@
<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/powerrename/dll/PowerRenameExt.vcxproj" />
<BuildDependency Project="src/modules/powerrename/lib/PowerRenameLib.vcxproj" />
<BuildDependency Project="src/modules/previewpane/Common/PreviewHandlerCommon.csproj" />

View File

Before

Width:  |  Height:  |  Size: 497 KiB

After

Width:  |  Height:  |  Size: 497 KiB

View File

Before

Width:  |  Height:  |  Size: 276 KiB

After

Width:  |  Height:  |  Size: 276 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

Before

Width:  |  Height:  |  Size: 269 KiB

After

Width:  |  Height:  |  Size: 269 KiB

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 228 KiB

After

Width:  |  Height:  |  Size: 228 KiB

View File

Before

Width:  |  Height:  |  Size: 491 KiB

After

Width:  |  Height:  |  Size: 491 KiB

View File

Before

Width:  |  Height:  |  Size: 706 KiB

After

Width:  |  Height:  |  Size: 706 KiB

View File

Before

Width:  |  Height:  |  Size: 408 KiB

After

Width:  |  Height:  |  Size: 408 KiB

View File

Before

Width:  |  Height:  |  Size: 384 KiB

After

Width:  |  Height:  |  Size: 384 KiB

View File

Before

Width:  |  Height:  |  Size: 493 KiB

After

Width:  |  Height:  |  Size: 493 KiB

View File

Before

Width:  |  Height:  |  Size: 492 KiB

After

Width:  |  Height:  |  Size: 492 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

Before

Width:  |  Height:  |  Size: 311 KiB

After

Width:  |  Height:  |  Size: 311 KiB

View File

Before

Width:  |  Height:  |  Size: 297 KiB

After

Width:  |  Height:  |  Size: 297 KiB

View File

Before

Width:  |  Height:  |  Size: 284 KiB

After

Width:  |  Height:  |  Size: 284 KiB

View File

Before

Width:  |  Height:  |  Size: 225 KiB

After

Width:  |  Height:  |  Size: 225 KiB

View File

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

View File

Before

Width:  |  Height:  |  Size: 890 KiB

After

Width:  |  Height:  |  Size: 890 KiB

View File

Before

Width:  |  Height:  |  Size: 858 KiB

After

Width:  |  Height:  |  Size: 858 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -1594,6 +1594,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
L"PowerToys.PowerRename.exe",
L"PowerToys.ImageResizer.exe",
L"PowerToys.LightSwitchService.exe",
L"PowerToys.PowerDisplay.exe",
L"PowerToys.GcodeThumbnailProvider.exe",
L"PowerToys.BgcodeThumbnailProvider.exe",
L"PowerToys.PdfThumbnailProvider.exe",

View File

@@ -47,6 +47,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
call move /Y ..\..\..\NewPlus.wxs.bk ..\..\..\NewPlus.wxs
call move /Y ..\..\..\Peek.wxs.bk ..\..\..\Peek.wxs
call move /Y ..\..\..\PowerRename.wxs.bk ..\..\..\PowerRename.wxs
call move /Y ..\..\..\PowerDisplay.wxs.bk ..\..\..\PowerDisplay.wxs
call move /Y ..\..\..\Product.wxs.bk ..\..\..\Product.wxs
call move /Y ..\..\..\RegistryPreview.wxs.bk ..\..\..\RegistryPreview.wxs
call move /Y ..\..\..\Resources.wxs.bk ..\..\..\Resources.wxs
@@ -123,6 +124,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
<Compile Include="KeyboardManager.wxs" />
<Compile Include="Peek.wxs" />
<Compile Include="PowerRename.wxs" />
<Compile Include="PowerDisplay.wxs" />
<Compile Include="DscResources.wxs" />
<Compile Include="RegistryPreview.wxs" />
<Compile Include="Run.wxs" />

View File

@@ -53,6 +53,7 @@
<ComponentGroupRef Id="LightSwitchComponentGroup" />
<ComponentGroupRef Id="PeekComponentGroup" />
<ComponentGroupRef Id="PowerRenameComponentGroup" />
<ComponentGroupRef Id="PowerDisplayComponentGroup" />
<ComponentGroupRef Id="RegistryPreviewComponentGroup" />
<ComponentGroupRef Id="RunComponentGroup" />
<ComponentGroupRef Id="SettingsComponentGroup" />

View File

@@ -212,6 +212,10 @@ Generate-FileComponents -fileListName "PeekAssetsFiles" -wxsFilePath $PSScriptRo
Generate-FileList -fileDepsJson "" -fileListName PowerRenameAssetsFiles -wxsFilePath $PSScriptRoot\PowerRename.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\PowerRename\"
Generate-FileComponents -fileListName "PowerRenameAssetsFiles" -wxsFilePath $PSScriptRoot\PowerRename.wxs
#PowerDisplay
Generate-FileList -fileDepsJson "" -fileListName PowerDisplayAssetsFiles -wxsFilePath $PSScriptRoot\PowerDisplay.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\PowerDisplay\"
Generate-FileComponents -fileListName "PowerDisplayAssetsFiles" -wxsFilePath $PSScriptRoot\PowerDisplay.wxs
#RegistryPreview
Generate-FileList -fileDepsJson "" -fileListName RegistryPreviewAssetsFiles -wxsFilePath $PSScriptRoot\RegistryPreview.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\RegistryPreview\"
Generate-FileComponents -fileListName "RegistryPreviewAssetsFiles" -wxsFilePath $PSScriptRoot\RegistryPreview.wxs

View File

@@ -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();
}

View File

@@ -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());

View File

@@ -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();

View File

@@ -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();

View File

@@ -71,5 +71,10 @@ namespace PowerToys.GPOWrapperProjection
{
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredLightSwitchEnabledValue();
}
public static GpoRuleConfigured GetConfiguredGrabAndMoveEnabledValue()
{
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredGrabAndMoveEnabledValue();
}
}
}

View File

@@ -36,6 +36,7 @@ namespace ManagedCommon
ShortcutGuide,
PowerOCR,
Workspaces,
GrabAndMove,
ZoomIt,
GeneralSettings,
}

View File

@@ -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";

View File

@@ -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();

View File

@@ -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";
@@ -317,6 +318,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);

View File

@@ -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>
@@ -150,10 +151,9 @@
<decimal value="0" />
</disabledValue>
</policy>
<!-- TEMPORARILY_DISABLED: PowerDisplay
<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>
@@ -161,7 +161,6 @@
<decimal value="0" />
</disabledValue>
</policy>
-->
<policy name="ConfigureEnabledUtilityEnvironmentVariables" class="Both" displayName="$(string.ConfigureEnabledUtilityEnvironmentVariables)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityEnvironmentVariables">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_75_0" />

View File

@@ -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.
@@ -249,7 +250,7 @@ If you don't configure this policy, the user will be able to control the setting
<string id="ConfigureEnabledUtilityCmdPal">CmdPal: Configure enabled state</string>
<string id="ConfigureEnabledUtilityCropAndLock">Crop And Lock: Configure enabled state</string>
<string id="ConfigureEnabledUtilityLightSwitch">Light Switch: Configure enabled state</string>
<!-- <string id="ConfigureEnabledUtilityPowerDisplay">PowerDisplay: Configure enabled state</string> --><!-- TEMPORARILY_DISABLED: PowerDisplay -->
<string id="ConfigureEnabledUtilityPowerDisplay">PowerDisplay: Configure enabled state</string>
<string id="ConfigureEnabledUtilityEnvironmentVariables">Environment Variables: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFancyZones">FancyZones: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileLocksmith">File Locksmith: Configure enabled state</string>

View File

@@ -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;
}
}

View File

@@ -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 };
}

View File

@@ -8,6 +8,7 @@ struct CommandResult
{
int exit_code;
std::wstring output;
std::wstring command_name;
};
struct IProcessFinder

View File

@@ -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

View File

@@ -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);
}
};
}

View File

@@ -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"));
}

View File

@@ -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;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

View 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>

View 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

View 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>

View File

@@ -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>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
#include "pch.h"

View 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>

View 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
//////////////////////////////

View File

@@ -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

View File

@@ -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>

View 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();
}

View File

@@ -0,0 +1 @@
#include "pch.h"

View 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>

View File

@@ -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
//////////////////////////////

View 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"));
}

View 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;
};

View File

@@ -0,0 +1,180 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Linq;
using HostsUILib;
using HostsUILib.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Hosts.Tests
{
[TestClass]
public class ValidationHelperTest
{
[DataTestMethod]
[DataRow("0.0.0.0")]
[DataRow("127.0.0.1")]
[DataRow("192.168.1.1")]
[DataRow("255.255.255.255")]
[DataRow("10.0.0.1")]
[DataRow("172.16.0.1")]
[DataRow("1.2.3.4")]
[DataRow("01.01.01.01")]
[DataRow("0.0.0.1")]
public void ValidIPv4_ValidAddresses_ReturnsTrue(string address)
{
Assert.IsTrue(ValidationHelper.ValidIPv4(address));
}
[DataTestMethod]
[DataRow("256.0.0.0")]
[DataRow("0.256.0.0")]
[DataRow("0.0.256.0")]
[DataRow("0.0.0.256")]
[DataRow("999.999.999.999")]
[DataRow("1.2.3")]
[DataRow("1.2.3.4.5")]
[DataRow("1.2.3.")]
[DataRow(".1.2.3")]
[DataRow("1..2.3")]
[DataRow("abc.def.ghi.jkl")]
[DataRow("192.168.1.1/24")]
[DataRow("192.168.1.1:80")]
[DataRow("192.168.1")]
[DataRow("-1.0.0.0")]
public void ValidIPv4_InvalidAddresses_ReturnsFalse(string address)
{
Assert.IsFalse(ValidationHelper.ValidIPv4(address));
}
[DataTestMethod]
[DataRow(null)]
[DataRow("")]
[DataRow(" ")]
[DataRow("\t")]
[DataRow("\n")]
public void ValidIPv4_NullOrWhitespace_ReturnsFalse(string address)
{
Assert.IsFalse(ValidationHelper.ValidIPv4(address));
}
[DataTestMethod]
[DataRow("::1")]
[DataRow("::")]
[DataRow("2001:0db8:85a3:0000:0000:8a2e:0370:7334")]
[DataRow("2001:db8:85a3:0:0:8a2e:370:7334")]
[DataRow("2001:db8:85a3::8a2e:370:7334")]
[DataRow("fe80::1")]
[DataRow("ff02::1")]
[DataRow("2001:db8::1")]
[DataRow("::ffff:192.168.1.1")]
[DataRow("fe80::1%eth0")]
public void ValidIPv6_ValidAddresses_ReturnsTrue(string address)
{
Assert.IsTrue(ValidationHelper.ValidIPv6(address));
}
[DataTestMethod]
[DataRow("2001:db8:85a3:0:0:8a2e:370:7334:extra")]
[DataRow("gggg::1")]
[DataRow("12345::1")]
[DataRow("192.168.1.1")]
[DataRow("::ffff:999.999.999.999")]
[DataRow("hello")]
[DataRow("2001:db8:85a3::8a2e:370:7334:1234:5678")]
public void ValidIPv6_InvalidAddresses_ReturnsFalse(string address)
{
Assert.IsFalse(ValidationHelper.ValidIPv6(address));
}
[DataTestMethod]
[DataRow(null)]
[DataRow("")]
[DataRow(" ")]
[DataRow("\t")]
public void ValidIPv6_NullOrWhitespace_ReturnsFalse(string address)
{
Assert.IsFalse(ValidationHelper.ValidIPv6(address));
}
[DataTestMethod]
[DataRow("localhost")]
[DataRow("example.com")]
[DataRow("sub.domain.example.com")]
[DataRow("my-host")]
[DataRow("host1 host2")]
[DataRow("host1 host2 host3")]
[DataRow("example.com www.example.com")]
public void ValidHosts_ValidHostnames_ReturnsTrue(string hosts)
{
Assert.IsTrue(ValidationHelper.ValidHosts(hosts, validateHostsLength: false));
}
[DataTestMethod]
[DataRow(null)]
[DataRow("")]
[DataRow(" ")]
[DataRow("\t")]
public void ValidHosts_NullOrWhitespace_ReturnsFalse(string hosts)
{
Assert.IsFalse(ValidationHelper.ValidHosts(hosts, validateHostsLength: false));
}
[TestMethod]
public void ValidHosts_WithLengthValidation_ExceedsMaxCount_ReturnsFalse()
{
// Create a host string with one more than MaxHostsCount hosts
var hosts = string.Join(" ", Enumerable.Range(1, Consts.MaxHostsCount + 1).Select(i => $"h{i}"));
Assert.IsFalse(ValidationHelper.ValidHosts(hosts, validateHostsLength: true));
}
[TestMethod]
public void ValidHosts_WithLengthValidation_AtMaxCount_ReturnsTrue()
{
// Create a host string with exactly MaxHostsCount hosts
var hosts = string.Join(" ", Enumerable.Range(1, Consts.MaxHostsCount).Select(i => $"h{i}"));
Assert.IsTrue(ValidationHelper.ValidHosts(hosts, validateHostsLength: true));
}
[TestMethod]
public void ValidHosts_WithLengthValidation_BelowMaxCount_ReturnsTrue()
{
string hosts = "host1 host2 host3";
Assert.IsTrue(ValidationHelper.ValidHosts(hosts, validateHostsLength: true));
}
[TestMethod]
public void ValidHosts_WithoutLengthValidation_ExceedsMaxCount_ReturnsTrue()
{
// When validateHostsLength is false, exceeding max count should still return true
var hosts = string.Join(" ", Enumerable.Range(1, Consts.MaxHostsCount + 1).Select(i => $"h{i}"));
Assert.IsTrue(ValidationHelper.ValidHosts(hosts, validateHostsLength: false));
}
[TestMethod]
public void ValidHosts_SingleHost_ReturnsTrue()
{
Assert.IsTrue(ValidationHelper.ValidHosts("localhost", validateHostsLength: true));
}
[TestMethod]
public void ValidHosts_InvalidHostname_ReturnsFalse()
{
Assert.IsFalse(ValidationHelper.ValidHosts("host_with!invalid@chars", validateHostsLength: false));
}
[TestMethod]
public void ValidHosts_HostWithSubdomains_ReturnsTrue()
{
Assert.IsTrue(ValidationHelper.ValidHosts("sub.domain.example.com", validateHostsLength: true));
}
[TestMethod]
public void ValidHosts_MultipleValidHosts_WithLengthValidation_ReturnsTrue()
{
Assert.IsTrue(ValidationHelper.ValidHosts("example.com www.example.com api.example.com", validateHostsLength: true));
}
}
}

View File

@@ -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>

View File

@@ -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="&#xF16E;" />
</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="&#xE80A;" />
</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="&#xE713;" />
</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="&#xE711;" />
</Button>
</StackPanel>
</Border>

View File

@@ -64,8 +64,6 @@ public partial class OCROverlay : Window
InitializeComponent();
Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this, Wpf.Ui.Controls.WindowBackdropType.None);
PopulateLanguageMenu();
}

View File

@@ -31,7 +31,6 @@
<ItemGroup>
<PackageReference Include="System.ComponentModel.Composition" />
<PackageReference Include="System.Drawing.Common" />
<PackageReference Include="WPF-UI" />
</ItemGroup>
<ItemGroup>

View File

@@ -252,11 +252,13 @@ void AlwaysOnTop::ProcessCommand(HWND window)
}
Sound::Type soundType = Sound::Type::Off;
bool stateChanged = false;
bool topmost = IsTopmost(window);
if (topmost)
{
if (UnpinTopmostWindow(window))
{
stateChanged = true;
auto iter = m_topmostWindows.find(window);
if (iter != m_topmostWindows.end())
{
@@ -274,6 +276,7 @@ void AlwaysOnTop::ProcessCommand(HWND window)
{
if (PinTopmostWindow(window))
{
stateChanged = true;
soundType = Sound::Type::On;
AssignBorder(window);
@@ -281,7 +284,7 @@ void AlwaysOnTop::ProcessCommand(HWND window)
}
}
if (AlwaysOnTopSettings::settings()->enableSound)
if (stateChanged && AlwaysOnTopSettings::settings()->enableSound)
{
m_sound.Play(soundType);
}

View File

@@ -19,6 +19,7 @@ using Awake.Core;
using Awake.Core.Models;
using Awake.Core.Native;
using Awake.Properties;
using Awake.Telemetry;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Telemetry;
@@ -68,6 +69,7 @@ namespace Awake
if (parseResult.Errors.Count > 0)
{
// Shows errors and returns non-zero.
LogCLITelemetry(successful: false);
return rootCommand.Invoke(args);
}
@@ -96,6 +98,7 @@ namespace Awake
{
// Awake is already running - there is no need for us to process
// anything further
LogCLITelemetry(successful: false);
Exit(Core.Constants.AppName + " is already running! Exiting the application.", 1);
return 1;
}
@@ -103,6 +106,7 @@ namespace Awake
{
if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredAwakeEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
{
LogCLITelemetry(successful: false);
Exit("PowerToys.Awake tried to start with a group policy setting that disables the tool. Please contact your system administrator.", 1);
return 1;
}
@@ -125,7 +129,9 @@ namespace Awake
Bridge.GetPwrCapabilities(out _powerCapabilities);
Logger.LogInfo(JsonSerializer.Serialize(_powerCapabilities, _serializerOptions));
return await rootCommand.InvokeAsync(args);
var result = await rootCommand.InvokeAsync(args);
LogCLITelemetry(successful: result == 0);
return result;
}
}
}
@@ -216,6 +222,22 @@ namespace Awake
return rootCommand;
}
private static void LogCLITelemetry(bool successful)
{
try
{
PowerToysTelemetry.Log.WriteEvent(new AwakeCLICommandEvent
{
CommandName = "awake",
Successful = successful,
});
}
catch (Exception ex)
{
Logger.LogError($"Failed to log CLI telemetry: {ex.Message}");
}
}
private static void AwakeUnhandledExceptionCatcher(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is Exception exception)

View File

@@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace Awake.Telemetry
{
/// <summary>
/// Telemetry event for Awake CLI command execution.
/// </summary>
[EventData]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
public class AwakeCLICommandEvent : EventBase, IEvent
{
public AwakeCLICommandEvent()
{
EventName = "Awake_CLICommand";
CommandName = string.Empty;
}
/// <summary>
/// Gets or sets the name of the CLI command that was executed.
/// </summary>
public string CommandName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the command executed successfully.
/// </summary>
public bool Successful { get; set; }
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@@ -0,0 +1,57 @@
{
"solution": {
"path": "..\\..\\..\\PowerToys.slnx",
"projects": [
"src\\common\\CalculatorEngineCommon\\CalculatorEngineCommon.vcxproj",
"src\\common\\Common.UI.Controls\\Common.UI.Controls.csproj",
"src\\common\\ManagedCommon\\ManagedCommon.csproj",
"src\\common\\ManagedCsWin32\\ManagedCsWin32.csproj",
"src\\common\\ManagedTelemetry\\Telemetry\\ManagedTelemetry.csproj",
"src\\common\\interop\\PowerToys.Interop.vcxproj",
"src\\common\\version\\version.vcxproj",
"src\\modules\\cmdpal\\CmdPalKeyboardService\\CmdPalKeyboardService.vcxproj",
"src\\modules\\cmdpal\\CmdPalModuleInterface\\CmdPalModuleInterface.vcxproj",
"src\\modules\\cmdpal\\Microsoft.CmdPal.Common\\Microsoft.CmdPal.Common.csproj",
"src\\modules\\cmdpal\\Microsoft.CmdPal.UI.ViewModels\\Microsoft.CmdPal.UI.ViewModels.csproj",
"src\\modules\\cmdpal\\Microsoft.CmdPal.UI\\Microsoft.CmdPal.UI.csproj",
"src\\modules\\cmdpal\\Microsoft.Terminal.UI\\Microsoft.Terminal.UI.vcxproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Common.UnitTests\\Microsoft.CmdPal.Common.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Apps.UnitTests\\Microsoft.CmdPal.Ext.Apps.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Bookmarks.UnitTests\\Microsoft.CmdPal.Ext.Bookmarks.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Calc.UnitTests\\Microsoft.CmdPal.Ext.Calc.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Registry.UnitTests\\Microsoft.CmdPal.Ext.Registry.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests\\Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Shell.UnitTests\\Microsoft.CmdPal.Ext.Shell.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.System.UnitTests\\Microsoft.CmdPal.Ext.System.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.TimeDate.UnitTests\\Microsoft.CmdPal.Ext.TimeDate.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.UnitTestsBase\\Microsoft.CmdPal.Ext.UnitTestBase.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.WebSearch.UnitTests\\Microsoft.CmdPal.Ext.WebSearch.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.WindowWalker.UnitTests\\Microsoft.CmdPal.Ext.WindowWalker.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.UI.ViewModels.UnitTests\\Microsoft.CmdPal.UI.ViewModels.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.UITests\\Microsoft.CmdPal.UITests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CommandPalette.Extensions.Toolkit.UnitTests\\Microsoft.CommandPalette.Extensions.Toolkit.UnitTests.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.Apps\\Microsoft.CmdPal.Ext.Apps.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.Bookmark\\Microsoft.CmdPal.Ext.Bookmarks.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.Calc\\Microsoft.CmdPal.Ext.Calc.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.ClipboardHistory\\Microsoft.CmdPal.Ext.ClipboardHistory.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.Indexer\\Microsoft.CmdPal.Ext.Indexer.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.PerformanceMonitor\\Microsoft.CmdPal.Ext.PerformanceMonitor.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.Registry\\Microsoft.CmdPal.Ext.Registry.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.RemoteDesktop\\Microsoft.CmdPal.Ext.RemoteDesktop.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.Shell\\Microsoft.CmdPal.Ext.Shell.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.System\\Microsoft.CmdPal.Ext.System.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.TimeDate\\Microsoft.CmdPal.Ext.TimeDate.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.WebSearch\\Microsoft.CmdPal.Ext.WebSearch.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.WinGet\\Microsoft.CmdPal.Ext.WinGet.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.WindowWalker\\Microsoft.CmdPal.Ext.WindowWalker.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.WindowsServices\\Microsoft.CmdPal.Ext.WindowsServices.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.WindowsSettings\\Microsoft.CmdPal.Ext.WindowsSettings.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.WindowsTerminal\\Microsoft.CmdPal.Ext.WindowsTerminal.csproj",
"src\\modules\\cmdpal\\ext\\ProcessMonitorExtension\\ProcessMonitorExtension.csproj",
"src\\modules\\cmdpal\\ext\\SamplePagesExtension\\SamplePagesExtension.csproj",
"src\\modules\\cmdpal\\extensionsdk\\Microsoft.CommandPalette.Extensions.Toolkit\\Microsoft.CommandPalette.Extensions.Toolkit.csproj",
"src\\modules\\cmdpal\\extensionsdk\\Microsoft.CommandPalette.Extensions\\Microsoft.CommandPalette.Extensions.vcxproj"
]
}
}

View File

@@ -7,8 +7,14 @@
"src\\common\\ManagedCommon\\ManagedCommon.csproj",
"src\\common\\ManagedCsWin32\\ManagedCsWin32.csproj",
"src\\common\\ManagedTelemetry\\Telemetry\\ManagedTelemetry.csproj",
"src\\common\\SettingsAPI\\SettingsAPI.vcxproj",
"src\\common\\UITestAutomation\\UITestAutomation.csproj",
"src\\common\\interop\\PowerToys.Interop.vcxproj",
"src\\common\\logger\\logger.vcxproj",
"src\\common\\version\\version.vcxproj",
"src\\logging\\logging.vcxproj",
"src\\modules\\MouseUtils\\MouseJump.Common\\MouseJump.Common.csproj",
"src\\modules\\ZoomIt\\ZoomItSettingsInterop\\ZoomItSettingsInterop.vcxproj",
"src\\modules\\cmdpal\\CmdPalKeyboardService\\CmdPalKeyboardService.vcxproj",
"src\\modules\\cmdpal\\CmdPalModuleInterface\\CmdPalModuleInterface.vcxproj",
"src\\modules\\cmdpal\\Microsoft.CmdPal.Common\\Microsoft.CmdPal.Common.csproj",
@@ -51,7 +57,9 @@
"src\\modules\\cmdpal\\ext\\ProcessMonitorExtension\\ProcessMonitorExtension.csproj",
"src\\modules\\cmdpal\\ext\\SamplePagesExtension\\SamplePagesExtension.csproj",
"src\\modules\\cmdpal\\extensionsdk\\Microsoft.CommandPalette.Extensions.Toolkit\\Microsoft.CommandPalette.Extensions.Toolkit.csproj",
"src\\modules\\cmdpal\\extensionsdk\\Microsoft.CommandPalette.Extensions\\Microsoft.CommandPalette.Extensions.vcxproj"
"src\\modules\\cmdpal\\extensionsdk\\Microsoft.CommandPalette.Extensions\\Microsoft.CommandPalette.Extensions.vcxproj",
"src\\modules\\powerdisplay\\PowerDisplay.Models\\PowerDisplay.Models.csproj",
"src\\settings-ui\\Settings.UI.Library\\Settings.UI.Library.csproj"
]
}
}

View File

@@ -2,6 +2,7 @@
// 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.Globalization;
using Microsoft.CmdPal.Ext.Calc.Helper;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -10,6 +11,25 @@ namespace Microsoft.CmdPal.Ext.Calc.UnitTests;
[TestClass]
public class QueryHelperTests
{
private CultureInfo originalCulture;
private CultureInfo originalUiCulture;
[TestInitialize]
public void Setup()
{
originalCulture = CultureInfo.CurrentCulture;
CultureInfo.CurrentCulture = new CultureInfo("en-us", false);
originalUiCulture = CultureInfo.CurrentUICulture;
CultureInfo.CurrentUICulture = new CultureInfo("en-us", false);
}
[TestCleanup]
public void CleanUp()
{
CultureInfo.CurrentCulture = originalCulture;
CultureInfo.CurrentUICulture = originalUiCulture;
}
[DataTestMethod]
[DataRow("2²", "4")]
[DataRow("2³", "8")]

View File

@@ -2,6 +2,7 @@
// 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.Globalization;
using System.Linq;
using Microsoft.CmdPal.Ext.Calc.Helper;
using Microsoft.CmdPal.Ext.Calc.Pages;
@@ -14,6 +15,25 @@ namespace Microsoft.CmdPal.Ext.Calc.UnitTests;
[TestClass]
public class QueryTests : CommandPaletteUnitTestBase
{
private CultureInfo originalCulture;
private CultureInfo originalUiCulture;
[TestInitialize]
public void Setup()
{
originalCulture = CultureInfo.CurrentCulture;
CultureInfo.CurrentCulture = new CultureInfo("en-us", false);
originalUiCulture = CultureInfo.CurrentUICulture;
CultureInfo.CurrentUICulture = new CultureInfo("en-us", false);
}
[TestCleanup]
public void CleanUp()
{
CultureInfo.CurrentCulture = originalCulture;
CultureInfo.CurrentUICulture = originalUiCulture;
}
[DataTestMethod]
[DataRow("2+2", "4")]
[DataRow("5*3", "15")]
@@ -84,6 +104,40 @@ public class QueryTests : CommandPaletteUnitTestBase
Assert.IsTrue(result.Title.Contains(expected, System.StringComparison.Ordinal), $"Calc trigMode convert result isn't correct. Current result: {result.Title}");
}
[DataTestMethod]
[DataRow("sin(60)", "-0.30481", CalculateEngine.TrigMode.Radians, "de-DE")]
[DataRow("sin(60)", "0.866025", CalculateEngine.TrigMode.Degrees, "de-DE")]
[DataRow("sin(60)", "0.809016", CalculateEngine.TrigMode.Gradians, "de-DE")]
[DataRow("sin(60)", "-0.30481", CalculateEngine.TrigMode.Radians, "fr-FR")]
[DataRow("sin(60)", "0.866025", CalculateEngine.TrigMode.Degrees, "fr-FR")]
[DataRow("sin(60)", "0.809016", CalculateEngine.TrigMode.Gradians, "fr-FR")]
public void TrigModeSettingsTest_NonEnglishCulture(string input, string expected, CalculateEngine.TrigMode trigMode, string cultureName)
{
var previousCulture = CultureInfo.CurrentCulture;
var previousUiCulture = CultureInfo.CurrentUICulture;
try
{
CultureInfo.CurrentCulture = new CultureInfo(cultureName, false);
CultureInfo.CurrentUICulture = new CultureInfo(cultureName, false);
var settings = new Settings(trigUnit: trigMode, outputUseEnglishFormat: true);
var page = new CalculatorListPage(settings);
page.UpdateSearchText(string.Empty, input);
var result = page.GetItems().FirstOrDefault();
Assert.IsNotNull(result);
Assert.IsTrue(result.Title.Contains(expected, System.StringComparison.Ordinal), $"Calc trigMode convert result isn't correct for culture '{cultureName}'. Current result: {result.Title}");
}
finally
{
CultureInfo.CurrentCulture = previousCulture;
CultureInfo.CurrentUICulture = previousUiCulture;
}
}
[DataTestMethod]
[DataRow("2^64", "18446744073709551616", "0x10000000000000000")]
[DataRow("0-(2^64)", "-18446744073709551616", "-0x10000000000000000")]

View File

@@ -108,7 +108,6 @@ internal static class ResultHelper
tags.Add(new Tag
{
Text = Resources.windowwalker_NotResponding,
Foreground = ColorHelpers.FromRgb(220, 20, 60),
});
}

View File

@@ -0,0 +1,459 @@
// 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.Globalization;
using ColorPicker.Helpers;
using ManagedCommon;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ColorPicker.UnitTests.Helpers
{
/// <summary>
/// Test class to test <see cref="ColorFormatHelper"/> conversion methods not covered
/// by the existing <see cref="ColorConverterTest"/> (which tests HSL and HSV).
/// Covers: CMYK, HSB, HSI, HWB, CIE LAB, CIE XYZ, Oklab, Oklch, sRGB-to-linear, NCol.
/// </summary>
[TestClass]
public class ColorFormatConversionTest
{
[TestMethod]
public void ConvertToCMYK_Black_Returns0_0_0_1()
{
var result = ColorFormatHelper.ConvertToCMYKColor(Color.FromArgb(255, 0, 0, 0));
Assert.AreEqual(0d, result.Cyan);
Assert.AreEqual(0d, result.Magenta);
Assert.AreEqual(0d, result.Yellow);
Assert.AreEqual(1d, result.BlackKey);
}
[TestMethod]
public void ConvertToCMYK_White_Returns0_0_0_0()
{
var result = ColorFormatHelper.ConvertToCMYKColor(Color.FromArgb(255, 255, 255, 255));
Assert.AreEqual(0d, result.Cyan, 0.01);
Assert.AreEqual(0d, result.Magenta, 0.01);
Assert.AreEqual(0d, result.Yellow, 0.01);
Assert.AreEqual(0d, result.BlackKey, 0.01);
}
[TestMethod]
public void ConvertToCMYK_Red_Returns0_1_1_0()
{
var result = ColorFormatHelper.ConvertToCMYKColor(Color.FromArgb(255, 255, 0, 0));
Assert.AreEqual(0d, result.Cyan, 0.01);
Assert.AreEqual(1d, result.Magenta, 0.01);
Assert.AreEqual(1d, result.Yellow, 0.01);
Assert.AreEqual(0d, result.BlackKey, 0.01);
}
[TestMethod]
public void ConvertToCMYK_Green_Returns1_0_1_0()
{
var result = ColorFormatHelper.ConvertToCMYKColor(Color.FromArgb(255, 0, 255, 0));
Assert.AreEqual(1d, result.Cyan, 0.01);
Assert.AreEqual(0d, result.Magenta, 0.01);
Assert.AreEqual(1d, result.Yellow, 0.01);
Assert.AreEqual(0d, result.BlackKey, 0.01);
}
[TestMethod]
public void ConvertToCMYK_Blue_Returns1_1_0_0()
{
var result = ColorFormatHelper.ConvertToCMYKColor(Color.FromArgb(255, 0, 0, 255));
Assert.AreEqual(1d, result.Cyan, 0.01);
Assert.AreEqual(1d, result.Magenta, 0.01);
Assert.AreEqual(0d, result.Yellow, 0.01);
Assert.AreEqual(0d, result.BlackKey, 0.01);
}
[TestMethod]
public void ConvertToCMYK_MidGray_Returns0_0_0_Half()
{
// RGB(128, 128, 128) should give roughly K ≈ 0.498
var result = ColorFormatHelper.ConvertToCMYKColor(Color.FromArgb(255, 128, 128, 128));
Assert.AreEqual(0d, result.Cyan, 0.01);
Assert.AreEqual(0d, result.Magenta, 0.01);
Assert.AreEqual(0d, result.Yellow, 0.01);
Assert.AreEqual(128d / 255d, result.BlackKey, 0.01);
}
[TestMethod]
public void ConvertToHSB_Black_Returns0_0_0()
{
var result = ColorFormatHelper.ConvertToHSBColor(Color.FromArgb(255, 0, 0, 0));
Assert.AreEqual(0d, result.Hue, 0.5);
Assert.AreEqual(0d, result.Saturation, 0.01);
Assert.AreEqual(0d, result.Brightness, 0.01);
}
[TestMethod]
public void ConvertToHSB_White_Returns0_0_1()
{
var result = ColorFormatHelper.ConvertToHSBColor(Color.FromArgb(255, 255, 255, 255));
Assert.AreEqual(0d, result.Hue, 0.5);
Assert.AreEqual(0d, result.Saturation, 0.01);
Assert.AreEqual(1d, result.Brightness, 0.01);
}
[TestMethod]
public void ConvertToHSB_Red_Returns0_1_1()
{
var result = ColorFormatHelper.ConvertToHSBColor(Color.FromArgb(255, 255, 0, 0));
Assert.AreEqual(0d, result.Hue, 0.5);
Assert.AreEqual(1d, result.Saturation, 0.01);
Assert.AreEqual(1d, result.Brightness, 0.01);
}
[TestMethod]
public void ConvertToHSI_Black_Returns0_0_0()
{
var result = ColorFormatHelper.ConvertToHSIColor(Color.FromArgb(255, 0, 0, 0));
Assert.AreEqual(0d, result.Hue, 0.5);
Assert.AreEqual(0d, result.Saturation, 0.01);
Assert.AreEqual(0d, result.Intensity, 0.01);
}
[TestMethod]
public void ConvertToHSI_White_Returns0_0_1()
{
var result = ColorFormatHelper.ConvertToHSIColor(Color.FromArgb(255, 255, 255, 255));
Assert.AreEqual(0d, result.Hue, 0.5);
Assert.AreEqual(0d, result.Saturation, 0.01);
Assert.AreEqual(1d, result.Intensity, 0.01);
}
[TestMethod]
public void ConvertToHSI_Red_Returns0_1_Third()
{
// Pure red: intensity = (255+0+0)/(3*255) = 1/3
var result = ColorFormatHelper.ConvertToHSIColor(Color.FromArgb(255, 255, 0, 0));
Assert.AreEqual(0d, result.Hue, 0.5);
Assert.AreEqual(1d, result.Saturation, 0.01);
Assert.AreEqual(1d / 3d, result.Intensity, 0.01);
}
[TestMethod]
public void ConvertToHWB_Black_Returns0_0_1()
{
var result = ColorFormatHelper.ConvertToHWBColor(Color.FromArgb(255, 0, 0, 0));
Assert.AreEqual(0d, result.Hue, 0.5);
Assert.AreEqual(0d, result.Whiteness, 0.01);
Assert.AreEqual(1d, result.Blackness, 0.01);
}
[TestMethod]
public void ConvertToHWB_White_Returns0_1_0()
{
var result = ColorFormatHelper.ConvertToHWBColor(Color.FromArgb(255, 255, 255, 255));
Assert.AreEqual(0d, result.Hue, 0.5);
Assert.AreEqual(1d, result.Whiteness, 0.01);
Assert.AreEqual(0d, result.Blackness, 0.01);
}
[TestMethod]
public void ConvertToHWB_Red_Returns0_0_0()
{
var result = ColorFormatHelper.ConvertToHWBColor(Color.FromArgb(255, 255, 0, 0));
Assert.AreEqual(0d, result.Hue, 0.5);
Assert.AreEqual(0d, result.Whiteness, 0.01);
Assert.AreEqual(0d, result.Blackness, 0.01);
}
[TestMethod]
public void ConvertToHWB_MidGray_Returns0_Half_Half()
{
var result = ColorFormatHelper.ConvertToHWBColor(Color.FromArgb(255, 128, 128, 128));
Assert.AreEqual(0d, result.Hue, 0.5);
Assert.AreEqual(128d / 255d, result.Whiteness, 0.01);
Assert.AreEqual(1d - (128d / 255d), result.Blackness, 0.01);
}
[TestMethod]
public void ConvertToCIEXYZ_Black_Returns0_0_0()
{
var result = ColorFormatHelper.ConvertToCIEXYZColor(Color.FromArgb(255, 0, 0, 0));
Assert.AreEqual(0d, result.X, 0.001);
Assert.AreEqual(0d, result.Y, 0.001);
Assert.AreEqual(0d, result.Z, 0.001);
}
[TestMethod]
public void ConvertToCIEXYZ_White_ReturnsD65Illuminant()
{
// White should be close to D65 illuminant: X≈0.9505, Y≈1.0, Z≈1.089
var result = ColorFormatHelper.ConvertToCIEXYZColor(Color.FromArgb(255, 255, 255, 255));
Assert.AreEqual(0.9505d, result.X, 0.02);
Assert.AreEqual(1.0d, result.Y, 0.02);
Assert.AreEqual(1.089d, result.Z, 0.02);
}
[TestMethod]
public void ConvertToCIEXYZ_Red_HasExpectedValues()
{
// Pure red: X≈0.4124, Y≈0.2126, Z≈0.0193
var result = ColorFormatHelper.ConvertToCIEXYZColor(Color.FromArgb(255, 255, 0, 0));
Assert.AreEqual(0.4124d, result.X, 0.02);
Assert.AreEqual(0.2126d, result.Y, 0.02);
Assert.AreEqual(0.0193d, result.Z, 0.02);
}
[TestMethod]
public void ConvertToCIELAB_Black_Returns0_0_0()
{
var result = ColorFormatHelper.ConvertToCIELABColor(Color.FromArgb(255, 0, 0, 0));
Assert.AreEqual(0d, result.Lightness, 0.5);
Assert.AreEqual(0d, result.ChromaticityA, 0.5);
Assert.AreEqual(0d, result.ChromaticityB, 0.5);
}
[TestMethod]
public void ConvertToCIELAB_White_Returns100_0_0()
{
var result = ColorFormatHelper.ConvertToCIELABColor(Color.FromArgb(255, 255, 255, 255));
Assert.AreEqual(100d, result.Lightness, 1.0);
Assert.AreEqual(0d, result.ChromaticityA, 1.0);
Assert.AreEqual(0d, result.ChromaticityB, 1.0);
}
[TestMethod]
public void ConvertToCIELAB_Red_HasPositiveA()
{
// Red is in the +a* direction
var result = ColorFormatHelper.ConvertToCIELABColor(Color.FromArgb(255, 255, 0, 0));
Assert.IsTrue(result.ChromaticityA > 0, "Red should have positive a* in CIE LAB");
}
[TestMethod]
public void ConvertToCIELAB_Blue_HasNegativeB()
{
// Blue is in the -b* direction
var result = ColorFormatHelper.ConvertToCIELABColor(Color.FromArgb(255, 0, 0, 255));
Assert.IsTrue(result.ChromaticityB < 0, "Blue should have negative b* in CIE LAB");
}
[TestMethod]
public void ConvertToOklab_Black_Returns0_0_0()
{
var result = ColorFormatHelper.ConvertToOklabColor(Color.FromArgb(255, 0, 0, 0));
Assert.AreEqual(0d, result.Lightness, 0.01);
Assert.AreEqual(0d, result.ChromaticityA, 0.01);
Assert.AreEqual(0d, result.ChromaticityB, 0.01);
}
[TestMethod]
public void ConvertToOklab_White_Returns1_0_0()
{
var result = ColorFormatHelper.ConvertToOklabColor(Color.FromArgb(255, 255, 255, 255));
Assert.AreEqual(1d, result.Lightness, 0.02);
Assert.AreEqual(0d, result.ChromaticityA, 0.02);
Assert.AreEqual(0d, result.ChromaticityB, 0.02);
}
[TestMethod]
public void ConvertToOklch_Black_Returns0_0_0()
{
var result = ColorFormatHelper.ConvertToOklchColor(Color.FromArgb(255, 0, 0, 0));
Assert.AreEqual(0d, result.Lightness, 0.01);
Assert.AreEqual(0d, result.Chroma, 0.01);
}
[TestMethod]
public void ConvertToOklch_White_Returns1_0_Any()
{
var result = ColorFormatHelper.ConvertToOklchColor(Color.FromArgb(255, 255, 255, 255));
Assert.AreEqual(1d, result.Lightness, 0.02);
Assert.AreEqual(0d, result.Chroma, 0.02);
// Hue is undefined for achromatic colors, so we don't assert it
}
[TestMethod]
public void ConvertToOklch_Chroma_IsNonNegative()
{
// Chroma should always be non-negative
var colors = new[] { Color.Red, Color.Green, Color.Blue, Color.Yellow, Color.Cyan, Color.Magenta };
foreach (var color in colors)
{
var result = ColorFormatHelper.ConvertToOklchColor(color);
Assert.IsTrue(result.Chroma >= 0, $"Chroma should be non-negative for {color.Name}");
}
}
[TestMethod]
public void ConvertSRGBToLinear_Zero_ReturnsZero()
{
var result = ColorFormatHelper.ConvertSRGBToLinearRGB(0, 0, 0);
Assert.AreEqual(0d, result.R, 0.001);
Assert.AreEqual(0d, result.G, 0.001);
Assert.AreEqual(0d, result.B, 0.001);
}
[TestMethod]
public void ConvertSRGBToLinear_One_ReturnsOne()
{
var result = ColorFormatHelper.ConvertSRGBToLinearRGB(1, 1, 1);
Assert.AreEqual(1d, result.R, 0.001);
Assert.AreEqual(1d, result.G, 0.001);
Assert.AreEqual(1d, result.B, 0.001);
}
[TestMethod]
public void ConvertSRGBToLinear_SmallValues_UsesLinearPath()
{
// For small values (≤ 0.04045), the formula is linear: value / 12.92
var result = ColorFormatHelper.ConvertSRGBToLinearRGB(0.04, 0.04, 0.04);
Assert.AreEqual(0.04 / 12.92, result.R, 0.001);
}
[TestMethod]
public void ConvertSRGBToLinear_LargeValues_UsesGammaPath()
{
// For larger values, the gamma function is applied
// sRGB 0.5 should map to ~0.214
var result = ColorFormatHelper.ConvertSRGBToLinearRGB(0.5, 0.5, 0.5);
Assert.AreEqual(0.214, result.R, 0.01);
}
[TestMethod]
public void ConvertToNaturalColor_Red_ReturnsR0()
{
var result = ColorFormatHelper.ConvertToNaturalColor(Color.FromArgb(255, 255, 0, 0));
Assert.AreEqual("R0", result.Hue);
Assert.AreEqual(0d, result.Whiteness, 0.01);
Assert.AreEqual(0d, result.Blackness, 0.01);
}
[TestMethod]
public void ConvertToNaturalColor_Green_HueStartsWithG()
{
var result = ColorFormatHelper.ConvertToNaturalColor(Color.FromArgb(255, 0, 128, 0));
Assert.IsTrue(result.Hue.StartsWith('G'), $"Green should start with G, got {result.Hue}");
}
[TestMethod]
public void ConvertToNaturalColor_Blue_HueStartsWithB()
{
var result = ColorFormatHelper.ConvertToNaturalColor(Color.FromArgb(255, 0, 0, 255));
Assert.IsTrue(result.Hue.StartsWith('B'), $"Blue should start with B, got {result.Hue}");
}
[TestMethod]
public void ConvertToNaturalColor_Black_Returns0_0_100()
{
var result = ColorFormatHelper.ConvertToNaturalColor(Color.FromArgb(255, 0, 0, 0));
Assert.AreEqual(0d, result.Whiteness, 0.01);
Assert.AreEqual(1d, result.Blackness, 0.01);
}
[TestMethod]
public void ConvertToNaturalColor_White_Returns0_100_0()
{
var result = ColorFormatHelper.ConvertToNaturalColor(Color.FromArgb(255, 255, 255, 255));
Assert.AreEqual(1d, result.Whiteness, 0.01);
Assert.AreEqual(0d, result.Blackness, 0.01);
}
[TestMethod]
[DataRow("CMYK", "cmyk(0%, 100%, 100%, 0%)")]
[DataRow("HEX", "ff0000")]
[DataRow("RGB", "rgb(255, 0, 0)")]
[DataRow("HSL", "hsl(0, 100%, 50%)")]
[DataRow("HSV", "hsv(0, 100%, 100%)")]
[DataRow("HSB", "hsb(0, 100%, 100%)")]
[DataRow("HSI", "hsi(0, 100%, 33%)")]
[DataRow("HWB", "hwb(0, 0%, 0%)")]
[DataRow("Decimal", "255")]
[DataRow("HEX Int", "0xFFFF0000")]
[DataRow("VEC4", "(1f, 0f, 0f, 1f)")]
public void GetStringRepresentation_Red(string type, string expected)
{
var result = ColorRepresentationHelper.GetStringRepresentation(Color.FromArgb(255, 255, 0, 0), type, ColorFormatHelper.GetDefaultFormat(type));
Assert.AreEqual(expected, result);
}
[TestMethod]
[DataRow("CMYK", "cmyk(0%, 0%, 0%, 0%)")]
[DataRow("HEX", "ffffff")]
[DataRow("RGB", "rgb(255, 255, 255)")]
[DataRow("HSL", "hsl(0, 0%, 100%)")]
[DataRow("HSV", "hsv(0, 0%, 100%)")]
[DataRow("Decimal", "16777215")]
[DataRow("HEX Int", "0xFFFFFFFF")]
[DataRow("VEC4", "(1f, 1f, 1f, 1f)")]
public void GetStringRepresentation_White(string type, string expected)
{
var result = ColorRepresentationHelper.GetStringRepresentation(Color.FromArgb(255, 255, 255, 255), type, ColorFormatHelper.GetDefaultFormat(type));
Assert.AreEqual(expected, result);
}
[TestMethod]
[DataRow("CMYK", "cmyk(100%, 0%, 100%, 0%)")]
[DataRow("HEX", "00ff00")]
[DataRow("RGB", "rgb(0, 255, 0)")]
[DataRow("HSL", "hsl(120, 100%, 50%)")]
[DataRow("HSV", "hsv(120, 100%, 100%)")]
[DataRow("Decimal", "65280")]
[DataRow("HEX Int", "0xFF00FF00")]
public void GetStringRepresentation_Green(string type, string expected)
{
var result = ColorRepresentationHelper.GetStringRepresentation(Color.FromArgb(255, 0, 255, 0), type, ColorFormatHelper.GetDefaultFormat(type));
Assert.AreEqual(expected, result);
}
[TestMethod]
[DataRow("CMYK", "cmyk(100%, 100%, 0%, 0%)")]
[DataRow("HEX", "0000ff")]
[DataRow("RGB", "rgb(0, 0, 255)")]
[DataRow("HSL", "hsl(240, 100%, 50%)")]
[DataRow("HSV", "hsv(240, 100%, 100%)")]
[DataRow("Decimal", "16711680")]
[DataRow("HEX Int", "0xFF0000FF")]
public void GetStringRepresentation_Blue(string type, string expected)
{
var result = ColorRepresentationHelper.GetStringRepresentation(Color.FromArgb(255, 0, 0, 255), type, ColorFormatHelper.GetDefaultFormat(type));
Assert.AreEqual(expected, result);
}
[TestMethod]
public void GetStringRepresentation_EmptyFormat_ReturnsHex()
{
// When colorFormat is null or empty, should return hex
var result = ColorRepresentationHelper.GetStringRepresentation(Color.FromArgb(255, 255, 0, 0), "RGB", string.Empty);
Assert.AreEqual("ff0000", result);
}
[TestMethod]
public void GetStringRepresentation_NullFormat_ReturnsHex()
{
var result = ColorRepresentationHelper.GetStringRepresentation(Color.FromArgb(255, 255, 0, 0), "RGB", null);
Assert.AreEqual("ff0000", result);
}
[TestMethod]
[DataRow("RGB")]
[DataRow("HEX")]
[DataRow("CMYK")]
[DataRow("HSL")]
[DataRow("HSV")]
[DataRow("HSB")]
[DataRow("HSI")]
[DataRow("HWB")]
[DataRow("NCol")]
[DataRow("CIEXYZ")]
[DataRow("CIELAB")]
[DataRow("Oklab")]
[DataRow("Oklch")]
[DataRow("VEC4")]
[DataRow("Decimal")]
[DataRow("HEX Int")]
public void GetDefaultFormat_KnownTypes_ReturnsNonEmptyString(string formatName)
{
var result = ColorFormatHelper.GetDefaultFormat(formatName);
Assert.IsFalse(string.IsNullOrEmpty(result), $"Default format for {formatName} should not be empty");
}
}
}

View File

@@ -7,7 +7,9 @@ using System.Globalization;
using System.Text;
using ImageResizer.Cli;
using ImageResizer.Cli.Telemetry;
using ManagedCommon;
using Microsoft.PowerToys.Telemetry;
namespace ImageResizerCLI;
@@ -37,14 +39,33 @@ internal static class Program
try
{
var executor = new ImageResizerCliExecutor();
return executor.Run(args);
int result = executor.Run(args);
LogCLITelemetry(executor.CommandName, result == 0);
return result;
}
catch (Exception ex)
{
CliLogger.Error($"Unhandled exception: {ex.Message}");
CliLogger.Error($"Stack trace: {ex.StackTrace}");
Console.Error.WriteLine($"Fatal error: {ex.Message}");
LogCLITelemetry("resize", successful: false);
return 1;
}
}
private static void LogCLITelemetry(string commandName, bool successful)
{
try
{
PowerToysTelemetry.Log.WriteEvent(new ImageResizerCLICommandEvent
{
CommandName = commandName,
Successful = successful,
});
}
catch (Exception ex)
{
CliLogger.Error($"Failed to log CLI telemetry: {ex.Message}");
}
}
}

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