From 12e3455bb2bd62745fad87e1924ec8ef115ec54a Mon Sep 17 00:00:00 2001 From: aarushi singh <110608667+aarushisingh04@users.noreply.github.com> Date: Thu, 14 May 2026 22:34:58 +0530 Subject: [PATCH] Make DECSTR cursor restore behave like xterm in alt screen buffer (#20032) There is some disagreement among terminal emulators as to whether DECSTR (and therefore, soft reset) restores the cursor in the active buffer or in all buffers. We had previously chosen to restore the cursor in all buffers. xterm restores the cursor only in the active buffer. Closes #19918 --- src/host/ut_host/ScreenBufferTests.cpp | 25 +++++++++++++++++++++++++ src/terminal/adapter/adaptDispatch.cpp | 14 ++++++-------- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index bd54b1b462..fa3fbe5412 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -123,6 +123,7 @@ class ScreenBufferTests TEST_METHOD(VtResizePreservingAttributes); TEST_METHOD(VtSoftResetCursorPosition); + TEST_METHOD(VtSoftResetAltBufferCursorState); TEST_METHOD(VtScrollMarginsNewlineColor); @@ -1510,6 +1511,30 @@ void ScreenBufferTests::VtSoftResetCursorPosition() VERIFY_ARE_EQUAL(til::point(1, 1), cursor.GetPosition()); } +void ScreenBufferTests::VtSoftResetAltBufferCursorState() +{ + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + gci.LockConsole(); // Lock must be taken to manipulate buffer. + auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); }); + + auto& si = gci.GetActiveOutputBuffer(); + auto& stateMachine = si.GetStateMachine(); + + Log::Comment(L"Move cursor on the main buffer."); + stateMachine.ProcessString(L"\x1b[4;7H"); + VERIFY_ARE_EQUAL(til::point(6, 3), si.GetTextBuffer().GetCursor().GetPosition()); + + Log::Comment(L"Enter alt buffer, soft reset, and return to main buffer."); + stateMachine.ProcessString(L"\x1b[?1049h"); + VERIFY_IS_TRUE(gci.GetActiveOutputBuffer()._IsAltBuffer()); + stateMachine.ProcessString(L"\x1b[!p"); + stateMachine.ProcessString(L"\x1b[?1049l"); + VERIFY_IS_FALSE(gci.GetActiveOutputBuffer()._IsAltBuffer()); + + Log::Comment(L"Returning from alt buffer should restore the main cursor position."); + VERIFY_ARE_EQUAL(til::point(6, 3), gci.GetActiveOutputBuffer().GetTextBuffer().GetCursor().GetPosition()); +} + void ScreenBufferTests::VtScrollMarginsNewlineColor() { auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index e1fdb6a69c..633156c51d 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -3002,17 +3002,15 @@ void AdaptDispatch::SoftReset() SetGraphicsRendition({}); // Normal rendition. SetCharacterProtectionAttribute({}); // Default (unprotected) - // Reset the saved cursor state. - // Note that XTerm only resets the main buffer state, but that - // seems likely to be a bug. Most other terminals reset both. - _savedCursorState.at(0) = {}; // Main buffer - _savedCursorState.at(1) = {}; // Alt buffer + // Reset only the active saved cursor state. + // This matches xterm behavior when DECSTR is processed while using + // the alternate screen buffer (GH#19918). + _savedCursorState.at(_usingAltBuffer ? 1 : 0) = {}; - // The TerminalOutput state in these buffers must be reset to + // The TerminalOutput state in this buffer must be reset to // the same state as the _termOutput instance, which is not // necessarily equivalent to a full reset. - _savedCursorState.at(0).TermOutput = _termOutput; - _savedCursorState.at(1).TermOutput = _termOutput; + _savedCursorState.at(_usingAltBuffer ? 1 : 0).TermOutput = _termOutput; // Soft reset the Sixel parser if in use. if (_sixelParser)