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