Add support for LNM (Line Feed/New Line Mode) (#15261)

This PR adds support for the ANSI Line Feed/New Line mode (`LNM`), which
determines whether outputting a linefeed control should also trigger a
carriage return, and whether the `Return` key should generate an `LF` in
addition to `CR`.

## Detailed Description of the Pull Request / Additional comments

In ConHost, there was already a console mode which handled the output
side of things, but I've now also added a `TerminalInput` mode that
controls the behavior of the `Return` key. When `LNM` is set, both the
output and input modes are enabled, and when reset, they're disabled.

If they're not already matching, then `LNM` has no effect, and will be
reported as unknown when queried. This is the typical state for legacy
console applications, which expect a linefeed to trigger a carriage
return, but wouldn't want the `Return` key generating both `CR`+`LF`.

As part of this PR, I've also refactored the `ITerminalApi` interface to
consolidate what I'm now calling the "system" modes: bracketed paste,
auto wrap, and the new line feed mode. This closes another gap between
Terminal and ConHost, so both auto wrap, and line feed mode will now be
supported for conpty pass through.

## Validation Steps Performed

I've added an `LNM` test that checks the escape sequence is triggering
both of the expected mode changes, and added an additional `DECRQM` test
covering the currently implemented standard modes: the new `LNM`, and
the existing `IRM` (which wasn't previously tested). I've also extended
the `DECRQM` private mode test to cover `DECAWM` and Bracketed Paste
(which we also weren't previously testing).

I've manually tested `LNM` in Vttest to confirm the keyboard is working
as expected.

Closes #15167
This commit is contained in:
James Holderness
2023-05-13 00:16:48 +01:00
committed by GitHub
parent 1324a0148a
commit 3d737214a4
12 changed files with 169 additions and 126 deletions

View File

@@ -235,7 +235,7 @@ void Terminal::EraseScrollback()
bool Terminal::IsXtermBracketedPasteModeEnabled() const noexcept
{
return _bracketedPasteMode;
return _systemMode.test(Mode::BracketedPaste);
}
std::wstring_view Terminal::GetWorkingDirectory() noexcept

View File

@@ -111,17 +111,14 @@ public:
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 @@ private:
CursorType _defaultCursorShape = CursorType::Legacy;
til::enumset<Mode> _systemMode{ Mode::AutoWrap };
bool _snapOnInput = true;
bool _altGrAliasing = true;
bool _suppressApplicationTitle = false;
bool _bracketedPasteMode = false;
bool _trimBlockSelection = false;
bool _autoMarkPrompts = false;

View File

@@ -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);

View File

@@ -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:
// - <none>
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:
// - <none>
// - 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:
// - <none>
void ConhostInternalGetSet::SetBracketedPasteMode(const bool enabled)
{
ServiceLocator::LocateGlobals().getConsoleInformation().SetBracketedPasteMode(enabled);
}
// Routine Description:
// - Gets the current state of XTerm bracketed paste mode.
// Arguments:
// - <none>
// 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:

View File

@@ -38,13 +38,11 @@ public:
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 @@ public:
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;

View File

@@ -202,6 +202,7 @@ class ScreenBufferTests
TEST_METHOD(ScrollLines256Colors);
TEST_METHOD(SetLineFeedMode);
TEST_METHOD(SetScreenMode);
TEST_METHOD(SetOriginMode);
TEST_METHOD(SetAutoWrapMode);
@@ -5203,6 +5204,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();

View File

@@ -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),

View File

@@ -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;

View File

@@ -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.
@@ -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();
@@ -1719,7 +1728,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 +1780,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);
@@ -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;
@@ -1840,7 +1857,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 +1906,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 +2220,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 +2607,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.
@@ -2663,11 +2680,20 @@ 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();
// Reset bracketed paste mode
_api.SetBracketedPasteMode(false);
_api.SetSystemMode(ITerminalApi::Mode::BracketedPaste, false);
// Restore cursor blinking mode.
_api.GetTextBuffer().GetCursor().SetBlinkingAllowed(true);

View File

@@ -96,17 +96,6 @@ public:
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,17 +110,23 @@ public:
_textBuffer->SetCurrentAttributes(attrs);
}
void SetSystemMode(const Mode mode, const bool enabled)
{
Log::Comment(L"SetSystemMode MOCK called...");
_systemMode.set(mode, enabled);
}
bool GetSystemMode(const Mode mode) const
{
Log::Comment(L"GetSystemMode MOCK called...");
return _systemMode.test(mode);
}
void WarningBell() override
{
Log::Comment(L"WarningBell MOCK called...");
}
bool GetLineFeedMode() const override
{
Log::Comment(L"GetLineFeedMode MOCK called...");
return _getLineFeedModeResult;
}
void SetWindowTitle(const std::wstring_view title)
{
Log::Comment(L"SetWindowTitle MOCK called...");
@@ -184,17 +179,6 @@ public:
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 @@ public:
bool _setTextAttributesResult = false;
bool _returnResponseResult = false;
bool _getLineFeedModeResult = false;
til::enumset<Mode> _systemMode{ Mode::AutoWrap };
bool _setWindowTitleResult = false;
std::wstring_view _expectedWindowTitle{};
@@ -1755,7 +1739,39 @@ public:
_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
@@ -1763,7 +1779,7 @@ public:
// 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;
@@ -2262,13 +2278,13 @@ public:
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());

View File

@@ -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.

View File

@@ -38,6 +38,7 @@ namespace Microsoft::Console::VirtualTerminal
enum class Mode : size_t
{
LineFeed,
Ansi,
AutoRepeat,
Keypad,