Compare commits

...

11 commits

Author SHA1 Message Date
Michael Niksa 56d148e9f4 code format! 2021-09-17 15:31:43 -07:00
Michael Niksa 925ede61ff have a few commas 2021-09-17 15:30:40 -07:00
Michael Niksa 97b853518b some pre-review cleanup 2021-09-17 15:29:05 -07:00
Michael Niksa a19198a9f7 argument/option for passthrough mode 2021-09-17 14:51:19 -07:00
Michael Niksa 668e8a45a6 change activation mechanism for passthrough 2021-09-17 14:51:19 -07:00
Michael Niksa 773f928414 Define feature for this. Start piping through setting. 2021-09-17 14:51:00 -07:00
Michael Niksa 682ada1158 fix a thing where we would see the cursor on offs because it did not get completely flushed 2021-09-17 14:50:10 -07:00
Michael Niksa 5645af0e6e remove one bit of badness 2021-09-17 14:50:10 -07:00
Michael Niksa 154ff74c7d cooked read in cmd works omg. 2021-09-17 14:49:33 -07:00
Michael Niksa ebe4ff9eee the rest of the stuff I did on Tuesday. 2021-09-17 14:47:45 -07:00
Michael Niksa 06e3b43ae1 Make a VTApiRoutines servicer that does minimal translations instead of environmental simulation for some output methods. 2021-09-17 14:47:44 -07:00
54 changed files with 1714 additions and 80 deletions

View file

@ -84,6 +84,7 @@ LSHIFT
MENUCOMMAND
MENUDATA
MENUINFO
memchr
memicmp
mptt
mov

View file

@ -513,6 +513,7 @@ DECAUPSS
DECAWM
DECCKM
DECCOLM
DECCRA
DECDHL
decdld
DECDLD
@ -2590,6 +2591,7 @@ VSTS
VSTT
vstudio
vswhere
vtapi
vtapp
VTE
VTID

View file

@ -1420,23 +1420,23 @@
"$ref": "#/definitions/Color",
"default": "#0c0c0c",
"description": "Sets the background color of the text. Overrides \"background\" from the color scheme. Uses hex color format: \"#rrggbb\".",
"type": ["string", "null"]
"type": [ "string", "null" ]
},
"unfocusedAppearance": {
"$ref": "#/definitions/AppearanceConfig",
"description": "Sets the appearance of the terminal when it is unfocused.",
"type": ["object", "null"]
"type": [ "object", "null" ]
},
"font": {
"$ref": "#/definitions/FontConfig",
"description": "Sets the font options of the terminal.",
"type": ["object", "null"]
"type": [ "object", "null" ]
},
"backgroundImage": {
"description": "Sets the file location of the image to draw over the window background.",
"oneOf": [
{
"type": ["string", null]
"type": [ "string", null ]
},
{
"enum": [
@ -1514,7 +1514,7 @@
"cursorColor": {
"oneOf": [
{ "$ref": "#/definitions/Color" },
{"type": "null"}
{ "type": "null" }
],
"description": "Sets the color of the cursor. Overrides the cursor color from the color scheme. Uses hex color format: \"#rrggbb\"."
},
@ -1522,7 +1522,7 @@
"description": "Sets the percentage height of the cursor starting from the bottom. Only works when cursorShape is set to \"vintage\". Accepts values from 1-100.",
"maximum": 100,
"minimum": 1,
"type": ["integer","null"],
"type": [ "integer", "null" ],
"default": 25
},
"cursorShape": {
@ -1538,6 +1538,10 @@
],
"type": "string"
},
"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"
@ -1591,7 +1595,7 @@
"$ref": "#/definitions/Color",
"default": "#cccccc",
"description": "Sets the text color. Overrides \"foreground\" from the color scheme. Uses hex color format: \"#rrggbb\".",
"type": ["string", "null"]
"type": [ "string", "null" ]
},
"guid": {
"$ref": "#/definitions/ProfileGuid",
@ -1608,7 +1612,7 @@
"minimum": -1,
"type": "integer"
},
"icon":{ "$ref": "#/definitions/Icon" },
"icon": { "$ref": "#/definitions/Icon" },
"name": {
"description": "Name of the profile. Displays in the dropdown menu.",
"minLength": 1,
@ -1638,7 +1642,7 @@
},
"selectionBackground": {
"oneOf": [
{"$ref": "#/definitions/Color"},
{ "$ref": "#/definitions/Color" },
{ "type": "null" }
],
"description": "Sets the background color of selected text. Overrides selectionBackground set in the color scheme. Uses hex color format: \"#rrggbb\"."
@ -1655,7 +1659,7 @@
},
"source": {
"description": "Stores the name of the profile generator that originated this profile.",
"type": ["string", "null"]
"type": [ "string", "null" ]
},
"startingDirectory": {
"description": "The directory the shell starts in when it is loaded.",
@ -1669,11 +1673,11 @@
"tabColor": {
"$ref": "#/definitions/Color",
"description": "Sets the color of the profile's tab. Using the tab color picker will override this color.",
"type": ["string", "null"]
"type": [ "string", "null" ]
},
"tabTitle": {
"description": "If set, will replace the name as the title to pass to the shell on startup. Some shells (like bash) may choose to ignore this initial value, while others (cmd, powershell) may use this value over the lifetime of the application.",
"type": ["string", "null"]
"type": [ "string", "null" ]
},
"useAcrylic": {
"default": false,

View file

@ -884,13 +884,20 @@ namespace winrt::TerminalApp::implementation
std::filesystem::path azBridgePath{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
azBridgePath.replace_filename(L"TerminalAzBridge.exe");
connection = TerminalConnection::ConptyConnection();
connection.Initialize(TerminalConnection::ConptyConnection::CreateSettings(azBridgePath.wstring(),
L".",
L"Azure",
nullptr,
::base::saturated_cast<uint32_t>(settings.InitialRows()),
::base::saturated_cast<uint32_t>(settings.InitialCols()),
winrt::guid()));
auto valueSet = TerminalConnection::ConptyConnection::CreateSettings(azBridgePath.wstring(),
L".",
L"Azure",
nullptr,
::base::saturated_cast<uint32_t>(settings.InitialRows()),
::base::saturated_cast<uint32_t>(settings.InitialCols()),
winrt::guid());
if constexpr (Feature_VtPassthroughMode::IsEnabled())
{
valueSet.Insert(L"passthroughMode", Windows::Foundation::PropertyValue::CreateBoolean(settings.VtPassthrough()));
}
connection.Initialize(valueSet);
}
else
@ -928,13 +935,17 @@ namespace winrt::TerminalApp::implementation
}
auto conhostConn = TerminalConnection::ConptyConnection();
conhostConn.Initialize(TerminalConnection::ConptyConnection::CreateSettings(settings.Commandline(),
newWorkingDirectory,
settings.StartingTitle(),
envMap.GetView(),
::base::saturated_cast<uint32_t>(settings.InitialRows()),
::base::saturated_cast<uint32_t>(settings.InitialCols()),
winrt::guid()));
auto valueSet = TerminalConnection::ConptyConnection::CreateSettings(settings.Commandline(),
newWorkingDirectory,
settings.StartingTitle(),
envMap.GetView(),
::base::saturated_cast<uint32_t>(settings.InitialRows()),
::base::saturated_cast<uint32_t>(settings.InitialCols()),
winrt::guid());
valueSet.Insert(L"passthroughMode", Windows::Foundation::PropertyValue::CreateBoolean(settings.VtPassthrough()));
conhostConn.Initialize(valueSet);
sessionGuid = conhostConn.Guid();
connection = conhostConn;

View file

@ -338,6 +338,10 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
_initialCols = winrt::unbox_value_or<uint32_t>(settings.TryLookup(L"initialCols").try_as<Windows::Foundation::IPropertyValue>(), _initialCols);
_guid = winrt::unbox_value_or<winrt::guid>(settings.TryLookup(L"guid").try_as<Windows::Foundation::IPropertyValue>(), _guid);
_environment = settings.TryLookup(L"environment").try_as<Windows::Foundation::Collections::ValueSet>();
if constexpr (Feature_VtPassthroughMode::IsEnabled())
{
_passthroughMode = winrt::unbox_value_or<bool>(settings.TryLookup(L"passthroughMode").try_as<Windows::Foundation::IPropertyValue>(), _passthroughMode);
}
}
if (_guid == guid{})
@ -362,7 +366,14 @@ 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())
{
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

View file

@ -85,6 +85,10 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
std::wstring _u16Str{};
std::array<char, 4096> _buffer{};
#if TIL_FEATURE_VTPASSTHROUGHMODE_ENABLED
bool _passthroughMode{};
#endif
DWORD _OutputThread();
};
}

