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