Joystick class for Windows

Interesting point, I forgot to ask; What gamepad are you [@Luca_Carminati] trying to use?

I have 3 different USB controllers: 2 joysticks (THE C64 Joystick; Xin-Mo Dual Arcade) and 1 gamepad (PLAYSTATION®3).
GamePad.GetState(playerIndex).IsConnected always returns false for each of them and for any playerIndex value (I tried 0 to 9).
Probably they are not SDL-compatible.
My joystick class, instead, recognizes all the controllers (it uses the joyGetPosEx winmm API).

EDIT [I forgot to add, I looked over at my XBOX ONE controllers, Steelseries iPhone Gamepad and Nintendo Switch Gamepad and thought, what the hell!?]

None of them are even Windows compatible as far as I can be bothered doing research past images. and Sony only allowed the PS4 - and beyond - controllers to be compatible with Desktops.

Like I said, just post it :slight_smile:

EDIT

Have you checked them in the Windows Gamepad control tool to test button presses too?

I noticed you mentioned USB, so disregard compatibility above, but still, what the hell dude!?

Yes, they perfectly work in the tool.

Ok, here is the class.

using System.Collections.Generic;
using System.Runtime.InteropServices;

public enum JoystickDirection
{
    Left,
    Right,
    Up,
    Down
}

public struct JoystickState
{
    public bool Left;
    public bool Right;
    public bool Up;
    public bool Down;
    public int Buttons;
}

public static class Joystick
{
    public static bool IsConnected { get; private set; }

    static JoystickState JoystickState;
    static JoystickState PreviousJoystickState;

    static JOYINFOEX JoyInfo;

    public static void Update()
    {
        const uint JOYERR_NOERROR = 0;
        const uint JOY_RETURNALL = 0xFF;

        JoyInfo.dwSize = (uint)Marshal.SizeOf(JoyInfo);
        JoyInfo.dwFlags = JOY_RETURNALL;

        JoystickState state = new JoystickState();

        if (joyGetPosEx(0, ref JoyInfo) == JOYERR_NOERROR)
        {
            if (JoyInfo.dwXpos == 0) state.Left = true;
            else if (JoyInfo.dwXpos == 0xFFFF) state.Right = true;

            if (JoyInfo.dwYpos == 0) state.Up = true;
            else if (JoyInfo.dwYpos == 0xFFFF) state.Down = true;

            state.Buttons = (int)JoyInfo.dwButtons;

            IsConnected = true;
        }
        else
        {
            IsConnected = false;
        }

        PreviousJoystickState = JoystickState;
        JoystickState = state;
    }

    public static bool IsMoved(JoystickDirection direction)
    {
        if (direction == JoystickDirection.Left) return JoystickState.Left == true;
        if (direction == JoystickDirection.Right) return JoystickState.Right == true;
        if (direction == JoystickDirection.Up) return JoystickState.Up == true;
        if (direction == JoystickDirection.Down) return JoystickState.Down == true;
        return false;
    }

    public static bool HasJustBeenMoved(JoystickDirection direction)
    {
        if (direction == JoystickDirection.Left) return JoystickState.Left == true && PreviousJoystickState.Left == false;
        if (direction == JoystickDirection.Right) return JoystickState.Right == true && PreviousJoystickState.Right == false;
        if (direction == JoystickDirection.Up) return JoystickState.Up == true && PreviousJoystickState.Up == false;
        if (direction == JoystickDirection.Down) return JoystickState.Down == true && PreviousJoystickState.Down == false;
        return false;
    }

    public static bool IsPressed(int button)
    {
        if ((JoystickState.Buttons & (1u << (button - 1))) != 0) return true;
        return false;
    }

    public static bool HasJustBeenPressed(int button)
    {
        if ((JoystickState.Buttons & (1u << (button - 1))) != 0 && (PreviousJoystickState.Buttons & (1u << (button - 1))) == 0) return true;
        return false;
    }

    public static int[] GetPressedButtons()
    {
        return GetPressedButtons(JoystickState.Buttons);
    }

    public static int[] GetJustPressedButtons()
    {
        List<int> pressedButtons = new List<int>(GetPressedButtons(JoystickState.Buttons));
        List<int> previousPressedButtons = new List<int>(GetPressedButtons(PreviousJoystickState.Buttons));
        pressedButtons.RemoveAll(button => previousPressedButtons.Contains(button));
        return pressedButtons.ToArray();
    }

    static int[] GetPressedButtons(int buttons)
    {
        List<int> pressedButtons = new List<int>();
        if (buttons != 0)
        {
            for (int i = 0; i < 32; i++)
            {
                if ((buttons & (1u << i)) != 0)
                {
                    pressedButtons.Add(i + 1);
                }
            }
        }
        return pressedButtons.ToArray();
    }

