From 4b8ff8f1b007369c19ec77a8b4aba7ec84a76570 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 10 Feb 2022 20:24:05 +0000 Subject: [PATCH 01/28] Add GetTextBuffer and GetViewport to ConGetSet interface. --- src/host/outputStream.cpp | 22 ++++++++++++++++++++++ src/host/outputStream.hpp | 3 +++ src/terminal/adapter/conGetSet.hpp | 5 ++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 1b3ba0a2608..1c4db2f4e0d 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -57,6 +57,28 @@ void ConhostInternalGetSet::PrintString(const std::wstring_view string) THROW_IF_NTSTATUS_FAILED(ntstatus); } +// Routine Description: +// - Retrieves the text buffer for the active output buffer. +// Arguments: +// - +// Return Value: +// - a reference to the TextBuffer instance. +TextBuffer& ConhostInternalGetSet::GetTextBuffer() +{ + return _io.GetActiveOutputBuffer().GetTextBuffer(); +} + +// Routine Description: +// - Retrieves the virtual viewport of the active output buffer. +// Arguments: +// - +// Return Value: +// - the exclusive coordinates of the viewport. +SMALL_RECT ConhostInternalGetSet::GetViewport() const +{ + return _io.GetActiveOutputBuffer().GetVirtualViewport().ToExclusive(); +} + // Routine Description: // - Connects the GetConsoleScreenBufferInfoEx API call directly into our Driver Message servicing call inside Conhost.exe // Arguments: diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 458e1ab550f..17d375331f6 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -31,6 +31,9 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void PrintString(const std::wstring_view string) override; + TextBuffer& GetTextBuffer() override; + SMALL_RECT GetViewport() const override; + void GetConsoleScreenBufferInfoEx(CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) const override; void SetConsoleScreenBufferInfoEx(const CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) override; diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 1cfe0d2613b..da350e22d62 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -19,7 +19,7 @@ Author(s): #include "../parser/stateMachine.hpp" #include "../../types/inc/IInputEvent.hpp" #include "../../buffer/out/LineRendition.hpp" -#include "../../buffer/out/TextAttribute.hpp" +#include "../../buffer/out/textBuffer.hpp" #include "../../renderer/inc/RenderSettings.hpp" #include "../../inc/conattrs.hpp" @@ -37,6 +37,9 @@ namespace Microsoft::Console::VirtualTerminal virtual void PrintString(const std::wstring_view string) = 0; + virtual TextBuffer& GetTextBuffer() = 0; + virtual SMALL_RECT GetViewport() const = 0; + virtual void GetConsoleScreenBufferInfoEx(CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) const = 0; virtual void SetConsoleScreenBufferInfoEx(const CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) = 0; virtual void SetCursorPosition(const COORD position) = 0; From 6f5b9d32cc2b3644a1f1f8d889a9f3283ac8ef4b Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 10 Feb 2022 20:30:22 +0000 Subject: [PATCH 02/28] Replace most GetConsoleScreenBufferInfoEx usage. --- src/terminal/adapter/InteractDispatch.cpp | 15 +- src/terminal/adapter/adaptDispatch.cpp | 254 ++++++++-------------- src/terminal/adapter/adaptDispatch.hpp | 2 +- 3 files changed, 101 insertions(+), 170 deletions(-) diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp index 81b7290dc7c..df3f38d9e2e 100644 --- a/src/terminal/adapter/InteractDispatch.cpp +++ b/src/terminal/adapter/InteractDispatch.cpp @@ -135,23 +135,20 @@ bool InteractDispatch::MoveCursor(const size_t row, const size_t col) const size_t colFixed = col - 1; // First retrieve some information about the buffer - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); - - COORD coordCursor = csbiex.dwCursorPosition; + const auto viewport = _pConApi->GetViewport(); + auto coordCursor = _pConApi->GetTextBuffer().GetCursor().GetPosition(); // Safely convert the size_t positions we were given into shorts (which is the size the console deals with) THROW_IF_FAILED(SizeTToShort(rowFixed, &coordCursor.Y)); THROW_IF_FAILED(SizeTToShort(colFixed, &coordCursor.X)); // Set the line and column values as offsets from the viewport edge. Use safe math to prevent overflow. - THROW_IF_FAILED(ShortAdd(coordCursor.Y, csbiex.srWindow.Top, &coordCursor.Y)); - THROW_IF_FAILED(ShortAdd(coordCursor.X, csbiex.srWindow.Left, &coordCursor.X)); + THROW_IF_FAILED(ShortAdd(coordCursor.Y, viewport.Top, &coordCursor.Y)); + THROW_IF_FAILED(ShortAdd(coordCursor.X, viewport.Left, &coordCursor.X)); // Apply boundary tests to ensure the cursor isn't outside the viewport rectangle. - coordCursor.Y = std::clamp(coordCursor.Y, csbiex.srWindow.Top, gsl::narrow(csbiex.srWindow.Bottom - 1)); - coordCursor.X = std::clamp(coordCursor.X, csbiex.srWindow.Left, gsl::narrow(csbiex.srWindow.Right - 1)); + coordCursor.Y = std::clamp(coordCursor.Y, viewport.Top, gsl::narrow(viewport.Bottom - 1)); + coordCursor.X = std::clamp(coordCursor.X, viewport.Left, gsl::narrow(viewport.Right - 1)); // Finally, attempt to set the adjusted cursor position back into the console. _pConApi->SetCursorPosition(coordCursor); diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 773087fa539..4f0b6f8be6e 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -170,33 +170,25 @@ bool AdaptDispatch::CursorPrevLine(const size_t distance) bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset colOffset, const bool clampInMargins) const { // First retrieve some information about the buffer - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - // Make sure to reset the viewport (with MoveToBottom )to where it was - // before the user scrolled the console output - _pConApi->MoveToBottom(); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); - - // Calculate the viewport boundaries as inclusive values. - // srWindow is exclusive so we need to subtract 1 from the bottom. - const int viewportTop = csbiex.srWindow.Top; - const int viewportBottom = csbiex.srWindow.Bottom - 1; + const auto viewport = _pConApi->GetViewport(); + const auto& textBuffer = _pConApi->GetTextBuffer(); + const auto cursorPosition = textBuffer.GetCursor().GetPosition(); // Calculate the absolute margins of the scrolling area. - const int topMargin = viewportTop + _scrollMargins.Top; - const int bottomMargin = viewportTop + _scrollMargins.Bottom; + const int topMargin = viewport.Top + _scrollMargins.Top; + const int bottomMargin = viewport.Top + _scrollMargins.Bottom; const bool marginsSet = topMargin < bottomMargin; // For relative movement, the given offsets will be relative to // the current cursor position. - int row = csbiex.dwCursorPosition.Y; - int col = csbiex.dwCursorPosition.X; + int row = cursorPosition.Y; + int col = cursorPosition.X; // But if the row is absolute, it will be relative to the top of the // viewport, or the top margin, depending on the origin mode. if (rowOffset.IsAbsolute) { - row = _isOriginModeRelative ? topMargin : viewportTop; + row = _isOriginModeRelative ? topMargin : viewport.Top; } // And if the column is absolute, it'll be relative to column 0. @@ -209,8 +201,8 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col // Adjust the base position by the given offsets and clamp the results. // The row is constrained within the viewport's vertical boundaries, // while the column is constrained by the buffer width. - row = std::clamp(row + rowOffset.Value, viewportTop, viewportBottom); - col = std::clamp(col + colOffset.Value, 0, csbiex.dwSize.X - 1); + row = std::clamp(row + rowOffset.Value, viewport.Top, viewport.Bottom - 1); + col = std::clamp(col + colOffset.Value, 0, textBuffer.GetSize().Width() - 1); // If the operation needs to be clamped inside the margins, or the origin // mode is relative (which always requires margin clamping), then the row @@ -225,11 +217,11 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col // to the bottom margin. See // ScreenBufferTests::CursorUpDownOutsideMargins for a test of that // behavior. - if (csbiex.dwCursorPosition.Y >= topMargin) + if (cursorPosition.Y >= topMargin) { row = std::max(row, topMargin); } - if (csbiex.dwCursorPosition.Y <= bottomMargin) + if (cursorPosition.Y <= bottomMargin) { row = std::min(row, bottomMargin); } @@ -311,24 +303,19 @@ bool AdaptDispatch::CursorPosition(const size_t line, const size_t column) bool AdaptDispatch::CursorSaveState() { // First retrieve some information about the buffer - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - // Make sure to reset the viewport (with MoveToBottom )to where it was - // before the user scrolled the console output - _pConApi->MoveToBottom(); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); - - const auto attributes = _pConApi->GetTextAttributes(); + const auto viewport = _pConApi->GetViewport(); + const auto& textBuffer = _pConApi->GetTextBuffer(); + const auto attributes = textBuffer.GetCurrentAttributes(); // The cursor is given to us by the API as relative to the whole buffer. // But in VT speak, the cursor row should be relative to the current viewport top. - COORD coordCursor = csbiex.dwCursorPosition; - coordCursor.Y -= csbiex.srWindow.Top; + auto cursorPosition = textBuffer.GetCursor().GetPosition(); + cursorPosition.Y -= viewport.Top; // VT is also 1 based, not 0 based, so correct by 1. auto& savedCursorState = _savedCursorState.at(_usingAltBuffer); - savedCursorState.Column = coordCursor.X + 1; - savedCursorState.Row = coordCursor.Y + 1; + savedCursorState.Column = cursorPosition.X + 1; + savedCursorState.Row = cursorPosition.Y + 1; savedCursorState.IsOriginModeRelative = _isOriginModeRelative; savedCursorState.Attributes = attributes; savedCursorState.TermOutput = _termOutput; @@ -414,26 +401,21 @@ void AdaptDispatch::_InsertDeleteHelper(const size_t count, const bool isInsert) SHORT distance; THROW_IF_FAILED(SizeTToShort(count, &distance)); - // get current cursor, attributes - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - // Make sure to reset the viewport (with MoveToBottom )to where it was - // before the user scrolled the console output - _pConApi->MoveToBottom(); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); + // get current cursor + const auto& textBuffer = _pConApi->GetTextBuffer(); + const auto cursorPosition = textBuffer.GetCursor().GetPosition(); - const auto cursor = csbiex.dwCursorPosition; // Rectangle to cut out of the existing buffer. This is inclusive. SMALL_RECT srScroll; - srScroll.Left = cursor.X; - srScroll.Right = _pConApi->GetLineWidth(cursor.Y) - 1; - srScroll.Top = cursor.Y; + srScroll.Left = cursorPosition.X; + srScroll.Right = textBuffer.GetLineWidth(cursorPosition.Y) - 1; + srScroll.Top = cursorPosition.Y; srScroll.Bottom = srScroll.Top; // Paste coordinate for cut text above COORD coordDestination; - coordDestination.Y = cursor.Y; - coordDestination.X = cursor.X; + coordDestination.Y = cursorPosition.Y; + coordDestination.X = cursorPosition.X; if (isInsert) { @@ -480,13 +462,13 @@ bool AdaptDispatch::DeleteCharacter(const size_t count) // - Internal helper to erase one particular line of the buffer. Either from beginning to the cursor, from the cursor to the end, or the entire line. // - Used by both erase line (used just once) and by erase screen (used in a loop) to erase a portion of the buffer. // Arguments: -// - csbiex - Pointer to the console screen buffer that we will be erasing (and getting cursor data from within) +// - textBuffer - the text buffer that we will be erasing (and getting cursor data from within) // - eraseType - Enumeration mode of which kind of erase to perform: beginning to cursor, cursor to end, or entire line. // - lineId - The line number (array index value, starts at 0) of the line to operate on within the buffer. // - This is not aware of circular buffer. Line 0 is always the top visible line if you scrolled the whole way up the window. // Return Value: // - -void AdaptDispatch::_EraseSingleLineHelper(const CONSOLE_SCREEN_BUFFER_INFOEX& csbiex, +void AdaptDispatch::_EraseSingleLineHelper(const TextBuffer& textBuffer, const DispatchTypes::EraseType eraseType, const size_t lineId) const { @@ -502,7 +484,7 @@ void AdaptDispatch::_EraseSingleLineHelper(const CONSOLE_SCREEN_BUFFER_INFOEX& c coordStartPosition.X = 0; // from beginning and the whole line start from the left most edge of the buffer. break; case DispatchTypes::EraseType::ToEnd: - coordStartPosition.X = csbiex.dwCursorPosition.X; // from the current cursor position (including it) + coordStartPosition.X = textBuffer.GetCursor().GetPosition().X; // from the current cursor position (including it) break; } @@ -513,12 +495,12 @@ void AdaptDispatch::_EraseSingleLineHelper(const CONSOLE_SCREEN_BUFFER_INFOEX& c { case DispatchTypes::EraseType::FromBeginning: // +1 because if cursor were at the left edge, the length would be 0 and we want to paint at least the 1 character the cursor is on. - nLength = csbiex.dwCursorPosition.X + 1; + nLength = textBuffer.GetCursor().GetPosition().X + 1; break; case DispatchTypes::EraseType::ToEnd: case DispatchTypes::EraseType::All: // Remember the .X value is 1 farther than the right most column in the buffer. Therefore no +1. - nLength = _pConApi->GetLineWidth(lineId) - coordStartPosition.X; + nLength = textBuffer.GetLineWidth(lineId) - coordStartPosition.X; break; } @@ -537,13 +519,10 @@ void AdaptDispatch::_EraseSingleLineHelper(const CONSOLE_SCREEN_BUFFER_INFOEX& c // - True. bool AdaptDispatch::EraseCharacters(const size_t numChars) { - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); + const auto& textBuffer = _pConApi->GetTextBuffer(); + const COORD startPosition = textBuffer.GetCursor().GetPosition(); - const COORD startPosition = csbiex.dwCursorPosition; - - const SHORT remainingSpaces = csbiex.dwSize.X - startPosition.X; + const SHORT remainingSpaces = textBuffer.GetSize().Dimensions().X - startPosition.X; const size_t actualRemaining = gsl::narrow_cast((remainingSpaces < 0) ? 0 : remainingSpaces); // erase at max the number of characters remaining in the line from the current position. const auto eraseLength = (numChars <= actualRemaining) ? numChars : actualRemaining; @@ -594,12 +573,9 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) return !_pConApi->IsConsolePty(); } - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - // Make sure to reset the viewport (with MoveToBottom )to where it was - // before the user scrolled the console output - _pConApi->MoveToBottom(); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); + const auto viewport = _pConApi->GetViewport(); + auto& textBuffer = _pConApi->GetTextBuffer(); + const auto cursorPosition = textBuffer.GetCursor().GetPosition(); // When erasing the display, every line that is erased in full should be // reset to single width. When erasing to the end, this could include @@ -609,13 +585,13 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) // the line is double width). if (eraseType == DispatchTypes::EraseType::FromBeginning) { - const auto endRow = csbiex.dwCursorPosition.Y; - _pConApi->ResetLineRenditionRange(csbiex.srWindow.Top, endRow); + const auto endRow = cursorPosition.Y; + textBuffer.ResetLineRenditionRange(viewport.Top, endRow); } if (eraseType == DispatchTypes::EraseType::ToEnd) { - const auto startRow = csbiex.dwCursorPosition.Y + (csbiex.dwCursorPosition.X > 0 ? 1 : 0); - _pConApi->ResetLineRenditionRange(startRow, csbiex.srWindow.Bottom); + const auto startRow = cursorPosition.Y + (cursorPosition.X > 0 ? 1 : 0); + textBuffer.ResetLineRenditionRange(startRow, viewport.Bottom); } // What we need to erase is grouped into 3 types: @@ -631,23 +607,23 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) if (eraseType == DispatchTypes::EraseType::FromBeginning) { // For beginning and all, erase all complete lines before (above vertically) from the cursor position. - for (SHORT startLine = csbiex.srWindow.Top; startLine < csbiex.dwCursorPosition.Y; startLine++) + for (SHORT startLine = viewport.Top; startLine < cursorPosition.Y; startLine++) { - _EraseSingleLineHelper(csbiex, DispatchTypes::EraseType::All, startLine); + _EraseSingleLineHelper(textBuffer, DispatchTypes::EraseType::All, startLine); } } // 2. Cursor Line - _EraseSingleLineHelper(csbiex, eraseType, csbiex.dwCursorPosition.Y); + _EraseSingleLineHelper(textBuffer, eraseType, cursorPosition.Y); // 3. Lines after cursor line if (eraseType == DispatchTypes::EraseType::ToEnd) { // For beginning and all, erase all complete lines after (below vertically) the cursor position. // Remember that the viewport bottom value is 1 beyond the viewable area of the viewport. - for (SHORT startLine = csbiex.dwCursorPosition.Y + 1; startLine < csbiex.srWindow.Bottom; startLine++) + for (SHORT startLine = cursorPosition.Y + 1; startLine < viewport.Bottom; startLine++) { - _EraseSingleLineHelper(csbiex, DispatchTypes::EraseType::All, startLine); + _EraseSingleLineHelper(textBuffer, DispatchTypes::EraseType::All, startLine); } } @@ -664,11 +640,9 @@ bool AdaptDispatch::EraseInLine(const DispatchTypes::EraseType eraseType) { RETURN_BOOL_IF_FALSE(eraseType <= DispatchTypes::EraseType::All); - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); - - _EraseSingleLineHelper(csbiex, eraseType, csbiex.dwCursorPosition.Y); + const auto& textBuffer = _pConApi->GetTextBuffer(); + const auto cursorPosition = textBuffer.GetCursor().GetPosition(); + _EraseSingleLineHelper(textBuffer, eraseType, cursorPosition.Y); return true; } @@ -820,32 +794,28 @@ void AdaptDispatch::_OperatingStatus() const // - void AdaptDispatch::_CursorPositionReport() const { - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - // Make sure to reset the viewport (with MoveToBottom )to where it was - // before the user scrolled the console output - _pConApi->MoveToBottom(); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); + const auto viewport = _pConApi->GetViewport(); + const auto& textBuffer = _pConApi->GetTextBuffer(); // First pull the cursor position relative to the entire buffer out of the console. - COORD coordCursorPos = csbiex.dwCursorPosition; + auto cursorPosition = textBuffer.GetCursor().GetPosition(); // Now adjust it for its position in respect to the current viewport top. - coordCursorPos.Y -= csbiex.srWindow.Top; + cursorPosition.Y -= viewport.Top; // NOTE: 1,1 is the top-left corner of the viewport in VT-speak, so add 1. - coordCursorPos.X++; - coordCursorPos.Y++; + cursorPosition.X++; + cursorPosition.Y++; // If the origin mode is relative, line numbers start at top margin of the scrolling region. if (_isOriginModeRelative) { - coordCursorPos.Y -= _scrollMargins.Top; + cursorPosition.Y -= _scrollMargins.Top; } // Now send it back into the input channel of the console. // First format the response string. - const auto response = wil::str_printf(L"\x1b[%d;%dR", coordCursorPos.Y, coordCursorPos.X); + const auto response = wil::str_printf(L"\x1b[%d;%dR", cursorPosition.Y, cursorPosition.X); _WriteResponse(response); } @@ -894,26 +864,20 @@ void AdaptDispatch::_ScrollMovement(const ScrollDirection scrollDirection, const SHORT dist; THROW_IF_FAILED(SizeTToShort(distance, &dist)); - // get current cursor - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - // Make sure to reset the viewport (with MoveToBottom )to where it was - // before the user scrolled the console output - _pConApi->MoveToBottom(); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); + const auto viewport = _pConApi->GetViewport(); // Rectangle to cut out of the existing buffer. This is inclusive. // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. SMALL_RECT srScreen; srScreen.Left = 0; srScreen.Right = SHORT_MAX; - srScreen.Top = csbiex.srWindow.Top; - srScreen.Bottom = csbiex.srWindow.Bottom - 1; // srWindow is exclusive, hence the - 1 + srScreen.Top = viewport.Top; + srScreen.Bottom = viewport.Bottom - 1; // viewport is exclusive, hence the - 1 // Clip to the DECSTBM margin boundaries if (_scrollMargins.Top < _scrollMargins.Bottom) { - srScreen.Top = csbiex.srWindow.Top + _scrollMargins.Top; - srScreen.Bottom = csbiex.srWindow.Top + _scrollMargins.Bottom; + srScreen.Top = viewport.Top + _scrollMargins.Top; + srScreen.Bottom = viewport.Top + _scrollMargins.Bottom; } // Paste coordinate for cut text above @@ -1240,13 +1204,6 @@ bool AdaptDispatch::SetAutoWrapMode(const bool wrapAtEOL) void AdaptDispatch::_DoSetTopBottomScrollingMargins(const size_t topMargin, const size_t bottomMargin) { - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - // Make sure to reset the viewport (with MoveToBottom )to where it was - // before the user scrolled the console output - _pConApi->MoveToBottom(); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); - // so notes time: (input -> state machine out -> adapter out -> conhost internal) // having only a top param is legal ([3;r -> 3,0 -> 3,h -> 3,h,true) // having only a bottom param is legal ([;3r -> 0,3 -> 1,3 -> 1,3,true) @@ -1257,7 +1214,8 @@ void AdaptDispatch::_DoSetTopBottomScrollingMargins(const size_t topMargin, THROW_IF_FAILED(SizeTToShort(topMargin, &actualTop)); THROW_IF_FAILED(SizeTToShort(bottomMargin, &actualBottom)); - const SHORT screenHeight = csbiex.srWindow.Bottom - csbiex.srWindow.Top; + const auto viewport = _pConApi->GetViewport(); + const SHORT screenHeight = viewport.Bottom - viewport.Top; // The default top margin is line 1 if (actualTop == 0) { @@ -1426,12 +1384,9 @@ bool AdaptDispatch::UseMainScreenBuffer() // - True. bool AdaptDispatch::HorizontalTabSet() { - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); - - const auto width = csbiex.dwSize.X; - const auto column = csbiex.dwCursorPosition.X; + const auto& textBuffer = _pConApi->GetTextBuffer(); + const auto width = textBuffer.GetSize().Dimensions().X; + const auto column = textBuffer.GetCursor().GetPosition().X; _InitTabStopsForWidth(width); _tabStopColumns.at(column) = true; @@ -1450,13 +1405,10 @@ bool AdaptDispatch::HorizontalTabSet() // - True. bool AdaptDispatch::ForwardTab(const size_t numTabs) { - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); - - const auto width = csbiex.dwSize.X; - const auto row = csbiex.dwCursorPosition.Y; - auto column = csbiex.dwCursorPosition.X; + const auto& textBuffer = _pConApi->GetTextBuffer(); + const auto width = textBuffer.GetSize().Dimensions().X; + const auto row = textBuffer.GetCursor().GetPosition().Y; + auto column = textBuffer.GetCursor().GetPosition().X; auto tabsPerformed = 0u; _InitTabStopsForWidth(width); @@ -1482,13 +1434,10 @@ bool AdaptDispatch::ForwardTab(const size_t numTabs) // - True. bool AdaptDispatch::BackwardsTab(const size_t numTabs) { - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); - - const auto width = csbiex.dwSize.X; - const auto row = csbiex.dwCursorPosition.Y; - auto column = csbiex.dwCursorPosition.X; + const auto& textBuffer = _pConApi->GetTextBuffer(); + const auto width = textBuffer.GetSize().Dimensions().X; + const auto row = textBuffer.GetCursor().GetPosition().Y; + auto column = textBuffer.GetCursor().GetPosition().X; auto tabsPerformed = 0u; _InitTabStopsForWidth(width); @@ -1536,12 +1485,9 @@ bool AdaptDispatch::TabClear(const DispatchTypes::TabClearType clearType) // - void AdaptDispatch::_ClearSingleTabStop() { - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); - - const auto width = csbiex.dwSize.X; - const auto column = csbiex.dwCursorPosition.X; + const auto& textBuffer = _pConApi->GetTextBuffer(); + const auto width = textBuffer.GetSize().Dimensions().X; + const auto column = textBuffer.GetCursor().GetPosition().X; _InitTabStopsForWidth(width); _tabStopColumns.at(column) = false; @@ -1846,19 +1792,15 @@ bool AdaptDispatch::HardReset() // - True. bool AdaptDispatch::ScreenAlignmentPattern() { - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - // Make sure to reset the viewport (with MoveToBottom )to where it was - // before the user scrolled the console output - _pConApi->MoveToBottom(); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); + const auto viewport = _pConApi->GetViewport(); + const auto bufferWidth = _pConApi->GetTextBuffer().GetSize().Dimensions().X; // Fill the screen with the letter E using the default attributes. - auto fillPosition = COORD{ 0, csbiex.srWindow.Top }; - const auto fillLength = (csbiex.srWindow.Bottom - csbiex.srWindow.Top) * csbiex.dwSize.X; + auto fillPosition = COORD{ 0, viewport.Top }; + const auto fillLength = (viewport.Bottom - viewport.Top) * bufferWidth; _pConApi->FillRegion(fillPosition, fillLength, L'E', false); // Reset the line rendition for all of these rows. - _pConApi->ResetLineRenditionRange(csbiex.srWindow.Top, csbiex.srWindow.Bottom); + _pConApi->ResetLineRenditionRange(viewport.Top, viewport.Bottom); // Reset the meta/extended attributes (but leave the colors unchanged). TextAttribute attr = _pConApi->GetTextAttributes(); attr.SetStandardErase(); @@ -1887,17 +1829,12 @@ bool AdaptDispatch::ScreenAlignmentPattern() // - void AdaptDispatch::_EraseScrollback() { - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(csbiex); - // Make sure to reset the viewport (with MoveToBottom )to where it was - // before the user scrolled the console output - _pConApi->MoveToBottom(); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); - - const SMALL_RECT screen = csbiex.srWindow; + const SMALL_RECT screen = _pConApi->GetViewport(); const SHORT height = screen.Bottom - screen.Top; THROW_HR_IF(E_UNEXPECTED, height <= 0); - const COORD cursor = csbiex.dwCursorPosition; + const auto& textBuffer = _pConApi->GetTextBuffer(); + const auto bufferSize = textBuffer.GetSize().Dimensions(); + const auto cursorPosition = textBuffer.GetCursor().GetPosition(); // Rectangle to cut out of the existing buffer // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. @@ -1913,22 +1850,22 @@ void AdaptDispatch::_EraseScrollback() // this case we need to use the default attributes, hence standardFillAttrs is false. _pConApi->ScrollRegion(scroll, std::nullopt, destination, false); // Clear everything after the viewport. - const DWORD totalAreaBelow = csbiex.dwSize.X * (csbiex.dwSize.Y - height); + const DWORD totalAreaBelow = bufferSize.X * (bufferSize.Y - height); const COORD coordBelowStartPosition = { 0, height }; // Again we need to use the default attributes, hence standardFillAttrs is false. _pConApi->FillRegion(coordBelowStartPosition, totalAreaBelow, L' ', false); // Also reset the line rendition for all of the cleared rows. - _pConApi->ResetLineRenditionRange(height, csbiex.dwSize.Y); + _pConApi->ResetLineRenditionRange(height, bufferSize.Y); // Move the viewport (CAN'T be done in one call with SetConsolescreenBufferInfoEx, because legacy) SMALL_RECT newViewport; newViewport.Left = screen.Left; newViewport.Top = 0; - // SetWindowInfo uses an inclusive rect, while GetConsolescreenBufferInfo is exclusive + // SetWindowInfo uses an inclusive rect, while the screen rect is exclusive newViewport.Right = screen.Right - 1; newViewport.Bottom = height - 1; _pConApi->SetWindowInfo(true, newViewport); // Move the cursor to the same relative location. - const COORD newcursor = { cursor.X, cursor.Y - screen.Top }; + const COORD newcursor = { cursorPosition.X, cursorPosition.Y - screen.Top }; _pConApi->SetCursorPosition(newcursor); } @@ -2429,10 +2366,6 @@ void AdaptDispatch::_ReportDECSTBMSetting() const fmt::basic_memory_buffer response; response.append(L"\033P1$r"sv); - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); - auto marginTop = _scrollMargins.Top + 1; auto marginBottom = _scrollMargins.Bottom + 1; // If the margin top is greater than or equal to the bottom, then the @@ -2440,8 +2373,9 @@ void AdaptDispatch::_ReportDECSTBMSetting() const // of the window for the margin range. if (marginTop >= marginBottom) { + const auto viewport = _pConApi->GetViewport(); marginTop = 1; - marginBottom = csbiex.srWindow.Bottom - csbiex.srWindow.Top; + marginBottom = viewport.Bottom - viewport.Top; } fmt::format_to(std::back_inserter(response), FMT_COMPILE(L"{};{}"), marginTop, marginBottom); diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 928a4dd2acb..e34ce0893a2 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -163,7 +163,7 @@ namespace Microsoft::Console::VirtualTerminal }; bool _CursorMovePosition(const Offset rowOffset, const Offset colOffset, const bool clampInMargins) const; - void _EraseSingleLineHelper(const CONSOLE_SCREEN_BUFFER_INFOEX& csbiex, + void _EraseSingleLineHelper(const TextBuffer& textBuffer, const DispatchTypes::EraseType eraseType, const size_t lineId) const; void _EraseScrollback(); From e449c646d0195a033275e7fab5829913414632ff Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 10 Feb 2022 21:00:19 +0000 Subject: [PATCH 03/28] Make more use of GetTextBuffer where possible. --- src/terminal/adapter/adaptDispatch.cpp | 50 +++++++++++++------ .../adapter/adaptDispatchGraphics.cpp | 6 +-- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 4f0b6f8be6e..58e12b6b718 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -381,9 +381,7 @@ bool AdaptDispatch::CursorRestoreState() // - True. bool AdaptDispatch::CursorVisibility(const bool fIsVisible) { - // This uses a private API instead of the public one, because the public API - // will set the cursor shape back to legacy. - _pConApi->SetCursorVisibility(fIsVisible); + _pConApi->GetTextBuffer().GetCursor().SetIsVisible(fIsVisible); return true; } @@ -656,7 +654,7 @@ bool AdaptDispatch::EraseInLine(const DispatchTypes::EraseType eraseType) // - True. bool AdaptDispatch::SetLineRendition(const LineRendition rendition) { - _pConApi->SetCurrentLineRendition(rendition); + _pConApi->GetTextBuffer().SetCurrentLineRendition(rendition); return true; } @@ -1090,7 +1088,18 @@ bool AdaptDispatch::SetCursorKeysMode(const bool applicationMode) // - True if handled successfully. False otherwise. bool AdaptDispatch::EnableCursorBlinking(const bool enable) { - return _pConApi->EnableCursorBlinking(enable); + auto& cursor = _pConApi->GetTextBuffer().GetCursor(); + cursor.SetBlinkingAllowed(enable); + + // 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) + cursor.SetIsOn(true); + + return !_pConApi->IsConsolePty(); } // Routine Description: @@ -1793,16 +1802,17 @@ bool AdaptDispatch::HardReset() bool AdaptDispatch::ScreenAlignmentPattern() { const auto viewport = _pConApi->GetViewport(); - const auto bufferWidth = _pConApi->GetTextBuffer().GetSize().Dimensions().X; + auto& textBuffer = _pConApi->GetTextBuffer(); + const auto bufferWidth = textBuffer.GetSize().Dimensions().X; // Fill the screen with the letter E using the default attributes. auto fillPosition = COORD{ 0, viewport.Top }; const auto fillLength = (viewport.Bottom - viewport.Top) * bufferWidth; _pConApi->FillRegion(fillPosition, fillLength, L'E', false); // Reset the line rendition for all of these rows. - _pConApi->ResetLineRenditionRange(viewport.Top, viewport.Bottom); + textBuffer.ResetLineRenditionRange(viewport.Top, viewport.Bottom); // Reset the meta/extended attributes (but leave the colors unchanged). - TextAttribute attr = _pConApi->GetTextAttributes(); + auto attr = textBuffer.GetCurrentAttributes(); attr.SetStandardErase(); _pConApi->SetTextAttributes(attr); // Reset the origin mode to absolute. @@ -1832,7 +1842,7 @@ void AdaptDispatch::_EraseScrollback() const SMALL_RECT screen = _pConApi->GetViewport(); const SHORT height = screen.Bottom - screen.Top; THROW_HR_IF(E_UNEXPECTED, height <= 0); - const auto& textBuffer = _pConApi->GetTextBuffer(); + auto& textBuffer = _pConApi->GetTextBuffer(); const auto bufferSize = textBuffer.GetSize().Dimensions(); const auto cursorPosition = textBuffer.GetCursor().GetPosition(); @@ -1855,7 +1865,7 @@ void AdaptDispatch::_EraseScrollback() // Again we need to use the default attributes, hence standardFillAttrs is false. _pConApi->FillRegion(coordBelowStartPosition, totalAreaBelow, L' ', false); // Also reset the line rendition for all of the cleared rows. - _pConApi->ResetLineRenditionRange(height, bufferSize.Y); + textBuffer.ResetLineRenditionRange(height, bufferSize.Y); // Move the viewport (CAN'T be done in one call with SetConsolescreenBufferInfoEx, because legacy) SMALL_RECT newViewport; newViewport.Left = screen.Left; @@ -2028,8 +2038,10 @@ bool AdaptDispatch::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) return false; } - _pConApi->SetCursorStyle(actualType); - _pConApi->EnableCursorBlinking(fEnableBlinking); + auto& cursor = _pConApi->GetTextBuffer().GetCursor(); + cursor.SetType(actualType); + cursor.SetBlinkingAllowed(fEnableBlinking); + cursor.SetIsOn(true); // If we're a conpty, always return false, so that this cursor state will be // sent to the connected terminal @@ -2135,7 +2147,12 @@ bool AdaptDispatch::WindowManipulation(const DispatchTypes::WindowManipulationTy // - true bool AdaptDispatch::AddHyperlink(const std::wstring_view uri, const std::wstring_view params) { - _pConApi->AddHyperlink(uri, params); + auto& textBuffer = _pConApi->GetTextBuffer(); + auto attr = textBuffer.GetCurrentAttributes(); + const auto id = textBuffer.GetHyperlinkId(uri, params); + attr.SetHyperlinkId(id); + textBuffer.SetCurrentAttributes(attr); + textBuffer.AddHyperlinkToMap(uri, id); return true; } @@ -2145,7 +2162,10 @@ bool AdaptDispatch::AddHyperlink(const std::wstring_view uri, const std::wstring // - true bool AdaptDispatch::EndHyperlink() { - _pConApi->EndHyperlink(); + auto& textBuffer = _pConApi->GetTextBuffer(); + auto attr = textBuffer.GetCurrentAttributes(); + attr.SetHyperlinkId(0); + textBuffer.SetCurrentAttributes(attr); return true; } @@ -2302,7 +2322,7 @@ void AdaptDispatch::_ReportSGRSetting() const fmt::basic_memory_buffer response; response.append(L"\033P1$r0"sv); - const auto attr = _pConApi->GetTextAttributes(); + const auto attr = _pConApi->GetTextBuffer().GetCurrentAttributes(); // For each boolean attribute that is set, we add the appropriate // parameter value to the response string. const auto addAttribute = [&](const auto& parameter, const auto enabled) { diff --git a/src/terminal/adapter/adaptDispatchGraphics.cpp b/src/terminal/adapter/adaptDispatchGraphics.cpp index e955291d94a..0a03c1524e6 100644 --- a/src/terminal/adapter/adaptDispatchGraphics.cpp +++ b/src/terminal/adapter/adaptDispatchGraphics.cpp @@ -75,7 +75,7 @@ size_t AdaptDispatch::_SetRgbColorsHelper(const VTParameters options, // - True. bool AdaptDispatch::SetGraphicsRendition(const VTParameters options) { - TextAttribute attr = _pConApi->GetTextAttributes(); + TextAttribute attr = _pConApi->GetTextBuffer().GetCurrentAttributes(); // Run through the graphics options and apply them for (size_t i = 0; i < options.size(); i++) @@ -270,7 +270,7 @@ bool AdaptDispatch::SetGraphicsRendition(const VTParameters options) // - True. bool AdaptDispatch::PushGraphicsRendition(const VTParameters options) { - const auto currentAttributes = _pConApi->GetTextAttributes(); + const auto currentAttributes = _pConApi->GetTextBuffer().GetCurrentAttributes(); _sgrStack.Push(currentAttributes, options); return true; } @@ -284,7 +284,7 @@ bool AdaptDispatch::PushGraphicsRendition(const VTParameters options) // - True. bool AdaptDispatch::PopGraphicsRendition() { - const auto currentAttributes = _pConApi->GetTextAttributes(); + const auto currentAttributes = _pConApi->GetTextBuffer().GetCurrentAttributes(); _pConApi->SetTextAttributes(_sgrStack.Pop(currentAttributes)); return true; } From 981075daa76fdebf7d8065ebbbd0f23ea3942d04 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 10 Feb 2022 21:13:12 +0000 Subject: [PATCH 04/28] Remove unused ConGetSet methods. --- src/host/outputStream.cpp | 135 ----------------------------- src/host/outputStream.hpp | 14 --- src/host/screenInfo.cpp | 18 ---- src/host/screenInfo.hpp | 1 - src/terminal/adapter/conGetSet.hpp | 14 --- 5 files changed, 182 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 1c4db2f4e0d..3dda338833a 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -114,17 +114,6 @@ void ConhostInternalGetSet::SetCursorPosition(const COORD position) THROW_IF_FAILED(ServiceLocator::LocateGlobals().api->SetConsoleCursorPositionImpl(info, clampedPosition)); } -// Method Description: -// - Retrieves the current TextAttribute of the active screen buffer. -// Arguments: -// - -// Return Value: -// - the TextAttribute value. -TextAttribute ConhostInternalGetSet::GetTextAttributes() const -{ - return _io.GetActiveOutputBuffer().GetAttributes(); -} - // Method Description: // - Sets the current TextAttribute of the active screen buffer. Text // written to this buffer will be written with these attributes. @@ -137,46 +126,6 @@ void ConhostInternalGetSet::SetTextAttributes(const TextAttribute& attrs) _io.GetActiveOutputBuffer().SetAttributes(attrs); } -// Method Description: -// - Sets the line rendition attribute for the current row of the active screen -// buffer. This controls how character cells are scaled when the row is rendered. -// Arguments: -// - lineRendition: The new LineRendition attribute to use -// Return Value: -// - -void ConhostInternalGetSet::SetCurrentLineRendition(const LineRendition lineRendition) -{ - auto& textBuffer = _io.GetActiveOutputBuffer().GetTextBuffer(); - textBuffer.SetCurrentLineRendition(lineRendition); -} - -// Method Description: -// - Resets the line rendition attribute to SingleWidth for a specified range -// of row numbers. -// Arguments: -// - startRow: The row number of first line to be modified -// - endRow: The row number following the last line to be modified -// Return Value: -// - -void ConhostInternalGetSet::ResetLineRenditionRange(const size_t startRow, const size_t endRow) -{ - auto& textBuffer = _io.GetActiveOutputBuffer().GetTextBuffer(); - textBuffer.ResetLineRenditionRange(startRow, endRow); -} - -// Method Description: -// - Returns the number of cells that will fit on the specified row when -// rendered with its current line rendition. -// Arguments: -// - row: The row number of the line to measure -// Return Value: -// - the number of cells that will fit on the line -SHORT ConhostInternalGetSet::GetLineWidth(const size_t row) const -{ - const auto& textBuffer = _io.GetActiveOutputBuffer().GetTextBuffer(); - return textBuffer.GetLineWidth(row); -} - // Routine Description: // - Writes events to the input buffer already formed into IInputEvents // Arguments: @@ -288,42 +237,6 @@ void ConhostInternalGetSet::SetAutoWrapMode(const bool wrapAtEOL) WI_UpdateFlag(outputMode, ENABLE_WRAP_AT_EOL_OUTPUT, wrapAtEOL); } -// Routine Description: -// - Makes the cursor visible or hidden. Does not modify blinking state. -// Arguments: -// - visible - set to true to make the cursor visible, false to hide. -// Return Value: -// - -void ConhostInternalGetSet::SetCursorVisibility(const bool visible) -{ - auto& textBuffer = _io.GetActiveOutputBuffer().GetTextBuffer(); - textBuffer.GetCursor().SetIsVisible(visible); -} - -// Routine Description: -// - Enables or disables the cursor blinking. -// Arguments: -// - fEnable - set to true to enable blinking, false to disable -// Return Value: -// - true if successful. false otherwise. -bool ConhostInternalGetSet::EnableCursorBlinking(const bool fEnable) -{ - auto& textBuffer = _io.GetActiveOutputBuffer().GetTextBuffer(); - textBuffer.GetCursor().SetBlinkingAllowed(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) - textBuffer.GetCursor().SetIsOn(true); - - // If we are connected to a pty, return that we could not handle this - // so that the VT sequence gets flushed to terminal. - return !IsConsolePty(); -} - // Routine Description: // - Sets the top and bottom scrolling margins for the current page. This creates // a subsection of the screen that scrolls when input reaches the end of the @@ -514,17 +427,6 @@ CursorType ConhostInternalGetSet::GetUserDefaultCursorStyle() const return gci.GetCursorType(); } -// Routine Description: -// - Sets the current cursor style. -// Arguments: -// - style: The style of cursor to change the cursor to. -// Return Value: -// - -void ConhostInternalGetSet::SetCursorStyle(const CursorType style) -{ - _io.GetActiveOutputBuffer().GetTextBuffer().GetCursor().SetType(style); -} - // Routine Description: // - Forces the renderer to repaint the screen. If the input screen buffer is // not the active one, then just do nothing. We only want to redraw the @@ -714,18 +616,6 @@ void ConhostInternalGetSet::_modifyLines(const size_t count, const bool insert) } } -// Method Description: -// - Snaps the screen buffer's viewport to the "virtual bottom", the last place -// the viewport was before the user scrolled it (with the mouse or scrollbar) -// Arguments: -// - -// Return Value: -// - -void ConhostInternalGetSet::MoveToBottom() -{ - _io.GetActiveOutputBuffer().MoveToBottom(); -} - // Method Description: // - Retrieves the value in the colortable at the specified index. // Arguments: @@ -871,31 +761,6 @@ bool ConhostInternalGetSet::IsVtInputEnabled() const return _io.GetActiveInputBuffer()->IsInVirtualTerminalInputMode(); } -// Method Description: -// - Updates the buffer's current text attributes depending on whether we are -// starting/ending a hyperlink -// Arguments: -// - The hyperlink URI -// Return Value: -// - -void ConhostInternalGetSet::AddHyperlink(const std::wstring_view uri, const std::wstring_view params) const -{ - auto& screenInfo = _io.GetActiveOutputBuffer(); - auto attr = screenInfo.GetAttributes(); - const auto id = screenInfo.GetTextBuffer().GetHyperlinkId(uri, params); - attr.SetHyperlinkId(id); - screenInfo.GetTextBuffer().SetCurrentAttributes(attr); - screenInfo.GetTextBuffer().AddHyperlinkToMap(uri, id); -} - -void ConhostInternalGetSet::EndHyperlink() const -{ - auto& screenInfo = _io.GetActiveOutputBuffer(); - auto attr = screenInfo.GetAttributes(); - attr.SetHyperlinkId(0); - screenInfo.GetTextBuffer().SetCurrentAttributes(attr); -} - // Routine Description: // - Replaces the active soft font with the given bit pattern. // Arguments: diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 17d375331f6..9000c722e6d 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -39,13 +39,8 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void SetCursorPosition(const COORD position) override; - TextAttribute GetTextAttributes() const override; void SetTextAttributes(const TextAttribute& attrs) override; - void SetCurrentLineRendition(const LineRendition lineRendition) override; - void ResetLineRenditionRange(const size_t startRow, const size_t endRow) override; - SHORT GetLineWidth(const size_t row) const override; - void WriteInput(std::deque>& events, size_t& eventsWritten) override; void SetWindowInfo(bool const absolute, const SMALL_RECT& window) override; @@ -57,9 +52,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void SetAutoWrapMode(const bool wrapAtEOL) override; - void SetCursorVisibility(const bool visible) override; - bool EnableCursorBlinking(const bool enable) override; - void SetScrollingRegion(const SMALL_RECT& scrollMargins) override; void WarningBell() override; @@ -78,7 +70,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void ClearBuffer() override; CursorType GetUserDefaultCursorStyle() const override; - void SetCursorStyle(CursorType const style) override; void RefreshWindow() override; bool ResizeWindow(const size_t width, const size_t height) override; @@ -94,8 +85,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void DeleteLines(const size_t count) override; void InsertLines(const size_t count) override; - void MoveToBottom() override; - COLORREF GetColorTableEntry(const size_t tableIndex) const override; bool SetColorTableEntry(const size_t tableIndex, const COLORREF color) override; void SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) override; @@ -112,9 +101,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: bool IsVtInputEnabled() const override; - void AddHyperlink(const std::wstring_view uri, const std::wstring_view params) const override; - void EndHyperlink() const override; - void UpdateSoftFont(const gsl::span bitPattern, const SIZE cellSize, const size_t centeringHint) override; diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index 49dea7ba74b..a8d22144fc5 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -2632,24 +2632,6 @@ void SCREEN_INFORMATION::InitializeCursorRowAttributes() } } -// Method Description: -// - Moves the viewport to where we last believed the "virtual bottom" was. This -// emulates linux terminal behavior, where there's no buffer, only a -// viewport. This is called by WriteChars, on output from an application in -// VT mode, before the output is processed by the state machine. -// This ensures that if a user scrolls around in the buffer, and a client -// application uses VT to control the cursor/buffer, those commands are -// still processed relative to the coordinates before the user scrolled the buffer. -// Arguments: -// - -// Return Value: -// - -void SCREEN_INFORMATION::MoveToBottom() -{ - const auto virtualView = GetVirtualViewport(); - LOG_IF_NTSTATUS_FAILED(SetViewportOrigin(true, virtualView.Origin(), true)); -} - // Method Description: // - Returns the "virtual" Viewport - the viewport with its bottom at // `_virtualBottom`. For VT operations, this is essentially the mutable diff --git a/src/host/screenInfo.hpp b/src/host/screenInfo.hpp index 56554954bf0..4b0bdc1b9bc 100644 --- a/src/host/screenInfo.hpp +++ b/src/host/screenInfo.hpp @@ -222,7 +222,6 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console void SetTerminalConnection(_In_ Microsoft::Console::Render::VtEngine* const pTtyConnection); void UpdateBottom(); - void MoveToBottom(); FontInfo& GetCurrentFont() noexcept; const FontInfo& GetCurrentFont() const noexcept; diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index da350e22d62..be942fd3192 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -46,13 +46,8 @@ namespace Microsoft::Console::VirtualTerminal virtual bool IsVtInputEnabled() const = 0; - virtual TextAttribute GetTextAttributes() const = 0; virtual void SetTextAttributes(const TextAttribute& attrs) = 0; - virtual void SetCurrentLineRendition(const LineRendition lineRendition) = 0; - virtual void ResetLineRenditionRange(const size_t startRow, const size_t endRow) = 0; - virtual SHORT GetLineWidth(const size_t row) const = 0; - virtual void WriteInput(std::deque>& events, size_t& eventsWritten) = 0; virtual void SetWindowInfo(const bool absolute, const SMALL_RECT& window) = 0; @@ -63,9 +58,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void SetAutoWrapMode(const bool wrapAtEOL) = 0; - virtual void SetCursorVisibility(const bool visible) = 0; - virtual bool EnableCursorBlinking(const bool enable) = 0; - virtual void SetScrollingRegion(const SMALL_RECT& scrollMargins) = 0; virtual void WarningBell() = 0; virtual bool GetLineFeedMode() const = 0; @@ -78,7 +70,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void EraseAll() = 0; virtual void ClearBuffer() = 0; virtual CursorType GetUserDefaultCursorStyle() const = 0; - virtual void SetCursorStyle(const CursorType style) = 0; virtual void WriteControlInput(const KeyEvent key) = 0; virtual void RefreshWindow() = 0; @@ -92,8 +83,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void DeleteLines(const size_t count) = 0; virtual void InsertLines(const size_t count) = 0; - virtual void MoveToBottom() = 0; - virtual COLORREF GetColorTableEntry(const size_t tableIndex) const = 0; virtual bool SetColorTableEntry(const size_t tableIndex, const COLORREF color) = 0; virtual void SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) = 0; @@ -108,9 +97,6 @@ namespace Microsoft::Console::VirtualTerminal const COORD destinationOrigin, const bool standardFillAttrs) = 0; - virtual void AddHyperlink(const std::wstring_view uri, const std::wstring_view params) const = 0; - virtual void EndHyperlink() const = 0; - virtual void UpdateSoftFont(const gsl::span bitPattern, const SIZE cellSize, const size_t centeringHint) = 0; From 93fee5054d4601773047429b4a14eb9d20a697c4 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sat, 12 Feb 2022 17:49:32 +0000 Subject: [PATCH 05/28] Move IL/DL implementation to AdaptDispatch. --- src/host/outputStream.cpp | 64 -------------------------- src/host/outputStream.hpp | 5 -- src/terminal/adapter/adaptDispatch.cpp | 63 +++++++++++++++++++++++-- src/terminal/adapter/adaptDispatch.hpp | 3 +- src/terminal/adapter/conGetSet.hpp | 3 -- 5 files changed, 60 insertions(+), 78 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 3dda338833a..25103ea1a26 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -552,70 +552,6 @@ bool ConhostInternalGetSet::IsConsolePty() const return ServiceLocator::LocateGlobals().getConsoleInformation().IsInVtIoMode(); } -// Routine Description: -// - Deletes lines in the active screen buffer. -// Parameters: -// - count - the number of lines to delete -void ConhostInternalGetSet::DeleteLines(const size_t count) -{ - _modifyLines(count, false); -} - -// Routine Description: -// - Inserts lines in the active screen buffer. -// Parameters: -// - count - the number of lines to insert -void ConhostInternalGetSet::InsertLines(const size_t count) -{ - _modifyLines(count, true); -} - -// Routine Description: -// - internal logic for adding or removing lines in the active screen buffer -// this also moves the cursor to the left margin, which is expected behavior for IL and DL -// Parameters: -// - count - the number of lines to modify -// - insert - true if inserting lines, false if deleting lines -void ConhostInternalGetSet::_modifyLines(const size_t count, const bool insert) -{ - auto& screenInfo = _io.GetActiveOutputBuffer(); - auto& textBuffer = screenInfo.GetTextBuffer(); - const auto cursorPosition = textBuffer.GetCursor().GetPosition(); - if (screenInfo.IsCursorInMargins(cursorPosition)) - { - // Rectangle to cut out of the existing buffer. This is inclusive. - // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. - SMALL_RECT srScroll; - srScroll.Left = 0; - srScroll.Right = SHORT_MAX; - srScroll.Top = cursorPosition.Y; - srScroll.Bottom = screenInfo.GetViewport().BottomInclusive(); - // Clip to the DECSTBM margin boundary - if (screenInfo.AreMarginsSet()) - { - srScroll.Bottom = screenInfo.GetAbsoluteScrollMargins().BottomInclusive(); - } - // Paste coordinate for cut text above - COORD coordDestination; - coordDestination.X = 0; - if (insert) - { - coordDestination.Y = (cursorPosition.Y) + gsl::narrow(count); - } - else - { - coordDestination.Y = (cursorPosition.Y) - gsl::narrow(count); - } - - // Note the revealed lines are filled with the standard erase attributes. - ScrollRegion(srScroll, srScroll, coordDestination, true); - - // The IL and DL controls are also expected to move the cursor to the left margin. - // For now this is just column 0, since we don't yet support DECSLRM. - THROW_IF_NTSTATUS_FAILED(screenInfo.SetCursorPosition({ 0, cursorPosition.Y }, false)); - } -} - // Method Description: // - Retrieves the value in the colortable at the specified index. // Arguments: diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 9000c722e6d..0965fefc0c9 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -82,9 +82,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: bool IsConsolePty() const override; - void DeleteLines(const size_t count) override; - void InsertLines(const size_t count) override; - COLORREF GetColorTableEntry(const size_t tableIndex) const override; bool SetColorTableEntry(const size_t tableIndex, const COLORREF color) override; void SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) override; @@ -106,7 +103,5 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: const size_t centeringHint) override; private: - void _modifyLines(const size_t count, const bool insert); - Microsoft::Console::IIoProvider& _io; }; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 58e12b6b718..abb9880c87f 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -393,7 +393,7 @@ bool AdaptDispatch::CursorVisibility(const bool fIsVisible) // - isInsert - TRUE if insert mode (cut and paste to the right, away from the cursor). FALSE if delete mode (cut and paste to the left, toward the cursor) // Return Value: // - -void AdaptDispatch::_InsertDeleteHelper(const size_t count, const bool isInsert) const +void AdaptDispatch::_InsertDeleteCharacterHelper(const size_t count, const bool isInsert) { // We'll be doing short math on the distance since all console APIs use shorts. So check that we can successfully convert the uint into a short first. SHORT distance; @@ -439,7 +439,7 @@ void AdaptDispatch::_InsertDeleteHelper(const size_t count, const bool isInsert) // - True. bool AdaptDispatch::InsertCharacter(const size_t count) { - _InsertDeleteHelper(count, true); + _InsertDeleteCharacterHelper(count, true); return true; } @@ -452,7 +452,7 @@ bool AdaptDispatch::InsertCharacter(const size_t count) // - True. bool AdaptDispatch::DeleteCharacter(const size_t count) { - _InsertDeleteHelper(count, false); + _InsertDeleteCharacterHelper(count, false); return true; } @@ -1102,6 +1102,59 @@ bool AdaptDispatch::EnableCursorBlinking(const bool enable) return !_pConApi->IsConsolePty(); } +// Routine Description: +// - Internal logic for adding or removing lines in the active screen buffer. +// This also moves the cursor to the left margin, which is expected behavior for IL and DL. +// Parameters: +// - count - the number of lines to modify +// - isInsert - true if inserting lines, false if deleting lines +// Return Value: +// - +void AdaptDispatch::_InsertDeleteLineHelper(const size_t count, const bool isInsert) +{ + const auto viewport = _pConApi->GetViewport(); + const auto& textBuffer = _pConApi->GetTextBuffer(); + + const auto row = textBuffer.GetCursor().GetPosition().Y; + const auto relativeRow = row - viewport.Top; + + const bool marginsSet = _scrollMargins.Top < _scrollMargins.Bottom; + const auto rowInMargins = relativeRow >= _scrollMargins.Top && relativeRow <= _scrollMargins.Bottom; + if (!marginsSet || rowInMargins) + { + // Rectangle to cut out of the existing buffer. This is inclusive. + // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. + SMALL_RECT srScroll; + srScroll.Left = 0; + srScroll.Right = SHORT_MAX; + srScroll.Top = row; + srScroll.Bottom = viewport.Bottom - 1; // viewport is exclusive, hence the - 1 + // Clip to the DECSTBM margin boundary + if (marginsSet) + { + srScroll.Bottom = viewport.Top + _scrollMargins.Bottom; + } + // Paste coordinate for cut text above + COORD coordDestination; + coordDestination.X = 0; + if (isInsert) + { + coordDestination.Y = row + gsl::narrow_cast(count); + } + else + { + coordDestination.Y = row - gsl::narrow_cast(count); + } + + // Note the revealed lines are filled with the standard erase attributes. + _pConApi->ScrollRegion(srScroll, srScroll, coordDestination, true); + + // The IL and DL controls are also expected to move the cursor to the left margin. + // For now this is just column 0, since we don't yet support DECSLRM. + _pConApi->SetCursorPosition({ 0, row }); + } +} + // Routine Description: // - IL - This control function inserts one or more blank lines, starting at the cursor. // As lines are inserted, lines below the cursor and in the scrolling region move down. @@ -1112,7 +1165,7 @@ bool AdaptDispatch::EnableCursorBlinking(const bool enable) // - True. bool AdaptDispatch::InsertLine(const size_t distance) { - _pConApi->InsertLines(distance); + _InsertDeleteLineHelper(distance, true); return true; } @@ -1130,7 +1183,7 @@ bool AdaptDispatch::InsertLine(const size_t distance) // - True. bool AdaptDispatch::DeleteLine(const size_t distance) { - _pConApi->DeleteLines(distance); + _InsertDeleteLineHelper(distance, false); return true; } diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index e34ce0893a2..5e427674d84 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -168,7 +168,8 @@ namespace Microsoft::Console::VirtualTerminal const size_t lineId) const; void _EraseScrollback(); void _EraseAll(); - void _InsertDeleteHelper(const size_t count, const bool isInsert) const; + void _InsertDeleteCharacterHelper(const size_t count, const bool isInsert); + void _InsertDeleteLineHelper(const size_t count, const bool isInsert); void _ScrollMovement(const ScrollDirection dir, const size_t distance) const; void _DoSetTopBottomScrollingMargins(const size_t topMargin, diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index be942fd3192..41620fee9ee 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -80,9 +80,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void SuppressResizeRepaint() = 0; virtual bool IsConsolePty() const = 0; - virtual void DeleteLines(const size_t count) = 0; - virtual void InsertLines(const size_t count) = 0; - virtual COLORREF GetColorTableEntry(const size_t tableIndex) const = 0; virtual bool SetColorTableEntry(const size_t tableIndex, const COLORREF color) = 0; virtual void SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) = 0; From 20ab0c4b47dff016163f1cf6024bc0ce186a5785 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sat, 12 Feb 2022 18:07:50 +0000 Subject: [PATCH 06/28] Reimplement RI in AdaptDispatch. --- src/host/outputStream.cpp | 52 -------------------------- src/host/outputStream.hpp | 1 - src/terminal/adapter/adaptDispatch.cpp | 29 +++++++++++++- src/terminal/adapter/conGetSet.hpp | 1 - 4 files changed, 28 insertions(+), 55 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 25103ea1a26..9927feee1fc 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -310,58 +310,6 @@ void ConhostInternalGetSet::WarningBell() _io.GetActiveOutputBuffer().SendNotifyBeep(); } -// Routine Description: -// - Performs a "Reverse line feed", essentially, the opposite of '\n'. -// Return Value: -// - -void ConhostInternalGetSet::ReverseLineFeed() -{ - auto& screenInfo = _io.GetActiveOutputBuffer(); - const SMALL_RECT viewport = screenInfo.GetViewport().ToInclusive(); - const COORD oldCursorPosition = screenInfo.GetTextBuffer().GetCursor().GetPosition(); - COORD newCursorPosition = { oldCursorPosition.X, oldCursorPosition.Y - 1 }; - newCursorPosition = screenInfo.GetTextBuffer().ClampPositionWithinLine(newCursorPosition); - - // If the cursor is at the top of the viewport, we don't want to shift the viewport up. - // We want it to stay exactly where it is. - // In that case, shift the buffer contents down, to emulate inserting a line - // at the top of the buffer. - if (oldCursorPosition.Y > viewport.Top) - { - // Cursor is below the top line of the viewport - THROW_IF_NTSTATUS_FAILED(AdjustCursorPosition(screenInfo, newCursorPosition, TRUE, nullptr)); - } - else - { - // If we don't have margins, or the cursor is within the boundaries of the margins - // It's important to check if the cursor is in the margins, - // If it's not, but the margins are set, then we don't want to scroll anything - if (screenInfo.IsCursorInMargins(oldCursorPosition)) - { - // Cursor is at the top of the viewport - // Rectangle to cut out of the existing buffer. This is inclusive. - // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. - SMALL_RECT srScroll; - srScroll.Left = 0; - srScroll.Right = SHORT_MAX; - srScroll.Top = viewport.Top; - srScroll.Bottom = viewport.Bottom; - // Clip to the DECSTBM margin boundary - if (screenInfo.AreMarginsSet()) - { - srScroll.Bottom = screenInfo.GetAbsoluteScrollMargins().BottomInclusive(); - } - // Paste coordinate for cut text above - COORD coordDestination; - coordDestination.X = 0; - coordDestination.Y = viewport.Top + 1; - - // Note the revealed lines are filled with the standard erase attributes. - ScrollRegion(srScroll, srScroll, coordDestination, true); - } - } -} - // Routine Description: // - Sets the title of the console window. // Arguments: diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 0965fefc0c9..5362a91b8bd 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -58,7 +58,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: bool GetLineFeedMode() const override; void LineFeed(const bool withReturn) override; - void ReverseLineFeed() override; void SetWindowTitle(const std::wstring_view title) override; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index abb9880c87f..3e5d30dc53d 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1392,7 +1392,34 @@ bool AdaptDispatch::LineFeed(const DispatchTypes::LineFeedType lineFeedType) // - True. bool AdaptDispatch::ReverseLineFeed() { - _pConApi->ReverseLineFeed(); + const auto viewport = _pConApi->GetViewport(); + const auto& textBuffer = _pConApi->GetTextBuffer(); + const auto cursorPosition = textBuffer.GetCursor().GetPosition(); + + // Calculate the absolute margins of the scrolling area. + // If margins aren't set, we use the full viewport. + const bool marginsSet = _scrollMargins.Top < _scrollMargins.Bottom; + const short topMargin = marginsSet ? viewport.Top + _scrollMargins.Top : viewport.Top; + const short bottomMargin = marginsSet ? viewport.Top + _scrollMargins.Bottom : viewport.Bottom - 1; + + // If the cursor is at the top of the margin area, we shift the buffer + // contents down, to emulate inserting a line at that point. + if (cursorPosition.Y == topMargin) + { + // Rectangle to cut out of the existing buffer. This is inclusive. + // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. + const SMALL_RECT srScroll = { 0, topMargin, SHORT_MAX, bottomMargin }; + // Paste coordinate for cut text above + const COORD coordDestination = { 0, topMargin + 1 }; + // Note the revealed lines are filled with the standard erase attributes. + _pConApi->ScrollRegion(srScroll, srScroll, coordDestination, true); + } + else if (cursorPosition.Y > viewport.Top) + { + // Otherwise we move the cursor up, but not past the top of the viewport. + const COORD newCursorPosition{ cursorPosition.X, cursorPosition.Y - 1 }; + _pConApi->SetCursorPosition(newCursorPosition); + } return true; } diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 41620fee9ee..3034a42a84f 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -62,7 +62,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void WarningBell() = 0; virtual bool GetLineFeedMode() const = 0; virtual void LineFeed(const bool withReturn) = 0; - virtual void ReverseLineFeed() = 0; virtual void SetWindowTitle(const std::wstring_view title) = 0; virtual void UseAlternateScreenBuffer() = 0; virtual void UseMainScreenBuffer() = 0; From 6a0744b3ad4a907b267b89ff417e9fd8d9a7b4cc Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sat, 12 Feb 2022 18:16:53 +0000 Subject: [PATCH 07/28] Remove unused SCREEN_INFORMATION margin methods. --- src/host/screenInfo.cpp | 29 ----------------------------- src/host/screenInfo.hpp | 2 -- 2 files changed, 31 deletions(-) diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index a8d22144fc5..81e9c580da8 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -2701,35 +2701,6 @@ const FontInfoDesired& SCREEN_INFORMATION::GetDesiredFont() const noexcept return _desiredFont; } -// Method Description: -// - Returns true iff the scroll margins have been set. -// Arguments: -// - -// Return Value: -// - true iff the scroll margins have been set. -bool SCREEN_INFORMATION::AreMarginsSet() const noexcept -{ - return _scrollMargins.BottomInclusive() > _scrollMargins.Top(); -} - -// Routine Description: -// - Determines whether a cursor position is within the vertical bounds of the -// scroll margins, or the margins aren't set. -// Parameters: -// - cursorPosition - The cursor position to test -// Return value: -// - true iff the position is in bounds. -bool SCREEN_INFORMATION::IsCursorInMargins(const COORD cursorPosition) const noexcept -{ - // If the margins aren't set, then any position is considered in bounds. - if (!AreMarginsSet()) - { - return true; - } - const auto margins = GetAbsoluteScrollMargins().ToInclusive(); - return cursorPosition.Y <= margins.Bottom && cursorPosition.Y >= margins.Top; -} - // Routine Description: // - Engages the legacy VT handling quirk; see TextAttribute::StripErroneousVT16VersionsOfLegacyDefaults void SCREEN_INFORMATION::SetIgnoreLegacyEquivalentVTAttributes() noexcept diff --git a/src/host/screenInfo.hpp b/src/host/screenInfo.hpp index 4b0bdc1b9bc..200fddda8ce 100644 --- a/src/host/screenInfo.hpp +++ b/src/host/screenInfo.hpp @@ -196,8 +196,6 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console Microsoft::Console::Types::Viewport GetRelativeScrollMargins() const; Microsoft::Console::Types::Viewport GetAbsoluteScrollMargins() const; void SetScrollMargins(const Microsoft::Console::Types::Viewport margins); - bool AreMarginsSet() const noexcept; - bool IsCursorInMargins(const COORD cursorPosition) const noexcept; [[nodiscard]] NTSTATUS UseAlternateScreenBuffer(); void UseMainScreenBuffer(); From 177f5ecd7d622c88563dc1003d5bd6b4953e819c Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sat, 12 Feb 2022 22:58:01 +0000 Subject: [PATCH 08/28] Reimplement DECCOLM using ResizeWindow. --- src/host/outputStream.cpp | 29 +++++--------------------- src/host/outputStream.hpp | 3 --- src/terminal/adapter/adaptDispatch.cpp | 12 +++-------- src/terminal/adapter/conGetSet.hpp | 2 -- 4 files changed, 8 insertions(+), 38 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 9927feee1fc..fdc4b146fa8 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -79,28 +79,6 @@ SMALL_RECT ConhostInternalGetSet::GetViewport() const return _io.GetActiveOutputBuffer().GetVirtualViewport().ToExclusive(); } -// Routine Description: -// - Connects the GetConsoleScreenBufferInfoEx API call directly into our Driver Message servicing call inside Conhost.exe -// Arguments: -// - screenBufferInfo - Structure to hold screen buffer information like the public API call. -// Return Value: -// - -void ConhostInternalGetSet::GetConsoleScreenBufferInfoEx(CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) const -{ - ServiceLocator::LocateGlobals().api->GetConsoleScreenBufferInfoExImpl(_io.GetActiveOutputBuffer(), screenBufferInfo); -} - -// Routine Description: -// - Connects the SetConsoleScreenBufferInfoEx API call directly into our Driver Message servicing call inside Conhost.exe -// Arguments: -// - screenBufferInfo - Structure containing screen buffer information like the public API call. -// Return Value: -// - -void ConhostInternalGetSet::SetConsoleScreenBufferInfoEx(const CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) -{ - THROW_IF_FAILED(ServiceLocator::LocateGlobals().api->SetConsoleScreenBufferInfoExImpl(_io.GetActiveOutputBuffer(), screenBufferInfo)); -} - // Routine Description: // - Connects the SetCursorPosition API call directly into our Driver Message servicing call inside Conhost.exe // Arguments: @@ -449,9 +427,12 @@ bool ConhostInternalGetSet::ResizeWindow(const size_t width, const size_t height // We should do nothing if 0 is passed in for a size. RETURN_BOOL_IF_FALSE(width > 0 && height > 0); + auto api = ServiceLocator::LocateGlobals().api; + auto& screenInfo = _io.GetActiveOutputBuffer(); + CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - GetConsoleScreenBufferInfoEx(csbiex); + api->GetConsoleScreenBufferInfoExImpl(screenInfo, csbiex); const Viewport oldViewport = Viewport::FromInclusive(csbiex.srWindow); const Viewport newViewport = Viewport::FromDimensions(oldViewport.Origin(), sColumns, sRows); @@ -471,7 +452,7 @@ bool ConhostInternalGetSet::ResizeWindow(const size_t width, const size_t height const auto sre = newViewport.ToExclusive(); csbiex.srWindow = sre; - SetConsoleScreenBufferInfoEx(csbiex); + THROW_IF_FAILED(api->SetConsoleScreenBufferInfoExImpl(screenInfo, csbiex)); SetWindowInfo(true, sri); return true; } diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 5362a91b8bd..b2ad142c363 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -34,9 +34,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: TextBuffer& GetTextBuffer() override; SMALL_RECT GetViewport() const override; - void GetConsoleScreenBufferInfoEx(CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) const override; - void SetConsoleScreenBufferInfoEx(const CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) override; - void SetCursorPosition(const COORD position) override; void SetTextAttributes(const TextAttribute& attrs) override; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 3e5d30dc53d..e19b6c8bbd9 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -921,15 +921,9 @@ bool AdaptDispatch::ScrollDown(const size_t uiDistance) // - True. bool AdaptDispatch::SetColumns(const size_t columns) { - SHORT col; - THROW_IF_FAILED(SizeTToShort(columns, &col)); - - CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; - csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); - _pConApi->GetConsoleScreenBufferInfoEx(csbiex); - csbiex.dwSize.X = col; - _pConApi->SetConsoleScreenBufferInfoEx(csbiex); - + const auto viewport = _pConApi->GetViewport(); + const auto viewportHeight = viewport.Bottom - viewport.Top; + _pConApi->ResizeWindow(columns, viewportHeight); return true; } diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 3034a42a84f..99df5137541 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -40,8 +40,6 @@ namespace Microsoft::Console::VirtualTerminal virtual TextBuffer& GetTextBuffer() = 0; virtual SMALL_RECT GetViewport() const = 0; - virtual void GetConsoleScreenBufferInfoEx(CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) const = 0; - virtual void SetConsoleScreenBufferInfoEx(const CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) = 0; virtual void SetCursorPosition(const COORD position) = 0; virtual bool IsVtInputEnabled() const = 0; From ae439dbec004d1c494b263e7ecfd0f0ad301c79b Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sun, 13 Feb 2022 15:44:03 +0000 Subject: [PATCH 09/28] Reimplement EraseAll in AdaptDispatch. --- src/host/outputStream.cpp | 10 ----- src/host/outputStream.hpp | 1 - src/host/screenInfo.cpp | 51 ---------------------- src/host/screenInfo.hpp | 1 - src/terminal/adapter/adaptDispatch.cpp | 60 ++++++++++++++++++++++---- src/terminal/adapter/conGetSet.hpp | 1 - 6 files changed, 51 insertions(+), 73 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index fdc4b146fa8..00268260741 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -320,16 +320,6 @@ void ConhostInternalGetSet::UseMainScreenBuffer() _io.GetActiveOutputBuffer().UseMainScreenBuffer(); } -// Routine Description: -// - Performs a VT-style erase all operation on the buffer. -// See SCREEN_INFORMATION::VtEraseAll's description for details. -// Return Value: -// - -void ConhostInternalGetSet::EraseAll() -{ - THROW_IF_FAILED(_io.GetActiveOutputBuffer().VtEraseAll()); -} - // Routine Description: // - Clears the entire contents of the viewport, except for the cursor's row, // which is moved to the top line of the viewport. diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index b2ad142c363..48c8e7f1d6b 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -62,7 +62,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void UseMainScreenBuffer() override; - void EraseAll() override; void ClearBuffer() override; CursorType GetUserDefaultCursorStyle() const override; diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index 81e9c580da8..c74bdad5be4 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -2204,57 +2204,6 @@ void SCREEN_INFORMATION::SetViewport(const Viewport& newViewport, Tracing::s_TraceWindowViewport(_viewport); } -// Method Description: -// - Performs a VT Erase All operation. In most terminals, this is done by -// moving the viewport into the scrollback, clearing out the current screen. -// For them, there can never be any characters beneath the viewport, as the -// viewport is always at the bottom. So, we can accomplish the same behavior -// by using the LastNonspaceCharacter as the "bottom", and placing the new -// viewport underneath that character. -// Parameters: -// -// Return value: -// - S_OK if we succeeded, or another status if there was a failure. -[[nodiscard]] HRESULT SCREEN_INFORMATION::VtEraseAll() -{ - const COORD coordLastChar = _textBuffer->GetLastNonSpaceCharacter(); - short sNewTop = coordLastChar.Y + 1; - const Viewport oldViewport = _viewport; - // Stash away the current position of the cursor within the viewport. - // We'll need to restore the cursor to that same relative position, after - // we move the viewport. - const COORD oldCursorPos = _textBuffer->GetCursor().GetPosition(); - COORD relativeCursor = oldCursorPos; - oldViewport.ConvertToOrigin(&relativeCursor); - - short delta = (sNewTop + _viewport.Height()) - (GetBufferSize().Height()); - for (auto i = 0; i < delta; i++) - { - _textBuffer->IncrementCircularBuffer(); - sNewTop--; - } - - const COORD coordNewOrigin = { 0, sNewTop }; - RETURN_IF_FAILED(SetViewportOrigin(true, coordNewOrigin, true)); - // Restore the relative cursor position - _viewport.ConvertFromOrigin(&relativeCursor); - RETURN_IF_FAILED(SetCursorPosition(relativeCursor, false)); - - // Update all the rows in the current viewport with the standard erase attributes, - // i.e. the current background color, but with no meta attributes set. - auto fillAttributes = GetAttributes(); - fillAttributes.SetStandardErase(); - auto fillPosition = COORD{ 0, _viewport.Top() }; - auto fillLength = gsl::narrow_cast(_viewport.Height() * GetBufferSize().Width()); - auto fillData = OutputCellIterator{ fillAttributes, fillLength }; - Write(fillData, fillPosition, false); - - // Also reset the line rendition for the erased rows. - _textBuffer->ResetLineRenditionRange(_viewport.Top(), _viewport.BottomExclusive()); - - return S_OK; -} - // Method Description: // - Clear the entire contents of the viewport, except for the cursor's row, // which is moved to the top line of the viewport. diff --git a/src/host/screenInfo.hpp b/src/host/screenInfo.hpp index 200fddda8ce..2be6fc87ea8 100644 --- a/src/host/screenInfo.hpp +++ b/src/host/screenInfo.hpp @@ -214,7 +214,6 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console void SetDefaultAttributes(const TextAttribute& attributes, const TextAttribute& popupAttributes); - [[nodiscard]] HRESULT VtEraseAll(); [[nodiscard]] HRESULT ClearBuffer(); void SetTerminalConnection(_In_ Microsoft::Console::Render::VtEngine* const pTtyConnection); diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index e19b6c8bbd9..008280c9099 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1954,19 +1954,61 @@ void AdaptDispatch::_EraseScrollback() } //Routine Description: -// Erase All (^[[2J - ED) -// Erase the current contents of the viewport. In most terminals, because they -// only have a scrollback (and not a buffer per-se), they implement this -// by scrolling the current contents of the buffer off of the screen. -// We can't properly replicate this behavior with only the public API, because -// we need to know where the last character in the buffer is. (it may be below the viewport) -//Arguments: -// +// - Erase All (^[[2J - ED) +// Performs a VT Erase All operation. In most terminals, this is done by +// moving the viewport into the scrollback, clearing out the current screen. +// For them, there can never be any characters beneath the viewport, as the +// viewport is always at the bottom. So, we can accomplish the same behavior +// by using the LastNonspaceCharacter as the "bottom", and placing the new +// viewport underneath that character. +// Arguments: +// - // Return value: // - void AdaptDispatch::_EraseAll() { - _pConApi->EraseAll(); + const auto viewport = _pConApi->GetViewport(); + const short viewportHeight = viewport.Bottom - viewport.Top; + auto& textBuffer = _pConApi->GetTextBuffer(); + const auto bufferSize = textBuffer.GetSize(); + + // Stash away the current position of the cursor within the viewport. + // We'll need to restore the cursor to that same relative position, after + // we move the viewport. + auto cursorPosition = textBuffer.GetCursor().GetPosition(); + cursorPosition.Y -= viewport.Top; + + // Calculate new viewport position + short newViewportTop = textBuffer.GetLastNonSpaceCharacter().Y + 1; + const auto delta = (newViewportTop + viewportHeight) - (bufferSize.Height()); + for (auto i = 0; i < delta; i++) + { + textBuffer.IncrementCircularBuffer(); + newViewportTop--; + } + // Move the viewport + SMALL_RECT newViewport; + newViewport.Left = viewport.Left; + newViewport.Top = newViewportTop; + // SetWindowInfo uses an inclusive rect, while the viewport rect is exclusive + newViewport.Right = viewport.Right - 1; + newViewport.Bottom = newViewportTop + viewportHeight - 1; + _pConApi->SetWindowInfo(true, newViewport); + // Restore the relative cursor position + cursorPosition.Y += newViewportTop; + _pConApi->SetCursorPosition(cursorPosition); + + // Update all the rows in the current viewport with the standard erase attributes, + // i.e. the current background color, but with no meta attributes set. + auto fillAttributes = textBuffer.GetCurrentAttributes(); + fillAttributes.SetStandardErase(); + auto fillPosition = COORD{ 0, newViewportTop }; + auto fillLength = gsl::narrow_cast(viewportHeight * bufferSize.Width()); + auto fillData = OutputCellIterator{ fillAttributes, fillLength }; + textBuffer.Write(fillData, fillPosition, false); + + // Also reset the line rendition for the erased rows. + textBuffer.ResetLineRenditionRange(newViewportTop, newViewportTop + viewportHeight); } // Routine Description: diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 99df5137541..c8f5260cab3 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -64,7 +64,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void UseAlternateScreenBuffer() = 0; virtual void UseMainScreenBuffer() = 0; - virtual void EraseAll() = 0; virtual void ClearBuffer() = 0; virtual CursorType GetUserDefaultCursorStyle() const = 0; virtual void WriteControlInput(const KeyEvent key) = 0; From 23275abf993399f30e5676232392b53df5aa9a27 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 15 Feb 2022 17:09:40 +0000 Subject: [PATCH 10/28] Replace SetWindowInfo with SetViewportPosition. --- src/host/outputStream.cpp | 28 ++++++++++++++------------ src/host/outputStream.hpp | 4 +--- src/terminal/adapter/adaptDispatch.cpp | 18 +++-------------- src/terminal/adapter/conGetSet.hpp | 3 +-- 4 files changed, 20 insertions(+), 33 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 00268260741..9e8e462ad6d 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -79,6 +79,20 @@ SMALL_RECT ConhostInternalGetSet::GetViewport() const return _io.GetActiveOutputBuffer().GetVirtualViewport().ToExclusive(); } +// Routine Description: +// - Sets the position of the window viewport. +// Arguments: +// - position - the new position of the viewport. +// Return Value: +// - +void ConhostInternalGetSet::SetViewportPosition(const COORD position) +{ + auto& info = _io.GetActiveOutputBuffer(); + const auto dimensions = info.GetViewport().Dimensions(); + const auto windowRect = Viewport::FromDimensions(position, dimensions).ToInclusive(); + THROW_IF_FAILED(ServiceLocator::LocateGlobals().api->SetConsoleWindowInfoImpl(info, true, windowRect)); +} + // Routine Description: // - Connects the SetCursorPosition API call directly into our Driver Message servicing call inside Conhost.exe // Arguments: @@ -117,18 +131,6 @@ void ConhostInternalGetSet::WriteInput(std::deque>& eventsWritten = _io.GetActiveInputBuffer()->Write(events); } -// Routine Description: -// - Connects the SetWindowInfo API call directly into our Driver Message servicing call inside Conhost.exe -// Arguments: -// - absolute - Should the window be moved to an absolute position? If false, the movement is relative to the current pos. -// - window - Info about how to move the viewport -// Return Value: -// - -void ConhostInternalGetSet::SetWindowInfo(const bool absolute, const SMALL_RECT& window) -{ - THROW_IF_FAILED(ServiceLocator::LocateGlobals().api->SetConsoleWindowInfoImpl(_io.GetActiveOutputBuffer(), absolute, window)); -} - // Routine Description: // - Sets the various terminal input modes. // SetInputMode is an internal-only "API" call that the vt commands can execute, @@ -443,7 +445,7 @@ bool ConhostInternalGetSet::ResizeWindow(const size_t width, const size_t height csbiex.srWindow = sre; THROW_IF_FAILED(api->SetConsoleScreenBufferInfoExImpl(screenInfo, csbiex)); - SetWindowInfo(true, sri); + THROW_IF_FAILED(api->SetConsoleWindowInfoImpl(screenInfo, true, sri)); return true; } diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 48c8e7f1d6b..7d3c61328c7 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -33,15 +33,13 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: TextBuffer& GetTextBuffer() override; SMALL_RECT GetViewport() const override; - + void SetViewportPosition(const COORD position) override; void SetCursorPosition(const COORD position) override; void SetTextAttributes(const TextAttribute& attrs) override; void WriteInput(std::deque>& events, size_t& eventsWritten) override; - void SetWindowInfo(bool const absolute, const SMALL_RECT& window) override; - bool SetInputMode(const Microsoft::Console::VirtualTerminal::TerminalInput::Mode mode, const bool enabled) override; void SetParserMode(const Microsoft::Console::VirtualTerminal::StateMachine::Mode mode, const bool enabled) override; bool GetParserMode(const Microsoft::Console::VirtualTerminal::StateMachine::Mode mode) const override; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 008280c9099..564bccc44c3 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1940,14 +1940,8 @@ void AdaptDispatch::_EraseScrollback() _pConApi->FillRegion(coordBelowStartPosition, totalAreaBelow, L' ', false); // Also reset the line rendition for all of the cleared rows. textBuffer.ResetLineRenditionRange(height, bufferSize.Y); - // Move the viewport (CAN'T be done in one call with SetConsolescreenBufferInfoEx, because legacy) - SMALL_RECT newViewport; - newViewport.Left = screen.Left; - newViewport.Top = 0; - // SetWindowInfo uses an inclusive rect, while the screen rect is exclusive - newViewport.Right = screen.Right - 1; - newViewport.Bottom = height - 1; - _pConApi->SetWindowInfo(true, newViewport); + // Move the viewport + _pConApi->SetViewportPosition({ screen.Left, 0 }); // Move the cursor to the same relative location. const COORD newcursor = { cursorPosition.X, cursorPosition.Y - screen.Top }; _pConApi->SetCursorPosition(newcursor); @@ -1987,13 +1981,7 @@ void AdaptDispatch::_EraseAll() newViewportTop--; } // Move the viewport - SMALL_RECT newViewport; - newViewport.Left = viewport.Left; - newViewport.Top = newViewportTop; - // SetWindowInfo uses an inclusive rect, while the viewport rect is exclusive - newViewport.Right = viewport.Right - 1; - newViewport.Bottom = newViewportTop + viewportHeight - 1; - _pConApi->SetWindowInfo(true, newViewport); + _pConApi->SetViewportPosition({ viewport.Left, newViewportTop }); // Restore the relative cursor position cursorPosition.Y += newViewportTop; _pConApi->SetCursorPosition(cursorPosition); diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index c8f5260cab3..7e4673a0ff0 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -39,7 +39,7 @@ namespace Microsoft::Console::VirtualTerminal virtual TextBuffer& GetTextBuffer() = 0; virtual SMALL_RECT GetViewport() const = 0; - + virtual void SetViewportPosition(const COORD position) = 0; virtual void SetCursorPosition(const COORD position) = 0; virtual bool IsVtInputEnabled() const = 0; @@ -47,7 +47,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void SetTextAttributes(const TextAttribute& attrs) = 0; virtual void WriteInput(std::deque>& events, size_t& eventsWritten) = 0; - virtual void SetWindowInfo(const bool absolute, const SMALL_RECT& window) = 0; virtual bool SetInputMode(const TerminalInput::Mode mode, const bool enabled) = 0; virtual void SetParserMode(const StateMachine::Mode mode, const bool enabled) = 0; From 01df805d6ffe148c3ac8e714cec15fc5b24b8e34 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 15 Feb 2022 18:43:06 +0000 Subject: [PATCH 11/28] Remove SuppressResizeRepaint from ConGetSet. --- src/host/PtySignalInputThread.cpp | 3 ++- src/host/outputStream.cpp | 13 ------------- src/host/outputStream.hpp | 1 - src/terminal/adapter/InteractDispatch.cpp | 8 ++++++-- src/terminal/adapter/conGetSet.hpp | 1 - 5 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/host/PtySignalInputThread.cpp b/src/host/PtySignalInputThread.cpp index d480e135617..29ed7df78c7 100644 --- a/src/host/PtySignalInputThread.cpp +++ b/src/host/PtySignalInputThread.cpp @@ -138,7 +138,8 @@ void PtySignalInputThread::_DoResizeWindow(const ResizeWindowData& data) { if (_pConApi->ResizeWindow(data.sx, data.sy)) { - _pConApi->SuppressResizeRepaint(); + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + THROW_IF_FAILED(gci.GetVtIo()->SuppressResizeRepaint()); } } diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 9e8e462ad6d..5a358ec67aa 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -449,19 +449,6 @@ bool ConhostInternalGetSet::ResizeWindow(const size_t width, const size_t height return true; } -// Routine Description: -// - Forces the VT Renderer to NOT paint the next resize event. This is used by -// InteractDispatch, to prevent resizes from echoing between terminal and host. -// Arguments: -// - -// Return Value: -// - -void ConhostInternalGetSet::SuppressResizeRepaint() -{ - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - THROW_IF_FAILED(gci.GetVtIo()->SuppressResizeRepaint()); -} - // Routine Description: // - Checks if the console host is acting as a pty. // Arguments: diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 7d3c61328c7..12dafb6d2a9 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -66,7 +66,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void RefreshWindow() override; bool ResizeWindow(const size_t width, const size_t height) override; - void SuppressResizeRepaint() override; void WriteControlInput(const KeyEvent key) override; diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp index df3f38d9e2e..df8068499aa 100644 --- a/src/terminal/adapter/InteractDispatch.cpp +++ b/src/terminal/adapter/InteractDispatch.cpp @@ -5,10 +5,13 @@ #include "InteractDispatch.hpp" #include "conGetSet.hpp" +#include "../../host/conddkrefs.h" +#include "../../interactivity/inc/ServiceLocator.hpp" #include "../../interactivity/inc/EventSynthesis.hpp" #include "../../types/inc/Viewport.hpp" #include "../../inc/unicode.hpp" +using namespace Microsoft::Console::Interactivity; using namespace Microsoft::Console::Types; using namespace Microsoft::Console::VirtualTerminal; @@ -67,7 +70,7 @@ bool InteractDispatch::WriteString(const std::wstring_view string) for (const auto& wch : string) { - std::deque> convertedEvents = Microsoft::Console::Interactivity::CharToKeyEvents(wch, codepage); + std::deque> convertedEvents = CharToKeyEvents(wch, codepage); std::move(convertedEvents.begin(), convertedEvents.end(), @@ -108,7 +111,8 @@ bool InteractDispatch::WindowManipulation(const DispatchTypes::WindowManipulatio // the ConGetSet interface, that specifically handles a conpty resize. if (_pConApi->ResizeWindow(parameter2.value_or(0), parameter1.value_or(0))) { - _pConApi->SuppressResizeRepaint(); + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + THROW_IF_FAILED(gci.GetVtIo()->SuppressResizeRepaint()); } return true; default: diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 7e4673a0ff0..311d1d54f3b 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -72,7 +72,6 @@ namespace Microsoft::Console::VirtualTerminal virtual unsigned int GetConsoleOutputCP() const = 0; virtual bool ResizeWindow(const size_t width, const size_t height) = 0; - virtual void SuppressResizeRepaint() = 0; virtual bool IsConsolePty() const = 0; virtual COLORREF GetColorTableEntry(const size_t tableIndex) const = 0; From dcee1352814dd46a99610b1b1ff0e48f89fbdf93 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 15 Feb 2022 19:43:23 +0000 Subject: [PATCH 12/28] Reimplement SetInputMode in AdaptDispatch. --- src/host/outputStream.cpp | 26 -------------- src/host/outputStream.hpp | 1 - src/host/screenInfo.cpp | 4 ++- src/terminal/adapter/adaptDispatch.cpp | 47 +++++++++++++++++++------- src/terminal/adapter/adaptDispatch.hpp | 7 ++-- src/terminal/adapter/conGetSet.hpp | 2 -- 6 files changed, 43 insertions(+), 44 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 5a358ec67aa..2be9d9957a7 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -17,7 +17,6 @@ using namespace Microsoft::Console; using Microsoft::Console::Interactivity::ServiceLocator; using Microsoft::Console::VirtualTerminal::StateMachine; -using Microsoft::Console::VirtualTerminal::TerminalInput; ConhostInternalGetSet::ConhostInternalGetSet(_In_ IIoProvider& io) : _io{ io } @@ -131,31 +130,6 @@ void ConhostInternalGetSet::WriteInput(std::deque>& eventsWritten = _io.GetActiveInputBuffer()->Write(events); } -// Routine Description: -// - Sets the various terminal input modes. -// SetInputMode is an internal-only "API" call that the vt commands can execute, -// but it is not represented as a function call on out public API surface. -// Arguments: -// - mode - the input mode to change. -// - enabled - set to true to enable the mode, false to disable it. -// Return Value: -// - true if successful. false otherwise. -bool ConhostInternalGetSet::SetInputMode(const TerminalInput::Mode mode, const bool enabled) -{ - auto& terminalInput = _io.GetActiveInputBuffer()->GetTerminalInput(); - terminalInput.SetInputMode(mode, enabled); - - // If we're a conpty, AND WE'RE IN VT INPUT MODE, always pass input mode requests - // The VT Input mode check is to work around ssh.exe v7.7, which uses VT - // output, but not Input. - // The original comment said, "Once the conpty supports these types of input, - // this check can be removed. See GH#4911". Unfortunately, time has shown - // us that SSH 7.7 _also_ requests mouse input and that can have a user interface - // impact on the actual connected terminal. We can't remove this check, - // because SSH <=7.7 is out in the wild on all versions of Windows <=2004. - return !(IsConsolePty() && IsVtInputEnabled()); -} - // Routine Description: // - Sets the various StateMachine parser modes. // SetParserMode is an internal-only "API" call that the vt commands can execute, diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 12dafb6d2a9..4a2040bdd3a 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -40,7 +40,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void WriteInput(std::deque>& events, size_t& eventsWritten) override; - bool SetInputMode(const Microsoft::Console::VirtualTerminal::TerminalInput::Mode mode, const bool enabled) override; void SetParserMode(const Microsoft::Console::VirtualTerminal::StateMachine::Mode mode, const bool enabled) override; bool GetParserMode(const Microsoft::Console::VirtualTerminal::StateMachine::Mode mode) const override; void SetRenderMode(const RenderSettings::Mode mode, const bool enabled) override; diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index c74bdad5be4..b3ce7b76490 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -254,8 +254,10 @@ void SCREEN_INFORMATION::s_RemoveScreenBuffer(_In_ SCREEN_INFORMATION* const pSc { try { + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& terminalInput = gci.GetActiveInputBuffer()->GetTerminalInput(); auto getset = std::make_unique(*this); - auto adapter = std::make_unique(std::move(getset)); + auto adapter = std::make_unique(std::move(getset), terminalInput); auto engine = std::make_unique(std::move(adapter)); // Note that at this point in the setup, we haven't determined if we're // in VtIo mode or not yet. We'll set the OutputStateMachine's diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 564bccc44c3..2523face726 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -26,8 +26,9 @@ bool NoOp() noexcept } // Note: AdaptDispatch will take ownership of pConApi and pDefaults -AdaptDispatch::AdaptDispatch(std::unique_ptr pConApi) : +AdaptDispatch::AdaptDispatch(std::unique_ptr pConApi, TerminalInput& terminalInput) : _pConApi{ std::move(pConApi) }, + _terminalInput{ terminalInput }, _usingAltBuffer(false), _isOriginModeRelative(false), // by default, the DECOM origin mode is absolute. _isDECCOLMAllowed(false), // by default, DECCOLM is not allowed. @@ -948,6 +949,28 @@ bool AdaptDispatch::_DoDECCOLMHelper(const size_t columns) return true; } +// Routine Description: +// - Sets the various terminal input modes. +// Arguments: +// - mode - the input mode to change. +// - enable - set to true to enable the mode, false to disable it. +// Return Value: +// - true if successful. false otherwise. +bool AdaptDispatch::_SetInputMode(const TerminalInput::Mode mode, const bool enable) +{ + _terminalInput.SetInputMode(mode, enable); + + // If we're a conpty, AND WE'RE IN VT INPUT MODE, always pass input mode requests + // The VT Input mode check is to work around ssh.exe v7.7, which uses VT + // output, but not Input. + // The original comment said, "Once the conpty supports these types of input, + // this check can be removed. See GH#4911". Unfortunately, time has shown + // us that SSH 7.7 _also_ requests mouse input and that can have a user interface + // impact on the actual connected terminal. We can't remove this check, + // because SSH <=7.7 is out in the wild on all versions of Windows <=2004. + return !(_pConApi->IsConsolePty() && _pConApi->IsVtInputEnabled()); +} + // Routine Description: // - Support routine for routing private mode parameters to be set/reset as flags // Arguments: @@ -1050,7 +1073,7 @@ bool AdaptDispatch::ResetMode(const DispatchTypes::ModeParams param) // - True if handled successfully. False otherwise. bool AdaptDispatch::SetKeypadMode(const bool fApplicationMode) { - return _pConApi->SetInputMode(TerminalInput::Mode::Keypad, fApplicationMode); + return _SetInputMode(TerminalInput::Mode::Keypad, fApplicationMode); } // Method Description: @@ -1062,7 +1085,7 @@ bool AdaptDispatch::SetKeypadMode(const bool fApplicationMode) // - True if handled successfully. False otherwise. bool AdaptDispatch::EnableWin32InputMode(const bool win32InputMode) { - return _pConApi->SetInputMode(TerminalInput::Mode::Win32, win32InputMode); + return _SetInputMode(TerminalInput::Mode::Win32, win32InputMode); } // - DECCKM - Sets the cursor keys input mode to either Application mode or Normal mode (true, false respectively) @@ -1072,7 +1095,7 @@ bool AdaptDispatch::EnableWin32InputMode(const bool win32InputMode) // - True if handled successfully. False otherwise. bool AdaptDispatch::SetCursorKeysMode(const bool applicationMode) { - return _pConApi->SetInputMode(TerminalInput::Mode::CursorKey, applicationMode); + return _SetInputMode(TerminalInput::Mode::CursorKey, applicationMode); } // - att610 - Enables or disables the cursor blinking. @@ -1193,9 +1216,9 @@ bool AdaptDispatch::SetAnsiMode(const bool ansiMode) _termOutput = {}; _pConApi->SetParserMode(StateMachine::Mode::Ansi, ansiMode); - _pConApi->SetInputMode(TerminalInput::Mode::Ansi, ansiMode); + _SetInputMode(TerminalInput::Mode::Ansi, ansiMode); - // We don't check the SetInputMode return value, because we'll never want + // We don't check the _SetInputMode return value, because we'll never want // to forward a DECANM mode change over conpty. return true; } @@ -2019,7 +2042,7 @@ bool AdaptDispatch::EnableDECCOLMSupport(const bool enabled) noexcept // True if handled successfully. False otherwise. bool AdaptDispatch::EnableVT200MouseMode(const bool enabled) { - return _pConApi->SetInputMode(TerminalInput::Mode::DefaultMouseTracking, enabled); + return _SetInputMode(TerminalInput::Mode::DefaultMouseTracking, enabled); } //Routine Description: @@ -2031,7 +2054,7 @@ bool AdaptDispatch::EnableVT200MouseMode(const bool enabled) // True if handled successfully. False otherwise. bool AdaptDispatch::EnableUTF8ExtendedMouseMode(const bool enabled) { - return _pConApi->SetInputMode(TerminalInput::Mode::Utf8MouseEncoding, enabled); + return _SetInputMode(TerminalInput::Mode::Utf8MouseEncoding, enabled); } //Routine Description: @@ -2043,7 +2066,7 @@ bool AdaptDispatch::EnableUTF8ExtendedMouseMode(const bool enabled) // True if handled successfully. False otherwise. bool AdaptDispatch::EnableSGRExtendedMouseMode(const bool enabled) { - return _pConApi->SetInputMode(TerminalInput::Mode::SgrMouseEncoding, enabled); + return _SetInputMode(TerminalInput::Mode::SgrMouseEncoding, enabled); } //Routine Description: @@ -2054,7 +2077,7 @@ bool AdaptDispatch::EnableSGRExtendedMouseMode(const bool enabled) // True if handled successfully. False otherwise. bool AdaptDispatch::EnableButtonEventMouseMode(const bool enabled) { - return _pConApi->SetInputMode(TerminalInput::Mode::ButtonEventMouseTracking, enabled); + return _SetInputMode(TerminalInput::Mode::ButtonEventMouseTracking, enabled); } //Routine Description: @@ -2066,7 +2089,7 @@ bool AdaptDispatch::EnableButtonEventMouseMode(const bool enabled) // True if handled successfully. False otherwise. bool AdaptDispatch::EnableAnyEventMouseMode(const bool enabled) { - return _pConApi->SetInputMode(TerminalInput::Mode::AnyEventMouseTracking, enabled); + return _SetInputMode(TerminalInput::Mode::AnyEventMouseTracking, enabled); } //Routine Description: @@ -2078,7 +2101,7 @@ bool AdaptDispatch::EnableAnyEventMouseMode(const bool enabled) // True if handled successfully. False otherwise. bool AdaptDispatch::EnableAlternateScroll(const bool enabled) { - return _pConApi->SetInputMode(TerminalInput::Mode::AlternateScroll, enabled); + return _SetInputMode(TerminalInput::Mode::AlternateScroll, enabled); } //Routine Description: diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 5e427674d84..4ef37daa5ea 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -18,14 +18,15 @@ Author(s): #include "conGetSet.hpp" #include "FontBuffer.hpp" #include "terminalOutput.hpp" -#include "..\..\types\inc\sgrStack.hpp" +#include "../input/terminalInput.hpp" +#include "../../types/inc/sgrStack.hpp" namespace Microsoft::Console::VirtualTerminal { class AdaptDispatch : public ITermDispatch { public: - AdaptDispatch(std::unique_ptr pConApi); + AdaptDispatch(std::unique_ptr pConApi, TerminalInput& terminalInput); void Print(const wchar_t wchPrintable) override; void PrintString(const std::wstring_view string) override; @@ -178,6 +179,7 @@ namespace Microsoft::Console::VirtualTerminal void _CursorPositionReport() const; void _WriteResponse(const std::wstring_view reply) const; + bool _SetInputMode(const TerminalInput::Mode mode, const bool enable); bool _ModeParamsHelper(const DispatchTypes::ModeParams param, const bool enable); bool _DoDECCOLMHelper(const size_t columns); @@ -193,6 +195,7 @@ namespace Microsoft::Console::VirtualTerminal bool _initDefaultTabStops = true; std::unique_ptr _pConApi; + TerminalInput& _terminalInput; TerminalOutput _termOutput; std::unique_ptr _fontBuffer; std::optional _initialCodePage; diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 311d1d54f3b..fe449c51f68 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -15,7 +15,6 @@ Author(s): #pragma once -#include "../input/terminalInput.hpp" #include "../parser/stateMachine.hpp" #include "../../types/inc/IInputEvent.hpp" #include "../../buffer/out/LineRendition.hpp" @@ -48,7 +47,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void WriteInput(std::deque>& events, size_t& eventsWritten) = 0; - virtual bool SetInputMode(const TerminalInput::Mode mode, const bool enabled) = 0; virtual void SetParserMode(const StateMachine::Mode mode, const bool enabled) = 0; virtual bool GetParserMode(const StateMachine::Mode mode) const = 0; virtual void SetRenderMode(const RenderSettings::Mode mode, const bool enabled) = 0; From fdd93689c30715f41e6a028ae1dd4c89ef1836af Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 15 Feb 2022 20:20:49 +0000 Subject: [PATCH 13/28] Remove WriteControlInput from ConGetSet. --- src/host/outputStream.cpp | 17 ----------------- src/host/outputStream.hpp | 2 -- src/terminal/adapter/InteractDispatch.cpp | 2 +- src/terminal/adapter/conGetSet.hpp | 1 - 4 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 2be9d9957a7..b6ca3787bfb 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -337,23 +337,6 @@ void ConhostInternalGetSet::RefreshWindow() } } -// Routine Description: -// - Writes the input KeyEvent to the console as a console control event. This -// can be used for potentially generating Ctrl-C events, as -// HandleGenericKeyEvent will correctly generate the Ctrl-C response in -// the same way that it'd be handled from the window proc, with the proper -// processed vs raw input handling. -// If the input key is *not* a Ctrl-C key, then it will get written to the -// buffer just the same as any other KeyEvent. -// Arguments: -// - key - The keyevent to send to the console. -// Return Value: -// - -void ConhostInternalGetSet::WriteControlInput(const KeyEvent key) -{ - HandleGenericKeyEvent(key, false); -} - // Routine Description: // - Connects the SetConsoleOutputCP API call directly into our Driver Message servicing call inside Conhost.exe // Arguments: diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 4a2040bdd3a..3546d49513f 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -66,8 +66,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void RefreshWindow() override; bool ResizeWindow(const size_t width, const size_t height) override; - void WriteControlInput(const KeyEvent key) override; - void SetConsoleOutputCP(const unsigned int codepage) override; unsigned int GetConsoleOutputCP() const override; diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp index df8068499aa..6bceaf75a85 100644 --- a/src/terminal/adapter/InteractDispatch.cpp +++ b/src/terminal/adapter/InteractDispatch.cpp @@ -50,7 +50,7 @@ bool InteractDispatch::WriteInput(std::deque>& inpu // - True. bool InteractDispatch::WriteCtrlKey(const KeyEvent& event) { - _pConApi->WriteControlInput(event); + HandleGenericKeyEvent(event, false); return true; } diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index fe449c51f68..c860aca1467 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -63,7 +63,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void ClearBuffer() = 0; virtual CursorType GetUserDefaultCursorStyle() const = 0; - virtual void WriteControlInput(const KeyEvent key) = 0; virtual void RefreshWindow() = 0; virtual void SetConsoleOutputCP(const unsigned int codepage) = 0; From 9f78c07046f2123ddb1749299cfb033d6fc05032 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 15 Feb 2022 20:57:13 +0000 Subject: [PATCH 14/28] Remove ClearBuffer from ConGetSet. --- src/host/PtySignalInputThread.cpp | 3 ++- src/host/outputStream.cpp | 11 ----------- src/host/outputStream.hpp | 2 -- src/terminal/adapter/conGetSet.hpp | 1 - 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/host/PtySignalInputThread.cpp b/src/host/PtySignalInputThread.cpp index 29ed7df78c7..56874ae68b5 100644 --- a/src/host/PtySignalInputThread.cpp +++ b/src/host/PtySignalInputThread.cpp @@ -145,7 +145,8 @@ void PtySignalInputThread::_DoResizeWindow(const ResizeWindowData& data) void PtySignalInputThread::_DoClearBuffer() { - _pConApi->ClearBuffer(); + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + THROW_IF_FAILED(gci.GetActiveOutputBuffer().ClearBuffer()); } // Method Description: diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index b6ca3787bfb..8c34a7aed0b 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -296,17 +296,6 @@ void ConhostInternalGetSet::UseMainScreenBuffer() _io.GetActiveOutputBuffer().UseMainScreenBuffer(); } -// Routine Description: -// - Clears the entire contents of the viewport, except for the cursor's row, -// which is moved to the top line of the viewport. -// See SCREEN_INFORMATION::ClearBuffer's description for details. -// Return Value: -// - -void ConhostInternalGetSet::ClearBuffer() -{ - THROW_IF_FAILED(_io.GetActiveOutputBuffer().ClearBuffer()); -} - // Method Description: // - Retrieves the current user default cursor style. // Arguments: diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 3546d49513f..0292e5b76d1 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -59,8 +59,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void UseMainScreenBuffer() override; - void ClearBuffer() override; - CursorType GetUserDefaultCursorStyle() const override; void RefreshWindow() override; diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index c860aca1467..39258132684 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -61,7 +61,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void UseAlternateScreenBuffer() = 0; virtual void UseMainScreenBuffer() = 0; - virtual void ClearBuffer() = 0; virtual CursorType GetUserDefaultCursorStyle() const = 0; virtual void RefreshWindow() = 0; From f1f7bcf20425143548d98e8bfa9feb87d588c139 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Wed, 16 Feb 2022 00:01:44 +0000 Subject: [PATCH 15/28] Move Get/SetParserMode to AdaptDispatch. --- src/host/outputStream.cpp | 40 +++++++------------------- src/host/outputStream.hpp | 3 +- src/terminal/adapter/adaptDispatch.cpp | 29 +++++++++++++++++-- src/terminal/adapter/adaptDispatch.hpp | 2 ++ src/terminal/adapter/conGetSet.hpp | 3 +- 5 files changed, 41 insertions(+), 36 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 8c34a7aed0b..781026647e1 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -56,6 +56,17 @@ void ConhostInternalGetSet::PrintString(const std::wstring_view string) THROW_IF_NTSTATUS_FAILED(ntstatus); } +// Routine Description: +// - Retrieves the state machine for the active output buffer. +// Arguments: +// - +// Return Value: +// - a reference to the StateMachine instance. +StateMachine& ConhostInternalGetSet::GetStateMachine() +{ + return _io.GetActiveOutputBuffer().GetStateMachine(); +} + // Routine Description: // - Retrieves the text buffer for the active output buffer. // Arguments: @@ -130,35 +141,6 @@ void ConhostInternalGetSet::WriteInput(std::deque>& eventsWritten = _io.GetActiveInputBuffer()->Write(events); } -// Routine Description: -// - Sets the various StateMachine parser modes. -// SetParserMode is an internal-only "API" call that the vt commands can execute, -// but it is not represented as a function call on out public API surface. -// Arguments: -// - mode - the parser mode to change. -// - enabled - set to true to enable the mode, false to disable it. -// Return Value: -// - -void ConhostInternalGetSet::SetParserMode(const StateMachine::Mode mode, const bool enabled) -{ - auto& stateMachine = _io.GetActiveOutputBuffer().GetStateMachine(); - stateMachine.SetParserMode(mode, enabled); -} - -// Routine Description: -// - Retrieves the various StateMachine parser modes. -// GetParserMode is an internal-only "API" call that the vt commands can execute, -// but it is not represented as a function call on out public API surface. -// Arguments: -// - mode - the parser mode to query. -// Return Value: -// - true if the mode is enabled. false if disabled. -bool ConhostInternalGetSet::GetParserMode(const Microsoft::Console::VirtualTerminal::StateMachine::Mode mode) const -{ - auto& stateMachine = _io.GetActiveOutputBuffer().GetStateMachine(); - return stateMachine.GetParserMode(mode); -} - // Routine Description: // - Sets the various render modes. // Arguments: diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 0292e5b76d1..7b8373843e2 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -31,6 +31,7 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void PrintString(const std::wstring_view string) override; + Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() override; TextBuffer& GetTextBuffer() override; SMALL_RECT GetViewport() const override; void SetViewportPosition(const COORD position) override; @@ -40,8 +41,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void WriteInput(std::deque>& events, size_t& eventsWritten) override; - void SetParserMode(const Microsoft::Console::VirtualTerminal::StateMachine::Mode mode, const bool enabled) override; - bool GetParserMode(const Microsoft::Console::VirtualTerminal::StateMachine::Mode mode) const override; void SetRenderMode(const RenderSettings::Mode mode, const bool enabled) override; void SetAutoWrapMode(const bool wrapAtEOL) override; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 2523face726..42de3bfc3a7 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -320,7 +320,7 @@ bool AdaptDispatch::CursorSaveState() savedCursorState.IsOriginModeRelative = _isOriginModeRelative; savedCursorState.Attributes = attributes; savedCursorState.TermOutput = _termOutput; - savedCursorState.C1ControlsAccepted = _pConApi->GetParserMode(StateMachine::Mode::AcceptC1); + savedCursorState.C1ControlsAccepted = _GetParserMode(StateMachine::Mode::AcceptC1); savedCursorState.CodePage = _pConApi->GetConsoleOutputCP(); return true; @@ -949,6 +949,29 @@ bool AdaptDispatch::_DoDECCOLMHelper(const size_t columns) return true; } +// Routine Description : +// - Retrieves the various StateMachine parser modes. +// Arguments: +// - mode - the parser mode to query. +// Return Value: +// - true if the mode is enabled. false if disabled. +bool AdaptDispatch::_GetParserMode(const StateMachine::Mode mode) const +{ + return _pConApi->GetStateMachine().GetParserMode(mode); +} + +// Routine Description: +// - Sets the various StateMachine parser modes. +// Arguments: +// - mode - the parser mode to change. +// - enable - set to true to enable the mode, false to disable it. +// Return Value: +// - +void AdaptDispatch::_SetParserMode(const StateMachine::Mode mode, const bool enable) +{ + _pConApi->GetStateMachine().SetParserMode(mode, enable); +} + // Routine Description: // - Sets the various terminal input modes. // Arguments: @@ -1215,7 +1238,7 @@ bool AdaptDispatch::SetAnsiMode(const bool ansiMode) // need to be reset to defaults, even if the mode doesn't actually change. _termOutput = {}; - _pConApi->SetParserMode(StateMachine::Mode::Ansi, ansiMode); + _SetParserMode(StateMachine::Mode::Ansi, ansiMode); _SetInputMode(TerminalInput::Mode::Ansi, ansiMode); // We don't check the _SetInputMode return value, because we'll never want @@ -1759,7 +1782,7 @@ bool AdaptDispatch::SingleShift(const size_t gsetNumber) // - True. bool AdaptDispatch::AcceptC1Controls(const bool enabled) { - _pConApi->SetParserMode(StateMachine::Mode::AcceptC1, enabled); + _SetParserMode(StateMachine::Mode::AcceptC1, enabled); return true; } diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 4ef37daa5ea..00081409f5d 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -179,6 +179,8 @@ namespace Microsoft::Console::VirtualTerminal void _CursorPositionReport() const; void _WriteResponse(const std::wstring_view reply) const; + bool _GetParserMode(const StateMachine::Mode mode) const; + void _SetParserMode(const StateMachine::Mode mode, const bool enable); bool _SetInputMode(const TerminalInput::Mode mode, const bool enable); bool _ModeParamsHelper(const DispatchTypes::ModeParams param, const bool enable); bool _DoDECCOLMHelper(const size_t columns); diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 39258132684..355b5f60e60 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -36,6 +36,7 @@ namespace Microsoft::Console::VirtualTerminal virtual void PrintString(const std::wstring_view string) = 0; + virtual StateMachine& GetStateMachine() = 0; virtual TextBuffer& GetTextBuffer() = 0; virtual SMALL_RECT GetViewport() const = 0; virtual void SetViewportPosition(const COORD position) = 0; @@ -47,8 +48,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void WriteInput(std::deque>& events, size_t& eventsWritten) = 0; - virtual void SetParserMode(const StateMachine::Mode mode, const bool enabled) = 0; - virtual bool GetParserMode(const StateMachine::Mode mode) const = 0; virtual void SetRenderMode(const RenderSettings::Mode mode, const bool enabled) = 0; virtual void SetAutoWrapMode(const bool wrapAtEOL) = 0; From 51e7b2db14f5d3cef39b50db0272cc223e72e9a3 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 17 Feb 2022 08:43:26 +0000 Subject: [PATCH 16/28] Remove RefreshWindow from ConGetSet. --- src/host/outputStream.cpp | 18 ------------------ src/host/outputStream.hpp | 1 - src/terminal/adapter/InteractDispatch.cpp | 2 +- src/terminal/adapter/adaptDispatch.cpp | 2 +- src/terminal/adapter/conGetSet.hpp | 1 - 5 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 781026647e1..fd3c6fbd6ea 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -290,24 +290,6 @@ CursorType ConhostInternalGetSet::GetUserDefaultCursorStyle() const return gci.GetCursorType(); } -// Routine Description: -// - Forces the renderer to repaint the screen. If the input screen buffer is -// not the active one, then just do nothing. We only want to redraw the -// screen buffer that requested the repaint, and switching screen buffers -// will already force a repaint. -// Arguments: -// - -// Return Value: -// - -void ConhostInternalGetSet::RefreshWindow() -{ - auto& g = ServiceLocator::LocateGlobals(); - if (&_io.GetActiveOutputBuffer() == &g.getConsoleInformation().GetActiveOutputBuffer()) - { - g.pRender->TriggerRedrawAll(); - } -} - // Routine Description: // - Connects the SetConsoleOutputCP API call directly into our Driver Message servicing call inside Conhost.exe // Arguments: diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 7b8373843e2..d834204e758 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -60,7 +60,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: CursorType GetUserDefaultCursorStyle() const override; - void RefreshWindow() override; bool ResizeWindow(const size_t width, const size_t height) override; void SetConsoleOutputCP(const unsigned int codepage) override; diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp index 6bceaf75a85..a7d2aad44bf 100644 --- a/src/terminal/adapter/InteractDispatch.cpp +++ b/src/terminal/adapter/InteractDispatch.cpp @@ -104,7 +104,7 @@ bool InteractDispatch::WindowManipulation(const DispatchTypes::WindowManipulatio switch (function) { case DispatchTypes::WindowManipulationType::RefreshWindow: - _pConApi->RefreshWindow(); + _pConApi->GetTextBuffer().TriggerRedrawAll(); return true; case DispatchTypes::WindowManipulationType::ResizeWindowInCharacters: // TODO:GH#1765 We should introduce a better `ResizeConpty` function to diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 42de3bfc3a7..4e3f10a35ad 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -2279,7 +2279,7 @@ bool AdaptDispatch::WindowManipulation(const DispatchTypes::WindowManipulationTy switch (function) { case DispatchTypes::WindowManipulationType::RefreshWindow: - _pConApi->RefreshWindow(); + _pConApi->GetTextBuffer().TriggerRedrawAll(); return true; case DispatchTypes::WindowManipulationType::ResizeWindowInCharacters: _pConApi->ResizeWindow(parameter2.value_or(0), parameter1.value_or(0)); diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 355b5f60e60..827a0552b3f 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -61,7 +61,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void UseMainScreenBuffer() = 0; virtual CursorType GetUserDefaultCursorStyle() const = 0; - virtual void RefreshWindow() = 0; virtual void SetConsoleOutputCP(const unsigned int codepage) = 0; virtual unsigned int GetConsoleOutputCP() const = 0; From 05e286cd3b0a1758b67f6f1be0c7de4f5f807daf Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sat, 26 Feb 2022 00:46:33 +0000 Subject: [PATCH 17/28] Reimplement render ops in AdaptDispatch. --- src/host/outputStream.cpp | 92 -------------------------- src/host/outputStream.hpp | 10 --- src/host/screenInfo.cpp | 7 +- src/terminal/adapter/adaptDispatch.cpp | 39 ++++++++--- src/terminal/adapter/adaptDispatch.hpp | 7 +- src/terminal/adapter/conGetSet.hpp | 10 --- 6 files changed, 39 insertions(+), 126 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index fd3c6fbd6ea..ab978ecc254 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -141,25 +141,6 @@ void ConhostInternalGetSet::WriteInput(std::deque>& eventsWritten = _io.GetActiveInputBuffer()->Write(events); } -// Routine Description: -// - Sets the various render modes. -// Arguments: -// - mode - the render mode to change. -// - enabled - set to true to enable the mode, false to disable it. -// Return Value: -// - -void ConhostInternalGetSet::SetRenderMode(const RenderSettings::Mode mode, const bool enabled) -{ - auto& g = ServiceLocator::LocateGlobals(); - auto& renderSettings = g.getConsoleInformation().GetRenderSettings(); - renderSettings.SetRenderMode(mode, enabled); - - if (g.pRender) - { - g.pRender->TriggerRedrawAll(); - } -} - // Routine Description: // - Sets the ENABLE_WRAP_AT_EOL_OUTPUT mode. This controls whether the cursor moves // to the beginning of the next row when it reaches the end of the current row. @@ -370,60 +351,6 @@ bool ConhostInternalGetSet::IsConsolePty() const return ServiceLocator::LocateGlobals().getConsoleInformation().IsInVtIoMode(); } -// Method Description: -// - Retrieves the value in the colortable at the specified index. -// Arguments: -// - tableIndex: the index of the color table to retrieve. -// Return Value: -// - the COLORREF value for the color at that index in the table. -COLORREF ConhostInternalGetSet::GetColorTableEntry(const size_t tableIndex) const -{ - auto& g = ServiceLocator::LocateGlobals(); - auto& gci = g.getConsoleInformation(); - return gci.GetColorTableEntry(tableIndex); -} - -// Method Description: -// - Updates the value in the colortable at index tableIndex to the new color -// color. color is a COLORREF, format 0x00BBGGRR. -// Arguments: -// - tableIndex: the index of the color table to update. -// - color: the new COLORREF to use as that color table value. -// Return Value: -// - true if successful. false otherwise. -bool ConhostInternalGetSet::SetColorTableEntry(const size_t tableIndex, const COLORREF color) -{ - auto& g = ServiceLocator::LocateGlobals(); - auto& gci = g.getConsoleInformation(); - gci.SetColorTableEntry(tableIndex, color); - - // Update the screen colors if we're not a pty - // No need to force a redraw in pty mode. - if (g.pRender && !gci.IsInVtIoMode()) - { - g.pRender->TriggerRedrawAll(); - } - - // If we're a conpty, always return false, so that we send the updated color - // value to the terminal. Still handle the sequence so apps that use - // the API or VT to query the values of the color table still read the - // correct color. - return !gci.IsInVtIoMode(); -} - -// Routine Description: -// - Sets the position in the color table for the given color alias. -// Arguments: -// - alias: the color alias to update. -// - tableIndex: the new position of the alias in the color table. -// Return Value: -// - -void ConhostInternalGetSet::SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) -{ - auto& renderSettings = ServiceLocator::LocateGlobals().getConsoleInformation().GetRenderSettings(); - renderSettings.SetColorAliasIndex(alias, tableIndex); -} - // Routine Description: // - Fills a region of the screen buffer. // Arguments: @@ -514,22 +441,3 @@ bool ConhostInternalGetSet::IsVtInputEnabled() const { return _io.GetActiveInputBuffer()->IsInVirtualTerminalInputMode(); } - -// Routine Description: -// - Replaces the active soft font with the given bit pattern. -// Arguments: -// - bitPattern - An array of scanlines representing all the glyphs in the font. -// - cellSize - The cell size for an individual glyph. -// - centeringHint - The horizontal extent that glyphs are offset from center. -// Return Value: -// - -void ConhostInternalGetSet::UpdateSoftFont(const gsl::span bitPattern, - const SIZE cellSize, - const size_t centeringHint) -{ - auto* pRender = ServiceLocator::LocateGlobals().pRender; - if (pRender) - { - pRender->UpdateSoftFont(bitPattern, cellSize, centeringHint); - } -} diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index d834204e758..8aec4a96a42 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -41,8 +41,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void WriteInput(std::deque>& events, size_t& eventsWritten) override; - void SetRenderMode(const RenderSettings::Mode mode, const bool enabled) override; - void SetAutoWrapMode(const bool wrapAtEOL) override; void SetScrollingRegion(const SMALL_RECT& scrollMargins) override; @@ -67,10 +65,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: bool IsConsolePty() const override; - COLORREF GetColorTableEntry(const size_t tableIndex) const override; - bool SetColorTableEntry(const size_t tableIndex, const COLORREF color) override; - void SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) override; - void FillRegion(const COORD startPosition, const size_t fillLength, const wchar_t fillChar, @@ -83,10 +77,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: bool IsVtInputEnabled() const override; - void UpdateSoftFont(const gsl::span bitPattern, - const SIZE cellSize, - const size_t centeringHint) override; - private: Microsoft::Console::IIoProvider& _io; }; diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index b3ce7b76490..188974039d7 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -254,10 +254,13 @@ void SCREEN_INFORMATION::s_RemoveScreenBuffer(_In_ SCREEN_INFORMATION* const pSc { try { - auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& g = ServiceLocator::LocateGlobals(); + auto& gci = g.getConsoleInformation(); + auto& renderer = *g.pRender; + auto& renderSettings = gci.GetRenderSettings(); auto& terminalInput = gci.GetActiveInputBuffer()->GetTerminalInput(); auto getset = std::make_unique(*this); - auto adapter = std::make_unique(std::move(getset), terminalInput); + auto adapter = std::make_unique(std::move(getset), renderer, renderSettings, terminalInput); auto engine = std::make_unique(std::move(adapter)); // Note that at this point in the setup, we haven't determined if we're // in VtIo mode or not yet. We'll set the OutputStateMachine's diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 4e3f10a35ad..fd8022d22d0 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -5,6 +5,7 @@ #include "adaptDispatch.hpp" #include "conGetSet.hpp" +#include "../../renderer/base/renderer.hpp" #include "../../types/inc/Viewport.hpp" #include "../../types/inc/utils.hpp" #include "../../inc/unicode.hpp" @@ -26,8 +27,10 @@ bool NoOp() noexcept } // Note: AdaptDispatch will take ownership of pConApi and pDefaults -AdaptDispatch::AdaptDispatch(std::unique_ptr pConApi, TerminalInput& terminalInput) : +AdaptDispatch::AdaptDispatch(std::unique_ptr pConApi, Renderer& renderer, RenderSettings& renderSettings, TerminalInput& terminalInput) : _pConApi{ std::move(pConApi) }, + _renderer{ renderer }, + _renderSettings{ renderSettings }, _terminalInput{ terminalInput }, _usingAltBuffer(false), _isOriginModeRelative(false), // by default, the DECOM origin mode is absolute. @@ -1260,8 +1263,8 @@ bool AdaptDispatch::SetScreenMode(const bool reverseMode) { return false; } - - _pConApi->SetRenderMode(RenderSettings::Mode::ScreenReversed, reverseMode); + _renderSettings.SetRenderMode(RenderSettings::Mode::ScreenReversed, reverseMode); + _renderer.TriggerRedrawAll(); return true; } @@ -1901,7 +1904,7 @@ bool AdaptDispatch::HardReset() _ResetTabStops(); // Clear the soft font in the renderer and delete the font buffer. - _pConApi->UpdateSoftFont({}, {}, false); + _renderer.UpdateSoftFont({}, {}, false); _fontBuffer = nullptr; // GH#2715 - If all this succeeded, but we're in a conpty, return `false` to @@ -2207,7 +2210,7 @@ bool AdaptDispatch::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) // True if handled successfully. False otherwise. bool AdaptDispatch::SetCursorColor(const COLORREF cursorColor) { - return _pConApi->SetColorTableEntry(TextColor::CURSOR_COLOR, cursorColor); + return SetColorTableEntry(TextColor::CURSOR_COLOR, cursorColor); } // Routine Description: @@ -2230,7 +2233,21 @@ bool AdaptDispatch::SetClipboard(const std::wstring_view /*content*/) noexcept // True if handled successfully. False otherwise. bool AdaptDispatch::SetColorTableEntry(const size_t tableIndex, const DWORD dwColor) { - return _pConApi->SetColorTableEntry(tableIndex, dwColor); + _renderSettings.SetColorTableEntry(tableIndex, dwColor); + + // If we're a conpty, always return false, so that we send the updated color + // value to the terminal. Still handle the sequence so apps that use + // the API or VT to query the values of the color table still read the + // correct color. + if (_pConApi->IsConsolePty()) + { + return false; + } + + // Update the screen colors if we're not a pty + // No need to force a redraw in pty mode. + _renderer.TriggerRedrawAll(); + return true; } // Method Description: @@ -2241,8 +2258,8 @@ bool AdaptDispatch::SetColorTableEntry(const size_t tableIndex, const DWORD dwCo // True if handled successfully. False otherwise. bool AdaptDispatch::SetDefaultForeground(const DWORD dwColor) { - _pConApi->SetColorAliasIndex(ColorAlias::DefaultForeground, TextColor::DEFAULT_FOREGROUND); - return _pConApi->SetColorTableEntry(TextColor::DEFAULT_FOREGROUND, dwColor); + _renderSettings.SetColorAliasIndex(ColorAlias::DefaultForeground, TextColor::DEFAULT_FOREGROUND); + return SetColorTableEntry(TextColor::DEFAULT_FOREGROUND, dwColor); } // Method Description: @@ -2253,8 +2270,8 @@ bool AdaptDispatch::SetDefaultForeground(const DWORD dwColor) // True if handled successfully. False otherwise. bool AdaptDispatch::SetDefaultBackground(const DWORD dwColor) { - _pConApi->SetColorAliasIndex(ColorAlias::DefaultBackground, TextColor::DEFAULT_BACKGROUND); - return _pConApi->SetColorTableEntry(TextColor::DEFAULT_BACKGROUND, dwColor); + _renderSettings.SetColorAliasIndex(ColorAlias::DefaultBackground, TextColor::DEFAULT_BACKGROUND); + return SetColorTableEntry(TextColor::DEFAULT_BACKGROUND, dwColor); } //Routine Description: @@ -2405,7 +2422,7 @@ ITermDispatch::StringHandler AdaptDispatch::DownloadDRCS(const size_t fontNumber const auto bitPattern = _fontBuffer->GetBitPattern(); const auto cellSize = _fontBuffer->GetCellSize(); const auto centeringHint = _fontBuffer->GetTextCenteringHint(); - _pConApi->UpdateSoftFont(bitPattern, cellSize.to_win32_size(), centeringHint); + _renderer.UpdateSoftFont(bitPattern, cellSize.to_win32_size(), centeringHint); } return true; }; diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 00081409f5d..473f18828c4 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -25,8 +25,11 @@ namespace Microsoft::Console::VirtualTerminal { class AdaptDispatch : public ITermDispatch { + using Renderer = Microsoft::Console::Render::Renderer; + using RenderSettings = Microsoft::Console::Render::RenderSettings; + public: - AdaptDispatch(std::unique_ptr pConApi, TerminalInput& terminalInput); + AdaptDispatch(std::unique_ptr pConApi, Renderer& renderer, RenderSettings& renderSettings, TerminalInput& terminalInput); void Print(const wchar_t wchPrintable) override; void PrintString(const std::wstring_view string) override; @@ -197,6 +200,8 @@ namespace Microsoft::Console::VirtualTerminal bool _initDefaultTabStops = true; std::unique_ptr _pConApi; + Renderer& _renderer; + RenderSettings& _renderSettings; TerminalInput& _terminalInput; TerminalOutput _termOutput; std::unique_ptr _fontBuffer; diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 827a0552b3f..3c2d468d02c 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -48,8 +48,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void WriteInput(std::deque>& events, size_t& eventsWritten) = 0; - virtual void SetRenderMode(const RenderSettings::Mode mode, const bool enabled) = 0; - virtual void SetAutoWrapMode(const bool wrapAtEOL) = 0; virtual void SetScrollingRegion(const SMALL_RECT& scrollMargins) = 0; @@ -68,10 +66,6 @@ namespace Microsoft::Console::VirtualTerminal virtual bool ResizeWindow(const size_t width, const size_t height) = 0; virtual bool IsConsolePty() const = 0; - virtual COLORREF GetColorTableEntry(const size_t tableIndex) const = 0; - virtual bool SetColorTableEntry(const size_t tableIndex, const COLORREF color) = 0; - virtual void SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) = 0; - virtual void FillRegion(const COORD startPosition, const size_t fillLength, const wchar_t fillChar, @@ -81,9 +75,5 @@ namespace Microsoft::Console::VirtualTerminal const std::optional clipRect, const COORD destinationOrigin, const bool standardFillAttrs) = 0; - - virtual void UpdateSoftFont(const gsl::span bitPattern, - const SIZE cellSize, - const size_t centeringHint) = 0; }; } From f2869e7f9e5535c4357f958e8466a30c038ea5aa Mon Sep 17 00:00:00 2001 From: James Holderness Date: Fri, 4 Mar 2022 00:20:58 +0000 Subject: [PATCH 18/28] Reimplement SetCursorPosition in AdaptDispatch. --- src/host/getset.cpp | 3 - src/host/outputStream.cpp | 13 ----- src/host/outputStream.hpp | 1 - src/terminal/adapter/InteractDispatch.cpp | 10 +++- src/terminal/adapter/adaptDispatch.cpp | 71 ++++++++++++++--------- src/terminal/adapter/conGetSet.hpp | 1 - 6 files changed, 53 insertions(+), 46 deletions(-) diff --git a/src/host/getset.cpp b/src/host/getset.cpp index d95e4e13ecd..0fcbbd2a26c 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -700,9 +700,6 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont position.Y < 0)); // clang-format on - // MSFT: 15813316 - Try to use this SetCursorPosition call to inherit the cursor position. - RETURN_IF_FAILED(gci.GetVtIo()->SetCursorPosition(position)); - RETURN_IF_NTSTATUS_FAILED(buffer.SetCursorPosition(position, true)); LOG_IF_FAILED(ConsoleImeResizeCompStrView()); diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index ab978ecc254..1ad9bf72e4e 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -103,19 +103,6 @@ void ConhostInternalGetSet::SetViewportPosition(const COORD position) THROW_IF_FAILED(ServiceLocator::LocateGlobals().api->SetConsoleWindowInfoImpl(info, true, windowRect)); } -// Routine Description: -// - Connects the SetCursorPosition API call directly into our Driver Message servicing call inside Conhost.exe -// Arguments: -// - position - new cursor position to set like the public API call. -// Return Value: -// - -void ConhostInternalGetSet::SetCursorPosition(const COORD position) -{ - auto& info = _io.GetActiveOutputBuffer(); - const auto clampedPosition = info.GetTextBuffer().ClampPositionWithinLine(position); - THROW_IF_FAILED(ServiceLocator::LocateGlobals().api->SetConsoleCursorPositionImpl(info, clampedPosition)); -} - // Method Description: // - Sets the current TextAttribute of the active screen buffer. Text // written to this buffer will be written with these attributes. diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 8aec4a96a42..57ae0349e23 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -35,7 +35,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: TextBuffer& GetTextBuffer() override; SMALL_RECT GetViewport() const override; void SetViewportPosition(const COORD position) override; - void SetCursorPosition(const COORD position) override; void SetTextAttributes(const TextAttribute& attrs) override; diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp index a7d2aad44bf..343c8e9271d 100644 --- a/src/terminal/adapter/InteractDispatch.cpp +++ b/src/terminal/adapter/InteractDispatch.cpp @@ -140,7 +140,8 @@ bool InteractDispatch::MoveCursor(const size_t row, const size_t col) // First retrieve some information about the buffer const auto viewport = _pConApi->GetViewport(); - auto coordCursor = _pConApi->GetTextBuffer().GetCursor().GetPosition(); + auto& cursor = _pConApi->GetTextBuffer().GetCursor(); + auto coordCursor = cursor.GetPosition(); // Safely convert the size_t positions we were given into shorts (which is the size the console deals with) THROW_IF_FAILED(SizeTToShort(rowFixed, &coordCursor.Y)); @@ -154,8 +155,13 @@ bool InteractDispatch::MoveCursor(const size_t row, const size_t col) coordCursor.Y = std::clamp(coordCursor.Y, viewport.Top, gsl::narrow(viewport.Bottom - 1)); coordCursor.X = std::clamp(coordCursor.X, viewport.Left, gsl::narrow(viewport.Right - 1)); + // MSFT: 15813316 - Try to use this MoveCursor call to inherit the cursor position. + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + RETURN_IF_FAILED(gci.GetVtIo()->SetCursorPosition(coordCursor)); + // Finally, attempt to set the adjusted cursor position back into the console. - _pConApi->SetCursorPosition(coordCursor); + cursor.SetPosition(coordCursor); + cursor.SetHasMoved(true); return true; } diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index fd8022d22d0..a9de753192e 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -175,8 +175,9 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col { // First retrieve some information about the buffer const auto viewport = _pConApi->GetViewport(); - const auto& textBuffer = _pConApi->GetTextBuffer(); - const auto cursorPosition = textBuffer.GetCursor().GetPosition(); + auto& textBuffer = _pConApi->GetTextBuffer(); + auto& cursor = textBuffer.GetCursor(); + const auto cursorPosition = cursor.GetPosition(); // Calculate the absolute margins of the scrolling area. const int topMargin = viewport.Top + _scrollMargins.Top; @@ -233,7 +234,10 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col // Finally, attempt to set the adjusted cursor position back into the console. const COORD newPos = { gsl::narrow_cast(col), gsl::narrow_cast(row) }; - _pConApi->SetCursorPosition(newPos); + cursor.SetPosition(textBuffer.ClampPositionWithinLine(newPos)); + cursor.SetDelay(false); + cursor.SetIsOn(true); + cursor.SetHasMoved(true); return true; } @@ -1156,9 +1160,10 @@ bool AdaptDispatch::EnableCursorBlinking(const bool enable) void AdaptDispatch::_InsertDeleteLineHelper(const size_t count, const bool isInsert) { const auto viewport = _pConApi->GetViewport(); - const auto& textBuffer = _pConApi->GetTextBuffer(); + auto& textBuffer = _pConApi->GetTextBuffer(); - const auto row = textBuffer.GetCursor().GetPosition().Y; + auto& cursor = textBuffer.GetCursor(); + const auto row = cursor.GetPosition().Y; const auto relativeRow = row - viewport.Top; const bool marginsSet = _scrollMargins.Top < _scrollMargins.Bottom; @@ -1194,7 +1199,10 @@ void AdaptDispatch::_InsertDeleteLineHelper(const size_t count, const bool isIns // The IL and DL controls are also expected to move the cursor to the left margin. // For now this is just column 0, since we don't yet support DECSLRM. - _pConApi->SetCursorPosition({ 0, row }); + cursor.SetXPosition(0); + cursor.SetDelay(false); + cursor.SetIsOn(true); + cursor.SetHasMoved(true); } } @@ -1436,8 +1444,9 @@ bool AdaptDispatch::LineFeed(const DispatchTypes::LineFeedType lineFeedType) bool AdaptDispatch::ReverseLineFeed() { const auto viewport = _pConApi->GetViewport(); - const auto& textBuffer = _pConApi->GetTextBuffer(); - const auto cursorPosition = textBuffer.GetCursor().GetPosition(); + auto& textBuffer = _pConApi->GetTextBuffer(); + auto& cursor = textBuffer.GetCursor(); + const auto cursorPosition = cursor.GetPosition(); // Calculate the absolute margins of the scrolling area. // If margins aren't set, we use the full viewport. @@ -1461,7 +1470,10 @@ bool AdaptDispatch::ReverseLineFeed() { // Otherwise we move the cursor up, but not past the top of the viewport. const COORD newCursorPosition{ cursorPosition.X, cursorPosition.Y - 1 }; - _pConApi->SetCursorPosition(newCursorPosition); + cursor.SetPosition(textBuffer.ClampPositionWithinLine(newCursorPosition)); + cursor.SetDelay(false); + cursor.SetIsOn(true); + cursor.SetHasMoved(true); } return true; } @@ -1537,10 +1549,10 @@ bool AdaptDispatch::HorizontalTabSet() // - True. bool AdaptDispatch::ForwardTab(const size_t numTabs) { - const auto& textBuffer = _pConApi->GetTextBuffer(); - const auto width = textBuffer.GetSize().Dimensions().X; - const auto row = textBuffer.GetCursor().GetPosition().Y; - auto column = textBuffer.GetCursor().GetPosition().X; + auto& textBuffer = _pConApi->GetTextBuffer(); + auto& cursor = textBuffer.GetCursor(); + const auto width = textBuffer.GetLineWidth(cursor.GetPosition().Y); + auto column = cursor.GetPosition().X; auto tabsPerformed = 0u; _InitTabStopsForWidth(width); @@ -1553,7 +1565,10 @@ bool AdaptDispatch::ForwardTab(const size_t numTabs) } } - _pConApi->SetCursorPosition({ column, row }); + cursor.SetXPosition(column); + cursor.SetDelay(false); + cursor.SetIsOn(true); + cursor.SetHasMoved(true); return true; } @@ -1566,10 +1581,10 @@ bool AdaptDispatch::ForwardTab(const size_t numTabs) // - True. bool AdaptDispatch::BackwardsTab(const size_t numTabs) { - const auto& textBuffer = _pConApi->GetTextBuffer(); - const auto width = textBuffer.GetSize().Dimensions().X; - const auto row = textBuffer.GetCursor().GetPosition().Y; - auto column = textBuffer.GetCursor().GetPosition().X; + auto& textBuffer = _pConApi->GetTextBuffer(); + auto& cursor = textBuffer.GetCursor(); + const auto width = textBuffer.GetLineWidth(cursor.GetPosition().Y); + auto column = cursor.GetPosition().X; auto tabsPerformed = 0u; _InitTabStopsForWidth(width); @@ -1582,7 +1597,10 @@ bool AdaptDispatch::BackwardsTab(const size_t numTabs) } } - _pConApi->SetCursorPosition({ column, row }); + cursor.SetXPosition(column); + cursor.SetDelay(false); + cursor.SetIsOn(true); + cursor.SetHasMoved(true); return true; } @@ -1967,7 +1985,8 @@ void AdaptDispatch::_EraseScrollback() THROW_HR_IF(E_UNEXPECTED, height <= 0); auto& textBuffer = _pConApi->GetTextBuffer(); const auto bufferSize = textBuffer.GetSize().Dimensions(); - const auto cursorPosition = textBuffer.GetCursor().GetPosition(); + auto& cursor = textBuffer.GetCursor(); + const auto row = cursor.GetPosition().Y; // Rectangle to cut out of the existing buffer // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. @@ -1992,8 +2011,8 @@ void AdaptDispatch::_EraseScrollback() // Move the viewport _pConApi->SetViewportPosition({ screen.Left, 0 }); // Move the cursor to the same relative location. - const COORD newcursor = { cursorPosition.X, cursorPosition.Y - screen.Top }; - _pConApi->SetCursorPosition(newcursor); + cursor.SetYPosition(row - screen.Top); + cursor.SetHasMoved(true); } //Routine Description: @@ -2018,8 +2037,8 @@ void AdaptDispatch::_EraseAll() // Stash away the current position of the cursor within the viewport. // We'll need to restore the cursor to that same relative position, after // we move the viewport. - auto cursorPosition = textBuffer.GetCursor().GetPosition(); - cursorPosition.Y -= viewport.Top; + auto& cursor = textBuffer.GetCursor(); + const auto row = cursor.GetPosition().Y - viewport.Top; // Calculate new viewport position short newViewportTop = textBuffer.GetLastNonSpaceCharacter().Y + 1; @@ -2032,8 +2051,8 @@ void AdaptDispatch::_EraseAll() // Move the viewport _pConApi->SetViewportPosition({ viewport.Left, newViewportTop }); // Restore the relative cursor position - cursorPosition.Y += newViewportTop; - _pConApi->SetCursorPosition(cursorPosition); + cursor.SetYPosition(row + newViewportTop); + cursor.SetHasMoved(true); // Update all the rows in the current viewport with the standard erase attributes, // i.e. the current background color, but with no meta attributes set. diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 3c2d468d02c..142e5e1ec9c 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -40,7 +40,6 @@ namespace Microsoft::Console::VirtualTerminal virtual TextBuffer& GetTextBuffer() = 0; virtual SMALL_RECT GetViewport() const = 0; virtual void SetViewportPosition(const COORD position) = 0; - virtual void SetCursorPosition(const COORD position) = 0; virtual bool IsVtInputEnabled() const = 0; From 04ff8e3798b4f1a44f4a3f11ec39ccf27e3e8265 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sun, 6 Mar 2022 17:09:51 +0000 Subject: [PATCH 19/28] Reimplement FillRegion in AdaptDispatch. --- src/host/outputStream.cpp | 65 +++------- src/host/outputStream.hpp | 7 +- src/terminal/adapter/adaptDispatch.cpp | 173 +++++++++---------------- src/terminal/adapter/adaptDispatch.hpp | 4 +- src/terminal/adapter/conGetSet.hpp | 7 +- 5 files changed, 86 insertions(+), 170 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 1ad9bf72e4e..7d16ba11a96 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -338,52 +338,6 @@ bool ConhostInternalGetSet::IsConsolePty() const return ServiceLocator::LocateGlobals().getConsoleInformation().IsInVtIoMode(); } -// Routine Description: -// - Fills a region of the screen buffer. -// Arguments: -// - startPosition - The position to begin filling at. -// - fillLength - The number of characters to fill. -// - fillChar - Character to fill the target region with. -// - standardFillAttrs - If true, fill with the standard erase attributes. -// If false, fill with the default attributes. -// Return value: -// - -void ConhostInternalGetSet::FillRegion(const COORD startPosition, - const size_t fillLength, - const wchar_t fillChar, - const bool standardFillAttrs) -{ - auto& screenInfo = _io.GetActiveOutputBuffer(); - - if (fillLength == 0) - { - return; - } - - // For most VT erasing operations, the standard requires that the - // erased area be filled with the current background color, but with - // no additional meta attributes set. For all other cases, we just - // fill with the default attributes. - auto fillAttrs = TextAttribute{}; - if (standardFillAttrs) - { - fillAttrs = screenInfo.GetAttributes(); - fillAttrs.SetStandardErase(); - } - - const auto fillData = OutputCellIterator{ fillChar, fillAttrs, fillLength }; - screenInfo.Write(fillData, startPosition, false); - - // Notify accessibility - if (screenInfo.HasAccessibilityEventing()) - { - auto endPosition = startPosition; - const auto bufferSize = screenInfo.GetBufferSize(); - bufferSize.MoveInBounds(fillLength - 1, endPosition); - screenInfo.NotifyAccessibilityEventing(startPosition.X, startPosition.Y, endPosition.X, endPosition.Y); - } -} - // Routine Description: // - Moves a block of data in the screen buffer, optionally limiting the effects // of the move to a clipping rectangle. @@ -428,3 +382,22 @@ bool ConhostInternalGetSet::IsVtInputEnabled() const { return _io.GetActiveInputBuffer()->IsInVirtualTerminalInputMode(); } + +// Routine Description: +// - Lets accessibility apps know when an area of the screen has changed. +// Arguments: +// - changedRect - the area that has changed. +// Return value: +// - +void ConhostInternalGetSet::NotifyAccessibilityChange(const til::rect changedRect) +{ + auto& screenInfo = _io.GetActiveOutputBuffer(); + if (screenInfo.HasAccessibilityEventing() && screenInfo.IsActiveScreenBuffer()) + { + screenInfo.NotifyAccessibilityEventing( + gsl::narrow_cast(changedRect.left), + gsl::narrow_cast(changedRect.top), + gsl::narrow_cast(changedRect.right - 1), + gsl::narrow_cast(changedRect.bottom - 1)); + } +} diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 57ae0349e23..96482c31c6b 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -64,11 +64,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: bool IsConsolePty() const override; - void FillRegion(const COORD startPosition, - const size_t fillLength, - const wchar_t fillChar, - const bool standardFillAttrs) override; - void ScrollRegion(const SMALL_RECT scrollRect, const std::optional clipRect, const COORD destinationOrigin, @@ -76,6 +71,8 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: bool IsVtInputEnabled() const override; + void NotifyAccessibilityChange(const til::rect changedRect) override; + private: Microsoft::Console::IIoProvider& _io; }; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index a9de753192e..9ddfb0e36b8 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -465,53 +465,27 @@ bool AdaptDispatch::DeleteCharacter(const size_t count) } // Routine Description: -// - Internal helper to erase one particular line of the buffer. Either from beginning to the cursor, from the cursor to the end, or the entire line. -// - Used by both erase line (used just once) and by erase screen (used in a loop) to erase a portion of the buffer. +// - Fills an area of the buffer with a given character and attributes. // Arguments: -// - textBuffer - the text buffer that we will be erasing (and getting cursor data from within) -// - eraseType - Enumeration mode of which kind of erase to perform: beginning to cursor, cursor to end, or entire line. -// - lineId - The line number (array index value, starts at 0) of the line to operate on within the buffer. -// - This is not aware of circular buffer. Line 0 is always the top visible line if you scrolled the whole way up the window. +// - textBuffer - Target buffer to be filled. +// - fillRect - Area of the buffer that will be affected. +// - fillChar - Character to be written to the buffer. +// - fillAttrs - Attributes to be written to the buffer. // Return Value: // - -void AdaptDispatch::_EraseSingleLineHelper(const TextBuffer& textBuffer, - const DispatchTypes::EraseType eraseType, - const size_t lineId) const +void AdaptDispatch::_FillRect(TextBuffer& textBuffer, const til::rect fillRect, const wchar_t fillChar, const TextAttribute fillAttrs) { - COORD coordStartPosition = { 0 }; - THROW_IF_FAILED(SizeTToShort(lineId, &coordStartPosition.Y)); - - // determine start position from the erase type - // remember that erases are inclusive of the current cursor position. - switch (eraseType) - { - case DispatchTypes::EraseType::FromBeginning: - case DispatchTypes::EraseType::All: - coordStartPosition.X = 0; // from beginning and the whole line start from the left most edge of the buffer. - break; - case DispatchTypes::EraseType::ToEnd: - coordStartPosition.X = textBuffer.GetCursor().GetPosition().X; // from the current cursor position (including it) - break; - } - - DWORD nLength = 0; - - // determine length of erase from erase type - switch (eraseType) + if (fillRect.left < fillRect.right && fillRect.top < fillRect.bottom) { - case DispatchTypes::EraseType::FromBeginning: - // +1 because if cursor were at the left edge, the length would be 0 and we want to paint at least the 1 character the cursor is on. - nLength = textBuffer.GetCursor().GetPosition().X + 1; - break; - case DispatchTypes::EraseType::ToEnd: - case DispatchTypes::EraseType::All: - // Remember the .X value is 1 farther than the right most column in the buffer. Therefore no +1. - nLength = textBuffer.GetLineWidth(lineId) - coordStartPosition.X; - break; + const auto fillWidth = gsl::narrow_cast(fillRect.right - fillRect.left); + const auto fillData = OutputCellIterator{ fillChar, fillAttrs, fillWidth }; + const auto col = gsl::narrow_cast(fillRect.left); + for (auto row = gsl::narrow_cast(fillRect.top); row < fillRect.bottom; row++) + { + textBuffer.WriteLine(fillData, COORD{ col, row }, false); + } + _pConApi->NotifyAccessibilityChange(fillRect); } - - // Note that the region is filled with the standard erase attributes. - _pConApi->FillRegion(coordStartPosition, nLength, L' ', true); } // Routine Description: @@ -525,16 +499,14 @@ void AdaptDispatch::_EraseSingleLineHelper(const TextBuffer& textBuffer, // - True. bool AdaptDispatch::EraseCharacters(const size_t numChars) { - const auto& textBuffer = _pConApi->GetTextBuffer(); - const COORD startPosition = textBuffer.GetCursor().GetPosition(); - - const SHORT remainingSpaces = textBuffer.GetSize().Dimensions().X - startPosition.X; - const size_t actualRemaining = gsl::narrow_cast((remainingSpaces < 0) ? 0 : remainingSpaces); - // erase at max the number of characters remaining in the line from the current position. - const auto eraseLength = (numChars <= actualRemaining) ? numChars : actualRemaining; + auto& textBuffer = _pConApi->GetTextBuffer(); + const auto row = textBuffer.GetCursor().GetPosition().Y; + const auto startCol = textBuffer.GetCursor().GetPosition().X; + const auto endCol = std::min(startCol + numChars, textBuffer.GetLineWidth(row)); - // Note that the region is filled with the standard erase attributes. - _pConApi->FillRegion(startPosition, eraseLength, L' ', true); + auto eraseAttributes = textBuffer.GetCurrentAttributes(); + eraseAttributes.SetStandardErase(); + _FillRect(textBuffer, { startCol, row, gsl::narrow_cast(endCol), row + 1 }, L' ', eraseAttributes); return true; } @@ -581,7 +553,12 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) const auto viewport = _pConApi->GetViewport(); auto& textBuffer = _pConApi->GetTextBuffer(); - const auto cursorPosition = textBuffer.GetCursor().GetPosition(); + const auto bufferWidth = textBuffer.GetSize().Width(); + const auto row = textBuffer.GetCursor().GetPosition().Y; + const auto col = textBuffer.GetCursor().GetPosition().X; + + auto eraseAttributes = textBuffer.GetCurrentAttributes(); + eraseAttributes.SetStandardErase(); // When erasing the display, every line that is erased in full should be // reset to single width. When erasing to the end, this could include @@ -591,46 +568,15 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) // the line is double width). if (eraseType == DispatchTypes::EraseType::FromBeginning) { - const auto endRow = cursorPosition.Y; - textBuffer.ResetLineRenditionRange(viewport.Top, endRow); + textBuffer.ResetLineRenditionRange(viewport.Top, row); + _FillRect(textBuffer, { 0, viewport.Top, bufferWidth, row }, L' ', eraseAttributes); + _FillRect(textBuffer, { 0, row, col + 1, row + 1 }, L' ', eraseAttributes); } if (eraseType == DispatchTypes::EraseType::ToEnd) { - const auto startRow = cursorPosition.Y + (cursorPosition.X > 0 ? 1 : 0); - textBuffer.ResetLineRenditionRange(startRow, viewport.Bottom); - } - - // What we need to erase is grouped into 3 types: - // 1. Lines before cursor - // 2. Cursor Line - // 3. Lines after cursor - // We erase one or more of these based on the erase type: - // A. FromBeginning - Erase 1 and Some of 2. - // B. ToEnd - Erase some of 2 and 3. - // C. All - Erase 1, 2, and 3. - - // 1. Lines before cursor line - if (eraseType == DispatchTypes::EraseType::FromBeginning) - { - // For beginning and all, erase all complete lines before (above vertically) from the cursor position. - for (SHORT startLine = viewport.Top; startLine < cursorPosition.Y; startLine++) - { - _EraseSingleLineHelper(textBuffer, DispatchTypes::EraseType::All, startLine); - } - } - - // 2. Cursor Line - _EraseSingleLineHelper(textBuffer, eraseType, cursorPosition.Y); - - // 3. Lines after cursor line - if (eraseType == DispatchTypes::EraseType::ToEnd) - { - // For beginning and all, erase all complete lines after (below vertically) the cursor position. - // Remember that the viewport bottom value is 1 beyond the viewable area of the viewport. - for (SHORT startLine = cursorPosition.Y + 1; startLine < viewport.Bottom; startLine++) - { - _EraseSingleLineHelper(textBuffer, DispatchTypes::EraseType::All, startLine); - } + textBuffer.ResetLineRenditionRange(col > 0 ? row + 1 : row, viewport.Bottom); + _FillRect(textBuffer, { col, row, bufferWidth, row + 1 }, L' ', eraseAttributes); + _FillRect(textBuffer, { 0, row + 1, bufferWidth, viewport.Bottom }, L' ', eraseAttributes); } return true; @@ -644,13 +590,26 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) // - True if handled successfully. False otherwise. bool AdaptDispatch::EraseInLine(const DispatchTypes::EraseType eraseType) { - RETURN_BOOL_IF_FALSE(eraseType <= DispatchTypes::EraseType::All); - - const auto& textBuffer = _pConApi->GetTextBuffer(); - const auto cursorPosition = textBuffer.GetCursor().GetPosition(); - _EraseSingleLineHelper(textBuffer, eraseType, cursorPosition.Y); + auto& textBuffer = _pConApi->GetTextBuffer(); + const auto row = textBuffer.GetCursor().GetPosition().Y; + const auto col = textBuffer.GetCursor().GetPosition().X; - return true; + auto eraseAttributes = textBuffer.GetCurrentAttributes(); + eraseAttributes.SetStandardErase(); + switch (eraseType) + { + case DispatchTypes::EraseType::FromBeginning: + _FillRect(textBuffer, { 0, row, col + 1, row + 1 }, L' ', eraseAttributes); + return true; + case DispatchTypes::EraseType::ToEnd: + _FillRect(textBuffer, { col, row, textBuffer.GetLineWidth(row), row + 1 }, L' ', eraseAttributes); + return true; + case DispatchTypes::EraseType::All: + _FillRect(textBuffer, { 0, row, textBuffer.GetLineWidth(row), row + 1 }, L' ', eraseAttributes); + return true; + default: + return false; + } } // Routine Description: @@ -1947,9 +1906,7 @@ bool AdaptDispatch::ScreenAlignmentPattern() const auto bufferWidth = textBuffer.GetSize().Dimensions().X; // Fill the screen with the letter E using the default attributes. - auto fillPosition = COORD{ 0, viewport.Top }; - const auto fillLength = (viewport.Bottom - viewport.Top) * bufferWidth; - _pConApi->FillRegion(fillPosition, fillLength, L'E', false); + _FillRect(textBuffer, { 0, viewport.Top, bufferWidth, viewport.Bottom }, L'E', {}); // Reset the line rendition for all of these rows. textBuffer.ResetLineRenditionRange(viewport.Top, viewport.Bottom); // Reset the meta/extended attributes (but leave the colors unchanged). @@ -2002,10 +1959,7 @@ void AdaptDispatch::_EraseScrollback() // this case we need to use the default attributes, hence standardFillAttrs is false. _pConApi->ScrollRegion(scroll, std::nullopt, destination, false); // Clear everything after the viewport. - const DWORD totalAreaBelow = bufferSize.X * (bufferSize.Y - height); - const COORD coordBelowStartPosition = { 0, height }; - // Again we need to use the default attributes, hence standardFillAttrs is false. - _pConApi->FillRegion(coordBelowStartPosition, totalAreaBelow, L' ', false); + _FillRect(textBuffer, { 0, height, bufferSize.X, bufferSize.Y }, L' ', {}); // Also reset the line rendition for all of the cleared rows. textBuffer.ResetLineRenditionRange(height, bufferSize.Y); // Move the viewport @@ -2042,7 +1996,8 @@ void AdaptDispatch::_EraseAll() // Calculate new viewport position short newViewportTop = textBuffer.GetLastNonSpaceCharacter().Y + 1; - const auto delta = (newViewportTop + viewportHeight) - (bufferSize.Height()); + const short newViewportBottom = newViewportTop + viewportHeight; + const auto delta = newViewportBottom - (bufferSize.Height()); for (auto i = 0; i < delta; i++) { textBuffer.IncrementCircularBuffer(); @@ -2054,17 +2009,13 @@ void AdaptDispatch::_EraseAll() cursor.SetYPosition(row + newViewportTop); cursor.SetHasMoved(true); - // Update all the rows in the current viewport with the standard erase attributes, - // i.e. the current background color, but with no meta attributes set. - auto fillAttributes = textBuffer.GetCurrentAttributes(); - fillAttributes.SetStandardErase(); - auto fillPosition = COORD{ 0, newViewportTop }; - auto fillLength = gsl::narrow_cast(viewportHeight * bufferSize.Width()); - auto fillData = OutputCellIterator{ fillAttributes, fillLength }; - textBuffer.Write(fillData, fillPosition, false); + // Erase all the rows in the current viewport. + auto eraseAttributes = textBuffer.GetCurrentAttributes(); + eraseAttributes.SetStandardErase(); + _FillRect(textBuffer, { 0, newViewportTop, bufferSize.Width(), newViewportBottom }, L' ', eraseAttributes); // Also reset the line rendition for the erased rows. - textBuffer.ResetLineRenditionRange(newViewportTop, newViewportTop + viewportHeight); + textBuffer.ResetLineRenditionRange(newViewportTop, newViewportBottom); } // Routine Description: diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 473f18828c4..d7c171e774c 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -167,9 +167,7 @@ namespace Microsoft::Console::VirtualTerminal }; bool _CursorMovePosition(const Offset rowOffset, const Offset colOffset, const bool clampInMargins) const; - void _EraseSingleLineHelper(const TextBuffer& textBuffer, - const DispatchTypes::EraseType eraseType, - const size_t lineId) const; + void _FillRect(TextBuffer& textBuffer, const til::rect fillRect, const wchar_t fillChar, const TextAttribute fillAttrs); void _EraseScrollback(); void _EraseAll(); void _InsertDeleteCharacterHelper(const size_t count, const bool isInsert); diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 142e5e1ec9c..e57b6e2262d 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -65,14 +65,11 @@ namespace Microsoft::Console::VirtualTerminal virtual bool ResizeWindow(const size_t width, const size_t height) = 0; virtual bool IsConsolePty() const = 0; - virtual void FillRegion(const COORD startPosition, - const size_t fillLength, - const wchar_t fillChar, - const bool standardFillAttrs) = 0; - virtual void ScrollRegion(const SMALL_RECT scrollRect, const std::optional clipRect, const COORD destinationOrigin, const bool standardFillAttrs) = 0; + + virtual void NotifyAccessibilityChange(const til::rect changedRect) = 0; }; } From 1c151fd68b78ae3f95a69b0d49eb65039a9151f7 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 8 Mar 2022 19:41:50 +0000 Subject: [PATCH 20/28] Reimplement ScrollRegion in AdaptDispatch. --- src/host/outputStream.cpp | 32 ---- src/host/outputStream.hpp | 6 - src/terminal/adapter/adaptDispatch.cpp | 241 ++++++++++++------------- src/terminal/adapter/adaptDispatch.hpp | 8 +- src/terminal/adapter/conGetSet.hpp | 5 - 5 files changed, 121 insertions(+), 171 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 7d16ba11a96..1d5a995846e 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -338,38 +338,6 @@ bool ConhostInternalGetSet::IsConsolePty() const return ServiceLocator::LocateGlobals().getConsoleInformation().IsInVtIoMode(); } -// Routine Description: -// - Moves a block of data in the screen buffer, optionally limiting the effects -// of the move to a clipping rectangle. -// Arguments: -// - scrollRect - Region to copy/move (source and size). -// - clipRect - Optional clip region to contain buffer change effects. -// - destinationOrigin - Upper left corner of target region. -// - standardFillAttrs - If true, fill with the standard erase attributes. -// If false, fill with the default attributes. -// Return value: -// - -void ConhostInternalGetSet::ScrollRegion(const SMALL_RECT scrollRect, - const std::optional clipRect, - const COORD destinationOrigin, - const bool standardFillAttrs) -{ - auto& screenInfo = _io.GetActiveOutputBuffer(); - - // For most VT scrolling operations, the standard requires that the - // erased area be filled with the current background color, but with - // no additional meta attributes set. For all other cases, we just - // fill with the default attributes. - auto fillAttrs = TextAttribute{}; - if (standardFillAttrs) - { - fillAttrs = screenInfo.GetAttributes(); - fillAttrs.SetStandardErase(); - } - - ::ScrollRegion(screenInfo, scrollRect, clipRect, destinationOrigin, UNICODE_SPACE, fillAttrs); -} - // Routine Description: // - Checks if the InputBuffer is willing to accept VT Input directly // IsVtInputEnabled is an internal-only "API" call that the vt commands can execute, diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 96482c31c6b..f6ddd41f134 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -63,12 +63,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: unsigned int GetConsoleOutputCP() const override; bool IsConsolePty() const override; - - void ScrollRegion(const SMALL_RECT scrollRect, - const std::optional clipRect, - const COORD destinationOrigin, - const bool standardFillAttrs) override; - bool IsVtInputEnabled() const override; void NotifyAccessibilityChange(const til::rect changedRect) override; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 9ddfb0e36b8..3985607d726 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -394,48 +394,95 @@ bool AdaptDispatch::CursorVisibility(const bool fIsVisible) } // Routine Description: -// - This helper will do the work of performing an insert or delete character operation -// - Both operations are similar in that they cut text and move it left or right in the buffer, padding the leftover area with spaces. +// - Scrolls an area of the buffer in a vertical direction. // Arguments: -// - count - The number of characters to insert -// - isInsert - TRUE if insert mode (cut and paste to the right, away from the cursor). FALSE if delete mode (cut and paste to the left, toward the cursor) +// - textBuffer - Target buffer to be scrolled. +// - fillRect - Area of the buffer that will be affected. +// - delta - Distance to move (positive is down, negative is up). // Return Value: // - -void AdaptDispatch::_InsertDeleteCharacterHelper(const size_t count, const bool isInsert) +void AdaptDispatch::_ScrollRectVertically(TextBuffer& textBuffer, const til::rect scrollRect, const int32_t delta) { - // We'll be doing short math on the distance since all console APIs use shorts. So check that we can successfully convert the uint into a short first. - SHORT distance; - THROW_IF_FAILED(SizeTToShort(count, &distance)); - - // get current cursor - const auto& textBuffer = _pConApi->GetTextBuffer(); - const auto cursorPosition = textBuffer.GetCursor().GetPosition(); + const auto absoluteDelta = std::min(std::abs(delta), scrollRect.height()); + if (absoluteDelta < scrollRect.height()) + { + // For now we're assuming the scrollRect is always the full width of the + // buffer, but this will likely need to be extended to support scrolling + // of arbitrary widths at some point in the future. + const auto top = gsl::narrow_cast(delta > 0 ? scrollRect.top : (scrollRect.top + absoluteDelta)); + const auto height = gsl::narrow_cast(scrollRect.height() - absoluteDelta); + const auto actualDelta = gsl::narrow_cast(delta > 0 ? absoluteDelta : -absoluteDelta); + textBuffer.ScrollRows(top, height, actualDelta); + textBuffer.TriggerRedraw(Viewport::FromInclusive(scrollRect.to_small_rect())); + } - // Rectangle to cut out of the existing buffer. This is inclusive. - SMALL_RECT srScroll; - srScroll.Left = cursorPosition.X; - srScroll.Right = textBuffer.GetLineWidth(cursorPosition.Y) - 1; - srScroll.Top = cursorPosition.Y; - srScroll.Bottom = srScroll.Top; + // Rows revealed by the scroll are filled with standard erase attributes. + auto eraseRect = scrollRect; + eraseRect.top = delta > 0 ? scrollRect.top : (scrollRect.bottom - absoluteDelta); + eraseRect.bottom = eraseRect.top + absoluteDelta; + auto eraseAttributes = textBuffer.GetCurrentAttributes(); + eraseAttributes.SetStandardErase(); + _FillRect(textBuffer, eraseRect, L' ', eraseAttributes); - // Paste coordinate for cut text above - COORD coordDestination; - coordDestination.Y = cursorPosition.Y; - coordDestination.X = cursorPosition.X; + // Also reset the line rendition for the erased rows. + textBuffer.ResetLineRenditionRange(eraseRect.top, eraseRect.bottom); +} - if (isInsert) - { - // Insert makes space by moving characters out to the right. So move the destination of the cut/paste region. - THROW_IF_FAILED(ShortAdd(coordDestination.X, distance, &coordDestination.X)); - } - else +// Routine Description: +// - Scrolls an area of the buffer in a horizontal direction. +// Arguments: +// - textBuffer - Target buffer to be scrolled. +// - fillRect - Area of the buffer that will be affected. +// - delta - Distance to move (positive is right, negative is left). +// Return Value: +// - +void AdaptDispatch::_ScrollRectHorizontally(TextBuffer& textBuffer, const til::rect scrollRect, const int32_t delta) +{ + const auto absoluteDelta = std::min(std::abs(delta), scrollRect.width()); + if (absoluteDelta < scrollRect.width()) { - // Delete scrolls the affected region to the left, relying on the clipping rect to actually delete the characters. - THROW_IF_FAILED(ShortSub(coordDestination.X, distance, &coordDestination.X)); + const auto left = gsl::narrow_cast(delta > 0 ? scrollRect.left : (scrollRect.left + absoluteDelta)); + const auto top = gsl::narrow_cast(scrollRect.top); + const auto width = gsl::narrow_cast(scrollRect.width() - absoluteDelta); + const auto height = gsl::narrow_cast(scrollRect.height()); + const auto actualDelta = gsl::narrow_cast(delta > 0 ? absoluteDelta : -absoluteDelta); + + const auto source = Viewport::FromDimensions({ left, top }, width, height); + const auto target = Viewport::Offset(source, { actualDelta, 0 }); + const auto walkDirection = Viewport::DetermineWalkDirection(source, target); + auto sourcePos = source.GetWalkOrigin(walkDirection); + auto targetPos = target.GetWalkOrigin(walkDirection); + do + { + const auto data = OutputCell(*textBuffer.GetCellDataAt(sourcePos)); + textBuffer.Write(OutputCellIterator({ &data, 1 }), targetPos); + source.WalkInBounds(sourcePos, walkDirection); + } while (target.WalkInBounds(targetPos, walkDirection)); } - // Note the revealed characters are filled with the standard erase attributes. - _pConApi->ScrollRegion(srScroll, srScroll, coordDestination, true); + // Columns revealed by the scroll are filled with standard erase attributes. + auto eraseRect = scrollRect; + eraseRect.left = delta > 0 ? scrollRect.left : (scrollRect.right - absoluteDelta); + eraseRect.right = eraseRect.left + absoluteDelta; + auto eraseAttributes = textBuffer.GetCurrentAttributes(); + eraseAttributes.SetStandardErase(); + _FillRect(textBuffer, eraseRect, L' ', eraseAttributes); +} + +// Routine Description: +// - This helper will do the work of performing an insert or delete character operation +// - Both operations are similar in that they cut text and move it left or right in the buffer, padding the leftover area with spaces. +// Arguments: +// - delta - Number of characters to modify (positive if inserting, negative if deleting). +// Return Value: +// - +void AdaptDispatch::_InsertDeleteCharacterHelper(const int32_t delta) +{ + auto& textBuffer = _pConApi->GetTextBuffer(); + const auto row = textBuffer.GetCursor().GetPosition().Y; + const auto startCol = textBuffer.GetCursor().GetPosition().X; + const auto endCol = textBuffer.GetLineWidth(row); + _ScrollRectHorizontally(textBuffer, { startCol, row, endCol, row + 1 }, delta); } // Routine Description: @@ -447,7 +494,7 @@ void AdaptDispatch::_InsertDeleteCharacterHelper(const size_t count, const bool // - True. bool AdaptDispatch::InsertCharacter(const size_t count) { - _InsertDeleteCharacterHelper(count, true); + _InsertDeleteCharacterHelper(gsl::narrow_cast(count)); return true; } @@ -460,7 +507,7 @@ bool AdaptDispatch::InsertCharacter(const size_t count) // - True. bool AdaptDispatch::DeleteCharacter(const size_t count) { - _InsertDeleteCharacterHelper(count, false); + _InsertDeleteCharacterHelper(-gsl::narrow_cast(count)); return true; } @@ -819,39 +866,22 @@ void AdaptDispatch::_WriteResponse(const std::wstring_view reply) const // Routine Description: // - Generalizes scrolling movement for up/down // Arguments: -// - scrollDirection - Specific direction to move -// - distance - Magnitude of the move +// - delta - Distance to move (positive is down, negative is up) // Return Value: // - -void AdaptDispatch::_ScrollMovement(const ScrollDirection scrollDirection, const size_t distance) const +void AdaptDispatch::_ScrollMovement(const int32_t delta) { - // We'll be doing short math on the distance since all console APIs use shorts. So check that we can successfully convert the size_t into a short first. - SHORT dist; - THROW_IF_FAILED(SizeTToShort(distance, &dist)); - const auto viewport = _pConApi->GetViewport(); + auto& textBuffer = _pConApi->GetTextBuffer(); + const auto bufferWidth = textBuffer.GetSize().Width(); - // Rectangle to cut out of the existing buffer. This is inclusive. - // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. - SMALL_RECT srScreen; - srScreen.Left = 0; - srScreen.Right = SHORT_MAX; - srScreen.Top = viewport.Top; - srScreen.Bottom = viewport.Bottom - 1; // viewport is exclusive, hence the - 1 - // Clip to the DECSTBM margin boundaries - if (_scrollMargins.Top < _scrollMargins.Bottom) - { - srScreen.Top = viewport.Top + _scrollMargins.Top; - srScreen.Bottom = viewport.Top + _scrollMargins.Bottom; - } - - // Paste coordinate for cut text above - COORD coordDestination; - coordDestination.X = srScreen.Left; - coordDestination.Y = srScreen.Top + dist * (scrollDirection == ScrollDirection::Up ? -1 : 1); + // Calculate the absolute margins of the scrolling area. + // If margins aren't set, we use the full viewport. + const auto marginsSet = _scrollMargins.Top < _scrollMargins.Bottom; + const auto topMargin = marginsSet ? viewport.Top + _scrollMargins.Top : viewport.Top; + const auto bottomMargin = marginsSet ? viewport.Top + _scrollMargins.Bottom + 1 : viewport.Bottom; - // Note the revealed lines are filled with the standard erase attributes. - _pConApi->ScrollRegion(srScreen, srScreen, coordDestination, true); + _ScrollRectVertically(textBuffer, { 0, topMargin, bufferWidth, bottomMargin }, delta); } // Routine Description: @@ -862,7 +892,7 @@ void AdaptDispatch::_ScrollMovement(const ScrollDirection scrollDirection, const // - True. bool AdaptDispatch::ScrollUp(const size_t uiDistance) { - _ScrollMovement(ScrollDirection::Up, uiDistance); + _ScrollMovement(-gsl::narrow_cast(uiDistance)); return true; } @@ -874,7 +904,7 @@ bool AdaptDispatch::ScrollUp(const size_t uiDistance) // - True. bool AdaptDispatch::ScrollDown(const size_t uiDistance) { - _ScrollMovement(ScrollDirection::Down, uiDistance); + _ScrollMovement(gsl::narrow_cast(uiDistance)); return true; } @@ -1112,49 +1142,27 @@ bool AdaptDispatch::EnableCursorBlinking(const bool enable) // - Internal logic for adding or removing lines in the active screen buffer. // This also moves the cursor to the left margin, which is expected behavior for IL and DL. // Parameters: -// - count - the number of lines to modify -// - isInsert - true if inserting lines, false if deleting lines +// - delta - Number of lines to modify (positive if inserting, negative if deleting). // Return Value: // - -void AdaptDispatch::_InsertDeleteLineHelper(const size_t count, const bool isInsert) +void AdaptDispatch::_InsertDeleteLineHelper(const int32_t delta) { const auto viewport = _pConApi->GetViewport(); auto& textBuffer = _pConApi->GetTextBuffer(); + const auto bufferWidth = textBuffer.GetSize().Width(); auto& cursor = textBuffer.GetCursor(); - const auto row = cursor.GetPosition().Y; - const auto relativeRow = row - viewport.Top; + const auto relativeRow = cursor.GetPosition().Y - viewport.Top; - const bool marginsSet = _scrollMargins.Top < _scrollMargins.Bottom; + const auto marginsSet = _scrollMargins.Top < _scrollMargins.Bottom; const auto rowInMargins = relativeRow >= _scrollMargins.Top && relativeRow <= _scrollMargins.Bottom; if (!marginsSet || rowInMargins) { - // Rectangle to cut out of the existing buffer. This is inclusive. - // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. - SMALL_RECT srScroll; - srScroll.Left = 0; - srScroll.Right = SHORT_MAX; - srScroll.Top = row; - srScroll.Bottom = viewport.Bottom - 1; // viewport is exclusive, hence the - 1 - // Clip to the DECSTBM margin boundary - if (marginsSet) - { - srScroll.Bottom = viewport.Top + _scrollMargins.Bottom; - } - // Paste coordinate for cut text above - COORD coordDestination; - coordDestination.X = 0; - if (isInsert) - { - coordDestination.Y = row + gsl::narrow_cast(count); - } - else - { - coordDestination.Y = row - gsl::narrow_cast(count); - } - - // Note the revealed lines are filled with the standard erase attributes. - _pConApi->ScrollRegion(srScroll, srScroll, coordDestination, true); + // We emulate inserting and deleting by scrolling the area between the cursor and the bottom margin. + // If margins aren't set, the area extends to the bottom of the viewport. + const auto top = cursor.GetPosition().Y; + const auto bottom = marginsSet ? viewport.Top + _scrollMargins.Bottom + 1 : viewport.Bottom; + _ScrollRectVertically(textBuffer, { 0, top, bufferWidth, bottom }, delta); // The IL and DL controls are also expected to move the cursor to the left margin. // For now this is just column 0, since we don't yet support DECSLRM. @@ -1175,7 +1183,7 @@ void AdaptDispatch::_InsertDeleteLineHelper(const size_t count, const bool isIns // - True. bool AdaptDispatch::InsertLine(const size_t distance) { - _InsertDeleteLineHelper(distance, true); + _InsertDeleteLineHelper(gsl::narrow_cast(distance)); return true; } @@ -1193,7 +1201,7 @@ bool AdaptDispatch::InsertLine(const size_t distance) // - True. bool AdaptDispatch::DeleteLine(const size_t distance) { - _InsertDeleteLineHelper(distance, false); + _InsertDeleteLineHelper(-gsl::narrow_cast(distance)); return true; } @@ -1409,21 +1417,16 @@ bool AdaptDispatch::ReverseLineFeed() // Calculate the absolute margins of the scrolling area. // If margins aren't set, we use the full viewport. - const bool marginsSet = _scrollMargins.Top < _scrollMargins.Bottom; - const short topMargin = marginsSet ? viewport.Top + _scrollMargins.Top : viewport.Top; - const short bottomMargin = marginsSet ? viewport.Top + _scrollMargins.Bottom : viewport.Bottom - 1; + const auto marginsSet = _scrollMargins.Top < _scrollMargins.Bottom; + const auto topMargin = marginsSet ? viewport.Top + _scrollMargins.Top : viewport.Top; + const auto bottomMargin = marginsSet ? viewport.Top + _scrollMargins.Bottom + 1 : viewport.Bottom; // If the cursor is at the top of the margin area, we shift the buffer // contents down, to emulate inserting a line at that point. if (cursorPosition.Y == topMargin) { - // Rectangle to cut out of the existing buffer. This is inclusive. - // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. - const SMALL_RECT srScroll = { 0, topMargin, SHORT_MAX, bottomMargin }; - // Paste coordinate for cut text above - const COORD coordDestination = { 0, topMargin + 1 }; - // Note the revealed lines are filled with the standard erase attributes. - _pConApi->ScrollRegion(srScroll, srScroll, coordDestination, true); + const auto bufferWidth = textBuffer.GetSize().Width(); + _ScrollRectVertically(textBuffer, { 0, topMargin, bufferWidth, bottomMargin }, 1); } else if (cursorPosition.Y > viewport.Top) { @@ -1937,35 +1940,23 @@ bool AdaptDispatch::ScreenAlignmentPattern() // - void AdaptDispatch::_EraseScrollback() { - const SMALL_RECT screen = _pConApi->GetViewport(); - const SHORT height = screen.Bottom - screen.Top; - THROW_HR_IF(E_UNEXPECTED, height <= 0); + const auto viewport = _pConApi->GetViewport(); + const short height = viewport.Bottom - viewport.Top; auto& textBuffer = _pConApi->GetTextBuffer(); const auto bufferSize = textBuffer.GetSize().Dimensions(); auto& cursor = textBuffer.GetCursor(); const auto row = cursor.GetPosition().Y; - // Rectangle to cut out of the existing buffer - // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. - SMALL_RECT scroll = screen; - scroll.Left = 0; - scroll.Right = SHORT_MAX; - // Paste coordinate for cut text above - COORD destination; - destination.X = 0; - destination.Y = 0; - - // Typically a scroll operation should fill with standard erase attributes, but in - // this case we need to use the default attributes, hence standardFillAttrs is false. - _pConApi->ScrollRegion(scroll, std::nullopt, destination, false); + // Scroll the viewport content to the top of the buffer. + textBuffer.ScrollRows(viewport.Top, height, -viewport.Top); // Clear everything after the viewport. _FillRect(textBuffer, { 0, height, bufferSize.X, bufferSize.Y }, L' ', {}); // Also reset the line rendition for all of the cleared rows. textBuffer.ResetLineRenditionRange(height, bufferSize.Y); // Move the viewport - _pConApi->SetViewportPosition({ screen.Left, 0 }); + _pConApi->SetViewportPosition({ viewport.Left, 0 }); // Move the cursor to the same relative location. - cursor.SetYPosition(row - screen.Top); + cursor.SetYPosition(row - viewport.Top); cursor.SetHasMoved(true); } diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index d7c171e774c..ef7c0670608 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -170,9 +170,11 @@ namespace Microsoft::Console::VirtualTerminal void _FillRect(TextBuffer& textBuffer, const til::rect fillRect, const wchar_t fillChar, const TextAttribute fillAttrs); void _EraseScrollback(); void _EraseAll(); - void _InsertDeleteCharacterHelper(const size_t count, const bool isInsert); - void _InsertDeleteLineHelper(const size_t count, const bool isInsert); - void _ScrollMovement(const ScrollDirection dir, const size_t distance) const; + void _ScrollRectVertically(TextBuffer& textBuffer, const til::rect scrollRect, const int32_t delta); + void _ScrollRectHorizontally(TextBuffer& textBuffer, const til::rect scrollRect, const int32_t delta); + void _InsertDeleteCharacterHelper(const int32_t delta); + void _InsertDeleteLineHelper(const int32_t delta); + void _ScrollMovement(const int32_t delta); void _DoSetTopBottomScrollingMargins(const size_t topMargin, const size_t bottomMargin); diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index e57b6e2262d..478df01b250 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -65,11 +65,6 @@ namespace Microsoft::Console::VirtualTerminal virtual bool ResizeWindow(const size_t width, const size_t height) = 0; virtual bool IsConsolePty() const = 0; - virtual void ScrollRegion(const SMALL_RECT scrollRect, - const std::optional clipRect, - const COORD destinationOrigin, - const bool standardFillAttrs) = 0; - virtual void NotifyAccessibilityChange(const til::rect changedRect) = 0; }; } From b01001272ddea99b87e043c8bc93db7b07008724 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 15 Mar 2022 16:50:40 +0000 Subject: [PATCH 21/28] Standardize margin calculations. --- src/terminal/adapter/adaptDispatch.cpp | 104 +++++++++++++------------ src/terminal/adapter/adaptDispatch.hpp | 7 +- 2 files changed, 59 insertions(+), 52 deletions(-) diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 3985607d726..a23eff8dbe6 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -163,6 +163,36 @@ bool AdaptDispatch::CursorPrevLine(const size_t distance) return _CursorMovePosition(Offset::Backward(distance), Offset::Absolute(1), true); } +// Routine Description: +// - Returns the coordinates of the vertical scroll margins. +// Arguments: +// - viewport - The viewport rect (exclusive). +// - absolute - Should coordinates be absolute or relative to the viewport. +// Return Value: +// - A std::pair containing the top and bottom coordinates (inclusive). +std::pair AdaptDispatch::_GetVerticalMargins(const SMALL_RECT viewport, const bool absolute) +{ + // If the top is out of range, reset the margins completely. + const auto bottommostRow = viewport.Bottom - viewport.Top - 1; + if (_scrollMargins.Top >= bottommostRow) + { + _scrollMargins.Top = _scrollMargins.Bottom = 0; + _pConApi->SetScrollingRegion(_scrollMargins); + } + // If margins aren't set, use the full extent of the viewport. + const auto marginsSet = _scrollMargins.Top < _scrollMargins.Bottom; + auto topMargin = marginsSet ? _scrollMargins.Top : 0; + auto bottomMargin = marginsSet ? _scrollMargins.Bottom : bottommostRow; + // If the bottom is out of range, clamp it to the bottommost row. + bottomMargin = std::min(bottomMargin, bottommostRow); + if (absolute) + { + topMargin += viewport.Top; + bottomMargin += viewport.Top; + } + return { topMargin, bottomMargin }; +} + // Routine Description: // - Generalizes cursor movement to a specific position, which can be absolute or relative. // Arguments: @@ -171,18 +201,14 @@ bool AdaptDispatch::CursorPrevLine(const size_t distance) // - clampInMargins - Should the position be clamped within the scrolling margins // Return Value: // - True. -bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset colOffset, const bool clampInMargins) const +bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset colOffset, const bool clampInMargins) { // First retrieve some information about the buffer const auto viewport = _pConApi->GetViewport(); auto& textBuffer = _pConApi->GetTextBuffer(); auto& cursor = textBuffer.GetCursor(); const auto cursorPosition = cursor.GetPosition(); - - // Calculate the absolute margins of the scrolling area. - const int topMargin = viewport.Top + _scrollMargins.Top; - const int bottomMargin = viewport.Top + _scrollMargins.Bottom; - const bool marginsSet = topMargin < bottomMargin; + const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); // For relative movement, the given offsets will be relative to // the current cursor position. @@ -212,7 +238,7 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col // If the operation needs to be clamped inside the margins, or the origin // mode is relative (which always requires margin clamping), then the row // may need to be adjusted further. - if (marginsSet && (clampInMargins || _isOriginModeRelative)) + if (clampInMargins || _isOriginModeRelative) { // See microsoft/terminal#2929 - If the cursor is _below_ the top // margin, it should stay below the top margin. If it's _above_ the @@ -348,12 +374,14 @@ bool AdaptDispatch::CursorRestoreState() auto row = savedCursorState.Row; const auto col = savedCursorState.Column; - // If the origin mode is relative, and the scrolling region is set (the bottom is non-zero), - // we need to make sure the restored position is clamped within the margins. - if (savedCursorState.IsOriginModeRelative && _scrollMargins.Bottom != 0) + // If the origin mode is relative, we need to make sure the restored + // position is clamped within the margins. + if (savedCursorState.IsOriginModeRelative) { + const auto viewport = _pConApi->GetViewport(); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, false); // VT origin is at 1,1 so we need to add 1 to these margins. - row = std::clamp(row, _scrollMargins.Top + 1u, _scrollMargins.Bottom + 1u); + row = std::clamp(row, topMargin + 1u, bottomMargin + 1u); } // The saved coordinates are always absolute, so we need reset the origin mode temporarily. @@ -804,7 +832,7 @@ void AdaptDispatch::_OperatingStatus() const // - // Return Value: // - -void AdaptDispatch::_CursorPositionReport() const +void AdaptDispatch::_CursorPositionReport() { const auto viewport = _pConApi->GetViewport(); const auto& textBuffer = _pConApi->GetTextBuffer(); @@ -822,7 +850,8 @@ void AdaptDispatch::_CursorPositionReport() const // If the origin mode is relative, line numbers start at top margin of the scrolling region. if (_isOriginModeRelative) { - cursorPosition.Y -= _scrollMargins.Top; + const auto topMargin = _GetVerticalMargins(viewport, false).first; + cursorPosition.Y -= gsl::narrow_cast(topMargin); } // Now send it back into the input channel of the console. @@ -874,14 +903,8 @@ void AdaptDispatch::_ScrollMovement(const int32_t delta) const auto viewport = _pConApi->GetViewport(); auto& textBuffer = _pConApi->GetTextBuffer(); const auto bufferWidth = textBuffer.GetSize().Width(); - - // Calculate the absolute margins of the scrolling area. - // If margins aren't set, we use the full viewport. - const auto marginsSet = _scrollMargins.Top < _scrollMargins.Bottom; - const auto topMargin = marginsSet ? viewport.Top + _scrollMargins.Top : viewport.Top; - const auto bottomMargin = marginsSet ? viewport.Top + _scrollMargins.Bottom + 1 : viewport.Bottom; - - _ScrollRectVertically(textBuffer, { 0, topMargin, bufferWidth, bottomMargin }, delta); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); + _ScrollRectVertically(textBuffer, { 0, topMargin, bufferWidth, bottomMargin + 1 }, delta); } // Routine Description: @@ -1152,17 +1175,13 @@ void AdaptDispatch::_InsertDeleteLineHelper(const int32_t delta) const auto bufferWidth = textBuffer.GetSize().Width(); auto& cursor = textBuffer.GetCursor(); - const auto relativeRow = cursor.GetPosition().Y - viewport.Top; + const auto row = cursor.GetPosition().Y; - const auto marginsSet = _scrollMargins.Top < _scrollMargins.Bottom; - const auto rowInMargins = relativeRow >= _scrollMargins.Top && relativeRow <= _scrollMargins.Bottom; - if (!marginsSet || rowInMargins) + const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); + if (row >= topMargin && row <= bottomMargin) { // We emulate inserting and deleting by scrolling the area between the cursor and the bottom margin. - // If margins aren't set, the area extends to the bottom of the viewport. - const auto top = cursor.GetPosition().Y; - const auto bottom = marginsSet ? viewport.Top + _scrollMargins.Bottom + 1 : viewport.Bottom; - _ScrollRectVertically(textBuffer, { 0, top, bufferWidth, bottom }, delta); + _ScrollRectVertically(textBuffer, { 0, row, bufferWidth, bottomMargin + 1 }, delta); // The IL and DL controls are also expected to move the cursor to the left margin. // For now this is just column 0, since we don't yet support DECSLRM. @@ -1414,19 +1433,14 @@ bool AdaptDispatch::ReverseLineFeed() auto& textBuffer = _pConApi->GetTextBuffer(); auto& cursor = textBuffer.GetCursor(); const auto cursorPosition = cursor.GetPosition(); - - // Calculate the absolute margins of the scrolling area. - // If margins aren't set, we use the full viewport. - const auto marginsSet = _scrollMargins.Top < _scrollMargins.Bottom; - const auto topMargin = marginsSet ? viewport.Top + _scrollMargins.Top : viewport.Top; - const auto bottomMargin = marginsSet ? viewport.Top + _scrollMargins.Bottom + 1 : viewport.Bottom; + const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); // If the cursor is at the top of the margin area, we shift the buffer // contents down, to emulate inserting a line at that point. if (cursorPosition.Y == topMargin) { const auto bufferWidth = textBuffer.GetSize().Width(); - _ScrollRectVertically(textBuffer, { 0, topMargin, bufferWidth, bottomMargin }, 1); + _ScrollRectVertically(textBuffer, { 0, topMargin, bufferWidth, bottomMargin + 1 }, 1); } else if (cursorPosition.Y > viewport.Top) { @@ -2506,7 +2520,7 @@ void AdaptDispatch::_ReportSGRSetting() const // - None // Return Value: // - None -void AdaptDispatch::_ReportDECSTBMSetting() const +void AdaptDispatch::_ReportDECSTBMSetting() { using namespace std::string_view_literals; @@ -2514,18 +2528,10 @@ void AdaptDispatch::_ReportDECSTBMSetting() const fmt::basic_memory_buffer response; response.append(L"\033P1$r"sv); - auto marginTop = _scrollMargins.Top + 1; - auto marginBottom = _scrollMargins.Bottom + 1; - // If the margin top is greater than or equal to the bottom, then the - // margins aren't actually set, so we need to return the full height - // of the window for the margin range. - if (marginTop >= marginBottom) - { - const auto viewport = _pConApi->GetViewport(); - marginTop = 1; - marginBottom = viewport.Bottom - viewport.Top; - } - fmt::format_to(std::back_inserter(response), FMT_COMPILE(L"{};{}"), marginTop, marginBottom); + const auto viewport = _pConApi->GetViewport(); + const auto [marginTop, marginBottom] = _GetVerticalMargins(viewport, false); + // VT origin is at 1,1 so we need to add 1 to these margins. + fmt::format_to(std::back_inserter(response), FMT_COMPILE(L"{};{}"), marginTop + 1, marginBottom + 1); // The 'r' indicates this is an DECSTBM response, and ST ends the sequence. response.append(L"r\033\\"sv); diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index ef7c0670608..cbd6168381b 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -166,7 +166,8 @@ namespace Microsoft::Console::VirtualTerminal static constexpr Offset Unchanged() { return Forward(0); }; }; - bool _CursorMovePosition(const Offset rowOffset, const Offset colOffset, const bool clampInMargins) const; + std::pair _GetVerticalMargins(const SMALL_RECT viewport, const bool absolute); + bool _CursorMovePosition(const Offset rowOffset, const Offset colOffset, const bool clampInMargins); void _FillRect(TextBuffer& textBuffer, const til::rect fillRect, const wchar_t fillChar, const TextAttribute fillAttrs); void _EraseScrollback(); void _EraseAll(); @@ -179,7 +180,7 @@ namespace Microsoft::Console::VirtualTerminal void _DoSetTopBottomScrollingMargins(const size_t topMargin, const size_t bottomMargin); void _OperatingStatus() const; - void _CursorPositionReport() const; + void _CursorPositionReport(); void _WriteResponse(const std::wstring_view reply) const; bool _GetParserMode(const StateMachine::Mode mode) const; @@ -194,7 +195,7 @@ namespace Microsoft::Console::VirtualTerminal void _InitTabStopsForWidth(const size_t width); void _ReportSGRSetting() const; - void _ReportDECSTBMSetting() const; + void _ReportDECSTBMSetting(); std::vector _tabStopColumns; bool _initDefaultTabStops = true; From a1ccfa3a8c799c12bf0a2c134f36d875c126a605 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 15 Mar 2022 18:13:14 +0000 Subject: [PATCH 22/28] Use til::rect and til::point. --- src/host/outputStream.cpp | 10 ++--- src/host/outputStream.hpp | 4 +- src/terminal/adapter/InteractDispatch.cpp | 6 +-- src/terminal/adapter/adaptDispatch.cpp | 49 ++++++++++++----------- src/terminal/adapter/adaptDispatch.hpp | 2 +- src/terminal/adapter/conGetSet.hpp | 4 +- 6 files changed, 38 insertions(+), 37 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 1d5a995846e..9e850cb835d 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -84,9 +84,9 @@ TextBuffer& ConhostInternalGetSet::GetTextBuffer() // - // Return Value: // - the exclusive coordinates of the viewport. -SMALL_RECT ConhostInternalGetSet::GetViewport() const +til::rect ConhostInternalGetSet::GetViewport() const { - return _io.GetActiveOutputBuffer().GetVirtualViewport().ToExclusive(); + return til::rect{ _io.GetActiveOutputBuffer().GetVirtualViewport().ToInclusive() }; } // Routine Description: @@ -95,11 +95,11 @@ SMALL_RECT ConhostInternalGetSet::GetViewport() const // - position - the new position of the viewport. // Return Value: // - -void ConhostInternalGetSet::SetViewportPosition(const COORD position) +void ConhostInternalGetSet::SetViewportPosition(const til::point position) { auto& info = _io.GetActiveOutputBuffer(); - const auto dimensions = info.GetViewport().Dimensions(); - const auto windowRect = Viewport::FromDimensions(position, dimensions).ToInclusive(); + const auto dimensions = til::size{ info.GetViewport().Dimensions() }; + const auto windowRect = til::rect{ position, dimensions }.to_small_rect(); THROW_IF_FAILED(ServiceLocator::LocateGlobals().api->SetConsoleWindowInfoImpl(info, true, windowRect)); } diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index f6ddd41f134..c8cfaa92e53 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -33,8 +33,8 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() override; TextBuffer& GetTextBuffer() override; - SMALL_RECT GetViewport() const override; - void SetViewportPosition(const COORD position) override; + til::rect GetViewport() const override; + void SetViewportPosition(const til::point position) override; void SetTextAttributes(const TextAttribute& attrs) override; diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp index 343c8e9271d..dbbbc500372 100644 --- a/src/terminal/adapter/InteractDispatch.cpp +++ b/src/terminal/adapter/InteractDispatch.cpp @@ -139,7 +139,7 @@ bool InteractDispatch::MoveCursor(const size_t row, const size_t col) const size_t colFixed = col - 1; // First retrieve some information about the buffer - const auto viewport = _pConApi->GetViewport(); + const auto viewport = _pConApi->GetViewport().to_small_rect(); auto& cursor = _pConApi->GetTextBuffer().GetCursor(); auto coordCursor = cursor.GetPosition(); @@ -152,8 +152,8 @@ bool InteractDispatch::MoveCursor(const size_t row, const size_t col) THROW_IF_FAILED(ShortAdd(coordCursor.X, viewport.Left, &coordCursor.X)); // Apply boundary tests to ensure the cursor isn't outside the viewport rectangle. - coordCursor.Y = std::clamp(coordCursor.Y, viewport.Top, gsl::narrow(viewport.Bottom - 1)); - coordCursor.X = std::clamp(coordCursor.X, viewport.Left, gsl::narrow(viewport.Right - 1)); + coordCursor.Y = std::clamp(coordCursor.Y, viewport.Top, viewport.Bottom); + coordCursor.X = std::clamp(coordCursor.X, viewport.Left, viewport.Right); // MSFT: 15813316 - Try to use this MoveCursor call to inherit the cursor position. auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index a23eff8dbe6..f1a8957e0c9 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -170,10 +170,10 @@ bool AdaptDispatch::CursorPrevLine(const size_t distance) // - absolute - Should coordinates be absolute or relative to the viewport. // Return Value: // - A std::pair containing the top and bottom coordinates (inclusive). -std::pair AdaptDispatch::_GetVerticalMargins(const SMALL_RECT viewport, const bool absolute) +std::pair AdaptDispatch::_GetVerticalMargins(const til::rect viewport, const bool absolute) { // If the top is out of range, reset the margins completely. - const auto bottommostRow = viewport.Bottom - viewport.Top - 1; + const auto bottommostRow = viewport.bottom - viewport.top - 1; if (_scrollMargins.Top >= bottommostRow) { _scrollMargins.Top = _scrollMargins.Bottom = 0; @@ -187,8 +187,8 @@ std::pair AdaptDispatch::_GetVerticalMargins(const SMALL_RECT viewport bottomMargin = std::min(bottomMargin, bottommostRow); if (absolute) { - topMargin += viewport.Top; - bottomMargin += viewport.Top; + topMargin += viewport.top; + bottomMargin += viewport.top; } return { topMargin, bottomMargin }; } @@ -219,7 +219,7 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col // viewport, or the top margin, depending on the origin mode. if (rowOffset.IsAbsolute) { - row = _isOriginModeRelative ? topMargin : viewport.Top; + row = _isOriginModeRelative ? topMargin : viewport.top; } // And if the column is absolute, it'll be relative to column 0. @@ -232,7 +232,7 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col // Adjust the base position by the given offsets and clamp the results. // The row is constrained within the viewport's vertical boundaries, // while the column is constrained by the buffer width. - row = std::clamp(row + rowOffset.Value, viewport.Top, viewport.Bottom - 1); + row = std::clamp(row + rowOffset.Value, viewport.top, viewport.bottom - 1); col = std::clamp(col + colOffset.Value, 0, textBuffer.GetSize().Width() - 1); // If the operation needs to be clamped inside the margins, or the origin @@ -344,7 +344,7 @@ bool AdaptDispatch::CursorSaveState() // The cursor is given to us by the API as relative to the whole buffer. // But in VT speak, the cursor row should be relative to the current viewport top. auto cursorPosition = textBuffer.GetCursor().GetPosition(); - cursorPosition.Y -= viewport.Top; + cursorPosition.Y -= gsl::narrow_cast(viewport.top); // VT is also 1 based, not 0 based, so correct by 1. auto& savedCursorState = _savedCursorState.at(_usingAltBuffer); @@ -643,15 +643,15 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) // the line is double width). if (eraseType == DispatchTypes::EraseType::FromBeginning) { - textBuffer.ResetLineRenditionRange(viewport.Top, row); - _FillRect(textBuffer, { 0, viewport.Top, bufferWidth, row }, L' ', eraseAttributes); + textBuffer.ResetLineRenditionRange(viewport.top, row); + _FillRect(textBuffer, { 0, viewport.top, bufferWidth, row }, L' ', eraseAttributes); _FillRect(textBuffer, { 0, row, col + 1, row + 1 }, L' ', eraseAttributes); } if (eraseType == DispatchTypes::EraseType::ToEnd) { - textBuffer.ResetLineRenditionRange(col > 0 ? row + 1 : row, viewport.Bottom); + textBuffer.ResetLineRenditionRange(col > 0 ? row + 1 : row, viewport.bottom); _FillRect(textBuffer, { col, row, bufferWidth, row + 1 }, L' ', eraseAttributes); - _FillRect(textBuffer, { 0, row + 1, bufferWidth, viewport.Bottom }, L' ', eraseAttributes); + _FillRect(textBuffer, { 0, row + 1, bufferWidth, viewport.bottom }, L' ', eraseAttributes); } return true; @@ -841,7 +841,7 @@ void AdaptDispatch::_CursorPositionReport() auto cursorPosition = textBuffer.GetCursor().GetPosition(); // Now adjust it for its position in respect to the current viewport top. - cursorPosition.Y -= viewport.Top; + cursorPosition.Y -= gsl::narrow_cast(viewport.top); // NOTE: 1,1 is the top-left corner of the viewport in VT-speak, so add 1. cursorPosition.X++; @@ -942,7 +942,7 @@ bool AdaptDispatch::ScrollDown(const size_t uiDistance) bool AdaptDispatch::SetColumns(const size_t columns) { const auto viewport = _pConApi->GetViewport(); - const auto viewportHeight = viewport.Bottom - viewport.Top; + const auto viewportHeight = viewport.bottom - viewport.top; _pConApi->ResizeWindow(columns, viewportHeight); return true; } @@ -1314,7 +1314,7 @@ void AdaptDispatch::_DoSetTopBottomScrollingMargins(const size_t topMargin, THROW_IF_FAILED(SizeTToShort(bottomMargin, &actualBottom)); const auto viewport = _pConApi->GetViewport(); - const SHORT screenHeight = viewport.Bottom - viewport.Top; + const auto screenHeight = gsl::narrow_cast(viewport.bottom - viewport.top); // The default top margin is line 1 if (actualTop == 0) { @@ -1442,7 +1442,7 @@ bool AdaptDispatch::ReverseLineFeed() const auto bufferWidth = textBuffer.GetSize().Width(); _ScrollRectVertically(textBuffer, { 0, topMargin, bufferWidth, bottomMargin + 1 }, 1); } - else if (cursorPosition.Y > viewport.Top) + else if (cursorPosition.Y > viewport.top) { // Otherwise we move the cursor up, but not past the top of the viewport. const COORD newCursorPosition{ cursorPosition.X, cursorPosition.Y - 1 }; @@ -1923,9 +1923,9 @@ bool AdaptDispatch::ScreenAlignmentPattern() const auto bufferWidth = textBuffer.GetSize().Dimensions().X; // Fill the screen with the letter E using the default attributes. - _FillRect(textBuffer, { 0, viewport.Top, bufferWidth, viewport.Bottom }, L'E', {}); + _FillRect(textBuffer, { 0, viewport.top, bufferWidth, viewport.bottom }, L'E', {}); // Reset the line rendition for all of these rows. - textBuffer.ResetLineRenditionRange(viewport.Top, viewport.Bottom); + textBuffer.ResetLineRenditionRange(viewport.top, viewport.bottom); // Reset the meta/extended attributes (but leave the colors unchanged). auto attr = textBuffer.GetCurrentAttributes(); attr.SetStandardErase(); @@ -1955,22 +1955,23 @@ bool AdaptDispatch::ScreenAlignmentPattern() void AdaptDispatch::_EraseScrollback() { const auto viewport = _pConApi->GetViewport(); - const short height = viewport.Bottom - viewport.Top; + const auto top = gsl::narrow_cast(viewport.top); + const auto height = gsl::narrow_cast(viewport.bottom - viewport.top); auto& textBuffer = _pConApi->GetTextBuffer(); const auto bufferSize = textBuffer.GetSize().Dimensions(); auto& cursor = textBuffer.GetCursor(); const auto row = cursor.GetPosition().Y; // Scroll the viewport content to the top of the buffer. - textBuffer.ScrollRows(viewport.Top, height, -viewport.Top); + textBuffer.ScrollRows(top, height, -top); // Clear everything after the viewport. _FillRect(textBuffer, { 0, height, bufferSize.X, bufferSize.Y }, L' ', {}); // Also reset the line rendition for all of the cleared rows. textBuffer.ResetLineRenditionRange(height, bufferSize.Y); // Move the viewport - _pConApi->SetViewportPosition({ viewport.Left, 0 }); + _pConApi->SetViewportPosition({ viewport.left, 0 }); // Move the cursor to the same relative location. - cursor.SetYPosition(row - viewport.Top); + cursor.SetYPosition(row - top); cursor.SetHasMoved(true); } @@ -1989,7 +1990,7 @@ void AdaptDispatch::_EraseScrollback() void AdaptDispatch::_EraseAll() { const auto viewport = _pConApi->GetViewport(); - const short viewportHeight = viewport.Bottom - viewport.Top; + const auto viewportHeight = gsl::narrow_cast(viewport.bottom - viewport.top); auto& textBuffer = _pConApi->GetTextBuffer(); const auto bufferSize = textBuffer.GetSize(); @@ -1997,7 +1998,7 @@ void AdaptDispatch::_EraseAll() // We'll need to restore the cursor to that same relative position, after // we move the viewport. auto& cursor = textBuffer.GetCursor(); - const auto row = cursor.GetPosition().Y - viewport.Top; + const auto row = cursor.GetPosition().Y - viewport.top; // Calculate new viewport position short newViewportTop = textBuffer.GetLastNonSpaceCharacter().Y + 1; @@ -2009,7 +2010,7 @@ void AdaptDispatch::_EraseAll() newViewportTop--; } // Move the viewport - _pConApi->SetViewportPosition({ viewport.Left, newViewportTop }); + _pConApi->SetViewportPosition({ viewport.left, newViewportTop }); // Restore the relative cursor position cursor.SetYPosition(row + newViewportTop); cursor.SetHasMoved(true); diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index cbd6168381b..eff4bc2ae31 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -166,7 +166,7 @@ namespace Microsoft::Console::VirtualTerminal static constexpr Offset Unchanged() { return Forward(0); }; }; - std::pair _GetVerticalMargins(const SMALL_RECT viewport, const bool absolute); + std::pair _GetVerticalMargins(const til::rect viewport, const bool absolute); bool _CursorMovePosition(const Offset rowOffset, const Offset colOffset, const bool clampInMargins); void _FillRect(TextBuffer& textBuffer, const til::rect fillRect, const wchar_t fillChar, const TextAttribute fillAttrs); void _EraseScrollback(); diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 478df01b250..0804ea0273d 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -38,8 +38,8 @@ namespace Microsoft::Console::VirtualTerminal virtual StateMachine& GetStateMachine() = 0; virtual TextBuffer& GetTextBuffer() = 0; - virtual SMALL_RECT GetViewport() const = 0; - virtual void SetViewportPosition(const COORD position) = 0; + virtual til::rect GetViewport() const = 0; + virtual void SetViewportPosition(const til::point position) = 0; virtual bool IsVtInputEnabled() const = 0; From a9bf059d3492262902e259c92cc7f8b553056eb6 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 15 Mar 2022 19:55:31 +0000 Subject: [PATCH 23/28] Get the unit tests working. --- .../ConptyRoundtripTests.cpp | 13 +- src/host/ut_host/ApiRoutinesTests.cpp | 3 +- src/host/ut_host/ClipboardTests.cpp | 2 +- src/host/ut_host/CommandLineTests.cpp | 2 +- src/host/ut_host/CommandListPopupTests.cpp | 2 +- src/host/ut_host/CommandNumberPopupTests.cpp | 2 +- src/host/ut_host/ConptyOutputTests.cpp | 2 +- src/host/ut_host/CopyFromCharPopupTests.cpp | 2 +- src/host/ut_host/CopyToCharPopupTests.cpp | 2 +- src/host/ut_host/ObjectTests.cpp | 2 +- src/host/ut_host/ScreenBufferTests.cpp | 37 +- src/host/ut_host/SelectionTests.cpp | 2 +- src/renderer/base/renderer.hpp | 9 + src/renderer/inc/DummyRenderer.hpp | 1 - .../adapter/ut_adapter/adapterTest.cpp | 1170 ++++++----------- 15 files changed, 483 insertions(+), 768 deletions(-) diff --git a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp index 17c35ef3e4b..be35947cd2c 100644 --- a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp @@ -66,8 +66,9 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final m_state->InitEvents(); m_state->PrepareGlobalFont(); - m_state->PrepareGlobalScreenBuffer(TerminalViewWidth, TerminalViewHeight, TerminalViewWidth, TerminalViewHeight); m_state->PrepareGlobalInputBuffer(); + m_state->PrepareGlobalRenderer(); + m_state->PrepareGlobalScreenBuffer(TerminalViewWidth, TerminalViewHeight, TerminalViewWidth, TerminalViewHeight); return true; } @@ -75,6 +76,7 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final TEST_CLASS_CLEANUP(ClassCleanup) { m_state->CleanupGlobalScreenBuffer(); + m_state->CleanupGlobalRenderer(); m_state->CleanupGlobalFont(); m_state->CleanupGlobalInputBuffer(); @@ -101,8 +103,6 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final gci.SetFillAttribute(0x07); // DARK_WHITE on DARK_BLACK gci.CalculateDefaultColorIndices(); - g.pRender = new Renderer(gci.GetRenderSettings(), &gci.renderData, nullptr, 0, nullptr); - m_state->PrepareNewTextBufferInfo(true, TerminalViewWidth, TerminalViewHeight); auto& currentBuffer = gci.GetActiveOutputBuffer(); // Make sure a test hasn't left us in the alt buffer on accident @@ -154,8 +154,8 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final { m_state->CleanupNewTextBufferInfo(); - auto& g = ServiceLocator::LocateGlobals(); - delete g.pRender; + auto& engines = ServiceLocator::LocateGlobals().pRender->_engines; + std::fill(engines.begin(), engines.end(), nullptr); VERIFY_ARE_EQUAL(0u, expectedOutput.size(), L"Tests should drain all the output they push into the expected output buffer."); @@ -305,7 +305,8 @@ void ConptyRoundtripTests::_resizeConpty(const unsigned short sx, void ConptyRoundtripTests::_clearConpty() { // Taken verbatim from implementation in PtySignalInputThread::_DoClearBuffer - _pConApi->ClearBuffer(); + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + THROW_IF_FAILED(gci.GetActiveOutputBuffer().ClearBuffer()); } [[nodiscard]] std::tuple ConptyRoundtripTests::_performResize(const til::size newSize) diff --git a/src/host/ut_host/ApiRoutinesTests.cpp b/src/host/ut_host/ApiRoutinesTests.cpp index 36e8a156c01..3ca496909de 100644 --- a/src/host/ut_host/ApiRoutinesTests.cpp +++ b/src/host/ut_host/ApiRoutinesTests.cpp @@ -34,9 +34,8 @@ class ApiRoutinesTests m_state = std::make_unique(); m_state->PrepareGlobalFont(); - m_state->PrepareGlobalScreenBuffer(); - m_state->PrepareGlobalInputBuffer(); + m_state->PrepareGlobalScreenBuffer(); m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr); if (!m_pHistory) diff --git a/src/host/ut_host/ClipboardTests.cpp b/src/host/ut_host/ClipboardTests.cpp index 3cc38e2c2f6..486bd94e7a1 100644 --- a/src/host/ut_host/ClipboardTests.cpp +++ b/src/host/ut_host/ClipboardTests.cpp @@ -37,8 +37,8 @@ class ClipboardTests m_state = new CommonState(); m_state->PrepareGlobalFont(); - m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); + m_state->PrepareGlobalScreenBuffer(); return true; } diff --git a/src/host/ut_host/CommandLineTests.cpp b/src/host/ut_host/CommandLineTests.cpp index 6062690ea2a..4445eb0478a 100644 --- a/src/host/ut_host/CommandLineTests.cpp +++ b/src/host/ut_host/CommandLineTests.cpp @@ -40,8 +40,8 @@ class CommandLineTests TEST_METHOD_SETUP(MethodSetup) { - m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); + m_state->PrepareGlobalScreenBuffer(); m_state->PrepareReadHandle(); m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr); if (!m_pHistory) diff --git a/src/host/ut_host/CommandListPopupTests.cpp b/src/host/ut_host/CommandListPopupTests.cpp index b0bfe74255e..2b78f86942c 100644 --- a/src/host/ut_host/CommandListPopupTests.cpp +++ b/src/host/ut_host/CommandListPopupTests.cpp @@ -46,8 +46,8 @@ class CommandListPopupTests TEST_METHOD_SETUP(MethodSetup) { - m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); + m_state->PrepareGlobalScreenBuffer(); m_state->PrepareReadHandle(); m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr); // resize command history storage to 50 items so that we don't cycle on accident diff --git a/src/host/ut_host/CommandNumberPopupTests.cpp b/src/host/ut_host/CommandNumberPopupTests.cpp index 871ed5f46fe..dd71f072968 100644 --- a/src/host/ut_host/CommandNumberPopupTests.cpp +++ b/src/host/ut_host/CommandNumberPopupTests.cpp @@ -42,8 +42,8 @@ class CommandNumberPopupTests TEST_METHOD_SETUP(MethodSetup) { - m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); + m_state->PrepareGlobalScreenBuffer(); m_state->PrepareReadHandle(); m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr); if (!m_pHistory) diff --git a/src/host/ut_host/ConptyOutputTests.cpp b/src/host/ut_host/ConptyOutputTests.cpp index 6e4a9d558a8..d6458d10cdd 100644 --- a/src/host/ut_host/ConptyOutputTests.cpp +++ b/src/host/ut_host/ConptyOutputTests.cpp @@ -45,8 +45,8 @@ class ConptyOutputTests m_state->InitEvents(); m_state->PrepareGlobalFont(); - m_state->PrepareGlobalScreenBuffer(TerminalViewWidth, TerminalViewHeight, TerminalViewWidth, TerminalViewHeight); m_state->PrepareGlobalInputBuffer(); + m_state->PrepareGlobalScreenBuffer(TerminalViewWidth, TerminalViewHeight, TerminalViewWidth, TerminalViewHeight); return true; } diff --git a/src/host/ut_host/CopyFromCharPopupTests.cpp b/src/host/ut_host/CopyFromCharPopupTests.cpp index e4a42ee5ba8..ccc54172a18 100644 --- a/src/host/ut_host/CopyFromCharPopupTests.cpp +++ b/src/host/ut_host/CopyFromCharPopupTests.cpp @@ -41,8 +41,8 @@ class CopyFromCharPopupTests TEST_METHOD_SETUP(MethodSetup) { - m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); + m_state->PrepareGlobalScreenBuffer(); m_state->PrepareReadHandle(); m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr); if (!m_pHistory) diff --git a/src/host/ut_host/CopyToCharPopupTests.cpp b/src/host/ut_host/CopyToCharPopupTests.cpp index 752922a91f7..31726cc872a 100644 --- a/src/host/ut_host/CopyToCharPopupTests.cpp +++ b/src/host/ut_host/CopyToCharPopupTests.cpp @@ -41,8 +41,8 @@ class CopyToCharPopupTests TEST_METHOD_SETUP(MethodSetup) { - m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); + m_state->PrepareGlobalScreenBuffer(); m_state->PrepareReadHandle(); m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr); if (!m_pHistory) diff --git a/src/host/ut_host/ObjectTests.cpp b/src/host/ut_host/ObjectTests.cpp index 17f89d6cd5f..4e2b3eeccb1 100644 --- a/src/host/ut_host/ObjectTests.cpp +++ b/src/host/ut_host/ObjectTests.cpp @@ -24,8 +24,8 @@ class ObjectTests m_state->InitEvents(); m_state->PrepareGlobalFont(); - m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); + m_state->PrepareGlobalScreenBuffer(); return true; } diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index 68aec670704..f0e4684ebc6 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -43,8 +43,8 @@ class ScreenBufferTests m_state->InitEvents(); m_state->PrepareGlobalFont({ 1, 1 }); m_state->PrepareGlobalRenderer(); - m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); + m_state->PrepareGlobalScreenBuffer(); return true; } @@ -902,6 +902,8 @@ void ScreenBufferTests::EraseAllTests() auto& stateMachine = si.GetStateMachine(); auto& cursor = si._textBuffer->GetCursor(); + const auto eraseAll = L"\033[2J"; + VERIFY_ARE_EQUAL(si.GetViewport().Top(), 0); //////////////////////////////////////////////////////////////////////// @@ -912,7 +914,7 @@ void ScreenBufferTests::EraseAllTests() VERIFY_ARE_EQUAL(si.GetViewport().Top(), 0); VERIFY_ARE_EQUAL(cursor.GetPosition(), originalRelativePosition); - VERIFY_SUCCEEDED(si.VtEraseAll()); + stateMachine.ProcessString(eraseAll); auto viewport = si._viewport; VERIFY_ARE_EQUAL(viewport.Top(), 1); @@ -941,7 +943,7 @@ void ScreenBufferTests::EraseAllTests() viewport.RightInclusive(), viewport.BottomInclusive())); - VERIFY_SUCCEEDED(si.VtEraseAll()); + stateMachine.ProcessString(eraseAll); viewport = si._viewport; VERIFY_ARE_EQUAL(viewport.Top(), 4); newRelativePos = originalRelativePosition; @@ -972,7 +974,7 @@ void ScreenBufferTests::EraseAllTests() viewport.Top(), viewport.RightInclusive(), viewport.BottomInclusive())); - VERIFY_SUCCEEDED(si.VtEraseAll()); + stateMachine.ProcessString(eraseAll); viewport = si._viewport; auto heightFromBottom = si.GetBufferSize().Height() - (viewport.Height()); @@ -1225,6 +1227,10 @@ void ScreenBufferTests::VtResizeDECCOLM() auto getRelativeCursorPosition = [&]() { return si.GetTextBuffer().GetCursor().GetPosition() - si.GetViewport().Origin(); }; + auto areMarginsSet = [&]() { + const auto margins = si.GetRelativeScrollMargins(); + return margins.BottomInclusive() > margins.Top(); + }; stateMachine.ProcessString(setInitialMargins); stateMachine.ProcessString(setInitialCursor); @@ -1244,7 +1250,7 @@ void ScreenBufferTests::VtResizeDECCOLM() auto newViewHeight = si.GetViewport().Height(); auto newViewWidth = si.GetViewport().Width(); - VERIFY_IS_TRUE(si.AreMarginsSet()); + VERIFY_IS_TRUE(areMarginsSet()); VERIFY_ARE_EQUAL(initialMargins, si.GetRelativeScrollMargins()); VERIFY_ARE_EQUAL(initialCursorPosition, getRelativeCursorPosition()); VERIFY_ARE_EQUAL(initialSbHeight, newSbHeight); @@ -1272,7 +1278,7 @@ void ScreenBufferTests::VtResizeDECCOLM() newViewHeight = si.GetViewport().Height(); newViewWidth = si.GetViewport().Width(); - VERIFY_IS_FALSE(si.AreMarginsSet()); + VERIFY_IS_FALSE(areMarginsSet()); VERIFY_ARE_EQUAL(COORD({ 0, 0 }), getRelativeCursorPosition()); VERIFY_ARE_EQUAL(initialSbHeight, newSbHeight); VERIFY_ARE_EQUAL(initialViewHeight, newViewHeight); @@ -1298,7 +1304,7 @@ void ScreenBufferTests::VtResizeDECCOLM() newViewHeight = si.GetViewport().Height(); newViewWidth = si.GetViewport().Width(); - VERIFY_IS_TRUE(si.AreMarginsSet()); + VERIFY_IS_TRUE(areMarginsSet()); VERIFY_ARE_EQUAL(initialMargins, si.GetRelativeScrollMargins()); VERIFY_ARE_EQUAL(initialCursorPosition, getRelativeCursorPosition()); VERIFY_ARE_EQUAL(initialSbHeight, newSbHeight); @@ -1326,7 +1332,7 @@ void ScreenBufferTests::VtResizeDECCOLM() newViewHeight = si.GetViewport().Height(); newViewWidth = si.GetViewport().Width(); - VERIFY_IS_FALSE(si.AreMarginsSet()); + VERIFY_IS_FALSE(areMarginsSet()); VERIFY_ARE_EQUAL(COORD({ 0, 0 }), getRelativeCursorPosition()); VERIFY_ARE_EQUAL(initialSbHeight, newSbHeight); VERIFY_ARE_EQUAL(initialViewHeight, newViewHeight); @@ -5892,6 +5898,11 @@ void ScreenBufferTests::ScreenAlignmentPattern() const auto& cursor = si.GetTextBuffer().GetCursor(); WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING); + auto areMarginsSet = [&]() { + const auto margins = si.GetRelativeScrollMargins(); + return margins.BottomInclusive() > margins.Top(); + }; + Log::Comment(L"Set the initial buffer state."); const auto bufferWidth = si.GetBufferSize().Width(); @@ -5914,7 +5925,7 @@ void ScreenBufferTests::ScreenAlignmentPattern() // Set some margins. stateMachine.ProcessString(L"\x1b[10;20r"); - VERIFY_IS_TRUE(si.AreMarginsSet()); + VERIFY_IS_TRUE(areMarginsSet()); // Place the cursor in the center. auto cursorPos = COORD{ bufferWidth / 2, (viewportStart + viewportEnd) / 2 }; @@ -5932,7 +5943,7 @@ void ScreenBufferTests::ScreenAlignmentPattern() VERIFY_IS_TRUE(_ValidateLinesContain(viewportEnd, bufferHeight, L'Z', bufferAttr)); Log::Comment(L"Margins should not be set."); - VERIFY_IS_FALSE(si.AreMarginsSet()); + VERIFY_IS_FALSE(areMarginsSet()); Log::Comment(L"Cursor position should be moved to home."); auto homePosition = COORD{ 0, viewportStart }; @@ -6112,8 +6123,8 @@ void ScreenBufferTests::UpdateVirtualBottomWhenCursorMovesBelowIt() Log::Comment(L"The viewport itself should not have changed at this point"); VERIFY_ARE_EQUAL(initialOrigin, si.GetViewport().Origin()); - Log::Comment(L"But after MoveToBottom, the viewport should align with the new virtual bottom"); - si.MoveToBottom(); + Log::Comment(L"But after moving to the virtual viewport, we should align with the new virtual bottom"); + VERIFY_SUCCEEDED(si.SetViewportOrigin(true, si.GetVirtualViewport().Origin(), true)); VERIFY_ARE_EQUAL(newVirtualBottom, si.GetViewport().BottomInclusive()); } @@ -6148,7 +6159,7 @@ void ScreenBufferTests::RetainHorizontalOffsetWhenMovingToBottom() VERIFY_ARE_EQUAL(initialOrigin.X, si.GetViewport().Left()); Log::Comment(L"Move the viewport back to the virtual bottom"); - si.MoveToBottom(); + VERIFY_SUCCEEDED(si.SetViewportOrigin(true, si.GetVirtualViewport().Origin(), true)); Log::Comment(L"Verify Y offset has moved back and X is unchanged"); VERIFY_ARE_EQUAL(initialOrigin.Y, si.GetViewport().Top()); diff --git a/src/host/ut_host/SelectionTests.cpp b/src/host/ut_host/SelectionTests.cpp index 46df9aaeb89..9f02f3dfc00 100644 --- a/src/host/ut_host/SelectionTests.cpp +++ b/src/host/ut_host/SelectionTests.cpp @@ -419,8 +419,8 @@ class SelectionInputTests m_state = new CommonState(); m_state->PrepareGlobalFont(); - m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); + m_state->PrepareGlobalScreenBuffer(); m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr); if (!m_pHistory) { diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index 0a84c6b6e0b..7202dd6c05c 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -24,6 +24,14 @@ Author(s): #include "../../buffer/out/textBuffer.hpp" #include "../../buffer/out/CharRow.hpp" +// fwdecl unittest classes +#ifdef UNIT_TESTING +namespace TerminalCoreUnitTests +{ + class ConptyRoundtripTests; +}; +#endif + namespace Microsoft::Console::Render { class Renderer @@ -118,6 +126,7 @@ namespace Microsoft::Console::Render #ifdef UNIT_TESTING friend class ConptyOutputTests; + friend class TerminalCoreUnitTests::ConptyRoundtripTests; #endif }; } diff --git a/src/renderer/inc/DummyRenderer.hpp b/src/renderer/inc/DummyRenderer.hpp index 0d168311a6e..b1e43b9d5fc 100644 --- a/src/renderer/inc/DummyRenderer.hpp +++ b/src/renderer/inc/DummyRenderer.hpp @@ -20,6 +20,5 @@ class DummyRenderer final : public Microsoft::Console::Render::Renderer DummyRenderer(Microsoft::Console::Render::IRenderData* pData = nullptr) : Microsoft::Console::Render::Renderer(_renderSettings, pData, nullptr, 0, nullptr) {} -private: Microsoft::Console::Render::RenderSettings _renderSettings; }; diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index c75be3a58b2..3db978db6c8 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -4,6 +4,8 @@ #include "precomp.h" #include #include "../../inc/consoletaeftemplates.hpp" +#include "../../parser/OutputStateMachineEngine.hpp" +#include "../../../renderer/inc/DummyRenderer.hpp" #include "adaptDispatch.hpp" @@ -62,79 +64,24 @@ class TestGetSet final : public ConGetSet { } - void GetConsoleScreenBufferInfoEx(CONSOLE_SCREEN_BUFFER_INFOEX& sbiex) const override + StateMachine& GetStateMachine() override { - Log::Comment(L"GetConsoleScreenBufferInfoEx MOCK returning data..."); - - THROW_HR_IF(E_FAIL, !_getConsoleScreenBufferInfoExResult); - sbiex.dwSize = _bufferSize; - sbiex.srWindow = _viewport; - sbiex.dwCursorPosition = _cursorPos; - sbiex.wAttributes = _attribute.GetLegacyAttributes(); - } - void SetConsoleScreenBufferInfoEx(const CONSOLE_SCREEN_BUFFER_INFOEX& sbiex) override - { - Log::Comment(L"SetConsoleScreenBufferInfoEx MOCK returning data..."); - - THROW_HR_IF(E_FAIL, !_setConsoleScreenBufferInfoExResult); - VERIFY_ARE_EQUAL(_expectedCursorPos, sbiex.dwCursorPosition); - VERIFY_ARE_EQUAL(_expectedScreenBufferSize, sbiex.dwSize); - VERIFY_ARE_EQUAL(_expectedScreenBufferViewport, sbiex.srWindow); - VERIFY_ARE_EQUAL(_expectedAttribute, TextAttribute{ sbiex.wAttributes }); - } - void SetCursorPosition(const COORD position) override - { - Log::Comment(L"SetCursorPosition MOCK called..."); - - THROW_HR_IF(E_FAIL, !_setCursorPositionResult); - VERIFY_ARE_EQUAL(_expectedCursorPos, position); - _cursorPos = position; - } - - void SetWindowInfo(const bool absolute, const SMALL_RECT& window) override - { - Log::Comment(L"SetWindowInfo MOCK called..."); - - THROW_HR_IF(E_FAIL, !_setWindowInfoResult); - VERIFY_ARE_EQUAL(_expectedWindowAbsolute, absolute); - VERIFY_ARE_EQUAL(_expectedConsoleWindow, window); - _viewport = window; - } - - bool SetInputMode(const TerminalInput::Mode mode, const bool enabled) override - { - Log::Comment(L"SetInputMode MOCK called..."); - - if (_setInputModeResult) - { - VERIFY_ARE_EQUAL(_expectedInputMode, mode); - VERIFY_ARE_EQUAL(_expectedInputModeEnabled, enabled); - } - - return _setInputModeResult; + return *_stateMachine; } - void SetParserMode(const StateMachine::Mode mode, const bool enabled) override + TextBuffer& GetTextBuffer() override { - Log::Comment(L"SetParserMode MOCK called..."); - - if (_setParserModeResult) - { - VERIFY_ARE_EQUAL(_expectedParserMode, mode); - VERIFY_ARE_EQUAL(_expectedParserModeEnabled, enabled); - } + return *_textBuffer.get(); } - bool GetParserMode(const StateMachine::Mode /*mode*/) const override + til::rect GetViewport() const override { - Log::Comment(L"GetParserMode MOCK called..."); - - return false; + return til::rect{ _viewport.Left, _viewport.Top, _viewport.Right, _viewport.Bottom }; } - void SetRenderMode(const RenderSettings::Mode /*mode*/, const bool /*enabled*/) override + void SetViewportPosition(const til::point /*position*/) override { - Log::Comment(L"SetRenderMode MOCK called..."); + Log::Comment(L"SetViewportPosition MOCK called..."); } void SetAutoWrapMode(const bool /*wrapAtEOL*/) override @@ -142,63 +89,18 @@ class TestGetSet final : public ConGetSet Log::Comment(L"SetAutoWrapMode MOCK called..."); } - void SetCursorVisibility(const bool visible) override - { - Log::Comment(L"SetCursorVisibility MOCK called..."); - - THROW_HR_IF(E_FAIL, !_setCursorVisibilityResult); - VERIFY_ARE_EQUAL(_expectedCursorVisibility, visible); - } - - bool EnableCursorBlinking(const bool enable) override - { - Log::Comment(L"EnableCursorBlinking MOCK called..."); - - if (_enableCursorBlinkingResult) - { - VERIFY_ARE_EQUAL(_enable, enable); - } - - return _enableCursorBlinkingResult; - } - bool IsVtInputEnabled() const override { return false; } - TextAttribute GetTextAttributes() const - { - Log::Comment(L"GetTextAttributes MOCK called..."); - - THROW_HR_IF(E_FAIL, !_getTextAttributesResult); - return _attribute; - } - void SetTextAttributes(const TextAttribute& attrs) { Log::Comment(L"SetTextAttributes MOCK called..."); THROW_HR_IF(E_FAIL, !_setTextAttributesResult); VERIFY_ARE_EQUAL(_expectedAttribute, attrs); - _attribute = attrs; - } - - void SetCurrentLineRendition(const LineRendition /*lineRendition*/) - { - Log::Comment(L"SetCurrentLineRendition MOCK called..."); - } - - void ResetLineRenditionRange(const size_t /*startRow*/, const size_t /*endRow*/) - { - Log::Comment(L"ResetLineRenditionRange MOCK called..."); - } - - SHORT GetLineWidth(const size_t /*row*/) const - { - Log::Comment(L"GetLineWidth MOCK called..."); - - return _bufferSize.X; + _textBuffer->SetCurrentAttributes(attrs); } void WriteInput(std::deque>& events, size_t& eventsWritten) override @@ -222,16 +124,6 @@ class TestGetSet final : public ConGetSet eventsWritten = _events.size(); } - void WriteControlInput(_In_ KeyEvent key) override - { - Log::Comment(L"WriteControlInput MOCK called..."); - - THROW_HR_IF(E_FAIL, !_writeControlInputResult); - VERIFY_ARE_EQUAL('C', key.GetVirtualKeyCode()); - VERIFY_ARE_EQUAL(0x3, key.GetCharData()); - VERIFY_ARE_EQUAL(true, key.IsCtrlPressed()); - } - void SetScrollingRegion(const SMALL_RECT& scrollMargins) override { Log::Comment(L"SetScrollingRegion MOCK called..."); @@ -262,11 +154,6 @@ class TestGetSet final : public ConGetSet VERIFY_ARE_EQUAL(_expectedLineFeedWithReturn, withReturn); } - void ReverseLineFeed() override - { - Log::Comment(L"ReverseLineFeed MOCK called..."); - } - void SetWindowTitle(const std::wstring_view title) { Log::Comment(L"SetWindowTitle MOCK called..."); @@ -289,46 +176,17 @@ class TestGetSet final : public ConGetSet Log::Comment(L"UseMainScreenBuffer MOCK called..."); } - void EraseAll() override - { - Log::Comment(L"EraseAll MOCK called..."); - } - - void ClearBuffer() override - { - Log::Comment(L"ClearBuffer MOCK called..."); - } - CursorType GetUserDefaultCursorStyle() const override { return CursorType::Legacy; } - void SetCursorStyle(const CursorType cursorType) override - { - Log::Comment(L"SetCursorStyle MOCK called..."); - - THROW_HR_IF(E_FAIL, !_setCursorStyleResult); - VERIFY_ARE_EQUAL(_expectedCursorStyle, cursorType); - } - - void RefreshWindow() override - { - Log::Comment(L"RefreshWindow MOCK called..."); - } - bool ResizeWindow(const size_t /*width*/, const size_t /*height*/) override { Log::Comment(L"ResizeWindow MOCK called..."); return true; } - void SuppressResizeRepaint() override - { - Log::Comment(L"SuppressResizeRepaint MOCK called..."); - VERIFY_IS_TRUE(false, L"AdaptDispatch should never be calling this function."); - } - void SetConsoleOutputCP(const unsigned int codepage) override { Log::Comment(L"SetConsoleOutputCP MOCK called..."); @@ -348,78 +206,9 @@ class TestGetSet final : public ConGetSet return _isPty; } - void DeleteLines(const size_t /*count*/) override + void NotifyAccessibilityChange(const til::rect /*changedRect*/) override { - Log::Comment(L"DeleteLines MOCK called..."); - } - - void InsertLines(const size_t /*count*/) override - { - Log::Comment(L"InsertLines MOCK called..."); - } - - void MoveToBottom() override - { - Log::Comment(L"MoveToBottom MOCK called..."); - } - - COLORREF GetColorTableEntry(const size_t tableIndex) const noexcept override - { - Log::Comment(L"GetColorTableEntry MOCK called..."); - - if (_getColorTableEntryResult) - { - VERIFY_ARE_EQUAL(_expectedColorTableIndex, tableIndex); - // Simply returning the index as the color value makes it easy for - // tests to confirm that they've received the color they expected. - return gsl::narrow_cast(tableIndex); - } - - return INVALID_COLOR; - } - - bool SetColorTableEntry(const size_t tableIndex, const COLORREF color) noexcept override - { - Log::Comment(L"SetColorTableEntry MOCK called..."); - if (_setColorTableEntryResult) - { - VERIFY_ARE_EQUAL(_expectedColorTableIndex, tableIndex); - VERIFY_ARE_EQUAL(_expectedColorValue, color); - } - - return _setColorTableEntryResult; - } - - void SetColorAliasIndex(const ColorAlias /*alias*/, const size_t /*tableIndex*/) noexcept override - { - Log::Comment(L"SetColorAliasIndex MOCK called..."); - } - - void FillRegion(const COORD /*startPosition*/, - const size_t /*fillLength*/, - const wchar_t /*fillChar*/, - const bool /*standardFillAttrs*/) noexcept override - { - Log::Comment(L"FillRegion MOCK called..."); - } - - void ScrollRegion(const SMALL_RECT /*scrollRect*/, - const std::optional /*clipRect*/, - const COORD /*destinationOrigin*/, - const bool /*standardFillAttrs*/) noexcept override - { - Log::Comment(L"ScrollRegion MOCK called..."); - } - - void UpdateSoftFont(const gsl::span /*bitPattern*/, - const SIZE cellSize, - const size_t /*centeringHint*/) noexcept override - { - Log::Comment(L"UpdateSoftFont MOCK called..."); - - Log::Comment(NoThrowString().Format(L"Cell size: %dx%d", cellSize.cx, cellSize.cy)); - VERIFY_ARE_EQUAL(_expectedCellSize.cx, cellSize.cx); - VERIFY_ARE_EQUAL(_expectedCellSize.cy, cellSize.cy); + Log::Comment(L"NotifyAccessibilityChange MOCK called..."); } void PrepData() @@ -451,17 +240,10 @@ class TestGetSet final : public ConGetSet Log::Comment(L"Resetting mock data state."); // APIs succeed by default - _setCursorPositionResult = TRUE; - _getConsoleScreenBufferInfoExResult = TRUE; - _getTextAttributesResult = TRUE; _setTextAttributesResult = TRUE; _writeInputResult = TRUE; - _writeControlInputResult = TRUE; - _setWindowInfoResult = TRUE; - _moveToBottomResult = true; - _bufferSize.X = 100; - _bufferSize.Y = 600; + _textBuffer = std::make_unique(COORD{ 100, 600 }, TextAttribute{}, 0, false, _renderer); // Viewport sitting in the "middle" of the buffer somewhere (so all sides have excess buffer around them) _viewport.Top = 20; @@ -472,11 +254,9 @@ class TestGetSet final : public ConGetSet // Call cursor positions separately PrepCursor(xact, yact); - _cursorVisible = TRUE; - // Attribute default is gray on black. - _attribute = TextAttribute{ FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED }; - _expectedAttribute = _attribute; + _textBuffer->SetCurrentAttributes(TextAttribute{ FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED }); + _expectedAttribute = _textBuffer->GetCurrentAttributes(); _events.clear(); _retainInput = false; @@ -486,19 +266,22 @@ class TestGetSet final : public ConGetSet { Log::Comment(L"Adjusting cursor within viewport... Expected will match actual when done."); + COORD cursorPos = {}; + const COORD bufferSize = _textBuffer->GetSize().Dimensions(); + switch (xact) { case CursorX::LEFT: Log::Comment(L"Cursor set to left edge of buffer."); - _cursorPos.X = 0; + cursorPos.X = 0; break; case CursorX::RIGHT: Log::Comment(L"Cursor set to right edge of buffer."); - _cursorPos.X = _bufferSize.X - 1; + cursorPos.X = bufferSize.X - 1; break; case CursorX::XCENTER: Log::Comment(L"Cursor set to centered X of buffer."); - _cursorPos.X = _bufferSize.X / 2; + cursorPos.X = bufferSize.X / 2; break; } @@ -506,19 +289,25 @@ class TestGetSet final : public ConGetSet { case CursorY::TOP: Log::Comment(L"Cursor set to top edge of viewport."); - _cursorPos.Y = _viewport.Top; + cursorPos.Y = _viewport.Top; break; case CursorY::BOTTOM: Log::Comment(L"Cursor set to bottom edge of viewport."); - _cursorPos.Y = _viewport.Bottom - 1; + cursorPos.Y = _viewport.Bottom - 1; break; case CursorY::YCENTER: Log::Comment(L"Cursor set to centered Y of viewport."); - _cursorPos.Y = _viewport.Top + ((_viewport.Bottom - _viewport.Top) / 2); + cursorPos.Y = _viewport.Top + ((_viewport.Bottom - _viewport.Top) / 2); break; } - _expectedCursorPos = _cursorPos; + _textBuffer->GetCursor().SetPosition(cursorPos); + _expectedCursorPos = cursorPos; + } + + void ValidateExpectedCursorPos() + { + VERIFY_ARE_EQUAL(_expectedCursorPos, _textBuffer->GetCursor().GetPosition()); } void ValidateInputEvent(_In_ PCWSTR pwszExpectedResponse) @@ -547,16 +336,6 @@ class TestGetSet final : public ConGetSet } } - void AddHyperlink(const std::wstring_view /*uri*/, const std::wstring_view /*params*/) const - { - Log::Comment(L"AddHyperlink MOCK called..."); - } - - void EndHyperlink() const - { - Log::Comment(L"EndHyperlink MOCK called..."); - } - void _SetMarginsHelper(SMALL_RECT* rect, SHORT top, SHORT bottom) { rect->Top = top; @@ -588,66 +367,31 @@ class TestGetSet final : public ConGetSet }); } - COORD _bufferSize = { 0, 0 }; + StateMachine* _stateMachine; + DummyRenderer _renderer; + std::unique_ptr _textBuffer; SMALL_RECT _viewport = { 0, 0, 0, 0 }; - SMALL_RECT _expectedConsoleWindow = { 0, 0, 0, 0 }; - COORD _cursorPos = { 0, 0 }; SMALL_RECT _expectedScrollRegion = { 0, 0, 0, 0 }; SMALL_RECT _activeScrollRegion = { 0, 0, 0, 0 }; - bool _cursorVisible = false; - COORD _expectedCursorPos = { 0, 0 }; - TextAttribute _attribute = {}; TextAttribute _expectedAttribute = {}; unsigned int _expectedOutputCP = 0; bool _isPty = false; - bool _setCursorVisibilityResult = false; - bool _expectedCursorVisibility = false; - - bool _getConsoleScreenBufferInfoExResult = false; - bool _setCursorPositionResult = false; - bool _getTextAttributesResult = false; bool _setTextAttributesResult = false; bool _writeInputResult = false; - bool _writeControlInputResult = false; - - bool _setWindowInfoResult = false; - bool _expectedWindowAbsolute = false; - bool _setConsoleScreenBufferInfoExResult = false; - - COORD _expectedScreenBufferSize = { 0, 0 }; - SMALL_RECT _expectedScreenBufferViewport{ 0, 0, 0, 0 }; - bool _setInputModeResult = false; - TerminalInput::Mode _expectedInputMode; - bool _expectedInputModeEnabled = false; - bool _setParserModeResult = false; - StateMachine::Mode _expectedParserMode; - bool _expectedParserModeEnabled = false; - bool _enableCursorBlinkingResult = false; - bool _enable = false; // for cursor blinking + bool _setScrollingRegionResult = false; bool _getLineFeedModeResult = false; bool _lineFeedResult = false; bool _expectedLineFeedWithReturn = false; - bool _reverseLineFeedResult = false; bool _setWindowTitleResult = false; std::wstring_view _expectedWindowTitle{}; - bool _setCursorStyleResult = false; - CursorType _expectedCursorStyle; bool _setConsoleOutputCPResult = false; bool _getConsoleOutputCPResult = false; - bool _moveToBottomResult = false; - - bool _getColorTableEntryResult = false; - bool _setColorTableEntryResult = false; - size_t _expectedColorTableIndex = SIZE_MAX; - COLORREF _expectedColorValue = INVALID_COLOR; - - SIZE _expectedCellSize = {}; private: HANDLE _hCon; @@ -668,15 +412,27 @@ class AdapterTest { // give AdaptDispatch ownership of _testGetSet _testGetSet = api.get(); // keep a copy for us but don't manage its lifetime anymore. - _pDispatch = std::make_unique(std::move(api)); - fSuccess = _pDispatch != nullptr; + _terminalInput = TerminalInput{ nullptr }; + auto& renderer = _testGetSet->_renderer; + auto& renderSettings = renderer._renderSettings; + auto adapter = std::make_unique(std::move(api), renderer, renderSettings, _terminalInput); + + fSuccess = adapter.get() != nullptr; + if (fSuccess) + { + _pDispatch = adapter.get(); + auto engine = std::make_unique(std::move(adapter)); + _stateMachine = std::make_unique(std::move(engine)); + _testGetSet->_stateMachine = _stateMachine.get(); + } } return fSuccess; } TEST_METHOD_CLEANUP(CleanupMethods) { - _pDispatch.reset(); + _stateMachine.reset(); + _pDispatch = nullptr; _testGetSet = nullptr; return true; } @@ -738,7 +494,8 @@ class AdapterTest Log::Comment(L"Test 1: Cursor doesn't move when placed in corner of viewport."); _testGetSet->PrepData(direction); - VERIFY_IS_TRUE((_pDispatch.get()->*(moveFunc))(1)); + VERIFY_IS_TRUE((_pDispatch->*(moveFunc))(1)); + _testGetSet->ValidateExpectedCursorPos(); Log::Comment(L"Test 1b: Cursor moves to left of line with next/prev line command when cursor can't move higher/lower."); @@ -759,7 +516,8 @@ class AdapterTest if (fDoTest1b) { _testGetSet->_expectedCursorPos.X = 0; - VERIFY_IS_TRUE((_pDispatch.get()->*(moveFunc))(1)); + VERIFY_IS_TRUE((_pDispatch->*(moveFunc))(1)); + _testGetSet->ValidateExpectedCursorPos(); } else { @@ -794,7 +552,8 @@ class AdapterTest break; } - VERIFY_IS_TRUE((_pDispatch.get()->*(moveFunc))(1)); + VERIFY_IS_TRUE((_pDispatch->*(moveFunc))(1)); + _testGetSet->ValidateExpectedCursorPos(); // place cursor and move it up too far. It should get bounded by the viewport. Log::Comment(L"Test 3: Cursor moves and gets stuck at viewport when started away from edges and moved beyond edges."); @@ -811,7 +570,7 @@ class AdapterTest _testGetSet->_expectedCursorPos.Y = _testGetSet->_viewport.Bottom - 1; break; case CursorDirection::RIGHT: - _testGetSet->_expectedCursorPos.X = _testGetSet->_bufferSize.X - 1; + _testGetSet->_expectedCursorPos.X = _testGetSet->_textBuffer->GetSize().Dimensions().X - 1; break; case CursorDirection::LEFT: _testGetSet->_expectedCursorPos.X = 0; @@ -826,23 +585,8 @@ class AdapterTest break; } - VERIFY_IS_TRUE((_pDispatch.get()->*(moveFunc))(100)); - - // error cases - // SetCursorPosition throws failure. Parameters are otherwise normal. - Log::Comment(L"Test 4: When SetCursorPosition throws a failure, call fails and cursor doesn't move."); - _testGetSet->PrepData(direction); - _testGetSet->_setCursorPositionResult = FALSE; - - VERIFY_THROWS((_pDispatch.get()->*(moveFunc))(0), std::exception); - VERIFY_ARE_EQUAL(_testGetSet->_expectedCursorPos, _testGetSet->_cursorPos); - - // GetConsoleScreenBufferInfo throws failure. Parameters are otherwise normal. - Log::Comment(L"Test 5: When GetConsoleScreenBufferInfo throws a failure, call fails and cursor doesn't move."); - _testGetSet->PrepData(CursorX::LEFT, CursorY::TOP); - _testGetSet->_getConsoleScreenBufferInfoExResult = FALSE; - VERIFY_THROWS((_pDispatch.get()->*(moveFunc))(0), std::exception); - VERIFY_ARE_EQUAL(_testGetSet->_expectedCursorPos, _testGetSet->_cursorPos); + VERIFY_IS_TRUE((_pDispatch->*(moveFunc))(100)); + _testGetSet->ValidateExpectedCursorPos(); } TEST_METHOD(CursorPositionTest) @@ -859,7 +603,8 @@ class AdapterTest _testGetSet->_expectedCursorPos.X = sCol - 1; _testGetSet->_expectedCursorPos.Y = _testGetSet->_viewport.Top + (sRow - 1); - VERIFY_IS_TRUE(_pDispatch.get()->CursorPosition(sRow, sCol)); + VERIFY_IS_TRUE(_pDispatch->CursorPosition(sRow, sCol)); + _testGetSet->ValidateExpectedCursorPos(); Log::Comment(L"Test 2: Move to 0, 0 (which is 1,1 in VT speak)"); _testGetSet->PrepData(CursorX::RIGHT, CursorY::BOTTOM); @@ -868,32 +613,20 @@ class AdapterTest _testGetSet->_expectedCursorPos.X = 0; _testGetSet->_expectedCursorPos.Y = _testGetSet->_viewport.Top; - VERIFY_IS_TRUE(_pDispatch.get()->CursorPosition(1, 1)); + VERIFY_IS_TRUE(_pDispatch->CursorPosition(1, 1)); + _testGetSet->ValidateExpectedCursorPos(); Log::Comment(L"Test 3: Move beyond rectangle (down/right too far). Should be bounded back in."); _testGetSet->PrepData(CursorX::LEFT, CursorY::TOP); - sCol = (_testGetSet->_bufferSize.X) * 2; + sCol = (_testGetSet->_textBuffer->GetSize().Dimensions().X) * 2; sRow = (_testGetSet->_viewport.Bottom - _testGetSet->_viewport.Top) * 2; - _testGetSet->_expectedCursorPos.X = _testGetSet->_bufferSize.X - 1; + _testGetSet->_expectedCursorPos.X = _testGetSet->_textBuffer->GetSize().Dimensions().X - 1; _testGetSet->_expectedCursorPos.Y = _testGetSet->_viewport.Bottom - 1; - VERIFY_IS_TRUE(_pDispatch.get()->CursorPosition(sRow, sCol)); - - Log::Comment(L"Test 4: GetConsoleInfo API returns false. No move, return false."); - _testGetSet->PrepData(CursorX::LEFT, CursorY::TOP); - - _testGetSet->_getConsoleScreenBufferInfoExResult = FALSE; - - VERIFY_THROWS(_pDispatch.get()->CursorPosition(1, 1), std::exception); - - Log::Comment(L"Test 5: SetCursor API returns false. No move, return false."); - _testGetSet->PrepData(CursorX::LEFT, CursorY::TOP); - - _testGetSet->_setCursorPositionResult = FALSE; - - VERIFY_THROWS(_pDispatch.get()->CursorPosition(1, 1), std::exception); + VERIFY_IS_TRUE(_pDispatch->CursorPosition(sRow, sCol)); + _testGetSet->ValidateExpectedCursorPos(); } TEST_METHOD(CursorSingleDimensionMoveTest) @@ -922,7 +655,7 @@ class AdapterTest { case AbsolutePosition::CursorHorizontal: Log::Comment(L"Testing cursor horizontal movement."); - sRangeEnd = _testGetSet->_bufferSize.X; + sRangeEnd = _testGetSet->_textBuffer->GetSize().Dimensions().X; sRangeStart = 0; psCursorExpected = &_testGetSet->_expectedCursorPos.X; moveFunc = &AdaptDispatch::CursorHorizontalPositionAbsolute; @@ -949,7 +682,8 @@ class AdapterTest *psCursorExpected = sRangeStart + (sVal - 1); - VERIFY_IS_TRUE((_pDispatch.get()->*(moveFunc))(sVal)); + VERIFY_IS_TRUE((_pDispatch->*(moveFunc))(sVal)); + _testGetSet->ValidateExpectedCursorPos(); Log::Comment(L"Test 2: Move to 0 (which is 1 in VT speak)"); _testGetSet->PrepData(CursorX::RIGHT, CursorY::BOTTOM); @@ -957,7 +691,8 @@ class AdapterTest *psCursorExpected = sRangeStart; sVal = 1; - VERIFY_IS_TRUE((_pDispatch.get()->*(moveFunc))(sVal)); + VERIFY_IS_TRUE((_pDispatch->*(moveFunc))(sVal)); + _testGetSet->ValidateExpectedCursorPos(); Log::Comment(L"Test 3: Move beyond rectangle (down/right too far). Should be bounded back in."); _testGetSet->PrepData(CursorX::LEFT, CursorY::TOP); @@ -966,25 +701,8 @@ class AdapterTest *psCursorExpected = sRangeEnd - 1; - VERIFY_IS_TRUE((_pDispatch.get()->*(moveFunc))(sVal)); - - Log::Comment(L"Test 4: GetConsoleInfo API returns false. No move, return false."); - _testGetSet->PrepData(CursorX::LEFT, CursorY::TOP); - - _testGetSet->_getConsoleScreenBufferInfoExResult = FALSE; - - sVal = 1; - - VERIFY_THROWS((_pDispatch.get()->*(moveFunc))(sVal), std::exception); - - Log::Comment(L"Test 5: SetCursor API returns false. No move, return false."); - _testGetSet->PrepData(CursorX::LEFT, CursorY::TOP); - - _testGetSet->_setCursorPositionResult = FALSE; - - sVal = 1; - - VERIFY_THROWS((_pDispatch.get()->*(moveFunc))(sVal), std::exception); + VERIFY_IS_TRUE((_pDispatch->*(moveFunc))(sVal)); + _testGetSet->ValidateExpectedCursorPos(); } TEST_METHOD(CursorSaveRestoreTest) @@ -1006,11 +724,13 @@ class AdapterTest // Attributes are restored to defaults. _testGetSet->_expectedAttribute = {}; - VERIFY_IS_TRUE(_pDispatch.get()->CursorRestoreState(), L"By default, restore to top left corner (0,0 offset from viewport)."); + VERIFY_IS_TRUE(_pDispatch->CursorRestoreState(), L"By default, restore to top left corner (0,0 offset from viewport)."); + _testGetSet->ValidateExpectedCursorPos(); Log::Comment(L"Test 2: Place cursor in center. Save. Move cursor to corner. Restore. Should come back to center."); _testGetSet->PrepData(CursorX::XCENTER, CursorY::YCENTER); - VERIFY_IS_TRUE(_pDispatch.get()->CursorSaveState(), L"Succeed at saving position."); + VERIFY_IS_TRUE(_pDispatch->CursorSaveState(), L"Succeed at saving position."); + _testGetSet->ValidateExpectedCursorPos(); Log::Comment(L"Backup expected cursor (in the middle). Move cursor to corner. Then re-set expected cursor to middle."); // save expected cursor position @@ -1022,7 +742,8 @@ class AdapterTest // restore expected cursor position to center. _testGetSet->_expectedCursorPos = coordExpected; - VERIFY_IS_TRUE(_pDispatch.get()->CursorRestoreState(), L"Restoring to corner should succeed. API call inside will test that cursor matched expected position."); + VERIFY_IS_TRUE(_pDispatch->CursorRestoreState(), L"Restoring to corner should succeed. API call inside will test that cursor matched expected position."); + _testGetSet->ValidateExpectedCursorPos(); } TEST_METHOD(CursorHideShowTest) @@ -1040,17 +761,11 @@ class AdapterTest VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"fStartingVis", fStart)); VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"fEndingVis", fEnd)); - Log::Comment(L"Test 1: Verify successful API call modifies visibility state."); - _testGetSet->PrepData(); - _testGetSet->_cursorVisible = fStart; - _testGetSet->_setCursorVisibilityResult = true; - _testGetSet->_expectedCursorVisibility = fEnd; - VERIFY_IS_TRUE(_pDispatch.get()->CursorVisibility(fEnd)); - - Log::Comment(L"Test 3: When we fail to set updated cursor information, the dispatch should fail."); + Log::Comment(L"Verify successful API call modifies visibility state."); _testGetSet->PrepData(); - _testGetSet->_setCursorVisibilityResult = false; - VERIFY_THROWS(_pDispatch.get()->CursorVisibility(fEnd), std::exception); + _testGetSet->_textBuffer->GetCursor().SetIsVisible(fStart); + VERIFY_IS_TRUE(_pDispatch->CursorVisibility(fEnd)); + VERIFY_ARE_EQUAL(fEnd, _testGetSet->_textBuffer->GetCursor().IsVisible()); } TEST_METHOD(GraphicsBaseTests) @@ -1064,23 +779,16 @@ class AdapterTest VTParameter rgOptions[16]; size_t cOptions = 0; - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); - - Log::Comment(L"Test 2: Gracefully fail when getting attribute data fails."); - - _testGetSet->PrepData(); - _testGetSet->_getTextAttributesResult = FALSE; - - VERIFY_THROWS(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions }), std::exception); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); - Log::Comment(L"Test 3: Gracefully fail when setting attribute data fails."); + Log::Comment(L"Test 2: Gracefully fail when setting attribute data fails."); _testGetSet->PrepData(); _testGetSet->_setTextAttributesResult = FALSE; // Need at least one option in order for the call to be able to fail. rgOptions[0] = (DispatchTypes::GraphicsOptions)0; cOptions = 1; - VERIFY_THROWS(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions }), std::exception); + VERIFY_THROWS(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions }), std::exception); } TEST_METHOD(GraphicsSingleTests) @@ -1102,299 +810,300 @@ class AdapterTest size_t cOptions = 1; rgOptions[0] = graphicsOption; + TextAttribute startingAttribute; switch (graphicsOption) { case DispatchTypes::GraphicsOptions::Off: Log::Comment(L"Testing graphics 'Off/Reset'"); - _testGetSet->_attribute = TextAttribute{ (WORD)~_testGetSet->s_defaultFill }; + startingAttribute = TextAttribute{ (WORD)~_testGetSet->s_defaultFill }; _testGetSet->_expectedAttribute = TextAttribute{}; break; case DispatchTypes::GraphicsOptions::Intense: Log::Comment(L"Testing graphics 'Intense'"); - _testGetSet->_attribute = TextAttribute{ 0 }; + startingAttribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute.SetIntense(true); break; case DispatchTypes::GraphicsOptions::RGBColorOrFaint: Log::Comment(L"Testing graphics 'Faint'"); - _testGetSet->_attribute = TextAttribute{ 0 }; + startingAttribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute.SetFaint(true); break; case DispatchTypes::GraphicsOptions::Underline: Log::Comment(L"Testing graphics 'Underline'"); - _testGetSet->_attribute = TextAttribute{ 0 }; + startingAttribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute.SetUnderlined(true); break; case DispatchTypes::GraphicsOptions::DoublyUnderlined: Log::Comment(L"Testing graphics 'Doubly Underlined'"); - _testGetSet->_attribute = TextAttribute{ 0 }; + startingAttribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute.SetDoublyUnderlined(true); break; case DispatchTypes::GraphicsOptions::Overline: Log::Comment(L"Testing graphics 'Overline'"); - _testGetSet->_attribute = TextAttribute{ 0 }; + startingAttribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute = TextAttribute{ COMMON_LVB_GRID_HORIZONTAL }; break; case DispatchTypes::GraphicsOptions::Negative: Log::Comment(L"Testing graphics 'Negative'"); - _testGetSet->_attribute = TextAttribute{ 0 }; + startingAttribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute = TextAttribute{ COMMON_LVB_REVERSE_VIDEO }; break; case DispatchTypes::GraphicsOptions::Invisible: Log::Comment(L"Testing graphics 'Invisible'"); - _testGetSet->_attribute = TextAttribute{ 0 }; + startingAttribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute.SetInvisible(true); break; case DispatchTypes::GraphicsOptions::CrossedOut: Log::Comment(L"Testing graphics 'Crossed Out'"); - _testGetSet->_attribute = TextAttribute{ 0 }; + startingAttribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute = TextAttribute{ 0 }; _testGetSet->_expectedAttribute.SetCrossedOut(true); break; case DispatchTypes::GraphicsOptions::NotIntenseOrFaint: Log::Comment(L"Testing graphics 'No Intense or Faint'"); - _testGetSet->_attribute = TextAttribute{ 0 }; - _testGetSet->_attribute.SetIntense(true); - _testGetSet->_attribute.SetFaint(true); + startingAttribute = TextAttribute{ 0 }; + startingAttribute.SetIntense(true); + startingAttribute.SetFaint(true); _testGetSet->_expectedAttribute = TextAttribute{ 0 }; break; case DispatchTypes::GraphicsOptions::NoUnderline: Log::Comment(L"Testing graphics 'No Underline'"); - _testGetSet->_attribute = TextAttribute{ 0 }; - _testGetSet->_attribute.SetUnderlined(true); - _testGetSet->_attribute.SetDoublyUnderlined(true); + startingAttribute = TextAttribute{ 0 }; + startingAttribute.SetUnderlined(true); + startingAttribute.SetDoublyUnderlined(true); _testGetSet->_expectedAttribute = TextAttribute{ 0 }; break; case DispatchTypes::GraphicsOptions::NoOverline: Log::Comment(L"Testing graphics 'No Overline'"); - _testGetSet->_attribute = TextAttribute{ COMMON_LVB_GRID_HORIZONTAL }; + startingAttribute = TextAttribute{ COMMON_LVB_GRID_HORIZONTAL }; _testGetSet->_expectedAttribute = TextAttribute{ 0 }; break; case DispatchTypes::GraphicsOptions::Positive: Log::Comment(L"Testing graphics 'Positive'"); - _testGetSet->_attribute = TextAttribute{ COMMON_LVB_REVERSE_VIDEO }; + startingAttribute = TextAttribute{ COMMON_LVB_REVERSE_VIDEO }; _testGetSet->_expectedAttribute = TextAttribute{ 0 }; break; case DispatchTypes::GraphicsOptions::Visible: Log::Comment(L"Testing graphics 'Visible'"); - _testGetSet->_attribute = TextAttribute{ 0 }; - _testGetSet->_attribute.SetInvisible(true); + startingAttribute = TextAttribute{ 0 }; + startingAttribute.SetInvisible(true); _testGetSet->_expectedAttribute = TextAttribute{ 0 }; break; case DispatchTypes::GraphicsOptions::NotCrossedOut: Log::Comment(L"Testing graphics 'Not Crossed Out'"); - _testGetSet->_attribute = TextAttribute{ 0 }; - _testGetSet->_attribute.SetCrossedOut(true); + startingAttribute = TextAttribute{ 0 }; + startingAttribute.SetCrossedOut(true); _testGetSet->_expectedAttribute = TextAttribute{ 0 }; break; case DispatchTypes::GraphicsOptions::ForegroundBlack: Log::Comment(L"Testing graphics 'Foreground Color Black'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_BLACK); break; case DispatchTypes::GraphicsOptions::ForegroundBlue: Log::Comment(L"Testing graphics 'Foreground Color Blue'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_BLUE); break; case DispatchTypes::GraphicsOptions::ForegroundGreen: Log::Comment(L"Testing graphics 'Foreground Color Green'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_GREEN); break; case DispatchTypes::GraphicsOptions::ForegroundCyan: Log::Comment(L"Testing graphics 'Foreground Color Cyan'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_CYAN); break; case DispatchTypes::GraphicsOptions::ForegroundRed: Log::Comment(L"Testing graphics 'Foreground Color Red'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_RED); break; case DispatchTypes::GraphicsOptions::ForegroundMagenta: Log::Comment(L"Testing graphics 'Foreground Color Magenta'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_GREEN | FOREGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_GREEN | FOREGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_MAGENTA); break; case DispatchTypes::GraphicsOptions::ForegroundYellow: Log::Comment(L"Testing graphics 'Foreground Color Yellow'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_BLUE | FOREGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_BLUE | FOREGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_YELLOW); break; case DispatchTypes::GraphicsOptions::ForegroundWhite: Log::Comment(L"Testing graphics 'Foreground Color White'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_WHITE); break; case DispatchTypes::GraphicsOptions::ForegroundDefault: Log::Comment(L"Testing graphics 'Foreground Color Default'"); - _testGetSet->_attribute = TextAttribute{ (WORD)~_testGetSet->s_wDefaultAttribute }; // set the current attribute to the opposite of default so we can ensure all relevant bits flip. + startingAttribute = TextAttribute{ (WORD)~_testGetSet->s_wDefaultAttribute }; // set the current attribute to the opposite of default so we can ensure all relevant bits flip. // To get expected value, take what we started with and change ONLY the background series of bits to what the Default says. - _testGetSet->_expectedAttribute = _testGetSet->_attribute; // expect = starting + _testGetSet->_expectedAttribute = startingAttribute; // expect = starting _testGetSet->_expectedAttribute.SetDefaultForeground(); // set the foreground as default break; case DispatchTypes::GraphicsOptions::BackgroundBlack: Log::Comment(L"Testing graphics 'Background Color Black'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_BLACK); break; case DispatchTypes::GraphicsOptions::BackgroundBlue: Log::Comment(L"Testing graphics 'Background Color Blue'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_BLUE); break; case DispatchTypes::GraphicsOptions::BackgroundGreen: Log::Comment(L"Testing graphics 'Background Color Green'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_GREEN); break; case DispatchTypes::GraphicsOptions::BackgroundCyan: Log::Comment(L"Testing graphics 'Background Color Cyan'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_CYAN); break; case DispatchTypes::GraphicsOptions::BackgroundRed: Log::Comment(L"Testing graphics 'Background Color Red'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_RED); break; case DispatchTypes::GraphicsOptions::BackgroundMagenta: Log::Comment(L"Testing graphics 'Background Color Magenta'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_GREEN | BACKGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_GREEN | BACKGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_MAGENTA); break; case DispatchTypes::GraphicsOptions::BackgroundYellow: Log::Comment(L"Testing graphics 'Background Color Yellow'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_BLUE | BACKGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_BLUE | BACKGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_YELLOW); break; case DispatchTypes::GraphicsOptions::BackgroundWhite: Log::Comment(L"Testing graphics 'Background Color White'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_INTENSITY }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_INTENSITY }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::DARK_WHITE); break; case DispatchTypes::GraphicsOptions::BackgroundDefault: Log::Comment(L"Testing graphics 'Background Color Default'"); - _testGetSet->_attribute = TextAttribute{ (WORD)~_testGetSet->s_wDefaultAttribute }; // set the current attribute to the opposite of default so we can ensure all relevant bits flip. + startingAttribute = TextAttribute{ (WORD)~_testGetSet->s_wDefaultAttribute }; // set the current attribute to the opposite of default so we can ensure all relevant bits flip. // To get expected value, take what we started with and change ONLY the background series of bits to what the Default says. - _testGetSet->_expectedAttribute = _testGetSet->_attribute; // expect = starting + _testGetSet->_expectedAttribute = startingAttribute; // expect = starting _testGetSet->_expectedAttribute.SetDefaultBackground(); // set the background as default break; case DispatchTypes::GraphicsOptions::BrightForegroundBlack: Log::Comment(L"Testing graphics 'Bright Foreground Color Black'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_BLACK); break; case DispatchTypes::GraphicsOptions::BrightForegroundBlue: Log::Comment(L"Testing graphics 'Bright Foreground Color Blue'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_GREEN }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_GREEN }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_BLUE); break; case DispatchTypes::GraphicsOptions::BrightForegroundGreen: Log::Comment(L"Testing graphics 'Bright Foreground Color Green'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_BLUE }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_RED | FOREGROUND_BLUE }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_GREEN); break; case DispatchTypes::GraphicsOptions::BrightForegroundCyan: Log::Comment(L"Testing graphics 'Bright Foreground Color Cyan'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_RED }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_RED }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_CYAN); break; case DispatchTypes::GraphicsOptions::BrightForegroundRed: Log::Comment(L"Testing graphics 'Bright Foreground Color Red'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_BLUE | FOREGROUND_GREEN }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_BLUE | FOREGROUND_GREEN }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_RED); break; case DispatchTypes::GraphicsOptions::BrightForegroundMagenta: Log::Comment(L"Testing graphics 'Bright Foreground Color Magenta'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_GREEN }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_GREEN }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_MAGENTA); break; case DispatchTypes::GraphicsOptions::BrightForegroundYellow: Log::Comment(L"Testing graphics 'Bright Foreground Color Yellow'"); - _testGetSet->_attribute = TextAttribute{ FOREGROUND_BLUE }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ FOREGROUND_BLUE }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_YELLOW); break; case DispatchTypes::GraphicsOptions::BrightForegroundWhite: Log::Comment(L"Testing graphics 'Bright Foreground Color White'"); - _testGetSet->_attribute = TextAttribute{ 0 }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ 0 }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_WHITE); break; case DispatchTypes::GraphicsOptions::BrightBackgroundBlack: Log::Comment(L"Testing graphics 'Bright Background Color Black'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_BLACK); break; case DispatchTypes::GraphicsOptions::BrightBackgroundBlue: Log::Comment(L"Testing graphics 'Bright Background Color Blue'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_GREEN }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_GREEN }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_BLUE); break; case DispatchTypes::GraphicsOptions::BrightBackgroundGreen: Log::Comment(L"Testing graphics 'Bright Background Color Green'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_BLUE }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_RED | BACKGROUND_BLUE }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_GREEN); break; case DispatchTypes::GraphicsOptions::BrightBackgroundCyan: Log::Comment(L"Testing graphics 'Bright Background Color Cyan'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_RED }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_RED }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_CYAN); break; case DispatchTypes::GraphicsOptions::BrightBackgroundRed: Log::Comment(L"Testing graphics 'Bright Background Color Red'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_BLUE | BACKGROUND_GREEN }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_BLUE | BACKGROUND_GREEN }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_RED); break; case DispatchTypes::GraphicsOptions::BrightBackgroundMagenta: Log::Comment(L"Testing graphics 'Bright Background Color Magenta'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_GREEN }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_GREEN }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_MAGENTA); break; case DispatchTypes::GraphicsOptions::BrightBackgroundYellow: Log::Comment(L"Testing graphics 'Bright Background Color Yellow'"); - _testGetSet->_attribute = TextAttribute{ BACKGROUND_BLUE }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ BACKGROUND_BLUE }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_YELLOW); break; case DispatchTypes::GraphicsOptions::BrightBackgroundWhite: Log::Comment(L"Testing graphics 'Bright Background Color White'"); - _testGetSet->_attribute = TextAttribute{ 0 }; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + startingAttribute = TextAttribute{ 0 }; + _testGetSet->_expectedAttribute = startingAttribute; _testGetSet->_expectedAttribute.SetIndexedBackground(TextColor::BRIGHT_WHITE); break; default: @@ -1402,7 +1111,8 @@ class AdapterTest break; } - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); + _testGetSet->_textBuffer->SetCurrentAttributes(startingAttribute); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); } TEST_METHOD(GraphicsPushPopTests) @@ -1556,82 +1266,82 @@ class AdapterTest Log::Comment(L"Resetting graphics options"); rgOptions[0] = DispatchTypes::GraphicsOptions::Off; _testGetSet->_expectedAttribute = {}; - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); Log::Comment(L"Testing graphics 'Foreground Color Blue'"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundBlue; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_BLUE); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); Log::Comment(L"Enabling brightness"); rgOptions[0] = DispatchTypes::GraphicsOptions::Intense; _testGetSet->_expectedAttribute.SetIntense(true); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); - VERIFY_IS_TRUE(_testGetSet->_attribute.IsIntense()); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_TRUE(_testGetSet->_textBuffer->GetCurrentAttributes().IsIntense()); Log::Comment(L"Testing graphics 'Foreground Color Green, with brightness'"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundGreen; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_GREEN); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); - VERIFY_IS_TRUE(WI_IsFlagSet(_testGetSet->_attribute.GetLegacyAttributes(), FOREGROUND_GREEN)); - VERIFY_IS_TRUE(_testGetSet->_attribute.IsIntense()); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_TRUE(WI_IsFlagSet(_testGetSet->_textBuffer->GetCurrentAttributes().GetLegacyAttributes(), FOREGROUND_GREEN)); + VERIFY_IS_TRUE(_testGetSet->_textBuffer->GetCurrentAttributes().IsIntense()); Log::Comment(L"Test 2: Disable brightness, use a bright color, next normal call remains not bright"); Log::Comment(L"Resetting graphics options"); rgOptions[0] = DispatchTypes::GraphicsOptions::Off; _testGetSet->_expectedAttribute = {}; - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); - VERIFY_IS_TRUE(WI_IsFlagClear(_testGetSet->_attribute.GetLegacyAttributes(), FOREGROUND_INTENSITY)); - VERIFY_IS_FALSE(_testGetSet->_attribute.IsIntense()); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_TRUE(WI_IsFlagClear(_testGetSet->_textBuffer->GetCurrentAttributes().GetLegacyAttributes(), FOREGROUND_INTENSITY)); + VERIFY_IS_FALSE(_testGetSet->_textBuffer->GetCurrentAttributes().IsIntense()); Log::Comment(L"Testing graphics 'Foreground Color Bright Blue'"); rgOptions[0] = DispatchTypes::GraphicsOptions::BrightForegroundBlue; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_BLUE); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); - VERIFY_IS_FALSE(_testGetSet->_attribute.IsIntense()); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_FALSE(_testGetSet->_textBuffer->GetCurrentAttributes().IsIntense()); Log::Comment(L"Testing graphics 'Foreground Color Blue', brightness of 9x series doesn't persist"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundBlue; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_BLUE); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); - VERIFY_IS_FALSE(_testGetSet->_attribute.IsIntense()); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_FALSE(_testGetSet->_textBuffer->GetCurrentAttributes().IsIntense()); Log::Comment(L"Test 3: Enable brightness, use a bright color, brightness persists to next normal call"); Log::Comment(L"Resetting graphics options"); rgOptions[0] = DispatchTypes::GraphicsOptions::Off; _testGetSet->_expectedAttribute = {}; - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); - VERIFY_IS_FALSE(_testGetSet->_attribute.IsIntense()); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_FALSE(_testGetSet->_textBuffer->GetCurrentAttributes().IsIntense()); Log::Comment(L"Testing graphics 'Foreground Color Blue'"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundBlue; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_BLUE); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); - VERIFY_IS_FALSE(_testGetSet->_attribute.IsIntense()); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_FALSE(_testGetSet->_textBuffer->GetCurrentAttributes().IsIntense()); Log::Comment(L"Enabling brightness"); rgOptions[0] = DispatchTypes::GraphicsOptions::Intense; _testGetSet->_expectedAttribute.SetIntense(true); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); - VERIFY_IS_TRUE(_testGetSet->_attribute.IsIntense()); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_TRUE(_testGetSet->_textBuffer->GetCurrentAttributes().IsIntense()); Log::Comment(L"Testing graphics 'Foreground Color Bright Blue'"); rgOptions[0] = DispatchTypes::GraphicsOptions::BrightForegroundBlue; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::BRIGHT_BLUE); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); - VERIFY_IS_TRUE(_testGetSet->_attribute.IsIntense()); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_TRUE(_testGetSet->_textBuffer->GetCurrentAttributes().IsIntense()); Log::Comment(L"Testing graphics 'Foreground Color Blue, with brightness', brightness of 9x series doesn't affect brightness"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundBlue; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_BLUE); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); - VERIFY_IS_TRUE(_testGetSet->_attribute.IsIntense()); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_TRUE(_testGetSet->_textBuffer->GetCurrentAttributes().IsIntense()); Log::Comment(L"Testing graphics 'Foreground Color Green, with brightness'"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundGreen; _testGetSet->_expectedAttribute.SetIndexedForeground(TextColor::DARK_GREEN); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); - VERIFY_IS_TRUE(_testGetSet->_attribute.IsIntense()); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_TRUE(_testGetSet->_textBuffer->GetCurrentAttributes().IsIntense()); } TEST_METHOD(DeviceStatusReportTests) @@ -1640,7 +1350,7 @@ class AdapterTest Log::Comment(L"Test 1: Verify failure when using bad status."); _testGetSet->PrepData(); - VERIFY_IS_FALSE(_pDispatch.get()->DeviceStatusReport((DispatchTypes::AnsiStatusType)-1)); + VERIFY_IS_FALSE(_pDispatch->DeviceStatusReport((DispatchTypes::AnsiStatusType)-1)); } TEST_METHOD(DeviceStatus_OperatingStatusTests) @@ -1649,7 +1359,7 @@ class AdapterTest Log::Comment(L"Test 1: Verify good operating condition."); _testGetSet->PrepData(); - VERIFY_IS_TRUE(_pDispatch.get()->DeviceStatusReport(DispatchTypes::AnsiStatusType::OS_OperatingStatus)); + VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::AnsiStatusType::OS_OperatingStatus)); _testGetSet->ValidateInputEvent(L"\x1b[0n"); } @@ -1663,7 +1373,7 @@ class AdapterTest _testGetSet->PrepData(CursorX::XCENTER, CursorY::YCENTER); // start with the cursor position in the buffer. - COORD coordCursorExpected = _testGetSet->_cursorPos; + COORD coordCursorExpected = _testGetSet->_textBuffer->GetCursor().GetPosition(); // to get to VT, we have to adjust it to its position relative to the viewport top. coordCursorExpected.Y -= _testGetSet->_viewport.Top; @@ -1672,7 +1382,7 @@ class AdapterTest coordCursorExpected.X++; coordCursorExpected.Y++; - VERIFY_IS_TRUE(_pDispatch.get()->DeviceStatusReport(DispatchTypes::AnsiStatusType::CPR_CursorPositionReport)); + VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::AnsiStatusType::CPR_CursorPositionReport)); wchar_t pwszBuffer[50]; @@ -1688,7 +1398,7 @@ class AdapterTest auto retentionScope{ _testGetSet->EnableInputRetentionInScope() }; // start with the cursor position in the buffer. - til::point coordCursorExpectedFirst{ _testGetSet->_cursorPos }; + til::point coordCursorExpectedFirst{ _testGetSet->_textBuffer->GetCursor().GetPosition() }; // to get to VT, we have to adjust it to its position relative to the viewport top. coordCursorExpectedFirst -= til::point{ 0, _testGetSet->_viewport.Top }; @@ -1696,15 +1406,17 @@ class AdapterTest // Then note that VT is 1,1 based for the top left, so add 1. (The rest of the console uses 0,0 for array index bases.) coordCursorExpectedFirst += til::point{ 1, 1 }; - VERIFY_IS_TRUE(_pDispatch.get()->DeviceStatusReport(DispatchTypes::AnsiStatusType::CPR_CursorPositionReport)); + VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::AnsiStatusType::CPR_CursorPositionReport)); - _testGetSet->_cursorPos.X++; - _testGetSet->_cursorPos.Y++; + auto cursorPos = _testGetSet->_textBuffer->GetCursor().GetPosition(); + cursorPos.X++; + cursorPos.Y++; + _testGetSet->_textBuffer->GetCursor().SetPosition(cursorPos); auto coordCursorExpectedSecond{ coordCursorExpectedFirst }; coordCursorExpectedSecond += til::point{ 1, 1 }; - VERIFY_IS_TRUE(_pDispatch.get()->DeviceStatusReport(DispatchTypes::AnsiStatusType::CPR_CursorPositionReport)); + VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::AnsiStatusType::CPR_CursorPositionReport)); wchar_t pwszBuffer[50]; @@ -1719,7 +1431,7 @@ class AdapterTest Log::Comment(L"Test 1: Verify normal response."); _testGetSet->PrepData(); - VERIFY_IS_TRUE(_pDispatch.get()->DeviceAttributes()); + VERIFY_IS_TRUE(_pDispatch->DeviceAttributes()); PCWSTR pwszExpectedResponse = L"\x1b[?1;0c"; _testGetSet->ValidateInputEvent(pwszExpectedResponse); @@ -1728,7 +1440,7 @@ class AdapterTest _testGetSet->PrepData(); _testGetSet->_writeInputResult = FALSE; - VERIFY_THROWS(_pDispatch.get()->DeviceAttributes(), std::exception); + VERIFY_THROWS(_pDispatch->DeviceAttributes(), std::exception); } TEST_METHOD(SecondaryDeviceAttributesTests) @@ -1737,7 +1449,7 @@ class AdapterTest Log::Comment(L"Test 1: Verify normal response."); _testGetSet->PrepData(); - VERIFY_IS_TRUE(_pDispatch.get()->SecondaryDeviceAttributes()); + VERIFY_IS_TRUE(_pDispatch->SecondaryDeviceAttributes()); PCWSTR pwszExpectedResponse = L"\x1b[>0;10;1c"; _testGetSet->ValidateInputEvent(pwszExpectedResponse); @@ -1746,7 +1458,7 @@ class AdapterTest _testGetSet->PrepData(); _testGetSet->_writeInputResult = FALSE; - VERIFY_THROWS(_pDispatch.get()->SecondaryDeviceAttributes(), std::exception); + VERIFY_THROWS(_pDispatch->SecondaryDeviceAttributes(), std::exception); } TEST_METHOD(TertiaryDeviceAttributesTests) @@ -1755,7 +1467,7 @@ class AdapterTest Log::Comment(L"Test 1: Verify normal response."); _testGetSet->PrepData(); - VERIFY_IS_TRUE(_pDispatch.get()->TertiaryDeviceAttributes()); + VERIFY_IS_TRUE(_pDispatch->TertiaryDeviceAttributes()); PCWSTR pwszExpectedResponse = L"\x1bP!|00000000\x1b\\"; _testGetSet->ValidateInputEvent(pwszExpectedResponse); @@ -1764,7 +1476,7 @@ class AdapterTest _testGetSet->PrepData(); _testGetSet->_writeInputResult = FALSE; - VERIFY_THROWS(_pDispatch.get()->TertiaryDeviceAttributes(), std::exception); + VERIFY_THROWS(_pDispatch->TertiaryDeviceAttributes(), std::exception); } TEST_METHOD(RequestTerminalParametersTests) @@ -1773,28 +1485,28 @@ class AdapterTest Log::Comment(L"Test 1: Verify response for unsolicited permission."); _testGetSet->PrepData(); - VERIFY_IS_TRUE(_pDispatch.get()->RequestTerminalParameters(DispatchTypes::ReportingPermission::Unsolicited)); + VERIFY_IS_TRUE(_pDispatch->RequestTerminalParameters(DispatchTypes::ReportingPermission::Unsolicited)); _testGetSet->ValidateInputEvent(L"\x1b[2;1;1;128;128;1;0x"); Log::Comment(L"Test 2: Verify response for solicited permission."); _testGetSet->PrepData(); - VERIFY_IS_TRUE(_pDispatch.get()->RequestTerminalParameters(DispatchTypes::ReportingPermission::Solicited)); + VERIFY_IS_TRUE(_pDispatch->RequestTerminalParameters(DispatchTypes::ReportingPermission::Solicited)); _testGetSet->ValidateInputEvent(L"\x1b[3;1;1;128;128;1;0x"); Log::Comment(L"Test 3: Verify failure with invalid parameter."); _testGetSet->PrepData(); - VERIFY_IS_FALSE(_pDispatch.get()->RequestTerminalParameters((DispatchTypes::ReportingPermission)2)); + VERIFY_IS_FALSE(_pDispatch->RequestTerminalParameters((DispatchTypes::ReportingPermission)2)); Log::Comment(L"Test 4: Verify failure when WriteInput doesn't work."); _testGetSet->PrepData(); _testGetSet->_writeInputResult = FALSE; - VERIFY_THROWS(_pDispatch.get()->RequestTerminalParameters(DispatchTypes::ReportingPermission::Unsolicited), std::exception); + VERIFY_THROWS(_pDispatch->RequestTerminalParameters(DispatchTypes::ReportingPermission::Unsolicited), std::exception); } TEST_METHOD(RequestSettingsTests) { const auto requestSetting = [=](const std::wstring_view settingId = {}) { - const auto stringHandler = _pDispatch.get()->RequestSetting(); + const auto stringHandler = _pDispatch->RequestSetting(); for (auto ch : settingId) { stringHandler(ch); @@ -1804,7 +1516,7 @@ class AdapterTest Log::Comment(L"Requesting DECSTBM margins (5 to 10)."); _testGetSet->PrepData(); - _pDispatch.get()->SetTopBottomScrollingMargins(5, 10); + _pDispatch->SetTopBottomScrollingMargins(5, 10); requestSetting(L"r"); _testGetSet->ValidateInputEvent(L"\033P1$r5;10r\033\\"); @@ -1812,79 +1524,88 @@ class AdapterTest _testGetSet->PrepData(); // Set screen height to 25 - this will be the expected margin range. _testGetSet->_viewport.Bottom = _testGetSet->_viewport.Top + 25; - _pDispatch.get()->SetTopBottomScrollingMargins(0, 0); + _pDispatch->SetTopBottomScrollingMargins(0, 0); requestSetting(L"r"); _testGetSet->ValidateInputEvent(L"\033P1$r1;25r\033\\"); Log::Comment(L"Requesting SGR attributes (default)."); _testGetSet->PrepData(); - _testGetSet->_attribute = {}; + TextAttribute attribute = {}; + _testGetSet->_textBuffer->SetCurrentAttributes(attribute); requestSetting(L"m"); _testGetSet->ValidateInputEvent(L"\033P1$r0m\033\\"); Log::Comment(L"Requesting SGR attributes (intense, underlined, reversed)."); _testGetSet->PrepData(); - _testGetSet->_attribute = {}; - _testGetSet->_attribute.SetIntense(true); - _testGetSet->_attribute.SetUnderlined(true); - _testGetSet->_attribute.SetReverseVideo(true); + attribute = {}; + attribute.SetIntense(true); + attribute.SetUnderlined(true); + attribute.SetReverseVideo(true); + _testGetSet->_textBuffer->SetCurrentAttributes(attribute); requestSetting(L"m"); _testGetSet->ValidateInputEvent(L"\033P1$r0;1;4;7m\033\\"); Log::Comment(L"Requesting SGR attributes (faint, blinking, invisible)."); _testGetSet->PrepData(); - _testGetSet->_attribute = {}; - _testGetSet->_attribute.SetFaint(true); - _testGetSet->_attribute.SetBlinking(true); - _testGetSet->_attribute.SetInvisible(true); + attribute = {}; + attribute.SetFaint(true); + attribute.SetBlinking(true); + attribute.SetInvisible(true); + _testGetSet->_textBuffer->SetCurrentAttributes(attribute); requestSetting(L"m"); _testGetSet->ValidateInputEvent(L"\033P1$r0;2;5;8m\033\\"); Log::Comment(L"Requesting SGR attributes (italic, crossed-out)."); _testGetSet->PrepData(); - _testGetSet->_attribute = {}; - _testGetSet->_attribute.SetItalic(true); - _testGetSet->_attribute.SetCrossedOut(true); + attribute = {}; + attribute.SetItalic(true); + attribute.SetCrossedOut(true); + _testGetSet->_textBuffer->SetCurrentAttributes(attribute); requestSetting(L"m"); _testGetSet->ValidateInputEvent(L"\033P1$r0;3;9m\033\\"); Log::Comment(L"Requesting SGR attributes (doubly underlined, overlined)."); _testGetSet->PrepData(); - _testGetSet->_attribute = {}; - _testGetSet->_attribute.SetDoublyUnderlined(true); - _testGetSet->_attribute.SetOverlined(true); + attribute = {}; + attribute.SetDoublyUnderlined(true); + attribute.SetOverlined(true); + _testGetSet->_textBuffer->SetCurrentAttributes(attribute); requestSetting(L"m"); _testGetSet->ValidateInputEvent(L"\033P1$r0;21;53m\033\\"); Log::Comment(L"Requesting SGR attributes (standard colors)."); _testGetSet->PrepData(); - _testGetSet->_attribute = {}; - _testGetSet->_attribute.SetIndexedForeground(TextColor::DARK_YELLOW); - _testGetSet->_attribute.SetIndexedBackground(TextColor::DARK_CYAN); + attribute = {}; + attribute.SetIndexedForeground(TextColor::DARK_YELLOW); + attribute.SetIndexedBackground(TextColor::DARK_CYAN); + _testGetSet->_textBuffer->SetCurrentAttributes(attribute); requestSetting(L"m"); _testGetSet->ValidateInputEvent(L"\033P1$r0;33;46m\033\\"); Log::Comment(L"Requesting SGR attributes (AIX colors)."); _testGetSet->PrepData(); - _testGetSet->_attribute = {}; - _testGetSet->_attribute.SetIndexedForeground(TextColor::BRIGHT_CYAN); - _testGetSet->_attribute.SetIndexedBackground(TextColor::BRIGHT_YELLOW); + attribute = {}; + attribute.SetIndexedForeground(TextColor::BRIGHT_CYAN); + attribute.SetIndexedBackground(TextColor::BRIGHT_YELLOW); + _testGetSet->_textBuffer->SetCurrentAttributes(attribute); requestSetting(L"m"); _testGetSet->ValidateInputEvent(L"\033P1$r0;96;103m\033\\"); Log::Comment(L"Requesting SGR attributes (ITU indexed colors)."); _testGetSet->PrepData(); - _testGetSet->_attribute = {}; - _testGetSet->_attribute.SetIndexedForeground256(123); - _testGetSet->_attribute.SetIndexedBackground256(45); + attribute = {}; + attribute.SetIndexedForeground256(123); + attribute.SetIndexedBackground256(45); + _testGetSet->_textBuffer->SetCurrentAttributes(attribute); requestSetting(L"m"); _testGetSet->ValidateInputEvent(L"\033P1$r0;38;5;123;48;5;45m\033\\"); Log::Comment(L"Requesting SGR attributes (ITU RGB colors)."); _testGetSet->PrepData(); - _testGetSet->_attribute = {}; - _testGetSet->_attribute.SetForeground(RGB(12, 34, 56)); - _testGetSet->_attribute.SetBackground(RGB(65, 43, 21)); + attribute = {}; + attribute.SetForeground(RGB(12, 34, 56)); + attribute.SetBackground(RGB(65, 43, 21)); + _testGetSet->_textBuffer->SetCurrentAttributes(attribute); requestSetting(L"m"); _testGetSet->ValidateInputEvent(L"\033P1$r0;38;2;12;34;56;48;2;65;43;21m\033\\"); @@ -1897,87 +1618,72 @@ class AdapterTest TEST_METHOD(CursorKeysModeTest) { Log::Comment(L"Starting test..."); + _terminalInput.SetInputMode(TerminalInput::Mode::CursorKey, true); // success cases // set numeric mode = true Log::Comment(L"Test 1: application mode = false"); - _testGetSet->_setInputModeResult = true; - _testGetSet->_expectedInputMode = TerminalInput::Mode::CursorKey; - _testGetSet->_expectedInputModeEnabled = false; - - VERIFY_IS_TRUE(_pDispatch.get()->SetCursorKeysMode(false)); + VERIFY_IS_TRUE(_pDispatch->SetCursorKeysMode(false)); + VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::CursorKey)); // set numeric mode = false Log::Comment(L"Test 2: application mode = true"); - _testGetSet->_setInputModeResult = true; - _testGetSet->_expectedInputMode = TerminalInput::Mode::CursorKey; - _testGetSet->_expectedInputModeEnabled = true; - - VERIFY_IS_TRUE(_pDispatch.get()->SetCursorKeysMode(true)); + VERIFY_IS_TRUE(_pDispatch->SetCursorKeysMode(true)); + VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::CursorKey)); } TEST_METHOD(KeypadModeTest) { Log::Comment(L"Starting test..."); + _terminalInput.SetInputMode(TerminalInput::Mode::Keypad, true); // success cases // set numeric mode = true Log::Comment(L"Test 1: application mode = false"); - _testGetSet->_setInputModeResult = true; - _testGetSet->_expectedInputMode = TerminalInput::Mode::Keypad; - _testGetSet->_expectedInputModeEnabled = false; - - VERIFY_IS_TRUE(_pDispatch.get()->SetKeypadMode(false)); + VERIFY_IS_TRUE(_pDispatch->SetKeypadMode(false)); + VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::Keypad)); // set numeric mode = false Log::Comment(L"Test 2: application mode = true"); - _testGetSet->_setInputModeResult = true; - _testGetSet->_expectedInputMode = TerminalInput::Mode::Keypad; - _testGetSet->_expectedInputModeEnabled = true; - - VERIFY_IS_TRUE(_pDispatch.get()->SetKeypadMode(true)); + VERIFY_IS_TRUE(_pDispatch->SetKeypadMode(true)); + VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::Keypad)); } TEST_METHOD(AnsiModeTest) { Log::Comment(L"Starting test..."); + _stateMachine->SetParserMode(StateMachine::Mode::Ansi, false); // success cases // set ansi mode = true Log::Comment(L"Test 1: ansi mode = true"); - _testGetSet->_setParserModeResult = true; - _testGetSet->_expectedParserMode = StateMachine::Mode::Ansi; - _testGetSet->_expectedParserModeEnabled = true; - - VERIFY_IS_TRUE(_pDispatch.get()->SetAnsiMode(true)); + VERIFY_IS_TRUE(_pDispatch->SetAnsiMode(true)); + VERIFY_IS_TRUE(_stateMachine->GetParserMode(StateMachine::Mode::Ansi)); // set ansi mode = false Log::Comment(L"Test 2: ansi mode = false."); - _testGetSet->_setParserModeResult = true; - _testGetSet->_expectedParserMode = StateMachine::Mode::Ansi; - _testGetSet->_expectedParserModeEnabled = false; - - VERIFY_IS_TRUE(_pDispatch.get()->SetAnsiMode(false)); + VERIFY_IS_TRUE(_pDispatch->SetAnsiMode(false)); + VERIFY_IS_FALSE(_stateMachine->GetParserMode(StateMachine::Mode::Ansi)); } TEST_METHOD(AllowBlinkingTest) { Log::Comment(L"Starting test..."); + _testGetSet->PrepData(); + // success cases - // set numeric mode = true + // set blinking mode = true Log::Comment(L"Test 1: enable blinking = true"); - _testGetSet->_enableCursorBlinkingResult = TRUE; - _testGetSet->_enable = true; - - VERIFY_IS_TRUE(_pDispatch.get()->EnableCursorBlinking(true)); + _testGetSet->_textBuffer->GetCursor().SetBlinkingAllowed(false); + VERIFY_IS_TRUE(_pDispatch->EnableCursorBlinking(true)); + VERIFY_IS_TRUE(_testGetSet->_textBuffer->GetCursor().IsBlinkingAllowed()); - // set numeric mode = false + // set blinking mode = false Log::Comment(L"Test 2: enable blinking = false"); - _testGetSet->_enableCursorBlinkingResult = TRUE; - _testGetSet->_enable = false; - - VERIFY_IS_TRUE(_pDispatch.get()->EnableCursorBlinking(false)); + _testGetSet->_textBuffer->GetCursor().SetBlinkingAllowed(true); + VERIFY_IS_TRUE(_pDispatch->EnableCursorBlinking(false)); + VERIFY_IS_FALSE(_testGetSet->_textBuffer->GetCursor().IsBlinkingAllowed()); } TEST_METHOD(ScrollMarginsTest) @@ -1985,85 +1691,82 @@ class AdapterTest Log::Comment(L"Starting test..."); SMALL_RECT srTestMargins = { 0 }; - _testGetSet->_bufferSize = { 100, 600 }; + _testGetSet->_textBuffer = std::make_unique(COORD{ 100, 600 }, TextAttribute{}, 0, false, _testGetSet->_renderer); _testGetSet->_viewport.Right = 8; _testGetSet->_viewport.Bottom = 8; - _testGetSet->_getConsoleScreenBufferInfoExResult = TRUE; SHORT sScreenHeight = _testGetSet->_viewport.Bottom - _testGetSet->_viewport.Top; Log::Comment(L"Test 1: Verify having both values is valid."); _testGetSet->_SetMarginsHelper(&srTestMargins, 2, 6); _testGetSet->_setScrollingRegionResult = TRUE; - _testGetSet->_setCursorPositionResult = true; - _testGetSet->_moveToBottomResult = true; - VERIFY_IS_TRUE(_pDispatch.get()->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); + VERIFY_IS_TRUE(_pDispatch->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); Log::Comment(L"Test 2: Verify having only top is valid."); _testGetSet->_SetMarginsHelper(&srTestMargins, 7, 0); _testGetSet->_expectedScrollRegion.Bottom = _testGetSet->_viewport.Bottom - 1; // We expect the bottom to be the bottom of the viewport, exclusive. _testGetSet->_setScrollingRegionResult = TRUE; - VERIFY_IS_TRUE(_pDispatch.get()->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); + VERIFY_IS_TRUE(_pDispatch->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); Log::Comment(L"Test 3: Verify having only bottom is valid."); _testGetSet->_SetMarginsHelper(&srTestMargins, 0, 7); _testGetSet->_setScrollingRegionResult = TRUE; - VERIFY_IS_TRUE(_pDispatch.get()->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); + VERIFY_IS_TRUE(_pDispatch->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); Log::Comment(L"Test 4: Verify having no values is valid."); _testGetSet->_SetMarginsHelper(&srTestMargins, 0, 0); _testGetSet->_setScrollingRegionResult = TRUE; - VERIFY_IS_TRUE(_pDispatch.get()->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); + VERIFY_IS_TRUE(_pDispatch->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); Log::Comment(L"Test 5: Verify having both values, but bad bounds has no effect."); _testGetSet->_SetMarginsHelper(&srTestMargins, 7, 3); _testGetSet->_setScrollingRegionResult = TRUE; _testGetSet->_activeScrollRegion = {}; - VERIFY_IS_TRUE(_pDispatch.get()->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); + VERIFY_IS_TRUE(_pDispatch->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); VERIFY_ARE_EQUAL(SMALL_RECT{}, _testGetSet->_activeScrollRegion); Log::Comment(L"Test 6: Verify setting margins to (0, height) clears them"); // First set, _testGetSet->_setScrollingRegionResult = TRUE; _testGetSet->_SetMarginsHelper(&srTestMargins, 2, 6); - VERIFY_IS_TRUE(_pDispatch.get()->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); + VERIFY_IS_TRUE(_pDispatch->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); // Then clear _testGetSet->_SetMarginsHelper(&srTestMargins, 0, sScreenHeight); _testGetSet->_expectedScrollRegion.Top = 0; _testGetSet->_expectedScrollRegion.Bottom = 0; - VERIFY_IS_TRUE(_pDispatch.get()->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); + VERIFY_IS_TRUE(_pDispatch->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); Log::Comment(L"Test 7: Verify setting margins to (1, height) clears them"); // First set, _testGetSet->_setScrollingRegionResult = TRUE; _testGetSet->_SetMarginsHelper(&srTestMargins, 2, 6); - VERIFY_IS_TRUE(_pDispatch.get()->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); + VERIFY_IS_TRUE(_pDispatch->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); // Then clear _testGetSet->_SetMarginsHelper(&srTestMargins, 1, sScreenHeight); _testGetSet->_expectedScrollRegion.Top = 0; _testGetSet->_expectedScrollRegion.Bottom = 0; - VERIFY_IS_TRUE(_pDispatch.get()->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); + VERIFY_IS_TRUE(_pDispatch->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); Log::Comment(L"Test 8: Verify setting margins to (1, 0) clears them"); // First set, _testGetSet->_setScrollingRegionResult = TRUE; _testGetSet->_SetMarginsHelper(&srTestMargins, 2, 6); - VERIFY_IS_TRUE(_pDispatch.get()->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); + VERIFY_IS_TRUE(_pDispatch->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); // Then clear _testGetSet->_SetMarginsHelper(&srTestMargins, 1, 0); _testGetSet->_expectedScrollRegion.Top = 0; _testGetSet->_expectedScrollRegion.Bottom = 0; - VERIFY_IS_TRUE(_pDispatch.get()->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); + VERIFY_IS_TRUE(_pDispatch->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); Log::Comment(L"Test 9: Verify having top and bottom margin the same has no effect."); _testGetSet->_SetMarginsHelper(&srTestMargins, 4, 4); _testGetSet->_setScrollingRegionResult = TRUE; _testGetSet->_activeScrollRegion = {}; - VERIFY_IS_TRUE(_pDispatch.get()->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); + VERIFY_IS_TRUE(_pDispatch->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); VERIFY_ARE_EQUAL(SMALL_RECT{}, _testGetSet->_activeScrollRegion); Log::Comment(L"Test 10: Verify having top margin out of bounds has no effect."); @@ -2071,7 +1774,7 @@ class AdapterTest _testGetSet->_SetMarginsHelper(&srTestMargins, sScreenHeight + 1, sScreenHeight + 10); _testGetSet->_setScrollingRegionResult = TRUE; _testGetSet->_activeScrollRegion = {}; - VERIFY_IS_TRUE(_pDispatch.get()->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); + VERIFY_IS_TRUE(_pDispatch->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); VERIFY_ARE_EQUAL(SMALL_RECT{}, _testGetSet->_activeScrollRegion); Log::Comment(L"Test 11: Verify having bottom margin out of bounds has no effect."); @@ -2079,7 +1782,7 @@ class AdapterTest _testGetSet->_SetMarginsHelper(&srTestMargins, 1, sScreenHeight + 1); _testGetSet->_setScrollingRegionResult = TRUE; _testGetSet->_activeScrollRegion = {}; - VERIFY_IS_TRUE(_pDispatch.get()->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); + VERIFY_IS_TRUE(_pDispatch->SetTopBottomScrollingMargins(srTestMargins.Top, srTestMargins.Bottom)); VERIFY_ARE_EQUAL(SMALL_RECT{}, _testGetSet->_activeScrollRegion); } @@ -2092,21 +1795,21 @@ class AdapterTest Log::Comment(L"Test 1: Line feed without carriage return."); _testGetSet->_expectedLineFeedWithReturn = false; - VERIFY_IS_TRUE(_pDispatch.get()->LineFeed(DispatchTypes::LineFeedType::WithoutReturn)); + VERIFY_IS_TRUE(_pDispatch->LineFeed(DispatchTypes::LineFeedType::WithoutReturn)); Log::Comment(L"Test 2: Line feed with carriage return."); _testGetSet->_expectedLineFeedWithReturn = true; - VERIFY_IS_TRUE(_pDispatch.get()->LineFeed(DispatchTypes::LineFeedType::WithReturn)); + VERIFY_IS_TRUE(_pDispatch->LineFeed(DispatchTypes::LineFeedType::WithReturn)); Log::Comment(L"Test 3: Line feed depends on mode, and mode reset."); _testGetSet->_getLineFeedModeResult = false; _testGetSet->_expectedLineFeedWithReturn = false; - VERIFY_IS_TRUE(_pDispatch.get()->LineFeed(DispatchTypes::LineFeedType::DependsOnMode)); + VERIFY_IS_TRUE(_pDispatch->LineFeed(DispatchTypes::LineFeedType::DependsOnMode)); Log::Comment(L"Test 4: Line feed depends on mode, and mode set."); _testGetSet->_getLineFeedModeResult = true; _testGetSet->_expectedLineFeedWithReturn = true; - VERIFY_IS_TRUE(_pDispatch.get()->LineFeed(DispatchTypes::LineFeedType::DependsOnMode)); + VERIFY_IS_TRUE(_pDispatch->LineFeed(DispatchTypes::LineFeedType::DependsOnMode)); } TEST_METHOD(SetConsoleTitleTest) @@ -2117,13 +1820,13 @@ class AdapterTest _testGetSet->_setWindowTitleResult = TRUE; _testGetSet->_expectedWindowTitle = L"Foo bar"; - VERIFY_IS_TRUE(_pDispatch.get()->SetWindowTitle(_testGetSet->_expectedWindowTitle)); + VERIFY_IS_TRUE(_pDispatch->SetWindowTitle(_testGetSet->_expectedWindowTitle)); Log::Comment(L"Test 2: set title to be null"); _testGetSet->_setWindowTitleResult = FALSE; _testGetSet->_expectedWindowTitle = {}; - VERIFY_IS_TRUE(_pDispatch.get()->SetWindowTitle({})); + VERIFY_IS_TRUE(_pDispatch->SetWindowTitle({})); } TEST_METHOD(TestMouseModes) @@ -2131,52 +1834,46 @@ class AdapterTest Log::Comment(L"Starting test..."); Log::Comment(L"Test 1: Test Default Mouse Mode"); - _testGetSet->_expectedInputModeEnabled = true; - _testGetSet->_expectedInputMode = TerminalInput::Mode::DefaultMouseTracking; - _testGetSet->_setInputModeResult = true; - VERIFY_IS_TRUE(_pDispatch.get()->EnableVT200MouseMode(true)); - _testGetSet->_expectedInputModeEnabled = false; - VERIFY_IS_TRUE(_pDispatch.get()->EnableVT200MouseMode(false)); + _terminalInput.SetInputMode(TerminalInput::Mode::DefaultMouseTracking, false); + VERIFY_IS_TRUE(_pDispatch->EnableVT200MouseMode(true)); + VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::DefaultMouseTracking)); + VERIFY_IS_TRUE(_pDispatch->EnableVT200MouseMode(false)); + VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::DefaultMouseTracking)); Log::Comment(L"Test 2: Test UTF-8 Extended Mouse Mode"); - _testGetSet->_expectedInputModeEnabled = true; - _testGetSet->_expectedInputMode = TerminalInput::Mode::Utf8MouseEncoding; - _testGetSet->_setInputModeResult = true; - VERIFY_IS_TRUE(_pDispatch.get()->EnableUTF8ExtendedMouseMode(true)); - _testGetSet->_expectedInputModeEnabled = false; - VERIFY_IS_TRUE(_pDispatch.get()->EnableUTF8ExtendedMouseMode(false)); + _terminalInput.SetInputMode(TerminalInput::Mode::Utf8MouseEncoding, false); + VERIFY_IS_TRUE(_pDispatch->EnableUTF8ExtendedMouseMode(true)); + VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::Utf8MouseEncoding)); + VERIFY_IS_TRUE(_pDispatch->EnableUTF8ExtendedMouseMode(false)); + VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::Utf8MouseEncoding)); Log::Comment(L"Test 3: Test SGR Extended Mouse Mode"); - _testGetSet->_expectedInputModeEnabled = true; - _testGetSet->_expectedInputMode = TerminalInput::Mode::SgrMouseEncoding; - _testGetSet->_setInputModeResult = true; - VERIFY_IS_TRUE(_pDispatch.get()->EnableSGRExtendedMouseMode(true)); - _testGetSet->_expectedInputModeEnabled = false; - VERIFY_IS_TRUE(_pDispatch.get()->EnableSGRExtendedMouseMode(false)); + _terminalInput.SetInputMode(TerminalInput::Mode::SgrMouseEncoding, false); + VERIFY_IS_TRUE(_pDispatch->EnableSGRExtendedMouseMode(true)); + VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::SgrMouseEncoding)); + VERIFY_IS_TRUE(_pDispatch->EnableSGRExtendedMouseMode(false)); + VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::SgrMouseEncoding)); Log::Comment(L"Test 4: Test Button-Event Mouse Mode"); - _testGetSet->_expectedInputModeEnabled = true; - _testGetSet->_expectedInputMode = TerminalInput::Mode::ButtonEventMouseTracking; - _testGetSet->_setInputModeResult = true; - VERIFY_IS_TRUE(_pDispatch.get()->EnableButtonEventMouseMode(true)); - _testGetSet->_expectedInputModeEnabled = false; - VERIFY_IS_TRUE(_pDispatch.get()->EnableButtonEventMouseMode(false)); + _terminalInput.SetInputMode(TerminalInput::Mode::ButtonEventMouseTracking, false); + VERIFY_IS_TRUE(_pDispatch->EnableButtonEventMouseMode(true)); + VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::ButtonEventMouseTracking)); + VERIFY_IS_TRUE(_pDispatch->EnableButtonEventMouseMode(false)); + VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::ButtonEventMouseTracking)); Log::Comment(L"Test 5: Test Any-Event Mouse Mode"); - _testGetSet->_expectedInputModeEnabled = true; - _testGetSet->_expectedInputMode = TerminalInput::Mode::AnyEventMouseTracking; - _testGetSet->_setInputModeResult = true; - VERIFY_IS_TRUE(_pDispatch.get()->EnableAnyEventMouseMode(true)); - _testGetSet->_expectedInputModeEnabled = false; - VERIFY_IS_TRUE(_pDispatch.get()->EnableAnyEventMouseMode(false)); + _terminalInput.SetInputMode(TerminalInput::Mode::AnyEventMouseTracking, false); + VERIFY_IS_TRUE(_pDispatch->EnableAnyEventMouseMode(true)); + VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::AnyEventMouseTracking)); + VERIFY_IS_TRUE(_pDispatch->EnableAnyEventMouseMode(false)); + VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::AnyEventMouseTracking)); Log::Comment(L"Test 6: Test Alt Scroll Mouse Mode"); - _testGetSet->_expectedInputModeEnabled = true; - _testGetSet->_expectedInputMode = TerminalInput::Mode::AlternateScroll; - _testGetSet->_setInputModeResult = true; - VERIFY_IS_TRUE(_pDispatch.get()->EnableAlternateScroll(true)); - _testGetSet->_expectedInputModeEnabled = false; - VERIFY_IS_TRUE(_pDispatch.get()->EnableAlternateScroll(false)); + _terminalInput.SetInputMode(TerminalInput::Mode::AlternateScroll, false); + VERIFY_IS_TRUE(_pDispatch->EnableAlternateScroll(true)); + VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::AlternateScroll)); + VERIFY_IS_TRUE(_pDispatch->EnableAlternateScroll(false)); + VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::AlternateScroll)); } TEST_METHOD(Xterm256ColorTest) @@ -2188,36 +1885,35 @@ class AdapterTest VTParameter rgOptions[16]; size_t cOptions = 3; - _testGetSet->_getColorTableEntryResult = true; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + _testGetSet->_expectedAttribute = _testGetSet->_textBuffer->GetCurrentAttributes(); Log::Comment(L"Test 1: Change Foreground"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundExtended; rgOptions[1] = DispatchTypes::GraphicsOptions::BlinkOrXterm256Index; rgOptions[2] = (DispatchTypes::GraphicsOptions)2; // Green _testGetSet->_expectedAttribute.SetIndexedForeground256(TextColor::DARK_GREEN); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); Log::Comment(L"Test 2: Change Background"); rgOptions[0] = DispatchTypes::GraphicsOptions::BackgroundExtended; rgOptions[1] = DispatchTypes::GraphicsOptions::BlinkOrXterm256Index; rgOptions[2] = (DispatchTypes::GraphicsOptions)9; // Bright Red _testGetSet->_expectedAttribute.SetIndexedBackground256(TextColor::BRIGHT_RED); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); Log::Comment(L"Test 3: Change Foreground to RGB color"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundExtended; rgOptions[1] = DispatchTypes::GraphicsOptions::BlinkOrXterm256Index; rgOptions[2] = (DispatchTypes::GraphicsOptions)42; // Arbitrary Color _testGetSet->_expectedAttribute.SetIndexedForeground256(42); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); Log::Comment(L"Test 4: Change Background to RGB color"); rgOptions[0] = DispatchTypes::GraphicsOptions::BackgroundExtended; rgOptions[1] = DispatchTypes::GraphicsOptions::BlinkOrXterm256Index; rgOptions[2] = (DispatchTypes::GraphicsOptions)142; // Arbitrary Color _testGetSet->_expectedAttribute.SetIndexedBackground256(142); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); Log::Comment(L"Test 5: Change Foreground to Legacy Attr while BG is RGB color"); // Unfortunately this test isn't all that good, because the adapterTest adapter isn't smart enough @@ -2227,7 +1923,7 @@ class AdapterTest rgOptions[1] = DispatchTypes::GraphicsOptions::BlinkOrXterm256Index; rgOptions[2] = (DispatchTypes::GraphicsOptions)9; // Bright Red _testGetSet->_expectedAttribute.SetIndexedForeground256(TextColor::BRIGHT_RED); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, cOptions })); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, cOptions })); } TEST_METHOD(XtermExtendedColorDefaultParameterTest) @@ -2238,34 +1934,33 @@ class AdapterTest VTParameter rgOptions[16]; - _testGetSet->_getColorTableEntryResult = true; - _testGetSet->_expectedAttribute = _testGetSet->_attribute; + _testGetSet->_expectedAttribute = _testGetSet->_textBuffer->GetCurrentAttributes(); Log::Comment(L"Test 1: Change Indexed Foreground with missing index parameter"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundExtended; rgOptions[1] = DispatchTypes::GraphicsOptions::BlinkOrXterm256Index; _testGetSet->_expectedAttribute.SetIndexedForeground256(TextColor::DARK_BLACK); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, 2 })); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, 2 })); Log::Comment(L"Test 2: Change Indexed Background with default index parameter"); rgOptions[0] = DispatchTypes::GraphicsOptions::BackgroundExtended; rgOptions[1] = DispatchTypes::GraphicsOptions::BlinkOrXterm256Index; rgOptions[2] = {}; _testGetSet->_expectedAttribute.SetIndexedBackground256(TextColor::DARK_BLACK); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, 3 })); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, 3 })); Log::Comment(L"Test 3: Change RGB Foreground with all RGB parameters missing"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundExtended; rgOptions[1] = DispatchTypes::GraphicsOptions::RGBColorOrFaint; _testGetSet->_expectedAttribute.SetForeground(RGB(0, 0, 0)); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, 2 })); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, 2 })); Log::Comment(L"Test 4: Change RGB Background with some missing RGB parameters"); rgOptions[0] = DispatchTypes::GraphicsOptions::BackgroundExtended; rgOptions[1] = DispatchTypes::GraphicsOptions::RGBColorOrFaint; rgOptions[2] = 123; _testGetSet->_expectedAttribute.SetBackground(RGB(123, 0, 0)); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, 3 })); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, 3 })); Log::Comment(L"Test 5: Change RGB Foreground with some default RGB parameters"); rgOptions[0] = DispatchTypes::GraphicsOptions::ForegroundExtended; @@ -2274,21 +1969,20 @@ class AdapterTest rgOptions[3] = {}; rgOptions[4] = 123; _testGetSet->_expectedAttribute.SetForeground(RGB(0, 0, 123)); - VERIFY_IS_TRUE(_pDispatch.get()->SetGraphicsRendition({ rgOptions, 5 })); + VERIFY_IS_TRUE(_pDispatch->SetGraphicsRendition({ rgOptions, 5 })); } TEST_METHOD(SetColorTableValue) { _testGetSet->PrepData(); - _testGetSet->_setColorTableEntryResult = true; const auto testColor = RGB(1, 2, 3); - _testGetSet->_expectedColorValue = testColor; + const auto& renderSettings = _testGetSet->_renderer._renderSettings; for (size_t i = 0; i < 256; i++) { - _testGetSet->_expectedColorTableIndex = i; - VERIFY_IS_TRUE(_pDispatch.get()->SetColorTableEntry(i, testColor)); + VERIFY_IS_TRUE(_pDispatch->SetColorTableEntry(i, testColor)); + VERIFY_ARE_EQUAL(testColor, renderSettings.GetColorTableEntry(i)); } } @@ -2298,43 +1992,49 @@ class AdapterTest using FontSet = DispatchTypes::DrcsFontSet; using FontUsage = DispatchTypes::DrcsFontUsage; - const auto decdld = [=](const auto cmw, const auto cmh, const auto ss, const auto u, const std::wstring_view data = {}) { - const auto ec = DispatchTypes::DrcsEraseControl::AllChars; - const auto css = DispatchTypes::DrcsCharsetSize::Size94; + FontBuffer fontBuffer; + SIZE expectedCellSize; + + const auto decdld = [&](const auto cmw, const auto cmh, const auto ss, const auto u, const std::wstring_view data = {}) { const auto cellMatrix = static_cast(cmw); - const auto stringHandler = _pDispatch.get()->DownloadDRCS(0, 0, ec, cellMatrix, ss, u, cmh, css); - if (stringHandler) + RETURN_BOOL_IF_FALSE(fontBuffer.SetEraseControl(DispatchTypes::DrcsEraseControl::AllChars)); + RETURN_BOOL_IF_FALSE(fontBuffer.SetAttributes(cellMatrix, cmh, ss, u)); + RETURN_BOOL_IF_FALSE(fontBuffer.SetStartChar(0, DispatchTypes::DrcsCharsetSize::Size94)); + + fontBuffer.AddSixelData(L'B'); // Charset identifier + for (auto ch : data) { - stringHandler(L'B'); // Charset identifier - for (auto ch : data) - { - stringHandler(ch); - } - stringHandler(L'\033'); // String terminator + fontBuffer.AddSixelData(ch); } - return stringHandler != nullptr; + RETURN_BOOL_IF_FALSE(fontBuffer.FinalizeSixelData()); + + const auto cellSize = fontBuffer.GetCellSize().to_win32_size(); + Log::Comment(NoThrowString().Format(L"Cell size: %dx%d", cellSize.cx, cellSize.cy)); + VERIFY_ARE_EQUAL(expectedCellSize.cx, cellSize.cx); + VERIFY_ARE_EQUAL(expectedCellSize.cy, cellSize.cy); + return true; }; // Matrix sizes at 80x24 should always use a 10x10 cell size (VT2xx). Log::Comment(L"Matrix 5x10 for 80x24 font set with text usage"); - _testGetSet->_expectedCellSize = { 10, 10 }; + expectedCellSize = { 10, 10 }; VERIFY_IS_TRUE(decdld(CellMatrix::Size5x10, 0, FontSet::Size80x24, FontUsage::Text)); Log::Comment(L"Matrix 6x10 for 80x24 font set with text usage"); - _testGetSet->_expectedCellSize = { 10, 10 }; + expectedCellSize = { 10, 10 }; VERIFY_IS_TRUE(decdld(CellMatrix::Size6x10, 0, FontSet::Size80x24, FontUsage::Text)); Log::Comment(L"Matrix 7x10 for 80x24 font set with text usage"); - _testGetSet->_expectedCellSize = { 10, 10 }; + expectedCellSize = { 10, 10 }; VERIFY_IS_TRUE(decdld(CellMatrix::Size7x10, 0, FontSet::Size80x24, FontUsage::Text)); // At 132x24 the cell size is typically 6x10 (VT240), but could be 10x10 (VT220) Log::Comment(L"Matrix 5x10 for 132x24 font set with text usage"); - _testGetSet->_expectedCellSize = { 6, 10 }; + expectedCellSize = { 6, 10 }; VERIFY_IS_TRUE(decdld(CellMatrix::Size5x10, 0, FontSet::Size132x24, FontUsage::Text)); Log::Comment(L"Matrix 6x10 for 132x24 font set with text usage"); - _testGetSet->_expectedCellSize = { 6, 10 }; + expectedCellSize = { 6, 10 }; VERIFY_IS_TRUE(decdld(CellMatrix::Size6x10, 0, FontSet::Size132x24, FontUsage::Text)); Log::Comment(L"Matrix 7x10 for 132x24 font set with text usage (VT220 only)"); - _testGetSet->_expectedCellSize = { 10, 10 }; + expectedCellSize = { 10, 10 }; VERIFY_IS_TRUE(decdld(CellMatrix::Size7x10, 0, FontSet::Size132x24, FontUsage::Text)); // Full cell usage is invalid for all matrix sizes except 6x10 at 132x24. @@ -2347,7 +2047,7 @@ class AdapterTest Log::Comment(L"Matrix 5x10 for 132x24 font set with full cell usage (invalid)"); VERIFY_IS_FALSE(decdld(CellMatrix::Size5x10, 0, FontSet::Size132x24, FontUsage::FullCell)); Log::Comment(L"Matrix 6x10 for 132x24 font set with full cell usage"); - _testGetSet->_expectedCellSize = { 6, 10 }; + expectedCellSize = { 6, 10 }; VERIFY_IS_TRUE(decdld(CellMatrix::Size6x10, 0, FontSet::Size132x24, FontUsage::FullCell)); Log::Comment(L"Matrix 7x10 for 132x24 font set with full cell usage (invalid)"); VERIFY_IS_FALSE(decdld(CellMatrix::Size7x10, 0, FontSet::Size132x24, FontUsage::FullCell)); @@ -2364,15 +2064,15 @@ class AdapterTest // The height parameter has no effect when a matrix size is used. Log::Comment(L"Matrix 7x10 with unused height parameter"); - _testGetSet->_expectedCellSize = { 10, 10 }; + expectedCellSize = { 10, 10 }; VERIFY_IS_TRUE(decdld(CellMatrix::Size7x10, 20, FontSet::Size80x24, FontUsage::Text)); // Full cell fonts with explicit dimensions are accepted as their given cell size. Log::Comment(L"Explicit 13x17 for 80x24 font set with full cell usage"); - _testGetSet->_expectedCellSize = { 13, 17 }; + expectedCellSize = { 13, 17 }; VERIFY_IS_TRUE(decdld(13, 17, FontSet::Size80x24, FontUsage::FullCell)); Log::Comment(L"Explicit 9x25 for 132x24 font set with full cell usage"); - _testGetSet->_expectedCellSize = { 9, 25 }; + expectedCellSize = { 9, 25 }; VERIFY_IS_TRUE(decdld(9, 25, FontSet::Size132x24, FontUsage::FullCell)); // Cell sizes outside the maximum supported range (16x32) are invalid. @@ -2381,174 +2081,170 @@ class AdapterTest // Text fonts with explicit dimensions are interpreted as their closest matching device. Log::Comment(L"Explicit 12x12 for 80x24 font set with text usage (VT320)"); - _testGetSet->_expectedCellSize = { 15, 12 }; + expectedCellSize = { 15, 12 }; VERIFY_IS_TRUE(decdld(12, 12, FontSet::Size80x24, FontUsage::Text)); Log::Comment(L"Explicit 9x20 for 80x24 font set with text usage (VT340)"); - _testGetSet->_expectedCellSize = { 10, 20 }; + expectedCellSize = { 10, 20 }; VERIFY_IS_TRUE(decdld(9, 20, FontSet::Size80x24, FontUsage::Text)); Log::Comment(L"Explicit 10x30 for 80x24 font set with text usage (VT382)"); - _testGetSet->_expectedCellSize = { 12, 30 }; + expectedCellSize = { 12, 30 }; VERIFY_IS_TRUE(decdld(10, 30, FontSet::Size80x24, FontUsage::Text)); Log::Comment(L"Explicit 8x16 for 80x24 font set with text usage (VT420/VT5xx)"); - _testGetSet->_expectedCellSize = { 10, 16 }; + expectedCellSize = { 10, 16 }; VERIFY_IS_TRUE(decdld(8, 16, FontSet::Size80x24, FontUsage::Text)); Log::Comment(L"Explicit 7x12 for 132x24 font set with text usage (VT320)"); - _testGetSet->_expectedCellSize = { 9, 12 }; + expectedCellSize = { 9, 12 }; VERIFY_IS_TRUE(decdld(7, 12, FontSet::Size132x24, FontUsage::Text)); Log::Comment(L"Explicit 5x20 for 132x24 font set with text usage (VT340)"); - _testGetSet->_expectedCellSize = { 6, 20 }; + expectedCellSize = { 6, 20 }; VERIFY_IS_TRUE(decdld(5, 20, FontSet::Size132x24, FontUsage::Text)); Log::Comment(L"Explicit 6x30 for 132x24 font set with text usage (VT382)"); - _testGetSet->_expectedCellSize = { 7, 30 }; + expectedCellSize = { 7, 30 }; VERIFY_IS_TRUE(decdld(6, 30, FontSet::Size132x24, FontUsage::Text)); Log::Comment(L"Explicit 5x16 for 132x24 font set with text usage (VT420/VT5xx)"); - _testGetSet->_expectedCellSize = { 6, 16 }; + expectedCellSize = { 6, 16 }; VERIFY_IS_TRUE(decdld(5, 16, FontSet::Size132x24, FontUsage::Text)); // Font sets with more than 24 lines must be VT420/VT5xx. Log::Comment(L"80x36 font set with text usage (VT420/VT5xx)"); - _testGetSet->_expectedCellSize = { 10, 10 }; + expectedCellSize = { 10, 10 }; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size80x36, FontUsage::Text)); Log::Comment(L"80x48 font set with text usage (VT420/VT5xx)"); - _testGetSet->_expectedCellSize = { 10, 8 }; + expectedCellSize = { 10, 8 }; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size80x48, FontUsage::Text)); Log::Comment(L"132x36 font set with text usage (VT420/VT5xx)"); - _testGetSet->_expectedCellSize = { 6, 10 }; + expectedCellSize = { 6, 10 }; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size132x36, FontUsage::Text)); Log::Comment(L"132x48 font set with text usage (VT420/VT5xx)"); - _testGetSet->_expectedCellSize = { 6, 8 }; + expectedCellSize = { 6, 8 }; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size132x48, FontUsage::Text)); Log::Comment(L"80x36 font set with full cell usage (VT420/VT5xx)"); - _testGetSet->_expectedCellSize = { 10, 10 }; + expectedCellSize = { 10, 10 }; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size80x36, FontUsage::FullCell)); Log::Comment(L"80x48 font set with full cell usage (VT420/VT5xx)"); - _testGetSet->_expectedCellSize = { 10, 8 }; + expectedCellSize = { 10, 8 }; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size80x48, FontUsage::FullCell)); Log::Comment(L"132x36 font set with full cell usage (VT420/VT5xx)"); - _testGetSet->_expectedCellSize = { 6, 10 }; + expectedCellSize = { 6, 10 }; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size132x36, FontUsage::FullCell)); Log::Comment(L"132x48 font set with full cell usage (VT420/VT5xx)"); - _testGetSet->_expectedCellSize = { 6, 8 }; + expectedCellSize = { 6, 8 }; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size132x48, FontUsage::FullCell)); // Without an explicit size, the cell size is estimated from the number of sixels // used in the character bitmaps. But note that sixel heights are always a multiple // of 6, so will often be larger than the cell size for which they were intended. Log::Comment(L"8x12 bitmap for 80x24 font set with text usage (VT2xx)"); - _testGetSet->_expectedCellSize = { 10, 10 }; + expectedCellSize = { 10, 10 }; const auto bitmapOf8x12 = L"????????/????????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size80x24, FontUsage::Text, bitmapOf8x12)); Log::Comment(L"12x12 bitmap for 80x24 font set with text usage (VT320)"); - _testGetSet->_expectedCellSize = { 15, 12 }; + expectedCellSize = { 15, 12 }; const auto bitmapOf12x12 = L"????????????/????????????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size80x24, FontUsage::Text, bitmapOf12x12)); Log::Comment(L"9x24 bitmap for 80x24 font set with text usage (VT340)"); - _testGetSet->_expectedCellSize = { 10, 20 }; + expectedCellSize = { 10, 20 }; const auto bitmapOf9x24 = L"?????????/?????????/?????????/?????????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size80x24, FontUsage::Text, bitmapOf9x24)); Log::Comment(L"10x30 bitmap for 80x24 font set with text usage (VT382)"); - _testGetSet->_expectedCellSize = { 12, 30 }; + expectedCellSize = { 12, 30 }; const auto bitmapOf10x30 = L"??????????/??????????/??????????/??????????/??????????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size80x24, FontUsage::Text, bitmapOf10x30)); Log::Comment(L"8x18 bitmap for 80x24 font set with text usage (VT420/VT5xx)"); - _testGetSet->_expectedCellSize = { 10, 16 }; + expectedCellSize = { 10, 16 }; const auto bitmapOf8x18 = L"????????/????????/????????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size80x24, FontUsage::Text, bitmapOf8x18)); Log::Comment(L"5x12 bitmap for 132x24 font set with text usage (VT240)"); - _testGetSet->_expectedCellSize = { 6, 10 }; + expectedCellSize = { 6, 10 }; const auto bitmapOf5x12 = L"?????/?????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size132x24, FontUsage::Text, bitmapOf5x12)); Log::Comment(L"7x12 bitmap for 132x24 font set with text usage (VT320)"); - _testGetSet->_expectedCellSize = { 9, 12 }; + expectedCellSize = { 9, 12 }; const auto bitmapOf7x12 = L"???????/???????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size132x24, FontUsage::Text, bitmapOf7x12)); Log::Comment(L"5x24 bitmap for 132x24 font set with text usage (VT340)"); - _testGetSet->_expectedCellSize = { 6, 20 }; + expectedCellSize = { 6, 20 }; const auto bitmapOf5x24 = L"?????/?????/?????/?????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size132x24, FontUsage::Text, bitmapOf5x24)); Log::Comment(L"6x30 bitmap for 132x24 font set with text usage (VT382)"); - _testGetSet->_expectedCellSize = { 7, 30 }; + expectedCellSize = { 7, 30 }; const auto bitmapOf6x30 = L"??????/??????/??????/??????/??????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size132x24, FontUsage::Text, bitmapOf6x30)); Log::Comment(L"5x18 bitmap for 132x24 font set with text usage (VT420/VT5xx)"); - _testGetSet->_expectedCellSize = { 6, 16 }; + expectedCellSize = { 6, 16 }; const auto bitmapOf5x18 = L"?????/?????/?????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size132x24, FontUsage::Text, bitmapOf5x18)); Log::Comment(L"15x12 bitmap for 80x24 font set with full cell usage (VT320)"); - _testGetSet->_expectedCellSize = { 15, 12 }; + expectedCellSize = { 15, 12 }; const auto bitmapOf15x12 = L"???????????????/???????????????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size80x24, FontUsage::FullCell, bitmapOf15x12)); Log::Comment(L"10x24 bitmap for 80x24 font set with full cell usage (VT340)"); - _testGetSet->_expectedCellSize = { 10, 20 }; + expectedCellSize = { 10, 20 }; const auto bitmapOf10x24 = L"??????????/??????????/??????????/??????????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size80x24, FontUsage::FullCell, bitmapOf10x24)); Log::Comment(L"12x30 bitmap for 80x24 font set with full cell usage (VT382)"); - _testGetSet->_expectedCellSize = { 12, 30 }; + expectedCellSize = { 12, 30 }; const auto bitmapOf12x30 = L"????????????/????????????/????????????/????????????/????????????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size80x24, FontUsage::FullCell, bitmapOf12x30)); Log::Comment(L"10x18 bitmap for 80x24 font set with full cell usage (VT420/VT5xx)"); - _testGetSet->_expectedCellSize = { 10, 16 }; + expectedCellSize = { 10, 16 }; const auto bitmapOf10x18 = L"??????????/??????????/??????????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size80x24, FontUsage::FullCell, bitmapOf10x18)); Log::Comment(L"6x12 bitmap for 132x24 font set with full cell usage (VT240)"); - _testGetSet->_expectedCellSize = { 6, 10 }; + expectedCellSize = { 6, 10 }; const auto bitmapOf6x12 = L"??????/??????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size132x24, FontUsage::FullCell, bitmapOf6x12)); Log::Comment(L"9x12 bitmap for 132x24 font set with full cell usage (VT320)"); - _testGetSet->_expectedCellSize = { 9, 12 }; + expectedCellSize = { 9, 12 }; const auto bitmapOf9x12 = L"?????????/?????????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size132x24, FontUsage::FullCell, bitmapOf9x12)); Log::Comment(L"6x24 bitmap for 132x24 font set with full cell usage (VT340)"); - _testGetSet->_expectedCellSize = { 6, 20 }; + expectedCellSize = { 6, 20 }; const auto bitmapOf6x24 = L"??????/??????/??????/??????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size132x24, FontUsage::FullCell, bitmapOf6x24)); Log::Comment(L"7x30 bitmap for 132x24 font set with full cell usage (VT382)"); - _testGetSet->_expectedCellSize = { 7, 30 }; + expectedCellSize = { 7, 30 }; const auto bitmapOf7x30 = L"???????/???????/???????/???????/???????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size132x24, FontUsage::FullCell, bitmapOf7x30)); Log::Comment(L"6x18 bitmap for 132x24 font set with full cell usage (VT420/VT5xx)"); - _testGetSet->_expectedCellSize = { 6, 16 }; + expectedCellSize = { 6, 16 }; const auto bitmapOf6x18 = L"??????/??????/??????"; VERIFY_IS_TRUE(decdld(CellMatrix::Default, 0, FontSet::Size132x24, FontUsage::FullCell, bitmapOf6x18)); } TEST_METHOD(TogglingC1ParserMode) { + _stateMachine->SetParserMode(StateMachine::Mode::AcceptC1, false); + Log::Comment(L"1. Accept C1 controls"); - _testGetSet->_setParserModeResult = true; - _testGetSet->_expectedParserMode = StateMachine::Mode::AcceptC1; - _testGetSet->_expectedParserModeEnabled = true; - VERIFY_IS_TRUE(_pDispatch.get()->AcceptC1Controls(true)); + VERIFY_IS_TRUE(_pDispatch->AcceptC1Controls(true)); + VERIFY_IS_TRUE(_stateMachine->GetParserMode(StateMachine::Mode::AcceptC1)); Log::Comment(L"2. Don't accept C1 controls"); - _testGetSet->_setParserModeResult = true; - _testGetSet->_expectedParserMode = StateMachine::Mode::AcceptC1; - _testGetSet->_expectedParserModeEnabled = false; - VERIFY_IS_TRUE(_pDispatch.get()->AcceptC1Controls(false)); + VERIFY_IS_TRUE(_pDispatch->AcceptC1Controls(false)); + VERIFY_IS_FALSE(_stateMachine->GetParserMode(StateMachine::Mode::AcceptC1)); Log::Comment(L"3. Designate ISO-2022 coding system"); // Code page should be set to ISO-8859-1 and C1 parsing enabled _testGetSet->_setConsoleOutputCPResult = true; _testGetSet->_expectedOutputCP = 28591; - _testGetSet->_setParserModeResult = true; - _testGetSet->_expectedParserMode = StateMachine::Mode::AcceptC1; - _testGetSet->_expectedParserModeEnabled = true; - VERIFY_IS_TRUE(_pDispatch.get()->DesignateCodingSystem(DispatchTypes::CodingSystem::ISO2022)); + VERIFY_IS_TRUE(_pDispatch->DesignateCodingSystem(DispatchTypes::CodingSystem::ISO2022)); + VERIFY_IS_TRUE(_stateMachine->GetParserMode(StateMachine::Mode::AcceptC1)); Log::Comment(L"4. Designate UTF-8 coding system"); // Code page should be set to UTF-8 and C1 parsing disabled _testGetSet->_setConsoleOutputCPResult = true; _testGetSet->_expectedOutputCP = CP_UTF8; - _testGetSet->_setParserModeResult = true; - _testGetSet->_expectedParserMode = StateMachine::Mode::AcceptC1; - _testGetSet->_expectedParserModeEnabled = false; - VERIFY_IS_TRUE(_pDispatch.get()->DesignateCodingSystem(DispatchTypes::CodingSystem::UTF8)); + VERIFY_IS_TRUE(_pDispatch->DesignateCodingSystem(DispatchTypes::CodingSystem::UTF8)); + VERIFY_IS_FALSE(_stateMachine->GetParserMode(StateMachine::Mode::AcceptC1)); } private: + TerminalInput _terminalInput{ nullptr }; TestGetSet* _testGetSet; // non-ownership pointer - std::unique_ptr _pDispatch; + AdaptDispatch* _pDispatch; // non-ownership pointer + std::unique_ptr _stateMachine; }; From b510caffa3562dd1571d2a57c6bdc83392d8df7a Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sun, 20 Mar 2022 10:27:56 +0000 Subject: [PATCH 24/28] Work around audit failures in interactivity classes. --- src/interactivity/inc/IConsoleInputThread.hpp | 2 +- src/terminal/adapter/InteractDispatch.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/interactivity/inc/IConsoleInputThread.hpp b/src/interactivity/inc/IConsoleInputThread.hpp index c23d09515d1..bbc25afa041 100644 --- a/src/interactivity/inc/IConsoleInputThread.hpp +++ b/src/interactivity/inc/IConsoleInputThread.hpp @@ -34,7 +34,7 @@ namespace Microsoft::Console::Interactivity // .ctor IConsoleInputThread() : _hThread(nullptr), - _dwThreadId((DWORD)(-1)) {} + _dwThreadId(gsl::narrow_cast(-1)) {} // Protected Variables HANDLE _hThread; diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp index dbbbc500372..32a30c6f927 100644 --- a/src/terminal/adapter/InteractDispatch.cpp +++ b/src/terminal/adapter/InteractDispatch.cpp @@ -3,6 +3,12 @@ #include "precomp.h" +// Some of the interactivity classes pulled in by ServiceLocator.hpp are not +// yet audit-safe, so for now we'll need to disable a bunch of warnings. +#pragma warning(disable : 26432) +#pragma warning(disable : 26440) +#pragma warning(disable : 26455) + #include "InteractDispatch.hpp" #include "conGetSet.hpp" #include "../../host/conddkrefs.h" From 446002ed47d2e1a85f256362689435682caa5558 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sun, 10 Apr 2022 17:41:43 +0100 Subject: [PATCH 25/28] Add a helper method to set cursor movement flags. --- src/terminal/adapter/adaptDispatch.cpp | 35 +++++++++++++++----------- src/terminal/adapter/adaptDispatch.hpp | 1 + 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index f1a8957e0c9..ac8bbfbeb5a 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -261,11 +261,26 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col // Finally, attempt to set the adjusted cursor position back into the console. const COORD newPos = { gsl::narrow_cast(col), gsl::narrow_cast(row) }; cursor.SetPosition(textBuffer.ClampPositionWithinLine(newPos)); + _ApplyCursorMovementFlags(cursor); + + return true; +} + +// Routine Description: +// - Helper method which applies a bunch of flags that are typically set whenever +// the cursor is moved. The IsOn flag is set to true, and the Delay flag to false, +// to force a blinking cursor to be visible, so the user can immediately see the +// new position. The HasMoved flag is set to let the accessibility notifier know +// that there was movement that needs to be reported. +// Arguments: +// - cursor - The cursor instance to be updated +// Return Value: +// - +void AdaptDispatch::_ApplyCursorMovementFlags(Cursor& cursor) noexcept +{ cursor.SetDelay(false); cursor.SetIsOn(true); cursor.SetHasMoved(true); - - return true; } // Routine Description: @@ -1186,9 +1201,7 @@ void AdaptDispatch::_InsertDeleteLineHelper(const int32_t delta) // The IL and DL controls are also expected to move the cursor to the left margin. // For now this is just column 0, since we don't yet support DECSLRM. cursor.SetXPosition(0); - cursor.SetDelay(false); - cursor.SetIsOn(true); - cursor.SetHasMoved(true); + _ApplyCursorMovementFlags(cursor); } } @@ -1447,9 +1460,7 @@ bool AdaptDispatch::ReverseLineFeed() // Otherwise we move the cursor up, but not past the top of the viewport. const COORD newCursorPosition{ cursorPosition.X, cursorPosition.Y - 1 }; cursor.SetPosition(textBuffer.ClampPositionWithinLine(newCursorPosition)); - cursor.SetDelay(false); - cursor.SetIsOn(true); - cursor.SetHasMoved(true); + _ApplyCursorMovementFlags(cursor); } return true; } @@ -1542,9 +1553,7 @@ bool AdaptDispatch::ForwardTab(const size_t numTabs) } cursor.SetXPosition(column); - cursor.SetDelay(false); - cursor.SetIsOn(true); - cursor.SetHasMoved(true); + _ApplyCursorMovementFlags(cursor); return true; } @@ -1574,9 +1583,7 @@ bool AdaptDispatch::BackwardsTab(const size_t numTabs) } cursor.SetXPosition(column); - cursor.SetDelay(false); - cursor.SetIsOn(true); - cursor.SetHasMoved(true); + _ApplyCursorMovementFlags(cursor); return true; } diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index eff4bc2ae31..4c3b409a73a 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -168,6 +168,7 @@ namespace Microsoft::Console::VirtualTerminal std::pair _GetVerticalMargins(const til::rect viewport, const bool absolute); bool _CursorMovePosition(const Offset rowOffset, const Offset colOffset, const bool clampInMargins); + void _ApplyCursorMovementFlags(Cursor& cursor) noexcept; void _FillRect(TextBuffer& textBuffer, const til::rect fillRect, const wchar_t fillChar, const TextAttribute fillAttrs); void _EraseScrollback(); void _EraseAll(); From 64b4a0f3bbf56bf1a7d6f689011669500cdadba7 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sun, 10 Apr 2022 22:59:36 +0100 Subject: [PATCH 26/28] Add missing dependency for adapter unit tests. --- src/terminal/adapter/ut_adapter/Adapter.UnitTests.vcxproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/terminal/adapter/ut_adapter/Adapter.UnitTests.vcxproj b/src/terminal/adapter/ut_adapter/Adapter.UnitTests.vcxproj index 8ee87c5ba50..2d4b31d28c0 100644 --- a/src/terminal/adapter/ut_adapter/Adapter.UnitTests.vcxproj +++ b/src/terminal/adapter/ut_adapter/Adapter.UnitTests.vcxproj @@ -28,6 +28,9 @@ {06ec74cb-9a12-429c-b551-8562ec964846} + + {af0a096a-8b3a-4949-81ef-7df8f0fee91f} + {18d09a24-8240-42d6-8cb6-236eee820263} From 036345f5fa5af4fd52e13b9ce67b7ecc30f68d1b Mon Sep 17 00:00:00 2001 From: James Holderness Date: Wed, 13 Apr 2022 23:23:32 +0100 Subject: [PATCH 27/28] Pass til::rect by reference. --- src/host/outputStream.cpp | 2 +- src/host/outputStream.hpp | 2 +- src/terminal/adapter/adaptDispatch.cpp | 8 ++++---- src/terminal/adapter/adaptDispatch.hpp | 8 ++++---- src/terminal/adapter/conGetSet.hpp | 2 +- src/terminal/adapter/ut_adapter/adapterTest.cpp | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 9e850cb835d..ce7ec044f48 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -357,7 +357,7 @@ bool ConhostInternalGetSet::IsVtInputEnabled() const // - changedRect - the area that has changed. // Return value: // - -void ConhostInternalGetSet::NotifyAccessibilityChange(const til::rect changedRect) +void ConhostInternalGetSet::NotifyAccessibilityChange(const til::rect& changedRect) { auto& screenInfo = _io.GetActiveOutputBuffer(); if (screenInfo.HasAccessibilityEventing() && screenInfo.IsActiveScreenBuffer()) diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index c8cfaa92e53..8d1cc3c4c39 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -65,7 +65,7 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: bool IsConsolePty() const override; bool IsVtInputEnabled() const override; - void NotifyAccessibilityChange(const til::rect changedRect) override; + void NotifyAccessibilityChange(const til::rect& changedRect) override; private: Microsoft::Console::IIoProvider& _io; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index ac8bbfbeb5a..53b54f3b5ee 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -170,7 +170,7 @@ bool AdaptDispatch::CursorPrevLine(const size_t distance) // - absolute - Should coordinates be absolute or relative to the viewport. // Return Value: // - A std::pair containing the top and bottom coordinates (inclusive). -std::pair AdaptDispatch::_GetVerticalMargins(const til::rect viewport, const bool absolute) +std::pair AdaptDispatch::_GetVerticalMargins(const til::rect& viewport, const bool absolute) { // If the top is out of range, reset the margins completely. const auto bottommostRow = viewport.bottom - viewport.top - 1; @@ -444,7 +444,7 @@ bool AdaptDispatch::CursorVisibility(const bool fIsVisible) // - delta - Distance to move (positive is down, negative is up). // Return Value: // - -void AdaptDispatch::_ScrollRectVertically(TextBuffer& textBuffer, const til::rect scrollRect, const int32_t delta) +void AdaptDispatch::_ScrollRectVertically(TextBuffer& textBuffer, const til::rect& scrollRect, const int32_t delta) { const auto absoluteDelta = std::min(std::abs(delta), scrollRect.height()); if (absoluteDelta < scrollRect.height()) @@ -479,7 +479,7 @@ void AdaptDispatch::_ScrollRectVertically(TextBuffer& textBuffer, const til::rec // - delta - Distance to move (positive is right, negative is left). // Return Value: // - -void AdaptDispatch::_ScrollRectHorizontally(TextBuffer& textBuffer, const til::rect scrollRect, const int32_t delta) +void AdaptDispatch::_ScrollRectHorizontally(TextBuffer& textBuffer, const til::rect& scrollRect, const int32_t delta) { const auto absoluteDelta = std::min(std::abs(delta), scrollRect.width()); if (absoluteDelta < scrollRect.width()) @@ -563,7 +563,7 @@ bool AdaptDispatch::DeleteCharacter(const size_t count) // - fillAttrs - Attributes to be written to the buffer. // Return Value: // - -void AdaptDispatch::_FillRect(TextBuffer& textBuffer, const til::rect fillRect, const wchar_t fillChar, const TextAttribute fillAttrs) +void AdaptDispatch::_FillRect(TextBuffer& textBuffer, const til::rect& fillRect, const wchar_t fillChar, const TextAttribute fillAttrs) { if (fillRect.left < fillRect.right && fillRect.top < fillRect.bottom) { diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 4c3b409a73a..89bc2eb3647 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -166,14 +166,14 @@ namespace Microsoft::Console::VirtualTerminal static constexpr Offset Unchanged() { return Forward(0); }; }; - std::pair _GetVerticalMargins(const til::rect viewport, const bool absolute); + std::pair _GetVerticalMargins(const til::rect& viewport, const bool absolute); bool _CursorMovePosition(const Offset rowOffset, const Offset colOffset, const bool clampInMargins); void _ApplyCursorMovementFlags(Cursor& cursor) noexcept; - void _FillRect(TextBuffer& textBuffer, const til::rect fillRect, const wchar_t fillChar, const TextAttribute fillAttrs); + void _FillRect(TextBuffer& textBuffer, const til::rect& fillRect, const wchar_t fillChar, const TextAttribute fillAttrs); void _EraseScrollback(); void _EraseAll(); - void _ScrollRectVertically(TextBuffer& textBuffer, const til::rect scrollRect, const int32_t delta); - void _ScrollRectHorizontally(TextBuffer& textBuffer, const til::rect scrollRect, const int32_t delta); + void _ScrollRectVertically(TextBuffer& textBuffer, const til::rect& scrollRect, const int32_t delta); + void _ScrollRectHorizontally(TextBuffer& textBuffer, const til::rect& scrollRect, const int32_t delta); void _InsertDeleteCharacterHelper(const int32_t delta); void _InsertDeleteLineHelper(const int32_t delta); void _ScrollMovement(const int32_t delta); diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 0804ea0273d..dd1359fb0ba 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -65,6 +65,6 @@ namespace Microsoft::Console::VirtualTerminal virtual bool ResizeWindow(const size_t width, const size_t height) = 0; virtual bool IsConsolePty() const = 0; - virtual void NotifyAccessibilityChange(const til::rect changedRect) = 0; + virtual void NotifyAccessibilityChange(const til::rect& changedRect) = 0; }; } diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index 3db978db6c8..1529ff490e0 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -206,7 +206,7 @@ class TestGetSet final : public ConGetSet return _isPty; } - void NotifyAccessibilityChange(const til::rect /*changedRect*/) override + void NotifyAccessibilityChange(const til::rect& /*changedRect*/) override { Log::Comment(L"NotifyAccessibilityChange MOCK called..."); } From 79d6a5dea237e9e68d168f459d9eff429a0999f5 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Wed, 13 Apr 2022 23:26:17 +0100 Subject: [PATCH 28/28] Remove unnecessary IsActiveScreenBuffer test. --- src/host/outputStream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index ce7ec044f48..b5cbe08238a 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -360,7 +360,7 @@ bool ConhostInternalGetSet::IsVtInputEnabled() const void ConhostInternalGetSet::NotifyAccessibilityChange(const til::rect& changedRect) { auto& screenInfo = _io.GetActiveOutputBuffer(); - if (screenInfo.HasAccessibilityEventing() && screenInfo.IsActiveScreenBuffer()) + if (screenInfo.HasAccessibilityEventing()) { screenInfo.NotifyAccessibilityEventing( gsl::narrow_cast(changedRect.left),