Skip to content

Engine Library

hifihedgehog edited this page Mar 19, 2026 · 56 revisions

Engine Library

The PadForge.Engine assembly is a shared class library containing data types, interfaces, and enums used by both the Engine (input pipeline) and App (UI/ViewModel) assemblies. It has no UI dependencies and targets net10.0-windows.

Project file: PadForge.Engine/PadForge.Engine.csproj Namespace: PadForge.Engine


Gamepad

File: PadForge.Engine/Common/GamepadTypes.cs

Minimal struct matching the XInput XINPUT_GAMEPAD layout. Used as the output of the mapping pipeline (Step 3 -> Step 4 -> Step 5).

public struct Gamepad
{
    public ushort Buttons;
    public ushort LeftTrigger;
    public ushort RightTrigger;
    public short ThumbLX;
    public short ThumbLY;
    public short ThumbRX;
    public short ThumbRY;

    public bool IsButtonPressed(ushort flag);
    public void SetButton(ushort flag, bool pressed);
    public void Clear();
}

Button Flag Constants

Constant Value Description
DPAD_UP 0x0001 D-pad up
DPAD_DOWN 0x0002 D-pad down
DPAD_LEFT 0x0004 D-pad left
DPAD_RIGHT 0x0008 D-pad right
START 0x0010 Start button
BACK 0x0020 Back button
LEFT_THUMB 0x0040 Left stick click
RIGHT_THUMB 0x0080 Right stick click
LEFT_SHOULDER 0x0100 Left bumper
RIGHT_SHOULDER 0x0200 Right bumper
GUIDE 0x0400 Guide/home button
A 0x1000 A button
B 0x2000 B button
X 0x4000 X button
Y 0x8000 Y button

VJoyRawState

File: PadForge.Engine/Common/GamepadTypes.cs

Raw vJoy output state for custom (non-gamepad) configurations. Bypasses the fixed Gamepad struct to support arbitrary axis/button/POV counts.

public struct VJoyRawState
{
    public short[] Axes;      // Up to 8 axes (signed short range -32768..32767)
    public uint[] Buttons;    // Button state as 4 x 32-bit words = 128 buttons max
    public int[] Povs;        // Up to 4 POV hat switches (-1=centered, 0-35900=direction)

    public static VJoyRawState Create(int nAxes, int nButtons, int nPovs);
    public void SetButton(int index, bool pressed);
    public bool IsButtonPressed(int index);
    public void Clear();
}

Create()

public static VJoyRawState Create(int nAxes, int nButtons, int nPovs)

Creates a zeroed state with specified capacities. Axes clamped to max 8, buttons to max 128 (stored as (N+31)/32 uint words), POVs to max 4.

Button Storage

Buttons use a 128-bit bitmask stored as uint[4]. Each uint holds 32 buttons. Index calculation:

  • word = index / 32
  • bit = index % 32

POV Values

POV values are in hundredths of degrees:

  • 0 = North
  • 4500 = NE
  • 9000 = East
  • 13500 = SE
  • 18000 = South
  • 22500 = SW
  • 27000 = West
  • 31500 = NW
  • 0xFFFFFFFF (-1) = Centered

Clear()

Resets axes to 0, buttons to 0, POVs to -1 (centered).


MidiRawState

File: PadForge.Engine/Common/GamepadTypes.cs

Dynamic-sized MIDI output state for MidiVirtualController. Bypasses the fixed Gamepad struct to support arbitrary CC and note counts.

public struct MidiRawState
{
    public byte[] CcValues;   // CC values 0-127 per CC slot
    public bool[] Notes;      // Note on/off per note slot

    public static MidiRawState Create(int ccCount, int noteCount);
}

Create() allocates arrays of the specified sizes with CC values initialized to 0. Clear() resets CC values to 64 (center for axes).


KbmRawState

File: PadForge.Engine/Common/GamepadTypes.cs

