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 enhanced key support for ConPty #5021

Merged
5 commits merged into from
Mar 24, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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
48 changes: 39 additions & 9 deletions src/terminal/parser/InputStateMachineEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,8 @@ bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch,
case CsiActionCodes::CSI_F1:
case CsiActionCodes::CSI_F2:
case CsiActionCodes::CSI_F4:
modifierState = _GetCursorKeysModifierState(parameters);
success = _GetCursorKeysVkey(wch, vkey);
modifierState = _GetCursorKeysModifierState(parameters, static_cast<CsiActionCodes>(wch));
break;
case CsiActionCodes::CursorBackTab:
modifierState = SHIFT_PRESSED;
Expand Down Expand Up @@ -602,7 +602,10 @@ void InputStateMachineEngine::_GenerateWrappedSequence(const wchar_t wch,
input.push_back(next);
}

_GetSingleKeypress(wch, vkey, currentModifiers, input);
// Use modifierState instead of currentModifiers here.
// This allows other modifiers like ENHANCED_KEY to get
// through on the KeyPress.
_GetSingleKeypress(wch, vkey, modifierState, input);

if (ctrl)
{
Expand Down Expand Up @@ -741,22 +744,37 @@ bool InputStateMachineEngine::_WriteMouseEvent(const size_t column, const size_t
// - Retrieves the modifier state from a set of parameters for a cursor keys
// sequence. This is for Arrow keys, Home, End, etc.
// Arguments:
// - rgusParams - the set of parameters to get the modifier state from.
// - cParams - the number of elements in rgusParams
// - parameters - the set of parameters to get the modifier state from.
// - actionCode - the actionCode for the sequence we're operating on.
// Return Value:
// - the INPUT_RECORD compatible modifier state.
DWORD InputStateMachineEngine::_GetCursorKeysModifierState(const std::basic_string_view<size_t> parameters) noexcept
DWORD InputStateMachineEngine::_GetCursorKeysModifierState(const std::basic_string_view<size_t> parameters, const CsiActionCodes actionCode) noexcept
{
// Both Cursor keys and generic keys keep their modifiers in the same index.
return _GetGenericKeysModifierState(parameters);
DWORD modifiers = 0;
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved
if (_IsModified(parameters.size()) && parameters.size() >= 2)
{
modifiers = _GetModifier(parameters.at(1));
}

// Enhanced Keys (from https://docs.microsoft.com/en-us/windows/console/key-event-record-str):
// Enhanced keys for the IBM 101- and 102-key keyboards are the INS, DEL,
// HOME, END, PAGE UP, PAGE DOWN, and direction keys in the clusters to the left
// of the keypad; and the divide (/) and ENTER keys in the keypad.
// This snippet detects the direction keys + HOME + END
// actionCode should be one of the above, so just make sure it's not a CSI_F# code
if (actionCode < CsiActionCodes::CSI_F1 || actionCode > CsiActionCodes::CSI_F4)
{
WI_SetFlag(modifiers, ENHANCED_KEY);
}

return modifiers;
}

// Method Description:
// - Retrieves the modifier state from a set of parameters for a "Generic"
// keypress - one who's sequence is terminated with a '~'.
// Arguments:
// - rgusParams - the set of parameters to get the modifier state from.
// - cParams - the number of elements in rgusParams
// - parameters - the set of parameters to get the modifier state from.
// Return Value:
// - the INPUT_RECORD compatible modifier state.
DWORD InputStateMachineEngine::_GetGenericKeysModifierState(const std::basic_string_view<size_t> parameters) noexcept
Expand All @@ -766,6 +784,18 @@ DWORD InputStateMachineEngine::_GetGenericKeysModifierState(const std::basic_str
{
modifiers = _GetModifier(parameters.at(1));
}

// Enhanced Keys (from https://docs.microsoft.com/en-us/windows/console/key-event-record-str):
// Enhanced keys for the IBM 101- and 102-key keyboards are the INS, DEL,
// HOME, END, PAGE UP, PAGE DOWN, and direction keys in the clusters to the left
// of the keypad; and the divide (/) and ENTER keys in the keypad.
// This snippet detects the non-direction keys
const auto identifier = (GenericKeyIdentifiers)til::at(parameters, 0);
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved
if (identifier <= GenericKeyIdentifiers::Next)
{
modifiers = WI_SetFlag(modifiers, ENHANCED_KEY);
}

return modifiers;
}

Expand Down
2 changes: 1 addition & 1 deletion src/terminal/parser/InputStateMachineEngine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ namespace Microsoft::Console::VirtualTerminal
bool _lookingForDSR;
DWORD _mouseButtonState = 0;

DWORD _GetCursorKeysModifierState(const std::basic_string_view<size_t> parameters) noexcept;
DWORD _GetCursorKeysModifierState(const std::basic_string_view<size_t> parameters, const CsiActionCodes actionCode) noexcept;
DWORD _GetGenericKeysModifierState(const std::basic_string_view<size_t> parameters) noexcept;
DWORD _GetSGRMouseModifierState(const std::basic_string_view<size_t> parameters) noexcept;
bool _GenerateKeyFromChar(const wchar_t wch, short& vkey, DWORD& modifierState) noexcept;
Expand Down
58 changes: 57 additions & 1 deletion src/terminal/parser/ut_parser/InputEngineTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ bool ModifiersEquivalent(DWORD a, DWORD b)
bool fShift = IsShiftPressed(a) == IsShiftPressed(b);
bool fAlt = IsAltPressed(a) == IsAltPressed(b);
bool fCtrl = IsCtrlPressed(a) == IsCtrlPressed(b);
return fShift && fCtrl && fAlt;
bool fEnhanced = WI_IsFlagSet(a, ENHANCED_KEY) == WI_IsFlagSet(b, ENHANCED_KEY);
return fShift && fCtrl && fAlt && fEnhanced;
}

class TestState
Expand Down Expand Up @@ -251,6 +252,7 @@ class Microsoft::Console::VirtualTerminal::InputEngineTest
TEST_METHOD(NonAsciiTest);
TEST_METHOD(CursorPositioningTest);
TEST_METHOD(CSICursorBackTabTest);
TEST_METHOD(EnhancedKeysTest);
TEST_METHOD(AltBackspaceTest);
TEST_METHOD(AltCtrlDTest);
TEST_METHOD(AltIntermediateTest);
Expand Down Expand Up @@ -663,6 +665,7 @@ void InputEngineTest::WindowManipulationTest()
L"Processing \"%s\"", seq.c_str()));
_stateMachine->ProcessString(seq);
}
VerifyExpectedInputDrained();
}

