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>
70 lines
2.9 KiB
C#
70 lines
2.9 KiB
C#
using VRio.Core.Device;
|
||
|
||
namespace VRio.Core.Input;
|
||
|
||
/// <summary>
|
||
/// How a key drives an axis: <see cref="Deflect"/> holds the axis at the
|
||
/// binding's value while the key is down and springs back when released
|
||
/// (stick, pedals); <see cref="Rate"/> walks the axis by value-per-second
|
||
/// while held and the position sticks (throttle).
|
||
/// </summary>
|
||
public enum KeyAxisMode
|
||
{
|
||
Deflect,
|
||
Rate,
|
||
}
|
||
|
||
/// <summary>A keyboard key pressing a RIO input address.</summary>
|
||
/// <param name="Key">Key name as the host reports it (System.Windows.Forms
|
||
/// Keys.ToString(): "W", "Up", "NumPad1", …); matched case-insensitively.</param>
|
||
/// <param name="Address">RIO input address (button 0x00–0x47 or keypad 0x50–0x6F).</param>
|
||
/// <param name="Toggle">Latch on press instead of momentary press/release.</param>
|
||
public sealed record KeyButtonBinding(string Key, int Address, bool Toggle);
|
||
|
||
/// <summary>
|
||
/// A keyboard key driving a RIO axis in normalized units, where 1 is the
|
||
/// axis' full realistic travel (<see cref="RioAxisRange.Full"/> carries the
|
||
/// wire sign, so bindings never deal with the throttle's negative direction).
|
||
/// </summary>
|
||
/// <param name="Value">Deflect: the normalized position held while the key is
|
||
/// down. Rate: normalized units per second, signed.</param>
|
||
public sealed record KeyAxisBinding(string Key, RioAxis Axis, KeyAxisMode Mode, float Value);
|
||
|
||
/// <summary>A gamepad button pressing a RIO input address.</summary>
|
||
public sealed record PadButtonBinding(PadButtons Button, int Address, bool Toggle);
|
||
|
||
/// <summary>
|
||
/// A gamepad analog control driving a RIO axis. With <paramref name="Rate"/>
|
||
/// zero the (deadzoned, optionally inverted) pad value maps directly to the
|
||
/// normalized axis position; with a positive rate the pad value is a speed —
|
||
/// the axis integrates by value × rate per second and holds (throttle-style).
|
||
/// </summary>
|
||
public sealed record PadAxisBinding(PadAxis Source, RioAxis Axis, bool Invert, float Deadzone, float Rate);
|
||
|
||
/// <summary>An immutable set of input bindings (one parsed profile file).</summary>
|
||
public sealed class BindingProfile
|
||
{
|
||
public static readonly BindingProfile Empty = new(
|
||
Array.Empty<KeyButtonBinding>(), Array.Empty<KeyAxisBinding>(),
|
||
Array.Empty<PadButtonBinding>(), Array.Empty<PadAxisBinding>());
|
||
|
||
public BindingProfile(
|
||
IReadOnlyList<KeyButtonBinding> keyButtons,
|
||
IReadOnlyList<KeyAxisBinding> keyAxes,
|
||
IReadOnlyList<PadButtonBinding> padButtons,
|
||
IReadOnlyList<PadAxisBinding> padAxes)
|
||
{
|
||
KeyButtons = keyButtons;
|
||
KeyAxes = keyAxes;
|
||
PadButtons = padButtons;
|
||
PadAxes = padAxes;
|
||
}
|
||
|
||
public IReadOnlyList<KeyButtonBinding> KeyButtons { get; }
|
||
public IReadOnlyList<KeyAxisBinding> KeyAxes { get; }
|
||
public IReadOnlyList<PadButtonBinding> PadButtons { get; }
|
||
public IReadOnlyList<PadAxisBinding> PadAxes { get; }
|
||
|
||
public int Count => KeyButtons.Count + KeyAxes.Count + PadButtons.Count + PadAxes.Count;
|
||
}
|