Raw keyboard + mouse output state for the KeyboardMouseVirtualController. Key states are packed into 4 x 64-bit words covering 256 Windows virtual key codes. Mouse axes are signed short range for delta movement per frame.

public struct KbmRawState
{
    public ulong Keys0, Keys1, Keys2, Keys3;   // 256 VK codes (4 × 64-bit words)
    public short MouseDeltaX;                    // Mouse X delta (signed)
    public short MouseDeltaY;                    // Mouse Y delta (signed)
    public short ScrollDelta;                    // Scroll delta (positive = up)
    public byte MouseButtons;                    // Bit 0=LMB, 1=RMB, 2=MMB, 3=X1, 4=X2
    public short PreDzMouseDeltaX;               // Mouse X before deadzone (UI preview)
    public short PreDzMouseDeltaY;               // Mouse Y before deadzone (UI preview)
    public short PreDzScrollDelta;               // Scroll before deadzone (UI preview)

    public bool GetKey(byte vk);
    public void SetKey(byte vk, bool pressed);
    public bool GetMouseButton(int index);
    public void SetMouseButton(int index, bool pressed);
    public void Clear();
    public static KbmRawState Combine(KbmRawState a, KbmRawState b);
}

Key Storage

Keys use a 256-bit bitmask stored as ulong[4]. Each ulong holds 64 virtual key codes. Index calculation:

  • word = vk / 64
  • bit = vk % 64

Combine()

Merges two KBM states for multi-device assignment:

  • Keys and mouse buttons: OR'd
  • Mouse deltas and scroll: largest absolute magnitude wins

Pre-Deadzone Fields

PreDzMouseDeltaX, PreDzMouseDeltaY, and PreDzScrollDelta store mouse/scroll values before center offset and deadzone processing. Used by the UI layer to display raw stick and trigger previews for KBM slots.


VirtualControllerType

File: PadForge.Engine/Common/VirtualControllerTypes.cs

public enum VirtualControllerType
{
    Xbox360 = 0,
    DualShock4 = 1,
    VJoy = 2,
    Midi = 3,
    KeyboardMouse = 4
}

IVirtualController

File: PadForge.Engine/Common/VirtualControllerTypes.cs

public interface IVirtualController : IDisposable
{
    VirtualControllerType Type { get; }
    bool IsConnected { get; }
    int FeedbackPadIndex { get; set; }
    void Connect();
    void Disconnect();
    void SubmitGamepadState(Gamepad gp);
    void RegisterFeedbackCallback(int padIndex, Vibration[] vibrationStates);
}

FeedbackPadIndex tracks which slot this VC occupies so feedback callbacks write to the correct VibrationStates element after a slot reorder via SwapSlotData.

Abstraction over virtual controller operations. Concrete implementations live in the App assembly:

  • Xbox360VirtualController (ViGEm)
  • DS4VirtualController (ViGEm)
  • VJoyVirtualController (direct P/Invoke to vJoyInterface.dll)
  • MidiVirtualController (Windows MIDI Services SDK)
  • KeyboardMouseVirtualController (Win32 SendInput)

CustomInputState

File: PadForge.Engine/Common/CustomInputState.cs

API-agnostic snapshot of a device's complete input state at a single point in time.

public class CustomInputState
{
    public const int MaxAxis = 24;
    public const int MaxSliders = 8;
    public const int MaxPovs = 4;
    public const int MaxButtons = 256;

    public int[] Axis;       // 0-65535, center = 32767
    public int[] Sliders;    // 0-65535
    public int[] Povs;       // centidegrees 0-35900, or -1 for centered
    public bool[] Buttons;   // true = pressed
    public float[] Gyro;     // [X, Y, Z] radians per second
    public float[] Accel;    // [X, Y, Z] meters per second squared
}

Constructors

public CustomInputState()
public CustomInputState(int[] axes, int[] sliders, int[] povs, bool[] buttons)

Default constructor creates zeroed arrays with default sizes. POVs initialize to -1 (centered). Copy constructor copies arrays up to max lengths (snapshot isolation).

Methods

