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