comparison src/joystick/win32/SDL_dxjoystick.c @ 1661:281d3f4870e5 SDL-1.3

Moved DirectInput joystick code to 1.3 branch
author Sam Lantinga <slouken@libsdl.org>
date Sun, 21 May 2006 17:27:13 +0000
parents
children 782fd950bd46
comparison
equal deleted inserted replaced
1660:8b9d79e7eacf 1661:281d3f4870e5
1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2006 Sam Lantinga
4
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.
9
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.
14
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
18
19 Sam Lantinga
20 slouken@libsdl.org
21 */
22 #include "SDL_config.h"
23
24 #ifdef SDL_JOYSTICK_DINPUT
25
26 /* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de
27 * A. Formiga's WINMM driver.
28 *
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.
31 *
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. */
35
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"
41
42 #define WIN32_LEAN_AND_MEAN
43 #include <windows.h>
44
45 #define DIRECTINPUT_VERSION 0x0500
46 #include <dinput.h>
47
48 #define INPUT_QSIZE 32 /* Buffer up to 32 input messages */
49
50 extern HINSTANCE SDL_Instance;
51 extern int DX5_Load();
52 extern void DX5_Unload();
53 extern HRESULT (WINAPI *DInputCreate)(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUT *ppDI, LPUNKNOWN punkOuter);
54
55 static LPDIRECTINPUT dinput = NULL;
56
57 #define MAX_JOYSTICKS 8
58 #define MAX_INPUTS 256 /* each joystick can have up to 256 inputs */
59 #define AXIS_MIN -32768 /* minimum value for axis coordinate */
60 #define AXIS_MAX 32767 /* maximum value for axis coordinate */
61 #define JOY_AXIS_THRESHOLD (((AXIS_MAX)-(AXIS_MIN))/100) /* 1% motion */
62
63 typedef enum Type { BUTTON, AXIS, HAT } Type;
64
65 /* array to hold joystick ID values */
66 static DIDEVICEINSTANCE SYS_Joystick[MAX_JOYSTICKS];
67 static int SYS_NumJoysticks;
68
69 extern HWND SDL_Window;
70
71 typedef struct input_t
72 {
73 /* DirectInput offset for this input type: */
74 DWORD ofs;
75
76 /* Button, axis or hat: */
77 Type type;
78
79 /* SDL input offset: */
80 Uint8 num;
81 } input_t;
82
83 /* The private structure used to keep track of a joystick */
84 struct joystick_hwdata
85 {
86 LPDIRECTINPUTDEVICE2 InputDevice;
87 int buffered;
88
89 input_t Inputs[MAX_INPUTS];
90 int NumInputs;
91 };
92
93 /* Convert a DirectInput return code to a text message */
94 static void SetDIerror(char *function, int code)
95 {
96 static char *error;
97 static char errbuf[1024];
98
99 errbuf[0] = 0;
100 switch (code) {
101 case DIERR_GENERIC:
102 error = "Undefined error!";
103 break;
104 case DIERR_OLDDIRECTINPUTVERSION:
105 error = "Your version of DirectInput needs upgrading";
106 break;
107 case DIERR_INVALIDPARAM:
108 error = "Invalid parameters";
109 break;
110 case DIERR_OUTOFMEMORY:
111 error = "Out of memory";
112 break;
113 case DIERR_DEVICENOTREG:
114 error = "Device not registered";
115 break;
116 case DIERR_NOINTERFACE:
117 error = "Interface not supported";
118 break;
119 case DIERR_NOTINITIALIZED:
120 error = "Device not initialized";
121 break;
122 default:
123 sprintf(errbuf, "%s: Unknown DirectInput error: 0x%x",
124 function, code);
125 break;
126 }
127 if ( ! errbuf[0] ) {
128 sprintf(errbuf, "%s: %s", function, error);
129 }
130 SDL_SetError("%s", errbuf);
131 return;
132 }
133
134
135 BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance,
136 VOID* pContext )
137 {
138 memcpy(&SYS_Joystick[SYS_NumJoysticks], pdidInstance, sizeof(DIDEVICEINSTANCE));
139 SYS_NumJoysticks++;
140
141 if( SYS_NumJoysticks >= MAX_JOYSTICKS )
142 return DIENUM_STOP;
143
144 return DIENUM_CONTINUE;
145 }
146
147 static BOOL CALLBACK DIJoystick_EnumDevObjectsProc(LPCDIDEVICEOBJECTINSTANCE dev,
148 LPVOID pvRef)
149 {
150 SDL_Joystick *joystick = (SDL_Joystick*)pvRef;
151 HRESULT result;
152 input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs];
153 const int SupportedMask = DIDFT_BUTTON | DIDFT_POV | DIDFT_AXIS;
154 if(!(dev->dwType & SupportedMask))
155 return DIENUM_CONTINUE; /* unsupported */
156
157 in->ofs = dev->dwOfs;
158
159 if(dev->dwType & DIDFT_BUTTON) {
160 in->type = BUTTON;
161 in->num = joystick->nbuttons;
162 joystick->nbuttons++;
163 } else if(dev->dwType & DIDFT_POV) {
164 in->type = HAT;
165 in->num = joystick->nhats;
166 joystick->nhats++;
167 } else { /* dev->dwType & DIDFT_AXIS */
168 DIPROPRANGE diprg;
169 DIPROPDWORD dilong;
170
171 in->type = AXIS;
172 in->num = joystick->naxes;
173
174 diprg.diph.dwSize = sizeof(diprg);
175 diprg.diph.dwHeaderSize = sizeof(diprg.diph);
176 diprg.diph.dwObj = dev->dwOfs;
177 diprg.diph.dwHow = DIPH_BYOFFSET;
178 diprg.lMin = AXIS_MIN;
179 diprg.lMax = AXIS_MAX;
180
181 result = IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice, DIPROP_RANGE, &diprg.diph);
182 if ( result != DI_OK )
183 return DIENUM_CONTINUE; /* don't use this axis */
184
185 /* Set dead zone to 0. */
186 dilong.diph.dwSize = sizeof(dilong);
187 dilong.diph.dwHeaderSize = sizeof(dilong.diph);
188 dilong.diph.dwObj = dev->dwOfs;
189 dilong.diph.dwHow = DIPH_BYOFFSET;
190 dilong.dwData = 0;
191 result = IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice, DIPROP_DEADZONE, &dilong.diph);
192 if ( result != DI_OK )
193 return DIENUM_CONTINUE; /* don't use this axis */
194
195 joystick->naxes++;
196 }
197
198 joystick->hwdata->NumInputs++;
199
200 if(joystick->hwdata->NumInputs == MAX_INPUTS)
201 return DIENUM_STOP; /* too many */
202
203 return DIENUM_CONTINUE;
204 }
205
206 /* Function to scan the system for joysticks.
207 * This function should set SDL_numjoysticks to the number of available
208 * joysticks. Joystick 0 should be the system default joystick.
209 * It should return 0, or -1 on an unrecoverable fatal error.
210 */
211 int SDL_SYS_JoystickInit(void)
212 {
213 HRESULT result;
214
215 SYS_NumJoysticks = 0;
216
217 /* Create the DirectInput object */
218 if ( DX5_Load() < 0 ) {
219 SDL_SetError("Couldn't load DirectInput");
220 return(-1);
221 }
222 result = DInputCreate(SDL_Instance, DIRECTINPUT_VERSION,
223 &dinput, NULL);
224 if ( result != DI_OK ) {
225 DX5_Unload();
226 SetDIerror("DirectInputCreate", result);
227 return(-1);
228 }
229
230 result = IDirectInput_EnumDevices(dinput,
231 DIDEVTYPE_JOYSTICK,
232 EnumJoysticksCallback,
233 NULL,
234 DIEDFL_ATTACHEDONLY );
235
236 return SYS_NumJoysticks;
237 }
238
239 /* Function to get the device-dependent name of a joystick */
240 const char *SDL_SYS_JoystickName(int index)
241 {
242 /***-> test for invalid index ? */
243 return(SYS_Joystick[index].tszProductName);
244 }
245
246 /* Function to open a joystick for use.
247 The joystick to open is specified by the index field of the joystick.
248 This should fill the nbuttons and naxes fields of the joystick structure.
249 It returns 0, or -1 if there is an error.
250 */
251 int SDL_SYS_JoystickOpen(SDL_Joystick *joystick)
252 {
253 HRESULT result;
254 LPDIRECTINPUTDEVICE device;
255
256 /* allocate memory for system specific hardware data */
257 joystick->hwdata = (struct joystick_hwdata *) malloc(sizeof(*joystick->hwdata));
258 if (joystick->hwdata == NULL)
259 {
260 SDL_OutOfMemory();
261 return(-1);
262 }
263 memset(joystick->hwdata, 0, sizeof(*joystick->hwdata));
264 joystick->hwdata->buffered = 1;
265
266 result = IDirectInput_CreateDevice(dinput, &SYS_Joystick[joystick->index].guidInstance,
267 &device, NULL);
268 if ( result != DI_OK ) {
269 SetDIerror("DirectInput::CreateDevice", result);
270 return(-1);
271 }
272
273 result = IDirectInputDevice_QueryInterface(device,
274 &IID_IDirectInputDevice2, (LPVOID *)&joystick->hwdata->InputDevice);
275 IDirectInputDevice_Release(device);
276 if ( result != DI_OK ) {
277 SetDIerror("DirectInputDevice::QueryInterface", result);
278 return(-1);
279 }
280
281 result = IDirectInputDevice2_SetCooperativeLevel(joystick->hwdata->InputDevice, SDL_Window,
282 DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
283 if ( result != DI_OK ) {
284 SetDIerror("DirectInputDevice::SetCooperativeLevel", result);
285 return(-1);
286 }
287
288 result = IDirectInputDevice2_SetDataFormat(joystick->hwdata->InputDevice, &c_dfDIJoystick);
289 if ( result != DI_OK ) {
290 SetDIerror("DirectInputDevice::SetDataFormat", result);
291 return(-1);
292 }
293
294 IDirectInputDevice2_EnumObjects(joystick->hwdata->InputDevice,
295 DIJoystick_EnumDevObjectsProc,
296 joystick,
297 DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);
298
299 {
300 DIPROPDWORD dipdw;
301 memset(&dipdw, 0, sizeof(dipdw));
302 dipdw.diph.dwSize = sizeof(dipdw);
303 dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
304 dipdw.diph.dwObj = 0;
305 dipdw.diph.dwHow = DIPH_DEVICE;
306 dipdw.dwData = INPUT_QSIZE;
307 result = IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice,
308 DIPROP_BUFFERSIZE, &dipdw.diph);
309
310 if ( result == DI_POLLEDDEVICE )
311 {
312 /* This device doesn't support buffering, so we're forced
313 * to use less reliable polling. */
314 joystick->hwdata->buffered = 0;
315 } else if ( result != DI_OK ) {
316 SetDIerror("DirectInputDevice::SetProperty", result);
317 return(-1);
318 }
319 }
320
321 return(0);
322 }
323
324 static Uint8 TranslatePOV(DWORD value)
325 {
326 const int HAT_VALS[] = {
327 SDL_HAT_UP,
328 SDL_HAT_UP | SDL_HAT_RIGHT,
329 SDL_HAT_RIGHT,
330 SDL_HAT_DOWN | SDL_HAT_RIGHT,
331 SDL_HAT_DOWN,
332 SDL_HAT_DOWN | SDL_HAT_LEFT,
333 SDL_HAT_LEFT,
334 SDL_HAT_UP | SDL_HAT_LEFT
335 };
336
337 if(LOWORD(value) == 0xFFFF)
338 return SDL_HAT_CENTERED;
339
340 /* Round the value up: */
341 value += 4500 / 2;
342 value %= 36000;
343 value /= 4500;
344
345 if(value >= 8)
346 return SDL_HAT_CENTERED; /* shouldn't happen */
347
348 return HAT_VALS[value];
349 }
350
351 /* SDL_PrivateJoystick* doesn't discard duplicate events, so we need to
352 * do it. */
353 static int SDL_PrivateJoystickAxis_Int(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
354 {
355 if(joystick->axes[axis] != value)
356 return SDL_PrivateJoystickAxis(joystick, axis, value);
357 return 0;
358 }
359
360 static int SDL_PrivateJoystickHat_Int(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
361 {
362 if(joystick->hats[hat] != value)
363 return SDL_PrivateJoystickHat(joystick, hat, value);
364 return 0;
365 }
366
367 static int SDL_PrivateJoystickButton_Int(SDL_Joystick *joystick, Uint8 button, Uint8 state)
368 {
369 if(joystick->buttons[button] != state)
370 return SDL_PrivateJoystickButton(joystick, button, state);
371 return 0;
372 }
373
374 /* Function to update the state of a joystick - called as a device poll.
375 * This function shouldn't update the joystick structure directly,
376 * but instead should call SDL_PrivateJoystick*() to deliver events
377 * and update joystick device state.
378 */
379 void SDL_SYS_JoystickUpdate_Polled(SDL_Joystick *joystick)
380 {
381 DIJOYSTATE state;
382 HRESULT result;
383 int i;
384
385 result = IDirectInputDevice2_GetDeviceState(joystick->hwdata->InputDevice, sizeof(state), &state);
386 if ( result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED ) {
387 IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice);
388 result = IDirectInputDevice2_GetDeviceState(joystick->hwdata->InputDevice, sizeof(state), &state);
389 }
390
391 /* Set each known axis, button and POV. */
392 for(i = 0; i < joystick->hwdata->NumInputs; ++i)
393 {
394 const input_t *in = &joystick->hwdata->Inputs[i];
395
396 switch(in->type)
397 {
398 case AXIS:
399 switch(in->ofs)
400 {
401 case DIJOFS_X: SDL_PrivateJoystickAxis_Int(joystick, in->num, (Sint16)state.lX); break;
402 case DIJOFS_Y: SDL_PrivateJoystickAxis_Int(joystick, in->num, (Sint16)state.lY); break;
403 case DIJOFS_Z: SDL_PrivateJoystickAxis_Int(joystick, in->num, (Sint16)state.lZ); break;
404 case DIJOFS_RX: SDL_PrivateJoystickAxis_Int(joystick, in->num, (Sint16)state.lRx); break;
405 case DIJOFS_RY: SDL_PrivateJoystickAxis_Int(joystick, in->num, (Sint16)state.lRy); break;
406 case DIJOFS_RZ: SDL_PrivateJoystickAxis_Int(joystick, in->num, (Sint16)state.lRz); break;
407 case DIJOFS_SLIDER(0): SDL_PrivateJoystickAxis_Int(joystick, in->num, (Sint16)state.rglSlider[0]); break;
408 case DIJOFS_SLIDER(1): SDL_PrivateJoystickAxis_Int(joystick, in->num, (Sint16)state.rglSlider[0]); break;
409 }
410
411 break;
412
413 case BUTTON:
414 SDL_PrivateJoystickButton_Int(joystick, in->num, (Uint8) (state.rgbButtons[in->ofs - DIJOFS_BUTTON0]?SDL_PRESSED:SDL_RELEASED));
415 break;
416 case HAT:
417 {
418 Uint8 pos = TranslatePOV(state.rgdwPOV[in->ofs - DIJOFS_POV(0)]);
419 SDL_PrivateJoystickHat_Int(joystick, in->num, pos);
420 break;
421 }
422 }
423 }
424 }
425
426 void SDL_SYS_JoystickUpdate_Buffered(SDL_Joystick *joystick)
427 {
428 int i;
429 HRESULT result;
430 DWORD numevents;
431 DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
432
433 numevents = INPUT_QSIZE;
434 result = IDirectInputDevice2_GetDeviceData(
435 joystick->hwdata->InputDevice, sizeof(DIDEVICEOBJECTDATA),
436 evtbuf, &numevents, 0);
437 if ( result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED ) {
438 IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice);
439 result = IDirectInputDevice2_GetDeviceData(
440 joystick->hwdata->InputDevice, sizeof(DIDEVICEOBJECTDATA),
441 evtbuf, &numevents, 0);
442 }
443
444 /* Handle the events */
445 if ( result != DI_OK )
446 return;
447
448 for(i = 0; i < (int) numevents; ++i)
449 {
450 int j;
451
452 for(j = 0; j < joystick->hwdata->NumInputs; ++j)
453 {
454 const input_t *in = &joystick->hwdata->Inputs[j];
455
456 if(evtbuf[i].dwOfs != in->ofs)
457 continue;
458
459 switch(in->type)
460 {
461 case AXIS:
462 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)evtbuf[i].dwData);
463 break;
464 case BUTTON:
465 SDL_PrivateJoystickButton(joystick, in->num, (Uint8) (evtbuf[i].dwData?SDL_PRESSED:SDL_RELEASED));
466 break;
467 case HAT:
468 {
469 Uint8 pos = TranslatePOV(evtbuf[i].dwData);
470 SDL_PrivateJoystickHat(joystick, in->num, pos);
471 }
472 }
473 }
474 }
475 }
476
477 void SDL_SYS_JoystickUpdate(SDL_Joystick *joystick)
478 {
479 HRESULT result;
480
481 result = IDirectInputDevice2_Poll(joystick->hwdata->InputDevice);
482 if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
483 IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice);
484 IDirectInputDevice2_Poll(joystick->hwdata->InputDevice);
485 }
486
487 if(joystick->hwdata->buffered)
488 SDL_SYS_JoystickUpdate_Buffered(joystick);
489 else
490 SDL_SYS_JoystickUpdate_Polled(joystick);
491 }
492
493 /* Function to close a joystick after use */
494 void SDL_SYS_JoystickClose(SDL_Joystick *joystick)
495 {
496 IDirectInputDevice2_Unacquire(joystick->hwdata->InputDevice);
497 IDirectInputDevice2_Release(joystick->hwdata->InputDevice);
498
499 if (joystick->hwdata != NULL) {
500 /* free system specific hardware data */
501 free(joystick->hwdata);
502 }
503 }
504
505 /* Function to perform any system-specific joystick related cleanup */
506 void SDL_SYS_JoystickQuit(void)
507 {
508 IDirectInput_Release(dinput);
509 dinput = NULL;
510 DX5_Unload();
511 }
512
513 #endif /* SDL_JOYSTICK_DINPUT */