public CustomInputState Clone()

Deep copy of all arrays.

public static void GetAxisMask(
    DeviceObjectItem[] items, int numAxes,
    out int axisMask, out int actuatorMask, out int actuatorCount)

Scans device object items to build axis and force-feedback actuator bitmasks. Bit N set = axis/actuator N exists.

Value Conventions

Array Range Center Description
Axis 0-65535 32767 Indices 0-5 = X, Y, Z, Rx, Ry, Rz. 6-23 = additional
Sliders 0-65535 32767 Overflow or dedicated slider controls
Povs 0-35900 or -1 -1 Centidegrees. -1 = centered
Buttons bool false 256 max (covers full Windows VK code range)
Gyro float[3] 0.0 Radians/second. Only for gyro-capable devices
Accel float[3] 0.0 m/s^2. Only for accelerometer-capable devices

ISdlInputDevice

File: PadForge.Engine/Common/ISdlInputDevice.cs

Common interface for all SDL-based input device wrappers (joystick/gamepad, keyboard, mouse).

public interface ISdlInputDevice : IDisposable
{
    // Identity
    uint SdlInstanceId { get; }
    string Name { get; }
    Guid InstanceGuid { get; }
    Guid ProductGuid { get; }
    string DevicePath { get; }
    string SerialNumber { get; }
    ushort VendorId { get; }
    ushort ProductId { get; }

    // Capabilities
    int NumAxes { get; }
    int NumButtons { get; }
    int RawButtonCount { get; }
    int NumHats { get; }
    bool HasRumble { get; }
    bool HasHaptic { get; }
    bool HasGyro { get; }
    bool HasAccel { get; }
    bool IsAttached { get; }

    // Haptic
    HapticEffectStrategy HapticStrategy { get; }
    IntPtr HapticHandle { get; }
    uint HapticFeatures { get; }
    int NumHapticAxes { get; }

    // State reading
    CustomInputState GetCurrentState(bool forceRaw = false);
    DeviceObjectItem[] GetDeviceObjects();
    int GetInputDeviceType();

    // Force feedback
    bool SetRumble(ushort low, ushort high, uint durationMs = uint.MaxValue);
    bool StopRumble();
}

HapticEffectStrategy

public enum HapticEffectStrategy
{
    None,
    LeftRight,
    Sine,
    Constant
}

Priority order chosen at haptic open time based on SDL_GetHapticFeatures:

  1. LeftRight — Best match for dual-motor rumble (low + high frequency).
  2. Sine — Periodic effect. Period varies by which motor is stronger.
  3. Constant — Fallback. Level from dominant motor.

WebControllerDevice

File: PadForge.Engine/Common/WebControllerDevice.cs

Virtual input device representing a browser-connected gamepad. Implements ISdlInputDevice so it integrates with the standard input pipeline.

Property Value
VID / PID 0xBEEF / 0xCA7E
Axes 6 (LX, LY, LT, RX, RY, RT — 0-65535 range)
Buttons 11 (standard Xbox layout: A, B, X, Y, LB, RB, Back, Start, LS, RS, Guide)
POV Hats 1
HasRumble true (via browser Vibration API)

State is written by the WebSocket thread and read by the polling thread via volatile reference swaps.


DeviceObjectItem

File: PadForge.Engine/Common/DeviceObjectItem.cs

Describes a single input object (axis, button, hat, slider) on a device. Used by the mapping UI and pipeline.

public class DeviceObjectItem
{
    // Identity
    public string Name { get; set; }
    public Guid ObjectTypeGuid { get; set; }
    public DeviceObjectTypeFlags ObjectType { get; set; }

    // Position
    public int InputIndex { get; set; }
    public int Offset { get; set; }

    // Aspect
    public ObjectAspect Aspect { get; set; }

    // Computed helpers
    public bool IsForceActuator { get; }
    public bool IsAxis { get; }
    public bool IsButton { get; }
    public bool IsPov { get; }
    public bool IsSlider { get; }
}

Property Details

