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