Named-pipe transport for the DOSBox-X fork's namedpipe backend

Both devices now serve \.\pipe\vrio and \.\pipe\vplasma for the whole
app lifetime alongside the COM rows — the com0com-free path. Framing per
the pinned contract (0x00 len data / 0x01 DTR+RTS lines, one lines frame
on connect, unknown type = log + drop): PipeFraming/PipeFrameDecoder and
VRioPipeService (TX paced at the wire rate, peer DTR edges feed
HostHandshake) in VRio.Core, listener-only VPlasmaPipeService twin in
VPlasma.Core. Busy pipe names retry, so vRIO's built-in glass and the
standalone vPLASMA coexist. README documents the transport and the fork
conf lines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-12 23:03:02 -05:00
co-authored by Claude Fable 5
parent 0674cf5ba4
commit e507f1740c
8 changed files with 1247 additions and 0 deletions
+25
View File
@@ -18,6 +18,9 @@ namespace VRio.App;
/// RIOJoy at the other end of the null-modem pair, and every click here
/// arrives at RIOJoy exactly as if the physical cockpit sent it; point the
/// game's COM2 passthrough at the plasma pair and the glass lights up.
/// Both devices also serve the DOSBox-X fork's named-pipe transport
/// (<c>\\.\pipe\vrio</c>, <c>\\.\pipe\vplasma</c>) for the whole app
/// lifetime — the com0com-free path.
/// </summary>
internal sealed class MainForm : Form
{
@@ -36,8 +39,10 @@ internal sealed class MainForm : Form
private readonly VRioDevice _device = new();
private readonly VRioSerialService _service;
private readonly VRioPipeService _pipeService;
private readonly VPlasmaDevice _plasmaDevice = new();
private readonly VPlasmaSerialService _plasmaService;
private readonly VPlasmaPipeService _plasmaPipeService;
private readonly PanelCanvas _canvas = new();
private readonly PlasmaCanvas _plasmaCanvas = new(PanelCanvas.PlasmaDotPitch)
{
@@ -190,6 +195,11 @@ internal sealed class MainForm : Form
_service = new VRioSerialService(_device);
_plasmaService = new VPlasmaSerialService(_plasmaDevice);
// The named-pipe endpoints for DOSBox-X's namedpipe serial backend.
// Unlike a COM port, a listening pipe costs nothing and conflicts with
// nothing, so both servers run for the whole app lifetime.
_pipeService = new VRioPipeService(_device);
_plasmaPipeService = new VPlasmaPipeService(_plasmaDevice);
_router = new InputRouter(_device);
_lampMirror = new KeyboardLampMirror(_device);
@@ -224,6 +234,17 @@ internal sealed class MainForm : Form
_service.ConnectionChanged += open => RunOnUi(() => OnConnectionChanged(open));
_service.HostHandshake += high => RunOnUi(() =>
PrependLog(high ? "Host raised DTR (board-reset handshake)" : "Host dropped DTR"));
_pipeService.Logged += line => RunOnUi(() => PrependLog($"pipe: {line}"));
_pipeService.HostHandshake += high => RunOnUi(() =>
PrependLog(high ? "Pipe host raised DTR (board-reset handshake)" : "Pipe host dropped DTR"));
_pipeService.ClientChanged += connected => RunOnUi(() =>
{
// The RIO is a single-host device; two live transports means two
// hosts fighting over one board.
if (connected && _service.IsOpen)
PrependLog($"Warning: a pipe client connected while {_service.PortName} is open — close one transport.");
});
_plasmaPipeService.Logged += line => RunOnUi(() => PrependLog($"vPLASMA pipe: {line}"));
// Built-in plasma glass: a pure listener on its own port, same gestures
// as the standalone vPLASMA — double-click cycles the self-test pages,
@@ -305,6 +326,8 @@ internal sealed class MainForm : Form
_rawKeyboard.Dispose();
_service.Dispose();
_plasmaService.Dispose();
_pipeService.Dispose();
_plasmaPipeService.Dispose();
};
RefreshPorts();
@@ -313,6 +336,8 @@ internal sealed class MainForm : Form
LoadBindings();
_plasmaCanvas.UpdateFrom(_plasmaDevice); // paint the power-on frame
AutoOpenPreferredPorts();
_pipeService.Start();
_plasmaPipeService.Start();
}
private Panel BuildControlStrip()