Property Type Default Description
Name string "" Human-readable name (e.g., "X Axis", "Button 3")
ObjectTypeGuid Guid Guid.Empty Well-known GUID from ObjectGuid
ObjectType DeviceObjectTypeFlags All Classification flags
InputIndex int 0 Zero-based index into CustomInputState arrays
Offset int 0 Byte offset for legacy mapping compatibility
Aspect ObjectAspect Position Object aspect (only Position is defined)

Computed Properties

  • IsForceActuator: (ObjectType & ForceFeedbackActuator) != 0
  • IsAxis: (ObjectType & Axis) != 0
  • IsButton: (ObjectType & Button) != 0
  • IsPov: (ObjectType & PointOfViewController) != 0
  • IsSlider: ObjectTypeGuid == ObjectGuid.Slider

ToString()

Returns "{Name} ({TypeLabel}, Index {InputIndex})" where TypeLabel is "Axis", "Slider", "POV", "Button", or "Object".


InputTypes

File: PadForge.Engine/Common/InputTypes.cs

DeviceObjectTypeFlags

[Flags]
public enum DeviceObjectTypeFlags : int
{
    All = 0,
    RelativeAxis = 1,
    AbsoluteAxis = 2,
    Axis = 3,                        // RelativeAxis | AbsoluteAxis
    PushButton = 4,
    ToggleButton = 8,
    Button = 12,                     // PushButton | ToggleButton
    PointOfViewController = 16,
    Collection = 64,
    NoData = 128,
    ForceFeedbackActuator = 0x01000000,
    ForceFeedbackEffectTrigger = 0x02000000
}

ObjectAspect

[Flags]
public enum ObjectAspect : int
{
    Position = 0x100
}

EffectParameterFlags

[Flags]
public enum EffectParameterFlags : int
{
    None = 0
}

ObjectGuid

Static class providing well-known GUIDs for device object types. Values match DirectInput GUID constants.

public static class ObjectGuid
{
    public static readonly Guid XAxis;          // {A36D02E0-C9F3-11CF-BFC7-444553540000}
    public static readonly Guid YAxis;          // {A36D02E1-...}
    public static readonly Guid ZAxis;          // {A36D02E2-...}
    public static readonly Guid RxAxis;         // {A36D02F4-...}
    public static readonly Guid RyAxis;         // {A36D02F5-...}
    public static readonly Guid RzAxis;         // {A36D02E3-...}
    public static readonly Guid Slider;         // {A36D02E4-...}
    public static readonly Guid Button;         // {A36D02F0-...}
    public static readonly Guid Key;            // {55728220-D33C-11CF-BFC7-444553540000}
    public static readonly Guid PovController;  // {A36D02F2-...}
    public static readonly Guid Unknown;        // Guid.Empty
}

InputDeviceType

Integer constants matching DirectInput device type values. Used for UserDevice.CapType.

public static class InputDeviceType
{
    public const int Device = 17;
    public const int Mouse = 18;
    public const int Keyboard = 19;
    public const int Joystick = 20;
    public const int Gamepad = 21;
    public const int Driving = 22;
    public const int Flight = 23;
    public const int FirstPerson = 24;
    public const int Supplemental = 25;
}

MapType

public enum MapType : int
{
    None = 0,
    Axis = 1,
    Button = 2,
    Slider = 3,
    POV = 4
}

ForceFeedbackState

File: PadForge.Engine/Common/ForceFeedbackState.cs

Manages force feedback (rumble) state for a single device with change detection.

public class ForceFeedbackState
{
    // Public state
    public ushort LeftMotorSpeed { get; }    // 0-65535
    public ushort RightMotorSpeed { get; }   // 0-65535
    public bool IsActive { get; }

    // Methods
    public void SetDeviceForces(UserDevice ud, ISdlInputDevice device, PadSetting ps, Vibration v);
    public void StopDeviceForces(ISdlInputDevice device);
    public bool Changed(PadSetting ps);
}

SetDeviceForces()

