Compare commits

...

11 Commits

Author SHA1 Message Date
Dustin L. Howett
cd2a396b5e Merge remote-tracking branch 'origin/main' into dev/duhowett/connection-utf8 2025-12-17 12:42:08 -06:00
Dustin L. Howett
9c46e34a3f Make WriteOutput take utf-8 as well 2025-12-10 13:08:02 -06:00
Dustin L. Howett
b2d54c146a Az: switch some static strings to u8 2025-12-10 12:25:00 -06:00
Dustin L. Howett
4fdb3fafb2 Debug: use a clever utf8 2025-12-10 10:45:08 -06:00
Dustin L. Howett
5bfbbbd7a2 Echo: use BaseTerminalConnection 2025-12-10 10:45:00 -06:00
Dustin L. Howett
7bb324d047 Transition dhSend16 to a helper 2025-12-09 20:35:15 -06:00
Dustin L. Howett
3d5342d60d Use Helper 2025-12-09 16:12:06 -06:00
Dustin L. Howett
48603ac834 format+helpers 2025-12-08 12:44:02 -06:00
Dustin L. Howett
faa5f8b08b leave a note-to-self 2025-12-05 21:28:39 -06:00
Dustin L. Howett
3f48fe6760 ok, it builds and works 2025-12-05 21:28:18 -06:00
Dustin L. Howett
db65c42f2c What if the connection wrote its output as a utf-8 array? 2025-12-05 21:13:30 -06:00
16 changed files with 166 additions and 131 deletions

View File

@@ -39,9 +39,9 @@ namespace winrt::Microsoft::TerminalApp::implementation
_wrappedConnection.Start();
}
void WriteInput(const winrt::array_view<const char16_t> buffer)
void WriteInput(const winrt::array_view<const uint8_t> buffer)
{
_pairedTap->_PrintInput(winrt_array_to_wstring_view(buffer));
_pairedTap->_PrintInput(winrt_array_to_string_view(buffer));
_wrappedConnection.WriteInput(buffer);
}
void Resize(uint32_t rows, uint32_t columns) { _wrappedConnection.Resize(rows, columns); }
@@ -80,7 +80,7 @@ namespace winrt::Microsoft::TerminalApp::implementation
_start.count_down();
}
void DebugTapConnection::WriteInput(const winrt::array_view<const char16_t> buffer)
void DebugTapConnection::WriteInput(const winrt::array_view<const uint8_t> buffer)
{
// If the user types into the tap side, forward it to the input side
if (auto strongInput{ _inputSide.get() })
@@ -120,25 +120,44 @@ namespace winrt::Microsoft::TerminalApp::implementation
return ConnectionState::Failed;
}
void DebugTapConnection::_OutputHandler(const winrt::array_view<const char16_t> str)
void DebugTapConnection::_OutputHandler(const winrt::array_view<const uint8_t>& 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
// 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.
for (size_t lfPos = 0; (lfPos = output.find(L'\u240A', lfPos)) != std::wstring::npos;)
uint8_t buffer[5] = { 0xe2, 0x90, 0x00, '\r', '\n' };
uint32_t i = 0, s = 0;
while (i < str.size())
{
output.insert(++lfPos, L"\r\n");
while (i < str.size() && str[i] > 0x20 && str[i] != 0x7f)
{
++i;
}
if (i > s)
{
TerminalOutput.raise(winrt::array_view{ str.data() + s, (i - s) });
s = i;
}
if (i >= str.size())
break;
auto ch = str[i];
if (ch == '\x20')
buffer[2] = 0xA3;
else if (ch == '\x7f')
buffer[2] = 0xA1;
else
buffer[2] = 0x80 | ch;
// If we encountered a LF, emit the extra \r\n from the buffer to pretty print it.
TerminalOutput.raise(winrt::array_view{ buffer, ch == '\x0a' ? 5u : 3u });
++i;
++s;
}
TerminalOutput.raise(winrt_wstring_to_array_view(output));
}
// Called by the DebugInputTapConnection to print user input
void DebugTapConnection::_PrintInput(const std::wstring_view str)
void DebugTapConnection::_PrintInput(const std::string_view str)
{
auto clean{ til::visualize_control_codes(str) };
auto formatted{ wil::str_printf<std::wstring>(L"\x1b[91m%ls\x1b[m", clean.data()) };
TerminalOutput.raise(winrt_wstring_to_array_view(formatted));
static constexpr std::string_view buffer{ "\x1b[91m\x1b[m" }; // two escape sequences we need to write, back to back
TerminalOutput.raise(winrt_u8string_to_array_view(buffer.substr(0,5)));
_OutputHandler(winrt_u8string_to_array_view(str));
TerminalOutput.raise(winrt_u8string_to_array_view(buffer.substr(5)));
}
// Wire us up so that we can forward input through