View file

@ -56,7 +56,9 @@
<Midl Include="AzureConnection.idl" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources\en-US\Resources.resw" />
<PRIResource Include="Resources\en-US\Resources.resw">
<SubType>Designer</SubType>
</PRIResource>
<OCResourceDirectory Include="Resources" />
<None Include="packages.config" />
</ItemGroup>
@ -88,11 +90,11 @@
</Target>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(IntDir)..\OpenConsoleProxy;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(IntDir)..\OpenConsoleProxy;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>$(OpenConsoleCommonOutDir)\conptylib.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project>
</Project>

View file

@ -32,6 +32,7 @@
<Midl Include="EchoConnection.idl" />
<Midl Include="AzureConnection.idl" />
<Midl Include="ConptyConnection.idl" />
<Midl Include="ConnectionInformation.idl" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View file

@ -73,9 +73,8 @@ 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) {
_sendInputToConnection(wstr);
});
auto pfnWriteInput = std::bind(&ControlCore::_sendInputToConnection, this, std::placeholders::_1);
_terminal->SetWriteInputCallback(pfnWriteInput);
// GH#8969: pre-seed working directory to prevent potential races
_terminal->SetWorkingDirectory(_settings.StartingDirectory());

View file

@ -22,6 +22,7 @@ namespace Microsoft.Terminal.Core
Boolean ForceVTInput;
Boolean TrimBlockSelection;
Boolean DetectURLs;
Boolean VtPassthrough;
Windows.Foundation.IReference<Microsoft.Terminal.Core.Color> TabColor;
Windows.Foundation.IReference<Microsoft.Terminal.Core.Color> StartingTabColor;

View file

@ -20,6 +20,8 @@ namespace Microsoft::Terminal::Core
virtual bool PrintString(std::wstring_view string) noexcept = 0;
virtual bool ExecuteChar(wchar_t wch) noexcept = 0;
virtual bool ReturnResponse(std::wstring_view responseString) noexcept = 0;
virtual TextAttribute GetTextAttributes() const noexcept = 0;
virtual void SetTextAttributes(const TextAttribute& attrs) noexcept = 0;

View file

@ -928,8 +928,10 @@ void Terminal::_WriteBuffer(const std::wstring_view& stringView)
//
// If wch is a surrogate character we need to read 2 code units
// from the stringView to form a single code point.
const auto isSurrogate = wch >= 0xD800 && wch <= 0xDFFF;
const auto view = stringView.substr(i, isSurrogate ? 2 : 1);
const auto remaining = stringView.substr(i);
const auto isSurrogate = [](wchar_t wch) -> bool { return wch >= 0xD800 && wch <= 0xDFFF; };
const auto foundLast = std::find_if(remaining.cbegin(), remaining.cend(), isSurrogate);
const std::wstring_view view(remaining.data(), std::distance(remaining.cbegin(), foundLast));
const OutputCellIterator it{ view, _buffer->GetCurrentAttributes() };
const auto end = _buffer->Write(it);
const auto cellDistance = end.GetCellDistance(it);
@ -1133,7 +1135,7 @@ void Terminal::_NotifyTerminalCursorPositionChanged() noexcept
}
}
void Terminal::SetWriteInputCallback(std::function<void(std::wstring&)> pfn) noexcept
void Terminal::SetWriteInputCallback(std::function<void(std::wstring_view)> pfn) noexcept
{
_pfnWriteInput.swap(pfn);
}