public void SetDeviceForces(UserDevice ud, ISdlInputDevice device, PadSetting ps, Vibration v)
  1. Reads gain settings from PadSetting: ForceOverall, LeftMotorStrength, RightMotorStrength (all 0-100).
  2. Applies per-motor and overall gain scaling to raw XInput motor speeds.
  3. Swaps motors if ForceSwapMotor is configured.
  4. Change detection: Only sends to hardware when values differ from cached values. Each SDL_RumbleJoystick call restarts the hardware rumble, causing brief gaps. By only sending on change with uint.MaxValue duration (~49 days), rumble stays continuous.
  5. Routes to either SetHapticForces() (for haptic devices) or SetRumble() (for rumble devices).

SetHapticForces()

private bool SetHapticForces(ISdlInputDevice device, ushort left, ushort right)

Translates dual-motor rumble to SDL haptic effects based on the device's HapticEffectStrategy:

Strategy SDL Effect Large Motor Small Motor
LeftRight SDL_HAPTIC_LEFTRIGHT large_magnitude = left small_magnitude = right
Sine SDL_HAPTIC_SINE magnitude = max/2, period = 120 period = 40
Constant SDL_HAPTIC_CONSTANT level = max/2 N/A

Creates effect on first call via SDL_CreateHapticEffect, updates in-place on subsequent calls via SDL_UpdateHapticEffect.

Changed()

public bool Changed(PadSetting ps)

Returns true if any force feedback setting in the PadSetting has changed since last call. Caches: ForceType, ForceSwapMotor, LeftMotorStrength, RightMotorStrength, ForceOverall.


Vibration

File: PadForge.Engine/Common/ForceFeedbackState.cs

public class Vibration
{
    // Scalar (used by ViGEm Xbox/DS4 callbacks and rumble path)
    public ushort LeftMotorSpeed { get; set; }   // Low-frequency, heavy rumble. 0-65535
    public ushort RightMotorSpeed { get; set; }  // High-frequency, light buzz. 0-65535

    // Directional FFB (populated by vJoy FFB callback for haptic devices)
    public bool HasDirectionalData { get; set; }
    public uint EffectType { get; set; }
    public short SignedMagnitude { get; set; }   // -10000 to +10000
    public ushort Direction { get; set; }         // Polar 0-32767 (0=North)
    public uint Period { get; set; }              // ms, for periodic effects
    public byte DeviceGain { get; set; } = 255;   // 0-255

    // Condition effect data (spring/damper/friction/inertia)
    public bool HasConditionData { get; set; }
    public ConditionAxisData[] ConditionAxes { get; set; }
    public int ConditionAxisCount { get; set; }   // 1 for wheels, 2 for joysticks

    public Vibration();
    public Vibration(ushort leftMotor, ushort rightMotor);
}

The scalar fields (LeftMotorSpeed, RightMotorSpeed) are set by ViGEm FeedbackReceived callbacks and the vJoy FFB rumble fallback path. The directional fields are populated by the vJoy FFB callback for devices with haptic support (constant force, periodic effects, ramp). The condition fields carry per-axis Spring/Damper/Friction/Inertia parameters. Read by Step 2 on the polling thread.

ConditionAxisData

public struct ConditionAxisData
{
    public short PositiveCoefficient;    // 0-10000
    public short NegativeCoefficient;    // 0-10000
    public short Offset;                 // -10000 to +10000
    public uint DeadBand;                // 0-10000
    public uint PositiveSaturation;      // 0-10000
    public uint NegativeSaturation;      // 0-10000
}

Per-axis condition parameters for spring/damper/friction/inertia effects. Index 0 = X axis, Index 1 = Y axis.


RumbleLogger

File: PadForge.Engine/Common/RumbleLogger.cs

public static class RumbleLogger
{
    public static bool Enabled { get; set; }
    public static void Log(string message);
}

Diagnostic logger for force feedback debugging. Disabled by default. Set Enabled = true in InputService.Start() to activate. Thread-safe. Writes timestamped messages to a log file using Stopwatch for high-resolution timing.


