-
Notifications
You must be signed in to change notification settings - Fork 7.8k
CmdPal: Cloak the window instead of hiding it #39170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,6 +25,7 @@ | |
| using Windows.UI.WindowManagement; | ||
| using Windows.Win32; | ||
| using Windows.Win32.Foundation; | ||
| using Windows.Win32.Graphics.Dwm; | ||
| using Windows.Win32.UI.Input.KeyboardAndMouse; | ||
| using Windows.Win32.UI.Shell; | ||
| using Windows.Win32.UI.WindowsAndMessaging; | ||
|
|
@@ -232,6 +233,16 @@ private void ShowHwnd(IntPtr hwndValue, MonitorBehavior target) | |
| PositionCentered(display); | ||
|
|
||
| PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_SHOW); | ||
|
|
||
| // instead of showing the window, uncloak it from DWM | ||
| // This will make it visible to the user, without the animation or frames for | ||
| // loading XAML with composition | ||
| unsafe | ||
| { | ||
| BOOL value = false; | ||
| PInvoke.DwmSetWindowAttribute(_hwnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAK, (void*)&value, (uint)sizeof(BOOL)); | ||
| } | ||
|
|
||
| PInvoke.SetForegroundWindow(hwnd); | ||
| PInvoke.SetActiveWindow(hwnd); | ||
| } | ||
|
|
@@ -289,15 +300,29 @@ public void Receive(ShowWindowMessage message) | |
| ShowHwnd(message.Hwnd, settings.SummonOn); | ||
| } | ||
|
|
||
| public void Receive(HideWindowMessage message) => PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE); | ||
| public void Receive(HideWindowMessage message) => HideWindow(); | ||
|
|
||
| public void Receive(QuitMessage message) => | ||
|
|
||
| // This might come in on a background thread | ||
| DispatcherQueue.TryEnqueue(() => Close()); | ||
|
|
||
| public void Receive(DismissMessage message) => | ||
| PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE); | ||
| HideWindow(); | ||
|
|
||
| private void HideWindow() | ||
| { | ||
| // Hide our window | ||
|
|
||
| // Instead of hiding the window, cloak it from DWM | ||
| // This will make it invisible to the user, such that we can show it again | ||
| // by uncloaking it, which avoids an unnecessary "flicker in" that XAML does | ||
| unsafe | ||
| { | ||
| BOOL value = true; | ||
| PInvoke.DwmSetWindowAttribute(_hwnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAK, (void*)&value, (uint)sizeof(BOOL)); | ||
| } | ||
| } | ||
|
|
||
| internal void MainWindow_Closed(object sender, WindowEventArgs args) | ||
| { | ||
|
|
@@ -386,7 +411,9 @@ internal void MainWindow_Activated(object sender, WindowActivatedEventArgs args) | |
| return; | ||
| } | ||
|
|
||
| PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE); | ||
| // This will DWM cloak our window: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit if we change the implementation detail inside HideWindow are we going to remember to change this comment? |
||
| HideWindow(); | ||
|
|
||
| PowerToysTelemetry.Log.WriteEvent(new CmdPalDismissedOnLostFocus()); | ||
| } | ||
|
|
||
|
|
@@ -479,18 +506,41 @@ private void HandleSummon(string commandId) | |
| var isRootHotkey = string.IsNullOrEmpty(commandId); | ||
| PowerToysTelemetry.Log.WriteEvent(new CmdPalHotkeySummoned(isRootHotkey)); | ||
|
|
||
| var isVisible = this.Visible; | ||
| unsafe | ||
| { | ||
| // We need to check if our window is cloaked or not. A cloaked window is still | ||
| // technically visible, because SHOW/HIDE != iconic (minimized) != cloaked | ||
| // (these are all separate states) | ||
| long attr = 0; | ||
| PInvoke.DwmGetWindowAttribute(_hwnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAKED, &attr, sizeof(long)); | ||
| if (attr == 1 /* DWM_CLOAKED_APP */) | ||
| { | ||
| isVisible = false; | ||
| } | ||
| } | ||
|
|
||
| // Note to future us: the wParam will have the index of the hotkey we registered. | ||
| // We can use that in the future to differentiate the hotkeys we've pressed | ||
| // so that we can bind hotkeys to individual commands | ||
| if (!this.Visible || !isRootHotkey) | ||
| if (!isVisible || !isRootHotkey) | ||
| { | ||
| Activate(); | ||
|
|
||
| Summon(commandId); | ||
| } | ||
| else if (isRootHotkey) | ||
| { | ||
| PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE); | ||
| // If there's a debugger attached... | ||
| if (System.Diagnostics.Debugger.IsAttached) | ||
| { | ||
| // ... then manually hide our window. When debugged, we won't get the cool cloaking, | ||
| // but that's the price to pay for having the HWND not light-dismiss while we're debugging. | ||
| PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Who shows it again if there's a debugger attached?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| return; | ||
| } | ||
|
|
||
| HideWindow(); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -518,22 +568,6 @@ private LRESULT HotKeyPrc( | |
|
|
||
| var hotkey = _hotkeys[hotkeyIndex]; | ||
| HandleSummon(hotkey.CommandId); | ||
|
|
||
| // var isRootHotkey = string.IsNullOrEmpty(hotkey.CommandId); | ||
|
|
||
| // // Note to future us: the wParam will have the index of the hotkey we registered. | ||
| // // We can use that in the future to differentiate the hotkeys we've pressed | ||
| // // so that we can bind hotkeys to individual commands | ||
| // if (!this.Visible || !isRootHotkey) | ||
| // { | ||
| // Activate(); | ||
|
|
||
| // Summon(hotkey.CommandId); | ||
| // } | ||
| // else if (isRootHotkey) | ||
| // { | ||
| // PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_HIDE); | ||
| // } | ||
| } | ||
|
|
||
| return (LRESULT)IntPtr.Zero; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this mean that window scanning tools will erroneously consider it to be visible? since DWM isn't part of the user32 window model
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO "some apps that report the visible windows but don't know how to deal with CLOAK'd hwnds are wrong for a few releases" is a less bad problem than "CmdPal flash-bangs all of our users"