Avoid encoding VT via win32 input mode (#16407)

This changeset avoids re-encoding output from `AdaptDispatch`
via the win32-input-mode mechanism when VT input is enabled.
That is, an `AdaptDispatch` output like `\x1b[C` would otherwise
result in dozens of characters of input.

Related to #16343

## Validation Steps Performed
* Replace conhost with this
* Launch a Win32 application inside WSL
* ASCII keyboard inputs are represented as single `INPUT_RECORD`s 
This commit is contained in:
Leonard Hecker 2023-12-05 00:05:25 +01:00 committed by GitHub
parent 91fd7d0101
commit 0da37a134a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 29 deletions

View File

@ -602,6 +602,27 @@ size_t InputBuffer::Write(const std::span<const INPUT_RECORD>& inEvents)
}
}
void InputBuffer::WriteString(const std::wstring_view& text)
try
{
if (text.empty())
{
return;
}
const auto initiallyEmptyQueue = _storage.empty();
_writeString(text);
if (initiallyEmptyQueue && !_storage.empty())
{
ServiceLocator::LocateGlobals().hInputEvent.SetEvent();
}
WakeUpReadersWaitingForData();
}
CATCH_LOG()
// This can be considered a "privileged" variant of Write() which allows FOCUS_EVENTs to generate focus VT sequences.
// If we didn't do this, someone could write a FOCUS_EVENT_RECORD with WriteConsoleInput, exit without flushing the
// input buffer and the next application will suddenly get a "\x1b[I" sequence in their input. See GH#13238.
@ -828,21 +849,7 @@ void InputBuffer::_HandleTerminalInputCallback(const TerminalInput::StringType&
return;
}
for (const auto& wch : text)
{
if (wch == UNICODE_NULL)
{
// Convert null byte back to input event with proper control state
const auto zeroKey = OneCoreSafeVkKeyScanW(0);
uint32_t ctrlState = 0;
WI_SetFlagIf(ctrlState, SHIFT_PRESSED, WI_IsFlagSet(zeroKey, 0x100));
WI_SetFlagIf(ctrlState, LEFT_CTRL_PRESSED, WI_IsFlagSet(zeroKey, 0x200));
WI_SetFlagIf(ctrlState, LEFT_ALT_PRESSED, WI_IsFlagSet(zeroKey, 0x400));
_storage.push_back(SynthesizeKeyEvent(true, 1, LOBYTE(zeroKey), 0, wch, ctrlState));
continue;
}
_storage.push_back(SynthesizeKeyEvent(true, 1, 0, 0, wch, 0));
}
_writeString(text);
if (!_vtInputShouldSuppress)
{
@ -856,6 +863,25 @@ void InputBuffer::_HandleTerminalInputCallback(const TerminalInput::StringType&
}
}
void InputBuffer::_writeString(const std::wstring_view& text)
{
for (const auto& wch : text)
{
if (wch == UNICODE_NULL)
{
// Convert null byte back to input event with proper control state
const auto zeroKey = OneCoreSafeVkKeyScanW(0);
uint32_t ctrlState = 0;
WI_SetFlagIf(ctrlState, SHIFT_PRESSED, WI_IsFlagSet(zeroKey, 0x100));
WI_SetFlagIf(ctrlState, LEFT_CTRL_PRESSED, WI_IsFlagSet(zeroKey, 0x200));
WI_SetFlagIf(ctrlState, LEFT_ALT_PRESSED, WI_IsFlagSet(zeroKey, 0x400));
_storage.push_back(SynthesizeKeyEvent(true, 1, LOBYTE(zeroKey), 0, wch, ctrlState));
continue;
}
_storage.push_back(SynthesizeKeyEvent(true, 1, 0, 0, wch, 0));
}
}
TerminalInput& InputBuffer::GetTerminalInput()
{
return _termInput;

View File

@ -58,6 +58,7 @@ public:
size_t Prepend(const std::span<const INPUT_RECORD>& inEvents);
size_t Write(const INPUT_RECORD& inEvent);
size_t Write(const std::span<const INPUT_RECORD>& inEvents);
void WriteString(const std::wstring_view& text);
void WriteFocusEvent(bool focused) noexcept;
bool WriteMouseEvent(til::point position, unsigned int button, short keyState, short wheelDelta);
@ -96,6 +97,7 @@ private:
void _WriteBuffer(const std::span<const INPUT_RECORD>& inRecords, _Out_ size_t& eventsWritten, _Out_ bool& setWaitEvent);
bool _CoalesceEvent(const INPUT_RECORD& inEvent) noexcept;
void _HandleTerminalInputCallback(const Microsoft::Console::VirtualTerminal::TerminalInput::StringType& text);
void _writeString(const std::wstring_view& text);
#ifdef UNIT_TESTING
friend class InputBufferTests;

View File

@ -33,24 +33,11 @@ ConhostInternalGetSet::ConhostInternalGetSet(_In_ IIoProvider& io) :
// - <none>
void ConhostInternalGetSet::ReturnResponse(const std::wstring_view response)
{
InputEventQueue inEvents;
// generate a paired key down and key up event for every
// character to be sent into the console's input buffer
for (const auto& wch : response)
{
// This wasn't from a real keyboard, so we're leaving key/scan codes blank.
auto keyEvent = SynthesizeKeyEvent(true, 1, 0, 0, wch, 0);
inEvents.push_back(keyEvent);
keyEvent.Event.KeyEvent.bKeyDown = false;
inEvents.push_back(keyEvent);
}
// TODO GH#4954 During the input refactor we may want to add a "priority" input list
// to make sure that "response" input is spooled directly into the application.
// We switched this to an append (vs. a prepend) to fix GH#1637, a bug where two CPR
// could collide with each other.
_io.GetActiveInputBuffer()->Write(inEvents);
_io.GetActiveInputBuffer()->WriteString(response);
}
// Routine Description: