Files
CydandClaude Fable 5 0ca6b5b01f RGB keyboard lamp mirror: vRIO Dynamic Lighting port, live in-game
The polish-backlog item, implemented from vRIO KeyboardLampMirror:
game-commanded RIO lamp states paint per-key RGB keyboards through
Windows Dynamic Lighting (WinRT LampArray). Keys bound to lamp
addresses in the active bindings profile glow with the panel palette
(red banks, yellow Secondary/Screen columns), flash modes use the
exact L4MFDVIEW formula so keyboard and on-screen buttons blink in
step, unbound keys are blacked out so the board reads as the button
field, and zone-lit keyboards fall back to a board-wide mirror of the
strongest lamp. Advantage over vRIO: Dynamic Lighting grants LEDs to
the FOREGROUND app - which is the game - so no Windows settings
dance.

Isolation: L4KEYLIGHT.cpp compiles /std:c++17 + DEFAULT packing +
conformance (per-file vcxproj settings; the engine /Zp1 would break
the WinRT ABI) with a scalars-only interface, and all WinRT work runs
on a private worker thread (watcher, claiming, 100ms paint loop).
On by default with a bindings map present; RP412KEYLIGHT=0 opts out;
missing Dynamic Lighting logs once and stays dormant.

Verified live on the dev laptop: claimed its 24-zone keyboard
(board-wide mirror) during a race; race cycling with per-race
start/stop of the mirror thread stays green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 10:49:35 -05:00

123 lines
3.1 KiB
C++

#pragma once
#include "l4rio.h"
#include "l4padbindings.h"
//########################################################################
//############################### PadRIO #################################
//########################################################################
//
// A cockpit-less RIO: synthesizes the RIOBase control surface from an
// XInput controller and the PC keyboard. Selected with L4CONTROLS=PAD.
//
// Every input is REBINDABLE: bindings.txt beside the exe (vRIO's
// profile format, written with the full default layout on first run)
// maps keys and pad controls to RIO button addresses (0x00-0x47), the
// pilot/external keypads (0x50-0x6F, delivered as arcade KeyEvents;
// unbound by default - the game never reads them), and the five axes
// with deflect/rate semantics. Defaults: vRIO's complete board layout
// (number+letter rows = MFD banks, F-keys = secondary columns) with
// flight on the numpad - 8/2/4/6 stick, 7/9 pedals, 0 trigger - and
// the modifiers: Shift/Ctrl throttle up/down, Alt reverse thrust,
// Space trigger, arrows hat.
//
// Set L4PADFLIP to a string containing 'X' and/or 'Y' to invert the
// stick axes on top of whatever the profile produces.
//
class PadRIO :
public RIOBase
{
public:
PadRIO();
~PadRIO();
Logical
TestInstance() const;
Logical
GetNextEvent(RIOEvent *destinationPointer);
void
RequestAnalogUpdate();
void
GeneralReset();
void
ResetThrottle();
void
SetLamp(int lampNumber, int state);
// Lamp state the game has commanded, indexed by RIO lamp number.
// The on-screen cockpit buttons read this to light themselves.
enum { lampCount = 64 };
unsigned char
lampState[lampCount];
//---------------------------------------------------------------
// On-screen cockpit buttons (MFDSplitView strips) press RIO units
// through these; they are no-ops when no PadRIO is active (e.g.
// real serial hardware selected).
//---------------------------------------------------------------
static void
SetScreenButton(int unit, Logical pressed);
static int
GetLampState(int unit);
static Logical
IsActive()
{ return activeInstance != NULL; }
protected:
void
PollInputs();
void
QueueEvent(const RIOEvent &an_event);
enum { queueSize = 64 };
enum { buttonUnits = 0x48 }; // through LastMappableButton
RIOEvent
eventQueue[queueSize];
int
queueHead, queueTail;
static PadRIO
*activeInstance;
// pressed state driven by the on-screen cockpit buttons
unsigned char
screenButton[0x48];
unsigned long
lastPollTick,
lastPadCheckTick;
int
padIndex; // connected XInput slot, -1 = none
Logical
padReported; // one-time connect/disconnect log flags
Logical
analogRequested;
unsigned char
buttonDown[buttonUnits];
// keypad pressed-state (0x50-0x6F), for KeyEvent edges
enum { keypadUnits = 0x20 };
unsigned char
keypadDown[keypadUnits];
PadBindingProfile
profile;
Scalar
throttleAccum;
Logical
invertX, invertY;
Logical
keyLightActive; // Dynamic Lighting keyboard mirror running
Scalar
sentThrottle, sentLeftPedal, sentRightPedal,
sentJoystickX, sentJoystickY;
};