Files
BT411/engine/MUNGA_L4/L4PADRIO.h
T
CydandClaude Fable 5 ac57a474ef Input: the RIOBase seam + PadRIO -- the glass cockpit gets hands (step 2b)
L4RIO.h splits the abstract cockpit-control surface (RIOBase: enums, the five
analog Scalars, GetNextEvent/SetLamp, no-op serial ops, NEW IsOperational) out
of the serial RIO (RIO : PCSerialPacket, RIOBase -- byte-for-byte behavior kept,
ctor assigns as before); LBE4ControlsManager holds a RIOBase* and gains the
gated L4CONTROLS=PAD factory arm (BT_GLASS; OFF build logs+ignores the token).
NEW gated TUs: L4PADRIO (XInput+keyboard synthesize the surface; 3s hot-plug
re-probe; focus-guarded keys; per-poll AnalogEvent heartbeat; lampState[] +
static SetScreenButton/GetLampState for the panel) and L4PADBINDINGS
(content\bindings.txt profile, self-documenting default written on first run;
deflect/slew/set axis model; addresses validated against ButtonCount).
Verified live (glass build, L4CONTROLS=PAD): bindings written+parsed 44/10/5,
XInput pad detected, 121 streamed mappings install via stock PrimaryRIO path,
2157 frames clean. Pod build (gates OFF) compiles the split with zero
behavioral delta.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 22:11:16 -05:00

111 lines
2.8 KiB
C++

#pragma once
//###########################################################################
//
// L4PADRIO -- the hardware-less cockpit device (BT_GLASS only).
//
// PadRIO synthesizes the pod's abstract control surface (RIOBase: the five
// analog Scalars + button/keypad events + lamps) from an XInput controller
// and the PC keyboard, so the whole stock RIO path above the seam -- the
// LBE4ControlsManager push, MechRIOMapper, streamed .CTL mappings, lamp
// writes -- runs with no serial hardware. Selected at runtime with
// L4CONTROLS=PAD (the factory arm in L4CTRL.cpp).
//
// Input model (bindings: L4PADBINDINGS.h, content\bindings.txt):
// - keyboard keys emit button/keypad edge events and drive the analog
// channels (spring-stick deflect / throttle-lever slew / detent set)
// - XInput pad buttons emit button events; pad axes drive the channels
// directly (or as slew rates); hot-plug re-probe every ~3 s
// - on-screen cockpit buttons (the L4PADPANEL window, or any WndProc)
// inject through the static SetScreenButton() entry
// - lamps are recorded in lampState[] for the on-screen panel to render
//
// The keyboard is only read while a window of THIS process has focus.
// L4PADFLIP=1 inverts both stick axes.
//
//###########################################################################
#include "l4rio.h"
#include "l4padbindings.h"
class PadRIO :
public RIOBase
{
public:
PadRIO();
~PadRIO();
Logical
GetNextEvent(RIOEvent *destinationPointer);
void
SetLamp(int lampNumber, int state);
Logical
IsOperational() const
{ return True; }
//
// The on-screen panel / WndProc entries. Safe no-ops when no PadRIO
// is active (pod build never links this TU; a glass build without
// L4CONTROLS=PAD never constructs one). Same-thread use only: the
// panel WndProc runs on the app thread's message pump.
//
static Logical
IsActive();
static void
SetScreenButton(int unit, int pressed);
static int
GetLampState(int unit);
enum { LampCount = 128 };
protected:
void
Poll();
void
PushEvent(const RIOEvent &event);
void
EmitButton(int address, int pressed);
void
EmitKeypad(int unit, int key);
PadBindingProfile
bindings;
//
// Event queue (ring; drop-newest on overflow, logged).
//
enum { EventQueueSize = 128 };
RIOEvent
eventQueue[EventQueueSize];
int
eventHead, eventTail;
//
// Poll state.
//
unsigned long
lastPollMilliseconds;
unsigned long
lastPadProbeMilliseconds;
int
padIndex; // connected XInput slot, -1 = none
unsigned
previousPadButtons;
unsigned char
previousKeyHeld[192]; // per key BINDING (parallel to the profile)
float
channelValue[PadBindingProfile::ChannelCount];
float
channelReturnRate[PadBindingProfile::ChannelCount];
int
flipStickAxes; // L4PADFLIP
int
lampState[LampCount];
static PadRIO
*activeInstance;
};