- InputRouter.InvertJoystickY flips the composed joystick Y (keys and
pad alike) before wire scaling; panel drags write the device directly
and stay untouched. New "Invert joystick Y" checkbox in the Input
group, default on.
- StampGitVersion target bakes "YYYY.MM.DD (shortsha)" of HEAD into
InformationalVersion; the title shows it via Application.ProductVersion
so a running build can be matched to its vYYYY.MM.DD release tag.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Settings "Background light control" list only offers apps that have
BOTH package identity and the com.microsoft.windows.lighting
windows.appExtension in their manifest (declaration verified against
Lenovo's LegionLightingController manifest) — without a grant, Windows
hands the LEDs to vRIO only while it is the foreground window, i.e.
never during gameplay.
pkg/AppxManifest.xml is a sparse package (external location) that
grants the existing VRio.App.exe identity as-is: win32App runtime
behavior, runFullTrust + unvirtualizedResources, and the lighting
app-extension. Register-vRIO.ps1 registers/unregisters it against any
exe folder (repo build or an extracted dist zip); unsigned registration
needs Developer Mode. Identity is granted through shell activation
only — launch from the Start menu entry or an AUMID taskbar pin, not by
double-clicking the exe (the script prints this warning).
Verified end-to-end on this machine: process reports the package full
name, vRIO appears in the background-control list, and the wire log
shows the withheld → available transition once granted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Opt-in checkbox in the Input group: keys bound to lamp-capable button
addresses glow with the panel's palette (red family, yellow for the
Secondary/Screen columns) and blink at the panel's own flash rates, so
keyboard and on-screen panel stay in step; unbound keys are blacked out
so the keyboard reads as the button field. Zone-lit keyboards without
per-key addressing (e.g. this laptop's 24-zone ITE board) fall back to
mirroring the strongest current lamp board-wide. A picker under the
checkbox narrows the mirror to one keyboard when several are attached
(hot-plug aware; releases the others back to Windows).
KeyboardLampMirror claims keyboard-kind LampArrays via a DeviceWatcher,
repaints at 100 ms only when colors actually change, survives paint
faults (a leaked Timer exception would kill the process), and logs
attach/detach plus IsAvailable transitions with a pointer to the
Dynamic Lighting background-control setting. Disabling releases every
claim so the LEDs revert to the ambient scene.
Also: the wire log gets a horizontal scrollbar (long lines don't wrap).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The "Emulate the v4.2 reply-wedge bug" checkbox and "Wedge analog now"
button are not needed for the renderer-comparison work, so remove them
from the control strip: Device group shrinks 60px and the Input group,
counters, help text, and wire log move up to match (the log gains the
height). EmulateReplyWedge/WedgeAnalogNow stay in VRio.Core with their
tests; only the UI entry points are gone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A single failed port write killed the paced writer thread permanently
while the port still reported open, so every later byte (ACKs, the
CheckRequest handshake) queued forever: wire tap showed 2 of 3 bytes of
a stray ButtonPressed at t=54ms, then 1167 unanswered CheckRequests.
The trigger was com0com flow control with the game side not yet open --
the third byte blocked past WriteTimeout, threw, and the catch returned.
A real UART cannot wedge; it shifts bits into the line whether or not
anyone listens. On a write fault, drop the stalled byte plus the stale
backlog, log the stall/recovery transition once, and keep the writer
alive -- TX resumes as soon as the host drains its end.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The host waits up to 5s after CheckRequest for TestModeChange ENTER
(8C 01 0D) before anything else, and sends no requests until the
matching EXIT (8C 00 0C) arrives; vRIO jumped straight to the
CheckReply dump, so hosts logged "RIO never came back from check
request" and skipped the version exchange. Bracket the per-board
BoardOk replies with enter/exit, byte-for-byte what the real v4.2
board sends on the wire tap.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rework the shipped default profile around a required Xbox controller,
refined from live-game testing:
- All five axes are pad-only now (left stick / triggers / right stick);
the key-axis bindings (arrows/WASD-style deflect and rate) are gone
from the defaults, though the grammar remains supported.
- Keyboard becomes the button field, geometry mirroring the panel:
number row + QWERTY row = upper MFD bank, home + bottom rows =
lower MFD bank as two 4-key blocks with an unbound gap key (G / B)
standing in for the panel keypad gap, F1-F6 / F7-F12 = the
Secondary / Screen columns top-to-bottom (0x16/0x17, 0x1E/0x1F
deliberately unmapped), numpad = the full internal keypad with the
hex keys on the operators (/ * - + . Enter = A-F).
- Hat rides the arrow keys, Main stays on Space; Pinky / Middle /
Upper / Panic are pad-only so no panic key sits inside the MFD field.
- Pad B and LeftShoulder swap to Middle / Panic.
- Rest of the throttle column and the external keypad stay unmapped.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
vRIO wrote whole reply packets in one SerialPort.Write; through a
com0com null-modem (no UART) the host saw the bytes ~30-40 us apart,
a burst no real board can produce and the prime suspect in a rocky
game init. Replies now leave one byte per 10-bit frame (~1.04 ms), so
the 45-byte CheckRequest response takes ~47 ms like real hardware
(measured 0.94-1.10 ms gaps over a com0com pair).
- Transmit frames queue to a writer thread; each byte is scheduled
against a monotonic slot deadline slot = max(prev + period, now),
so the stream averages true 9600 baud without bursting after idle.
- After a write the schedule is floored at the actual emission time:
a late wake-up can never be followed by a catch-up burst - two
frames closer than the frame time is structurally impossible.
- 1 ms system timer resolution while the port is open (timeBeginPeriod)
so the pacer sleeps most of each gap and only spins the last ~1.8 ms.
- Side benefit: UI clicks no longer block on SerialPort.Write (a
stalled port could previously hang the UI for the 2 s write timeout).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New input pipeline: sources feed an InputRouter (VRio.Core.Input) that
calls the same VRioDevice press/release/axis entry points the mouse
canvas uses, so routed input is indistinguishable on the wire.
- Bindings live in %APPDATA%\vRIO\bindings.txt (plain text, commented
defaults written on first run; Reload/Edit buttons in the new Input
group). Keys and pad buttons press any RIO address, momentary or
toggle; axes bind in normalized units of each axis realistic travel
window with deflect (spring-back), rate (position holds), deadzone,
and invert options - RioAxisRange supplies the wire signs.
- Router suppresses key auto-repeat, edge-detects pad buttons, and
hold-counts per address so overlapping sources press once and
release last; axes only write when the composed value changes, so
mouse drags keep working while sources idle.
- Xbox pad via a zero-dependency XInput P/Invoke poller (xinput1_4,
fallback xinput9_1_0), throttled rescan while disconnected.
- MainForm intercepts bound keys in ProcessCmdKey so arrows/space
reach the panel instead of moving focus; keys release on focus
loss; center-axes and host ResetRequest reset the rate integrators.
- Canvas highlights router-held addresses like clicks.
- Defaults: arrows=stick, W/S=throttle, Q/E=pedals, numpad=internal
keypad, IJKL/Space=hat+main; pad left stick/triggers/right stick =
stick/pedals/throttle-rate, ABXY/dpad/shoulders = named buttons.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Axes now span the hardware windows from RIOJoy's calibrator instead of
the full 14-bit wire range: throttle rests at 0 and travels to -800
(forward runs negative, matching the ratchet math), spring-loaded
pedals rest at 0 and press to +500, stick covers +/-80 around center
(new RioAxisRange in VRio.Core documents the provenance).
- Stick X sign flipped: RIOJoy maps negative samples to the high half of
its output axis, so dragging right now lowers raw X.
- Removed the Rz mix bar - the real RIO has no such indicator.
- Wire log: newest entries on top with the bound trimming the oldest
lines, and fixed the anchoring bug that let the box grow past the
window edge (the panel got its height only after children snapshotted
their anchors), which also hid the Clear log button.
- Tighter layout: axis readout sits under the status lines, encoder
strip shrunk to fit the gauges, and the window sizes itself to the
canvas + control strip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Speaks the device side of the RIO serial protocol (per riojoy's
PROTOCOL.md) on a COM port, behind an interactive replica of the
profile editor's cockpit panel: click cells to press buttons/keys,
drag the encoder gauges to move the five analog axes, and watch
host-commanded lamp states (incl. flash modes) light the cells.
Device behavior grounded in the real v4.2 firmware dump: version 4.2,
4-retry NAK budget ending in RESTART, and an optional emulation of the
analog reply-wedge latch leak for exercising host recovery watchdogs.
Verified: 33 unit tests, plus an interop harness driving RIOJoy's
actual RioSerialLink against VRioDevice over an in-memory transport
(version/check/analog/lamp/button/keypad/reset all round-trip).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>