diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt index daf1feb13af..3045f47721c 100644 --- a/.github/actions/spelling/allow/apis.txt +++ b/.github/actions/spelling/allow/apis.txt @@ -95,11 +95,12 @@ lround Lsa lsass LSHIFT +memchr memicmp MENUCOMMAND MENUDATA -MENUINFO MENUITEMINFOW +MENUINFO MOUSELEAVE mov mptt diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 652be19189b..a68bc4a326c 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -519,6 +519,7 @@ DECAUPSS DECAWM DECCKM DECCOLM +DECCRA DECDHL decdld DECDLD @@ -2632,6 +2633,7 @@ VSTS VSTT vstudio vswhere +vtapi vtapp VTE VTID diff --git a/.vscode/settings.json b/.vscode/settings.json index 142fa20f6ab..604f91797d9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -94,7 +94,11 @@ "xlocinfo": "cpp", "xmemory": "cpp", "xstddef": "cpp", - "xtr1common": "cpp" + "xtr1common": "cpp", + "coroutine": "cpp", + "format": "cpp", + "forward_list": "cpp", + "latch": "cpp" }, "files.exclude": { "**/bin/**": true, diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index ac7ab589514..ba840fdcf6a 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -2051,6 +2051,10 @@ "default": false, "description": "When true, this profile should always open in an elevated context. If the window isn't running as an Administrator, then a new elevated window will be created." }, + "experimental.connection.passthroughMode": { + "description": "When set to true, directs the PTY for this connection to use pass-through mode instead of the original Conhost PTY simulation engine. This is an experimental feature, and its continued existence is not guaranteed.", + "type": "boolean" + }, "experimental.retroTerminalEffect": { "description": "When set to true, enable retro terminal effects. This is an experimental feature, and its continued existence is not guaranteed.", "type": "boolean" diff --git a/src/cascadia/PublicTerminalCore/HwndTerminal.cpp b/src/cascadia/PublicTerminalCore/HwndTerminal.cpp index 1f188b21037..d318d13f906 100644 --- a/src/cascadia/PublicTerminalCore/HwndTerminal.cpp +++ b/src/cascadia/PublicTerminalCore/HwndTerminal.cpp @@ -242,7 +242,7 @@ HRESULT HwndTerminal::Initialize() _terminal->Create(COORD{ 80, 25 }, 1000, *_renderer); _terminal->SetColorTableEntry(TextColor::DEFAULT_BACKGROUND, RGB(12, 12, 12)); _terminal->SetColorTableEntry(TextColor::DEFAULT_FOREGROUND, RGB(204, 204, 204)); - _terminal->SetWriteInputCallback([=](std::wstring& input) noexcept { _WriteTextToConnection(input); }); + _terminal->SetWriteInputCallback([=](std::wstring_view input) noexcept { _WriteTextToConnection(input); }); localPointerToThread->EnablePainting(); _multiClickTime = std::chrono::milliseconds{ GetDoubleClickTime() }; @@ -282,7 +282,7 @@ void HwndTerminal::RegisterScrollCallback(std::function cal _terminal->SetScrollPositionChangedCallback(callback); } -void HwndTerminal::_WriteTextToConnection(const std::wstring& input) noexcept +void HwndTerminal::_WriteTextToConnection(const std::wstring_view input) noexcept { if (!_pfnWriteCallback) { diff --git a/src/cascadia/PublicTerminalCore/HwndTerminal.hpp b/src/cascadia/PublicTerminalCore/HwndTerminal.hpp index 0510675efc0..3a364bd0f18 100644 --- a/src/cascadia/PublicTerminalCore/HwndTerminal.hpp +++ b/src/cascadia/PublicTerminalCore/HwndTerminal.hpp @@ -108,7 +108,7 @@ struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo friend void _stdcall TerminalKillFocus(void* terminal); void _UpdateFont(int newDpi); - void _WriteTextToConnection(const std::wstring& text) noexcept; + void _WriteTextToConnection(const std::wstring_view text) noexcept; HRESULT _CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, bool const fAlsoCopyFormatting); HRESULT _CopyToSystemClipboard(std::string stringToCopy, LPCWSTR lpszFormat); void _PasteTextFromClipboard() noexcept; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 91694653088..a137bf1637b 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1070,13 +1070,20 @@ namespace winrt::TerminalApp::implementation std::filesystem::path azBridgePath{ wil::GetModuleFileNameW(nullptr) }; azBridgePath.replace_filename(L"TerminalAzBridge.exe"); connection = TerminalConnection::ConptyConnection(); - connection.Initialize(TerminalConnection::ConptyConnection::CreateSettings(azBridgePath.wstring(), - L".", - L"Azure", - nullptr, - ::base::saturated_cast(settings.InitialRows()), - ::base::saturated_cast(settings.InitialCols()), - winrt::guid())); + auto valueSet = TerminalConnection::ConptyConnection::CreateSettings(azBridgePath.wstring(), + L".", + L"Azure", + nullptr, + ::base::saturated_cast(settings.InitialRows()), + ::base::saturated_cast(settings.InitialCols()), + winrt::guid()); + + if constexpr (Feature_VtPassthroughMode::IsEnabled()) + { + valueSet.Insert(L"passthroughMode", Windows::Foundation::PropertyValue::CreateBoolean(settings.VtPassthrough())); + } + + connection.Initialize(valueSet); } else @@ -1114,13 +1121,17 @@ namespace winrt::TerminalApp::implementation } auto conhostConn = TerminalConnection::ConptyConnection(); - conhostConn.Initialize(TerminalConnection::ConptyConnection::CreateSettings(settings.Commandline(), - newWorkingDirectory, - settings.StartingTitle(), - envMap.GetView(), - ::base::saturated_cast(settings.InitialRows()), - ::base::saturated_cast(settings.InitialCols()), - winrt::guid())); + auto valueSet = TerminalConnection::ConptyConnection::CreateSettings(settings.Commandline(), + newWorkingDirectory, + settings.StartingTitle(), + envMap.GetView(), + ::base::saturated_cast(settings.InitialRows()), + ::base::saturated_cast(settings.InitialCols()), + winrt::guid()); + + valueSet.Insert(L"passthroughMode", Windows::Foundation::PropertyValue::CreateBoolean(settings.VtPassthrough())); + + conhostConn.Initialize(valueSet); sessionGuid = conhostConn.Guid(); connection = conhostConn; diff --git a/src/cascadia/TerminalConnection/ConptyConnection.cpp b/src/cascadia/TerminalConnection/ConptyConnection.cpp index b4e5978440b..762d7b4f90a 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.cpp +++ b/src/cascadia/TerminalConnection/ConptyConnection.cpp @@ -266,6 +266,10 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation _initialCols = winrt::unbox_value_or(settings.TryLookup(L"initialCols").try_as(), _initialCols); _guid = winrt::unbox_value_or(settings.TryLookup(L"guid").try_as(), _guid); _environment = settings.TryLookup(L"environment").try_as(); + if constexpr (Feature_VtPassthroughMode::IsEnabled()) + { + _passthroughMode = winrt::unbox_value_or(settings.TryLookup(L"passthroughMode").try_as(), _passthroughMode); + } } if (_guid == guid{}) @@ -295,7 +299,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation // handoff from an already-started PTY process. if (!_inPipe) { - THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(dimensions, PSEUDOCONSOLE_RESIZE_QUIRK | PSEUDOCONSOLE_WIN32_INPUT_MODE, &_inPipe, &_outPipe, &_hPC)); + DWORD flags = PSEUDOCONSOLE_RESIZE_QUIRK | PSEUDOCONSOLE_WIN32_INPUT_MODE; + + if constexpr (Feature_VtPassthroughMode::IsEnabled()) + { + if (_passthroughMode) + { + WI_SetFlag(flags, PSEUDOCONSOLE_PASSTHROUGH_MODE); + } + } + + THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(dimensions, flags, &_inPipe, &_outPipe, &_hPC)); THROW_IF_FAILED(_LaunchAttachedClient()); } // But if it was an inbound handoff... attempt to synchronize the size of it with what our connection diff --git a/src/cascadia/TerminalConnection/ConptyConnection.h b/src/cascadia/TerminalConnection/ConptyConnection.h index 4089a944675..fc4ed55c08e 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.h +++ b/src/cascadia/TerminalConnection/ConptyConnection.h @@ -85,6 +85,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation til::u8state _u8State{}; std::wstring _u16Str{}; std::array _buffer{}; + bool _passthroughMode{}; DWORD _OutputThread(); }; diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index f473a817439..820686d322b 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -77,7 +77,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // This event is explicitly revoked in the destructor: does not need weak_ref _connectionOutputEventToken = _connection.TerminalOutput({ this, &ControlCore::_connectionOutputHandler }); - _terminal->SetWriteInputCallback([this](std::wstring& wstr) { + _terminal->SetWriteInputCallback([this](std::wstring_view wstr) { _sendInputToConnection(wstr); }); diff --git a/src/cascadia/TerminalCore/ICoreSettings.idl b/src/cascadia/TerminalCore/ICoreSettings.idl index dd0b4939b3b..5b05a72c05e 100644 --- a/src/cascadia/TerminalCore/ICoreSettings.idl +++ b/src/cascadia/TerminalCore/ICoreSettings.idl @@ -22,6 +22,7 @@ namespace Microsoft.Terminal.Core Boolean ForceVTInput; Boolean TrimBlockSelection; Boolean DetectURLs; + Boolean VtPassthrough; Windows.Foundation.IReference TabColor; Windows.Foundation.IReference StartingTabColor; diff --git a/src/cascadia/TerminalCore/ITerminalApi.hpp b/src/cascadia/TerminalCore/ITerminalApi.hpp index ebff6fd2072..3aebe7ea1c8 100644 --- a/src/cascadia/TerminalCore/ITerminalApi.hpp +++ b/src/cascadia/TerminalCore/ITerminalApi.hpp @@ -21,6 +21,8 @@ namespace Microsoft::Terminal::Core virtual void PrintString(std::wstring_view string) = 0; + virtual bool ReturnResponse(std::wstring_view responseString) = 0; + virtual TextAttribute GetTextAttributes() const = 0; virtual void SetTextAttributes(const TextAttribute& attrs) = 0; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 4b84c73584a..5db2cff6b9e 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1164,7 +1164,7 @@ void Terminal::_NotifyTerminalCursorPositionChanged() noexcept } } -void Terminal::SetWriteInputCallback(std::function pfn) noexcept +void Terminal::SetWriteInputCallback(std::function pfn) noexcept { _pfnWriteInput.swap(pfn); } diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index a8b67233396..d094115d4de 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -77,10 +77,10 @@ class Microsoft::Terminal::Core::Terminal final : void UpdateAppearance(const winrt::Microsoft::Terminal::Core::ICoreAppearance& appearance); void SetFontInfo(const FontInfo& fontInfo); - // Write goes through the parser + // Write comes from the PTY and goes to our parser to be stored in the output buffer void Write(std::wstring_view stringView); - // WritePastedText goes directly to the connection + // WritePastedText comes from our input and goes back to the PTY's input channel void WritePastedText(std::wstring_view stringView); [[nodiscard]] std::unique_lock LockForReading(); @@ -97,6 +97,7 @@ class Microsoft::Terminal::Core::Terminal final : #pragma region ITerminalApi // These methods are defined in TerminalApi.cpp void PrintString(std::wstring_view stringView) override; + bool ReturnResponse(std::wstring_view responseString) override; TextAttribute GetTextAttributes() const override; void SetTextAttributes(const TextAttribute& attrs) override; Microsoft::Console::Types::Viewport GetBufferSize() override; @@ -197,7 +198,7 @@ class Microsoft::Terminal::Core::Terminal final : const bool IsUiaDataInitialized() const noexcept override; #pragma endregion - void SetWriteInputCallback(std::function pfn) noexcept; + void SetWriteInputCallback(std::function pfn) noexcept; void SetWarningBellCallback(std::function pfn) noexcept; void SetTitleChangedCallback(std::function pfn) noexcept; void SetTabColorChangedCallback(std::function)> pfn) noexcept; @@ -252,7 +253,7 @@ class Microsoft::Terminal::Core::Terminal final : #pragma endregion private: - std::function _pfnWriteInput; + std::function _pfnWriteInput; std::function _pfnWarningBell; std::function _pfnTitleChanged; std::function _pfnCopyToClipboard; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 592ce52d540..69d1b42cda0 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -21,6 +21,16 @@ TextAttribute Terminal::GetTextAttributes() const return _buffer->GetCurrentAttributes(); } +bool Terminal::ReturnResponse(std::wstring_view responseString) +{ + if (!_pfnWriteInput) + { + return false; + } + _pfnWriteInput(responseString); + return true; +} + void Terminal::SetTextAttributes(const TextAttribute& attrs) { _buffer->SetCurrentAttributes(attrs); diff --git a/src/cascadia/TerminalCore/TerminalDispatch.cpp b/src/cascadia/TerminalCore/TerminalDispatch.cpp index 6adf0daed61..1747185b64a 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.cpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.cpp @@ -442,6 +442,58 @@ bool TerminalDispatch::ResetMode(const DispatchTypes::ModeParams param) return _ModeParamsHelper(param, false); } +// Routine Description: +// - DSR - Reports status of a console property back to the STDIN based on the type of status requested. +// - This particular routine responds to ANSI status patterns only (CSI # n), not the DEC format (CSI ? # n) +// Arguments: +// - statusType - ANSI status type indicating what property we should report back +// Return Value: +// - True if handled successfully. False otherwise. +bool TerminalDispatch::DeviceStatusReport(const DispatchTypes::AnsiStatusType statusType) +{ + bool success = false; + + switch (statusType) + { + case DispatchTypes::AnsiStatusType::OS_OperatingStatus: + success = _OperatingStatus(); + break; + case DispatchTypes::AnsiStatusType::CPR_CursorPositionReport: + success = _CursorPositionReport(); + break; + } + + return success; +} + +// Routine Description: +// - DSR-OS - Reports the operating status back to the input channel +// Arguments: +// - +// Return Value: +// - True if handled successfully. False otherwise. +bool TerminalDispatch::_OperatingStatus() const +{ + // We always report a good operating condition. + return _WriteResponse(L"\x1b[0n"); +} + +// Routine Description: +// - DSR-CPR - Reports the current cursor position within the viewport back to the input channel +// Arguments: +// - +// Return Value: +// - True if handled successfully. False otherwise. +bool TerminalDispatch::_CursorPositionReport() const +{ + // Now send it back into the input channel of the console. + // First format the response string. + const auto pos = _terminalApi.GetCursorPosition(); + // VT has origin at 1,1 where as we use 0,0 internally + const auto response = wil::str_printf(L"\x1b[%d;%dR", pos.Y + 1, pos.X + 1); + return _WriteResponse(response); +} + // Method Description: // - Start a hyperlink // Arguments: @@ -545,6 +597,18 @@ bool TerminalDispatch::DoConEmuAction(const std::wstring_view string) return false; } +// Routine Description: +// - Helper to send a string reply to the input stream of the console. +// - Used by various commands where the program attached would like a reply to one of the commands issued. +// Arguments: +// - reply - The reply string to transmit back to the input stream +// Return Value: +// - True if the string was sent to the connected application. False otherwise. +bool TerminalDispatch::_WriteResponse(const std::wstring_view reply) const +{ + return _terminalApi.ReturnResponse(reply); +} + // Routine Description: // - Support routine for routing private mode parameters to be set/reset as flags // Arguments: diff --git a/src/cascadia/TerminalCore/TerminalDispatch.hpp b/src/cascadia/TerminalCore/TerminalDispatch.hpp index 1ec29bf8e08..cfec1aaadfe 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.hpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.hpp @@ -75,6 +75,8 @@ class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatc bool SetMode(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams /*param*/) override; // DECSET bool ResetMode(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams /*param*/) override; // DECRST + bool DeviceStatusReport(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::AnsiStatusType /*statusType*/) override; // DSR, DSR-OS, DSR-CPR + bool AddHyperlink(const std::wstring_view uri, const std::wstring_view params) override; bool EndHyperlink() override; @@ -90,8 +92,12 @@ class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatc TextAttribute& attr, const bool isForeground); + bool _WriteResponse(const std::wstring_view reply) const; bool _ModeParamsHelper(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams param, const bool enable); + bool _OperatingStatus() const; + bool _CursorPositionReport() const; + void _ClearSingleTabStop(); void _ClearAllTabStops(); void _ResetTabStops(); diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp index 86eefe56868..dac23b33a0e 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp @@ -298,6 +298,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return Feature_AtlasEngine::IsEnabled(); } + bool ProfileViewModel::VtPassthroughAvailable() const noexcept + { + return Feature_VtPassthroughMode::IsEnabled(); + } + bool ProfileViewModel::UseParentProcessDirectory() { return StartingDirectory().empty(); diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h index 925fb567c2b..a4bc0f22bcb 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h @@ -73,6 +73,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void CreateUnfocusedAppearance(); void DeleteUnfocusedAppearance(); bool AtlasEngineAvailable() const noexcept; + bool VtPassthroughAvailable() const noexcept; VIEW_MODEL_OBSERVABLE_PROPERTY(ProfileSubPage, CurrentPage); @@ -103,6 +104,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_profile, BellStyle); OBSERVABLE_PROJECTED_SETTING(_profile, UseAtlasEngine); OBSERVABLE_PROJECTED_SETTING(_profile, Elevate); + OBSERVABLE_PROJECTED_SETTING(_profile, VtPassthrough) WINRT_PROPERTY(bool, IsBaseLayer, false); WINRT_PROPERTY(IHostedInWindow, WindowRoot, nullptr); diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl index 8571c7c9441..b05b76317a0 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl @@ -63,6 +63,7 @@ namespace Microsoft.Terminal.Settings.Editor Boolean ShowUnfocusedAppearance { get; }; AppearanceViewModel UnfocusedAppearance { get; }; Boolean AtlasEngineAvailable { get; }; + Boolean VtPassthroughAvailable { get; }; void CreateUnfocusedAppearance(); void DeleteUnfocusedAppearance(); @@ -94,5 +95,6 @@ namespace Microsoft.Terminal.Settings.Editor OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Settings.Model.BellStyle, BellStyle); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, UseAtlasEngine); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, Elevate); + OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, VtPassthrough); } } diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml index 517cc3f9113..40d12cd30f9 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml @@ -127,6 +127,16 @@ + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index fcafb0bcf4f..09954874cec 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -1043,6 +1043,10 @@ Enable experimental text rendering engine An option to enable an experimental text rendering engine + + Enable experimental virtual terminal passthrough + An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY + Audible An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user. diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index 5cba5156767..279237f9fba 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -76,7 +76,8 @@ Author(s): X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible) \ X(bool, UseAtlasEngine, "experimental.useAtlasEngine", false) \ X(Windows::Foundation::Collections::IVector, BellSound, "bellSound", nullptr) \ - X(bool, Elevate, "elevate", false) + X(bool, Elevate, "elevate", false) \ + X(bool, VtPassthrough, "experimental.connection.passthroughMode", false) // Intentionally omitted Profile settings: // * Name diff --git a/src/cascadia/TerminalSettingsModel/Profile.idl b/src/cascadia/TerminalSettingsModel/Profile.idl index d54c6431a53..f8f3dc8d1cd 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.idl +++ b/src/cascadia/TerminalSettingsModel/Profile.idl @@ -64,6 +64,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.ScrollbarState, ScrollState); INHERITABLE_PROFILE_SETTING(String, Padding); INHERITABLE_PROFILE_SETTING(String, Commandline); + INHERITABLE_PROFILE_SETTING(Boolean, VtPassthrough); INHERITABLE_PROFILE_SETTING(String, StartingDirectory); String EvaluatedStartingDirectory { get; }; diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp index 491affc5e7f..0b56266b708 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp @@ -241,6 +241,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation _Padding = profile.Padding(); _Commandline = profile.Commandline(); + _VtPassthrough = profile.VtPassthrough(); _StartingDirectory = profile.EvaluatedStartingDirectory(); diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index 909ad8ab37a..40301422a8a 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -91,6 +91,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::TerminalSettings, bool, FocusFollowMouse, false); INHERITABLE_SETTING(Model::TerminalSettings, bool, TrimBlockSelection, true); INHERITABLE_SETTING(Model::TerminalSettings, bool, DetectURLs, true); + INHERITABLE_SETTING(Model::TerminalSettings, bool, VtPassthrough, false); INHERITABLE_SETTING(Model::TerminalSettings, Windows::Foundation::IReference, TabColor, nullptr); diff --git a/src/cascadia/UnitTests_TerminalCore/InputTest.cpp b/src/cascadia/UnitTests_TerminalCore/InputTest.cpp index 3ee7c74784a..8c6d3245162 100644 --- a/src/cascadia/UnitTests_TerminalCore/InputTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/InputTest.cpp @@ -31,7 +31,7 @@ namespace TerminalCoreUnitTests TEST_METHOD(AltShiftKey); TEST_METHOD(InvalidKeyEvent); - void _VerifyExpectedInput(std::wstring& actualInput) + void _VerifyExpectedInput(std::wstring_view actualInput) { VERIFY_ARE_EQUAL(expectedinput.size(), actualInput.size()); VERIFY_ARE_EQUAL(expectedinput, actualInput); diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index 171cbb7eb4b..08f114eb389 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -45,7 +45,8 @@ X(bool, SuppressApplicationTitle) \ X(bool, ForceVTInput, false) \ X(winrt::hstring, StartingTitle) \ - X(bool, DetectURLs, true) + X(bool, DetectURLs, true) \ + X(bool, VtPassthrough, false) // --------------------------- Control Settings --------------------------- // All of these settings are defined in IControlSettings. diff --git a/src/features.xml b/src/features.xml index d9bc3a87e90..c2fde9d2765 100644 --- a/src/features.xml +++ b/src/features.xml @@ -84,4 +84,15 @@ Dev + + + Feature_VtPassthroughMode + Enables passthrough option per profile in Terminal and ConPTY ability to use passthrough API dispatch engine + AlwaysDisabled + + + Dev + Preview + + diff --git a/src/host/CommandListPopup.cpp b/src/host/CommandListPopup.cpp index 4aabc3f63ae..7cdc44eb0a2 100644 --- a/src/host/CommandListPopup.cpp +++ b/src/host/CommandListPopup.cpp @@ -342,7 +342,7 @@ void CommandListPopup::_drawList() WriteCoord.Y += 1i16; } - auto& api = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().api; + auto api = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().api; WriteCoord.Y = _region.Top + 1i16; SHORT i = std::max(gsl::narrow(_bottomIndex - Height() + 1), 0i16); @@ -379,10 +379,10 @@ void CommandListPopup::_drawList() WriteCoord.X = _region.Left + 1i16; - LOG_IF_FAILED(api.WriteConsoleOutputCharacterAImpl(_screenInfo, - { CommandNumberPtr, CommandNumberLength }, - WriteCoord, - CommandNumberLength)); + LOG_IF_FAILED(api->WriteConsoleOutputCharacterAImpl(_screenInfo, + { CommandNumberPtr, CommandNumberLength }, + WriteCoord, + CommandNumberLength)); // write command to screen auto command = _history.GetNth(i); @@ -417,10 +417,10 @@ void CommandListPopup::_drawList() WriteCoord.X = gsl::narrow(WriteCoord.X + CommandNumberLength); size_t used; - LOG_IF_FAILED(api.WriteConsoleOutputCharacterWImpl(_screenInfo, - { command.data(), lStringLength }, - WriteCoord, - used)); + LOG_IF_FAILED(api->WriteConsoleOutputCharacterWImpl(_screenInfo, + { command.data(), lStringLength }, + WriteCoord, + used)); // write attributes to screen if (i == _currentCommand) diff --git a/src/host/ConsoleArguments.cpp b/src/host/ConsoleArguments.cpp index 333f9e1d859..b3f03db0d3f 100644 --- a/src/host/ConsoleArguments.cpp +++ b/src/host/ConsoleArguments.cpp @@ -24,6 +24,7 @@ const std::wstring_view ConsoleArguments::WIN32_INPUT_MODE = L"--win32input"; const std::wstring_view ConsoleArguments::FEATURE_ARG = L"--feature"; const std::wstring_view ConsoleArguments::FEATURE_PTY_ARG = L"pty"; const std::wstring_view ConsoleArguments::COM_SERVER_ARG = L"-Embedding"; +const std::wstring_view ConsoleArguments::PASSTHROUGH_ARG = L"--passthrough"; std::wstring EscapeArgument(std::wstring_view ac) { @@ -461,6 +462,12 @@ void ConsoleArguments::s_ConsumeArg(_Inout_ std::vector& args, _In s_ConsumeArg(args, i); hr = S_OK; } + else if (arg == PASSTHROUGH_ARG) + { + _passthroughMode = true; + s_ConsumeArg(args, i); + hr = S_OK; + } else if (arg.substr(0, FILEPATH_LEADER_PREFIX.length()) == FILEPATH_LEADER_PREFIX) { // beginning of command line -- includes file path @@ -596,6 +603,11 @@ bool ConsoleArguments::ShouldRunAsComServer() const return _runAsComServer; } +bool ConsoleArguments::IsPassthroughMode() const noexcept +{ + return _passthroughMode; +} + HANDLE ConsoleArguments::GetServerHandle() const { return ULongToHandle(_serverHandle); diff --git a/src/host/ConsoleArguments.hpp b/src/host/ConsoleArguments.hpp index d82dc0bf24d..bb4a95b21e6 100644 --- a/src/host/ConsoleArguments.hpp +++ b/src/host/ConsoleArguments.hpp @@ -36,6 +36,7 @@ class ConsoleArguments bool IsHeadless() const; bool ShouldCreateServerHandle() const; bool ShouldRunAsComServer() const; + bool IsPassthroughMode() const noexcept; HANDLE GetServerHandle() const; HANDLE GetVtInHandle() const; @@ -77,6 +78,7 @@ class ConsoleArguments static const std::wstring_view FEATURE_ARG; static const std::wstring_view FEATURE_PTY_ARG; static const std::wstring_view COM_SERVER_ARG; + static const std::wstring_view PASSTHROUGH_ARG; private: #ifdef UNIT_TESTING @@ -95,7 +97,8 @@ class ConsoleArguments const DWORD serverHandle, const DWORD signalHandle, const bool inheritCursor, - const bool runAsComServer) : + const bool runAsComServer, + const bool passthroughMode) : _commandline(commandline), _clientCommandline(clientCommandline), _vtInHandle(vtInHandle), @@ -111,7 +114,8 @@ class ConsoleArguments _signalHandle(signalHandle), _inheritCursor(inheritCursor), _resizeQuirk(false), - _runAsComServer{ runAsComServer } + _runAsComServer{ runAsComServer }, + _passthroughMode{ passthroughMode } { } #endif @@ -133,6 +137,7 @@ class ConsoleArguments short _width; short _height; + bool _passthroughMode{ false }; bool _runAsComServer; bool _createServerHandle; DWORD _serverHandle; @@ -189,6 +194,7 @@ namespace WEX L"Signal Handle: '0x%x'\r\n", L"Inherit Cursor: '%ws'\r\n", L"Run As Com Server: '%ws'\r\n", + L"Passthrough Mode: '%ws'\r\n", ci.GetClientCommandline().c_str(), s_ToBoolString(ci.HasVtHandles()), ci.GetVtInHandle(), @@ -203,7 +209,8 @@ namespace WEX s_ToBoolString(ci.HasSignalHandle()), ci.GetSignalHandle(), s_ToBoolString(ci.GetInheritCursor()), - s_ToBoolString(ci.ShouldRunAsComServer())); + s_ToBoolString(ci.ShouldRunAsComServer()), + s_ToBoolString(ci.IsPassthroughMode())); } private: @@ -232,7 +239,9 @@ namespace WEX expected.GetServerHandle() == actual.GetServerHandle() && expected.HasSignalHandle() == actual.HasSignalHandle() && expected.GetSignalHandle() == actual.GetSignalHandle() && - expected.GetInheritCursor() == actual.GetInheritCursor(); + expected.GetInheritCursor() == actual.GetInheritCursor() && + expected.ShouldRunAsComServer() == actual.ShouldRunAsComServer() && + expected.IsPassthroughMode() == actual.IsPassthroughMode(); } static bool AreSame(const ConsoleArguments& expected, const ConsoleArguments& actual) @@ -257,7 +266,9 @@ namespace WEX !object.ShouldCreateServerHandle() && object.GetServerHandle() == 0 && (object.GetSignalHandle() == 0 || object.GetSignalHandle() == INVALID_HANDLE_VALUE) && - !object.GetInheritCursor(); + !object.GetInheritCursor() && + !object.ShouldRunAsComServer() && + !object.IsPassthroughMode(); } }; } diff --git a/src/host/VtApiRoutines.cpp b/src/host/VtApiRoutines.cpp new file mode 100644 index 00000000000..f2b007a2ffb --- /dev/null +++ b/src/host/VtApiRoutines.cpp @@ -0,0 +1,863 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" +#include "VtApiRoutines.h" +#include "../interactivity/inc/ServiceLocator.hpp" +#include "../types/inc/convert.hpp" + +using namespace Microsoft::Console::Interactivity; + +// When someone attempts to use the console APIs to do a "read back" +// of the console buffer, we have to give them **something**. +// These two structures are just some gaudy-colored replacement character +// text to give them data but represent they've done something that cannot +// be supported under VT passthrough mode. +// ---- +// They can't be supported because in passthrough we maintain no internal +// buffer to answer these questions, and there is no VT sequence that lets +// us query the final terminal's buffer state. Even if a VT sequence did exist +// (and we personally believe it shouldn't), there's a possibility that it would +// read a massive amount of data and cause severe perf issues as applications coded +// to this old API are likely leaning on it heavily and asking for this data in a +// loop via VT would be a nightmare of parsing and formatting and over-the-wire transmission. + +static constexpr CHAR_INFO s_readBackUnicode{ + { UNICODE_REPLACEMENT }, + FOREGROUND_INTENSITY | FOREGROUND_RED | BACKGROUND_GREEN +}; + +static constexpr CHAR_INFO s_readBackAscii{ + { L'?' }, + FOREGROUND_INTENSITY | FOREGROUND_RED | BACKGROUND_GREEN +}; + +VtApiRoutines::VtApiRoutines() : + m_inputCodepage(ServiceLocator::LocateGlobals().getConsoleInformation().CP), + m_outputCodepage(ServiceLocator::LocateGlobals().getConsoleInformation().OutputCP), + m_inputMode(), + m_outputMode(), + m_pUsualRoutines(), + m_pVtEngine(), + m_listeningForDSR(false) +{ +} + +#pragma warning(push) +#pragma warning(disable : 4100) // unreferenced param + +void VtApiRoutines::GetConsoleInputCodePageImpl(ULONG& codepage) noexcept +{ + codepage = m_inputCodepage; + return; +} + +void VtApiRoutines::GetConsoleOutputCodePageImpl(ULONG& codepage) noexcept +{ + codepage = m_outputCodepage; + return; +} + +void VtApiRoutines::GetConsoleInputModeImpl(InputBuffer& context, + ULONG& mode) noexcept +{ + mode = m_inputMode; + return; +} + +void VtApiRoutines::GetConsoleOutputModeImpl(SCREEN_INFORMATION& context, + ULONG& mode) noexcept +{ + mode = m_outputMode; + return; +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleInputModeImpl(InputBuffer& context, + const ULONG mode) noexcept +{ + m_inputMode = mode; + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleOutputModeImpl(SCREEN_INFORMATION& context, + const ULONG Mode) noexcept +{ + m_outputMode = Mode; + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::GetNumberOfConsoleInputEventsImpl(const InputBuffer& context, + ULONG& events) noexcept +{ + return m_pUsualRoutines->GetNumberOfConsoleInputEventsImpl(context, events); +} + +void VtApiRoutines::_SynchronizeCursor(std::unique_ptr& waiter) noexcept +{ + // If we're about to tell the caller to wait, let's synchronize the cursor we have with what + // the terminal is presenting in case there's a cooked read going on. + // TODO GH#10001: we only need to do this in cooked read mode. + if (waiter) + { + m_listeningForDSR = true; + (void)m_pVtEngine->_ListenForDSR(); + (void)m_pVtEngine->RequestCursor(); + } +} + +[[nodiscard]] HRESULT VtApiRoutines::PeekConsoleInputAImpl(IConsoleInputObject& context, + std::deque>& outEvents, + const size_t eventsToRead, + INPUT_READ_HANDLE_DATA& readHandleState, + std::unique_ptr& waiter) noexcept +{ + const auto hr = m_pUsualRoutines->PeekConsoleInputAImpl(context, outEvents, eventsToRead, readHandleState, waiter); + _SynchronizeCursor(waiter); + return hr; +} + +[[nodiscard]] HRESULT VtApiRoutines::PeekConsoleInputWImpl(IConsoleInputObject& context, + std::deque>& outEvents, + const size_t eventsToRead, + INPUT_READ_HANDLE_DATA& readHandleState, + std::unique_ptr& waiter) noexcept +{ + const auto hr = m_pUsualRoutines->PeekConsoleInputWImpl(context, outEvents, eventsToRead, readHandleState, waiter); + _SynchronizeCursor(waiter); + return hr; +} + +[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleInputAImpl(IConsoleInputObject& context, + std::deque>& outEvents, + const size_t eventsToRead, + INPUT_READ_HANDLE_DATA& readHandleState, + std::unique_ptr& waiter) noexcept +{ + const auto hr = m_pUsualRoutines->ReadConsoleInputAImpl(context, outEvents, eventsToRead, readHandleState, waiter); + _SynchronizeCursor(waiter); + return hr; +} + +[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleInputWImpl(IConsoleInputObject& context, + std::deque>& outEvents, + const size_t eventsToRead, + INPUT_READ_HANDLE_DATA& readHandleState, + std::unique_ptr& waiter) noexcept +{ + const auto hr = m_pUsualRoutines->ReadConsoleInputWImpl(context, outEvents, eventsToRead, readHandleState, waiter); + _SynchronizeCursor(waiter); + return hr; +} + +[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleAImpl(IConsoleInputObject& context, + gsl::span buffer, + size_t& written, + std::unique_ptr& waiter, + const std::string_view initialData, + const std::wstring_view exeName, + INPUT_READ_HANDLE_DATA& readHandleState, + const HANDLE clientHandle, + const DWORD controlWakeupMask, + DWORD& controlKeyState) noexcept +{ + const auto hr = m_pUsualRoutines->ReadConsoleAImpl(context, buffer, written, waiter, initialData, exeName, readHandleState, clientHandle, controlWakeupMask, controlKeyState); + // If we're about to tell the caller to wait, let's synchronize the cursor we have with what + // the terminal is presenting in case there's a cooked read going on. + // TODO GH10001: we only need to do this in cooked read mode. + if (clientHandle) + { + m_listeningForDSR = true; + (void)m_pVtEngine->_ListenForDSR(); + (void)m_pVtEngine->RequestCursor(); + } + return hr; +} + +[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleWImpl(IConsoleInputObject& context, + gsl::span buffer, + size_t& written, + std::unique_ptr& waiter, + const std::string_view initialData, + const std::wstring_view exeName, + INPUT_READ_HANDLE_DATA& readHandleState, + const HANDLE clientHandle, + const DWORD controlWakeupMask, + DWORD& controlKeyState) noexcept +{ + const auto hr = m_pUsualRoutines->ReadConsoleWImpl(context, buffer, written, waiter, initialData, exeName, readHandleState, clientHandle, controlWakeupMask, controlKeyState); + // If we're about to tell the caller to wait, let's synchronize the cursor we have with what + // the terminal is presenting in case there's a cooked read going on. + // TODO GH10001: we only need to do this in cooked read mode. + if (clientHandle) + { + m_listeningForDSR = true; + (void)m_pVtEngine->_ListenForDSR(); + (void)m_pVtEngine->RequestCursor(); + } + return hr; +} + +[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleAImpl(IConsoleOutputObject& context, + const std::string_view buffer, + size_t& read, + bool requiresVtQuirk, + std::unique_ptr& waiter) noexcept +{ + if (CP_UTF8 == m_outputCodepage) + { + (void)m_pVtEngine->WriteTerminalUtf8(buffer); + } + else + { + (void)m_pVtEngine->WriteTerminalW(ConvertToW(m_outputCodepage, buffer)); + } + + (void)m_pVtEngine->_Flush(); + read = buffer.size(); + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleWImpl(IConsoleOutputObject& context, + const std::wstring_view buffer, + size_t& read, + bool requiresVtQuirk, + std::unique_ptr& waiter) noexcept +{ + (void)m_pVtEngine->WriteTerminalW(buffer); + (void)m_pVtEngine->_Flush(); + read = buffer.size(); + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleLangIdImpl(LANGID& langId) noexcept +{ + return m_pUsualRoutines->GetConsoleLangIdImpl(langId); +} + +[[nodiscard]] HRESULT VtApiRoutines::FillConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext, + const WORD attribute, + const size_t lengthToWrite, + const COORD startingCoordinate, + size_t& cellsModified) noexcept +{ + (void)m_pVtEngine->_CursorPosition(startingCoordinate); + (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(attribute), true); + (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(attribute >> 4), false); + (void)m_pVtEngine->_WriteFill(lengthToWrite, s_readBackAscii.Char.AsciiChar); + (void)m_pVtEngine->_Flush(); + cellsModified = lengthToWrite; + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::FillConsoleOutputCharacterAImpl(IConsoleOutputObject& OutContext, + const char character, + const size_t lengthToWrite, + const COORD startingCoordinate, + size_t& cellsModified) noexcept +{ + // I mean... if you get your jollies by using UTF8 for single byte codepoints... + // we may as well skip a lot of conversion work and just write it out. + if (m_outputCodepage == CP_UTF8 && character <= 0x7F) + { + (void)m_pVtEngine->_CursorPosition(startingCoordinate); + (void)m_pVtEngine->_WriteFill(lengthToWrite, character); + (void)m_pVtEngine->_Flush(); + cellsModified = lengthToWrite; + return S_OK; + } + else + { + const auto wstr = ConvertToW(m_outputCodepage, std::string_view{ &character, 1 }); + return FillConsoleOutputCharacterWImpl(OutContext, wstr.front(), lengthToWrite, startingCoordinate, cellsModified); + } +} + +[[nodiscard]] HRESULT VtApiRoutines::FillConsoleOutputCharacterWImpl(IConsoleOutputObject& OutContext, + const wchar_t character, + const size_t lengthToWrite, + const COORD startingCoordinate, + size_t& cellsModified, + const bool enablePowershellShim) noexcept +{ + (void)m_pVtEngine->_CursorPosition(startingCoordinate); + const std::wstring_view sv{ &character, 1 }; + + // TODO GH10001: horrible. it'll WC2MB over and over...we should do that once then emit... and then rep... + // TODO GH10001: there's probably an optimization for if ((character & 0x7F) == character) --> call the UTF8 one. + for (size_t i = 0; i < lengthToWrite; ++i) + { + (void)m_pVtEngine->WriteTerminalW(sv); + } + + (void)m_pVtEngine->_Flush(); + cellsModified = lengthToWrite; + return S_OK; +} + +//// Process based. Restrict in protocol side? +//HRESULT GenerateConsoleCtrlEventImpl(const ULONG ProcessGroupFilter, +// const ULONG ControlEvent); + +void VtApiRoutines::SetConsoleActiveScreenBufferImpl(SCREEN_INFORMATION& newContext) noexcept +{ + return; +} + +void VtApiRoutines::FlushConsoleInputBuffer(InputBuffer& context) noexcept +{ + m_pUsualRoutines->FlushConsoleInputBuffer(context); +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleInputCodePageImpl(const ULONG codepage) noexcept +{ + m_inputCodepage = codepage; + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleOutputCodePageImpl(const ULONG codepage) noexcept +{ + m_outputCodepage = codepage; + return S_OK; +} + +void VtApiRoutines::GetConsoleCursorInfoImpl(const SCREEN_INFORMATION& context, + ULONG& size, + bool& isVisible) noexcept +{ + // TODO GH10001: good luck capturing this out of the input buffer when it comes back in. + //m_pVtEngine->RequestCursor(); + return; +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleCursorInfoImpl(SCREEN_INFORMATION& context, + const ULONG size, + const bool isVisible) noexcept +{ + isVisible ? (void)m_pVtEngine->_ShowCursor() : (void)m_pVtEngine->_HideCursor(); + (void)m_pVtEngine->_Flush(); + return S_OK; +} + +//// driver will pare down for non-Ex method +void VtApiRoutines::GetConsoleScreenBufferInfoExImpl(const SCREEN_INFORMATION& context, + CONSOLE_SCREEN_BUFFER_INFOEX& data) noexcept +{ + // TODO GH10001: this is technically full of potentially incorrect data. do we care? should we store it in here with set? + return m_pUsualRoutines->GetConsoleScreenBufferInfoExImpl(context, data); +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleScreenBufferInfoExImpl(SCREEN_INFORMATION& context, + const CONSOLE_SCREEN_BUFFER_INFOEX& data) noexcept +{ + (void)m_pVtEngine->_ResizeWindow(data.srWindow.Right - data.srWindow.Left, data.srWindow.Bottom - data.srWindow.Top); + (void)m_pVtEngine->_CursorPosition(data.dwCursorPosition); + (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(data.wAttributes), true); + (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(data.wAttributes >> 4), false); + //color table? + // popup attributes... hold internally? + // TODO GH10001: popups are gonna erase the stuff behind them... deal with that somehow. + (void)m_pVtEngine->_Flush(); + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleScreenBufferSizeImpl(SCREEN_INFORMATION& context, + const COORD size) noexcept +{ + // Don't transmit. The terminal figures out its own buffer size. + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleCursorPositionImpl(SCREEN_INFORMATION& context, + const COORD position) noexcept +{ + if (m_listeningForDSR) + { + context.GetActiveBuffer().GetTextBuffer().GetCursor().SetPosition(position); + m_pVtEngine->SetTerminalCursorTextPosition(position); + } + else + { + (void)m_pVtEngine->_CursorPosition(position); + (void)m_pVtEngine->_Flush(); + } + return S_OK; +} + +void VtApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& context, + COORD& size) noexcept +{ + m_pUsualRoutines->GetLargestConsoleWindowSizeImpl(context, size); // This is likely super weird but not weirder than existing ConPTY answers. + return; +} + +[[nodiscard]] HRESULT VtApiRoutines::ScrollConsoleScreenBufferAImpl(SCREEN_INFORMATION& context, + const SMALL_RECT& source, + const COORD target, + std::optional clip, + const char fillCharacter, + const WORD fillAttribute) noexcept +{ + // TODO GH10001: Use DECCRA + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::ScrollConsoleScreenBufferWImpl(SCREEN_INFORMATION& context, + const SMALL_RECT& source, + const COORD target, + std::optional clip, + const wchar_t fillCharacter, + const WORD fillAttribute, + const bool enableCmdShim) noexcept +{ + // TODO GH10001: Use DECCRA + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleTextAttributeImpl(SCREEN_INFORMATION& context, + const WORD attribute) noexcept +{ + (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(attribute), true); + (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(attribute >> 4), false); + (void)m_pVtEngine->_Flush(); + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleWindowInfoImpl(SCREEN_INFORMATION& context, + const bool isAbsolute, + const SMALL_RECT& windowRect) noexcept +{ + (void)m_pVtEngine->_ResizeWindow(windowRect.Right - windowRect.Left, windowRect.Bottom - windowRect.Top); + (void)m_pVtEngine->_Flush(); + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleOutputAttributeImpl(const SCREEN_INFORMATION& context, + const COORD origin, + gsl::span buffer, + size_t& written) noexcept +{ + std::fill_n(buffer.data(), buffer.size(), s_readBackUnicode.Attributes); // should be same as the ascii one. + written = buffer.size(); + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleOutputCharacterAImpl(const SCREEN_INFORMATION& context, + const COORD origin, + gsl::span buffer, + size_t& written) noexcept +{ + std::fill_n(buffer.data(), buffer.size(), s_readBackAscii.Char.AsciiChar); + written = buffer.size(); + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleOutputCharacterWImpl(const SCREEN_INFORMATION& context, + const COORD origin, + gsl::span buffer, + size_t& written) noexcept +{ + std::fill_n(buffer.data(), buffer.size(), s_readBackUnicode.Char.UnicodeChar); + written = buffer.size(); + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleInputAImpl(InputBuffer& context, + const gsl::span buffer, + size_t& written, + const bool append) noexcept +{ + return m_pUsualRoutines->WriteConsoleInputAImpl(context, buffer, written, append); +} + +[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleInputWImpl(InputBuffer& context, + const gsl::span buffer, + size_t& written, + const bool append) noexcept +{ + return m_pUsualRoutines->WriteConsoleInputWImpl(context, buffer, written, append); +} + +extern HRESULT _ConvertCellsToWInplace(const UINT codepage, + gsl::span buffer, + const Viewport& rectangle) noexcept; + +[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleOutputAImpl(SCREEN_INFORMATION& context, + gsl::span buffer, + const Microsoft::Console::Types::Viewport& requestRectangle, + Microsoft::Console::Types::Viewport& writtenRectangle) noexcept +{ + // No UTF8 optimization because the entire `CHAR_INFO` grid system doesn't make sense for UTF-8 + // with up to 4 bytes per cell...or more! + + RETURN_IF_FAILED(_ConvertCellsToWInplace(m_outputCodepage, buffer, requestRectangle)); + return WriteConsoleOutputWImpl(context, buffer, requestRectangle, writtenRectangle); +} + +[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleOutputWImpl(SCREEN_INFORMATION& context, + gsl::span buffer, + const Microsoft::Console::Types::Viewport& requestRectangle, + Microsoft::Console::Types::Viewport& writtenRectangle) noexcept +{ + COORD cursor{ requestRectangle.Left(), requestRectangle.Top() }; + + const size_t width = requestRectangle.Width(); + size_t pos = 0; + + while (pos < buffer.size()) + { + (void)m_pVtEngine->_CursorPosition(cursor); + + const auto subspan = buffer.subspan(pos, width); + + for (const auto& ci : subspan) + { + (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(ci.Attributes), true); + (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(ci.Attributes >> 4), false); + (void)m_pVtEngine->WriteTerminalW(std::wstring_view{ &ci.Char.UnicodeChar, 1 }); + } + + ++cursor.Y; + pos += width; + } + + (void)m_pVtEngine->_Flush(); + + //TODO GH10001: trim to buffer size? + writtenRectangle = requestRectangle; + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext, + const gsl::span attrs, + const COORD target, + size_t& used) noexcept +{ + (void)m_pVtEngine->_CursorPosition(target); + + for (const auto& attr : attrs) + { + (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(attr), true); + (void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast(attr >> 4), false); + (void)m_pVtEngine->WriteTerminalUtf8(std::string_view{ &s_readBackAscii.Char.AsciiChar, 1 }); + } + + (void)m_pVtEngine->_Flush(); + + used = attrs.size(); + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleOutputCharacterAImpl(IConsoleOutputObject& OutContext, + const std::string_view text, + const COORD target, + size_t& used) noexcept +{ + if (m_outputCodepage == CP_UTF8) + { + (void)m_pVtEngine->_CursorPosition(target); + (void)m_pVtEngine->WriteTerminalUtf8(text); + (void)m_pVtEngine->_Flush(); + return S_OK; + } + else + { + return WriteConsoleOutputCharacterWImpl(OutContext, ConvertToW(m_outputCodepage, text), target, used); + } +} + +[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleOutputCharacterWImpl(IConsoleOutputObject& OutContext, + const std::wstring_view text, + const COORD target, + size_t& used) noexcept +{ + (void)m_pVtEngine->_CursorPosition(target); + (void)m_pVtEngine->WriteTerminalW(text); + (void)m_pVtEngine->_Flush(); + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleOutputAImpl(const SCREEN_INFORMATION& context, + gsl::span buffer, + const Microsoft::Console::Types::Viewport& sourceRectangle, + Microsoft::Console::Types::Viewport& readRectangle) noexcept +{ + std::fill_n(buffer.data(), buffer.size(), s_readBackAscii); + // TODO GH10001: do we need to constrict readRectangle to within the known buffer size... probably. + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleOutputWImpl(const SCREEN_INFORMATION& context, + gsl::span buffer, + const Microsoft::Console::Types::Viewport& sourceRectangle, + Microsoft::Console::Types::Viewport& readRectangle) noexcept +{ + std::fill_n(buffer.data(), buffer.size(), s_readBackUnicode); + // TODO GH10001: do we need to constrict readRectangle to within the known buffer size... probably. + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleTitleAImpl(gsl::span title, + size_t& written, + size_t& needed) noexcept +{ + written = 0; + needed = 0; + + if (!title.empty()) + { + title.front() = ANSI_NULL; + } + + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleTitleWImpl(gsl::span title, + size_t& written, + size_t& needed) noexcept +{ + written = 0; + needed = 0; + + if (!title.empty()) + { + title.front() = UNICODE_NULL; + } + + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleOriginalTitleAImpl(gsl::span title, + size_t& written, + size_t& needed) noexcept +{ + written = 0; + needed = 0; + + if (!title.empty()) + { + title.front() = ANSI_NULL; + } + + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleOriginalTitleWImpl(gsl::span title, + size_t& written, + size_t& needed) noexcept +{ + written = 0; + needed = 0; + + if (!title.empty()) + { + title.front() = UNICODE_NULL; + } + + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleTitleAImpl(const std::string_view title) noexcept +{ + return SetConsoleTitleWImpl(ConvertToW(m_inputCodepage, title)); +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleTitleWImpl(const std::wstring_view title) noexcept +{ + (void)m_pVtEngine->UpdateTitle(title); + (void)m_pVtEngine->_Flush(); + return S_OK; +} + +void VtApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept +{ + buttons = 2; + return; +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleFontSizeImpl(const SCREEN_INFORMATION& context, + const DWORD index, + COORD& size) noexcept +{ + size.X = 8; + size.Y = 12; + return S_OK; +} + +//// driver will pare down for non-Ex method +[[nodiscard]] HRESULT VtApiRoutines::GetCurrentConsoleFontExImpl(const SCREEN_INFORMATION& context, + const bool isForMaximumWindowSize, + CONSOLE_FONT_INFOEX& consoleFontInfoEx) noexcept +{ + return S_OK; +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleDisplayModeImpl(SCREEN_INFORMATION& context, + const ULONG flags, + COORD& newSize) noexcept +{ + return S_OK; +} + +void VtApiRoutines::GetConsoleDisplayModeImpl(ULONG& flags) noexcept +{ + flags = 0; + return; +} + +[[nodiscard]] HRESULT VtApiRoutines::AddConsoleAliasAImpl(const std::string_view source, + const std::string_view target, + const std::string_view exeName) noexcept +{ + return m_pUsualRoutines->AddConsoleAliasAImpl(source, target, exeName); +} + +[[nodiscard]] HRESULT VtApiRoutines::AddConsoleAliasWImpl(const std::wstring_view source, + const std::wstring_view target, + const std::wstring_view exeName) noexcept +{ + return m_pUsualRoutines->AddConsoleAliasWImpl(source, target, exeName); +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasAImpl(const std::string_view source, + gsl::span target, + size_t& written, + const std::string_view exeName) noexcept +{ + return m_pUsualRoutines->GetConsoleAliasAImpl(source, target, written, exeName); +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasWImpl(const std::wstring_view source, + gsl::span target, + size_t& written, + const std::wstring_view exeName) noexcept +{ + return m_pUsualRoutines->GetConsoleAliasWImpl(source, target, written, exeName); +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasesLengthAImpl(const std::string_view exeName, + size_t& bufferRequired) noexcept +{ + return m_pUsualRoutines->GetConsoleAliasesLengthAImpl(exeName, bufferRequired); +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasesLengthWImpl(const std::wstring_view exeName, + size_t& bufferRequired) noexcept +{ + return m_pUsualRoutines->GetConsoleAliasesLengthWImpl(exeName, bufferRequired); +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasExesLengthAImpl(size_t& bufferRequired) noexcept +{ + return m_pUsualRoutines->GetConsoleAliasExesLengthAImpl(bufferRequired); +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasExesLengthWImpl(size_t& bufferRequired) noexcept +{ + return m_pUsualRoutines->GetConsoleAliasExesLengthWImpl(bufferRequired); +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasesAImpl(const std::string_view exeName, + gsl::span alias, + size_t& written) noexcept +{ + return m_pUsualRoutines->GetConsoleAliasesAImpl(exeName, alias, written); +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasesWImpl(const std::wstring_view exeName, + gsl::span alias, + size_t& written) noexcept +{ + return m_pUsualRoutines->GetConsoleAliasesWImpl(exeName, alias, written); +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasExesAImpl(gsl::span aliasExes, + size_t& written) noexcept +{ + return m_pUsualRoutines->GetConsoleAliasExesAImpl(aliasExes, written); +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasExesWImpl(gsl::span aliasExes, + size_t& written) noexcept +{ + return m_pUsualRoutines->GetConsoleAliasExesWImpl(aliasExes, written); +} + +[[nodiscard]] HRESULT VtApiRoutines::ExpungeConsoleCommandHistoryAImpl(const std::string_view exeName) noexcept +{ + return m_pUsualRoutines->ExpungeConsoleCommandHistoryAImpl(exeName); +} + +[[nodiscard]] HRESULT VtApiRoutines::ExpungeConsoleCommandHistoryWImpl(const std::wstring_view exeName) noexcept +{ + return m_pUsualRoutines->ExpungeConsoleCommandHistoryWImpl(exeName); +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleNumberOfCommandsAImpl(const std::string_view exeName, + const size_t numberOfCommands) noexcept +{ + return m_pUsualRoutines->SetConsoleNumberOfCommandsAImpl(exeName, numberOfCommands); +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleNumberOfCommandsWImpl(const std::wstring_view exeName, + const size_t numberOfCommands) noexcept +{ + return m_pUsualRoutines->SetConsoleNumberOfCommandsWImpl(exeName, numberOfCommands); +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleCommandHistoryLengthAImpl(const std::string_view exeName, + size_t& length) noexcept +{ + return m_pUsualRoutines->GetConsoleCommandHistoryLengthAImpl(exeName, length); +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleCommandHistoryLengthWImpl(const std::wstring_view exeName, + size_t& length) noexcept +{ + return m_pUsualRoutines->GetConsoleCommandHistoryLengthWImpl(exeName, length); +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleCommandHistoryAImpl(const std::string_view exeName, + gsl::span commandHistory, + size_t& written) noexcept +{ + return m_pUsualRoutines->GetConsoleCommandHistoryAImpl(exeName, commandHistory, written); +} + +[[nodiscard]] HRESULT VtApiRoutines::GetConsoleCommandHistoryWImpl(const std::wstring_view exeName, + gsl::span commandHistory, + size_t& written) noexcept +{ + return m_pUsualRoutines->GetConsoleCommandHistoryWImpl(exeName, commandHistory, written); +} + +void VtApiRoutines::GetConsoleWindowImpl(HWND& hwnd) noexcept +{ + hwnd = ServiceLocator::LocatePseudoWindow(); + return; +} + +void VtApiRoutines::GetConsoleSelectionInfoImpl(CONSOLE_SELECTION_INFO& consoleSelectionInfo) noexcept +{ + consoleSelectionInfo = { 0 }; + return; +} + +void VtApiRoutines::GetConsoleHistoryInfoImpl(CONSOLE_HISTORY_INFO& consoleHistoryInfo) noexcept +{ + m_pUsualRoutines->GetConsoleHistoryInfoImpl(consoleHistoryInfo); + return; +} + +[[nodiscard]] HRESULT VtApiRoutines::SetConsoleHistoryInfoImpl(const CONSOLE_HISTORY_INFO& consoleHistoryInfo) noexcept +{ + return m_pUsualRoutines->SetConsoleHistoryInfoImpl(consoleHistoryInfo); +} + +[[nodiscard]] HRESULT VtApiRoutines::SetCurrentConsoleFontExImpl(IConsoleOutputObject& context, + const bool isForMaximumWindowSize, + const CONSOLE_FONT_INFOEX& consoleFontInfoEx) noexcept +{ + return S_OK; +} + +#pragma warning(pop) diff --git a/src/host/VtApiRoutines.h b/src/host/VtApiRoutines.h new file mode 100644 index 00000000000..ef6dd351e80 --- /dev/null +++ b/src/host/VtApiRoutines.h @@ -0,0 +1,392 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- VtApiRoutines.h + +Abstract: +- This file defines the interface to respond to all API calls by using VT on behalf of the client + +Author: +- Michael Niksa (miniksa) 26-Jul-2021 + +Revision History: +- Adapted from original items in srvinit.cpp, getset.cpp, directio.cpp, stream.cpp +--*/ + +#pragma once + +#include "../server/IApiRoutines.h" +#include "../renderer/vt/Xterm256Engine.hpp" + +class VtApiRoutines : public IApiRoutines +{ +public: + VtApiRoutines(); + +#pragma region ObjectManagement + /*HRESULT CreateInitialObjects(_Out_ InputBuffer** const ppInputObject, + _Out_ SCREEN_INFORMATION** const ppOutputObject); + */ + +#pragma endregion + +#pragma region L1 + void GetConsoleInputCodePageImpl(ULONG& codepage) noexcept override; + + void GetConsoleOutputCodePageImpl(ULONG& codepage) noexcept override; + + void GetConsoleInputModeImpl(InputBuffer& context, + ULONG& mode) noexcept override; + + void GetConsoleOutputModeImpl(SCREEN_INFORMATION& context, + ULONG& mode) noexcept override; + + [[nodiscard]] HRESULT SetConsoleInputModeImpl(InputBuffer& context, + const ULONG mode) noexcept override; + + [[nodiscard]] HRESULT SetConsoleOutputModeImpl(SCREEN_INFORMATION& context, + const ULONG Mode) noexcept override; + + [[nodiscard]] HRESULT GetNumberOfConsoleInputEventsImpl(const InputBuffer& context, + ULONG& events) noexcept override; + + [[nodiscard]] HRESULT PeekConsoleInputAImpl(IConsoleInputObject& context, + std::deque>& outEvents, + const size_t eventsToRead, + INPUT_READ_HANDLE_DATA& readHandleState, + std::unique_ptr& waiter) noexcept override; + + [[nodiscard]] HRESULT PeekConsoleInputWImpl(IConsoleInputObject& context, + std::deque>& outEvents, + const size_t eventsToRead, + INPUT_READ_HANDLE_DATA& readHandleState, + std::unique_ptr& waiter) noexcept override; + + [[nodiscard]] HRESULT ReadConsoleInputAImpl(IConsoleInputObject& context, + std::deque>& outEvents, + const size_t eventsToRead, + INPUT_READ_HANDLE_DATA& readHandleState, + std::unique_ptr& waiter) noexcept override; + + [[nodiscard]] HRESULT ReadConsoleInputWImpl(IConsoleInputObject& context, + std::deque>& outEvents, + const size_t eventsToRead, + INPUT_READ_HANDLE_DATA& readHandleState, + std::unique_ptr& waiter) noexcept override; + + [[nodiscard]] HRESULT ReadConsoleAImpl(IConsoleInputObject& context, + gsl::span buffer, + size_t& written, + std::unique_ptr& waiter, + const std::string_view initialData, + const std::wstring_view exeName, + INPUT_READ_HANDLE_DATA& readHandleState, + const HANDLE clientHandle, + const DWORD controlWakeupMask, + DWORD& controlKeyState) noexcept override; + + [[nodiscard]] HRESULT ReadConsoleWImpl(IConsoleInputObject& context, + gsl::span buffer, + size_t& written, + std::unique_ptr& waiter, + const std::string_view initialData, + const std::wstring_view exeName, + INPUT_READ_HANDLE_DATA& readHandleState, + const HANDLE clientHandle, + const DWORD controlWakeupMask, + DWORD& controlKeyState) noexcept override; + + [[nodiscard]] HRESULT WriteConsoleAImpl(IConsoleOutputObject& context, + const std::string_view buffer, + size_t& read, + bool requiresVtQuirk, + std::unique_ptr& waiter) noexcept override; + + [[nodiscard]] HRESULT WriteConsoleWImpl(IConsoleOutputObject& context, + const std::wstring_view buffer, + size_t& read, + bool requiresVtQuirk, + std::unique_ptr& waiter) noexcept override; + +#pragma region ThreadCreationInfo + [[nodiscard]] HRESULT GetConsoleLangIdImpl(LANGID& langId) noexcept override; +#pragma endregion + +#pragma endregion + +#pragma region L2 + + [[nodiscard]] HRESULT FillConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext, + const WORD attribute, + const size_t lengthToWrite, + const COORD startingCoordinate, + size_t& cellsModified) noexcept override; + + [[nodiscard]] HRESULT FillConsoleOutputCharacterAImpl(IConsoleOutputObject& OutContext, + const char character, + const size_t lengthToWrite, + const COORD startingCoordinate, + size_t& cellsModified) noexcept override; + + [[nodiscard]] HRESULT FillConsoleOutputCharacterWImpl(IConsoleOutputObject& OutContext, + const wchar_t character, + const size_t lengthToWrite, + const COORD startingCoordinate, + size_t& cellsModified, + const bool enablePowershellShim = false) noexcept override; + + //// Process based. Restrict in protocol side? + //HRESULT GenerateConsoleCtrlEventImpl(const ULONG ProcessGroupFilter, + // const ULONG ControlEvent); + + void SetConsoleActiveScreenBufferImpl(SCREEN_INFORMATION& newContext) noexcept override; + + void FlushConsoleInputBuffer(InputBuffer& context) noexcept override; + + [[nodiscard]] HRESULT SetConsoleInputCodePageImpl(const ULONG codepage) noexcept override; + + [[nodiscard]] HRESULT SetConsoleOutputCodePageImpl(const ULONG codepage) noexcept override; + + void GetConsoleCursorInfoImpl(const SCREEN_INFORMATION& context, + ULONG& size, + bool& isVisible) noexcept override; + + [[nodiscard]] HRESULT SetConsoleCursorInfoImpl(SCREEN_INFORMATION& context, + const ULONG size, + const bool isVisible) noexcept override; + + //// driver will pare down for non-Ex method + void GetConsoleScreenBufferInfoExImpl(const SCREEN_INFORMATION& context, + CONSOLE_SCREEN_BUFFER_INFOEX& data) noexcept override; + + [[nodiscard]] HRESULT SetConsoleScreenBufferInfoExImpl(SCREEN_INFORMATION& context, + const CONSOLE_SCREEN_BUFFER_INFOEX& data) noexcept override; + + [[nodiscard]] HRESULT SetConsoleScreenBufferSizeImpl(SCREEN_INFORMATION& context, + const COORD size) noexcept override; + + [[nodiscard]] HRESULT SetConsoleCursorPositionImpl(SCREEN_INFORMATION& context, + const COORD position) noexcept override; + + void GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& context, + COORD& size) noexcept override; + + [[nodiscard]] HRESULT ScrollConsoleScreenBufferAImpl(SCREEN_INFORMATION& context, + const SMALL_RECT& source, + const COORD target, + std::optional clip, + const char fillCharacter, + const WORD fillAttribute) noexcept override; + + [[nodiscard]] HRESULT ScrollConsoleScreenBufferWImpl(SCREEN_INFORMATION& context, + const SMALL_RECT& source, + const COORD target, + std::optional clip, + const wchar_t fillCharacter, + const WORD fillAttribute, + const bool enableCmdShim = false) noexcept override; + + [[nodiscard]] HRESULT SetConsoleTextAttributeImpl(SCREEN_INFORMATION& context, + const WORD attribute) noexcept override; + + [[nodiscard]] HRESULT SetConsoleWindowInfoImpl(SCREEN_INFORMATION& context, + const bool isAbsolute, + const SMALL_RECT& windowRect) noexcept override; + + [[nodiscard]] HRESULT ReadConsoleOutputAttributeImpl(const SCREEN_INFORMATION& context, + const COORD origin, + gsl::span buffer, + size_t& written) noexcept override; + + [[nodiscard]] HRESULT ReadConsoleOutputCharacterAImpl(const SCREEN_INFORMATION& context, + const COORD origin, + gsl::span buffer, + size_t& written) noexcept override; + + [[nodiscard]] HRESULT ReadConsoleOutputCharacterWImpl(const SCREEN_INFORMATION& context, + const COORD origin, + gsl::span buffer, + size_t& written) noexcept override; + + [[nodiscard]] HRESULT WriteConsoleInputAImpl(InputBuffer& context, + const gsl::span buffer, + size_t& written, + const bool append) noexcept override; + + [[nodiscard]] HRESULT WriteConsoleInputWImpl(InputBuffer& context, + const gsl::span buffer, + size_t& written, + const bool append) noexcept override; + + [[nodiscard]] HRESULT WriteConsoleOutputAImpl(SCREEN_INFORMATION& context, + gsl::span buffer, + const Microsoft::Console::Types::Viewport& requestRectangle, + Microsoft::Console::Types::Viewport& writtenRectangle) noexcept override; + + [[nodiscard]] HRESULT WriteConsoleOutputWImpl(SCREEN_INFORMATION& context, + gsl::span buffer, + const Microsoft::Console::Types::Viewport& requestRectangle, + Microsoft::Console::Types::Viewport& writtenRectangle) noexcept override; + + [[nodiscard]] HRESULT WriteConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext, + const gsl::span attrs, + const COORD target, + size_t& used) noexcept override; + + [[nodiscard]] HRESULT WriteConsoleOutputCharacterAImpl(IConsoleOutputObject& OutContext, + const std::string_view text, + const COORD target, + size_t& used) noexcept override; + + [[nodiscard]] HRESULT WriteConsoleOutputCharacterWImpl(IConsoleOutputObject& OutContext, + const std::wstring_view text, + const COORD target, + size_t& used) noexcept override; + + [[nodiscard]] HRESULT ReadConsoleOutputAImpl(const SCREEN_INFORMATION& context, + gsl::span buffer, + const Microsoft::Console::Types::Viewport& sourceRectangle, + Microsoft::Console::Types::Viewport& readRectangle) noexcept override; + + [[nodiscard]] HRESULT ReadConsoleOutputWImpl(const SCREEN_INFORMATION& context, + gsl::span buffer, + const Microsoft::Console::Types::Viewport& sourceRectangle, + Microsoft::Console::Types::Viewport& readRectangle) noexcept override; + + [[nodiscard]] HRESULT GetConsoleTitleAImpl(gsl::span title, + size_t& written, + size_t& needed) noexcept override; + + [[nodiscard]] HRESULT GetConsoleTitleWImpl(gsl::span title, + size_t& written, + size_t& needed) noexcept override; + + [[nodiscard]] HRESULT GetConsoleOriginalTitleAImpl(gsl::span title, + size_t& written, + size_t& needed) noexcept override; + + [[nodiscard]] HRESULT GetConsoleOriginalTitleWImpl(gsl::span title, + size_t& written, + size_t& needed) noexcept override; + + [[nodiscard]] HRESULT SetConsoleTitleAImpl(const std::string_view title) noexcept override; + + [[nodiscard]] HRESULT SetConsoleTitleWImpl(const std::wstring_view title) noexcept override; + +#pragma endregion + +#pragma region L3 + void GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept override; + + [[nodiscard]] HRESULT GetConsoleFontSizeImpl(const SCREEN_INFORMATION& context, + const DWORD index, + COORD& size) noexcept override; + + //// driver will pare down for non-Ex method + [[nodiscard]] HRESULT GetCurrentConsoleFontExImpl(const SCREEN_INFORMATION& context, + const bool isForMaximumWindowSize, + CONSOLE_FONT_INFOEX& consoleFontInfoEx) noexcept override; + + [[nodiscard]] HRESULT SetConsoleDisplayModeImpl(SCREEN_INFORMATION& context, + const ULONG flags, + COORD& newSize) noexcept override; + + void GetConsoleDisplayModeImpl(ULONG& flags) noexcept override; + + [[nodiscard]] HRESULT AddConsoleAliasAImpl(const std::string_view source, + const std::string_view target, + const std::string_view exeName) noexcept override; + + [[nodiscard]] HRESULT AddConsoleAliasWImpl(const std::wstring_view source, + const std::wstring_view target, + const std::wstring_view exeName) noexcept override; + + [[nodiscard]] HRESULT GetConsoleAliasAImpl(const std::string_view source, + gsl::span target, + size_t& written, + const std::string_view exeName) noexcept override; + + [[nodiscard]] HRESULT GetConsoleAliasWImpl(const std::wstring_view source, + gsl::span target, + size_t& written, + const std::wstring_view exeName) noexcept override; + + [[nodiscard]] HRESULT GetConsoleAliasesLengthAImpl(const std::string_view exeName, + size_t& bufferRequired) noexcept override; + + [[nodiscard]] HRESULT GetConsoleAliasesLengthWImpl(const std::wstring_view exeName, + size_t& bufferRequired) noexcept override; + + [[nodiscard]] HRESULT GetConsoleAliasExesLengthAImpl(size_t& bufferRequired) noexcept override; + + [[nodiscard]] HRESULT GetConsoleAliasExesLengthWImpl(size_t& bufferRequired) noexcept override; + + [[nodiscard]] HRESULT GetConsoleAliasesAImpl(const std::string_view exeName, + gsl::span alias, + size_t& written) noexcept override; + + [[nodiscard]] HRESULT GetConsoleAliasesWImpl(const std::wstring_view exeName, + gsl::span alias, + size_t& written) noexcept override; + + [[nodiscard]] HRESULT GetConsoleAliasExesAImpl(gsl::span aliasExes, + size_t& written) noexcept override; + + [[nodiscard]] HRESULT GetConsoleAliasExesWImpl(gsl::span aliasExes, + size_t& written) noexcept override; + +#pragma region CMDext Private API + + [[nodiscard]] HRESULT ExpungeConsoleCommandHistoryAImpl(const std::string_view exeName) noexcept override; + + [[nodiscard]] HRESULT ExpungeConsoleCommandHistoryWImpl(const std::wstring_view exeName) noexcept override; + + [[nodiscard]] HRESULT SetConsoleNumberOfCommandsAImpl(const std::string_view exeName, + const size_t numberOfCommands) noexcept override; + + [[nodiscard]] HRESULT SetConsoleNumberOfCommandsWImpl(const std::wstring_view exeName, + const size_t numberOfCommands) noexcept override; + + [[nodiscard]] HRESULT GetConsoleCommandHistoryLengthAImpl(const std::string_view exeName, + size_t& length) noexcept override; + + [[nodiscard]] HRESULT GetConsoleCommandHistoryLengthWImpl(const std::wstring_view exeName, + size_t& length) noexcept override; + + [[nodiscard]] HRESULT GetConsoleCommandHistoryAImpl(const std::string_view exeName, + gsl::span commandHistory, + size_t& written) noexcept override; + + [[nodiscard]] HRESULT GetConsoleCommandHistoryWImpl(const std::wstring_view exeName, + gsl::span commandHistory, + size_t& written) noexcept override; + +#pragma endregion + + void GetConsoleWindowImpl(HWND& hwnd) noexcept override; + + void GetConsoleSelectionInfoImpl(CONSOLE_SELECTION_INFO& consoleSelectionInfo) noexcept override; + + void GetConsoleHistoryInfoImpl(CONSOLE_HISTORY_INFO& consoleHistoryInfo) noexcept override; + + [[nodiscard]] HRESULT SetConsoleHistoryInfoImpl(const CONSOLE_HISTORY_INFO& consoleHistoryInfo) noexcept override; + + [[nodiscard]] HRESULT SetCurrentConsoleFontExImpl(IConsoleOutputObject& context, + const bool isForMaximumWindowSize, + const CONSOLE_FONT_INFOEX& consoleFontInfoEx) noexcept override; + +#pragma endregion + + IApiRoutines* m_pUsualRoutines; + UINT& m_inputCodepage; + UINT& m_outputCodepage; + ULONG m_inputMode; + ULONG m_outputMode; + bool m_listeningForDSR; + Microsoft::Console::Render::Xterm256Engine* m_pVtEngine; + +private: + void _SynchronizeCursor(std::unique_ptr& waiter) noexcept; +}; diff --git a/src/host/VtInputThread.cpp b/src/host/VtInputThread.cpp index c5a4501a869..e5a7be636ae 100644 --- a/src/host/VtInputThread.cpp +++ b/src/host/VtInputThread.cpp @@ -31,7 +31,8 @@ VtInputThread::VtInputThread(_In_ wil::unique_hfile hPipe, _u8State{}, _dwThreadId{ 0 }, _exitRequested{ false }, - _exitResult{ S_OK } + _exitResult{ S_OK }, + _pfnSetLookingForDSR{} { THROW_HR_IF(E_HANDLE, _hFile.get() == INVALID_HANDLE_VALUE); @@ -50,6 +51,9 @@ VtInputThread::VtInputThread(_In_ wil::unique_hfile hPipe, // 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); + + // we need this callback to capture the reply if someone requests a status from the terminal + _pfnSetLookingForDSR = std::bind(&InputStateMachineEngine::SetLookingForDSR, engineRef, std::placeholders::_1); } // Method Description: @@ -138,6 +142,14 @@ void VtInputThread::DoReadInput(const bool throwOnFail) } } +void VtInputThread::SetLookingForDSR(const bool looking) noexcept +{ + if (_pfnSetLookingForDSR) + { + _pfnSetLookingForDSR(looking); + } +} + // Method Description: // - The ThreadProc for the VT Input Thread. Reads input from the pipe, and // passes it to _HandleRunInput to be processed by the diff --git a/src/host/VtInputThread.hpp b/src/host/VtInputThread.hpp index da3f08a7a0b..9d54d6bbf8d 100644 --- a/src/host/VtInputThread.hpp +++ b/src/host/VtInputThread.hpp @@ -26,6 +26,7 @@ namespace Microsoft::Console [[nodiscard]] HRESULT Start(); static DWORD WINAPI StaticVtInputThreadProc(_In_ LPVOID lpParameter); void DoReadInput(const bool throwOnFail); + void SetLookingForDSR(const bool looking) noexcept; private: [[nodiscard]] HRESULT _HandleRunInput(const std::string_view u8Str); @@ -38,6 +39,8 @@ namespace Microsoft::Console bool _exitRequested; HRESULT _exitResult; + std::function _pfnSetLookingForDSR; + std::unique_ptr _pInputStateMachine; til::u8state _u8State; }; diff --git a/src/host/VtIo.cpp b/src/host/VtIo.cpp index 5b482ecd185..6863ef080a6 100644 --- a/src/host/VtIo.cpp +++ b/src/host/VtIo.cpp @@ -14,6 +14,8 @@ #include "input.h" // ProcessCtrlEvents #include "output.h" // CloseConsoleProcessState +#include "VtApiRoutines.h" + using namespace Microsoft::Console; using namespace Microsoft::Console::Render; using namespace Microsoft::Console::VirtualTerminal; @@ -71,6 +73,7 @@ VtIo::VtIo() : _lookingForCursorPosition = pArgs->GetInheritCursor(); _resizeQuirk = pArgs->IsResizeQuirkEnabled(); _win32InputMode = pArgs->IsWin32InputModeEnabled(); + _passthroughMode = pArgs->IsPassthroughMode(); // If we were already given VT handles, set up the VT IO engine to use those. if (pArgs->InConptyMode()) @@ -137,8 +140,9 @@ VtIo::VtIo() : { return S_FALSE; } + auto& globals = ServiceLocator::LocateGlobals(); - const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + const CONSOLE_INFORMATION& gci = globals.getConsoleInformation(); try { @@ -155,22 +159,60 @@ VtIo::VtIo() : switch (_IoMode) { case VtIoMode::XTERM_256: - _pVtRenderEngine = std::make_unique(std::move(_hOutput), - initialViewport); + { + auto xterm256Engine = std::make_unique(std::move(_hOutput), + initialViewport); + if constexpr (Feature_VtPassthroughMode::IsEnabled()) + { + if (_passthroughMode) + { + auto vtapi = new VtApiRoutines(); + vtapi->m_pVtEngine = xterm256Engine.get(); + vtapi->m_pUsualRoutines = globals.api; + + xterm256Engine->SetPassthroughMode(true); + + if (_pVtInputThread) + { + auto pfnSetListenForDSR = std::bind(&VtInputThread::SetLookingForDSR, _pVtInputThread.get(), std::placeholders::_1); + xterm256Engine->SetLookingForDSRCallback(pfnSetListenForDSR); + } + + globals.api = vtapi; + } + } + + _pVtRenderEngine = std::move(xterm256Engine); break; + } case VtIoMode::XTERM: + { _pVtRenderEngine = std::make_unique(std::move(_hOutput), initialViewport, false); + if (_passthroughMode) + { + return E_NOTIMPL; + } break; + } case VtIoMode::XTERM_ASCII: + { _pVtRenderEngine = std::make_unique(std::move(_hOutput), initialViewport, true); + + if (_passthroughMode) + { + return E_NOTIMPL; + } break; + } default: + { return E_FAIL; } + } if (_pVtRenderEngine) { _pVtRenderEngine->SetTerminalOwner(this); diff --git a/src/host/VtIo.hpp b/src/host/VtIo.hpp index d3d3410ec1b..6c204d6cf44 100644 --- a/src/host/VtIo.hpp +++ b/src/host/VtIo.hpp @@ -66,6 +66,7 @@ namespace Microsoft::Console::VirtualTerminal bool _resizeQuirk{ false }; bool _win32InputMode{ false }; + bool _passthroughMode{ false }; std::unique_ptr _pVtRenderEngine; std::unique_ptr _pVtInputThread; diff --git a/src/host/directio.cpp b/src/host/directio.cpp index dd060156621..cc6d26fd153 100644 --- a/src/host/directio.cpp +++ b/src/host/directio.cpp @@ -604,9 +604,9 @@ void EventsToUnicode(_Inout_ std::deque>& inEvents, // - rectangle - This is the rectangle describing the region that the buffer covers. // Return Value: // - Generally S_OK. Could be a memory or math error code. -[[nodiscard]] static HRESULT _ConvertCellsToWInplace(const UINT codepage, - gsl::span buffer, - const Viewport& rectangle) noexcept +[[nodiscard]] HRESULT _ConvertCellsToWInplace(const UINT codepage, + gsl::span buffer, + const Viewport& rectangle) noexcept { try { diff --git a/src/host/globals.cpp b/src/host/globals.cpp index 6cbff4b52d1..d9450f0ce92 100644 --- a/src/host/globals.cpp +++ b/src/host/globals.cpp @@ -7,6 +7,11 @@ #pragma hdrstop +Globals::Globals() +{ + api = &defaultApiRoutines; +} + CONSOLE_INFORMATION& Globals::getConsoleInformation() { return ciConsoleInformation; diff --git a/src/host/globals.h b/src/host/globals.h index 3b116ea8ef5..50e42c82d5c 100644 --- a/src/host/globals.h +++ b/src/host/globals.h @@ -34,6 +34,8 @@ TRACELOGGING_DECLARE_PROVIDER(g_hConhostV2EventTraceProvider); class Globals { public: + Globals(); + UINT uiOEMCP = GetOEMCP(); UINT uiWindowsCP = GetACP(); HINSTANCE hInstance; @@ -65,7 +67,7 @@ class Globals bool IsHeadless() const; - ApiRoutines api; + IApiRoutines* api; bool handoffTarget = false; @@ -80,4 +82,5 @@ class Globals private: CONSOLE_INFORMATION ciConsoleInformation; + ApiRoutines defaultApiRoutines; }; diff --git a/src/host/host-common.vcxitems b/src/host/host-common.vcxitems index 46acdde1aea..fdd645c5940 100644 --- a/src/host/host-common.vcxitems +++ b/src/host/host-common.vcxitems @@ -52,6 +52,7 @@ + @@ -110,6 +111,7 @@ + diff --git a/src/host/lib/hostlib.vcxproj.filters b/src/host/lib/hostlib.vcxproj.filters index b8b3df1e25b..fb42149b626 100644 --- a/src/host/lib/hostlib.vcxproj.filters +++ b/src/host/lib/hostlib.vcxproj.filters @@ -177,6 +177,9 @@ Source Files + + Source Files + @@ -347,8 +350,11 @@ Header Files + + Header Files + - \ No newline at end of file + diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index cbae4519159..1b3ba0a2608 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -65,7 +65,7 @@ void ConhostInternalGetSet::PrintString(const std::wstring_view string) // - void ConhostInternalGetSet::GetConsoleScreenBufferInfoEx(CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) const { - ServiceLocator::LocateGlobals().api.GetConsoleScreenBufferInfoExImpl(_io.GetActiveOutputBuffer(), screenBufferInfo); + ServiceLocator::LocateGlobals().api->GetConsoleScreenBufferInfoExImpl(_io.GetActiveOutputBuffer(), screenBufferInfo); } // Routine Description: @@ -76,7 +76,7 @@ void ConhostInternalGetSet::GetConsoleScreenBufferInfoEx(CONSOLE_SCREEN_BUFFER_I // - void ConhostInternalGetSet::SetConsoleScreenBufferInfoEx(const CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) { - THROW_IF_FAILED(ServiceLocator::LocateGlobals().api.SetConsoleScreenBufferInfoExImpl(_io.GetActiveOutputBuffer(), screenBufferInfo)); + THROW_IF_FAILED(ServiceLocator::LocateGlobals().api->SetConsoleScreenBufferInfoExImpl(_io.GetActiveOutputBuffer(), screenBufferInfo)); } // Routine Description: @@ -89,7 +89,7 @@ void ConhostInternalGetSet::SetCursorPosition(const COORD position) { auto& info = _io.GetActiveOutputBuffer(); const auto clampedPosition = info.GetTextBuffer().ClampPositionWithinLine(position); - THROW_IF_FAILED(ServiceLocator::LocateGlobals().api.SetConsoleCursorPositionImpl(info, clampedPosition)); + THROW_IF_FAILED(ServiceLocator::LocateGlobals().api->SetConsoleCursorPositionImpl(info, clampedPosition)); } // Method Description: @@ -177,7 +177,7 @@ void ConhostInternalGetSet::WriteInput(std::deque>& // - void ConhostInternalGetSet::SetWindowInfo(const bool absolute, const SMALL_RECT& window) { - THROW_IF_FAILED(ServiceLocator::LocateGlobals().api.SetConsoleWindowInfoImpl(_io.GetActiveOutputBuffer(), absolute, window)); + THROW_IF_FAILED(ServiceLocator::LocateGlobals().api->SetConsoleWindowInfoImpl(_io.GetActiveOutputBuffer(), absolute, window)); } // Routine Description: diff --git a/src/host/popup.cpp b/src/host/popup.cpp index 34683011145..4d4020ee7db 100644 --- a/src/host/popup.cpp +++ b/src/host/popup.cpp @@ -169,10 +169,10 @@ void Popup::_DrawPrompt(const UINT id) } size_t used; - LOG_IF_FAILED(ServiceLocator::LocateGlobals().api.WriteConsoleOutputCharacterWImpl(_screenInfo, - text, - WriteCoord, - used)); + LOG_IF_FAILED(ServiceLocator::LocateGlobals().api->WriteConsoleOutputCharacterWImpl(_screenInfo, + text, + WriteCoord, + used)); } // Routine Description: diff --git a/src/host/sources.inc b/src/host/sources.inc index aa7eae927dd..c77653b8bf2 100644 --- a/src/host/sources.inc +++ b/src/host/sources.inc @@ -93,6 +93,7 @@ SOURCES = \ ..\CommandListPopup.cpp \ ..\CopyFromCharPopup.cpp \ ..\CopyToCharPopup.cpp \ + ..\VtApiRoutines.cpp \ # ------------------------------------- diff --git a/src/host/srvinit.cpp b/src/host/srvinit.cpp index 055c9ec5546..6332b1bc7e8 100644 --- a/src/host/srvinit.cpp +++ b/src/host/srvinit.cpp @@ -944,7 +944,7 @@ DWORD WINAPI ConsoleIoThread(LPVOID lpParameter) auto& globals = ServiceLocator::LocateGlobals(); CONSOLE_API_MSG ReceiveMsg; - ReceiveMsg._pApiRoutines = &globals.api; + ReceiveMsg._pApiRoutines = globals.api; ReceiveMsg._pDeviceComm = globals.pDeviceComm; PCONSOLE_API_MSG ReplyMsg = nullptr; @@ -956,7 +956,7 @@ DWORD WINAPI ConsoleIoThread(LPVOID lpParameter) std::unique_ptr capturedMessage{ static_cast(lpParameter) }; ReceiveMsg = *capturedMessage.get(); - ReceiveMsg._pApiRoutines = &globals.api; + ReceiveMsg._pApiRoutines = globals.api; ReceiveMsg._pDeviceComm = globals.pDeviceComm; IoSorter::ServiceIoOperation(&ReceiveMsg, &ReplyMsg); } @@ -984,7 +984,7 @@ DWORD WINAPI ConsoleIoThread(LPVOID lpParameter) ReplyMsg = nullptr; continue; } - + ReceiveMsg._pApiRoutines = globals.api; IoSorter::ServiceIoOperation(&ReceiveMsg, &ReplyMsg); } diff --git a/src/host/ut_host/ConsoleArgumentsTests.cpp b/src/host/ut_host/ConsoleArgumentsTests.cpp index 69ab4debe81..e264e264123 100644 --- a/src/host/ut_host/ConsoleArgumentsTests.cpp +++ b/src/host/ut_host/ConsoleArgumentsTests.cpp @@ -83,7 +83,8 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe \"this is the commandline\""; @@ -105,7 +106,8 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --headless \"--vtmode bar this is the commandline\""; @@ -127,7 +129,8 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --headless --server 0x4 this is the commandline"; @@ -149,7 +152,8 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0x4, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --headless\t--vtmode\txterm\tthis\tis\tthe\tcommandline"; @@ -171,7 +175,8 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --headless\\ foo\\ --outpipe\\ bar\\ this\\ is\\ the\\ commandline"; @@ -193,7 +198,8 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --headless\\\tfoo\\\t--outpipe\\\tbar\\\tthis\\\tis\\\tthe\\\tcommandline"; @@ -215,7 +221,8 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --vtmode a\\\\\\\\\"b c\" d e"; @@ -237,7 +244,8 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe this is the commandline"; @@ -259,7 +267,8 @@ void ConsoleArgumentsTests::ArgSplittingTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? } @@ -286,7 +295,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe foo"; @@ -308,7 +318,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe foo -- bar"; @@ -330,7 +341,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --vtmode foo foo -- bar"; @@ -352,7 +364,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe console --vtmode foo foo -- bar"; @@ -374,7 +387,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe console --vtmode foo --outpipe foo -- bar"; @@ -396,7 +410,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --vtmode foo -- --outpipe foo bar"; @@ -418,7 +433,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --vtmode -- --headless bar"; @@ -440,7 +456,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --"; @@ -462,7 +479,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe"; @@ -484,7 +502,8 @@ void ConsoleArgumentsTests::ClientCommandlineTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? } @@ -511,7 +530,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --server 0x4"; @@ -533,7 +553,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe 0x4 0x8"; @@ -555,7 +576,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode false); // successful parse? commandline = L"conhost.exe --server 0x4 0x8"; @@ -577,7 +599,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode false); // successful parse? commandline = L"conhost.exe 0x4 --server 0x8"; @@ -599,7 +622,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode false); // successful parse? commandline = L"conhost.exe --server 0x4 --server 0x8"; @@ -621,7 +645,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode false); // successful parse? commandline = L"conhost.exe 0x4 -ForceV1"; @@ -643,7 +668,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe -ForceV1"; @@ -665,7 +691,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe 0x4 -ForceNoHandoff"; @@ -687,7 +714,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe -ForceNoHandoff"; @@ -709,7 +737,8 @@ void ConsoleArgumentsTests::LegacyFormatsTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? } @@ -760,7 +789,8 @@ void ConsoleArgumentsTests::CombineVtPipeHandleTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --vtmode xterm-256color"; @@ -782,7 +812,8 @@ void ConsoleArgumentsTests::CombineVtPipeHandleTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? } @@ -819,7 +850,8 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --width 120"; @@ -841,7 +873,8 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --height 30"; @@ -863,7 +896,8 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --width 0"; @@ -885,7 +919,8 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --width -1"; @@ -907,7 +942,8 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --width foo"; @@ -929,7 +965,8 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode false); // successful parse? commandline = L"conhost.exe --width 2foo"; @@ -951,7 +988,8 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode false); // successful parse? commandline = L"conhost.exe --width 65535"; @@ -973,7 +1011,8 @@ void ConsoleArgumentsTests::InitialSizeTests() 0ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode false); // successful parse? } @@ -1000,7 +1039,8 @@ void ConsoleArgumentsTests::HeadlessArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --headless 0x4"; @@ -1022,7 +1062,8 @@ void ConsoleArgumentsTests::HeadlessArgTests() 4ul, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --headless --headless"; @@ -1044,7 +1085,8 @@ void ConsoleArgumentsTests::HeadlessArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe -- foo.exe --headless"; @@ -1066,7 +1108,8 @@ void ConsoleArgumentsTests::HeadlessArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? } @@ -1097,7 +1140,8 @@ void ConsoleArgumentsTests::SignalHandleTests() 4ul, // serverHandle 8ul, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --server 0x4 --signal ASDF"; @@ -1119,7 +1163,8 @@ void ConsoleArgumentsTests::SignalHandleTests() 4ul, // serverHandle 0ul, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode false); // successful parse? commandline = L"conhost.exe --signal --server 0x4"; @@ -1141,7 +1186,8 @@ void ConsoleArgumentsTests::SignalHandleTests() 0ul, // serverHandle 0ul, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode false); // successful parse? } @@ -1172,7 +1218,8 @@ void ConsoleArgumentsTests::FeatureArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --feature tty"; ArgTestsRunner(L"#2 Error case, pass an unsupported feature", @@ -1193,7 +1240,8 @@ void ConsoleArgumentsTests::FeatureArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode false); // successful parse? commandline = L"conhost.exe --feature pty --feature pty"; @@ -1215,7 +1263,8 @@ void ConsoleArgumentsTests::FeatureArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode true); // successful parse? commandline = L"conhost.exe --feature pty --feature tty"; @@ -1237,7 +1286,8 @@ void ConsoleArgumentsTests::FeatureArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode false); // successful parse? commandline = L"conhost.exe --feature pty --feature"; @@ -1259,7 +1309,8 @@ void ConsoleArgumentsTests::FeatureArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode false); // successful parse? commandline = L"conhost.exe --feature pty --feature --signal foo"; @@ -1281,6 +1332,7 @@ void ConsoleArgumentsTests::FeatureArgTests() 0, // serverHandle 0, // signalHandle false, // inheritCursor - false), // runAsComServer + false, // runAsComServer + false), // passthroughMode false); // successful parse? } diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index a61839d17c5..fc5887b2892 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -1863,7 +1863,7 @@ void ScreenBufferTests::ResizeAltBufferGetScreenBufferInfo() altBuffer.SetViewportSize(&newBufferSize); CONSOLE_SCREEN_BUFFER_INFOEX csbiex{ 0 }; - g.api.GetConsoleScreenBufferInfoExImpl(mainBuffer, csbiex); + g.api->GetConsoleScreenBufferInfoExImpl(mainBuffer, csbiex); const auto newActualMainView = mainBuffer.GetViewport(); const auto newActualAltView = altBuffer.GetViewport(); @@ -4973,7 +4973,7 @@ void ScreenBufferTests::SnapCursorWithTerminalScrolling() Log::Comment(NoThrowString().Format( L"Call SetConsoleCursorPosition to snap to the cursor")); - VERIFY_SUCCEEDED(g.api.SetConsoleCursorPositionImpl(si, secondWindowOrigin)); + VERIFY_SUCCEEDED(g.api->SetConsoleCursorPositionImpl(si, secondWindowOrigin)); const auto fourthView = si._viewport; const auto fourthVirtualBottom = si._virtualBottom; @@ -5049,12 +5049,12 @@ void ScreenBufferTests::ClearAlternateBuffer() #pragma region Test ScrollConsoleScreenBufferWImpl() // Clear text of alt buffer (same params as in CMD) - VERIFY_SUCCEEDED(g.api.ScrollConsoleScreenBufferWImpl(siMain, - { 0, 0, 120, 9001 }, - { 0, -9001 }, - std::nullopt, - L' ', - 7)); + VERIFY_SUCCEEDED(g.api->ScrollConsoleScreenBufferWImpl(siMain, + { 0, 0, 120, 9001 }, + { 0, -9001 }, + std::nullopt, + L' ', + 7)); // Verify text is now gone VERIFY_ARE_EQUAL(L" ", altBuffer.GetTextBuffer().GetCellDataAt({ 0, 0 })->Chars()); @@ -5062,7 +5062,7 @@ void ScreenBufferTests::ClearAlternateBuffer() #pragma region Test SetConsoleCursorPositionImpl() // Reset cursor position as we do with CLS command (same params as in CMD) - VERIFY_SUCCEEDED(g.api.SetConsoleCursorPositionImpl(siMain, { 0 })); + VERIFY_SUCCEEDED(g.api->SetConsoleCursorPositionImpl(siMain, { 0 })); // Verify state of alt buffer auto& altBufferCursor = altBuffer.GetTextBuffer().GetCursor(); diff --git a/src/inc/conpty-static.h b/src/inc/conpty-static.h index 052708a6bd5..b745a8b1631 100644 --- a/src/inc/conpty-static.h +++ b/src/inc/conpty-static.h @@ -17,6 +17,7 @@ extern "C" { #define PSEUDOCONSOLE_RESIZE_QUIRK (2u) #define PSEUDOCONSOLE_WIN32_INPUT_MODE (4u) +#define PSEUDOCONSOLE_PASSTHROUGH_MODE (8u) HRESULT WINAPI ConptyCreatePseudoConsole(COORD size, HANDLE hInput, HANDLE hOutput, DWORD dwFlags, HPCON* phPC); diff --git a/src/interactivity/win32/windowio.cpp b/src/interactivity/win32/windowio.cpp index 0734af874f2..2d5d8436ec3 100644 --- a/src/interactivity/win32/windowio.cpp +++ b/src/interactivity/win32/windowio.cpp @@ -565,11 +565,11 @@ BOOL HandleSysKeyEvent(const HWND hWnd, const UINT Message, const WPARAM wParam, CONSOLE_FONT_INFOEX font = { 0 }; font.cbSize = sizeof(font); - RETURN_IF_FAILED(globals.api.GetCurrentConsoleFontExImpl(screenInfo, false, font)); + RETURN_IF_FAILED(globals.api->GetCurrentConsoleFontExImpl(screenInfo, false, font)); font.dwFontSize.Y += delta; - RETURN_IF_FAILED(globals.api.SetCurrentConsoleFontExImpl(screenInfo, false, font)); + RETURN_IF_FAILED(globals.api->SetCurrentConsoleFontExImpl(screenInfo, false, font)); return S_OK; } diff --git a/src/renderer/vt/Xterm256Engine.cpp b/src/renderer/vt/Xterm256Engine.cpp index 499feb2002d..33a0e92ec59 100644 --- a/src/renderer/vt/Xterm256Engine.cpp +++ b/src/renderer/vt/Xterm256Engine.cpp @@ -30,8 +30,10 @@ Xterm256Engine::Xterm256Engine(_In_ wil::unique_hfile hPipe, const RenderSettings& /*renderSettings*/, const gsl::not_null pData, const bool /*usingSoftFont*/, - const bool /*isSettingDefaultBrushes*/) noexcept + const bool isSettingDefaultBrushes) noexcept { + RETURN_HR_IF(S_FALSE, _passthrough && isSettingDefaultBrushes); + RETURN_IF_FAILED(VtEngine::_RgbUpdateDrawingBrushes(textAttributes)); RETURN_IF_FAILED(_UpdateHyperlinkAttr(textAttributes, pData)); diff --git a/src/renderer/vt/Xterm256Engine.hpp b/src/renderer/vt/Xterm256Engine.hpp index 5c8cde61831..9d704c34dd8 100644 --- a/src/renderer/vt/Xterm256Engine.hpp +++ b/src/renderer/vt/Xterm256Engine.hpp @@ -18,6 +18,8 @@ Author(s): #include "XtermEngine.hpp" +class VtApiRoutines; + namespace Microsoft::Console::Render { class Xterm256Engine : public XtermEngine @@ -36,6 +38,8 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT ManuallyClearScrollback() noexcept override; + friend class ::VtApiRoutines; + private: [[nodiscard]] HRESULT _UpdateExtendedAttrs(const TextAttribute& textAttributes) noexcept; [[nodiscard]] HRESULT _UpdateHyperlinkAttr(const TextAttribute& textAttributes, diff --git a/src/renderer/vt/XtermEngine.cpp b/src/renderer/vt/XtermEngine.cpp index cb8004cfd0f..f9cbb183477 100644 --- a/src/renderer/vt/XtermEngine.cpp +++ b/src/renderer/vt/XtermEngine.cpp @@ -46,6 +46,16 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe, // during the frame. _nextCursorIsVisible = false; + // Do not perform synchronization clearing in passthrough mode. + // In passthrough, the terminal leads and we follow what it is + // handling from the client application. + // (This is in contrast to full PTY mode where WE, the ConPTY, lead and + // it follows our state.) + if (_passthrough) + { + _firstPaint = false; + } + if (_firstPaint) { // MSFT:17815688 diff --git a/src/renderer/vt/state.cpp b/src/renderer/vt/state.cpp index bfea737bb12..2f9d83decda 100644 --- a/src/renderer/vt/state.cpp +++ b/src/renderer/vt/state.cpp @@ -54,7 +54,8 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe, _bufferLine{}, _buffer{}, _formatBuffer{}, - _conversionBuffer{} + _conversionBuffer{}, + _pfnSetLookingForDSR{} { #ifndef UNIT_TESTING // When unit testing, we can instantiate a VtEngine without a pipe. @@ -65,6 +66,37 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe, #endif } +// Method Description: +// - Writes a fill of characters to our file handle (repeat of same character over and over) +[[nodiscard]] HRESULT VtEngine::_WriteFill(const size_t n, const char c) noexcept +try +{ + _trace.TraceStringFill(n, c); +#ifdef UNIT_TESTING + if (_usingTestCallback) + { + const std::string str(n, c); + // Try to get the last error. If that wasn't set, then the test probably + // doesn't set last error. No matter. We'll just return with E_FAIL + // then. This is a unit test, we don't particularly care. + const auto succeeded = _pfnTestCallback(str.data(), str.size()); + auto hr = E_FAIL; + if (!succeeded) + { + const auto err = ::GetLastError(); + // If there wasn't an error in GLE, just use E_FAIL + hr = SUCCEEDED_WIN32(err) ? hr : HRESULT_FROM_WIN32(err); + } + return succeeded ? S_OK : hr; + } +#endif + + // TODO GH10001: Replace me with REP + _buffer.append(n, c); + return S_OK; +} +CATCH_RETURN(); + // Method Description: // - Writes the characters to our file handle. If we're building the unit tests, // we can instead write to the test callback, in order to avoid needing to @@ -379,6 +411,23 @@ HRESULT VtEngine::RequestCursor() noexcept return S_OK; } +// Method Description: +// - Sends a notification through to the `VtInputThread` that it should +// watch for and capture the response from a DSR message we're about to send. +// This is typically `RequestCursor` at the time of writing this, but in theory +// could be another DSR as well. +// Arguments: +// - +// Return Value: +// - S_OK if all goes well. Invalid state error if no notification function is installed. +// (see `SetLookingForDSRCallback` to install one.) +[[nodiscard]] HRESULT VtEngine::_ListenForDSR() noexcept +{ + RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !_pfnSetLookingForDSR); + _pfnSetLookingForDSR(true); + return S_OK; +} + // Method Description: // - Tell the vt renderer to begin a resize operation. During a resize // operation, the vt renderer should _not_ request to be repainted during a @@ -415,7 +464,7 @@ void VtEngine::EndResizeRequest() // conpty scenario. // - See also: GH#3490, #4354, #4741 // Arguments: -// - +// - resizeQuirk - True to turn on the quirk. False otherwise. // Return Value: // - true iff we were started with the `--resizeQuirk` flag enabled. void VtEngine::SetResizeQuirk(const bool resizeQuirk) @@ -423,6 +472,29 @@ void VtEngine::SetResizeQuirk(const bool resizeQuirk) _resizeQuirk = resizeQuirk; } +// Method Description: +// - Configure the renderer to understand that we're operating in limited-draw +// passthrough mode. We do not need to handle full responsibility for replicating +// buffer state to the attached terminal. +// Arguments: +// - passthrough - True to turn on passthrough mode. False otherwise. +// Return Value: +// - true iff we were started with an output mode for passthrough. false otherwise. +void VtEngine::SetPassthroughMode(const bool passthrough) noexcept +{ + _passthrough = passthrough; +} + +void VtEngine::SetLookingForDSRCallback(std::function pfnLooking) noexcept +{ + _pfnSetLookingForDSR = pfnLooking; +} + +void VtEngine::SetTerminalCursorTextPosition(const COORD cursor) noexcept +{ + _lastText = cursor; +} + // Method Description: // - Manually emit a "Erase Scrollback" sequence to the connected terminal. We // need to do this in certain cases that we've identified where we believe the diff --git a/src/renderer/vt/tracing.cpp b/src/renderer/vt/tracing.cpp index 6e76efed8d5..5e8fb5b5815 100644 --- a/src/renderer/vt/tracing.cpp +++ b/src/renderer/vt/tracing.cpp @@ -64,6 +64,23 @@ std::string toPrintableString(const std::string_view& inString) } return retval; } +void RenderTracing::TraceStringFill(const size_t n, const char c) const +{ +#ifndef UNIT_TESTING + if (TraceLoggingProviderEnabled(g_hConsoleVtRendererTraceProvider, WINEVENT_LEVEL_VERBOSE, TIL_KEYWORD_TRACE)) + { + TraceLoggingWrite(g_hConsoleVtRendererTraceProvider, + "VtEngine_TraceStringFill", + TraceLoggingUInt64(gsl::narrow_cast(n)), + TraceLoggingChar(c), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + } +#else + UNREFERENCED_PARAMETER(n); + UNREFERENCED_PARAMETER(c); +#endif UNIT_TESTING +} void RenderTracing::TraceString(const std::string_view& instr) const { #ifndef UNIT_TESTING diff --git a/src/renderer/vt/tracing.hpp b/src/renderer/vt/tracing.hpp index f3aa7fbb005..973605a19e7 100644 --- a/src/renderer/vt/tracing.hpp +++ b/src/renderer/vt/tracing.hpp @@ -26,6 +26,7 @@ namespace Microsoft::Console::VirtualTerminal public: RenderTracing(); ~RenderTracing(); + void TraceStringFill(const size_t n, const char c) const; void TraceString(const std::string_view& str) const; void TraceInvalidate(const til::rect& view) const; void TraceLastText(const til::point lastText) const; diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index 56afda42116..ee2a1cd720d 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -80,6 +80,9 @@ namespace Microsoft::Console::Render void BeginResizeRequest(); void EndResizeRequest(); void SetResizeQuirk(const bool resizeQuirk); + void SetPassthroughMode(const bool passthrough) noexcept; + void SetLookingForDSRCallback(std::function pfnLooking) noexcept; + void SetTerminalCursorTextPosition(const COORD coordCursor) noexcept; [[nodiscard]] virtual HRESULT ManuallyClearScrollback() noexcept; [[nodiscard]] HRESULT RequestWin32Input() noexcept; @@ -92,6 +95,8 @@ namespace Microsoft::Console::Render TextAttribute _lastTextAttributes; + std::function _pfnSetLookingForDSR; + Microsoft::Console::Types::Viewport _lastViewport; std::pmr::unsynchronized_pool_resource _pool; @@ -126,8 +131,10 @@ namespace Microsoft::Console::Render bool _delayedEolWrap{ false }; bool _resizeQuirk{ false }; + bool _passthrough{ false }; std::optional _newBottomLineBG{ std::nullopt }; + [[nodiscard]] HRESULT _WriteFill(const size_t n, const char c) noexcept; [[nodiscard]] HRESULT _Write(std::string_view const str) noexcept; [[nodiscard]] HRESULT _Flush() noexcept; @@ -186,6 +193,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT _EndHyperlink() noexcept; [[nodiscard]] HRESULT _RequestCursor() noexcept; + [[nodiscard]] HRESULT _ListenForDSR() noexcept; [[nodiscard]] HRESULT _RequestWin32Input() noexcept; diff --git a/src/server/IApiRoutines.h b/src/server/IApiRoutines.h index 996227f0636..a4e4ba047f1 100644 --- a/src/server/IApiRoutines.h +++ b/src/server/IApiRoutines.h @@ -44,6 +44,8 @@ class IApiRoutines #pragma endregion + virtual ~IApiRoutines(){}; + #pragma region L1 virtual void GetConsoleInputCodePageImpl(ULONG& codepage) noexcept = 0; diff --git a/src/terminal/parser/InputStateMachineEngine.cpp b/src/terminal/parser/InputStateMachineEngine.cpp index 2965ec4b617..40a9d0c4094 100644 --- a/src/terminal/parser/InputStateMachineEngine.cpp +++ b/src/terminal/parser/InputStateMachineEngine.cpp @@ -101,6 +101,11 @@ InputStateMachineEngine::InputStateMachineEngine(std::unique_ptr pDispatch, const bool lookingForDSR); + void SetLookingForDSR(const bool looking) noexcept; + bool ActionExecute(const wchar_t wch) override; bool ActionExecuteFromEscape(const wchar_t wch) override; diff --git a/src/tools/closetest/closetest.cpp b/src/tools/closetest/closetest.cpp index 16d2a184fa8..00da8d53b83 100644 --- a/src/tools/closetest/closetest.cpp +++ b/src/tools/closetest/closetest.cpp @@ -298,6 +298,7 @@ static std::vector getConsoleProcessList() const DWORD count1 = GetConsoleProcessList(&ret[0], (DWORD)ret.size()); assert(count1 >= 1 && "GetConsoleProcessList failed"); ret.resize(count1); +#pragma warning(suppress : 4189) // It's used in the assert const DWORD count2 = GetConsoleProcessList(&ret[0], (DWORD)ret.size()); assert(count1 == count2 && "GetConsoleProcessList failed"); return ret; @@ -373,7 +374,7 @@ static void spawnChildTree(DWORD masterPid, const std::vector& ext PROCESS_INFORMATION pi{}; success = CreateProcessW(exeName().c_str(), &cmdline[0], nullptr, nullptr, FALSE, 0, nullptr, nullptr, &sui, &pi); assert(success && "CreateProcessW failed"); - +#pragma warning(suppress : 4189) // It's used in the assert const DWORD waitRet = WaitForSingleObject(readyEvent, INFINITE); assert(waitRet == WAIT_OBJECT_0 && "WaitForSingleObject failed"); CloseHandle(readyEvent); @@ -483,6 +484,7 @@ static BOOL WINAPI ctrlHandler(DWORD type) static HANDLE duplicateHandle(HANDLE srcProc, HANDLE srcHandle) { HANDLE ret{}; +#pragma warning(suppress : 4189) // It's used in the assert const auto success = DuplicateHandle(srcProc, srcHandle, GetCurrentProcess(), &ret, 0, FALSE, DUPLICATE_SAME_ACCESS); assert(success && "DuplicateHandle failed"); @@ -603,6 +605,7 @@ static int doChild(std::deque argv) // Assign self to a job object. if (jobHandle != nullptr) { +#pragma warning(suppress : 4189) // It's used in the assert const BOOL success = AssignProcessToJobObject(jobHandle, GetCurrentProcess()); assert(success && "AssignProcessToJobObject failed"); diff --git a/src/winconpty/winconpty.cpp b/src/winconpty/winconpty.cpp index 7653ce6bd5c..0292c0f47d2 100644 --- a/src/winconpty/winconpty.cpp +++ b/src/winconpty/winconpty.cpp @@ -94,12 +94,13 @@ HRESULT _CreatePseudoConsole(const HANDLE hToken, RETURN_IF_WIN32_BOOL_FALSE(SetHandleInformation(signalPipeConhostSide.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)); // GH4061: Ensure that the path to executable in the format is escaped so C:\Program.exe cannot collide with C:\Program Files - const wchar_t* pwszFormat = L"\"%s\" --headless %s%s%s--width %hu --height %hu --signal 0x%x --server 0x%x"; + const wchar_t* pwszFormat = L"\"%s\" --headless %s%s%s%s--width %hu --height %hu --signal 0x%x --server 0x%x"; // This is plenty of space to hold the formatted string wchar_t cmd[MAX_PATH]{}; const BOOL bInheritCursor = (dwFlags & PSEUDOCONSOLE_INHERIT_CURSOR) == PSEUDOCONSOLE_INHERIT_CURSOR; const BOOL bResizeQuirk = (dwFlags & PSEUDOCONSOLE_RESIZE_QUIRK) == PSEUDOCONSOLE_RESIZE_QUIRK; const BOOL bWin32InputMode = (dwFlags & PSEUDOCONSOLE_WIN32_INPUT_MODE) == PSEUDOCONSOLE_WIN32_INPUT_MODE; + const BOOL bPassthroughMode = (dwFlags & PSEUDOCONSOLE_PASSTHROUGH_MODE) == PSEUDOCONSOLE_PASSTHROUGH_MODE; swprintf_s(cmd, MAX_PATH, pwszFormat, @@ -107,6 +108,7 @@ HRESULT _CreatePseudoConsole(const HANDLE hToken, bInheritCursor ? L"--inheritcursor " : L"", bWin32InputMode ? L"--win32input " : L"", bResizeQuirk ? L"--resizeQuirk " : L"", + bPassthroughMode ? L"--passthrough " : L"", size.X, size.Y, signalPipeConhostSide.get(), diff --git a/src/winconpty/winconpty.h b/src/winconpty/winconpty.h index f2a7ff429ee..5c6a2f501c0 100644 --- a/src/winconpty/winconpty.h +++ b/src/winconpty/winconpty.h @@ -25,6 +25,7 @@ typedef struct _PseudoConsole // #define PSEUDOCONSOLE_INHERIT_CURSOR (0x1) #define PSEUDOCONSOLE_RESIZE_QUIRK (0x2) #define PSEUDOCONSOLE_WIN32_INPUT_MODE (0x4) +#define PSEUDOCONSOLE_PASSTHROUGH_MODE (0x8) // Implementations of the various PseudoConsole functions. HRESULT _CreatePseudoConsole(const HANDLE hToken,