Ported from BT411, which needed the same thing for its glass cockpit. PadRIO reads XInput, which covers Xbox-class pads and nothing else. A flight stick, a HOTAS throttle, a twist grip, rudder pedals or a wheel arrive through DirectInput instead, and until now the game could not see any of them - the only generic-joystick path left was the 1995 single- device DIJoystick behind L4CONTROLS=DIJOYSTICK, which is untouched here. L4JOY is the reader: up to four devices as normalized state blocks, hot- plug re-enumeration on the same ~3 s cadence PadRIO uses to look for a pad, and a device lost mid-race zeroed rather than left holding whatever was pressed when it went. XInput-class devices are excluded by VID/PID against the RawInput paths carrying the "IG_" marker - without that an Xbox pad arrives through both APIs and every button counts twice. bindings.txt gains four rows in the grammar it already had, using its own vocabulary (deadzone/rate) rather than BT411's: joydev <slot> [product-name substring] joyaxis <src> axis <axis> [invert] [deadzone <d>] [rate <n>] joybutton <n> button <addr> [toggle] joyhat <n> <up|down|left|right> button <addr> Slots resolve to a live device every poll, by name substring or ordinal, so unplugging and replugging does not rewrite anyone's file. Two things the pod's shape forced that BT411 solved differently: Pedals - a signed composite axis that decomposes into the pod's two pedals, positive right and negative left. The pod has a pedal each side; a twist grip or rudder bar is one signed control, and pressing one or the other but never both is exactly what it wants to say. It is a channel name like any other, so a pad stick can drive the turn too. A joyaxis on Throttle with no rate is a real lever and OWNS the channel - full travel maps onto the 0..1 the pod runs on, instead of nudging the accumulator that a spring-centred pad stick has to use. RP412JOYCONFIG=1 (joyconfig.bat) runs the capture wizard before the console screen: it asks the player to move each control, and derives the sign convention from the DIRECTION of the move. That is the point of it - a stick that reads positive pushed right and one that reads negative are equally common, and no amount of documentation gets a player to work out which they own. It writes only its own section, between marker lines, so hand-edited keyboard and pad rows survive re-running it. The wizard also prints every axis at rest before it starts. A driver that refuses the +-32767 range we ask for reports its own, and an axis then sits hard over instead of near zero; seeing "X +1.00" on an untouched stick is the difference between a five-minute fix and a bug report that says it configured itself. Each capture reports the move it saw for the same reason. Verified on the Logitech Extreme 3D on this machine. Enumeration finds it and excludes the Xbox pad, which still arrives separately through XInput. Every row shape parses - 7 axes, 2 buttons, 4 hat directions - and three deliberately malformed rows (a bad axis name, button 99, a "sideways" hat) are each rejected by line number rather than silently dropped. The wizard lists the device with its axes at rest reading X +0.00 Y -0.01 RZ -0.04 SL0 +1.00, waits on the first prompt without self-triggering, and with a hand on the stick captures X to steering, Y to pitch, RZ to the pedals and SL0 to the throttle, inverting the ones that read backwards. Running the captures through to a written file needs a hand on the stick, so that part is the machine's to confirm, not this build's. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
198 lines
5.5 KiB
C
198 lines
5.5 KiB
C
//===========================================================================//
|
|
// File: l4padbindings.h //
|
|
// Project: MUNGA Brick: PadRIO binding profiles //
|
|
// Contents: The rebindable input profile (vRIO bindings format) //
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc. //
|
|
// PROPRIETARY AND CONFIDENTIAL //
|
|
//===========================================================================//
|
|
|
|
#pragma once
|
|
|
|
#include "..\munga\style.h"
|
|
|
|
//########################################################################
|
|
// The bindings file (vRIO's format, shared grammar):
|
|
//
|
|
// key <name> button <addr> [toggle]
|
|
// key <name> axis <axis> deflect <n> | rate <n>
|
|
// pad <button> button <addr> [toggle]
|
|
// padaxis <src> axis <axis> [invert] [deadzone <d>] [rate <n>]
|
|
// joydev <slot> [product-name substring...]
|
|
// joyaxis <src> axis <axis> [invert] [deadzone <d>] [rate <n>]
|
|
// joybutton <n> button <addr> [toggle]
|
|
// joyhat <n> <up|down|left|right> button <addr>
|
|
//
|
|
// <addr> is a RIO input address: lamp buttons 0x00-0x47, internal
|
|
// keypad 0x50-0x5F, external keypad 0x60-0x6F. Loaded from
|
|
// bindings.txt beside the exe; written there (self-documenting, with
|
|
// the full default profile) on first run. Bad lines are logged and
|
|
// skipped, good lines always win.
|
|
//
|
|
// The joy* rows drive generic DirectInput devices - flight sticks,
|
|
// HOTAS throttles, twist grips, rudder pedals (L4JOY.h). They attach to
|
|
// the most recent joydev slot, or slot 0 if no joydev came first. A slot
|
|
// naming a product substring binds THAT device; a bare slot binds the
|
|
// Nth attached non-XInput device. Source names follow the DirectInput
|
|
// layout - X Y Z RX RY RZ SL0 SL1 - where a twist grip is usually RZ
|
|
// and a HOTAS throttle usually Z or SL0. RP412JOYCONFIG=1 writes these
|
|
// rows for you by asking the player to move each control.
|
|
//########################################################################
|
|
|
|
enum PadBindRioAxis
|
|
{
|
|
BindAxisThrottle = 0,
|
|
BindAxisLeftPedal,
|
|
BindAxisRightPedal,
|
|
BindAxisJoystickY,
|
|
BindAxisJoystickX,
|
|
//
|
|
// A signed composite, not a channel the pod has: positive presses
|
|
// the right pedal, negative the left. One physical control - a
|
|
// rudder bar, a twist grip, a stick axis - works the pedal pair the
|
|
// way a foot never could, one or the other and never both. It
|
|
// decomposes into the real pair when the poll applies it.
|
|
//
|
|
BindAxisPedals,
|
|
BindAxisCount
|
|
};
|
|
|
|
enum PadBindKeyMode
|
|
{
|
|
BindKeyDeflect = 0, // hold at value while down, spring back
|
|
BindKeyRate // walk by value/second while down, position holds
|
|
};
|
|
|
|
enum PadBindPadAxis
|
|
{
|
|
BindPadLeftStickX = 0,
|
|
BindPadLeftStickY,
|
|
BindPadRightStickX,
|
|
BindPadRightStickY,
|
|
BindPadLeftTrigger,
|
|
BindPadRightTrigger,
|
|
BindPadAxisCount
|
|
};
|
|
|
|
struct PadKeyButtonBinding
|
|
{
|
|
int virtualKey;
|
|
int address; // 0x00-0x47 button, 0x50-0x6F keypad
|
|
Logical toggle;
|
|
// runtime edge/latch state
|
|
Logical wasDown;
|
|
Logical latched;
|
|
};
|
|
|
|
struct PadKeyAxisBinding
|
|
{
|
|
int virtualKey;
|
|
int axis; // PadBindRioAxis
|
|
int mode; // PadBindKeyMode
|
|
Scalar value;
|
|
};
|
|
|
|
struct PadPadButtonBinding
|
|
{
|
|
unsigned short padMask; // XINPUT_GAMEPAD_*
|
|
int address;
|
|
Logical toggle;
|
|
Logical wasDown;
|
|
Logical latched;
|
|
};
|
|
|
|
struct PadPadAxisBinding
|
|
{
|
|
int source; // PadBindPadAxis
|
|
int axis; // PadBindRioAxis
|
|
Logical invert;
|
|
Scalar deadzone; // normalized 0..1
|
|
Scalar rate; // 0 = direct position, >0 = speed integrate
|
|
};
|
|
|
|
//
|
|
// Generic joystick (DirectInput - L4JOY.h). device is a joydev SLOT,
|
|
// resolved to a live device at poll time by the slot's name substring or
|
|
// by its ordinal, so unplugging and replugging does not rewrite the file.
|
|
//
|
|
enum { BindJoyDeviceSlots = 4 };
|
|
|
|
enum PadBindJoyAxis
|
|
{
|
|
BindJoyAxisX = 0,
|
|
BindJoyAxisY,
|
|
BindJoyAxisZ,
|
|
BindJoyAxisRX,
|
|
BindJoyAxisRY,
|
|
BindJoyAxisRZ,
|
|
BindJoyAxisSL0,
|
|
BindJoyAxisSL1,
|
|
BindJoyAxisCount
|
|
};
|
|
|
|
struct PadJoyAxisBinding
|
|
{
|
|
int device; // joydev slot
|
|
int source; // PadBindJoyAxis
|
|
int axis; // PadBindRioAxis
|
|
Logical invert;
|
|
Scalar deadzone; // normalized 0..1
|
|
Scalar rate; // 0 = direct position, >0 = speed integrate
|
|
};
|
|
|
|
struct PadJoyButtonBinding
|
|
{
|
|
int device; // joydev slot
|
|
int button; // 0-31
|
|
int address;
|
|
Logical toggle;
|
|
Logical wasDown;
|
|
Logical latched;
|
|
};
|
|
|
|
struct PadJoyHatBinding
|
|
{
|
|
int device; // joydev slot
|
|
int hat; // 0-3
|
|
int direction; // 0 up, 1 right, 2 down, 3 left
|
|
int address;
|
|
};
|
|
|
|
struct PadBindingProfile
|
|
{
|
|
enum
|
|
{
|
|
maxKeyButtons = 128,
|
|
maxKeyAxes = 32,
|
|
maxPadButtons = 32,
|
|
maxPadAxes = 16,
|
|
maxJoyAxes = 24,
|
|
maxJoyButtons = 48,
|
|
maxJoyHats = 16
|
|
};
|
|
|
|
PadKeyButtonBinding keyButtons[maxKeyButtons];
|
|
int keyButtonCount;
|
|
PadKeyAxisBinding keyAxes[maxKeyAxes];
|
|
int keyAxisCount;
|
|
PadPadButtonBinding padButtons[maxPadButtons];
|
|
int padButtonCount;
|
|
PadPadAxisBinding padAxes[maxPadAxes];
|
|
int padAxisCount;
|
|
|
|
PadJoyAxisBinding joyAxes[maxJoyAxes];
|
|
int joyAxisCount;
|
|
PadJoyButtonBinding joyButtons[maxJoyButtons];
|
|
int joyButtonCount;
|
|
PadJoyHatBinding joyHats[maxJoyHats];
|
|
int joyHatCount;
|
|
// "" = take the slot's ordinal attached device
|
|
char joyDeviceMatch[BindJoyDeviceSlots][64];
|
|
};
|
|
|
|
// Load bindings.txt from the working directory into the profile,
|
|
// writing the default profile file first when absent. Errors go to
|
|
// the debug log with line numbers.
|
|
void
|
|
PadBindings_Load(PadBindingProfile *profile);
|