Use Char[] for Connection stdin (#19655)

This makes it possible to pass through non-null-terminated strings.
This is needed for the tmux control mode which passes string slices.
This commit is contained in:
Leonard Hecker
2025-12-17 18:25:26 +01:00
committed by GitHub
parent aefacd6a12
commit f321c30cc1
14 changed files with 49 additions and 89 deletions

View File

@@ -120,9 +120,9 @@ namespace winrt::Microsoft::TerminalApp::implementation
return ConnectionState::Failed; return ConnectionState::Failed;
} }
void DebugTapConnection::_OutputHandler(const std::wstring_view str) void DebugTapConnection::_OutputHandler(const winrt::array_view<const char16_t> str)
{ {
auto output = til::visualize_control_codes(str); auto output = til::visualize_control_codes(winrt_array_to_wstring_view(str));
// To make the output easier to read, we introduce a line break whenever // To make the output easier to read, we introduce a line break whenever
// an LF control is encountered. But at this point, the LF would have // an LF control is encountered. But at this point, the LF would have
// been converted to U+240A (␊), so that's what we need to search for. // been converted to U+240A (␊), so that's what we need to search for.
@@ -130,7 +130,7 @@ namespace winrt::Microsoft::TerminalApp::implementation
{ {
output.insert(++lfPos, L"\r\n"); output.insert(++lfPos, L"\r\n");
} }
TerminalOutput.raise(output); TerminalOutput.raise(winrt_wstring_to_array_view(output));
} }
// Called by the DebugInputTapConnection to print user input // Called by the DebugInputTapConnection to print user input
@@ -138,7 +138,7 @@ namespace winrt::Microsoft::TerminalApp::implementation
{ {
auto clean{ til::visualize_control_codes(str) }; auto clean{ til::visualize_control_codes(str) };
auto formatted{ wil::str_printf<std::wstring>(L"\x1b[91m%ls\x1b[m", clean.data()) }; auto formatted{ wil::str_printf<std::wstring>(L"\x1b[91m%ls\x1b[m", clean.data()) };
TerminalOutput.raise(formatted); TerminalOutput.raise(winrt_wstring_to_array_view(formatted));
} }
// Wire us up so that we can forward input through // Wire us up so that we can forward input through

View File

@@ -31,7 +31,7 @@ namespace winrt::Microsoft::TerminalApp::implementation
private: private:
void _PrintInput(const std::wstring_view data); void _PrintInput(const std::wstring_view data);
void _OutputHandler(const std::wstring_view str); void _OutputHandler(const winrt::array_view<const char16_t> str);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection::TerminalOutput_revoker _outputRevoker; winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection::TerminalOutput_revoker _outputRevoker;
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection::StateChanged_revoker _stateChangedRevoker; winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection::StateChanged_revoker _stateChangedRevoker;

View File

