From dc773550fbed7b085520e7538d7df4559be911db Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 11 Apr 2023 16:21:03 +0100 Subject: [PATCH 1/3] Consolidate the "system" modes. --- src/cascadia/TerminalCore/Terminal.cpp | 2 +- src/cascadia/TerminalCore/Terminal.hpp | 10 +-- src/cascadia/TerminalCore/TerminalApi.cpp | 25 +----- src/host/outputStream.cpp | 78 ++++++++----------- src/host/outputStream.hpp | 8 +- src/terminal/adapter/ITerminalApi.hpp | 14 ++-- src/terminal/adapter/adaptDispatch.cpp | 16 ++-- .../adapter/ut_adapter/adapterTest.cpp | 44 ++++------- 8 files changed, 74 insertions(+), 123 deletions(-) diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 99fa8453472..f4b7482cf49 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -236,7 +236,7 @@ void Terminal::EraseScrollback() bool Terminal::IsXtermBracketedPasteModeEnabled() const noexcept { - return _bracketedPasteMode; + return _systemMode.test(Mode::BracketedPaste); } std::wstring_view Terminal::GetWorkingDirectory() noexcept diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index d03d927c19b..1c8e36d72b4 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -111,17 +111,14 @@ class Microsoft::Terminal::Core::Terminal final : til::rect GetViewport() const noexcept override; void SetViewportPosition(const til::point position) noexcept override; void SetTextAttributes(const TextAttribute& attrs) noexcept override; - void SetAutoWrapMode(const bool wrapAtEOL) noexcept override; - bool GetAutoWrapMode() const noexcept override; + void SetSystemMode(const Mode mode, const bool enabled) noexcept override; + bool GetSystemMode(const Mode mode) const noexcept override; void WarningBell() override; - bool GetLineFeedMode() const noexcept override; void SetWindowTitle(const std::wstring_view title) override; CursorType GetUserDefaultCursorStyle() const noexcept override; bool ResizeWindow(const til::CoordType width, const til::CoordType height) noexcept override; void SetConsoleOutputCP(const unsigned int codepage) noexcept override; unsigned int GetConsoleOutputCP() const noexcept override; - void SetBracketedPasteMode(const bool enabled) noexcept override; - bool GetBracketedPasteMode() const noexcept override; void CopyToClipboard(std::wstring_view content) override; void SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) override; void SetWorkingDirectory(std::wstring_view uri) override; @@ -320,10 +317,11 @@ class Microsoft::Terminal::Core::Terminal final : CursorType _defaultCursorShape = CursorType::Legacy; + til::enumset _systemMode{ Mode::AutoWrap }; + bool _snapOnInput = true; bool _altGrAliasing = true; bool _suppressApplicationTitle = false; - bool _bracketedPasteMode = false; bool _trimBlockSelection = false; bool _autoMarkPrompts = false; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 3c211d69631..28b320f638c 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -61,15 +61,14 @@ void Terminal::SetTextAttributes(const TextAttribute& attrs) noexcept _activeBuffer().SetCurrentAttributes(attrs); } -void Terminal::SetAutoWrapMode(const bool /*wrapAtEOL*/) noexcept +void Terminal::SetSystemMode(const Mode mode, const bool enabled) noexcept { - // TODO: This will be needed to support DECAWM. + _systemMode.set(mode, enabled); } -bool Terminal::GetAutoWrapMode() const noexcept +bool Terminal::GetSystemMode(const Mode mode) const noexcept { - // TODO: This will be needed to support DECAWM. - return true; + return _systemMode.test(mode); } void Terminal::WarningBell() @@ -77,12 +76,6 @@ void Terminal::WarningBell() _pfnWarningBell(); } -bool Terminal::GetLineFeedMode() const noexcept -{ - // TODO: This will be needed to support LNM. - return false; -} - void Terminal::SetWindowTitle(const std::wstring_view title) { if (!_suppressApplicationTitle) @@ -114,16 +107,6 @@ unsigned int Terminal::GetConsoleOutputCP() const noexcept return CP_UTF8; } -void Terminal::SetBracketedPasteMode(const bool enabled) noexcept -{ - _bracketedPasteMode = enabled; -} - -bool Terminal::GetBracketedPasteMode() const noexcept -{ - return _bracketedPasteMode; -} - void Terminal::CopyToClipboard(std::wstring_view content) { _pfnCopyToClipboard(content); diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 2bf21f7dbfa..58707827cb2 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -114,40 +114,49 @@ void ConhostInternalGetSet::SetTextAttributes(const TextAttribute& attrs) } // Routine Description: -// - Sets the ENABLE_WRAP_AT_EOL_OUTPUT mode. This controls whether the cursor moves -// to the beginning of the next row when it reaches the end of the current row. +// - Sets the state of one of the system modes. // Arguments: -// - wrapAtEOL - set to true to wrap, false to overwrite the last character. +// - mode - The mode being updated. +// - enabled - True to enable the mode, false to disable it. // Return Value: // - -void ConhostInternalGetSet::SetAutoWrapMode(const bool wrapAtEOL) +void ConhostInternalGetSet::SetSystemMode(const Mode mode, const bool enabled) { - auto& outputMode = _io.GetActiveOutputBuffer().OutputMode; - WI_UpdateFlag(outputMode, ENABLE_WRAP_AT_EOL_OUTPUT, wrapAtEOL); + switch (mode) + { + case Mode::AutoWrap: + WI_UpdateFlag(_io.GetActiveOutputBuffer().OutputMode, ENABLE_WRAP_AT_EOL_OUTPUT, enabled); + break; + case Mode::LineFeed: + WI_UpdateFlag(_io.GetActiveOutputBuffer().OutputMode, DISABLE_NEWLINE_AUTO_RETURN, !enabled); + break; + case Mode::BracketedPaste: + ServiceLocator::LocateGlobals().getConsoleInformation().SetBracketedPasteMode(enabled); + break; + default: + THROW_HR(E_INVALIDARG); + } } // Routine Description: -// - Retrieves the current state of ENABLE_WRAP_AT_EOL_OUTPUT mode. +// - Retrieves the current state of one of the system modes. // Arguments: -// - +// - mode - The mode being queried. // Return Value: // - true if the mode is enabled. false otherwise. -bool ConhostInternalGetSet::GetAutoWrapMode() const +bool ConhostInternalGetSet::GetSystemMode(const Mode mode) const { - const auto outputMode = _io.GetActiveOutputBuffer().OutputMode; - return WI_IsFlagSet(outputMode, ENABLE_WRAP_AT_EOL_OUTPUT); -} - -// Method Description: -// - Retrieves the current Line Feed/New Line (LNM) mode. -// Arguments: -// - None -// Return Value: -// - true if a line feed also produces a carriage return. false otherwise. -bool ConhostInternalGetSet::GetLineFeedMode() const -{ - auto& screenInfo = _io.GetActiveOutputBuffer(); - return WI_IsFlagClear(screenInfo.OutputMode, DISABLE_NEWLINE_AUTO_RETURN); + switch (mode) + { + case Mode::AutoWrap: + return WI_IsFlagSet(_io.GetActiveOutputBuffer().OutputMode, ENABLE_WRAP_AT_EOL_OUTPUT); + case Mode::LineFeed: + return WI_IsFlagClear(_io.GetActiveOutputBuffer().OutputMode, DISABLE_NEWLINE_AUTO_RETURN); + case Mode::BracketedPaste: + return ServiceLocator::LocateGlobals().getConsoleInformation().GetBracketedPasteMode(); + default: + THROW_HR(E_INVALIDARG); + } } // Routine Description: @@ -245,29 +254,6 @@ unsigned int ConhostInternalGetSet::GetConsoleOutputCP() const return ServiceLocator::LocateGlobals().getConsoleInformation().OutputCP; } -// Routine Description: -// - Sets the XTerm bracketed paste mode. This controls whether pasted content is -// bracketed with control sequences to differentiate it from typed text. -// Arguments: -// - enable - set to true to enable bracketing, false to disable. -// Return Value: -// - -void ConhostInternalGetSet::SetBracketedPasteMode(const bool enabled) -{ - ServiceLocator::LocateGlobals().getConsoleInformation().SetBracketedPasteMode(enabled); -} - -// Routine Description: -// - Gets the current state of XTerm bracketed paste mode. -// Arguments: -// - -// Return Value: -// - true if the mode is enabled, false if not. -bool ConhostInternalGetSet::GetBracketedPasteMode() const -{ - return ServiceLocator::LocateGlobals().getConsoleInformation().GetBracketedPasteMode(); -} - // Routine Description: // - Copies the given content to the clipboard. // Arguments: diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index f555efbcb1a..6e0120cc997 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -38,13 +38,11 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void SetTextAttributes(const TextAttribute& attrs) override; - void SetAutoWrapMode(const bool wrapAtEOL) override; - bool GetAutoWrapMode() const override; + void SetSystemMode(const Mode mode, const bool enabled) override; + bool GetSystemMode(const Mode mode) const override; void WarningBell() override; - bool GetLineFeedMode() const override; - void SetWindowTitle(const std::wstring_view title) override; void UseAlternateScreenBuffer() override; @@ -60,8 +58,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void SetConsoleOutputCP(const unsigned int codepage) override; unsigned int GetConsoleOutputCP() const override; - void SetBracketedPasteMode(const bool enabled) override; - bool GetBracketedPasteMode() const override; void CopyToClipboard(const std::wstring_view content) override; void SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) override; void SetWorkingDirectory(const std::wstring_view uri) override; diff --git a/src/terminal/adapter/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index b0987a22014..cf32f308943 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -48,11 +48,17 @@ namespace Microsoft::Console::VirtualTerminal virtual void SetTextAttributes(const TextAttribute& attrs) = 0; - virtual void SetAutoWrapMode(const bool wrapAtEOL) = 0; - virtual bool GetAutoWrapMode() const = 0; + enum class Mode : size_t + { + AutoWrap, + LineFeed, + BracketedPaste + }; + + virtual void SetSystemMode(const Mode mode, const bool enabled) = 0; + virtual bool GetSystemMode(const Mode mode) const = 0; virtual void WarningBell() = 0; - virtual bool GetLineFeedMode() const = 0; virtual void SetWindowTitle(const std::wstring_view title) = 0; virtual void UseAlternateScreenBuffer() = 0; virtual void UseMainScreenBuffer() = 0; @@ -64,8 +70,6 @@ namespace Microsoft::Console::VirtualTerminal virtual void SetConsoleOutputCP(const unsigned int codepage) = 0; virtual unsigned int GetConsoleOutputCP() const = 0; - virtual void SetBracketedPasteMode(const bool enabled) = 0; - virtual bool GetBracketedPasteMode() const = 0; virtual void CopyToClipboard(const std::wstring_view content) = 0; virtual void SetTaskbarProgress(const DispatchTypes::TaskbarState state, const size_t progress) = 0; virtual void SetWorkingDirectory(const std::wstring_view uri) = 0; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 6a4d7b8d672..64ddd729598 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -74,7 +74,7 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string) auto& textBuffer = _api.GetTextBuffer(); auto& cursor = textBuffer.GetCursor(); auto cursorPosition = cursor.GetPosition(); - const auto wrapAtEOL = _api.GetAutoWrapMode(); + const auto wrapAtEOL = _api.GetSystemMode(ITerminalApi::Mode::AutoWrap); const auto attributes = textBuffer.GetCurrentAttributes(); // Turn off the cursor until we're done, so it isn't refreshed unnecessarily. @@ -1719,7 +1719,7 @@ bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con CursorPosition(1, 1); return true; case DispatchTypes::ModeParams::DECAWM_AutoWrapMode: - _api.SetAutoWrapMode(enable); + _api.SetSystemMode(ITerminalApi::Mode::AutoWrap, enable); // Resetting DECAWM should also reset the delayed wrap flag. if (!enable) { @@ -1771,7 +1771,7 @@ bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con _SetAlternateScreenBufferMode(enable); return true; case DispatchTypes::ModeParams::XTERM_BracketedPasteMode: - _api.SetBracketedPasteMode(enable); + _api.SetSystemMode(ITerminalApi::Mode::BracketedPaste, enable); return !_api.IsConsolePty(); case DispatchTypes::ModeParams::W32IM_Win32InputMode: _terminalInput.SetInputMode(TerminalInput::Mode::Win32, enable); @@ -1840,7 +1840,7 @@ bool AdaptDispatch::RequestMode(const DispatchTypes::ModeParams param) enabled = _modes.test(Mode::Origin); break; case DispatchTypes::ModeParams::DECAWM_AutoWrapMode: - enabled = _api.GetAutoWrapMode(); + enabled = _api.GetSystemMode(ITerminalApi::Mode::AutoWrap); break; case DispatchTypes::ModeParams::DECARM_AutoRepeatMode: enabled = _terminalInput.GetInputMode(TerminalInput::Mode::AutoRepeat); @@ -1889,7 +1889,7 @@ bool AdaptDispatch::RequestMode(const DispatchTypes::ModeParams param) enabled = _usingAltBuffer; break; case DispatchTypes::ModeParams::XTERM_BracketedPasteMode: - enabled = _api.GetBracketedPasteMode(); + enabled = _api.GetSystemMode(ITerminalApi::Mode::BracketedPaste); break; case DispatchTypes::ModeParams::W32IM_Win32InputMode: enabled = _terminalInput.GetInputMode(TerminalInput::Mode::Win32); @@ -2203,7 +2203,7 @@ bool AdaptDispatch::LineFeed(const DispatchTypes::LineFeedType lineFeedType) switch (lineFeedType) { case DispatchTypes::LineFeedType::DependsOnMode: - _DoLineFeed(textBuffer, _api.GetLineFeedMode(), false); + _DoLineFeed(textBuffer, _api.GetSystemMode(ITerminalApi::Mode::LineFeed), false); return true; case DispatchTypes::LineFeedType::WithoutReturn: _DoLineFeed(textBuffer, false, false); @@ -2590,7 +2590,7 @@ bool AdaptDispatch::SoftReset() { _api.GetTextBuffer().GetCursor().SetIsVisible(true); // Cursor enabled. _modes.reset(Mode::InsertReplace, Mode::Origin); // Replace mode; Absolute cursor addressing. - _api.SetAutoWrapMode(true); // Wrap at end of line. + _api.SetSystemMode(ITerminalApi::Mode::AutoWrap, true); // Wrap at end of line. _terminalInput.SetInputMode(TerminalInput::Mode::CursorKey, false); // Normal characters. _terminalInput.SetInputMode(TerminalInput::Mode::Keypad, false); // Numeric characters. @@ -2667,7 +2667,7 @@ bool AdaptDispatch::HardReset() _terminalInput.ResetInputModes(); // Reset bracketed paste mode - _api.SetBracketedPasteMode(false); + _api.SetSystemMode(ITerminalApi::Mode::BracketedPaste, false); // Restore cursor blinking mode. _api.GetTextBuffer().GetCursor().SetBlinkingAllowed(true); diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index dcb917750d6..b99ccd525d5 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -96,17 +96,6 @@ class TestGetSet final : public ITerminalApi Log::Comment(L"SetViewportPosition MOCK called..."); } - void SetAutoWrapMode(const bool /*wrapAtEOL*/) override - { - Log::Comment(L"SetAutoWrapMode MOCK called..."); - } - - bool GetAutoWrapMode() const override - { - Log::Comment(L"GetAutoWrapMode MOCK called..."); - return true; - } - bool IsVtInputEnabled() const override { return false; @@ -121,15 +110,21 @@ class TestGetSet final : public ITerminalApi _textBuffer->SetCurrentAttributes(attrs); } - void WarningBell() override + void SetSystemMode(const Mode mode, const bool enabled) { - Log::Comment(L"WarningBell MOCK called..."); + Log::Comment(L"SetSystemMode MOCK called..."); + _systemMode.set(mode, enabled); } - bool GetLineFeedMode() const override + bool GetSystemMode(const Mode mode) const { - Log::Comment(L"GetLineFeedMode MOCK called..."); - return _getLineFeedModeResult; + Log::Comment(L"GetSystemMode MOCK called..."); + return _systemMode.test(mode); + } + + void WarningBell() override + { + Log::Comment(L"WarningBell MOCK called..."); } void SetWindowTitle(const std::wstring_view title) @@ -184,17 +179,6 @@ class TestGetSet final : public ITerminalApi return _expectedOutputCP; } - void SetBracketedPasteMode(const bool /*enabled*/) override - { - Log::Comment(L"SetBracketedPasteMode MOCK called..."); - } - - bool GetBracketedPasteMode() const override - { - Log::Comment(L"GetBracketedPasteMode MOCK called..."); - return false; - } - void CopyToClipboard(const std::wstring_view /*content*/) { Log::Comment(L"CopyToClipboard MOCK called..."); @@ -386,7 +370,7 @@ class TestGetSet final : public ITerminalApi bool _setTextAttributesResult = false; bool _returnResponseResult = false; - bool _getLineFeedModeResult = false; + til::enumset _systemMode{ Mode::AutoWrap }; bool _setWindowTitleResult = false; std::wstring_view _expectedWindowTitle{}; @@ -2262,13 +2246,13 @@ class AdapterTest VERIFY_ARE_EQUAL(til::point(0, 1), cursor.GetPosition()); Log::Comment(L"Test 3: Line feed depends on mode, and mode reset."); - _testGetSet->_getLineFeedModeResult = false; + _testGetSet->_systemMode.reset(ITerminalApi::Mode::LineFeed); cursor.SetPosition({ 10, 0 }); VERIFY_IS_TRUE(_pDispatch->LineFeed(DispatchTypes::LineFeedType::DependsOnMode)); VERIFY_ARE_EQUAL(til::point(10, 1), cursor.GetPosition()); Log::Comment(L"Test 4: Line feed depends on mode, and mode set."); - _testGetSet->_getLineFeedModeResult = true; + _testGetSet->_systemMode.set(ITerminalApi::Mode::LineFeed); cursor.SetPosition({ 10, 0 }); VERIFY_IS_TRUE(_pDispatch->LineFeed(DispatchTypes::LineFeedType::DependsOnMode)); VERIFY_ARE_EQUAL(til::point(0, 1), cursor.GetPosition()); From 6ad0992c3672c6a980479adb6399b2dcb40f1621 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 11 Apr 2023 18:30:45 +0100 Subject: [PATCH 2/3] Add support for LNM. --- src/terminal/adapter/DispatchTypes.hpp | 1 + src/terminal/adapter/adaptDispatch.cpp | 26 ++++++++++++++++++++++++++ src/terminal/input/terminalInput.cpp | 9 +++++++++ src/terminal/input/terminalInput.hpp | 1 + 4 files changed, 37 insertions(+) diff --git a/src/terminal/adapter/DispatchTypes.hpp b/src/terminal/adapter/DispatchTypes.hpp index b1af645cc4b..27252a87efc 100644 --- a/src/terminal/adapter/DispatchTypes.hpp +++ b/src/terminal/adapter/DispatchTypes.hpp @@ -418,6 +418,7 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes enum ModeParams : VTInt { IRM_InsertReplaceMode = ANSIStandardMode(4), + LNM_LineFeedNewLineMode = ANSIStandardMode(20), DECCKM_CursorKeysMode = DECPrivateMode(1), DECANM_AnsiMode = DECPrivateMode(2), DECCOLM_SetNumberOfColumns = DECPrivateMode(3), diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 64ddd729598..d22b24ebfb6 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1696,6 +1696,15 @@ bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con case DispatchTypes::ModeParams::IRM_InsertReplaceMode: _modes.set(Mode::InsertReplace, enable); return true; + case DispatchTypes::ModeParams::LNM_LineFeedNewLineMode: + // VT apps expect that the system and input modes are the same, so if + // they become out of sync, we just act as if LNM mode isn't supported. + if (_api.GetSystemMode(ITerminalApi::Mode::LineFeed) == _terminalInput.GetInputMode(TerminalInput::Mode::LineFeed)) + { + _api.SetSystemMode(ITerminalApi::Mode::LineFeed, enable); + _terminalInput.SetInputMode(TerminalInput::Mode::LineFeed, enable); + } + return true; case DispatchTypes::ModeParams::DECCKM_CursorKeysMode: _terminalInput.SetInputMode(TerminalInput::Mode::CursorKey, enable); return !_PassThroughInputModes(); @@ -1820,6 +1829,14 @@ bool AdaptDispatch::RequestMode(const DispatchTypes::ModeParams param) case DispatchTypes::ModeParams::IRM_InsertReplaceMode: enabled = _modes.test(Mode::InsertReplace); break; + case DispatchTypes::ModeParams::LNM_LineFeedNewLineMode: + // VT apps expect that the system and input modes are the same, so if + // they become out of sync, we just act as if LNM mode isn't supported. + if (_api.GetSystemMode(ITerminalApi::Mode::LineFeed) == _terminalInput.GetInputMode(TerminalInput::Mode::LineFeed)) + { + enabled = _terminalInput.GetInputMode(TerminalInput::Mode::LineFeed); + } + break; case DispatchTypes::ModeParams::DECCKM_CursorKeysMode: enabled = _terminalInput.GetInputMode(TerminalInput::Mode::CursorKey); break; @@ -2663,6 +2680,15 @@ bool AdaptDispatch::HardReset() // Cursor to 1,1 - the Soft Reset guarantees this is absolute CursorPosition(1, 1); + // We only reset the system line feed mode if the input mode is set. If it + // isn't set, that either means they're both reset, and there's nothing for + // us to do, or they're out of sync, which implies the system mode was set + // via the console API, so it's not our responsibility. + if (_terminalInput.GetInputMode(TerminalInput::Mode::LineFeed)) + { + _api.SetSystemMode(ITerminalApi::Mode::LineFeed, false); + } + // Reset input modes to their initial state _terminalInput.ResetInputModes(); diff --git a/src/terminal/input/terminalInput.cpp b/src/terminal/input/terminalInput.cpp index 0af36efe603..0e17f178574 100644 --- a/src/terminal/input/terminalInput.cpp +++ b/src/terminal/input/terminalInput.cpp @@ -600,6 +600,15 @@ bool TerminalInput::HandleKey(const IInputEvent* const pInEvent) return true; } + // When the Line Feed mode is set, a VK_RETURN key should send both CR and LF. + // When reset, we fall through to the default behavior, which is to send just + // CR, or when the Ctrl modifier is pressed, just LF. + if (keyEvent.GetVirtualKeyCode() == VK_RETURN && _inputMode.test(Mode::LineFeed)) + { + _SendInputSequence(L"\r\n"); + return true; + } + // Many keyboard layouts have an AltGr key, which makes widely used characters accessible. // For instance on a German keyboard layout "[" is written by pressing AltGr+8. // Furthermore Ctrl+Alt is traditionally treated as an alternative way to AltGr by Windows. diff --git a/src/terminal/input/terminalInput.hpp b/src/terminal/input/terminalInput.hpp index 41c91072c8b..da88bdad979 100644 --- a/src/terminal/input/terminalInput.hpp +++ b/src/terminal/input/terminalInput.hpp @@ -38,6 +38,7 @@ namespace Microsoft::Console::VirtualTerminal enum class Mode : size_t { + LineFeed, Ansi, AutoRepeat, Keypad, From d42716a9e6e4991f9dec6657bc75a27cc0db63d8 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sun, 16 Apr 2023 18:11:52 +0100 Subject: [PATCH 3/3] Add some unit tests. --- src/host/ut_host/ScreenBufferTests.cpp | 23 ++++++++++++ .../adapter/ut_adapter/adapterTest.cpp | 36 +++++++++++++++++-- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index 48d4e277190..c6d38ad813b 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -201,6 +201,7 @@ class ScreenBufferTests TEST_METHOD(ScrollLines256Colors); + TEST_METHOD(SetLineFeedMode); TEST_METHOD(SetScreenMode); TEST_METHOD(SetOriginMode); TEST_METHOD(SetAutoWrapMode); @@ -5156,6 +5157,28 @@ void ScreenBufferTests::ScrollLines256Colors() } } +void ScreenBufferTests::SetLineFeedMode() +{ + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer(); + auto& stateMachine = si.GetStateMachine(); + const auto& terminalInput = gci.GetActiveInputBuffer()->GetTerminalInput(); + + // We need to start with newline auto return disabled for LNM to be active. + WI_SetFlag(si.OutputMode, DISABLE_NEWLINE_AUTO_RETURN); + auto restoreMode = wil::scope_exit([&] { WI_ClearFlag(si.OutputMode, DISABLE_NEWLINE_AUTO_RETURN); }); + + Log::Comment(L"When LNM is set, newline auto return and line feed mode are enabled."); + stateMachine.ProcessString(L"\x1B[20h"); + VERIFY_IS_TRUE(WI_IsFlagClear(si.OutputMode, DISABLE_NEWLINE_AUTO_RETURN)); + VERIFY_IS_TRUE(terminalInput.GetInputMode(TerminalInput::Mode::LineFeed)); + + Log::Comment(L"When LNM is reset, newline auto return and line feed mode are disabled."); + stateMachine.ProcessString(L"\x1B[20l"); + VERIFY_IS_FALSE(WI_IsFlagClear(si.OutputMode, DISABLE_NEWLINE_AUTO_RETURN)); + VERIFY_IS_FALSE(terminalInput.GetInputMode(TerminalInput::Mode::LineFeed)); +} + void ScreenBufferTests::SetScreenMode() { auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index b99ccd525d5..7a04ea59166 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -1739,7 +1739,39 @@ class AdapterTest _testGetSet->ValidateInputEvent(L"\033P0$r\033\\"); } - TEST_METHOD(RequestModeTests) + TEST_METHOD(RequestStandardModeTests) + { + // The mode numbers below correspond to the ANSIStandardMode values + // in the ModeParams enum in DispatchTypes.hpp. + + BEGIN_TEST_METHOD_PROPERTIES() + TEST_METHOD_PROPERTY(L"Data:modeNumber", L"{4, 20}") + END_TEST_METHOD_PROPERTIES() + + VTInt modeNumber; + VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"modeNumber", modeNumber)); + const auto mode = DispatchTypes::ANSIStandardMode(modeNumber); + + // DISABLE_ + Log::Comment(NoThrowString().Format(L"Setting standard mode %d", modeNumber)); + _testGetSet->PrepData(); + VERIFY_IS_TRUE(_pDispatch->SetMode(mode)); + VERIFY_IS_TRUE(_pDispatch->RequestMode(mode)); + + wchar_t expectedResponse[20]; + swprintf_s(expectedResponse, ARRAYSIZE(expectedResponse), L"\x1b[%d;1$y", modeNumber); + _testGetSet->ValidateInputEvent(expectedResponse); + + Log::Comment(NoThrowString().Format(L"Resetting standard mode %d", modeNumber)); + _testGetSet->PrepData(); + VERIFY_IS_TRUE(_pDispatch->ResetMode(mode)); + VERIFY_IS_TRUE(_pDispatch->RequestMode(mode)); + + swprintf_s(expectedResponse, ARRAYSIZE(expectedResponse), L"\x1b[%d;2$y", modeNumber); + _testGetSet->ValidateInputEvent(expectedResponse); + } + + TEST_METHOD(RequestPrivateModeTests) { // The mode numbers below correspond to the DECPrivateMode values // in the ModeParams enum in DispatchTypes.hpp. We don't include @@ -1747,7 +1779,7 @@ class AdapterTest // and DECRQM would not then be applicable. BEGIN_TEST_METHOD_PROPERTIES() - TEST_METHOD_PROPERTY(L"Data:modeNumber", L"{1, 3, 5, 6, 8, 12, 25, 40, 66, 67, 1000, 1002, 1003, 1004, 1005, 1006, 1007, 1049, 9001}") + TEST_METHOD_PROPERTY(L"Data:modeNumber", L"{1, 3, 5, 6, 7, 8, 12, 25, 40, 66, 67, 1000, 1002, 1003, 1004, 1005, 1006, 1007, 1049, 2004, 9001}") END_TEST_METHOD_PROPERTIES() VTInt modeNumber;