[FHL] Make VTApiRoutines, which does VT translation for output (#11264)

Make a VTApiRoutines servicer that does minimal translations instead of
environmental simulation for some output methods. Remaining methods are
backed on the existing console host infrastructure (primarily input
related methods).

## PR Checklist
* [x] I work here
* [x] It's Fix-Hack-Learn quality so it's behind a feature gate so we
  can keep refining it. But it's a start!

To turn this on, you will have to be in the Dev or Preview rings
(feature staged).  Then add `experimental.connection.passthroughMode:
true` to a profile and on the next launch, the flags will propagate down
through the `ConptyConnection` into the underlying `Openconsole.exe`
startup and tell it to use the passthrough mode instead of the full
simulation mode.

## Validation Steps Performed
- Played with it manually in CMD.exe, it seems to work mostly.
- Played with it manually in Ubuntu WSL, it seems to work.
- Played with it manually in Powershell and it's mostly sad. It'll get
  there.

Starts #1173
This commit is contained in:
Michael Niksa 2022-03-30 16:22:42 -07:00 committed by GitHub
parent a24d419b7b
commit 2c2f4f9be2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 1826 additions and 131 deletions

View File

@ -95,11 +95,12 @@ lround
Lsa
lsass
LSHIFT
memchr
memicmp
MENUCOMMAND
MENUDATA
MENUINFO
MENUITEMINFOW
MENUINFO
MOUSELEAVE
mov
mptt

View File

@ -519,6 +519,7 @@ DECAUPSS
DECAWM
DECCKM
DECCOLM
DECCRA
DECDHL
decdld
DECDLD
@ -2632,6 +2633,7 @@ VSTS
VSTT
vstudio
vswhere
vtapi
vtapp
VTE
VTID

View File

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

View File

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

View File

@ -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<void(int, int, int)> cal
_terminal->SetScrollPositionChangedCallback(callback);
}
void HwndTerminal::_WriteTextToConnection(const std::wstring& input) noexcept
void HwndTerminal::_WriteTextToConnection(const std::wstring_view input) noexcept
{
if (!_pfnWriteCallback)
{

View File

@ -108,7 +108,7 @@ private:
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;

View File

@ -1070,13 +1070,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
@ -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<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

@ -266,6 +266,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{})
@ -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

View File

@ -85,6 +85,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
til::u8state _u8State{};
std::wstring _u16Str{};
std::array<char, 4096> _buffer{};
bool _passthroughMode{};
DWORD _OutputThread();
};

View File

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

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

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

View File

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

@ -77,10 +77,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();
@ -97,6 +97,7 @@ public:
#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 @@ public:
const bool IsUiaDataInitialized() const noexcept 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;
@ -252,7 +253,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

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

View File

@ -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:
// - <none>
// 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:
// - <none>
// 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<std::wstring>(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:

View File

@ -75,6 +75,8 @@ public:
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 @@ private:
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();

View File

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

View File

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

View File

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

View File

@ -127,6 +127,16 @@
<ToggleSwitch IsOn="{x:Bind Profile.UseAtlasEngine, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- VtPassthrough -->
<local:SettingContainer x:Uid="Profile_VtPassthrough"
ClearSettingValue="{x:Bind Profile.ClearVtPassthrough}"
HasSettingValue="{x:Bind Profile.HasVtPassthrough, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.VtPassthroughOverrideSource, Mode=OneWay}"
Visibility="{x:Bind Profile.VtPassthroughAvailable}">
<ToggleSwitch IsOn="{x:Bind Profile.VtPassthrough, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
</StackPanel>
</ScrollViewer>
</Grid>

View File

@ -1043,6 +1043,10 @@
<value>Enable experimental text rendering engine</value>
<comment>An option to enable an experimental text rendering engine</comment>
</data>
<data name="Profile_VtPassthrough.Header" xml:space="preserve">
<value>Enable experimental virtual terminal passthrough</value>
<comment>An option to enable experimental virtual terminal passthrough connectivity option with the underlying ConPTY</comment>
</data>
<data name="Profile_BellStyleAudible.Content" xml:space="preserve">
<value>Audible</value>
<comment>An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user.</comment>

View File

@ -76,7 +76,8 @@ Author(s):
X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible) \
X(bool, UseAtlasEngine, "experimental.useAtlasEngine", false) \
X(Windows::Foundation::Collections::IVector<winrt::hstring>, 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

View File

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

View File

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

View File

@ -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<Microsoft::Terminal::Core::Color>, TabColor, nullptr);

View File

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

View File

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

View File

