Skip to content

Commit 4a243f0

Browse files
authored
Add support for VT paging operations (#16615)
This PR adds support for multiples pages in the VT architecture, along with new operations for moving between those pages: `NP` (Next Page), `PP` (Preceding Page), `PPA` (Page Position Absolute), `PPR` (Page Position Relative), and `PPB` (Page Position Back). There's also a new mode, `DECPCCM` (Page Cursor Coupling Mode), which determines whether or not the active page is also the visible page, and a new query sequence, `DECRQDE` (Request Displayed Extent), which can be used to query the visible page. ## References and Relevant Issues When combined with `DECCRA` (Copy Rectangular Area), which can copy between pages, you can layer content on top of existing output, and still restore the original data afterwards. So this could serve as an alternative solution to #10810. ## Detailed Description of the Pull Request / Additional comments On the original DEC terminals that supported paging, you couldn't have both paging and scrollback at the same time - only the one or the other. But modern terminals typically allow both, so we support that too. The way it works, the currently visible page will be attached to the scrollback, and any content that scrolls off the top will thus be saved. But the background pages will not have scrollback, so their content is lost if it scrolls off the top. And when the screen is resized, only the visible page will be reflowed. Background pages are not affected by a resize until they become active. At that point they just receive the traditional style of resize, where the content is clipped or padded to match the new dimensions. I'm not sure this is the best way to handle resizing, but we can always consider other approaches once people have had a chance to try it out. ## Validation Steps Performed I've added some unit tests covering the new operations, and also done a lot of manual testing. Closes #13892 Tests added/passed
1 parent 097a2c1 commit 4a243f0

23 files changed

+1059
-475
lines changed

.github/actions/spelling/expect/expect.txt

+4
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ DECNKM
406406
DECNRCM
407407
DECOM
408408
decommit
409+
DECPCCM
409410
DECPCTERM
410411
DECPS
411412
DECRARA
@@ -414,6 +415,7 @@ DECREQTPARM
414415
DECRLM
415416
DECRPM
416417
DECRQCRA
418+
DECRQDE
417419
DECRQM
418420
DECRQPSR
419421
DECRQSS
@@ -2123,6 +2125,7 @@ XIn
21232125
XManifest
21242126
XMath
21252127
xorg
2128+
XPan
21262129
XResource
21272130
xsi
21282131
xstyler
@@ -2142,6 +2145,7 @@ YCast
21422145
YCENTER
21432146
YCount
21442147
YLimit
2148+
YPan
21452149
YSubstantial
21462150
YVIRTUALSCREEN
21472151
YWalk

src/cascadia/TerminalCore/Terminal.hpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,7 @@ class Microsoft::Terminal::Core::Terminal final :
131131
// These methods are defined in TerminalApi.cpp
132132
void ReturnResponse(const std::wstring_view response) override;
133133
Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() noexcept override;
134-
TextBuffer& GetTextBuffer() noexcept override;
135-
til::rect GetViewport() const noexcept override;
134+
BufferState GetBufferAndViewport() noexcept override;
136135
void SetViewportPosition(const til::point position) noexcept override;
137136
void SetTextAttributes(const TextAttribute& attrs) noexcept override;
138137
void SetSystemMode(const Mode mode, const bool enabled) noexcept override;

src/cascadia/TerminalCore/TerminalApi.cpp

+2-7
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,9 @@ Microsoft::Console::VirtualTerminal::StateMachine& Terminal::GetStateMachine() n
3434
return *_stateMachine;
3535
}
3636

37-
TextBuffer& Terminal::GetTextBuffer() noexcept
37+
ITerminalApi::BufferState Terminal::GetBufferAndViewport() noexcept
3838
{
39-
return _activeBuffer();
40-
}
41-
42-
til::rect Terminal::GetViewport() const noexcept
43-
{
44-
return til::rect{ _GetMutableViewport().ToInclusive() };
39+
return { _activeBuffer(), til::rect{ _GetMutableViewport().ToInclusive() }, !_inAltBuffer() };
4540
}
4641

4742
void Terminal::SetViewportPosition(const til::point position) noexcept

src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp

+14-9
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ namespace TerminalCoreUnitTests
4444
VERIFY_ARE_EQUAL(selection, expected);
4545
}
4646

47+
TextBuffer& GetTextBuffer(Terminal& term)
48+
{
49+
return term.GetBufferAndViewport().buffer;
50+
}
51+
4752
TEST_METHOD(SelectUnit)
4853
{
4954
Terminal term{ Terminal::TestDummyMarker{} };
@@ -394,7 +399,7 @@ namespace TerminalCoreUnitTests
394399
const auto burrito = L"\xD83C\xDF2F";
395400

396401
// Insert wide glyph at position (4,10)
397-
term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 });
402+
GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 });
398403
term.Write(burrito);
399404

