Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for DECBKM (Backarrow Key Mode) #13894

Merged
5 commits merged into from
Sep 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actions/spelling/expect/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ DECALN
DECANM
DECAUPSS
DECAWM
DECBKM
DECCKM
DECCOLM
DECCRA
Expand Down
1 change: 1 addition & 0 deletions src/terminal/adapter/DispatchTypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
ATT610_StartCursorBlink = DECPrivateMode(12),
DECTCEM_TextCursorEnableMode = DECPrivateMode(25),
XTERM_EnableDECCOLMSupport = DECPrivateMode(40),
DECBKM_BackarrowKeyMode = DECPrivateMode(67),
VT200_MOUSE_MODE = DECPrivateMode(1000),
BUTTON_EVENT_MOUSE_MODE = DECPrivateMode(1002),
ANY_EVENT_MOUSE_MODE = DECPrivateMode(1003),
Expand Down
28 changes: 10 additions & 18 deletions src/terminal/adapter/adaptDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -978,23 +978,7 @@ bool AdaptDispatch::_SetInputMode(const TerminalInput::Mode mode, const bool ena
// impact on the actual connected terminal. We can't remove this check,
// because SSH <=7.7 is out in the wild on all versions of Windows <=2004.

// GH#12799 - If the app requested that we disable focus events, DON'T pass
// that through. ConPTY would _always_ like to know about focus events.

return !_api.IsConsolePty() ||
!_api.IsVtInputEnabled() ||
(!enable && mode == TerminalInput::Mode::FocusEvent);

// Another way of writing the above statement is:
//
// const bool inConpty = _api.IsConsolePty();
// const bool shouldPassthrough = inConpty && _api.IsVtInputEnabled();
// const bool disabledFocusEvents = inConpty && (!enable && mode == TerminalInput::Mode::FocusEvent);
// return !shouldPassthrough || disabledFocusEvents;
//
// It's like a "filter" left to right. Due to the early return via
// !IsConsolePty, once you're at the !enable part, IsConsolePty can only be
// true anymore.
return !_api.IsConsolePty() || !_api.IsVtInputEnabled();
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviewers, if you didn't read the PR description, the additional check here that was specific to the FocusEvent mode has now been moved down into the EnableFocusEventMode method. I think this reduces some of the confusion that was introduced here, and also avoids unnecessary overhead in the other input mode operations.

This is isn't strictly related to DECBKM mode, though (other than as a minor optimisation), so if you'd prefer not to include this change in the PR, it wouldn't be a problem to revert it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes way more sense. Thanks!

}

// Routine Description:
Expand Down Expand Up @@ -1038,6 +1022,9 @@ bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con
case DispatchTypes::ModeParams::XTERM_EnableDECCOLMSupport:
success = EnableDECCOLMSupport(enable);
break;
case DispatchTypes::ModeParams::DECBKM_BackarrowKeyMode:
success = _SetInputMode(TerminalInput::Mode::BackarrowKey, enable);
break;
case DispatchTypes::ModeParams::VT200_MOUSE_MODE:
success = EnableVT200MouseMode(enable);
break;
Expand Down Expand Up @@ -1874,6 +1861,9 @@ bool AdaptDispatch::HardReset()
EnableSGRExtendedMouseMode(false);
EnableAnyEventMouseMode(false);

// Reset the Backarrow Key mode
_SetInputMode(TerminalInput::Mode::BackarrowKey, false);

// Delete all current tab stops and reapply
_ResetTabStops();

Expand Down Expand Up @@ -2088,7 +2078,9 @@ bool AdaptDispatch::EnableAnyEventMouseMode(const bool enabled)
// - True if handled successfully. False otherwise.
bool AdaptDispatch::EnableFocusEventMode(const bool enabled)
{
return _SetInputMode(TerminalInput::Mode::FocusEvent, enabled);
// GH#12799 - If the app requested that we disable focus events, DON'T pass
// that through. ConPTY would _always_ like to know about focus events.
return _SetInputMode(TerminalInput::Mode::FocusEvent, enabled) || !enabled;
}