InputHookManager

File: PadForge.Engine/Common/InputHookManager.cs

Manages WH_KEYBOARD_LL and WH_MOUSE_LL low-level Windows hooks for suppressing mapped inputs from keyboards and mice. Only suppresses inputs that are in the active suppression sets — non-mapped keys/buttons pass through normally.

public class InputHookManager : IDisposable
{
    void Start();
    void Stop();
    void SetSuppressedKeys(HashSet<int> vkCodes);
    void SetSuppressedMouseButtons(HashSet<int> buttons);
    bool HasAnySuppression { get; }
    void MoveMouse(int dx, int dy);
    void ScrollMouse(int clicks);
}

Mouse Action Methods

void MoveMouse(int dx, int dy)

Moves the mouse cursor by the given delta using SendInput with MOUSEINPUT (MOUSEEVENTF_MOVE). Called by macro mouse move actions.

void ScrollMouse(int clicks)

Scrolls the mouse wheel by the given number of clicks using SendInput with MOUSEINPUT (MOUSEEVENTF_WHEEL). Positive = scroll up, negative = scroll down. Called by macro mouse scroll actions.

Threading

Hooks require a thread with a message pump. InputHookManager creates a dedicated background thread running a GetMessage loop. Hooks are installed on this thread via SetWindowsHookExW.

  • Start() creates the thread and waits (via ManualResetEventSlim) until hooks are installed before returning.
  • Stop() posts WM_QUIT to the hook thread to exit the message loop, then joins the thread.

Suppression Sets

Suppression sets use volatile reference swap for thread safety — the hook callbacks read the current set reference without locking, and updates replace the entire HashSet atomically.

void SetSuppressedKeys(HashSet<int> vkCodes)       // Virtual key codes to suppress
void SetSuppressedMouseButtons(HashSet<int> buttons) // 0=Left, 1=Right, 2=Middle, 3=XButton1, 4=XButton2

Hook Callbacks

  • Keyboard: Intercepts WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP. Reads KBDLLHOOKSTRUCT.vkCode and checks against the suppression set. Returns (IntPtr)1 to suppress, or CallNextHookEx to pass through.
  • Mouse: Intercepts button messages (WM_LBUTTONDOWN/UP, WM_RBUTTONDOWN/UP, WM_MBUTTONDOWN/UP, WM_XBUTTONDOWN/UP). Converts the message to a button ID via MouseMessageToButtonId() and checks against the suppression set. Mouse movement and wheel events always pass through.

Button ID Mapping

Mouse Message Button ID
WM_LBUTTONDOWN/UP 0 (Left)
WM_RBUTTONDOWN/UP 1 (Right)
WM_MBUTTONDOWN/UP 2 (Middle)
WM_XBUTTONDOWN/UP (XBUTTON1) 3
WM_XBUTTONDOWN/UP (XBUTTON2) 4
Other (move, wheel) -1 (pass through)

P/Invoke

Function DLL Purpose
SetWindowsHookExW user32.dll Install low-level hook
UnhookWindowsHookEx user32.dll Remove hook
CallNextHookEx user32.dll Pass input to next hook
GetModuleHandleW kernel32.dll Get module handle for hook registration
GetMessageW user32.dll Message pump loop
PostThreadMessageW user32.dll Post WM_QUIT to hook thread
GetCurrentThreadId kernel32.dll Get hook thread ID

RawInputListener

File: PadForge.Engine/Common/RawInputListener.cs

Manages Windows Raw Input registration and message processing for keyboard and mouse devices. Runs on a dedicated message-pump thread.

Key capabilities:

  • Enumerates keyboards and mice via GetRawInputDeviceList
  • Processes WM_INPUT messages for per-device input state
  • Mouse scroll wheel axis support: captures WM_MOUSEWHEEL data and exposes scroll delta as an axis value

SdlMouseWrapper

File: PadForge.App/Common/Input/SdlMouseWrapper.cs

