Compare commits

...

1 Commits

Author SHA1 Message Date
Dustin L. Howett
92a8c5894d HAX: serial port support 2025-12-17 20:49:35 -06:00
5 changed files with 220 additions and 0 deletions

View File

@@ -1488,6 +1488,9 @@ namespace winrt::TerminalApp::implementation
auto connectionType = profile.ConnectionType();
Windows::Foundation::Collections::ValueSet valueSet;
// 36d045b3-13bf-4ac0-9947-e2fd8562baa1
static constexpr winrt::guid sg{ std::string_view{ "36d045b3-13bf-4ac0-9947-e2fd8562baa1" } };
if (connectionType == TerminalConnection::AzureConnection::ConnectionType() &&
TerminalConnection::AzureConnection::IsAzureConnectionAvailable())
{
@@ -1504,6 +1507,17 @@ namespace winrt::TerminalApp::implementation
profile.Guid());
}
else if (connectionType == sg)
{
connection = TerminalConnection::SerialConnection{};
valueSet = TerminalConnection::SerialConnection::CreateSettings(
settings.Commandline(),
settings.InitialRows(),
settings.InitialCols(),
winrt::guid{},
profile.Guid());
}
else
{
auto settingsInternal{ winrt::get_self<Settings::TerminalSettings>(settings) };

View File

@@ -0,0 +1,144 @@
#include "pch.h"
#include "SerialConnection.h"
#include "SerialConnection.g.cpp"
using namespace winrt::Microsoft::Terminal::TerminalConnection;
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
winrt::Windows::Foundation::Collections::ValueSet SerialConnection::CreateSettings(hstring const& deviceDescription, uint32_t rows, uint32_t columns, winrt::guid const& guid, winrt::guid const& profileGuid)
{
Windows::Foundation::Collections::ValueSet vs{};
vs.Insert(L"device", Windows::Foundation::PropertyValue::CreateString(deviceDescription));
vs.Insert(L"initialRows", Windows::Foundation::PropertyValue::CreateUInt32(rows));
vs.Insert(L"initialCols", Windows::Foundation::PropertyValue::CreateUInt32(columns));
vs.Insert(L"guid", Windows::Foundation::PropertyValue::CreateGuid(guid));
vs.Insert(L"profileGuid", Windows::Foundation::PropertyValue::CreateGuid(profileGuid));
return vs;
}
void SerialConnection::Initialize(winrt::Windows::Foundation::Collections::ValueSet const& settings)
{
std::wstring device{ winrt::unbox_value<hstring>(settings.Lookup(L"device")) };
if (auto pos = device.find_first_of(L": "); pos != std::wstring::npos)
{
std::wstring comm{ device.substr(pos + 1) };
if (!comm.empty())
{
_dcb.DCBlength = sizeof(_dcb);
_dcbValid = !!BuildCommDCBW(comm.c_str(), &_dcb); // OK to fail
if (!_dcbValid)
{
LOG_LAST_ERROR();
}
}
device.resize(pos);
}
_devicePath = std::move(device);
}
void SerialConnection::Start()
{
_transitionToState(ConnectionState::Connecting);
_port.reset(CreateFileW(_devicePath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr));
if (_dcbValid)
{
SetCommState(_port.get(), &_dcb);
}
COMMTIMEOUTS timeouts{
.ReadIntervalTimeout = MAXDWORD,
.ReadTotalTimeoutMultiplier = MAXDWORD,
.ReadTotalTimeoutConstant = 10000,
};
SetCommTimeouts(_port.get(), &timeouts);
auto strong{ get_strong() };
_ioThread = std::thread{
[strong = std::move(strong), this]() {
_transitionToState(ConnectionState::Connected);
IoThreadBody();
// strong maintains our lifetime until the I/O thread dies
}
};
}
void SerialConnection::WriteInput(array_view<char16_t const> buffer)
{
const auto data = winrt_array_to_wstring_view(buffer);
std::string temp;
LOG_IF_FAILED(til::u16u8(data, temp, _u16State));
if (!WriteFile(_port.get(), temp.data(), static_cast<DWORD>(temp.length()), nullptr, nullptr))
{
_transitionToState(ConnectionState::Failed);
_port.reset();
}
//FlushFileBuffers(_port.get());
}
void SerialConnection::Resize(uint32_t rows, uint32_t columns)
{
UNREFERENCED_PARAMETER(rows);
UNREFERENCED_PARAMETER(columns);
return;
}
void SerialConnection::Close()
{
_transitionToState(ConnectionState::Closing);
_port.reset();
_ioThread.join();
_transitionToState(ConnectionState::Closed);
}
void SerialConnection::IoThreadBody()
{
char buffer[128 * 1024];
DWORD read = 0;
til::u8state u8State;
std::wstring wstr;
for (;;)
{
DCB ndcb{};
ndcb.DCBlength = sizeof(ndcb);
GetCommState(_port.get(), &ndcb);
// 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(_port.get(), &buffer[0], sizeof(buffer), &read, nullptr))
{
if (GetLastError() != ERROR_IO_PENDING)
{
break;
}
}
// If we hit a parsing error, eat it. It's bad utf-8, we can't do anything with it.
LOG_IF_FAILED(til::u8u16({ &buffer[0], gsl::narrow_cast<size_t>(read) }, wstr, u8State));
if (!wstr.empty())
{
try
{
TerminalOutput.raise(wstr);
}
CATCH_LOG();
}
if (read == 0)
{
// TODO???
//break;
}
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
break;
}
}
return;
}
}

