src/haptic/windows/SDL_syshaptic.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 17 Mar 2014 19:11:18 -0400
changeset 8629 4c01875a4620
parent 8281 f280c6b5d08c
child 8634 a60eaa04d3fb
permissions -rw-r--r--
Fixed SDL_HapticOpenFromJoystick() with DirectInput devices.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 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 #ifdef SDL_HAPTIC_DINPUT
    24 
    25 #include "SDL_assert.h"
    26 #include "SDL_thread.h"
    27 #include "SDL_mutex.h"
    28 #include "SDL_timer.h"
    29 #include "SDL_hints.h"
    30 #include "SDL_haptic.h"
    31 #include "../SDL_syshaptic.h"
    32 #include "SDL_joystick.h"
    33 #include "../../joystick/SDL_sysjoystick.h"     /* For the real SDL_Joystick */
    34 #include "../../joystick/windows/SDL_dxjoystick_c.h"      /* For joystick hwdata */
    35 
    36 #include "SDL_syshaptic_c.h"
    37 
    38 /*
    39  * List of available haptic devices.
    40  */
    41 typedef struct SDL_hapticlist_item
    42 {
    43     DIDEVICEINSTANCE instance;
    44     char *name;
    45     SDL_Haptic *haptic;
    46     DIDEVCAPS capabilities;
    47     Uint8 bXInputHaptic; /* Supports force feedback via XInput. */
    48     Uint8 userid; /* XInput userid index for this joystick */
    49     struct SDL_hapticlist_item *next;
    50 } SDL_hapticlist_item;
    51 
    52 
    53 /*
    54  * Haptic system hardware data.
    55  */
    56 struct haptic_hwdata
    57 {
    58     LPDIRECTINPUTDEVICE8 device;
    59     DWORD axes[3];              /* Axes to use. */
    60     SDL_bool is_joystick;       /* Device is loaded as joystick. */
    61     Uint8 bXInputHaptic; /* Supports force feedback via XInput. */
    62     Uint8 userid; /* XInput userid index for this joystick */
    63     SDL_Thread *thread;
    64     SDL_mutex *mutex;
    65     volatile Uint32 stopTicks;
    66     volatile int stopThread;
    67 };
    68 
    69 
    70 /*
    71  * Haptic system effect data.
    72  */
    73 struct haptic_hweffect
    74 {
    75     DIEFFECT effect;
    76     LPDIRECTINPUTEFFECT ref;
    77     XINPUT_VIBRATION vibration;
    78 };
    79 
    80 
    81 /*
    82  * Internal stuff.
    83  */
    84 static SDL_bool coinitialized = SDL_FALSE;
    85 static LPDIRECTINPUT8 dinput = NULL;
    86 static SDL_bool loaded_xinput = SDL_FALSE;
    87 static SDL_hapticlist_item *SDL_hapticlist = NULL;
    88 static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
    89 static int numhaptics = 0;
    90 
    91 /*
    92  * External stuff.
    93  */
    94 extern HWND SDL_HelperWindow;
    95 
    96 
    97 /*
    98  * Prototypes.
    99  */
   100 static int 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_HapticOpenFromDevice8(SDL_Haptic * haptic,
   105                                          LPDIRECTINPUTDEVICE8 device8,
   106                                          SDL_bool is_joystick);
   107 static int SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, const Uint8 userid);
   108 static DWORD DIGetTriggerButton(Uint16 button);
   109 static int SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir,
   110                                 int naxes);
   111 static int SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
   112                               SDL_HapticEffect * src);
   113 static void SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type);
   114 static REFGUID SDL_SYS_HapticEffectType(SDL_HapticEffect * effect);
   115 static int SDLCALL SDL_RunXInputHaptic(void *arg);
   116 
   117 /* Callbacks. */
   118 static BOOL CALLBACK EnumHapticsCallback(const DIDEVICEINSTANCE *
   119                                          pdidInstance, VOID * pContext);
   120 static BOOL CALLBACK DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv);
   121 
   122 
   123 /*
   124  * Like SDL_SetError but for DX error codes.
   125  */
   126 static int
   127 DI_SetError(const char *str, HRESULT err)
   128 {
   129     /*
   130        SDL_SetError("Haptic: %s - %s: %s", str,
   131        DXGetErrorString8A(err), DXGetErrorDescription8A(err));
   132      */
   133     return SDL_SetError("Haptic error %s", str);
   134 }
   135 
   136 
   137 /*
   138  * Checks to see if two GUID are the same.
   139  */
   140 static int
   141 DI_GUIDIsSame(const GUID * a, const GUID * b)
   142 {
   143     return (SDL_memcmp(a, b, sizeof (GUID)) == 0);
   144 }
   145 
   146 
   147 /*
   148  * Initializes the haptic subsystem.
   149  */
   150 int
   151 SDL_SYS_HapticInit(void)
   152 {
   153     const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
   154     HRESULT ret;
   155     HINSTANCE instance;
   156 
   157     if (dinput != NULL) {       /* Already open. */
   158         return SDL_SetError("Haptic: SubSystem already open.");
   159     }
   160 
   161     ret = WIN_CoInitialize();
   162     if (FAILED(ret)) {
   163         return DI_SetError("Coinitialize", ret);
   164     }
   165 
   166     coinitialized = SDL_TRUE;
   167 
   168     ret = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER,
   169                            &IID_IDirectInput8, (LPVOID) & dinput);
   170     if (FAILED(ret)) {
   171         SDL_SYS_HapticQuit();
   172         return DI_SetError("CoCreateInstance", ret);
   173     }
   174 
   175     /* Because we used CoCreateInstance, we need to Initialize it, first. */
   176     instance = GetModuleHandle(NULL);
   177     if (instance == NULL) {
   178         SDL_SYS_HapticQuit();
   179         return SDL_SetError("GetModuleHandle() failed with error code %d.",
   180                             GetLastError());
   181     }
   182     ret = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION);
   183     if (FAILED(ret)) {
   184         SDL_SYS_HapticQuit();
   185         return DI_SetError("Initializing DirectInput device", ret);
   186     }
   187 
   188     /* Look for haptic devices. */
   189     ret = IDirectInput8_EnumDevices(dinput,
   190                                    0,
   191                                    EnumHapticsCallback,
   192                                    NULL,
   193                                    DIEDFL_FORCEFEEDBACK |
   194                                    DIEDFL_ATTACHEDONLY);
   195     if (FAILED(ret)) {
   196         SDL_SYS_HapticQuit();
   197         return DI_SetError("Enumerating DirectInput devices", ret);
   198     }
   199 
   200     if (!env || SDL_atoi(env)) {
   201         loaded_xinput = (WIN_LoadXInputDLL() == 0);
   202     }
   203 
   204     if (loaded_xinput) {
   205         DWORD i;
   206         for (i = 0; i < SDL_XINPUT_MAX_DEVICES; i++) {
   207             XInputHaptic_MaybeAddDevice(i);
   208         }
   209     }
   210 
   211     return numhaptics;
   212 }
   213 
   214 
   215 int
   216 DirectInputHaptic_MaybeAddDevice(const DIDEVICEINSTANCE * pdidInstance)
   217 {
   218     HRESULT ret;
   219     LPDIRECTINPUTDEVICE8 device;
   220     const DWORD needflags = DIDC_ATTACHED | DIDC_FORCEFEEDBACK;
   221     DIDEVCAPS capabilities;
   222     SDL_hapticlist_item *item = NULL;
   223 
   224     if (dinput == NULL) {
   225         return -1;  /* not initialized. We'll pick these up on enumeration if we init later. */
   226     }
   227 
   228     /* Make sure we don't already have it */
   229     for (item = SDL_hapticlist; item; item = item->next) {
   230         if ( (!item->bXInputHaptic) && (SDL_memcmp(&item->instance, pdidInstance, sizeof (*pdidInstance)) == 0) ) {
   231             return -1;  /* Already added */
   232         }
   233     }
   234 
   235     /* Open the device */
   236     ret = IDirectInput8_CreateDevice(dinput, &pdidInstance->guidInstance, &device, NULL);
   237     if (FAILED(ret)) {
   238         /* DI_SetError("Creating DirectInput device",ret); */
   239         return -1;
   240     }
   241 
   242     /* Get capabilities. */
   243     SDL_zero(capabilities);
   244     capabilities.dwSize = sizeof (DIDEVCAPS);
   245     ret = IDirectInputDevice8_GetCapabilities(device, &capabilities);
   246     IDirectInputDevice8_Release(device);
   247     if (FAILED(ret)) {
   248         /* DI_SetError("Getting device capabilities",ret); */
   249         return -1;
   250     }
   251 
   252     if ((capabilities.dwFlags & needflags) != needflags) {
   253         return -1;  /* not a device we can use. */
   254     }
   255 
   256     item = (SDL_hapticlist_item *)SDL_calloc(1, sizeof(SDL_hapticlist_item));
   257     if (item == NULL) {
   258         return SDL_OutOfMemory();
   259     }
   260 
   261     item->name = WIN_StringToUTF8(pdidInstance->tszProductName);
   262     if (!item->name) {
   263         SDL_free(item);
   264         return -1;
   265     }
   266 
   267     /* Copy the instance over, useful for creating devices. */
   268     SDL_memcpy(&item->instance, pdidInstance, sizeof (DIDEVICEINSTANCE));
   269     SDL_memcpy(&item->capabilities, &capabilities, sizeof (capabilities));
   270 
   271     if (SDL_hapticlist_tail == NULL) {
   272         SDL_hapticlist = SDL_hapticlist_tail = item;
   273     } else {
   274         SDL_hapticlist_tail->next = item;
   275         SDL_hapticlist_tail = item;
   276     }
   277 
   278     /* Device has been added. */
   279     ++numhaptics;
   280 
   281     return numhaptics;
   282 }
   283 
   284 
   285 int
   286 DirectInputHaptic_MaybeRemoveDevice(const DIDEVICEINSTANCE * pdidInstance)
   287 {
   288     SDL_hapticlist_item *item;
   289     SDL_hapticlist_item *prev = NULL;
   290 
   291     if (dinput == NULL) {
   292         return -1;  /* not initialized, ignore this. */
   293     }
   294 
   295     for (item = SDL_hapticlist; item != NULL; item = item->next) {
   296         if ( (!item->bXInputHaptic) && (SDL_memcmp(&item->instance, pdidInstance, sizeof (*pdidInstance)) == 0) ) {
   297             /* found it, remove it. */
   298             const int retval = item->haptic ? item->haptic->index : -1;
   299             if (prev != NULL) {
   300                 prev->next = item->next;
   301             } else {
   302                 SDL_assert(SDL_hapticlist == item);
   303                 SDL_hapticlist = item->next;
   304             }
   305             if (item == SDL_hapticlist_tail) {
   306                 SDL_hapticlist_tail = prev;
   307             }
   308             --numhaptics;
   309             /* !!! TODO: Send a haptic remove event? */
   310             SDL_free(item);
   311             return retval;
   312         }
   313         prev = item;
   314     }
   315 
   316     return -1;
   317 }
   318 
   319 
   320 int
   321 XInputHaptic_MaybeAddDevice(const DWORD dwUserid)
   322 {
   323     const Uint8 userid = (Uint8) dwUserid;
   324     XINPUT_CAPABILITIES caps;
   325     const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
   326     SDL_hapticlist_item *item;
   327 
   328     if ((!loaded_xinput) || (dwUserid >= SDL_XINPUT_MAX_DEVICES)) {
   329         return -1;
   330     }
   331 
   332     /* Make sure we don't already have it */
   333     for (item = SDL_hapticlist; item; item = item->next) {
   334         if ((item->bXInputHaptic) && (item->userid == userid)) {
   335             return -1;  /* Already added */
   336         }
   337     }
   338 
   339     if (XINPUTGETCAPABILITIES(dwUserid, XINPUT_FLAG_GAMEPAD, &caps) != ERROR_SUCCESS) {
   340         return -1;  /* maybe controller isn't plugged in. */
   341     }
   342 
   343     /* XInput < 1.4 is probably only for original XBox360 controllers,
   344         which don't offer the flag, and always have force feedback */
   345     if ( (bIs14OrLater) && ((caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) == 0) ) {
   346         return -1;  /* no force feedback on this device. */
   347     }
   348 
   349     item = (SDL_hapticlist_item *)SDL_malloc( sizeof(SDL_hapticlist_item));
   350     if (item == NULL) {
   351         return SDL_OutOfMemory();
   352     }
   353 
   354     SDL_zerop(item);
   355 
   356     /* !!! FIXME: I'm not bothering to query for a real name right now (can we even?) */
   357     {
   358         char buf[64];
   359         SDL_snprintf(buf, sizeof (buf), "XInput Controller #%u", (unsigned int) (userid+1));
   360         item->name = SDL_strdup(buf);
   361     }
   362 
   363     if (!item->name) {
   364         SDL_free(item);
   365         return -1;
   366     }
   367 
   368     /* Copy the instance over, useful for creating devices. */
   369     item->bXInputHaptic = 1;
   370     item->userid = userid;
   371 
   372     if (SDL_hapticlist_tail == NULL) {
   373         SDL_hapticlist = SDL_hapticlist_tail = item;
   374     } else {
   375         SDL_hapticlist_tail->next = item;
   376         SDL_hapticlist_tail = item;
   377     }
   378 
   379     /* Device has been added. */
   380     ++numhaptics;
   381 
   382     return numhaptics;
   383 }
   384 
   385 
   386 int
   387 XInputHaptic_MaybeRemoveDevice(const DWORD dwUserid)
   388 {
   389     const Uint8 userid = (Uint8) dwUserid;
   390     SDL_hapticlist_item *item;
   391     SDL_hapticlist_item *prev = NULL;
   392 
   393     if ((!loaded_xinput) || (dwUserid >= SDL_XINPUT_MAX_DEVICES)) {
   394         return -1;
   395     }
   396 
   397     for (item = SDL_hapticlist; item != NULL; item = item->next) {
   398         if ((item->bXInputHaptic) && (item->userid == userid)) {
   399             /* found it, remove it. */
   400             const int retval = item->haptic ? item->haptic->index : -1;
   401             if (prev != NULL) {
   402                 prev->next = item->next;
   403             } else {
   404                 SDL_assert(SDL_hapticlist == item);
   405                 SDL_hapticlist = item->next;
   406             }
   407             if (item == SDL_hapticlist_tail) {
   408                 SDL_hapticlist_tail = prev;
   409             }
   410             --numhaptics;
   411             /* !!! TODO: Send a haptic remove event? */
   412             SDL_free(item);
   413             return retval;
   414         }
   415         prev = item;
   416     }
   417 
   418     return -1;
   419 }
   420 
   421 
   422 /*
   423  * Callback to find the haptic devices.
   424  */
   425 static BOOL CALLBACK
   426 EnumHapticsCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
   427 {
   428     (void) pContext;
   429     DirectInputHaptic_MaybeAddDevice(pdidInstance);
   430     return DIENUM_CONTINUE;  /* continue enumerating */
   431 }
   432 
   433 
   434 int
   435 SDL_SYS_NumHaptics()
   436 {
   437     return numhaptics;
   438 }
   439 
   440 static SDL_hapticlist_item *
   441 HapticByDevIndex(int device_index)
   442 {
   443     SDL_hapticlist_item *item = SDL_hapticlist;
   444 
   445     if ((device_index < 0) || (device_index >= numhaptics)) {
   446         return NULL;
   447     }
   448 
   449     while (device_index > 0) {
   450         SDL_assert(item != NULL);
   451         --device_index;
   452         item = item->next;
   453     }
   454 
   455     return item;
   456 }
   457 
   458 /*
   459  * Return the name of a haptic device, does not need to be opened.
   460  */
   461 const char *
   462 SDL_SYS_HapticName(int index)
   463 {
   464     SDL_hapticlist_item *item = HapticByDevIndex(index);
   465     return item->name;
   466 }
   467 
   468 
   469 /*
   470  * Callback to get all supported effects.
   471  */
   472 #define EFFECT_TEST(e,s)               \
   473 if (DI_GUIDIsSame(&pei->guid, &(e)))   \
   474    haptic->supported |= (s)
   475 static BOOL CALLBACK
   476 DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv)
   477 {
   478     /* Prepare the haptic device. */
   479     SDL_Haptic *haptic = (SDL_Haptic *) pv;
   480 
   481     /* Get supported. */
   482     EFFECT_TEST(GUID_Spring, SDL_HAPTIC_SPRING);
   483     EFFECT_TEST(GUID_Damper, SDL_HAPTIC_DAMPER);
   484     EFFECT_TEST(GUID_Inertia, SDL_HAPTIC_INERTIA);
   485     EFFECT_TEST(GUID_Friction, SDL_HAPTIC_FRICTION);
   486     EFFECT_TEST(GUID_ConstantForce, SDL_HAPTIC_CONSTANT);
   487     EFFECT_TEST(GUID_CustomForce, SDL_HAPTIC_CUSTOM);
   488     EFFECT_TEST(GUID_Sine, SDL_HAPTIC_SINE);
   489     /* !!! FIXME: put this back when we have more bits in 2.1 */
   490     /* EFFECT_TEST(GUID_Square, SDL_HAPTIC_SQUARE); */
   491     EFFECT_TEST(GUID_Triangle, SDL_HAPTIC_TRIANGLE);
   492     EFFECT_TEST(GUID_SawtoothUp, SDL_HAPTIC_SAWTOOTHUP);
   493     EFFECT_TEST(GUID_SawtoothDown, SDL_HAPTIC_SAWTOOTHDOWN);
   494     EFFECT_TEST(GUID_RampForce, SDL_HAPTIC_RAMP);
   495 
   496     /* Check for more. */
   497     return DIENUM_CONTINUE;
   498 }
   499 
   500 
   501 /*
   502  * Callback to get supported axes.
   503  */
   504 static BOOL CALLBACK
   505 DI_DeviceObjectCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
   506 {
   507     SDL_Haptic *haptic = (SDL_Haptic *) pvRef;
   508 
   509     if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) {
   510 
   511         haptic->hwdata->axes[haptic->naxes] = dev->dwOfs;
   512         haptic->naxes++;
   513 
   514         /* Currently using the artificial limit of 3 axes. */
   515         if (haptic->naxes >= 3) {
   516             return DIENUM_STOP;
   517         }
   518     }
   519 
   520     return DIENUM_CONTINUE;
   521 }
   522 
   523 
   524 /*
   525  * Opens the haptic device from the file descriptor.
   526  *
   527  *    Steps:
   528  *       - Open temporary DirectInputDevice interface.
   529  *       - Create DirectInputDevice8 interface.
   530  *       - Release DirectInputDevice interface.
   531  *       - Call SDL_SYS_HapticOpenFromDevice8
   532  */
   533 static int
   534 SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic, DIDEVICEINSTANCE instance)
   535 {
   536     HRESULT ret;
   537     int ret2;
   538     LPDIRECTINPUTDEVICE8 device;
   539     LPDIRECTINPUTDEVICE8 device8;
   540 
   541     /* Open the device */
   542     ret = IDirectInput8_CreateDevice(dinput, &instance.guidInstance,
   543                                     &device, NULL);
   544     if (FAILED(ret)) {
   545         DI_SetError("Creating DirectInput device", ret);
   546         return -1;
   547     }
   548 
   549     /* Now get the IDirectInputDevice8 interface, instead. */
   550     ret = IDirectInputDevice8_QueryInterface(device,
   551                                             &IID_IDirectInputDevice8,
   552                                             (LPVOID *) &device8);
   553     /* Done with the temporary one now. */
   554     IDirectInputDevice8_Release(device);
   555     if (FAILED(ret)) {
   556         DI_SetError("Querying DirectInput interface", ret);
   557         return -1;
   558     }
   559 
   560     ret2 = SDL_SYS_HapticOpenFromDevice8(haptic, device8, SDL_FALSE);
   561     if (ret2 < 0) {
   562         IDirectInputDevice8_Release(device8);
   563         return -1;
   564     }
   565 
   566     return 0;
   567 }
   568 
   569 static int
   570 SDL_SYS_HapticOpenFromXInput(SDL_Haptic *haptic, const Uint8 userid)
   571 {
   572     char threadName[32];
   573     XINPUT_VIBRATION vibration = { 0, 0 };  /* stop any current vibration */
   574     XINPUTSETSTATE(userid, &vibration);
   575 
   576     haptic->supported = SDL_HAPTIC_LEFTRIGHT;
   577 
   578     haptic->neffects = 1;
   579     haptic->nplaying = 1;
   580 
   581     /* Prepare effects memory. */
   582     haptic->effects = (struct haptic_effect *)
   583         SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
   584     if (haptic->effects == NULL) {
   585         return SDL_OutOfMemory();
   586     }
   587     /* Clear the memory */
   588     SDL_memset(haptic->effects, 0,
   589                sizeof(struct haptic_effect) * haptic->neffects);
   590 
   591     haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata));
   592     if (haptic->hwdata == NULL) {
   593         SDL_free(haptic->effects);
   594         haptic->effects = NULL;
   595         return SDL_OutOfMemory();
   596     }
   597     SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
   598 
   599     haptic->hwdata->bXInputHaptic = 1;
   600     haptic->hwdata->userid = userid;
   601 
   602     haptic->hwdata->mutex = SDL_CreateMutex();
   603     if (haptic->hwdata->mutex == NULL) {
   604         SDL_free(haptic->effects);
   605         SDL_free(haptic->hwdata);
   606         haptic->effects = NULL;
   607         return SDL_SetError("Couldn't create XInput haptic mutex");
   608     }
   609 
   610     SDL_snprintf(threadName, sizeof (threadName), "SDLXInputDev%d", (int) userid);
   611 
   612 #if defined(__WIN32__) && !defined(HAVE_LIBC)  /* !!! FIXME: this is nasty. */
   613     #undef SDL_CreateThread
   614     #if SDL_DYNAMIC_API
   615     haptic->hwdata->thread = SDL_CreateThread_REAL(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL);
   616     #else
   617     haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL);
   618     #endif
   619 #else
   620     haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata);
   621 #endif
   622     if (haptic->hwdata->thread == NULL) {
   623         SDL_DestroyMutex(haptic->hwdata->mutex);
   624         SDL_free(haptic->effects);
   625         SDL_free(haptic->hwdata);
   626         haptic->effects = NULL;
   627         return SDL_SetError("Couldn't create XInput haptic thread");
   628     }
   629 
   630     return 0;
   631  }
   632 
   633 /*
   634  * Opens the haptic device from the file descriptor.
   635  *
   636  *    Steps:
   637  *       - Set cooperative level.
   638  *       - Set data format.
   639  *       - Acquire exclusiveness.
   640  *       - Reset actuators.
   641  *       - Get supported features.
   642  */
   643 static int
   644 SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic,
   645                               LPDIRECTINPUTDEVICE8 device8, SDL_bool is_joystick)
   646 {
   647     HRESULT ret;
   648     DIPROPDWORD dipdw;
   649 
   650     /* Allocate the hwdata */
   651     haptic->hwdata = (struct haptic_hwdata *)SDL_malloc(sizeof(*haptic->hwdata));
   652     if (haptic->hwdata == NULL) {
   653         return SDL_OutOfMemory();
   654     }
   655     SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
   656 
   657     /* We'll use the device8 from now on. */
   658     haptic->hwdata->device = device8;
   659     haptic->hwdata->is_joystick = is_joystick;
   660 
   661     /* !!! FIXME: opening a haptic device here first will make an attempt to
   662        !!! FIXME:  SDL_JoystickOpen() that same device fail later, since we
   663        !!! FIXME:  have it open in exclusive mode. But this will allow
   664        !!! FIXME:  SDL_JoystickOpen() followed by SDL_HapticOpenFromJoystick()
   665        !!! FIXME:  to work, and that's probably the common case. Still,
   666        !!! FIXME:  ideally, We need to unify the opening code. */
   667 
   668     if (!is_joystick) {  /* if is_joystick, we already set this up elsewhere. */
   669         /* Grab it exclusively to use force feedback stuff. */
   670         ret = IDirectInputDevice8_SetCooperativeLevel(haptic->hwdata->device,
   671                                                       SDL_HelperWindow,
   672                                                       DISCL_EXCLUSIVE |
   673                                                       DISCL_BACKGROUND);
   674         if (FAILED(ret)) {
   675             DI_SetError("Setting cooperative level to exclusive", ret);
   676             goto acquire_err;
   677         }
   678 
   679         /* Set data format. */
   680         ret = IDirectInputDevice8_SetDataFormat(haptic->hwdata->device,
   681                                                 &c_dfDIJoystick2);
   682         if (FAILED(ret)) {
   683             DI_SetError("Setting data format", ret);
   684             goto acquire_err;
   685         }
   686 
   687         /* Get number of axes. */
   688         ret = IDirectInputDevice8_EnumObjects(haptic->hwdata->device,
   689                                               DI_DeviceObjectCallback,
   690                                               haptic, DIDFT_AXIS);
   691         if (FAILED(ret)) {
   692             DI_SetError("Getting device axes", ret);
   693             goto acquire_err;
   694         }
   695 
   696         /* Acquire the device. */
   697         ret = IDirectInputDevice8_Acquire(haptic->hwdata->device);
   698         if (FAILED(ret)) {
   699             DI_SetError("Acquiring DirectInput device", ret);
   700             goto acquire_err;
   701         }
   702     }
   703 
   704     /* Reset all actuators - just in case. */
   705     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
   706                                                        DISFFC_RESET);
   707     if (FAILED(ret)) {
   708         DI_SetError("Resetting device", ret);
   709         goto acquire_err;
   710     }
   711 
   712     /* Enabling actuators. */
   713     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
   714                                                        DISFFC_SETACTUATORSON);
   715     if (FAILED(ret)) {
   716         DI_SetError("Enabling actuators", ret);
   717         goto acquire_err;
   718     }
   719 
   720     /* Get supported effects. */
   721     ret = IDirectInputDevice8_EnumEffects(haptic->hwdata->device,
   722                                           DI_EffectCallback, haptic,
   723                                           DIEFT_ALL);
   724     if (FAILED(ret)) {
   725         DI_SetError("Enumerating supported effects", ret);
   726         goto acquire_err;
   727     }
   728     if (haptic->supported == 0) {       /* Error since device supports nothing. */
   729         SDL_SetError("Haptic: Internal error on finding supported effects.");
   730         goto acquire_err;
   731     }
   732 
   733     /* Check autogain and autocenter. */
   734     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
   735     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
   736     dipdw.diph.dwObj = 0;
   737     dipdw.diph.dwHow = DIPH_DEVICE;
   738     dipdw.dwData = 10000;
   739     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
   740                                           DIPROP_FFGAIN, &dipdw.diph);
   741     if (!FAILED(ret)) {         /* Gain is supported. */
   742         haptic->supported |= SDL_HAPTIC_GAIN;
   743     }
   744     dipdw.diph.dwObj = 0;
   745     dipdw.diph.dwHow = DIPH_DEVICE;
   746     dipdw.dwData = DIPROPAUTOCENTER_OFF;
   747     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
   748                                           DIPROP_AUTOCENTER, &dipdw.diph);
   749     if (!FAILED(ret)) {         /* Autocenter is supported. */
   750         haptic->supported |= SDL_HAPTIC_AUTOCENTER;
   751     }
   752 
   753     /* Status is always supported. */
   754     haptic->supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE;
   755 
   756     /* Check maximum effects. */
   757     haptic->neffects = 128;     /* This is not actually supported as thus under windows,
   758                                    there is no way to tell the number of EFFECTS that a
   759                                    device can hold, so we'll just use a "random" number
   760                                    instead and put warnings in SDL_haptic.h */
   761     haptic->nplaying = 128;     /* Even more impossible to get this then neffects. */
   762 
   763     /* Prepare effects memory. */
   764     haptic->effects = (struct haptic_effect *)
   765         SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
   766     if (haptic->effects == NULL) {
   767         SDL_OutOfMemory();
   768         goto acquire_err;
   769     }
   770     /* Clear the memory */
   771     SDL_memset(haptic->effects, 0,
   772                sizeof(struct haptic_effect) * haptic->neffects);
   773 
   774     return 0;
   775 
   776     /* Error handling */
   777   acquire_err:
   778     IDirectInputDevice8_Unacquire(haptic->hwdata->device);
   779     return -1;
   780 
   781 }
   782 
   783 
   784 /*
   785  * Opens a haptic device for usage.
   786  */
   787 int
   788 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
   789 {
   790     SDL_hapticlist_item *item = HapticByDevIndex(haptic->index);
   791     return (item->bXInputHaptic) ? SDL_SYS_HapticOpenFromXInput(haptic, item->userid) : SDL_SYS_HapticOpenFromInstance(haptic, item->instance);
   792 }
   793 
   794 
   795 /*
   796  * Opens a haptic device from first mouse it finds for usage.
   797  */
   798 int
   799 SDL_SYS_HapticMouse(void)
   800 {
   801     SDL_hapticlist_item *item;
   802     int index = 0;
   803 
   804     /* Grab the first mouse haptic device we find. */
   805     for (item = SDL_hapticlist; item != NULL; item = item->next) {
   806         SDL_assert(index >= 0);
   807         if (item->capabilities.dwDevType == DI8DEVCLASS_POINTER ) {
   808             return index;
   809         }
   810         ++index;
   811     }
   812 
   813     return -1;
   814 }
   815 
   816 
   817 /*
   818  * Checks to see if a joystick has haptic features.
   819  */
   820 int
   821 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
   822 {
   823     const struct joystick_hwdata *hwdata = joystick->hwdata;
   824     return ( (hwdata->bXInputHaptic) ||
   825              ((hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) != 0) );
   826 }
   827 
   828 
   829 /*
   830  * Checks to see if the haptic device and joystick are in reality the same.
   831  */
   832 int
   833 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
   834 {
   835     if (joystick->hwdata->bXInputHaptic != haptic->hwdata->bXInputHaptic) {
   836         return 0;  /* one is XInput, one is not; not the same device. */
   837     } else if (joystick->hwdata->bXInputHaptic) {  /* XInput */
   838         return (haptic->hwdata->userid == joystick->hwdata->userid);
   839     } else {  /* DirectInput */
   840         HRESULT ret;
   841         DIDEVICEINSTANCE hap_instance, joy_instance;
   842 
   843         hap_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   844         joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   845 
   846         /* Get the device instances. */
   847         ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device,
   848                                             &hap_instance);
   849         if (FAILED(ret)) {
   850             return 0;
   851         }
   852         ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
   853                                                 &joy_instance);
   854         if (FAILED(ret)) {
   855             return 0;
   856         }
   857 
   858         if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance))
   859             return 1;
   860     }
   861 
   862     return 0;
   863 }
   864 
   865 
   866 /*
   867  * Opens a SDL_Haptic from a SDL_Joystick.
   868  */
   869 int
   870 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
   871 {
   872     SDL_hapticlist_item *item;
   873     int index = 0;
   874 
   875     /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */
   876     if (joystick->hwdata->bXInputDevice) {
   877         const Uint8 userid = joystick->hwdata->userid;
   878         for (item = SDL_hapticlist; item != NULL; item = item->next) {
   879             if ((item->bXInputHaptic) && (item->userid == userid)) {
   880                 SDL_assert(joystick->hwdata->bXInputHaptic);
   881                 haptic->index = index;
   882                 return SDL_SYS_HapticOpenFromXInput(haptic, userid);
   883             }
   884             ++index;
   885         }
   886     } else {
   887         HRESULT idret;
   888         DIDEVICEINSTANCE joy_instance;
   889 
   890         joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   891         idret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, &joy_instance);
   892         if (FAILED(idret)) {
   893             return -1;
   894         }
   895 
   896         for (item = SDL_hapticlist; item != NULL; item = item->next) {
   897             if (DI_GUIDIsSame(&item->instance.guidInstance, &joy_instance.guidInstance)) {
   898                 haptic->index = index;
   899                 return SDL_SYS_HapticOpenFromDevice8(haptic, joystick->hwdata->InputDevice, SDL_TRUE);
   900             }
   901             ++index;
   902         }
   903     }
   904 
   905     /* No match to our haptic list */
   906     return -1;
   907 }
   908 
   909 
   910 /*
   911  * Closes the haptic device.
   912  */
   913 void
   914 SDL_SYS_HapticClose(SDL_Haptic * haptic)
   915 {
   916     if (haptic->hwdata) {
   917 
   918         /* Free effects. */
   919         SDL_free(haptic->effects);
   920         haptic->effects = NULL;
   921         haptic->neffects = 0;
   922 
   923         /* Clean up */
   924         if (haptic->hwdata->bXInputHaptic) {
   925             haptic->hwdata->stopThread = 1;
   926             SDL_WaitThread(haptic->hwdata->thread, NULL);
   927             SDL_DestroyMutex(haptic->hwdata->mutex);
   928         } else {
   929             IDirectInputDevice8_Unacquire(haptic->hwdata->device);
   930             /* Only release if isn't grabbed by a joystick. */
   931             if (haptic->hwdata->is_joystick == 0) {
   932                 IDirectInputDevice8_Release(haptic->hwdata->device);
   933             }
   934         }
   935 
   936         /* Free */
   937         SDL_free(haptic->hwdata);
   938         haptic->hwdata = NULL;
   939     }
   940 }
   941 
   942 
   943 /*
   944  * Clean up after system specific haptic stuff
   945  */
   946 void
   947 SDL_SYS_HapticQuit(void)
   948 {
   949     SDL_hapticlist_item *item;
   950     SDL_hapticlist_item *next = NULL;
   951     SDL_Haptic *hapticitem = NULL;
   952 
   953     extern SDL_Haptic *SDL_haptics;
   954     for (hapticitem = SDL_haptics; hapticitem; hapticitem = hapticitem->next) {
   955         if ((hapticitem->hwdata->bXInputHaptic) && (hapticitem->hwdata->thread)) {
   956             /* we _have_ to stop the thread before we free the XInput DLL! */
   957             hapticitem->hwdata->stopThread = 1;
   958             SDL_WaitThread(hapticitem->hwdata->thread, NULL);
   959             hapticitem->hwdata->thread = NULL;
   960         }
   961     }
   962 
   963     for (item = SDL_hapticlist; item; item = next) {
   964         /* Opened and not closed haptics are leaked, this is on purpose.
   965          * Close your haptic devices after usage. */
   966         /* !!! FIXME: (...is leaking on purpose a good idea?) */
   967         next = item->next;
   968         SDL_free(item->name);
   969         SDL_free(item);
   970     }
   971 
   972     if (loaded_xinput) {
   973         WIN_UnloadXInputDLL();
   974         loaded_xinput = SDL_FALSE;
   975     }
   976 
   977     if (dinput != NULL) {
   978         IDirectInput8_Release(dinput);
   979         dinput = NULL;
   980     }
   981 
   982     if (coinitialized) {
   983         WIN_CoUninitialize();
   984         coinitialized = SDL_FALSE;
   985     }
   986 }
   987 
   988 
   989 /*
   990  * Converts an SDL trigger button to an DIEFFECT trigger button.
   991  */
   992 static DWORD
   993 DIGetTriggerButton(Uint16 button)
   994 {
   995     DWORD dwTriggerButton;
   996 
   997     dwTriggerButton = DIEB_NOTRIGGER;
   998 
   999     if (button != 0) {
  1000         dwTriggerButton = DIJOFS_BUTTON(button - 1);
  1001     }
  1002 
  1003     return dwTriggerButton;
  1004 }
  1005 
  1006 
  1007 /*
  1008  * Sets the direction.
  1009  */
  1010 static int
  1011 SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir, int naxes)
  1012 {
  1013     LONG *rglDir;
  1014 
  1015     /* Handle no axes a part. */
  1016     if (naxes == 0) {
  1017         effect->dwFlags |= DIEFF_SPHERICAL;     /* Set as default. */
  1018         effect->rglDirection = NULL;
  1019         return 0;
  1020     }
  1021 
  1022     /* Has axes. */
  1023     rglDir = SDL_malloc(sizeof(LONG) * naxes);
  1024     if (rglDir == NULL) {
  1025         return SDL_OutOfMemory();
  1026     }
  1027     SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
  1028     effect->rglDirection = rglDir;
  1029 
  1030     switch (dir->type) {
  1031     case SDL_HAPTIC_POLAR:
  1032         effect->dwFlags |= DIEFF_POLAR;
  1033         rglDir[0] = dir->dir[0];
  1034         return 0;
  1035     case SDL_HAPTIC_CARTESIAN:
  1036         effect->dwFlags |= DIEFF_CARTESIAN;
  1037         rglDir[0] = dir->dir[0];
  1038         if (naxes > 1)
  1039             rglDir[1] = dir->dir[1];
  1040         if (naxes > 2)
  1041             rglDir[2] = dir->dir[2];
  1042         return 0;
  1043     case SDL_HAPTIC_SPHERICAL:
  1044         effect->dwFlags |= DIEFF_SPHERICAL;
  1045         rglDir[0] = dir->dir[0];
  1046         if (naxes > 1)
  1047             rglDir[1] = dir->dir[1];
  1048         if (naxes > 2)
  1049             rglDir[2] = dir->dir[2];
  1050         return 0;
  1051 
  1052     default:
  1053         return SDL_SetError("Haptic: Unknown direction type.");
  1054     }
  1055 }
  1056 
  1057 #define CONVERT(x)   (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
  1058 /*
  1059  * Creates the DIEFFECT from a SDL_HapticEffect.
  1060  */
  1061 static int
  1062 SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
  1063                    SDL_HapticEffect * src)
  1064 {
  1065     int i;
  1066     DICONSTANTFORCE *constant;
  1067     DIPERIODIC *periodic;
  1068     DICONDITION *condition;     /* Actually an array of conditions - one per axis. */
  1069     DIRAMPFORCE *ramp;
  1070     DICUSTOMFORCE *custom;
  1071     DIENVELOPE *envelope;
  1072     SDL_HapticConstant *hap_constant;
  1073     SDL_HapticPeriodic *hap_periodic;
  1074     SDL_HapticCondition *hap_condition;
  1075     SDL_HapticRamp *hap_ramp;
  1076     SDL_HapticCustom *hap_custom;
  1077     DWORD *axes;
  1078 
  1079     /* Set global stuff. */
  1080     SDL_memset(dest, 0, sizeof(DIEFFECT));
  1081     dest->dwSize = sizeof(DIEFFECT);    /* Set the structure size. */
  1082     dest->dwSamplePeriod = 0;   /* Not used by us. */
  1083     dest->dwGain = 10000;       /* Gain is set globally, not locally. */
  1084     dest->dwFlags = DIEFF_OBJECTOFFSETS;        /* Seems obligatory. */
  1085 
  1086     /* Envelope. */
  1087     envelope = SDL_malloc(sizeof(DIENVELOPE));
  1088     if (envelope == NULL) {
  1089         return SDL_OutOfMemory();
  1090     }
  1091     SDL_memset(envelope, 0, sizeof(DIENVELOPE));
  1092     dest->lpEnvelope = envelope;
  1093     envelope->dwSize = sizeof(DIENVELOPE);      /* Always should be this. */
  1094 
  1095     /* Axes. */
  1096     dest->cAxes = haptic->naxes;
  1097     if (dest->cAxes > 0) {
  1098         axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
  1099         if (axes == NULL) {
  1100             return SDL_OutOfMemory();
  1101         }
  1102         axes[0] = haptic->hwdata->axes[0];      /* Always at least one axis. */
  1103         if (dest->cAxes > 1) {
  1104             axes[1] = haptic->hwdata->axes[1];
  1105         }
  1106         if (dest->cAxes > 2) {
  1107             axes[2] = haptic->hwdata->axes[2];
  1108         }
  1109         dest->rgdwAxes = axes;
  1110     }
  1111 
  1112 
  1113     /* The big type handling switch, even bigger then Linux's version. */
  1114     switch (src->type) {
  1115     case SDL_HAPTIC_CONSTANT:
  1116         hap_constant = &src->constant;
  1117         constant = SDL_malloc(sizeof(DICONSTANTFORCE));
  1118         if (constant == NULL) {
  1119             return SDL_OutOfMemory();
  1120         }
  1121         SDL_memset(constant, 0, sizeof(DICONSTANTFORCE));
  1122 
  1123         /* Specifics */
  1124         constant->lMagnitude = CONVERT(hap_constant->level);
  1125         dest->cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
  1126         dest->lpvTypeSpecificParams = constant;
  1127 
  1128         /* Generics */
  1129         dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
  1130         dest->dwTriggerButton = DIGetTriggerButton(hap_constant->button);
  1131         dest->dwTriggerRepeatInterval = hap_constant->interval;
  1132         dest->dwStartDelay = hap_constant->delay * 1000;        /* In microseconds. */
  1133 
  1134         /* Direction. */
  1135         if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
  1136             < 0) {
  1137             return -1;
  1138         }
  1139 
  1140         /* Envelope */
  1141         if ((hap_constant->attack_length == 0)
  1142             && (hap_constant->fade_length == 0)) {
  1143             SDL_free(dest->lpEnvelope);
  1144             dest->lpEnvelope = NULL;
  1145         } else {
  1146             envelope->dwAttackLevel = CONVERT(hap_constant->attack_level);
  1147             envelope->dwAttackTime = hap_constant->attack_length * 1000;
  1148             envelope->dwFadeLevel = CONVERT(hap_constant->fade_level);
  1149             envelope->dwFadeTime = hap_constant->fade_length * 1000;
  1150         }
  1151 
  1152         break;
  1153 
  1154     case SDL_HAPTIC_SINE:
  1155     /* !!! FIXME: put this back when we have more bits in 2.1 */
  1156     /* case SDL_HAPTIC_SQUARE: */
  1157     case SDL_HAPTIC_TRIANGLE:
  1158     case SDL_HAPTIC_SAWTOOTHUP:
  1159     case SDL_HAPTIC_SAWTOOTHDOWN:
  1160         hap_periodic = &src->periodic;
  1161         periodic = SDL_malloc(sizeof(DIPERIODIC));
  1162         if (periodic == NULL) {
  1163             return SDL_OutOfMemory();
  1164         }
  1165         SDL_memset(periodic, 0, sizeof(DIPERIODIC));
  1166 
  1167         /* Specifics */
  1168         periodic->dwMagnitude = CONVERT(hap_periodic->magnitude);
  1169         periodic->lOffset = CONVERT(hap_periodic->offset);
  1170         periodic->dwPhase = hap_periodic->phase;
  1171         periodic->dwPeriod = hap_periodic->period * 1000;
  1172         dest->cbTypeSpecificParams = sizeof(DIPERIODIC);
  1173         dest->lpvTypeSpecificParams = periodic;
  1174 
  1175         /* Generics */
  1176         dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
  1177         dest->dwTriggerButton = DIGetTriggerButton(hap_periodic->button);
  1178         dest->dwTriggerRepeatInterval = hap_periodic->interval;
  1179         dest->dwStartDelay = hap_periodic->delay * 1000;        /* In microseconds. */
  1180 
  1181         /* Direction. */
  1182         if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
  1183             < 0) {
  1184             return -1;
  1185         }
  1186 
  1187         /* Envelope */
  1188         if ((hap_periodic->attack_length == 0)
  1189             && (hap_periodic->fade_length == 0)) {
  1190             SDL_free(dest->lpEnvelope);
  1191             dest->lpEnvelope = NULL;
  1192         } else {
  1193             envelope->dwAttackLevel = CONVERT(hap_periodic->attack_level);
  1194             envelope->dwAttackTime = hap_periodic->attack_length * 1000;
  1195             envelope->dwFadeLevel = CONVERT(hap_periodic->fade_level);
  1196             envelope->dwFadeTime = hap_periodic->fade_length * 1000;
  1197         }
  1198 
  1199         break;
  1200 
  1201     case SDL_HAPTIC_SPRING:
  1202     case SDL_HAPTIC_DAMPER:
  1203     case SDL_HAPTIC_INERTIA:
  1204     case SDL_HAPTIC_FRICTION:
  1205         hap_condition = &src->condition;
  1206         condition = SDL_malloc(sizeof(DICONDITION) * dest->cAxes);
  1207         if (condition == NULL) {
  1208             return SDL_OutOfMemory();
  1209         }
  1210         SDL_memset(condition, 0, sizeof(DICONDITION));
  1211 
  1212         /* Specifics */
  1213         for (i = 0; i < (int) dest->cAxes; i++) {
  1214             condition[i].lOffset = CONVERT(hap_condition->center[i]);
  1215             condition[i].lPositiveCoefficient =
  1216                 CONVERT(hap_condition->right_coeff[i]);
  1217             condition[i].lNegativeCoefficient =
  1218                 CONVERT(hap_condition->left_coeff[i]);
  1219             condition[i].dwPositiveSaturation =
  1220                 CONVERT(hap_condition->right_sat[i]);
  1221             condition[i].dwNegativeSaturation =
  1222                 CONVERT(hap_condition->left_sat[i]);
  1223             condition[i].lDeadBand = CONVERT(hap_condition->deadband[i]);
  1224         }
  1225         dest->cbTypeSpecificParams = sizeof(DICONDITION) * dest->cAxes;
  1226         dest->lpvTypeSpecificParams = condition;
  1227 
  1228         /* Generics */
  1229         dest->dwDuration = hap_condition->length * 1000;        /* In microseconds. */
  1230         dest->dwTriggerButton = DIGetTriggerButton(hap_condition->button);
  1231         dest->dwTriggerRepeatInterval = hap_condition->interval;
  1232         dest->dwStartDelay = hap_condition->delay * 1000;       /* In microseconds. */
  1233 
  1234         /* Direction. */
  1235         if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
  1236             < 0) {
  1237             return -1;
  1238         }
  1239 
  1240         /* Envelope - Not actually supported by most CONDITION implementations. */
  1241         SDL_free(dest->lpEnvelope);
  1242         dest->lpEnvelope = NULL;
  1243 
  1244         break;
  1245 
  1246     case SDL_HAPTIC_RAMP:
  1247         hap_ramp = &src->ramp;
  1248         ramp = SDL_malloc(sizeof(DIRAMPFORCE));
  1249         if (ramp == NULL) {
  1250             return SDL_OutOfMemory();
  1251         }
  1252         SDL_memset(ramp, 0, sizeof(DIRAMPFORCE));
  1253 
  1254         /* Specifics */
  1255         ramp->lStart = CONVERT(hap_ramp->start);
  1256         ramp->lEnd = CONVERT(hap_ramp->end);
  1257         dest->cbTypeSpecificParams = sizeof(DIRAMPFORCE);
  1258         dest->lpvTypeSpecificParams = ramp;
  1259 
  1260         /* Generics */
  1261         dest->dwDuration = hap_ramp->length * 1000;     /* In microseconds. */
  1262         dest->dwTriggerButton = DIGetTriggerButton(hap_ramp->button);
  1263         dest->dwTriggerRepeatInterval = hap_ramp->interval;
  1264         dest->dwStartDelay = hap_ramp->delay * 1000;    /* In microseconds. */
  1265 
  1266         /* Direction. */
  1267         if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
  1268             return -1;
  1269         }
  1270 
  1271         /* Envelope */
  1272         if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
  1273             SDL_free(dest->lpEnvelope);
  1274             dest->lpEnvelope = NULL;
  1275         } else {
  1276             envelope->dwAttackLevel = CONVERT(hap_ramp->attack_level);
  1277             envelope->dwAttackTime = hap_ramp->attack_length * 1000;
  1278             envelope->dwFadeLevel = CONVERT(hap_ramp->fade_level);
  1279             envelope->dwFadeTime = hap_ramp->fade_length * 1000;
  1280         }
  1281 
  1282         break;
  1283 
  1284     case SDL_HAPTIC_CUSTOM:
  1285         hap_custom = &src->custom;
  1286         custom = SDL_malloc(sizeof(DICUSTOMFORCE));
  1287         if (custom == NULL) {
  1288             return SDL_OutOfMemory();
  1289         }
  1290         SDL_memset(custom, 0, sizeof(DICUSTOMFORCE));
  1291 
  1292         /* Specifics */
  1293         custom->cChannels = hap_custom->channels;
  1294         custom->dwSamplePeriod = hap_custom->period * 1000;
  1295         custom->cSamples = hap_custom->samples;
  1296         custom->rglForceData =
  1297             SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
  1298         for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) {      /* Copy data. */
  1299             custom->rglForceData[i] = CONVERT(hap_custom->data[i]);
  1300         }
  1301         dest->cbTypeSpecificParams = sizeof(DICUSTOMFORCE);
  1302         dest->lpvTypeSpecificParams = custom;
  1303 
  1304         /* Generics */
  1305         dest->dwDuration = hap_custom->length * 1000;   /* In microseconds. */
  1306         dest->dwTriggerButton = DIGetTriggerButton(hap_custom->button);
  1307         dest->dwTriggerRepeatInterval = hap_custom->interval;
  1308         dest->dwStartDelay = hap_custom->delay * 1000;  /* In microseconds. */
  1309 
  1310         /* Direction. */
  1311         if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
  1312             0) {
  1313             return -1;
  1314         }
  1315 
  1316         /* Envelope */
  1317         if ((hap_custom->attack_length == 0)
  1318             && (hap_custom->fade_length == 0)) {
  1319             SDL_free(dest->lpEnvelope);
  1320             dest->lpEnvelope = NULL;
  1321         } else {
  1322             envelope->dwAttackLevel = CONVERT(hap_custom->attack_level);
  1323             envelope->dwAttackTime = hap_custom->attack_length * 1000;
  1324             envelope->dwFadeLevel = CONVERT(hap_custom->fade_level);
  1325             envelope->dwFadeTime = hap_custom->fade_length * 1000;
  1326         }
  1327 
  1328         break;
  1329 
  1330 
  1331     default:
  1332         return SDL_SetError("Haptic: Unknown effect type.");
  1333     }
  1334 
  1335     return 0;
  1336 }
  1337 
  1338 
  1339 /*
  1340  * Frees an DIEFFECT allocated by SDL_SYS_ToDIEFFECT.
  1341  */
  1342 static void
  1343 SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type)
  1344 {
  1345     DICUSTOMFORCE *custom;
  1346 
  1347     SDL_free(effect->lpEnvelope);
  1348     effect->lpEnvelope = NULL;
  1349     SDL_free(effect->rgdwAxes);
  1350     effect->rgdwAxes = NULL;
  1351     if (effect->lpvTypeSpecificParams != NULL) {
  1352         if (type == SDL_HAPTIC_CUSTOM) {        /* Must free the custom data. */
  1353             custom = (DICUSTOMFORCE *) effect->lpvTypeSpecificParams;
  1354             SDL_free(custom->rglForceData);
  1355             custom->rglForceData = NULL;
  1356         }
  1357         SDL_free(effect->lpvTypeSpecificParams);
  1358         effect->lpvTypeSpecificParams = NULL;
  1359     }
  1360     SDL_free(effect->rglDirection);
  1361     effect->rglDirection = NULL;
  1362 }
  1363 
  1364 
  1365 /*
  1366  * Gets the effect type from the generic SDL haptic effect wrapper.
  1367  */
  1368 static REFGUID
  1369 SDL_SYS_HapticEffectType(SDL_HapticEffect * effect)
  1370 {
  1371     switch (effect->type) {
  1372     case SDL_HAPTIC_CONSTANT:
  1373         return &GUID_ConstantForce;
  1374 
  1375     case SDL_HAPTIC_RAMP:
  1376         return &GUID_RampForce;
  1377 
  1378     /* !!! FIXME: put this back when we have more bits in 2.1 */
  1379     /* case SDL_HAPTIC_SQUARE:
  1380         return &GUID_Square; */
  1381 
  1382     case SDL_HAPTIC_SINE:
  1383         return &GUID_Sine;
  1384 
  1385     case SDL_HAPTIC_TRIANGLE:
  1386         return &GUID_Triangle;
  1387 
  1388     case SDL_HAPTIC_SAWTOOTHUP:
  1389         return &GUID_SawtoothUp;
  1390 
  1391     case SDL_HAPTIC_SAWTOOTHDOWN:
  1392         return &GUID_SawtoothDown;
  1393 
  1394     case SDL_HAPTIC_SPRING:
  1395         return &GUID_Spring;
  1396 
  1397     case SDL_HAPTIC_DAMPER:
  1398         return &GUID_Damper;
  1399 
  1400     case SDL_HAPTIC_INERTIA:
  1401         return &GUID_Inertia;
  1402 
  1403     case SDL_HAPTIC_FRICTION:
  1404         return &GUID_Friction;
  1405 
  1406     case SDL_HAPTIC_CUSTOM:
  1407         return &GUID_CustomForce;
  1408 
  1409     default:
  1410         return NULL;
  1411     }
  1412 }
  1413 
  1414 
  1415 /*
  1416  * Creates a new haptic effect.
  1417  */
  1418 int
  1419 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
  1420                         SDL_HapticEffect * base)
  1421 {
  1422     HRESULT ret;
  1423     REFGUID type = SDL_SYS_HapticEffectType(base);
  1424 
  1425     if ((type == NULL) && (!haptic->hwdata->bXInputHaptic)) {
  1426         SDL_SetError("Haptic: Unknown effect type.");
  1427         goto err_hweffect;
  1428     }
  1429 
  1430     /* Alloc the effect. */
  1431     effect->hweffect = (struct haptic_hweffect *)
  1432         SDL_malloc(sizeof(struct haptic_hweffect));
  1433     if (effect->hweffect == NULL) {
  1434         SDL_OutOfMemory();
  1435         goto err_hweffect;
  1436     }
  1437 
  1438     SDL_zerop(effect->hweffect);
  1439 
  1440     if (haptic->hwdata->bXInputHaptic) {
  1441         SDL_assert(base->type == SDL_HAPTIC_LEFTRIGHT);  /* should catch this at higher level */
  1442         return SDL_SYS_HapticUpdateEffect(haptic, effect, base);
  1443     }
  1444 
  1445     /* Get the effect. */
  1446     if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
  1447         goto err_effectdone;
  1448     }
  1449 
  1450     /* Create the actual effect. */
  1451     ret = IDirectInputDevice8_CreateEffect(haptic->hwdata->device, type,
  1452                                            &effect->hweffect->effect,
  1453                                            &effect->hweffect->ref, NULL);
  1454     if (FAILED(ret)) {
  1455         DI_SetError("Unable to create effect", ret);
  1456         goto err_effectdone;
  1457     }
  1458 
  1459     return 0;
  1460 
  1461   err_effectdone:
  1462     SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, base->type);
  1463   err_hweffect:
  1464     SDL_free(effect->hweffect);
  1465     effect->hweffect = NULL;
  1466     return -1;
  1467 }
  1468 
  1469 
  1470 /*
  1471  * Updates an effect.
  1472  */
  1473 int
  1474 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
  1475                            struct haptic_effect *effect,
  1476                            SDL_HapticEffect * data)
  1477 {
  1478     HRESULT ret;
  1479     DWORD flags;
  1480     DIEFFECT temp;
  1481 
  1482     if (haptic->hwdata->bXInputHaptic) {
  1483         XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
  1484         SDL_assert(data->type == SDL_HAPTIC_LEFTRIGHT);
  1485         vib->wLeftMotorSpeed = data->leftright.large_magnitude;
  1486         vib->wRightMotorSpeed = data->leftright.small_magnitude;
  1487         SDL_LockMutex(haptic->hwdata->mutex);
  1488         if (haptic->hwdata->stopTicks) {  /* running right now? Update it. */
  1489             XINPUTSETSTATE(haptic->hwdata->userid, vib);
  1490         }
  1491         SDL_UnlockMutex(haptic->hwdata->mutex);
  1492         return 0;
  1493     }
  1494 
  1495     /* Get the effect. */
  1496     SDL_memset(&temp, 0, sizeof(DIEFFECT));
  1497     if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) {
  1498         goto err_update;
  1499     }
  1500 
  1501     /* Set the flags.  Might be worthwhile to diff temp with loaded effect and
  1502      *  only change those parameters. */
  1503     flags = DIEP_DIRECTION |
  1504         DIEP_DURATION |
  1505         DIEP_ENVELOPE |
  1506         DIEP_STARTDELAY |
  1507         DIEP_TRIGGERBUTTON |
  1508         DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS;
  1509 
  1510     /* Create the actual effect. */
  1511     ret =
  1512         IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags);
  1513     if (FAILED(ret)) {
  1514         DI_SetError("Unable to update effect", ret);
  1515         goto err_update;
  1516     }
  1517 
  1518     /* Copy it over. */
  1519     SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, data->type);
  1520     SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(DIEFFECT));
  1521 
  1522     return 0;
  1523 
  1524   err_update:
  1525     SDL_SYS_HapticFreeDIEFFECT(&temp, data->type);
  1526     return -1;
  1527 }
  1528 
  1529 
  1530 /*
  1531  * Runs an effect.
  1532  */
  1533 int
  1534 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
  1535                         Uint32 iterations)
  1536 {
  1537     HRESULT ret;
  1538     DWORD iter;
  1539 
  1540     if (haptic->hwdata->bXInputHaptic) {
  1541         XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
  1542         SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT);  /* should catch this at higher level */
  1543         SDL_LockMutex(haptic->hwdata->mutex);
  1544         if(effect->effect.leftright.length == SDL_HAPTIC_INFINITY || iterations == SDL_HAPTIC_INFINITY) {
  1545             haptic->hwdata->stopTicks = SDL_HAPTIC_INFINITY;
  1546         } else if ((!effect->effect.leftright.length) || (!iterations)) {
  1547             /* do nothing. Effect runs for zero milliseconds. */
  1548         } else {
  1549             haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations);
  1550             if ((haptic->hwdata->stopTicks == SDL_HAPTIC_INFINITY) || (haptic->hwdata->stopTicks == 0)) {
  1551                 haptic->hwdata->stopTicks = 1;  /* fix edge cases. */
  1552             }
  1553         }
  1554         SDL_UnlockMutex(haptic->hwdata->mutex);
  1555         return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1;
  1556     }
  1557 
  1558     /* Check if it's infinite. */
  1559     if (iterations == SDL_HAPTIC_INFINITY) {
  1560         iter = INFINITE;
  1561     } else
  1562         iter = iterations;
  1563 
  1564     /* Run the effect. */
  1565     ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0);
  1566     if (FAILED(ret)) {
  1567         return DI_SetError("Running the effect", ret);
  1568     }
  1569 
  1570     return 0;
  1571 }
  1572 
  1573 
  1574 /*
  1575  * Stops an effect.
  1576  */
  1577 int
  1578 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1579 {
  1580     HRESULT ret;
  1581 
  1582     if (haptic->hwdata->bXInputHaptic) {
  1583         XINPUT_VIBRATION vibration = { 0, 0 };
  1584         SDL_LockMutex(haptic->hwdata->mutex);
  1585         haptic->hwdata->stopTicks = 0;
  1586         SDL_UnlockMutex(haptic->hwdata->mutex);
  1587         return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
  1588     }
  1589 
  1590     ret = IDirectInputEffect_Stop(effect->hweffect->ref);
  1591     if (FAILED(ret)) {
  1592         return DI_SetError("Unable to stop effect", ret);
  1593     }
  1594 
  1595     return 0;
  1596 }
  1597 
  1598 
  1599 /*
  1600  * Frees the effect.
  1601  */
  1602 void
  1603 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1604 {
  1605     HRESULT ret;
  1606 
  1607     if (haptic->hwdata->bXInputHaptic) {
  1608         SDL_SYS_HapticStopEffect(haptic, effect);
  1609     } else {
  1610         ret = IDirectInputEffect_Unload(effect->hweffect->ref);
  1611         if (FAILED(ret)) {
  1612             DI_SetError("Removing effect from the device", ret);
  1613         }
  1614         SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
  1615                                    effect->effect.type);
  1616     }
  1617     SDL_free(effect->hweffect);
  1618     effect->hweffect = NULL;
  1619 }
  1620 
  1621 
  1622 /*
  1623  * Gets the status of a haptic effect.
  1624  */
  1625 int
  1626 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
  1627                               struct haptic_effect *effect)
  1628 {
  1629     HRESULT ret;
  1630     DWORD status;
  1631 
  1632     ret = IDirectInputEffect_GetEffectStatus(effect->hweffect->ref, &status);
  1633     if (FAILED(ret)) {
  1634         return DI_SetError("Getting effect status", ret);
  1635     }
  1636 
  1637     if (status == 0)
  1638         return SDL_FALSE;
  1639     return SDL_TRUE;
  1640 }
  1641 
  1642 
  1643 /*
  1644  * Sets the gain.
  1645  */
  1646 int
  1647 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
  1648 {
  1649     HRESULT ret;
  1650     DIPROPDWORD dipdw;
  1651 
  1652     /* Create the weird structure thingy. */
  1653     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  1654     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1655     dipdw.diph.dwObj = 0;
  1656     dipdw.diph.dwHow = DIPH_DEVICE;
  1657     dipdw.dwData = gain * 100;  /* 0 to 10,000 */
  1658 
  1659     /* Try to set the autocenter. */
  1660     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
  1661                                           DIPROP_FFGAIN, &dipdw.diph);
  1662     if (FAILED(ret)) {
  1663         return DI_SetError("Setting gain", ret);
  1664     }
  1665 
  1666     return 0;
  1667 }
  1668 
  1669 
  1670 /*
  1671  * Sets the autocentering.
  1672  */
  1673 int
  1674 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
  1675 {
  1676     HRESULT ret;
  1677     DIPROPDWORD dipdw;
  1678 
  1679     /* Create the weird structure thingy. */
  1680     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  1681     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1682     dipdw.diph.dwObj = 0;
  1683     dipdw.diph.dwHow = DIPH_DEVICE;
  1684     dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF :
  1685         DIPROPAUTOCENTER_ON;
  1686 
  1687     /* Try to set the autocenter. */
  1688     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
  1689                                           DIPROP_AUTOCENTER, &dipdw.diph);
  1690     if (FAILED(ret)) {
  1691         return DI_SetError("Setting autocenter", ret);
  1692     }
  1693 
  1694     return 0;
  1695 }
  1696 
  1697 
  1698 /*
  1699  * Pauses the device.
  1700  */
  1701 int
  1702 SDL_SYS_HapticPause(SDL_Haptic * haptic)
  1703 {
  1704     HRESULT ret;
  1705 
  1706     /* Pause the device. */
  1707     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1708                                                        DISFFC_PAUSE);
  1709     if (FAILED(ret)) {
  1710         return DI_SetError("Pausing the device", ret);
  1711     }
  1712 
  1713     return 0;
  1714 }
  1715 
  1716 
  1717 /*
  1718  * Pauses the device.
  1719  */
  1720 int
  1721 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
  1722 {
  1723     HRESULT ret;
  1724 
  1725     /* Unpause the device. */
  1726     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1727                                                        DISFFC_CONTINUE);
  1728     if (FAILED(ret)) {
  1729         return DI_SetError("Pausing the device", ret);
  1730     }
  1731 
  1732     return 0;
  1733 }
  1734 
  1735 
  1736 /*
  1737  * Stops all the playing effects on the device.
  1738  */
  1739 int
  1740 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
  1741 {
  1742     HRESULT ret;
  1743 
  1744     if (haptic->hwdata->bXInputHaptic) {
  1745         XINPUT_VIBRATION vibration = { 0, 0 };
  1746         SDL_LockMutex(haptic->hwdata->mutex);
  1747         haptic->hwdata->stopTicks = 0;
  1748         SDL_UnlockMutex(haptic->hwdata->mutex);
  1749         return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
  1750     }
  1751 
  1752     /* Try to stop the effects. */
  1753     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1754                                                        DISFFC_STOPALL);
  1755     if (FAILED(ret)) {
  1756         return DI_SetError("Stopping the device", ret);
  1757     }
  1758 
  1759     return 0;
  1760 }
  1761 
  1762 
  1763 /* !!! FIXME: this is a hack, remove this later. */
  1764 /* Since XInput doesn't offer a way to vibrate for X time, we hook into
  1765  *  SDL_PumpEvents() to check if it's time to stop vibrating with some
  1766  *  frequency.
  1767  * In practice, this works for 99% of use cases. But in an ideal world,
  1768  *  we do this in a separate thread so that:
  1769  *    - we aren't bound to when the app chooses to pump the event queue.
  1770  *    - we aren't adding more polling to the event queue
  1771  *    - we can emulate all the haptic effects correctly (start on a delay,
  1772  *      mix multiple effects, etc).
  1773  *
  1774  * Mostly, this is here to get rumbling to work, and all the other features
  1775  *  are absent in the XInput path for now.  :(
  1776  */
  1777 static int SDLCALL
  1778 SDL_RunXInputHaptic(void *arg)
  1779 {
  1780     struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg;
  1781 
  1782     while (!hwdata->stopThread) {
  1783         SDL_Delay(50);
  1784         SDL_LockMutex(hwdata->mutex);
  1785         /* If we're currently running and need to stop... */
  1786         if (hwdata->stopTicks) {
  1787             if ((hwdata->stopTicks != SDL_HAPTIC_INFINITY) && SDL_TICKS_PASSED(SDL_GetTicks(), hwdata->stopTicks)) {
  1788                 XINPUT_VIBRATION vibration = { 0, 0 };
  1789                 hwdata->stopTicks = 0;
  1790                 XINPUTSETSTATE(hwdata->userid, &vibration);
  1791             }
  1792         }
  1793         SDL_UnlockMutex(hwdata->mutex);
  1794     }
  1795 
  1796     return 0;
  1797 }
  1798 
  1799 #endif /* SDL_HAPTIC_DINPUT */
  1800 
  1801 /* vi: set ts=4 sw=4 expandtab: */