16 October 2017

C# - Using a joypad input device without DirectX

While looking for a way to use my joypad in a C# application, everything I found used DirectInput to do it. As it turns out however, a single simple Windows API call is all that is needed:

internal enum JoypadErrors : uint
{
    None = 0, //NOERROR
    BadDeviceID = 2,
    InvalidParameter = 11,
    BadParameter = 165, //PARMS
    Fault = 166, //NOCANDO
    Unplugged = 167
}

[Flags]
internal enum JoypadFlags : uint //Calibration flags left out.
{
    X = 1,
    Y = 2,
    Z = 4,
    R = 8,
    U = 16,
    V = 32,
    POV = 64,
    Buttons = 128,
    RawData = 256,
    POVCTS = 512,
    Centered = 1024,
    All = X | Y | Z | R | U | V | POV | Buttons
}

[Flags]
internal enum JoypadButtons : uint
{
    I = 1,
    II = 2,
    III = 4,
    IV = 8,
    V = 16,
    VI = 32,
    VII = 64,
    VIII = 128,
    IX = 256,
    X = 512,
    XI = 1024,
    XII = 2048,
    XIII = 4096,
    XIV = 8192,
    XV = 16384,
    XVI = 32768
    //This goes on until button 32.
}

internal struct JoyInfoEx
{
    public int Size;
    public JoypadFlags Flags;
    public int X;
    public int Y;
    public int Z;
    public int R;
    public int U;
    public int V;
    public JoypadButtons Buttons;
    public int ButtonNumber;
    public int POV;
    public int Reserved1;
    public int Reserved2;
}

[DllImport("winmm", EntryPoint = "joyGetPosEx", SetLastError = true)]
private static extern JoypadErrors JoyGetPosEx(uint id, ref JoyInfoEx info);

You ask this function for the current state of the joypad's controls like so:

var info = new JoyInfoEx();
info.Size = Marshal.SizeOf(info); //Don't forget.
info.Flags = JoypadFlags.All; //What you want to get.
var result = JoyGetPosEx(0, ref info); //And that's it.

What do the received values mean? This is how they map to my Trustmaster Firestorm:

The 10 buttons are obvious. All the pressed ones are combined into a single value, so you need to extract them using a bitwise operation.

The point-of-view control returns a value between 0 and 31500, representing an angle in degrees. When unused, it returns 65565.

The left and right stick both have two axes. They all return a value between 0 and 65565. When centered on an axis, it's value is 32767.

And thats all there's to it. You get to work with the values you got.