diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index 815e1f808ca..c1c9b536626 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -1455,6 +1455,7 @@ bool SCREEN_INFORMATION::IsMaximizedY() const const short cursorHeightInViewportAfter = newCursor.GetPosition().Y - _viewport.Top(); COORD coordCursorHeightDiff = { 0 }; coordCursorHeightDiff.Y = cursorHeightInViewportAfter - cursorHeightInViewportBefore; + LOG_IF_FAILED(SetViewportOrigin(false, coordCursorHeightDiff, true)); _textBuffer.swap(newTextBuffer); @@ -1476,7 +1477,6 @@ bool SCREEN_INFORMATION::IsMaximizedY() const if (foundWrappedLine || charRow.WasWrapForced()) { foundWrappedLine = true; - _renderTarget.TriggerRedraw(Viewport::FromDimensions({ 0, y }, _viewport.Width(), 1)); } } } diff --git a/src/renderer/vt/XtermEngine.cpp b/src/renderer/vt/XtermEngine.cpp index 2c1d8a08ed1..f652ceb3d52 100644 --- a/src/renderer/vt/XtermEngine.cpp +++ b/src/renderer/vt/XtermEngine.cpp @@ -19,7 +19,7 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe, _ColorTable(ColorTable), _cColorTable(cColorTable), _fUseAsciiOnly(fUseAsciiOnly), - _previousLineWrapped(false), + // _previousLineWrapped(false), _usingUnderLine(false), _needToDisableCursor(false), _lastCursorIsVisible(false), @@ -235,6 +235,8 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe, { HRESULT hr = S_OK; + _trace.TraceMoveCursor(coord); + if (coord.X != _lastText.X || coord.Y != _lastText.Y) { if (coord.X == 0 && coord.Y == 0) @@ -248,8 +250,15 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe, // If the previous line wrapped, then the cursor is already at this // position, we just don't know it yet. Don't emit anything. - if (_previousLineWrapped) + bool previousLineWrapped = false; + if (_wrappedRow.has_value()) + { + previousLineWrapped = coord.Y == _wrappedRow.value() + 1; + } + + if (previousLineWrapped) { + _trace.TraceWrapped(); hr = S_OK; } else @@ -298,6 +307,9 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe, _newBottomLine = false; } _deferredCursorPos = INVALID_COORDS; + + _wrappedRow = std::nullopt; + return hr; } diff --git a/src/renderer/vt/paint.cpp b/src/renderer/vt/paint.cpp index 920dc86aeb2..bda79f6197a 100644 --- a/src/renderer/vt/paint.cpp +++ b/src/renderer/vt/paint.cpp @@ -376,8 +376,6 @@ using namespace Microsoft::Console::Types; return S_OK; } - RETURN_IF_FAILED(_MoveCursor(coord)); - std::wstring unclusteredString; unclusteredString.reserve(clusters.size()); short totalWidth = 0; @@ -445,10 +443,37 @@ using namespace Microsoft::Console::Types; (totalWidth - numSpaces) : totalWidth; + if (cchActual == 0) + { + // If the previous row wrapped, but this line is empty, then we actually + // do want to move the cursor down. Otherwise, we'll possibly end up + // accidentally erasing the last character from the previous line, as + // the cursor is still waiting on that character for the next character + // to follow it. + _wrappedRow = std::nullopt; + // TODO:: Write a test that emulates ~/vttests/reflow-120.py + // TODO:: Write a test that emulates ~/vttests/reflow-advanced.py + } + + // Move the cursor to the start of this run. + RETURN_IF_FAILED(_MoveCursor(coord)); + // Write the actual text string std::wstring wstr = std::wstring(unclusteredString.data(), cchActual); RETURN_IF_FAILED(VtEngine::_WriteTerminalUtf8(wstr)); + // If we've written text to the last column of the viewport, then mark + // that we've wrapped this line. The next time we attempt to move the + // cursor, if we're trying to move it to the start of the next line, + // we'll remember that this line was wrapped, and not manually break the + // line. + // Don't do this is the last character we're writing is a space - The last + // char will always be a space, but if we see that, we shouldn't wrap. + if ((_lastText.X + (totalWidth - numSpaces)) > _lastViewport.RightInclusive()) + { + _wrappedRow = coord.Y; + } + // Update our internal tracker of the cursor's position. // See MSFT:20266233 // If the cursor is at the rightmost column of the terminal, and we write a diff --git a/src/renderer/vt/tracing.cpp b/src/renderer/vt/tracing.cpp index 3c5f4e0aa63..04c8d54c8b5 100644 --- a/src/renderer/vt/tracing.cpp +++ b/src/renderer/vt/tracing.cpp @@ -210,3 +210,30 @@ void RenderTracing::TraceLastText(const COORD lastTextPos) const UNREFERENCED_PARAMETER(lastTextPos); #endif UNIT_TESTING } + +void RenderTracing::TraceMoveCursor(const COORD pos) const +{ +#ifndef UNIT_TESTING + const auto lastTextStr = _CoordToString(pos); + const auto cursor = lastTextStr.c_str(); + TraceLoggingWrite(g_hConsoleVtRendererTraceProvider, + "VtEngine_TraceMoveCursor", + TraceLoggingString(cursor), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); +#else + UNREFERENCED_PARAMETER(pos); +#endif UNIT_TESTING +} + +void RenderTracing::TraceWrapped() const +{ +#ifndef UNIT_TESTING + const auto* const msg = "Wrapped instead of \\r\\n"; + TraceLoggingWrite(g_hConsoleVtRendererTraceProvider, + "VtEngine_TraceWrapped", + TraceLoggingString(msg), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); +#else + UNREFERENCED_PARAMETER(pos); +#endif UNIT_TESTING +} diff --git a/src/renderer/vt/tracing.hpp b/src/renderer/vt/tracing.hpp index 8d923adc813..ef00bea81be 100644 --- a/src/renderer/vt/tracing.hpp +++ b/src/renderer/vt/tracing.hpp @@ -29,6 +29,8 @@ namespace Microsoft::Console::VirtualTerminal void TraceString(const std::string_view& str) const; void TraceInvalidate(const Microsoft::Console::Types::Viewport view) const; void TraceLastText(const COORD lastText) const; + void TraceMoveCursor(const COORD pos) const; + void TraceWrapped() const; void TraceInvalidateAll(const Microsoft::Console::Types::Viewport view) const; void TraceTriggerCircling(const bool newFrame) const; void TraceStartPaint(const bool quickReturn, diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index cedd2722f53..ade20e90c1b 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -144,6 +144,8 @@ namespace Microsoft::Console::Render Microsoft::Console::VirtualTerminal::RenderTracing _trace; bool _inResizeRequest{ false }; + std::optional _wrappedRow{ std::nullopt }; + [[nodiscard]] HRESULT _Write(std::string_view const str) noexcept; [[nodiscard]] HRESULT _WriteFormattedString(const std::string* const pFormat, ...) noexcept; [[nodiscard]] HRESULT _Flush() noexcept;