400405
// Simulate click at (x,y) = (5,10)
@@ -417,7 +422,7 @@ namespace TerminalCoreUnitTests
417422
const auto burrito = L"\xD83C\xDF2F";
418423

419424
// Insert wide glyph at position (4,10)
420-
term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 });
425+
GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 });
421426
term.Write(burrito);
422427

423428
// Simulate click at (x,y) = (5,10)
@@ -440,11 +445,11 @@ namespace TerminalCoreUnitTests
440445
const auto burrito = L"\xD83C\xDF2F";
441446

442447
// Insert wide glyph at position (4,10)
443-
term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 });
448+
GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 });
444449
term.Write(burrito);
445450

446451
// Insert wide glyph at position (7,11)
447-
term.GetTextBuffer().GetCursor().SetPosition({ 7, 11 });
452+
GetTextBuffer(term).GetCursor().SetPosition({ 7, 11 });
448453
term.Write(burrito);
449454

450455
// Simulate ALT + click at (x,y) = (5,8)
@@ -496,7 +501,7 @@ namespace TerminalCoreUnitTests
496501

497502
// Insert text at position (4,10)
498503
const std::wstring_view text = L"doubleClickMe";
499-
term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 });
504+
GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 });
500505
term.Write(text);
501506

502507
// Simulate double click at (x,y) = (5,10)
@@ -540,7 +545,7 @@ namespace TerminalCoreUnitTests
540545

541546
// Insert text at position (4,10)
542547
const std::wstring_view text = L"C:\\Terminal>";
543-
term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 });
548+
GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 });
544549
term.Write(text);
545550

546551
// Simulate click at (x,y) = (15,10)
@@ -568,7 +573,7 @@ namespace TerminalCoreUnitTests
568573

569574
// Insert text at position (4,10)
570575
const std::wstring_view text = L"doubleClickMe dragThroughHere";
571-
term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 });
576+
GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 });
572577
term.Write(text);
573578

574579
// Simulate double click at (x,y) = (5,10)
@@ -597,7 +602,7 @@ namespace TerminalCoreUnitTests
597602

598603
// Insert text at position (21,10)
599604
const std::wstring_view text = L"doubleClickMe dragThroughHere";
600-
term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 });
605+
GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 });
601606
term.Write(text);
602607

603608
// Simulate double click at (x,y) = (21,10)
@@ -685,7 +690,7 @@ namespace TerminalCoreUnitTests
685690

686691
// Insert text at position (4,10)
687692
const std::wstring_view text = L"doubleClickMe dragThroughHere";
688-
term.GetTextBuffer().GetCursor().SetPosition({ 4, 10 });
693+
GetTextBuffer(term).GetCursor().SetPosition({ 4, 10 });
689694
term.Write(text);
690695

691696
// Step 1: Create a selection on "doubleClickMe"

src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ void TerminalApiTest::CursorVisibility()
152152
VERIFY_IS_TRUE(term._mainBuffer->GetCursor().IsOn());
153153
VERIFY_IS_TRUE(term._mainBuffer->GetCursor().IsBlinkingAllowed());
154154

155-
term.GetTextBuffer().GetCursor().SetIsVisible(false);
155+
auto& textBuffer = term.GetBufferAndViewport().buffer;
156+
textBuffer.GetCursor().SetIsVisible(false);
156157
VERIFY_IS_FALSE(term._mainBuffer->GetCursor().IsVisible());
157158
VERIFY_IS_TRUE(term._mainBuffer->GetCursor().IsOn());
158159
VERIFY_IS_TRUE(term._mainBuffer->GetCursor().IsBlinkingAllowed());

src/host/outputStream.cpp

+6-15
Original file line numberDiff line numberDiff line change
@@ -52,25 +52,16 @@ StateMachine& ConhostInternalGetSet::GetStateMachine()
5252
}
5353

5454
// Routine Description:
55-
// - Retrieves the text buffer for the active output buffer.
55+
// - Retrieves the text buffer and virtual viewport for the active output
56+
// buffer. Also returns a flag indicating whether it's the main buffer.
5657
// Arguments:
5758
// - <none>
5859
// Return Value:
59-
// - a reference to the TextBuffer instance.
60-
TextBuffer& ConhostInternalGetSet::GetTextBuffer()
60+
// - a tuple with the buffer reference, viewport, and main buffer flag.
61+
ITerminalApi::BufferState ConhostInternalGetSet::GetBufferAndViewport()
6162
{
62-
return _io.GetActiveOutputBuffer().GetTextBuffer();
63-
}
64-
65-
// Routine Description:
66-
// - Retrieves the virtual viewport of the active output buffer.
67-
// Arguments:
68-
// - <none>
69-
// Return Value:
70-
// - the exclusive coordinates of the viewport.
71-
til::rect ConhostInternalGetSet::GetViewport() const
72-
{
73-
return _io.GetActiveOutputBuffer().GetVirtualViewport().ToExclusive();
63+
auto& info = _io.GetActiveOutputBuffer();
64+
return { info.GetTextBuffer(), info.GetVirtualViewport().ToExclusive(), info.Next == nullptr };
7465
}
7566

