mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-11 04:38:24 -06:00
The InputStateMachine should dispatch Intermediate characters (#1267)
The OutputStateMachine needs to collect "Intermediate" characters to be able to call [`Designate G0 Character Set`](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Controls-beginning-with-ESC) (as well as other sequences we don't yet support). However, the InputStateMachine used by conpty to process input should _not_ collect these characters. The input engine uses `\x1b` as an indicator that a key was pressed with `Alt`. For keys like `/`, we want to dispatch the key immediately, instead of collecting it and leaving us in the Escape state.
This commit is contained in:
parent
dba918beab
commit
94bcbb9204
@ -149,6 +149,9 @@ EndProject
|
|||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererVt", "src\renderer\vt\lib\vt.vcxproj", "{990F2657-8580-4828-943F-5DD657D11842}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererVt", "src\renderer\vt\lib\vt.vcxproj", "{990F2657-8580-4828-943F-5DD657D11842}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VtPipeTerm", "src\tools\vtpipeterm\VtPipeTerm.vcxproj", "{814DBDDE-894E-4327-A6E1-740504850098}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VtPipeTerm", "src\tools\vtpipeterm\VtPipeTerm.vcxproj", "{814DBDDE-894E-4327-A6E1-740504850098}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}
|
||||||
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ConEchoKey", "src\tools\echokey\ConEchoKey.vcxproj", "{814CBEEE-894E-4327-A6E1-740504850098}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ConEchoKey", "src\tools\echokey\ConEchoKey.vcxproj", "{814CBEEE-894E-4327-A6E1-740504850098}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|||||||
@ -52,6 +52,7 @@ namespace Microsoft::Console::VirtualTerminal
|
|||||||
|
|
||||||
virtual bool FlushAtEndOfString() const = 0;
|
virtual bool FlushAtEndOfString() const = 0;
|
||||||
virtual bool DispatchControlCharsFromEscape() const = 0;
|
virtual bool DispatchControlCharsFromEscape() const = 0;
|
||||||
|
virtual bool DispatchIntermediatesFromEscape() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline IStateMachineEngine::~IStateMachineEngine() {}
|
inline IStateMachineEngine::~IStateMachineEngine() {}
|
||||||
|
|||||||
@ -883,6 +883,19 @@ bool InputStateMachineEngine::DispatchControlCharsFromEscape() const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Routine Description:
|
||||||
|
// - Returns false if the engine wants to be able to collect intermediate
|
||||||
|
// characters in the Escape state. We do _not_ want to buffer any characters
|
||||||
|
// as intermediates, because we use ESC as a prefix to indicate a key was
|
||||||
|
// pressed while Alt was pressed.
|
||||||
|
// Return Value:
|
||||||
|
// - True iff we should dispatch in the Escape state when we encounter a
|
||||||
|
// Intermediate character.
|
||||||
|
bool InputStateMachineEngine::DispatchIntermediatesFromEscape() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Method Description:
|
// Method Description:
|
||||||
// - Retrieves the type of window manipulation operation from the parameter pool
|
// - Retrieves the type of window manipulation operation from the parameter pool
|
||||||
// stored during Param actions.
|
// stored during Param actions.
|
||||||
|
|||||||
@ -66,6 +66,7 @@ namespace Microsoft::Console::VirtualTerminal
|
|||||||
|
|
||||||
bool FlushAtEndOfString() const override;
|
bool FlushAtEndOfString() const override;
|
||||||
bool DispatchControlCharsFromEscape() const override;
|
bool DispatchControlCharsFromEscape() const override;
|
||||||
|
bool DispatchIntermediatesFromEscape() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::unique_ptr<IInteractDispatch> _pDispatch;
|
const std::unique_ptr<IInteractDispatch> _pDispatch;
|
||||||
|
|||||||
@ -1317,6 +1317,18 @@ bool OutputStateMachineEngine::DispatchControlCharsFromEscape() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Routine Description:
|
||||||
|
// - Returns false if the engine wants to be able to collect intermediate
|
||||||
|
// characters in the Escape state. We do want to buffer characters as
|
||||||
|
// intermediates. We need them for things like Designate G0 Character Set
|
||||||
|
// Return Value:
|
||||||
|
// - True iff we should dispatch in the Escape state when we encounter a
|
||||||
|
// Intermediate character.
|
||||||
|
bool OutputStateMachineEngine::DispatchIntermediatesFromEscape() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Converts a hex character to its equivalent integer value.
|
// - Converts a hex character to its equivalent integer value.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
|
|||||||
@ -58,6 +58,7 @@ namespace Microsoft::Console::VirtualTerminal
|
|||||||
|
|
||||||
bool FlushAtEndOfString() const override;
|
bool FlushAtEndOfString() const override;
|
||||||
bool DispatchControlCharsFromEscape() const override;
|
bool DispatchControlCharsFromEscape() const override;
|
||||||
|
bool DispatchIntermediatesFromEscape() const override;
|
||||||
|
|
||||||
void SetTerminalConnection(Microsoft::Console::ITerminalOutputConnection* const pTtyConnection,
|
void SetTerminalConnection(Microsoft::Console::ITerminalOutputConnection* const pTtyConnection,
|
||||||
std::function<bool()> pfnFlushToTerminal);
|
std::function<bool()> pfnFlushToTerminal);
|
||||||
|
|||||||
@ -853,8 +853,16 @@ void StateMachine::_EventEscape(const wchar_t wch)
|
|||||||
}
|
}
|
||||||
else if (s_IsIntermediate(wch))
|
else if (s_IsIntermediate(wch))
|
||||||
{
|
{
|
||||||
_ActionCollect(wch);
|
if (_pEngine->DispatchIntermediatesFromEscape())
|
||||||
_EnterEscapeIntermediate();
|
{
|
||||||
|
_ActionEscDispatch(wch);
|
||||||
|
_EnterGround();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ActionCollect(wch);
|
||||||
|
_EnterEscapeIntermediate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (s_IsCsiIndicator(wch))
|
else if (s_IsCsiIndicator(wch))
|
||||||
{
|
{
|
||||||
|
|||||||
@ -229,6 +229,7 @@ class Microsoft::Console::VirtualTerminal::InputEngineTest
|
|||||||
TEST_METHOD(CSICursorBackTabTest);
|
TEST_METHOD(CSICursorBackTabTest);
|
||||||
TEST_METHOD(AltBackspaceTest);
|
TEST_METHOD(AltBackspaceTest);
|
||||||
TEST_METHOD(AltCtrlDTest);
|
TEST_METHOD(AltCtrlDTest);
|
||||||
|
TEST_METHOD(AltIntermediateTest);
|
||||||
|
|
||||||
friend class TestInteractDispatch;
|
friend class TestInteractDispatch;
|
||||||
};
|
};
|
||||||
@ -765,3 +766,63 @@ void InputEngineTest::AltCtrlDTest()
|
|||||||
Log::Comment(NoThrowString().Format(L"Processing \"\\x1b\\x04\""));
|
Log::Comment(NoThrowString().Format(L"Processing \"\\x1b\\x04\""));
|
||||||
_stateMachine->ProcessString(seq);
|
_stateMachine->ProcessString(seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputEngineTest::AltIntermediateTest()
|
||||||
|
{
|
||||||
|
// Tests GH#1209. When we process a alt+key combination where the key just
|
||||||
|
// so happens to be an intermediate character, we should make sure that an
|
||||||
|
// immediately subsequent ctrl character is handled correctly.
|
||||||
|
TestState testState;
|
||||||
|
|
||||||
|
// We'll test this by creating both a TerminalInput and an
|
||||||
|
// InputStateMachine, and piping the KeyEvents generated by the
|
||||||
|
// InputStateMachine into the TerminalInput.
|
||||||
|
std::wstring expectedTranslation{};
|
||||||
|
|
||||||
|
// First create the callback TerminalInput will call - this will be
|
||||||
|
// triggered second, after both the state machine and the TerminalInput have
|
||||||
|
// translated the characters.
|
||||||
|
auto pfnTerminalInputCallback = [&](std::deque<std::unique_ptr<IInputEvent>>& inEvents) {
|
||||||
|
// Get all the characters:
|
||||||
|
std::wstring wstr = L"";
|
||||||
|
for (auto& ev : inEvents)
|
||||||
|
{
|
||||||
|
if (ev->EventType() == InputEventType::KeyEvent)
|
||||||
|
{
|
||||||
|
auto& k = static_cast<KeyEvent&>(*ev);
|
||||||
|
auto wch = k.GetCharData();
|
||||||
|
wstr += wch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VERIFY_ARE_EQUAL(expectedTranslation, wstr);
|
||||||
|
};
|
||||||
|
TerminalInput terminalInput{ pfnTerminalInputCallback };
|
||||||
|
|
||||||
|
// Create the callback that's fired when the state machine wants to write
|
||||||
|
// input. We'll take the events and put them straight into the
|
||||||
|
// TerminalInput.
|
||||||
|
auto pfnInputStateMachineCallback = [&](std::deque<std::unique_ptr<IInputEvent>>& inEvents) {
|
||||||
|
for (auto& ev : inEvents)
|
||||||
|
{
|
||||||
|
terminalInput.HandleKey(ev.get());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto inputEngine = std::make_unique<InputStateMachineEngine>(new TestInteractDispatch(pfnInputStateMachineCallback, &testState));
|
||||||
|
auto stateMachine = std::make_unique<StateMachine>(inputEngine.release());
|
||||||
|
VERIFY_IS_NOT_NULL(stateMachine);
|
||||||
|
testState._stateMachine = stateMachine.get();
|
||||||
|
|
||||||
|
// Write a Alt+/, Ctrl+e pair to the input engine, then take it's output and
|
||||||
|
// run it through the terminalInput translator. We should get ^[/^E back
|
||||||
|
// out.
|
||||||
|
std::wstring seq = L"\x1b/";
|
||||||
|
expectedTranslation = seq;
|
||||||
|
Log::Comment(NoThrowString().Format(L"Processing \"\\x1b/\""));
|
||||||
|
stateMachine->ProcessString(seq);
|
||||||
|
|
||||||
|
seq = L"\x05"; // 0x05 is ^E
|
||||||
|
expectedTranslation = seq;
|
||||||
|
Log::Comment(NoThrowString().Format(L"Processing \"\\x05\""));
|
||||||
|
stateMachine->ProcessString(seq);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user