diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs index ce20c9be77f6..9ef990976273 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs @@ -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,7 +300,7 @@ 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) => @@ -297,7 +308,21 @@ public void Receive(QuitMessage message) => 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: + HideWindow(); + PowerToysTelemetry.Log.WriteEvent(new CmdPalDismissedOnLostFocus()); } @@ -479,10 +506,24 @@ 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(); @@ -490,7 +531,16 @@ private void HandleSummon(string 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); + 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; diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/NativeMethods.txt b/src/modules/cmdpal/Microsoft.CmdPal.UI/NativeMethods.txt index 4db59d7070f5..e4880b02348b 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/NativeMethods.txt +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/NativeMethods.txt @@ -36,3 +36,7 @@ ExtractIconEx WM_RBUTTONUP WM_LBUTTONUP WM_LBUTTONDBLCLK + +DwmGetWindowAttribute +DwmSetWindowAttribute +DWM_CLOAKED_APP \ No newline at end of file