The RioGamepad driver now installs and enumerates, so the user-mode
feeder path is verifiable. Add tools/RioJoySmokeTest, a standalone
on-cabinet utility that drives the real HidFeederJoystickSink (open the
device, submit reports via IOCTL_RIO_SUBMIT_REPORT) and reads the gamepad
back through winmm joyGetPosEx, asserting axes (min/mid/max), buttons,
and the POV hat all surface to the OS. Verified: all checks pass against
the installed driver.
Update docs to match reality (PLAN.md predated the HidFeederJoystickSink
commit): Phase 1 is now test-signed/installed/verified with the VHF
LowerFilters requirement noted; the stale "NullJoystickSink placeholder"
remainders in Phases 3 and 5 are corrected to reflect the wired,
verified feeder. driver/README.md notes the end-to-end verification.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author the custom virtual HID gamepad that replaces vJoy, and pin its wire
format on both sides. Builds clean to RioGamepad.sys against the EWDK
(KMDF 1.15 + VHF, x64, warnings-as-errors).
driver/RioGamepad/:
- ReportDescriptor.h: 6x16-bit axes (X,Y,Z,Rx,Ry,Rz), one 4-direction hat with
null state, and 96 buttons — the legacy vJoy layout. 25-byte input report.
- Device.c/Driver.c: KMDF root-enumerated device that creates the VHF virtual HID
device (VhfCreate in DeviceAdd, VhfStart in D0Entry, VhfDelete on cleanup) and
exposes a device interface + IOCTL_RIO_SUBMIT_REPORT that forwards the caller's
report bytes to VhfReadReportSubmit. Thin relay: no report logic in the kernel.
- Public.h: device-interface GUID, IOCTL, and the report byte layout shared with
the C# client. RioGamepad.inf + build.cmd (EWDK build, catalog/sign disabled).
src/RioJoy.Core/Hid/RioHidReport.cs: packs AxisOutputs + hat + 96 buttons into
the exact 25-byte report (LE axes, hat nibble with 0x0F=centered, button bitmap).
13 new xUnit tests (136 total).
Remaining (deploy-side): test-sign + pnputil install + verify in joy.cpl, and
wire the real DeviceIoControl feeder sink (replacing NullJoystickSink). The
EWDK's in-build catalog task (DrvCat) can't load Microsoft.Kits.Logger on this
image, so the .cat is produced with inf2cat/signtool at install time instead.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire the Core pieces into a runnable tray app with per-game profiles and the
three-state serial-yield auto-switch:
- Profiles/: RioProfile + AppConfig model; ConfigStore (System.Text.Json,
round-tripped); RioIniImporter ports the legacy RIO.ini (button table, invert
flags, plasma greeting); AutoSwitchResolver + AutoSwitchWatcher resolve the
foreground executable into Yield (native game) / Activate (profile) / Idle, with
native always winning and change-only notifications. IForegroundProcessProvider
abstracts the OS.
- RioRuntime assembles a profile's live pipeline: serial ButtonPressed/Released +
KeyPressed/Released → InputRouter (via RioAddress); AnalogReply → AxisCalibrator
→ the six joystick axes; RIO commands → calibration resets + version/check
requests + lamp re-init. SerialLampSink sends lamp feedback over the link;
NullJoystickSink is a placeholder until the Phase 1 HID feeder exists.
- RioJoy.Tray: NotifyIcon menu mirroring the legacy console menu (axis resets,
version/status, raw-axes & poll-rate toggles, quit) + profile selection
(auto vs. manual) + "start with Windows"; RioCoordinator owns the serial
acquire/release tied to the watcher (native-game COM-port yield). OS adapters:
ForegroundProcessProvider (Win32 foreground PID→exe) and AutoStartManager (HKCU
Run key).
- tests: 18 new xUnit tests (123 total) for config round-trip, ini import,
the three-state resolver + watcher, and RioRuntime end-to-end over the fake
transport (button→joystick, keypad-offset→keyboard, analog→six axes).
The joystick output stays a no-op until the Phase 1 driver; on-cabinet
verification of the acquire/release lifecycle remains.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port the analog calibration math and the plasma/VFD command set:
- Calibration/: AxisCalibrator ports UpdateThrottle/UpdatePadal/UpdateJoystick
(riovjoy2.cpp#L1504+) — throttle deadzone + ratchet field, pedal deadzones,
joystick X/Y auto-ranging from observed min/max, rudder mixing (enableZR), and
the per-axis invert flags. Stateful (start positions, last outputs, observed
extremes) like the legacy globals, with the RIOcmd axis resets. Final outputs
clamp to the documented 0..32766 range (also guards a legacy compounding quirk).
AxisOutputs carries the six values; IJoystickSink gains SetAxis(JoyAxis,value)
so calibrated axes reach the HID feeder.
- Plasma/: PlasmaCommands builds the CPlasma ESC sequences (clear/cursor/font/
attr/box draw+fill/text) + GetFontSize + the PlasmaPosText auto-fit/centering;
PlasmaDisplay writes them over the secondary COM transport.
- tests: 21 new xUnit tests (105 total) for throttle proportional/saturation/
invert, pedal ZR mix vs. direct, joystick centering/direction/invert/reset, the
output clamp, and plasma byte sequences/font sizes/auto-fit positioning.
Hardware verification of axis feel + plasma output remains; the game-specific
PlasmaScoreDraw layout is deferred to profile content (Phase 5/7).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port the iRIO decode and Press_V2/Release_V2 routing into testable C#:
- Mapping/: RioMapEntry decodes the 16-bit map word (lamp/mouse/hat/joy/extended/
alt/ctrl/shift flags + value) and resolves the routing Kind by the legacy
precedence (joy+hat+mouse => RIO command; none => keyboard; else joy>hat>mouse).
RioAddress translates button/keypad events to table addresses (+0x50/+0x60
keypad offsets); RioInputMap is the 112-entry per-profile table replacing the
hard-coded iRIO[].
- InputRouter ports Press_V2/Release_V2: modifier press/release ordering,
scancode keys, joystick buttons, POV hat, mouse move/click (deltas corrected
per PROTOCOL.md), RIO-command dispatch, and lamp feedback (bright on press, dim
on release; RIO commands carry none). InitializeLamps() dims all lamp entries.
- Output is split behind sink interfaces (IInputSink/IJoystickSink/ILampSink/
IRioCommandSink) so routing is pure + unit-tested; Output/SendInputSink is the
real SendInput keyboard/mouse adapter (scancode injection).
- tests: 30 new xUnit tests (84 total) for entry decode, address translation,
and router routing/precedence/lamp/modifier ordering via a recording sink.
The joystick sink's real adapter (HID feeder -> RioGamepad via DeviceIoControl)
is blocked on the Phase 1 driver; routing already targets IJoystickSink.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port the RIO wire protocol from legacy/riovjoy2.cpp into testable C#:
- Protocol/: command + length table, 7-bit checksum, packet builder, and a
streaming receive-side framing state machine (PacketParser) that mirrors the
legacy ReadCommBlock framing/resync (high-bit-mid-packet abort). Typed RIO->PC
decodes: AnalogReport (14-bit sign-extend), VersionInfo, CheckStatus; lamp-state
composition.
- Serial/: RioSerialLink drives an async receive loop with ACK/NAK reply policy
(legacy force-accept vs. opt-in VerifyInboundChecksum), the analog poll timer,
and the >5s reset-recovery watchdog. IRioTransport abstracts the COM port; the
SerialPort-backed transport does 9600 8N1 + DTR reset pulse, and acquire/release
is just create/dispose (foundation for native-game serial yield).
- tests/RioJoy.Core.Tests: 54 xUnit tests covering checksum, framing/resync,
builder round-trips, analog sign-extension + sentinel rejection, lamp combos,
and the read loop driven against an in-memory fake transport.
Hardware verification (version/check/analog against a cabinet) remains; it can't
be done off-device.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Modernization of the legacy vJoy-based RIO cockpit interface for Win10/11,
removing the vJoy dependency in favor of a custom VHF/UMDF HID driver,
rewritten in C#/.NET 8 as a background tray app with per-game profiles.
- Reorganize: legacy C++ -> legacy/, cockpit art -> docs/reference/
- RioJoy.sln: src/RioJoy.Core (lib) + src/RioJoy.Tray (tray app), net8.0-windows x64
- driver/ placeholder for the RioGamepad WDK driver
- docs/PLAN.md (7-phase plan; profiles + serial-yield model)
- docs/PROTOCOL.md (RIO wire format + iRIO input-map reference)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>