diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index b5f048bc3c2..0c94916712e 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -496,6 +496,7 @@ DECALN DECANM DECAUPSS DECAWM +DECBKM DECCKM DECCOLM DECCRA diff --git a/src/terminal/adapter/DispatchTypes.hpp b/src/terminal/adapter/DispatchTypes.hpp index afa1de0f797..5a0bf610eb6 100644 --- a/src/terminal/adapter/DispatchTypes.hpp +++ b/src/terminal/adapter/DispatchTypes.hpp @@ -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), diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 549d4b3f366..400096db064 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -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(); } // Routine Description: @@ -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; @@ -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(); @@ -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: diff --git a/src/terminal/adapter/ut_adapter/inputTest.cpp b/src/terminal/adapter/ut_adapter/inputTest.cpp index ace7de68019..f1c9bf2698b 100644 --- a/src/terminal/adapter/ut_adapter/inputTest.cpp +++ b/src/terminal/adapter/ut_adapter/inputTest.cpp @@ -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) { @@ -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); +} diff --git a/src/terminal/input/terminalInput.cpp b/src/terminal/input/terminalInput.cpp index 79b5ef74f5f..19f9104eb44 100644 --- a/src/terminal/input/terminalInput.cpp +++ b/src/terminal/input/terminalInput.cpp @@ -71,9 +71,8 @@ static constexpr std::array s_cursorKeysVt52Mapping{ TermKeyMap{ VK_END, L"\033F" }, }; -static constexpr std::array s_keypadNumericMapping{ +static constexpr std::array 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~" }, @@ -103,9 +102,8 @@ static constexpr std::array 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 s_keypadApplicationMapping{ +static constexpr std::array 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~" }, @@ -153,9 +151,8 @@ static constexpr std::array s_keypadApplicationMapping{ // TermKeyMap{ VK_TAB, L"\x1bOI" }, // So I left them here as a reference just in case. }; -static constexpr std::array s_keypadVt52Mapping{ +static constexpr std::array 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~" }, @@ -211,10 +208,7 @@ static constexpr std::array 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 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 s_simpleModifiedKeyMapping{ TermKeyMap{ VK_TAB, CTRL_PRESSED, L"\t" }, TermKeyMap{ VK_TAB, SHIFT_PRESSED, L"\x1b[Z" }, TermKeyMap{ VK_DIVIDE, CTRL_PRESSED, L"\x1F" }, @@ -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. diff --git a/src/terminal/input/terminalInput.hpp b/src/terminal/input/terminalInput.hpp index 8f0ac5dcbee..80ff4e8c06f 100644 --- a/src/terminal/input/terminalInput.hpp +++ b/src/terminal/input/terminalInput.hpp @@ -41,6 +41,7 @@ namespace Microsoft::Console::VirtualTerminal Ansi, Keypad, CursorKey, + BackarrowKey, Win32, Utf8MouseEncoding,