comparison src/haptic/win32/SDL_syshaptic.c @ 2713:0906692aa6a4

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