Search code examples
c++qtwinapijoystick

c++ programming for joystick in windows


i want to connect joystick to my c++ qt program in windows.I studied about programming with win32 api using the joystickapi library in microsoft guid for joystick program But I didn't understand exactly What should I do after checking the system compatibility?

Completion description:

my code for system compatibility:

JOYINFO joyinfo;
UINT wNumDevs, wDeviceID;
BOOL bDev1Attached, bDev2Attached;
void joyCapablitis(){
    if((wNumDevs = joyGetNumDevs()) == 0)
        qDebug()<<"no driver available";
    bDev1Attached = joyGetPos(JOYSTICKID1,&joyinfo) != JOYERR_UNPLUGGED;
    bDev2Attached = wNumDevs == 2 && joyGetPos(JOYSTICKID2,&joyinfo) !=
            JOYERR_UNPLUGGED;
    if(bDev1Attached || bDev2Attached)   // decide which joystick to use
    {

        wDeviceID = bDev1Attached ? JOYSTICKID1 : JOYSTICKID2;

    }
    else
        qDebug()<<"no device available";

}

I want to track the movement of buttons and simulate it in software.How can I get joystick information (such as number of button and button on or off) from win32 api?


Solution

  • I wrote a QJoystick lib in a form of a Qt module but it's not yet released.

    Here is a bit of code for Windows.

    First, you need to initialize DirectInput:

    #include "comdef.h"
    
    #define SAFE_DELETE(p)  { if(p) { delete (p);     (p) = nullptr; }}
    #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p) = nullptr; }}
    
    #define AS(x, p) reinterpret_cast<x>(p)
    
    BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE*       deviceInstance, void* controller);
    BOOL CALLBACK EnumObjectsCallback  (const DIDEVICEOBJECTINSTANCE* objectInstance, void* controller);
    
    static LPDIRECTINPUT8 DirectInputPtr = nullptr;
    
    HRESULT hr;
    
    if (DirectInputPtr == nullptr)
    {
        if (FAILED(hr = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, AS(void**, &DirectInputPtr), nullptr)))
            qCritical("QJoystick failed to obtain direct input interface: %s", qPrintable(hrError(hr)));
    }
    

    Second, setup callbacks:

    if (DirectInputPtr)
    {
        if (FAILED(hr = DirectInputPtr->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, this, DIEDFL_ATTACHEDONLY)))
            qDebug("QJoystick EnumDevices failed");
    
        if (joystickPtr)
        {
            if (FAILED(hr = joystickPtr->SetDataFormat(&c_dfDIJoystick2)))
                qWarning("QJoystick: failed to set data format for id %d", deviceId);
    
            if (SUCCEEDED(hr = joystickPtr->EnumObjects(EnumObjectsCallback, this, DIDFT_ALL)))
            {
                DIDEVICEINSTANCE joystickinfo;
                joystickinfo.dwSize = sizeof(joystickinfo);
    
                if (FAILED(hr = joystickPtr->GetDeviceInfo(&joystickinfo)))
                {
                    qCritical() << "QJoystick GetDeviceInfo failed:" << hrError(hr);
                    SAFE_RELEASE(joystickPtr)
                }
    
                description = QString::fromWCharArray(joystickinfo.tszProductName);
                guid        = QString("%1").arg(joystickinfo.guidProduct.Data1, 8, 16, QLatin1Char('0'));
            }
            else
                qCritical("QJoystick: set callback on EnumObjects failed for %d", deviceId);
        }
    }
    

    EnumJoysticksCallback is where you create a virtual device for your joystick:

    BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* deviceInstance, void* pController)
    {
        QJoystickPrivate* controller = AS(QJoystickPrivate*, pController);
        BOOL result = DIENUM_CONTINUE;
    
        if (controller->enumCounter == controller->deviceId)
        {
            HRESULT hr = DirectInputPtr->CreateDevice(deviceInstance->guidInstance, &(controller->joystickPtr), nullptr);
    
            if (SUCCEEDED(hr))
                result = DIENUM_STOP;
            else
                qWarning("QJoystick: CreateDevice failed %d", controller->deviceId);
        }
        else
            ++controller->enumCounter;
    
        return result;
    }
    

    And EnumObjectsCallback will be executed for every button or axis during the enumeration process:

    BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* objectInstance, void* pController)
    {
        QJoystickPrivate* controller = AS(QJoystickPrivate*, pController);
    
        //Set the range for axis
        if (objectInstance->dwType & DIDFT_AXIS)
        {
            DIPROPRANGE diprg;
    
            diprg.diph.dwSize       = sizeof(DIPROPRANGE);
            diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
            diprg.diph.dwHow        = DIPH_BYID;
            diprg.diph.dwObj        = objectInstance->dwType; // Specify the enumerated axis
    
            diprg.lMin = -1000;
            diprg.lMax = +1000;
    
            if (FAILED(controller->joystickPtr->SetProperty(DIPROP_RANGE, &diprg.diph)))
                return DIENUM_STOP;
        }
    
        if (objectInstance->guidType == GUID_XAxis  || objectInstance->guidType == GUID_YAxis  || objectInstance->guidType == GUID_ZAxis  ||
            objectInstance->guidType == GUID_RxAxis || objectInstance->guidType == GUID_RyAxis || objectInstance->guidType == GUID_RzAxis ||
            objectInstance->guidType == GUID_Slider)
        {
            // axis
        }
        else if (objectInstance->guidType == GUID_POV)
            // pov...
        else if (objectInstance->guidType == GUID_Button)
            // button...
        else if (objectInstance->guidType == GUID_Unknown)
            // unknown...
    
        return DIENUM_CONTINUE;
    }