Added the concept of the HelperWindow to help with DirectInput.
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2006 Sam Lantinga
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "SDL_config.h"
24 #ifdef SDL_JOYSTICK_DINPUT
26 /* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de
27 * A. Formiga's WINMM driver.
29 * Hats and sliders are completely untested; the app I'm writing this for mostly
30 * doesn't use them and I don't own any joysticks with them.
32 * We don't bother to use event notification here. It doesn't seem to work
33 * with polled devices, and it's fine to call IDirectInputDevice2_GetDeviceData and
34 * let it return 0 events. */
36 #include "SDL_error.h"
37 #include "SDL_events.h"
38 #include "SDL_joystick.h"
39 #include "../SDL_sysjoystick.h"
40 #include "../SDL_joystick_c.h"
42 #define WIN32_LEAN_AND_MEAN
45 #define DIRECTINPUT_VERSION 0x0500
48 /* Used for the c_dfDIJoystick2 symbol (no imports are used) */
49 # pragma comment (lib, "dinput.lib")
53 # pragma comment (lib, "dxerr.lib")
56 /* an ISO hack for VisualC++ */
58 #define snprintf _snprintf
61 #define INPUT_QSIZE 32 /* Buffer up to 32 input messages */
62 #define MAX_JOYSTICKS 8
63 #define MAX_INPUTS 256 /* each joystick can have up to 256 inputs */
64 #define AXIS_MIN -32768 /* minimum value for axis coordinate */
65 #define AXIS_MAX 32767 /* maximum value for axis coordinate */
66 #define JOY_AXIS_THRESHOLD (((AXIS_MAX)-(AXIS_MIN))/100) /* 1% motion */
68 /* external variables referenced. */
69 extern HINSTANCE SDL_Instance;
70 extern HWND SDL_HelperWindow;
74 static LPDIRECTINPUT dinput = NULL;
75 extern HRESULT(WINAPI * DInputCreate) (HINSTANCE hinst, DWORD dwVersion,
78 static DIDEVICEINSTANCE SYS_Joystick[MAX_JOYSTICKS]; /* array to hold joystick ID values */
79 static int SYS_NumJoysticks;
80 static HINSTANCE DInputDLL = NULL;
83 /* local prototypes */
84 static void SetDIerror(const char *function, HRESULT code);
85 static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE *
86 pdidInstance, VOID * pContext);
87 static BOOL CALLBACK EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev,
89 static Uint8 TranslatePOV(DWORD value);
90 static int SDL_PrivateJoystickAxis_Int(SDL_Joystick * joystick, Uint8 axis,
92 static int SDL_PrivateJoystickHat_Int(SDL_Joystick * joystick, Uint8 hat,
94 static int SDL_PrivateJoystickButton_Int(SDL_Joystick * joystick,
95 Uint8 button, Uint8 state);
100 { BUTTON, AXIS, HAT } Type;
102 typedef struct input_t
104 /* DirectInput offset for this input type: */
107 /* Button, axis or hat: */
110 /* SDL input offset: */
114 /* The private structure used to keep track of a joystick */
115 struct joystick_hwdata
117 LPDIRECTINPUTDEVICE2 InputDevice;
118 DIDEVCAPS Capabilities;
121 input_t Inputs[MAX_INPUTS];
125 /* Convert a DirectInput return code to a text message */
127 SetDIerror(const char *function, HRESULT code)
129 SDL_SetError("%s() [%s]: %s", function,
130 DXGetErrorString(code), DXGetErrorDescription(code));
134 /* Function to scan the system for joysticks.
135 * This function should set SDL_numjoysticks to the number of available
136 * joysticks. Joystick 0 should be the system default joystick.
137 * It should return 0, or -1 on an unrecoverable fatal error.
140 SDL_SYS_JoystickInit(void)
144 SYS_NumJoysticks = 0;
146 result = CoInitialize(NULL);
147 if (FAILED(result)) {
148 SetDIerror("CoInitialize", result);
152 result = CoCreateInstance(&CLSID_DirectInput, NULL, CLSCTX_INPROC_SERVER,
153 &IID_IDirectInput, &dinput);
155 if (FAILED(result)) {
156 SetDIerror("CoCreateInstance", result);
160 /* Because we used CoCreateInstance, we need to Initialize it, first. */
162 IDirectInput_Initialize(dinput, SDL_Instance, DIRECTINPUT_VERSION);
164 if (FAILED(result)) {
165 SetDIerror("IDirectInput::Initialize", result);
169 /* Look for joysticks, wheels, head trackers, gamepads, etc.. */
170 result = IDirectInput_EnumDevices(dinput,
172 EnumJoysticksCallback,
173 NULL, DIEDFL_ATTACHEDONLY);
175 return SYS_NumJoysticks;
179 EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
181 SDL_memcpy(&SYS_Joystick[SYS_NumJoysticks], pdidInstance,
182 sizeof(DIDEVICEINSTANCE));
185 if (SYS_NumJoysticks >= MAX_JOYSTICKS)
188 return DIENUM_CONTINUE;
191 /* Function to get the device-dependent name of a joystick */
193 SDL_SYS_JoystickName(int index)
195 /***-> test for invalid index ? */
196 return (SYS_Joystick[index].tszProductName);
199 /* Function to open a joystick for use.
200 The joystick to open is specified by the index field of the joystick.
201 This should fill the nbuttons and naxes fields of the joystick structure.
202 It returns 0, or -1 if there is an error.
205 SDL_SYS_JoystickOpen(SDL_Joystick * joystick)
208 LPDIRECTINPUTDEVICE device;
211 SDL_memset(&dipdw, 0, sizeof(DIPROPDWORD));
212 dipdw.diph.dwSize = sizeof(DIPROPDWORD);
213 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
216 /* allocate memory for system specific hardware data */
218 (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
219 if (joystick->hwdata == NULL) {
223 SDL_memset(joystick->hwdata, 0, sizeof(struct joystick_hwdata));
224 joystick->hwdata->buffered = 1;
225 joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS);
228 IDirectInput_CreateDevice(dinput,
229 &SYS_Joystick[joystick->index].
230 guidInstance, &device, NULL);
231 if (FAILED(result)) {
232 SetDIerror("IDirectInput::CreateDevice", result);
236 /* Now get the IDirectInputDevice2 interface, instead. */
237 result = IDirectInputDevice_QueryInterface(device,
238 &IID_IDirectInputDevice2,
239 (LPVOID *) & joystick->
240 hwdata->InputDevice);
241 /* We are done with this object. Use the stored one from now on. */
242 IDirectInputDevice_Release(device);
244 if (FAILED(result)) {
245 SetDIerror("IDirectInputDevice::QueryInterface", result);
249 /* Aquire shared access. Exclusive access is required for forces,
252 IDirectInputDevice2_SetCooperativeLevel(joystick->hwdata->
253 InputDevice, SDL_HelperWindow,
256 if (FAILED(result)) {
257 SetDIerror("IDirectInputDevice2::SetCooperativeLevel", result);
261 /* Use the extended data structure: DIJOYSTATE2. */
263 IDirectInputDevice2_SetDataFormat(joystick->hwdata->InputDevice,
265 if (FAILED(result)) {
266 SetDIerror("IDirectInputDevice2::SetDataFormat", result);
270 /* Get device capabilities */
272 IDirectInputDevice2_GetCapabilities(joystick->hwdata->InputDevice,
273 &joystick->hwdata->Capabilities);
275 if (FAILED(result)) {
276 SetDIerror("IDirectInputDevice2::GetCapabilities", result);
281 if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
283 result = IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice);
285 if (FAILED(result)) {
286 SetDIerror("IDirectInputDevice2::Acquire", result);
290 /* reset all accuators. */
292 IDirectInputDevice2_SendForceFeedbackCommand(joystick->hwdata->
296 if (FAILED(result)) {
297 SetDIerror("IDirectInputDevice2::SendForceFeedbackCommand",
302 result = IDirectInputDevice2_Unacquire(joystick->hwdata->InputDevice);
304 if (FAILED(result)) {
305 SetDIerror("IDirectInputDevice2::Unacquire", result);
309 /* Turn on auto-centering for a ForceFeedback device (until told
311 dipdw.diph.dwObj = 0;
312 dipdw.diph.dwHow = DIPH_DEVICE;
313 dipdw.dwData = DIPROPAUTOCENTER_ON;
316 IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice,
317 DIPROP_AUTOCENTER, &dipdw.diph);
319 if (FAILED(result)) {
320 SetDIerror("IDirectInputDevice2::SetProperty", result);
325 /* What buttons and axes does it have? */
326 IDirectInputDevice2_EnumObjects(joystick->hwdata->InputDevice,
327 EnumDevObjectsCallback, joystick,
328 DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);
330 dipdw.diph.dwObj = 0;
331 dipdw.diph.dwHow = DIPH_DEVICE;
332 dipdw.dwData = INPUT_QSIZE;
334 /* Set the buffer size */
336 IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice,
337 DIPROP_BUFFERSIZE, &dipdw.diph);
339 if (result == DI_POLLEDDEVICE) {
340 /* This device doesn't support buffering, so we're forced
341 * to use less reliable polling. */
342 joystick->hwdata->buffered = 0;
343 } else if (FAILED(result)) {
344 SetDIerror("IDirectInputDevice2::SetProperty", result);
352 EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
354 SDL_Joystick *joystick = (SDL_Joystick *) pvRef;
356 input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs];
358 in->ofs = dev->dwOfs;
360 if (dev->dwType & DIDFT_BUTTON) {
362 in->num = joystick->nbuttons;
363 joystick->nbuttons++;
364 } else if (dev->dwType & DIDFT_POV) {
366 in->num = joystick->nhats;
368 } else if (dev->dwType & DIDFT_AXIS) {
373 in->num = joystick->naxes;
375 diprg.diph.dwSize = sizeof(diprg);
376 diprg.diph.dwHeaderSize = sizeof(diprg.diph);
377 diprg.diph.dwObj = dev->dwOfs;
378 diprg.diph.dwHow = DIPH_BYOFFSET;
379 diprg.lMin = AXIS_MIN;
380 diprg.lMax = AXIS_MAX;
383 IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice,
384 DIPROP_RANGE, &diprg.diph);
385 if (FAILED(result)) {
386 return DIENUM_CONTINUE; /* don't use this axis */
389 /* Set dead zone to 0. */
390 dilong.diph.dwSize = sizeof(dilong);
391 dilong.diph.dwHeaderSize = sizeof(dilong.diph);
392 dilong.diph.dwObj = dev->dwOfs;
393 dilong.diph.dwHow = DIPH_BYOFFSET;
396 IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice,
397 DIPROP_DEADZONE, &dilong.diph);
398 if (FAILED(result)) {
399 return DIENUM_CONTINUE; /* don't use this axis */
404 /* not supported at this time */
405 return DIENUM_CONTINUE;
408 joystick->hwdata->NumInputs++;
410 if (joystick->hwdata->NumInputs == MAX_INPUTS) {
411 return DIENUM_STOP; /* too many */
414 return DIENUM_CONTINUE;
417 /* Function to update the state of a joystick - called as a device poll.
418 * This function shouldn't update the joystick structure directly,
419 * but instead should call SDL_PrivateJoystick*() to deliver events
420 * and update joystick device state.
423 SDL_SYS_JoystickUpdate_Polled(SDL_Joystick * joystick)
430 IDirectInputDevice2_GetDeviceState(joystick->hwdata->InputDevice,
431 sizeof(DIJOYSTATE2), &state);
432 if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
433 IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice);
435 IDirectInputDevice2_GetDeviceState(joystick->hwdata->InputDevice,
436 sizeof(DIJOYSTATE2), &state);
439 /* Set each known axis, button and POV. */
440 for (i = 0; i < joystick->hwdata->NumInputs; ++i) {
441 const input_t *in = &joystick->hwdata->Inputs[i];
447 SDL_PrivateJoystickAxis_Int(joystick, in->num,
451 SDL_PrivateJoystickAxis_Int(joystick, in->num,
455 SDL_PrivateJoystickAxis_Int(joystick, in->num,
459 SDL_PrivateJoystickAxis_Int(joystick, in->num,
463 SDL_PrivateJoystickAxis_Int(joystick, in->num,
467 SDL_PrivateJoystickAxis_Int(joystick, in->num,
470 case DIJOFS_SLIDER(0):
471 SDL_PrivateJoystickAxis_Int(joystick, in->num,
472 (Sint16) state.rglSlider[0]);
474 case DIJOFS_SLIDER(1):
475 SDL_PrivateJoystickAxis_Int(joystick, in->num,
476 (Sint16) state.rglSlider[1]);
483 SDL_PrivateJoystickButton_Int(joystick, in->num,
492 Uint8 pos = TranslatePOV(state.rgdwPOV[in->ofs -
494 SDL_PrivateJoystickHat_Int(joystick, in->num, pos);
502 SDL_SYS_JoystickUpdate_Buffered(SDL_Joystick * joystick)
507 DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
509 numevents = INPUT_QSIZE;
511 IDirectInputDevice2_GetDeviceData(joystick->hwdata->InputDevice,
512 sizeof(DIDEVICEOBJECTDATA), evtbuf,
514 if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
515 IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice);
517 IDirectInputDevice2_GetDeviceData(joystick->hwdata->InputDevice,
518 sizeof(DIDEVICEOBJECTDATA),
519 evtbuf, &numevents, 0);
522 /* Handle the events or punt */
526 for (i = 0; i < (int) numevents; ++i) {
529 for (j = 0; j < joystick->hwdata->NumInputs; ++j) {
530 const input_t *in = &joystick->hwdata->Inputs[j];
532 if (evtbuf[i].dwOfs != in->ofs)
537 SDL_PrivateJoystickAxis(joystick, in->num,
538 (Sint16) evtbuf[i].dwData);
541 SDL_PrivateJoystickButton(joystick, in->num,
543 dwData ? SDL_PRESSED :
548 Uint8 pos = TranslatePOV(evtbuf[i].dwData);
549 SDL_PrivateJoystickHat(joystick, in->num, pos);
558 TranslatePOV(DWORD value)
560 const int HAT_VALS[] = {
562 SDL_HAT_UP | SDL_HAT_RIGHT,
564 SDL_HAT_DOWN | SDL_HAT_RIGHT,
566 SDL_HAT_DOWN | SDL_HAT_LEFT,
568 SDL_HAT_UP | SDL_HAT_LEFT
571 if (LOWORD(value) == 0xFFFF)
572 return SDL_HAT_CENTERED;
574 /* Round the value up: */
580 return SDL_HAT_CENTERED; /* shouldn't happen */
582 return HAT_VALS[value];
585 /* SDL_PrivateJoystick* doesn't discard duplicate events, so we need to
588 SDL_PrivateJoystickAxis_Int(SDL_Joystick * joystick, Uint8 axis, Sint16 value)
590 if (joystick->axes[axis] != value)
591 return SDL_PrivateJoystickAxis(joystick, axis, value);
596 SDL_PrivateJoystickHat_Int(SDL_Joystick * joystick, Uint8 hat, Uint8 value)
598 if (joystick->hats[hat] != value)
599 return SDL_PrivateJoystickHat(joystick, hat, value);
604 SDL_PrivateJoystickButton_Int(SDL_Joystick * joystick, Uint8 button,
607 if (joystick->buttons[button] != state)
608 return SDL_PrivateJoystickButton(joystick, button, state);
613 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
617 result = IDirectInputDevice2_Poll(joystick->hwdata->InputDevice);
618 if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
619 IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice);
620 IDirectInputDevice2_Poll(joystick->hwdata->InputDevice);
623 if (joystick->hwdata->buffered)
624 SDL_SYS_JoystickUpdate_Buffered(joystick);
626 SDL_SYS_JoystickUpdate_Polled(joystick);
629 /* Function to close a joystick after use */
631 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
633 IDirectInputDevice2_Unacquire(joystick->hwdata->InputDevice);
634 IDirectInputDevice2_Release(joystick->hwdata->InputDevice);
636 if (joystick->hwdata != NULL) {
637 /* free system specific hardware data */
638 SDL_free(joystick->hwdata);
642 /* Function to perform any system-specific joystick related cleanup */
644 SDL_SYS_JoystickQuit(void)
646 IDirectInput_Release(dinput);
650 #endif /* SDL_JOYSTICK_DINPUT */
651 /* vi: set ts=4 sw=4 expandtab: */