diff --git a/src/host/VtInputThread.cpp b/src/host/VtInputThread.cpp index e02ce62c86c..b94b94fbe64 100644 --- a/src/host/VtInputThread.cpp +++ b/src/host/VtInputThread.cpp @@ -43,7 +43,13 @@ VtInputThread::VtInputThread(_In_ wil::unique_hfile hPipe, auto engine = std::make_unique(std::move(dispatch), inheritCursor); + auto engineRef = engine.get(); + _pInputStateMachine = std::make_unique(std::move(engine)); + + // we need this callback to be able to flush an unknown input sequence to the app + auto flushCallback = std::bind(&StateMachine::FlushToTerminal, _pInputStateMachine.get()); + engineRef->SetFlushToInputQueueCallback(flushCallback); } // Method Description: diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 327676d4e69..55f0cfcd693 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -891,3 +891,16 @@ bool ConhostInternalGetSet::PrivateScrollRegion(const SMALL_RECT scrollRect, destinationOrigin, standardFillAttrs)); } + +// Routine Description: +// - Checks if the InputBuffer is willing to accept VT Input directly +// PrivateIsVtInputEnabled is an internal-only "API" call that the vt commands can execute, +// but it is not represented as a function call on our public API surface. +// Arguments: +// - +// Return value: +// - true if enabled (see IsInVirtualTerminalInputMode). false otherwise. +bool ConhostInternalGetSet::PrivateIsVtInputEnabled() const +{ + return _io.GetActiveInputBuffer()->IsInVirtualTerminalInputMode(); +} diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 6c744753288..eff56c90be2 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -166,6 +166,8 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: const COORD destinationOrigin, const bool standardFillAttrs) noexcept override; + bool PrivateIsVtInputEnabled() const override; + private: Microsoft::Console::IIoProvider& _io; }; diff --git a/src/terminal/adapter/IInteractDispatch.hpp b/src/terminal/adapter/IInteractDispatch.hpp index 6110bd2575b..f1d3062859f 100644 --- a/src/terminal/adapter/IInteractDispatch.hpp +++ b/src/terminal/adapter/IInteractDispatch.hpp @@ -39,5 +39,7 @@ namespace Microsoft::Console::VirtualTerminal virtual bool MoveCursor(const size_t row, const size_t col) = 0; + + virtual bool IsVtInputEnabled() const = 0; }; } diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp index c184e764f01..18c3d65e0bd 100644 --- a/src/terminal/adapter/InteractDispatch.cpp +++ b/src/terminal/adapter/InteractDispatch.cpp @@ -203,3 +203,14 @@ bool InteractDispatch::MoveCursor(const size_t row, const size_t col) return success; } + +// Routine Description: +// - Checks if the InputBuffer is willing to accept VT Input directly +// Arguments: +// - +// Return value: +// - true if enabled (see IsInVirtualTerminalInputMode). false otherwise. +bool InteractDispatch::IsVtInputEnabled() const +{ + return _pConApi->PrivateIsVtInputEnabled(); +} diff --git a/src/terminal/adapter/InteractDispatch.hpp b/src/terminal/adapter/InteractDispatch.hpp index cac59f304bd..482d0eae536 100644 --- a/src/terminal/adapter/InteractDispatch.hpp +++ b/src/terminal/adapter/InteractDispatch.hpp @@ -32,6 +32,8 @@ namespace Microsoft::Console::VirtualTerminal const std::basic_string_view parameters) override; // DTTERM_WindowManipulation bool MoveCursor(const size_t row, const size_t col) override; + bool IsVtInputEnabled() const override; + private: std::unique_ptr _pConApi; }; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index f6c416d4fc5..64d770d6e5d 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1039,7 +1039,18 @@ bool AdaptDispatch::ResetPrivateModes(const std::basic_string_viewPrivateSetKeypadMode(fApplicationMode); + bool success = true; + success = _pConApi->PrivateSetKeypadMode(fApplicationMode); + + // If we're a conpty, always return false + bool isPty = false; + _pConApi->IsConsolePty(isPty); + if (isPty) + { + return false; + } + + return success; } // - DECCKM - Sets the cursor keys input mode to either Application mode or Normal mode (true, false respectively) @@ -1049,7 +1060,18 @@ bool AdaptDispatch::SetKeypadMode(const bool fApplicationMode) // - True if handled successfully. False otherwise. bool AdaptDispatch::SetCursorKeysMode(const bool applicationMode) { - return _pConApi->PrivateSetCursorKeysMode(applicationMode); + bool success = true; + success = _pConApi->PrivateSetCursorKeysMode(applicationMode); + + // If we're a conpty, always return false + bool isPty = false; + _pConApi->IsConsolePty(isPty); + if (isPty) + { + return false; + } + + return success; } // - att610 - Enables or disables the cursor blinking. @@ -1688,7 +1710,18 @@ bool AdaptDispatch::EnableDECCOLMSupport(const bool enabled) noexcept // True if handled successfully. False otherwise. bool AdaptDispatch::EnableVT200MouseMode(const bool enabled) { - return _pConApi->PrivateEnableVT200MouseMode(enabled); + bool success = true; + success = _pConApi->PrivateEnableVT200MouseMode(enabled); + + // If we're a conpty, always return false + bool isPty = false; + _pConApi->IsConsolePty(isPty); + if (isPty) + { + return false; + } + + return success; } //Routine Description: @@ -1700,7 +1733,18 @@ bool AdaptDispatch::EnableVT200MouseMode(const bool enabled) // True if handled successfully. False otherwise. bool AdaptDispatch::EnableUTF8ExtendedMouseMode(const bool enabled) { - return _pConApi->PrivateEnableUTF8ExtendedMouseMode(enabled); + bool success = true; + success = _pConApi->PrivateEnableUTF8ExtendedMouseMode(enabled); + + // If we're a conpty, always return false + bool isPty = false; + _pConApi->IsConsolePty(isPty); + if (isPty) + { + return false; + } + + return success; } //Routine Description: @@ -1712,7 +1756,18 @@ bool AdaptDispatch::EnableUTF8ExtendedMouseMode(const bool enabled) // True if handled successfully. False otherwise. bool AdaptDispatch::EnableSGRExtendedMouseMode(const bool enabled) { - return _pConApi->PrivateEnableSGRExtendedMouseMode(enabled); + bool success = true; + success = _pConApi->PrivateEnableSGRExtendedMouseMode(enabled); + + // If we're a conpty, always return false + bool isPty = false; + _pConApi->IsConsolePty(isPty); + if (isPty) + { + return false; + } + + return success; } //Routine Description: @@ -1723,7 +1778,18 @@ bool AdaptDispatch::EnableSGRExtendedMouseMode(const bool enabled) // True if handled successfully. False otherwise. bool AdaptDispatch::EnableButtonEventMouseMode(const bool enabled) { - return _pConApi->PrivateEnableButtonEventMouseMode(enabled); + bool success = true; + success = _pConApi->PrivateEnableButtonEventMouseMode(enabled); + + // If we're a conpty, always return false + bool isPty = false; + _pConApi->IsConsolePty(isPty); + if (isPty) + { + return false; + } + + return success; } //Routine Description: @@ -1735,7 +1801,18 @@ bool AdaptDispatch::EnableButtonEventMouseMode(const bool enabled) // True if handled successfully. False otherwise. bool AdaptDispatch::EnableAnyEventMouseMode(const bool enabled) { - return _pConApi->PrivateEnableAnyEventMouseMode(enabled); + bool success = true; + success = _pConApi->PrivateEnableAnyEventMouseMode(enabled); + + // If we're a conpty, always return false + bool isPty = false; + _pConApi->IsConsolePty(isPty); + if (isPty) + { + return false; + } + + return success; } //Routine Description: @@ -1747,7 +1824,18 @@ bool AdaptDispatch::EnableAnyEventMouseMode(const bool enabled) // True if handled successfully. False otherwise. bool AdaptDispatch::EnableAlternateScroll(const bool enabled) { - return _pConApi->PrivateEnableAlternateScroll(enabled); + bool success = true; + success = _pConApi->PrivateEnableAlternateScroll(enabled); + + // If we're a conpty, always return false + bool isPty = false; + _pConApi->IsConsolePty(isPty); + if (isPty) + { + return false; + } + + return success; } //Routine Description: diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index b7e214f3df6..8a41cf1fa71 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -35,6 +35,8 @@ namespace Microsoft::Console::VirtualTerminal virtual bool SetConsoleCursorPosition(const COORD position) = 0; virtual bool SetConsoleTextAttribute(const WORD attr) = 0; + virtual bool PrivateIsVtInputEnabled() const = 0; + virtual bool PrivateSetLegacyAttributes(const WORD attr, const bool foreground, const bool background, diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index 5ca91dbc1c1..e98e69991ec 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -214,6 +214,11 @@ class TestGetSet final : public ConGetSet return _setConsoleTextAttributeResult; } + bool PrivateIsVtInputEnabled() const override + { + return false; + } + bool PrivateSetLegacyAttributes(const WORD attr, const bool foreground, const bool background, const bool meta) override { Log::Comment(L"PrivateSetLegacyAttributes MOCK called..."); diff --git a/src/terminal/parser/InputStateMachineEngine.cpp b/src/terminal/parser/InputStateMachineEngine.cpp index 71fc36aec80..12a86780b9c 100644 --- a/src/terminal/parser/InputStateMachineEngine.cpp +++ b/src/terminal/parser/InputStateMachineEngine.cpp @@ -92,7 +92,8 @@ InputStateMachineEngine::InputStateMachineEngine(std::unique_ptr pDispatch, const bool lookingForDSR) : _pDispatch(std::move(pDispatch)), - _lookingForDSR(lookingForDSR) + _lookingForDSR(lookingForDSR), + _pfnFlushToInputQueue(nullptr) { THROW_HR_IF_NULL(E_INVALIDARG, _pDispatch.get()); } @@ -256,6 +257,27 @@ bool InputStateMachineEngine::ActionPrintString(const std::wstring_view string) // - true iff we successfully dispatched the sequence. bool InputStateMachineEngine::ActionPassThroughString(const std::wstring_view string) { + if (_pDispatch->IsVtInputEnabled()) + { + // Synthesize string into key events that we'll write to the buffer + // similar to TerminalInput::_SendInputSequence + if (!string.empty()) + { + try + { + std::deque> inputEvents; + for (const auto& wch : string) + { + inputEvents.push_back(std::make_unique(true, 1ui16, 0ui16, 0ui16, wch, 0)); + } + return _pDispatch->WriteInput(inputEvents); + } + catch (...) + { + LOG_HR(wil::ResultFromCaughtException()); + } + } + } return ActionPrintString(string); } @@ -271,6 +293,11 @@ bool InputStateMachineEngine::ActionPassThroughString(const std::wstring_view st bool InputStateMachineEngine::ActionEscDispatch(const wchar_t wch, const std::basic_string_view /*intermediates*/) { + if (_pDispatch->IsVtInputEnabled() && _pfnFlushToInputQueue) + { + return _pfnFlushToInputQueue(); + } + bool success = false; // 0x7f is DEL, which we treat effectively the same as a ctrl character. @@ -309,6 +336,11 @@ bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, const std::basic_string_view intermediates, const std::basic_string_view parameters) { + if (_pDispatch->IsVtInputEnabled() && _pfnFlushToInputQueue) + { + return _pfnFlushToInputQueue(); + } + DWORD modifierState = 0; short vkey = 0; unsigned int function = 0; @@ -440,6 +472,11 @@ bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, bool InputStateMachineEngine::ActionSs3Dispatch(const wchar_t wch, const std::basic_string_view /*parameters*/) { + if (_pDispatch->IsVtInputEnabled() && _pfnFlushToInputQueue) + { + return _pfnFlushToInputQueue(); + } + // Ss3 sequence keys aren't modified. // When F1-F4 *are* modified, they're sent as CSI sequences, not SS3's. const DWORD modifierState = 0; @@ -1043,6 +1080,22 @@ bool InputStateMachineEngine::DispatchIntermediatesFromEscape() const noexcept return true; } +// Method Description: +// - Sets us up for vt input passthrough. +// We'll set a couple members, and if they aren't null, when we get a +// sequence we don't understand, we'll pass it along to the app +// instead of eating it ourselves. +// Arguments: +// - pfnFlushToInputQueue: This is a callback to the underlying state machine to +// trigger it to call ActionPassThroughString with whatever sequence it's +// currently processing. +// Return Value: +// - +void InputStateMachineEngine::SetFlushToInputQueueCallback(std::function pfnFlushToInputQueue) +{ + _pfnFlushToInputQueue = pfnFlushToInputQueue; +} + // Method Description: // - Retrieves the type of window manipulation operation from the parameter pool // stored during Param actions. diff --git a/src/terminal/parser/InputStateMachineEngine.hpp b/src/terminal/parser/InputStateMachineEngine.hpp index 0ba8eaace33..ee7fc591a47 100644 --- a/src/terminal/parser/InputStateMachineEngine.hpp +++ b/src/terminal/parser/InputStateMachineEngine.hpp @@ -168,8 +168,11 @@ namespace Microsoft::Console::VirtualTerminal bool DispatchControlCharsFromEscape() const noexcept override; bool DispatchIntermediatesFromEscape() const noexcept override; + void SetFlushToInputQueueCallback(std::function pfnFlushToInputQueue); + private: const std::unique_ptr _pDispatch; + std::function _pfnFlushToInputQueue; bool _lookingForDSR; DWORD _mouseButtonState = 0; @@ -180,7 +183,6 @@ namespace Microsoft::Console::VirtualTerminal bool _IsModified(const size_t paramCount) noexcept; DWORD _GetModifier(const size_t parameter) noexcept; - DWORD _GetSGRModifier(const size_t parameter) noexcept; bool _UpdateSGRMouseButtonState(const wchar_t wch, const std::basic_string_view parameters, diff --git a/src/terminal/parser/ut_parser/InputEngineTest.cpp b/src/terminal/parser/ut_parser/InputEngineTest.cpp index 180147ef6d5..270fc89551d 100644 --- a/src/terminal/parser/ut_parser/InputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/InputEngineTest.cpp @@ -310,6 +310,8 @@ class Microsoft::Console::VirtualTerminal::TestInteractDispatch final : public I virtual bool MoveCursor(const size_t row, const size_t col) override; + virtual bool IsVtInputEnabled() const override; + private: std::function>&)> _pfnWriteInputCallback; TestState* _testState; // non-ownership pointer @@ -376,6 +378,11 @@ bool TestInteractDispatch::MoveCursor(const size_t row, const size_t col) return true; } +bool TestInteractDispatch::IsVtInputEnabled() const +{ + return true; +} + void InputEngineTest::C0Test() { auto pfn = std::bind(&TestState::TestInputCallback, &testState, std::placeholders::_1);