diff --git a/src/cascadia/TerminalCore/TerminalDispatch.cpp b/src/cascadia/TerminalCore/TerminalDispatch.cpp index f5d9a02559a..9e41146d5ed 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.cpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.cpp @@ -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; +} diff --git a/src/cascadia/TerminalCore/TerminalDispatch.hpp b/src/cascadia/TerminalCore/TerminalDispatch.hpp index 5321f67dbfa..e3be6ecbe92 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.hpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.hpp @@ -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 diff --git a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp index 762f1ff7f9a..39abbb76fa1 100644 --- a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp @@ -158,6 +158,8 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final TEST_METHOD(PassthroughClearScrollback); + TEST_METHOD(PassthroughHardReset); + TEST_METHOD(PassthroughCursorShapeImmediately); TEST_METHOD(TestWrappingALongString); @@ -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(); @@ -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 }); @@ -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 }); + } +} diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 35eb0b64eac..236d10283e7 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -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) { @@ -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); diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index be2763a2708..156488f01fa 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -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;