Repository: microsoft/terminal (language: cpp)

Issue:
After minimizing, unable to restore window with Alt-Tab 
### Windows Terminal version

1.14.1962.0

### Windows build number

Version 21H2 (OS Build 19044.1826)

### Other Software

_No response_

### Steps to reproduce

Open and minimize terminal
Press Alt+Tab to switch back to it
Previous app loses focus, but Terminal doesn't show and remains minimized

NB: When this happened, I tested it multiple times, and it happened every time. I then restored the window and Alt+Tab worked normally, then minimized it and it didn't. _However_, now it's working fine, so I'm not sure if it was some weird Windows issue (W10 continues, to this day, to be _extremely_ broken WRT focus issues, constantly leaving windows I click on _behind_ other windows and requiring me to click on the foreground, unfocused window then click back in the other one to force it to come to the front). Still, I'm reporting just in case it is actually an issue, and maybe somebody else can reproduce it or will have it happen to them to confirm.

### Expected Behavior

_No response_

### Actual Behavior

See above


Discussion / hints:
I have same issue I tried change preview to stable version but it didn't change anything, still happening this bug.
Does this only happen when running elevated (as Administrator)? this looks similar to #13592 which was just filed. I can't repro this here on Windows 11, but maybe the focus/foreground work attached to #2988 regressed this in Windows 10 😨 
> Does this only happen when running elevated (as Administrator)? this looks similar to #13592 which was just filed. I can't repro this here on Windows 11, but maybe the focus/foreground work attached to #2988 regressed this in Windows 10 😨

Yes, it happen on administrator mod I didn't even notice the administrator mod. 
Sorry, I forgot to mention this in my OP, but it was NOT running as admin at the time. And, if it matters, I had about four or five tabs open, with a mix of PS and cmd.
Alright, I'm deduping here, this will be the master tracking thread. 

* Doesn't seem limited to Windows 10 (at least, I can repro this on win11 in an elevated window _sometimes_)
* Only occurs when minimizing to the taskbar, using the taskbar icon itself.
* Doesn't seem limited to elevated windows (but seems more frequent)
* **If you're stuck in this state, you should be able to hover the icon in the taskbar till you get a preview of the window, and then you can right click -> "Restore" the window that way**

There's also MSFT:40530963 tracking internally.
> * Only occurs when minimizing to the taskbar, using the taskbar icon itself.

Ah, and there it is. Kinda surprised I didn't think to try this, but yeah, if I minimize this way, it happens every time, with a non-admin window. Doesn't get stuck though, just have to click the taskbar button again.
@zadjii-msft I narrowed it down to 6b936d9a74a5d677387f3edc9a68e6b33d566606. Commenting out this line fixes the issue:
https://github.com/microsoft/terminal/blob/feabe41a08604345a1eeb4b38fd1c99b811f8ecd/src/cascadia/TerminalControl/ControlCore.cpp#L1929

I tried various ways to debug this further and fix the issue, but failed at it so far. 😥
I wonder if it's because that results in a xproc call (sending a message to OpenConsole.exe via a pipe) on the main thread. I wonder if user32 doesn't like us doing that... A resume_background right before that would be an interesting experiment 
I have the same issue. when run as Administrator, is not restored if it was minimized
i can always reproduce if i double click terminal icon on taskbar when its minimized. (in administrator mode)
Same problem here, windows 10, elevated terminal.
Though this hint was pretty helpful since I closed many windows with multiple tabs and sessions open so far. Thanks for that.
>  If you're stuck in this state, you should be able to hover the icon in the taskbar till you get a preview of the window, and then you can right click -> "Restore" the window that way
Notes (_ongoing_)

* I was able to get an unelevated dev build of the Terminal into this state, on Windows 11.
* In that state, we only get a few messages when alt-tabbing to the window:
  - `0x7f`
  - `WM_WINDOWPOSCHANGING`
    - the flags are: `0x13` = `SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOMOVE`
    - `hwndInsertAfter` is not a special value.
  - `WM_WINDOWPOSCHANGED`
  - and that's it. There's nothing else, no ShowWindow or anything...
