Repository: microsoft/terminal (language: cpp)

Issue:
Mouse pointer "vanishes" when moved over Terminal Window
              📝where'd this go

_Originally posted by @zadjii-msft in https://github.com/microsoft/terminal/pull/18215#discussion_r1880139859_
            
Probably this


Discussion / hints:
@inoperable Could you add any more details/? That PR merged literally 5 days ago, I'm not sure it's even in a canary build currently
At some point the mouse pointer vanishes permanently after alt tab, I'm not sure what triggers it though. When restarting the terminal it seems fine for a while and then it start again. 

It's weird, I thought it's because software rendering first, but nope,also toggled some other settings but I can't figure out when exactly it decides I don't need a mouse pointer shown at all.

Will update if I can pinpoint it more

Drives me nuts 
It trigger must be in some onmouseover event handling on terminal window, not over the tab strip or borders (if that's of sny help). I'll get back to once I got more
This is behavior that's on-by-default for most text fields in Windows. When you start typing in a text input, the cursor vanishes. 

Can you check if this setting is on: (just searching for "Pointer" should get you there)
![Image](https://github.com/user-attachments/assets/da3b4d85-4b15-4e61-8920-98367e1eef25)

![Image](https://github.com/user-attachments/assets/32714634-1ccf-4ef2-9585-8d617582ba9f)

Oh, and please confirm with us what exact version(-number) of Windows Terminal you're using. 🙂
It's not the behavior intended, I'm very sure about it, the is setting OS mouse settings is a good point to eliminate culprits - but it's not it.
What happens is that the pointer gets hidden all the time once the terminal window is in focus and the mouse pointer hovers over it. I can see settings buttons highlighting (as per on mouse over event) but no mouse pointer.

Term is 1.23.241213001-llm, OS 27764.1000 & 19045.4651
And this seems to be only a 27xxx insiders build exklusive, since I can't replicate it with 19045.xxxx 

o.O - so I guess I just wasted your time unnecessary...
It's definitely a insiders build exclusive, I couldn't replicate the same on 19045.4651. I apologize for confusions therefore, maybe this is not entirely a waste of time since this might make a come back agai, I'll fiddle around with the insiders build again and will update this thread once I know more when/how
It's possible that this is a bug across any Windows 11 version, because I did struggle quite a bit with making cursor hiding work right. The problem is that the WinUI APIs for doing so are just kind of bad and not very robust. It would not surprise me if they broke between Windows versions.
Leading theory: The current cursor that we get from the WinUI API is non-null _but also_ hidden. This means that when we restore it, we restore a hidden cursor that won't ever go away, because nothing explicitly sets a visible cursor in our code either. Ideally, WinUI would have a cursor stack that you can push/pop into, but in the absence of that we'll just do the next best thing:
* Remove this code:
  https://github.com/microsoft/terminal/blob/8598ed78d58cf6b10372fca4132bfa9553ac6735/src/cascadia/TerminalControl/TermControl.cpp#L99-L103
* Explicitly restore a visible cursor here:
  https://github.com/microsoft/terminal/blob/8598ed78d58cf6b10372fca4132bfa9553ac6735/src/cascadia/TerminalControl/TermControl.cpp#L79-L80
Hello Again, it actually happens again on Win10 and Win11, I caught the bug after a while in Win10 as well. I cant figure out though when/how its triggered. 

![Image](https://github.com/user-attachments/assets/46cbb406-3c13-4bdb-b443-b639867ca1bf)
No mouse pointer, but the mouse hovers over the button and it reacts with highlighting

![Image](https://github.com/user-attachments/assets/51693d28-bba7-4ffd-835f-6ef46a2d31b3)
Here the same thing, this happens in settiings and in terminal window
![Image](https://github.com/user-attachments/assets/7d2be51b-4079-42ac-a74c-e4051b372cdb)
Invisible Mouse™
So, to clarify @lhecker I think you are right, and have it figured out (as mentioned above) 
@lhecker , @zadjii-msft 

I have another problem (but with cursor of mouse too)

i see this since begin use Windows Terminal terminal-1.17.11461.0 to latest canary release

i see this effect only when use WinSSHTerm + Putty
[https://winsshterm.blogspot.com, https://www.putty.org]

when Alt-Tab from window (WinSSHTerm + Putty) to Windows Terminal window, the mouse cursor set to OS BUSY mode.

No effect when returning from other program. No effect when returning to another window (not WT window).

problem reproduction 100%
open NEW issue for this?
https://github.com/user-attachments/assets/b0170e74-ba67-4323-9244-b766cb9bbe71

Yes, I think that's a separate issue.

Fix patch:
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index 9f17c8692dd..3724edd542c 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -57,7 +57,7 @@ DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::MouseButtonState
 //   the current foreground window, but still on top of another Terminal window in the background.
 static void hideCursorUntilMoved()
 {
-    static CoreCursor previousCursor{ nullptr };
+    static bool cursorIsHidden;
     static const auto shouldVanish = []() {
         BOOL shouldVanish = TRUE;
         SystemParametersInfoW(SPI_GETMOUSEVANISH, 0, &shouldVanish, 0);
@@ -68,16 +68,16 @@ static void hideCursorUntilMoved()
 
         const auto window = CoreWindow::GetForCurrentThread();
         static constexpr auto releaseCapture = [](CoreWindow window, PointerEventArgs) {
-            if (previousCursor)
+            if (cursorIsHidden)
             {
                 window.ReleasePointerCapture();
             }
         };
         static constexpr auto restoreCursor = [](CoreWindow window, PointerEventArgs) {
-            if (previousCursor)
+            if (cursorIsHidden)
             {
-                window.PointerCursor(previousCursor);
-                previousCursor = nullptr;
+                cursorIsHidden = false;
+                window.PointerCursor(CoreCursor{ CoreCursorType::Arrow, 0 });
             }
         };
 
@@ -90,20 +90,14 @@ static void hideCursorUntilMoved()
         return true;
     }();
 
-    if (shouldVanish && !previousCursor)
+    if (shouldVanish && !cursorIsHidden)
     {
         try
         {
             const auto window = CoreWindow::GetForCurrentThread();
-
-            previousCursor = window.PointerCursor();
-            if (!previousCursor)
-            {
-                return;
-            }
-
             window.PointerCursor(nullptr);
             window.SetPointerCapture();
+            cursorIsHidden = true;
         }
         catch (...)
         {
diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp
index c7db8e38e1a..ac255aa95b2 100644
--- a/src/cascadia/WindowsTerminal/AppHost.cpp
+++ b/src/cascadia/WindowsTerminal/AppHost.cpp
@@ -815,7 +815,7 @@ void AppHost::_WindowActivated(bool activated)
 {
     _windowLogic.WindowActivated(activated);
 
-    if (activated && _isWindowInitialized != WindowInitializedState::NotInitialized)
+    if (activated)
     {
         QueryPerformanceCounter(&_lastActivatedTime);
         _virtualDesktopId = {};
diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp
index 6c25d4c8ade..dd9499d00ec 100644
--- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp
+++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp
@@ -413,13 +413,8 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow)
                 // It almost seems like there's a pattern here...
                 (msg.wParam == VK_SPACE && msg.message == WM_SYSKEYDOWN))
             {
-                if (const auto w = _mostRecentWindow())
-                {
-                    const auto vkey = gsl::narrow_cast<uint32_t>(msg.wParam);
-                    const auto scanCode = gsl::narrow_cast<uint8_t>(msg.lParam >> 16);
-                    w->OnDirectKeyEvent(vkey, scanCode, keyDown);
-                    continue;
-                }
+                _dispatchSpecialKey(msg);
+                continue;
             }
         }
 
@@ -442,6 +437,40 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow)
     __assume(false);
 }
 
+void WindowEmperor::_dispatchSpecialKey(MSG& msg) const
+{
+    const auto hwnd = msg.hwnd;
+    AppHost* window = nullptr;
+
+    // Just in case someone has targed a specific HWND,
+    // we'll try to dispatch it to the corresponding class.
+    // Usually this will not find anything because under WinUI the hidden CoreInput
+    // window is responsible for all input handling (for whatever reason).
+    for (const auto& h : _windows)
+    {
+        const auto w = h->GetWindow();
+        if (w && w->GetHandle() == hwnd)
+        {
+            window = h.get();
+            break;
+        }
+    }
+
+    if (!window)
+    {
+        window = _mostRecentWindow();
+        if (!window)
+        {
+            return;
+        }
+    }
+
+    const auto vkey = gsl::narrow_cast<uint32_t>(msg.wParam);
+    const auto scanCode = gsl::narrow_cast<uint8_t>(msg.lParam >> 16);
+    const bool keyDown = msg.message & 1;
+    window->OnDirectKeyEvent(vkey, scanCode, keyDown);
+}
+
 void WindowEmperor::_dispatchCommandline(winrt::TerminalApp::CommandlineArgs args)
 {
     const auto exitCode = args.ExitCode();
@@ -826,14 +855,10 @@ LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM c
         case WM_QUERYENDSESSION:
             // For WM_QUERYENDSESSION and WM_ENDSESSION, refer to:
             // https://docs.microsoft.com/en-us/windows/win32/rstmgr/guidelines-for-applications
-            if (lParam == ENDSESSION_CLOSEAPP)
-            {
-                // ENDSESSION_CLOSEAPP: The application is using a file that must be replaced,
-                // the system is being serviced, or system resources are exhausted.
-                RegisterApplicationRestart(nullptr, RESTART_NO_CRASH | RESTART_NO_HANG);
-                return TRUE;
-            }
-            return FALSE;
+            // ENDSESSION_CLOSEAPP: The application is using a file that must be replaced,
+            // the system is being serviced, or system resources are exhausted.
+            RegisterApplicationRestart(nullptr, RESTART_NO_CRASH | RESTART_NO_HANG);
+            return TRUE;
         case WM_ENDSESSION:
             _forcePersistence = true;
             PostQuitMessage(0);
diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h
index db3034bc6db..6b9b101507f 100644
--- a/src/cascadia/WindowsTerminal/WindowEmperor.h
+++ b/src/cascadia/WindowsTerminal/WindowEmperor.h
@@ -50,6 +50,7 @@ class WindowEmperor
     AppHost* _mostRecentWindow() const noexcept;
     bool _summonWindow(const SummonWindowSelectionArgs& args) const;
     void _summonAllWindows() const;
+    void _dispatchSpecialKey(MSG& msg) const;
     void _dispatchCommandline(winrt::TerminalApp::CommandlineArgs args);
     safe_void_coroutine _dispatchCommandlineCurrentDesktop(winrt::TerminalApp::CommandlineArgs args);
     LRESULT _messageHandler(HWND window, UINT message, WPARAM wParam, LPARAM lParam) noexcept;


Test patch: