comparison src/haptic/windows/SDL_syshaptic.c @ 5062:e8916fe9cfc8

Fixed bug #925 Changed "win32" to "windows"
author Sam Lantinga <slouken@libsdl.org>
date Thu, 20 Jan 2011 18:04:05 -0800
parents src/haptic/win32/SDL_syshaptic.c@8cc00819c8d6
children 327f181542f1
comparison
equal deleted inserted replaced
5061:9e9940eae455 5062:e8916fe9cfc8
1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 2008 Edgar Simo
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_HAPTIC_DINPUT
25
26 #include "SDL_haptic.h"
27 #include "../SDL_syshaptic.h"
28 #include "SDL_joystick.h"
29 #include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */
30 #include "../../joystick/windows/SDL_dxjoystick_c.h" /* For joystick hwdata */
31
32
33 #define MAX_HAPTICS 32
34
35
36 /*
37 * List of available haptic devices.
38 */
39 static struct
40 {
41 DIDEVICEINSTANCE instance;
42 SDL_Haptic *haptic;
43 DIDEVCAPS capabilities;
44 } SDL_hapticlist[MAX_HAPTICS];
45
46
47 /*
48 * Haptic system hardware data.
49 */
50 struct haptic_hwdata
51 {
52 LPDIRECTINPUTDEVICE2 device;
53 DWORD axes[3]; /* Axes to use. */
54 int is_joystick; /* Device is loaded as joystick. */
55 };
56
57
58 /*
59 * Haptic system effect data.
60 */
61 struct haptic_hweffect
62 {
63 DIEFFECT effect;
64 LPDIRECTINPUTEFFECT ref;
65 };
66
67
68 /*
69 * Internal stuff.
70 */
71 static LPDIRECTINPUT dinput = NULL;
72
73
74 /*
75 * External stuff.
76 */
77 extern HWND SDL_HelperWindow;
78
79
80 /*
81 * Prototypes.
82 */
83 static void DI_SetError(const char *str, HRESULT err);
84 static int DI_GUIDIsSame(const GUID * a, const GUID * b);
85 static int SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic,
86 DIDEVICEINSTANCE instance);
87 static int SDL_SYS_HapticOpenFromDevice2(SDL_Haptic * haptic,
88 LPDIRECTINPUTDEVICE2 device2);
89 static DWORD DIGetTriggerButton(Uint16 button);
90 static int SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir,
91 int naxes);
92 static int SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
93 SDL_HapticEffect * src);
94 static void SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type);
95 static REFGUID SDL_SYS_HapticEffectType(SDL_HapticEffect * effect);
96 /* Callbacks. */
97 static BOOL CALLBACK EnumHapticsCallback(const DIDEVICEINSTANCE *
98 pdidInstance, VOID * pContext);
99 static BOOL CALLBACK DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv);
100
101
102 /*
103 * Like SDL_SetError but for DX error codes.
104 */
105 static void
106 DI_SetError(const char *str, HRESULT err)
107 {
108 /*
109 SDL_SetError("Haptic: %s - %s: %s", str,
110 DXGetErrorString8A(err), DXGetErrorDescription8A(err));
111 */
112 SDL_SetError("Haptic error %s", str);
113 }
114
115
116 /*
117 * Checks to see if two GUID are the same.
118 */
119 static int
120 DI_GUIDIsSame(const GUID * a, const GUID * b)
121 {
122 if (((a)->Data1 == (b)->Data1) &&
123 ((a)->Data2 == (b)->Data2) &&
124 ((a)->Data3 == (b)->Data3) &&
125 (SDL_strcmp((a)->Data4, (b)->Data4) == 0))
126 return 1;
127 return 0;
128 }
129
130
131 /*
132 * Initializes the haptic subsystem.
133 */
134 int
135 SDL_SYS_HapticInit(void)
136 {
137 HRESULT ret;
138 HINSTANCE instance;
139
140 if (dinput != NULL) { /* Already open. */
141 SDL_SetError("Haptic: SubSystem already open.");
142 return -1;
143 }
144
145 /* Clear all the memory. */
146 SDL_memset(SDL_hapticlist, 0, sizeof(SDL_hapticlist));
147
148 SDL_numhaptics = 0;
149
150 ret = CoInitialize(NULL);
151 if (FAILED(ret)) {
152 DI_SetError("Coinitialize", ret);
153 return -1;
154 }
155
156 ret = CoCreateInstance(&CLSID_DirectInput, NULL, CLSCTX_INPROC_SERVER,
157 &IID_IDirectInput, (LPVOID) & dinput);
158 if (FAILED(ret)) {
159 DI_SetError("CoCreateInstance", ret);
160 return -1;
161 }
162
163 /* Because we used CoCreateInstance, we need to Initialize it, first. */
164 instance = GetModuleHandle(NULL);
165 if (instance == NULL) {
166 SDL_SetError("GetModuleHandle() failed with error code %d.",
167 GetLastError());
168 return -1;
169 }
170 ret = IDirectInput_Initialize(dinput, instance, DIRECTINPUT_VERSION);
171 if (FAILED(ret)) {
172 DI_SetError("Initializing DirectInput device", ret);
173 return -1;
174 }
175
176 /* Look for haptic devices. */
177 ret = IDirectInput_EnumDevices(dinput,
178 0,
179 EnumHapticsCallback,
180 NULL,
181 DIEDFL_FORCEFEEDBACK |
182 DIEDFL_ATTACHEDONLY);
183 if (FAILED(ret)) {
184 DI_SetError("Enumerating DirectInput devices", ret);
185 return -1;
186 }
187
188 return SDL_numhaptics;
189 }
190
191 /*
192 * Callback to find the haptic devices.
193 */
194 static BOOL CALLBACK
195 EnumHapticsCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
196 {
197 HRESULT ret;
198 LPDIRECTINPUTDEVICE device;
199
200 /* Copy the instance over, useful for creating devices. */
201 SDL_memcpy(&SDL_hapticlist[SDL_numhaptics].instance, pdidInstance,
202 sizeof(DIDEVICEINSTANCE));
203
204 /* Open the device */
205 ret = IDirectInput_CreateDevice(dinput, &pdidInstance->guidInstance,
206 &device, NULL);
207 if (FAILED(ret)) {
208 /* DI_SetError("Creating DirectInput device",ret); */
209 return DIENUM_CONTINUE;
210 }
211
212 /* Get capabilities. */
213 SDL_hapticlist[SDL_numhaptics].capabilities.dwSize = sizeof(DIDEVCAPS);
214 ret = IDirectInputDevice_GetCapabilities(device,
215 &SDL_hapticlist[SDL_numhaptics].
216 capabilities);
217 if (FAILED(ret)) {
218 /* DI_SetError("Getting device capabilities",ret); */
219 IDirectInputDevice_Release(device);
220 return DIENUM_CONTINUE;
221 }
222
223 /* Close up device and count it. */
224 IDirectInputDevice_Release(device);
225 SDL_numhaptics++;
226
227 /* Watch out for hard limit. */
228 if (SDL_numhaptics >= MAX_HAPTICS)
229 return DIENUM_STOP;
230
231 return DIENUM_CONTINUE;
232 }
233
234
235 /*
236 * Return the name of a haptic device, does not need to be opened.
237 */
238 const char *
239 SDL_SYS_HapticName(int index)
240 {
241 return SDL_hapticlist[index].instance.tszProductName;
242 }
243
244
245 /*
246 * Callback to get all supported effects.
247 */
248 #define EFFECT_TEST(e,s) \
249 if (DI_GUIDIsSame(&pei->guid, &(e))) \
250 haptic->supported |= (s)
251 static BOOL CALLBACK
252 DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv)
253 {
254 /* Prepare the haptic device. */
255 SDL_Haptic *haptic = (SDL_Haptic *) pv;
256
257 /* Get supported. */
258 EFFECT_TEST(GUID_Spring, SDL_HAPTIC_SPRING);
259 EFFECT_TEST(GUID_Damper, SDL_HAPTIC_DAMPER);
260 EFFECT_TEST(GUID_Inertia, SDL_HAPTIC_INERTIA);
261 EFFECT_TEST(GUID_Friction, SDL_HAPTIC_FRICTION);
262 EFFECT_TEST(GUID_ConstantForce, SDL_HAPTIC_CONSTANT);
263 EFFECT_TEST(GUID_CustomForce, SDL_HAPTIC_CUSTOM);
264 EFFECT_TEST(GUID_Sine, SDL_HAPTIC_SINE);
265 EFFECT_TEST(GUID_Square, SDL_HAPTIC_SQUARE);
266 EFFECT_TEST(GUID_Triangle, SDL_HAPTIC_TRIANGLE);
267 EFFECT_TEST(GUID_SawtoothUp, SDL_HAPTIC_SAWTOOTHUP);
268 EFFECT_TEST(GUID_SawtoothDown, SDL_HAPTIC_SAWTOOTHDOWN);
269 EFFECT_TEST(GUID_RampForce, SDL_HAPTIC_RAMP);
270
271 /* Check for more. */
272 return DIENUM_CONTINUE;
273 }
274
275
276 /*
277 * Callback to get supported axes.
278 */
279 static BOOL CALLBACK
280 DI_DeviceObjectCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
281 {
282 SDL_Haptic *haptic = (SDL_Haptic *) pvRef;
283
284 if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) {
285
286 haptic->hwdata->axes[haptic->naxes] = dev->dwOfs;
287 haptic->naxes++;
288
289 /* Currently using the artificial limit of 3 axes. */
290 if (haptic->naxes >= 3) {
291 return DIENUM_STOP;
292 }
293 }
294
295 return DIENUM_CONTINUE;
296 }
297
298
299 /*
300 * Opens the haptic device from the file descriptor.
301 *
302 * Steps:
303 * - Open temporary DirectInputDevice interface.
304 * - Create DirectInputDevice2 interface.
305 * - Release DirectInputDevice interface.
306 * - Call SDL_SYS_HapticOpenFromDevice2
307 */
308 static int
309 SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic, DIDEVICEINSTANCE instance)
310 {
311 HRESULT ret;
312 int ret2;
313 LPDIRECTINPUTDEVICE device;
314
315 /* Allocate the hwdata */
316 haptic->hwdata = (struct haptic_hwdata *)
317 SDL_malloc(sizeof(*haptic->hwdata));
318 if (haptic->hwdata == NULL) {
319 SDL_OutOfMemory();
320 goto creat_err;
321 }
322 SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
323
324 /* Open the device */
325 ret = IDirectInput_CreateDevice(dinput, &instance.guidInstance,
326 &device, NULL);
327 if (FAILED(ret)) {
328 DI_SetError("Creating DirectInput device", ret);
329 goto creat_err;
330 }
331
332 /* Now get the IDirectInputDevice2 interface, instead. */
333 ret = IDirectInputDevice_QueryInterface(device,
334 &IID_IDirectInputDevice2,
335 (LPVOID *) & haptic->hwdata->
336 device);
337 /* Done with the temporary one now. */
338 IDirectInputDevice_Release(device);
339 if (FAILED(ret)) {
340 DI_SetError("Querying DirectInput interface", ret);
341 goto creat_err;
342 }
343
344 ret2 = SDL_SYS_HapticOpenFromDevice2(haptic, haptic->hwdata->device);
345 if (ret2 < 0) {
346 goto query_err;
347 }
348
349 return 0;
350
351 query_err:
352 IDirectInputDevice2_Release(haptic->hwdata->device);
353 creat_err:
354 if (haptic->hwdata != NULL) {
355 SDL_free(haptic->hwdata);
356 haptic->hwdata = NULL;
357 }
358 return -1;
359 }
360
361
362 /*
363 * Opens the haptic device from the file descriptor.
364 *
365 * Steps:
366 * - Set cooperative level.
367 * - Set data format.
368 * - Acquire exclusiveness.
369 * - Reset actuators.
370 * - Get supported featuers.
371 */
372 static int
373 SDL_SYS_HapticOpenFromDevice2(SDL_Haptic * haptic,
374 LPDIRECTINPUTDEVICE2 device2)
375 {
376 HRESULT ret;
377 DIPROPDWORD dipdw;
378
379 /* We'll use the device2 from now on. */
380 haptic->hwdata->device = device2;
381
382 /* Grab it exclusively to use force feedback stuff. */
383 ret = IDirectInputDevice2_SetCooperativeLevel(haptic->hwdata->device,
384 SDL_HelperWindow,
385 DISCL_EXCLUSIVE |
386 DISCL_BACKGROUND);
387 if (FAILED(ret)) {
388 DI_SetError("Setting cooperative level to exclusive", ret);
389 goto acquire_err;
390 }
391
392 /* Set data format. */
393 ret = IDirectInputDevice2_SetDataFormat(haptic->hwdata->device,
394 &c_dfDIJoystick2);
395 if (FAILED(ret)) {
396 DI_SetError("Setting data format", ret);
397 goto acquire_err;
398 }
399
400 /* Get number of axes. */
401 ret = IDirectInputDevice2_EnumObjects(haptic->hwdata->device,
402 DI_DeviceObjectCallback,
403 haptic, DIDFT_AXIS);
404 if (FAILED(ret)) {
405 DI_SetError("Getting device axes", ret);
406 goto acquire_err;
407 }
408
409 /* Acquire the device. */
410 ret = IDirectInputDevice2_Acquire(haptic->hwdata->device);
411 if (FAILED(ret)) {
412 DI_SetError("Acquiring DirectInput device", ret);
413 goto acquire_err;
414 }
415
416 /* Reset all actuators - just in case. */
417 ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device,
418 DISFFC_RESET);
419 if (FAILED(ret)) {
420 DI_SetError("Resetting device", ret);
421 goto acquire_err;
422 }
423
424 /* Enabling actuators. */
425 ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device,
426 DISFFC_SETACTUATORSON);
427 if (FAILED(ret)) {
428 DI_SetError("Enabling actuators", ret);
429 goto acquire_err;
430 }
431
432 /* Get supported effects. */
433 ret = IDirectInputDevice2_EnumEffects(haptic->hwdata->device,
434 DI_EffectCallback, haptic,
435 DIEFT_ALL);
436 if (FAILED(ret)) {
437 DI_SetError("Enumerating supported effects", ret);
438 goto acquire_err;
439 }
440 if (haptic->supported == 0) { /* Error since device supports nothing. */
441 SDL_SetError("Haptic: Internal error on finding supported effects.");
442 goto acquire_err;
443 }
444
445 /* Check autogain and autocenter. */
446 dipdw.diph.dwSize = sizeof(DIPROPDWORD);
447 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
448 dipdw.diph.dwObj = 0;
449 dipdw.diph.dwHow = DIPH_DEVICE;
450 dipdw.dwData = 10000;
451 ret = IDirectInputDevice2_SetProperty(haptic->hwdata->device,
452 DIPROP_FFGAIN, &dipdw.diph);
453 if (!FAILED(ret)) { /* Gain is supported. */
454 haptic->supported |= SDL_HAPTIC_GAIN;
455 }
456 dipdw.diph.dwObj = 0;
457 dipdw.diph.dwHow = DIPH_DEVICE;
458 dipdw.dwData = DIPROPAUTOCENTER_OFF;
459 ret = IDirectInputDevice2_SetProperty(haptic->hwdata->device,
460 DIPROP_AUTOCENTER, &dipdw.diph);
461 if (!FAILED(ret)) { /* Autocenter is supported. */
462 haptic->supported |= SDL_HAPTIC_AUTOCENTER;
463 }
464
465 /* Status is always supported. */
466 haptic->supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE;
467
468 /* Check maximum effects. */
469 haptic->neffects = 128; /* This is not actually supported as thus under windows,
470 there is no way to tell the number of EFFECTS that a
471 device can hold, so we'll just use a "random" number
472 instead and put warnings in SDL_haptic.h */
473 haptic->nplaying = 128; /* Even more impossible to get this then neffects. */
474
475 /* Prepare effects memory. */
476 haptic->effects = (struct haptic_effect *)
477 SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
478 if (haptic->effects == NULL) {
479 SDL_OutOfMemory();
480 goto acquire_err;
481 }
482 /* Clear the memory */
483 SDL_memset(haptic->effects, 0,
484 sizeof(struct haptic_effect) * haptic->neffects);
485
486 return 0;
487
488 /* Error handling */
489 acquire_err:
490 IDirectInputDevice2_Unacquire(haptic->hwdata->device);
491 return -1;
492
493 }
494
495
496 /*
497 * Opens a haptic device for usage.
498 */
499 int
500 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
501 {
502 return SDL_SYS_HapticOpenFromInstance(haptic,
503 SDL_hapticlist[haptic->index].
504 instance);
505 }
506
507
508 /*
509 * Opens a haptic device from first mouse it finds for usage.
510 */
511 int
512 SDL_SYS_HapticMouse(void)
513 {
514 int i;
515
516 /* Grab the first mouse haptic device we find. */
517 for (i = 0; i < SDL_numhaptics; i++) {
518 if (SDL_hapticlist[i].capabilities.dwDevType == DIDEVTYPE_MOUSE) {
519 return i;
520 }
521 }
522
523 return -1;
524 }
525
526
527 /*
528 * Checks to see if a joystick has haptic features.
529 */
530 int
531 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
532 {
533 if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
534 return SDL_TRUE;
535 }
536
537 return SDL_FALSE;
538 }
539
540
541 /*
542 * Checks to see if the haptic device and joystick and in reality the same.
543 */
544 int
545 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
546 {
547 HRESULT ret;
548 DIDEVICEINSTANCE hap_instance, joy_instance;
549
550 /* Get the device instances. */
551 ret = IDirectInputDevice2_GetDeviceInfo(haptic->hwdata->device,
552 &hap_instance);
553 if (FAILED(ret)) {
554 return 0;
555 }
556 ret = IDirectInputDevice2_GetDeviceInfo(joystick->hwdata->InputDevice,
557 &joy_instance);
558 if (FAILED(ret)) {
559 return 0;
560 }
561
562 if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance))
563 return 1;
564
565 return 0;
566 }
567
568
569 /*
570 * Opens a SDL_Haptic from a SDL_Joystick.
571 */
572 int
573 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
574 {
575 int ret;
576
577 /* Allocate the hwdata */
578 haptic->hwdata = (struct haptic_hwdata *)
579 SDL_malloc(sizeof(*haptic->hwdata));
580 if (haptic->hwdata == NULL) {
581 SDL_OutOfMemory();
582 return -1;
583 }
584 SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
585
586 /* Now open the device. */
587 ret =
588 SDL_SYS_HapticOpenFromDevice2(haptic, joystick->hwdata->InputDevice);
589 if (ret < 0) {
590 return -1;
591 }
592
593 /* It's using the joystick device. */
594 haptic->hwdata->is_joystick = 1;
595
596 return 0;
597 }
598
599
600 /*
601 * Closes the haptic device.
602 */
603 void
604 SDL_SYS_HapticClose(SDL_Haptic * haptic)
605 {
606 if (haptic->hwdata) {
607
608 /* Free effects. */
609 SDL_free(haptic->effects);
610 haptic->effects = NULL;
611 haptic->neffects = 0;
612
613 /* Clean up */
614 IDirectInputDevice2_Unacquire(haptic->hwdata->device);
615 /* Only release if isn't grabbed by a joystick. */
616 if (haptic->hwdata->is_joystick == 0) {
617 IDirectInputDevice2_Release(haptic->hwdata->device);
618 }
619
620 /* Free */
621 SDL_free(haptic->hwdata);
622 haptic->hwdata = NULL;
623 }
624 }
625
626
627 /*
628 * Clean up after system specific haptic stuff
629 */
630 void
631 SDL_SYS_HapticQuit(void)
632 {
633 IDirectInput_Release(dinput);
634 dinput = NULL;
635 }
636
637
638 /*
639 * Converts an SDL trigger button to an DIEFFECT trigger button.
640 */
641 static DWORD
642 DIGetTriggerButton(Uint16 button)
643 {
644 DWORD dwTriggerButton;
645
646 dwTriggerButton = DIEB_NOTRIGGER;
647
648 if (button != 0) {
649 dwTriggerButton = DIJOFS_BUTTON(button - 1);
650 }
651
652 return dwTriggerButton;
653 }
654
655
656 /*
657 * Sets the direction.
658 */
659 static int
660 SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir, int naxes)
661 {
662 LONG *rglDir;
663
664 /* Handle no axes a part. */
665 if (naxes == 0) {
666 effect->dwFlags |= DIEFF_SPHERICAL; /* Set as default. */
667 effect->rglDirection = NULL;
668 return 0;
669 }
670
671 /* Has axes. */
672 rglDir = SDL_malloc(sizeof(LONG) * naxes);
673 if (rglDir == NULL) {
674 SDL_OutOfMemory();
675 return -1;
676 }
677 SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
678 effect->rglDirection = rglDir;
679
680 switch (dir->type) {
681 case SDL_HAPTIC_POLAR:
682 effect->dwFlags |= DIEFF_POLAR;
683 rglDir[0] = dir->dir[0];
684 return 0;
685 case SDL_HAPTIC_CARTESIAN:
686 effect->dwFlags |= DIEFF_CARTESIAN;
687 rglDir[0] = dir->dir[0];
688 if (naxes > 1)
689 rglDir[1] = dir->dir[1];
690 if (naxes > 2)
691 rglDir[2] = dir->dir[2];
692 return 0;
693 case SDL_HAPTIC_SPHERICAL:
694 effect->dwFlags |= DIEFF_SPHERICAL;
695 rglDir[0] = dir->dir[0];
696 if (naxes > 1)
697 rglDir[1] = dir->dir[1];
698 if (naxes > 2)
699 rglDir[2] = dir->dir[2];
700 return 0;
701
702 default:
703 SDL_SetError("Haptic: Unknown direction type.");
704 return -1;
705 }
706 }
707
708 #define CONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
709 /*
710 * Creates the DIEFFECT from a SDL_HapticEffect.
711 */
712 static int
713 SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
714 SDL_HapticEffect * src)
715 {
716 int i;
717 DICONSTANTFORCE *constant;
718 DIPERIODIC *periodic;
719 DICONDITION *condition; /* Actually an array of conditions - one per axis. */
720 DIRAMPFORCE *ramp;
721 DICUSTOMFORCE *custom;
722 DIENVELOPE *envelope;
723 SDL_HapticConstant *hap_constant;
724 SDL_HapticPeriodic *hap_periodic;
725 SDL_HapticCondition *hap_condition;
726 SDL_HapticRamp *hap_ramp;
727 SDL_HapticCustom *hap_custom;
728 DWORD *axes;
729
730 /* Set global stuff. */
731 SDL_memset(dest, 0, sizeof(DIEFFECT));
732 dest->dwSize = sizeof(DIEFFECT); /* Set the structure size. */
733 dest->dwSamplePeriod = 0; /* Not used by us. */
734 dest->dwGain = 10000; /* Gain is set globally, not locally. */
735 dest->dwFlags = DIEFF_OBJECTOFFSETS; /* Seems obligatory. */
736
737 /* Envelope. */
738 envelope = SDL_malloc(sizeof(DIENVELOPE));
739 if (envelope == NULL) {
740 SDL_OutOfMemory();
741 return -1;
742 }
743 SDL_memset(envelope, 0, sizeof(DIENVELOPE));
744 dest->lpEnvelope = envelope;
745 envelope->dwSize = sizeof(DIENVELOPE); /* Always should be this. */
746
747 /* Axes. */
748 dest->cAxes = haptic->naxes;
749 if (dest->cAxes > 0) {
750 axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
751 if (axes == NULL) {
752 SDL_OutOfMemory();
753 return -1;
754 }
755 axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */
756 if (dest->cAxes > 1) {
757 axes[1] = haptic->hwdata->axes[1];
758 }
759 if (dest->cAxes > 2) {
760 axes[2] = haptic->hwdata->axes[2];
761 }
762 dest->rgdwAxes = axes;
763 }
764
765
766 /* The big type handling switch, even bigger then linux's version. */
767 switch (src->type) {
768 case SDL_HAPTIC_CONSTANT:
769 hap_constant = &src->constant;
770 constant = SDL_malloc(sizeof(DICONSTANTFORCE));
771 if (constant == NULL) {
772 SDL_OutOfMemory();
773 return -1;
774 }
775 SDL_memset(constant, 0, sizeof(DICONSTANTFORCE));
776
777 /* Specifics */
778 constant->lMagnitude = CONVERT(hap_constant->level);
779 dest->cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
780 dest->lpvTypeSpecificParams = constant;
781
782 /* Generics */
783 dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
784 dest->dwTriggerButton = DIGetTriggerButton(hap_constant->button);
785 dest->dwTriggerRepeatInterval = hap_constant->interval;
786 dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */
787
788 /* Direction. */
789 if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
790 < 0) {
791 return -1;
792 }
793
794 /* Envelope */
795 if ((hap_constant->attack_length == 0)
796 && (hap_constant->fade_length == 0)) {
797 SDL_free(dest->lpEnvelope);
798 dest->lpEnvelope = NULL;
799 } else {
800 envelope->dwAttackLevel = CONVERT(hap_constant->attack_level);
801 envelope->dwAttackTime = hap_constant->attack_length * 1000;
802 envelope->dwFadeLevel = CONVERT(hap_constant->fade_level);
803 envelope->dwFadeTime = hap_constant->fade_length * 1000;
804 }
805
806 break;
807
808 case SDL_HAPTIC_SINE:
809 case SDL_HAPTIC_SQUARE:
810 case SDL_HAPTIC_TRIANGLE:
811 case SDL_HAPTIC_SAWTOOTHUP:
812 case SDL_HAPTIC_SAWTOOTHDOWN:
813 hap_periodic = &src->periodic;
814 periodic = SDL_malloc(sizeof(DIPERIODIC));
815 if (periodic == NULL) {
816 SDL_OutOfMemory();
817 return -1;
818 }
819 SDL_memset(periodic, 0, sizeof(DIPERIODIC));
820
821 /* Specifics */
822 periodic->dwMagnitude = CONVERT(hap_periodic->magnitude);
823 periodic->lOffset = CONVERT(hap_periodic->offset);
824 periodic->dwPhase = hap_periodic->phase;
825 periodic->dwPeriod = hap_periodic->period * 1000;
826 dest->cbTypeSpecificParams = sizeof(DIPERIODIC);
827 dest->lpvTypeSpecificParams = periodic;
828
829 /* Generics */
830 dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
831 dest->dwTriggerButton = DIGetTriggerButton(hap_periodic->button);
832 dest->dwTriggerRepeatInterval = hap_periodic->interval;
833 dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */
834
835 /* Direction. */
836 if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
837 < 0) {
838 return -1;
839 }
840
841 /* Envelope */
842 if ((hap_periodic->attack_length == 0)
843 && (hap_periodic->fade_length == 0)) {
844 SDL_free(dest->lpEnvelope);
845 dest->lpEnvelope = NULL;
846 } else {
847 envelope->dwAttackLevel = CONVERT(hap_periodic->attack_level);
848 envelope->dwAttackTime = hap_periodic->attack_length * 1000;
849 envelope->dwFadeLevel = CONVERT(hap_periodic->fade_level);
850 envelope->dwFadeTime = hap_periodic->fade_length * 1000;
851 }
852
853 break;
854
855 case SDL_HAPTIC_SPRING:
856 case SDL_HAPTIC_DAMPER:
857 case SDL_HAPTIC_INERTIA:
858 case SDL_HAPTIC_FRICTION:
859 hap_condition = &src->condition;
860 condition = SDL_malloc(sizeof(DICONDITION) * dest->cAxes);
861 if (condition == NULL) {
862 SDL_OutOfMemory();
863 return -1;
864 }
865 SDL_memset(condition, 0, sizeof(DICONDITION));
866
867 /* Specifics */
868 for (i = 0; i < (int) dest->cAxes; i++) {
869 condition[i].lOffset = CONVERT(hap_condition->center[i]);
870 condition[i].lPositiveCoefficient =
871 CONVERT(hap_condition->right_coeff[i]);
872 condition[i].lNegativeCoefficient =
873 CONVERT(hap_condition->left_coeff[i]);
874 condition[i].dwPositiveSaturation =
875 CONVERT(hap_condition->right_sat[i]);
876 condition[i].dwNegativeSaturation =
877 CONVERT(hap_condition->left_sat[i]);
878 condition[i].lDeadBand = CONVERT(hap_condition->deadband[i]);
879 }
880 dest->cbTypeSpecificParams = sizeof(DICONDITION) * dest->cAxes;
881 dest->lpvTypeSpecificParams = condition;
882
883 /* Generics */
884 dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */
885 dest->dwTriggerButton = DIGetTriggerButton(hap_condition->button);
886 dest->dwTriggerRepeatInterval = hap_condition->interval;
887 dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */
888
889 /* Direction. */
890 if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
891 < 0) {
892 return -1;
893 }
894
895 /* Envelope - Not actually supported by most CONDITION implementations. */
896 SDL_free(dest->lpEnvelope);
897 dest->lpEnvelope = NULL;
898
899 break;
900
901 case SDL_HAPTIC_RAMP:
902 hap_ramp = &src->ramp;
903 ramp = SDL_malloc(sizeof(DIRAMPFORCE));
904 if (ramp == NULL) {
905 SDL_OutOfMemory();
906 return -1;
907 }
908 SDL_memset(ramp, 0, sizeof(DIRAMPFORCE));
909
910 /* Specifics */
911 ramp->lStart = CONVERT(hap_ramp->start);
912 ramp->lEnd = CONVERT(hap_ramp->end);
913 dest->cbTypeSpecificParams = sizeof(DIRAMPFORCE);
914 dest->lpvTypeSpecificParams = ramp;
915
916 /* Generics */
917 dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */
918 dest->dwTriggerButton = DIGetTriggerButton(hap_ramp->button);
919 dest->dwTriggerRepeatInterval = hap_ramp->interval;
920 dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */
921
922 /* Direction. */
923 if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
924 return -1;
925 }
926
927 /* Envelope */
928 if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
929 SDL_free(dest->lpEnvelope);
930 dest->lpEnvelope = NULL;
931 } else {
932 envelope->dwAttackLevel = CONVERT(hap_ramp->attack_level);
933 envelope->dwAttackTime = hap_ramp->attack_length * 1000;
934 envelope->dwFadeLevel = CONVERT(hap_ramp->fade_level);
935 envelope->dwFadeTime = hap_ramp->fade_length * 1000;
936 }
937
938 break;
939
940 case SDL_HAPTIC_CUSTOM:
941 hap_custom = &src->custom;
942 custom = SDL_malloc(sizeof(DICUSTOMFORCE));
943 if (custom == NULL) {
944 SDL_OutOfMemory();
945 return -1;
946 }
947 SDL_memset(custom, 0, sizeof(DICUSTOMFORCE));
948
949 /* Specifics */
950 custom->cChannels = hap_custom->channels;
951 custom->dwSamplePeriod = hap_custom->period * 1000;
952 custom->cSamples = hap_custom->samples;
953 custom->rglForceData =
954 SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
955 for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { /* Copy data. */
956 custom->rglForceData[i] = CONVERT(hap_custom->data[i]);
957 }
958 dest->cbTypeSpecificParams = sizeof(DICUSTOMFORCE);
959 dest->lpvTypeSpecificParams = custom;
960
961 /* Generics */
962 dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */
963 dest->dwTriggerButton = DIGetTriggerButton(hap_custom->button);
964 dest->dwTriggerRepeatInterval = hap_custom->interval;
965 dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */
966
967 /* Direction. */
968 if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
969 0) {
970 return -1;
971 }
972
973 /* Envelope */
974 if ((hap_custom->attack_length == 0)
975 && (hap_custom->fade_length == 0)) {
976 SDL_free(dest->lpEnvelope);
977 dest->lpEnvelope = NULL;
978 } else {
979 envelope->dwAttackLevel = CONVERT(hap_custom->attack_level);
980 envelope->dwAttackTime = hap_custom->attack_length * 1000;
981 envelope->dwFadeLevel = CONVERT(hap_custom->fade_level);
982 envelope->dwFadeTime = hap_custom->fade_length * 1000;
983 }
984
985 break;
986
987
988 default:
989 SDL_SetError("Haptic: Unknown effect type.");
990 return -1;
991 }
992
993 return 0;
994 }
995
996
997 /*
998 * Frees an DIEFFECT allocated by SDL_SYS_ToDIEFFECT.
999 */
1000 static void
1001 SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type)
1002 {
1003 DICUSTOMFORCE *custom;
1004
1005 if (effect->lpEnvelope != NULL) {
1006 SDL_free(effect->lpEnvelope);
1007 effect->lpEnvelope = NULL;
1008 }
1009 if (effect->rgdwAxes != NULL) {
1010 SDL_free(effect->rgdwAxes);
1011 effect->rgdwAxes = NULL;
1012 }
1013 if (effect->lpvTypeSpecificParams != NULL) {
1014 if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */
1015 custom = (DICUSTOMFORCE *) effect->lpvTypeSpecificParams;
1016 SDL_free(custom->rglForceData);
1017 custom->rglForceData = NULL;
1018 }
1019 SDL_free(effect->lpvTypeSpecificParams);
1020 effect->lpvTypeSpecificParams = NULL;
1021 }
1022 if (effect->rglDirection != NULL) {
1023 SDL_free(effect->rglDirection);
1024 effect->rglDirection = NULL;
1025 }
1026 }
1027
1028
1029 /*
1030 * Gets the effect type from the generic SDL haptic effect wrapper.
1031 */
1032 static REFGUID
1033 SDL_SYS_HapticEffectType(SDL_HapticEffect * effect)
1034 {
1035 switch (effect->type) {
1036 case SDL_HAPTIC_CONSTANT:
1037 return &GUID_ConstantForce;
1038
1039 case SDL_HAPTIC_RAMP:
1040 return &GUID_RampForce;
1041
1042 case SDL_HAPTIC_SQUARE:
1043 return &GUID_Square;
1044
1045 case SDL_HAPTIC_SINE:
1046 return &GUID_Sine;
1047
1048 case SDL_HAPTIC_TRIANGLE:
1049 return &GUID_Triangle;
1050
1051 case SDL_HAPTIC_SAWTOOTHUP:
1052 return &GUID_SawtoothUp;
1053
1054 case SDL_HAPTIC_SAWTOOTHDOWN:
1055 return &GUID_SawtoothDown;
1056
1057 case SDL_HAPTIC_SPRING:
1058 return &GUID_Spring;
1059
1060 case SDL_HAPTIC_DAMPER:
1061 return &GUID_Damper;
1062
1063 case SDL_HAPTIC_INERTIA:
1064 return &GUID_Inertia;
1065
1066 case SDL_HAPTIC_FRICTION:
1067 return &GUID_Friction;
1068
1069 case SDL_HAPTIC_CUSTOM:
1070 return &GUID_CustomForce;
1071
1072 default:
1073 SDL_SetError("Haptic: Unknown effect type.");
1074 return NULL;
1075 }
1076 }
1077
1078
1079 /*
1080 * Creates a new haptic effect.
1081 */
1082 int
1083 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1084 SDL_HapticEffect * base)
1085 {
1086 HRESULT ret;
1087
1088 /* Get the type. */
1089 REFGUID type = SDL_SYS_HapticEffectType(base);
1090 if (type == NULL) {
1091 goto err_hweffect;
1092 }
1093
1094 /* Alloc the effect. */
1095 effect->hweffect = (struct haptic_hweffect *)
1096 SDL_malloc(sizeof(struct haptic_hweffect));
1097 if (effect->hweffect == NULL) {
1098 SDL_OutOfMemory();
1099 goto err_hweffect;
1100 }
1101
1102 /* Get the effect. */
1103 if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
1104 goto err_effectdone;
1105 }
1106
1107 /* Create the actual effect. */
1108 ret = IDirectInputDevice2_CreateEffect(haptic->hwdata->device, type,
1109 &effect->hweffect->effect,
1110 &effect->hweffect->ref, NULL);
1111 if (FAILED(ret)) {
1112 DI_SetError("Unable to create effect", ret);
1113 goto err_effectdone;
1114 }
1115
1116 return 0;
1117
1118 err_effectdone:
1119 SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, base->type);
1120 err_hweffect:
1121 if (effect->hweffect != NULL) {
1122 SDL_free(effect->hweffect);
1123 effect->hweffect = NULL;
1124 }
1125 return -1;
1126 }
1127
1128
1129 /*
1130 * Updates an effect.
1131 */
1132 int
1133 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
1134 struct haptic_effect *effect,
1135 SDL_HapticEffect * data)
1136 {
1137 HRESULT ret;
1138 DWORD flags;
1139 DIEFFECT temp;
1140
1141 /* Get the effect. */
1142 SDL_memset(&temp, 0, sizeof(DIEFFECT));
1143 if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) {
1144 goto err_update;
1145 }
1146
1147 /* Set the flags. Might be worthwhile to diff temp with loaded effect and
1148 * only change those parameters. */
1149 flags = DIEP_DIRECTION |
1150 DIEP_DURATION |
1151 DIEP_ENVELOPE |
1152 DIEP_STARTDELAY |
1153 DIEP_TRIGGERBUTTON |
1154 DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS;
1155
1156 /* Create the actual effect. */
1157 ret =
1158 IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags);
1159 if (FAILED(ret)) {
1160 DI_SetError("Unable to update effect", ret);
1161 goto err_update;
1162 }
1163
1164 /* Copy it over. */
1165 SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, data->type);
1166 SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(DIEFFECT));
1167
1168 return 0;
1169
1170 err_update:
1171 SDL_SYS_HapticFreeDIEFFECT(&temp, data->type);
1172 return -1;
1173 }
1174
1175
1176 /*
1177 * Runs an effect.
1178 */
1179 int
1180 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1181 Uint32 iterations)
1182 {
1183 HRESULT ret;
1184 DWORD iter;
1185
1186 /* Check if it's infinite. */
1187 if (iterations == SDL_HAPTIC_INFINITY) {
1188 iter = INFINITE;
1189 } else
1190 iter = iterations;
1191
1192 /* Run the effect. */
1193 ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0);
1194 if (FAILED(ret)) {
1195 DI_SetError("Running the effect", ret);
1196 return -1;
1197 }
1198
1199 return 0;
1200 }
1201
1202
1203 /*
1204 * Stops an effect.
1205 */
1206 int
1207 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1208 {
1209 HRESULT ret;
1210
1211 ret = IDirectInputEffect_Stop(effect->hweffect->ref);
1212 if (FAILED(ret)) {
1213 DI_SetError("Unable to stop effect", ret);
1214 return -1;
1215 }
1216
1217 return 0;
1218 }
1219
1220
1221 /*
1222 * Frees the effect.
1223 */
1224 void
1225 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1226 {
1227 HRESULT ret;
1228
1229 ret = IDirectInputEffect_Unload(effect->hweffect->ref);
1230 if (FAILED(ret)) {
1231 DI_SetError("Removing effect from the device", ret);
1232 }
1233 SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
1234 effect->effect.type);
1235 SDL_free(effect->hweffect);
1236 effect->hweffect = NULL;
1237 }
1238
1239
1240 /*
1241 * Gets the status of a haptic effect.
1242 */
1243 int
1244 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
1245 struct haptic_effect *effect)
1246 {
1247 HRESULT ret;
1248 DWORD status;
1249
1250 ret = IDirectInputEffect_GetEffectStatus(effect->hweffect->ref, &status);
1251 if (FAILED(ret)) {
1252 DI_SetError("Getting effect status", ret);
1253 return -1;
1254 }
1255
1256 if (status == 0)
1257 return SDL_FALSE;
1258 return SDL_TRUE;
1259 }
1260
1261
1262 /*
1263 * Sets the gain.
1264 */
1265 int
1266 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
1267 {
1268 HRESULT ret;
1269 DIPROPDWORD dipdw;
1270
1271 /* Create the weird structure thingy. */
1272 dipdw.diph.dwSize = sizeof(DIPROPDWORD);
1273 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
1274 dipdw.diph.dwObj = 0;
1275 dipdw.diph.dwHow = DIPH_DEVICE;
1276 dipdw.dwData = gain * 100; /* 0 to 10,000 */
1277
1278 /* Try to set the autocenter. */
1279 ret = IDirectInputDevice2_SetProperty(haptic->hwdata->device,
1280 DIPROP_FFGAIN, &dipdw.diph);
1281 if (FAILED(ret)) {
1282 DI_SetError("Setting gain", ret);
1283 return -1;
1284 }
1285
1286 return 0;
1287 }
1288
1289
1290 /*
1291 * Sets the autocentering.
1292 */
1293 int
1294 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
1295 {
1296 HRESULT ret;
1297 DIPROPDWORD dipdw;
1298
1299 /* Create the weird structure thingy. */
1300 dipdw.diph.dwSize = sizeof(DIPROPDWORD);
1301 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
1302 dipdw.diph.dwObj = 0;
1303 dipdw.diph.dwHow = DIPH_DEVICE;
1304 dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF :
1305 DIPROPAUTOCENTER_ON;
1306
1307 /* Try to set the autocenter. */
1308 ret = IDirectInputDevice2_SetProperty(haptic->hwdata->device,
1309 DIPROP_AUTOCENTER, &dipdw.diph);
1310 if (FAILED(ret)) {
1311 DI_SetError("Setting autocenter", ret);
1312 return -1;
1313 }
1314
1315 return 0;
1316 }
1317
1318
1319 /*
1320 * Pauses the device.
1321 */
1322 int
1323 SDL_SYS_HapticPause(SDL_Haptic * haptic)
1324 {
1325 HRESULT ret;
1326
1327 /* Pause the device. */
1328 ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device,
1329 DISFFC_PAUSE);
1330 if (FAILED(ret)) {
1331 DI_SetError("Pausing the device", ret);
1332 return -1;
1333 }
1334
1335 return 0;
1336 }
1337
1338
1339 /*
1340 * Pauses the device.
1341 */
1342 int
1343 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
1344 {
1345 HRESULT ret;
1346
1347 /* Unpause the device. */
1348 ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device,
1349 DISFFC_CONTINUE);
1350 if (FAILED(ret)) {
1351 DI_SetError("Pausing the device", ret);
1352 return -1;
1353 }
1354
1355 return 0;
1356 }
1357
1358
1359 /*
1360 * Stops all the playing effects on the device.
1361 */
1362 int
1363 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
1364 {
1365 HRESULT ret;
1366
1367 /* Try to stop the effects. */
1368 ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device,
1369 DISFFC_STOPALL);
1370 if (FAILED(ret)) {
1371 DI_SetError("Stopping the device", ret);
1372 return -1;
1373 }
1374
1375 return 0;
1376 }
1377
1378
1379 #endif /* SDL_HAPTIC_DINPUT */