0
\$\begingroup\$

I am using an XBox 360 gamepad for Windows. I am having an issue with the following legacy code base, which relies on JOYINFOEX, JOYCAPS and MMRESULT:

ji.dwButtonNumber returns 0x1 no matter what button is selected (I suspect this is deprecated.) Update() is called in the Message Dispatch loop of my application. I am able to get the gamepad count, device driver name, its capabilities, etc. but am unable to get unique values for each button when it is selected, thoughts?

class JoyStick 
{
public:

    enum JOYSTICK {id1, id2};
    JOYCAPS&          GetCaps      ( enum JOYSTICK id );
    static JOYINFOEX& GetInfo(enum JOYSTICK id);
    static UINT       GetStickCount( void );

//...

void Scene1::Update()
{
    JOYINFOEX ji;
    WPARAM key;             
    
    ji = JoyStick::GetInfo(JoyStick::id1);
    key = theapp->GetKey();
    
    switch (ji.dwButtonNumber)
    {
        case GP_A :
        {
            TRACE("Button Number %i pressed\n", ji.dwButtonNumber);   // ji.dwButtonNumber == 0x1
            break;
        }
        case GP_B :
        {
            TRACE("Button Number %i pressed\n", ji.dwButtonNumber);     // ji.dwButtonNumber == 0x1
            break;
        }
        case GP_X :
//...
```
\$\endgroup\$
2

1 Answer 1

1
\$\begingroup\$

After a little work I was able to come up with the following C++ framework, which uses the article found here https://docs.microsoft.com/en-us/windows/win32/xinput/getting-started-with-xinput. I found this to work with my XBox gamepad controller. Compiled on Visual Studio 2019

GamePad.h

#incldude <xinput.h>
class GamePad 
{
public:
    GamePad();
    virtual ~GamePad();

    static BOOL is_vibrating;
    
    enum XIUSER { id0 = 0, id1 = 1, id2 = 2, id3 = 3 };

    typedef struct CONNECTED_CONTROLLERS {
        BOOL id0_connected = FALSE;
        BOOL id1_connected = FALSE;
        BOOL id2_connected = FALSE;
        BOOL id3_connected = FALSE;
    } CONTROLLER_STATE, *PCONTROLLER_STATE;

    typedef struct LAST_VK {
        DWORD id0_vk = VK_PAD_START;
        DWORD id1_vk = VK_PAD_START;
        DWORD id2_vk = VK_PAD_START;
        DWORD id3_vk = VK_PAD_START;
    } LAST_VK, *PLAST_VK;

    static PXINPUT_CAPABILITIES pCapabilities;
    static PXINPUT_KEYSTROKE pKeystroke;
    static PCONTROLLER_STATE pControllerState;
    static PLAST_VK pLast_VK;
    

    void XIGetState(void);
    void XISetState(enum XIUSER Index);
    void XIGetCaps(void);               // get the gamepad capabilities
    DWORD XIGetVKey(void);              // get the gamepad virtualkey needed by Scene
    
    static void XISetVibration(enum XIUSER Index);
    
};

GamePad.cpp

#include "gamepad.h"


PXINPUT_CAPABILITIES GamePad::pCapabilities;
PXINPUT_KEYSTROKE GamePad::pKeystroke;
GamePad::PCONTROLLER_STATE GamePad::pControllerState;
GamePad::PLAST_VK GamePad::pLast_VK;
BOOL GamePad::is_vibrating;



//#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE  7849
//#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
//#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD    30

GamePad::GamePad()
{


    TRACE("GamePad::GamePad invoked\n");

    // allocate memory for the data structure
    pCapabilities = new XINPUT_CAPABILITIES;
    pKeystroke = new XINPUT_KEYSTROKE;
    pControllerState = new CONTROLLER_STATE;
    pLast_VK = new LAST_VK;

    pKeystroke->Unicode = 0;
    pKeystroke->Flags = XINPUT_KEYSTROKE_KEYUP;
    pKeystroke->UserIndex = XUSER_INDEX_ANY;
    pKeystroke->HidCode = 0;
    pKeystroke->VirtualKey = VK_PAD_START;
    
    GamePad::is_vibrating = FALSE;

    XIGetState();


    if (GamePad::pCapabilities != NULL)
    {
        for (DWORD i = 0; i < XUSER_MAX_COUNT; i++)
        {
            DWORD dwResult = XInputGetCapabilities(i, XINPUT_FLAG_GAMEPAD, GamePad::pCapabilities);
            if (dwResult == ERROR_SUCCESS)
            {
                TRACE("XInputGetCapabilities returned Controller IS connected, on %ld\n", i);
                switch (i)
                {
                case 0:
                    pControllerState->id0_connected = TRUE; // id0 was initialized with FALSE, here we see that there is a controller present on id0
                    break;
                case 1:
                    pControllerState->id1_connected = TRUE; // id1 was initialized with FALSE, here we see that there is a controller present on id1
                    break;
                case 2:
                    pControllerState->id2_connected = TRUE; // id2 was initialized with FALSE, here we see that there is a controller present on id2
                    break;
                case 3:
                    pControllerState->id3_connected = TRUE; // id3 was initialized with FALSE, here we see that there is a controller present on id3
                    break;
                }

                // Controller is connected
            }
            else
            {
                TRACE("XInputGetCapabilities returned Controller is NOT connected on %ld\n", i);

                switch (i)
                {
                case 0:
                    pControllerState->id0_connected = FALSE; // id0 was initialized with FALSE, here we see that there is no controller present on id0
                    break;
                case 1:
                    pControllerState->id1_connected = FALSE; // id1 was initialized with FALSE, here we see that there is no controller present on id1
                    break;
                case 2:
                    pControllerState->id2_connected = FALSE; // id2 was initialized with FALSE, here we see that there is no controller present on id2
                    break;
                case 3:
                    pControllerState->id3_connected = FALSE; // id3 was initialized with FALSE, here we see that there is no controller present on id3
                    break;
                }
                // Controller is not connected
            }

        }
    }

}


GamePad::~GamePad()
{
    TRACE("GamePad::~GamePad invoked\n");

    delete(pCapabilities);
    delete(pKeystroke);
    delete(pControllerState);
    delete(pLast_VK);

}



void GamePad::XIGetState(void)
{

    TRACE("GamePad::XIGetState invoked\n");

    DWORD dwResult;
    for (DWORD i = 0; i < XUSER_MAX_COUNT; i++)
    {
        XINPUT_STATE state;
        ZeroMemory(&state, sizeof(XINPUT_STATE));

        // Simply get the state of the controller from XInput.
        dwResult = XInputGetState(i, &state);
        if (dwResult == ERROR_SUCCESS)
        {
            TRACE("XInputGetState returned Controller IS connected, on %ld\n", i);
            switch (i)
            {
            case 0:
                pControllerState->id0_connected = TRUE; // id0 was initialized with FALSE, here we see that there is a controller present on id0
                break;
            case 1:
                pControllerState->id1_connected = TRUE; // id1 was initialized with FALSE, here we see that there is a controller present on id1
                break;
            case 2:
                pControllerState->id2_connected = TRUE; // id2 was initialized with FALSE, here we see that there is a controller present on id2
                break;
            case 3:
                pControllerState->id3_connected = TRUE; // id3 was initialized with FALSE, here we see that there is a controller present on id3
                break;
            }

            // Controller is connected
        }
        else
        {
            TRACE("XInputGetState returned Controller is NOT connected on %ld\n", i);

            switch (i)
            {
            case 0:
                pControllerState->id0_connected = FALSE; // id0 was initialized with FALSE, here we see that there is no controller present on id0
                break;
            case 1:
                pControllerState->id1_connected = FALSE; // id1 was initialized with FALSE, here we see that there is no controller present on id1
                break;
            case 2:
                pControllerState->id2_connected = FALSE; // id2 was initialized with FALSE, here we see that there is no controller present on id2
                break;
            case 3:
                pControllerState->id3_connected = FALSE; // id3 was initialized with FALSE, here we see that there is no controller present on id3
                break;
            }
            // Controller is not connected
        }
            
    }

}



//
// currently only support for controller 0 or Index == id0
// Vibration starts easily but does not shut down so easily
void GamePad::XISetVibration(enum XIUSER Index)
{
    TRACE("GamePad::XISetVibration invoked\n");
    

    if (pControllerState->id0_connected == TRUE)
    {
        DWORD dwResult;

        XINPUT_VIBRATION vibration;
        vibration.wLeftMotorSpeed = 0; // use any value between 0-65535 here
        vibration.wRightMotorSpeed = 0; // use any value between 0-65535 here

        if (!GamePad::is_vibrating)                 // turn off the vibration
        {
            ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
            vibration.wLeftMotorSpeed = 32000; // use any value between 0-65535 here
            vibration.wRightMotorSpeed = 16000; // use any value between 0-65535 here
            dwResult = XInputSetState(Index, &vibration);
            if (dwResult == ERROR_SUCCESS)
            {
                GamePad::is_vibrating = TRUE;
                // Controller is connected
            }
            else
            {
                // Controller is not connected
            }

        }
        else {                                                                      // turn on the vibration

            ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));                   
            vibration.wLeftMotorSpeed = 0; // use any value between 0-65535 here
            vibration.wRightMotorSpeed = 0; // use any value between 0-65535 here
            dwResult = XInputSetState(Index, &vibration);
            if (dwResult == ERROR_SUCCESS)
            {
                GamePad::is_vibrating = FALSE;
                // Controller is connected
            }
            else
            {
                // Controller is not connected
            }
        }

    }
}


void GamePad::XISetState(enum XIUSER Index)
{
    TRACE("GamePad::XISetState invoked\n");
    
    PXINPUT_VIBRATION pVibration;
    DWORD dwResult;

    pVibration = new XINPUT_VIBRATION;
    pVibration->wRightMotorSpeed = 32000;
    pVibration->wLeftMotorSpeed = 16000;
    
    dwResult = XInputSetState(Index, pVibration);
    if (dwResult == ERROR_SUCCESS)
    {
        // Controller is connected
    }
    else
    {
        // Controller is not connected
    }

    
}


void GamePad::XIGetCaps(void)
{
    TRACE("GamePad::XIGetCaps invoked\n");

    DWORD dwResult;
    for (DWORD i = 0; i < XUSER_MAX_COUNT; i++)
    {

        if (GamePad::pCapabilities != NULL)
        {
            dwResult = XInputGetCapabilities(i, XINPUT_FLAG_GAMEPAD, GamePad::pCapabilities);
            if (dwResult == ERROR_SUCCESS)
            {
                TRACE("XInputGetCapabilities returned Controller IS connected, on %ld\n", i);
                switch (i)
                {
                case 0:
                    pControllerState->id0_connected = TRUE; // id0 was initialized with FALSE, here we see that there is a controller present on id0
                    break;
                case 1:
                    pControllerState->id1_connected = TRUE; // id1 was initialized with FALSE, here we see that there is a controller present on id1
                    break;
                case 2:
                    pControllerState->id2_connected = TRUE; // id2 was initialized with FALSE, here we see that there is a controller present on id2
                    break;
                case 3:
                    pControllerState->id3_connected = TRUE; // id3 was initialized with FALSE, here we see that there is a controller present on id3
                    break;
                }

                // Controller is connected
            }
            else
            {
                TRACE("XInputGetCapabilities returned Controller is NOT connected on %ld\n", i);

                switch (i)
                {
                case 0:
                    pControllerState->id0_connected = FALSE; // id0 was initialized with FALSE, here we see that there is no controller present on id0
                    break;
                case 1:
                    pControllerState->id1_connected = FALSE; // id1 was initialized with FALSE, here we see that there is no controller present on id1
                    break;
                case 2:
                    pControllerState->id2_connected = FALSE; // id2 was initialized with FALSE, here we see that there is no controller present on id2
                    break;
                case 3:
                    pControllerState->id3_connected = FALSE; // id3 was initialized with FALSE, here we see that there is no controller present on id3
                    break;
                }
                // Controller is not connected
            }

        }
    }
}

DWORD GamePad::XIGetVKey(void)
{

        
    DWORD dwResult;
    for (DWORD i = 0; i < XUSER_MAX_COUNT; i++)
    {

        dwResult = XInputGetKeystroke(i, 0, GamePad::pKeystroke);
        if (dwResult == ERROR_SUCCESS)
        {
            TRACE("XInputGetKeyStroke returned Controller IS connected, on %ld\n", i);
            switch (i)
            {
            case 0:
                pControllerState->id0_connected = TRUE; // id0 was initialized with FALSE, here we see that there is a controller present on id0
                pLast_VK->id0_vk = GamePad::pKeystroke->VirtualKey;
                return pLast_VK->id0_vk;
                break;
            case 1:
                pControllerState->id1_connected = TRUE; // id1 was initialized with FALSE, here we see that there is a controller present on id1
                pLast_VK->id1_vk = GamePad::pKeystroke->VirtualKey;
                return pLast_VK->id1_vk;
                break;
            case 2:
                pControllerState->id2_connected = TRUE; // id2 was initialized with FALSE, here we see that there is a controller present on id2
                pLast_VK->id2_vk = GamePad::pKeystroke->VirtualKey;
                return pLast_VK->id2_vk;
                break;
            case 3:
                pControllerState->id3_connected = TRUE; // id3 was initialized with FALSE, here we see that there is a controller present on id3
                pLast_VK->id3_vk = GamePad::pKeystroke->VirtualKey;
                return pLast_VK->id3_vk;
                break;
            }

            // Controller is connected
        }
        else
        {
            TRACE("XInputGetKeyStroke returned Controller is NOT connected on %ld\n", i);

            switch (i)
            {
            case 0:
                pControllerState->id0_connected = FALSE; // id0 was initialized with FALSE, here we see that there is no controller present on id0
                return pLast_VK->id0_vk;
                break;
            case 1:
                pControllerState->id1_connected = FALSE; // id1 was initialized with FALSE, here we see that there is no controller present on id1
                return pLast_VK->id1_vk;
                break;
            case 2:
                pControllerState->id2_connected = FALSE; // id2 was initialized with FALSE, here we see that there is no controller present on id2
                return pLast_VK->id2_vk;
                break;
            case 3:
                pControllerState->id3_connected = FALSE; // id3 was initialized with FALSE, here we see that there is no controller present on id3
                return pLast_VK->id3_vk;
                break;
            }
            // Controller is not connected

        }

    }

    return -1; // no other control path was satisfied. Could be here if all controllers were unplugged
}
\$\endgroup\$
3
  • 1
    \$\begingroup\$ The name of the class implies that it only encapsulates the concept of a single gamepad, yet your are handling all of them. I recommend simplifying it down to one gamepad. You can then just have an InputHandler that contains an array of 4 gamepads: std::array<GamePad, 4> xboxControllers{};. See my implementation for examples: github.com/cugone/Abrams2019/tree/master/Engine/Code/Engine/… \$\endgroup\$
    – Casey
    Dec 12, 2020 at 19:23
  • \$\begingroup\$ Thank you. I appreciate the feedback, insight and direction. Here is some of my code. github.com/david-harding I am not sharing the SDK yet that is used to create samples. It encapsulates the DirectX 7.0 APIs and build a level of abstraction. Not sure if there is any interest in seeing it all or not. \$\endgroup\$
    – DHarding
    Dec 28, 2020 at 6:19
  • \$\begingroup\$ If this solved your problem, please hit the green checkmark to flag it as "Accepted" \$\endgroup\$
    – DMGregory
    Jan 10, 2021 at 22:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .