src/haptic/windows/SDL_syshaptic.c
author Gabriel Jacobo <gabomdq@gmail.com>
Mon, 24 Feb 2014 10:25:02 -0300
changeset 8247 fd220c4df418
parent 8185 c2996dc8ef6b
child 8281 f280c6b5d08c
permissions -rw-r--r--
Zero out haptic linked list items on creation

(thanks to Turo Lamminen for the report!)
     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 
   943     if (loaded_xinput) {
   944         WIN_UnloadXInputDLL();
   945         loaded_xinput = SDL_FALSE;
   946     }
   947 
   948     for (item = SDL_hapticlist; item; item = next) {
   949         /* Opened and not closed haptics are leaked, this is on purpose.
   950          * Close your haptic devices after usage. */
   951         next = item->next;
   952         SDL_free(item->name);
   953         SDL_free(item);
   954     }
   955 
   956     if (dinput != NULL) {
   957         IDirectInput8_Release(dinput);
   958         dinput = NULL;
   959     }
   960 
   961     if (coinitialized) {
   962         WIN_CoUninitialize();
   963         coinitialized = SDL_FALSE;
   964     }
   965 }
   966 
   967 
   968 /*
   969  * Converts an SDL trigger button to an DIEFFECT trigger button.
   970  */
   971 static DWORD
   972 DIGetTriggerButton(Uint16 button)
   973 {
   974     DWORD dwTriggerButton;
   975 
   976     dwTriggerButton = DIEB_NOTRIGGER;
   977 
   978     if (button != 0) {
   979         dwTriggerButton = DIJOFS_BUTTON(button - 1);
   980     }
   981 
   982     return dwTriggerButton;
   983 }
   984 
   985 
   986 /*
   987  * Sets the direction.
   988  */
   989 static int
   990 SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir, int naxes)
   991 {
   992     LONG *rglDir;
   993 
   994     /* Handle no axes a part. */
   995     if (naxes == 0) {
   996         effect->dwFlags |= DIEFF_SPHERICAL;     /* Set as default. */
   997         effect->rglDirection = NULL;
   998         return 0;
   999     }
  1000 
  1001     /* Has axes. */
  1002     rglDir = SDL_malloc(sizeof(LONG) * naxes);
  1003     if (rglDir == NULL) {
  1004         return SDL_OutOfMemory();
  1005     }
  1006     SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
  1007     effect->rglDirection = rglDir;
  1008 
  1009     switch (dir->type) {
  1010     case SDL_HAPTIC_POLAR:
  1011         effect->dwFlags |= DIEFF_POLAR;
  1012         rglDir[0] = dir->dir[0];
  1013         return 0;
  1014     case SDL_HAPTIC_CARTESIAN:
  1015         effect->dwFlags |= DIEFF_CARTESIAN;
  1016         rglDir[0] = dir->dir[0];
  1017         if (naxes > 1)
  1018             rglDir[1] = dir->dir[1];
  1019         if (naxes > 2)
  1020             rglDir[2] = dir->dir[2];
  1021         return 0;
  1022     case SDL_HAPTIC_SPHERICAL:
  1023         effect->dwFlags |= DIEFF_SPHERICAL;
  1024         rglDir[0] = dir->dir[0];
  1025         if (naxes > 1)
  1026             rglDir[1] = dir->dir[1];
  1027         if (naxes > 2)
  1028             rglDir[2] = dir->dir[2];
  1029         return 0;
  1030 
  1031     default:
  1032         return SDL_SetError("Haptic: Unknown direction type.");
  1033     }
  1034 }
  1035 
  1036 #define CONVERT(x)   (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
  1037 /*
  1038  * Creates the DIEFFECT from a SDL_HapticEffect.
  1039  */
  1040 static int
  1041 SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
  1042                    SDL_HapticEffect * src)
  1043 {
  1044     int i;
  1045     DICONSTANTFORCE *constant;
  1046     DIPERIODIC *periodic;
  1047     DICONDITION *condition;     /* Actually an array of conditions - one per axis. */
  1048     DIRAMPFORCE *ramp;
  1049     DICUSTOMFORCE *custom;
  1050     DIENVELOPE *envelope;
  1051     SDL_HapticConstant *hap_constant;
  1052     SDL_HapticPeriodic *hap_periodic;
  1053     SDL_HapticCondition *hap_condition;
  1054     SDL_HapticRamp *hap_ramp;
  1055     SDL_HapticCustom *hap_custom;
  1056     DWORD *axes;
  1057 
  1058     /* Set global stuff. */
  1059     SDL_memset(dest, 0, sizeof(DIEFFECT));
  1060     dest->dwSize = sizeof(DIEFFECT);    /* Set the structure size. */
  1061     dest->dwSamplePeriod = 0;   /* Not used by us. */
  1062     dest->dwGain = 10000;       /* Gain is set globally, not locally. */
  1063     dest->dwFlags = DIEFF_OBJECTOFFSETS;        /* Seems obligatory. */
  1064 
  1065     /* Envelope. */
  1066     envelope = SDL_malloc(sizeof(DIENVELOPE));
  1067     if (envelope == NULL) {
  1068         return SDL_OutOfMemory();
  1069     }
  1070     SDL_memset(envelope, 0, sizeof(DIENVELOPE));
  1071     dest->lpEnvelope = envelope;
  1072     envelope->dwSize = sizeof(DIENVELOPE);      /* Always should be this. */
  1073 
  1074     /* Axes. */
  1075     dest->cAxes = haptic->naxes;
  1076     if (dest->cAxes > 0) {
  1077         axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
  1078         if (axes == NULL) {
  1079             return SDL_OutOfMemory();
  1080         }
  1081         axes[0] = haptic->hwdata->axes[0];      /* Always at least one axis. */
  1082         if (dest->cAxes > 1) {
  1083             axes[1] = haptic->hwdata->axes[1];
  1084         }
  1085         if (dest->cAxes > 2) {
  1086             axes[2] = haptic->hwdata->axes[2];
  1087         }
  1088         dest->rgdwAxes = axes;
  1089     }
  1090 
  1091 
  1092     /* The big type handling switch, even bigger then Linux's version. */
  1093     switch (src->type) {
  1094     case SDL_HAPTIC_CONSTANT:
  1095         hap_constant = &src->constant;
  1096         constant = SDL_malloc(sizeof(DICONSTANTFORCE));
  1097         if (constant == NULL) {
  1098             return SDL_OutOfMemory();
  1099         }
  1100         SDL_memset(constant, 0, sizeof(DICONSTANTFORCE));
  1101 
  1102         /* Specifics */
  1103         constant->lMagnitude = CONVERT(hap_constant->level);
  1104         dest->cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
  1105         dest->lpvTypeSpecificParams = constant;
  1106 
  1107         /* Generics */
  1108         dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
  1109         dest->dwTriggerButton = DIGetTriggerButton(hap_constant->button);
  1110         dest->dwTriggerRepeatInterval = hap_constant->interval;
  1111         dest->dwStartDelay = hap_constant->delay * 1000;        /* In microseconds. */
  1112 
  1113         /* Direction. */
  1114         if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
  1115             < 0) {
  1116             return -1;
  1117         }
  1118 
  1119         /* Envelope */
  1120         if ((hap_constant->attack_length == 0)
  1121             && (hap_constant->fade_length == 0)) {
  1122             SDL_free(dest->lpEnvelope);
  1123             dest->lpEnvelope = NULL;
  1124         } else {
  1125             envelope->dwAttackLevel = CONVERT(hap_constant->attack_level);
  1126             envelope->dwAttackTime = hap_constant->attack_length * 1000;
  1127             envelope->dwFadeLevel = CONVERT(hap_constant->fade_level);
  1128             envelope->dwFadeTime = hap_constant->fade_length * 1000;
  1129         }
  1130 
  1131         break;
  1132 
  1133     case SDL_HAPTIC_SINE:
  1134     /* !!! FIXME: put this back when we have more bits in 2.1 */
  1135     /* case SDL_HAPTIC_SQUARE: */
  1136     case SDL_HAPTIC_TRIANGLE:
  1137     case SDL_HAPTIC_SAWTOOTHUP:
  1138     case SDL_HAPTIC_SAWTOOTHDOWN:
  1139         hap_periodic = &src->periodic;
  1140         periodic = SDL_malloc(sizeof(DIPERIODIC));
  1141         if (periodic == NULL) {
  1142             return SDL_OutOfMemory();
  1143         }
  1144         SDL_memset(periodic, 0, sizeof(DIPERIODIC));
  1145 
  1146         /* Specifics */
  1147         periodic->dwMagnitude = CONVERT(hap_periodic->magnitude);
  1148         periodic->lOffset = CONVERT(hap_periodic->offset);
  1149         periodic->dwPhase = hap_periodic->phase;
  1150         periodic->dwPeriod = hap_periodic->period * 1000;
  1151         dest->cbTypeSpecificParams = sizeof(DIPERIODIC);
  1152         dest->lpvTypeSpecificParams = periodic;
  1153 
  1154         /* Generics */
  1155         dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
  1156         dest->dwTriggerButton = DIGetTriggerButton(hap_periodic->button);
  1157         dest->dwTriggerRepeatInterval = hap_periodic->interval;
  1158         dest->dwStartDelay = hap_periodic->delay * 1000;        /* In microseconds. */
  1159 
  1160         /* Direction. */
  1161         if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
  1162             < 0) {
  1163             return -1;
  1164         }
  1165 
  1166         /* Envelope */
  1167         if ((hap_periodic->attack_length == 0)
  1168             && (hap_periodic->fade_length == 0)) {
  1169             SDL_free(dest->lpEnvelope);
  1170             dest->lpEnvelope = NULL;
  1171         } else {
  1172             envelope->dwAttackLevel = CONVERT(hap_periodic->attack_level);
  1173             envelope->dwAttackTime = hap_periodic->attack_length * 1000;
  1174             envelope->dwFadeLevel = CONVERT(hap_periodic->fade_level);
  1175             envelope->dwFadeTime = hap_periodic->fade_length * 1000;
  1176         }
  1177 
  1178         break;
  1179 
  1180     case SDL_HAPTIC_SPRING:
  1181     case SDL_HAPTIC_DAMPER:
  1182     case SDL_HAPTIC_INERTIA:
  1183     case SDL_HAPTIC_FRICTION:
  1184         hap_condition = &src->condition;
  1185         condition = SDL_malloc(sizeof(DICONDITION) * dest->cAxes);
  1186         if (condition == NULL) {
  1187             return SDL_OutOfMemory();
  1188         }
  1189         SDL_memset(condition, 0, sizeof(DICONDITION));
  1190 
  1191         /* Specifics */
  1192         for (i = 0; i < (int) dest->cAxes; i++) {
  1193             condition[i].lOffset = CONVERT(hap_condition->center[i]);
  1194             condition[i].lPositiveCoefficient =
  1195                 CONVERT(hap_condition->right_coeff[i]);
  1196             condition[i].lNegativeCoefficient =
  1197                 CONVERT(hap_condition->left_coeff[i]);
  1198             condition[i].dwPositiveSaturation =
  1199                 CONVERT(hap_condition->right_sat[i]);
  1200             condition[i].dwNegativeSaturation =
  1201                 CONVERT(hap_condition->left_sat[i]);
  1202             condition[i].lDeadBand = CONVERT(hap_condition->deadband[i]);
  1203         }
  1204         dest->cbTypeSpecificParams = sizeof(DICONDITION) * dest->cAxes;
  1205         dest->lpvTypeSpecificParams = condition;
  1206 
  1207         /* Generics */
  1208         dest->dwDuration = hap_condition->length * 1000;        /* In microseconds. */
  1209         dest->dwTriggerButton = DIGetTriggerButton(hap_condition->button);
  1210         dest->dwTriggerRepeatInterval = hap_condition->interval;
  1211         dest->dwStartDelay = hap_condition->delay * 1000;       /* In microseconds. */
  1212 
  1213         /* Direction. */
  1214         if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
  1215             < 0) {
  1216             return -1;
  1217         }
  1218 
  1219         /* Envelope - Not actually supported by most CONDITION implementations. */
  1220         SDL_free(dest->lpEnvelope);
  1221         dest->lpEnvelope = NULL;
  1222 
  1223         break;
  1224 
  1225     case SDL_HAPTIC_RAMP:
  1226         hap_ramp = &src->ramp;
  1227         ramp = SDL_malloc(sizeof(DIRAMPFORCE));
  1228         if (ramp == NULL) {
  1229             return SDL_OutOfMemory();
  1230         }
  1231         SDL_memset(ramp, 0, sizeof(DIRAMPFORCE));
  1232 
  1233         /* Specifics */
  1234         ramp->lStart = CONVERT(hap_ramp->start);
  1235         ramp->lEnd = CONVERT(hap_ramp->end);
  1236         dest->cbTypeSpecificParams = sizeof(DIRAMPFORCE);
  1237         dest->lpvTypeSpecificParams = ramp;
  1238 
  1239         /* Generics */
  1240         dest->dwDuration = hap_ramp->length * 1000;     /* In microseconds. */
  1241         dest->dwTriggerButton = DIGetTriggerButton(hap_ramp->button);
  1242         dest->dwTriggerRepeatInterval = hap_ramp->interval;
  1243         dest->dwStartDelay = hap_ramp->delay * 1000;    /* In microseconds. */
  1244 
  1245         /* Direction. */
  1246         if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
  1247             return -1;
  1248         }
  1249 
  1250         /* Envelope */
  1251         if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
  1252             SDL_free(dest->lpEnvelope);
  1253             dest->lpEnvelope = NULL;
  1254         } else {
  1255             envelope->dwAttackLevel = CONVERT(hap_ramp->attack_level);
  1256             envelope->dwAttackTime = hap_ramp->attack_length * 1000;
  1257             envelope->dwFadeLevel = CONVERT(hap_ramp->fade_level);
  1258             envelope->dwFadeTime = hap_ramp->fade_length * 1000;
  1259         }
  1260 
  1261         break;
  1262 
  1263     case SDL_HAPTIC_CUSTOM:
  1264         hap_custom = &src->custom;
  1265         custom = SDL_malloc(sizeof(DICUSTOMFORCE));
  1266         if (custom == NULL) {
  1267             return SDL_OutOfMemory();
  1268         }
  1269         SDL_memset(custom, 0, sizeof(DICUSTOMFORCE));
  1270 
  1271         /* Specifics */
  1272         custom->cChannels = hap_custom->channels;
  1273         custom->dwSamplePeriod = hap_custom->period * 1000;
  1274         custom->cSamples = hap_custom->samples;
  1275         custom->rglForceData =
  1276             SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
  1277         for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) {      /* Copy data. */
  1278             custom->rglForceData[i] = CONVERT(hap_custom->data[i]);
  1279         }
  1280         dest->cbTypeSpecificParams = sizeof(DICUSTOMFORCE);
  1281         dest->lpvTypeSpecificParams = custom;
  1282 
  1283         /* Generics */
  1284         dest->dwDuration = hap_custom->length * 1000;   /* In microseconds. */
  1285         dest->dwTriggerButton = DIGetTriggerButton(hap_custom->button);
  1286         dest->dwTriggerRepeatInterval = hap_custom->interval;
  1287         dest->dwStartDelay = hap_custom->delay * 1000;  /* In microseconds. */
  1288 
  1289         /* Direction. */
  1290         if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
  1291             0) {
  1292             return -1;
  1293         }
  1294 
  1295         /* Envelope */
  1296         if ((hap_custom->attack_length == 0)
  1297             && (hap_custom->fade_length == 0)) {
  1298             SDL_free(dest->lpEnvelope);
  1299             dest->lpEnvelope = NULL;
  1300         } else {
  1301             envelope->dwAttackLevel = CONVERT(hap_custom->attack_level);
  1302             envelope->dwAttackTime = hap_custom->attack_length * 1000;
  1303             envelope->dwFadeLevel = CONVERT(hap_custom->fade_level);
  1304             envelope->dwFadeTime = hap_custom->fade_length * 1000;
  1305         }
  1306 
  1307         break;
  1308 
  1309 
  1310     default:
  1311         return SDL_SetError("Haptic: Unknown effect type.");
  1312     }
  1313 
  1314     return 0;
  1315 }
  1316 
  1317 
  1318 /*
  1319  * Frees an DIEFFECT allocated by SDL_SYS_ToDIEFFECT.
  1320  */
  1321 static void
  1322 SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type)
  1323 {
  1324     DICUSTOMFORCE *custom;
  1325 
  1326     SDL_free(effect->lpEnvelope);
  1327     effect->lpEnvelope = NULL;
  1328     SDL_free(effect->rgdwAxes);
  1329     effect->rgdwAxes = NULL;
  1330     if (effect->lpvTypeSpecificParams != NULL) {
  1331         if (type == SDL_HAPTIC_CUSTOM) {        /* Must free the custom data. */
  1332             custom = (DICUSTOMFORCE *) effect->lpvTypeSpecificParams;
  1333             SDL_free(custom->rglForceData);
  1334             custom->rglForceData = NULL;
  1335         }
  1336         SDL_free(effect->lpvTypeSpecificParams);
  1337         effect->lpvTypeSpecificParams = NULL;
  1338     }
  1339     SDL_free(effect->rglDirection);
  1340     effect->rglDirection = NULL;
  1341 }
  1342 
  1343 
  1344 /*
  1345  * Gets the effect type from the generic SDL haptic effect wrapper.
  1346  */
  1347 static REFGUID
  1348 SDL_SYS_HapticEffectType(SDL_HapticEffect * effect)
  1349 {
  1350     switch (effect->type) {
  1351     case SDL_HAPTIC_CONSTANT:
  1352         return &GUID_ConstantForce;
  1353 
  1354     case SDL_HAPTIC_RAMP:
  1355         return &GUID_RampForce;
  1356 
  1357     /* !!! FIXME: put this back when we have more bits in 2.1 */
  1358     /* case SDL_HAPTIC_SQUARE:
  1359         return &GUID_Square; */
  1360 
  1361     case SDL_HAPTIC_SINE:
  1362         return &GUID_Sine;
  1363 
  1364     case SDL_HAPTIC_TRIANGLE:
  1365         return &GUID_Triangle;
  1366 
  1367     case SDL_HAPTIC_SAWTOOTHUP:
  1368         return &GUID_SawtoothUp;
  1369 
  1370     case SDL_HAPTIC_SAWTOOTHDOWN:
  1371         return &GUID_SawtoothDown;
  1372 
  1373     case SDL_HAPTIC_SPRING:
  1374         return &GUID_Spring;
  1375 
  1376     case SDL_HAPTIC_DAMPER:
  1377         return &GUID_Damper;
  1378 
  1379     case SDL_HAPTIC_INERTIA:
  1380         return &GUID_Inertia;
  1381 
  1382     case SDL_HAPTIC_FRICTION:
  1383         return &GUID_Friction;
  1384 
  1385     case SDL_HAPTIC_CUSTOM:
  1386         return &GUID_CustomForce;
  1387 
  1388     default:
  1389         return NULL;
  1390     }
  1391 }
  1392 
  1393 
  1394 /*
  1395  * Creates a new haptic effect.
  1396  */
  1397 int
  1398 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
  1399                         SDL_HapticEffect * base)
  1400 {
  1401     HRESULT ret;
  1402     REFGUID type = SDL_SYS_HapticEffectType(base);
  1403 
  1404     if ((type == NULL) && (!haptic->hwdata->bXInputHaptic)) {
  1405         SDL_SetError("Haptic: Unknown effect type.");
  1406         goto err_hweffect;
  1407     }
  1408 
  1409     /* Alloc the effect. */
  1410     effect->hweffect = (struct haptic_hweffect *)
  1411         SDL_malloc(sizeof(struct haptic_hweffect));
  1412     if (effect->hweffect == NULL) {
  1413         SDL_OutOfMemory();
  1414         goto err_hweffect;
  1415     }
  1416 
  1417     SDL_zerop(effect->hweffect);
  1418 
  1419     if (haptic->hwdata->bXInputHaptic) {
  1420         SDL_assert(base->type == SDL_HAPTIC_LEFTRIGHT);  /* should catch this at higher level */
  1421         return SDL_SYS_HapticUpdateEffect(haptic, effect, base);
  1422     }
  1423 
  1424     /* Get the effect. */
  1425     if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
  1426         goto err_effectdone;
  1427     }
  1428 
  1429     /* Create the actual effect. */
  1430     ret = IDirectInputDevice8_CreateEffect(haptic->hwdata->device, type,
  1431                                            &effect->hweffect->effect,
  1432                                            &effect->hweffect->ref, NULL);
  1433     if (FAILED(ret)) {
  1434         DI_SetError("Unable to create effect", ret);
  1435         goto err_effectdone;
  1436     }
  1437 
  1438     return 0;
  1439 
  1440   err_effectdone:
  1441     SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, base->type);
  1442   err_hweffect:
  1443     SDL_free(effect->hweffect);
  1444     effect->hweffect = NULL;
  1445     return -1;
  1446 }
  1447 
  1448 
  1449 /*
  1450  * Updates an effect.
  1451  */
  1452 int
  1453 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
  1454                            struct haptic_effect *effect,
  1455                            SDL_HapticEffect * data)
  1456 {
  1457     HRESULT ret;
  1458     DWORD flags;
  1459     DIEFFECT temp;
  1460 
  1461     if (haptic->hwdata->bXInputHaptic) {
  1462         XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
  1463         SDL_assert(data->type == SDL_HAPTIC_LEFTRIGHT);
  1464         vib->wLeftMotorSpeed = data->leftright.large_magnitude;
  1465         vib->wRightMotorSpeed = data->leftright.small_magnitude;
  1466         SDL_LockMutex(haptic->hwdata->mutex);
  1467         if (haptic->hwdata->stopTicks) {  /* running right now? Update it. */
  1468             XINPUTSETSTATE(haptic->hwdata->userid, vib);
  1469         }
  1470         SDL_UnlockMutex(haptic->hwdata->mutex);
  1471         return 0;
  1472     }
  1473 
  1474     /* Get the effect. */
  1475     SDL_memset(&temp, 0, sizeof(DIEFFECT));
  1476     if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) {
  1477         goto err_update;
  1478     }
  1479 
  1480     /* Set the flags.  Might be worthwhile to diff temp with loaded effect and
  1481      *  only change those parameters. */
  1482     flags = DIEP_DIRECTION |
  1483         DIEP_DURATION |
  1484         DIEP_ENVELOPE |
  1485         DIEP_STARTDELAY |
  1486         DIEP_TRIGGERBUTTON |
  1487         DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS;
  1488 
  1489     /* Create the actual effect. */
  1490     ret =
  1491         IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags);
  1492     if (FAILED(ret)) {
  1493         DI_SetError("Unable to update effect", ret);
  1494         goto err_update;
  1495     }
  1496 
  1497     /* Copy it over. */
  1498     SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, data->type);
  1499     SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(DIEFFECT));
  1500 
  1501     return 0;
  1502 
  1503   err_update:
  1504     SDL_SYS_HapticFreeDIEFFECT(&temp, data->type);
  1505     return -1;
  1506 }
  1507 
  1508 
  1509 /*
  1510  * Runs an effect.
  1511  */
  1512 int
  1513 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
  1514                         Uint32 iterations)
  1515 {
  1516     HRESULT ret;
  1517     DWORD iter;
  1518 
  1519     if (haptic->hwdata->bXInputHaptic) {
  1520         XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
  1521         SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT);  /* should catch this at higher level */
  1522         SDL_LockMutex(haptic->hwdata->mutex);
  1523         if(effect->effect.leftright.length == SDL_HAPTIC_INFINITY || iterations == SDL_HAPTIC_INFINITY) {
  1524             haptic->hwdata->stopTicks = SDL_HAPTIC_INFINITY;
  1525         } else if ((!effect->effect.leftright.length) || (!iterations)) {
  1526             /* do nothing. Effect runs for zero milliseconds. */
  1527         } else {
  1528             haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations);
  1529             if ((haptic->hwdata->stopTicks == SDL_HAPTIC_INFINITY) || (haptic->hwdata->stopTicks == 0)) {
  1530                 haptic->hwdata->stopTicks = 1;  /* fix edge cases. */
  1531             }
  1532         }
  1533         SDL_UnlockMutex(haptic->hwdata->mutex);
  1534         return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1;
  1535     }
  1536 
  1537     /* Check if it's infinite. */
  1538     if (iterations == SDL_HAPTIC_INFINITY) {
  1539         iter = INFINITE;
  1540     } else
  1541         iter = iterations;
  1542 
  1543     /* Run the effect. */
  1544     ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0);
  1545     if (FAILED(ret)) {
  1546         return DI_SetError("Running the effect", ret);
  1547     }
  1548 
  1549     return 0;
  1550 }
  1551 
  1552 
  1553 /*
  1554  * Stops an effect.
  1555  */
  1556 int
  1557 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1558 {
  1559     HRESULT ret;
  1560 
  1561     if (haptic->hwdata->bXInputHaptic) {
  1562         XINPUT_VIBRATION vibration = { 0, 0 };
  1563         SDL_LockMutex(haptic->hwdata->mutex);
  1564         haptic->hwdata->stopTicks = 0;
  1565         SDL_UnlockMutex(haptic->hwdata->mutex);
  1566         return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
  1567     }
  1568 
  1569     ret = IDirectInputEffect_Stop(effect->hweffect->ref);
  1570     if (FAILED(ret)) {
  1571         return DI_SetError("Unable to stop effect", ret);
  1572     }
  1573 
  1574     return 0;
  1575 }
  1576 
  1577 
  1578 /*
  1579  * Frees the effect.
  1580  */
  1581 void
  1582 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1583 {
  1584     HRESULT ret;
  1585 
  1586     if (haptic->hwdata->bXInputHaptic) {
  1587         SDL_SYS_HapticStopEffect(haptic, effect);
  1588     } else {
  1589         ret = IDirectInputEffect_Unload(effect->hweffect->ref);
  1590         if (FAILED(ret)) {
  1591             DI_SetError("Removing effect from the device", ret);
  1592         }
  1593         SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
  1594                                    effect->effect.type);
  1595     }
  1596     SDL_free(effect->hweffect);
  1597     effect->hweffect = NULL;
  1598 }
  1599 
  1600 
  1601 /*
  1602  * Gets the status of a haptic effect.
  1603  */
  1604 int
  1605 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
  1606                               struct haptic_effect *effect)
  1607 {
  1608     HRESULT ret;
  1609     DWORD status;
  1610 
  1611     ret = IDirectInputEffect_GetEffectStatus(effect->hweffect->ref, &status);
  1612     if (FAILED(ret)) {
  1613         return DI_SetError("Getting effect status", ret);
  1614     }
  1615 
  1616     if (status == 0)
  1617         return SDL_FALSE;
  1618     return SDL_TRUE;
  1619 }
  1620 
  1621 
  1622 /*
  1623  * Sets the gain.
  1624  */
  1625 int
  1626 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
  1627 {
  1628     HRESULT ret;
  1629     DIPROPDWORD dipdw;
  1630 
  1631     /* Create the weird structure thingy. */
  1632     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  1633     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1634     dipdw.diph.dwObj = 0;
  1635     dipdw.diph.dwHow = DIPH_DEVICE;
  1636     dipdw.dwData = gain * 100;  /* 0 to 10,000 */
  1637 
  1638     /* Try to set the autocenter. */
  1639     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
  1640                                           DIPROP_FFGAIN, &dipdw.diph);
  1641     if (FAILED(ret)) {
  1642         return DI_SetError("Setting gain", ret);
  1643     }
  1644 
  1645     return 0;
  1646 }
  1647 
  1648 
  1649 /*
  1650  * Sets the autocentering.
  1651  */
  1652 int
  1653 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
  1654 {
  1655     HRESULT ret;
  1656     DIPROPDWORD dipdw;
  1657 
  1658     /* Create the weird structure thingy. */
  1659     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  1660     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1661     dipdw.diph.dwObj = 0;
  1662     dipdw.diph.dwHow = DIPH_DEVICE;
  1663     dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF :
  1664         DIPROPAUTOCENTER_ON;
  1665 
  1666     /* Try to set the autocenter. */
  1667     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
  1668                                           DIPROP_AUTOCENTER, &dipdw.diph);
  1669     if (FAILED(ret)) {
  1670         return DI_SetError("Setting autocenter", ret);
  1671     }
  1672 
  1673     return 0;
  1674 }
  1675 
  1676 
  1677 /*
  1678  * Pauses the device.
  1679  */
  1680 int
  1681 SDL_SYS_HapticPause(SDL_Haptic * haptic)
  1682 {
  1683     HRESULT ret;
  1684 
  1685     /* Pause the device. */
  1686     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1687                                                        DISFFC_PAUSE);
  1688     if (FAILED(ret)) {
  1689         return DI_SetError("Pausing the device", ret);
  1690     }
  1691 
  1692     return 0;
  1693 }
  1694 
  1695 
  1696 /*
  1697  * Pauses the device.
  1698  */
  1699 int
  1700 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
  1701 {
  1702     HRESULT ret;
  1703 
  1704     /* Unpause the device. */
  1705     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1706                                                        DISFFC_CONTINUE);
  1707     if (FAILED(ret)) {
  1708         return DI_SetError("Pausing the device", ret);
  1709     }
  1710 
  1711     return 0;
  1712 }
  1713 
  1714 
  1715 /*
  1716  * Stops all the playing effects on the device.
  1717  */
  1718 int
  1719 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
  1720 {
  1721     HRESULT ret;
  1722 
  1723     if (haptic->hwdata->bXInputHaptic) {
  1724         XINPUT_VIBRATION vibration = { 0, 0 };
  1725         SDL_LockMutex(haptic->hwdata->mutex);
  1726         haptic->hwdata->stopTicks = 0;
  1727         SDL_UnlockMutex(haptic->hwdata->mutex);
  1728         return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
  1729     }
  1730 
  1731     /* Try to stop the effects. */
  1732     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1733                                                        DISFFC_STOPALL);
  1734     if (FAILED(ret)) {
  1735         return DI_SetError("Stopping the device", ret);
  1736     }
  1737 
  1738     return 0;
  1739 }
  1740 
  1741 
  1742 /* !!! FIXME: this is a hack, remove this later. */
  1743 /* Since XInput doesn't offer a way to vibrate for X time, we hook into
  1744  *  SDL_PumpEvents() to check if it's time to stop vibrating with some
  1745  *  frequency.
  1746  * In practice, this works for 99% of use cases. But in an ideal world,
  1747  *  we do this in a separate thread so that:
  1748  *    - we aren't bound to when the app chooses to pump the event queue.
  1749  *    - we aren't adding more polling to the event queue
  1750  *    - we can emulate all the haptic effects correctly (start on a delay,
  1751  *      mix multiple effects, etc).
  1752  *
  1753  * Mostly, this is here to get rumbling to work, and all the other features
  1754  *  are absent in the XInput path for now.  :(
  1755  */
  1756 static int SDLCALL
  1757 SDL_RunXInputHaptic(void *arg)
  1758 {
  1759     struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg;
  1760 
  1761     while (!hwdata->stopThread) {
  1762         SDL_Delay(50);
  1763         SDL_LockMutex(hwdata->mutex);
  1764         /* If we're currently running and need to stop... */
  1765         if (hwdata->stopTicks) {
  1766             if ((hwdata->stopTicks != SDL_HAPTIC_INFINITY) && SDL_TICKS_PASSED(SDL_GetTicks(), hwdata->stopTicks)) {
  1767                 XINPUT_VIBRATION vibration = { 0, 0 };
  1768                 hwdata->stopTicks = 0;
  1769                 XINPUTSETSTATE(hwdata->userid, &vibration);
  1770             }
  1771         }
  1772         SDL_UnlockMutex(hwdata->mutex);
  1773     }
  1774 
  1775     return 0;
  1776 }
  1777 
  1778 #endif /* SDL_HAPTIC_DINPUT */
  1779 
  1780 /* vi: set ts=4 sw=4 expandtab: */