View file

@ -73,10 +73,10 @@ public:
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<til::ticket_lock> LockForReading();
@ -91,6 +91,7 @@ public:
// These methods are defined in TerminalApi.cpp
bool PrintString(std::wstring_view stringView) noexcept override;
bool ExecuteChar(wchar_t wch) noexcept override;
bool ReturnResponse(std::wstring_view responseString) noexcept override;
TextAttribute GetTextAttributes() const noexcept override;
void SetTextAttributes(const TextAttribute& attrs) noexcept override;
Microsoft::Console::Types::Viewport GetBufferSize() noexcept override;
@ -201,7 +202,7 @@ public:
void ColorSelection(const COORD coordSelectionStart, const COORD coordSelectionEnd, const TextAttribute) override;
#pragma endregion
void SetWriteInputCallback(std::function<void(std::wstring&)> pfn) noexcept;
void SetWriteInputCallback(std::function<void(std::wstring_view)> pfn) noexcept;
void SetWarningBellCallback(std::function<void()> pfn) noexcept;
void SetTitleChangedCallback(std::function<void(std::wstring_view)> pfn) noexcept;
void SetTabColorChangedCallback(std::function<void(const std::optional<til::color>)> pfn) noexcept;
@ -242,7 +243,7 @@ public:
#pragma endregion
private:
std::function<void(std::wstring&)> _pfnWriteInput;
std::function<void(std::wstring_view)> _pfnWriteInput;
std::function<void()> _pfnWarningBell;
std::function<void(std::wstring_view)> _pfnTitleChanged;
std::function<void(std::wstring_view)> _pfnCopyToClipboard;

View file

@ -26,6 +26,18 @@ try
}
CATCH_RETURN_FALSE()
bool Terminal::ReturnResponse(std::wstring_view responseString) noexcept
try
{
if (!_pfnWriteInput)
{
return false;
}
_pfnWriteInput(responseString);
return true;
}
CATCH_LOG_RETURN_FALSE()
TextAttribute Terminal::GetTextAttributes() const noexcept
{
return _buffer->GetCurrentAttributes();

View file

@ -462,6 +462,58 @@ bool TerminalDispatch::ResetMode(const DispatchTypes::ModeParams param) noexcept
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) noexcept
{
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:
// - <none>
// Return Value:
// - True if handled successfully. False otherwise.
bool TerminalDispatch::_OperatingStatus() const noexcept
{
// 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:
// - <none>
// Return Value:
// - True if handled successfully. False otherwise.
bool TerminalDispatch::_CursorPositionReport() const noexcept
{
// 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<std::wstring>(L"\x1b[%d;%dR", pos.Y + 1, pos.X + 1);
return _WriteResponse(response);
}
// Method Description:
// - Start a hyperlink
// Arguments:
@ -561,6 +613,18 @@ bool TerminalDispatch::DoConEmuAction(const std::wstring_view string) noexcept
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 noexcept
{
return _terminalApi.ReturnResponse(reply);
}
// Routine Description:
// - Support routine for routing private mode parameters to be set/reset as flags
// Arguments:

View file

@ -76,6 +76,8 @@ public:
bool SetMode(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams /*param*/) noexcept override; // DECSET
bool ResetMode(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams /*param*/) noexcept override; // DECRST
bool DeviceStatusReport(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::AnsiStatusType /*statusType*/) noexcept override; // DSR, DSR-OS, DSR-CPR
bool AddHyperlink(const std::wstring_view uri, const std::wstring_view params) noexcept override;
bool EndHyperlink() noexcept override;
@ -91,8 +93,12 @@ private:
TextAttribute& attr,
const bool isForeground) noexcept;
bool _WriteResponse(const std::wstring_view reply) const noexcept;
bool _ModeParamsHelper(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams param, const bool enable) noexcept;
bool _OperatingStatus() const noexcept;
bool _CursorPositionReport() const noexcept;
bool _ClearSingleTabStop() noexcept;
bool _ClearAllTabStops() noexcept;
void _ResetTabStops() noexcept;

View file

@ -35,6 +35,7 @@ static constexpr std::string_view AltGrAliasingKey{ "altGrAliasing" };
static constexpr std::string_view ConnectionTypeKey{ "connectionType" };
static constexpr std::string_view CommandlineKey{ "commandline" };
static constexpr std::string_view VtPassthroughKey{ "experimental.connection.passthroughMode" };
static constexpr std::string_view FontInfoKey{ "font" };
static constexpr std::string_view AcrylicTransparencyKey{ "acrylicOpacity" };
static constexpr std::string_view UseAcrylicKey{ "useAcrylic" };
@ -108,6 +109,7 @@ winrt::com_ptr<Profile> Profile::CopySettings(winrt::com_ptr<Profile> source)
profile->_AltGrAliasing = source->_AltGrAliasing;
profile->_BellStyle = source->_BellStyle;
profile->_ConnectionType = source->_ConnectionType;
profile->_VtPassthrough = source->_VtPassthrough;
profile->_Origin = source->_Origin;
// Copy over the font info
@ -356,6 +358,7 @@ void Profile::LayerJson(const Json::Value& json)
// Control Settings
JsonUtils::GetValueForKey(json, ConnectionTypeKey, _ConnectionType);
JsonUtils::GetValueForKey(json, CommandlineKey, _Commandline);
JsonUtils::GetValueForKey(json, VtPassthroughKey, _VtPassthrough);
JsonUtils::GetValueForKey(json, AcrylicTransparencyKey, _AcrylicOpacity);
JsonUtils::GetValueForKey(json, UseAcrylicKey, _UseAcrylic);
JsonUtils::GetValueForKey(json, SuppressApplicationTitleKey, _SuppressApplicationTitle);
@ -548,6 +551,7 @@ Json::Value Profile::ToJson() const
// Control Settings
JsonUtils::SetValueForKey(json, ConnectionTypeKey, _ConnectionType);
JsonUtils::SetValueForKey(json, CommandlineKey, _Commandline);
JsonUtils::SetValueForKey(json, VtPassthroughKey, _VtPassthrough);
JsonUtils::SetValueForKey(json, AcrylicTransparencyKey, _AcrylicOpacity);
JsonUtils::SetValueForKey(json, UseAcrylicKey, _UseAcrylic);
JsonUtils::SetValueForKey(json, SuppressApplicationTitleKey, _SuppressApplicationTitle);

View file

@ -131,6 +131,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::Profile, hstring, Commandline, L"cmd.exe");
INHERITABLE_SETTING(Model::Profile, hstring, StartingDirectory);
INHERITABLE_SETTING(Model::Profile, bool, VtPassthrough, false);
INHERITABLE_SETTING(Model::Profile, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale);
INHERITABLE_SETTING(Model::Profile, bool, ForceFullRepaintRendering, false);

