-
Notifications
You must be signed in to change notification settings - Fork 6
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 (Common types), PadForge.Engine.Data (data models), PadForge.Engine.Common (InputHookManager), SDL3 (P/Invoke)
- Gamepad (GamepadTypes.cs)
- VJoyRawState (GamepadTypes.cs)
- KbmRawState (GamepadTypes.cs)
- MidiRawState (GamepadTypes.cs)
- VirtualControllerType (VirtualControllerTypes.cs)
- IVirtualController (VirtualControllerTypes.cs)
- CustomInputState (CustomInputState.cs)
- CustomInputUpdate (CustomInputUpdate.cs)
- CustomInputHelper (CustomInputHelper.cs)
- ISdlInputDevice (ISdlInputDevice.cs)
- SdlDeviceWrapper (SdlDeviceWrapper.cs)
- HapticEffectStrategy (SdlDeviceWrapper.cs)
- SdlKeyboardWrapper (SdlKeyboardWrapper.cs)
- SdlMouseWrapper (SdlMouseWrapper.cs)
- WebControllerDevice (WebControllerDevice.cs)
- DeviceObjectItem (DeviceObjectItem.cs)
- DeviceEffectItem (DeviceEffectItem.cs)
- InputTypes (InputTypes.cs)
- ForceFeedbackState (ForceFeedbackState.cs)
- FfbEffectTypes (ForceFeedbackState.cs)
- Vibration (ForceFeedbackState.cs)
- ConditionAxisData (ForceFeedbackState.cs)
- RumbleLogger (RumbleLogger.cs)
- InputHookManager (InputHookManager.cs)
- RawInputListener (RawInputListener.cs)
- PadSetting (Data/PadSetting.cs)
- VJoyMappingEntry (Data/PadSetting.cs)
- UserSetting (Data/UserSetting.cs)
- UserDevice (Data/UserDevice.cs)
- DeadZoneShape (Data/DeadZoneShape.cs)
- MappingTranslation (Data/MappingTranslation.cs)
- SDL3 P/Invoke (SDL3Minimal.cs)
File: PadForge.Engine/Common/GamepadTypes.cs
Namespace: PadForge.Engine
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
{
// Fields
public ushort Buttons; // Bitmask of button flags
public ushort LeftTrigger; // 0-65535
public ushort RightTrigger; // 0-65535
public short ThumbLX; // -32768 to 32767
public short ThumbLY; // -32768 to 32767
public short ThumbRX; // -32768 to 32767
public short ThumbRY; // -32768 to 32767
// Methods
public bool IsButtonPressed(ushort flag);
public void SetButton(ushort flag, bool pressed);
public void Clear();
}| 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 |
| Method | Signature | Description |
|---|---|---|
IsButtonPressed |
bool IsButtonPressed(ushort flag) |
Returns true if the specified button flag bit is set in Buttons
|
SetButton |
void SetButton(ushort flag, bool pressed) |
Sets or clears a button flag bit using bitwise OR/AND |
Clear |
void Clear() |
Resets all fields to zero |
File: PadForge.Engine/Common/GamepadTypes.cs
Namespace: PadForge.Engine
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();
}| Method | Signature | Description |
|---|---|---|
Create |
static VJoyRawState Create(int nAxes, int nButtons, int nPovs) |
Factory method. Axes clamped to max 8, buttons to max 128 (stored as (N+31)/32 uint words), POVs to max 4. All values zeroed. |
SetButton |
void SetButton(int index, bool pressed) |
Sets a button by 0-based index. Calculates word = index/32, bit = index%32. No-op if index out of range. |
IsButtonPressed |
bool IsButtonPressed(int index) |
Returns true if the button at the given index is set. Returns false if index out of range. |
Clear |
void Clear() |
Resets axes to 0, buttons to 0, POVs to -1 (centered). |
Buttons use a 128-bit bitmask stored as uint[4]. Each uint holds 32 buttons.
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.
File: PadForge.Engine/Common/GamepadTypes.cs
Namespace: PadForge.Engine
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
{
// Key state (256 VK codes packed into 4 ulongs)
public ulong Keys0; // VK 0-63
public ulong Keys1; // VK 64-127
public ulong Keys2; // VK 128-191
public ulong Keys3; // VK 192-255
// Mouse output
public short MouseDeltaX; // Mouse X delta (signed, pixels per frame)
public short MouseDeltaY; // Mouse Y delta (signed, pixels per frame)
public short ScrollDelta; // Mouse scroll delta (positive = up)
public byte MouseButtons; // Bit 0=LMB, 1=RMB, 2=MMB, 3=X1, 4=X2
// Pre-deadzone values (for UI stick/trigger preview)
public short PreDzMouseDeltaX; // Mouse X before center offset + deadzone
public short PreDzMouseDeltaY; // Mouse Y before center offset + deadzone
public short PreDzScrollDelta; // Scroll before deadzone
// Methods
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);
}| Method | Signature | Description |
|---|---|---|
GetKey |
bool GetKey(byte vk) |
Returns true if virtual key code is set. Calculates word = vk/64, bit = vk%64. |
SetKey |
void SetKey(byte vk, bool pressed) |
Sets or clears a virtual key code bit. |
GetMouseButton |
bool GetMouseButton(int index) |
Returns true if mouse button bit is set (0=LMB, 1=RMB, 2=MMB, 3=X1, 4=X2). |
SetMouseButton |
void SetMouseButton(int index, bool pressed) |
Sets or clears a mouse button bit. |
Clear |
void Clear() |
Resets all keys, mouse deltas, scroll, mouse buttons, and pre-deadzone fields to zero. |
Combine |
static KbmRawState Combine(KbmRawState a, KbmRawState b) |
Merges two KBM states for multi-device assignment. Keys and mouse buttons are OR'd. Mouse deltas and scroll take the largest absolute magnitude. Pre-deadzone fields also use largest magnitude. |
File: PadForge.Engine/Common/GamepadTypes.cs
Namespace: PadForge.Engine
Dynamic-sized MIDI output state for MidiVirtualController. CC values are 0-127 (MIDI range). Notes are boolean (on/off).
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);
public void Clear();
public static MidiRawState Combine(MidiRawState a, MidiRawState b);
}| Method | Signature | Description |
|---|---|---|
Create |
static MidiRawState Create(int ccCount, int noteCount) |
Allocates arrays of specified sizes. CC values initialized to 0. |
Clear |
void Clear() |
Resets CC values to 64 (center for axes), notes to false. |
Combine |
static MidiRawState Combine(MidiRawState a, MidiRawState b) |
Merges two MIDI states. CCs take the value furthest from center (64); notes are OR'd. |
File: PadForge.Engine/Common/VirtualControllerTypes.cs
Namespace: PadForge.Engine
public enum VirtualControllerType
{
Xbox360 = 0,
DualShock4 = 1,
VJoy = 2,
Midi = 3,
KeyboardMouse = 4
}File: PadForge.Engine/Common/VirtualControllerTypes.cs
Namespace: PadForge.Engine
Abstraction over virtual controller operations. Concrete implementations live in the App assembly:
-
Xbox360VirtualController(ViGEm) -
DS4VirtualController(ViGEm) -
VJoyVirtualController(direct P/Invoke tovJoyInterface.dll) -
MidiVirtualController(Windows MIDI Services SDK) -
KeyboardMouseVirtualController(Win32 SendInput)
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);
}| Member | Type | Description |
|---|---|---|
Type |
VirtualControllerType |
The type of virtual controller |
IsConnected |
bool |
Whether the virtual controller is currently connected/plugged in |
FeedbackPadIndex |
int |
Tracks which slot this VC occupies so feedback callbacks write to the correct VibrationStates element after a slot reorder via SwapSlotData
|
Connect() |
void |
Creates and plugs in the virtual controller |
Disconnect() |
void |
Unplugs and destroys the virtual controller |
SubmitGamepadState(Gamepad) |
void |
Sends the current gamepad state to the virtual controller |
RegisterFeedbackCallback(int, Vibration[]) |
void |
Registers a callback that writes rumble feedback to the shared VibrationStates array at the given pad index |
File: PadForge.Engine/Common/CustomInputState.cs
Namespace: PadForge.Engine
API-agnostic snapshot of a device's complete input state at a single point in time.
public class CustomInputState
{
// Constants
public const int MaxAxis = 24;
public const int MaxSliders = 8;
public const int MaxPovs = 4;
public const int MaxButtons = 256;
// Fields
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);
// Methods
public CustomInputState Clone();
public static void GetAxisMask(DeviceObjectItem[] items, int numAxes,
out int axisMask, out int actuatorMask, out int actuatorCount);
}| Constructor | Description |
|---|---|
CustomInputState() |
Creates zeroed arrays with default sizes. POVs initialize to -1 (centered). Gyro and Accel are 3-element float arrays. |
CustomInputState(int[], int[], int[], bool[]) |
Copies arrays up to max lengths (snapshot isolation). POVs initialized to -1 before copy. |
| Method | Signature | Description |
|---|---|---|
Clone |
CustomInputState Clone() |
Deep copy of all arrays including Gyro and Accel. |
GetAxisMask |
static void GetAxisMask(DeviceObjectItem[], int, out int, out int, out int) |
Scans device object items to build axis and force-feedback actuator bitmasks. Bit N set = axis/actuator N exists. |
| 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 |
File: PadForge.Engine/Common/CustomInputUpdate.cs
Namespace: PadForge.Engine
Represents a single buffered input change between two CustomInputState snapshots. Used by the input recorder and update pipeline.
public struct CustomInputUpdate
{
public MapType Type; // Axis, Button, Slider, or POV
public int Index; // Zero-based index within the type's array
public int Value; // New value after the change
public override string ToString(); // Returns "Type Index = Value"
}| Field | Type | Description |
|---|---|---|
Type |
MapType |
The type of input source that changed |
Index |
int |
Zero-based index within the type's array (e.g., Axis[0], Buttons[5]) |
Value |
int |
New value. Axes/sliders: unsigned 0-65535. POVs: centidegrees or -1. Buttons: 1=pressed, 0=released. |
File: PadForge.Engine/Common/CustomInputHelper.cs
Namespace: PadForge.Engine
Static helper class providing constants and utility methods for working with CustomInputState.
| Constant | Type | Value | Description |
|---|---|---|---|
MaxAxis |
int |
24 | Mirrors CustomInputState.MaxAxis
|
MaxSliders |
int |
8 | Mirrors CustomInputState.MaxSliders
|
MaxPovs |
int |
4 | Mirrors CustomInputState.MaxPovs
|
MaxButtons |
int |
256 | Mirrors CustomInputState.MaxButtons
|
AxisCenter |
int |
32767 | Unsigned axis center value |
AxisMin |
int |
0 | Unsigned axis minimum |
AxisMax |
int |
65535 | Unsigned axis maximum |
PovCentered |
int |
-1 | POV centered value (no direction pressed) |
| Method | Signature | Description |
|---|---|---|
GetUpdates |
static CustomInputUpdate[] GetUpdates(CustomInputState oldState, CustomInputState newState) |
Generates an array of CustomInputUpdate items describing every difference between two input states. Compares axes, sliders, POVs, and buttons. Null states treated as all-zero. Returns empty array if no changes. |
File: PadForge.Engine/Common/ISdlInputDevice.cs
Namespace: PadForge.Engine
Common interface for all SDL-based input device wrappers (joystick/gamepad, keyboard, mouse, web controller). Allows the input pipeline (Steps 2-5) to read state from any device type uniformly.
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();
}| Property | Type | Description |
|---|---|---|
SdlInstanceId |
uint |
SDL instance ID (unique per device connection session). 0 = invalid. |
Name |
string |
Human-readable device name |
InstanceGuid |
Guid |
Deterministic GUID for settings matching (derived from path/serial/VID+PID) |
ProductGuid |
Guid |
Product GUID from VID/PID for device family identification |
DevicePath |
string |
Device file system path (may be empty) |
SerialNumber |
string |
Serial number (e.g., Bluetooth MAC). May be empty. |
VendorId |
ushort |
USB Vendor ID |
ProductId |
ushort |
USB Product ID |
NumAxes |
int |
Number of axes. 6 for gamepads (LX, LY, LT, RX, RY, RT). |
NumButtons |
int |
Number of buttons. 11 for gamepads (standard layout). |
RawButtonCount |
int |
Total raw joystick buttons before gamepad remapping. May exceed NumButtons. |
NumHats |
int |
Number of POV hat switches. 1 for gamepads (D-pad). |
HasRumble |
bool |
Whether the device supports simple rumble vibration |
HasHaptic |
bool |
Whether the device has an SDL haptic handle open |
HasGyro |
bool |
Whether the device has a gyroscope sensor |
HasAccel |
bool |
Whether the device has an accelerometer sensor |
IsAttached |
bool |
Whether the device handle is still valid and connected |
HapticStrategy |
HapticEffectStrategy |
Best haptic strategy chosen at open time |
HapticHandle |
IntPtr |
SDL haptic device handle (IntPtr.Zero if none) |
HapticFeatures |
uint |
Bitmask of SDL_HAPTIC_* feature flags |
NumHapticAxes |
int |
Number of haptic axes (1=wheel, 2+=joystick) |
| Method | Signature | Description |
|---|---|---|
GetCurrentState |
CustomInputState GetCurrentState(bool forceRaw = false) |
Reads current input state. forceRaw=true bypasses SDL gamepad remapping. |
GetDeviceObjects |
DeviceObjectItem[] GetDeviceObjects() |
Returns metadata for each axis, hat, and button on the device. |
GetInputDeviceType |
int GetInputDeviceType() |
Returns an InputDeviceType constant classifying the device. |
SetRumble |
bool SetRumble(ushort low, ushort high, uint durationMs) |
Sends rumble. Default duration uint.MaxValue (~49 days). Returns success. |
StopRumble |
bool StopRumble() |
Stops all rumble (calls SetRumble(0, 0, 0)). |
File: PadForge.Engine/Common/SdlDeviceWrapper.cs
Namespace: PadForge.Engine
Wraps an SDL joystick (and optionally its Gamepad overlay) to provide unified device access: open/close, state polling, rumble, GUID construction, and device object enumeration. Implements ISdlInputDevice.
| Property | Type | Default | Description |
|---|---|---|---|
Joystick |
IntPtr |
IntPtr.Zero |
Raw SDL joystick handle. Always valid when device is open. |
GameController |
IntPtr |
IntPtr.Zero |
SDL Gamepad handle. Zero if not recognized as a gamepad. |
Haptic |
IntPtr |
IntPtr.Zero |
SDL haptic device handle. Non-zero when haptic FFB is available. |
ProductVersion |
ushort |
0 | USB Product Version |
JoystickType |
SDL_JoystickType |
UNKNOWN |
SDL joystick type classification |
IsGameController |
bool |
(computed) |
true if opened as an SDL Gamepad |
| Method | Signature | Description |
|---|---|---|
Open |
bool Open(uint instanceId) |
Opens the SDL device. Tries Gamepad first, falls back to raw Joystick. Populates all properties including haptic, sensors, GUIDs. Returns true on success. |
GetCurrentState |
CustomInputState GetCurrentState(bool forceRaw = false) |
Reads input state. Routes to GetGamepadState() (remapped) or GetJoystickState() (raw) depending on device type and forceRaw flag. |
GetDeviceObjects |
DeviceObjectItem[] GetDeviceObjects() |
Builds array of DeviceObjectItem describing each axis, hat, and button. First 6 axes get standard GUIDs (X, Y, Z, Rx, Ry, Rz); extras get Slider GUIDs. |
GetInputDeviceType |
int GetInputDeviceType() |
Maps SDL_JoystickType to InputDeviceType constant. |
SetRumble |
bool SetRumble(ushort lowFreq, ushort highFreq, uint durationMs) |
Sends rumble via SDL_RumbleJoystick. Returns false if no rumble support. |
StopRumble |
bool StopRumble() |
Calls SetRumble(0, 0, 0). |
| Method | Signature | Description |
|---|---|---|
BuildProductGuid |
static Guid BuildProductGuid(ushort vid, ushort pid) |
Builds a synthetic product GUID from VID and PID. Layout: bytes[0-1]=VID LE, bytes[2-3]=PID LE, bytes[4-15]=0x00. |
BuildInstanceGuid |
static Guid BuildInstanceGuid(string devicePath, ushort vid, ushort pid, uint instanceId, string serial = null) |
Builds a deterministic instance GUID. Priority: VID+PID+Serial (best, stable across reboots), device path (good for wired), VID+PID+SDL ID (last resort, session-specific). Uses MD5 hash. |
HatToCentidegrees |
static int HatToCentidegrees(byte hat) |
Converts SDL hat bitmask to centidegrees (-1 for centered). |
DpadToCentidegrees |
static int DpadToCentidegrees(bool up, bool down, bool left, bool right) |
Converts 4 D-pad booleans to centidegrees. Supports 8-way diagonals. |
GetGamepadState() reads through SDL's gamecontrollerdb mapping layer. Produces a standardized layout:
- Axes: [0]=LX, [1]=LY, [2]=LT, [3]=RX, [4]=RY, [5]=RT
- Buttons: [0]=A, [1]=B, [2]=X, [3]=Y, [4]=LB, [5]=RB, [6]=Back, [7]=Start, [8]=LS, [9]=RS, [10]=Guide
- POV[0]: D-pad synthesized from gamepad D-pad buttons
- Sensors: Gyro and Accel arrays populated if available
Guide button suppression: When Back+Start+Guide are all pressed simultaneously, Guide is suppressed (Windows/XInput synthesizes Guide from this combo).
Extra raw buttons: Raw joystick buttons beyond index 10 are appended (e.g., DualSense touchpad click), but indices already consumed by the gamepad mapping are excluded via ParseMappedButtonIndices().
GetJoystickState() reads raw joystick input (no gamepad remapping):
- Axes: SDL signed (-32768..32767) converted to unsigned (0..65535) by subtracting
short.MinValue. FirstMaxAxisgo toAxis[], overflow toSliders[]. - Hats: SDL bitmask converted to centidegrees via
HatToCentidegrees. - Buttons: Uses
RawButtonCount(notNumButtons) for full raw button coverage.
SDL3 may return a raw VID/PID string (e.g., "0x16c0/0x05e1") for devices not in its database. IsRawVidPidName() detects this, and TryGetHidProductString() queries the Windows HID product string via CreateFile + HidD_GetProductString P/Invoke.
OpenHaptic() opens SDL_OpenHapticFromJoystick and picks the best effect strategy:
- LeftRight (best match for dual-motor)
- Sine (periodic fallback)
- Constant (last resort)
For devices that already have simple rumble AND LeftRight haptic support, simple rumble is preferred and haptic is skipped (more reliable for gamepads). Gain is set to 100 if SDL_HAPTIC_GAIN is supported.
File: PadForge.Engine/Common/SdlDeviceWrapper.cs
Namespace: PadForge.Engine
public enum HapticEffectStrategy
{
None, // No haptic support
LeftRight, // Best: SDL_HAPTIC_LEFTRIGHT (dual-motor)
Sine, // Periodic effect (period varies by motor)
Constant // Fallback: constant level from dominant motor
}File: PadForge.Engine/Common/SdlKeyboardWrapper.cs
Namespace: PadForge.Engine
Wraps a keyboard device for unified input via ISdlInputDevice. State is read from Raw Input (per-device) via RawInputListener.
| Property | Type | Value/Description |
|---|---|---|
NumAxes |
int |
0 |
NumButtons |
int |
Up to 256 (min of 256 and MaxButtons) |
RawButtonCount |
int |
0 |
NumHats |
int |
0 |
HasRumble |
bool |
false |
HasHaptic |
bool |
false |
HasGyro |
bool |
false |
HasAccel |
bool |
false |
RawInputHandle |
IntPtr |
The Raw Input device handle for per-device state reading |
| Method | Signature | Description |
|---|---|---|
Open |
bool Open(RawInputListener.DeviceInfo deviceInfo) |
Opens from a Raw Input device enumeration result. Builds GUID from device path. Uses path hash as pseudo SDL instance ID. |
GetCurrentState |
CustomInputState GetCurrentState(bool forceRaw) |
Reads keyboard state from RawInputListener.GetKeyboardState, then merges hooked key state via InputHookManager.MergeHookedKeyState (for suppressed keys that never reach Raw Input). |
GetDeviceObjects |
DeviceObjectItem[] |
Returns 256 button items with ObjectGuid.Key GUIDs. Names from SDL.VirtualKeyName table. |
GetInputDeviceType |
int |
Returns InputDeviceType.Keyboard (19). |
SetRumble / StopRumble
|
Always returns false. |
File: PadForge.Engine/Common/SdlMouseWrapper.cs
Namespace: PadForge.Engine
Wraps a mouse device for unified input via ISdlInputDevice. State is read from Raw Input (per-device) via RawInputListener.
| Constant | Value | Description |
|---|---|---|
MouseButtons |
5 | Left, Middle, Right, X1, X2 |
MouseAxes |
3 | X Motion, Y Motion, Scroll |
AxisCenter |
32767 | Center value for mouse axis output |
MotionScale |
2048f | Multiplier for mouse delta to axis value |
ScrollScale |
128f | Multiplier for scroll delta to axis value |
| Property | Type | Value/Description |
|---|---|---|
NumAxes |
int |
3 (X Motion, Y Motion, Scroll) |
NumButtons |
int |
5 (Left, Middle, Right, X1, X2) |
RawButtonCount |
int |
0 |
NumHats |
int |
0 |
HasRumble |
bool |
false |
RawInputHandle |
IntPtr |
The Raw Input device handle |
| Method | Signature | Description |
|---|---|---|
Open |
bool Open(RawInputListener.DeviceInfo deviceInfo) |
Opens from a Raw Input device enumeration result. |
GetCurrentState |
CustomInputState GetCurrentState(bool forceRaw) |
Reads mouse delta from ConsumeMouseDelta, scroll from ConsumeMouseScroll, buttons from GetMouseButtons + InputHookManager.MergeHookedMouseState. Axes are computed as AxisCenter + (delta * Scale) clamped to 0-65535. |
GetDeviceObjects |
DeviceObjectItem[] |
Returns 3 RelativeAxis items (X Motion, Y Motion, Scroll with XAxis/YAxis/ZAxis GUIDs) + 5 PushButton items (Left Click, Middle Click, Right Click, X1, X2). |
GetInputDeviceType |
int |
Returns InputDeviceType.Mouse (18). |
File: PadForge.Engine/Common/WebControllerDevice.cs
Namespace: PadForge.Engine
Virtual input device representing a browser-connected gamepad. Implements ISdlInputDevice so it integrates with the standard input pipeline. State is written by the WebSocket thread and read by the polling thread via volatile reference swaps.
| Constant | Value | Description |
|---|---|---|
WebVendorId |
0xBEEF |
Distinctive VID to avoid ViGEm filter false positives |
WebProductId |
0xCA7E |
Distinctive PID |
WebProductGuid |
{BEBC0000-...} |
Fixed ProductGuid for all web controller instances |
| Property | Value |
|---|---|
| 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) |
| HasHaptic | false |
| HasGyro | false |
| HasAccel | false |
public WebControllerDevice(string clientId, string displayName)Creates a new web controller. clientId is a unique identifier from browser localStorage. InstanceGuid is derived from the client ID via MD5. SdlInstanceId is the hash code of the client ID. Stick axes initialized to center (32767), trigger axes to 0.
| Event | Signature | Description |
|---|---|---|
RumbleRequested |
Action<ushort, ushort> |
Fired when SetRumble is called. Parameters: (lowFrequency, highFrequency) in 0-65535 range. |
| Method | Signature | Description |
|---|---|---|
UpdateAxis |
void UpdateAxis(int code, int value) |
Updates an axis value (0=LX, 1=LY, 2=LT, 3=RX, 4=RY, 5=RT). Thread-safe via clone + lock. |
UpdateButton |
void UpdateButton(int code, bool pressed) |
Updates a button state (0=A through 10=Guide). Thread-safe. |
UpdatePov |
void UpdatePov(int value) |
Updates POV hat value (centidegrees or -1). Thread-safe. |
SetConnected |
void SetConnected(bool connected) |
Sets the connection state (volatile write). |
File: PadForge.Engine/Common/DeviceObjectItem.cs
Namespace: PadForge.Engine
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; } // Default: ""
public Guid ObjectTypeGuid { get; set; } // Default: Guid.Empty
public DeviceObjectTypeFlags ObjectType { get; set; } // Default: All
// Position
public int InputIndex { get; set; } // Default: 0
public int Offset { get; set; } // Default: 0
// Aspect
public ObjectAspect Aspect { get; set; } // Default: Position
// Computed helpers (read-only)
public bool IsForceActuator { get; }
public bool IsAxis { get; }
public bool IsButton { get; }
public bool IsPov { get; }
public bool IsSlider { get; }
public override string ToString(); // "{Name} ({TypeLabel}, Index {InputIndex})"
}| Property | Type | Default | Description |
|---|---|---|---|
Name |
string |
"" |
Human-readable name (e.g., "X Axis", "Button 3", "Left Stick X") |
ObjectTypeGuid |
Guid |
Guid.Empty |
Well-known GUID from ObjectGuid (XAxis, YAxis, Button, etc.) |
ObjectType |
DeviceObjectTypeFlags |
All |
Classification flags (AbsoluteAxis, PushButton, etc.) |
InputIndex |
int |
0 |
Zero-based index into CustomInputState arrays |
Offset |
int |
0 |
Byte offset (synthetic for SDL, used for mapping compatibility) |
Aspect |
ObjectAspect |
Position |
Object aspect |
| Property | Logic |
|---|---|
IsForceActuator |
(ObjectType & ForceFeedbackActuator) != 0 |
IsAxis |
(ObjectType & Axis) != 0 |
IsButton |
(ObjectType & Button) != 0 |
IsPov |
(ObjectType & PointOfViewController) != 0 |
IsSlider |
ObjectTypeGuid == ObjectGuid.Slider |
File: PadForge.Engine/Common/DeviceEffectItem.cs
Namespace: PadForge.Engine
Describes a force feedback effect supported by a device. Under SDL3, rumble is the primary effect type. Kept for UI display and settings compatibility.
| Field | GUID | Description |
|---|---|---|
ConstantForce |
{13541C20-8E33-11D0-9AD0-00A0C9A06E35} |
GUID_ConstantForce (DirectInput) |
Square |
{13541C22-...} |
GUID_Square |
Sine |
{13541C23-...} |
GUID_Sine |
Triangle |
{13541C24-...} |
GUID_Triangle |
Spring |
{13541C27-...} |
GUID_Spring |
Damper |
{13541C28-...} |
GUID_Damper |
SdlRumble |
{53444C52-554D-424C-...} |
Synthetic GUID for SDL rumble ("SDLRUMBL") |
| Property | Type | Default | Description |
|---|---|---|---|
EffectGuid |
Guid |
Guid.Empty |
GUID identifying the effect type |
Name |
string |
"" |
Human-readable name (e.g., "Constant Force", "Rumble") |
Parameters |
EffectParameterFlags |
None |
Which parameters the effect supports |
| Method | Signature | Description |
|---|---|---|
CreateRumbleEffect |
static DeviceEffectItem CreateRumbleEffect() |
Creates an item representing SDL rumble support (SdlRumble GUID, name "Rumble"). |
File: PadForge.Engine/Common/InputTypes.cs
Namespace: PadForge.Engine
[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
}[Flags]
public enum ObjectAspect : int
{
Position = 0x100
}[Flags]
public enum EffectParameterFlags : int
{
None = 0
}Static class providing well-known GUIDs for device object types. Values match DirectInput GUID constants.
| Field | GUID | Description |
|---|---|---|
XAxis |
{A36D02E0-C9F3-11CF-BFC7-444553540000} |
GUID_XAxis |
YAxis |
{A36D02E1-C9F3-11CF-BFC7-444553540000} |
GUID_YAxis |
ZAxis |
{A36D02E2-C9F3-11CF-BFC7-444553540000} |
GUID_ZAxis |
RxAxis |
{A36D02F4-C9F3-11CF-BFC7-444553540000} |
GUID_RxAxis |
RyAxis |
{A36D02F5-C9F3-11CF-BFC7-444553540000} |
GUID_RyAxis |
RzAxis |
{A36D02E3-C9F3-11CF-BFC7-444553540000} |
GUID_RzAxis |
Slider |
{A36D02E4-C9F3-11CF-BFC7-444553540000} |
GUID_Slider |
Button |
{A36D02F0-C9F3-11CF-BFC7-444553540000} |
GUID_Button |
Key |
{55728220-D33C-11CF-BFC7-444553540000} |
GUID_Key |
PovController |
{A36D02F2-C9F3-11CF-BFC7-444553540000} |
GUID_POV |
Unknown |
Guid.Empty |
GUID_Unknown |
Integer constants matching DirectInput device type values. Used for UserDevice.CapType.
| Constant | Value | Description |
|---|---|---|
Device |
17 | Generic device |
Mouse |
18 | Mouse |
Keyboard |
19 | Keyboard |
Joystick |
20 | Joystick |
Gamepad |
21 | Gamepad |
Driving |
22 | Steering wheel |
Flight |
23 | Flight stick |
FirstPerson |
24 | First-person device |
Supplemental |
25 | Supplemental device (guitar, drum, dance pad) |
public enum MapType : int
{
None = 0,
Axis = 1,
Button = 2,
Slider = 3,
POV = 4
}File: PadForge.Engine/Common/ForceFeedbackState.cs
Namespace: PadForge.Engine
Manages force feedback (rumble) state for a single device with change detection. Uses change-detection to only send rumble when motor values differ, with uint.MaxValue duration (~49 days) to mimic XInput's "set and forget" behavior.
| Property | Type | Description |
|---|---|---|
LeftMotorSpeed |
ushort |
Most recent left (low-frequency) motor speed sent to device. 0-65535. Read-only. |
RightMotorSpeed |
ushort |
Most recent right (high-frequency) motor speed sent to device. 0-65535. Read-only. |
IsActive |
bool |
Whether force feedback is currently active on the device. Read-only. |
| Field | Type | Description |
|---|---|---|
_cachedLeftMotorSpeed |
ushort |
Last sent left motor speed |
_cachedRightMotorSpeed |
ushort |
Last sent right motor speed |
_hapticEffectId |
int |
SDL haptic effect ID (-1 = none) |
_hapticEffectCreated |
bool |
Whether a haptic effect has been created |
_cachedEffectType |
uint |
Last sent FFB effect type |
_cachedSignedMag |
short |
Last sent signed magnitude |
_cachedDirection |
ushort |
Last sent polar direction |
_cachedPeriod |
uint |
Last sent period |
_cachedHasCondition |
bool |
Last sent condition data flag |
_cachedHasDirectional |
bool |
Last sent directional data flag |
| Method | Signature | Description |
|---|---|---|
SetDeviceForces |
void SetDeviceForces(UserDevice ud, ISdlInputDevice device, PadSetting ps, Vibration v) |
Main entry point. Reads gain settings from PadSetting (ForceOverall, LeftMotorStrength, RightMotorStrength, ForceSwapMotor). Routes to Path 1 (directional haptic) when `v.HasDirectionalData |
StopDeviceForces |
void StopDeviceForces(ISdlInputDevice device) |
Stops all rumble/haptic and resets all cached state. Routes to StopAndDestroyHapticEffect() for haptic devices or StopRumble() for rumble devices. |
| Method | Description |
|---|---|
SetDirectionalHapticForces(device, v, overallGain) |
Sends directional constant or periodic force. Single-axis (wheels): projects polar direction onto steering axis via sin(angle). Multi-axis (joysticks): full 2D polar. Falls back to scalar if effect type unsupported. |
SetConditionHapticForces(device, v, overallGain) |
Sends condition effects (spring/damper/friction/inertia) with per-axis coefficients. Scales HID ranges (-10000..+10000) to SDL ranges (-32767..+32767). Falls back to scalar if unsupported. |
SetHapticForces(device, left, right) |
Scalar haptic fallback. Translates dual-motor values to SDL haptic effect based on HapticEffectStrategy (LeftRight, Sine, or Constant). |
ApplyHapticEffect(device, ref effect) |
Creates effect on first call (SDL_CreateHapticEffect + SDL_RunHapticEffect), updates in-place on subsequent calls (SDL_UpdateHapticEffect). Avoids create/destroy churn. |
StopAndDestroyHapticEffect(device) |
Stops and destroys the active haptic effect. Resets _hapticEffectId and _hapticEffectCreated. |
| 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 |
File: PadForge.Engine/Common/ForceFeedbackState.cs
Namespace: PadForge.Engine
Static class with FFB effect type constants matching vJoy FFBEType enum values. Defined in Engine so both Engine and App can reference them.
| Constant | Value | Description |
|---|---|---|
None |
0 | No effect |
Const |
1 | Constant force |
Ramp |
2 | Ramp force |
Square |
3 | Square wave periodic |
Sine |
4 | Sine wave periodic |
Triangle |
5 | Triangle wave periodic |
SawUp |
6 | Sawtooth up periodic |
SawDown |
7 | Sawtooth down periodic |
Spring |
8 | Spring condition |
Damper |
9 | Damper condition |
Inertia |
10 | Inertia condition |
Friction |
11 | Friction condition |
File: PadForge.Engine/Common/ForceFeedbackState.cs
Namespace: PadForge.Engine
Represents vibration/force feedback state for a virtual controller slot. Carries both scalar motor speeds (for rumble devices) and directional FFB data (for haptic joysticks/wheels).
public class Vibration
{
// Scalar fields (ViGEm Xbox/DS4 callbacks and rumble path)
public ushort LeftMotorSpeed { get; set; } // 0-65535, low-frequency heavy rumble
public ushort RightMotorSpeed { get; set; } // 0-65535, high-frequency light buzz
// Directional FFB fields (vJoy FFB callback for haptic devices)
public bool HasDirectionalData { get; set; }
public uint EffectType { get; set; } // FfbEffectTypes constant
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, device-level gain
// Condition effect fields (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
// Constructors
public Vibration();
public Vibration(ushort leftMotor, ushort rightMotor);
}| Field | Type | Default | Description |
|---|---|---|---|
LeftMotorSpeed |
ushort |
0 | Left motor (low-frequency, heavy rumble) speed. Set by ViGEm callbacks. |
RightMotorSpeed |
ushort |
0 | Right motor (high-frequency, light buzz) speed. Set by ViGEm callbacks. |
HasDirectionalData |
bool |
false |
True when directional FFB data is available (vJoy path) |
EffectType |
uint |
0 | Primary effect type (see FfbEffectTypes) |
SignedMagnitude |
short |
0 | -10000 to +10000. Negative = opposite direction for constant force. |
Direction |
ushort |
0 | Polar direction in HID units 0-32767 (0=North, ~8192=East, ~16384=South, ~24576=West) |
Period |
uint |
0 | Period in ms for periodic effects |
DeviceGain |
byte |
255 | Device-level gain 0-255. Applied on top of per-effect gain. |
HasConditionData |
bool |
false |
True when per-axis condition data is available |
ConditionAxes |
ConditionAxisData[] |
null |
Per-axis condition coefficients. Index 0=X, 1=Y. |
ConditionAxisCount |
int |
0 | Number of valid entries (1 for wheels, 2 for joysticks) |
File: PadForge.Engine/Common/ForceFeedbackState.cs
Namespace: PadForge.Engine
Per-axis condition effect parameters for spring/damper/friction/inertia effects.
public struct ConditionAxisData
{
public short PositiveCoefficient; // 0-10000, force when displacement > center
public short NegativeCoefficient; // 0-10000, force when displacement < center
public short Offset; // -10000 to +10000, center point offset
public uint DeadBand; // 0-10000, dead band around center
public uint PositiveSaturation; // 0-10000
public uint NegativeSaturation; // 0-10000
}File: PadForge.Engine/Common/RumbleLogger.cs
Namespace: PadForge.Engine
Lightweight diagnostic logger for force feedback debugging. Disabled by default. Thread-safe.
public static class RumbleLogger
{
public static bool Enabled { get; set; } // Default: false
public static void Log(string message);
public static void Close();
}| Member | Description |
|---|---|
Enabled |
Set to true in InputService.Start() to activate logging. |
Log(string) |
Writes timestamped message to rumble_log.txt next to the executable. Uses Stopwatch for high-resolution timing. Thread-safe via lock. Only writes when Enabled is true. Creates log file on first call. |
Close() |
Closes and disposes the log file StreamWriter. Call on app shutdown. |
File: PadForge.Engine/Common/InputHookManager.cs
Namespace: PadForge.Engine.Common
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.
public class InputHookManager : IDisposable
{
void Start();
void Stop();
void SetSuppressedKeys(HashSet<int> vkCodes);
void SetSuppressedMouseButtons(HashSet<int> buttons);
bool HasAnySuppression { get; }
static void MergeHookedKeyState(bool[] dest, int count);
static void MergeHookedMouseState(bool[] dest, int count);
}| Method | Signature | Description |
|---|---|---|
Start |
void Start() |
Creates a dedicated background thread with a GetMessage loop and installs both hooks. Blocks (via ManualResetEventSlim) until hooks are installed (5s timeout). |
Stop |
void Stop() |
Posts WM_QUIT to the hook thread, joins (2s timeout), clears hooked state arrays. |
SetSuppressedKeys |
void SetSuppressedKeys(HashSet<int> vkCodes) |
Updates the VK codes to suppress. Clears hooked state for keys removed from suppression set. Uses volatile reference swap for thread safety. |
SetSuppressedMouseButtons |
void SetSuppressedMouseButtons(HashSet<int> buttons) |
Updates mouse button IDs to suppress (0=Left, 1=Right, 2=Middle, 3=X1, 4=X2). Same volatile reference swap pattern. |
HasAnySuppression |
bool (property) |
Returns true if any keys or mouse buttons are being suppressed. |
MergeHookedKeyState |
static void MergeHookedKeyState(bool[] dest, int count) |
Merges suppressed-key state into a destination array. For suppressed keys, hook state is authoritative (replaces dest). Called by SdlKeyboardWrapper.GetCurrentState(). |
MergeHookedMouseState |
static void MergeHookedMouseState(bool[] dest, int count) |
Same principle for mouse buttons. Called by SdlMouseWrapper.GetCurrentState(). |
-
Keyboard: Intercepts
WM_KEYDOWN,WM_KEYUP,WM_SYSKEYDOWN,WM_SYSKEYUP. ReadsKBDLLHOOKSTRUCT.vkCode. Returns(IntPtr)1to suppress, orCallNextHookExto pass through. Captures key state into_hookedKeyState[]before suppressing (WH_KEYBOARD_LL runs in RIT before WM_INPUT is generated). -
Mouse: Intercepts button messages (
WM_LBUTTONDOWN/UP,WM_RBUTTONDOWN/UP,WM_MBUTTONDOWN/UP,WM_XBUTTONDOWN/UP). Converts message to button ID viaMouseMessageToButtonId(). Captures button state into_hookedMouseState[].
| 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) |
| 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 |
File: PadForge.Engine/Common/RawInputListener.cs
Namespace: PadForge.Engine
Static class that receives keyboard and mouse input via Windows Raw Input API, even when the application window is not focused (RIDEV_INPUTSINK). Creates a hidden message-only window (HWND_MESSAGE) on a dedicated background thread.
State is tracked per-device via RAWINPUT.header.hDevice, so arcade encoders and other multi-device setups get isolated input.
public struct DeviceInfo
{
public IntPtr Handle; // Raw Input device handle
public string Name; // Device display name
public string DevicePath; // Device interface path
public ushort VendorId; // USB VID
public ushort ProductId; // USB PID
}| Field | Type | Description |
|---|---|---|
AggregateKeyboardHandle |
IntPtr |
Sentinel value new IntPtr(-99). Aggregates state from all keyboards. |
AggregateMouseHandle |
IntPtr |
Sentinel value new IntPtr(-98). Aggregates state from all mice. |
| Method | Signature | Description |
|---|---|---|
Start |
static void Start() |
Creates the message-pump thread, registers for Raw Input keyboard and mouse. Blocks until the window is created. |
Stop |
static void Stop() |
Posts WM_QUIT to the message pump thread, joins it. |
EnumerateKeyboards |
static DeviceInfo[] EnumerateKeyboards() |
Returns all connected keyboard devices via GetRawInputDeviceList. |
EnumerateMice |
static DeviceInfo[] EnumerateMice() |
Returns all connected mouse devices. |
GetKeyboardState |
static void GetKeyboardState(IntPtr hDevice, bool[] dest, int count) |
Copies per-device keyboard key states into dest. Uses aggregate handle for combined output. |
ConsumeMouseDelta |
static void ConsumeMouseDelta(IntPtr hDevice, out int dx, out int dy) |
Returns and resets accumulated mouse delta for the device. |
ConsumeMouseScroll |
static int ConsumeMouseScroll(IntPtr hDevice) |
Returns and resets accumulated scroll wheel delta. |
GetMouseButtons |
static void GetMouseButtons(IntPtr hDevice, bool[] dest) |
Copies per-device mouse button states (5 buttons: L, R, M, X1, X2). |
-
Keyboard (
WM_INPUT,RIM_TYPEKEYBOARD): ReadsRAWKEYBOARD.VKey, handlesRI_KEY_E0extended keys (maps right Ctrl/Alt/Shift to distinct VK codes, NumLock/Insert/Home/etc. to extended versions). Tracks per-device key state in aConcurrentDictionary<IntPtr, bool[]>. -
Mouse (
WM_INPUT,RIM_TYPEMOUSE): AccumulateslLastX/lLastYdeltas. Tracks button up/down viausButtonFlags. Captures scroll wheel viaRI_MOUSE_WHEEL. -
Scroll Wheel:
usButtonDatais a signedshortrepresenting scroll amount. Accumulated per-device, consumed byConsumeMouseScroll.
File: PadForge.Engine/Data/PadSetting.cs
Namespace: PadForge.Engine.Data
Contains the complete mapping configuration for a device-to-slot assignment. All mapping properties are string-typed descriptors in the format: "Button N", "Axis N", "IHAxis N", "POV N Dir", "Slider N", or "" (unmapped). The class is declared partial.
PadSettings are stored separately from UserSettings and linked via PadSettingChecksum. Multiple UserSettings can share the same PadSetting when devices use identical mappings.
Numeric settings (dead zones, gains) are stored as strings for XML serialization consistency.
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
PadSettingChecksum |
string |
[XmlElement] |
"" |
Checksum computed from all mapping/setting properties. Links UserSettings to PadSettings. |
| Property | Type | Serialization | Default |
|---|---|---|---|
ButtonA |
string |
[XmlElement] |
"" |
ButtonB |
string |
[XmlElement] |
"" |
ButtonX |
string |
[XmlElement] |
"" |
ButtonY |
string |
[XmlElement] |
"" |
LeftShoulder |
string |
[XmlElement] |
"" |
RightShoulder |
string |
[XmlElement] |
"" |
ButtonBack |
string |
[XmlElement] |
"" |
ButtonStart |
string |
[XmlElement] |
"" |
ButtonGuide |
string |
[XmlElement] |
"" |
LeftThumbButton |
string |
[XmlElement] |
"" |
RightThumbButton |
string |
[XmlElement] |
"" |
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
DPad |
string |
[XmlElement] |
"" |
Combined D-Pad mapping. If set to a POV descriptor (e.g., "POV 0"), all four directions auto-extracted. Individual overrides take priority. |
DPadUp |
string |
[XmlElement] |
"" |
|
DPadDown |
string |
[XmlElement] |
"" |
|
DPadLeft |
string |
[XmlElement] |
"" |
|
DPadRight |
string |
[XmlElement] |
"" |
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
LeftTrigger |
string |
[XmlElement] |
"" |
Mapping descriptor |
RightTrigger |
string |
[XmlElement] |
"" |
Mapping descriptor |
LeftTriggerDeadZone |
string |
[XmlElement] |
"0" |
0-100% |
RightTriggerDeadZone |
string |
[XmlElement] |
"0" |
0-100% |
LeftTriggerAntiDeadZone |
string |
[XmlElement] |
"0" |
0-100% |
RightTriggerAntiDeadZone |
string |
[XmlElement] |
"0" |
0-100% |
LeftTriggerMaxRange |
string |
[XmlElement] |
"100" |
1-100% |
RightTriggerMaxRange |
string |
[XmlElement] |
"100" |
1-100% |
LeftTriggerSensitivityCurve |
string |
[XmlElement] |
"0" |
-100 to 100 (0=linear, +100=exponential, -100=logarithmic) |
RightTriggerSensitivityCurve |
string |
[XmlElement] |
"0" |
-100 to 100 |
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
LeftThumbAxisX |
string |
[XmlElement] |
"" |
|
LeftThumbAxisY |
string |
[XmlElement] |
"" |
|
RightThumbAxisX |
string |
[XmlElement] |
"" |
|
RightThumbAxisY |
string |
[XmlElement] |
"" |
|
LeftThumbAxisXNeg |
string |
[XmlElement] |
"" |
Negative-direction (used when buttons map to bidirectional axes) |
LeftThumbAxisYNeg |
string |
[XmlElement] |
"" |
|
RightThumbAxisXNeg |
string |
[XmlElement] |
"" |
|
RightThumbAxisYNeg |
string |
[XmlElement] |
"" |
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
LeftThumbDeadZoneX |
string |
[XmlElement] |
"0" |
Left stick dead zone X (0-100%) |
LeftThumbDeadZoneY |
string |
[XmlElement] |
"0" |
Left stick dead zone Y |
RightThumbDeadZoneX |
string |
[XmlElement] |
"0" |
Right stick dead zone X |
RightThumbDeadZoneY |
string |
[XmlElement] |
"0" |
Right stick dead zone Y |
LeftThumbDeadZoneShape |
string |
[XmlElement] |
"2" |
DeadZoneShape enum value. 2 = ScaledRadial. |
RightThumbDeadZoneShape |
string |
[XmlElement] |
"2" |
DeadZoneShape enum value |
LeftThumbAntiDeadZone |
string |
[XmlElement] |
"0" |
Legacy unified (0-100%). Use per-axis X/Y instead. |
RightThumbAntiDeadZone |
string |
[XmlElement] |
"0" |
Legacy unified |
LeftThumbAntiDeadZoneX |
string |
[XmlElement] |
"0" |
Left stick anti-dead zone X (0-100%) |
LeftThumbAntiDeadZoneY |
string |
[XmlElement] |
"0" |
Left stick anti-dead zone Y |
RightThumbAntiDeadZoneX |
string |
[XmlElement] |
"0" |
Right stick anti-dead zone X |
RightThumbAntiDeadZoneY |
string |
[XmlElement] |
"0" |
Right stick anti-dead zone Y |
LeftThumbLinear |
string |
[XmlElement] |
"0" |
Response curve (0-100%). 0=default, 100=fully linear. |
RightThumbLinear |
string |
[XmlElement] |
"0" |
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
LeftThumbSensitivityCurveX |
string |
[XmlElement] |
"0" |
-100 to 100 (0=linear, +100=exponential, -100=logarithmic) |
LeftThumbSensitivityCurveY |
string |
[XmlElement] |
"0" |
|
RightThumbSensitivityCurveX |
string |
[XmlElement] |
"0" |
|
RightThumbSensitivityCurveY |
string |
[XmlElement] |
"0" |
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
LeftThumbMaxRangeX |
string |
[XmlElement] |
"100" |
Left stick X max range (1-100%). Symmetric/positive direction. |
LeftThumbMaxRangeY |
string |
[XmlElement] |
"100" |
|
RightThumbMaxRangeX |
string |
[XmlElement] |
"100" |
|
RightThumbMaxRangeY |
string |
[XmlElement] |
"100" |
|
LeftThumbMaxRangeXNeg |
string |
[XmlElement] |
null |
Left stick X negative (left) direction. Null = inherit symmetric. |
LeftThumbMaxRangeYNeg |
string |
[XmlElement] |
null |
Left stick Y negative (down) direction |
RightThumbMaxRangeXNeg |
string |
[XmlElement] |
null |
|
RightThumbMaxRangeYNeg |
string |
[XmlElement] |
null |
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
LeftThumbCenterOffsetX |
string |
[XmlElement] |
"0" |
-100 to 100%. Corrects stick drift before dead zone. |
LeftThumbCenterOffsetY |
string |
[XmlElement] |
"0" |
|
RightThumbCenterOffsetX |
string |
[XmlElement] |
"0" |
|
RightThumbCenterOffsetY |
string |
[XmlElement] |
"0" |
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
ForceType |
string |
[XmlElement] |
"1" |
0=Off, 1=SDL Rumble |
ForceOverall |
string |
[XmlElement] |
"100" |
Overall strength 0-100%. Multiplier for both motors. |
ForceSwapMotor |
string |
[XmlElement] |
"0" |
"0"=no swap, "1"=swap left/right motors |
LeftMotorStrength |
string |
[XmlElement] |
"100" |
Left (low-frequency) motor strength 0-100% |
RightMotorStrength |
string |
[XmlElement] |
"100" |
Right (high-frequency) motor strength 0-100% |
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
AudioRumbleEnabled |
string |
[XmlElement] |
"0" |
Enable audio bass rumble. "0"=off, "1"=on. |
AudioRumbleSensitivity |
string |
[XmlElement] |
"4" |
Bass detection sensitivity (1-20) |
AudioRumbleCutoffHz |
string |
[XmlElement] |
"80" |
Low-pass cutoff frequency in Hz (40-200) |
AudioRumbleLeftMotor |
string |
[XmlElement] |
"100" |
Left motor strength for audio rumble (0-100%) |
AudioRumbleRightMotor |
string |
[XmlElement] |
"100" |
Right motor strength for audio rumble (0-100%) |
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
AxisToButtonThreshold |
string |
[XmlElement] |
"50" |
Threshold 0-100% for treating axis as button press |
LeftThumbAxisXInvert |
string |
[XmlElement] |
"0" |
Invert left stick X. "0" or "1". |
LeftThumbAxisYInvert |
string |
[XmlElement] |
"0" |
|
RightThumbAxisXInvert |
string |
[XmlElement] |
"0" |
|
RightThumbAxisYInvert |
string |
[XmlElement] |
"0" |
For custom vJoy configurations with arbitrary axis/button/POV counts. Keys use target names like "VJoyAxis0", "VJoyAxis0Neg", "VJoyBtn0", "VJoyPov0Up". Values are mapping descriptors.
| Property | Type | Serialization | Description |
|---|---|---|---|
VJoyMappingEntries |
VJoyMappingEntry[] |
[XmlArray("VJoyMappings")] [XmlArrayItem("Map")] |
Serializable array for XML persistence |
| Method | Signature | Description |
|---|---|---|
GetVJoyMapping |
string GetVJoyMapping(string key) |
Gets a vJoy mapping value by key. Returns "" if not found. |
SetVJoyMapping |
void SetVJoyMapping(string key, string value) |
Sets a vJoy mapping value. Empty/null removes the key. |
FlushVJoyMappings |
void FlushVJoyMappings() |
Flushes in-memory dictionary back to serializable array. |
Same pattern as vJoy. Keys: "MidiCC0", "MidiCC0Neg", "MidiNote0", etc.
| Property | Type | Serialization | Description |
|---|---|---|---|
MidiMappingEntries |
VJoyMappingEntry[] |
[XmlArray("MidiMappings")] [XmlArrayItem("Map")] |
Serializable array |
| Method | Signature | Description |
|---|---|---|
GetMidiMapping |
string GetMidiMapping(string key) |
Gets a MIDI mapping value. |
SetMidiMapping |
void SetMidiMapping(string key, string value) |
Sets a MIDI mapping value. |
FlushMidiMappings |
void FlushMidiMappings() |
Flushes dictionary to array. |
Keys: "KbmKey41" (VK_A), "KbmMouseX", "KbmMouseXNeg", "KbmMBtn0", "KbmScroll", etc.
| Property | Type | Serialization | Description |
|---|---|---|---|
KbmMappingEntries |
VJoyMappingEntry[] |
[XmlArray("KbmMappings")] [XmlArrayItem("Map")] |
Serializable array |
| Method | Signature | Description |
|---|---|---|
GetKbmMapping |
string GetKbmMapping(string key) |
Gets a KBM mapping value. |
SetKbmMapping |
void SetKbmMapping(string key, string value) |
Sets a KBM mapping value. |
FlushKbmMappings |
void FlushKbmMappings() |
Flushes dictionary to array. |
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
GameFileName |
string |
[XmlElement] |
"" |
Optional game executable name. Empty = global (applies to all games). |
| Property | Type | Serialization | Description |
|---|---|---|---|
HasAnyMapping |
bool |
[XmlIgnore] |
Returns true if at least one mapping property has a non-empty descriptor (checks standard, vJoy, and MIDI). |
| Method | Signature | Description |
|---|---|---|
MigrateAntiDeadZones |
void MigrateAntiDeadZones() |
Migrates legacy unified anti-dead zone values to per-axis X/Y properties. Call after deserialization. |
MigrateMaxRangeDirections |
void MigrateMaxRangeDirections() |
Migrates symmetric max range to per-direction properties. If negative-direction is null/empty, copies the symmetric value. |
ComputeChecksum |
string ComputeChecksum() |
Computes 8-character hex checksum (first 4 bytes of MD5) from all mapping/setting properties. vJoy/MIDI/KBM keys sorted for deterministic output. |
UpdateChecksum |
void UpdateChecksum() |
Calls ComputeChecksum() and stores result in PadSettingChecksum. |
ClearMappingDescriptors |
void ClearMappingDescriptors() |
Clears all mapping descriptors (standard, vJoy, MIDI) while preserving dead zone and FFB settings. |
GetAllMappingDescriptors |
List<string> GetAllMappingDescriptors() |
Returns all non-empty mapping descriptor strings from standard, vJoy, and MIDI entries. |
ToJson |
string ToJson(VirtualControllerType outputType, bool isCustomVJoy) |
Serializes to JSON for clipboard copy/paste. Embeds __OutputType and __IsCustomVJoy metadata for cross-layout support. Includes __VJoyMappings, __MidiMappings, __KbmMappings arrays if present. |
FromJson |
static PadSetting FromJson(string json) |
Deserializes JSON to new PadSetting. Returns null on invalid JSON. |
FromJson |
static PadSetting FromJson(string json, out VirtualControllerType, out bool) |
Same but also extracts source layout metadata. |
CopyFrom |
void CopyFrom(PadSetting source) |
Copies all copyable properties. Deep-copies vJoy/MIDI/KBM mapping arrays. Invalidates cached dictionaries. |
CopyFromTranslated |
void CopyFromTranslated(PadSetting source, VirtualControllerType srcType, bool srcCustom, VirtualControllerType tgtType, bool tgtCustom) |
Cross-layout copy with positional translation via MappingTranslation. Non-mapping settings copied directly; mapping properties translated by canonical position. |
CloneDeep |
PadSetting CloneDeep() |
Deep copy including checksum and GameFileName. |
File: PadForge.Engine/Data/PadSetting.cs
Namespace: PadForge.Engine.Data
Key-value entry for vJoy/MIDI/KBM mapping persistence in XML. Used by all three dictionary-based mapping systems.
public class VJoyMappingEntry
{
[XmlAttribute] public string Key { get; set; } = "";
[XmlAttribute] public string Value { get; set; } = "";
}File: PadForge.Engine/Data/UserSetting.cs
Namespace: PadForge.Engine.Data
Links a physical input device to a virtual controller slot and a mapping configuration. One UserSetting per device-to-slot assignment. Implements INotifyPropertyChanged.
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
InstanceGuid |
Guid |
[XmlElement] |
Instance GUID of the physical device | |
InstanceName |
string |
[XmlElement] |
"" |
Human-readable instance name (for offline display) |
ProductGuid |
Guid |
[XmlElement] |
Product GUID for matching when instance GUIDs change | |
ProductName |
string |
[XmlElement] |
"" |
Human-readable product name |
MapTo |
int |
[XmlElement] |
-1 |
Virtual controller slot index (0-15). -1 = unmapped. Raises PropertyChanged on set. |
PadSettingChecksum |
string |
[XmlElement] |
"" |
Links to a PadSetting record |
IsEnabled |
bool |
[XmlElement] |
true |
Whether this mapping is enabled. Disabled = skipped in pipeline. |
DateCreated |
DateTime |
[XmlElement] |
DateTime.Now |
Creation timestamp |
DateUpdated |
DateTime |
[XmlElement] |
DateTime.Now |
Last modification timestamp |
| Property | Type | Serialization | Description |
|---|---|---|---|
OutputState |
Gamepad |
[XmlIgnore] |
Mapped gamepad output state from Step 3. Written by background thread. |
RawMappedState |
Gamepad |
[XmlIgnore] |
Raw mapped state: axis-selected and Y-negated but BEFORE center offset, dead zone, anti-dead zone, linear, and max range. Used by UI preview. |
VJoyRawOutputState |
VJoyRawState |
[XmlIgnore] |
Mapped vJoy raw output for custom vJoy slots. |
MidiRawOutputState |
MidiRawState |
[XmlIgnore] |
Mapped MIDI raw output for MIDI slots. |
KbmRawOutputState |
KbmRawState |
[XmlIgnore] |
Mapped KBM raw output for KeyboardMouse slots. |
_cachedPadSetting |
PadSetting |
[XmlIgnore] (internal) |
Cached PadSetting reference set by SettingsManager. |
| Method | Signature | Description |
|---|---|---|
GetPadSetting |
PadSetting GetPadSetting() |
Returns the cached PadSetting. |
SetPadSetting |
void SetPadSetting(PadSetting ps) |
Sets the cached PadSetting. Called by SettingsManager during load/sync. |
File: PadForge.Engine/Data/UserDevice.cs
Namespace: PadForge.Engine.Data
Data model for a single physical input device. Contains both serializable (settings-persisted) properties and runtime-only fields used during the input pipeline. Partial class. Implements INotifyPropertyChanged.
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
InstanceGuid |
Guid |
[XmlElement] |
Unique device instance GUID (deterministic from device path) | |
InstanceName |
string |
[XmlElement] |
"" |
Human-readable instance name (e.g., "Xbox Controller") |
ProductGuid |
Guid |
[XmlElement] |
Product GUID in PIDVID format | |
ProductName |
string |
[XmlElement] |
"" |
Human-readable product name |
VendorId |
ushort |
[XmlElement] |
0 | USB Vendor ID |
ProdId |
ushort |
[XmlElement] |
0 | USB Product ID |
DevRevision |
ushort |
[XmlElement] |
0 | USB Product Version / Revision |
DevicePath |
string |
[XmlElement] |
"" |
Device file system path |
SerialNumber |
string |
[XmlElement] |
"" |
Device serial number (e.g., Bluetooth MAC) |
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
CapAxeCount |
int |
[XmlElement] |
0 | Number of axes |
CapButtonCount |
int |
[XmlElement] |
0 | Number of buttons (gamepad-mapped count for gamepad devices) |
RawButtonCount |
int |
[XmlElement] |
0 | Total raw joystick buttons before gamepad remapping |
CapPovCount |
int |
[XmlElement] |
0 | Number of POV hat switches |
CapType |
int |
[XmlElement] |
0 |
InputDeviceType constant |
HasGyro |
bool |
[XmlElement] |
false |
Gyroscope sensor support |
HasAccel |
bool |
[XmlElement] |
false |
Accelerometer sensor support |
| Property | Type | Serialization | Default | Description |
|---|---|---|---|---|
DateCreated |
DateTime |
[XmlElement] |
DateTime.Now |
First creation timestamp |
DateUpdated |
DateTime |
[XmlElement] |
DateTime.Now |
Last update timestamp |
IsEnabled |
bool |
[XmlElement] |
true |
Whether device is enabled for mapping |
IsHidden |
bool |
[XmlElement] |
false |
Whether device is hidden from UI |
DisplayName |
string |
[XmlElement] |
"" |
User-assigned display name (overrides InstanceName if set) |
HidHideEnabled |
bool |
[XmlElement] |
false |
Whether device is hidden from games via HidHide when assigned |
ConsumeInputEnabled |
bool |
[XmlElement] |
false |
Whether mapped KB/mouse inputs are suppressed via hooks |
ForceRawJoystickMode |
bool |
[XmlElement] |
false |
Bypass SDL gamepad remapping, read raw joystick indices |
HidHideInstanceIds |
List<string> |
[XmlArray] [XmlArrayItem("Id")] |
new() |
Cached HID instance IDs for HidHide blacklisting (persisted for offline devices) |
| Property | Type | Serialization | Description |
|---|---|---|---|
Device |
ISdlInputDevice |
[XmlIgnore] |
Live device handle for state reading and rumble. Set in Step 1. |
IsOnline |
bool |
[XmlIgnore] |
Whether device is currently connected and opened. |
InputState |
CustomInputState |
[XmlIgnore] |
Current input state snapshot. Written by Step 2. Atomic reference. |
InputUpdates |
CustomInputUpdate[] |
[XmlIgnore] |
Buffered input changes since last poll cycle. |
InputStateTime |
DateTime |
[XmlIgnore] |
Timestamp of current InputState. |
OldInputState |
CustomInputState |
[XmlIgnore] |
Previous input state for change detection. |
OldInputUpdates |
CustomInputUpdate[] |
[XmlIgnore] |
Previous buffered updates. |
OldInputStateTime |
DateTime |
[XmlIgnore] |
Timestamp of previous OldInputState. |
ActuatorCount |
int |
[XmlIgnore] |
Total number of FFB actuator axes. |
DeviceObjects |
DeviceObjectItem[] |
[XmlIgnore] |
Device object metadata populated in Step 1. |
ForceFeedbackState |
ForceFeedbackState |
[XmlIgnore] |
Per-device force feedback state tracker. |
| Property | Type | Serialization | Description |
|---|---|---|---|
IsMouse |
bool |
[XmlIgnore] |
CapType == InputDeviceType.Mouse |
IsKeyboard |
bool |
[XmlIgnore] |
CapType == InputDeviceType.Keyboard |
HasForceFeedback |
bool |
[XmlIgnore] |
`ActuatorCount > 0 |
ResolvedName |
string |
[XmlIgnore] |
DisplayName if set, then InstanceName, then ProductName, then "(Unknown Device)" |
StatusText |
string |
[XmlIgnore] |
"Disabled", "Online", or "Offline" |
| Method | Signature | Description |
|---|---|---|
LoadInstance |
void LoadInstance(Guid instanceGuid, string instanceName, Guid productGuid, string productName) |
Populates identity properties. |
LoadCapabilities |
void LoadCapabilities(int axeCount, int buttonCount, int povCount, int type) |
Populates capability properties. |
LoadFromSdlDevice |
void LoadFromSdlDevice(SdlDeviceWrapper wrapper) |
Convenience method: calls LoadFromDevice + sets DevRevision. |
LoadFromKeyboardDevice |
void LoadFromKeyboardDevice(SdlKeyboardWrapper wrapper) |
Populates from keyboard wrapper. |
LoadFromMouseDevice |
void LoadFromMouseDevice(SdlMouseWrapper wrapper) |
Populates from mouse wrapper. |
LoadFromWebDevice |
void LoadFromWebDevice(WebControllerDevice wrapper) |
Populates from web controller. |
ClearRuntimeState |
void ClearRuntimeState() |
Clears Device, IsOnline, InputState, DeviceObjects, ForceFeedbackState. Preserves serializable properties. |
NotifyStateChanged |
void NotifyStateChanged() |
Raises PropertyChanged for IsOnline, StatusText, InputState. |
ToString |
string |
Returns "{ResolvedName} [{InstanceGuid:N}]". |
File: PadForge.Engine/Data/DeadZoneShape.cs
Namespace: PadForge.Engine.Data
Deadzone algorithm for thumbstick axes.
public enum DeadZoneShape
{
Axial = 0, // Independent per-axis (square/cross shape). Legacy behavior.
Radial = 1, // Circular/elliptical magnitude check, no output rescaling.
ScaledRadial = 2, // Circular/elliptical with output rescaling (industry standard). DEFAULT.
SlopedAxial = 3, // Axis-dependent thresholds: DZ grows as other axis increases.
SlopedScaledAxial = 4, // Sloped axis-dependent with output rescaling.
Hybrid = 5, // Scaled Radial followed by Sloped Scaled Axial (best hybrid).
}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.
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.
| Method | Signature | Description |
|---|---|---|
GetPosition |
static MappingSlot GetPosition(string propertyName, VirtualControllerType type, bool isCustomVJoy) |
Maps a layout-specific property name to its canonical MappingSlot
|
GetPropertyName |
static string GetPropertyName(MappingSlot slot, VirtualControllerType type, bool isCustomVJoy) |
Maps a canonical MappingSlot to a layout-specific property name |
IsSameLayout |
static bool IsSameLayout(VirtualControllerType srcType, bool srcIsCustomVJoy, VirtualControllerType tgtType, bool tgtIsCustomVJoy) |
Returns true if source and target use the same property names (no translation needed) |
GetLayoutLabel |
static string GetLayoutLabel(VirtualControllerType type, bool isCustomVJoy) |
Returns display label (e.g., "Xbox 360", "DualShock 4", "vJoy", "MIDI", "KB+M") |
| Layout | Property Name Examples | Notes |
|---|---|---|
| Gamepad (Xbox 360 / DS4 / vJoy gamepad preset) |
ButtonA, LeftThumbAxisX, DPadUp
|
Xbox 360 and DS4 share the same property names. Maps: A=Btn0, B=Btn1, X=Btn2, Y=Btn3, LB=Btn4, RB=Btn5, Back=Btn6, Start=Btn7, LS=Btn8, RS=Btn9, Guide=Btn10. Axes: LX=0, LY=1, LT=2, RX=3, RY=4, RT=5. |
| vJoy Custom |
VJoyBtn0, VJoyAxis2, VJoyAxis2Neg, VJoyPov0Up
|
Indexed by position. POV 0 only maps to D-Pad. |
| MIDI |
MidiNote0, MidiCC3, MidiCC3Neg
|
No D-Pad support (returns null). |
| KB+M |
KbmMBtn0, KbmMouseX, KbmMouseXNeg, KbmKey20, KbmScroll
|
Mouse buttons 0-4, keyboard VK codes, 3 mouse axes. D-Pad mapped to arrow keys. |
private enum LayoutKind { Gamepad, VJoyCustom, Midi, Kbm }- Xbox360, DS4, and vJoy gamepad preset all resolve to
LayoutKind.Gamepad. - vJoy with
isCustomVJoy=trueresolves toLayoutKind.VJoyCustom. -
IsSameLayoutcompares resolvedLayoutKindvalues.
File: PadForge.Engine/Common/SDL3Minimal.cs
Namespace: SDL3
Minimal SDL3 P/Invoke declarations for joystick, gamepad, keyboard, mouse, and haptic support. Only the functions actually used by PadForge are declared. Native library: "SDL3".
| Constant | Value | Description |
|---|---|---|
SDL_INIT_VIDEO |
0x00000020 |
Required for keyboard/mouse |
SDL_INIT_JOYSTICK |
0x00000200 |
Joystick subsystem |
SDL_INIT_HAPTIC |
0x00001000 |
Haptic subsystem |
SDL_INIT_GAMEPAD |
0x00002000 |
Gamepad subsystem (was SDL_INIT_GAMECONTROLLER) |
| Constant | Value |
|---|---|
SDL_HAT_CENTERED |
0x00 |
SDL_HAT_UP |
0x01 |
SDL_HAT_RIGHT |
0x02 |
SDL_HAT_DOWN |
0x04 |
SDL_HAT_LEFT |
0x08 |
SDL_HAT_RIGHTUP |
0x03 |
SDL_HAT_RIGHTDOWN |
0x06 |
SDL_HAT_LEFTUP |
0x09 |
SDL_HAT_LEFTDOWN |
0x0C |
| Constant | Value | Description |
|---|---|---|
SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS |
"SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS" |
Allow events when app not focused |
SDL_HINT_JOYSTICK_RAWINPUT |
"SDL_JOYSTICK_RAWINPUT" |
Do NOT set (conflicts with XInput enumeration) |
SDL_HINT_JOYSTICK_XINPUT |
"SDL_JOYSTICK_XINPUT" |
Enables Xbox controller enumeration |
SDL_HINT_JOYSTICK_HIDAPI_SWITCH2 |
"SDL_JOYSTICK_HIDAPI_SWITCH2" |
Switch 2 controller support |
SDL_HINT_VIDEO_ALLOW_SCREENSAVER |
"SDL_VIDEO_ALLOW_SCREENSAVER" |
Allow screensaver |
SDL_JoystickType:
| Value | Name |
|---|---|
| 0 | SDL_JOYSTICK_TYPE_UNKNOWN |
| 1 | SDL_JOYSTICK_TYPE_GAMEPAD |
| 2 | SDL_JOYSTICK_TYPE_WHEEL |
| 3 | SDL_JOYSTICK_TYPE_ARCADE_STICK |
| 4 | SDL_JOYSTICK_TYPE_FLIGHT_STICK |
| 5 | SDL_JOYSTICK_TYPE_DANCE_PAD |
| 6 | SDL_JOYSTICK_TYPE_GUITAR |
| 7 | SDL_JOYSTICK_TYPE_DRUM_KIT |
| 8 | SDL_JOYSTICK_TYPE_ARCADE_PAD |
| 9 | SDL_JOYSTICK_TYPE_THROTTLE |
| 10 | SDL_JOYSTICK_TYPE_COUNT |
SDL_PowerState:
| Value | Name |
|---|---|
| -1 | SDL_POWERSTATE_ERROR |
| 0 | SDL_POWERSTATE_UNKNOWN |
| 1 | SDL_POWERSTATE_ON_BATTERY |
| 2 | SDL_POWERSTATE_NO_BATTERY |
| 3 | SDL_POWERSTATE_CHARGING |
| 4 | SDL_POWERSTATE_CHARGED |
SDL_GUID (16 bytes): data0 through data15. Methods: ToGuid() (converts to .NET Guid), ToByteArray().
SDL_HapticDirection (16 bytes): type (byte), dir0, dir1, dir2 (int).
SDL_HapticLeftRight (12 bytes): type, length, large_magnitude, small_magnitude.
SDL_HapticConstant (40 bytes): type, direction, length, delay, button, interval, level, attack_length, attack_level, fade_length, fade_level.
SDL_HapticPeriodic (44 bytes): type, direction, length, delay, button, interval, period, magnitude, offset, phase, attack_length, attack_level, fade_length, fade_level.
SDL_HapticCondition (68 bytes): type, direction, length, delay, button, interval, per-axis arrays (3 axes): right_sat[0-2], left_sat[0-2], right_coeff[0-2], left_coeff[0-2], deadband[0-2], center[0-2].
SDL_HapticRamp (44 bytes): type, direction, length, delay, button, interval, start, end, attack_length, attack_level, fade_length, fade_level.
SDL_HapticEffect (72 bytes, explicit layout): Union overlaying type, leftright, constant, periodic, condition, ramp all at FieldOffset(0).
| Constant | Value | Description |
|---|---|---|
SDL_HAPTIC_CONSTANT |
1 << 0 |
Constant force |
SDL_HAPTIC_SINE |
1 << 1 |
Sine wave |
SDL_HAPTIC_SQUARE |
1 << 2 |
Square wave |
SDL_HAPTIC_TRIANGLE |
1 << 3 |
Triangle wave |
SDL_HAPTIC_SAWTOOTHUP |
1 << 4 |
Sawtooth up |
SDL_HAPTIC_SAWTOOTHDOWN |
1 << 5 |
Sawtooth down |
SDL_HAPTIC_RAMP |
1 << 6 |
Ramp |
SDL_HAPTIC_SPRING |
1 << 7 |
Spring condition |
SDL_HAPTIC_DAMPER |
1 << 8 |
Damper condition |
SDL_HAPTIC_INERTIA |
1 << 9 |
Inertia condition |
SDL_HAPTIC_FRICTION |
1 << 10 |
Friction condition |
SDL_HAPTIC_LEFTRIGHT |
1 << 11 |
Left/right dual-motor |
SDL_HAPTIC_CUSTOM |
1 << 15 |
Custom effect |
SDL_HAPTIC_GAIN |
1 << 16 |
Gain control supported |
SDL_HAPTIC_AUTOCENTER |
1 << 17 |
Auto-center supported |
SDL_HAPTIC_INFINITY |
0xFFFFFFFF |
Infinite duration |
SDL_HAPTIC_POLAR |
0 (byte) | Polar direction type |
SDL_HAPTIC_CARTESIAN |
1 (byte) | Cartesian direction type |
SDL_HAPTIC_SPHERICAL |
2 (byte) | Spherical direction type |
SDL_HAPTIC_STEERING_AXIS |
3 (byte) | Steering axis direction type |
| Constant | Value | Description |
|---|---|---|
SDL_GAMEPAD_AXIS_LEFTX |
0 | Left stick X |
SDL_GAMEPAD_AXIS_LEFTY |
1 | Left stick Y |
SDL_GAMEPAD_AXIS_RIGHTX |
2 | Right stick X |
SDL_GAMEPAD_AXIS_RIGHTY |
3 | Right stick Y |
SDL_GAMEPAD_AXIS_LEFT_TRIGGER |
4 | Left trigger |
SDL_GAMEPAD_AXIS_RIGHT_TRIGGER |
5 | Right trigger |
SDL_GAMEPAD_AXIS_COUNT |
6 | Total axis count |
| Constant | Value | Description |
|---|---|---|
SDL_GAMEPAD_BUTTON_SOUTH |
0 | A |
SDL_GAMEPAD_BUTTON_EAST |
1 | B |
SDL_GAMEPAD_BUTTON_WEST |
2 | X |
SDL_GAMEPAD_BUTTON_NORTH |
3 | Y |
SDL_GAMEPAD_BUTTON_BACK |
4 | Back/Select |
SDL_GAMEPAD_BUTTON_GUIDE |
5 | Guide/Home |
SDL_GAMEPAD_BUTTON_START |
6 | Start |
SDL_GAMEPAD_BUTTON_LEFT_STICK |
7 | Left stick click |
SDL_GAMEPAD_BUTTON_RIGHT_STICK |
8 | Right stick click |
SDL_GAMEPAD_BUTTON_LEFT_SHOULDER |
9 | Left bumper |
SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER |
10 | Right bumper |
SDL_GAMEPAD_BUTTON_DPAD_UP |
11 | D-pad up |
SDL_GAMEPAD_BUTTON_DPAD_DOWN |
12 | D-pad down |
SDL_GAMEPAD_BUTTON_DPAD_LEFT |
13 | D-pad left |
SDL_GAMEPAD_BUTTON_DPAD_RIGHT |
14 | D-pad right |
SDL_GAMEPAD_BUTTON_COUNT |
21 | Total button count |
| Constant | Value | Description |
|---|---|---|
SDL_SENSOR_ACCEL |
1 | Accelerometer |
SDL_SENSOR_GYRO |
2 | Gyroscope |
SDL_SENSOR_ACCEL_L |
3 | Left accelerometer |
SDL_SENSOR_GYRO_L |
4 | Left gyroscope |
SDL_SENSOR_ACCEL_R |
5 | Right accelerometer |
SDL_SENSOR_GYRO_R |
6 | Right gyroscope |
| Constant | Value | Description |
|---|---|---|
SDL_BUTTON_LMASK |
1 << 0 |
Left button |
SDL_BUTTON_MMASK |
1 << 1 |
Middle button |
SDL_BUTTON_RMASK |
1 << 2 |
Right button |
SDL_BUTTON_X1MASK |
1 << 3 |
X1 button |
SDL_BUTTON_X2MASK |
1 << 4 |
X2 button |
Static string[256] array providing human-readable names for Windows Virtual Key codes. Populated by BuildVirtualKeyNames(). Covers: Backspace, Tab, Enter, Shift/Ctrl/Alt, Escape, Space, navigation keys, 0-9, A-Z, LWin/RWin/Apps, Numpad 0-9 and operators, F1-F24, NumLock, ScrollLock, LShift/RShift/LCtrl/RCtrl/LAlt/RAlt, OEM keys (Semicolon, Equals, Comma, Minus, Period, Slash, Grave, Brackets, Backslash, Apostrophe). Used by SdlKeyboardWrapper.GetDeviceObjects() for button naming.
Lifecycle: SDL_Init, SDL_Quit, SDL_EnableScreenSaver, SDL_GetError, SDL_SetHint, SDL_free
Joystick Enumeration: SDL_GetJoysticks, SDL_GetJoystickGUIDForID, SDL_GetJoystickVendorForID, SDL_GetJoystickProductForID, SDL_GetJoystickProductVersionForID, SDL_GetJoystickTypeForID, SDL_GetJoystickNameForID, SDL_GetJoystickPathForID, SDL_IsGamepad
Gamepad Mappings: SDL_AddGamepadMappingsFromFile, SDL_AddGamepadMapping, GetGamepadMapping
Joystick Instance: SDL_OpenJoystick, SDL_CloseJoystick, SDL_GetJoystickID, SDL_JoystickConnected
Gamepad Instance: SDL_OpenGamepad, SDL_CloseGamepad, SDL_GetGamepadJoystick
Gamepad State: SDL_GetGamepadAxis, SDL_GetGamepadButton
Joystick State: SDL_UpdateJoysticks, SDL_PumpEvents, SDL_GetJoystickAxis, SDL_GetJoystickButton, SDL_GetJoystickHat, SDL_GetNumJoystickAxes, SDL_GetNumJoystickButtons, SDL_GetNumJoystickHats
Joystick Properties: SDL_GetJoystickName, SDL_GetJoystickVendor, SDL_GetJoystickProduct, SDL_GetJoystickProductVersion, SDL_GetJoystickType, SDL_GetJoystickPath, SDL_GetJoystickSerial, SDL_GetJoystickGUID, SDL_GetJoystickProperties, SDL_GetBooleanProperty, SDL_GetJoystickPowerInfo
Sensors: SDL_GamepadHasSensor, SDL_SetGamepadSensorEnabled, SDL_GetGamepadSensorData
Rumble: SDL_RumbleJoystick
Haptic: SDL_OpenHapticFromJoystick, SDL_CloseHaptic, SDL_GetHapticFeatures, SDL_CreateHapticEffect, SDL_UpdateHapticEffect, SDL_RunHapticEffect, SDL_StopHapticEffect, SDL_DestroyHapticEffect, SDL_SetHapticGain, SDL_GetNumHapticAxes
Keyboard: SDL_GetKeyboards, SDL_GetKeyboardNameForID, SDL_GetKeyboardState
Mouse: SDL_GetMice, SDL_GetMouseNameForID, SDL_GetMouseState, SDL_GetRelativeMouseState
Version: SDL_GetVersion, SDL_Linked_Version (returns (major, minor, patch) tuple)