Search code examples
c++directshowdirectx-9joystickdirectinput

How to implement Joystick code using in C++ using Microsoft's IDirectInputDevice8?


I need to do blocking and non blocking calls to poll on an Direct Input device using IDirectInputDevice8 interface.

In linux to do blocking, we use select ex:

while( ::select(_jdev+1, &set, NULL, NULL, &tv) > 0) 
{
    if( ::read( _jdev, &js, sizeof( js_event ) ) != sizeof( js_event ) )
    {
        perror( "Joystick : read error" );
        return;
    }

    _handleEvent( js );
}

How can I do the same thing using the idirectinputdevice8 interface: https://msdn.microsoft.com/en-us/library/windows/desktop/ee417816(v=vs.85).aspx

Even if I set IDirectInputDevice8::SetEventNotification(), I still have to call poll() everytime to fetch new data and this is not workable solution since it will cause the cpu to spin.

How can I accomplish this?

*** At the moment, I can find,iterate,connect and fetch data from the Joystick Device. I just do not have blocking calls implemented.

...HERE is my experimental/test code... Please Ignore Syntax errors

#include <windows.h>
#include <dinput.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include "joystick.h"


LPDIRECTINPUT8 di;
HRESULT hr;
LPDIRECTINPUTDEVICE8 joystick;
DIDEVCAPS capabilities;

BOOL CALLBACK
enumCallback(const DIDEVICEINSTANCE* instance, VOID* context)
{
    HRESULT hr;
    hr = di->CreateDevice(instance->guidInstance, &joystick, NULL);
    if (FAILED(hr)) {
        return DIENUM_CONTINUE;
    }
    return DIENUM_STOP;
}

int JoyStickProp()
{
    if (FAILED(hr = joystick->SetDataFormat(&c_dfDIJoystick2))) {
        return hr;
    }
    if (FAILED(hr = joystick->SetCooperativeLevel(NULL, DISCL_EXCLUSIVE |
        DISCL_FOREGROUND))) {
        return hr;
    }
    capabilities.dwSize = sizeof(DIDEVCAPS);
    if (FAILED(hr = joystick->GetCapabilities(&capabilities))) {
        return hr;
    }
}

HRESULT JoyStickPoll(DIJOYSTATE2 *js)
{
    HRESULT     hr;

    if (joystick == NULL) {
        return S_OK;
    }

    // Poll the device to read the current state
    hr = joystick->Poll();
    if (FAILED(hr)) {
        hr = joystick->Acquire();
        while (hr == DIERR_INPUTLOST) {
            hr = joystick->Acquire();
        }

        if ((hr == DIERR_INVALIDPARAM) || (hr == DIERR_NOTINITIALIZED)) {
            return E_FAIL;
        }

        // If another application has control of this device, return successfully.
        // We'll just have to wait our turn to use the joystick.
        if (hr == DIERR_OTHERAPPHASPRIO) {
            return S_OK;
        }
    }

    // Get the input's device state
    if (FAILED(hr = joystick->GetDeviceState(sizeof(DIJOYSTATE2), js))) {
        return hr; // The device should have been acquired during the Poll()
    }

    return S_OK;
}

int main()
{


    // Create a DirectInput device
    if (FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
        IID_IDirectInput8, (VOID**)&di, NULL))) {
        return hr;
    }

    // Look for the first simple joystick we can find.
    if (FAILED(hr = di->EnumDevices(DI8DEVCLASS_GAMECTRL, enumCallback,
        NULL, DIEDFL_ATTACHEDONLY))) {
        return hr;
    }
    // Make sure we got a joystick
    if (joystick == NULL) {
        printf("Joystick not found.\n");
        return E_FAIL;
    }

    JoyStickProp();
    DIJOYSTATE2 diState;

    HANDLE ghWriteEvent;
    // Create joystick event stuff here
    ghWriteEvent = CreateEvent(
        NULL,               // default security attributes
        FALSE,               // manual-reset event
        FALSE,              // initial state is nonsignaled
        TEXT("WriteEvent")  // object name
        );

    //makesure we can read fromt the joystick before waiting for an event
    JoyStickPoll(&diState);
    printf("x axis %d", diState.lX);
    printf("y axis %d", diState.lY);
    JoyStickPoll(&diState);
    printf("x axis %d", diState.lX);
    printf("y axis %d", diState.lY);
    JoyStickPoll(&diState);
    printf("x axis %d", diState.lX);
    printf("y axis %d", diState.lY);
    JoyStickPoll(&diState);
    printf("x axis %d", diState.lX);
    printf("y axis %d", diState.lY);
    JoyStickPoll(&diState);
    printf("x axis %d", diState.lX);
    printf("y axis %d", diState.lY);
    JoyStickPoll(&diState);
    printf("x axis %d", diState.lX);
    printf("y axis %d", diState.lY);
joystick->SetEventNotification(ghWriteEvent);
    while (TRUE) {

        DWORD dwResult = WaitForSingleObject(
            ghWriteEvent, // event handle
            INFINITE);

        switch (dwResult) {
        case WAIT_OBJECT_0:
            // Event 1 has been set. If the event was created as
            // autoreset, it has also been reset. 
            int x = 0;
            //ProcessInputEvent1();
            break;
        }
    }
}

Solution

  • The MSDN documentation for IDirectInputDevice8::SetEventNotification() gives you relevant code snippets.

    You need to call CreateEvent() and provide the returned HANDLE as an argument to SetEventNotification(). The event will be signaled for you when a state change occurs. You can wait on the event, and then once you have the notification, you get new state data using IDirectInputDevice8::GetDeviceState().

    All standard Wait Functions are applicable to waiting for the event to be signaled. The following code snippet shows you one way, using MsgWaitForMultipleObjects():

    while (TRUE) { 
    
        dwResult = MsgWaitForMultipleObjects(2, ah, FALSE, INFINITE, QS_ALLINPUT); 
        switch (dwResult) { 
            case WAIT_OBJECT_0: 
                // Event 1 has been set. If the event was created as
                // autoreset, it has also been reset. 
                ProcessInputEvent1();
                break; 
    

    The IDirectInputDevice8::Poll() method is used for devices that do not generate event notifications. You are supposed to poll them because they are not capable of operating the way you want them to.

    Some joysticks and other game devices, or particular objects on them, do not generate hardware interrupts and do not return any data or signal any events until you call the IDirectInputDevice8::Poll method.

    You poll only when you need to when notifications don't work for a device. The link above explains how to identify whether polling is necessary.