View File

@@ -16,7 +16,7 @@ namespace winrt::Microsoft::TerminalApp::implementation
void Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/){};
~DebugTapConnection();
void Start();
void WriteInput(const winrt::array_view<const char16_t> data);
void WriteInput(const winrt::array_view<const uint8_t> data);
void Resize(uint32_t rows, uint32_t columns);
void Close();
@@ -30,8 +30,8 @@ namespace winrt::Microsoft::TerminalApp::implementation
til::typed_event<winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection, winrt::Windows::Foundation::IInspectable> StateChanged;
private:
void _PrintInput(const std::wstring_view data);
void _OutputHandler(const winrt::array_view<const char16_t> str);
void _PrintInput(const std::string_view data);
void _OutputHandler(const winrt::array_view<const uint8_t>& str);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection::TerminalOutput_revoker _outputRevoker;
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection::StateChanged_revoker _stateChangedRevoker;

View File

@@ -94,15 +94,9 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - helper that will write an unterminated string (generally, from a resource) to the output stream.
// Arguments:
// - 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)
{
TerminalOutput.raise(winrt_wstring_to_array_view(str + L"\r\n"));
WriteUtf16Output(str + L"\r\n");
}
// Method description:
@@ -118,7 +112,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
catch (const std::exception& runtimeException)
{
// This also catches the AzureException, which has a .what()
TerminalOutput.raise(winrt_wstring_to_array_view(_colorize(91, til::u8u16(std::string{ runtimeException.what() }))));
WriteUtf16Output(_colorize(91, til::u8u16(std::string{ runtimeException.what() })));
}
catch (...)
{
@@ -157,7 +151,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
_transitionToState(ConnectionState::Connecting);
}
std::optional<std::wstring> AzureConnection::_ReadUserInput(InputMode mode)
std::optional<std::string> AzureConnection::_ReadUserInput(InputMode mode)
{
std::unique_lock<std::mutex> inputLock{ _inputMutex };
@@ -168,20 +162,20 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
_currentInputMode = mode;
TerminalOutput.raise(winrt_wstring_to_array_view(L"> \x1b[92m")); // Make prompted user input green
WriteUtf8Output("> \x1b[92m"); // Make prompted user input green
_inputEvent.wait(inputLock, [this, mode]() {
return _currentInputMode != mode || _isStateAtOrBeyond(ConnectionState::Closing);
});
TerminalOutput.raise(winrt_wstring_to_array_view(L"\x1b[m"));
WriteUtf8Output("\x1b[m");
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
return std::nullopt;
}
std::wstring readInput{};
std::string readInput{};
_userInput.swap(readInput);
return readInput;
}
@@ -191,12 +185,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - handles the different possible inputs in the different states
// Arguments:
// the user's input
void AzureConnection::WriteInput(const winrt::array_view<const char16_t> buffer)
void AzureConnection::WriteInput(const winrt::array_view<const uint8_t> buffer)
{
_writeInput(winrt_array_to_wstring_view(buffer));
_writeInput(winrt_array_to_string_view(buffer));
}
void AzureConnection::_writeInput(const std::wstring_view data)
void AzureConnection::_writeInput(const std::string_view data)
{
// We read input while connected AND connecting.
if (!_isStateOneOf(ConnectionState::Connected, ConnectionState::Connecting))
@@ -206,8 +200,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
if (_state == AzureState::TermConnected)
{
auto buff{ winrt::to_string(data) };
WinHttpWebSocketSend(_webSocket.get(), WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE, buff.data(), gsl::narrow<DWORD>(buff.size()));
WinHttpWebSocketSend(_webSocket.get(), WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE, const_cast<char*>(data.data()), gsl::narrow<DWORD>(data.size()));
return;
}
@@ -217,19 +210,19 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
if (_userInput.size() > 0)
{
_userInput.pop_back();
TerminalOutput.raise(winrt_wstring_to_array_view(L"\x08 \x08")); // overstrike the character with a space
WriteUtf8Output("\x08 \x08"); // overstrike the character with a space
}
}
else
{
TerminalOutput.raise(winrt_wstring_to_array_view(data)); // echo back
WriteUtf8Output(data); // echo back
switch (_currentInputMode)
{
case InputMode::Line:
if (data.size() > 0 && gsl::at(data, 0) == UNICODE_CARRIAGERETURN)
{
TerminalOutput.raise(winrt_wstring_to_array_view(L"\r\n")); // we probably got a \r, so we need to advance to the next line.
WriteUtf8Output("\r\n"); // we probably got a \r, so we need to advance to the next line.
_currentInputMode = InputMode::None; // toggling the mode indicates completion
_inputEvent.notify_one();
break;
@@ -421,21 +414,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
case WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE:
case WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE:
{
const auto result{ til::u8u16(std::string_view{ _buffer.data(), read }, _u16Str, _u8State) };
if (FAILED(result))
{
// EXIT POINT
_transitionToState(ConnectionState::Failed);
return gsl::narrow<DWORD>(result);
}
if (_u16Str.empty())
if (read == 0)
{
continue;
}
// Pass the output to our registered event handlers
TerminalOutput.raise(winrt_wstring_to_array_view(_u16Str));
TerminalOutput.raise(winrt::array_view{ reinterpret_cast<const uint8_t*>(_buffer.data()), read });
break;
}
case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE:
@@ -532,14 +517,14 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
const auto& tenantSelection = maybeTenantSelection.value();
if (tenantSelection == RS_(L"AzureUserEntry_RemoveStored"))
if (tenantSelection == RS_A(L"AzureUserEntry_RemoveStored"))
{
// User wants to remove the stored settings
_RemoveCredentials();
_state = AzureState::DeviceFlow;
return;
}
else if (tenantSelection == RS_(L"AzureUserEntry_NewLogin"))
else if (tenantSelection == RS_A(L"AzureUserEntry_NewLogin"))
{
// User wants to login with a different account
_state = AzureState::DeviceFlow;
@@ -720,13 +705,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
const auto& storeCredentials = maybeStoreCredentials.value();
if (storeCredentials == RS_(L"AzureUserEntry_Yes"))
if (storeCredentials == RS_A(L"AzureUserEntry_Yes"))
{
_StoreCredential();
_WriteStringWithNewline(RS_(L"AzureTokensStored"));
break;
}
else if (storeCredentials == RS_(L"AzureUserEntry_No"))
else if (storeCredentials == RS_A(L"AzureUserEntry_No"))
{
break; // we're done, but the user wants nothing.
}
@@ -778,7 +763,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const auto shellType = _ParsePreferredShellType(settingsResponse);
_WriteStringWithNewline(RS_(L"AzureRequestingTerminal"));
const auto socketUri = _GetTerminal(shellType);
TerminalOutput.raise(winrt_wstring_to_array_view(L"\r\n"));
WriteUtf16Output(L"\r\n");
//// Step 8: connecting to said terminal
{
@@ -810,7 +795,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
_state = AzureState::TermConnected;
std::wstring queuedUserInput{};
std::string queuedUserInput{};
std::swap(_userInput, queuedUserInput);
if (queuedUserInput.size() > 0)
{

View File

@@ -22,12 +22,10 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
void Initialize(const Windows::Foundation::Collections::ValueSet& settings);
void Start();
void WriteInput(const winrt::array_view<const char16_t> buffer);
void WriteInput(const winrt::array_view<const uint8_t> buffer);
void Resize(uint32_t rows, uint32_t columns);
void Close();
til::event<TerminalOutputHandler> TerminalOutput;
private:
til::CoordType _initialRows{};
til::CoordType _initialCols{};
@@ -66,8 +64,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
std::vector<::Microsoft::Terminal::Azure::Tenant> _tenantList;
std::optional<::Microsoft::Terminal::Azure::Tenant> _currentTenant;
void _writeInput(const std::wstring_view str);
void _WriteStringWithNewline(std::wstring str);
void _writeInput(const std::string_view str);
void _WriteStringWithNewline(const std::wstring_view str);
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);
@@ -88,19 +85,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
Line
};
InputMode _currentInputMode{ InputMode::None };
std::wstring _userInput;
std::string _userInput;
std::condition_variable _inputEvent;
std::mutex _inputMutex;
std::optional<std::wstring> _ReadUserInput(InputMode mode);
std::optional<std::string> _ReadUserInput(InputMode mode);
winrt::Windows::Web::Http::HttpClient _httpClient{ nullptr };
wil::unique_winhttp_hinternet _socketSessionHandle;
wil::unique_winhttp_hinternet _socketConnectionHandle;
wil::unique_winhttp_hinternet _webSocket;
til::u8state _u8State{};
std::wstring _u16Str;
std::array<char, 4096> _buffer{};
static winrt::hstring _ParsePreferredShellType(const winrt::Windows::Data::Json::JsonObject& settingsResponse);

View File

@@ -18,6 +18,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
til::typed_event<ITerminalConnection, winrt::Windows::Foundation::IInspectable> StateChanged;
til::event<TerminalOutputHandler> TerminalOutput;
protected:
template<typename U>
@@ -101,6 +102,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
return _isStateOneOf(ConnectionState::Connected);
}
void WriteUtf16Output(std::wstring_view str)
{
auto converted{ til::u16u8(str) };
TerminalOutput.raise(winrt::array_view{ reinterpret_cast<const uint8_t*>(converted.data()), static_cast<uint32_t>(converted.size()) });
}
void WriteUtf8Output(std::string_view str)
{
TerminalOutput.raise(winrt::array_view{ reinterpret_cast<const uint8_t*>(str.data()), static_cast<uint32_t>(str.size()) });
}
winrt::guid _sessionId{};
private:

View File

@@ -477,28 +477,31 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const auto hr = wil::ResultFromCaughtException();
// GH#11556 - make sure to format the error code to this string as an UNSIGNED int
auto failureText = RS_fmt(L"ProcessFailedToLaunch", _formatStatus(hr), _commandline);
const auto failureText = RS_fmt(L"ProcessFailedToLaunch", _formatStatus(hr), _commandline);
WriteUtf16Output(failureText);
// If the path was invalid, let's present an informative message to the user
if (hr == HRESULT_FROM_WIN32(ERROR_DIRECTORY))
{
failureText.append(L"\r\n");
failureText.append(RS_fmt(L"BadPathText", _startingDirectory));
const auto badPathText = RS_fmt(L"BadPathText", _startingDirectory);
WriteUtf16Output(L"\r\n");
WriteUtf16Output(badPathText);
}
// If the requested action requires elevation, display appropriate message
else if (hr == HRESULT_FROM_WIN32(ERROR_ELEVATION_REQUIRED))
{
failureText.append(L"\r\n");
failureText.append(RS_(L"ElevationRequired"));
const auto elevationText = RS_(L"ElevationRequired");
WriteUtf16Output(L"\r\n");
WriteUtf16Output(elevationText);
}
// If the requested executable was not found, display appropriate message
else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
{
failureText.append(L"\r\n");
failureText.append(RS_(L"FileNotFound"));
const auto fileNotFoundText = RS_(L"FileNotFound");
WriteUtf16Output(L"\r\n");
WriteUtf16Output(fileNotFoundText);
}
TerminalOutput.raise(winrt_wstring_to_array_view(failureText));
_transitionToState(ConnectionState::Failed);
// Tear down any state we may have accumulated.
@@ -517,7 +520,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const auto msg1 = RS_fmt(L"ProcessExited", _formatStatus(status));
const auto msg2 = RS_(L"CtrlDToClose");
const auto msg = fmt::format(FMT_COMPILE(L"\r\n{}\r\n{}\r\n"), msg1, msg2);
TerminalOutput.raise(winrt_wstring_to_array_view(msg));
WriteUtf16Output(msg);
}
CATCH_LOG();
}
@@ -545,10 +548,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
CATCH_LOG()
void ConptyConnection::WriteInput(const winrt::array_view<const char16_t> buffer)
void ConptyConnection::WriteInput(const winrt::array_view<const uint8_t> data)
{
const auto data = winrt_array_to_wstring_view(buffer);
if (!_isConnected())
{
return;
@@ -572,10 +573,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
}
if (FAILED_LOG(til::u16u8(data, _writeBuffer)))
{
return;
}
// Since we are using overlapped I/O, we have to store the send buffer until the work is done.
_writeBuffer.assign(winrt_array_to_string_view(data));
if (!WriteFile(_pipe.get(), _writeBuffer.data(), gsl::narrow_cast<DWORD>(_writeBuffer.length()), nullptr, &_writeOverlapped))
{
@@ -742,11 +741,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const wil::unique_event overlappedEvent{ CreateEventExW(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS) };
OVERLAPPED overlapped{ .hEvent = overlappedEvent.get() };
bool overlappedPending = false;
char buffer[128 * 1024];
DWORD read = 0;
til::u8state u8State;
std::wstring wstr;
char buffer[128 * 1024], buffer2[128 * 1024];
char* thisBuffer = buffer;
DWORD read = 0;
char* lastBuffer = buffer2;
DWORD lastRead = 0;
// If we use overlapped IO We want to queue ReadFile() calls before processing the
// string, because TerminalOutput.raise() may take a while (relatively speaking).
@@ -757,7 +757,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// When we have a `wstr` that's ready for processing we must do so without blocking.
// Otherwise, whatever the user typed will be delayed until the next IO operation.
// With overlapped IO that's not a problem because the ReadFile() calls won't block.
if (!ReadFile(_pipe.get(), &buffer[0], sizeof(buffer), &read, &overlapped))
if (!ReadFile(_pipe.get(), thisBuffer, sizeof(buffer), &read, &overlapped))
{
if (GetLastError() != ERROR_IO_PENDING)
{
@@ -769,7 +769,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// wstr can be empty in two situations:
// * The previous call to til::u8u16 failed.
// * We're using overlapped IO, and it's the first iteration.
if (!wstr.empty())
if (lastBuffer && lastRead)
{
if (!_receivedFirstByte)
{
@@ -789,7 +789,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
try
{
TerminalOutput.raise(winrt_wstring_to_array_view(wstr));
TerminalOutput.raise(winrt::array_view{ reinterpret_cast<const uint8_t*>(lastBuffer), lastRead });
}
CATCH_LOG();
}
@@ -829,8 +829,9 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
// If we hit a parsing error, eat it. It's bad utf-8, we can't do anything with it.
FAILED_LOG(til::u8u16({ &buffer[0], gsl::narrow_cast<size_t>(read) }, wstr, u8State));
// Prepare the buffers for the next loop
std::swap(thisBuffer, lastBuffer);
std::swap(read, lastRead);
}
return 0;

View File

@@ -21,7 +21,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
static safe_void_coroutine final_release(std::unique_ptr<ConptyConnection> connection);
void Start();
void WriteInput(const winrt::array_view<const char16_t> buffer);
void WriteInput(const winrt::array_view<const uint8_t> buffer);
void Resize(uint32_t rows, uint32_t columns);
void ResetSize();
void Close() noexcept;
@@ -52,8 +52,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const winrt::guid& guid,
const winrt::guid& profileGuid);
til::event<TerminalOutputHandler> TerminalOutput;
private:
static void closePseudoConsoleAsync(HPCON hPC) noexcept;
static HRESULT NewHandoff(HANDLE* in, HANDLE* out, HANDLE signal, HANDLE reference, HANDLE server, HANDLE client, const TERMINAL_STARTUP_INFO* startupInfo) noexcept;

View File

@@ -13,28 +13,29 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
void EchoConnection::Start() noexcept
{
_transitionToState(ConnectionState::Connected);
}
void EchoConnection::WriteInput(const winrt::array_view<const char16_t> buffer)
void EchoConnection::WriteInput(const winrt::array_view<const uint8_t> buffer)
{
const auto data = winrt_array_to_wstring_view(buffer);
std::wstringstream prettyPrint;
for (const auto& wch : data)
const auto data = winrt_array_to_string_view(buffer);
std::stringstream prettyPrint;
for (const auto& ch : data)
{
if (wch < 0x20)
if (ch < 0x20)
{
prettyPrint << L"^" << gsl::narrow_cast<wchar_t>(wch + 0x40);
prettyPrint << "^" << gsl::narrow_cast<char>(ch + 0x40);
}
else if (wch == 0x7f)
else if (ch == 0x7f)
{
prettyPrint << L"0x7f";
prettyPrint << "0x7f";
}
else
{
prettyPrint << wch;
prettyPrint << ch;
}
}
TerminalOutput.raise(winrt_wstring_to_array_view(prettyPrint.str()));
WriteUtf8Output(prettyPrint.str());
}
void EchoConnection::Resize(uint32_t /*rows*/, uint32_t /*columns*/) noexcept

View File

@@ -4,25 +4,20 @@
#pragma once
#include "EchoConnection.g.h"
#include "BaseTerminalConnection.h"
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
struct EchoConnection : EchoConnectionT<EchoConnection>
struct EchoConnection : EchoConnectionT<EchoConnection>, BaseTerminalConnection<EchoConnection>
{
EchoConnection() noexcept;
void Start() noexcept;
void WriteInput(const winrt::array_view<const char16_t> buffer);
void WriteInput(const winrt::array_view<const uint8_t> buffer);
void Resize(uint32_t rows, uint32_t columns) noexcept;
void Close() noexcept;
void Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) const noexcept {};
winrt::guid SessionId() const noexcept { return {}; }
ConnectionState State() const noexcept { return ConnectionState::Connected; }
til::event<TerminalOutputHandler> TerminalOutput;
til::typed_event<ITerminalConnection, IInspectable> StateChanged;
};
}

View File

@@ -13,14 +13,14 @@ namespace Microsoft.Terminal.TerminalConnection
Failed
};
delegate void TerminalOutputHandler(Char[] output);
delegate void TerminalOutputHandler(UInt8[] output);
interface ITerminalConnection
{
void Initialize(Windows.Foundation.Collections.ValueSet settings);
void Start();
void WriteInput(Char[] data);
void WriteInput(UInt8[] data);
void Resize(UInt32 rows, UInt32 columns);
void Close();

View File

@@ -476,7 +476,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
if (_connection)
{
_connection.WriteInput(winrt_wstring_to_array_view(wstr));
const auto utf8String{ til::u16u8(wstr) };
_connection.WriteInput(winrt_u8string_to_array_view(utf8String));
}
}
@@ -2236,13 +2237,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto noticeArgs = winrt::make<NoticeEventArgs>(NoticeLevel::Info, RS_(L"TermControlReadOnly"));
RaiseNotice.raise(*this, std::move(noticeArgs));
}
void ControlCore::_connectionOutputHandler(const winrt::array_view<const char16_t> str)
void ControlCore::_connectionOutputHandler(const winrt::array_view<const uint8_t>& data)
{
try
{
FAILED_LOG(til::u8u16(winrt_array_to_string_view(data), _u16ConversionBuffer, _u8State));
if (!_u16ConversionBuffer.empty()) [[likely]]
{
const auto lock = _terminal->LockForWriting();
_terminal->Write(winrt_array_to_wstring_view(str));
_terminal->Write(_u16ConversionBuffer);
}
if (!_pendingResponses.empty())

View File

@@ -349,7 +349,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _raiseReadOnlyWarning();
void _updateAntiAliasingMode();
void _connectionOutputHandler(winrt::array_view<const char16_t> str);
void _connectionOutputHandler(const winrt::array_view<const uint8_t>& data);
void _connectionStateChangedHandler(const TerminalConnection::ITerminalConnection&, const Windows::Foundation::IInspectable&);
void _updateHoveredCell(const std::optional<til::point> terminalPosition);
void _setOpacity(const float opacity, const bool focused = true);
@@ -459,6 +459,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TerminalConnection::ITerminalConnection::StateChanged_revoker _connectionStateChangedRevoker;
TerminalConnection::ITerminalConnection _connection{ nullptr };
std::wstring _u16ConversionBuffer;
til::u8state _u8State;
friend class ControlUnitTests::ControlCoreTests;
friend class ControlUnitTests::ControlInteractivityTests;
bool _inUnitTests{ false };

View File

@@ -7,20 +7,20 @@
using namespace ::winrt::Microsoft::Terminal::TerminalConnection;
using namespace ::winrt::Windows::Foundation;
static constexpr std::wstring_view PromptTextPlain{ L"C:\\> " };
static constexpr std::wstring_view PromptTextPowerline{ L"\x1b[49;34m\xe0b6\x1b[1;97;44m C:\\ \x1b[m\x1b[46;34m\xe0b8\x1b[49;36m\xe0b8\x1b[m " };
static constexpr std::u8string_view PromptTextPlain{ u8"C:\\> " };
static constexpr std::u8string_view PromptTextPowerline{ u8"\x1b[49;34m\xe0b6\x1b[1;97;44m C:\\ \x1b[m\x1b[46;34m\xe0b8\x1b[49;36m\xe0b8\x1b[m " };
// clang-format off
static constexpr std::wstring_view PreviewText{
L"\x001b"
L"c" // Hard Reset (RIS); on separate lines to avoid becoming 0x01BC
L"Windows Terminal\r\n"
L"{0}\x1b[93m" L"git\x1b[m diff \x1b[90m-w\x1b[m\r\n"
L"\x1b[1m" L"diff --git a/win b/win\x1b[m\r\n"
L"\x1b[36m@@ -1 +1 @@\x1b[m\r\n"
L"\x1b[31m- Windows Console\x1b[m\r\n"
L"\x1b[32m+ Windows Terminal!\x1b[m\r\n"
L"{0}\x1b[93mWrite-Host \x1b[36m\"\xd83c\xdf2f!\"\x1b[1D\x1b[m"
static constexpr std::u8string_view PreviewText{
u8"\x001b"
u8"c" // Hard Reset (RIS); on separate lines to avoid becoming 0x01BC
u8"Windows Terminal\r\n"
u8"{0}\x1b[93m" u8"git\x1b[m diff \x1b[90m-w\x1b[m\r\n"
u8"\x1b[1m" u8"diff --git a/win b/win\x1b[m\r\n"
u8"\x1b[36m@@ -1 +1 @@\x1b[m\r\n"
u8"\x1b[31m- Windows Console\x1b[m\r\n"
u8"\x1b[32m+ Windows Terminal!\x1b[m\r\n"
u8"{0}\x1b[93mWrite-Host \x1b[36m\"🌯!\"\x1b[1D\x1b[m"
};
// clang-format on
@@ -32,14 +32,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
const auto prompt = _displayPowerlineGlyphs ? PromptTextPowerline : PromptTextPlain;
const auto text = fmt::format(FMT_COMPILE(PreviewText), prompt);
TerminalOutput.raise(winrt_wstring_to_array_view(text));
TerminalOutput.raise(winrt_u8string_to_array_view(text));
}
void PreviewConnection::Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) noexcept
{
}
void PreviewConnection::WriteInput(const winrt::array_view<const char16_t> /*data*/)
void PreviewConnection::WriteInput(const winrt::array_view<const uint8_t> /*data*/)
{
}

View File

@@ -23,7 +23,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Initialize(const Windows::Foundation::Collections::ValueSet& settings) noexcept;
void Start();
void WriteInput(const winrt::array_view<const char16_t> buffer);
void WriteInput(const winrt::array_view<const uint8_t> buffer);
void Resize(uint32_t rows, uint32_t columns) noexcept;
void Close() noexcept;

View File

@@ -16,7 +16,7 @@ namespace ControlUnitTests
void Initialize(const winrt::Windows::Foundation::Collections::ValueSet& /*settings*/){};
void Start() noexcept {};
void WriteInput(const winrt::array_view<const char16_t> data)
void WriteInput(const winrt::array_view<const uint8_t> data)
{
TerminalOutput.raise(data);
}

View File

@@ -298,6 +298,28 @@ inline std::wstring_view winrt_array_to_wstring_view(const winrt::array_view<con
return { reinterpret_cast<const wchar_t*>(str.data()), str.size() };
}
template<typename T, typename CharTraits>
requires(sizeof(T) == 1)
inline winrt::array_view<const uint8_t> winrt_u8string_to_array_view(const std::basic_string_view<T, CharTraits>& str)
{
#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1).
return winrt::array_view<const uint8_t>{ reinterpret_cast<const uint8_t*>(str.data()), gsl::narrow<uint32_t>(str.size()) };
}
template<typename T, typename CharTraits, typename Allocator>
requires(sizeof(T) == 1)
inline winrt::array_view<const uint8_t> winrt_u8string_to_array_view(const std::basic_string<T, CharTraits, Allocator>& str)
{
#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1).
return winrt_u8string_to_array_view(std::basic_string_view<T, CharTraits>{ str });
}
inline std::string_view winrt_array_to_string_view(const winrt::array_view<const uint8_t>& str) noexcept
{
#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1).
return { reinterpret_cast<const char*>(str.data()), str.size() };
}
// This is a helper method for deserializing a SAFEARRAY of
// COM objects and converting it to a vector that
// owns the extracted COM objects