//Routine Description:
Expand Down
63 changes: 63 additions & 0 deletions src/terminal/adapter/ut_adapter/inputTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class Microsoft::Console::VirtualTerminal::InputTest
TEST_METHOD(TerminalInputNullKeyTests);
TEST_METHOD(DifferentModifiersTest);
TEST_METHOD(CtrlNumTest);
TEST_METHOD(BackarrowKeyModeTest);

wchar_t GetModifierChar(const bool fShift, const bool fAlt, const bool fCtrl)
{
Expand Down Expand Up @@ -788,3 +789,65 @@ void InputTest::CtrlNumTest()
s_expectedInput = L"9";
TestKey(pInput, uiKeystate, vkey);
}

void InputTest::BackarrowKeyModeTest()
{
Log::Comment(L"Starting test...");

const auto pInput = new TerminalInput(s_TerminalInputTestCallback);
const BYTE vkey = VK_BACK;

Log::Comment(L"Sending backspace key combos with DECBKM enabled.");
pInput->SetInputMode(TerminalInput::Mode::BackarrowKey, true);

s_expectedInput = L"\x8";
TestKey(pInput, 0, vkey);

s_expectedInput = L"\x8";
TestKey(pInput, SHIFT_PRESSED, vkey);

s_expectedInput = L"\x7f";
TestKey(pInput, LEFT_CTRL_PRESSED, vkey);

s_expectedInput = L"\x7f";
TestKey(pInput, LEFT_CTRL_PRESSED | SHIFT_PRESSED, vkey);

s_expectedInput = L"\x1b\x8";
TestKey(pInput, LEFT_ALT_PRESSED, vkey);

s_expectedInput = L"\x1b\x8";
TestKey(pInput, LEFT_ALT_PRESSED | SHIFT_PRESSED, vkey);

s_expectedInput = L"\x1b\x7f";
TestKey(pInput, LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED, vkey);

s_expectedInput = L"\x1b\x7f";
TestKey(pInput, LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED | SHIFT_PRESSED, vkey);

Log::Comment(L"Sending backspace key combos with DECBKM disabled.");
pInput->SetInputMode(TerminalInput::Mode::BackarrowKey, false);

s_expectedInput = L"\x7f";
TestKey(pInput, 0, vkey);

s_expectedInput = L"\x7f";
TestKey(pInput, SHIFT_PRESSED, vkey);

s_expectedInput = L"\x8";
TestKey(pInput, LEFT_CTRL_PRESSED, vkey);

s_expectedInput = L"\x8";
TestKey(pInput, LEFT_CTRL_PRESSED | SHIFT_PRESSED, vkey);

s_expectedInput = L"\x1b\x7f";
TestKey(pInput, LEFT_ALT_PRESSED, vkey);

s_expectedInput = L"\x1b\x7f";
TestKey(pInput, LEFT_ALT_PRESSED | SHIFT_PRESSED, vkey);

s_expectedInput = L"\x1b\x8";
TestKey(pInput, LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED, vkey);

s_expectedInput = L"\x1b\x8";
TestKey(pInput, LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED | SHIFT_PRESSED, vkey);
}
33 changes: 23 additions & 10 deletions src/terminal/input/terminalInput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,8 @@ static constexpr std::array<TermKeyMap, 6> s_cursorKeysVt52Mapping{
TermKeyMap{ VK_END, L"\033F" },
};

