KEYBOARD LAMP MIRROR (L4KEYLIGHT). Ported from RP412, itself a port of vRIO's KeyboardLampMirror. Keys bound to a lamp address in bindings.txt glow with the panel palette through Windows Dynamic Lighting -- yellow for the map's side columns (0x10-0x1F), red for the rest -- flashing in step with the on-screen buttons (its LampLevel copy matches the FIXED BTLampBrightnessOf). Per-key boards light each bound key; zone-lit boards mirror the strongest lamp board-wide. All WinRT runs on a private worker thread: watcher, claim, and a 100 ms paint loop that repaints only on change. RP412's packing hazard does NOT transfer: it forces default struct packing there because its engine is /Zp1, which would break the WinRT ABI. BT411's BT_OPTS carries no /Zp, so only the dialect flags are needed -- /std:c++17 /permissive- per-file, since the project otherwise builds C++14 /permissive. The scalars-only interface is kept anyway so the isolation survives if packing is ever added. Wired in PadRIO: map built from bindings.keyBindings (ActionButton binds only, first-binding-wins per key), fed from PadRIO::SetLamp, stopped in the dtor (which hands the LEDs back to Windows). BT_KEYLIGHT=0 opts out; a machine without Dynamic Lighting logs once and stays dormant. Verified live: claimed this machine's 24-zone keyboard and mirrors 25 bound keys; BT_KEYLIGHT=0 and the pod profile produce zero keylight lines and run clean. SHIPPED CONTROLS REFERENCE. docs/CONTROLS.md carries the keyboard table plus the full 72-BUTTON POD MAP, grouped by the display each bank surrounds, with the coolant-valve detent warning (1-5-50-CLOSED, one press past max shuts the loop) and the jam/eject procedure. mkdist flattens it to ASCII as CONTROLS.txt at the zip root, the README's own idiom. players/README.txt gained pointers to it, to environ.ini, to -fit, and to the RGB mirror. KB: context/glass-cockpit.md and docs/GLASS_COCKPIT.md updated for the whole branch -- layout modes, L4RIOBANK and the under-glass rule, the letterbox fit and its ordering trap, player-tunable displays, the measured legend grid, the closed dead-button backlog, and this mirror. Verified: five-mode regression (surround/exploded/dock/pod/dev) boots and simulates with zero faults; mkdist writes a 5149-byte pure-ASCII CONTROLS.txt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
175 lines
5.2 KiB
C++
175 lines
5.2 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);
|
|
|
|
//
|
|
// Legacy typed-channel suppression (the btinput BTInputSuppressKey
|
|
// pattern, glass-side). A key CLAIMED by a bindings.txt row (or by the
|
|
// hardcoded `/V view-toggle and J/K/L preset-cycle keys) is swallowed
|
|
// from the engine's WM_CHAR/WM_KEYUP key-command feed (L4CTRL.cpp:1506
|
|
// block) -- otherwise every bound letter double-dispatches into the 1995
|
|
// in-cockpit dispatcher ('w' = pilot select 0, 'a'/'s'/'d'/'f' = MFD2
|
|
// preset pages, NUMPAD key-up VKs alias to 'b'/'d'/'e' hotkeys, ...).
|
|
// Unbound keys keep their authentic 1995 typed meaning. Returns 0 when
|
|
// no PadRIO is active (the feed flows untouched on dev/pod builds).
|
|
//
|
|
static int
|
|
SuppressKey(unsigned int key_value, int is_char);
|
|
|
|
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)
|
|
//
|
|
// Generic-joystick edge state, per BINDING (parallel to the profile's
|
|
// joy tables -- the previousKeyHeld pattern). A device detach reads
|
|
// everything as released, so held buttons let go automatically (the
|
|
// issue-#24 rule).
|
|
//
|
|
unsigned char
|
|
previousJoyButtonHeld[48];
|
|
unsigned char
|
|
previousJoyHatHeld[16];
|
|
//
|
|
// Direct-axis RELEASE EDGES (issue #36, night-2: "the mech may keep
|
|
// turning when control is released"). A direct-mode axis write was
|
|
// gated on raw != 0, so an axis returning INSIDE the deadzone simply
|
|
// stopped writing -- the channel LATCHED at the last deflection (fatal
|
|
// on channels with no keyboard spring, e.g. the Turn composite). Track
|
|
// the previous post-deadzone value per binding: the outside->inside
|
|
// transition writes 0 exactly once, then leaves the channel to other
|
|
// inputs (the composition model is preserved).
|
|
//
|
|
float
|
|
previousPadAxisRaw[12]; // parallel to padAxisBindings
|
|
float
|
|
previousJoyAxisRaw[24]; // parallel to joyAxisBindings
|
|
float
|
|
channelValue[PadBindingProfile::ChannelCount];
|
|
float
|
|
channelReturnRate[PadBindingProfile::ChannelCount];
|
|
int
|
|
flipStickAxes; // L4PADFLIP
|
|
|
|
int
|
|
lampState[LampCount];
|
|
|
|
// RGB keyboard lamp mirror running? (l4keylight.h; BT_KEYLIGHT=0 off,
|
|
// also stays False when no bound key carries a lamp address)
|
|
Logical
|
|
keyLightActive;
|
|
|
|
//
|
|
// Typed-channel suppression tables (built from the loaded bindings +
|
|
// the hardcoded view/preset keys; see SuppressKey).
|
|
//
|
|
unsigned char
|
|
suppressChar[256]; // WM_CHAR values
|
|
unsigned char
|
|
suppressKeyUp[256]; // WM_KEYUP values (VK aliases)
|
|
void
|
|
AddKeySuppression(int virtual_key);
|
|
void
|
|
BuildKeySuppression();
|
|
|
|
static PadRIO
|
|
*activeInstance;
|
|
|
|
public:
|
|
//
|
|
// Is the desktop pad RIO the live input device? (0 on real pod hardware,
|
|
// whose RIO is the serial Ranger board -- see BTPadRIOActive.)
|
|
//
|
|
static int
|
|
HasActiveInstance() { return activeInstance != 0; }
|
|
};
|