@ -84,4 +84,15 @@
<brandingToken>Dev</brandingToken>
</alwaysEnabledBrandingTokens>
</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();
}
};
}

863
src/host/VtApiRoutines.cpp Normal file
View File

@ -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<IWaitRoutine>& 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<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);
_SynchronizeCursor(waiter);
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);
_SynchronizeCursor(waiter);
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);
_SynchronizeCursor(waiter);
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);
_SynchronizeCursor(waiter);
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 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<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 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<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(static_cast<BYTE>(attribute), true);
(void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast<BYTE>(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<BYTE>(data.wAttributes), true);
(void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast<BYTE>(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<SMALL_RECT> 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<SMALL_RECT> 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<BYTE>(attribute), true);
(void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast<BYTE>(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(static_cast<BYTE>(ci.Attributes), true);
(void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast<BYTE>(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<const WORD> attrs,
const COORD target,
size_t& used) noexcept
{
(void)m_pVtEngine->_CursorPosition(target);
for (const auto& attr : attrs)
{
(void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast<BYTE>(attr), true);
(void)m_pVtEngine->_SetGraphicsRendition16Color(static_cast<BYTE>(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<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 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<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 GH10001: 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)

392
src/host/VtApiRoutines.h Normal file
View File

@ -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<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;
private:
void _SynchronizeCursor(std::unique_ptr<IWaitRoutine>& waiter) noexcept;
};

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

@ -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<Xterm256Engine>(std::move(_hOutput),
initialViewport);
{
auto xterm256Engine = std::make_unique<Xterm256Engine>(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<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

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

@ -604,9 +604,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

@ -7,6 +7,11 @@
#pragma hdrstop
Globals::Globals()
{
api = &defaultApiRoutines;
}
CONSOLE_INFORMATION& Globals::getConsoleInformation()
{
return ciConsoleInformation;

View File

@ -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 @@ public:
bool IsHeadless() const;
ApiRoutines api;
IApiRoutines* api;
bool handoffTarget = false;
@ -80,4 +82,5 @@ public:
private:
CONSOLE_INFORMATION ciConsoleInformation;
ApiRoutines defaultApiRoutines;
};

View File

@ -52,6 +52,7 @@
<ClCompile Include="..\tracing.cpp" />
<ClCompile Include="..\utils.cpp" />
<ClCompile Include="..\utf8ToWideCharParser.cpp" />
<ClCompile Include="..\VtApiRoutines.cpp" />
<ClCompile Include="..\VtInputThread.cpp" />
<ClCompile Include="..\VtIo.cpp" />
<ClCompile Include="..\writeData.cpp" />
@ -110,6 +111,7 @@
<ClInclude Include="..\tracing.hpp" />
<ClInclude Include="..\utils.hpp" />
<ClInclude Include="..\utf8ToWideCharParser.hpp" />
<ClInclude Include="..\VtApiRoutines.h" />
<ClInclude Include="..\VtInputThread.hpp" />
<ClInclude Include="..\VtIo.hpp" />
<ClInclude Include="..\writeData.hpp" />

View File

@ -177,6 +177,9 @@
<ClCompile Include="..\CursorBlinker.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\VtApiRoutines.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\precomp.h">
@ -347,8 +350,11 @@
<ClInclude Include="..\IIoProvider.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\VtApiRoutines.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup>
</Project>
</Project>

View File

@ -65,7 +65,7 @@ void ConhostInternalGetSet::PrintString(const std::wstring_view string)
// - <none>
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
// - <none>
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<std::unique_ptr<IInputEvent>>&
// - <none>
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:

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

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

View File

@ -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<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);
}
@ -984,7 +984,7 @@ DWORD WINAPI ConsoleIoThread(LPVOID lpParameter)
ReplyMsg = nullptr;
continue;
}
ReceiveMsg._pApiRoutines = globals.api;
IoSorter::ServiceIoOperation(&ReceiveMsg, &ReplyMsg);
}

View File

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

View File

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

View File

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

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

@ -30,8 +30,10 @@ Xterm256Engine::Xterm256Engine(_In_ wil::unique_hfile hPipe,
const RenderSettings& /*renderSettings*/,
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
@ -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,

View File

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

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 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:
// - <none>
// 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:
// - <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 +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<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::rect& view) const;
void TraceLastText(const til::point lastText) const;

View File

@ -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<void(bool)> 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<void(bool)> _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<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;
@ -186,6 +193,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

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

@ -298,6 +298,7 @@ static std::vector<DWORD> 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<std::wstring>& 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<std::wstring> 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");

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,