mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-11 04:38:24 -06:00
Add support for the DECALN escape sequence (#3968)
## Summary of the Pull Request This adds support for the [`DECALN`](https://vt100.net/docs/vt510-rm/DECALN.html) escape sequence, which produces a kind of test pattern, originally used on VT terminals to adjust the screen alignment. It's needed to pass several of the tests in the [Vttest](https://invisible-island.net/vttest/) suite. ## PR Checklist * [x] Closes #3671 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Requires documentation to be updated * [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx ## Detailed Description of the Pull Request / Additional comments To start with, the `ActionEscDispatch` method in the `OutputStateMachineEngine` needed to be extended to check for a new intermediate type (`#`). Then when that intermediate is followed by an `8`, it dispatches to a new `ScreenAlignmentPattern` method in the `ITermDispatch` interface. The implementation of the `ScreenAlignmentPattern` itself is fairly simple. It uses the recently added `PrivateFillRegion` API to fill the screen with the character `E` using default attributes. Then in addition to that, a bunch of VT properties are reset: * The meta/extended attributes are reset (although the active colors must be left unchanged). * The origin mode is set to absolute positioning. * The scrolling margins are cleared. * The cursor position is moved to home. ## Validation Steps Performed I've added a screen buffer test that makes sure the `DECALN` sequence fills the screen with the correct character and attributes, and that the above mentioned properties are all updated appropriately. I've also tested in Vttest, and confirmed that the first two pages of the _Test of cursor movements_ are now showing the frame of E's that are expected there.
This commit is contained in:
parent
d7ae8e6db9
commit
c0b8b85a47
@ -200,6 +200,8 @@ class ScreenBufferTests
|
||||
TEST_METHOD(CursorUpDownExactlyAtMargins);
|
||||
|
||||
TEST_METHOD(CursorSaveRestore);
|
||||
|
||||
TEST_METHOD(ScreenAlignmentPattern);
|
||||
};
|
||||
|
||||
void ScreenBufferTests::SingleAlternateBufferCreationTest()
|
||||
@ -5519,3 +5521,62 @@ void ScreenBufferTests::CursorSaveRestore()
|
||||
stateMachine.ProcessString(resetDECOM);
|
||||
stateMachine.ProcessString(L"\x1b[r");
|
||||
}
|
||||
|
||||
void ScreenBufferTests::ScreenAlignmentPattern()
|
||||
{
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
|
||||
auto& stateMachine = si.GetStateMachine();
|
||||
const auto& cursor = si.GetTextBuffer().GetCursor();
|
||||
WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
|
||||
Log::Comment(L"Set the initial buffer state.");
|
||||
|
||||
const auto bufferWidth = si.GetBufferSize().Width();
|
||||
const auto bufferHeight = si.GetBufferSize().Height();
|
||||
|
||||
// Move the viewport down a few lines, and only cover part of the buffer width.
|
||||
si.SetViewport(Viewport::FromDimensions({ 5, 10 }, { bufferWidth - 10, 30 }), true);
|
||||
const auto viewportStart = si.GetViewport().Top();
|
||||
const auto viewportEnd = si.GetViewport().BottomExclusive();
|
||||
|
||||
// Fill the entire buffer with Zs. Blue on Green.
|
||||
const auto bufferAttr = TextAttribute{ FOREGROUND_BLUE | BACKGROUND_GREEN };
|
||||
_FillLines(0, bufferHeight, L'Z', bufferAttr);
|
||||
|
||||
// Set the initial attributes.
|
||||
auto initialAttr = TextAttribute{ RGB(12, 34, 56), RGB(78, 90, 12) };
|
||||
initialAttr.SetMetaAttributes(COMMON_LVB_REVERSE_VIDEO | COMMON_LVB_UNDERSCORE);
|
||||
si.SetAttributes(initialAttr);
|
||||
|
||||
// Set some margins.
|
||||
stateMachine.ProcessString(L"\x1b[10;20r");
|
||||
VERIFY_IS_TRUE(si.AreMarginsSet());
|
||||
|
||||
// Place the cursor in the center.
|
||||
auto cursorPos = COORD{ bufferWidth / 2, (viewportStart + viewportEnd) / 2 };
|
||||
VERIFY_SUCCEEDED(si.SetCursorPosition(cursorPos, true));
|
||||
|
||||
Log::Comment(L"Execute the DECALN escape sequence.");
|
||||
stateMachine.ProcessString(L"\x1b#8");
|
||||
|
||||
Log::Comment(L"Lines within view should be filled with Es, with default attributes.");
|
||||
auto defaultAttr = TextAttribute{};
|
||||
VERIFY_IS_TRUE(_ValidateLinesContain(viewportStart, viewportEnd, L'E', defaultAttr));
|
||||
|
||||
Log::Comment(L"Field of Zs outside viewport should remain unchanged.");
|
||||
VERIFY_IS_TRUE(_ValidateLinesContain(0, viewportStart, L'Z', bufferAttr));
|
||||
VERIFY_IS_TRUE(_ValidateLinesContain(viewportEnd, bufferHeight, L'Z', bufferAttr));
|
||||
|
||||
Log::Comment(L"Margins should not be set.");
|
||||
VERIFY_IS_FALSE(si.AreMarginsSet());
|
||||
|
||||
Log::Comment(L"Cursor position shold be moved to home.");
|
||||
auto homePosition = COORD{ 0, viewportStart };
|
||||
VERIFY_ARE_EQUAL(homePosition, cursor.GetPosition());
|
||||
|
||||
Log::Comment(L"Meta/rendition attributes should be reset.");
|
||||
auto expectedAttr = initialAttr;
|
||||
expectedAttr.SetMetaAttributes(0);
|
||||
VERIFY_ARE_EQUAL(expectedAttr, si.GetAttributes());
|
||||
}
|
||||
|
||||
@ -90,6 +90,7 @@ public:
|
||||
|
||||
virtual bool SoftReset() = 0; // DECSTR
|
||||
virtual bool HardReset() = 0; // RIS
|
||||
virtual bool ScreenAlignmentPattern() = 0; // DECALN
|
||||
|
||||
virtual bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) = 0; // DECSCUSR
|
||||
virtual bool SetCursorColor(const COLORREF Color) = 0; // OSCSetCursorColor, OSCResetCursorColor
|
||||
|
||||
@ -1543,6 +1543,42 @@ bool AdaptDispatch::HardReset()
|
||||
return fSuccess;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - DECALN - Fills the entire screen with a test pattern of uppercase Es,
|
||||
// resets the margins and rendition attributes, and moves the cursor to
|
||||
// the home position.
|
||||
// Arguments:
|
||||
// - None
|
||||
// Return Value:
|
||||
// - True if handled successfully. False otherwise.
|
||||
bool AdaptDispatch::ScreenAlignmentPattern()
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 };
|
||||
csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX);
|
||||
// Make sure to reset the viewport (with MoveToBottom )to where it was
|
||||
// before the user scrolled the console output
|
||||
bool fSuccess = !!(_conApi->MoveToBottom() && _conApi->GetConsoleScreenBufferInfoEx(&csbiex));
|
||||
|
||||
if (fSuccess)
|
||||
{
|
||||
// Fill the screen with the letter E using the default attributes.
|
||||
auto fillPosition = COORD{ 0, csbiex.srWindow.Top };
|
||||
auto fillLength = (csbiex.srWindow.Bottom - csbiex.srWindow.Top) * csbiex.dwSize.X;
|
||||
fSuccess = _conApi->PrivateFillRegion(fillPosition, fillLength, L'E', false);
|
||||
// Reset the meta/extended attributes (but leave the colors unchanged).
|
||||
fSuccess = fSuccess && _conApi->PrivateSetLegacyAttributes(0, false, false, true);
|
||||
fSuccess = fSuccess && _conApi->PrivateSetExtendedTextAttributes(ExtendedAttributes::Normal);
|
||||
// Reset the origin mode to absolute.
|
||||
fSuccess = fSuccess && SetOriginMode(false);
|
||||
// Clear the scrolling margins.
|
||||
fSuccess = fSuccess && _DoSetTopBottomScrollingMargins(0, 0);
|
||||
// Set the cursor position to home.
|
||||
fSuccess = fSuccess && CursorPosition(1, 1);
|
||||
}
|
||||
|
||||
return fSuccess;
|
||||
}
|
||||
|
||||
//Routine Description:
|
||||
// Erase Scrollback (^[[3J - ED extension by xterm)
|
||||
// Because conhost doesn't exactly have a scrollback, We have to be tricky here.
|
||||
|
||||
@ -84,6 +84,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
bool DesignateCharset(const wchar_t wchCharset) override; // DesignateCharset
|
||||
bool SoftReset() override; // DECSTR
|
||||
bool HardReset() override; // RIS
|
||||
bool ScreenAlignmentPattern() override; // DECALN
|
||||
bool EnableDECCOLMSupport(const bool fEnabled) override; // ?40
|
||||
bool EnableVT200MouseMode(const bool fEnabled) override; // ?1000
|
||||
bool EnableUTF8ExtendedMouseMode(const bool fEnabled) override; // ?1005
|
||||
|
||||
@ -87,6 +87,7 @@ public:
|
||||
|
||||
bool SoftReset() override { return false; } // DECSTR
|
||||
bool HardReset() override { return false; } // RIS
|
||||
bool ScreenAlignmentPattern() override { return false; } // DECALN
|
||||
|
||||
bool SetCursorStyle(const DispatchTypes::CursorStyle /*cursorStyle*/) override { return false; } // DECSCUSR
|
||||
bool SetCursorColor(const COLORREF /*Color*/) override { return false; } // OSCSetCursorColor, OSCResetCursorColor
|
||||
|
||||
@ -257,6 +257,20 @@ bool OutputStateMachineEngine::ActionEscDispatch(const wchar_t wch,
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (wchIntermediate == L'#')
|
||||
{
|
||||
switch (wch)
|
||||
{
|
||||
case VTActionCodes::DECALN_ScreenAlignmentPattern:
|
||||
fSuccess = _dispatch->ScreenAlignmentPattern();
|
||||
TermTelemetry::Instance().Log(TermTelemetry::Codes::DECALN);
|
||||
break;
|
||||
default:
|
||||
// If no functions to call, overall dispatch was a failure.
|
||||
fSuccess = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ClearLastChar();
|
||||
|
||||
@ -127,7 +127,8 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
// 'q' is overloaded - no postfix is DECLL, ' ' postfix is DECSCUSR, and '"' is DECSCA
|
||||
DECSCUSR_SetCursorStyle = L'q', // I believe we'll only ever implement DECSCUSR
|
||||
DTTERM_WindowManipulation = L't',
|
||||
REP_RepeatCharacter = L'b'
|
||||
REP_RepeatCharacter = L'b',
|
||||
DECALN_ScreenAlignmentPattern = L'8'
|
||||
};
|
||||
|
||||
enum OscActionCodes : unsigned int
|
||||
|
||||
@ -246,6 +246,7 @@ void TermTelemetry::WriteFinalTraceLog() const
|
||||
TraceLoggingUInt32(_uiTimesUsed[OSCFG], "OscForegroundColor"),
|
||||
TraceLoggingUInt32(_uiTimesUsed[OSCBG], "OscBackgroundColor"),
|
||||
TraceLoggingUInt32(_uiTimesUsed[REP], "REP"),
|
||||
TraceLoggingUInt32(_uiTimesUsed[DECALN], "DECALN"),
|
||||
TraceLoggingUInt32Array(_uiTimesFailed, ARRAYSIZE(_uiTimesFailed), "Failed"),
|
||||
TraceLoggingUInt32(_uiTimesFailedOutsideRange, "FailedOutsideRange"));
|
||||
}
|
||||
|
||||
@ -84,6 +84,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
REP,
|
||||
OSCFG,
|
||||
OSCBG,
|
||||
DECALN,
|
||||
// Only use this last enum as a count of the number of codes.
|
||||
NUMBER_OF_CODES
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user