* We don't even get into `ControlCore::_focusChanged` even remotely. Checks out, the window never gets shown so no duh it's not getting focused.
* The current window styles are... `34CF0000` -> `WS_VISIBLE | WS_MINIMIZE` _is_ set...
* The ConPty HWND seems to be owned correctly, and `WS_VISIBLE | WS_MINIMIZE` are both set
* Below is two tabs worth of messages on attempted alt-tabbing:
  ```
  MessageHandler: 0x00000046 WM_WINDOWPOSCHANGING
  InteractivityFactory::PseudoWindowProc 0x00000047 WM_WINDOWPOSCHANGED
  InteractivityFactory::PseudoWindowProc 0x0000000d WM_GETTEXT
  InteractivityFactory::PseudoWindowProc 0x00000047 WM_WINDOWPOSCHANGED
  InteractivityFactory::PseudoWindowProc 0x0000000d WM_GETTEXT
  MessageHandler: 0x00000047 WM_WINDOWPOSCHANGED
  InteractivityFactory::PseudoWindowProc 0x0000001c WM_ACTIVATEAPP
  InteractivityFactory::PseudoWindowProc 0x00000086 WM_NCACTIVATE
  InteractivityFactory::PseudoWindowProc 0x0000000d WM_GETTEXT
  InteractivityFactory::PseudoWindowProc 0x0000000d WM_GETTEXT
  InteractivityFactory::PseudoWindowProc 0x00000006 WM_ACTIVATE
  InteractivityFactory::PseudoWindowProc 0x00000281 WM_IME_SETCONTEXT
  InteractivityFactory::PseudoWindowProc 0x00000282 WM_IME_NOTIFY
  InteractivityFactory::PseudoWindowProc 0x00000007 WM_SETFOCUS
  MessageHandler: 0x0000001e WM_TIMECHANGE
  InteractivityFactory::PseudoWindowProc 0x0000001e WM_TIMECHANGE
  InteractivityFactory::PseudoWindowProc 0x0000001e WM_TIMECHANGE
  ```
* Activating the child conhost seems... weird? But maybe harmless
* This repros way more frequently in a win10 VM. There, **the first time I try to minimize by clicking on the taskbar, it doesn't do anything**. Like, the window just stays open. If I click the taskbar again, it does minimize. THAT is when the window gets stuck. Friggen weird.
* Debugging with Evan:
  * When we first click the taskbar icon, the kernel tries to SwitchToWindow to the main terminal window. That's by itself not that weird. Maybe the OS doesn't think the Terminal window is active & FG. Not terribly uncommon, but still weird.
  * The next click: The main terminal window minimizes, via `xxxminmaximizeex`.
  * The conpty window minimizes as a result (unclear how exactly. Presumably, due to conpty minimizing itself via `ConhostInternalGetSet::ShowWindow`'s call to `::ShowWindowAsync(hwnd, showOrHide ? SW_SHOWNOACTIVATE : SW_MINIMIZE)`
  * At this point, both windows are minimized.
  * You alt-tab to the window. 
  * `xxxSwitchToThisWindow` gets called ON THE CONPTY HWND. Why that one?
  * Why doesn't that end up restoring the terminal HWND? If anything, it restores the conpty hwnd, so it should cause the Terminal to get `SC_RESTORE`'d
* Even outside of the repro case:
  * We hit `IslandWindow::ShowWindowChanged(false)` every time we minimize with the taskbar. What? why. That doesn't make sense, right? Like
  * we DONT get a  `IslandWindow::ShowWindowChanged(true)` on restore. wat
* In the repro case:
  * We don't get a `TerminalPage::_ShowWindowChanged` on the attempted restore. I suspect we never get it out of conpty. 
  * Nope we don't get `bp Microsoft_Terminal_control!winrt::Microsoft::Terminal::Control::implementation::ControlCore::_terminalShowWindowChanged`.
  * Nor do we hit a `bp Microsoft_Terminal_control!Microsoft::Console::VirtualTerminal::AdaptDispatch::WindowManipulation`. So it's not even getting out of the conpty.
In the 10 VM, the taskbar icon is not immediately highlighted when launching the Terminal. As if it doesn't know that the terminal is in the FG. That would explain the initial SwitchToWindow

![gh-13589-000](https://user-images.githubusercontent.com/18356694/181360423-7716a04d-6e37-4144-9ec0-1d813e0c443e.gif)


But then after the "restore", the terminal is indicated as being active. Hmm.
#13158

```c++
    // 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.
    //
    // Should we need to restore this functionality in the future, we should
    // only do so with great caution.
```

Oh hey look, that came back to bite 😠 
Same problem here, windows 10 (20H2), elevated terminal.
This afternoon, every time after I built a project in vs2019, the terminal suddenly disappear.

Fix patch:
diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp
index 7464aae2382..b82bc9d3d99 100644
--- a/src/cascadia/WindowsTerminal/IslandWindow.cpp
+++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp
@@ -844,10 +844,20 @@ void IslandWindow::SetAlwaysOnTop(const bool alwaysOnTop)
 // - <none>
 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<void(bool)> 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<void(bool)> 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<void(bool)> fun
 // - <none>
 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);
     }


Test patch: