2

The Joysticks works as expected except that if it's not plugged my app crashes. The interesting thing, I used it on the computer that I make the development in, it does not crash when not plugged, only on other computers. So that's something to keep in mind

The way I used the API is as follow :

I setup global variables

LPDIRECTINPUTDEVICE8 JoystickDriver::joystick;
LPDIRECTINPUT8 JoystickDriver::di;
DIDEVCAPS JoystickDriver::capabilities;
HRESULT JoystickDriver::hr;
DIJOYSTATE2 JoystickDriver::jstate;

In the callback function that is executed at a fixed time step

Main Callback :

if (*isInitialized != 1 )
{
    js->initialize();
    js->selectJoystick();
    js->setProperties();
    js->enumAxes();     
    *isInitialized = 1;     
}

if (js->PollData(&Joystick::jstate) == S_OK)
{
    slider[0] = (real_T)(1000 - Joystick::jstate.rglSlider[0]) / 20;

    pov[0] = (real_T)Joystick::jstate.lX;
    pov[1] = (real_T)Joystick::jstate.lY;
    pov[2] = (real_T)Joystick::jstate.lRz;

    Buttons[0] = (uint8_T)(Joystick::jstate.rgbButtons[0] & 0x80);
    Buttons[1] = (uint8_T)(Joystick::jstate.rgbButtons[1] & 0x80);
    Buttons[2] = (uint8_T)(Joystick::jstate.rgbButtons[2] & 0x80);
    Buttons[3] = (uint8_T)(Joystick::jstate.rgbButtons[3] & 0x80);
    Buttons[4] = (uint8_T)(Joystick::jstate.rgbButtons[4] & 0x80);

}

In the Class definition/implementation

Joystick.h



class Joystick
{
public:
    Joystick();
    ~Joystick();

    HRESULT initialize();
    HRESULT selectJoystick();
    static BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE* instance, VOID* context);
    HRESULT setProperties();
    HRESULT enumAxes();
    static BOOL CALLBACK enumAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, VOID* context);
    HRESULT PollData(DIJOYSTATE2 *js);
    void disConnectJoystick();

    static LPDIRECTINPUTDEVICE8 joystick;
    static LPDIRECTINPUT8 di;
    static DIDEVCAPS capabilities;
    static HRESULT hr;
    static DIJOYSTATE2 jstate;
};

Joystick.cpp

#include "Joystick.h"


Joystick::Joystick()
{
}


Joystick::~Joystick()
{
}


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


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



BOOL CALLBACK Joystick::enumCallback(const DIDEVICEINSTANCE* instance, VOID* context)
{

    // Obtain an interface to the enumerated joystick.
    Joystick::hr = (Joystick::di)->CreateDevice(instance->guidInstance, &Joystick::joystick, NULL);

    // If it failed, then we can't use this joystick. (Maybe the user unplugged
    // it while we were in the middle of enumerating it.)
    if (FAILED(Joystick::hr)) {
        return DIENUM_CONTINUE;
    }

    // Stop enumeration. Note: we're just taking the first joystick we get. You
    // could store all the enumerated joysticks and let the user pick.
    return DIENUM_STOP;
}


HRESULT Joystick::setProperties()
{
    if (FAILED(Joystick::hr = Joystick::joystick->SetDataFormat(&c_dfDIJoystick2)))
    {
        return Joystick::hr;
    }

    // Set the cooperative level to let DInput know how this device should
    // interact with the system and with other DInput applications.
    if (FAILED(Joystick::hr = Joystick::joystick->SetCooperativeLevel(NULL, DISCL_EXCLUSIVE | DISCL_FOREGROUND)))
    {
        return Joystick::hr;
    }

    // Determine how many axis the joystick has (so we don't error out setting
    // properties for unavailable axis)
    Joystick::capabilities.dwSize = sizeof(DIDEVCAPS);
    if (FAILED(Joystick::hr = Joystick::joystick->GetCapabilities(&Joystick::capabilities)))
    {
        return Joystick::hr;
    }
    return E_FAIL;
}


HRESULT Joystick::enumAxes()
{
    if (FAILED(Joystick::hr = Joystick::joystick->EnumObjects(Joystick::enumAxesCallback, NULL, DIDFT_AXIS)))
    {
        return Joystick::hr;
    }
    else
        return Joystick::hr;
}


BOOL CALLBACK Joystick::enumAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, VOID* context)
{
    HWND hDlg = (HWND)context;

    DIPROPRANGE propRange;
    propRange.diph.dwSize = sizeof(DIPROPRANGE);
    propRange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
    propRange.diph.dwHow = DIPH_BYID;
    propRange.diph.dwObj = instance->dwType;
    propRange.lMin = -1000;
    propRange.lMax = +1000;

    // Set the range for the axis
    if (FAILED(Joystick::joystick->SetProperty(DIPROP_RANGE, &propRange.diph)))
    {
        return DIENUM_STOP;
    }

    return DIENUM_CONTINUE;
}

HRESULT Joystick::PollData(DIJOYSTATE2 *js)
{

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


    // Poll the device to read the current state
    Joystick::hr = Joystick::joystick->Poll();
    if (FAILED(Joystick::hr))
    {
        // DInput is telling us that the input stream has been
        // interrupted. We aren't tracking any state between polls, so
        // we don't have any special reset that needs to be done. We
        // just re-acquire and try again.
        Joystick::hr = Joystick::joystick->Acquire();
        while (Joystick::hr == DIERR_INPUTLOST)
        {
            Joystick::hr = Joystick::joystick->Acquire();
        }

        // If we encounter a fatal error, return failure.
        if ((Joystick::hr == DIERR_INVALIDPARAM) || (Joystick::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 (Joystick::hr == DIERR_OTHERAPPHASPRIO)
        {
            return S_OK;
        }
    }

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

    return S_OK;
}


void Joystick::disConnectJoystick()
{
    if (Joystick::joystick)
    {
        Joystick::joystick->Unacquire();
    }
}
Asesh
  • 3,186
  • 2
  • 21
  • 31
Sam Gomari
  • 733
  • 4
  • 13
  • 37
  • 2
    What happens when calls in `Joystick::initialize` fail? You returning a failure code whether the DI8Create call fails or succeeds, and ignoring it in any case. If there isn't a joystick, is `Joystick::di` dereferencable? Have you identified what line the program crashes on? – 1201ProgramAlarm Aug 05 '20 at 23:14
  • 1
    @1201ProgramAlarm it might be the line where it says if (Joystick::joystick == NULL) { printf("Joystick not found.\n"); return E_FAIL; } but I maybe wrong, but I removed the return E_FAIL, it made no difference. The code is being used in an Sfunction in Simulink, I do initialize in mdlStart() and in mdlOutput(), the latter of which get executed every time step, I am not sure if I should do initialize only one time. The reason I execute in mdlOutput() everytime step, is if someone plugged the joystick during simulation, but this way might be wrong. – Sam Gomari Aug 06 '20 at 00:26
  • 1
    @1201ProgramAlarm I am not sure what you were getting at ? Could you elaborate ? – Sam Gomari Aug 10 '20 at 16:54
  • 1
    `selectJoystick` can leave `Joystick::joystick` NULL if the enumeration fails. You ignore the return value from `selectJoystick`, and call `setProperties` whether you have a joystick or not. If you don't have a joystick that function can result in a crash in the first line. – 1201ProgramAlarm Aug 10 '20 at 23:09
  • 1
    so how do I solve it, I shouldn't call the setProperties unless selectJoystick is not NULL ? – Sam Gomari Aug 11 '20 at 05:17
  • @1201ProgramAlarm how do you solve it ? – Sam Gomari Oct 12 '20 at 19:35
  • You solve it by paying attention to the returned error codes, checking `Joystick::joystick` for a null value in appropriate places, then properly handling the situation when there is no joystick. – 1201ProgramAlarm Oct 14 '20 at 16:20

0 Answers0