diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs index 558bd3ecad..afba510d86 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs @@ -205,6 +205,9 @@ public sealed partial class MainWindow : WindowEx, { var hwnd = new HWND(hwndValue != 0 ? hwndValue : _hwnd); + // Make sure our HWND is cloaked before any possible window manipulations + Cloak(); + // Remember, IsIconic == "minimized", which is entirely different state // from "show/hide" // If we're currently minimized, restore us first, before we reveal @@ -218,16 +221,11 @@ public sealed partial class MainWindow : WindowEx, var display = GetScreen(hwnd, target); PositionCentered(display); + // Just to be sure, SHOW our hwnd. 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, &value, (uint)sizeof(BOOL)); - } + // Once we're done, uncloak to avoid all animations + Uncloak(); PInvoke.SetForegroundWindow(hwnd); PInvoke.SetActiveWindow(hwnd); @@ -286,7 +284,14 @@ public sealed partial class MainWindow : WindowEx, ShowHwnd(message.Hwnd, settings.SummonOn); } - public void Receive(HideWindowMessage message) => HideWindow(); + public void Receive(HideWindowMessage message) + { + // This might come in off the UI thread. Make sure to hop back. + DispatcherQueue.TryEnqueue(() => + { + HideWindow(); + }); + } public void Receive(QuitMessage message) => @@ -298,34 +303,24 @@ public sealed partial class MainWindow : WindowEx, private void HideWindow() { - // First, use EnumWindows to find the first HWND that's not us, and set it as the foreground window - PInvoke.EnumWindows( - (hwnd, lParam) => - { - if (hwnd != _hwnd && PInvoke.IsWindowVisible(hwnd)) - { - // and make sure it's not a tool window - var exStyle = (WINDOW_EX_STYLE)PInvoke.GetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE); + // Cloak our HWND to avoid all animations. + Cloak(); - if (exStyle.HasFlag(WINDOW_EX_STYLE.WS_EX_TOOLWINDOW)) - { - return true; // continue enumerating - } + // Then hide our HWND, to make sure that the OS gives the FG / focus back to another app + // (there's no way for us to guess what the right hwnd might be, only the OS can do it right) + this.Hide(); - PInvoke.SetForegroundWindow(hwnd); - return false; // stop enumerating - } + // 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 + this.Show(); - return true; // continue enumerating - }, - IntPtr.Zero); + // Intentionally leave the window cloaked. So our window is "visible", + // but also cloaked, so you can't see it. + } - // THEN, - // Hide our window by DWM cloaking it - - // 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 + private void Cloak() + { unsafe { BOOL value = true; @@ -333,6 +328,15 @@ public sealed partial class MainWindow : WindowEx, } } + private void Uncloak() + { + unsafe + { + BOOL value = false; + PInvoke.DwmSetWindowAttribute(_hwnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAK, &value, (uint)sizeof(BOOL)); + } + } + internal void MainWindow_Closed(object sender, WindowEventArgs args) { var serviceProvider = App.Current.Services; @@ -555,6 +559,7 @@ public sealed partial class MainWindow : WindowEx, 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 @@ -584,7 +589,11 @@ public sealed partial class MainWindow : WindowEx, { // ... 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); + // PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE); + Cloak(); + this.Hide(); + + // Uncloak(); return; }