static constexpr std::array<TermKeyMap, 20> s_keypadNumericMapping{
static constexpr std::array<TermKeyMap, 19> s_keypadNumericMapping{
TermKeyMap{ VK_TAB, L"\x09" },
TermKeyMap{ VK_BACK, L"\x7f" },
TermKeyMap{ VK_PAUSE, L"\x1a" },
TermKeyMap{ VK_ESCAPE, L"\x1b" },
TermKeyMap{ VK_INSERT, L"\x1b[2~" },
Expand Down Expand Up @@ -103,9 +102,8 @@ static constexpr std::array<TermKeyMap, 20> s_keypadNumericMapping{
//It seems to me as though this was used for early numpad implementations, where presently numlock would enable
// "numeric" mode, outputting the numbers on the keys, while "application" mode does things like pgup/down, arrow keys, etc.
//These keys aren't translated at all in numeric mode, so I figured I'd leave them out of the numeric table.
static constexpr std::array<TermKeyMap, 20> s_keypadApplicationMapping{
static constexpr std::array<TermKeyMap, 19> s_keypadApplicationMapping{
TermKeyMap{ VK_TAB, L"\x09" },
TermKeyMap{ VK_BACK, L"\x7f" },
TermKeyMap{ VK_PAUSE, L"\x1a" },
TermKeyMap{ VK_ESCAPE, L"\x1b" },
TermKeyMap{ VK_INSERT, L"\x1b[2~" },
Expand Down Expand Up @@ -153,9 +151,8 @@ static constexpr std::array<TermKeyMap, 20> s_keypadApplicationMapping{
// TermKeyMap{ VK_TAB, L"\x1bOI" }, // So I left them here as a reference just in case.
};

static constexpr std::array<TermKeyMap, 20> s_keypadVt52Mapping{
static constexpr std::array<TermKeyMap, 19> s_keypadVt52Mapping{
TermKeyMap{ VK_TAB, L"\x09" },
TermKeyMap{ VK_BACK, L"\x7f" },
TermKeyMap{ VK_PAUSE, L"\x1a" },
TermKeyMap{ VK_ESCAPE, L"\x1b" },
TermKeyMap{ VK_INSERT, L"\x1b[2~" },
Expand Down Expand Up @@ -211,10 +208,7 @@ static constexpr std::array<TermKeyMap, 22> s_modifierKeyMapping{
// These sequences are not later updated to encode the modifier state in the
// sequence itself, they are just weird exceptional cases to the general
// rules above.
static constexpr std::array<TermKeyMap, 14> s_simpleModifiedKeyMapping{
TermKeyMap{ VK_BACK, CTRL_PRESSED, L"\x8" },
TermKeyMap{ VK_BACK, ALT_PRESSED, L"\x1b\x7f" },
TermKeyMap{ VK_BACK, CTRL_PRESSED | ALT_PRESSED, L"\x1b\x8" },
static constexpr std::array<TermKeyMap, 11> s_simpleModifiedKeyMapping{
TermKeyMap{ VK_TAB, CTRL_PRESSED, L"\t" },
TermKeyMap{ VK_TAB, SHIFT_PRESSED, L"\x1b[Z" },
TermKeyMap{ VK_DIVIDE, CTRL_PRESSED, L"\x1F" },
Expand Down Expand Up @@ -562,6 +556,25 @@ bool TerminalInput::HandleKey(const IInputEvent* const pInEvent)
return false;
}

// The VK_BACK key depends on the state of Backarrow Key mode (DECBKM).
// If the mode is set, we should send BS. If reset, we should send DEL.
if (keyEvent.GetVirtualKeyCode() == VK_BACK)
{
// The Ctrl modifier reverses the interpretation of DECBKM.
const auto backarrowMode = _inputMode.test(Mode::BackarrowKey) != keyEvent.IsCtrlPressed();
const auto seq = backarrowMode ? L'\x08' : L'\x7f';
// The Alt modifier adds an escape prefix.
if (keyEvent.IsAltPressed())
{
_SendEscapedInputSequence(seq);
}
else
{
_SendInputSequence({ &seq, 1 });
}
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.
Expand Down
1 change: 1 addition & 0 deletions src/terminal/input/terminalInput.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ namespace Microsoft::Console::VirtualTerminal
Ansi,
Keypad,
CursorKey,
BackarrowKey,
Win32,

Utf8MouseEncoding,
Expand Down