Files
riojoy/docs/PLAN.md
T
CydandClaude Opus 4.8 39a3dab1fc Phase 0: scaffold modern RIOJoy solution + plan
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>
2026-06-26 12:43:01 -05:00

169 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RIOJoy — modernization plan
Modernize the cockpit RIO interface app for Windows 10/11, **removing the vJoy
dependency** and replacing it with a custom virtual HID device, rewritten in
C#/.NET as a background tray app with **per-game profiles**.
The legacy app is preserved under [`legacy/`](../legacy/) as the behavioral
reference. The RIO wire format and input map are documented in
[PROTOCOL.md](PROTOCOL.md).
---
## Purpose
The cockpits run two native games (**Firestorm**, **Red Planet**) that talk to
the RIO hardware **directly** and never use this app. RIOJoy exists to **broaden
which other games can run in the cockpits**: arbitrary games don't know about the
cockpit's extra hardware (5 analog axes, 96 lighted buttons, the plasma/VFD
display, the labeled wallpaper), so RIOJoy bridges the RIO to whatever input
those games *do* understand — joystick, keyboard, and mouse — and drives the
cockpit's outputs on their behalf.
---
## Target architecture
```
┌─────────────────────── C# / .NET 8 tray app (x64) ───────────────────────┐
│ Serial (RIO protocol) → Input mapper (profile) → Output router │
│ COM port, 9600 8N1 72 inputs + keypads ├─ Keyboard/Mouse: SendInput (P/Invoke)
│ packet parse + ACK/NAK decode iRIO bitfield ├─ Joystick: HID report → DeviceIoControl ↓
│ analog poll + recovery axis calibration └─ Lamps: LampRequest back over serial
│ Plasma/VFD on 2nd COM · Profiles + auto-switch + tray UI + logging │
└───────────────────────────────────────────────┬───────────────────────────┘
│ IOCTL (input report bytes)
┌─────────────────────────────────────────────────▼──────────────────────────┐
│ RioGamepad.sys — KMDF + VHF (vhf.sys) virtual HID device │
│ Report descriptor: X,Y,Z,Rx,Ry,Rz (16-bit) · 1 hat · 96 buttons │
│ Control device + custom IOCTL → VhfReadReportSubmit() → Windows sees │
│ a HID gamepad │
└─────────────────────────────────────────────────────────────────────────────┘
```
### Decisions (confirmed)
- **Virtual joystick:** custom **VHF/UMDF HID driver** (full fidelity: 6 axes,
96 buttons, 1 hat — exactly the legacy vJoy layout). Not ViGEm (can't hold 96
buttons).
- **Stack:** C# / **.NET 8** (LTS), x64. Driver is C (WDK), separate toolchain.
- **Form:** background **tray app** (NotifyIcon), auto-start with Windows.
- **Targets:** Windows 10/11 **x64 only**. The legacy x86 / WinXP targets are
dropped.
### What ports over vs. what's new
| Concern | Legacy | Modern |
|---|---|---|
| Serial + RIO protocol | overlapped I/O + watch thread | `SerialPort` + async loop; faithful port of the packet state machine |
| Input decode | `iRIO[]` bitfield + `Press_V2` | same semantics, ported to C# |
| Keyboard/mouse | `SendInput` scancode | `SendInput` via P/Invoke (≈verbatim) |
| Joystick | vJoy `SetAxis/SetBtn/SetDiscPov` | **HID report → IOCTL → RioGamepad.sys** |
| Lamps | `LampRequest` over serial | same |
| Axis calibration | `UpdateJoystick/Throttle/Padal` | ported math |
| Plasma display | `CPlasma` (COM2) | ported; content per-profile |
| Config | SimpleIni, single file, hard-coded COM1 | profile library (JSON), configurable ports; importer for legacy `RIO.ini` |
---
## Profiles (the core abstraction)
A **profile** fully describes how the cockpit behaves for **one non-native
game**. Everything is per-profile, not global:
- Button/keypad mapping (the decoded `iRIO` table for this game)
- Axis calibration, curves, and invert flags
- Lamp behavior
- Plasma/VFD content (or "off")
- Cockpit overlay labels + the generated wallpaper (Phase 7)
- Display/resolution targets
Profiles **never** describe the native games — those are the "hands-off" case.
### Serial-port yield & the auto-switch state machine
Because the native games own the RIO's COM port directly, RIOJoy must yield it.
A process/window watcher drives three states:
| Detected running app | RIOJoy behavior |
|---|---|
| **Native game** (Firestorm, Red Planet) | **Release COM port, go fully dormant** — no serial, no HID, no overlay |
| **Supported non-native game** | Acquire port, load that game's profile, drive HID / keyboard / mouse / lamps / plasma / wallpaper |
| **Nothing / desktop** | Idle — port released or a configurable neutral default |
Config therefore holds: the per-game profile library, a **list of native games
to yield to**, and the executable→profile match rules. Manual override from the
tray menu is always available.
---
## Phases
### Phase 0 — Repo & scaffold ✅ (this commit)
- `git init`, remote `https://gitea.mysticmachines.com/VWE/riovjoy2.git`.
- Legacy C++ moved to `legacy/`; cockpit art to `docs/reference/`.
- Solution `RioJoy.sln` with `src/RioJoy.Core` (lib) + `src/RioJoy.Tray`
(tray app); `driver/` placeholder for the WDK project.
- This plan + [PROTOCOL.md](PROTOCOL.md).
### Phase 1 — Virtual HID driver (highest risk; do first)
- KMDF + VHF virtual HID gamepad: report descriptor = 6×16-bit axes, 1 hat,
96 buttons.
- Control device + custom IOCTL → `VhfReadReportSubmit`.
- Test harness (throwaway C#) wiggles axes/buttons; verify in `joy.cpl`.
- Test-signing setup for the cabinets.
### Phase 2 — Serial + RIO protocol core (`RioJoy.Core`)
- `SerialPort` wrapper; packet parser/builder (length table, 7-bit checksum,
ACK/NAK, framing resync).
- Analog poll timer + >5 s reset-recovery.
- **Clean COM-port acquire/release** (foundation for serial yield).
- Verify against hardware: version reply, check reply, analog stream.
### Phase 3 — Input mapping + output routing
- Port `iRIO` decode and routing precedence (keyboard/mouse/joy/hat/RIO-command).
- Keyboard/mouse via `SendInput` P/Invoke; lamp feedback over serial.
- HID-report feeder → the Phase 1 driver via `DeviceIoControl`.
### Phase 4 — Axis calibration + plasma display
- Port `UpdateJoystick/Throttle/Padal` math (deadzones, ratchet, rudder).
- Port the `CPlasma` ESC command set on the secondary COM port.
### Phase 5 — Tray app + profiles
- NotifyIcon + menu mirroring the legacy console menu (reset/recalibrate axes,
version/status, toggle raw-axis & poll-rate readouts, quit) + status/log window.
- **Profile library**, manual selection, and the **three-state auto-switch
watcher** (incl. native-game yield).
- Config persistence; auto-start.
### Phase 6 — Packaging / signing / deploy
- Driver install via `pnputil`; app installer; test-signing script.
- Cabinet deployment doc. (Production option: attestation signing.)
### Phase 7 — Profile/mapping editor + cockpit overlay generator
Replaces the legacy Google-Sheet → `.data` → GIMP → Script-Fu pipeline
(see [`docs/reference/customBackground/`](reference/customBackground/)).
- **Mapping editor:** per-button UI to set action + label + lamp, no hex
bit-twiddling; clone-from-existing profile.
- **Overlay generator:** render labels into named regions over a base cockpit
image using **SkiaSharp**, porting the auto-fit/justification logic from
`sg-goobie-MFD.scm`; export the per-profile wallpaper and re-apply via
`SystemParametersInfo`.
- **Region authoring:** extract the label rectangles from `riojoy.xcf` into a
`regions.json`; in-app box editor for future tweaks.
- The unified profile JSON supersedes both `RIO.ini` and the Google Sheet, with
importers for each.
- To confirm at Phase 7: target wallpaper resolution(s); static wallpaper vs.
live overlay (e.g. lit-button highlighting mirroring lamp state).
---
## Open items / risks
- **Driver signing** is the main friction point. Test-signing is fine for owned
cabinets; redistribution needs attestation signing (EV cert + Partner Center).
- **Input injection vs. session:** `SendInput` targets the interactive session —
fine for a tray app, which is why a Windows service was rejected.
- **Legacy quirks to decide on** (see PROTOCOL.md ⚠️ notes): disabled inbound
checksum verification; odd mouse-move deltas.