@@ -94,9 +94,15 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - helper that will write an unterminated string (generally, from a resource) to the output stream. // - helper that will write an unterminated string (generally, from a resource) to the output stream.
// Arguments: // Arguments:
// - str: the string to write. // - str: the string to write.
void AzureConnection::_WriteStringWithNewline(std::wstring str)
{
str.append(L"\r\n");
TerminalOutput.raise(winrt_wstring_to_array_view(str));
}
void AzureConnection::_WriteStringWithNewline(const std::wstring_view str) void AzureConnection::_WriteStringWithNewline(const std::wstring_view str)
{ {
TerminalOutput.raise(str + L"\r\n"); TerminalOutput.raise(winrt_wstring_to_array_view(str + L"\r\n"));
} }
// Method description: // Method description:
@@ -112,7 +118,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
catch (const std::exception& runtimeException) catch (const std::exception& runtimeException)
{ {
// This also catches the AzureException, which has a .what() // This also catches the AzureException, which has a .what()
TerminalOutput.raise(_colorize(91, til::u8u16(std::string{ runtimeException.what() }))); TerminalOutput.raise(winrt_wstring_to_array_view(_colorize(91, til::u8u16(std::string{ runtimeException.what() }))));
} }
catch (...) catch (...)
{ {
@@ -162,13 +168,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
_currentInputMode = mode; _currentInputMode = mode;
TerminalOutput.raise(L"> \x1b[92m"); // Make prompted user input green TerminalOutput.raise(winrt_wstring_to_array_view(L"> \x1b[92m")); // Make prompted user input green
_inputEvent.wait(inputLock, [this, mode]() { _inputEvent.wait(inputLock, [this, mode]() {
return _currentInputMode != mode || _isStateAtOrBeyond(ConnectionState::Closing); return _currentInputMode != mode || _isStateAtOrBeyond(ConnectionState::Closing);
}); });
TerminalOutput.raise(L"\x1b[m"); TerminalOutput.raise(winrt_wstring_to_array_view(L"\x1b[m"));
if (_isStateAtOrBeyond(ConnectionState::Closing)) if (_isStateAtOrBeyond(ConnectionState::Closing))
{ {
@@ -211,19 +217,19 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
if (_userInput.size() > 0) if (_userInput.size() > 0)
{ {
_userInput.pop_back(); _userInput.pop_back();
TerminalOutput.raise(L"\x08 \x08"); // overstrike the character with a space TerminalOutput.raise(winrt_wstring_to_array_view(L"\x08 \x08")); // overstrike the character with a space
} }
} }
else else
{ {
TerminalOutput.raise(data); // echo back TerminalOutput.raise(winrt_wstring_to_array_view(data)); // echo back
switch (_currentInputMode) switch (_currentInputMode)
{ {
case InputMode::Line: case InputMode::Line:
if (data.size() > 0 && gsl::at(data, 0) == UNICODE_CARRIAGERETURN) if (data.size() > 0 && gsl::at(data, 0) == UNICODE_CARRIAGERETURN)
{ {
TerminalOutput.raise(L"\r\n"); // we probably got a \r, so we need to advance to the next line. TerminalOutput.raise(winrt_wstring_to_array_view(L"\r\n")); // we probably got a \r, so we need to advance to the next line.
_currentInputMode = InputMode::None; // toggling the mode indicates completion _currentInputMode = InputMode::None; // toggling the mode indicates completion
_inputEvent.notify_one(); _inputEvent.notify_one();
break; break;
@@ -429,7 +435,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
} }
// Pass the output to our registered event handlers // Pass the output to our registered event handlers
TerminalOutput.raise(_u16Str); TerminalOutput.raise(winrt_wstring_to_array_view(_u16Str));
break; break;
} }
case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE: case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE:
@@ -772,7 +778,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const auto shellType = _ParsePreferredShellType(settingsResponse); const auto shellType = _ParsePreferredShellType(settingsResponse);
_WriteStringWithNewline(RS_(L"AzureRequestingTerminal")); _WriteStringWithNewline(RS_(L"AzureRequestingTerminal"));
const auto socketUri = _GetTerminal(shellType); const auto socketUri = _GetTerminal(shellType);
TerminalOutput.raise(L"\r\n"); TerminalOutput.raise(winrt_wstring_to_array_view(L"\r\n"));
//// Step 8: connecting to said terminal //// Step 8: connecting to said terminal
{ {

View File

@@ -67,6 +67,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
std::optional<::Microsoft::Terminal::Azure::Tenant> _currentTenant; std::optional<::Microsoft::Terminal::Azure::Tenant> _currentTenant;
void _writeInput(const std::wstring_view str); void _writeInput(const std::wstring_view str);
void _WriteStringWithNewline(std::wstring str);
void _WriteStringWithNewline(const std::wstring_view str); void _WriteStringWithNewline(const std::wstring_view str);
void _WriteCaughtExceptionRecord(); void _WriteCaughtExceptionRecord();
winrt::Windows::Data::Json::JsonObject _SendRequestReturningJson(std::wstring_view uri, const winrt::Windows::Web::Http::IHttpContent& content = nullptr, winrt::Windows::Web::Http::HttpMethod method = nullptr, const winrt::Windows::Foundation::Uri referer = nullptr); winrt::Windows::Data::Json::JsonObject _SendRequestReturningJson(std::wstring_view uri, const winrt::Windows::Web::Http::IHttpContent& content = nullptr, winrt::Windows::Web::Http::HttpMethod method = nullptr, const winrt::Windows::Foundation::Uri referer = nullptr);

View File

@@ -477,31 +477,28 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const auto hr = wil::ResultFromCaughtException(); const auto hr = wil::ResultFromCaughtException();
// GH#11556 - make sure to format the error code to this string as an UNSIGNED int // GH#11556 - make sure to format the error code to this string as an UNSIGNED int
const auto failureText = RS_fmt(L"ProcessFailedToLaunch", _formatStatus(hr), _commandline); auto failureText = RS_fmt(L"ProcessFailedToLaunch", _formatStatus(hr), _commandline);
TerminalOutput.raise(failureText);
// If the path was invalid, let's present an informative message to the user // If the path was invalid, let's present an informative message to the user
if (hr == HRESULT_FROM_WIN32(ERROR_DIRECTORY)) if (hr == HRESULT_FROM_WIN32(ERROR_DIRECTORY))
{ {
const auto badPathText = RS_fmt(L"BadPathText", _startingDirectory); failureText.append(L"\r\n");
TerminalOutput.raise(L"\r\n"); failureText.append(RS_fmt(L"BadPathText", _startingDirectory));
TerminalOutput.raise(badPathText);
} }
// If the requested action requires elevation, display appropriate message // If the requested action requires elevation, display appropriate message
else if (hr == HRESULT_FROM_WIN32(ERROR_ELEVATION_REQUIRED)) else if (hr == HRESULT_FROM_WIN32(ERROR_ELEVATION_REQUIRED))
{ {
const auto elevationText = RS_(L"ElevationRequired"); failureText.append(L"\r\n");
TerminalOutput.raise(L"\r\n"); failureText.append(RS_(L"ElevationRequired"));
TerminalOutput.raise(elevationText);
} }
// If the requested executable was not found, display appropriate message // If the requested executable was not found, display appropriate message
else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
{ {
const auto fileNotFoundText = RS_(L"FileNotFound"); failureText.append(L"\r\n");
TerminalOutput.raise(L"\r\n"); failureText.append(RS_(L"FileNotFound"));
TerminalOutput.raise(fileNotFoundText);
} }
TerminalOutput.raise(winrt_wstring_to_array_view(failureText));
_transitionToState(ConnectionState::Failed); _transitionToState(ConnectionState::Failed);
// Tear down any state we may have accumulated. // Tear down any state we may have accumulated.
@@ -520,7 +517,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const auto msg1 = RS_fmt(L"ProcessExited", _formatStatus(status)); const auto msg1 = RS_fmt(L"ProcessExited", _formatStatus(status));
const auto msg2 = RS_(L"CtrlDToClose"); const auto msg2 = RS_(L"CtrlDToClose");
const auto msg = fmt::format(FMT_COMPILE(L"\r\n{}\r\n{}\r\n"), msg1, msg2); const auto msg = fmt::format(FMT_COMPILE(L"\r\n{}\r\n{}\r\n"), msg1, msg2);
TerminalOutput.raise(msg); TerminalOutput.raise(winrt_wstring_to_array_view(msg));
} }
CATCH_LOG(); CATCH_LOG();
} }
@@ -792,7 +789,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
try try
{ {
TerminalOutput.raise(wstr); TerminalOutput.raise(winrt_wstring_to_array_view(wstr));
} }
CATCH_LOG(); CATCH_LOG();
} }

