diff --git a/src/host/getset.cpp b/src/host/getset.cpp index eaff59cb3e8..66746140276 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -1341,7 +1341,14 @@ void DoSrvPrivateShowCursor(SCREEN_INFORMATION& screenInfo, const bool show) noe void DoSrvPrivateAllowCursorBlinking(SCREEN_INFORMATION& screenInfo, const bool fEnable) { screenInfo.GetActiveBuffer().GetTextBuffer().GetCursor().SetBlinkingAllowed(fEnable); - screenInfo.GetActiveBuffer().GetTextBuffer().GetCursor().SetIsOn(!fEnable); + + // GH#2642 - From what we've gathered from other terminals, when blinking is + // disabled, the cursor should remain On always, and have the visibility + // controlled by the IsVisible property. So when you do a printf "\e[?12l" + // to disable blinking, the cursor stays stuck On. At this point, only the + // cursor visibility property controls whether the user can see it or not. + // (Yes, the cursor can be On and NOT Visible) + screenInfo.GetActiveBuffer().GetTextBuffer().GetCursor().SetIsOn(true); } // Routine Description: diff --git a/src/host/output.cpp b/src/host/output.cpp index b2446cfc614..e81af6efd10 100644 --- a/src/host/output.cpp +++ b/src/host/output.cpp @@ -457,8 +457,20 @@ void SetActiveScreenBuffer(SCREEN_INFORMATION& screenInfo) CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); gci.pCurrentScreenBuffer = &screenInfo; - // initialize cursor - screenInfo.GetTextBuffer().GetCursor().SetIsOn(false); + // initialize cursor GH#4102 - Typically, the cursor is set to on by the + // cursor blinker. Unfortunately, in conpty mode, there is no cursor + // blinker. So, in conpty mode, we need to leave the cursor on always. The + // cursor can still be set to hidden, and whether the cursor should be + // blinking will still be passed through to the terminal, but internally, + // the cursor should always be on. + // + // In particular, some applications make use of a calling + // `SetConsoleScreenBuffer` and `SetCursorPosition` without printing any + // text in between these calls. If we initialize the cursor to Off in conpty + // mode, then the cursor will remain off until they print text. This can + // lead to alignment problems in the terminal, because we won't move the + // terminal's cursor in this _exact_ scenario. + screenInfo.GetTextBuffer().GetCursor().SetIsOn(gci.IsInVtIoMode()); // set font screenInfo.RefreshFontWithRenderer(); diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index 17af02adaef..26b7e1fc5bb 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -208,6 +208,8 @@ class ScreenBufferTests TEST_METHOD(CursorSaveRestore); TEST_METHOD(ScreenAlignmentPattern); + + TEST_METHOD(TestCursorIsOn); }; void ScreenBufferTests::SingleAlternateBufferCreationTest() @@ -5730,3 +5732,54 @@ void ScreenBufferTests::ScreenAlignmentPattern() expectedAttr.SetMetaAttributes(0); VERIFY_ARE_EQUAL(expectedAttr, si.GetAttributes()); } + +void ScreenBufferTests::TestCursorIsOn() +{ + auto& g = ServiceLocator::LocateGlobals(); + auto& gci = g.getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer(); + auto& tbi = si.GetTextBuffer(); + auto& stateMachine = si.GetStateMachine(); + auto& cursor = tbi.GetCursor(); + + stateMachine.ProcessString(L"Hello World"); + VERIFY_IS_TRUE(cursor.IsOn()); + VERIFY_IS_TRUE(cursor.IsBlinkingAllowed()); + VERIFY_IS_TRUE(cursor.IsVisible()); + + stateMachine.ProcessString(L"\x1b[?12l"); + VERIFY_IS_TRUE(cursor.IsOn()); + VERIFY_IS_FALSE(cursor.IsBlinkingAllowed()); + VERIFY_IS_TRUE(cursor.IsVisible()); + + stateMachine.ProcessString(L"\x1b[?12h"); + VERIFY_IS_TRUE(cursor.IsOn()); + VERIFY_IS_TRUE(cursor.IsBlinkingAllowed()); + VERIFY_IS_TRUE(cursor.IsVisible()); + + cursor.SetIsOn(false); + stateMachine.ProcessString(L"\x1b[?12l"); + VERIFY_IS_TRUE(cursor.IsOn()); + VERIFY_IS_FALSE(cursor.IsBlinkingAllowed()); + VERIFY_IS_TRUE(cursor.IsVisible()); + + stateMachine.ProcessString(L"\x1b[?12h"); + VERIFY_IS_TRUE(cursor.IsOn()); + VERIFY_IS_TRUE(cursor.IsBlinkingAllowed()); + VERIFY_IS_TRUE(cursor.IsVisible()); + + stateMachine.ProcessString(L"\x1b[?25l"); + VERIFY_IS_TRUE(cursor.IsOn()); + VERIFY_IS_TRUE(cursor.IsBlinkingAllowed()); + VERIFY_IS_FALSE(cursor.IsVisible()); + + stateMachine.ProcessString(L"\x1b[?25h"); + VERIFY_IS_TRUE(cursor.IsOn()); + VERIFY_IS_TRUE(cursor.IsBlinkingAllowed()); + VERIFY_IS_TRUE(cursor.IsVisible()); + + stateMachine.ProcessString(L"\x1b[?12;25l"); + VERIFY_IS_TRUE(cursor.IsOn()); + VERIFY_IS_FALSE(cursor.IsBlinkingAllowed()); + VERIFY_IS_FALSE(cursor.IsVisible()); +}