From 12a5c95b98231c35709f3378dca7124ad5d89d07 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Mon, 29 Apr 2024 12:55:09 +0100 Subject: [PATCH] Fix the DECTCEM reset position in the conpty stream (#17148) ## Summary of the Pull Request When the conpty renderer determines that it needs to hide the cursor, it does so by inserting a `DECTCEM` reset sequence at the start of the output buffer, assuming that is the start of the frame. But when the `_noFlushOnEnd` flag is set, you can have multiple frames pending in the buffer, and the `DECTCEM` sequence will then end up in the wrong place. This PR fixes the issue by saving the buffer size at the start of the frame, and using that saved offset as the insert position for the `DECTCEM` sequence. ## Validation Steps Performed I have a game that was frequently affected by this issue (the cursor would be visible when it was meant to be hidden). With this PR applied, it now works perfectly. ## PR Checklist - [x] Closes #15449 (cherry picked from commit ef318a14502ee933e4034d7b4bf5739d7d394ad1) Service-Card-Id: 92457090 Service-Version: 1.20 --- src/renderer/vt/XtermEngine.cpp | 3 ++- src/renderer/vt/state.cpp | 1 + src/renderer/vt/vtrenderer.hpp | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/renderer/vt/XtermEngine.cpp b/src/renderer/vt/XtermEngine.cpp index 14822351585..75fc3510fa6 100644 --- a/src/renderer/vt/XtermEngine.cpp +++ b/src/renderer/vt/XtermEngine.cpp @@ -45,6 +45,7 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe, // visible, then PaintCursor will be called, and we'll set this to true // during the frame. _nextCursorIsVisible = false; + _startOfFrameBufferIndex = _buffer.size(); // Do not perform synchronization clearing in passthrough mode. // In passthrough, the terminal leads and we follow what it is @@ -112,7 +113,7 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe, // by prepending a cursor off. if (_lastCursorIsVisible != Tribool::False) { - _buffer.insert(0, "\x1b[?25l"); + _buffer.insert(_startOfFrameBufferIndex, "\x1b[?25l"); _lastCursorIsVisible = Tribool::False; } // If the cursor was NOT previously visible, then that's fine! we don't diff --git a/src/renderer/vt/state.cpp b/src/renderer/vt/state.cpp index 5bb6b7d694d..46b1d872088 100644 --- a/src/renderer/vt/state.cpp +++ b/src/renderer/vt/state.cpp @@ -149,6 +149,7 @@ void VtEngine::_flushImpl() noexcept { const auto fSuccess = WriteFile(_hFile.get(), _buffer.data(), gsl::narrow_cast(_buffer.size()), nullptr, nullptr); _buffer.clear(); + _startOfFrameBufferIndex = 0; if (!fSuccess) { LOG_LAST_ERROR(); diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index 676672ee814..9e8f8982756 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -94,6 +94,7 @@ namespace Microsoft::Console::Render protected: wil::unique_hfile _hFile; std::string _buffer; + size_t _startOfFrameBufferIndex = 0; std::string _formatBuffer; std::string _conversionBuffer;