View file

@ -71,6 +71,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; };

View file

@ -283,6 +283,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
_Padding = profile.Padding();
_Commandline = profile.Commandline();
_VtPassthrough = profile.VtPassthrough();
_StartingDirectory = profile.EvaluatedStartingDirectory();

View file

@ -99,6 +99,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::TerminalSettings, bool, FocusFollowMouse, false);
INHERITABLE_SETTING(Model::TerminalSettings, bool, TrimBlockSelection, false);
INHERITABLE_SETTING(Model::TerminalSettings, bool, DetectURLs, true);
INHERITABLE_SETTING(Model::TerminalSettings, bool, VtPassthrough, false);
INHERITABLE_SETTING(Model::TerminalSettings, Windows::Foundation::IReference<Microsoft::Terminal::Core::Color>, TabColor, nullptr);

View file

@ -88,4 +88,14 @@
<!-- This feature will not ship to Stable until it is complete. -->
<alwaysDisabledReleaseTokens />
</feature>
<feature>
<name>Feature_VtPassthroughMode</name>
<description>Enables passthrough option per profile in Terminal and ConPTY ability to use passthrough API dispatch engine</description>
<stage>AlwaysDisabled</stage>
<!-- Did it this way instead of "release tokens" to ensure it won't go into Windows Inbox either... -->
<alwaysEnabledBrandingTokens>
<brandingToken>Dev</brandingToken>
<brandingToken>Preview</brandingToken>
</alwaysEnabledBrandingTokens>
</feature>
</featureStaging>

View file

@ -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<SHORT>(_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<SHORT>(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)

View file

@ -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<std::wstring>& 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);

View file

@ -36,6 +36,7 @@ public:
bool IsHeadless() const;
bool ShouldCreateServerHandle() const;
bool ShouldRunAsComServer() const;
bool IsPassthroughMode() const noexcept;
HANDLE GetServerHandle() const;
HANDLE GetVtInHandle() const;
@ -77,6 +78,7 @@ public:
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 @@ private:
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 @@ private:
_signalHandle(signalHandle),
_inheritCursor(inheritCursor),
_resizeQuirk(false),
_runAsComServer{ runAsComServer }
_runAsComServer{ runAsComServer },
_passthroughMode{ passthroughMode }
{
}
#endif
@ -133,6 +137,7 @@ private:
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();
}
};
}

885
src/host/VtApiRoutines.cpp Normal file
View file

