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