    [StructLayout(LayoutKind.Sequential)]
    struct JOYINFOEX
    {
        public uint dwSize;
        public uint dwFlags;
        public uint dwXpos;
        public uint dwYpos;
        public uint dwZpos;
        public uint dwRpos;
        public uint dwUpos;
        public uint dwVpos;
        public uint dwButtons;
        public uint dwButtonNumber;
        public uint dwPOV;
        public uint dwReserved1;
        public uint dwReserved2;
    }

    [DllImport("winmm.dll")]
    static extern uint joyGetPosEx(uint uJoyID, ref JOYINFOEX pji);
}

This class only manages one joystick (the first one it finds) but you can improve it by changing the uJoyID parameter of the joyGetPosEx function.

Usage:

Joystick.Update()
// Updates the joystick state.

Joystick.IsPressed(button)
// Returns true if the specified button is pressed. button can be a value from 1 to 32.

Joystick.HasJustBeenPressed(button)
// Returns true if the specified button has been pressed during the last update, otherwise false until the player releases and presses the button again. Useful if you want the player to fire only once when the button is pressed (autofire OFF).

Joystick.IsMoved(direction)
// Returns true if the joystick is moved in the specified direction.

Joystick.HasJustBeenMoved(direction)
// Returns true if the joystick has been moved in the specified direction during the last update.

Joystick.GetPressedButtons()
// Returns an array containing all the pressed buttons.

Joystick.GetJustPressedButtons()
// Returns an array containing all the buttons that has been pressed during the last update.

I have released a generic solution if you want to look at it

For complete beginners, explaining why this is at the aft of the loop may explain a lot also, explain how to handle DLL’s properly to avoid memory leaks.

Not questioning you here, just a nudge to get you to explain things, I often find, we understand things better when we are asked to teach them, this is my methodology.

Good work and thank you for your contribution here!

I think Microsoft’s documentation can explain it much better than I do. You can find it on https://docs.microsoft.com/en-us/previous-versions/ms709354(v%3Dvs.85). It is simply a system function and I don’t think we should worry about memory leaks. The documentation also explains well the JOYINFOEX structure which also contains data such as Z, R, U, and V pos as well as POV.

1 Like

It’s at the bottom of the Class, not the bottom of a loop. It’s just an import so there won’t be a memory leak.

Typically Imports are put a the top of the file just as a matter of practice.

1 Like

Anyway, it’s a real shame (and a limit) that Monogame can’t handle all kinds of controllers, isn’t it? Maybe future versions will take this into account?

Luca, Monogame doesn’t need to handle all controllers, it’s not a game engine.

If you want your game based on Monogame to support all controllers, then you can do it.

Just look at the demo I posted, I support almost anything with a USB connection.

Don’t expect Monogame to do everything for you, it’s a damn good start, but you still have to work to make anything with it

1 Like

If developing for Steam, I would recommend checking out the SteamInput API. That’s how we support a crazy number of devices, many natively with matching in-game input glyphs (and even virtual gamepads on phones via SteamLinkTouch). Whether connected via USB, Bluetooth, or otherwise. SteamInput uses SDL under the hood.

https://muffed.kelsam.net/docs/game/engine/input/devices/#connect-natively-supported-devices

Maybe I got the wrong idea of ​​what Monogame is and what it does or what it should do. As a matter of fact, when I created my first project and saw that the first line of the Update method said “if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed...” I thought: “Well, I don’t have to worry about creating code to handle gamepads, since they are already supported.”. You can imagine that I was disappointed when I found out that this is not exactly the case. It’s as if the Keyboard class only handles certain keyboard types.
With that, don’t get me wrong. Monogame is already doing a lot and that’s fine. I’m just wondering if things like this are expected to be added / improved in future releases. I also think of future beginner game makers who at some point will have to choose which game engine to bet on.

I think I’ll stay with my class: it’s simple and does what I need. But out of curiosity, I took a look at the site but I don’t see anything related to gamepad / input.

A gamepad is just a HID device

Instead of having a bespoke gamepad class and a joystick class and a throttle class and a “what the hell is this thing?” class, you can have a generic input device class.

Horses for courses though, if you don’t expect anyone to ever plug in a “ElITe Pro-Mysterion twenty two axis force feedback AI joystick” what is the point of supporting it :smiley:

1 Like

I correct myself: I don’t pretend that Monogame supports every type of gamepad, even the most unlikely, but I expect that at least those recognized by Windows (therefore in some standard way) are normally managed. As I said, I have 3 completely different types of gamepads, but Windows recognizes them without problems, which is what my class does. For me, the question always remains: one day, will Monogame be able to manage the gamepads that Windows (and me too) consider standard?

Have you tried XINPUT?

Have you tried XINPUT?

The first line of the documentation says: “XInput is an API that allows applications to receive input from the Xbox Controller for Windows.
And since I don’t ONLY want to manage Xbox controllers, my solution is ideal for me.

1 Like

…I have always loved Jackie Chan…