Skip to content

Commit

Permalink
Implement Hard Reset for Terminal
Browse files Browse the repository at this point in the history
## Summary of the Pull Request

This _actually_ implements `\033c`
([RIS](https://vt100.net/docs/vt220-rm/chapter4.html)) for the Windows Terminal.
I thought I had done this in #4433, but that PR actually only passthrough'd
`\x1b[3J`. I didn't realize at the time that #2715 was mostly about hard reset,
not erase scrollback.

Not only should conpty pass through RIS, but the Terminal should also be
prepared to actually handle that sequence. So this PR adds that support as well.

## References

* #4433: original PR I thought fixed this.

## PR Checklist
* [x] Closes #2715 for real this time
* [x] I work here
* [x] Tests added/passed
* [n/a] Requires documentation to be updated

## Validation Steps Performed

Actually tested `printf \033c` in the Terminal this time
  • Loading branch information
zadjii-msft committed Mar 13, 2020
1 parent 38058a7 commit 52124c7
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 4 deletions.
97 changes: 97 additions & 0 deletions src/cascadia/TerminalCore/TerminalDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,100 @@ bool TerminalDispatch::_PrivateModeParamsHelper(const DispatchTypes::PrivateMode
}
return success;
}

bool TerminalDispatch::SoftReset() noexcept
{
// TODO:GH#1883 much of this method is not yet implemented in the Terminal,
// because the Terminal _doesn't need to_ yet. The terminal is only ever
// connected to conpty, so it doesn't implement most of these things that
// Hard/Soft Reset would reset. As those things ar implemented, they should
// also get cleared here.
//
// This code is left here (from its original form in conhost) as a reminder
// of what needs to be done.

bool success = CursorVisibility(true); // Cursor enabled.
// if (success)
// {
// success = SetOriginMode(false); // Absolute cursor addressing.
// }
// if (success)
// {
// success = SetAutoWrapMode(true); // Wrap at end of line.
// }
if (success)
{
success = SetCursorKeysMode(false); // Normal characters.
}
if (success)
{
success = SetKeypadMode(false); // Numeric characters.
}
// if (success)
// {
// // Top margin = 1; bottom margin = page length.
// success = _DoSetTopBottomScrollingMargins(0, 0);
// }
// if (success)
// {
// success = DesignateCharset(DispatchTypes::VTCharacterSets::USASCII); // Default Charset
// }
if (success)
{
const auto opt = DispatchTypes::GraphicsOptions::Off;
success = SetGraphicsRendition({ &opt, 1 }); // Normal rendition.
}
// if (success)
// {
// // Reset the saved cursor state.
// // Note that XTerm only resets the main buffer state, but that
// // seems likely to be a bug. Most other terminals reset both.
// _savedCursorState.at(0) = {}; // Main buffer
// _savedCursorState.at(1) = {}; // Alt buffer
// }

return success;
}

bool TerminalDispatch::HardReset() noexcept
{
// TODO:GH#1883 much of this method is not yet implemented in the Terminal,
// because the Terminal _doesn't need to_ yet. The terminal is only ever
// connected to conpty, so it doesn't implement most of these things that
// Hard/Soft Reset would reset. As those things ar implemented, they should
// also get cleared here.
//
// This code is left here (from its original form in conhost) as a reminder
// of what needs to be done.

// Sets the SGR state to normal - this must be done before EraseInDisplay
// to ensure that it clears with the default background color.
bool success = SoftReset();

// Clears the screen - Needs to be done in two operations.
if (success)
{
success = EraseInDisplay(DispatchTypes::EraseType::All);
}
if (success)
{
success = EraseInDisplay(DispatchTypes::EraseType::Scrollback);
}

// // Set the DECSCNM screen mode back to normal.
// if (success)
// {
// success = SetScreenMode(false);
// }

// Cursor to 1,1 - the Soft Reset guarantees this is absolute
if (success)
{
success = CursorPosition(1, 1);
}

// // delete all current tab stops and reapply
// _pConApi->PrivateSetDefaultTabStops();

return success;
}
3 changes: 3 additions & 0 deletions src/cascadia/TerminalCore/TerminalDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatc
bool SetCursorKeysMode(const bool applicationMode) noexcept override; // DECCKM
bool SetKeypadMode(const bool applicationMode) noexcept override; // DECKPAM, DECKPNM

bool SoftReset() noexcept override; // DECSTR
bool HardReset() noexcept override; // RIS

bool EnableVT200MouseMode(const bool enabled) noexcept override; // ?1000
bool EnableUTF8ExtendedMouseMode(const bool enabled) noexcept override; // ?1005
bool EnableSGRExtendedMouseMode(const bool enabled) noexcept override; // ?1006
Expand Down
72 changes: 70 additions & 2 deletions src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final

TEST_METHOD(PassthroughClearScrollback);

TEST_METHOD(PassthroughHardReset);

TEST_METHOD(PassthroughCursorShapeImmediately);

TEST_METHOD(TestWrappingALongString);
Expand Down Expand Up @@ -926,7 +928,7 @@ void ConptyRoundtripTests::PassthroughCursorShapeImmediately()
void ConptyRoundtripTests::PassthroughClearScrollback()
{
Log::Comment(NoThrowString().Format(
L"Write more lines of outout. We should use \r\n to move the cursor"));
L"Write more lines of output than there are lines in the viewport. Clear the scrollback with ^[[3J"));
VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());

