Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 55 additions & 21 deletions src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Copy link
Member

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

Copy link
Member Author

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"

}

PInvoke.SetForegroundWindow(hwnd);
PInvoke.SetActiveWindow(hwnd);
}
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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:
Copy link
Member

Choose a reason for hiding this comment

The 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());
}

Expand Down Expand Up @@ -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);
Copy link
Member

Choose a reason for hiding this comment

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

Who shows it again if there's a debugger attached?

Copy link
Member Author

Choose a reason for hiding this comment

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

ShowHwnd actually does always call SW_SHOW. That just no-ops if the window is already shown

return;
}

HideWindow();
}
}

Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/modules/cmdpal/Microsoft.CmdPal.UI/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ ExtractIconEx
WM_RBUTTONUP
WM_LBUTTONUP
WM_LBUTTONDBLCLK

DwmGetWindowAttribute
DwmSetWindowAttribute
DWM_CLOAKED_APP
Loading