View File

@@ -0,0 +1,38 @@
#pragma once
#include "SerialConnection.g.h"
#include "BaseTerminalConnection.h"
#include <til/spsc.h>
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
struct SerialConnection : SerialConnectionT<SerialConnection>, BaseTerminalConnection<SerialConnection>
{
SerialConnection() = default;
static winrt::Windows::Foundation::Collections::ValueSet CreateSettings(hstring const& deviceDescription, uint32_t rows, uint32_t columns, winrt::guid const& guid, winrt::guid const& profileGuid);
void Initialize(winrt::Windows::Foundation::Collections::ValueSet const& settings);
void Start();
void WriteInput(array_view<char16_t const> data);
void Resize(uint32_t rows, uint32_t columns);
void Close();
til::event<TerminalOutputHandler> TerminalOutput;
private:
std::wstring _devicePath;
wil::unique_hfile _port;
bool _dcbValid{ false };
DCB _dcb{};
void IoThreadBody();
std::thread _ioThread;
til::spsc::producer<char16_t> _chan{ nullptr };
til::u16state _u16State;
};
}
namespace winrt::Microsoft::Terminal::TerminalConnection::factory_implementation
{
BASIC_FACTORY(SerialConnection)
}

View File

@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "ITerminalConnection.idl";
namespace Microsoft.Terminal.TerminalConnection
{
runtimeclass SerialConnection : [default] ITerminalConnection
{
SerialConnection();
static Windows.Foundation.Collections.ValueSet CreateSettings(String deviceDescription,
UInt32 rows,
UInt32 columns,
Guid guid,
Guid profileGuid);
};
}

View File

@@ -33,6 +33,9 @@
<ClInclude Include="EchoConnection.h">
<DependentUpon>EchoConnection.idl</DependentUpon>
</ClInclude>
<ClInclude Include="SerialConnection.h">
<DependentUpon>SerialConnection.idl</DependentUpon>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="CTerminalHandoff.cpp" />
@@ -52,6 +55,9 @@
<ClCompile Include="ConptyConnection.cpp">
<DependentUpon>ConptyConnection.idl</DependentUpon>
</ClCompile>
<ClCompile Include="SerialConnection.cpp">
<DependentUpon>SerialConnection.idl</DependentUpon>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
</ItemGroup>
<ItemGroup>
@@ -60,6 +66,7 @@
<Midl Include="ConptyConnection.idl" />
<Midl Include="EchoConnection.idl" />
<Midl Include="AzureConnection.idl" />
<Midl Include="SerialConnection.idl" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources\en-US\Resources.resw">