From 5ae9884031994f281e254dcb3b3f1d4dbfe1f567 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 16 Jan 2024 13:27:13 +0000 Subject: [PATCH 01/19] Hook up the paging escape sequences. --- src/terminal/adapter/ITermDispatch.hpp | 5 ++ src/terminal/adapter/adaptDispatch.cpp | 60 +++++++++++++++++++ src/terminal/adapter/adaptDispatch.hpp | 5 ++ src/terminal/adapter/termDispatch.hpp | 5 ++ .../parser/OutputStateMachineEngine.cpp | 15 +++++ .../parser/OutputStateMachineEngine.hpp | 5 ++ 6 files changed, 95 insertions(+) diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index d0c96b6949e..13df0d4888b 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -49,6 +49,11 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch virtual bool DeleteCharacter(const VTInt count) = 0; // DCH virtual bool ScrollUp(const VTInt distance) = 0; // SU virtual bool ScrollDown(const VTInt distance) = 0; // SD + virtual bool NextPage(const VTInt pageCount) = 0; // NP + virtual bool PrecedingPage(const VTInt pageCount) = 0; // PP + virtual bool PagePositionAbsolute(const VTInt page) = 0; // PPA + virtual bool PagePositionRelative(const VTInt pageCount) = 0; // PPR + virtual bool PagePositionBack(const VTInt pageCount) = 0; // PPB virtual bool InsertLine(const VTInt distance) = 0; // IL virtual bool DeleteLine(const VTInt distance) = 0; // DL virtual bool InsertColumn(const VTInt distance) = 0; // DECIC diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index dfe540936d0..81e4515ad8b 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1753,6 +1753,66 @@ bool AdaptDispatch::ScrollDown(const VTInt uiDistance) return true; } +// Routine Description: +// - NP - Moves the active position one or more pages ahead, and moves the +// cursor to home. +// Arguments: +// - pageCount - Number of pages to move +// Return Value: +// - True. +bool AdaptDispatch::NextPage(const VTInt /*pageCount*/) +{ + return true; +} + +// Routine Description: +// - PP - Moves the active position one or more pages back, and moves the +// cursor to home. +// Arguments: +// - pageCount - Number of pages to move +// Return Value: +// - True. +bool AdaptDispatch::PrecedingPage(const VTInt /*pageCount*/) +{ + return true; +} + +// Routine Description: +// - PPA - Moves the active position to the specified page number, without +// altering the cursor coordinates. +// Arguments: +// - page - Destination page +// Return Value: +// - True. +bool AdaptDispatch::PagePositionAbsolute(const VTInt /*page*/) +{ + return true; +} + +// Routine Description: +// - PPR - Moves the active position one or more pages ahead, without altering +// the cursor coordinates. +// Arguments: +// - pageCount - Number of pages to move +// Return Value: +// - True. +bool AdaptDispatch::PagePositionRelative(const VTInt /*pageCount*/) +{ + return true; +} + +// Routine Description: +// - PPB - Moves the active position one or more pages back, without altering +// the cursor coordinates. +// Arguments: +// - pageCount - Number of pages to move +// Return Value: +// - True. +bool AdaptDispatch::PagePositionBack(const VTInt /*pageCount*/) +{ + return true; +} + // Routine Description: // - DECCOLM not only sets the number of columns, but also clears the screen buffer, // resets the page margins and origin mode, and places the cursor at 1,1 diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 9285d1126b7..5d77b9090b3 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -81,6 +81,11 @@ namespace Microsoft::Console::VirtualTerminal bool RequestTerminalParameters(const DispatchTypes::ReportingPermission permission) override; // DECREQTPARM bool ScrollUp(const VTInt distance) override; // SU bool ScrollDown(const VTInt distance) override; // SD + bool NextPage(const VTInt pageCount) override; // NP + bool PrecedingPage(const VTInt pageCount) override; // PP + bool PagePositionAbsolute(const VTInt page) override; // PPA + bool PagePositionRelative(const VTInt pageCount) override; // PPR + bool PagePositionBack(const VTInt pageCount) override; // PPB bool InsertLine(const VTInt distance) override; // IL bool DeleteLine(const VTInt distance) override; // DL bool InsertColumn(const VTInt distance) override; // DECIC diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp index e2b8f298329..a4c648616ce 100644 --- a/src/terminal/adapter/termDispatch.hpp +++ b/src/terminal/adapter/termDispatch.hpp @@ -42,6 +42,11 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons bool DeleteCharacter(const VTInt /*count*/) override { return false; } // DCH bool ScrollUp(const VTInt /*distance*/) override { return false; } // SU bool ScrollDown(const VTInt /*distance*/) override { return false; } // SD + bool NextPage(const VTInt /*pageCount*/) override { return false; } // NP + bool PrecedingPage(const VTInt /*pageCount*/) override { return false; } // PP + bool PagePositionAbsolute(const VTInt /*page*/) override { return false; } // PPA + bool PagePositionRelative(const VTInt /*pageCount*/) override { return false; } // PPR + bool PagePositionBack(const VTInt /*pageCount*/) override { return false; } // PPB bool InsertLine(const VTInt /*distance*/) override { return false; } // IL bool DeleteLine(const VTInt /*distance*/) override { return false; } // DL bool InsertColumn(const VTInt /*distance*/) override { return false; } // DECIC diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index 34d40fc850d..ccfe699d746 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -555,6 +555,12 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParamete case CsiActionCodes::SD_ScrollDown: success = _dispatch->ScrollDown(parameters.at(0)); break; + case CsiActionCodes::NP_NextPage: + success = _dispatch->NextPage(parameters.at(0)); + break; + case CsiActionCodes::PP_PrecedingPage: + success = _dispatch->PrecedingPage(parameters.at(0)); + break; case CsiActionCodes::ANSISYSRC_CursorRestore: success = _dispatch->CursorRestoreState(); break; @@ -600,6 +606,15 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParamete } success = true; break; + case CsiActionCodes::PPA_PagePositionAbsolute: + success = _dispatch->PagePositionAbsolute(parameters.at(0)); + break; + case CsiActionCodes::PPR_PagePositionRelative: + success = _dispatch->PagePositionRelative(parameters.at(0)); + break; + case CsiActionCodes::PPB_PagePositionBack: + success = _dispatch->PagePositionBack(parameters.at(0)); + break; case CsiActionCodes::DECSCUSR_SetCursorStyle: success = _dispatch->SetCursorStyle(parameters.at(0)); break; diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index 1ff22ab5a99..852d1514437 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -122,6 +122,8 @@ namespace Microsoft::Console::VirtualTerminal DCH_DeleteCharacter = VTID("P"), SU_ScrollUp = VTID("S"), SD_ScrollDown = VTID("T"), + NP_NextPage = VTID("U"), + PP_PrecedingPage = VTID("V"), DECST8C_SetTabEvery8Columns = VTID("?W"), ECH_EraseCharacters = VTID("X"), CBT_CursorBackTab = VTID("Z"), @@ -147,6 +149,9 @@ namespace Microsoft::Console::VirtualTerminal DTTERM_WindowManipulation = VTID("t"), // NOTE: Overlaps with DECSLPP. Fix when/if implemented. ANSISYSRC_CursorRestore = VTID("u"), DECREQTPARM_RequestTerminalParameters = VTID("x"), + PPA_PagePositionAbsolute = VTID(" P"), + PPR_PagePositionRelative = VTID(" Q"), + PPB_PagePositionBack = VTID(" R"), DECSCUSR_SetCursorStyle = VTID(" q"), DECSTR_SoftReset = VTID("!p"), DECSCA_SetCharacterProtectionAttribute = VTID("\"q"), From 6b43e44db9158f0d0ed926e124ad81b5b387a738 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sat, 20 Jan 2024 20:21:13 +0000 Subject: [PATCH 02/19] Add api returning both buffer and viewport. --- src/cascadia/TerminalCore/Terminal.hpp | 1 + src/cascadia/TerminalCore/TerminalApi.cpp | 5 +++++ src/host/outputStream.cpp | 13 +++++++++++++ src/host/outputStream.hpp | 1 + src/terminal/adapter/ITerminalApi.hpp | 1 + src/terminal/adapter/ut_adapter/adapterTest.cpp | 6 ++++++ 6 files changed, 27 insertions(+) diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 80c5e3cc905..75268719c79 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -131,6 +131,7 @@ class Microsoft::Terminal::Core::Terminal final : // These methods are defined in TerminalApi.cpp void ReturnResponse(const std::wstring_view response) override; Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() noexcept override; + std::tuple GetBufferAndViewport() noexcept override; TextBuffer& GetTextBuffer() noexcept override; til::rect GetViewport() const noexcept override; void SetViewportPosition(const til::point position) noexcept override; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 9a5ea2221ac..5178981e40f 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -33,6 +33,11 @@ Microsoft::Console::VirtualTerminal::StateMachine& Terminal::GetStateMachine() n return *_stateMachine; } +std::tuple Terminal::GetBufferAndViewport() noexcept +{ + return { _activeBuffer(), til::rect{ _GetMutableViewport().ToInclusive() }, !_inAltBuffer() }; +} + TextBuffer& Terminal::GetTextBuffer() noexcept { return _activeBuffer(); diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 70b270e3cd8..91e8d1c08ff 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -51,6 +51,19 @@ StateMachine& ConhostInternalGetSet::GetStateMachine() return _io.GetActiveOutputBuffer().GetStateMachine(); } +// Routine Description: +// - Retrieves the text buffer and virtual viewport for the active output +// buffer. Also returns a flag indicating whether it's the main buffer. +// Arguments: +// - +// Return Value: +// - a tuple with the buffer reference, viewport, and main buffer flag. +std::tuple ConhostInternalGetSet::GetBufferAndViewport() +{ + auto& info = _io.GetActiveOutputBuffer(); + return { info.GetTextBuffer(), info.GetVirtualViewport().ToExclusive(), info.Next == nullptr }; +} + // Routine Description: // - Retrieves the text buffer for the active output buffer. // Arguments: diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 45ea6052e21..7aaad569196 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -32,6 +32,7 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void ReturnResponse(const std::wstring_view response) override; Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() override; + std::tuple GetBufferAndViewport() override; TextBuffer& GetTextBuffer() override; til::rect GetViewport() const override; void SetViewportPosition(const til::point position) override; diff --git a/src/terminal/adapter/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index 3375a968b04..c30559ac6a8 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -40,6 +40,7 @@ namespace Microsoft::Console::VirtualTerminal virtual void ReturnResponse(const std::wstring_view response) = 0; virtual StateMachine& GetStateMachine() = 0; + virtual std::tuple GetBufferAndViewport() = 0; virtual TextBuffer& GetTextBuffer() = 0; virtual til::rect GetViewport() const = 0; virtual void SetViewportPosition(const til::point position) = 0; diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index da8b7a35d3a..ebb37e95bb4 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -81,6 +81,12 @@ class TestGetSet final : public ITerminalApi return *_stateMachine; } + std::tuple GetBufferAndViewport() override + { + const auto viewport = til::rect{ _viewport.left, _viewport.top, _viewport.right, _viewport.bottom }; + return { *_textBuffer.get(), viewport, true }; + } + TextBuffer& GetTextBuffer() override { return *_textBuffer.get(); From 8c1fd6661074f06740d0439262702748cce6ae5e Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 25 Jan 2024 01:06:19 +0000 Subject: [PATCH 03/19] Add class for managing VT page buffers. --- src/terminal/adapter/PageManager.cpp | 166 ++++++++++++++++++ src/terminal/adapter/PageManager.hpp | 56 ++++++ src/terminal/adapter/lib/adapter.vcxproj | 2 + .../adapter/lib/adapter.vcxproj.filters | 6 + src/terminal/adapter/sources.inc | 1 + 5 files changed, 231 insertions(+) create mode 100644 src/terminal/adapter/PageManager.cpp create mode 100644 src/terminal/adapter/PageManager.hpp diff --git a/src/terminal/adapter/PageManager.cpp b/src/terminal/adapter/PageManager.cpp new file mode 100644 index 00000000000..012db36a38c --- /dev/null +++ b/src/terminal/adapter/PageManager.cpp @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" + +#include "PageManager.hpp" +#include "../../renderer/base/renderer.hpp" + +using namespace Microsoft::Console::VirtualTerminal; + +Page::Page(TextBuffer& buffer, const til::rect& viewport, const til::CoordType number) noexcept : + _buffer{ buffer }, + _viewport{ viewport }, + _number(number) +{ +} + +TextBuffer& Page::Buffer() const noexcept +{ + return _buffer; +} + +til::rect Page::Viewport() const noexcept +{ + return _viewport; +} + +til::CoordType Page::Number() const noexcept +{ + return _number; +} + +PageManager::PageManager(ITerminalApi& api, Renderer& renderer) noexcept : + _api{ api }, + _renderer{ renderer } +{ +} + +void PageManager::Reset() +{ + _activePageNumber = 1; + _visiblePageNumber = 1; + _buffers = {}; +} + +Page PageManager::Get(const til::CoordType pageNumber) const +{ + const auto requestedPageNumber = std::min(std::max(pageNumber, 1), MAX_PAGES); + auto [visibleBuffer, visibleViewport, isMainBuffer] = _api.GetBufferAndViewport(); + if (!isMainBuffer) + { + return { visibleBuffer, visibleViewport, 1 }; + } + else if (requestedPageNumber == _visiblePageNumber) + { + return { visibleBuffer, visibleViewport, _visiblePageNumber }; + } + else + { + const auto pageSize = visibleViewport.size(); + auto& pageBuffer = _getBuffer(requestedPageNumber, pageSize); + return { pageBuffer, til::rect{ pageSize }, requestedPageNumber }; + } +} + +Page PageManager::ActivePage() const +{ + return Get(_activePageNumber); +} + +Page PageManager::VisiblePage() const +{ + return Get(_visiblePageNumber); +} + +void PageManager::MoveTo(const til::CoordType pageNumber) +{ + auto [visibleBuffer, visibleViewport, isMainBuffer] = _api.GetBufferAndViewport(); + if (!isMainBuffer) + { + return; + } + + const auto pageSize = visibleViewport.size(); + const auto visibleTop = visibleViewport.top; + const auto wasVisible = _activePageNumber == _visiblePageNumber; + const auto newPageNumber = std::min(std::max(pageNumber, 1), MAX_PAGES); + auto redrawRequired = false; + + // If we're changing the visible page, what we do is swap out the current + // visible page into its backing buffer, and swap in the new page from the + // backing buffer to the main buffer. That way the rest of the system only + // ever has to deal with the main buffer. + if (_visiblePageNumber != newPageNumber) + { + const auto& newBuffer = _getBuffer(newPageNumber, pageSize); + auto& saveBuffer = _getBuffer(_visiblePageNumber, pageSize); + for (auto i = 0; i < pageSize.height; i++) + { + saveBuffer.GetMutableRowByOffset(i).CopyFrom(visibleBuffer.GetRowByOffset(visibleTop + i)); + } + for (auto i = 0; i < pageSize.height; i++) + { + visibleBuffer.GetMutableRowByOffset(visibleTop + i).CopyFrom(newBuffer.GetRowByOffset(i)); + } + _visiblePageNumber = newPageNumber; + redrawRequired = true; + } + + // If the active page was previously visible, and is now still visible, + // there is no need to update any buffer properties, because we'll have + // been using the main buffer in both cases. + const auto isVisible = newPageNumber == _visiblePageNumber; + if (!wasVisible || !isVisible) + { + // Otherwise we need to copy the properties from the old buffer to the + // new, so we retain the current attributes and cursor position. This + // is only needed if they are actually different. + auto& oldBuffer = wasVisible ? visibleBuffer : _getBuffer(_activePageNumber, pageSize); + auto& newBuffer = isVisible ? visibleBuffer : _getBuffer(newPageNumber, pageSize); + if (&oldBuffer != &newBuffer) + { + // When copying the cursor position, we need to adjust the y + // coordinate to account for scrollback in the visible buffer. + const auto oldTop = wasVisible ? visibleTop : 0; + const auto newTop = isVisible ? visibleTop : 0; + auto position = oldBuffer.GetCursor().GetPosition(); + position.y = position.y - oldTop + newTop; + newBuffer.SetCurrentAttributes(oldBuffer.GetCurrentAttributes()); + newBuffer.CopyProperties(oldBuffer); + newBuffer.GetCursor().SetPosition(position); + } + // If we moved from the visible buffer to a background buffer + // we need to hide the cursor in the visible buffer. It should + // be restored when moving back. + if (wasVisible && !isVisible) + { + visibleBuffer.GetCursor().SetIsVisible(false); + } + } + + _activePageNumber = newPageNumber; + if (redrawRequired) + { + _renderer.TriggerRedrawAll(); + } +} + +void PageManager::MoveRelative(const til::CoordType pageCount) +{ + MoveTo(_activePageNumber + pageCount); +} + +TextBuffer& PageManager::_getBuffer(const til::CoordType pageNumber, const til::size pageSize) const +{ + auto& buffer = til::at(_buffers, pageNumber - 1); + if (buffer == nullptr) + { + buffer = std::make_unique(pageSize, TextAttribute{}, 0, false, _renderer); + } + else if (buffer->GetSize().Dimensions() != pageSize) + { + buffer->ResizeTraditional(pageSize); + } + return *buffer; +} diff --git a/src/terminal/adapter/PageManager.hpp b/src/terminal/adapter/PageManager.hpp new file mode 100644 index 00000000000..7eae6a9f8fb --- /dev/null +++ b/src/terminal/adapter/PageManager.hpp @@ -0,0 +1,56 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- PageManager.hpp + +Abstract: +- This manages the text buffers required by the VT paging operations. +--*/ + +#pragma once + +#include "ITerminalApi.hpp" +#include "til.h" + +namespace Microsoft::Console::VirtualTerminal +{ + class Page + { + public: + Page(TextBuffer& buffer, const til::rect& viewport, const til::CoordType number) noexcept; + TextBuffer& Buffer() const noexcept; + til::rect Viewport() const noexcept; + til::CoordType Number() const noexcept; + + private: + TextBuffer& _buffer; + til::rect _viewport; + til::CoordType _number; + }; + + class PageManager + { + using Renderer = Microsoft::Console::Render::Renderer; + + public: + PageManager(ITerminalApi& api, Renderer& renderer) noexcept; + void Reset(); + Page Get(const til::CoordType pageNumber) const; + Page ActivePage() const; + Page VisiblePage() const; + void MoveTo(const til::CoordType pageNumber); + void MoveRelative(const til::CoordType pageCount); + + private: + TextBuffer& _getBuffer(const til::CoordType pageNumber, const til::size pageSize) const; + + ITerminalApi& _api; + Renderer& _renderer; + til::CoordType _activePageNumber = 1; + til::CoordType _visiblePageNumber = 1; + static constexpr til::CoordType MAX_PAGES = 6; + mutable std::array, MAX_PAGES> _buffers; + }; +} diff --git a/src/terminal/adapter/lib/adapter.vcxproj b/src/terminal/adapter/lib/adapter.vcxproj index 2780c818dfa..a05c8e5b83e 100644 --- a/src/terminal/adapter/lib/adapter.vcxproj +++ b/src/terminal/adapter/lib/adapter.vcxproj @@ -15,6 +15,7 @@ + @@ -29,6 +30,7 @@ + diff --git a/src/terminal/adapter/lib/adapter.vcxproj.filters b/src/terminal/adapter/lib/adapter.vcxproj.filters index 798522c651f..501f6ce9bdc 100644 --- a/src/terminal/adapter/lib/adapter.vcxproj.filters +++ b/src/terminal/adapter/lib/adapter.vcxproj.filters @@ -36,6 +36,9 @@ Source Files + + Source Files + @@ -74,6 +77,9 @@ Header Files + + Header Files + diff --git a/src/terminal/adapter/sources.inc b/src/terminal/adapter/sources.inc index a7a07a29814..3ffd6bed7c7 100644 --- a/src/terminal/adapter/sources.inc +++ b/src/terminal/adapter/sources.inc @@ -34,6 +34,7 @@ SOURCES= \ ..\FontBuffer.cpp \ ..\InteractDispatch.cpp \ ..\MacroBuffer.cpp \ + ..\PageManager.cpp \ ..\adaptDispatchGraphics.cpp \ ..\terminalOutput.cpp \ From af6fd191322cb8d4f28134b9861eab2f86c3419e Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 25 Jan 2024 10:42:22 +0000 Subject: [PATCH 04/19] Integrate page manager into AdaptDispatch. --- src/terminal/adapter/adaptDispatch.cpp | 302 +++++++++++------- src/terminal/adapter/adaptDispatch.hpp | 7 +- .../adapter/adaptDispatchGraphics.cpp | 8 +- 3 files changed, 187 insertions(+), 130 deletions(-) diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 81e4515ad8b..4fee8348b3d 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -23,7 +23,8 @@ AdaptDispatch::AdaptDispatch(ITerminalApi& api, Renderer& renderer, RenderSettin _renderSettings{ renderSettings }, _terminalInput{ terminalInput }, _usingAltBuffer(false), - _termOutput() + _termOutput(), + _pages{ api, renderer } { } @@ -73,13 +74,14 @@ void AdaptDispatch::PrintString(const std::wstring_view string) void AdaptDispatch::_WriteToBuffer(const std::wstring_view string) { - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + auto& textBuffer = page.Buffer(); auto& cursor = textBuffer.GetCursor(); auto cursorPosition = cursor.GetPosition(); const auto wrapAtEOL = _api.GetSystemMode(ITerminalApi::Mode::AutoWrap); const auto& attributes = textBuffer.GetCurrentAttributes(); - const auto viewport = _api.GetViewport(); + const auto viewport = page.Viewport(); const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(textBuffer.GetSize().Width()); @@ -107,7 +109,7 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string) // different position from where the EOL was marked. if (delayedCursorPosition == cursorPosition) { - _DoLineFeed(textBuffer, true, true); + _DoLineFeed(page, true, true); cursorPosition = cursor.GetPosition(); // We need to recalculate the width when moving to a new line. lineWidth = textBuffer.GetLineWidth(cursorPosition.y); @@ -336,8 +338,9 @@ std::pair AdaptDispatch::_GetHorizontalMargins(const til::CoordType bu bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset colOffset, const bool clampInMargins) { // First retrieve some information about the buffer - const auto viewport = _api.GetViewport(); - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + auto& textBuffer = page.Buffer(); auto& cursor = textBuffer.GetCursor(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto cursorPosition = cursor.GetPosition(); @@ -500,8 +503,9 @@ bool AdaptDispatch::CursorPosition(const VTInt line, const VTInt column) bool AdaptDispatch::CursorSaveState() { // First retrieve some information about the buffer - const auto viewport = _api.GetViewport(); - const auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + const auto& textBuffer = page.Buffer(); const auto& attributes = textBuffer.GetCurrentAttributes(); // The cursor is given to us by the API as relative to the whole buffer. @@ -520,6 +524,7 @@ bool AdaptDispatch::CursorSaveState() auto& savedCursorState = _savedCursorState.at(_usingAltBuffer); savedCursorState.Column = cursorPosition.x + 1; savedCursorState.Row = cursorPosition.y + 1; + savedCursorState.Page = page.Number(); savedCursorState.IsDelayedEOLWrap = textBuffer.GetCursor().IsDelayedEOLWrap(); savedCursorState.IsOriginModeRelative = _modes.test(Mode::Origin); savedCursorState.Attributes = attributes; @@ -543,13 +548,16 @@ bool AdaptDispatch::CursorRestoreState() // Restore the origin mode first, since the cursor coordinates may be relative. _modes.set(Mode::Origin, savedCursorState.IsOriginModeRelative); + // Restore the page number. + PagePositionAbsolute(savedCursorState.Page); + // We can then restore the position with a standard CUP operation. CursorPosition(savedCursorState.Row, savedCursorState.Column); // If the delayed wrap flag was set when the cursor was saved, we need to restore that now. if (savedCursorState.IsDelayedEOLWrap) { - _api.GetTextBuffer().GetCursor().DelayEOLWrap(); + _pages.ActivePage().Buffer().GetCursor().DelayEOLWrap(); } // Restore text attributes. @@ -692,8 +700,9 @@ void AdaptDispatch::_ScrollRectHorizontally(TextBuffer& textBuffer, const til::r // - void AdaptDispatch::_InsertDeleteCharacterHelper(const VTInt delta) { - const auto viewport = _api.GetViewport(); - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + auto& textBuffer = page.Buffer(); const auto row = textBuffer.GetCursor().GetPosition().y; const auto col = textBuffer.GetCursor().GetPosition().x; const auto lineWidth = textBuffer.GetLineWidth(row); @@ -761,7 +770,8 @@ void AdaptDispatch::_FillRect(TextBuffer& textBuffer, const til::rect& fillRect, // - True. bool AdaptDispatch::EraseCharacters(const VTInt numChars) { - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + auto& textBuffer = page.Buffer(); const auto row = textBuffer.GetCursor().GetPosition().y; const auto startCol = textBuffer.GetCursor().GetPosition().x; const auto endCol = std::min(startCol + numChars, textBuffer.GetLineWidth(row)); @@ -803,8 +813,9 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) return _EraseAll(); } - const auto viewport = _api.GetViewport(); - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto row = textBuffer.GetCursor().GetPosition().y; const auto col = textBuffer.GetCursor().GetPosition().x; @@ -846,7 +857,8 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) // - True if handled successfully. False otherwise. bool AdaptDispatch::EraseInLine(const DispatchTypes::EraseType eraseType) { - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + auto& textBuffer = page.Buffer(); const auto row = textBuffer.GetCursor().GetPosition().y; const auto col = textBuffer.GetCursor().GetPosition().x; @@ -910,8 +922,9 @@ void AdaptDispatch::_SelectiveEraseRect(TextBuffer& textBuffer, const til::rect& // - True if handled successfully. False otherwise. bool AdaptDispatch::SelectiveEraseInDisplay(const DispatchTypes::EraseType eraseType) { - const auto viewport = _api.GetViewport(); - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto row = textBuffer.GetCursor().GetPosition().y; const auto col = textBuffer.GetCursor().GetPosition().x; @@ -948,7 +961,8 @@ bool AdaptDispatch::SelectiveEraseInDisplay(const DispatchTypes::EraseType erase // - True if handled successfully. False otherwise. bool AdaptDispatch::SelectiveEraseInLine(const DispatchTypes::EraseType eraseType) { - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + auto& textBuffer = page.Buffer(); const auto row = textBuffer.GetCursor().GetPosition().y; const auto col = textBuffer.GetCursor().GetPosition().x; @@ -1024,9 +1038,10 @@ void AdaptDispatch::_ChangeRectAttributes(TextBuffer& textBuffer, const til::rec // - void AdaptDispatch::_ChangeRectOrStreamAttributes(const til::rect& changeArea, const ChangeOps& changeOps) { - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + auto& textBuffer = page.Buffer(); const auto bufferSize = textBuffer.GetSize().Dimensions(); - const auto changeRect = _CalculateRectArea(changeArea.top, changeArea.left, changeArea.bottom, changeArea.right, bufferSize); + const auto changeRect = _CalculateRectArea(page, changeArea.top, changeArea.left, changeArea.bottom, changeArea.right); const auto lineCount = changeRect.height(); // If the change extent is rectangular, we can apply the change with a @@ -1053,16 +1068,17 @@ void AdaptDispatch::_ChangeRectOrStreamAttributes(const til::rect& changeArea, c // - Helper method to calculate the applicable buffer coordinates for use with // the various rectangular area operations. // Arguments: +// - page - The target page. // - top - The first row of the area. // - left - The first column of the area. // - bottom - The last row of the area (inclusive). // - right - The last column of the area (inclusive). -// - bufferSize - The size of the target buffer. // Return value: // - An exclusive rect with the absolute buffer coordinates. -til::rect AdaptDispatch::_CalculateRectArea(const VTInt top, const VTInt left, const VTInt bottom, const VTInt right, const til::size bufferSize) +til::rect AdaptDispatch::_CalculateRectArea(const Page& page, const VTInt top, const VTInt left, const VTInt bottom, const VTInt right) { - const auto viewport = _api.GetViewport(); + const auto bufferSize = page.Buffer().GetSize().Dimensions(); + const auto viewport = page.Viewport(); // We start by calculating the margin offsets and maximum dimensions. // If the origin mode isn't set, we use the viewport extent. @@ -1208,25 +1224,22 @@ bool AdaptDispatch::ReverseAttributesRectangularArea(const VTInt top, const VTIn // - left - The first column of the source area. // - bottom - The last row of the source area (inclusive). // - right - The last column of the source area (inclusive). -// - page - The source page number (unused for now). +// - page - The source page number. // - dstTop - The first row of the destination. // - dstLeft - The first column of the destination. -// - dstPage - The destination page number (unused for now). +// - dstPage - The destination page number. // Return Value: // - True. -bool AdaptDispatch::CopyRectangularArea(const VTInt top, const VTInt left, const VTInt bottom, const VTInt right, const VTInt /*page*/, const VTInt dstTop, const VTInt dstLeft, const VTInt /*dstPage*/) +bool AdaptDispatch::CopyRectangularArea(const VTInt top, const VTInt left, const VTInt bottom, const VTInt right, const VTInt page, const VTInt dstTop, const VTInt dstLeft, const VTInt dstPage) { - // GH#13892 We don't yet support the paging extension, so for now we ignore - // the page parameters. This is the same as if the maximum page count was 1. - - auto& textBuffer = _api.GetTextBuffer(); - const auto bufferSize = textBuffer.GetSize().Dimensions(); - const auto srcRect = _CalculateRectArea(top, left, bottom, right, bufferSize); + const auto src = _pages.Get(page); + const auto dst = _pages.Get(dstPage); + const auto srcRect = _CalculateRectArea(src, top, left, bottom, right); const auto dstBottom = dstTop + srcRect.height() - 1; const auto dstRight = dstLeft + srcRect.width() - 1; - const auto dstRect = _CalculateRectArea(dstTop, dstLeft, dstBottom, dstRight, bufferSize); + const auto dstRect = _CalculateRectArea(dst, dstTop, dstLeft, dstBottom, dstRight); - if (dstRect && dstRect.origin() != srcRect.origin()) + if (dstRect && (dstRect.origin() != srcRect.origin() || src.Number() != dst.Number())) { // If the source is bigger than the available space at the destination // it needs to be clipped, so we only care about the destination size. @@ -1238,18 +1251,18 @@ bool AdaptDispatch::CopyRectangularArea(const VTInt top, const VTInt left, const // Note that we read two cells from the source before we start writing // to the target, so a two-cell DBCS character can't accidentally delete // itself when moving one cell horizontally. - auto next = OutputCell(*textBuffer.GetCellDataAt(srcPos)); + auto next = OutputCell(*src.Buffer().GetCellDataAt(srcPos)); do { const auto current = next; const auto currentSrcPos = srcPos; srcView.WalkInBounds(srcPos, walkDirection); - next = OutputCell(*textBuffer.GetCellDataAt(srcPos)); + next = OutputCell(*src.Buffer().GetCellDataAt(srcPos)); // If the source position is offscreen (which can occur on double // width lines), then we shouldn't copy anything to the destination. - if (currentSrcPos.x < textBuffer.GetLineWidth(currentSrcPos.y)) + if (currentSrcPos.x < src.Buffer().GetLineWidth(currentSrcPos.y)) { - textBuffer.WriteLine(OutputCellIterator({ ¤t, 1 }), dstPos); + dst.Buffer().WriteLine(OutputCellIterator({ ¤t, 1 }), dstPos); } } while (dstView.WalkInBounds(dstPos, walkDirection)); _api.NotifyAccessibilityChange(dstRect); @@ -1271,8 +1284,9 @@ bool AdaptDispatch::CopyRectangularArea(const VTInt top, const VTInt left, const // - True. bool AdaptDispatch::FillRectangularArea(const VTParameter ch, const VTInt top, const VTInt left, const VTInt bottom, const VTInt right) { - auto& textBuffer = _api.GetTextBuffer(); - const auto fillRect = _CalculateRectArea(top, left, bottom, right, textBuffer.GetSize().Dimensions()); + const auto page = _pages.ActivePage(); + auto& textBuffer = page.Buffer(); + const auto fillRect = _CalculateRectArea(page, top, left, bottom, right); // The standard only allows for characters in the range of the GL and GR // character set tables, but we also support additional Unicode characters @@ -1303,8 +1317,9 @@ bool AdaptDispatch::FillRectangularArea(const VTParameter ch, const VTInt top, c // - True. bool AdaptDispatch::EraseRectangularArea(const VTInt top, const VTInt left, const VTInt bottom, const VTInt right) { - auto& textBuffer = _api.GetTextBuffer(); - const auto eraseRect = _CalculateRectArea(top, left, bottom, right, textBuffer.GetSize().Dimensions()); + const auto page = _pages.ActivePage(); + auto& textBuffer = page.Buffer(); + const auto eraseRect = _CalculateRectArea(page, top, left, bottom, right); const auto eraseAttributes = _GetEraseAttributes(textBuffer); _FillRect(textBuffer, eraseRect, whitespace, eraseAttributes); return true; @@ -1322,8 +1337,9 @@ bool AdaptDispatch::EraseRectangularArea(const VTInt top, const VTInt left, cons // - True. bool AdaptDispatch::SelectiveEraseRectangularArea(const VTInt top, const VTInt left, const VTInt bottom, const VTInt right) { - auto& textBuffer = _api.GetTextBuffer(); - const auto eraseRect = _CalculateRectArea(top, left, bottom, right, textBuffer.GetSize().Dimensions()); + const auto page = _pages.ActivePage(); + auto& textBuffer = page.Buffer(); + const auto eraseRect = _CalculateRectArea(page, top, left, bottom, right); _SelectiveEraseRect(textBuffer, eraseRect); return true; } @@ -1356,7 +1372,7 @@ bool AdaptDispatch::SelectAttributeChangeExtent(const DispatchTypes::ChangeExten // the buffer memory. // Arguments: // - id - a numeric label used to identify the request. -// - page - The page number (unused for now). +// - page - The page number. // - top - The first row of the area. // - left - The first column of the area. // - bottom - The last row of the area (inclusive). @@ -1369,7 +1385,9 @@ bool AdaptDispatch::RequestChecksumRectangularArea(const VTInt id, const VTInt p // If this feature is not enabled, we'll just report a zero checksum. if constexpr (Feature_VtChecksumReport::IsEnabled()) { - if (page == 1) + // If the page number is 0, then we're meant to return a checksum of all + // of the pages, but we have no need for that, so we'll just return 0. + if (page != 0) { // As part of the checksum, we need to include the color indices of each // cell, and in the case of default colors, those indices come from the @@ -1380,8 +1398,9 @@ bool AdaptDispatch::RequestChecksumRectangularArea(const VTInt id, const VTInt p defaultFgIndex = defaultFgIndex < 16 ? defaultFgIndex : 7; defaultBgIndex = defaultBgIndex < 16 ? defaultBgIndex : 0; - const auto& textBuffer = _api.GetTextBuffer(); - const auto eraseRect = _CalculateRectArea(top, left, bottom, right, textBuffer.GetSize().Dimensions()); + const auto target = _pages.Get(page); + const auto& textBuffer = target.Buffer(); + const auto eraseRect = _CalculateRectArea(target, top, left, bottom, right); for (auto row = eraseRect.top; row < eraseRect.bottom; row++) { for (auto col = eraseRect.left; col < eraseRect.right; col++) @@ -1441,7 +1460,8 @@ bool AdaptDispatch::SetLineRendition(const LineRendition rendition) // The line rendition can't be changed if left/right margins are allowed. if (!_modes.test(Mode::AllowDECSLRM)) { - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + auto& textBuffer = page.Buffer(); const auto eraseAttributes = _GetEraseAttributes(textBuffer); textBuffer.SetCurrentLineRendition(rendition, eraseAttributes); // There is some variation in how this was handled by the different DEC @@ -1648,8 +1668,9 @@ void AdaptDispatch::_DeviceStatusReport(const wchar_t* parameters) const // - void AdaptDispatch::_CursorPositionReport(const bool extendedReport) { - const auto viewport = _api.GetViewport(); - const auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + const auto& textBuffer = page.Buffer(); // First pull the cursor position relative to the entire buffer out of the console. til::point cursorPosition{ textBuffer.GetCursor().GetPosition() }; @@ -1671,9 +1692,8 @@ void AdaptDispatch::_CursorPositionReport(const bool extendedReport) // Now send it back into the input channel of the console. if (extendedReport) { - // An extended report should also include the page number, but for now - // we hard-code it to 1, since we don't yet support paging (GH#13892). - const auto pageNumber = 1; + // An extended report also includes the page number. + const auto pageNumber = page.Number(); const auto response = wil::str_printf(L"\x1b[?%d;%d;%dR", cursorPosition.y, cursorPosition.x, pageNumber); _api.ReturnResponse(response); } @@ -1721,8 +1741,9 @@ void AdaptDispatch::_MacroChecksumReport(const VTParameter id) const // - void AdaptDispatch::_ScrollMovement(const VTInt delta) { - const auto viewport = _api.GetViewport(); - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); @@ -1760,9 +1781,10 @@ bool AdaptDispatch::ScrollDown(const VTInt uiDistance) // - pageCount - Number of pages to move // Return Value: // - True. -bool AdaptDispatch::NextPage(const VTInt /*pageCount*/) +bool AdaptDispatch::NextPage(const VTInt pageCount) { - return true; + PagePositionRelative(pageCount); + return CursorPosition(1, 1); } // Routine Description: @@ -1772,9 +1794,10 @@ bool AdaptDispatch::NextPage(const VTInt /*pageCount*/) // - pageCount - Number of pages to move // Return Value: // - True. -bool AdaptDispatch::PrecedingPage(const VTInt /*pageCount*/) +bool AdaptDispatch::PrecedingPage(const VTInt pageCount) { - return true; + PagePositionBack(pageCount); + return CursorPosition(1, 1); } // Routine Description: @@ -1784,8 +1807,9 @@ bool AdaptDispatch::PrecedingPage(const VTInt /*pageCount*/) // - page - Destination page // Return Value: // - True. -bool AdaptDispatch::PagePositionAbsolute(const VTInt /*page*/) +bool AdaptDispatch::PagePositionAbsolute(const VTInt page) { + _pages.MoveTo(page); return true; } @@ -1796,8 +1820,9 @@ bool AdaptDispatch::PagePositionAbsolute(const VTInt /*page*/) // - pageCount - Number of pages to move // Return Value: // - True. -bool AdaptDispatch::PagePositionRelative(const VTInt /*pageCount*/) +bool AdaptDispatch::PagePositionRelative(const VTInt pageCount) { + _pages.MoveRelative(pageCount); return true; } @@ -1808,8 +1833,9 @@ bool AdaptDispatch::PagePositionRelative(const VTInt /*pageCount*/) // - pageCount - Number of pages to move // Return Value: // - True. -bool AdaptDispatch::PagePositionBack(const VTInt /*pageCount*/) +bool AdaptDispatch::PagePositionBack(const VTInt pageCount) { + _pages.MoveRelative(-pageCount); return true; } @@ -1825,7 +1851,8 @@ void AdaptDispatch::_SetColumnMode(const bool enable) // Only proceed if DECCOLM is allowed. Return true, as this is technically a successful handling. if (_modes.test(Mode::AllowDECCOLM) && !_api.IsConsolePty()) { - const auto viewport = _api.GetViewport(); + const auto page = _pages.VisiblePage(); + const auto viewport = page.Viewport(); const auto viewportHeight = viewport.bottom - viewport.top; const auto viewportWidth = (enable ? DispatchTypes::s_sDECCOLMSetColumns : DispatchTypes::s_sDECCOLMResetColumns); _api.ResizeWindow(viewportWidth, viewportHeight); @@ -1851,7 +1878,8 @@ void AdaptDispatch::_SetAlternateScreenBufferMode(const bool enable) if (enable) { CursorSaveState(); - const auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto& textBuffer = page.Buffer(); _api.UseAlternateScreenBuffer(_GetEraseAttributes(textBuffer)); _usingAltBuffer = true; } @@ -1930,17 +1958,17 @@ bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con // Resetting DECAWM should also reset the delayed wrap flag. if (!enable) { - _api.GetTextBuffer().GetCursor().ResetDelayEOLWrap(); + _pages.ActivePage().Buffer().GetCursor().ResetDelayEOLWrap(); } return true; case DispatchTypes::ModeParams::DECARM_AutoRepeatMode: _terminalInput.SetInputMode(TerminalInput::Mode::AutoRepeat, enable); return !_PassThroughInputModes(); case DispatchTypes::ModeParams::ATT610_StartCursorBlink: - _api.GetTextBuffer().GetCursor().SetBlinkingAllowed(enable); + _pages.ActivePage().Buffer().GetCursor().SetBlinkingAllowed(enable); return !_api.IsConsolePty(); case DispatchTypes::ModeParams::DECTCEM_TextCursorEnableMode: - _api.GetTextBuffer().GetCursor().SetIsVisible(enable); + _pages.ActivePage().Buffer().GetCursor().SetIsVisible(enable); return true; case DispatchTypes::ModeParams::XTERM_EnableDECCOLMSupport: _modes.set(Mode::AllowDECCOLM, enable); @@ -1957,8 +1985,9 @@ bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con if (enable) { // If we've allowed left/right margins, we can't have line renditions. - const auto viewport = _api.GetViewport(); - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + auto& textBuffer = page.Buffer(); textBuffer.ResetLineRenditionRange(viewport.top, viewport.bottom); } return true; @@ -2081,10 +2110,10 @@ bool AdaptDispatch::RequestMode(const DispatchTypes::ModeParams param) enabled = _terminalInput.GetInputMode(TerminalInput::Mode::AutoRepeat); break; case DispatchTypes::ModeParams::ATT610_StartCursorBlink: - enabled = _api.GetTextBuffer().GetCursor().IsBlinkingAllowed(); + enabled = _pages.ActivePage().Buffer().GetCursor().IsBlinkingAllowed(); break; case DispatchTypes::ModeParams::DECTCEM_TextCursorEnableMode: - enabled = _api.GetTextBuffer().GetCursor().IsVisible(); + enabled = _pages.ActivePage().Buffer().GetCursor().IsVisible(); break; case DispatchTypes::ModeParams::XTERM_EnableDECCOLMSupport: // DECCOLM is not supported in conpty mode @@ -2170,8 +2199,9 @@ bool AdaptDispatch::SetKeypadMode(const bool fApplicationMode) // - void AdaptDispatch::_InsertDeleteLineHelper(const VTInt delta) { - const auto viewport = _api.GetViewport(); - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); auto& cursor = textBuffer.GetCursor(); @@ -2231,8 +2261,9 @@ bool AdaptDispatch::DeleteLine(const VTInt distance) // - void AdaptDispatch::_InsertDeleteColumnHelper(const VTInt delta) { - const auto viewport = _api.GetViewport(); - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto& cursor = textBuffer.GetCursor(); @@ -2316,7 +2347,8 @@ void AdaptDispatch::_DoSetTopBottomScrollingMargins(const VTInt topMargin, til::CoordType actualTop = topMargin; til::CoordType actualBottom = bottomMargin; - const auto viewport = _api.GetViewport(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); const auto screenHeight = viewport.bottom - viewport.top; // The default top margin is line 1 if (actualTop == 0) @@ -2393,7 +2425,8 @@ void AdaptDispatch::_DoSetLeftRightScrollingMargins(const VTInt leftMargin, til::CoordType actualLeft = leftMargin; til::CoordType actualRight = rightMargin; - const auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); // The default left margin is column 1 if (actualLeft == 0) @@ -2486,14 +2519,15 @@ bool AdaptDispatch::CarriageReturn() // Routine Description: // - Helper method for executing a line feed, possibly preceded by carriage return. // Arguments: -// - textBuffer - Target buffer on which the line feed is executed. +// - page - Target page on which the line feed is executed. // - withReturn - Set to true if a carriage return should be performed as well. // - wrapForced - Set to true is the line feed was the result of the line wrapping. // Return Value: // - -void AdaptDispatch::_DoLineFeed(TextBuffer& textBuffer, const bool withReturn, const bool wrapForced) +void AdaptDispatch::_DoLineFeed(const Page& page, const bool withReturn, const bool wrapForced) { - const auto viewport = _api.GetViewport(); + const auto viewport = page.Viewport(); + auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto bufferHeight = textBuffer.GetSize().Height(); const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); @@ -2590,17 +2624,17 @@ void AdaptDispatch::_DoLineFeed(TextBuffer& textBuffer, const bool withReturn, c // - True if handled successfully. False otherwise. bool AdaptDispatch::LineFeed(const DispatchTypes::LineFeedType lineFeedType) { - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); switch (lineFeedType) { case DispatchTypes::LineFeedType::DependsOnMode: - _DoLineFeed(textBuffer, _api.GetSystemMode(ITerminalApi::Mode::LineFeed), false); + _DoLineFeed(page, _api.GetSystemMode(ITerminalApi::Mode::LineFeed), false); return true; case DispatchTypes::LineFeedType::WithoutReturn: - _DoLineFeed(textBuffer, false, false); + _DoLineFeed(page, false, false); return true; case DispatchTypes::LineFeedType::WithReturn: - _DoLineFeed(textBuffer, true, false); + _DoLineFeed(page, true, false); return true; default: return false; @@ -2616,8 +2650,9 @@ bool AdaptDispatch::LineFeed(const DispatchTypes::LineFeedType lineFeedType) // - True. bool AdaptDispatch::ReverseLineFeed() { - const auto viewport = _api.GetViewport(); - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + auto& textBuffer = page.Buffer(); auto& cursor = textBuffer.GetCursor(); const auto cursorPosition = cursor.GetPosition(); const auto bufferWidth = textBuffer.GetSize().Width(); @@ -2648,8 +2683,9 @@ bool AdaptDispatch::ReverseLineFeed() // - True. bool AdaptDispatch::BackIndex() { - const auto viewport = _api.GetViewport(); - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + auto& textBuffer = page.Buffer(); auto& cursor = textBuffer.GetCursor(); const auto cursorPosition = cursor.GetPosition(); const auto bufferWidth = textBuffer.GetSize().Width(); @@ -2679,8 +2715,9 @@ bool AdaptDispatch::BackIndex() // - True. bool AdaptDispatch::ForwardIndex() { - const auto viewport = _api.GetViewport(); - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + auto& textBuffer = page.Buffer(); auto& cursor = textBuffer.GetCursor(); const auto cursorPosition = cursor.GetPosition(); const auto bufferWidth = textBuffer.GetSize().Width(); @@ -2721,7 +2758,8 @@ bool AdaptDispatch::SetWindowTitle(std::wstring_view title) // - True. bool AdaptDispatch::HorizontalTabSet() { - const auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto& textBuffer = page.Buffer(); const auto width = textBuffer.GetSize().Dimensions().width; const auto column = textBuffer.GetCursor().GetPosition().x; @@ -2742,14 +2780,15 @@ bool AdaptDispatch::HorizontalTabSet() // - True. bool AdaptDispatch::ForwardTab(const VTInt numTabs) { - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + auto& textBuffer = page.Buffer(); auto& cursor = textBuffer.GetCursor(); auto column = cursor.GetPosition().x; const auto row = cursor.GetPosition().y; const auto width = textBuffer.GetLineWidth(row); auto tabsPerformed = 0; - const auto viewport = _api.GetViewport(); + const auto viewport = page.Viewport(); const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(width); const auto clampToMargin = row >= topMargin && row <= bottomMargin && column <= rightMargin; @@ -2791,14 +2830,15 @@ bool AdaptDispatch::ForwardTab(const VTInt numTabs) // - True. bool AdaptDispatch::BackwardsTab(const VTInt numTabs) { - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + auto& textBuffer = page.Buffer(); auto& cursor = textBuffer.GetCursor(); auto column = cursor.GetPosition().x; const auto row = cursor.GetPosition().y; const auto width = textBuffer.GetLineWidth(row); auto tabsPerformed = 0; - const auto viewport = _api.GetViewport(); + const auto viewport = page.Viewport(); const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(width); const auto clampToMargin = row >= topMargin && row <= bottomMargin && column >= leftMargin; @@ -2850,7 +2890,8 @@ bool AdaptDispatch::TabClear(const DispatchTypes::TabClearType clearType) // - void AdaptDispatch::_ClearSingleTabStop() { - const auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto& textBuffer = page.Buffer(); const auto width = textBuffer.GetSize().Dimensions().width; const auto column = textBuffer.GetCursor().GetPosition().x; @@ -3091,7 +3132,7 @@ bool AdaptDispatch::AnnounceCodeStructure(const VTInt ansiLevel) // True if handled successfully. False otherwise. bool AdaptDispatch::SoftReset() { - _api.GetTextBuffer().GetCursor().SetIsVisible(true); // Cursor enabled. + _pages.ActivePage().Buffer().GetCursor().SetIsVisible(true); // Cursor enabled. // Replace mode; Absolute cursor addressing; Disallow left/right margins. _modes.reset(Mode::InsertReplace, Mode::Origin, Mode::AllowDECSLRM); @@ -3156,6 +3197,9 @@ bool AdaptDispatch::HardReset() _usingAltBuffer = false; } + // Reset all page buffers. + _pages.Reset(); + // Completely reset the TerminalOutput state. _termOutput = {}; if (_initialCodePage.has_value()) @@ -3196,7 +3240,7 @@ bool AdaptDispatch::HardReset() _api.SetSystemMode(ITerminalApi::Mode::BracketedPaste, false); // Restore cursor blinking mode. - _api.GetTextBuffer().GetCursor().SetBlinkingAllowed(true); + _pages.ActivePage().Buffer().GetCursor().SetBlinkingAllowed(true); // Delete all current tab stops and reapply TabSet(DispatchTypes::TabSetType::SetEvery8Columns); @@ -3242,8 +3286,9 @@ bool AdaptDispatch::HardReset() // - True. bool AdaptDispatch::ScreenAlignmentPattern() { - const auto viewport = _api.GetViewport(); - auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Dimensions().width; // Fill the screen with the letter E using the default attributes. @@ -3279,10 +3324,11 @@ bool AdaptDispatch::ScreenAlignmentPattern() // - True if handled successfully. False otherwise. bool AdaptDispatch::_EraseScrollback() { - const auto viewport = _api.GetViewport(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); const auto top = viewport.top; const auto height = viewport.bottom - viewport.top; - auto& textBuffer = _api.GetTextBuffer(); + auto& textBuffer = page.Buffer(); const auto bufferSize = textBuffer.GetSize().Dimensions(); auto& cursor = textBuffer.GetCursor(); const auto row = cursor.GetPosition().y; @@ -3315,9 +3361,10 @@ bool AdaptDispatch::_EraseScrollback() // - True if handled successfully. False otherwise. bool AdaptDispatch::_EraseAll() { - const auto viewport = _api.GetViewport(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); const auto viewportHeight = viewport.bottom - viewport.top; - auto& textBuffer = _api.GetTextBuffer(); + auto& textBuffer = page.Buffer(); const auto bufferSize = textBuffer.GetSize(); const auto inPtyMode = _api.IsConsolePty(); @@ -3427,7 +3474,7 @@ bool AdaptDispatch::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) return false; } - auto& cursor = _api.GetTextBuffer().GetCursor(); + auto& cursor = _pages.ActivePage().Buffer().GetCursor(); cursor.SetType(actualType); cursor.SetBlinkingAllowed(fEnableBlinking); @@ -3589,14 +3636,17 @@ bool AdaptDispatch::WindowManipulation(const DispatchTypes::WindowManipulationTy _api.ShowWindow(false); return true; case DispatchTypes::WindowManipulationType::RefreshWindow: - _api.GetTextBuffer().TriggerRedrawAll(); + _pages.VisiblePage().Buffer().TriggerRedrawAll(); return true; case DispatchTypes::WindowManipulationType::ResizeWindowInCharacters: _api.ResizeWindow(parameter2.value_or(0), parameter1.value_or(0)); return true; case DispatchTypes::WindowManipulationType::ReportTextSizeInCharacters: - _api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033[8;{};{}t"), _api.GetViewport().height(), _api.GetTextBuffer().GetSize().Width())); + { + const auto page = _pages.VisiblePage(); + _api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033[8;{};{}t"), page.Viewport().height(), page.Buffer().GetSize().Width())); return true; + } default: return false; } @@ -3610,7 +3660,7 @@ bool AdaptDispatch::WindowManipulation(const DispatchTypes::WindowManipulationTy // - true bool AdaptDispatch::AddHyperlink(const std::wstring_view uri, const std::wstring_view params) { - auto& textBuffer = _api.GetTextBuffer(); + auto& textBuffer = _pages.ActivePage().Buffer(); auto attr = textBuffer.GetCurrentAttributes(); const auto id = textBuffer.GetHyperlinkId(uri, params); attr.SetHyperlinkId(id); @@ -3625,7 +3675,7 @@ bool AdaptDispatch::AddHyperlink(const std::wstring_view uri, const std::wstring // - true bool AdaptDispatch::EndHyperlink() { - auto& textBuffer = _api.GetTextBuffer(); + auto& textBuffer = _pages.ActivePage().Buffer(); auto attr = textBuffer.GetCurrentAttributes(); attr.SetHyperlinkId(0); textBuffer.SetCurrentAttributes(attr); @@ -4341,7 +4391,7 @@ void AdaptDispatch::_ReportSGRSetting() const fmt::basic_memory_buffer response; response.append(L"\033P1$r0"sv); - const auto& attr = _api.GetTextBuffer().GetCurrentAttributes(); + const auto& attr = _pages.ActivePage().Buffer().GetCurrentAttributes(); const auto ulStyle = attr.GetUnderlineStyle(); // For each boolean attribute that is set, we add the appropriate // parameter value to the response string. @@ -4410,7 +4460,8 @@ void AdaptDispatch::_ReportDECSTBMSetting() fmt::basic_memory_buffer response; response.append(L"\033P1$r"sv); - const auto viewport = _api.GetViewport(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); 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); @@ -4434,7 +4485,7 @@ void AdaptDispatch::_ReportDECSLRMSetting() fmt::basic_memory_buffer response; response.append(L"\033P1$r"sv); - const auto bufferWidth = _api.GetTextBuffer().GetSize().Width(); + const auto bufferWidth = _pages.ActivePage().Buffer().GetSize().Width(); const auto [marginLeft, marginRight] = _GetHorizontalMargins(bufferWidth); // 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"{};{}"), marginLeft + 1, marginRight + 1); @@ -4458,7 +4509,7 @@ void AdaptDispatch::_ReportDECSCASetting() const fmt::basic_memory_buffer response; response.append(L"\033P1$r"sv); - const auto& attr = _api.GetTextBuffer().GetCurrentAttributes(); + const auto& attr = _pages.ActivePage().Buffer().GetCurrentAttributes(); response.append(attr.IsProtected() ? L"1"sv : L"0"sv); // The '"q' indicates this is an DECSCA response, and ST ends the sequence. @@ -4576,8 +4627,9 @@ ITermDispatch::StringHandler AdaptDispatch::RestorePresentationState(const Dispa // - None void AdaptDispatch::_ReportCursorInformation() { - const auto viewport = _api.GetViewport(); - const auto& textBuffer = _api.GetTextBuffer(); + const auto page = _pages.ActivePage(); + const auto viewport = page.Viewport(); + const auto& textBuffer = page.Buffer(); const auto& cursor = textBuffer.GetCursor(); const auto& attributes = textBuffer.GetCurrentAttributes(); @@ -4598,9 +4650,6 @@ void AdaptDispatch::_ReportCursorInformation() cursorPosition.y -= _GetVerticalMargins(viewport, false).first; } - // Paging is not supported yet (GH#13892). - const auto pageNumber = 1; - // Only some of the rendition attributes are reported. // Bit Attribute // 1 bold @@ -4646,7 +4695,7 @@ void AdaptDispatch::_ReportCursorInformation() FMT_COMPILE(L"\033P1$u{};{};{};{};{};{};{};{};{};{}{}{}{}\033\\"), cursorPosition.y, cursorPosition.x, - pageNumber, + page.Number(), renditionAttributes, characterAttributes, flags, @@ -4683,7 +4732,6 @@ ITermDispatch::StringHandler AdaptDispatch::_RestoreCursorInformation() VTParameter row{}; VTParameter column{}; }; - auto& textBuffer = _api.GetTextBuffer(); return [&, state = State{}](const auto ch) mutable { if (numeric.test(state.field)) { @@ -4705,7 +4753,7 @@ ITermDispatch::StringHandler AdaptDispatch::_RestoreCursorInformation() } else if (state.field == Field::Page) { - // Paging is not supported yet (GH#13892). + PagePositionAbsolute(state.value); } else if (state.field == Field::GL && state.value <= 3) { @@ -4730,6 +4778,8 @@ ITermDispatch::StringHandler AdaptDispatch::_RestoreCursorInformation() state.value = ch; if (state.field == Field::SGR) { + const auto page = _pages.ActivePage(); + auto& textBuffer = page.Buffer(); auto attr = textBuffer.GetCurrentAttributes(); attr.SetIntense(state.value & 1); attr.SetUnderlineStyle(state.value & 2 ? UnderlineStyle::SinglyUnderlined : UnderlineStyle::NoUnderline); @@ -4740,6 +4790,8 @@ ITermDispatch::StringHandler AdaptDispatch::_RestoreCursorInformation() } else if (state.field == Field::Attr) { + const auto page = _pages.ActivePage(); + auto& textBuffer = page.Buffer(); auto attr = textBuffer.GetCurrentAttributes(); attr.SetProtected(state.value & 1); textBuffer.SetCurrentAttributes(attr); @@ -4768,6 +4820,8 @@ ITermDispatch::StringHandler AdaptDispatch::_RestoreCursorInformation() // above, so we only need to worry about setting it. if (delayedEOLWrap) { + const auto page = _pages.ActivePage(); + auto& textBuffer = page.Buffer(); textBuffer.GetCursor().DelayEOLWrap(); } } @@ -4815,7 +4869,7 @@ void AdaptDispatch::_ReportTabStops() // In order to be compatible with the original hardware terminals, we only // report tab stops up to the current buffer width, even though there may // be positions recorded beyond that limit. - const auto width = _api.GetTextBuffer().GetSize().Dimensions().width; + const auto width = _pages.ActivePage().Buffer().GetSize().Dimensions().width; _InitTabStopsForWidth(width); using namespace std::string_view_literals; @@ -4851,7 +4905,7 @@ ITermDispatch::StringHandler AdaptDispatch::_RestoreTabStops() // In order to be compatible with the original hardware terminals, we need // to be able to set tab stops up to at least 132 columns, even though the // current buffer width may be less than that. - const auto width = std::max(_api.GetTextBuffer().GetSize().Dimensions().width, 132); + const auto width = std::max(_pages.ActivePage().Buffer().GetSize().Dimensions().width, 132); _ClearAllTabStops(); _InitTabStopsForWidth(width); diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 5d77b9090b3..1581a1aa693 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -18,6 +18,7 @@ Author(s): #include "ITerminalApi.hpp" #include "FontBuffer.hpp" #include "MacroBuffer.hpp" +#include "PageManager.hpp" #include "terminalOutput.hpp" #include "../input/terminalInput.hpp" #include "../../types/inc/sgrStack.hpp" @@ -194,6 +195,7 @@ namespace Microsoft::Console::VirtualTerminal { VTInt Row = 1; VTInt Column = 1; + VTInt Page = 1; bool IsDelayedEOLWrap = false; bool IsOriginModeRelative = false; TextAttribute Attributes = {}; @@ -227,7 +229,7 @@ namespace Microsoft::Console::VirtualTerminal void _SelectiveEraseRect(TextBuffer& textBuffer, const til::rect& eraseRect); void _ChangeRectAttributes(TextBuffer& textBuffer, const til::rect& changeRect, const ChangeOps& changeOps); void _ChangeRectOrStreamAttributes(const til::rect& changeArea, const ChangeOps& changeOps); - til::rect _CalculateRectArea(const VTInt top, const VTInt left, const VTInt bottom, const VTInt right, const til::size bufferSize); + til::rect _CalculateRectArea(const Page& page, const VTInt top, const VTInt left, const VTInt bottom, const VTInt right); bool _EraseScrollback(); bool _EraseAll(); TextAttribute _GetEraseAttributes(const TextBuffer& textBuffer) const noexcept; @@ -245,7 +247,7 @@ namespace Microsoft::Console::VirtualTerminal const VTInt rightMargin, const bool homeCursor = false); - void _DoLineFeed(TextBuffer& textBuffer, const bool withReturn, const bool wrapForced); + void _DoLineFeed(const Page& page, const bool withReturn, const bool wrapForced); void _DeviceStatusReport(const wchar_t* parameters) const; void _CursorPositionReport(const bool extendedReport); @@ -286,6 +288,7 @@ namespace Microsoft::Console::VirtualTerminal RenderSettings& _renderSettings; TerminalInput& _terminalInput; TerminalOutput _termOutput; + PageManager _pages; std::unique_ptr _fontBuffer; std::shared_ptr _macroBuffer; std::optional _initialCodePage; diff --git a/src/terminal/adapter/adaptDispatchGraphics.cpp b/src/terminal/adapter/adaptDispatchGraphics.cpp index ac1b0180eff..f60662cc913 100644 --- a/src/terminal/adapter/adaptDispatchGraphics.cpp +++ b/src/terminal/adapter/adaptDispatchGraphics.cpp @@ -422,7 +422,7 @@ void AdaptDispatch::_ApplyGraphicsOptions(const VTParameters options, // - True. bool AdaptDispatch::SetGraphicsRendition(const VTParameters options) { - auto attr = _api.GetTextBuffer().GetCurrentAttributes(); + auto attr = _pages.ActivePage().Buffer().GetCurrentAttributes(); _ApplyGraphicsOptions(options, attr); _api.SetTextAttributes(attr); return true; @@ -438,7 +438,7 @@ bool AdaptDispatch::SetGraphicsRendition(const VTParameters options) // - True. bool AdaptDispatch::SetCharacterProtectionAttribute(const VTParameters options) { - auto& textBuffer = _api.GetTextBuffer(); + auto& textBuffer = _pages.ActivePage().Buffer(); auto attr = textBuffer.GetCurrentAttributes(); for (size_t i = 0; i < options.size(); i++) { @@ -470,7 +470,7 @@ bool AdaptDispatch::SetCharacterProtectionAttribute(const VTParameters options) // - True. bool AdaptDispatch::PushGraphicsRendition(const VTParameters options) { - const auto& currentAttributes = _api.GetTextBuffer().GetCurrentAttributes(); + const auto& currentAttributes = _pages.ActivePage().Buffer().GetCurrentAttributes(); _sgrStack.Push(currentAttributes, options); return true; } @@ -484,7 +484,7 @@ bool AdaptDispatch::PushGraphicsRendition(const VTParameters options) // - True. bool AdaptDispatch::PopGraphicsRendition() { - const auto& currentAttributes = _api.GetTextBuffer().GetCurrentAttributes(); + const auto& currentAttributes = _pages.ActivePage().Buffer().GetCurrentAttributes(); _api.SetTextAttributes(_sgrStack.Pop(currentAttributes)); return true; } From e451c5b9d8268e6d9bc6e58278f9006178a84037 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 25 Jan 2024 11:43:57 +0000 Subject: [PATCH 05/19] Remove unused ITerminalApi methods. --- src/cascadia/TerminalCore/Terminal.hpp | 2 -- src/cascadia/TerminalCore/TerminalApi.cpp | 10 -------- .../UnitTests_TerminalCore/SelectionTest.cpp | 23 +++++++++++-------- .../TerminalApiTest.cpp | 3 ++- src/host/outputStream.cpp | 22 ------------------ src/host/outputStream.hpp | 2 -- src/terminal/adapter/ITerminalApi.hpp | 2 -- src/terminal/adapter/InteractDispatch.cpp | 4 ++-- .../adapter/ut_adapter/adapterTest.cpp | 15 +++--------- 9 files changed, 21 insertions(+), 62 deletions(-) diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 75268719c79..c12ecaff28a 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -132,8 +132,6 @@ class Microsoft::Terminal::Core::Terminal final : void ReturnResponse(const std::wstring_view response) override; Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() noexcept override; std::tuple GetBufferAndViewport() noexcept override; - TextBuffer& GetTextBuffer() noexcept override; - til::rect GetViewport() const noexcept override; void SetViewportPosition(const til::point position) noexcept override; void SetTextAttributes(const TextAttribute& attrs) noexcept override; void SetSystemMode(const Mode mode, const bool enabled) noexcept override; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 5178981e40f..5c2ea8a04cb 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -38,16 +38,6 @@ std::tuple Terminal::GetBufferAndViewport() noexce return { _activeBuffer(), til::rect{ _GetMutableViewport().ToInclusive() }, !_inAltBuffer() }; } -TextBuffer& Terminal::GetTextBuffer() noexcept -{ - return _activeBuffer(); -} - -til::rect Terminal::GetViewport() const noexcept -{ - return til::rect{ _GetMutableViewport().ToInclusive() }; -} - void Terminal::SetViewportPosition(const til::point position) noexcept try { diff --git a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp index 195eb17fcc3..7974a153dcf 100644 --- a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp @@ -44,6 +44,11 @@ namespace TerminalCoreUnitTests VERIFY_ARE_EQUAL(selection, expected); } + TextBuffer& GetTextBuffer(Terminal& term) + { + return std::get(term.GetBufferAndViewport()); + } + TEST_METHOD(SelectUnit) { Terminal term{ Terminal::TestDummyMarker{} }; @@ -394,7 +399,7 @@ namespace TerminalCoreUnitTests const auto burrito = L"\xD83C\xDF2F"; // Insert wide glyph at position (4,10) - term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 }); + GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 }); term.Write(burrito); // Simulate click at (x,y) = (5,10) @@ -417,7 +422,7 @@ namespace TerminalCoreUnitTests const auto burrito = L"\xD83C\xDF2F"; // Insert wide glyph at position (4,10) - term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 }); + GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 }); term.Write(burrito); // Simulate click at (x,y) = (5,10) @@ -440,11 +445,11 @@ namespace TerminalCoreUnitTests const auto burrito = L"\xD83C\xDF2F"; // Insert wide glyph at position (4,10) - term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 }); + GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 }); term.Write(burrito); // Insert wide glyph at position (7,11) - term.GetTextBuffer().GetCursor().SetPosition({ 7, 11 }); + GetTextBuffer(term).GetCursor().SetPosition({ 7, 11 }); term.Write(burrito); // Simulate ALT + click at (x,y) = (5,8) @@ -496,7 +501,7 @@ namespace TerminalCoreUnitTests // Insert text at position (4,10) const std::wstring_view text = L"doubleClickMe"; - term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 }); + GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 }); term.Write(text); // Simulate double click at (x,y) = (5,10) @@ -540,7 +545,7 @@ namespace TerminalCoreUnitTests // Insert text at position (4,10) const std::wstring_view text = L"C:\\Terminal>"; - term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 }); + GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 }); term.Write(text); // Simulate click at (x,y) = (15,10) @@ -568,7 +573,7 @@ namespace TerminalCoreUnitTests // Insert text at position (4,10) const std::wstring_view text = L"doubleClickMe dragThroughHere"; - term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 }); + GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 }); term.Write(text); // Simulate double click at (x,y) = (5,10) @@ -597,7 +602,7 @@ namespace TerminalCoreUnitTests // Insert text at position (21,10) const std::wstring_view text = L"doubleClickMe dragThroughHere"; - term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 }); + GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 }); term.Write(text); // Simulate double click at (x,y) = (21,10) @@ -685,7 +690,7 @@ namespace TerminalCoreUnitTests // Insert text at position (4,10) const std::wstring_view text = L"doubleClickMe dragThroughHere"; - term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 }); + GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 }); term.Write(text); // Step 1: Create a selection on "doubleClickMe" diff --git a/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp b/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp index 9f002db5098..ad8a853265e 100644 --- a/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp @@ -152,7 +152,8 @@ void TerminalApiTest::CursorVisibility() VERIFY_IS_TRUE(term._mainBuffer->GetCursor().IsOn()); VERIFY_IS_TRUE(term._mainBuffer->GetCursor().IsBlinkingAllowed()); - term.GetTextBuffer().GetCursor().SetIsVisible(false); + auto& textBuffer = std::get(term.GetBufferAndViewport()); + textBuffer.GetCursor().SetIsVisible(false); VERIFY_IS_FALSE(term._mainBuffer->GetCursor().IsVisible()); VERIFY_IS_TRUE(term._mainBuffer->GetCursor().IsOn()); VERIFY_IS_TRUE(term._mainBuffer->GetCursor().IsBlinkingAllowed()); diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 91e8d1c08ff..934686de0f4 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -64,28 +64,6 @@ std::tuple ConhostInternalGetSet::GetBufferAndView return { info.GetTextBuffer(), info.GetVirtualViewport().ToExclusive(), info.Next == nullptr }; } -// 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. -til::rect ConhostInternalGetSet::GetViewport() const -{ - return _io.GetActiveOutputBuffer().GetVirtualViewport().ToExclusive(); -} - // Routine Description: // - Sets the position of the window viewport. // Arguments: diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 7aaad569196..8c30c415262 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -33,8 +33,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() override; std::tuple GetBufferAndViewport() override; - TextBuffer& GetTextBuffer() 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/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index c30559ac6a8..6d05c80ba46 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -41,8 +41,6 @@ namespace Microsoft::Console::VirtualTerminal virtual StateMachine& GetStateMachine() = 0; virtual std::tuple GetBufferAndViewport() = 0; - virtual TextBuffer& GetTextBuffer() = 0; - virtual til::rect GetViewport() const = 0; virtual void SetViewportPosition(const til::point position) = 0; virtual bool IsVtInputEnabled() const = 0; diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp index cb06c985c91..0ecbcfbd3b3 100644 --- a/src/terminal/adapter/InteractDispatch.cpp +++ b/src/terminal/adapter/InteractDispatch.cpp @@ -108,7 +108,7 @@ bool InteractDispatch::WindowManipulation(const DispatchTypes::WindowManipulatio _api.ShowWindow(false); return true; case DispatchTypes::WindowManipulationType::RefreshWindow: - _api.GetTextBuffer().TriggerRedrawAll(); + std::get(_api.GetBufferAndViewport()).TriggerRedrawAll(); return true; case DispatchTypes::WindowManipulationType::ResizeWindowInCharacters: // TODO:GH#1765 We should introduce a better `ResizeConpty` function to @@ -135,7 +135,7 @@ bool InteractDispatch::WindowManipulation(const DispatchTypes::WindowManipulatio bool InteractDispatch::MoveCursor(const VTInt row, const VTInt col) { // First retrieve some information about the buffer - const auto viewport = _api.GetViewport(); + const auto viewport = std::get(_api.GetBufferAndViewport()); // In VT, the origin is 1,1. For our array, it's 0,0. So subtract 1. // Apply boundary tests to ensure the cursor isn't outside the viewport rectangle. diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index ebb37e95bb4..de8aed57627 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -87,16 +87,6 @@ class TestGetSet final : public ITerminalApi return { *_textBuffer.get(), viewport, true }; } - TextBuffer& GetTextBuffer() override - { - return *_textBuffer.get(); - } - - til::rect GetViewport() const override - { - return { _viewport.left, _viewport.top, _viewport.right, _viewport.bottom }; - } - void SetViewportPosition(const til::point /*position*/) override { Log::Comment(L"SetViewportPosition MOCK called..."); @@ -3285,7 +3275,7 @@ class AdapterTest setMacroText(63, L"Macro 63"); const auto getBufferOutput = [&]() { - const auto& textBuffer = _testGetSet->GetTextBuffer(); + const auto& textBuffer = std::get(_testGetSet->GetBufferAndViewport()); const auto cursorPos = textBuffer.GetCursor().GetPosition(); return textBuffer.GetRowByOffset(cursorPos.y).GetText().substr(0, cursorPos.x); }; @@ -3336,7 +3326,8 @@ class AdapterTest { _testGetSet->PrepData(); _pDispatch->WindowManipulation(DispatchTypes::WindowManipulationType::ReportTextSizeInCharacters, NULL, NULL); - const std::wstring expectedResponse = fmt::format(L"\033[8;{};{}t", _testGetSet->GetViewport().height(), _testGetSet->GetTextBuffer().GetSize().Width()); + const auto [textBuffer, viewport, _] = _testGetSet->GetBufferAndViewport(); + const std::wstring expectedResponse = fmt::format(L"\033[8;{};{}t", viewport.height(), textBuffer.GetSize().Width()); _testGetSet->ValidateInputEvent(expectedResponse.c_str()); } From 22535b0b856b8c08eaa945e93062921e808eac49 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 25 Jan 2024 14:13:19 +0000 Subject: [PATCH 06/19] Add support for page cursor coupling mode. --- src/terminal/adapter/DispatchTypes.hpp | 1 + src/terminal/adapter/PageManager.cpp | 16 ++++++++++++---- src/terminal/adapter/PageManager.hpp | 5 +++-- src/terminal/adapter/adaptDispatch.cpp | 18 ++++++++++++++---- src/terminal/adapter/adaptDispatch.hpp | 5 +++-- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/terminal/adapter/DispatchTypes.hpp b/src/terminal/adapter/DispatchTypes.hpp index 52852bed6a2..a0a74d8c83a 100644 --- a/src/terminal/adapter/DispatchTypes.hpp +++ b/src/terminal/adapter/DispatchTypes.hpp @@ -531,6 +531,7 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes ATT610_StartCursorBlink = DECPrivateMode(12), DECTCEM_TextCursorEnableMode = DECPrivateMode(25), XTERM_EnableDECCOLMSupport = DECPrivateMode(40), + DECPCCM_PageCursorCouplingMode = DECPrivateMode(64), DECNKM_NumericKeypadMode = DECPrivateMode(66), DECBKM_BackarrowKeyMode = DECPrivateMode(67), DECLRMM_LeftRightMarginMode = DECPrivateMode(69), diff --git a/src/terminal/adapter/PageManager.cpp b/src/terminal/adapter/PageManager.cpp index 012db36a38c..84da971fecd 100644 --- a/src/terminal/adapter/PageManager.cpp +++ b/src/terminal/adapter/PageManager.cpp @@ -73,7 +73,7 @@ Page PageManager::VisiblePage() const return Get(_visiblePageNumber); } -void PageManager::MoveTo(const til::CoordType pageNumber) +void PageManager::MoveTo(const til::CoordType pageNumber, const bool makeVisible) { auto [visibleBuffer, visibleViewport, isMainBuffer] = _api.GetBufferAndViewport(); if (!isMainBuffer) @@ -91,7 +91,7 @@ void PageManager::MoveTo(const til::CoordType pageNumber) // visible page into its backing buffer, and swap in the new page from the // backing buffer to the main buffer. That way the rest of the system only // ever has to deal with the main buffer. - if (_visiblePageNumber != newPageNumber) + if (makeVisible && _visiblePageNumber != newPageNumber) { const auto& newBuffer = _getBuffer(newPageNumber, pageSize); auto& saveBuffer = _getBuffer(_visiblePageNumber, pageSize); @@ -146,9 +146,17 @@ void PageManager::MoveTo(const til::CoordType pageNumber) } } -void PageManager::MoveRelative(const til::CoordType pageCount) +void PageManager::MoveRelative(const til::CoordType pageCount, const bool makeVisible) { - MoveTo(_activePageNumber + pageCount); + MoveTo(_activePageNumber + pageCount, makeVisible); +} + +void PageManager::MakeActivePageVisible() +{ + if (_activePageNumber != _visiblePageNumber) + { + MoveTo(_activePageNumber, true); + } } TextBuffer& PageManager::_getBuffer(const til::CoordType pageNumber, const til::size pageSize) const diff --git a/src/terminal/adapter/PageManager.hpp b/src/terminal/adapter/PageManager.hpp index 7eae6a9f8fb..87fcda14312 100644 --- a/src/terminal/adapter/PageManager.hpp +++ b/src/terminal/adapter/PageManager.hpp @@ -40,8 +40,9 @@ namespace Microsoft::Console::VirtualTerminal Page Get(const til::CoordType pageNumber) const; Page ActivePage() const; Page VisiblePage() const; - void MoveTo(const til::CoordType pageNumber); - void MoveRelative(const til::CoordType pageCount); + void MoveTo(const til::CoordType pageNumber, const bool makeVisible); + void MoveRelative(const til::CoordType pageCount, const bool makeVisible); + void MakeActivePageVisible(); private: TextBuffer& _getBuffer(const til::CoordType pageNumber, const til::size pageSize) const; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 4fee8348b3d..a05e39dd9b9 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1809,7 +1809,7 @@ bool AdaptDispatch::PrecedingPage(const VTInt pageCount) // - True. bool AdaptDispatch::PagePositionAbsolute(const VTInt page) { - _pages.MoveTo(page); + _pages.MoveTo(page, _modes.test(Mode::PageCursorCoupling)); return true; } @@ -1822,7 +1822,7 @@ bool AdaptDispatch::PagePositionAbsolute(const VTInt page) // - True. bool AdaptDispatch::PagePositionRelative(const VTInt pageCount) { - _pages.MoveRelative(pageCount); + _pages.MoveRelative(pageCount, _modes.test(Mode::PageCursorCoupling)); return true; } @@ -1835,7 +1835,7 @@ bool AdaptDispatch::PagePositionRelative(const VTInt pageCount) // - True. bool AdaptDispatch::PagePositionBack(const VTInt pageCount) { - _pages.MoveRelative(-pageCount); + _pages.MoveRelative(-pageCount, _modes.test(Mode::PageCursorCoupling)); return true; } @@ -1973,6 +1973,13 @@ bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con case DispatchTypes::ModeParams::XTERM_EnableDECCOLMSupport: _modes.set(Mode::AllowDECCOLM, enable); return true; + case DispatchTypes::ModeParams::DECPCCM_PageCursorCouplingMode: + _modes.set(Mode::PageCursorCoupling, enable); + if (enable) + { + _pages.MakeActivePageVisible(); + } + return true; case DispatchTypes::ModeParams::DECNKM_NumericKeypadMode: _terminalInput.SetInputMode(TerminalInput::Mode::Keypad, enable); return !_PassThroughInputModes(); @@ -2122,6 +2129,9 @@ bool AdaptDispatch::RequestMode(const DispatchTypes::ModeParams param) enabled = _modes.test(Mode::AllowDECCOLM); } break; + case DispatchTypes::ModeParams::DECPCCM_PageCursorCouplingMode: + enabled = _modes.test(Mode::PageCursorCoupling); + break; case DispatchTypes::ModeParams::DECNKM_NumericKeypadMode: enabled = _terminalInput.GetInputMode(TerminalInput::Mode::Keypad); break; @@ -3250,7 +3260,7 @@ bool AdaptDispatch::HardReset() _fontBuffer = nullptr; // Reset internal modes to their initial state - _modes = {}; + _modes = { Mode::PageCursorCoupling }; // Clear and release the macro buffer. if (_macroBuffer) diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 1581a1aa693..43ba3c3eb1c 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -184,7 +184,8 @@ namespace Microsoft::Console::VirtualTerminal AllowDECCOLM, AllowDECSLRM, EraseColor, - RectangularChangeExtent + RectangularChangeExtent, + PageCursorCoupling }; enum class ScrollDirection { @@ -303,7 +304,7 @@ namespace Microsoft::Console::VirtualTerminal til::inclusive_rect _scrollMargins; - til::enumset _modes; + til::enumset _modes{ Mode::PageCursorCoupling }; SgrStack _sgrStack; From 7046768db5dbb65797a44b5091cf7724039a967e Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 25 Jan 2024 14:55:17 +0000 Subject: [PATCH 07/19] Add support for DECRQDE query. --- src/terminal/adapter/ITermDispatch.hpp | 1 + src/terminal/adapter/adaptDispatch.cpp | 17 +++++++++++++++++ src/terminal/adapter/adaptDispatch.hpp | 1 + src/terminal/adapter/termDispatch.hpp | 1 + .../parser/OutputStateMachineEngine.cpp | 3 +++ .../parser/OutputStateMachineEngine.hpp | 1 + 6 files changed, 24 insertions(+) diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index 13df0d4888b..b6aba36eccd 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -54,6 +54,7 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch virtual bool PagePositionAbsolute(const VTInt page) = 0; // PPA virtual bool PagePositionRelative(const VTInt pageCount) = 0; // PPR virtual bool PagePositionBack(const VTInt pageCount) = 0; // PPB + virtual bool RequestDisplayedExtent() = 0; // DECRQDE virtual bool InsertLine(const VTInt distance) = 0; // IL virtual bool DeleteLine(const VTInt distance) = 0; // DL virtual bool InsertColumn(const VTInt distance) = 0; // DECIC diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index a05e39dd9b9..7266f28628e 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1839,6 +1839,23 @@ bool AdaptDispatch::PagePositionBack(const VTInt pageCount) return true; } +// Routine Description: +// - DECRQDE - Requests the area of page memory that is currently visible. +// Arguments: +// - None +// Return Value: +// - True. +bool AdaptDispatch::RequestDisplayedExtent() +{ + const auto page = _pages.VisiblePage(); + const auto width = page.Viewport().width(); + const auto height = page.Viewport().height(); + const auto left = page.Viewport().left + 1; + const auto top = 1; + _api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033[{};{};{};{};{}\"w"), height, width, left, top, page.Number())); + return true; +} + // Routine Description: // - DECCOLM not only sets the number of columns, but also clears the screen buffer, // resets the page margins and origin mode, and places the cursor at 1,1 diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 43ba3c3eb1c..8419332991c 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -87,6 +87,7 @@ namespace Microsoft::Console::VirtualTerminal bool PagePositionAbsolute(const VTInt page) override; // PPA bool PagePositionRelative(const VTInt pageCount) override; // PPR bool PagePositionBack(const VTInt pageCount) override; // PPB + bool RequestDisplayedExtent() override; // DECRQDE bool InsertLine(const VTInt distance) override; // IL bool DeleteLine(const VTInt distance) override; // DL bool InsertColumn(const VTInt distance) override; // DECIC diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp index a4c648616ce..71e5155867f 100644 --- a/src/terminal/adapter/termDispatch.hpp +++ b/src/terminal/adapter/termDispatch.hpp @@ -47,6 +47,7 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons bool PagePositionAbsolute(const VTInt /*page*/) override { return false; } // PPA bool PagePositionRelative(const VTInt /*pageCount*/) override { return false; } // PPR bool PagePositionBack(const VTInt /*pageCount*/) override { return false; } // PPB + bool RequestDisplayedExtent() override { return false; } // DECRQDE bool InsertLine(const VTInt /*distance*/) override { return false; } // IL bool DeleteLine(const VTInt /*distance*/) override { return false; } // DL bool InsertColumn(const VTInt /*distance*/) override { return false; } // DECIC diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index ccfe699d746..5610fcec8fc 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -624,6 +624,9 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParamete case CsiActionCodes::DECSCA_SetCharacterProtectionAttribute: success = _dispatch->SetCharacterProtectionAttribute(parameters); break; + case CsiActionCodes::DECRQDE_RequestDisplayedExtent: + success = _dispatch->RequestDisplayedExtent(); + break; case CsiActionCodes::XT_PushSgr: case CsiActionCodes::XT_PushSgrAlias: success = _dispatch->PushGraphicsRendition(parameters); diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index 852d1514437..ed7b0d471ee 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -155,6 +155,7 @@ namespace Microsoft::Console::VirtualTerminal DECSCUSR_SetCursorStyle = VTID(" q"), DECSTR_SoftReset = VTID("!p"), DECSCA_SetCharacterProtectionAttribute = VTID("\"q"), + DECRQDE_RequestDisplayedExtent = VTID("\"v"), XT_PushSgrAlias = VTID("#p"), XT_PopSgrAlias = VTID("#q"), XT_PushSgr = VTID("#{"), From bdb890fa5c48f77430b7b1c4a9e46ac60eec6db9 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 25 Jan 2024 17:34:42 +0000 Subject: [PATCH 08/19] Use Page instead of TextBuffer in more AdaptDispatch methods. --- src/terminal/adapter/adaptDispatch.cpp | 213 ++++++++++++------------- src/terminal/adapter/adaptDispatch.hpp | 14 +- 2 files changed, 108 insertions(+), 119 deletions(-) diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 7266f28628e..e35978e3e8c 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -81,8 +81,7 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string) const auto wrapAtEOL = _api.GetSystemMode(ITerminalApi::Mode::AutoWrap); const auto& attributes = textBuffer.GetCurrentAttributes(); - const auto viewport = page.Viewport(); - const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(textBuffer.GetSize().Width()); auto lineWidth = textBuffer.GetLineWidth(cursorPosition.y); @@ -134,7 +133,7 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string) } const auto row = cursorPosition.y; const auto cellCount = measureIt.GetCellDistance(it); - _ScrollRectHorizontally(textBuffer, { cursorPosition.x, row, state.columnLimit, row + 1 }, cellCount); + _ScrollRectHorizontally(page, { cursorPosition.x, row, state.columnLimit, row + 1 }, cellCount); } state.columnBegin = cursorPosition.x; @@ -278,19 +277,19 @@ bool AdaptDispatch::CursorPrevLine(const VTInt distance) // 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. +// - page - The page that the margins will apply to. +// - absolute - Should coordinates be absolute or relative to the page top. // Return Value: // - A std::pair containing the top and bottom coordinates (inclusive). -std::pair AdaptDispatch::_GetVerticalMargins(const til::rect& viewport, const bool absolute) noexcept +std::pair AdaptDispatch::_GetVerticalMargins(const Page& page, const bool absolute) noexcept { // If the top is out of range, reset the margins completely. - const auto bottommostRow = viewport.bottom - viewport.top - 1; + const auto bottommostRow = page.Viewport().bottom - page.Viewport().top - 1; if (_scrollMargins.top >= bottommostRow) { _scrollMargins.top = _scrollMargins.bottom = 0; } - // If margins aren't set, use the full extent of the viewport. + // If margins aren't set, use the full extent of the page. const auto marginsSet = _scrollMargins.top < _scrollMargins.bottom; auto topMargin = marginsSet ? _scrollMargins.top : 0; auto bottomMargin = marginsSet ? _scrollMargins.bottom : bottommostRow; @@ -298,8 +297,8 @@ std::pair AdaptDispatch::_GetVerticalMargins(const til::rect& viewport bottomMargin = std::min(bottomMargin, bottommostRow); if (absolute) { - topMargin += viewport.top; - bottomMargin += viewport.top; + topMargin += page.Viewport().top; + bottomMargin += page.Viewport().top; } return { topMargin, bottomMargin }; } @@ -344,7 +343,7 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col auto& cursor = textBuffer.GetCursor(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto cursorPosition = cursor.GetPosition(); - const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); // For relative movement, the given offsets will be relative to @@ -517,7 +516,7 @@ bool AdaptDispatch::CursorSaveState() if (_modes.test(Mode::Origin)) { cursorPosition.x -= _GetHorizontalMargins(textBuffer.GetSize().Width()).first; - cursorPosition.y -= _GetVerticalMargins(viewport, false).first; + cursorPosition.y -= _GetVerticalMargins(page, false).first; } // VT is also 1 based, not 0 based, so correct by 1. @@ -574,10 +573,10 @@ bool AdaptDispatch::CursorRestoreState() // the Erase Color mode is set, we use the default attributes, but when reset, // we use the active color attributes with the character attributes cleared. // Arguments: -// - textBuffer - Target buffer that is being erased. +// - page - Target page that is being erased. // Return Value: // - The erase TextAttribute value. -TextAttribute AdaptDispatch::_GetEraseAttributes(const TextBuffer& textBuffer) const noexcept +TextAttribute AdaptDispatch::_GetEraseAttributes(const Page& page) const noexcept { if (_modes.test(Mode::EraseColor)) { @@ -585,7 +584,7 @@ TextAttribute AdaptDispatch::_GetEraseAttributes(const TextBuffer& textBuffer) c } else { - auto eraseAttributes = textBuffer.GetCurrentAttributes(); + auto eraseAttributes = page.Buffer().GetCurrentAttributes(); eraseAttributes.SetStandardErase(); return eraseAttributes; } @@ -594,13 +593,14 @@ TextAttribute AdaptDispatch::_GetEraseAttributes(const TextBuffer& textBuffer) c // Routine Description: // - Scrolls an area of the buffer in a vertical direction. // Arguments: -// - textBuffer - Target buffer to be scrolled. -// - fillRect - Area of the buffer that will be affected. +// - page - Target page to be scrolled. +// - fillRect - Area of the page that will be affected. // - delta - Distance to move (positive is down, negative is up). // Return Value: // - -void AdaptDispatch::_ScrollRectVertically(TextBuffer& textBuffer, const til::rect& scrollRect, const VTInt delta) +void AdaptDispatch::_ScrollRectVertically(const Page& page, const til::rect& scrollRect, const VTInt delta) { + auto& textBuffer = page.Buffer(); const auto absoluteDelta = std::min(std::abs(delta), scrollRect.height()); if (absoluteDelta < scrollRect.height()) { @@ -639,8 +639,8 @@ void AdaptDispatch::_ScrollRectVertically(TextBuffer& textBuffer, const til::rec auto eraseRect = scrollRect; eraseRect.top = delta > 0 ? scrollRect.top : (scrollRect.bottom - absoluteDelta); eraseRect.bottom = eraseRect.top + absoluteDelta; - const auto eraseAttributes = _GetEraseAttributes(textBuffer); - _FillRect(textBuffer, eraseRect, whitespace, eraseAttributes); + const auto eraseAttributes = _GetEraseAttributes(page); + _FillRect(page, eraseRect, whitespace, eraseAttributes); // Also reset the line rendition for the erased rows. textBuffer.ResetLineRenditionRange(eraseRect.top, eraseRect.bottom); @@ -649,13 +649,14 @@ void AdaptDispatch::_ScrollRectVertically(TextBuffer& textBuffer, const til::rec // 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. +// - page - Target page to be scrolled. +// - fillRect - Area of the page 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 VTInt delta) +void AdaptDispatch::_ScrollRectHorizontally(const Page& page, const til::rect& scrollRect, const VTInt delta) { + auto& textBuffer = page.Buffer(); const auto absoluteDelta = std::min(std::abs(delta), scrollRect.width()); if (absoluteDelta < scrollRect.width()) { @@ -687,8 +688,8 @@ void AdaptDispatch::_ScrollRectHorizontally(TextBuffer& textBuffer, const til::r auto eraseRect = scrollRect; eraseRect.left = delta > 0 ? scrollRect.left : (scrollRect.right - absoluteDelta); eraseRect.right = eraseRect.left + absoluteDelta; - const auto eraseAttributes = _GetEraseAttributes(textBuffer); - _FillRect(textBuffer, eraseRect, whitespace, eraseAttributes); + const auto eraseAttributes = _GetEraseAttributes(page); + _FillRect(page, eraseRect, whitespace, eraseAttributes); } // Routine Description: @@ -701,18 +702,17 @@ void AdaptDispatch::_ScrollRectHorizontally(TextBuffer& textBuffer, const til::r void AdaptDispatch::_InsertDeleteCharacterHelper(const VTInt delta) { const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); auto& textBuffer = page.Buffer(); const auto row = textBuffer.GetCursor().GetPosition().y; const auto col = textBuffer.GetCursor().GetPosition().x; const auto lineWidth = textBuffer.GetLineWidth(row); - const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); const auto [leftMargin, rightMargin] = (row >= topMargin && row <= bottomMargin) ? _GetHorizontalMargins(lineWidth) : std::make_pair(0, lineWidth - 1); if (col >= leftMargin && col <= rightMargin) { - _ScrollRectHorizontally(textBuffer, { col, row, rightMargin + 1, row + 1 }, delta); + _ScrollRectHorizontally(page, { col, row, rightMargin + 1, row + 1 }, delta); // The ICH and DCH controls are expected to reset the delayed wrap flag. textBuffer.GetCursor().ResetDelayEOLWrap(); } @@ -747,15 +747,15 @@ bool AdaptDispatch::DeleteCharacter(const VTInt count) // Routine Description: // - Fills an area of the buffer with a given character and attributes. // Arguments: -// - textBuffer - Target buffer to be filled. -// - fillRect - Area of the buffer that will be affected. +// - page - Target page to be filled. +// - fillRect - Area of the page that will be affected. // - fillChar - Character to be written to the buffer. // - fillAttrs - Attributes to be written to the buffer. // Return Value: // - -void AdaptDispatch::_FillRect(TextBuffer& textBuffer, const til::rect& fillRect, const std::wstring_view& fillChar, const TextAttribute& fillAttrs) const +void AdaptDispatch::_FillRect(const Page& page, const til::rect& fillRect, const std::wstring_view& fillChar, const TextAttribute& fillAttrs) const { - textBuffer.FillRect(fillRect, fillChar, fillAttrs); + page.Buffer().FillRect(fillRect, fillChar, fillAttrs); _api.NotifyAccessibilityChange(fillRect); } @@ -779,8 +779,8 @@ bool AdaptDispatch::EraseCharacters(const VTInt numChars) // The ECH control is expected to reset the delayed wrap flag. textBuffer.GetCursor().ResetDelayEOLWrap(); - const auto eraseAttributes = _GetEraseAttributes(textBuffer); - _FillRect(textBuffer, { startCol, row, endCol, row + 1 }, whitespace, eraseAttributes); + const auto eraseAttributes = _GetEraseAttributes(page); + _FillRect(page, { startCol, row, endCol, row + 1 }, whitespace, eraseAttributes); return true; } @@ -825,7 +825,7 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) // take care of that themselves when they set the cursor position. textBuffer.GetCursor().ResetDelayEOLWrap(); - const auto eraseAttributes = _GetEraseAttributes(textBuffer); + const auto eraseAttributes = _GetEraseAttributes(page); // 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 @@ -836,14 +836,14 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) if (eraseType == DispatchTypes::EraseType::FromBeginning) { textBuffer.ResetLineRenditionRange(viewport.top, row); - _FillRect(textBuffer, { 0, viewport.top, bufferWidth, row }, whitespace, eraseAttributes); - _FillRect(textBuffer, { 0, row, col + 1, row + 1 }, whitespace, eraseAttributes); + _FillRect(page, { 0, viewport.top, bufferWidth, row }, whitespace, eraseAttributes); + _FillRect(page, { 0, row, col + 1, row + 1 }, whitespace, eraseAttributes); } if (eraseType == DispatchTypes::EraseType::ToEnd) { textBuffer.ResetLineRenditionRange(col > 0 ? row + 1 : row, viewport.bottom); - _FillRect(textBuffer, { col, row, bufferWidth, row + 1 }, whitespace, eraseAttributes); - _FillRect(textBuffer, { 0, row + 1, bufferWidth, viewport.bottom }, whitespace, eraseAttributes); + _FillRect(page, { col, row, bufferWidth, row + 1 }, whitespace, eraseAttributes); + _FillRect(page, { 0, row + 1, bufferWidth, viewport.bottom }, whitespace, eraseAttributes); } return true; @@ -865,17 +865,17 @@ bool AdaptDispatch::EraseInLine(const DispatchTypes::EraseType eraseType) // The EL control is expected to reset the delayed wrap flag. textBuffer.GetCursor().ResetDelayEOLWrap(); - const auto eraseAttributes = _GetEraseAttributes(textBuffer); + const auto eraseAttributes = _GetEraseAttributes(page); switch (eraseType) { case DispatchTypes::EraseType::FromBeginning: - _FillRect(textBuffer, { 0, row, col + 1, row + 1 }, whitespace, eraseAttributes); + _FillRect(page, { 0, row, col + 1, row + 1 }, whitespace, eraseAttributes); return true; case DispatchTypes::EraseType::ToEnd: - _FillRect(textBuffer, { col, row, textBuffer.GetLineWidth(row), row + 1 }, whitespace, eraseAttributes); + _FillRect(page, { col, row, textBuffer.GetLineWidth(row), row + 1 }, whitespace, eraseAttributes); return true; case DispatchTypes::EraseType::All: - _FillRect(textBuffer, { 0, row, textBuffer.GetLineWidth(row), row + 1 }, whitespace, eraseAttributes); + _FillRect(page, { 0, row, textBuffer.GetLineWidth(row), row + 1 }, whitespace, eraseAttributes); return true; default: return false; @@ -885,17 +885,17 @@ bool AdaptDispatch::EraseInLine(const DispatchTypes::EraseType eraseType) // Routine Description: // - Selectively erases unprotected cells in an area of the buffer. // Arguments: -// - textBuffer - Target buffer to be erased. -// - eraseRect - Area of the buffer that will be affected. +// - page - Target page to be erased. +// - eraseRect - Area of the page that will be affected. // Return Value: // - -void AdaptDispatch::_SelectiveEraseRect(TextBuffer& textBuffer, const til::rect& eraseRect) +void AdaptDispatch::_SelectiveEraseRect(const Page& page, const til::rect& eraseRect) { if (eraseRect) { for (auto row = eraseRect.top; row < eraseRect.bottom; row++) { - auto& rowBuffer = textBuffer.GetMutableRowByOffset(row); + auto& rowBuffer = page.Buffer().GetMutableRowByOffset(row); for (auto col = eraseRect.left; col < eraseRect.right; col++) { // Only unprotected cells are affected. @@ -903,7 +903,7 @@ void AdaptDispatch::_SelectiveEraseRect(TextBuffer& textBuffer, const til::rect& { // The text is cleared but the attributes are left as is. rowBuffer.ClearCell(col); - textBuffer.TriggerRedraw(Viewport::FromCoord({ col, row })); + page.Buffer().TriggerRedraw(Viewport::FromCoord({ col, row })); } } } @@ -935,15 +935,15 @@ bool AdaptDispatch::SelectiveEraseInDisplay(const DispatchTypes::EraseType erase switch (eraseType) { case DispatchTypes::EraseType::FromBeginning: - _SelectiveEraseRect(textBuffer, { 0, viewport.top, bufferWidth, row }); - _SelectiveEraseRect(textBuffer, { 0, row, col + 1, row + 1 }); + _SelectiveEraseRect(page, { 0, viewport.top, bufferWidth, row }); + _SelectiveEraseRect(page, { 0, row, col + 1, row + 1 }); return true; case DispatchTypes::EraseType::ToEnd: - _SelectiveEraseRect(textBuffer, { col, row, bufferWidth, row + 1 }); - _SelectiveEraseRect(textBuffer, { 0, row + 1, bufferWidth, viewport.bottom }); + _SelectiveEraseRect(page, { col, row, bufferWidth, row + 1 }); + _SelectiveEraseRect(page, { 0, row + 1, bufferWidth, viewport.bottom }); return true; case DispatchTypes::EraseType::All: - _SelectiveEraseRect(textBuffer, { 0, viewport.top, bufferWidth, viewport.bottom }); + _SelectiveEraseRect(page, { 0, viewport.top, bufferWidth, viewport.bottom }); return true; default: return false; @@ -972,13 +972,13 @@ bool AdaptDispatch::SelectiveEraseInLine(const DispatchTypes::EraseType eraseTyp switch (eraseType) { case DispatchTypes::EraseType::FromBeginning: - _SelectiveEraseRect(textBuffer, { 0, row, col + 1, row + 1 }); + _SelectiveEraseRect(page, { 0, row, col + 1, row + 1 }); return true; case DispatchTypes::EraseType::ToEnd: - _SelectiveEraseRect(textBuffer, { col, row, textBuffer.GetLineWidth(row), row + 1 }); + _SelectiveEraseRect(page, { col, row, textBuffer.GetLineWidth(row), row + 1 }); return true; case DispatchTypes::EraseType::All: - _SelectiveEraseRect(textBuffer, { 0, row, textBuffer.GetLineWidth(row), row + 1 }); + _SelectiveEraseRect(page, { 0, row, textBuffer.GetLineWidth(row), row + 1 }); return true; default: return false; @@ -988,18 +988,18 @@ bool AdaptDispatch::SelectiveEraseInLine(const DispatchTypes::EraseType eraseTyp // Routine Description: // - Changes the attributes of each cell in a rectangular area of the buffer. // Arguments: -// - textBuffer - Target buffer to be changed. -// - changeRect - A rectangular area of the buffer that will be affected. +// - page - Target page to be changed. +// - changeRect - A rectangular area of the page that will be affected. // - changeOps - Changes that will be applied to each of the attributes. // Return Value: // - -void AdaptDispatch::_ChangeRectAttributes(TextBuffer& textBuffer, const til::rect& changeRect, const ChangeOps& changeOps) +void AdaptDispatch::_ChangeRectAttributes(const Page& page, const til::rect& changeRect, const ChangeOps& changeOps) { if (changeRect) { for (auto row = changeRect.top; row < changeRect.bottom; row++) { - auto& rowBuffer = textBuffer.GetMutableRowByOffset(row); + auto& rowBuffer = page.Buffer().GetMutableRowByOffset(row); for (auto col = changeRect.left; col < changeRect.right; col++) { auto attr = rowBuffer.GetAttrByColumn(col); @@ -1022,7 +1022,7 @@ void AdaptDispatch::_ChangeRectAttributes(TextBuffer& textBuffer, const til::rec rowBuffer.ReplaceAttributes(col, col + 1, attr); } } - textBuffer.TriggerRedraw(Viewport::FromExclusive(changeRect)); + page.Buffer().TriggerRedraw(Viewport::FromExclusive(changeRect)); _api.NotifyAccessibilityChange(changeRect); } } @@ -1039,7 +1039,7 @@ void AdaptDispatch::_ChangeRectAttributes(TextBuffer& textBuffer, const til::rec void AdaptDispatch::_ChangeRectOrStreamAttributes(const til::rect& changeArea, const ChangeOps& changeOps) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); + const auto& textBuffer = page.Buffer(); const auto bufferSize = textBuffer.GetSize().Dimensions(); const auto changeRect = _CalculateRectArea(page, changeArea.top, changeArea.left, changeArea.bottom, changeArea.right); const auto lineCount = changeRect.height(); @@ -1048,7 +1048,7 @@ void AdaptDispatch::_ChangeRectOrStreamAttributes(const til::rect& changeArea, c // single call. The same is true for a stream extent that is only one line. if (_modes.test(Mode::RectangularChangeExtent) || lineCount == 1) { - _ChangeRectAttributes(textBuffer, changeRect, changeOps); + _ChangeRectAttributes(page, changeRect, changeOps); } // If the stream extent is more than one line we require three passes. The // top line is altered from the left offset up to the end of the line. The @@ -1058,9 +1058,9 @@ void AdaptDispatch::_ChangeRectOrStreamAttributes(const til::rect& changeArea, c else if (lineCount > 1 && changeRect.right > changeRect.left) { const auto bufferWidth = bufferSize.width; - _ChangeRectAttributes(textBuffer, { changeRect.origin(), til::size{ bufferWidth - changeRect.left, 1 } }, changeOps); - _ChangeRectAttributes(textBuffer, { { 0, changeRect.top + 1 }, til::size{ bufferWidth, lineCount - 2 } }, changeOps); - _ChangeRectAttributes(textBuffer, { { 0, changeRect.bottom - 1 }, til::size{ changeRect.right, 1 } }, changeOps); + _ChangeRectAttributes(page, { changeRect.origin(), til::size{ bufferWidth - changeRect.left, 1 } }, changeOps); + _ChangeRectAttributes(page, { { 0, changeRect.top + 1 }, til::size{ bufferWidth, lineCount - 2 } }, changeOps); + _ChangeRectAttributes(page, { { 0, changeRect.bottom - 1 }, til::size{ changeRect.right, 1 } }, changeOps); } } @@ -1082,7 +1082,7 @@ til::rect AdaptDispatch::_CalculateRectArea(const Page& page, const VTInt top, c // We start by calculating the margin offsets and maximum dimensions. // If the origin mode isn't set, we use the viewport extent. - const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, false); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, false); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferSize.width); const auto yOffset = _modes.test(Mode::Origin) ? topMargin : 0; const auto yMaximum = _modes.test(Mode::Origin) ? bottomMargin + 1 : viewport.height(); @@ -1285,7 +1285,7 @@ bool AdaptDispatch::CopyRectangularArea(const VTInt top, const VTInt left, const bool AdaptDispatch::FillRectangularArea(const VTParameter ch, const VTInt top, const VTInt left, const VTInt bottom, const VTInt right) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); + const auto& textBuffer = page.Buffer(); const auto fillRect = _CalculateRectArea(page, top, left, bottom, right); // The standard only allows for characters in the range of the GL and GR @@ -1299,7 +1299,7 @@ bool AdaptDispatch::FillRectangularArea(const VTParameter ch, const VTInt top, c { const auto fillChar = _termOutput.TranslateKey(gsl::narrow_cast(charValue)); const auto& fillAttributes = textBuffer.GetCurrentAttributes(); - _FillRect(textBuffer, fillRect, { &fillChar, 1 }, fillAttributes); + _FillRect(page, fillRect, { &fillChar, 1 }, fillAttributes); } return true; @@ -1318,10 +1318,9 @@ bool AdaptDispatch::FillRectangularArea(const VTParameter ch, const VTInt top, c bool AdaptDispatch::EraseRectangularArea(const VTInt top, const VTInt left, const VTInt bottom, const VTInt right) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); const auto eraseRect = _CalculateRectArea(page, top, left, bottom, right); - const auto eraseAttributes = _GetEraseAttributes(textBuffer); - _FillRect(textBuffer, eraseRect, whitespace, eraseAttributes); + const auto eraseAttributes = _GetEraseAttributes(page); + _FillRect(page, eraseRect, whitespace, eraseAttributes); return true; } @@ -1338,9 +1337,8 @@ bool AdaptDispatch::EraseRectangularArea(const VTInt top, const VTInt left, cons bool AdaptDispatch::SelectiveEraseRectangularArea(const VTInt top, const VTInt left, const VTInt bottom, const VTInt right) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); const auto eraseRect = _CalculateRectArea(page, top, left, bottom, right); - _SelectiveEraseRect(textBuffer, eraseRect); + _SelectiveEraseRect(page, eraseRect); return true; } @@ -1462,7 +1460,7 @@ bool AdaptDispatch::SetLineRendition(const LineRendition rendition) { const auto page = _pages.ActivePage(); auto& textBuffer = page.Buffer(); - const auto eraseAttributes = _GetEraseAttributes(textBuffer); + const auto eraseAttributes = _GetEraseAttributes(page); textBuffer.SetCurrentLineRendition(rendition, eraseAttributes); // There is some variation in how this was handled by the different DEC // terminals, but the STD 070 reference (on page D-13) makes it clear that @@ -1686,7 +1684,7 @@ void AdaptDispatch::_CursorPositionReport(const bool extendedReport) if (_modes.test(Mode::Origin)) { cursorPosition.x -= _GetHorizontalMargins(textBuffer.GetSize().Width()).first; - cursorPosition.y -= _GetVerticalMargins(viewport, false).first; + cursorPosition.y -= _GetVerticalMargins(page, false).first; } // Now send it back into the input channel of the console. @@ -1742,12 +1740,11 @@ void AdaptDispatch::_MacroChecksumReport(const VTParameter id) const void AdaptDispatch::_ScrollMovement(const VTInt delta) { const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); - auto& textBuffer = page.Buffer(); + const auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); - const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); - _ScrollRectVertically(textBuffer, { leftMargin, topMargin, rightMargin + 1, bottomMargin + 1 }, delta); + _ScrollRectVertically(page, { leftMargin, topMargin, rightMargin + 1, bottomMargin + 1 }, delta); } // Routine Description: @@ -1896,8 +1893,7 @@ void AdaptDispatch::_SetAlternateScreenBufferMode(const bool enable) { CursorSaveState(); const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); - _api.UseAlternateScreenBuffer(_GetEraseAttributes(textBuffer)); + _api.UseAlternateScreenBuffer(_GetEraseAttributes(page)); _usingAltBuffer = true; } else @@ -2227,7 +2223,6 @@ bool AdaptDispatch::SetKeypadMode(const bool fApplicationMode) void AdaptDispatch::_InsertDeleteLineHelper(const VTInt delta) { const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); @@ -2235,12 +2230,12 @@ void AdaptDispatch::_InsertDeleteLineHelper(const VTInt delta) const auto col = cursor.GetPosition().x; const auto row = cursor.GetPosition().y; - const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); if (row >= topMargin && row <= bottomMargin && col >= leftMargin && col <= rightMargin) { // We emulate inserting and deleting by scrolling the area between the cursor and the bottom margin. - _ScrollRectVertically(textBuffer, { leftMargin, row, rightMargin + 1, bottomMargin + 1 }, delta); + _ScrollRectVertically(page, { leftMargin, row, rightMargin + 1, bottomMargin + 1 }, delta); // The IL and DL controls are also expected to move the cursor to the left margin. cursor.SetXPosition(leftMargin); @@ -2289,7 +2284,6 @@ bool AdaptDispatch::DeleteLine(const VTInt distance) void AdaptDispatch::_InsertDeleteColumnHelper(const VTInt delta) { const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); @@ -2297,12 +2291,12 @@ void AdaptDispatch::_InsertDeleteColumnHelper(const VTInt delta) const auto col = cursor.GetPosition().x; const auto row = cursor.GetPosition().y; - const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); if (row >= topMargin && row <= bottomMargin && col >= leftMargin && col <= rightMargin) { // We emulate inserting and deleting by scrolling the area between the cursor and the right margin. - _ScrollRectHorizontally(textBuffer, { col, topMargin, rightMargin + 1, bottomMargin + 1 }, delta); + _ScrollRectHorizontally(page, { col, topMargin, rightMargin + 1, bottomMargin + 1 }, delta); } } @@ -2557,7 +2551,7 @@ void AdaptDispatch::_DoLineFeed(const Page& page, const bool withReturn, const b auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto bufferHeight = textBuffer.GetSize().Height(); - const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); auto& cursor = textBuffer.GetCursor(); @@ -2591,7 +2585,7 @@ void AdaptDispatch::_DoLineFeed(const Page& page, const bool withReturn, const b // If the top margin isn't at the top of the viewport, or the // horizontal margins are set, then we're just scrolling the margin // area and the cursor stays where it is. - _ScrollRectVertically(textBuffer, { leftMargin, topMargin, rightMargin + 1, bottomMargin + 1 }, -1); + _ScrollRectVertically(page, { leftMargin, topMargin, rightMargin + 1, bottomMargin + 1 }, -1); } else if (viewport.bottom < bufferHeight) { @@ -2607,11 +2601,11 @@ void AdaptDispatch::_DoLineFeed(const Page& page, const bool withReturn, const b // pan we reset the newly revealed row with the erase attributes. if (bottomMargin < viewport.bottom - 1) { - _ScrollRectVertically(textBuffer, { 0, bottomMargin + 1, bufferWidth, viewport.bottom + 1 }, 1); + _ScrollRectVertically(page, { 0, bottomMargin + 1, bufferWidth, viewport.bottom + 1 }, 1); } else { - const auto eraseAttributes = _GetEraseAttributes(textBuffer); + const auto eraseAttributes = _GetEraseAttributes(page); textBuffer.GetMutableRowByOffset(newPosition.y).Reset(eraseAttributes); } } @@ -2620,7 +2614,7 @@ void AdaptDispatch::_DoLineFeed(const Page& page, const bool withReturn, const b // If the viewport has reached the end of the buffer, we can't pan down, // so we cycle the row coordinates, which effectively scrolls the buffer // content up. In this case we don't need to move the cursor down. - const auto eraseAttributes = _GetEraseAttributes(textBuffer); + const auto eraseAttributes = _GetEraseAttributes(page); textBuffer.IncrementCircularBuffer(eraseAttributes); _api.NotifyBufferRotation(1); @@ -2634,7 +2628,7 @@ void AdaptDispatch::_DoLineFeed(const Page& page, const bool withReturn, const b // copy the lower part of the viewport down so it remains static. if (bottomMargin < viewport.bottom - 1) { - _ScrollRectVertically(textBuffer, { 0, bottomMargin, bufferWidth, bufferHeight }, 1); + _ScrollRectVertically(page, { 0, bottomMargin, bufferWidth, bufferHeight }, 1); } } @@ -2684,13 +2678,13 @@ bool AdaptDispatch::ReverseLineFeed() const auto cursorPosition = cursor.GetPosition(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); - const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, 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 && cursorPosition.x >= leftMargin && cursorPosition.x <= rightMargin) { - _ScrollRectVertically(textBuffer, { leftMargin, topMargin, rightMargin + 1, bottomMargin + 1 }, 1); + _ScrollRectVertically(page, { leftMargin, topMargin, rightMargin + 1, bottomMargin + 1 }, 1); } else if (cursorPosition.y > viewport.top) { @@ -2711,18 +2705,17 @@ bool AdaptDispatch::ReverseLineFeed() bool AdaptDispatch::BackIndex() { const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); auto& textBuffer = page.Buffer(); auto& cursor = textBuffer.GetCursor(); const auto cursorPosition = cursor.GetPosition(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); - const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); // If the cursor is at the left of the margin area, we shift the buffer right. if (cursorPosition.x == leftMargin && cursorPosition.y >= topMargin && cursorPosition.y <= bottomMargin) { - _ScrollRectHorizontally(textBuffer, { leftMargin, topMargin, rightMargin + 1, bottomMargin + 1 }, 1); + _ScrollRectHorizontally(page, { leftMargin, topMargin, rightMargin + 1, bottomMargin + 1 }, 1); } // Otherwise we move the cursor left, but not past the start of the line. else if (cursorPosition.x > 0) @@ -2743,18 +2736,17 @@ bool AdaptDispatch::BackIndex() bool AdaptDispatch::ForwardIndex() { const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); auto& textBuffer = page.Buffer(); auto& cursor = textBuffer.GetCursor(); const auto cursorPosition = cursor.GetPosition(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); - const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); // If the cursor is at the right of the margin area, we shift the buffer left. if (cursorPosition.x == rightMargin && cursorPosition.y >= topMargin && cursorPosition.y <= bottomMargin) { - _ScrollRectHorizontally(textBuffer, { leftMargin, topMargin, rightMargin + 1, bottomMargin + 1 }, -1); + _ScrollRectHorizontally(page, { leftMargin, topMargin, rightMargin + 1, bottomMargin + 1 }, -1); } // Otherwise we move the cursor right, but not past the end of the line. else if (cursorPosition.x < textBuffer.GetLineWidth(cursorPosition.y) - 1) @@ -2815,8 +2807,7 @@ bool AdaptDispatch::ForwardTab(const VTInt numTabs) const auto width = textBuffer.GetLineWidth(row); auto tabsPerformed = 0; - const auto viewport = page.Viewport(); - const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(width); const auto clampToMargin = row >= topMargin && row <= bottomMargin && column <= rightMargin; const auto maxColumn = clampToMargin ? rightMargin : width - 1; @@ -2865,8 +2856,7 @@ bool AdaptDispatch::BackwardsTab(const VTInt numTabs) const auto width = textBuffer.GetLineWidth(row); auto tabsPerformed = 0; - const auto viewport = page.Viewport(); - const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, true); + const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(width); const auto clampToMargin = row >= topMargin && row <= bottomMargin && column >= leftMargin; const auto minColumn = clampToMargin ? leftMargin : 0; @@ -3319,7 +3309,7 @@ bool AdaptDispatch::ScreenAlignmentPattern() const auto bufferWidth = textBuffer.GetSize().Dimensions().width; // Fill the screen with the letter E using the default attributes. - _FillRect(textBuffer, { 0, viewport.top, bufferWidth, viewport.bottom }, L"E", {}); + _FillRect(page, { 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). @@ -3432,8 +3422,8 @@ bool AdaptDispatch::_EraseAll() cursor.SetHasMoved(true); // Erase all the rows in the current viewport. - const auto eraseAttributes = _GetEraseAttributes(textBuffer); - _FillRect(textBuffer, { 0, newViewportTop, bufferSize.Width(), newViewportBottom }, whitespace, eraseAttributes); + const auto eraseAttributes = _GetEraseAttributes(page); + _FillRect(page, { 0, newViewportTop, bufferSize.Width(), newViewportBottom }, whitespace, eraseAttributes); // Also reset the line rendition for the erased rows. textBuffer.ResetLineRenditionRange(newViewportTop, newViewportBottom); @@ -4488,8 +4478,7 @@ void AdaptDispatch::_ReportDECSTBMSetting() response.append(L"\033P1$r"sv); const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); - const auto [marginTop, marginBottom] = _GetVerticalMargins(viewport, false); + const auto [marginTop, marginBottom] = _GetVerticalMargins(page, 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); @@ -4674,7 +4663,7 @@ void AdaptDispatch::_ReportCursorInformation() if (_modes.test(Mode::Origin)) { cursorPosition.x -= _GetHorizontalMargins(textBuffer.GetSize().Width()).first; - cursorPosition.y -= _GetVerticalMargins(viewport, false).first; + cursorPosition.y -= _GetVerticalMargins(page, false).first; } // Only some of the rendition attributes are reported. diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 8419332991c..44ff91a4556 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -223,20 +223,20 @@ namespace Microsoft::Console::VirtualTerminal }; void _WriteToBuffer(const std::wstring_view string); - std::pair _GetVerticalMargins(const til::rect& viewport, const bool absolute) noexcept; + std::pair _GetVerticalMargins(const Page& page, const bool absolute) noexcept; std::pair _GetHorizontalMargins(const til::CoordType bufferWidth) noexcept; 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 std::wstring_view& fillChar, const TextAttribute& fillAttrs) const; - void _SelectiveEraseRect(TextBuffer& textBuffer, const til::rect& eraseRect); - void _ChangeRectAttributes(TextBuffer& textBuffer, const til::rect& changeRect, const ChangeOps& changeOps); + void _FillRect(const Page& page, const til::rect& fillRect, const std::wstring_view& fillChar, const TextAttribute& fillAttrs) const; + void _SelectiveEraseRect(const Page& page, const til::rect& eraseRect); + void _ChangeRectAttributes(const Page& page, const til::rect& changeRect, const ChangeOps& changeOps); void _ChangeRectOrStreamAttributes(const til::rect& changeArea, const ChangeOps& changeOps); til::rect _CalculateRectArea(const Page& page, const VTInt top, const VTInt left, const VTInt bottom, const VTInt right); bool _EraseScrollback(); bool _EraseAll(); - TextAttribute _GetEraseAttributes(const TextBuffer& textBuffer) const noexcept; - void _ScrollRectVertically(TextBuffer& textBuffer, const til::rect& scrollRect, const VTInt delta); - void _ScrollRectHorizontally(TextBuffer& textBuffer, const til::rect& scrollRect, const VTInt delta); + TextAttribute _GetEraseAttributes(const Page& page) const noexcept; + void _ScrollRectVertically(const Page& page, const til::rect& scrollRect, const VTInt delta); + void _ScrollRectHorizontally(const Page& page, const til::rect& scrollRect, const VTInt delta); void _InsertDeleteCharacterHelper(const VTInt delta); void _InsertDeleteLineHelper(const VTInt delta); void _InsertDeleteColumnHelper(const VTInt delta); From f82f40dbefb3c63d729c025899b98e264bc13410 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 25 Jan 2024 23:37:34 +0000 Subject: [PATCH 09/19] Handle cursor and attribute access through Page object. --- src/terminal/adapter/PageManager.cpp | 22 +++ src/terminal/adapter/PageManager.hpp | 3 + src/terminal/adapter/adaptDispatch.cpp | 170 +++++++++--------- .../adapter/adaptDispatchGraphics.cpp | 18 +- 4 files changed, 118 insertions(+), 95 deletions(-) diff --git a/src/terminal/adapter/PageManager.cpp b/src/terminal/adapter/PageManager.cpp index 84da971fecd..98e1f8e1bbc 100644 --- a/src/terminal/adapter/PageManager.cpp +++ b/src/terminal/adapter/PageManager.cpp @@ -30,6 +30,28 @@ til::CoordType Page::Number() const noexcept return _number; } +Cursor& Page::Cursor() const noexcept +{ + return _buffer.GetCursor(); +} + +const TextAttribute& Page::Attributes() const noexcept +{ + return _buffer.GetCurrentAttributes(); +} + +void Page::SetAttributes(const TextAttribute& attr, ITerminalApi* api) const +{ + _buffer.SetCurrentAttributes(attr); + // If the api parameter was specified, we need to pass the new attributes + // through to the api. This occurs when there's a potential for the colors + // to be changed, which may require some legacy remapping in conhost. + if (api) + { + api->SetTextAttributes(attr); + } +} + PageManager::PageManager(ITerminalApi& api, Renderer& renderer) noexcept : _api{ api }, _renderer{ renderer } diff --git a/src/terminal/adapter/PageManager.hpp b/src/terminal/adapter/PageManager.hpp index 87fcda14312..eace4753126 100644 --- a/src/terminal/adapter/PageManager.hpp +++ b/src/terminal/adapter/PageManager.hpp @@ -23,6 +23,9 @@ namespace Microsoft::Console::VirtualTerminal TextBuffer& Buffer() const noexcept; til::rect Viewport() const noexcept; til::CoordType Number() const noexcept; + Cursor& Cursor() const noexcept; + const TextAttribute& Attributes() const noexcept; + void SetAttributes(const TextAttribute& attr, ITerminalApi* api = nullptr) const; private: TextBuffer& _buffer; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index e35978e3e8c..c6eeca8a3b7 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -76,10 +76,10 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string) { const auto page = _pages.ActivePage(); auto& textBuffer = page.Buffer(); - auto& cursor = textBuffer.GetCursor(); + auto& cursor = page.Cursor(); auto cursorPosition = cursor.GetPosition(); const auto wrapAtEOL = _api.GetSystemMode(ITerminalApi::Mode::AutoWrap); - const auto& attributes = textBuffer.GetCurrentAttributes(); + const auto& attributes = page.Attributes(); const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(textBuffer.GetSize().Width()); @@ -339,8 +339,8 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col // First retrieve some information about the buffer const auto page = _pages.ActivePage(); const auto viewport = page.Viewport(); - auto& textBuffer = page.Buffer(); - auto& cursor = textBuffer.GetCursor(); + const auto& textBuffer = page.Buffer(); + auto& cursor = page.Cursor(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto cursorPosition = cursor.GetPosition(); const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); @@ -505,11 +505,10 @@ bool AdaptDispatch::CursorSaveState() const auto page = _pages.ActivePage(); const auto viewport = page.Viewport(); const auto& textBuffer = page.Buffer(); - 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. - auto cursorPosition = textBuffer.GetCursor().GetPosition(); + auto cursorPosition = page.Cursor().GetPosition(); cursorPosition.y -= viewport.top; // Although if origin mode is set, the cursor is relative to the margin origin. @@ -524,9 +523,9 @@ bool AdaptDispatch::CursorSaveState() savedCursorState.Column = cursorPosition.x + 1; savedCursorState.Row = cursorPosition.y + 1; savedCursorState.Page = page.Number(); - savedCursorState.IsDelayedEOLWrap = textBuffer.GetCursor().IsDelayedEOLWrap(); + savedCursorState.IsDelayedEOLWrap = page.Cursor().IsDelayedEOLWrap(); savedCursorState.IsOriginModeRelative = _modes.test(Mode::Origin); - savedCursorState.Attributes = attributes; + savedCursorState.Attributes = page.Attributes(); savedCursorState.TermOutput = _termOutput; return true; @@ -554,13 +553,14 @@ bool AdaptDispatch::CursorRestoreState() CursorPosition(savedCursorState.Row, savedCursorState.Column); // If the delayed wrap flag was set when the cursor was saved, we need to restore that now. + const auto page = _pages.ActivePage(); if (savedCursorState.IsDelayedEOLWrap) { - _pages.ActivePage().Buffer().GetCursor().DelayEOLWrap(); + page.Cursor().DelayEOLWrap(); } // Restore text attributes. - _api.SetTextAttributes(savedCursorState.Attributes); + page.SetAttributes(savedCursorState.Attributes, &_api); // Restore designated character sets. _termOutput.RestoreFrom(savedCursorState.TermOutput); @@ -584,7 +584,7 @@ TextAttribute AdaptDispatch::_GetEraseAttributes(const Page& page) const noexcep } else { - auto eraseAttributes = page.Buffer().GetCurrentAttributes(); + auto eraseAttributes = page.Attributes(); eraseAttributes.SetStandardErase(); return eraseAttributes; } @@ -702,9 +702,9 @@ void AdaptDispatch::_ScrollRectHorizontally(const Page& page, const til::rect& s void AdaptDispatch::_InsertDeleteCharacterHelper(const VTInt delta) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); - const auto row = textBuffer.GetCursor().GetPosition().y; - const auto col = textBuffer.GetCursor().GetPosition().x; + const auto& textBuffer = page.Buffer(); + const auto row = page.Cursor().GetPosition().y; + const auto col = page.Cursor().GetPosition().x; const auto lineWidth = textBuffer.GetLineWidth(row); const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); const auto [leftMargin, rightMargin] = (row >= topMargin && row <= bottomMargin) ? @@ -714,7 +714,7 @@ void AdaptDispatch::_InsertDeleteCharacterHelper(const VTInt delta) { _ScrollRectHorizontally(page, { col, row, rightMargin + 1, row + 1 }, delta); // The ICH and DCH controls are expected to reset the delayed wrap flag. - textBuffer.GetCursor().ResetDelayEOLWrap(); + page.Cursor().ResetDelayEOLWrap(); } } @@ -771,13 +771,13 @@ void AdaptDispatch::_FillRect(const Page& page, const til::rect& fillRect, const bool AdaptDispatch::EraseCharacters(const VTInt numChars) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); - const auto row = textBuffer.GetCursor().GetPosition().y; - const auto startCol = textBuffer.GetCursor().GetPosition().x; + const auto& textBuffer = page.Buffer(); + const auto row = page.Cursor().GetPosition().y; + const auto startCol = page.Cursor().GetPosition().x; const auto endCol = std::min(startCol + numChars, textBuffer.GetLineWidth(row)); // The ECH control is expected to reset the delayed wrap flag. - textBuffer.GetCursor().ResetDelayEOLWrap(); + page.Cursor().ResetDelayEOLWrap(); const auto eraseAttributes = _GetEraseAttributes(page); _FillRect(page, { startCol, row, endCol, row + 1 }, whitespace, eraseAttributes); @@ -817,13 +817,13 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) const auto viewport = page.Viewport(); auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); - const auto row = textBuffer.GetCursor().GetPosition().y; - const auto col = textBuffer.GetCursor().GetPosition().x; + const auto row = page.Cursor().GetPosition().y; + const auto col = page.Cursor().GetPosition().x; // The ED control is expected to reset the delayed wrap flag. // The special case variants above ("erase all" and "erase scrollback") // take care of that themselves when they set the cursor position. - textBuffer.GetCursor().ResetDelayEOLWrap(); + page.Cursor().ResetDelayEOLWrap(); const auto eraseAttributes = _GetEraseAttributes(page); @@ -858,12 +858,12 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) bool AdaptDispatch::EraseInLine(const DispatchTypes::EraseType eraseType) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); - const auto row = textBuffer.GetCursor().GetPosition().y; - const auto col = textBuffer.GetCursor().GetPosition().x; + const auto& textBuffer = page.Buffer(); + const auto row = page.Cursor().GetPosition().y; + const auto col = page.Cursor().GetPosition().x; // The EL control is expected to reset the delayed wrap flag. - textBuffer.GetCursor().ResetDelayEOLWrap(); + page.Cursor().ResetDelayEOLWrap(); const auto eraseAttributes = _GetEraseAttributes(page); switch (eraseType) @@ -924,13 +924,13 @@ bool AdaptDispatch::SelectiveEraseInDisplay(const DispatchTypes::EraseType erase { const auto page = _pages.ActivePage(); const auto viewport = page.Viewport(); - auto& textBuffer = page.Buffer(); + const auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); - const auto row = textBuffer.GetCursor().GetPosition().y; - const auto col = textBuffer.GetCursor().GetPosition().x; + const auto row = page.Cursor().GetPosition().y; + const auto col = page.Cursor().GetPosition().x; // The DECSED control is expected to reset the delayed wrap flag. - textBuffer.GetCursor().ResetDelayEOLWrap(); + page.Cursor().ResetDelayEOLWrap(); switch (eraseType) { @@ -962,12 +962,12 @@ bool AdaptDispatch::SelectiveEraseInDisplay(const DispatchTypes::EraseType erase bool AdaptDispatch::SelectiveEraseInLine(const DispatchTypes::EraseType eraseType) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); - const auto row = textBuffer.GetCursor().GetPosition().y; - const auto col = textBuffer.GetCursor().GetPosition().x; + const auto& textBuffer = page.Buffer(); + const auto row = page.Cursor().GetPosition().y; + const auto col = page.Cursor().GetPosition().x; // The DECSEL control is expected to reset the delayed wrap flag. - textBuffer.GetCursor().ResetDelayEOLWrap(); + page.Cursor().ResetDelayEOLWrap(); switch (eraseType) { @@ -1285,7 +1285,6 @@ bool AdaptDispatch::CopyRectangularArea(const VTInt top, const VTInt left, const bool AdaptDispatch::FillRectangularArea(const VTParameter ch, const VTInt top, const VTInt left, const VTInt bottom, const VTInt right) { const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); const auto fillRect = _CalculateRectArea(page, top, left, bottom, right); // The standard only allows for characters in the range of the GL and GR @@ -1298,7 +1297,7 @@ bool AdaptDispatch::FillRectangularArea(const VTParameter ch, const VTInt top, c if (glChar || grChar || unicodeChar) { const auto fillChar = _termOutput.TranslateKey(gsl::narrow_cast(charValue)); - const auto& fillAttributes = textBuffer.GetCurrentAttributes(); + const auto& fillAttributes = page.Attributes(); _FillRect(page, fillRect, { &fillChar, 1 }, fillAttributes); } @@ -1466,7 +1465,7 @@ bool AdaptDispatch::SetLineRendition(const LineRendition rendition) // terminals, but the STD 070 reference (on page D-13) makes it clear that // the delayed wrap (aka the Last Column Flag) was expected to be reset when // line rendition controls were executed. - textBuffer.GetCursor().ResetDelayEOLWrap(); + page.Cursor().ResetDelayEOLWrap(); } return true; } @@ -1671,7 +1670,7 @@ void AdaptDispatch::_CursorPositionReport(const bool extendedReport) const auto& textBuffer = page.Buffer(); // First pull the cursor position relative to the entire buffer out of the console. - til::point cursorPosition{ textBuffer.GetCursor().GetPosition() }; + til::point cursorPosition{ page.Cursor().GetPosition() }; // Now adjust it for its position in respect to the current viewport top. cursorPosition.y -= viewport.top; @@ -1971,17 +1970,17 @@ bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con // Resetting DECAWM should also reset the delayed wrap flag. if (!enable) { - _pages.ActivePage().Buffer().GetCursor().ResetDelayEOLWrap(); + _pages.ActivePage().Cursor().ResetDelayEOLWrap(); } return true; case DispatchTypes::ModeParams::DECARM_AutoRepeatMode: _terminalInput.SetInputMode(TerminalInput::Mode::AutoRepeat, enable); return !_PassThroughInputModes(); case DispatchTypes::ModeParams::ATT610_StartCursorBlink: - _pages.ActivePage().Buffer().GetCursor().SetBlinkingAllowed(enable); + _pages.ActivePage().Cursor().SetBlinkingAllowed(enable); return !_api.IsConsolePty(); case DispatchTypes::ModeParams::DECTCEM_TextCursorEnableMode: - _pages.ActivePage().Buffer().GetCursor().SetIsVisible(enable); + _pages.ActivePage().Cursor().SetIsVisible(enable); return true; case DispatchTypes::ModeParams::XTERM_EnableDECCOLMSupport: _modes.set(Mode::AllowDECCOLM, enable); @@ -2130,10 +2129,10 @@ bool AdaptDispatch::RequestMode(const DispatchTypes::ModeParams param) enabled = _terminalInput.GetInputMode(TerminalInput::Mode::AutoRepeat); break; case DispatchTypes::ModeParams::ATT610_StartCursorBlink: - enabled = _pages.ActivePage().Buffer().GetCursor().IsBlinkingAllowed(); + enabled = _pages.ActivePage().Cursor().IsBlinkingAllowed(); break; case DispatchTypes::ModeParams::DECTCEM_TextCursorEnableMode: - enabled = _pages.ActivePage().Buffer().GetCursor().IsVisible(); + enabled = _pages.ActivePage().Cursor().IsVisible(); break; case DispatchTypes::ModeParams::XTERM_EnableDECCOLMSupport: // DECCOLM is not supported in conpty mode @@ -2223,10 +2222,10 @@ bool AdaptDispatch::SetKeypadMode(const bool fApplicationMode) void AdaptDispatch::_InsertDeleteLineHelper(const VTInt delta) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); + const auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); - auto& cursor = textBuffer.GetCursor(); + auto& cursor = page.Cursor(); const auto col = cursor.GetPosition().x; const auto row = cursor.GetPosition().y; @@ -2284,10 +2283,10 @@ bool AdaptDispatch::DeleteLine(const VTInt distance) void AdaptDispatch::_InsertDeleteColumnHelper(const VTInt delta) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); + const auto& textBuffer = page.Buffer(); const auto bufferWidth = textBuffer.GetSize().Width(); - const auto& cursor = textBuffer.GetCursor(); + const auto& cursor = page.Cursor(); const auto col = cursor.GetPosition().x; const auto row = cursor.GetPosition().y; @@ -2554,7 +2553,7 @@ void AdaptDispatch::_DoLineFeed(const Page& page, const bool withReturn, const b const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); - auto& cursor = textBuffer.GetCursor(); + auto& cursor = page.Cursor(); const auto currentPosition = cursor.GetPosition(); auto newPosition = currentPosition; @@ -2673,8 +2672,8 @@ bool AdaptDispatch::ReverseLineFeed() { const auto page = _pages.ActivePage(); const auto viewport = page.Viewport(); - auto& textBuffer = page.Buffer(); - auto& cursor = textBuffer.GetCursor(); + const auto& textBuffer = page.Buffer(); + auto& cursor = page.Cursor(); const auto cursorPosition = cursor.GetPosition(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); @@ -2705,8 +2704,8 @@ bool AdaptDispatch::ReverseLineFeed() bool AdaptDispatch::BackIndex() { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); - auto& cursor = textBuffer.GetCursor(); + const auto& textBuffer = page.Buffer(); + auto& cursor = page.Cursor(); const auto cursorPosition = cursor.GetPosition(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); @@ -2736,8 +2735,8 @@ bool AdaptDispatch::BackIndex() bool AdaptDispatch::ForwardIndex() { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); - auto& cursor = textBuffer.GetCursor(); + const auto& textBuffer = page.Buffer(); + auto& cursor = page.Cursor(); const auto cursorPosition = cursor.GetPosition(); const auto bufferWidth = textBuffer.GetSize().Width(); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); @@ -2780,7 +2779,7 @@ bool AdaptDispatch::HorizontalTabSet() const auto page = _pages.ActivePage(); const auto& textBuffer = page.Buffer(); const auto width = textBuffer.GetSize().Dimensions().width; - const auto column = textBuffer.GetCursor().GetPosition().x; + const auto column = page.Cursor().GetPosition().x; _InitTabStopsForWidth(width); _tabStopColumns.at(column) = true; @@ -2800,8 +2799,8 @@ bool AdaptDispatch::HorizontalTabSet() bool AdaptDispatch::ForwardTab(const VTInt numTabs) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); - auto& cursor = textBuffer.GetCursor(); + const auto& textBuffer = page.Buffer(); + auto& cursor = page.Cursor(); auto column = cursor.GetPosition().x; const auto row = cursor.GetPosition().y; const auto width = textBuffer.GetLineWidth(row); @@ -2849,8 +2848,8 @@ bool AdaptDispatch::ForwardTab(const VTInt numTabs) bool AdaptDispatch::BackwardsTab(const VTInt numTabs) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); - auto& cursor = textBuffer.GetCursor(); + const auto& textBuffer = page.Buffer(); + auto& cursor = page.Cursor(); auto column = cursor.GetPosition().x; const auto row = cursor.GetPosition().y; const auto width = textBuffer.GetLineWidth(row); @@ -2910,7 +2909,7 @@ void AdaptDispatch::_ClearSingleTabStop() const auto page = _pages.ActivePage(); const auto& textBuffer = page.Buffer(); const auto width = textBuffer.GetSize().Dimensions().width; - const auto column = textBuffer.GetCursor().GetPosition().x; + const auto column = page.Cursor().GetPosition().x; _InitTabStopsForWidth(width); _tabStopColumns.at(column) = false; @@ -3149,7 +3148,7 @@ bool AdaptDispatch::AnnounceCodeStructure(const VTInt ansiLevel) // True if handled successfully. False otherwise. bool AdaptDispatch::SoftReset() { - _pages.ActivePage().Buffer().GetCursor().SetIsVisible(true); // Cursor enabled. + _pages.ActivePage().Cursor().SetIsVisible(true); // Cursor enabled. // Replace mode; Absolute cursor addressing; Disallow left/right margins. _modes.reset(Mode::InsertReplace, Mode::Origin, Mode::AllowDECSLRM); @@ -3257,7 +3256,7 @@ bool AdaptDispatch::HardReset() _api.SetSystemMode(ITerminalApi::Mode::BracketedPaste, false); // Restore cursor blinking mode. - _pages.ActivePage().Buffer().GetCursor().SetBlinkingAllowed(true); + _pages.ActivePage().Cursor().SetBlinkingAllowed(true); // Delete all current tab stops and reapply TabSet(DispatchTypes::TabSetType::SetEvery8Columns); @@ -3313,9 +3312,9 @@ bool AdaptDispatch::ScreenAlignmentPattern() // 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). - auto attr = textBuffer.GetCurrentAttributes(); + auto attr = page.Attributes(); attr.SetStandardErase(); - _api.SetTextAttributes(attr); + page.SetAttributes(attr); // Reset the origin mode to absolute, and disallow left/right margins. _modes.reset(Mode::Origin, Mode::AllowDECSLRM); // Clear the scrolling margins. @@ -3347,7 +3346,7 @@ bool AdaptDispatch::_EraseScrollback() const auto height = viewport.bottom - viewport.top; auto& textBuffer = page.Buffer(); const auto bufferSize = textBuffer.GetSize().Dimensions(); - auto& cursor = textBuffer.GetCursor(); + auto& cursor = page.Cursor(); const auto row = cursor.GetPosition().y; textBuffer.ClearScrollback(top, height); @@ -3388,7 +3387,7 @@ bool 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& cursor = textBuffer.GetCursor(); + auto& cursor = page.Cursor(); const auto row = cursor.GetPosition().y - viewport.top; // Calculate new viewport position. Typically we want to move one line below @@ -3491,7 +3490,7 @@ bool AdaptDispatch::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) return false; } - auto& cursor = _pages.ActivePage().Buffer().GetCursor(); + auto& cursor = _pages.ActivePage().Cursor(); cursor.SetType(actualType); cursor.SetBlinkingAllowed(fEnableBlinking); @@ -3677,12 +3676,12 @@ bool AdaptDispatch::WindowManipulation(const DispatchTypes::WindowManipulationTy // - true bool AdaptDispatch::AddHyperlink(const std::wstring_view uri, const std::wstring_view params) { - auto& textBuffer = _pages.ActivePage().Buffer(); - auto attr = textBuffer.GetCurrentAttributes(); - const auto id = textBuffer.GetHyperlinkId(uri, params); + const auto page = _pages.ActivePage(); + auto attr = page.Attributes(); + const auto id = page.Buffer().GetHyperlinkId(uri, params); attr.SetHyperlinkId(id); - textBuffer.SetCurrentAttributes(attr); - textBuffer.AddHyperlinkToMap(uri, id); + page.SetAttributes(attr); + page.Buffer().AddHyperlinkToMap(uri, id); return true; } @@ -3692,10 +3691,10 @@ bool AdaptDispatch::AddHyperlink(const std::wstring_view uri, const std::wstring // - true bool AdaptDispatch::EndHyperlink() { - auto& textBuffer = _pages.ActivePage().Buffer(); - auto attr = textBuffer.GetCurrentAttributes(); + const auto page = _pages.ActivePage(); + auto attr = page.Attributes(); attr.SetHyperlinkId(0); - textBuffer.SetCurrentAttributes(attr); + page.SetAttributes(attr); return true; } @@ -4408,7 +4407,7 @@ void AdaptDispatch::_ReportSGRSetting() const fmt::basic_memory_buffer response; response.append(L"\033P1$r0"sv); - const auto& attr = _pages.ActivePage().Buffer().GetCurrentAttributes(); + const auto& attr = _pages.ActivePage().Attributes(); const auto ulStyle = attr.GetUnderlineStyle(); // For each boolean attribute that is set, we add the appropriate // parameter value to the response string. @@ -4525,7 +4524,7 @@ void AdaptDispatch::_ReportDECSCASetting() const fmt::basic_memory_buffer response; response.append(L"\033P1$r"sv); - const auto& attr = _pages.ActivePage().Buffer().GetCurrentAttributes(); + const auto& attr = _pages.ActivePage().Attributes(); response.append(attr.IsProtected() ? L"1"sv : L"0"sv); // The '"q' indicates this is an DECSCA response, and ST ends the sequence. @@ -4646,8 +4645,8 @@ void AdaptDispatch::_ReportCursorInformation() const auto page = _pages.ActivePage(); const auto viewport = page.Viewport(); const auto& textBuffer = page.Buffer(); - const auto& cursor = textBuffer.GetCursor(); - const auto& attributes = textBuffer.GetCurrentAttributes(); + const auto& cursor = page.Cursor(); + const auto& attributes = page.Attributes(); // First pull the cursor position relative to the entire buffer out of the console. til::point cursorPosition{ cursor.GetPosition() }; @@ -4795,22 +4794,20 @@ ITermDispatch::StringHandler AdaptDispatch::_RestoreCursorInformation() if (state.field == Field::SGR) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); - auto attr = textBuffer.GetCurrentAttributes(); + auto attr = page.Attributes(); attr.SetIntense(state.value & 1); attr.SetUnderlineStyle(state.value & 2 ? UnderlineStyle::SinglyUnderlined : UnderlineStyle::NoUnderline); attr.SetBlinking(state.value & 4); attr.SetReverseVideo(state.value & 8); attr.SetInvisible(state.value & 16); - textBuffer.SetCurrentAttributes(attr); + page.SetAttributes(attr); } else if (state.field == Field::Attr) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); - auto attr = textBuffer.GetCurrentAttributes(); + auto attr = page.Attributes(); attr.SetProtected(state.value & 1); - textBuffer.SetCurrentAttributes(attr); + page.SetAttributes(attr); } else if (state.field == Field::Sizes) { @@ -4837,8 +4834,7 @@ ITermDispatch::StringHandler AdaptDispatch::_RestoreCursorInformation() if (delayedEOLWrap) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); - textBuffer.GetCursor().DelayEOLWrap(); + page.Cursor().DelayEOLWrap(); } } } diff --git a/src/terminal/adapter/adaptDispatchGraphics.cpp b/src/terminal/adapter/adaptDispatchGraphics.cpp index f60662cc913..f7dab41cd1c 100644 --- a/src/terminal/adapter/adaptDispatchGraphics.cpp +++ b/src/terminal/adapter/adaptDispatchGraphics.cpp @@ -422,9 +422,10 @@ void AdaptDispatch::_ApplyGraphicsOptions(const VTParameters options, // - True. bool AdaptDispatch::SetGraphicsRendition(const VTParameters options) { - auto attr = _pages.ActivePage().Buffer().GetCurrentAttributes(); + const auto page = _pages.ActivePage(); + auto attr = page.Attributes(); _ApplyGraphicsOptions(options, attr); - _api.SetTextAttributes(attr); + page.SetAttributes(attr, &_api); return true; } @@ -438,8 +439,8 @@ bool AdaptDispatch::SetGraphicsRendition(const VTParameters options) // - True. bool AdaptDispatch::SetCharacterProtectionAttribute(const VTParameters options) { - auto& textBuffer = _pages.ActivePage().Buffer(); - auto attr = textBuffer.GetCurrentAttributes(); + const auto page = _pages.ActivePage(); + auto attr = page.Attributes(); for (size_t i = 0; i < options.size(); i++) { const LogicalAttributeOptions opt = options.at(i); @@ -456,7 +457,7 @@ bool AdaptDispatch::SetCharacterProtectionAttribute(const VTParameters options) break; } } - textBuffer.SetCurrentAttributes(attr); + page.SetAttributes(attr); return true; } @@ -470,7 +471,7 @@ bool AdaptDispatch::SetCharacterProtectionAttribute(const VTParameters options) // - True. bool AdaptDispatch::PushGraphicsRendition(const VTParameters options) { - const auto& currentAttributes = _pages.ActivePage().Buffer().GetCurrentAttributes(); + const auto& currentAttributes = _pages.ActivePage().Attributes(); _sgrStack.Push(currentAttributes, options); return true; } @@ -484,7 +485,8 @@ bool AdaptDispatch::PushGraphicsRendition(const VTParameters options) // - True. bool AdaptDispatch::PopGraphicsRendition() { - const auto& currentAttributes = _pages.ActivePage().Buffer().GetCurrentAttributes(); - _api.SetTextAttributes(_sgrStack.Pop(currentAttributes)); + const auto page = _pages.ActivePage(); + const auto& currentAttributes = page.Attributes(); + page.SetAttributes(_sgrStack.Pop(currentAttributes), &_api); return true; } From 15a7c46d6008aaa5d459d6a85e38a2062d5ad9c6 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Fri, 26 Jan 2024 01:00:06 +0000 Subject: [PATCH 10/19] Get buffer/viewport dimensions through Page object. --- src/terminal/adapter/PageManager.cpp | 35 +++ src/terminal/adapter/PageManager.hpp | 7 + src/terminal/adapter/adaptDispatch.cpp | 302 +++++++++++-------------- 3 files changed, 173 insertions(+), 171 deletions(-) diff --git a/src/terminal/adapter/PageManager.cpp b/src/terminal/adapter/PageManager.cpp index 98e1f8e1bbc..39f97a12b8a 100644 --- a/src/terminal/adapter/PageManager.cpp +++ b/src/terminal/adapter/PageManager.cpp @@ -52,6 +52,41 @@ void Page::SetAttributes(const TextAttribute& attr, ITerminalApi* api) const } } +til::CoordType Page::Top() const noexcept +{ + return _viewport.top; +} + +til::CoordType Page::Bottom() const noexcept +{ + return _viewport.bottom; +} + +til::CoordType Page::Width() const noexcept +{ + return _buffer.GetSize().Width(); +} + +til::CoordType Page::Height() const noexcept +{ + return _viewport.bottom - _viewport.top; +} + +til::CoordType Page::BufferHeight() const noexcept +{ + return _buffer.GetSize().Height(); +} + +til::CoordType Page::XPanOffset() const noexcept +{ + return _viewport.left; +} + +til::CoordType Page::YPanOffset() const noexcept +{ + return 0; // Vertical panning is not yet supported +} + PageManager::PageManager(ITerminalApi& api, Renderer& renderer) noexcept : _api{ api }, _renderer{ renderer } diff --git a/src/terminal/adapter/PageManager.hpp b/src/terminal/adapter/PageManager.hpp index eace4753126..ab913902609 100644 --- a/src/terminal/adapter/PageManager.hpp +++ b/src/terminal/adapter/PageManager.hpp @@ -26,6 +26,13 @@ namespace Microsoft::Console::VirtualTerminal Cursor& Cursor() const noexcept; const TextAttribute& Attributes() const noexcept; void SetAttributes(const TextAttribute& attr, ITerminalApi* api = nullptr) const; + til::CoordType Top() const noexcept; + til::CoordType Bottom() const noexcept; + til::CoordType Width() const noexcept; + til::CoordType Height() const noexcept; + til::CoordType BufferHeight() const noexcept; + til::CoordType XPanOffset() const noexcept; + til::CoordType YPanOffset() const noexcept; private: TextBuffer& _buffer; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index c6eeca8a3b7..42b43e6d729 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -82,7 +82,7 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string) const auto& attributes = page.Attributes(); const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); - const auto [leftMargin, rightMargin] = _GetHorizontalMargins(textBuffer.GetSize().Width()); + const auto [leftMargin, rightMargin] = _GetHorizontalMargins(page.Width()); auto lineWidth = textBuffer.GetLineWidth(cursorPosition.y); if (cursorPosition.x <= rightMargin && cursorPosition.y >= topMargin && cursorPosition.y <= bottomMargin) @@ -284,7 +284,7 @@ bool AdaptDispatch::CursorPrevLine(const VTInt distance) std::pair AdaptDispatch::_GetVerticalMargins(const Page& page, const bool absolute) noexcept { // If the top is out of range, reset the margins completely. - const auto bottommostRow = page.Viewport().bottom - page.Viewport().top - 1; + const auto bottommostRow = page.Height() - 1; if (_scrollMargins.top >= bottommostRow) { _scrollMargins.top = _scrollMargins.bottom = 0; @@ -297,8 +297,8 @@ std::pair AdaptDispatch::_GetVerticalMargins(const Page& page, const b bottomMargin = std::min(bottomMargin, bottommostRow); if (absolute) { - topMargin += page.Viewport().top; - bottomMargin += page.Viewport().top; + topMargin += page.Top(); + bottomMargin += page.Top(); } return { topMargin, bottomMargin }; } @@ -306,13 +306,13 @@ std::pair AdaptDispatch::_GetVerticalMargins(const Page& page, const b // Routine Description: // - Returns the coordinates of the horizontal scroll margins. // Arguments: -// - bufferWidth - The width of the buffer +// - pageWidth - The width of the page // Return Value: // - A std::pair containing the left and right coordinates (inclusive). -std::pair AdaptDispatch::_GetHorizontalMargins(const til::CoordType bufferWidth) noexcept +std::pair AdaptDispatch::_GetHorizontalMargins(const til::CoordType pageWidth) noexcept { // If the left is out of range, reset the margins completely. - const auto rightmostColumn = bufferWidth - 1; + const auto rightmostColumn = pageWidth - 1; if (_scrollMargins.left >= rightmostColumn) { _scrollMargins.left = _scrollMargins.right = 0; @@ -338,13 +338,12 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col { // First retrieve some information about the buffer const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); const auto& textBuffer = page.Buffer(); auto& cursor = page.Cursor(); - const auto bufferWidth = textBuffer.GetSize().Width(); + const auto pageWidth = page.Width(); const auto cursorPosition = cursor.GetPosition(); const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); - const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); + const auto [leftMargin, rightMargin] = _GetHorizontalMargins(pageWidth); // For relative movement, the given offsets will be relative to // the current cursor position. @@ -352,10 +351,10 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col auto 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. + // page, or the top margin, depending on the origin mode. if (rowOffset.IsAbsolute) { - row = _modes.test(Mode::Origin) ? topMargin : viewport.top; + row = _modes.test(Mode::Origin) ? topMargin : page.Top(); } // And if the column is absolute, it'll be relative to column 0, @@ -367,10 +366,10 @@ 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, + // The row is constrained within the page's vertical boundaries, // while the column is constrained by the buffer width. - row = std::clamp(row + rowOffset.Value, viewport.top, viewport.bottom - 1); - col = std::clamp(col + colOffset.Value, 0, bufferWidth - 1); + row = std::clamp(row + rowOffset.Value, page.Top(), page.Bottom() - 1); + col = std::clamp(col + colOffset.Value, 0, pageWidth - 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 @@ -503,18 +502,16 @@ bool AdaptDispatch::CursorSaveState() { // First retrieve some information about the buffer const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); - const auto& textBuffer = page.Buffer(); // 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. + // But in VT speak, the cursor row should be relative to the current page top. auto cursorPosition = page.Cursor().GetPosition(); - cursorPosition.y -= viewport.top; + cursorPosition.y -= page.Top(); // Although if origin mode is set, the cursor is relative to the margin origin. if (_modes.test(Mode::Origin)) { - cursorPosition.x -= _GetHorizontalMargins(textBuffer.GetSize().Width()).first; + cursorPosition.x -= _GetHorizontalMargins(page.Width()).first; cursorPosition.y -= _GetVerticalMargins(page, false).first; } @@ -608,7 +605,7 @@ void AdaptDispatch::_ScrollRectVertically(const Page& page, const til::rect& scr const auto width = scrollRect.width(); const auto height = scrollRect.height() - absoluteDelta; const auto actualDelta = delta > 0 ? absoluteDelta : -absoluteDelta; - if (width == textBuffer.GetSize().Width()) + if (width == page.Width()) { // If the scrollRect is the full width of the buffer, we can scroll // more efficiently by rotating the row storage. @@ -786,13 +783,13 @@ bool AdaptDispatch::EraseCharacters(const VTInt numChars) } // Routine Description: -// - ED - Erases a portion of the current viewable area (viewport) of the console. +// - ED - Erases a portion of the current page of the console. // Arguments: // - eraseType - Determines whether to erase: // From beginning (top-left corner) to the cursor // From cursor to end (bottom-right corner) -// The entire viewport area -// The scrollback (outside the viewport area) +// The entire page +// The scrollback (outside the page area) // Return Value: // - True if handled successfully. False otherwise. bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) @@ -803,7 +800,7 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) // Scrollback clears erase everything in the "scrollback" of a *nix terminal // Everything that's scrolled off the screen so far. // Or if it's an Erase All, then we also need to handle that specially - // by moving the current contents of the viewport into the scrollback. + // by moving the current contents of the page into the scrollback. if (eraseType == DispatchTypes::EraseType::Scrollback) { return _EraseScrollback(); @@ -814,9 +811,8 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) } const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); auto& textBuffer = page.Buffer(); - const auto bufferWidth = textBuffer.GetSize().Width(); + const auto pageWidth = page.Width(); const auto row = page.Cursor().GetPosition().y; const auto col = page.Cursor().GetPosition().x; @@ -835,15 +831,15 @@ bool AdaptDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) // the line is double width). if (eraseType == DispatchTypes::EraseType::FromBeginning) { - textBuffer.ResetLineRenditionRange(viewport.top, row); - _FillRect(page, { 0, viewport.top, bufferWidth, row }, whitespace, eraseAttributes); + textBuffer.ResetLineRenditionRange(page.Top(), row); + _FillRect(page, { 0, page.Top(), pageWidth, row }, whitespace, eraseAttributes); _FillRect(page, { 0, row, col + 1, row + 1 }, whitespace, eraseAttributes); } if (eraseType == DispatchTypes::EraseType::ToEnd) { - textBuffer.ResetLineRenditionRange(col > 0 ? row + 1 : row, viewport.bottom); - _FillRect(page, { col, row, bufferWidth, row + 1 }, whitespace, eraseAttributes); - _FillRect(page, { 0, row + 1, bufferWidth, viewport.bottom }, whitespace, eraseAttributes); + textBuffer.ResetLineRenditionRange(col > 0 ? row + 1 : row, page.Bottom()); + _FillRect(page, { col, row, pageWidth, row + 1 }, whitespace, eraseAttributes); + _FillRect(page, { 0, row + 1, pageWidth, page.Bottom() }, whitespace, eraseAttributes); } return true; @@ -912,20 +908,18 @@ void AdaptDispatch::_SelectiveEraseRect(const Page& page, const til::rect& erase } // Routine Description: -// - DECSED - Selectively erases unprotected cells in a portion of the viewport. +// - DECSED - Selectively erases unprotected cells in a portion of the page. // Arguments: // - eraseType - Determines whether to erase: // From beginning (top-left corner) to the cursor // From cursor to end (bottom-right corner) -// The entire viewport area +// The entire page area // Return Value: // - True if handled successfully. False otherwise. bool AdaptDispatch::SelectiveEraseInDisplay(const DispatchTypes::EraseType eraseType) { const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); - const auto& textBuffer = page.Buffer(); - const auto bufferWidth = textBuffer.GetSize().Width(); + const auto pageWidth = page.Width(); const auto row = page.Cursor().GetPosition().y; const auto col = page.Cursor().GetPosition().x; @@ -935,15 +929,15 @@ bool AdaptDispatch::SelectiveEraseInDisplay(const DispatchTypes::EraseType erase switch (eraseType) { case DispatchTypes::EraseType::FromBeginning: - _SelectiveEraseRect(page, { 0, viewport.top, bufferWidth, row }); + _SelectiveEraseRect(page, { 0, page.Top(), pageWidth, row }); _SelectiveEraseRect(page, { 0, row, col + 1, row + 1 }); return true; case DispatchTypes::EraseType::ToEnd: - _SelectiveEraseRect(page, { col, row, bufferWidth, row + 1 }); - _SelectiveEraseRect(page, { 0, row + 1, bufferWidth, viewport.bottom }); + _SelectiveEraseRect(page, { col, row, pageWidth, row + 1 }); + _SelectiveEraseRect(page, { 0, row + 1, pageWidth, page.Bottom() }); return true; case DispatchTypes::EraseType::All: - _SelectiveEraseRect(page, { 0, viewport.top, bufferWidth, viewport.bottom }); + _SelectiveEraseRect(page, { 0, page.Top(), pageWidth, page.Bottom() }); return true; default: return false; @@ -1039,8 +1033,6 @@ void AdaptDispatch::_ChangeRectAttributes(const Page& page, const til::rect& cha void AdaptDispatch::_ChangeRectOrStreamAttributes(const til::rect& changeArea, const ChangeOps& changeOps) { const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); - const auto bufferSize = textBuffer.GetSize().Dimensions(); const auto changeRect = _CalculateRectArea(page, changeArea.top, changeArea.left, changeArea.bottom, changeArea.right); const auto lineCount = changeRect.height(); @@ -1057,9 +1049,9 @@ void AdaptDispatch::_ChangeRectOrStreamAttributes(const til::rect& changeArea, c // must be greater than the left, otherwise the operation is ignored. else if (lineCount > 1 && changeRect.right > changeRect.left) { - const auto bufferWidth = bufferSize.width; - _ChangeRectAttributes(page, { changeRect.origin(), til::size{ bufferWidth - changeRect.left, 1 } }, changeOps); - _ChangeRectAttributes(page, { { 0, changeRect.top + 1 }, til::size{ bufferWidth, lineCount - 2 } }, changeOps); + const auto pageWidth = page.Width(); + _ChangeRectAttributes(page, { changeRect.origin(), til::size{ pageWidth - changeRect.left, 1 } }, changeOps); + _ChangeRectAttributes(page, { { 0, changeRect.top + 1 }, til::size{ pageWidth, lineCount - 2 } }, changeOps); _ChangeRectAttributes(page, { { 0, changeRect.bottom - 1 }, til::size{ changeRect.right, 1 } }, changeOps); } } @@ -1077,17 +1069,17 @@ void AdaptDispatch::_ChangeRectOrStreamAttributes(const til::rect& changeArea, c // - An exclusive rect with the absolute buffer coordinates. til::rect AdaptDispatch::_CalculateRectArea(const Page& page, const VTInt top, const VTInt left, const VTInt bottom, const VTInt right) { - const auto bufferSize = page.Buffer().GetSize().Dimensions(); - const auto viewport = page.Viewport(); + const auto pageWidth = page.Width(); + const auto pageHeight = page.Height(); // We start by calculating the margin offsets and maximum dimensions. - // If the origin mode isn't set, we use the viewport extent. + // If the origin mode isn't set, we use the page extent. const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, false); - const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferSize.width); + const auto [leftMargin, rightMargin] = _GetHorizontalMargins(pageWidth); const auto yOffset = _modes.test(Mode::Origin) ? topMargin : 0; - const auto yMaximum = _modes.test(Mode::Origin) ? bottomMargin + 1 : viewport.height(); + const auto yMaximum = _modes.test(Mode::Origin) ? bottomMargin + 1 : pageHeight; const auto xOffset = _modes.test(Mode::Origin) ? leftMargin : 0; - const auto xMaximum = _modes.test(Mode::Origin) ? rightMargin + 1 : bufferSize.width; + const auto xMaximum = _modes.test(Mode::Origin) ? rightMargin + 1 : pageWidth; auto fillRect = til::inclusive_rect{}; fillRect.left = left + xOffset; @@ -1103,9 +1095,9 @@ til::rect AdaptDispatch::_CalculateRectArea(const Page& page, const VTInt top, c fillRect.top = std::min(fillRect.top, yMaximum) - 1; fillRect.bottom = std::min(fillRect.bottom, yMaximum) - 1; - // To get absolute coordinates we offset with the viewport top. - fillRect.top += viewport.top; - fillRect.bottom += viewport.top; + // To get absolute coordinates we offset with the page top. + fillRect.top += page.Top(); + fillRect.bottom += page.Top(); return til::rect{ fillRect }; } @@ -1657,7 +1649,7 @@ void AdaptDispatch::_DeviceStatusReport(const wchar_t* parameters) const } // Routine Description: -// - CPR and DECXCPR- Reports the current cursor position within the viewport, +// - CPR and DECXCPR- Reports the current cursor position within the page, // as well as the current page number if this is an extended report. // Arguments: // - extendedReport - Set to true if the report should include the page number @@ -1666,23 +1658,21 @@ void AdaptDispatch::_DeviceStatusReport(const wchar_t* parameters) const void AdaptDispatch::_CursorPositionReport(const bool extendedReport) { const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); - const auto& textBuffer = page.Buffer(); // First pull the cursor position relative to the entire buffer out of the console. til::point cursorPosition{ page.Cursor().GetPosition() }; - // Now adjust it for its position in respect to the current viewport top. - cursorPosition.y -= viewport.top; + // Now adjust it for its position in respect to the current page top. + cursorPosition.y -= page.Top(); - // NOTE: 1,1 is the top-left corner of the viewport in VT-speak, so add 1. + // NOTE: 1,1 is the top-left corner of the page in VT-speak, so add 1. cursorPosition.x++; cursorPosition.y++; // If the origin mode is set, the cursor is relative to the margin origin. if (_modes.test(Mode::Origin)) { - cursorPosition.x -= _GetHorizontalMargins(textBuffer.GetSize().Width()).first; + cursorPosition.x -= _GetHorizontalMargins(page.Width()).first; cursorPosition.y -= _GetVerticalMargins(page, false).first; } @@ -1739,10 +1729,8 @@ void AdaptDispatch::_MacroChecksumReport(const VTParameter id) const void AdaptDispatch::_ScrollMovement(const VTInt delta) { const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); - const auto bufferWidth = textBuffer.GetSize().Width(); const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); - const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); + const auto [leftMargin, rightMargin] = _GetHorizontalMargins(page.Width()); _ScrollRectVertically(page, { leftMargin, topMargin, rightMargin + 1, bottomMargin + 1 }, delta); } @@ -1846,8 +1834,8 @@ bool AdaptDispatch::RequestDisplayedExtent() const auto page = _pages.VisiblePage(); const auto width = page.Viewport().width(); const auto height = page.Viewport().height(); - const auto left = page.Viewport().left + 1; - const auto top = 1; + const auto left = page.XPanOffset() + 1; + const auto top = page.YPanOffset() + 1; _api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033[{};{};{};{};{}\"w"), height, width, left, top, page.Number())); return true; } @@ -1865,10 +1853,9 @@ void AdaptDispatch::_SetColumnMode(const bool enable) if (_modes.test(Mode::AllowDECCOLM) && !_api.IsConsolePty()) { const auto page = _pages.VisiblePage(); - const auto viewport = page.Viewport(); - const auto viewportHeight = viewport.bottom - viewport.top; - const auto viewportWidth = (enable ? DispatchTypes::s_sDECCOLMSetColumns : DispatchTypes::s_sDECCOLMResetColumns); - _api.ResizeWindow(viewportWidth, viewportHeight); + const auto pageHeight = page.Height(); + const auto pageWidth = (enable ? DispatchTypes::s_sDECCOLMSetColumns : DispatchTypes::s_sDECCOLMResetColumns); + _api.ResizeWindow(pageWidth, pageHeight); _modes.set(Mode::Column, enable); _modes.reset(Mode::Origin, Mode::AllowDECSLRM); CursorPosition(1, 1); @@ -2005,9 +1992,8 @@ bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con { // If we've allowed left/right margins, we can't have line renditions. const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); auto& textBuffer = page.Buffer(); - textBuffer.ResetLineRenditionRange(viewport.top, viewport.bottom); + textBuffer.ResetLineRenditionRange(page.Top(), page.Bottom()); } return true; case DispatchTypes::ModeParams::DECECM_EraseColorMode: @@ -2222,15 +2208,12 @@ bool AdaptDispatch::SetKeypadMode(const bool fApplicationMode) void AdaptDispatch::_InsertDeleteLineHelper(const VTInt delta) { const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); - const auto bufferWidth = textBuffer.GetSize().Width(); - auto& cursor = page.Cursor(); const auto col = cursor.GetPosition().x; const auto row = cursor.GetPosition().y; const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); - const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); + const auto [leftMargin, rightMargin] = _GetHorizontalMargins(page.Width()); if (row >= topMargin && row <= bottomMargin && col >= leftMargin && col <= rightMargin) { // We emulate inserting and deleting by scrolling the area between the cursor and the bottom margin. @@ -2283,15 +2266,12 @@ bool AdaptDispatch::DeleteLine(const VTInt distance) void AdaptDispatch::_InsertDeleteColumnHelper(const VTInt delta) { const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); - const auto bufferWidth = textBuffer.GetSize().Width(); - const auto& cursor = page.Cursor(); const auto col = cursor.GetPosition().x; const auto row = cursor.GetPosition().y; const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); - const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); + const auto [leftMargin, rightMargin] = _GetHorizontalMargins(page.Width()); if (row >= topMargin && row <= bottomMargin && col >= leftMargin && col <= rightMargin) { // We emulate inserting and deleting by scrolling the area between the cursor and the right margin. @@ -2368,23 +2348,22 @@ void AdaptDispatch::_DoSetTopBottomScrollingMargins(const VTInt topMargin, til::CoordType actualBottom = bottomMargin; const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); - const auto screenHeight = viewport.bottom - viewport.top; + const auto pageHeight = page.Height(); // The default top margin is line 1 if (actualTop == 0) { actualTop = 1; } - // The default bottom margin is the screen height + // The default bottom margin is the page height if (actualBottom == 0) { - actualBottom = screenHeight; + actualBottom = pageHeight; } // The top margin must be less than the bottom margin, and the - // bottom margin must be less than or equal to the screen height - if (actualTop < actualBottom && actualBottom <= screenHeight) + // bottom margin must be less than or equal to the page height + if (actualTop < actualBottom && actualBottom <= pageHeight) { - if (actualTop == 1 && actualBottom == screenHeight) + if (actualTop == 1 && actualBottom == pageHeight) { // Client requests setting margins to the entire screen // - clear them instead of setting them. @@ -2446,23 +2425,22 @@ void AdaptDispatch::_DoSetLeftRightScrollingMargins(const VTInt leftMargin, til::CoordType actualRight = rightMargin; const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); - const auto bufferWidth = textBuffer.GetSize().Width(); + const auto pageWidth = page.Width(); // The default left margin is column 1 if (actualLeft == 0) { actualLeft = 1; } - // The default right margin is the buffer width + // The default right margin is the page width if (actualRight == 0) { - actualRight = bufferWidth; + actualRight = pageWidth; } // The left margin must be less than the right margin, and the // right margin must be less than or equal to the buffer width - if (actualLeft < actualRight && actualRight <= bufferWidth) + if (actualLeft < actualRight && actualRight <= pageWidth) { - if (actualLeft == 1 && actualRight == bufferWidth) + if (actualLeft == 1 && actualRight == pageWidth) { // Client requests setting margins to the entire screen // - clear them instead of setting them. @@ -2546,12 +2524,11 @@ bool AdaptDispatch::CarriageReturn() // - void AdaptDispatch::_DoLineFeed(const Page& page, const bool withReturn, const bool wrapForced) { - const auto viewport = page.Viewport(); auto& textBuffer = page.Buffer(); - const auto bufferWidth = textBuffer.GetSize().Width(); - const auto bufferHeight = textBuffer.GetSize().Height(); + const auto pageWidth = page.Width(); + const auto bufferHeight = page.BufferHeight(); const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); - const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); + const auto [leftMargin, rightMargin] = _GetHorizontalMargins(pageWidth); auto& cursor = page.Cursor(); const auto currentPosition = cursor.GetPosition(); @@ -2575,32 +2552,32 @@ void AdaptDispatch::_DoLineFeed(const Page& page, const bool withReturn, const b { // If we're not at the bottom margin, or outside the horizontal margins, // then there's no scrolling, so we make sure we don't move past the - // bottom of the viewport. - newPosition.y = std::min(currentPosition.y + 1, viewport.bottom - 1); + // bottom of the page. + newPosition.y = std::min(currentPosition.y + 1, page.Bottom() - 1); newPosition = textBuffer.ClampPositionWithinLine(newPosition); } - else if (topMargin > viewport.top || leftMargin > 0 || rightMargin < bufferWidth - 1) + else if (topMargin > page.Top() || leftMargin > 0 || rightMargin < pageWidth - 1) { - // If the top margin isn't at the top of the viewport, or the + // If the top margin isn't at the top of the page, or the // horizontal margins are set, then we're just scrolling the margin // area and the cursor stays where it is. _ScrollRectVertically(page, { leftMargin, topMargin, rightMargin + 1, bottomMargin + 1 }, -1); } - else if (viewport.bottom < bufferHeight) + else if (page.Bottom() < bufferHeight) { - // If the top margin is at the top of the viewport, then we'll scroll + // If the top margin is at the top of the page, then we'll scroll // the content up by panning the viewport down, and also move the cursor // down a row. But we only do this if the viewport hasn't yet reached // the end of the buffer. - _api.SetViewportPosition({ viewport.left, viewport.top + 1 }); + _api.SetViewportPosition({ page.XPanOffset(), page.Top() + 1 }); newPosition.y++; - // And if the bottom margin didn't cover the full viewport, we copy the - // lower part of the viewport down so it remains static. But for a full + // And if the bottom margin didn't cover the full page, we copy the + // lower part of the page down so it remains static. But for a full // pan we reset the newly revealed row with the erase attributes. - if (bottomMargin < viewport.bottom - 1) + if (bottomMargin < page.Bottom() - 1) { - _ScrollRectVertically(page, { 0, bottomMargin + 1, bufferWidth, viewport.bottom + 1 }, 1); + _ScrollRectVertically(page, { 0, bottomMargin + 1, pageWidth, page.Bottom() + 1 }, 1); } else { @@ -2623,11 +2600,11 @@ void AdaptDispatch::_DoLineFeed(const Page& page, const bool withReturn, const b cursor.SetIsOn(false); textBuffer.TriggerScroll({ 0, -1 }); - // And again, if the bottom margin didn't cover the full viewport, we - // copy the lower part of the viewport down so it remains static. - if (bottomMargin < viewport.bottom - 1) + // And again, if the bottom margin didn't cover the full page, we + // copy the lower part of the page down so it remains static. + if (bottomMargin < page.Bottom() - 1) { - _ScrollRectVertically(page, { 0, bottomMargin, bufferWidth, bufferHeight }, 1); + _ScrollRectVertically(page, { 0, bottomMargin, pageWidth, bufferHeight }, 1); } } @@ -2671,12 +2648,10 @@ bool AdaptDispatch::LineFeed(const DispatchTypes::LineFeedType lineFeedType) bool AdaptDispatch::ReverseLineFeed() { const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); const auto& textBuffer = page.Buffer(); auto& cursor = page.Cursor(); const auto cursorPosition = cursor.GetPosition(); - const auto bufferWidth = textBuffer.GetSize().Width(); - const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); + const auto [leftMargin, rightMargin] = _GetHorizontalMargins(page.Width()); const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); // If the cursor is at the top of the margin area, we shift the buffer @@ -2685,9 +2660,9 @@ bool AdaptDispatch::ReverseLineFeed() { _ScrollRectVertically(page, { leftMargin, topMargin, rightMargin + 1, bottomMargin + 1 }, 1); } - else if (cursorPosition.y > viewport.top) + else if (cursorPosition.y > page.Top()) { - // Otherwise we move the cursor up, but not past the top of the viewport. + // Otherwise we move the cursor up, but not past the top of the page. cursor.SetPosition(textBuffer.ClampPositionWithinLine({ cursorPosition.x, cursorPosition.y - 1 })); _ApplyCursorMovementFlags(cursor); } @@ -2704,11 +2679,9 @@ bool AdaptDispatch::ReverseLineFeed() bool AdaptDispatch::BackIndex() { const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); auto& cursor = page.Cursor(); const auto cursorPosition = cursor.GetPosition(); - const auto bufferWidth = textBuffer.GetSize().Width(); - const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); + const auto [leftMargin, rightMargin] = _GetHorizontalMargins(page.Width()); const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); // If the cursor is at the left of the margin area, we shift the buffer right. @@ -2738,8 +2711,7 @@ bool AdaptDispatch::ForwardIndex() const auto& textBuffer = page.Buffer(); auto& cursor = page.Cursor(); const auto cursorPosition = cursor.GetPosition(); - const auto bufferWidth = textBuffer.GetSize().Width(); - const auto [leftMargin, rightMargin] = _GetHorizontalMargins(bufferWidth); + const auto [leftMargin, rightMargin] = _GetHorizontalMargins(page.Width()); const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); // If the cursor is at the right of the margin area, we shift the buffer left. @@ -2777,11 +2749,9 @@ bool AdaptDispatch::SetWindowTitle(std::wstring_view title) bool AdaptDispatch::HorizontalTabSet() { const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); - const auto width = textBuffer.GetSize().Dimensions().width; const auto column = page.Cursor().GetPosition().x; - _InitTabStopsForWidth(width); + _InitTabStopsForWidth(page.Width()); _tabStopColumns.at(column) = true; return true; @@ -2907,11 +2877,9 @@ bool AdaptDispatch::TabClear(const DispatchTypes::TabClearType clearType) void AdaptDispatch::_ClearSingleTabStop() { const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); - const auto width = textBuffer.GetSize().Dimensions().width; const auto column = page.Cursor().GetPosition().x; - _InitTabStopsForWidth(width); + _InitTabStopsForWidth(page.Width()); _tabStopColumns.at(column) = false; } @@ -3303,14 +3271,12 @@ bool AdaptDispatch::HardReset() bool AdaptDispatch::ScreenAlignmentPattern() { const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); auto& textBuffer = page.Buffer(); - const auto bufferWidth = textBuffer.GetSize().Dimensions().width; // Fill the screen with the letter E using the default attributes. - _FillRect(page, { 0, viewport.top, bufferWidth, viewport.bottom }, L"E", {}); + _FillRect(page, { 0, page.Top(), page.Width(), page.Bottom() }, L"E", {}); // Reset the line rendition for all of these rows. - textBuffer.ResetLineRenditionRange(viewport.top, viewport.bottom); + textBuffer.ResetLineRenditionRange(page.Top(), page.Bottom()); // Reset the meta/extended attributes (but leave the colors unchanged). auto attr = page.Attributes(); attr.SetStandardErase(); @@ -3329,8 +3295,8 @@ bool AdaptDispatch::ScreenAlignmentPattern() //Routine Description: // - Erase Scrollback (^[[3J - ED extension by xterm) // Because conhost doesn't exactly have a scrollback, We have to be tricky here. -// We need to move the entire viewport to 0,0, and clear everything outside -// (0, 0, viewportWidth, viewportHeight) To give the appearance that +// We need to move the entire page to 0,0, and clear everything outside +// (0, 0, pageWidth, pageHeight) To give the appearance that // everything above the viewport was cleared. // We don't want to save the text BELOW the viewport, because in *nix, there isn't anything there // (There isn't a scroll-forward, only a scrollback) @@ -3341,19 +3307,15 @@ bool AdaptDispatch::ScreenAlignmentPattern() bool AdaptDispatch::_EraseScrollback() { const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); - const auto top = viewport.top; - const auto height = viewport.bottom - viewport.top; auto& textBuffer = page.Buffer(); - const auto bufferSize = textBuffer.GetSize().Dimensions(); auto& cursor = page.Cursor(); const auto row = cursor.GetPosition().y; - textBuffer.ClearScrollback(top, height); + textBuffer.ClearScrollback(page.Top(), page.Height()); // Move the viewport - _api.SetViewportPosition({ viewport.left, 0 }); + _api.SetViewportPosition({ page.XPanOffset(), 0 }); // Move the cursor to the same relative location. - cursor.SetYPosition(row - top); + cursor.SetYPosition(row - page.Top()); cursor.SetHasMoved(true); // GH#2715 - If this succeeded, but we're in a conpty, return `false` to @@ -3378,25 +3340,25 @@ bool AdaptDispatch::_EraseScrollback() bool AdaptDispatch::_EraseAll() { const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); - const auto viewportHeight = viewport.bottom - viewport.top; + const auto pageWidth = page.Width(); + const auto pageHeight = page.Height(); + const auto bufferHeight = page.BufferHeight(); auto& textBuffer = page.Buffer(); - const auto bufferSize = textBuffer.GetSize(); const auto inPtyMode = _api.IsConsolePty(); - // Stash away the current position of the cursor within the viewport. + // Stash away the current position of the cursor within the page. // We'll need to restore the cursor to that same relative position, after // we move the viewport. auto& cursor = page.Cursor(); - const auto row = cursor.GetPosition().y - viewport.top; + const auto row = cursor.GetPosition().y - page.Top(); - // Calculate new viewport position. Typically we want to move one line below + // Calculate new page position. Typically we want to move one line below // the last non-space row, but if the last non-space character is the very // start of the buffer, then we shouldn't move down at all. const auto lastChar = textBuffer.GetLastNonSpaceCharacter(); - auto newViewportTop = lastChar == til::point{} ? 0 : lastChar.y + 1; - auto newViewportBottom = newViewportTop + viewportHeight; - const auto delta = newViewportBottom - (bufferSize.Height()); + auto newPageTop = lastChar == til::point{} ? 0 : lastChar.y + 1; + auto newPageBottom = newPageTop + pageHeight; + const auto delta = newPageBottom - bufferHeight; if (delta > 0) { for (auto i = 0; i < delta; i++) @@ -3404,8 +3366,8 @@ bool AdaptDispatch::_EraseAll() textBuffer.IncrementCircularBuffer(); } _api.NotifyBufferRotation(delta); - newViewportTop -= delta; - newViewportBottom -= delta; + newPageTop -= delta; + newPageBottom -= delta; // We don't want to trigger a scroll in pty mode, because we're going to // pass through the ED sequence anyway, and this will just result in the // buffer being scrolled up by two pages instead of one. @@ -3415,21 +3377,21 @@ bool AdaptDispatch::_EraseAll() } } // Move the viewport - _api.SetViewportPosition({ viewport.left, newViewportTop }); + _api.SetViewportPosition({ page.XPanOffset(), newPageTop }); // Restore the relative cursor position - cursor.SetYPosition(row + newViewportTop); + cursor.SetYPosition(row + newPageTop); cursor.SetHasMoved(true); - // Erase all the rows in the current viewport. + // Erase all the rows in the current page. const auto eraseAttributes = _GetEraseAttributes(page); - _FillRect(page, { 0, newViewportTop, bufferSize.Width(), newViewportBottom }, whitespace, eraseAttributes); + _FillRect(page, { 0, newPageTop, pageWidth, newPageBottom }, whitespace, eraseAttributes); // Also reset the line rendition for the erased rows. - textBuffer.ResetLineRenditionRange(newViewportTop, newViewportBottom); + textBuffer.ResetLineRenditionRange(newPageTop, newPageBottom); // Clear any marks that remain below the start of the - textBuffer.ClearMarksInRange(til::point{ 0, newViewportTop }, - til::point{ bufferSize.Width(), bufferSize.Height() }); + textBuffer.ClearMarksInRange(til::point{ 0, newPageTop }, + til::point{ pageWidth, bufferHeight }); // GH#5683 - If this succeeded, but we're in a conpty, return `false` to // make the state machine propagate this ED sequence to the connected @@ -3660,7 +3622,7 @@ bool AdaptDispatch::WindowManipulation(const DispatchTypes::WindowManipulationTy case DispatchTypes::WindowManipulationType::ReportTextSizeInCharacters: { const auto page = _pages.VisiblePage(); - _api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033[8;{};{}t"), page.Viewport().height(), page.Buffer().GetSize().Width())); + _api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033[8;{};{}t"), page.Height(), page.Width())); return true; } default: @@ -4500,8 +4462,8 @@ void AdaptDispatch::_ReportDECSLRMSetting() fmt::basic_memory_buffer response; response.append(L"\033P1$r"sv); - const auto bufferWidth = _pages.ActivePage().Buffer().GetSize().Width(); - const auto [marginLeft, marginRight] = _GetHorizontalMargins(bufferWidth); + const auto pageWidth = _pages.ActivePage().Width(); + const auto [marginLeft, marginRight] = _GetHorizontalMargins(pageWidth); // 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"{};{}"), marginLeft + 1, marginRight + 1); @@ -4643,25 +4605,23 @@ ITermDispatch::StringHandler AdaptDispatch::RestorePresentationState(const Dispa void AdaptDispatch::_ReportCursorInformation() { const auto page = _pages.ActivePage(); - const auto viewport = page.Viewport(); - const auto& textBuffer = page.Buffer(); const auto& cursor = page.Cursor(); const auto& attributes = page.Attributes(); // First pull the cursor position relative to the entire buffer out of the console. til::point cursorPosition{ cursor.GetPosition() }; - // Now adjust it for its position in respect to the current viewport top. - cursorPosition.y -= viewport.top; + // Now adjust it for its position in respect to the current page top. + cursorPosition.y -= page.Top(); - // NOTE: 1,1 is the top-left corner of the viewport in VT-speak, so add 1. + // NOTE: 1,1 is the top-left corner of the page in VT-speak, so add 1. cursorPosition.x++; cursorPosition.y++; // If the origin mode is set, the cursor is relative to the margin origin. if (_modes.test(Mode::Origin)) { - cursorPosition.x -= _GetHorizontalMargins(textBuffer.GetSize().Width()).first; + cursorPosition.x -= _GetHorizontalMargins(page.Width()).first; cursorPosition.y -= _GetVerticalMargins(page, false).first; } @@ -4881,7 +4841,7 @@ void AdaptDispatch::_ReportTabStops() // In order to be compatible with the original hardware terminals, we only // report tab stops up to the current buffer width, even though there may // be positions recorded beyond that limit. - const auto width = _pages.ActivePage().Buffer().GetSize().Dimensions().width; + const auto width = _pages.ActivePage().Width(); _InitTabStopsForWidth(width); using namespace std::string_view_literals; @@ -4917,7 +4877,7 @@ ITermDispatch::StringHandler AdaptDispatch::_RestoreTabStops() // In order to be compatible with the original hardware terminals, we need // to be able to set tab stops up to at least 132 columns, even though the // current buffer width may be less than that. - const auto width = std::max(_pages.ActivePage().Buffer().GetSize().Dimensions().width, 132); + const auto width = std::max(_pages.ActivePage().Width(), 132); _ClearAllTabStops(); _InitTabStopsForWidth(width); From 79f26f5ce3b51290b4bf42b13ed0ae05ad238ade Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sun, 28 Jan 2024 14:59:22 +0000 Subject: [PATCH 11/19] Add some terms to spellbot dictionary. --- .github/actions/spelling/expect/expect.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index e4d2d676eb4..b2d52e5529d 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -419,6 +419,7 @@ DECNKM DECNRCM DECOM decommit +DECPCCM DECPCTERM DECPS DECRARA @@ -427,6 +428,7 @@ DECREQTPARM DECRLM DECRPM DECRQCRA +DECRQDE DECRQM DECRQPSR DECRQSS @@ -2173,6 +2175,7 @@ XManifest XMath XMFLOAT xorg +XPan XResource xsi xstyler @@ -2192,6 +2195,7 @@ YCast YCENTER YCount YLimit +YPan YSubstantial YVIRTUALSCREEN YWalk From 6ded0413e60d3c24e10738e90ba4ea656415cfb1 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 30 Jan 2024 18:58:51 +0000 Subject: [PATCH 12/19] Correct viewport positioning on background pages. --- src/terminal/adapter/adaptDispatch.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 42b43e6d729..8846e99e6e0 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -3306,7 +3306,7 @@ bool AdaptDispatch::ScreenAlignmentPattern() // - True if handled successfully. False otherwise. bool AdaptDispatch::_EraseScrollback() { - const auto page = _pages.ActivePage(); + const auto page = _pages.VisiblePage(); auto& textBuffer = page.Buffer(); auto& cursor = page.Cursor(); const auto row = cursor.GetPosition().y; @@ -3376,8 +3376,11 @@ bool AdaptDispatch::_EraseAll() textBuffer.TriggerScroll({ 0, -delta }); } } - // Move the viewport - _api.SetViewportPosition({ page.XPanOffset(), newPageTop }); + // Move the viewport if necessary. + if (newPageTop != page.Top()) + { + _api.SetViewportPosition({ page.XPanOffset(), newPageTop }); + } // Restore the relative cursor position cursor.SetYPosition(row + newPageTop); cursor.SetHasMoved(true); From 293a73ff4df0549890f9b347dedb03464e47c9b5 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 30 Jan 2024 20:31:12 +0000 Subject: [PATCH 13/19] Use struct in place of tuple. --- src/cascadia/TerminalCore/Terminal.hpp | 2 +- src/cascadia/TerminalCore/TerminalApi.cpp | 2 +- src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp | 2 +- src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp | 2 +- src/host/outputStream.cpp | 2 +- src/host/outputStream.hpp | 2 +- src/terminal/adapter/ITerminalApi.hpp | 9 ++++++++- src/terminal/adapter/InteractDispatch.cpp | 4 ++-- src/terminal/adapter/ut_adapter/adapterTest.cpp | 4 ++-- 9 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index c12ecaff28a..641f81828e8 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -131,7 +131,7 @@ class Microsoft::Terminal::Core::Terminal final : // These methods are defined in TerminalApi.cpp void ReturnResponse(const std::wstring_view response) override; Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() noexcept override; - std::tuple GetBufferAndViewport() noexcept override; + BufferState GetBufferAndViewport() noexcept override; void SetViewportPosition(const til::point position) noexcept override; void SetTextAttributes(const TextAttribute& attrs) noexcept override; void SetSystemMode(const Mode mode, const bool enabled) noexcept override; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 5c2ea8a04cb..83c4d24af01 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -33,7 +33,7 @@ Microsoft::Console::VirtualTerminal::StateMachine& Terminal::GetStateMachine() n return *_stateMachine; } -std::tuple Terminal::GetBufferAndViewport() noexcept +ITerminalApi::BufferState Terminal::GetBufferAndViewport() noexcept { return { _activeBuffer(), til::rect{ _GetMutableViewport().ToInclusive() }, !_inAltBuffer() }; } diff --git a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp index 7974a153dcf..1e55fb1f6e9 100644 --- a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp @@ -46,7 +46,7 @@ namespace TerminalCoreUnitTests TextBuffer& GetTextBuffer(Terminal& term) { - return std::get(term.GetBufferAndViewport()); + return term.GetBufferAndViewport().buffer; } TEST_METHOD(SelectUnit) diff --git a/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp b/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp index ad8a853265e..157bad1805d 100644 --- a/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp @@ -152,7 +152,7 @@ void TerminalApiTest::CursorVisibility() VERIFY_IS_TRUE(term._mainBuffer->GetCursor().IsOn()); VERIFY_IS_TRUE(term._mainBuffer->GetCursor().IsBlinkingAllowed()); - auto& textBuffer = std::get(term.GetBufferAndViewport()); + auto& textBuffer = term.GetBufferAndViewport().buffer; textBuffer.GetCursor().SetIsVisible(false); VERIFY_IS_FALSE(term._mainBuffer->GetCursor().IsVisible()); VERIFY_IS_TRUE(term._mainBuffer->GetCursor().IsOn()); diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 934686de0f4..7ff5264d24e 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -58,7 +58,7 @@ StateMachine& ConhostInternalGetSet::GetStateMachine() // - // Return Value: // - a tuple with the buffer reference, viewport, and main buffer flag. -std::tuple ConhostInternalGetSet::GetBufferAndViewport() +ITerminalApi::BufferState ConhostInternalGetSet::GetBufferAndViewport() { auto& info = _io.GetActiveOutputBuffer(); return { info.GetTextBuffer(), info.GetVirtualViewport().ToExclusive(), info.Next == nullptr }; diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 8c30c415262..28e380cb987 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -32,7 +32,7 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void ReturnResponse(const std::wstring_view response) override; Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() override; - std::tuple GetBufferAndViewport() override; + BufferState GetBufferAndViewport() override; void SetViewportPosition(const til::point position) override; void SetTextAttributes(const TextAttribute& attrs) override; diff --git a/src/terminal/adapter/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index 6d05c80ba46..e09e7ac34d8 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -39,8 +39,15 @@ namespace Microsoft::Console::VirtualTerminal virtual void ReturnResponse(const std::wstring_view response) = 0; + struct BufferState + { + TextBuffer& buffer; + til::rect viewport; + bool isMainBuffer; + }; + virtual StateMachine& GetStateMachine() = 0; - virtual std::tuple GetBufferAndViewport() = 0; + virtual BufferState GetBufferAndViewport() = 0; virtual void SetViewportPosition(const til::point position) = 0; virtual bool IsVtInputEnabled() const = 0; diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp index 0ecbcfbd3b3..a102368ec5b 100644 --- a/src/terminal/adapter/InteractDispatch.cpp +++ b/src/terminal/adapter/InteractDispatch.cpp @@ -108,7 +108,7 @@ bool InteractDispatch::WindowManipulation(const DispatchTypes::WindowManipulatio _api.ShowWindow(false); return true; case DispatchTypes::WindowManipulationType::RefreshWindow: - std::get(_api.GetBufferAndViewport()).TriggerRedrawAll(); + _api.GetBufferAndViewport().buffer.TriggerRedrawAll(); return true; case DispatchTypes::WindowManipulationType::ResizeWindowInCharacters: // TODO:GH#1765 We should introduce a better `ResizeConpty` function to @@ -135,7 +135,7 @@ bool InteractDispatch::WindowManipulation(const DispatchTypes::WindowManipulatio bool InteractDispatch::MoveCursor(const VTInt row, const VTInt col) { // First retrieve some information about the buffer - const auto viewport = std::get(_api.GetBufferAndViewport()); + const auto viewport = _api.GetBufferAndViewport().viewport; // In VT, the origin is 1,1. For our array, it's 0,0. So subtract 1. // Apply boundary tests to ensure the cursor isn't outside the viewport rectangle. diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index de8aed57627..a61386404bf 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -81,7 +81,7 @@ class TestGetSet final : public ITerminalApi return *_stateMachine; } - std::tuple GetBufferAndViewport() override + BufferState GetBufferAndViewport() override { const auto viewport = til::rect{ _viewport.left, _viewport.top, _viewport.right, _viewport.bottom }; return { *_textBuffer.get(), viewport, true }; @@ -3275,7 +3275,7 @@ class AdapterTest setMacroText(63, L"Macro 63"); const auto getBufferOutput = [&]() { - const auto& textBuffer = std::get(_testGetSet->GetBufferAndViewport()); + const auto& textBuffer = _testGetSet->GetBufferAndViewport().buffer; const auto cursorPos = textBuffer.GetCursor().GetPosition(); return textBuffer.GetRowByOffset(cursorPos.y).GetText().substr(0, cursorPos.x); }; From 69e203746dd989f1fbad2886e4959eaaef95b75d Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 30 Jan 2024 22:33:35 +0000 Subject: [PATCH 14/19] Eliminate some unnecessary textbuffer locals. --- src/terminal/adapter/adaptDispatch.cpp | 33 +++++++++----------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 8846e99e6e0..fe25736b791 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -338,7 +338,6 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col { // First retrieve some information about the buffer const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); auto& cursor = page.Cursor(); const auto pageWidth = page.Width(); const auto cursorPosition = cursor.GetPosition(); @@ -409,7 +408,7 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col } // Finally, attempt to set the adjusted cursor position back into the console. - cursor.SetPosition(textBuffer.ClampPositionWithinLine({ col, row })); + cursor.SetPosition(page.Buffer().ClampPositionWithinLine({ col, row })); _ApplyCursorMovementFlags(cursor); return true; @@ -699,10 +698,9 @@ void AdaptDispatch::_ScrollRectHorizontally(const Page& page, const til::rect& s void AdaptDispatch::_InsertDeleteCharacterHelper(const VTInt delta) { const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); const auto row = page.Cursor().GetPosition().y; const auto col = page.Cursor().GetPosition().x; - const auto lineWidth = textBuffer.GetLineWidth(row); + const auto lineWidth = page.Buffer().GetLineWidth(row); const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); const auto [leftMargin, rightMargin] = (row >= topMargin && row <= bottomMargin) ? _GetHorizontalMargins(lineWidth) : @@ -768,10 +766,9 @@ void AdaptDispatch::_FillRect(const Page& page, const til::rect& fillRect, const bool AdaptDispatch::EraseCharacters(const VTInt numChars) { const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); const auto row = page.Cursor().GetPosition().y; const auto startCol = page.Cursor().GetPosition().x; - const auto endCol = std::min(startCol + numChars, textBuffer.GetLineWidth(row)); + const auto endCol = std::min(startCol + numChars, page.Buffer().GetLineWidth(row)); // The ECH control is expected to reset the delayed wrap flag. page.Cursor().ResetDelayEOLWrap(); @@ -1388,7 +1385,6 @@ bool AdaptDispatch::RequestChecksumRectangularArea(const VTInt id, const VTInt p defaultBgIndex = defaultBgIndex < 16 ? defaultBgIndex : 0; const auto target = _pages.Get(page); - const auto& textBuffer = target.Buffer(); const auto eraseRect = _CalculateRectArea(target, top, left, bottom, right); for (auto row = eraseRect.top; row < eraseRect.bottom; row++) { @@ -1399,7 +1395,7 @@ bool AdaptDispatch::RequestChecksumRectangularArea(const VTInt id, const VTInt p // predate Unicode, though, so we'd need a custom mapping table // to lookup the correct checksums. Considering this is only for // testing at the moment, that doesn't seem worth the effort. - const auto cell = textBuffer.GetCellDataAt({ col, row }); + const auto cell = target.Buffer().GetCellDataAt({ col, row }); for (auto ch : cell->Chars()) { // That said, I've made a special allowance for U+2426, @@ -1450,9 +1446,8 @@ bool AdaptDispatch::SetLineRendition(const LineRendition rendition) if (!_modes.test(Mode::AllowDECSLRM)) { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); const auto eraseAttributes = _GetEraseAttributes(page); - textBuffer.SetCurrentLineRendition(rendition, eraseAttributes); + page.Buffer().SetCurrentLineRendition(rendition, eraseAttributes); // There is some variation in how this was handled by the different DEC // terminals, but the STD 070 reference (on page D-13) makes it clear that // the delayed wrap (aka the Last Column Flag) was expected to be reset when @@ -1992,8 +1987,7 @@ bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con { // If we've allowed left/right margins, we can't have line renditions. const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); - textBuffer.ResetLineRenditionRange(page.Top(), page.Bottom()); + page.Buffer().ResetLineRenditionRange(page.Top(), page.Bottom()); } return true; case DispatchTypes::ModeParams::DECECM_EraseColorMode: @@ -2708,7 +2702,6 @@ bool AdaptDispatch::BackIndex() bool AdaptDispatch::ForwardIndex() { const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); auto& cursor = page.Cursor(); const auto cursorPosition = cursor.GetPosition(); const auto [leftMargin, rightMargin] = _GetHorizontalMargins(page.Width()); @@ -2720,7 +2713,7 @@ bool AdaptDispatch::ForwardIndex() _ScrollRectHorizontally(page, { leftMargin, topMargin, rightMargin + 1, bottomMargin + 1 }, -1); } // Otherwise we move the cursor right, but not past the end of the line. - else if (cursorPosition.x < textBuffer.GetLineWidth(cursorPosition.y) - 1) + else if (cursorPosition.x < page.Buffer().GetLineWidth(cursorPosition.y) - 1) { cursor.SetXPosition(cursorPosition.x + 1); _ApplyCursorMovementFlags(cursor); @@ -2769,11 +2762,10 @@ bool AdaptDispatch::HorizontalTabSet() bool AdaptDispatch::ForwardTab(const VTInt numTabs) { const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); auto& cursor = page.Cursor(); auto column = cursor.GetPosition().x; const auto row = cursor.GetPosition().y; - const auto width = textBuffer.GetLineWidth(row); + const auto width = page.Buffer().GetLineWidth(row); auto tabsPerformed = 0; const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); @@ -2818,11 +2810,10 @@ bool AdaptDispatch::ForwardTab(const VTInt numTabs) bool AdaptDispatch::BackwardsTab(const VTInt numTabs) { const auto page = _pages.ActivePage(); - const auto& textBuffer = page.Buffer(); auto& cursor = page.Cursor(); auto column = cursor.GetPosition().x; const auto row = cursor.GetPosition().y; - const auto width = textBuffer.GetLineWidth(row); + const auto width = page.Buffer().GetLineWidth(row); auto tabsPerformed = 0; const auto [topMargin, bottomMargin] = _GetVerticalMargins(page, true); @@ -3271,12 +3262,11 @@ bool AdaptDispatch::HardReset() bool AdaptDispatch::ScreenAlignmentPattern() { const auto page = _pages.ActivePage(); - auto& textBuffer = page.Buffer(); // Fill the screen with the letter E using the default attributes. _FillRect(page, { 0, page.Top(), page.Width(), page.Bottom() }, L"E", {}); // Reset the line rendition for all of these rows. - textBuffer.ResetLineRenditionRange(page.Top(), page.Bottom()); + page.Buffer().ResetLineRenditionRange(page.Top(), page.Bottom()); // Reset the meta/extended attributes (but leave the colors unchanged). auto attr = page.Attributes(); attr.SetStandardErase(); @@ -3307,11 +3297,10 @@ bool AdaptDispatch::ScreenAlignmentPattern() bool AdaptDispatch::_EraseScrollback() { const auto page = _pages.VisiblePage(); - auto& textBuffer = page.Buffer(); auto& cursor = page.Cursor(); const auto row = cursor.GetPosition().y; - textBuffer.ClearScrollback(page.Top(), page.Height()); + page.Buffer().ClearScrollback(page.Top(), page.Height()); // Move the viewport _api.SetViewportPosition({ page.XPanOffset(), 0 }); // Move the cursor to the same relative location. From b35d5ed22c954316de88c014a85e806c259da16f Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 30 Jan 2024 23:34:57 +0000 Subject: [PATCH 15/19] Add some more comments. --- src/terminal/adapter/PageManager.cpp | 45 +++++++++++++++++++++------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/src/terminal/adapter/PageManager.cpp b/src/terminal/adapter/PageManager.cpp index 39f97a12b8a..5408b249738 100644 --- a/src/terminal/adapter/PageManager.cpp +++ b/src/terminal/adapter/PageManager.cpp @@ -54,22 +54,28 @@ void Page::SetAttributes(const TextAttribute& attr, ITerminalApi* api) const til::CoordType Page::Top() const noexcept { + // If we ever support vertical window panning, the page top won't + // necessarily align with the viewport top, so it's best we always + // treat them as distinct properties. return _viewport.top; } til::CoordType Page::Bottom() const noexcept { + // Similarly, the page bottom won't always match the viewport bottom. return _viewport.bottom; } til::CoordType Page::Width() const noexcept { + // The page width could also one day be different from the buffer width, + // so again it's best treated as a distinct property. return _buffer.GetSize().Width(); } til::CoordType Page::Height() const noexcept { - return _viewport.bottom - _viewport.top; + return Bottom() - Top(); } til::CoordType Page::BufferHeight() const noexcept @@ -104,20 +110,29 @@ Page PageManager::Get(const til::CoordType pageNumber) const { const auto requestedPageNumber = std::min(std::max(pageNumber, 1), MAX_PAGES); auto [visibleBuffer, visibleViewport, isMainBuffer] = _api.GetBufferAndViewport(); + + // If we're not in the main buffer (either because an app has enabled the + // alternate buffer mode, or switched the conhost screen buffer), then VT + // paging doesn't apply, so we disregard the requested page number and just + // use the visible buffer (with a fixed page number of 1). if (!isMainBuffer) { return { visibleBuffer, visibleViewport, 1 }; } - else if (requestedPageNumber == _visiblePageNumber) + + // If the requested page number happens to be the visible page, then we + // can also just use the visible buffer as is. + if (requestedPageNumber == _visiblePageNumber) { return { visibleBuffer, visibleViewport, _visiblePageNumber }; } - else - { - const auto pageSize = visibleViewport.size(); - auto& pageBuffer = _getBuffer(requestedPageNumber, pageSize); - return { pageBuffer, til::rect{ pageSize }, requestedPageNumber }; - } + + // Otherwise we're working with a background buffer, so we need to + // retrieve that from the buffer array, and resize it to match the + // active page size. + const auto pageSize = visibleViewport.size(); + auto& pageBuffer = _getBuffer(requestedPageNumber, pageSize); + return { pageBuffer, til::rect{ pageSize }, requestedPageNumber }; } Page PageManager::ActivePage() const @@ -187,9 +202,11 @@ void PageManager::MoveTo(const til::CoordType pageNumber, const bool makeVisible newBuffer.CopyProperties(oldBuffer); newBuffer.GetCursor().SetPosition(position); } - // If we moved from the visible buffer to a background buffer - // we need to hide the cursor in the visible buffer. It should - // be restored when moving back. + // If we moved from the visible buffer to a background buffer we need + // to hide the cursor in the visible buffer. This is because the page + // number is like a third dimension in the cursor coordinate system. + // If the cursor isn't on the visible page, it's the same as if its + // x/y coordinates are outside the visible viewport. if (wasVisible && !isVisible) { visibleBuffer.GetCursor().SetIsVisible(false); @@ -221,10 +238,16 @@ TextBuffer& PageManager::_getBuffer(const til::CoordType pageNumber, const til:: auto& buffer = til::at(_buffers, pageNumber - 1); if (buffer == nullptr) { + // Page buffers are created on demand, and are sized to match the active + // page dimensions without any scrollback rows. buffer = std::make_unique(pageSize, TextAttribute{}, 0, false, _renderer); } else if (buffer->GetSize().Dimensions() != pageSize) { + // If a buffer already exists for the page, and the page dimensions have + // changed while it was inactive, it will need to be resized. + // TODO: We don't currently reflow the existing content in this case, but + // that may be something we want to reconsider. buffer->ResizeTraditional(pageSize); } return *buffer; From d4527255600fd80cb2aa3c077549292f6793b9be Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sun, 18 Feb 2024 16:18:19 +0000 Subject: [PATCH 16/19] Only propagate ED2 through conpty for the active buffer. --- src/terminal/adapter/adaptDispatch.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index fe25736b791..ff1aaf98b06 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -3390,8 +3390,10 @@ bool AdaptDispatch::_EraseAll() // terminal application. While we're in conpty mode, when the client // requests a Erase All operation, we need to manually tell the // connected terminal to do the same thing, so that the terminal will - // move it's own buffer contents into the scrollback. - return !inPtyMode; + // move it's own buffer contents into the scrollback. But this only + // applies if we're in the active buffer, since this should have no + // visible effect for an inactive buffer. + return !(inPtyMode && textBuffer.IsActiveBuffer()); } //Routine Description: From c5d0c121fb50ad3da1201bbc5fcbbe555133a7ad Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 7 Mar 2024 20:25:06 +0000 Subject: [PATCH 17/19] Update the DECXCPR test to check the page number. --- src/terminal/adapter/ut_adapter/adapterTest.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index a61386404bf..6130d2df3f9 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -1587,14 +1587,23 @@ class AdapterTest coordCursorExpected.x++; coordCursorExpected.y++; - // Until we support paging (GH#13892) the reported page number should always be 1. - const auto pageExpected = 1; + // By default, the initial page number should be 1. + auto pageExpected = 1; VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::ExtendedCursorPositionReport, {})); wchar_t pwszBuffer[50]; swprintf_s(pwszBuffer, ARRAYSIZE(pwszBuffer), L"\x1b[?%d;%d;%dR", coordCursorExpected.y, coordCursorExpected.x, pageExpected); _testGetSet->ValidateInputEvent(pwszBuffer); + + // Now test with the page number set to 3. + pageExpected = 3; + _pDispatch->PagePositionAbsolute(pageExpected); + + VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::ExtendedCursorPositionReport, {})); + + swprintf_s(pwszBuffer, ARRAYSIZE(pwszBuffer), L"\x1b[?%d;%d;%dR", coordCursorExpected.y, coordCursorExpected.x, pageExpected); + _testGetSet->ValidateInputEvent(pwszBuffer); } TEST_METHOD(DeviceStatus_MacroSpaceReportTest) From 14de021b2cc9a5dac86c4894d795ce40c829f6fc Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sat, 9 Mar 2024 20:51:14 +0000 Subject: [PATCH 18/19] Add some unit tests for the new paging operations. --- .../adapter/ut_adapter/adapterTest.cpp | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index 6130d2df3f9..03c900d44c9 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -1767,6 +1767,42 @@ class AdapterTest VERIFY_THROWS(_pDispatch->TertiaryDeviceAttributes(), std::exception); } + TEST_METHOD(RequestDisplayedExtentTests) + { + Log::Comment(L"Starting test..."); + + Log::Comment(L"Test 1: Verify DECRQDE response in home position"); + _testGetSet->PrepData(); + _testGetSet->_viewport.left = 0; + _testGetSet->_viewport.right = 80; + _testGetSet->_viewport.top = 0; + _testGetSet->_viewport.bottom = 24; + VERIFY_IS_TRUE(_pDispatch->RequestDisplayedExtent()); + _testGetSet->ValidateInputEvent(L"\x1b[24;80;1;1;1\"w"); + + Log::Comment(L"Test 2: Verify DECRQDE response when panned horizontally"); + _testGetSet->_viewport.left += 5; + _testGetSet->_viewport.right += 5; + VERIFY_IS_TRUE(_pDispatch->RequestDisplayedExtent()); + _testGetSet->ValidateInputEvent(L"\x1b[24;80;6;1;1\"w"); + + Log::Comment(L"Test 3: Verify DECRQDE response on page 3"); + _pDispatch->PagePositionAbsolute(3); + VERIFY_IS_TRUE(_pDispatch->RequestDisplayedExtent()); + _testGetSet->ValidateInputEvent(L"\x1b[24;80;6;1;3\"w"); + + Log::Comment(L"Test 3: Verify DECRQDE response when active page not visible"); + _pDispatch->ResetMode(DispatchTypes::ModeParams::DECPCCM_PageCursorCouplingMode); + _pDispatch->PagePositionAbsolute(1); + VERIFY_IS_TRUE(_pDispatch->RequestDisplayedExtent()); + _testGetSet->ValidateInputEvent(L"\x1b[24;80;6;1;3\"w"); + + Log::Comment(L"Test 4: Verify DECRQDE response when page 1 visible again"); + _pDispatch->SetMode(DispatchTypes::ModeParams::DECPCCM_PageCursorCouplingMode); + VERIFY_IS_TRUE(_pDispatch->RequestDisplayedExtent()); + _testGetSet->ValidateInputEvent(L"\x1b[24;80;6;1;1\"w"); + } + TEST_METHOD(RequestTerminalParametersTests) { Log::Comment(L"Starting test..."); @@ -3367,6 +3403,89 @@ class AdapterTest VERIFY_IS_TRUE(_pDispatch->DoVsCodeAction(LR"(Completions;10;20;30;{ "foo": "what;ever", "bar": 2 })")); } + TEST_METHOD(PageMovementTests) + { + _testGetSet->PrepData(CursorX::XCENTER, CursorY::YCENTER); + auto& pages = _pDispatch->_pages; + const auto startPos = pages.ActivePage().Cursor().GetPosition(); + const auto homePos = til::point{ 0, pages.ActivePage().Top() }; + + // Testing PPA (page position absolute) + VERIFY_ARE_EQUAL(1, pages.ActivePage().Number(), L"Initial page is 1"); + _pDispatch->PagePositionAbsolute(3); + VERIFY_ARE_EQUAL(3, pages.ActivePage().Number(), L"PPA 3 moves to page 3"); + _pDispatch->PagePositionAbsolute(VTParameter{}); + VERIFY_ARE_EQUAL(1, pages.ActivePage().Number(), L"PPA with omitted page moves to 1"); + _pDispatch->PagePositionAbsolute(9999); + VERIFY_ARE_EQUAL(6, pages.ActivePage().Number(), L"PPA is clamped at page 6"); + VERIFY_ARE_EQUAL(startPos, pages.ActivePage().Cursor().GetPosition(), L"Cursor position never changes"); + + _testGetSet->PrepData(CursorX::XCENTER, CursorY::YCENTER); + _pDispatch->PagePositionAbsolute(1); // Reset to page 1 + + // Testing PPR (page position relative) + VERIFY_ARE_EQUAL(1, pages.ActivePage().Number(), L"Initial page is 1"); + _pDispatch->PagePositionRelative(2); + VERIFY_ARE_EQUAL(3, pages.ActivePage().Number(), L"PPR 2 moves forward 2 pages"); + _pDispatch->PagePositionRelative(VTParameter{}); + VERIFY_ARE_EQUAL(4, pages.ActivePage().Number(), L"PPR with omitted count moves forward 1"); + _pDispatch->PagePositionRelative(9999); + VERIFY_ARE_EQUAL(6, pages.ActivePage().Number(), L"PPR is clamped at page 6"); + VERIFY_ARE_EQUAL(startPos, pages.ActivePage().Cursor().GetPosition(), L"Cursor position never changes"); + + _testGetSet->PrepData(CursorX::XCENTER, CursorY::YCENTER); + + // Testing PPB (page position back) + VERIFY_ARE_EQUAL(6, pages.ActivePage().Number(), L"Initial page is 6"); + _pDispatch->PagePositionBack(2); + VERIFY_ARE_EQUAL(4, pages.ActivePage().Number(), L"PPB 2 moves back 2 pages"); + _pDispatch->PagePositionBack(VTParameter{}); + VERIFY_ARE_EQUAL(3, pages.ActivePage().Number(), L"PPB with omitted count moves back 1"); + _pDispatch->PagePositionBack(9999); + VERIFY_ARE_EQUAL(1, pages.ActivePage().Number(), L"PPB is clamped at page 1"); + VERIFY_ARE_EQUAL(startPos, pages.ActivePage().Cursor().GetPosition(), L"Cursor position never changes"); + + _testGetSet->PrepData(CursorX::XCENTER, CursorY::YCENTER); + + // Testing NP (next page) + VERIFY_ARE_EQUAL(1, pages.ActivePage().Number(), L"Initial page is 1"); + _pDispatch->NextPage(2); + VERIFY_ARE_EQUAL(3, pages.ActivePage().Number(), L"NP 2 moves forward 2 pages"); + _pDispatch->NextPage(VTParameter{}); + VERIFY_ARE_EQUAL(4, pages.ActivePage().Number(), L"NP with omitted count moves forward 1"); + _pDispatch->NextPage(9999); + VERIFY_ARE_EQUAL(6, pages.ActivePage().Number(), L"NP is clamped at page 6"); + VERIFY_ARE_EQUAL(homePos, pages.ActivePage().Cursor().GetPosition(), L"Cursor position is reset to home"); + + _testGetSet->PrepData(CursorX::XCENTER, CursorY::YCENTER); + + // Testing PP (preceding page) + VERIFY_ARE_EQUAL(6, pages.ActivePage().Number(), L"Initial page is 6"); + _pDispatch->PrecedingPage(2); + VERIFY_ARE_EQUAL(4, pages.ActivePage().Number(), L"PP 2 moves back 2 pages"); + _pDispatch->PrecedingPage(VTParameter{}); + VERIFY_ARE_EQUAL(3, pages.ActivePage().Number(), L"PP with omitted count moves back 1"); + _pDispatch->PrecedingPage(9999); + VERIFY_ARE_EQUAL(1, pages.ActivePage().Number(), L"PP is clamped at page 1"); + VERIFY_ARE_EQUAL(homePos, pages.ActivePage().Cursor().GetPosition(), L"Cursor position is reset to home"); + + // Testing DECPCCM (page cursor coupling mode) + _pDispatch->SetMode(DispatchTypes::ModeParams::DECPCCM_PageCursorCouplingMode); + _pDispatch->PagePositionAbsolute(2); + VERIFY_ARE_EQUAL(2, pages.ActivePage().Number()); + VERIFY_ARE_EQUAL(2, pages.VisiblePage().Number(), L"Visible page should follow active if DECPCCM set"); + _pDispatch->ResetMode(DispatchTypes::ModeParams::DECPCCM_PageCursorCouplingMode); + _pDispatch->PagePositionAbsolute(4); + VERIFY_ARE_EQUAL(4, pages.ActivePage().Number()); + VERIFY_ARE_EQUAL(2, pages.VisiblePage().Number(), L"Visible page should not change if DECPCCM reset"); + _pDispatch->SetMode(DispatchTypes::ModeParams::DECPCCM_PageCursorCouplingMode); + VERIFY_ARE_EQUAL(4, pages.ActivePage().Number()); + VERIFY_ARE_EQUAL(4, pages.VisiblePage().Number(), L"Active page should become visible when DECPCCM set"); + + // Reset to page 1 + _pDispatch->PagePositionAbsolute(1); + } + private: TerminalInput _terminalInput; std::unique_ptr _testGetSet; From 2488e9a3036ddfaf61861eff6e5dde1ec7f7955a Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 2 May 2024 19:23:38 +0100 Subject: [PATCH 19/19] Replace obsolete GetTextBuffer api calls. --- src/terminal/adapter/adaptDispatch.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index bad52e82e3a..7649fd1e456 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -3743,7 +3743,7 @@ bool AdaptDispatch::DoConEmuAction(const std::wstring_view string) // This seems like basically the same as 133;B - the end of the prompt, the start of the commandline. else if (subParam == 12) { - _api.GetTextBuffer().StartCommand(); + _pages.ActivePage().Buffer().StartCommand(); return true; } @@ -3787,7 +3787,7 @@ bool AdaptDispatch::DoITerm2Action(const std::wstring_view string) bool handled = false; if (action == L"SetMark") { - _api.GetTextBuffer().StartPrompt(); + _pages.ActivePage().Buffer().StartPrompt(); handled = true; } @@ -3833,19 +3833,19 @@ bool AdaptDispatch::DoFinalTermAction(const std::wstring_view string) { case L'A': // FTCS_PROMPT { - _api.GetTextBuffer().StartPrompt(); + _pages.ActivePage().Buffer().StartPrompt(); handled = true; break; } case L'B': // FTCS_COMMAND_START { - _api.GetTextBuffer().StartCommand(); + _pages.ActivePage().Buffer().StartCommand(); handled = true; break; } case L'C': // FTCS_COMMAND_EXECUTED { - _api.GetTextBuffer().StartOutput(); + _pages.ActivePage().Buffer().StartOutput(); handled = true; break; } @@ -3866,7 +3866,7 @@ bool AdaptDispatch::DoFinalTermAction(const std::wstring_view string) UINT_MAX; } - _api.GetTextBuffer().EndCurrentCommand(error); + _pages.ActivePage().Buffer().EndCurrentCommand(error); handled = true; break;