DirectX's DirectInput component provides access to keyboards, mice, joysticks, and other human interface devices. Here, I'll focus on interfacing with joystick devices.
You'll need to include the DirectInput header file in order to use any of the DirectInput functions:
#include <dinput.h>
In order to link a final application binary, you'll need to include the following following libraries in your project:
dinput8.lib dxguid.lib
Before you can access the joystick, you must first initialize the DirectInput library:
LPDIRECTINPUT8 di; HRESULT hr; // Create a DirectInput device if (FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&di, NULL))) { return hr; }
DirectInput uses the EnumDevices() method to iterate through all of the available input devices on the system. In our case, we'll use the function callback method:
LPDIRECTINPUTDEVICE8 joystick; // 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; }
Here's the code for enumCallback():
BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE* instance, VOID* context) { HRESULT hr; // Obtain an interface to the enumerated joystick. hr = di->CreateDevice(instance->guidInstance, &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(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; }
Now that we have a joystick, we can set its properties and query its capabilities.
DIDEVCAPS capabilities; // Set the data format to "simple joystick" - a predefined data format // // A data format specifies which controls on a device we are interested in, // and how they should be reported. This tells DInput that we will be // passing a DIJOYSTATE2 structure to IDirectInputDevice::GetDeviceState(). if (FAILED(hr = joystick->SetDataFormat(&c_dfDIJoystick2))) { return hr; } // Set the cooperative level to let DInput know how this device should // interact with the system and with other DInput applications. if (FAILED(hr = joystick->SetCooperativeLevel(NULL, DISCL_EXCLUSIVE | DISCL_FOREGROUND))) { return hr; } // Determine how many axis the joystick has (so we don't error out setting // properties for unavailable axis) capabilities.dwSize = sizeof(DIDEVCAPS); if (FAILED(hr = joystick->GetCapabilities(&capabilities))) { return hr; }
The EnumObjects() function also requires a callback:
// Enumerate the axes of the joyctick and set the range of each axis. Note: // we could just use the defaults, but we're just trying to show an example // of enumerating device objects (axes, buttons, etc.). if (FAILED(hr = joystick->EnumObjects(enumAxesCallback, NULL, DIDFT_AXIS))) { return hr; } BOOL CALLBACK 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->SetProperty(DIPROP_RANGE, &propRange.diph))) { return DIENUM_STOP; } return DIENUM_CONTINUE; }
In order to get information on the joystick's current position and the state of its buttons, you must poll it. Here's an example of a poll function:
HRESULT poll(DIJOYSTATE2 *js) { HRESULT hr; if (joystick == NULL) { return S_OK; } // Poll the device to read the current state hr = joystick->Poll(); if (FAILED(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. hr = joystick->Acquire(); while (hr == DIERR_INPUTLOST) { hr = joystick->Acquire(); } // If we encounter a fatal error, return failure. 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; }
When you're done using the joystick, you should close the device.
if (joystick) { joystick->Unacquire(); }
There is some good information of supporting multiple joysticks in the MSDN article entitled Microsoft DirectInput and Developing for Microsoft SideWinder Digital Game Controllers. It's an older article, but the information is sound.
Jon Parise <jparise@cmu.edu> | Last modified: Monday, 24-Sep-2001 13:21:07 EDT |