Skip to content

[QuickAccent] Update Topmost logic to attempt to fix hybrid graphics issues - #41044

Merged
Gordon Lam (yeelam-gordon) merged 3 commits into
microsoft:mainfrom
daverayment:patch-4
Aug 22, 2025
Merged

Gordon Lam (yeelam-gordon) merged 3 commits into
microsoft:mainfrom
daverayment:patch-4

Conversation

@daverayment

Copy link
Copy Markdown
Collaborator

An attempt to fix a Quick Accent issue affecting laptops with 'Optimus' hybrid graphics modes, where the utility locks the machine into discrete graphics mode permanently.

Summary of the Pull Request

This PR changes the Topmost behaviour for Quick Accent from always true to only being true when the selection window is displayed.

PR Checklist

Detailed Description of the Pull Request / Additional comments

Topmost was set to true for the main application window on start, which persisted for the lifetime of the application. This PR change removes that, and instead dynamically toggles Topmost for the selection window as it is activated/deactivated. The assumption is that the FluentWindow-derived window is retaining graphics resources and presenting as an active GPU consumer because of the main application window's Topmost status, even if the selection window is hidden.

Validation Steps Performed

I've confirmed with manual tests that the existing QuickAccent functionality appears to function identically with this change. However, I do not own a laptop with a hybrid graphics capability, so am unable to test whether this fixes the underlying problem. I will keep my fingers crossed though 🤞😊

Topmost was set to true for the application window on start. This change removes that, and dynamically toggles Topmost for the selection window on activation/deactivation. This is an attempt to fix an issue for laptops with hybrid graphics.

This comment was marked as outdated.

@yeelam-gordon

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't verify as well, but this change looks reasonable to me.

@DHowett

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR addresses hybrid graphics issues on laptops with Optimus technology by modifying the Topmost window behavior in Quick Accent. Instead of keeping the main application window permanently topmost, the change dynamically sets the Topmost property only when the selection window is active.

  • Removes permanent Topmost setting from main application window initialization
  • Adds conditional Topmost behavior that activates only when the selection window is displayed
  • Includes explanatory comment linking the change to hybrid graphics issue resolution

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@daverayment

Copy link
Copy Markdown
Collaborator Author

After requesting feedback from users with hybrid graphics laptops, it was confirmed that this did not fix #34849, which I've reopened.

