Compare commits

...

15 Commits

Author SHA1 Message Date
Muyuan Li (from Dev Box)
f9679b937d Address review: handle NavigationFailed gracefully without rethrowing
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-14 16:20:42 +08:00
copilot-swe-agent[bot]
36300d3c75 Fix NullReferenceException in Frame_NavigationFailed when e.Exception is null
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/af982aa1-504b-47f1-9ec0-93b29602b2af

Co-authored-by: MuyuanMS <116717757+MuyuanMS@users.noreply.github.com>
2026-04-29 08:49:32 +00:00
copilot-swe-agent[bot]
1cde68ae04 Initial plan 2026-04-29 08:48:31 +00:00
moooyo
e6d346a59b [PowerDisplay] Default-off and confirm dialog for InputSource/ColorTemp/PowerState (#47303)
<!-- 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
Three per-monitor PowerDisplay features have failure modes recoverable
only via physical buttons:

Input Source — switching to an input with no signal goes black;
PowerToys can no longer drive a panel that isn't displaying its own
signal
Color Temperature — some monitors apply changes that cannot be reset via
DDC/CI; OSD reset required
Power State — VCP 0xD6 standby may not respond to subsequent DDC/CI wake
commands
This PR:

Defaults all three features off for newly-discovered monitors. Supports*
derivation from VCP capabilities is unchanged — unsupported checkboxes
are still greyed out.
Pops a confirmation dialog when a user toggles any of them on in
Settings, mirroring the existing Color Temperature warning. Cancel
reverts the checkbox; Enable keeps it.
Refactors the existing Color Temperature click handler into a shared
HandleDangerousFeatureClickAsync(sender, resourceKeyPrefix, setter)
helper. All three feature handlers now reuse it.
Renames PowerDisplay_ColorTemperature_EnableButton →
PowerDisplay_Dialog_Enable so the three dialogs share one button-text
resource (paralleling the existing shared PowerDisplay_Dialog_Cancel)

<!-- 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.7 (1M context) <noreply@anthropic.com>
2026-04-29 13:35:12 +08:00
moooyo
2aece74831 [PowerDisplay] Use localized "Built-in Display" name for internal display (#47321)
Set all WMI monitor's name to "Built-in Display" to avoid confusing.

related discussion: #47255

<!-- 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: #
<!-- - [ ] 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>
Co-authored-by: Niels Laute <niels.laute@live.nl>
2026-04-29 13:34:34 +08:00
Alex Mihaiuc
7861bc408c Replay both key down and up for Win in GrabAndMove (#47326)
<!-- 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
The code for keyup on the `Win` key would replay the previously absorbed
keydown event and just leave this keyup to propagate normally, thus
leading to a race condition between `CallNextHookEx` and `SendInput`.
This resulted in almost guaranteed out-of-order event (`Win` up,
followed by `Win` down) in the case of `Win+G` (the Xbox Game bar
shortcut).

Fixed by also absorbing the keyup for `Win`, but calling `SendInput` for
both keydown and keyup.

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

- [x] Closes: #47293 
<!-- - [ ] 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
- [ ] **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-29 12:04:20 +08:00
Mike Griese
b835cde4d2 CmdPal: Fix a bug where dock label settings wouldn't save (#47317)
This setting is totally vestigial, from the 0.9 dev cycle. 

Unfortunately, the JSON parser would see that it wasn't in the
settings.json, then it would write `null` to it. But `ShowLabels` was
just a thin alias for `ShowTitles`, so we'd end up parsing totally sane
JSON settings into having `null` for `ShowTitle`.

This fixes that. You can hide your titles again folks.
2026-04-29 11:12:43 +08:00
Alex Mihaiuc
f79df0663c Skip desktop / explorer targets in GrabAndMove (#47302)
<!-- 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
Added a few known Windows processes and window classes from desktop
elements to the implicit exclusion list, to avoid funny repositioning of
otherwise immovable content:

- The Windows Start menu.
- Tooltips from around the Notification Area (System Tray).
- The Alt-Tab and Win-Tab windows.
- Tooltips.

<!-- 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
- [ ] **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 <copilot@github.com>
2026-04-29 11:05:54 +08:00
Mike Griese
7211f7ed67 cmdpal: fix our settings load crash (#47296)
Fixes a category of crashes in CmdPal, related to our settings parsing. 

It would seem that I don't understand JSON parsing all that well in C#. 

Basically, we've got a bunch of places where we have
 
```c#
class Foo
{
  public Bar MySetting {get;init;} = new()
}
```

but when we JSON deserialize the settings, if there wasn't a
`"MySetting"` key, then the deserializer deserializes to `null`, not
`new()`. but since `Foo.MySetting` isn't a `Bar?`, then the compiler
can't check the fact that json _gets special rules to write a null to
it????_

Closes #47249

tested with the settings.json from that thread.
2026-04-28 12:24:27 -05:00
Alex Mihaiuc
215dfaf236 GrabAndMove release Alt key on other keypresses (#47261)
<!-- 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
When the Alt modifier is already pressed, "release" it on any other
interaction but the allowed mouse interactions.

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

- [x] Closes: #47257 
<!-- - [ ] 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
I ensured that pressing something like Shift, Ctrl, or even a character
key - besides Tab, of course, properly resets the internal state of Alt.
The win key was never affected by this behavior.

Co-authored-by: Copilot <copilot@github.com>
2026-04-28 17:40:00 +02:00
moooyo
f5a294bb66 [PowerDisplay] Add more log for PowerDisplay (#47270)
Users have reported missing monitors but the existing logs lack the
information needed to diagnose. Add discovery-phase logs that record the
raw MCCS capabilities string from the monitor, the parsed feature
support flags (brightness/contrast/color temperature/volume), the
specific reason a monitor is ignored, and DDC/CI API failures.

<!-- 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.7 (1M context) <noreply@anthropic.com>
2026-04-28 10:09:07 +00:00
Niels Laute
d10203b8ac Update README for PowerToys v0.99.0 release (#47227)
<!-- 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
2026-04-28 14:01:18 +08:00
Niels Laute
fecd2e72a7 Handle DockSize enum changes (#47212)
### ⚠️ REVIEWERS TO DECIDE. MERGE THIS PR OR GO FOR: #47214⚠️

### Root cause

PR #46699 (compact mode) replaced the original `DockSize` enum
(`Small`/`Medium`/`Large`) with a new one (Default/Compact). Existing
users have `DockSize`: `Small` (or `Medium`/`Large`) in their
`settings.json`, which the source-generated EnumConverter can't parse →
the entire settings file fails to load.

### Fix

Added a custom `JsonConverter<DockSize>` in
_src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Settings/DockSettings.cs_
and applied it via `[JsonConverter]` on the enum.

The converter:

- Accepts current values (`Default`, `Compact`) — case-insensitive.
- Maps any unknown legacy string (e.g. `Small`/`Medium`/`Large`) to
`DockSize.Default`, preserving the user's existing visual experience.
- Tolerates numeric values too, falling back to `Default` if out of
range.
- Writes as a string so output stays consistent with the source-gen
`UseStringEnumConverter` setting.
2026-04-26 06:35:47 -05:00
Niels Laute
fde1599f7d [Grab And Move] Add touchpad compatibility InfoBar to settings page (#47213)
## Summary

Adds an informational InfoBar at the top of the Grab And Move settings
page noting that the utility may not work with some touchpads and is
intended to be used with a physical mouse device.

<img width="1180" height="460" alt="image"
src="https://github.com/user-attachments/assets/4999d2f5-c785-41ab-b5fc-9dd8b7e72c7d"
/>


## Changes

- `src/settings-ui/Settings.UI/SettingsXAML/Views/GrabAndMovePage.xaml`
— new `InfoBar` (Severity=Informational, IsClosable=False) placed at the
top of the page's `StackPanel`.
- `src/settings-ui/Settings.UI/Strings/en-us/Resources.resw` — added two
resource strings:
  - `GrabAndMove_TouchpadInfoBar.Title` — "Touchpad compatibility"
  - `GrabAndMove_TouchpadInfoBar.Message` — explanatory text.

Follows the existing InfoBar pattern (e.g., `MouseUtilsPage.xaml`).

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-25 21:25:10 +02:00
Alex Mihaiuc
c68003c678 Avoid MessageBox in ZoomIt in PowerToys (#47215)
<!-- 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 stops the ZoomIt message boxes complaining about keyboard shortcut
conflicts - such conflicts are handled by the PowerToys settings window.

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

- [x] Closes: #47154
<!-- - [ ] 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
- [ ] **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
Setting any ZoomIt shortcut to the same sequence as other shortcuts
won't produce the MessageBox anymore.

The easiest is to use the default shortcuts and to bind the last one,
**Panorama activation** to `Ctrl+7` (instead of the default `Ctrl+8`).
`Ctrl+7` clashes with the ZoomIt **DemoType activation** shortcut. To
make the MessageBox pop-out before the fix, takes pressing a few
`Ctrl+1`s (the default **Zoom activation**).

---------

Co-authored-by: Copilot <copilot@github.com>
2026-04-25 21:23:21 +02:00
26 changed files with 492 additions and 230 deletions

View File

@@ -330,7 +330,9 @@ MRUINFO
REGSTR
# Misc Win32 APIs and PInvokes
DEFAULTTONEAREST
INVOKEIDLIST
LCMAP
MEMORYSTATUSEX
ABE
Mdt
@@ -394,3 +396,10 @@ Nonpaged
# XAML
Untargeted
# Program names
SEARCHHOST
SHELLEXPERIENCEHOST
SHELLHOST
STARTMENUEXPERIENCEHOST
WIDGETBOARD

View File

@@ -365,6 +365,7 @@ defaultlib
DEFAULTONLY
DEFAULTSIZE
defaulttonearest
DEFAULTTONEAREST
DEFAULTTONULL
DEFAULTTOPRIMARY
DEFERERASE
@@ -1367,6 +1368,7 @@ POINTERUPDATE
Pokedex
Pomodoro
Popups
popups
POPUPWINDOW
POSITIONITEM
POWERBROADCAST
@@ -1882,6 +1884,7 @@ toolwindow
TOPDOWNDIB
TOUCHEVENTF
TOUCHINPUT
touchpads
TPMLEFTALIGN
TPMRETURNCMD
TRACEHANDLE

View File

@@ -50,18 +50,18 @@ But to get started quickly, choose one of the installation methods below:
Go to the [PowerToys GitHub releases](https://aka.ms/installPowerToys), select **Assets** to reveal the installation files, and choose the one that matches your architecture and install scope. For most devices, that would be _x64 per-user_.
<!-- items that need to be updated release to release -->
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.99%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysUserSetup-0.98.1-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysUserSetup-0.98.1-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysSetup-0.98.1-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysSetup-0.98.1-arm64.exe
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.100%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.99.0/PowerToysUserSetup-0.99.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.99.0/PowerToysUserSetup-0.99.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.99.0/PowerToysSetup-0.99.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.99.0/PowerToysSetup-0.99.0-arm64.exe
| Description | Filename |
| --- | --- |
| Per user - x64 | [PowerToysUserSetup-0.98.1-x64.exe][ptUserX64] |
| Per user - ARM64 | [PowerToysUserSetup-0.98.1-arm64.exe][ptUserArm64] |
| Machine wide - x64 | [PowerToysSetup-0.98.1-x64.exe][ptMachineX64] |
| Machine wide - ARM64 | [PowerToysSetup-0.98.1-arm64.exe][ptMachineArm64] |
| Per user - x64 | [PowerToysUserSetup-0.99.0-x64.exe][ptUserX64] |
| Per user - ARM64 | [PowerToysUserSetup-0.99.0-arm64.exe][ptUserArm64] |
| Machine wide - x64 | [PowerToysSetup-0.99.0-x64.exe][ptMachineX64] |
| Machine wide - ARM64 | [PowerToysSetup-0.99.0-arm64.exe][ptMachineArm64] |
</details>
@@ -106,11 +106,11 @@ There are [community driven install methods](https://learn.microsoft.com/windows
[![What's new image](doc/images/readme/Release-Banner.png)](https://github.com/microsoft/PowerToys/releases)
To see what's new, check out the [release notes](https://github.com/microsoft/PowerToys/releases/tag/v0.98.1).
To see what's new, check out the [release notes](https://github.com/microsoft/PowerToys/releases/tag/v0.99.0).
## 🛣️ Roadmap
We are planning some nice new features and improvements for the next releases PowerDisplay, Command Palette improvements and a brand-new Shortcut Guide experience! Stay tuned for [v0.99][github-next-release-work]!
We are planning some nice new features and improvements for the next releases a brand-new Shortcut Guide experience, ensuring it's easier to find and install Command Palette extensions and so much more! Stay tuned for [v0.100][github-next-release-work]!
## ❤️ PowerToys Community

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 258 KiB

View File

@@ -579,14 +579,16 @@ static void StopResizing()
HideOverlay();
}
static void ReplayAbsorbedAlt()
static void ReplayAbsorbedModifier(bool alsoKeyUp)
{
INPUT keyboardInput = {};
keyboardInput.type = INPUT_KEYBOARD;
keyboardInput.ki.wVk = static_cast<WORD>(g_absorbedVk);
keyboardInput.ki.wScan = static_cast<WORD>(g_absorbedScanCode);
keyboardInput.ki.dwFlags = (g_absorbedFlags & LLKHF_EXTENDED) ? KEYEVENTF_EXTENDEDKEY : 0;
SendInput(1, &keyboardInput, sizeof(INPUT));
INPUT inputs[2] = {};
inputs[0].type = INPUT_KEYBOARD;
inputs[0].ki.wVk = static_cast<WORD>(g_absorbedVk);
inputs[0].ki.wScan = static_cast<WORD>(g_absorbedScanCode);
inputs[0].ki.dwFlags = (g_absorbedFlags & LLKHF_EXTENDED) ? KEYEVENTF_EXTENDEDKEY : 0;
inputs[1] = inputs[0];
inputs[1].ki.dwFlags |= KEYEVENTF_KEYUP;
SendInput(alsoKeyUp ? 2 : 1, inputs, sizeof(INPUT));
}
// ---------------------------------------------------------------------------
@@ -632,9 +634,65 @@ static void ShowTrayMenu(HWND hwnd)
static bool IsSystemClass(HWND hwnd)
{
wchar_t cls[64] = {};
GetClassNameW(hwnd, cls, 64);
return (wcscmp(cls, L"Progman") == 0 || wcscmp(cls, L"Shell_TrayWnd") == 0);
wchar_t cls[256] = {};
GetClassNameW(hwnd, cls, ARRAYSIZE(cls));
// Desktop and primary/secondary taskbars
if (wcscmp(cls, L"Progman") == 0 ||
wcscmp(cls, L"Shell_TrayWnd") == 0 ||
wcscmp(cls, L"Shell_SecondaryTrayWnd") == 0)
return true;
// System tray / notification area popups and overflow
if (wcscmp(cls, L"NotifyIconOverflowWindow") == 0 ||
wcscmp(cls, L"TopLevelWindowForOverflowXamlIsland") == 0)
return true;
// Tooltips (e.g. "Show hidden icons" tooltip)
if (wcscmp(cls, L"tooltips_class32") == 0)
return true;
// Task View (Win+Tab)
if (wcscmp(cls, L"MultitaskingViewFrame") == 0 ||
wcscmp(cls, L"XamlExplorerHostIslandWindow") == 0)
return true;
// System tray flyouts (Quick Settings, calendar, input switcher)
if (wcscmp(cls, L"Windows.UI.Composition.DesktopWindowContentBridge") == 0 ||
wcscmp(cls, L"Shell_InputSwitchTopLevelWindow") == 0)
return true;
return false;
}
std::wstring ToUpperInvariant(std::wstring_view input)
{
int required = LCMapStringEx(
LOCALE_NAME_INVARIANT,
LCMAP_UPPERCASE,
input.data(),
static_cast<int>(input.size()),
nullptr,
0,
nullptr,
nullptr,
0);
std::wstring result(required, L'\0');
LCMapStringEx(
LOCALE_NAME_INVARIANT,
LCMAP_UPPERCASE,
input.data(),
static_cast<int>(input.size()),
result.data(),
required,
nullptr,
nullptr,
0);
return result;
}
static bool IsExcluded(HWND hwnd)
@@ -642,6 +700,45 @@ static bool IsExcluded(HWND hwnd)
if (IsSystemClass(hwnd))
return true;
// To identify these for adding a new exception:
// 1. Resolve the hwnd class name.
// 2. Resolve the process path.
// 3. Add OutputDebugStringW() for the class name and process path.
// 4. Build the executable.
// 5. Check with the debugger (or with Sysinternals DebugView) the outputs.
// 6. Delete the added code.
// 7. Add the exception below, according to the pattern there.
//
// Shell experience windows: Start menu, Notifications (Win+N), Search,
// Quick Settings (volume / network / battery).
// These use the generic Windows.UI.Core.CoreWindow class, so filter by process.
{
wchar_t cls[256] = {};
GetClassNameW(hwnd, cls, ARRAYSIZE(cls));
if (wcscmp(cls, L"Windows.UI.Core.CoreWindow") == 0)
{
std::wstring processPath = ToUpperInvariant(get_process_path(hwnd));
if (processPath.find(L"STARTMENUEXPERIENCEHOST.EXE") != std::wstring::npos ||
processPath.find(L"SHELLEXPERIENCEHOST.EXE") != std::wstring::npos ||
processPath.find(L"SEARCHHOST.EXE") != std::wstring::npos)
return true;
}
else if (wcscmp(cls, L"ControlCenterWindow") == 0)
{
// The Quick Settings flyout.
std::wstring processPath = ToUpperInvariant(get_process_path(hwnd));
if (processPath.find(L"SHELLHOST.EXE") != std::wstring::npos)
return true;
}
else if (wcscmp(cls, L"WindowsDashboard") == 0)
{
// The Windows 11 Widgets flyout.
std::wstring processPath = ToUpperInvariant(get_process_path(hwnd));
if (processPath.find(L"WIDGETBOARD.EXE") != std::wstring::npos)
return true;
}
}
auto apps = g_excludedApps.load();
if (!apps || apps->empty())
return false;
@@ -735,8 +832,9 @@ static LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
g_dragConsumedAlt = false;
return 1;
}
// No drag happened; replay the keydown, then let keyup through
ReplayAbsorbedAlt();
// No drag happened; replay the keydown, THEN the keyup
ReplayAbsorbedModifier(true);
return 1; // swallow this keyup since the replay already sent one
}
}
goto forward; // let Win keyup pass through
@@ -793,7 +891,7 @@ static LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
return 1;
}
// No drag happened; replay the keydown, then let keyup through
ReplayAbsorbedAlt();
ReplayAbsorbedModifier(false);
}
}
}
@@ -803,7 +901,8 @@ static LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
if (g_altAbsorbed && !g_dragConsumedAlt)
{
g_altAbsorbed = false;
ReplayAbsorbedAlt();
g_altPressed = false;
ReplayAbsorbedModifier(false);
}
}
}
@@ -813,7 +912,7 @@ static LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
g_winAbsorbed = false;
g_winPressed = false;
ReplayAbsorbedAlt();
ReplayAbsorbedModifier(false);
}
// Track held non-modifier keys (used to suppress GrabAndMove when the modifier

View File

@@ -9784,7 +9784,10 @@ LRESULT APIENTRY MainWndProc(
{
if (!RegisterHotKey(hWnd, ZOOM_HOTKEY, g_ToggleMod, g_ToggleKey & 0xFF))
{
MessageBox(hWnd, L"The specified zoom toggle hotkey is already in use.\nSelect a different zoom toggle hotkey.", APPNAME, MB_ICONERROR);
if(!g_StartedByPowerToys)
{
MessageBox(hWnd, L"The specified zoom toggle hotkey is already in use.\nSelect a different zoom toggle hotkey.", APPNAME, MB_ICONERROR);
}
showOptions = TRUE;
}
}
@@ -9793,7 +9796,10 @@ LRESULT APIENTRY MainWndProc(
if (!RegisterHotKey(hWnd, LIVE_HOTKEY, g_LiveZoomToggleMod, g_LiveZoomToggleKey & 0xFF) ||
!RegisterHotKey(hWnd, LIVE_DRAW_HOTKEY, g_LiveZoomToggleMod ^ MOD_SHIFT, g_LiveZoomToggleKey & 0xFF))
{
MessageBox(hWnd, L"The specified live-zoom toggle hotkey is already in use.\nSelect a different zoom toggle hotkey.", APPNAME, MB_ICONERROR);
if(!g_StartedByPowerToys)
{
MessageBox(hWnd, L"The specified live-zoom toggle hotkey is already in use.\nSelect a different zoom toggle hotkey.", APPNAME, MB_ICONERROR);
}
showOptions = TRUE;
}
}
@@ -9801,7 +9807,10 @@ LRESULT APIENTRY MainWndProc(
{
if (!RegisterHotKey(hWnd, DRAW_HOTKEY, g_DrawToggleMod, g_DrawToggleKey & 0xFF))
{
MessageBox(hWnd, L"The specified draw w/out zoom hotkey is already in use.\nSelect a different draw w/out zoom hotkey.", APPNAME, MB_ICONERROR);
if(!g_StartedByPowerToys)
{
MessageBox(hWnd, L"The specified draw w/out zoom hotkey is already in use.\nSelect a different draw w/out zoom hotkey.", APPNAME, MB_ICONERROR);
}
showOptions = TRUE;
}
}
@@ -9809,7 +9818,10 @@ LRESULT APIENTRY MainWndProc(
{
if (!RegisterHotKey(hWnd, BREAK_HOTKEY, g_BreakToggleMod, g_BreakToggleKey & 0xFF))
{
MessageBox(hWnd, L"The specified break timer hotkey is already in use.\nSelect a different break timer hotkey.", APPNAME, MB_ICONERROR);
if(!g_StartedByPowerToys)
{
MessageBox(hWnd, L"The specified break timer hotkey is already in use.\nSelect a different break timer hotkey.", APPNAME, MB_ICONERROR);
}
showOptions = TRUE;
}
}
@@ -9818,7 +9830,10 @@ LRESULT APIENTRY MainWndProc(
if (!RegisterHotKey(hWnd, DEMOTYPE_HOTKEY, g_DemoTypeToggleMod, g_DemoTypeToggleKey & 0xFF) ||
!RegisterHotKey(hWnd, DEMOTYPE_RESET_HOTKEY, (g_DemoTypeToggleMod ^ MOD_SHIFT), g_DemoTypeToggleKey & 0xFF))
{
MessageBox(hWnd, L"The specified live-type hotkey is already in use.\nSelect a different live-type hotkey.", APPNAME, MB_ICONERROR);
if(!g_StartedByPowerToys)
{
MessageBox(hWnd, L"The specified live-type hotkey is already in use.\nSelect a different live-type hotkey.", APPNAME, MB_ICONERROR);
}
showOptions = TRUE;
}
}
@@ -9827,7 +9842,10 @@ LRESULT APIENTRY MainWndProc(
if (!RegisterHotKey(hWnd, SNIP_HOTKEY, g_SnipToggleMod, g_SnipToggleKey & 0xFF) ||
!RegisterHotKey(hWnd, SNIP_SAVE_HOTKEY, (g_SnipToggleMod ^ MOD_SHIFT), g_SnipToggleKey & 0xFF))
{
MessageBox(hWnd, L"The specified snip hotkey is already in use.\nSelect a different snip hotkey.", APPNAME, MB_ICONERROR);
if(!g_StartedByPowerToys)
{
MessageBox(hWnd, L"The specified snip hotkey is already in use.\nSelect a different snip hotkey.", APPNAME, MB_ICONERROR);
}
showOptions = TRUE;
}
}
@@ -9837,7 +9855,10 @@ LRESULT APIENTRY MainWndProc(
if (!RegisterHotKey(hWnd, SNIP_PANORAMA_HOTKEY, g_SnipPanoramaToggleMod | MOD_NOREPEAT, g_SnipPanoramaToggleKey & 0xFF) ||
!RegisterHotKey(hWnd, SNIP_PANORAMA_SAVE_HOTKEY, ( g_SnipPanoramaToggleMod ^ MOD_SHIFT ) | MOD_NOREPEAT, g_SnipPanoramaToggleKey & 0xFF))
{
MessageBox(hWnd, L"The specified panorama snip hotkey is already in use.\nSelect a different panorama snip hotkey.", APPNAME, MB_ICONERROR);
if(!g_StartedByPowerToys)
{
MessageBox(hWnd, L"The specified panorama snip hotkey is already in use.\nSelect a different panorama snip hotkey.", APPNAME, MB_ICONERROR);
}
showOptions = TRUE;
}
}
@@ -9845,7 +9866,10 @@ LRESULT APIENTRY MainWndProc(
{
if (!RegisterHotKey(hWnd, SNIP_OCR_HOTKEY, g_SnipOcrToggleMod, g_SnipOcrToggleKey & 0xFF))
{
MessageBox(hWnd, L"The specified snip OCR hotkey is already in use.\nSelect a different snip OCR hotkey.", APPNAME, MB_ICONERROR);
if(!g_StartedByPowerToys)
{
MessageBox(hWnd, L"The specified snip OCR hotkey is already in use.\nSelect a different snip OCR hotkey.", APPNAME, MB_ICONERROR);
}
showOptions = TRUE;
}
}
@@ -9855,7 +9879,10 @@ LRESULT APIENTRY MainWndProc(
!RegisterHotKey(hWnd, RECORD_CROP_HOTKEY, (g_RecordToggleMod ^ MOD_SHIFT) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF) ||
!RegisterHotKey(hWnd, RECORD_WINDOW_HOTKEY, (g_RecordToggleMod ^ MOD_ALT) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF))
{
MessageBox(hWnd, L"The specified record hotkey is already in use.\nSelect a different record hotkey.", APPNAME, MB_ICONERROR);
if(!g_StartedByPowerToys)
{
MessageBox(hWnd, L"The specified record hotkey is already in use.\nSelect a different record hotkey.", APPNAME, MB_ICONERROR);
}
showOptions = TRUE;
}
}

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using System.Text.Json.Serialization;
namespace Microsoft.CmdPal.UI.ViewModels;
@@ -11,9 +12,21 @@ public record AppStateModel
///////////////////////////////////////////////////////////////////////////
// STATE HERE
// Make sure that any new types you add are added to JsonSerializationContext!
public RecentCommandsManager RecentCommands { get; init; } = new();
private RecentCommandsManager? _recentCommands = new();
public ImmutableList<string> RunHistory { get; init; } = ImmutableList<string>.Empty;
public RecentCommandsManager RecentCommands
{
get => _recentCommands ?? new();
init => _recentCommands = value;
}
private ImmutableList<string>? _runHistory = ImmutableList<string>.Empty;
public ImmutableList<string> RunHistory
{
get => _runHistory ?? ImmutableList<string>.Empty;
init => _runHistory = value;
}
// END SETTINGS
///////////////////////////////////////////////////////////////////////////

View File

@@ -46,45 +46,6 @@ public partial class DockBandSettingsViewModel : ObservableObject
public IconInfoViewModel Icon => _adapter.IconViewModel;
private ShowLabelsOption _showLabels;
public ShowLabelsOption ShowLabels
{
get => _showLabels;
set
{
if (value != _showLabels)
{
_showLabels = value;
var newShowTitles = value switch
{
ShowLabelsOption.Default => (bool?)null,
ShowLabelsOption.ShowLabels => true,
ShowLabelsOption.HideLabels => false,
_ => null,
};
UpdateModel(_dockSettingsModel with { ShowTitles = newShowTitles });
}
}
}
private ShowLabelsOption FetchShowLabels()
{
if (_dockSettingsModel.ShowLabels == null)
{
return ShowLabelsOption.Default;
}
return _dockSettingsModel.ShowLabels.Value ? ShowLabelsOption.ShowLabels : ShowLabelsOption.HideLabels;
}
// used to map to ComboBox selection
public int ShowLabelsIndex
{
get => (int)ShowLabels;
set => ShowLabels = (ShowLabelsOption)value;
}
private DockPinSide PinSide
{
get => _pinSide;
@@ -138,7 +99,6 @@ public partial class DockBandSettingsViewModel : ObservableObject
_bandViewModel = bandViewModel;
_settingsService = settingsService;
_pinSide = FetchPinSide();
_showLabels = FetchShowLabels();
}
private DockPinSide FetchPinSide()

View File

@@ -559,7 +559,7 @@ public sealed partial class DockViewModel
}
// Create settings for the new band
var bandSettings = new DockBandSettings { ProviderId = topLevel.CommandProviderId, CommandId = bandId, ShowLabels = null };
var bandSettings = new DockBandSettings { ProviderId = topLevel.CommandProviderId, CommandId = bandId };
var dockSettings = _settings;
// Create the band view model

View File

@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation
// 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.
@@ -18,12 +18,24 @@ public record ProviderSettings
public bool IsEnabled { get; init; } = true;
public ImmutableDictionary<string, FallbackSettings> FallbackCommands { get; init; }
private ImmutableDictionary<string, FallbackSettings>? _fallbackCommands
= ImmutableDictionary<string, FallbackSettings>.Empty;
public ImmutableList<string> PinnedCommandIds { get; init; }
public ImmutableDictionary<string, FallbackSettings> FallbackCommands
{
get => _fallbackCommands ?? ImmutableDictionary<string, FallbackSettings>.Empty;
init => _fallbackCommands = value;
}
private ImmutableList<string>? _pinnedCommandIds
= ImmutableList<string>.Empty;
public ImmutableList<string> PinnedCommandIds
{
get => _pinnedCommandIds ?? ImmutableList<string>.Empty;
init => _pinnedCommandIds = value;
}
[JsonIgnore]
public string ProviderId { get; init; } = string.Empty;
@@ -37,7 +49,6 @@ public record ProviderSettings
{
}
[JsonConstructor]
public ProviderSettings(bool isEnabled)
{
IsEnabled = isEnabled;

View File

@@ -1,16 +1,20 @@
// Copyright (c) Microsoft Corporation
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using System.Text.Json.Serialization;
namespace Microsoft.CmdPal.UI.ViewModels;
public record RecentCommandsManager : IRecentCommandsManager
{
[JsonInclude]
internal ImmutableList<HistoryItem> History { get; init; } = ImmutableList<HistoryItem>.Empty;
private ImmutableList<HistoryItem>? _history = ImmutableList<HistoryItem>.Empty;
internal ImmutableList<HistoryItem> History
{
get => _history ?? ImmutableList<HistoryItem>.Empty;
init => _history = value;
}
public RecentCommandsManager()
{

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using System.Text.Json;
using System.Text.Json.Serialization;
using Windows.UI;
@@ -44,7 +45,7 @@ public record DockSettings
public string? BackgroundImagePath { get; init; }
// </Theme settings>
public ImmutableList<DockBandSettings> StartBands { get; init; } = ImmutableList.Create(
private ImmutableList<DockBandSettings>? _startBands = ImmutableList.Create(
new DockBandSettings
{
ProviderId = "com.microsoft.cmdpal.builtin.core",
@@ -54,12 +55,24 @@ public record DockSettings
{
ProviderId = "WinGet",
CommandId = "com.microsoft.cmdpal.winget",
ShowLabels = false,
ShowTitles = false,
});
public ImmutableList<DockBandSettings> CenterBands { get; init; } = ImmutableList<DockBandSettings>.Empty;
public ImmutableList<DockBandSettings> StartBands
{
get => _startBands ?? ImmutableList<DockBandSettings>.Empty;
init => _startBands = value;
}
public ImmutableList<DockBandSettings> EndBands { get; init; } = ImmutableList.Create(
private ImmutableList<DockBandSettings>? _centerBands = ImmutableList<DockBandSettings>.Empty;
public ImmutableList<DockBandSettings> CenterBands
{
get => _centerBands ?? ImmutableList<DockBandSettings>.Empty;
init => _centerBands = value;
}
private ImmutableList<DockBandSettings>? _endBands = ImmutableList.Create(
new DockBandSettings
{
ProviderId = "PerformanceMonitor",
@@ -71,6 +84,12 @@ public record DockSettings
CommandId = "com.microsoft.cmdpal.timedate.dockBand",
});
public ImmutableList<DockBandSettings> EndBands
{
get => _endBands ?? ImmutableList<DockBandSettings>.Empty;
init => _endBands = value;
}
public bool ShowLabels { get; init; } = true;
[JsonIgnore]
@@ -102,16 +121,6 @@ public record DockBandSettings
/// </summary>
public bool? ShowSubtitles { get; init; }
/// <summary>
/// Gets a value for backward compatibility. Maps to ShowTitles.
/// </summary>
[System.Text.Json.Serialization.JsonIgnore]
public bool? ShowLabels
{
get => ShowTitles;
init => ShowTitles = value;
}
/// <summary>
/// Resolves the effective value of <see cref="ShowTitles"/> for this band.
/// If this band doesn't have a specific value set, we'll fall back to the
@@ -135,12 +144,52 @@ public enum DockSide
Bottom = 3,
}
[JsonConverter(typeof(DockSizeJsonConverter))]
public enum DockSize
{
Default,
Compact,
}
/// <summary>
/// Custom converter for <see cref="DockSize"/> that preserves backward
/// compatibility with previously-persisted values. Earlier builds shipped a
/// <c>Small</c>/<c>Medium</c>/<c>Large</c> enum; those values are migrated to
/// <see cref="DockSize.Default"/> so existing settings.json files continue to
/// load instead of failing the entire deserialization.
/// </summary>
internal sealed class DockSizeJsonConverter : JsonConverter<DockSize>
{
public override DockSize Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
{
var value = reader.GetString();
if (Enum.TryParse<DockSize>(value, ignoreCase: true, out var parsed))
{
return parsed;
}
// Legacy values from the original Small/Medium/Large enum, or any
// other unknown string — fall back to Default so the user's
// settings file remains loadable after upgrading.
return DockSize.Default;
}
if (reader.TokenType == JsonTokenType.Number && reader.TryGetInt32(out var number))
{
return Enum.IsDefined(typeof(DockSize), number) ? (DockSize)number : DockSize.Default;
}
return DockSize.Default;
}
public override void Write(Utf8JsonWriter writer, DockSize value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
public enum DockBackdrop
{
Transparent,

View File

@@ -55,8 +55,14 @@ public record HotkeySettings// : ICmdLineRepresentable
// This is currently needed for FancyZones, we need to unify these two objects
// see src\common\settings_objects.h
private string? _key = string.Empty;
[JsonPropertyName("key")]
public string Key { get; init; } = string.Empty;
public string Key
{
get => _key ?? string.Empty;
init => _key = value;
}
public override string ToString()
{

View File

@@ -39,17 +39,41 @@ public record SettingsModel
public bool AllowExternalReload { get; init; }
public ImmutableDictionary<string, ProviderSettings> ProviderSettings { get; init; }
private ImmutableDictionary<string, ProviderSettings>? _providerSettings
= ImmutableDictionary<string, ProviderSettings>.Empty;
public string[] FallbackRanks { get; init; } = [];
public ImmutableDictionary<string, ProviderSettings> ProviderSettings
{
get => _providerSettings ?? ImmutableDictionary<string, ProviderSettings>.Empty;
init => _providerSettings = value;
}
public ImmutableDictionary<string, CommandAlias> Aliases { get; init; }
private string[]? _fallbackRanks = [];
public string[] FallbackRanks
{
get => _fallbackRanks ?? [];
init => _fallbackRanks = value;
}
private ImmutableDictionary<string, CommandAlias>? _aliases
= ImmutableDictionary<string, CommandAlias>.Empty;
public ImmutableList<TopLevelHotkey> CommandHotkeys { get; init; }
public ImmutableDictionary<string, CommandAlias> Aliases
{
get => _aliases ?? ImmutableDictionary<string, CommandAlias>.Empty;
init => _aliases = value;
}
private ImmutableList<TopLevelHotkey>? _commandHotkeys
= ImmutableList<TopLevelHotkey>.Empty;
public ImmutableList<TopLevelHotkey> CommandHotkeys
{
get => _commandHotkeys ?? ImmutableList<TopLevelHotkey>.Empty;
init => _commandHotkeys = value;
}
public MonitorBehavior SummonOn { get; init; } = MonitorBehavior.ToMouse;
public bool DisableAnimations { get; init; } = true;
@@ -62,7 +86,13 @@ public record SettingsModel
public bool EnableDock { get; init; }
public DockSettings DockSettings { get; init; } = new();
private DockSettings? _dockSettings = new();
public DockSettings DockSettings
{
get => _dockSettings ?? new();
init => _dockSettings = value;
}
// Theme settings
public UserTheme Theme { get; init; } = UserTheme.Default;

View File

@@ -196,7 +196,7 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IEx
{
ProviderId = this.CommandProviderId,
CommandId = this.Id,
ShowLabels = true,
ShowTitles = true,
};
}

View File

@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using ManagedCommon;
using Windows.Win32.Foundation;
using static PowerDisplay.Common.Drivers.NativeConstants;
using static PowerDisplay.Common.Drivers.PInvoke;
@@ -27,32 +28,55 @@ namespace PowerDisplay.Common.Drivers.DDC
{
if (hPhysicalMonitor == IntPtr.Zero)
{
Logger.LogWarning("DDC: Monitor ignored - null physical monitor handle");
return DdcCiValidationResult.Invalid;
}
var handleHex = $"0x{hPhysicalMonitor:X}";
try
{
// Try to get capabilities string (slow I2C operation)
var capsString = TryGetCapabilitiesString(hPhysicalMonitor);
if (string.IsNullOrEmpty(capsString))
{
Logger.LogWarning($"DDC: Monitor ignored (handle={handleHex}) - empty capabilities string from DDC/CI");
return DdcCiValidationResult.Invalid;
}
Logger.LogInfo($"DDC: Capabilities raw (handle={handleHex}, length={capsString.Length}): {capsString}");
// Parse the capabilities string
var parseResult = Utils.MccsCapabilitiesParser.Parse(capsString);
var capabilities = parseResult.Capabilities;
if (capabilities == null || capabilities.SupportedVcpCodes.Count == 0)
{
Logger.LogWarning($"DDC: Monitor ignored (handle={handleHex}) - parsed capabilities have no VCP codes (parseErrors={parseResult.Errors.Count})");
return DdcCiValidationResult.Invalid;
}
// Check if brightness (VCP 0x10) is supported - determines DDC/CI validity
bool supportsBrightness = capabilities.SupportsVcpCode(NativeConstants.VcpCodeBrightness);
bool supportsContrast = capabilities.SupportsVcpCode(NativeConstants.VcpCodeContrast);
bool supportsColorTemperature = capabilities.SupportsVcpCode(NativeConstants.VcpCodeSelectColorPreset);
bool supportsVolume = capabilities.SupportsVcpCode(NativeConstants.VcpCodeVolume);
Logger.LogInfo(
$"DDC: Capabilities parsed (handle={handleHex}) - " +
$"Brightness={supportsBrightness} Contrast={supportsContrast} " +
$"ColorTemperature={supportsColorTemperature} Volume={supportsVolume}");
if (!supportsBrightness)
{
Logger.LogWarning($"DDC: Monitor ignored (handle={handleHex}) - brightness (VCP 0x10) not advertised in capabilities");
}
return new DdcCiValidationResult(supportsBrightness, capsString, capabilities);
}
catch (Exception ex) when (ex is not OutOfMemoryException)
{
Logger.LogError($"DDC: Monitor ignored (handle={handleHex}) - exception during FetchCapabilities: {ex.Message}");
return DdcCiValidationResult.Invalid;
}
}
@@ -74,6 +98,7 @@ namespace PowerDisplay.Common.Drivers.DDC
// Get capabilities string length
if (!GetCapabilitiesStringLength(hPhysicalMonitor, out uint length) || length == 0)
{
Logger.LogWarning($"DDC: GetCapabilitiesStringLength failed (handle=0x{hPhysicalMonitor:X}, length={length})");
return null;
}
@@ -83,6 +108,7 @@ namespace PowerDisplay.Common.Drivers.DDC
{
if (!CapabilitiesRequestAndCapabilitiesReply(hPhysicalMonitor, buffer, length))
{
Logger.LogWarning($"DDC: CapabilitiesRequestAndCapabilitiesReply failed (handle=0x{hPhysicalMonitor:X})");
return null;
}
@@ -95,6 +121,7 @@ namespace PowerDisplay.Common.Drivers.DDC
}
catch (Exception ex) when (ex is not OutOfMemoryException)
{
Logger.LogError($"DDC: TryGetCapabilitiesString exception (handle=0x{hPhysicalMonitor:X}): {ex.Message}");
return null;
}
}

View File

@@ -11,7 +11,6 @@ using System.Threading.Tasks;
using ManagedCommon;
using PowerDisplay.Common.Interfaces;
using PowerDisplay.Common.Models;
using PowerDisplay.Common.Utils;
using WmiLight;
using Monitor = PowerDisplay.Common.Models.Monitor;
@@ -245,8 +244,9 @@ namespace PowerDisplay.Common.Drivers.WMI
/// <summary>
/// Discover supported monitors.
/// WMI brightness control is typically only available on internal laptop displays,
/// which don't have meaningful UserFriendlyName in WmiMonitorID, so we use "Built-in Display".
/// WMI brightness control is typically only available on internal laptop displays.
/// The monitor Name is left blank here; the ViewModel layer fills in a localized
/// "Built-in Display" string so it can be translated for the user's UI language.
/// </summary>
public async Task<IEnumerable<Monitor>> DiscoverMonitorsAsync(CancellationToken cancellationToken = default)
{
@@ -294,13 +294,12 @@ namespace PowerDisplay.Common.Drivers.WMI
? $"WMI_{edidId}_{monitorNumber}"
: $"WMI_Unknown_{monitorNumber}";
// Get display name from PnP manufacturer ID (e.g., "Lenovo Built-in Display")
var displayName = PnpIdHelper.GetBuiltInDisplayName(edidId);
// Name is left blank: MonitorViewModel injects a localized
// "Built-in Display" string for internal displays.
var monitor = new Monitor
{
Id = uniqueId,
Name = displayName,
Name = string.Empty,
CurrentBrightness = currentBrightness,
MinBrightness = 0,
MaxBrightness = 100,

View File

@@ -1,86 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
namespace PowerDisplay.Common.Utils;
/// <summary>
/// Helper class for mapping PnP (Plug and Play) manufacturer IDs to display names.
/// PnP IDs are 3-character codes assigned by Microsoft to hardware manufacturers.
/// See: https://uefi.org/pnp_id_list
/// </summary>
public static class PnpIdHelper
{
/// <summary>
/// Map of common laptop/monitor manufacturer PnP IDs to display names.
/// Only includes manufacturers known to produce laptops with internal displays.
/// </summary>
private static readonly FrozenDictionary<string, string> ManufacturerNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
// Major laptop manufacturers
{ "ACR", "Acer" },
{ "AUO", "AU Optronics" },
{ "BOE", "BOE" },
{ "CMN", "Chi Mei Innolux" },
{ "DEL", "Dell" },
{ "HWP", "HP" },
{ "IVO", "InfoVision" },
{ "LEN", "Lenovo" },
{ "LGD", "LG Display" },
{ "NCP", "Nanjing CEC Panda" },
{ "SAM", "Samsung" },
{ "SDC", "Samsung Display" },
{ "SEC", "Samsung Electronics" },
{ "SHP", "Sharp" },
{ "AUS", "ASUS" },
{ "MSI", "MSI" },
{ "APP", "Apple" },
{ "SNY", "Sony" },
{ "PHL", "Philips" },
{ "HSD", "HannStar" },
{ "CPT", "Chunghwa Picture Tubes" },
{ "QDS", "Quanta Display" },
{ "TMX", "Tianma Microelectronics" },
{ "CSO", "CSOT" },
// Microsoft Surface
{ "MSF", "Microsoft" },
}.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Extract the 3-character PnP manufacturer ID from an EDID ID.
/// </summary>
/// <param name="edidId">EDID ID like "LEN4038" or "BOE0900".</param>
/// <returns>The 3-character PnP ID (e.g., "LEN"), or null if invalid.</returns>
public static string? ExtractPnpId(string? edidId)
{
if (string.IsNullOrEmpty(edidId) || edidId.Length < 3)
{
return null;
}
// PnP ID is the first 3 characters
return edidId.Substring(0, 3).ToUpperInvariant();
}
/// <summary>
/// Get a user-friendly display name for an internal display based on its EDID ID.
/// </summary>
/// <param name="edidId">EDID ID like "LEN4038" or "BOE0900".</param>
/// <returns>Display name like "Lenovo Built-in Display" or "Built-in Display" as fallback.</returns>
public static string GetBuiltInDisplayName(string? edidId)
{
var pnpId = ExtractPnpId(edidId);
if (pnpId != null && ManufacturerNames.TryGetValue(pnpId, out var manufacturer))
{
return $"{manufacturer} Built-in Display";
}
return "Built-in Display";
}
}

View File

@@ -135,6 +135,10 @@
<data name="BuiltInMonitorTooltip.ToolTipService.ToolTip" xml:space="preserve">
<value>Built-in display</value>
</data>
<data name="BuiltInDisplayName" xml:space="preserve">
<value>Built-in display</value>
<comment>Display name shown in the monitor list for the laptop's internal/built-in display.</comment>
</data>
<data name="BrightnessTooltip.ToolTipService.ToolTip" xml:space="preserve">
<value>Brightness</value>
</data>

View File

@@ -415,13 +415,17 @@ public partial class MainViewModel
SupportsVolume = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x62) ?? false,
SupportsPowerState = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0xD6) ?? false,
// Default Enable* to match Supports* for new monitors (first-time setup)
// ApplyPreservedUserSettings will override these with saved user preferences if they exist
// Default Enable* for new monitors (first-time setup):
// - Contrast / Volume: enabled if the monitor advertises the VCP code (low-risk features).
// - InputSource / ColorTemperature / PowerState: always disabled by default. These can leave
// the monitor in a state recoverable only via physical buttons; users opt-in via the
// Settings UI checkbox, which raises a confirmation dialog (HandleDangerousFeatureClickAsync).
// ApplyPreservedUserSettings will override these with saved user preferences if they exist.
EnableContrast = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x12) ?? false,
EnableVolume = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x62) ?? false,
EnableInputSource = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x60) ?? false,
EnableColorTemperature = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x14) ?? false,
EnablePowerState = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0xD6) ?? false,
EnableInputSource = false,
EnableColorTemperature = false,
EnablePowerState = false,
// Monitor number for display name formatting
MonitorNumber = vm.MonitorNumber,

View File

@@ -215,13 +215,19 @@ public partial class MonitorViewModel : ObservableObject, IDisposable
// Subscribe to underlying Monitor property changes (e.g., Orientation updates in mirror mode)
_monitor.PropertyChanged += OnMonitorPropertyChanged;
// Initialize Show properties based on hardware capabilities
// Initialize Show properties for first-time detection. ApplyFeatureVisibility will
// override these whenever settings.json has a saved entry for this monitor, so these
// values only take effect for brand-new monitors (no persisted preference yet).
// Mirror CreateMonitorInfo's defaults to keep the flyout and settings.json in sync:
// - Brightness / Contrast / Volume: enabled if the hardware advertises the VCP code.
// - InputSource / ColorTemperature / PowerState: always disabled by default (dangerous
// features); the user opts in via the Settings UI confirmation dialog.
ShowBrightness = monitor.SupportsBrightness;
ShowContrast = monitor.SupportsContrast;
ShowVolume = monitor.SupportsVolume;
ShowInputSource = monitor.SupportsInputSource;
_showPowerState = monitor.SupportsPowerState;
_showColorTemperature = monitor.SupportsColorTemperature;
ShowInputSource = false;
_showPowerState = false;
_showColorTemperature = false;
// Initialize basic properties from monitor
_brightness = monitor.CurrentBrightness;
@@ -232,7 +238,9 @@ public partial class MonitorViewModel : ObservableObject, IDisposable
public string Id => _monitor.Id;
public string Name => _monitor.Name;
public string Name => IsInternal
? ResourceLoaderInstance.ResourceLoader.GetString("BuiltInDisplayName")
: _monitor.Name;
/// <summary>
/// Gets the monitor number from the underlying monitor model (Windows DISPLAY number)

View File

@@ -18,6 +18,14 @@
ModuleImageSource="ms-appx:///Assets/Settings/Modules/GrabAndMove.png">
<controls:SettingsPageControl.ModuleContent>
<StackPanel ChildrenTransitions="{StaticResource SettingsCardsAnimations}" Orientation="Vertical">
<InfoBar
x:Uid="GrabAndMove_TouchpadInfoBar"
Margin="0,0,0,8"
IsClosable="False"
IsOpen="True"
Severity="Informational" />
<controls:GPOInfoControl ShowWarning="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay}">
<tkcontrols:SettingsCard
Name="GrabAndMoveEnableToggleControlHeaderText"

View File

@@ -211,7 +211,11 @@
<CheckBox x:Uid="PowerDisplay_Monitor_EnableVolume" IsChecked="{x:Bind EnableVolume, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard ContentAlignment="Left" IsEnabled="{x:Bind SupportsInputSource, Mode=OneWay}">
<CheckBox x:Uid="PowerDisplay_Monitor_EnableInputSource" IsChecked="{x:Bind EnableInputSource, Mode=TwoWay}" />
<CheckBox
x:Uid="PowerDisplay_Monitor_EnableInputSource"
Click="EnableInputSource_Click"
IsChecked="{x:Bind EnableInputSource, Mode=TwoWay}"
Tag="{x:Bind}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard ContentAlignment="Left">
<CheckBox x:Uid="PowerDisplay_Monitor_EnableRotation" IsChecked="{x:Bind EnableRotation, Mode=TwoWay}" />
@@ -224,7 +228,11 @@
Tag="{x:Bind}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard ContentAlignment="Left" IsEnabled="{x:Bind SupportsPowerState, Mode=OneWay}">
<CheckBox x:Uid="PowerDisplay_Monitor_EnablePowerState" IsChecked="{x:Bind EnablePowerState, Mode=TwoWay}" />
<CheckBox
x:Uid="PowerDisplay_Monitor_EnablePowerState"
Click="EnablePowerState_Click"
IsChecked="{x:Bind EnablePowerState, Mode=TwoWay}"
Tag="{x:Bind}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard ContentAlignment="Left">
<CheckBox x:Uid="PowerDisplay_Monitor_HideMonitor" IsChecked="{x:Bind IsHidden, Mode=TwoWay}" />

View File

@@ -209,13 +209,40 @@ namespace Microsoft.PowerToys.Settings.UI.Views
}
}
// Flag to prevent reentrant handling during programmatic checkbox changes
private bool _isRestoringColorTempCheckbox;
// Flag to prevent reentrant Click handling while we programmatically restore
// a checkbox after the user cancels a dangerous-feature confirmation dialog.
private bool _isRestoringDangerousFeatureCheckbox;
private async void EnableColorTemperature_Click(object sender, RoutedEventArgs e)
{
// Skip if we're programmatically restoring the checkbox state
if (_isRestoringColorTempCheckbox)
await HandleDangerousFeatureClickAsync(
sender,
"PowerDisplay_ColorTemperature",
(monitor, value) => monitor.EnableColorTemperature = value);
}
private async void EnablePowerState_Click(object sender, RoutedEventArgs e)
{
await HandleDangerousFeatureClickAsync(
sender,
"PowerDisplay_PowerState",
(monitor, value) => monitor.EnablePowerState = value);
}
private async void EnableInputSource_Click(object sender, RoutedEventArgs e)
{
await HandleDangerousFeatureClickAsync(
sender,
"PowerDisplay_InputSource",
(monitor, value) => monitor.EnableInputSource = value);
}
private async Task HandleDangerousFeatureClickAsync(
object sender,
string resourceKeyPrefix,
Action<MonitorInfo, bool> setter)
{
if (_isRestoringDangerousFeatureCheckbox)
{
return;
}
@@ -225,18 +252,17 @@ namespace Microsoft.PowerToys.Settings.UI.Views
return;
}
// Only show warning when enabling (checking the box)
// Only show the warning when the user is enabling the feature.
if (checkBox.IsChecked != true)
{
return;
}
// Show confirmation dialog with color temperature warning
var resourceLoader = ResourceLoaderInstance.ResourceLoader;
var dialog = new ContentDialog
{
XamlRoot = this.XamlRoot,
Title = resourceLoader.GetString("PowerDisplay_ColorTemperature_WarningTitle"),
Title = resourceLoader.GetString($"{resourceKeyPrefix}_WarningTitle"),
Content = new StackPanel
{
Spacing = 12,
@@ -244,31 +270,31 @@ namespace Microsoft.PowerToys.Settings.UI.Views
{
new TextBlock
{
Text = resourceLoader.GetString("PowerDisplay_ColorTemperature_WarningHeader"),
Text = resourceLoader.GetString($"{resourceKeyPrefix}_WarningHeader"),
FontWeight = Microsoft.UI.Text.FontWeights.Bold,
Foreground = (Microsoft.UI.Xaml.Media.Brush)Application.Current.Resources["SystemFillColorCriticalBrush"],
TextWrapping = TextWrapping.Wrap,
},
new TextBlock
{
Text = resourceLoader.GetString("PowerDisplay_ColorTemperature_WarningDescription"),
Text = resourceLoader.GetString($"{resourceKeyPrefix}_WarningDescription"),
TextWrapping = TextWrapping.Wrap,
},
new TextBlock
{
Text = resourceLoader.GetString("PowerDisplay_ColorTemperature_WarningList"),
Text = resourceLoader.GetString($"{resourceKeyPrefix}_WarningList"),
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness(20, 0, 0, 0),
},
new TextBlock
{
Text = resourceLoader.GetString("PowerDisplay_ColorTemperature_WarningConfirm"),
Text = resourceLoader.GetString($"{resourceKeyPrefix}_WarningConfirm"),
FontWeight = Microsoft.UI.Text.FontWeights.SemiBold,
TextWrapping = TextWrapping.Wrap,
},
},
},
PrimaryButtonText = resourceLoader.GetString("PowerDisplay_ColorTemperature_EnableButton"),
PrimaryButtonText = resourceLoader.GetString("PowerDisplay_Dialog_Enable"),
CloseButtonText = resourceLoader.GetString("PowerDisplay_Dialog_Cancel"),
DefaultButton = ContentDialogButton.Close,
};
@@ -277,16 +303,16 @@ namespace Microsoft.PowerToys.Settings.UI.Views
if (result != ContentDialogResult.Primary)
{
// User cancelled: revert checkbox to unchecked
_isRestoringColorTempCheckbox = true;
// User cancelled: revert checkbox to unchecked.
_isRestoringDangerousFeatureCheckbox = true;
try
{
checkBox.IsChecked = false;
monitor.EnableColorTemperature = false;
setter(monitor, false);
}
finally
{
_isRestoringColorTempCheckbox = false;
_isRestoringDangerousFeatureCheckbox = false;
}
}
}

View File

@@ -5501,7 +5501,41 @@ The break timer font matches the text font.</value>
<data name="PowerDisplay_ColorTemperature_WarningConfirm" xml:space="preserve">
<value>Do you want to enable color temperature control for this monitor?</value>
</data>
<data name="PowerDisplay_ColorTemperature_EnableButton" xml:space="preserve">
<data name="PowerDisplay_PowerState_WarningTitle" xml:space="preserve">
<value>Confirm power state control</value>
</data>
<data name="PowerDisplay_PowerState_WarningHeader" xml:space="preserve">
<value>⚠️ Warning: This action may be unsafe.</value>
</data>
<data name="PowerDisplay_PowerState_WarningDescription" xml:space="preserve">
<value>Enabling power state control may lead to unexpected behavior, including:</value>
</data>
<data name="PowerDisplay_PowerState_WarningList" xml:space="preserve">
<value>• Monitor may enter standby and not wake via software.
• You may need to press the monitors power button or reconnect the cable to recover.
• Some monitors may not restore the previous state correctly.</value>
</data>
<data name="PowerDisplay_PowerState_WarningConfirm" xml:space="preserve">
<value>Enable power state control for this monitor?</value>
</data>
<data name="PowerDisplay_InputSource_WarningTitle" xml:space="preserve">
<value>Confirm input source control</value>
</data>
<data name="PowerDisplay_InputSource_WarningHeader" xml:space="preserve">
<value>⚠️ Warning: This action may be unsafe.</value>
</data>
<data name="PowerDisplay_InputSource_WarningDescription" xml:space="preserve">
<value>Switching input sources may cause unintended results, including:</value>
</data>
<data name="PowerDisplay_InputSource_WarningList" xml:space="preserve">
<value>• Screen may go black if the selected input has no signal.
• Software control may be lost until you switch back using the monitors physical buttons.
• Some monitors may not reliably expose all input sources.</value>
</data>
<data name="PowerDisplay_InputSource_WarningConfirm" xml:space="preserve">
<value>Enable input source control for this monitor?</value>
</data>
<data name="PowerDisplay_Dialog_Enable" xml:space="preserve">
<value>Enable</value>
</data>
<data name="PowerDisplay_Dialog_Cancel" xml:space="preserve">
@@ -5943,6 +5977,13 @@ Text uses the current drawing color.</value>
<value>Grab And Move</value>
<comment>"Grab And Move" is the name of the utility</comment>
</data>
<data name="GrabAndMove_TouchpadInfoBar.Title" xml:space="preserve">
<value>Touchpad support</value>
</data>
<data name="GrabAndMove_TouchpadInfoBar.Message" xml:space="preserve">
<value>Grab And Move may not work reliably with some touchpads.</value>
<comment>"Grab And Move" is the name of the utility</comment>
</data>
<data name="LearnMore_GrabAndMove.Text" xml:space="preserve">
<value>Learn more about Grab And Move</value>
<comment>"Grab And Move" is the name of the utility</comment>

View File

@@ -9,6 +9,7 @@ using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
@@ -135,7 +136,18 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private void Frame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw e.Exception;
var sourcePage = e.SourcePageType?.FullName ?? "<unknown>";
if (e.Exception is null)
{
Logger.LogWarning($"Navigation to '{sourcePage}' failed without an exception.");
}
else
{
Logger.LogError($"Navigation to '{sourcePage}' failed.", e.Exception);
}
e.Handled = true;
}
private void Frame_Navigated(object sender, NavigationEventArgs e)