Fix C-M-z, C-M-x in Conpty (#4940)
## Summary of the Pull Request This PR ensures that Conpty properly treats `^[^Z` and `^[^X` as <kbd>Ctrl+Alt+z</kbd> and <kbd>Ctrl+Alt+x</kbd>, instead of <kbd>Ctrl+z</kbd> and <kbd>Ctrl+x</kbd>. ## References ## PR Checklist * [x] Closes #4201 * [x] I work here * [x] Tests added/passed * [n/a] Requires documentation to be updated ## Detailed Description of the Pull Request / Additional comments `^Z` and `^X` are special control characters, SUB and CAN. For the output state machine, these characters are supposed to be executed from _any_ state. However, we shouldn't do this for the input engine. With the current behavior, these characters are immediately executed regardless of what state we're in. That means we end up synthesizing <kbd>Ctrl+z/x</kbd> for these characters. However, for the InputStateMachine engine, when these characters are preceeded by `^[` (ESC), we want to treat them as <kbd>Ctrl+Alt+z/x</kbd>. This just adds a check in `StateMachine` to see if we should immediately execute these characters from any state, similar to many of the other exceptions we already perform in the StateMachine for the input engine. ## Validation Steps Performed * ran tests * checked `showkey -a` in gnome-terminal * checked `showkey -a` in conhost * checked `showkey -a` in vt pipeterm (conhost as a conpty terminal) * checked `showkey -a` in Windows Terminal
This commit is contained in:
parent
860affd608
commit
7b9c8c7055
|
@ -1167,8 +1167,13 @@ void StateMachine::ProcessCharacter(const wchar_t wch)
|
|||
_trace.TraceCharInput(wch);
|
||||
|
||||
// Process "from anywhere" events first.
|
||||
if (wch == AsciiChars::CAN ||
|
||||
wch == AsciiChars::SUB)
|
||||
const bool isFromAnywhereChar = (wch == AsciiChars::CAN || wch == AsciiChars::SUB);
|
||||
|
||||
// GH#4201 - If this sequence was ^[^X or ^[^Z, then we should
|
||||
// _ActionExecuteFromEscape, as to send a Ctrl+Alt+key key. We should only
|
||||
// do this for the InputStateMachineEngine - the OutputEngine should execute
|
||||
// these from any state.
|
||||
if (isFromAnywhereChar && !(_state == VTStates::Escape && _engine->DispatchControlCharsFromEscape()))
|
||||
{
|
||||
_ActionExecute(wch);
|
||||
_EnterGround();
|
||||
|
|
|
@ -259,6 +259,7 @@ class Microsoft::Console::VirtualTerminal::InputEngineTest
|
|||
TEST_METHOD(SGRMouseTest_Modifiers);
|
||||
TEST_METHOD(SGRMouseTest_Movement);
|
||||
TEST_METHOD(SGRMouseTest_Scroll);
|
||||
TEST_METHOD(CtrlAltZCtrlAltXTest);
|
||||
|
||||
friend class TestInteractDispatch;
|
||||
};
|
||||
|
@ -1141,3 +1142,68 @@ void InputEngineTest::SGRMouseTest_Scroll()
|
|||
// clang-format on
|
||||
VerifySGRMouseData(testData);
|
||||
}
|
||||
|
||||
void InputEngineTest::CtrlAltZCtrlAltXTest()
|
||||
{
|
||||
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();
|
||||
|
||||
// This is a test for GH#4201. See that issue for more details.
|
||||
Log::Comment(L"Test Ctrl+Alt+Z and Ctrl+Alt+X, which execute from anywhere "
|
||||
L"in the output engine, but should be Escape-Executed in the "
|
||||
L"input engine.");
|
||||
|
||||
DisableVerifyExceptions disable;
|
||||
|
||||
{
|
||||
auto inputSeq = L"\x1b\x1a"; // ^[^Z
|
||||
|
||||
wchar_t expectedWch = L'Z';
|
||||
short keyscan = VkKeyScanW(expectedWch);
|
||||
short vkey = keyscan & 0xff;
|
||||
WORD scanCode = (WORD)MapVirtualKeyW(vkey, MAPVK_VK_TO_VSC);
|
||||
|
||||
INPUT_RECORD inputRec;
|
||||
|
||||
inputRec.EventType = KEY_EVENT;
|
||||
inputRec.Event.KeyEvent.bKeyDown = TRUE;
|
||||
inputRec.Event.KeyEvent.dwControlKeyState = LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED;
|
||||
inputRec.Event.KeyEvent.wRepeatCount = 1;
|
||||
inputRec.Event.KeyEvent.wVirtualKeyCode = vkey;
|
||||
inputRec.Event.KeyEvent.wVirtualScanCode = scanCode;
|
||||
inputRec.Event.KeyEvent.uChar.UnicodeChar = expectedWch - 0x40;
|
||||
|
||||
testState.vExpectedInput.push_back(inputRec);
|
||||
|
||||
_stateMachine->ProcessString(inputSeq);
|
||||
}
|
||||
{
|
||||
auto inputSeq = L"\x1b\x18"; // ^[^X
|
||||
|
||||
wchar_t expectedWch = L'X';
|
||||
short keyscan = VkKeyScanW(expectedWch);
|
||||
short vkey = keyscan & 0xff;
|
||||
WORD scanCode = (WORD)MapVirtualKeyW(vkey, MAPVK_VK_TO_VSC);
|
||||
|
||||
INPUT_RECORD inputRec;
|
||||
|
||||
inputRec.EventType = KEY_EVENT;
|
||||
inputRec.Event.KeyEvent.bKeyDown = TRUE;
|
||||
inputRec.Event.KeyEvent.dwControlKeyState = LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED;
|
||||
inputRec.Event.KeyEvent.wRepeatCount = 1;
|
||||
inputRec.Event.KeyEvent.wVirtualKeyCode = vkey;
|
||||
inputRec.Event.KeyEvent.wVirtualScanCode = scanCode;
|
||||
inputRec.Event.KeyEvent.uChar.UnicodeChar = expectedWch - 0x40;
|
||||
|
||||
testState.vExpectedInput.push_back(inputRec);
|
||||
|
||||
_stateMachine->ProcessString(inputSeq);
|
||||
}
|
||||
|
||||
VerifyExpectedInputDrained();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue