Add SS3 cursor key encoding to ConPty (#5383)

## 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.
This commit is contained in:
Carlos Zamora 2020-04-17 09:14:38 -07:00 committed by GitHub
parent f04b7aae87
commit f1fe4c6ccd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 74 additions and 49 deletions

View file

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

View file

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

View file

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

View file

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