Skip to content

Commit

Permalink
Add SS3 cursor key encoding to ConPty (#5383)
Browse files Browse the repository at this point in the history
## Summary of the Pull Request
Adds SS3 cursor encoding for cursor keys and home/end button. Reverts a portion of #4913 that checks for VT Input Mode.

## PR Checklist
* [X] Closes #4873

## Validation Steps Performed
1. Open pwsh
2. run `wsl`
3. execute `printf "\e[?1h"`
4. verify keys work
5. exit back to pwsh
6. verify keys work still (didn't previously)

Also verified that those keys work in vim when connected to my Raspberry Pi over SSH.
  • Loading branch information
carlos-zamora authored Apr 17, 2020
1 parent f04b7aa commit f1fe4c6
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 49 deletions.
56 changes: 16 additions & 40 deletions src/terminal/adapter/adaptDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1056,11 +1056,8 @@ bool AdaptDispatch::SetKeypadMode(const bool fApplicationMode)
bool success = true;
success = _pConApi->PrivateSetKeypadMode(fApplicationMode);

// If we're a conpty, AND WE'RE IN VT INPUT MODE, always return false
// The VT Input mode check is to work around ssh.exe v7.7, which uses VT
// output, but not Input. Once the conpty supports these types of input,
// this check can be removed. See GH#4911
if (_pConApi->IsConsolePty() && _pConApi->PrivateIsVtInputEnabled())
// If we're a conpty, always return false
if (_pConApi->IsConsolePty())
{
return false;
}
Expand All @@ -1078,11 +1075,8 @@ bool AdaptDispatch::SetCursorKeysMode(const bool applicationMode)
bool success = true;
success = _pConApi->PrivateSetCursorKeysMode(applicationMode);

// If we're a conpty, AND WE'RE IN VT INPUT MODE, always return false
// The VT Input mode check is to work around ssh.exe v7.7, which uses VT
// output, but not Input. Once the conpty supports these types of input,
// this check can be removed. See GH#4911
if (_pConApi->IsConsolePty() && _pConApi->PrivateIsVtInputEnabled())
// If we're a conpty, always return false
if (_pConApi->IsConsolePty())
{
return false;
}
Expand Down Expand Up @@ -1878,11 +1872,8 @@ bool AdaptDispatch::EnableVT200MouseMode(const bool enabled)
bool success = true;
success = _pConApi->PrivateEnableVT200MouseMode(enabled);

// If we're a conpty, AND WE'RE IN VT INPUT MODE, always return false
// The VT Input mode check is to work around ssh.exe v7.7, which uses VT
// output, but not Input. Once the conpty supports these types of input,
// this check can be removed. See GH#4911
if (_pConApi->IsConsolePty() && _pConApi->PrivateIsVtInputEnabled())
// If we're a conpty, always return false
if (_pConApi->IsConsolePty())
{
return false;
}
Expand All @@ -1902,11 +1893,8 @@ bool AdaptDispatch::EnableUTF8ExtendedMouseMode(const bool enabled)
bool success = true;
success = _pConApi->PrivateEnableUTF8ExtendedMouseMode(enabled);

// If we're a conpty, AND WE'RE IN VT INPUT MODE, always return false
// The VT Input mode check is to work around ssh.exe v7.7, which uses VT
// output, but not Input. Once the conpty supports these types of input,
// this check can be removed. See GH#4911
if (_pConApi->IsConsolePty() && _pConApi->PrivateIsVtInputEnabled())
// If we're a conpty, always return false
if (_pConApi->IsConsolePty())
{
return false;
}
Expand All @@ -1926,11 +1914,8 @@ bool AdaptDispatch::EnableSGRExtendedMouseMode(const bool enabled)
bool success = true;
success = _pConApi->PrivateEnableSGRExtendedMouseMode(enabled);

// If we're a conpty, AND WE'RE IN VT INPUT MODE, always return false
// The VT Input mode check is to work around ssh.exe v7.7, which uses VT
// output, but not Input. Once the conpty supports these types of input,
// this check can be removed. See GH#4911
if (_pConApi->IsConsolePty() && _pConApi->PrivateIsVtInputEnabled())
// If we're a conpty, always return false
if (_pConApi->IsConsolePty())
{
return false;
}
Expand All @@ -1949,11 +1934,8 @@ bool AdaptDispatch::EnableButtonEventMouseMode(const bool enabled)
bool success = true;
success = _pConApi->PrivateEnableButtonEventMouseMode(enabled);

// If we're a conpty, AND WE'RE IN VT INPUT MODE, always return false
// The VT Input mode check is to work around ssh.exe v7.7, which uses VT
// output, but not Input. Once the conpty supports these types of input,
// this check can be removed. See GH#4911
if (_pConApi->IsConsolePty() && _pConApi->PrivateIsVtInputEnabled())
// If we're a conpty, always return false
if (_pConApi->IsConsolePty())
{
return false;
}
Expand All @@ -1973,11 +1955,8 @@ bool AdaptDispatch::EnableAnyEventMouseMode(const bool enabled)
bool success = true;
success = _pConApi->PrivateEnableAnyEventMouseMode(enabled);

// If we're a conpty, AND WE'RE IN VT INPUT MODE, always return false
// The VT Input mode check is to work around ssh.exe v7.7, which uses VT
// output, but not Input. Once the conpty supports these types of input,
// this check can be removed. See GH#4911
if (_pConApi->IsConsolePty() && _pConApi->PrivateIsVtInputEnabled())
// If we're a conpty, always return false
if (_pConApi->IsConsolePty())
{
return false;
}
Expand All @@ -1997,11 +1976,8 @@ bool AdaptDispatch::EnableAlternateScroll(const bool enabled)
bool success = true;
success = _pConApi->PrivateEnableAlternateScroll(enabled);

// If we're a conpty, AND WE'RE IN VT INPUT MODE, always return false
// The VT Input mode check is to work around ssh.exe v7.7, which uses VT
// output, but not Input. Once the conpty supports these types of input,
// this check can be removed. See GH#4911
if (_pConApi->IsConsolePty() && _pConApi->PrivateIsVtInputEnabled())
// If we're a conpty, always return false
if (_pConApi->IsConsolePty())
{
return false;
}
Expand Down
8 changes: 7 additions & 1 deletion src/terminal/parser/InputStateMachineEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,13 @@ struct Ss3ToVkey
short vkey;
};

static constexpr std::array<Ss3ToVkey, 4> s_ss3Map = {
static constexpr std::array<Ss3ToVkey, 10> s_ss3Map = {
Ss3ToVkey{ Ss3ActionCodes::ArrowUp, VK_UP },
Ss3ToVkey{ Ss3ActionCodes::ArrowDown, VK_DOWN },
Ss3ToVkey{ Ss3ActionCodes::ArrowRight, VK_RIGHT },
Ss3ToVkey{ Ss3ActionCodes::ArrowLeft, VK_LEFT },
Ss3ToVkey{ Ss3ActionCodes::End, VK_END },
Ss3ToVkey{ Ss3ActionCodes::Home, VK_HOME },
Ss3ToVkey{ Ss3ActionCodes::SS3_F1, VK_F1 },
Ss3ToVkey{ Ss3ActionCodes::SS3_F2, VK_F2 },
Ss3ToVkey{ Ss3ActionCodes::SS3_F3, VK_F3 },
Expand Down
14 changes: 6 additions & 8 deletions src/terminal/parser/InputStateMachineEngine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,12 @@ namespace Microsoft::Console::VirtualTerminal

enum class Ss3ActionCodes : wchar_t
{
// The "Cursor Keys" are sometimes sent as a SS3 in "application mode"
// But for now we'll only accept them as Normal Mode sequences, as CSI's.
// ArrowUp = L'A',
// ArrowDown = L'B',
// ArrowRight = L'C',
// ArrowLeft = L'D',
// Home = L'H',
// End = L'F',
ArrowUp = L'A',
ArrowDown = L'B',
ArrowRight = L'C',
ArrowLeft = L'D',
Home = L'H',
End = L'F',
SS3_F1 = L'P',
SS3_F2 = L'Q',
SS3_F3 = L'R',
Expand Down
45 changes: 45 additions & 0 deletions src/terminal/parser/ut_parser/InputEngineTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ class Microsoft::Console::VirtualTerminal::InputEngineTest
TEST_METHOD(CursorPositioningTest);
TEST_METHOD(CSICursorBackTabTest);
TEST_METHOD(EnhancedKeysTest);
TEST_METHOD(SS3CursorKeyTest);
TEST_METHOD(AltBackspaceTest);
TEST_METHOD(AltCtrlDTest);
TEST_METHOD(AltIntermediateTest);
Expand Down Expand Up @@ -844,6 +845,50 @@ void InputEngineTest::EnhancedKeysTest()
VerifyExpectedInputDrained();
}

void InputEngineTest::SS3CursorKeyTest()
{
auto pfn = std::bind(&TestState::TestInputCallback, &testState, std::placeholders::_1);
auto dispatch = std::make_unique<TestInteractDispatch>(pfn, &testState);
auto inputEngine = std::make_unique<InputStateMachineEngine>(std::move(dispatch));
auto _stateMachine = std::make_unique<StateMachine>(std::move(inputEngine));
VERIFY_IS_NOT_NULL(_stateMachine);
testState._stateMachine = _stateMachine.get();

// clang-format off
const std::map<int, std::wstring> cursorKeys{
{ VK_UP, L"\x1bOA" },
{ VK_DOWN, L"\x1bOB" },
{ VK_RIGHT, L"\x1bOC" },
{ VK_LEFT, L"\x1bOD" },
{ VK_HOME, L"\x1bOH" },
{ VK_END, L"\x1bOF" },
};
// clang-format on

for (const auto& [vkey, seq] : cursorKeys)
{
INPUT_RECORD inputRec;

const wchar_t wch = (wchar_t)MapVirtualKeyW(vkey, MAPVK_VK_TO_CHAR);
const WORD scanCode = (WORD)MapVirtualKeyW(vkey, MAPVK_VK_TO_VSC);

inputRec.EventType = KEY_EVENT;
inputRec.Event.KeyEvent.bKeyDown = TRUE;
inputRec.Event.KeyEvent.dwControlKeyState = 0;
inputRec.Event.KeyEvent.wRepeatCount = 1;
inputRec.Event.KeyEvent.wVirtualKeyCode = static_cast<WORD>(vkey);
inputRec.Event.KeyEvent.wVirtualScanCode = scanCode;
inputRec.Event.KeyEvent.uChar.UnicodeChar = wch;

testState.vExpectedInput.push_back(inputRec);

Log::Comment(NoThrowString().Format(
L"Processing \"%s\"", seq.c_str()));
_stateMachine->ProcessString(seq);
}
VerifyExpectedInputDrained();
}

void InputEngineTest::AltBackspaceTest()
{
auto pfn = std::bind(&TestState::TestInputCallback, &testState, std::placeholders::_1);
Expand Down

0 comments on commit f1fe4c6

Please sign in to comment.