7667
// Routine Description:

src/host/outputStream.hpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal::
3232
void ReturnResponse(const std::wstring_view response) override;
3333

3434
Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() override;
35-
TextBuffer& GetTextBuffer() override;
36-
til::rect GetViewport() const override;
35+
BufferState GetBufferAndViewport() override;
3736
void SetViewportPosition(const til::point position) override;
3837

3938
void SetTextAttributes(const TextAttribute& attrs) override;

src/terminal/adapter/DispatchTypes.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
531531
ATT610_StartCursorBlink = DECPrivateMode(12),
532532
DECTCEM_TextCursorEnableMode = DECPrivateMode(25),
533533
XTERM_EnableDECCOLMSupport = DECPrivateMode(40),
534+
DECPCCM_PageCursorCouplingMode = DECPrivateMode(64),
534535
DECNKM_NumericKeypadMode = DECPrivateMode(66),
535536
DECBKM_BackarrowKeyMode = DECPrivateMode(67),
536537
DECLRMM_LeftRightMarginMode = DECPrivateMode(69),

src/terminal/adapter/ITermDispatch.hpp

+6
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch
4949
virtual bool DeleteCharacter(const VTInt count) = 0; // DCH
5050
virtual bool ScrollUp(const VTInt distance) = 0; // SU
5151
virtual bool ScrollDown(const VTInt distance) = 0; // SD
52+
virtual bool NextPage(const VTInt pageCount) = 0; // NP
53+
virtual bool PrecedingPage(const VTInt pageCount) = 0; // PP
54+
virtual bool PagePositionAbsolute(const VTInt page) = 0; // PPA
55+
virtual bool PagePositionRelative(const VTInt pageCount) = 0; // PPR
56+
virtual bool PagePositionBack(const VTInt pageCount) = 0; // PPB
57+
virtual bool RequestDisplayedExtent() = 0; // DECRQDE
5258
virtual bool InsertLine(const VTInt distance) = 0; // IL
5359
virtual bool DeleteLine(const VTInt distance) = 0; // DL
5460
virtual bool InsertColumn(const VTInt distance) = 0; // DECIC

src/terminal/adapter/ITerminalApi.hpp

+8-2
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,15 @@ namespace Microsoft::Console::VirtualTerminal
3939

4040
virtual void ReturnResponse(const std::wstring_view response) = 0;
4141

42+
struct BufferState
43+
{
44+
TextBuffer& buffer;
45+
til::rect viewport;
46+
bool isMainBuffer;
47+
};
48+
4249
virtual StateMachine& GetStateMachine() = 0;
43-
virtual TextBuffer& GetTextBuffer() = 0;
44-
virtual til::rect GetViewport() const = 0;
50+
virtual BufferState GetBufferAndViewport() = 0;
4551
virtual void SetViewportPosition(const til::point position) = 0;
4652

4753
virtual bool IsVtInputEnabled() const = 0;

src/terminal/adapter/InteractDispatch.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ bool InteractDispatch::WindowManipulation(const DispatchTypes::WindowManipulatio
108108
_api.ShowWindow(false);
109109
return true;
110110
case DispatchTypes::WindowManipulationType::RefreshWindow:
111-
_api.GetTextBuffer().TriggerRedrawAll();
111+
_api.GetBufferAndViewport().buffer.TriggerRedrawAll();
112112
return true;
113113
case DispatchTypes::WindowManipulationType::ResizeWindowInCharacters:
114114
// TODO:GH#1765 We should introduce a better `ResizeConpty` function to
@@ -135,7 +135,7 @@ bool InteractDispatch::WindowManipulation(const DispatchTypes::WindowManipulatio
135135
bool InteractDispatch::MoveCursor(const VTInt row, const VTInt col)
136136
{
137137
// First retrieve some information about the buffer
138-
const auto viewport = _api.GetViewport();
138+
const auto viewport = _api.GetBufferAndViewport().viewport;
139139

140140
// In VT, the origin is 1,1. For our array, it's 0,0. So subtract 1.
141141
// Apply boundary tests to ensure the cursor isn't outside the viewport rectangle.

0 commit comments

Comments
 (0)