auto& g = ServiceLocator::LocateGlobals();
Expand Down Expand Up @@ -985,7 +987,7 @@ void ConptyRoundtripTests::PassthroughClearScrollback()
const auto termSecondView = term->GetViewport();
VERIFY_ARE_EQUAL(0, termSecondView.Top());

// Verify the top of the Terminal veiwoprt contains the contents of the old viewport
// Verify the top of the Terminal viewport contains the contents of the old viewport
for (short y = 0; y < termSecondView.BottomInclusive(); y++)
{
TestUtils::VerifyExpectedString(termTb, L"X ", { 0, y });
Expand All @@ -997,3 +999,69 @@ void ConptyRoundtripTests::PassthroughClearScrollback()
TestUtils::VerifyExpectedString(termTb, std::wstring(TerminalViewWidth, L' '), { 0, y });
}
}

void ConptyRoundtripTests::PassthroughHardReset()
{
// This test is highly similar to PassthroughClearScrollback.
Log::Comment(NoThrowString().Format(
L"Write more lines of output than there are lines in the viewport. Clear everything with ^[c"));
VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());

auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& hostSm = si.GetStateMachine();

auto& termTb = *term->_buffer;

_flushFirstFrame();

_logConpty = true;

const auto hostView = si.GetViewport();
const auto end = 2 * hostView.Height();
for (auto i = 0; i < end; i++)
{
Log::Comment(NoThrowString().Format(L"Writing line %d/%d", i, end));
expectedOutput.push_back("X");
if (i < hostView.BottomInclusive())
{
expectedOutput.push_back("\r\n");
}
else
{
// After we hit the bottom of the viewport, the newlines come in
// seperated for whatever reason.
expectedOutput.push_back("\r");
expectedOutput.push_back("\n");
expectedOutput.push_back("");
}

hostSm.ProcessString(L"X\n");

VERIFY_SUCCEEDED(renderer.PaintFrame());
}

VERIFY_SUCCEEDED(renderer.PaintFrame());

// Verify that we've printed height*2 lines of X's to the Terminal
const auto termFirstView = term->GetViewport();
for (short y = 0; y < 2 * termFirstView.Height(); y++)
{
TestUtils::VerifyExpectedString(termTb, L"X ", { 0, y });
}

// Write a Hard Reset VT sequence to the host, it should come through to the Terminal
expectedOutput.push_back("\033c");
hostSm.ProcessString(L"\033c");

const auto termSecondView = term->GetViewport();
VERIFY_ARE_EQUAL(0, termSecondView.Top());

// Verify everything has been cleared out
for (short y = 0; y < termFirstView.BottomInclusive(); y++)
{
TestUtils::VerifyExpectedString(termTb, std::wstring(TerminalViewWidth, L' '), { 0, y });
}
}
11 changes: 9 additions & 2 deletions src/terminal/adapter/adaptDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1461,6 +1461,9 @@ bool AdaptDispatch::DesignateCharset(const wchar_t wchCharset) noexcept
// True if handled successfully. False otherwise.
bool AdaptDispatch::SoftReset()
{
bool isPty = false;
_pConApi->IsConsolePty(isPty);

bool success = CursorVisibility(true); // Cursor enabled.
if (success)
{
Expand All @@ -1474,11 +1477,15 @@ bool AdaptDispatch::SoftReset()
{
success = SetCursorKeysMode(false); // Normal characters.
}
if (success)
// SetCursorKeysMode will return false if we're in conpty mode, as to
// trigger a passthrough. If that's the case, just power through here.
if (success || isPty)
{
success = SetKeypadMode(false); // Numeric characters.
}
if (success)
// SetKeypadMode will return false if we're in conpty mode, as to trigger a
// passthrough. If that's the case, just power through here.
if (success || isPty)
{
// Top margin = 1; bottom margin = page length.
success = _DoSetTopBottomScrollingMargins(0, 0);
Expand Down
7 changes: 7 additions & 0 deletions src/terminal/parser/OutputStateMachineEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,13 @@ bool OutputStateMachineEngine::ActionEscDispatch(const wchar_t wch,
}
}

// If we were unable to process the string, and there's a TTY attached to us,
// trigger the state machine to flush the string to the terminal.
if (_pfnFlushToTerminal != nullptr && !success)
{
success = _pfnFlushToTerminal();
}

_ClearLastChar();

return success;
Expand Down

0 comments on commit 52124c7

Please sign in to comment.