void InputEngineTest::NonAsciiTest()
Expand Down Expand Up @@ -716,6 +719,7 @@ void InputEngineTest::NonAsciiTest()
test.Event.KeyEvent.bKeyDown = FALSE;
testState.vExpectedInput.push_back(test);
_stateMachine->ProcessString(utf8Input);
VerifyExpectedInputDrained();
}

void InputEngineTest::CursorPositioningTest()
Expand Down Expand Up @@ -759,6 +763,7 @@ void InputEngineTest::CursorPositioningTest()
Log::Comment(NoThrowString().Format(
L"Processing \"%s\"", seq.c_str()));
_stateMachine->ProcessString(seq);
VerifyExpectedInputDrained();
}

void InputEngineTest::CSICursorBackTabTest()
Expand Down Expand Up @@ -786,6 +791,57 @@ void InputEngineTest::CSICursorBackTabTest()
Log::Comment(NoThrowString().Format(
L"Processing \"%s\"", seq.c_str()));
_stateMachine->ProcessString(seq);
VerifyExpectedInputDrained();
}

void InputEngineTest::EnhancedKeysTest()
{
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();

// The following vkeys should be handled as enhanced keys
// Reference: https://docs.microsoft.com/en-us/windows/console/key-event-record-str
// clang-format off
const std::map<int, std::wstring> enhancedKeys{
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved
{ VK_PRIOR, L"\x1b[5~"},
{ VK_NEXT, L"\x1b[6~"},
{ VK_END, L"\x1b[F"},
{ VK_HOME, L"\x1b[H"},
{ VK_LEFT, L"\x1b[D"},
{ VK_UP, L"\x1b[A"},
{ VK_RIGHT, L"\x1b[C"},
{ VK_DOWN, L"\x1b[B"},
{ VK_INSERT, L"\x1b[2~"},
{ VK_DELETE, L"\x1b[3~"}
};
// clang-format on

for (const auto& [vkey, seq] : enhancedKeys)
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved
{
INPUT_RECORD inputRec;

wchar_t wch = (wchar_t)MapVirtualKeyW(vkey, MAPVK_VK_TO_CHAR);
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved
WORD scanCode = (wchar_t)MapVirtualKeyW(vkey, MAPVK_VK_TO_VSC);
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved

inputRec.EventType = KEY_EVENT;
inputRec.Event.KeyEvent.bKeyDown = TRUE;
inputRec.Event.KeyEvent.dwControlKeyState = ENHANCED_KEY;
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()
Expand Down