comparison src/joystick/win32/SDL_dxjoystick.c @ 1895:c121d94672cb

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