Yu Leng (moooyo) pushed a commit that referenced this pull request Jun 29, 2026
The WinUI 3 selector set IsAlwaysOnTop once at construction and left it on
for the life of the (hidden but never-destroyed) process, reintroducing the
hybrid-graphics regression that PR #41044 fixed for the WPF original by
toggling Topmost = isActive (issue #34849, still open): a permanently
always-on-top overlay can keep a discrete GPU awake.

Toggle the WinUIEx WindowEx.IsAlwaysOnTop property in PowerAccent_OnChangeDisplay
(true on show, false on hide) instead, matching the sibling PowerDisplay's use
of the same property. This drops the cached presenter field, the constructor
null-check/warning and the SetAlwaysOnTop helper (WindowEx.IsAlwaysOnTop is
null-safe internally), and the now-unused ManagedCommon using.

Also fix the dev-doc "WinUI 3 Selector Notes" to reference the renamed
PowerAccentXAML/MainWindow.xaml(.cs) instead of the removed Selector.xaml(.cs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@moooyo Yu Leng (moooyo) mentioned this pull request Jun 29, 2026
10 of 11 tasks
Yu Leng (moooyo) added a commit that referenced this pull request Jul 3, 2026
## Summary of the Pull Request

Migrates the **Quick Accent (PowerAccent)** module's UI from WPF
(`System.Windows.*`) to **WinUI 3 (Windows App SDK)**, following the
pattern used by other migrated modules (ImageResizer, PowerDisplay). The
accent selector is now a self-contained WinUI 3 app
(`PowerToys.PowerAccent.exe`) shipped under `WinUI3Apps`, and
`PowerAccent.Core` is UI-framework-agnostic.


demo:

https://github.com/user-attachments/assets/400c33ee-0fc0-491e-841b-a546438edf91


## PR Checklist

- [x] Closes: #48889
- [x] **Communication:** Tracked task (#48889) agreed with core
contributors
- [x] **Tests:** Added/updated and all pass — new
`PowerAccent.Core.UnitTests` (21 tests for the positioning / DPI math);
existing `PowerAccent.Common.UnitTests` unaffected
- [x] **Localization:** No new localizable end-user strings — new
accessibility metadata uses non-localized
`AutomationProperties.AutomationId`, and the window title is the brand
name `"Quick Accent"` (literal, matching ColorPicker)
- [x] **Dev docs:** Updated `doc/devdocs/modules/quickaccent.md`
- [x] **New binaries:** Added on the required places
- [x] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json):
the `WinUI3Apps\` PowerAccent payloads are listed in
`ESRPSigning_core.json`
- [x] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs):
no manual `Product.wxs` entry — the self-contained `WinUI3Apps` output
(exe + `.pri` + Windows App SDK runtime) is harvested by the
`WinUI3ApplicationsFiles` glob (same as ImageResizer)
- [x] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml):
no change needed — `PowerAccent.Core.UnitTests` is discovered by the
existing `**\*UnitTest*.dll` VSTest glob
- [x] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml):
covered by `ESRPSigning_core.json`; no `release.yml` change needed
- [ ] **Documentation updated:** N/A — internal UI-framework migration
with no user-facing behavior change

## Detailed Description of the Pull Request / Additional comments

The migration spans three areas (tracked in #48889):

**UI (WPF → WinUI 3)**
- `PowerAccent.UI` is now a WinUI 3 app shell (custom `Program.Main`,
`WindowsPackageType=None`, `WindowsAppSDKSelfContained=true`).
- The accent selector is a non-activating `TransparentWindow` overlay
shown with `SW_SHOWNA` (never steals focus). It is made always-on-top
only while shown — the WinUIEx `WindowEx.IsAlwaysOnTop` property is
toggled `true` on show / `false` on hide in `OnChangeDisplay` (matching
the WPF original's `Topmost = isActive`), so the dormant,
never-destroyed overlay does not pin a discrete GPU awake on
hybrid-graphics laptops (issue #34849 / PR #41044).
- The accent "pill" selection visual is reproduced with
`VisualStateManager` (WinUI 3 has no `Style.Triggers`).
- **WinUI 3 gotcha:** x:Bind on a Window-rooted XAML initializes only on
`Window.Activated`, which never fires for this `SW_SHOWNA` overlay — so
the selector calls `Bindings.Update()` after `InitializeComponent()`;
without it the `ListView` renders empty.
- **Theme:** the long-lived, never-activated process follows the system
app theme automatically — `App.xaml` leaves `Application.RequestedTheme`
unset, so WinUI re-resolves the `{ThemeResource}` brushes (and retints
the acrylic) on a live light/dark switch with no manual `ThemeListener`
needed.
- **Layout parity with the WPF original:** the bar width hugs its
content (`itemCount × 48`, clamped to the monitor width — computed, not
measured, to avoid a racy `ListView` measure), and each cell pins
`MinWidth=48` (WinUI's `ListViewItem` defaults to 88, which would
otherwise leave wide gaps).
- **Accessibility:** UIA window name + `AutomationId`s on the character
list and description.

**Dependency**
- `PowerAccent.Core` no longer depends on WPF — it raises events and
takes an injected UI-thread marshaller.
- WinForms `SendKeys` → `SendInput` (CsWin32 P/Invoke); WPF-UI (Lepo)
removed; language data moved to the UI-/WinRT-agnostic
`PowerAccent.Common`.
- MVVM via CommunityToolkit.Mvvm with `[ObservableProperty]` **partial
properties** (WinRT-correct, clears MVVMTK0045).

**CI / Build / Installer**
- Signing config, WinUI3Apps glob harvest, and the new unit-test project
— see the checklist above.

## Validation Steps Performed

- **Build:** `x64 Debug` builds with **0 warnings / 0 errors**.
- **Unit tests:** `PowerAccent.Core.UnitTests` — **21/21 pass** (9
anchor positions × DPI 1.0/1.5/2.0, the offset and negative-origin
monitors, caret centering + edge clamping + flip-below).
- **XamlStyler:** `PowerAccentXAML/MainWindow.xaml` passes the passive
format check (CI mode).
- **Manual (single monitor, Top-center, light theme):**
  - Accent popup appears with the full accent list rendered.
- Bar hugs the characters and is centered; cell spacing matches the WPF
original.
- Switching the system theme (light/dark) is followed live by the popup.
- With `show_description` enabled, the description row is wide (≥600px)
and readable, with the accent bar centered above it.
- **Remaining manual validation** (tracked in #48889): multi-monitor,
per-monitor DPI, all 9 `toolbar_position` values, and high-contrast
theme.

---------

Co-authored-by: Yu Leng <yuleng@microsoft.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Yu Leng (moooyo) added a commit that referenced this pull request Aug 11, 2026
## Summary of the Pull Request

Follow-up to #49633: replace the way the accent bar's first frame is
protected. #49633 fixed the blank/stale first frame (#49489) by masking
it — `Selector.Opacity = 0`, unveil after two
`CompositionTarget.Rendering` ticks, backed by a 150 ms watchdog. This
PR removes the cause instead, using the technique the Command Palette
and Quick Access already ship: **DWM-cloak the overlay instead of hiding
it**, so it never stops rendering and there is no stale frame to put
back on screen.

No user-visible behaviour change is intended beyond removing the fixed
two-render-tick reveal delay; this is a mechanism swap plus the cleanup
it enables.

## PR Checklist

- [x] **Closes:** N/A — #49489 was already closed by #49633; this
replaces that fix's mechanism
- [x] **Communication:** follow-up to a merged PR in the same module, no
new feature surface
- [x] **Tests:** `PowerAccent.Core.UnitTests` 32/32 still pass (the pure
width logic from #49633 is untouched). The current-head CI status is
tracked in the PR checks; the compositor-specific manual measurements
below were captured before the final commit-fence follow-up and are
labeled accordingly
- [x] **Localization:** no new end-user-facing strings
- [x] **Dev docs:** N/A
- [x] **New binaries:** none
- [x] **Documentation updated:** N/A

## Detailed Description of the Pull Request / Additional comments

### Why cloaking

A hidden WinUI 3 window renders nothing. Its composition surface
therefore still holds the frame it was showing when it was hidden, and
`ShowWindow` puts that stale frame back on screen before the rebuilt
accent list has been laid out — that is #49489. Everything downstream of
that follows from "the window does not render while hidden":

* the bar cannot be measured before it is shown (a `Collapsed` subtree
is never measured), hence #49633's measure-twice workaround;
* #49633 deferred reveal using rendering ticks because it did not have a
composition-commit fence;
* this PR calls
`Microsoft.UI.Composition.Compositor.RequestCommitAsync()` after the bar
has been laid out, sized, positioned and scrolled, and only reveals
after that commit completes. This replaces the frame counter and
watchdog with an explicit compositor fence.

A cloaked window is equally invisible to the user but stays
`SW_SHOWNA`-shown, so XAML keeps laying it out and painting it. This is
exactly what `Microsoft.CmdPal.UI\MainWindow.xaml.cs` does, and its
comment names the same symptom:

```csharp
// TRICKY: show our HWND again. This will trick XAML into painting our
// HWND again, so that we avoid the "flicker" caused by a WinUI3 app
// window being first shown
```

`QuickAccess.UI\QuickAccessXAML\MainWindow.xaml.cs` uses the same
pattern, including the "warm up the window while cloaked" prewarm that
this PR also picks up — which is what removes the *first summon of the
process* case that #49633's second measurement existed for.

### What the summon looks like now

`Show()` still raises `Showing`, so the surface leaves `Collapsed` and
the bar lays out — but the window is still cloaked, so nothing reaches
the screen. The bar is then measured **once** (on a templated, laid-out,
non-collapsed subtree), sized, positioned and scrolled to the selection.
The compositor commit is then awaited, and only after it completes does
`Reveal()` uncloak the window. The first visible frame is a finished bar
by construction rather than by timing.

Removed as a result: `RevealTimeoutMs`, `FramesBeforeReveal`,
`_revealTimer`, `_revealGeneration`, `_renderedFrames`,
`_measuredContentWidthDip`, `ArmRevealTimeout`, `CancelPendingReveal`,
`WaitForFirstFrameThenReveal`, `OnRenderingBeforeReveal`, the local
`Reveal`, and the `Selector.Opacity` dance — 87 net lines out of
`MainWindow`. `_showGeneration` stays: a layout callback queued by a
dismissed summon still has to be dropped.

### `TransparentWindow`

The cloak lives in the shared window because `Hide()` owns the
`AppWindow.Hide()` that has to be replaced. It is **opt-in**
(`EnableCloakedHide()`), so Shortcut Guide's overlay and CmdPal's toast
keep hiding exactly as they do today; only Quick Accent enables it.
`Reveal()` is a no-op for them.

Two details worth review attention:

* **Hit-testing.** Cloaking takes a window out of composition but *not*
out of hit-testing, and this HWND sits exactly where the user is typing.
While cloaked the window is therefore made click-through
(`WS_EX_TRANSPARENT`), restored on reveal. Without this, an invisible
accent bar would swallow clicks meant for the app underneath.
* **`SW_HIDE` then `SW_SHOWNA`.** Same order as CmdPal: the hide is what
hands the foreground back to whatever window should own it, and the show
that follows leaves the window "shown" — which is what keeps XAML
painting — while the cloak keeps it off screen. If DWM refuses to cloak,
the HWND remains hidden; a later `Show()` retries instead of exposing an
un-laid-out frame.

### Relationship to #34849 / #41044

Always-on-top is still released on hide, so the dormant overlay is
`WS_EX_TOPMOST=False` exactly as before — verified below. Cloaking is
orthogonal to topmost. The one honest trade-off is that the HWND is now
permanently `WS_VISIBLE` (cloaked), so it keeps participating in
composition while dormant, the same as CmdPal and Quick Access already
do; it stays out of Alt-Tab and the taskbar via a hidden owner plus
`WS_EX_TOOLWINDOW`.

## Validation Steps Performed

> The build/test/live-state results below were recorded at `e372cdf`.
Current head `ea658bb` adds the explicit `RequestCommitAsync` fence
after that validation. Current-head CI is tracked by the PR checks, and
the live window-state/frame-capture checks should be repeated before
merge.

* `build-essentials`, `Common.UI.Controls`, `PowerAccent.UI` and
`PowerAccent.Core.UnitTests` all build clean (Debug|x64), 0 warnings.
* `PowerAccent.Core.UnitTests`: 32/32 pass.
* Live window-state measurement against the built
`PowerToys.PowerAccent.exe` (`DwmGetWindowAttribute(DWMWA_CLOAKED)` +
`IsWindowVisible` + ex-styles), driving a real summon of the
<kbd>R</kbd> bar with **All languages** selected:

  | phase | state |
  |---|---|
| dormant (prewarmed, before any summon) | `visible=True cloaked=1
topmost=False clickThrough=True` |
| summoned | `visible=True cloaked=0 topmost=True clickThrough=False` |
| dismissed | `visible=True cloaked=1 topmost=False clickThrough=True` |

i.e. the window is shown-and-painting the whole time, invisible and
click-through while dormant, and topmost/interactive only while
summoned.
* Screen-captured the summoned bar: all 22 characters for <kbd>R</kbd>
render (including the wide `₹ ៛ ﷼`), leading and trailing padding are
symmetric, nothing is clipped and the selection is on the first cell.

---------

Co-authored-by: Yu Leng <yuleng@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Boliang Zhang (LegendaryBlair) pushed a commit that referenced this pull request Aug 11, 2026
## Summary of the Pull Request

Follow-up to #49633: replace the way the accent bar's first frame is
protected. #49633 fixed the blank/stale first frame (#49489) by masking
it — `Selector.Opacity = 0`, unveil after two
`CompositionTarget.Rendering` ticks, backed by a 150 ms watchdog. This
PR removes the cause instead, using the technique the Command Palette
and Quick Access already ship: **DWM-cloak the overlay instead of hiding
it**, so it never stops rendering and there is no stale frame to put
back on screen.

No user-visible behaviour change is intended beyond removing the fixed
two-render-tick reveal delay; this is a mechanism swap plus the cleanup
it enables.

## PR Checklist

- [x] **Closes:** N/A — #49489 was already closed by #49633; this
replaces that fix's mechanism
- [x] **Communication:** follow-up to a merged PR in the same module, no
new feature surface
- [x] **Tests:** `PowerAccent.Core.UnitTests` 32/32 still pass (the pure
width logic from #49633 is untouched). The current-head CI status is
tracked in the PR checks; the compositor-specific manual measurements
below were captured before the final commit-fence follow-up and are
labeled accordingly
- [x] **Localization:** no new end-user-facing strings
- [x] **Dev docs:** N/A
- [x] **New binaries:** none
- [x] **Documentation updated:** N/A

## Detailed Description of the Pull Request / Additional comments

### Why cloaking

A hidden WinUI 3 window renders nothing. Its composition surface
therefore still holds the frame it was showing when it was hidden, and
`ShowWindow` puts that stale frame back on screen before the rebuilt
accent list has been laid out — that is #49489. Everything downstream of
that follows from "the window does not render while hidden":

* the bar cannot be measured before it is shown (a `Collapsed` subtree
is never measured), hence #49633's measure-twice workaround;
* #49633 deferred reveal using rendering ticks because it did not have a
composition-commit fence;
* this PR calls
`Microsoft.UI.Composition.Compositor.RequestCommitAsync()` after the bar
has been laid out, sized, positioned and scrolled, and only reveals
after that commit completes. This replaces the frame counter and
watchdog with an explicit compositor fence.

A cloaked window is equally invisible to the user but stays
`SW_SHOWNA`-shown, so XAML keeps laying it out and painting it. This is
exactly what `Microsoft.CmdPal.UI\MainWindow.xaml.cs` does, and its
comment names the same symptom:

```csharp
// TRICKY: show our HWND again. This will trick XAML into painting our
// HWND again, so that we avoid the "flicker" caused by a WinUI3 app
// window being first shown
```

`QuickAccess.UI\QuickAccessXAML\MainWindow.xaml.cs` uses the same
pattern, including the "warm up the window while cloaked" prewarm that
this PR also picks up — which is what removes the *first summon of the
process* case that #49633's second measurement existed for.

### What the summon looks like now

`Show()` still raises `Showing`, so the surface leaves `Collapsed` and
the bar lays out — but the window is still cloaked, so nothing reaches
the screen. The bar is then measured **once** (on a templated, laid-out,
non-collapsed subtree), sized, positioned and scrolled to the selection.
The compositor commit is then awaited, and only after it completes does
`Reveal()` uncloak the window. The first visible frame is a finished bar
by construction rather than by timing.

Removed as a result: `RevealTimeoutMs`, `FramesBeforeReveal`,
`_revealTimer`, `_revealGeneration`, `_renderedFrames`,
`_measuredContentWidthDip`, `ArmRevealTimeout`, `CancelPendingReveal`,
`WaitForFirstFrameThenReveal`, `OnRenderingBeforeReveal`, the local
`Reveal`, and the `Selector.Opacity` dance — 87 net lines out of
`MainWindow`. `_showGeneration` stays: a layout callback queued by a
dismissed summon still has to be dropped.

### `TransparentWindow`

The cloak lives in the shared window because `Hide()` owns the
`AppWindow.Hide()` that has to be replaced. It is **opt-in**
(`EnableCloakedHide()`), so Shortcut Guide's overlay and CmdPal's toast
keep hiding exactly as they do today; only Quick Accent enables it.
`Reveal()` is a no-op for them.

Two details worth review attention:

* **Hit-testing.** Cloaking takes a window out of composition but *not*
out of hit-testing, and this HWND sits exactly where the user is typing.
While cloaked the window is therefore made click-through
(`WS_EX_TRANSPARENT`), restored on reveal. Without this, an invisible
accent bar would swallow clicks meant for the app underneath.
* **`SW_HIDE` then `SW_SHOWNA`.** Same order as CmdPal: the hide is what
hands the foreground back to whatever window should own it, and the show
that follows leaves the window "shown" — which is what keeps XAML
painting — while the cloak keeps it off screen. If DWM refuses to cloak,
the HWND remains hidden; a later `Show()` retries instead of exposing an
un-laid-out frame.

### Relationship to #34849 / #41044

Always-on-top is still released on hide, so the dormant overlay is
`WS_EX_TOPMOST=False` exactly as before — verified below. Cloaking is
orthogonal to topmost. The one honest trade-off is that the HWND is now
permanently `WS_VISIBLE` (cloaked), so it keeps participating in
composition while dormant, the same as CmdPal and Quick Access already
do; it stays out of Alt-Tab and the taskbar via a hidden owner plus
`WS_EX_TOOLWINDOW`.

## Validation Steps Performed

> The build/test/live-state results below were recorded at `e372cdf`.
Current head `ea658bb` adds the explicit `RequestCommitAsync` fence
after that validation. Current-head CI is tracked by the PR checks, and
the live window-state/frame-capture checks should be repeated before
merge.

* `build-essentials`, `Common.UI.Controls`, `PowerAccent.UI` and
`PowerAccent.Core.UnitTests` all build clean (Debug|x64), 0 warnings.
* `PowerAccent.Core.UnitTests`: 32/32 pass.
* Live window-state measurement against the built
`PowerToys.PowerAccent.exe` (`DwmGetWindowAttribute(DWMWA_CLOAKED)` +
`IsWindowVisible` + ex-styles), driving a real summon of the
<kbd>R</kbd> bar with **All languages** selected:

  | phase | state |
  |---|---|
| dormant (prewarmed, before any summon) | `visible=True cloaked=1
topmost=False clickThrough=True` |
| summoned | `visible=True cloaked=0 topmost=True clickThrough=False` |
| dismissed | `visible=True cloaked=1 topmost=False clickThrough=True` |

i.e. the window is shown-and-painting the whole time, invisible and
click-through while dormant, and topmost/interactive only while
summoned.
* Screen-captured the summoned bar: all 22 characters for <kbd>R</kbd>
render (including the wide `₹ ៛ ﷼`), leading and trailing padding are
symmetric, nothing is clipped and the selection is on the first cell.

---------

Co-authored-by: Yu Leng <yuleng@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Product-Quick Accent Refers to the Quick Accent PowerToy

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Power Toys Quick Accent Stops the GPU

6 participants