Pass through DCS responses when VT input mode is disabled (#17845)

## Summary of the Pull Request

When an app makes a VT request that returns a `DCS` response, and it
hasn't also enabled VT input mode, the new passthrough implementation
loses that response. All the app receives is an `Alt`+`\` key press
triggered by the `ST` terminator. This PR fixes that issue.

## References and Relevant Issues

This is one of the unresolved issues tracked in #17643.

## Detailed Description of the Pull Request / Additional comments

The way `DCS` sequences are handled in the input state machine engine is
by returning a nullptr from `ActionDcsDispatch`, which tells the state
machine to ignore that content. But the sequence is still buffered, and
when the `ST` terminator is eventually received, that buffer is flushed,
which passes the whole thing through to the app.

Originally this only worked when VT input mode was enabled, otherwise
the `ST` sequence is converted into a key press, and the buffered `DCS`
content is lost. The way it works now is we set a flag when the `DCS`
introducer is received, and if that flag is set when the `ST` arrives,
we know to trigger a flush rather a key press.

## Validation Steps Performed

I've tested a `DA3` request from the cmd shell (i.e. `echo ^[[=c`), and
confirmed that now works as expected. I've also hacked Windows Terminal
to disable win32-input mode, so I could check how it works with conpty
clients generating standard VT input, and confirmed that an `Alt`+`\`
keypress is still translated correctly.
This commit is contained in:
James Holderness 2024-09-04 14:36:32 +01:00 committed by GitHub
parent 17a55da0f9
commit 6e5827add5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 15 additions and 1 deletions

View File

@ -327,6 +327,16 @@ bool InputStateMachineEngine::ActionPassThroughString(const std::wstring_view st
// - true iff we successfully dispatched the sequence.
bool InputStateMachineEngine::ActionEscDispatch(const VTID id)
{
// If the _expectingStringTerminator flag is set, that means we've been
// processing a DCS sequence and are waiting for the string terminator.
// Once we receive the ST sequence here, we can return false to force the
// buffered DCS content to be flushed.
if (_expectingStringTerminator && id == VTID("\\"))
{
_expectingStringTerminator = false;
return false;
}
if (_pDispatch->IsVtInputEnabled())
{
return false;
@ -524,7 +534,10 @@ bool InputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParameter
// - the data string handler function or nullptr if the sequence is not supported
IStateMachineEngine::StringHandler InputStateMachineEngine::ActionDcsDispatch(const VTID /*id*/, const VTParameters /*parameters*/) noexcept
{
// DCS escape sequences are not used in the input state machine.
// Returning a nullptr here will cause the content of the DCS sequence to be
// ignored, but it'll still be buffered by the state machine, so we can flush
// the whole thing once we receive the string terminator.
_expectingStringTerminator = true;
return nullptr;
}

View File

@ -187,6 +187,7 @@ namespace Microsoft::Console::VirtualTerminal
std::atomic<uint64_t> _deviceAttributes{ 0 };
bool _lookingForDSR = false;
bool _encounteredWin32InputModeSequence = false;
bool _expectingStringTerminator = false;
DWORD _mouseButtonState = 0;
std::chrono::milliseconds _doubleClickTime;
std::optional<til::point> _lastMouseClickPos{};