Wraps a physical mouse device for the input pipeline. Implements ISdlInputDevice.

  • Reads mouse state from RawInputListener per-device buffers
  • Scroll wheel intensity tracking: maintains scroll wheel delta as an axis value with decay, allowing scroll speed to be mapped like a proportional axis

MappingTranslation

File: PadForge.Engine/Data/MappingTranslation.cs Namespace: PadForge.Engine.Data

Static class that translates mapping property names between different virtual controller layouts using positional equivalence. When copying mappings from one slot to another with a different output type (e.g., Xbox 360 to vJoy custom, or DS4 to MIDI), property names must be translated because each layout uses different naming conventions for the same logical control.

Key Types

public enum ControlCategory { Button, Axis, AxisNeg, DPad }
public record MappingSlot(ControlCategory Category, int Position);

A MappingSlot represents a canonical position within a control category (e.g., "3rd button", "1st axis negative"). Translation works by converting a source property name to a MappingSlot, then converting that slot to the target layout's property name.

Key Methods

Method Description
GetPosition(string propertyName, VirtualControllerType type, bool isCustomVJoy) Maps a layout-specific property name to its canonical MappingSlot
GetPropertyName(MappingSlot slot, VirtualControllerType type, bool isCustomVJoy) Maps a canonical MappingSlot to a layout-specific property name
IsSameLayout(srcType, srcIsCustomVJoy, tgtType, tgtIsCustomVJoy) Returns true if source and target use the same property names (no translation needed)
GetLayoutLabel(VirtualControllerType type, bool isCustomVJoy) Returns display label (e.g., "Xbox 360", "vJoy", "MIDI", "KB+M")

Supported Layouts

Layout Property Name Examples Notes
Gamepad (Xbox 360 / DS4 / vJoy gamepad preset) ButtonA, LeftThumbAxisX, DPadUp Xbox 360 and DS4 share the same property names
vJoy Custom VJoyBtn0, VJoyAxis2, VJoyPov0Up Indexed by position
MIDI MidiNote0, MidiCC3, MidiCC3Neg No D-Pad support
KB+M KbmMBtn0, KbmMouseX, KbmKey20 Mouse buttons 0-4, then keyboard VK codes

Stick Calibration Fields (PadSetting)

Field Type Description
LeftThumbCenterOffsetX string Left stick X center offset (-100 to 100%). Default "0".
LeftThumbCenterOffsetY string Left stick Y center offset. Default "0".
RightThumbCenterOffsetX string Right stick X center offset. Default "0".
RightThumbCenterOffsetY string Right stick Y center offset. Default "0".
LeftThumbMaxRangeX string Left stick X max range (1-100%). Default "100".
LeftThumbMaxRangeY string Left stick Y max range. Default "100".
RightThumbMaxRangeX string Right stick X max range. Default "100".
RightThumbMaxRangeY string Right stick Y max range. Default "100".

Center offset is applied in Step 3 before dead zone processing. Max range scales the output range.

Audio Rumble Properties (PadSetting)

Field Type Description
AudioRumbleEnabled string Enable audio bass rumble. "0" = off, "1" = on. Default "0".
AudioRumbleSensitivity string Bass detection sensitivity. Default "4".
AudioRumbleCutoffHz string Low-pass cutoff frequency in Hz. Default "80".
AudioRumbleLeftMotor string Left motor strength percentage for audio rumble (0-100). Default "100".
AudioRumbleRightMotor string Right motor strength percentage for audio rumble (0-100). Default "100".

Input Hiding Fields (UserDevice)

Field Type Description
HidHideEnabled bool Whether this device is hidden from games via HidHide
ConsumeInputEnabled bool Whether mapped keyboard/mouse inputs are suppressed via hooks
ForceRawJoystickMode bool Bypass SDL3 gamepad remapping, read raw joystick indices
HidHideInstanceIds List<string> Cached HID instance IDs for blacklisting (persisted for offline devices)

Clone this wiki locally