@ -0,0 +1,885 @@
// 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;
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);
}
[[nodiscard]] HRESULT VtApiRoutines::PeekConsoleInputAImpl(IConsoleInputObject& context,
std::deque<std::unique_ptr<IInputEvent>>& outEvents,
const size_t eventsToRead,
INPUT_READ_HANDLE_DATA& readHandleState,
std::unique_ptr<IWaitRoutine>& waiter) noexcept
{
const auto hr = m_pUsualRoutines->PeekConsoleInputAImpl(context, outEvents, eventsToRead, readHandleState, waiter);
// 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: we only need to do this in cooked read mode.
if (waiter)
{
m_listeningForDSR = true;
(void)m_pVtEngine->_ListenForDSR();
(void)m_pVtEngine->RequestCursor();
}
return hr;
}
[[nodiscard]] HRESULT VtApiRoutines::PeekConsoleInputWImpl(IConsoleInputObject& context,
std::deque<std::unique_ptr<IInputEvent>>& outEvents,
const size_t eventsToRead,
INPUT_READ_HANDLE_DATA& readHandleState,
std::unique_ptr<IWaitRoutine>& waiter) noexcept
{
const auto hr = m_pUsualRoutines->PeekConsoleInputWImpl(context, outEvents, eventsToRead, readHandleState, waiter);
// 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: we only need to do this in cooked read mode.
if (waiter)
{
m_listeningForDSR = true;
(void)m_pVtEngine->_ListenForDSR();
(void)m_pVtEngine->RequestCursor();
}
return hr;
}
[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleInputAImpl(IConsoleInputObject& context,
std::deque<std::unique_ptr<IInputEvent>>& outEvents,
const size_t eventsToRead,
INPUT_READ_HANDLE_DATA& readHandleState,
std::unique_ptr<IWaitRoutine>& waiter) noexcept
{
const auto hr = m_pUsualRoutines->ReadConsoleInputAImpl(context, outEvents, eventsToRead, readHandleState, waiter);
// 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: we only need to do this in cooked read mode.
if (waiter)
{
m_listeningForDSR = true;
(void)m_pVtEngine->_ListenForDSR();
(void)m_pVtEngine->RequestCursor();
}
return hr;
}
[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleInputWImpl(IConsoleInputObject& context,
std::deque<std::unique_ptr<IInputEvent>>& outEvents,
const size_t eventsToRead,
INPUT_READ_HANDLE_DATA& readHandleState,
std::unique_ptr<IWaitRoutine>& waiter) noexcept
{
const auto hr = m_pUsualRoutines->ReadConsoleInputWImpl(context, outEvents, eventsToRead, readHandleState, waiter);
// 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: we only need to do this in cooked read mode.
if (waiter)
{
m_listeningForDSR = true;
(void)m_pVtEngine->_ListenForDSR();
(void)m_pVtEngine->RequestCursor();
}
return hr;
}
[[nodiscard]] HRESULT VtApiRoutines::ReadConsoleAImpl(IConsoleInputObject& context,
gsl::span<char> buffer,
size_t& written,
std::unique_ptr<IWaitRoutine>& 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: 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<char> buffer,
size_t& written,
std::unique_ptr<IWaitRoutine>& 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: 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<IWaitRoutine>& 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<IWaitRoutine>& 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(attribute, true);
(void)m_pVtEngine->_SetGraphicsRendition16Color(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: horrible. it'll WC2MB over and over...we should do that once then emit... and then rep...
// TODO: 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: 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: 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(data.wAttributes, true);
(void)m_pVtEngine->_SetGraphicsRendition16Color(data.wAttributes >> 4, false);
//color table?
// popup attributes... hold internally?
// TODO: 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<SMALL_RECT> clip,
const char fillCharacter,
const WORD fillAttribute) noexcept
{
// Use DECCRA
return S_OK;
}
[[nodiscard]] HRESULT VtApiRoutines::ScrollConsoleScreenBufferWImpl(SCREEN_INFORMATION& context,
const SMALL_RECT& source,
const COORD target,
std::optional<SMALL_RECT> clip,
const wchar_t fillCharacter,
const WORD fillAttribute,
const bool enableCmdShim) noexcept
{
// Use DECCRA
return S_OK;
}
[[nodiscard]] HRESULT VtApiRoutines::SetConsoleTextAttributeImpl(SCREEN_INFORMATION& context,
const WORD attribute) noexcept
{
(void)m_pVtEngine->_SetGraphicsRendition16Color(attribute, true);
(void)m_pVtEngine->_SetGraphicsRendition16Color(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<WORD> 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<char> 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<wchar_t> 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<const INPUT_RECORD> 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<const INPUT_RECORD> buffer,
size_t& written,
const bool append) noexcept
{
return m_pUsualRoutines->WriteConsoleInputWImpl(context, buffer, written, append);
}
extern HRESULT _ConvertCellsToWInplace(const UINT codepage,
gsl::span<CHAR_INFO> buffer,
const Viewport& rectangle) noexcept;
[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleOutputAImpl(SCREEN_INFORMATION& context,
gsl::span<CHAR_INFO> 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<CHAR_INFO> 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(ci.Attributes, true);
(void)m_pVtEngine->_SetGraphicsRendition16Color(ci.Attributes >> 4, false);
(void)m_pVtEngine->WriteTerminalW(std::wstring_view{ &ci.Char.UnicodeChar, 1 });
}
++cursor.Y;
pos += width;
}
(void)m_pVtEngine->_Flush();
//TODO: trim to buffer size?
writtenRectangle = requestRectangle;
return S_OK;
}
[[nodiscard]] HRESULT VtApiRoutines::WriteConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext,
const gsl::span<const WORD> attrs,
const COORD target,
size_t& used) noexcept
{
(void)m_pVtEngine->_CursorPosition(target);
for (const auto& attr : attrs)
{
(void)m_pVtEngine->_SetGraphicsRendition16Color(attr, true);
(void)m_pVtEngine->_SetGraphicsRendition16Color(attr >> 4, false);
(void)m_pVtEngine->WriteTerminalUtf8(std::string_view{ &s_readBackAscii.Char.AsciiChar, 1 });
}
#if 0 // discarded compression algorithm
const std::basic_string_view<decltype(attrs)::element_type> sv{ attrs.data(), attrs.size() };
for (size_t i = 0; i < attrs.size();)
{
const auto first = attrs[i];
// TODO: lhecker... inverse memchr()
const auto until = sv.find_first_not_of(first, i);
auto dist = until - first;
(void)m_pVtEngine->_SetGraphicsRendition16Color(first, true);
(void)m_pVtEngine->_SetGraphicsRendition16Color(first >> 4, false);
(void)m_pVtEngine->_WriteFill(dist, s_readBackAscii.Char.AsciiChar);
dist = dist ? dist : 1; // likely unnecessary but my brain has screwed up and made inf loops like this before...
i += dist;
}
#endif
(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<CHAR_INFO> buffer,
const Microsoft::Console::Types::Viewport& sourceRectangle,
Microsoft::Console::Types::Viewport& readRectangle) noexcept
{
std::fill_n(buffer.data(), buffer.size(), s_readBackAscii);
// TODO: 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<CHAR_INFO> buffer,
const Microsoft::Console::Types::Viewport& sourceRectangle,
Microsoft::Console::Types::Viewport& readRectangle) noexcept
{
std::fill_n(buffer.data(), buffer.size(), s_readBackUnicode);
// TODO: do we need to constrict readRectangle to within the known buffer size... probably.
return S_OK;
}
[[nodiscard]] HRESULT VtApiRoutines::GetConsoleTitleAImpl(gsl::span<char> 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<wchar_t> 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<char> 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<wchar_t> 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<char> 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<wchar_t> 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<char> alias,
size_t& written) noexcept
{
return m_pUsualRoutines->GetConsoleAliasesAImpl(exeName, alias, written);
}
[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasesWImpl(const std::wstring_view exeName,
gsl::span<wchar_t> alias,
size_t& written) noexcept
{
return m_pUsualRoutines->GetConsoleAliasesWImpl(exeName, alias, written);
}
[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasExesAImpl(gsl::span<char> aliasExes,
size_t& written) noexcept
{
return m_pUsualRoutines->GetConsoleAliasExesAImpl(aliasExes, written);
}
[[nodiscard]] HRESULT VtApiRoutines::GetConsoleAliasExesWImpl(gsl::span<wchar_t> 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<char> commandHistory,
size_t& written) noexcept
{
return m_pUsualRoutines->GetConsoleCommandHistoryAImpl(exeName, commandHistory, written);
}
[[nodiscard]] HRESULT VtApiRoutines::GetConsoleCommandHistoryWImpl(const std::wstring_view exeName,
gsl::span<wchar_t> 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)

389
src/host/VtApiRoutines.h Normal file
View file

@ -0,0 +1,389 @@
/*++
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<std::unique_ptr<IInputEvent>>& outEvents,
const size_t eventsToRead,
INPUT_READ_HANDLE_DATA& readHandleState,
std::unique_ptr<IWaitRoutine>& waiter) noexcept override;
[[nodiscard]] HRESULT PeekConsoleInputWImpl(IConsoleInputObject& context,
std::deque<std::unique_ptr<IInputEvent>>& outEvents,
const size_t eventsToRead,
INPUT_READ_HANDLE_DATA& readHandleState,
std::unique_ptr<IWaitRoutine>& waiter) noexcept override;
[[nodiscard]] HRESULT ReadConsoleInputAImpl(IConsoleInputObject& context,
std::deque<std::unique_ptr<IInputEvent>>& outEvents,
const size_t eventsToRead,
INPUT_READ_HANDLE_DATA& readHandleState,
std::unique_ptr<IWaitRoutine>& waiter) noexcept override;
[[nodiscard]] HRESULT ReadConsoleInputWImpl(IConsoleInputObject& context,
std::deque<std::unique_ptr<IInputEvent>>& outEvents,
const size_t eventsToRead,
INPUT_READ_HANDLE_DATA& readHandleState,
std::unique_ptr<IWaitRoutine>& waiter) noexcept override;
[[nodiscard]] HRESULT ReadConsoleAImpl(IConsoleInputObject& context,
gsl::span<char> buffer,
size_t& written,
std::unique_ptr<IWaitRoutine>& 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<char> buffer,
size_t& written,
std::unique_ptr<IWaitRoutine>& 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<IWaitRoutine>& waiter) noexcept override;
[[nodiscard]] HRESULT WriteConsoleWImpl(IConsoleOutputObject& context,
const std::wstring_view buffer,
size_t& read,
bool requiresVtQuirk,
std::unique_ptr<IWaitRoutine>& 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<SMALL_RECT> clip,
const char fillCharacter,
const WORD fillAttribute) noexcept override;
[[nodiscard]] HRESULT ScrollConsoleScreenBufferWImpl(SCREEN_INFORMATION& context,
const SMALL_RECT& source,
const COORD target,
std::optional<SMALL_RECT> 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<WORD> buffer,
size_t& written) noexcept override;
[[nodiscard]] HRESULT ReadConsoleOutputCharacterAImpl(const SCREEN_INFORMATION& context,
const COORD origin,
gsl::span<char> buffer,
size_t& written) noexcept override;
[[nodiscard]] HRESULT ReadConsoleOutputCharacterWImpl(const SCREEN_INFORMATION& context,
const COORD origin,
gsl::span<wchar_t> buffer,
size_t& written) noexcept override;
[[nodiscard]] HRESULT WriteConsoleInputAImpl(InputBuffer& context,
const gsl::span<const INPUT_RECORD> buffer,
size_t& written,
const bool append) noexcept override;
[[nodiscard]] HRESULT WriteConsoleInputWImpl(InputBuffer& context,
const gsl::span<const INPUT_RECORD> buffer,
size_t& written,
const bool append) noexcept override;
[[nodiscard]] HRESULT WriteConsoleOutputAImpl(SCREEN_INFORMATION& context,
gsl::span<CHAR_INFO> buffer,
const Microsoft::Console::Types::Viewport& requestRectangle,
Microsoft::Console::Types::Viewport& writtenRectangle) noexcept override;
[[nodiscard]] HRESULT WriteConsoleOutputWImpl(SCREEN_INFORMATION& context,
gsl::span<CHAR_INFO> buffer,
const Microsoft::Console::Types::Viewport& requestRectangle,
Microsoft::Console::Types::Viewport& writtenRectangle) noexcept override;
[[nodiscard]] HRESULT WriteConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext,
const gsl::span<const WORD> 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<CHAR_INFO> buffer,
const Microsoft::Console::Types::Viewport& sourceRectangle,
Microsoft::Console::Types::Viewport& readRectangle) noexcept override;
[[nodiscard]] HRESULT ReadConsoleOutputWImpl(const SCREEN_INFORMATION& context,
gsl::span<CHAR_INFO> buffer,
const Microsoft::Console::Types::Viewport& sourceRectangle,
Microsoft::Console::Types::Viewport& readRectangle) noexcept override;
[[nodiscard]] HRESULT GetConsoleTitleAImpl(gsl::span<char> title,
size_t& written,
size_t& needed) noexcept override;
[[nodiscard]] HRESULT GetConsoleTitleWImpl(gsl::span<wchar_t> title,
size_t& written,
size_t& needed) noexcept override;
[[nodiscard]] HRESULT GetConsoleOriginalTitleAImpl(gsl::span<char> title,
size_t& written,
size_t& needed) noexcept override;
[[nodiscard]] HRESULT GetConsoleOriginalTitleWImpl(gsl::span<wchar_t> 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<char> target,
size_t& written,
const std::string_view exeName) noexcept override;
[[nodiscard]] HRESULT GetConsoleAliasWImpl(const std::wstring_view source,
gsl::span<wchar_t> 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<char> alias,
size_t& written) noexcept override;
[[nodiscard]] HRESULT GetConsoleAliasesWImpl(const std::wstring_view exeName,
gsl::span<wchar_t> alias,
size_t& written) noexcept override;
[[nodiscard]] HRESULT GetConsoleAliasExesAImpl(gsl::span<char> aliasExes,
size_t& written) noexcept override;
[[nodiscard]] HRESULT GetConsoleAliasExesWImpl(gsl::span<wchar_t> 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<char> commandHistory,
size_t& written) noexcept override;
[[nodiscard]] HRESULT GetConsoleCommandHistoryWImpl(const std::wstring_view exeName,
gsl::span<wchar_t> 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;
};

View file

@ -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

View file

@ -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<void(bool)> _pfnSetLookingForDSR;
std::unique_ptr<Microsoft::Console::VirtualTerminal::StateMachine> _pInputStateMachine;
til::u8state _u8State;
};

View file

@ -13,6 +13,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;
@ -70,6 +72,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())
@ -136,8 +139,9 @@ VtIo::VtIo() :
{
return S_FALSE;
}
auto& globals = ServiceLocator::LocateGlobals();
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
const CONSOLE_INFORMATION& gci = globals.getConsoleInformation();
try
{
@ -154,22 +158,64 @@ VtIo::VtIo() :
switch (_IoMode)
{
case VtIoMode::XTERM_256:
_pVtRenderEngine = std::make_unique<Xterm256Engine>(std::move(_hOutput),
initialViewport);
{
auto xterm256Engine = std::make_unique<Xterm256Engine>(std::move(_hOutput),
initialViewport);
if (_passthroughMode)
{
auto vtapi = new VtApiRoutines();
vtapi->m_pVtEngine = xterm256Engine.get();
if (globals.api)
{
vtapi->m_pUsualRoutines = globals.api;
}
else
{
vtapi->m_pUsualRoutines = new ApiRoutines();
}
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<XtermEngine>(std::move(_hOutput),
initialViewport,
false);
if (_passthroughMode)
{
return E_NOTIMPL;
}
break;
}
case VtIoMode::XTERM_ASCII:
{
_pVtRenderEngine = std::make_unique<XtermEngine>(std::move(_hOutput),
initialViewport,
true);
if (_passthroughMode)
{
return E_NOTIMPL;
}
break;
}
default:
{
return E_FAIL;
}
}
if (_pVtRenderEngine)
{
_pVtRenderEngine->SetTerminalOwner(this);

View file

@ -63,6 +63,7 @@ namespace Microsoft::Console::VirtualTerminal
bool _resizeQuirk{ false };
bool _win32InputMode{ false };
bool _passthroughMode{ false };
std::unique_ptr<Microsoft::Console::Render::VtEngine> _pVtRenderEngine;
std::unique_ptr<Microsoft::Console::VtInputThread> _pVtInputThread;

View file

@ -648,9 +648,9 @@ void EventsToUnicode(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& 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<CHAR_INFO> buffer,
const Viewport& rectangle) noexcept
[[nodiscard]] HRESULT _ConvertCellsToWInplace(const UINT codepage,
gsl::span<CHAR_INFO> buffer,
const Viewport& rectangle) noexcept
{
try
{

View file

@ -68,7 +68,7 @@ public:
bool IsHeadless() const;
ApiRoutines api;
IApiRoutines* api;
bool handoffTarget = false;

View file

@ -8,9 +8,15 @@
<TargetName>ConhostV2Lib</TargetName>
<ConfigurationType>StaticLibrary</ConfigurationType>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\VtApiRoutines.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\VtApiRoutines.h" />
</ItemGroup>
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<!-- DONT ADD NEW FILES HERE, ADD THEM TO host-common.vcxitems -->
<Import Project="$(SolutionDir)src\host\host-common.vcxitems" />
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="$(SolutionDir)src\common.build.post.props" />
</Project>
</Project>

View file

@ -183,6 +183,9 @@
<ClCompile Include="..\ScreenBufferRenderTarget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\VtApiRoutines.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\precomp.h">
@ -356,6 +359,9 @@
<ClInclude Include="..\ScreenBufferRenderTarget.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\VtApiRoutines.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />

View file

@ -110,7 +110,7 @@ ConhostInternalGetSet::ConhostInternalGetSet(_In_ IIoProvider& io) :
// - true if successful (see DoSrvGetConsoleScreenBufferInfo). false otherwise.
bool ConhostInternalGetSet::GetConsoleScreenBufferInfoEx(CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) const
{
ServiceLocator::LocateGlobals().api.GetConsoleScreenBufferInfoExImpl(_io.GetActiveOutputBuffer(), screenBufferInfo);
ServiceLocator::LocateGlobals().api->GetConsoleScreenBufferInfoExImpl(_io.GetActiveOutputBuffer(), screenBufferInfo);
return true;
}
@ -122,7 +122,7 @@ bool ConhostInternalGetSet::GetConsoleScreenBufferInfoEx(CONSOLE_SCREEN_BUFFER_I
// - true if successful (see DoSrvSetConsoleScreenBufferInfo). false otherwise.
bool ConhostInternalGetSet::SetConsoleScreenBufferInfoEx(const CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo)
{
return SUCCEEDED(ServiceLocator::LocateGlobals().api.SetConsoleScreenBufferInfoExImpl(_io.GetActiveOutputBuffer(), screenBufferInfo));
return SUCCEEDED(ServiceLocator::LocateGlobals().api->SetConsoleScreenBufferInfoExImpl(_io.GetActiveOutputBuffer(), screenBufferInfo));
}
// Routine Description:
@ -135,7 +135,7 @@ bool ConhostInternalGetSet::SetConsoleCursorPosition(const COORD position)
{
auto& info = _io.GetActiveOutputBuffer();
const auto clampedPosition = info.GetTextBuffer().ClampPositionWithinLine(position);
return SUCCEEDED(ServiceLocator::LocateGlobals().api.SetConsoleCursorPositionImpl(info, clampedPosition));
return SUCCEEDED(ServiceLocator::LocateGlobals().api->SetConsoleCursorPositionImpl(info, clampedPosition));
}
// Method Description:
@ -233,7 +233,7 @@ bool ConhostInternalGetSet::PrivateWriteConsoleInputW(std::deque<std::unique_ptr
// - true if successful (see DoSrvSetConsoleWindowInfo). false otherwise.
bool ConhostInternalGetSet::SetConsoleWindowInfo(const bool absolute, const SMALL_RECT& window)
{
return SUCCEEDED(ServiceLocator::LocateGlobals().api.SetConsoleWindowInfoImpl(_io.GetActiveOutputBuffer(), absolute, window));
return SUCCEEDED(ServiceLocator::LocateGlobals().api->SetConsoleWindowInfoImpl(_io.GetActiveOutputBuffer(), absolute, window));
}
// Routine Description:

View file

@ -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:

View file

@ -95,6 +95,7 @@ SOURCES = \
..\CommandListPopup.cpp \
..\CopyFromCharPopup.cpp \
..\CopyToCharPopup.cpp \
..\VtApiRoutines.cpp \
# -------------------------------------

View file

@ -57,6 +57,7 @@ try
Globals.uiWindowsCP = GetACP();
Globals.pFontDefaultList = new RenderFontDefaults();
Globals.api = new ApiRoutines();
FontInfoBase::s_SetFontDefaultList(Globals.pFontDefaultList);
@ -883,7 +884,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;
@ -895,7 +896,7 @@ DWORD WINAPI ConsoleIoThread(LPVOID lpParameter)
std::unique_ptr<CONSOLE_API_MSG> capturedMessage{ static_cast<PCONSOLE_API_MSG>(lpParameter) };
ReceiveMsg = *capturedMessage.get();
ReceiveMsg._pApiRoutines = &globals.api;
ReceiveMsg._pApiRoutines = globals.api;
ReceiveMsg._pDeviceComm = globals.pDeviceComm;
IoSorter::ServiceIoOperation(&ReceiveMsg, &ReplyMsg);
}
@ -923,7 +924,7 @@ DWORD WINAPI ConsoleIoThread(LPVOID lpParameter)
ReplyMsg = nullptr;
continue;
}
ReceiveMsg._pApiRoutines = globals.api;
IoSorter::ServiceIoOperation(&ReceiveMsg, &ReplyMsg);
}

View file

@ -18,6 +18,10 @@ extern "C" {
#define PSEUDOCONSOLE_RESIZE_QUIRK (2u)
#define PSEUDOCONSOLE_WIN32_INPUT_MODE (4u)
#if TIL_FEATURE_VTPASSTHROUGHMODE_ENABLED
#define PSEUDOCONSOLE_PASSTHROUGH_MODE (8u)
#endif
HRESULT WINAPI ConptyCreatePseudoConsole(COORD size, HANDLE hInput, HANDLE hOutput, DWORD dwFlags, HPCON* phPC);
HRESULT WINAPI ConptyResizePseudoConsole(HPCON hPC, COORD size);

View file

@ -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;
}

View file

@ -302,6 +302,13 @@ using namespace Microsoft::Console::Render;
return _Write("\x1b[6n");
}
[[nodiscard]] HRESULT VtEngine::_ListenForDSR() noexcept
{
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !_pfnSetLookingForDSR);
_pfnSetLookingForDSR(true);
return S_OK;
}
// Method Description:
// - Formats and writes a sequence to change the terminal's title string
// Arguments:

View file

@ -28,8 +28,10 @@ Xterm256Engine::Xterm256Engine(_In_ wil::unique_hfile hPipe,
[[nodiscard]] HRESULT Xterm256Engine::UpdateDrawingBrushes(const TextAttribute& textAttributes,
const gsl::not_null<IRenderData*> 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));

View file

@ -18,6 +18,8 @@ Author(s):
#include "XtermEngine.hpp"
class VtApiRoutines;
namespace Microsoft::Console::Render
{
class Xterm256Engine : public XtermEngine
@ -35,6 +37,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,

View file

@ -44,6 +44,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

View file

@ -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: 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
@ -415,7 +447,7 @@ void VtEngine::EndResizeRequest()
// conpty scenario.
// - See also: GH#3490, #4354, #4741
// Arguments:
// - <none>
// - 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 +455,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<void(bool)> 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

View file

@ -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<uint64_t>(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

View file

@ -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::rectangle view) const;
void TraceLastText(const til::point lastText) const;

View file

@ -104,6 +104,9 @@ namespace Microsoft::Console::Render
void EndResizeRequest();
void SetResizeQuirk(const bool resizeQuirk);
void SetPassthroughMode(const bool passthrough) noexcept;
void SetLookingForDSRCallback(std::function<void(bool)> pfnLooking) noexcept;
void SetTerminalCursorTextPosition(const COORD coordCursor) noexcept;
[[nodiscard]] virtual HRESULT ManuallyClearScrollback() noexcept;
@ -118,6 +121,8 @@ namespace Microsoft::Console::Render
TextAttribute _lastTextAttributes;
std::function<void(bool)> _pfnSetLookingForDSR;
Microsoft::Console::Types::Viewport _lastViewport;
std::pmr::unsynchronized_pool_resource _pool;
@ -152,8 +157,10 @@ namespace Microsoft::Console::Render
bool _delayedEolWrap{ false };
bool _resizeQuirk{ false };
bool _passthrough{ false };
std::optional<TextColor> _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;
@ -212,6 +219,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT _EndHyperlink() noexcept;
[[nodiscard]] HRESULT _RequestCursor() noexcept;
[[nodiscard]] HRESULT _ListenForDSR() noexcept;
[[nodiscard]] HRESULT _RequestWin32Input() noexcept;

View file

@ -44,6 +44,8 @@ public:
#pragma endregion
virtual ~IApiRoutines(){};
#pragma region L1
virtual void GetConsoleInputCodePageImpl(ULONG& codepage) noexcept = 0;

View file

@ -105,6 +105,11 @@ InputStateMachineEngine::InputStateMachineEngine(std::unique_ptr<IInteractDispat
THROW_HR_IF_NULL(E_INVALIDARG, _pDispatch.get());
}
void InputStateMachineEngine::SetLookingForDSR(const bool looking) noexcept
{
_lookingForDSR = looking;
}
// Method Description:
// - Triggers the Execute action to indicate that the listener should
// immediately respond to a C0 control character.

View file

@ -131,6 +131,8 @@ namespace Microsoft::Console::VirtualTerminal
InputStateMachineEngine(std::unique_ptr<IInteractDispatch> pDispatch,
const bool lookingForDSR);
void SetLookingForDSR(const bool looking) noexcept;
bool ActionExecute(const wchar_t wch) override;
bool ActionExecuteFromEscape(const wchar_t wch) override;

View file

@ -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(),

View file

@ -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,