View File

@@ -34,7 +34,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
prettyPrint << wch; prettyPrint << wch;
} }
} }
TerminalOutput.raise(prettyPrint.str()); TerminalOutput.raise(winrt_wstring_to_array_view(prettyPrint.str()));
} }
void EchoConnection::Resize(uint32_t /*rows*/, uint32_t /*columns*/) noexcept void EchoConnection::Resize(uint32_t /*rows*/, uint32_t /*columns*/) noexcept

View File

@@ -13,7 +13,7 @@ namespace Microsoft.Terminal.TerminalConnection
Failed Failed
}; };
delegate void TerminalOutputHandler(String output); delegate void TerminalOutputHandler(Char[] output);
interface ITerminalConnection interface ITerminalConnection
{ {

View File

@@ -10,8 +10,6 @@
#include <DefaultSettings.h> #include <DefaultSettings.h>
#include <unicode.hpp> #include <unicode.hpp>
#include <utils.hpp>
#include <WinUser.h>
#include "EventArgs.h" #include "EventArgs.h"
#include "../../renderer/atlas/AtlasEngine.h" #include "../../renderer/atlas/AtlasEngine.h"
@@ -2238,13 +2236,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto noticeArgs = winrt::make<NoticeEventArgs>(NoticeLevel::Info, RS_(L"TermControlReadOnly")); auto noticeArgs = winrt::make<NoticeEventArgs>(NoticeLevel::Info, RS_(L"TermControlReadOnly"));
RaiseNotice.raise(*this, std::move(noticeArgs)); RaiseNotice.raise(*this, std::move(noticeArgs));
} }
void ControlCore::_connectionOutputHandler(const hstring& hstr) void ControlCore::_connectionOutputHandler(const winrt::array_view<const char16_t> str)
{ {
try try
{ {
{ {
const auto lock = _terminal->LockForWriting(); const auto lock = _terminal->LockForWriting();
_terminal->Write(hstr); _terminal->Write(winrt_array_to_wstring_view(str));
} }
if (!_pendingResponses.empty()) if (!_pendingResponses.empty())

View File

@@ -349,7 +349,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _raiseReadOnlyWarning(); void _raiseReadOnlyWarning();
void _updateAntiAliasingMode(); void _updateAntiAliasingMode();
void _connectionOutputHandler(const hstring& hstr); void _connectionOutputHandler(winrt::array_view<const char16_t> str);
void _connectionStateChangedHandler(const TerminalConnection::ITerminalConnection&, const Windows::Foundation::IInspectable&); void _connectionStateChangedHandler(const TerminalConnection::ITerminalConnection&, const Windows::Foundation::IInspectable&);
void _updateHoveredCell(const std::optional<til::point> terminalPosition); void _updateHoveredCell(const std::optional<til::point> terminalPosition);
void _setOpacity(const float opacity, const bool focused = true); void _setOpacity(const float opacity, const bool focused = true);

View File

@@ -28,10 +28,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{ {
PreviewConnection::PreviewConnection() noexcept = default; PreviewConnection::PreviewConnection() noexcept = default;
void PreviewConnection::Start() noexcept void PreviewConnection::Start()
{ {
// Send the preview text const auto prompt = _displayPowerlineGlyphs ? PromptTextPowerline : PromptTextPlain;
TerminalOutput.raise(fmt::format(PreviewText, _displayPowerlineGlyphs ? PromptTextPowerline : PromptTextPlain)); const auto text = fmt::format(FMT_COMPILE(PreviewText), prompt);
TerminalOutput.raise(winrt_wstring_to_array_view(text));
} }
void PreviewConnection::Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) noexcept void PreviewConnection::Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) noexcept
@@ -50,7 +51,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{ {
} }
void PreviewConnection::DisplayPowerlineGlyphs(bool d) noexcept void PreviewConnection::DisplayPowerlineGlyphs(bool d)
{ {
if (_displayPowerlineGlyphs != d) if (_displayPowerlineGlyphs != d)
{ {

View File

@@ -22,12 +22,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
PreviewConnection() noexcept; PreviewConnection() noexcept;
void Initialize(const Windows::Foundation::Collections::ValueSet& settings) noexcept; void Initialize(const Windows::Foundation::Collections::ValueSet& settings) noexcept;
void Start() noexcept; void Start();
void WriteInput(const winrt::array_view<const char16_t> buffer); void WriteInput(const winrt::array_view<const char16_t> buffer);
void Resize(uint32_t rows, uint32_t columns) noexcept; void Resize(uint32_t rows, uint32_t columns) noexcept;
void Close() noexcept; void Close() noexcept;
void DisplayPowerlineGlyphs(bool d) noexcept; void DisplayPowerlineGlyphs(bool d);
winrt::guid SessionId() const noexcept { return {}; } winrt::guid SessionId() const noexcept { return {}; }
winrt::Microsoft::Terminal::TerminalConnection::ConnectionState State() const noexcept { return winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::Connected; } winrt::Microsoft::Terminal::TerminalConnection::ConnectionState State() const noexcept { return winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::Connected; }

View File

@@ -104,13 +104,14 @@ namespace ControlUnitTests
auto _addInputCallback(const winrt::com_ptr<MockConnection>& conn, auto _addInputCallback(const winrt::com_ptr<MockConnection>& conn,
std::deque<std::wstring>& expectedOutput) std::deque<std::wstring>& expectedOutput)
{ {
conn->TerminalOutput([&](const hstring& hstr) { conn->TerminalOutput([&](const winrt::array_view<const char16_t> str) {
VERIFY_IS_GREATER_THAN(expectedOutput.size(), 0u); VERIFY_IS_GREATER_THAN(expectedOutput.size(), 0u);
const auto actual = winrt_array_to_wstring_view(str);
const auto expected = expectedOutput.front(); const auto expected = expectedOutput.front();
expectedOutput.pop_front(); expectedOutput.pop_front();
Log::Comment(fmt::format(L"Received: \"{}\"", TerminalCoreUnitTests::TestUtils::ReplaceEscapes(hstr.c_str())).c_str()); Log::Comment(fmt::format(L"Received: \"{}\"", til::visualize_nonspace_control_codes(std::wstring{ actual })).c_str());
Log::Comment(fmt::format(L"Expected: \"{}\"", TerminalCoreUnitTests::TestUtils::ReplaceEscapes(expected)).c_str()); Log::Comment(fmt::format(L"Expected: \"{}\"", til::visualize_nonspace_control_codes(expected)).c_str());
VERIFY_ARE_EQUAL(expected, hstr); VERIFY_ARE_EQUAL(expected, actual);
}); });
return std::move(wil::scope_exit([&]() { return std::move(wil::scope_exit([&]() {

View File

@@ -18,7 +18,7 @@ namespace ControlUnitTests
void Start() noexcept {}; void Start() noexcept {};
void WriteInput(const winrt::array_view<const char16_t> data) void WriteInput(const winrt::array_view<const char16_t> data)
{ {
TerminalOutput.raise(winrt_array_to_wstring_view(data)); TerminalOutput.raise(data);
} }
void Resize(uint32_t /*rows*/, uint32_t /*columns*/) noexcept {} void Resize(uint32_t /*rows*/, uint32_t /*columns*/) noexcept {}
void Close() noexcept {} void Close() noexcept {}

View File

@@ -109,50 +109,6 @@ public:
return iter; return iter;
}; };
// Function Description:
// - Replaces all escapes with the printable symbol for that escape
// character. This makes log parsing easier for debugging, as the literal
// escapes won't be written to the console output.
// Arguments:
// - str: the string to escape.
// Return Value:
// - A modified version of that string with non-printable characters replaced.
static std::string ReplaceEscapes(const std::string& str)
{
std::string escaped = str;
auto replaceFn = [&escaped](const std::string& search, const std::string& replace) {
size_t pos = escaped.find(search, 0);
while (pos != std::string::npos)
{
escaped.replace(pos, search.length(), replace);
pos = escaped.find(search, pos + replace.length());
}
};
replaceFn("\x1b", "^\x5b"); // ESC
replaceFn("\x08", "^\x48"); // BS
replaceFn("\x0A", "^\x4A"); // LF
replaceFn("\x0D", "^\x4D"); // CR
return escaped;
}
// Function Description:
// - Replaces all escapes with the printable symbol for that escape
// character. This makes log parsing easier for debugging, as the literal
// escapes won't be written to the console output.
// Arguments:
// - wstr: the string to escape.
// Return Value:
// - A modified version of that string with non-printable characters replaced.
static std::wstring ReplaceEscapes(const std::wstring& wstr)
{
std::wstring escaped = wstr;
std::replace(escaped.begin(), escaped.end(), L'\x1b', L'\x241b'); // ESC
std::replace(escaped.begin(), escaped.end(), L'\x08', L'\x2408'); // BS
std::replace(escaped.begin(), escaped.end(), L'\x0A', L'\x240A'); // LF
std::replace(escaped.begin(), escaped.end(), L'\x0D', L'\x240D'); // CR
return escaped;
}
template<class... T> template<class... T>
static bool VerifyLineContains(TextBufferCellIterator& actual, T&&... expectedContent) static bool VerifyLineContains(TextBufferCellIterator& actual, T&&... expectedContent)
{ {