Add support for Ctrl+# keys (#4938)

## Summary of the Pull Request

Fixes the <kbd>Ctrl+Num</kbd> keys in both conhost and the Terminal. These keys are supposed to be mapped to specific characters according to [this doc](https://vt100.net/docs/vt220-rm/table3-5.html). Now we actually handle them correctly.

## PR Checklist
* [x] Closes #3507 
* [x] I work here
* [x] Tests added/passed
* [n/a] Requires documentation to be updated

## Validation Steps Performed

* Ran test
* tested in `gnome-terminal` with `showkeys -a`
* tested in conhost with `showkeys -a`
* tested in Windows Terminal with `showkeys -a`
This commit is contained in:
Mike Griese 2020-03-18 17:39:22 -05:00 committed by GitHub
parent d50409b901
commit f7d106d3f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 73 additions and 1 deletions

View file

@ -45,6 +45,7 @@ public:
TEST_METHOD(TerminalInputModifierKeyTests);
TEST_METHOD(TerminalInputNullKeyTests);
TEST_METHOD(DifferentModifiersTest);
TEST_METHOD(CtrlNumTest);
wchar_t GetModifierChar(const bool fShift, const bool fAlt, const bool fCtrl)
{
@ -511,6 +512,13 @@ void InputTest::TerminalInputModifierKeyTests()
s_pwsInputBuffer[1] = wchShifted;
fExpectedKeyHandled = true;
}
else if (ControlPressed(uiKeystate) && (vkey >= '1' && vkey <= '9'))
{
// The C-# keys get translated into very specific control
// characters that don't play nicely with this test. These keys
// are tested in the CtrlNumTest Test instead.
continue;
}
else
{
fExpectedKeyHandled = false;
@ -714,3 +722,49 @@ void InputTest::DifferentModifiersTest()
// LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED is skipped because that's AltGr
TestKey(pInput, SHIFT_PRESSED | RIGHT_CTRL_PRESSED | RIGHT_ALT_PRESSED, vkey, L'?');
}
void InputTest::CtrlNumTest()
{
Log::Comment(L"Starting test...");
const TerminalInput* const pInput = new TerminalInput(s_TerminalInputTestCallback);
Log::Comment(L"Sending the various Ctrl+Num keys.");
unsigned int uiKeystate = LEFT_CTRL_PRESSED;
BYTE vkey = static_cast<WORD>('1');
s_pwszInputExpected = L"1";
TestKey(pInput, uiKeystate, vkey);
Log::Comment(NoThrowString().Format(
L"Skipping Ctrl+2, since that's supposed to send NUL, and doesn't play "
L"nicely with this test. Ctrl+2 is covered by other tests in this class."));
vkey = static_cast<WORD>('3');
s_pwszInputExpected = L"\x1b";
TestKey(pInput, uiKeystate, vkey);
vkey = static_cast<WORD>('4');
s_pwszInputExpected = L"\x1c";
TestKey(pInput, uiKeystate, vkey);
vkey = static_cast<WORD>('5');
s_pwszInputExpected = L"\x1d";
TestKey(pInput, uiKeystate, vkey);
vkey = static_cast<WORD>('6');
s_pwszInputExpected = L"\x1e";
TestKey(pInput, uiKeystate, vkey);
vkey = static_cast<WORD>('7');
s_pwszInputExpected = L"\x1f";
TestKey(pInput, uiKeystate, vkey);
vkey = static_cast<WORD>('8');
s_pwszInputExpected = L"\x7f";
TestKey(pInput, uiKeystate, vkey);
vkey = static_cast<WORD>('9');
s_pwszInputExpected = L"9";
TestKey(pInput, uiKeystate, vkey);
}

View file

@ -185,13 +185,31 @@ 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, 6> s_simpleModifiedKeyMapping{
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" },
TermKeyMap{ VK_TAB, CTRL_PRESSED, L"\t" },
TermKeyMap{ VK_TAB, SHIFT_PRESSED, L"\x1b[Z" },
TermKeyMap{ VK_DIVIDE, CTRL_PRESSED, L"\x1F" },
// GH#3507 - We should also be encoding Ctrl+# according to the following table:
// https://vt100.net/docs/vt220-rm/table3-5.html
// * 1 and 9 do not send any special characters, but they _should_ send
// through the character unmodified.
// * 0 doesn't seem to send even an unmodified '0' through.
// * Ctrl+2 is already special-cased below in `HandleKey`, so it's not
// included here.
TermKeyMap{ static_cast<WORD>('1'), CTRL_PRESSED, L"1" },
// TermKeyMap{ static_cast<WORD>('2'), CTRL_PRESSED, L"\x00" },
TermKeyMap{ static_cast<WORD>('3'), CTRL_PRESSED, L"\x1B" },
TermKeyMap{ static_cast<WORD>('4'), CTRL_PRESSED, L"\x1C" },
TermKeyMap{ static_cast<WORD>('5'), CTRL_PRESSED, L"\x1D" },
TermKeyMap{ static_cast<WORD>('6'), CTRL_PRESSED, L"\x1E" },
TermKeyMap{ static_cast<WORD>('7'), CTRL_PRESSED, L"\x1F" },
TermKeyMap{ static_cast<WORD>('8'), CTRL_PRESSED, L"\x7F" },
TermKeyMap{ static_cast<WORD>('9'), CTRL_PRESSED, L"9" },
// These two are not implemented here, because they are system keys.
// TermKeyMap{ VK_TAB, ALT_PRESSED, L""}, This is the Windows system shortcut for switching windows.
// TermKeyMap{ VK_ESCAPE, ALT_PRESSED, L""}, This is another Windows system shortcut for switching windows.