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