From ac9d69790f0cea3c27450fe1ee5427a4e28cc40d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 1 Aug 2022 15:33:56 -0500 Subject: [PATCH] Restore the ability for alt+tab to restore the Terminal after minimizing with taskbar (#13624) Curiously, at least on Windows 10 (and rarely on Windows 11), if you minimize the Terminal by clicking on the taskbar, then alt-tab to try and restore the window, the Taskbar will decide to call `SwitchToWindow` on the invisible, owned ConPTY window instead of the main window. When that happens, ConPTY'll get a `WM_SIZE(SIZE_RESTORED, lParam=0)`. The main window will NOT get a `SwitchToWindow` called. If ConPTY doesn't actually inform the hosting process about this, then the main HWND might stay hidden. * Refer to #13158 where we disabled this. * Closes #13589 * Closes #13248 * Tested manually on a Windows 10 VM. * Confirmed that opening tabs while maximized/snapped doesn't restore down. * `[Native]::ShowWindow([Native]::GetConsoleWindow(), 6)` still works (cherry picked from commit d1fc11248c51a541236682e7ea6f76e8e9306713) Service-Card-Id: 84673887 Service-Version: 1.15 (cherry picked from commit b670800460dae5c650318dba3eba0fde57965a14) Service-Card-Id: 84673886 Service-Version: 1.14 --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 16 ++++-- .../base/InteractivityFactory.cpp | 49 ++++++++++--------- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index a1a79a30d7c..ce278bdbae1 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -834,10 +834,20 @@ void IslandWindow::SetAlwaysOnTop(const bool alwaysOnTop) // - void IslandWindow::ShowWindowChanged(const bool showOrHide) { - const auto hwnd = GetHandle(); - if (hwnd) + if (const auto hwnd = GetHandle()) { - PostMessage(hwnd, WM_SYSCOMMAND, showOrHide ? SC_RESTORE : SC_MINIMIZE, 0); + // IMPORTANT! + // + // ONLY "restore" if already minimized. If the window is maximized or + // snapped, a restore will restore-down the window instead. + if (showOrHide == true && ::IsIconic(hwnd)) + { + ::PostMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); + } + else if (showOrHide == false) + { + ::PostMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); + } } } diff --git a/src/interactivity/base/InteractivityFactory.cpp b/src/interactivity/base/InteractivityFactory.cpp index 737057bda87..baa3ab861a0 100644 --- a/src/interactivity/base/InteractivityFactory.cpp +++ b/src/interactivity/base/InteractivityFactory.cpp @@ -436,6 +436,15 @@ void InteractivityFactory::SetPseudoWindowCallback(std::function fun // It can be fun to toggle WM_QUERYOPEN but DefWindowProc returns TRUE. case WM_SIZE: { + // Curiously, at least on Windows 10 (and rarely on Windows 11), if you + // minimize the Terminal by clicking on the taskbar, then alt-tab to try + // and restore the window, the Taskbar will decide to call + // SwitchToWindow on us, the invisible, owned window of the main window. + // When that happens, we'll get a WM_SIZE(SIZE_RESTORED, lParam=0). The + // main window will NOT get a SwitchToWindow called. If we don't + // actually inform the hosting process about this, then the main HWND + // might stay hidden. Refer to GH#13589 + if (wParam == SIZE_RESTORED) { _WritePseudoWindowCallback(true); @@ -447,23 +456,23 @@ void InteractivityFactory::SetPseudoWindowCallback(std::function fun } break; } - // case WM_WINDOWPOSCHANGING: - // As long as user32 didn't eat the `ShowWindow` call because the window state requested - // matches the existing WS_VISIBLE state of the HWND... we should hear from it in WM_WINDOWPOSCHANGING. - // WM_WINDOWPOSCHANGING can tell us a bunch through the flags fields. - // We can also check IsIconic/IsZoomed on the HWND during the message - // and we could suppress the change to prevent things from happening. + // case WM_WINDOWPOSCHANGING: + // As long as user32 didn't eat the `ShowWindow` call because the window state requested + // matches the existing WS_VISIBLE state of the HWND... we should hear from it in WM_WINDOWPOSCHANGING. + // WM_WINDOWPOSCHANGING can tell us a bunch through the flags fields. + // We can also check IsIconic/IsZoomed on the HWND during the message + // and we could suppress the change to prevent things from happening. // case WM_SYSCOMMAND: - // WM_SYSCOMMAND will not come through. Don't try. + // WM_SYSCOMMAND will not come through. Don't try. + // WM_SHOWWINDOW does come through on some of the messages. case WM_SHOWWINDOW: - // WM_SHOWWINDOW comes through on some of the messages. + { + if (0 == lParam) // Someone explicitly called ShowWindow on us. { - if (0 == lParam) // Someone explicitly called ShowWindow on us. - { - _WritePseudoWindowCallback((bool)wParam); - } + _WritePseudoWindowCallback((bool)wParam); } } + } // If we get this far, call the default window proc return DefWindowProcW(hWnd, Message, wParam, lParam); } @@ -478,18 +487,12 @@ void InteractivityFactory::SetPseudoWindowCallback(std::function fun // - void InteractivityFactory::_WritePseudoWindowCallback(bool showOrHide) { - // BODGY - // - // GH#13158 - At least temporarily, only allow the PTY to HIDE the terminal - // window. There seem to be many issues with this so far, and the quickest - // route to mitigate them seems to be limiting the interaction here to - // allowing ConPTY to minimize the terminal only. This will still allow - // applications to hide the Terminal via GetConsoleWindow(), but should - // broadly prevent any other impact of this feature. + // IMPORTANT! // - // Should we need to restore this functionality in the future, we should - // only do so with great caution. - if (_pseudoWindowMessageCallback && showOrHide == false) + // A hosting terminal window should only "restore" itself in response to + // this message, if it's already minimized. If the window is maximized a + // restore will restore-down the window instead. + if (_pseudoWindowMessageCallback) { _pseudoWindowMessageCallback(showOrHide); }