src/haptic/windows/SDL_syshaptic.c
author Gabriel Jacobo <gabomdq@gmail.com>
Wed, 21 Aug 2013 09:43:09 -0300
changeset 7677 871d43c6968a
parent 7623 fe1b8a78de0b
child 7678 286c42d7c5ed
permissions -rw-r--r--
OCD fixes: Adds a space before */
     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 and 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         if (SDL_hapticlist[i].name) {
   755             SDL_free(SDL_hapticlist[i].name);
   756             SDL_hapticlist[i].name = NULL;
   757         }
   758     }
   759 
   760     if (dinput != NULL) {
   761         IDirectInput8_Release(dinput);
   762         dinput = NULL;
   763     }
   764 
   765     if (coinitialized) {
   766         WIN_CoUninitialize();
   767         coinitialized = SDL_FALSE;
   768     }
   769 }
   770 
   771 
   772 /*
   773  * Converts an SDL trigger button to an DIEFFECT trigger button.
   774  */
   775 static DWORD
   776 DIGetTriggerButton(Uint16 button)
   777 {
   778     DWORD dwTriggerButton;
   779 
   780     dwTriggerButton = DIEB_NOTRIGGER;
   781 
   782     if (button != 0) {
   783         dwTriggerButton = DIJOFS_BUTTON(button - 1);
   784     }
   785 
   786     return dwTriggerButton;
   787 }
   788 
   789 
   790 /*
   791  * Sets the direction.
   792  */
   793 static int
   794 SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir, int naxes)
   795 {
   796     LONG *rglDir;
   797 
   798     /* Handle no axes a part. */
   799     if (naxes == 0) {
   800         effect->dwFlags |= DIEFF_SPHERICAL;     /* Set as default. */
   801         effect->rglDirection = NULL;
   802         return 0;
   803     }
   804 
   805     /* Has axes. */
   806     rglDir = SDL_malloc(sizeof(LONG) * naxes);
   807     if (rglDir == NULL) {
   808         return SDL_OutOfMemory();
   809     }
   810     SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
   811     effect->rglDirection = rglDir;
   812 
   813     switch (dir->type) {
   814     case SDL_HAPTIC_POLAR:
   815         effect->dwFlags |= DIEFF_POLAR;
   816         rglDir[0] = dir->dir[0];
   817         return 0;
   818     case SDL_HAPTIC_CARTESIAN:
   819         effect->dwFlags |= DIEFF_CARTESIAN;
   820         rglDir[0] = dir->dir[0];
   821         if (naxes > 1)
   822             rglDir[1] = dir->dir[1];
   823         if (naxes > 2)
   824             rglDir[2] = dir->dir[2];
   825         return 0;
   826     case SDL_HAPTIC_SPHERICAL:
   827         effect->dwFlags |= DIEFF_SPHERICAL;
   828         rglDir[0] = dir->dir[0];
   829         if (naxes > 1)
   830             rglDir[1] = dir->dir[1];
   831         if (naxes > 2)
   832             rglDir[2] = dir->dir[2];
   833         return 0;
   834 
   835     default:
   836         return SDL_SetError("Haptic: Unknown direction type.");
   837     }
   838 }
   839 
   840 #define CONVERT(x)   (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
   841 /*
   842  * Creates the DIEFFECT from a SDL_HapticEffect.
   843  */
   844 static int
   845 SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
   846                    SDL_HapticEffect * src)
   847 {
   848     int i;
   849     DICONSTANTFORCE *constant;
   850     DIPERIODIC *periodic;
   851     DICONDITION *condition;     /* Actually an array of conditions - one per axis. */
   852     DIRAMPFORCE *ramp;
   853     DICUSTOMFORCE *custom;
   854     DIENVELOPE *envelope;
   855     SDL_HapticConstant *hap_constant;
   856     SDL_HapticPeriodic *hap_periodic;
   857     SDL_HapticCondition *hap_condition;
   858     SDL_HapticRamp *hap_ramp;
   859     SDL_HapticCustom *hap_custom;
   860     DWORD *axes;
   861 
   862     /* Set global stuff. */
   863     SDL_memset(dest, 0, sizeof(DIEFFECT));
   864     dest->dwSize = sizeof(DIEFFECT);    /* Set the structure size. */
   865     dest->dwSamplePeriod = 0;   /* Not used by us. */
   866     dest->dwGain = 10000;       /* Gain is set globally, not locally. */
   867     dest->dwFlags = DIEFF_OBJECTOFFSETS;        /* Seems obligatory. */
   868 
   869     /* Envelope. */
   870     envelope = SDL_malloc(sizeof(DIENVELOPE));
   871     if (envelope == NULL) {
   872         return SDL_OutOfMemory();
   873     }
   874     SDL_memset(envelope, 0, sizeof(DIENVELOPE));
   875     dest->lpEnvelope = envelope;
   876     envelope->dwSize = sizeof(DIENVELOPE);      /* Always should be this. */
   877 
   878     /* Axes. */
   879     dest->cAxes = haptic->naxes;
   880     if (dest->cAxes > 0) {
   881         axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
   882         if (axes == NULL) {
   883             return SDL_OutOfMemory();
   884         }
   885         axes[0] = haptic->hwdata->axes[0];      /* Always at least one axis. */
   886         if (dest->cAxes > 1) {
   887             axes[1] = haptic->hwdata->axes[1];
   888         }
   889         if (dest->cAxes > 2) {
   890             axes[2] = haptic->hwdata->axes[2];
   891         }
   892         dest->rgdwAxes = axes;
   893     }
   894 
   895 
   896     /* The big type handling switch, even bigger then Linux's version. */
   897     switch (src->type) {
   898     case SDL_HAPTIC_CONSTANT:
   899         hap_constant = &src->constant;
   900         constant = SDL_malloc(sizeof(DICONSTANTFORCE));
   901         if (constant == NULL) {
   902             return SDL_OutOfMemory();
   903         }
   904         SDL_memset(constant, 0, sizeof(DICONSTANTFORCE));
   905 
   906         /* Specifics */
   907         constant->lMagnitude = CONVERT(hap_constant->level);
   908         dest->cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
   909         dest->lpvTypeSpecificParams = constant;
   910 
   911         /* Generics */
   912         dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
   913         dest->dwTriggerButton = DIGetTriggerButton(hap_constant->button);
   914         dest->dwTriggerRepeatInterval = hap_constant->interval;
   915         dest->dwStartDelay = hap_constant->delay * 1000;        /* In microseconds. */
   916 
   917         /* Direction. */
   918         if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
   919             < 0) {
   920             return -1;
   921         }
   922 
   923         /* Envelope */
   924         if ((hap_constant->attack_length == 0)
   925             && (hap_constant->fade_length == 0)) {
   926             SDL_free(dest->lpEnvelope);
   927             dest->lpEnvelope = NULL;
   928         } else {
   929             envelope->dwAttackLevel = CONVERT(hap_constant->attack_level);
   930             envelope->dwAttackTime = hap_constant->attack_length * 1000;
   931             envelope->dwFadeLevel = CONVERT(hap_constant->fade_level);
   932             envelope->dwFadeTime = hap_constant->fade_length * 1000;
   933         }
   934 
   935         break;
   936 
   937     case SDL_HAPTIC_SINE:
   938     /* !!! FIXME: put this back when we have more bits in 2.1 */
   939     /*case SDL_HAPTIC_SQUARE: */
   940     case SDL_HAPTIC_TRIANGLE:
   941     case SDL_HAPTIC_SAWTOOTHUP:
   942     case SDL_HAPTIC_SAWTOOTHDOWN:
   943         hap_periodic = &src->periodic;
   944         periodic = SDL_malloc(sizeof(DIPERIODIC));
   945         if (periodic == NULL) {
   946             return SDL_OutOfMemory();
   947         }
   948         SDL_memset(periodic, 0, sizeof(DIPERIODIC));
   949 
   950         /* Specifics */
   951         periodic->dwMagnitude = CONVERT(hap_periodic->magnitude);
   952         periodic->lOffset = CONVERT(hap_periodic->offset);
   953         periodic->dwPhase = hap_periodic->phase;
   954         periodic->dwPeriod = hap_periodic->period * 1000;
   955         dest->cbTypeSpecificParams = sizeof(DIPERIODIC);
   956         dest->lpvTypeSpecificParams = periodic;
   957 
   958         /* Generics */
   959         dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
   960         dest->dwTriggerButton = DIGetTriggerButton(hap_periodic->button);
   961         dest->dwTriggerRepeatInterval = hap_periodic->interval;
   962         dest->dwStartDelay = hap_periodic->delay * 1000;        /* In microseconds. */
   963 
   964         /* Direction. */
   965         if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
   966             < 0) {
   967             return -1;
   968         }
   969 
   970         /* Envelope */
   971         if ((hap_periodic->attack_length == 0)
   972             && (hap_periodic->fade_length == 0)) {
   973             SDL_free(dest->lpEnvelope);
   974             dest->lpEnvelope = NULL;
   975         } else {
   976             envelope->dwAttackLevel = CONVERT(hap_periodic->attack_level);
   977             envelope->dwAttackTime = hap_periodic->attack_length * 1000;
   978             envelope->dwFadeLevel = CONVERT(hap_periodic->fade_level);
   979             envelope->dwFadeTime = hap_periodic->fade_length * 1000;
   980         }
   981 
   982         break;
   983 
   984     case SDL_HAPTIC_SPRING:
   985     case SDL_HAPTIC_DAMPER:
   986     case SDL_HAPTIC_INERTIA:
   987     case SDL_HAPTIC_FRICTION:
   988         hap_condition = &src->condition;
   989         condition = SDL_malloc(sizeof(DICONDITION) * dest->cAxes);
   990         if (condition == NULL) {
   991             return SDL_OutOfMemory();
   992         }
   993         SDL_memset(condition, 0, sizeof(DICONDITION));
   994 
   995         /* Specifics */
   996         for (i = 0; i < (int) dest->cAxes; i++) {
   997             condition[i].lOffset = CONVERT(hap_condition->center[i]);
   998             condition[i].lPositiveCoefficient =
   999                 CONVERT(hap_condition->right_coeff[i]);
  1000             condition[i].lNegativeCoefficient =
  1001                 CONVERT(hap_condition->left_coeff[i]);
  1002             condition[i].dwPositiveSaturation =
  1003                 CONVERT(hap_condition->right_sat[i]);
  1004             condition[i].dwNegativeSaturation =
  1005                 CONVERT(hap_condition->left_sat[i]);
  1006             condition[i].lDeadBand = CONVERT(hap_condition->deadband[i]);
  1007         }
  1008         dest->cbTypeSpecificParams = sizeof(DICONDITION) * dest->cAxes;
  1009         dest->lpvTypeSpecificParams = condition;
  1010 
  1011         /* Generics */
  1012         dest->dwDuration = hap_condition->length * 1000;        /* In microseconds. */
  1013         dest->dwTriggerButton = DIGetTriggerButton(hap_condition->button);
  1014         dest->dwTriggerRepeatInterval = hap_condition->interval;
  1015         dest->dwStartDelay = hap_condition->delay * 1000;       /* In microseconds. */
  1016 
  1017         /* Direction. */
  1018         if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
  1019             < 0) {
  1020             return -1;
  1021         }
  1022 
  1023         /* Envelope - Not actually supported by most CONDITION implementations. */
  1024         SDL_free(dest->lpEnvelope);
  1025         dest->lpEnvelope = NULL;
  1026 
  1027         break;
  1028 
  1029     case SDL_HAPTIC_RAMP:
  1030         hap_ramp = &src->ramp;
  1031         ramp = SDL_malloc(sizeof(DIRAMPFORCE));
  1032         if (ramp == NULL) {
  1033             return SDL_OutOfMemory();
  1034         }
  1035         SDL_memset(ramp, 0, sizeof(DIRAMPFORCE));
  1036 
  1037         /* Specifics */
  1038         ramp->lStart = CONVERT(hap_ramp->start);
  1039         ramp->lEnd = CONVERT(hap_ramp->end);
  1040         dest->cbTypeSpecificParams = sizeof(DIRAMPFORCE);
  1041         dest->lpvTypeSpecificParams = ramp;
  1042 
  1043         /* Generics */
  1044         dest->dwDuration = hap_ramp->length * 1000;     /* In microseconds. */
  1045         dest->dwTriggerButton = DIGetTriggerButton(hap_ramp->button);
  1046         dest->dwTriggerRepeatInterval = hap_ramp->interval;
  1047         dest->dwStartDelay = hap_ramp->delay * 1000;    /* In microseconds. */
  1048 
  1049         /* Direction. */
  1050         if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
  1051             return -1;
  1052         }
  1053 
  1054         /* Envelope */
  1055         if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
  1056             SDL_free(dest->lpEnvelope);
  1057             dest->lpEnvelope = NULL;
  1058         } else {
  1059             envelope->dwAttackLevel = CONVERT(hap_ramp->attack_level);
  1060             envelope->dwAttackTime = hap_ramp->attack_length * 1000;
  1061             envelope->dwFadeLevel = CONVERT(hap_ramp->fade_level);
  1062             envelope->dwFadeTime = hap_ramp->fade_length * 1000;
  1063         }
  1064 
  1065         break;
  1066 
  1067     case SDL_HAPTIC_CUSTOM:
  1068         hap_custom = &src->custom;
  1069         custom = SDL_malloc(sizeof(DICUSTOMFORCE));
  1070         if (custom == NULL) {
  1071             return SDL_OutOfMemory();
  1072         }
  1073         SDL_memset(custom, 0, sizeof(DICUSTOMFORCE));
  1074 
  1075         /* Specifics */
  1076         custom->cChannels = hap_custom->channels;
  1077         custom->dwSamplePeriod = hap_custom->period * 1000;
  1078         custom->cSamples = hap_custom->samples;
  1079         custom->rglForceData =
  1080             SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
  1081         for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) {      /* Copy data. */
  1082             custom->rglForceData[i] = CONVERT(hap_custom->data[i]);
  1083         }
  1084         dest->cbTypeSpecificParams = sizeof(DICUSTOMFORCE);
  1085         dest->lpvTypeSpecificParams = custom;
  1086 
  1087         /* Generics */
  1088         dest->dwDuration = hap_custom->length * 1000;   /* In microseconds. */
  1089         dest->dwTriggerButton = DIGetTriggerButton(hap_custom->button);
  1090         dest->dwTriggerRepeatInterval = hap_custom->interval;
  1091         dest->dwStartDelay = hap_custom->delay * 1000;  /* In microseconds. */
  1092 
  1093         /* Direction. */
  1094         if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
  1095             0) {
  1096             return -1;
  1097         }
  1098 
  1099         /* Envelope */
  1100         if ((hap_custom->attack_length == 0)
  1101             && (hap_custom->fade_length == 0)) {
  1102             SDL_free(dest->lpEnvelope);
  1103             dest->lpEnvelope = NULL;
  1104         } else {
  1105             envelope->dwAttackLevel = CONVERT(hap_custom->attack_level);
  1106             envelope->dwAttackTime = hap_custom->attack_length * 1000;
  1107             envelope->dwFadeLevel = CONVERT(hap_custom->fade_level);
  1108             envelope->dwFadeTime = hap_custom->fade_length * 1000;
  1109         }
  1110 
  1111         break;
  1112 
  1113 
  1114     default:
  1115         return SDL_SetError("Haptic: Unknown effect type.");
  1116     }
  1117 
  1118     return 0;
  1119 }
  1120 
  1121 
  1122 /*
  1123  * Frees an DIEFFECT allocated by SDL_SYS_ToDIEFFECT.
  1124  */
  1125 static void
  1126 SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type)
  1127 {
  1128     DICUSTOMFORCE *custom;
  1129 
  1130     if (effect->lpEnvelope != NULL) {
  1131         SDL_free(effect->lpEnvelope);
  1132         effect->lpEnvelope = NULL;
  1133     }
  1134     if (effect->rgdwAxes != NULL) {
  1135         SDL_free(effect->rgdwAxes);
  1136         effect->rgdwAxes = NULL;
  1137     }
  1138     if (effect->lpvTypeSpecificParams != NULL) {
  1139         if (type == SDL_HAPTIC_CUSTOM) {        /* Must free the custom data. */
  1140             custom = (DICUSTOMFORCE *) effect->lpvTypeSpecificParams;
  1141             SDL_free(custom->rglForceData);
  1142             custom->rglForceData = NULL;
  1143         }
  1144         SDL_free(effect->lpvTypeSpecificParams);
  1145         effect->lpvTypeSpecificParams = NULL;
  1146     }
  1147     if (effect->rglDirection != NULL) {
  1148         SDL_free(effect->rglDirection);
  1149         effect->rglDirection = NULL;
  1150     }
  1151 }
  1152 
  1153 
  1154 /*
  1155  * Gets the effect type from the generic SDL haptic effect wrapper.
  1156  */
  1157 static REFGUID
  1158 SDL_SYS_HapticEffectType(SDL_HapticEffect * effect)
  1159 {
  1160     switch (effect->type) {
  1161     case SDL_HAPTIC_CONSTANT:
  1162         return &GUID_ConstantForce;
  1163 
  1164     case SDL_HAPTIC_RAMP:
  1165         return &GUID_RampForce;
  1166 
  1167     /* !!! FIXME: put this back when we have more bits in 2.1 */
  1168     /*case SDL_HAPTIC_SQUARE:
  1169         return &GUID_Square; */
  1170 
  1171     case SDL_HAPTIC_SINE:
  1172         return &GUID_Sine;
  1173 
  1174     case SDL_HAPTIC_TRIANGLE:
  1175         return &GUID_Triangle;
  1176 
  1177     case SDL_HAPTIC_SAWTOOTHUP:
  1178         return &GUID_SawtoothUp;
  1179 
  1180     case SDL_HAPTIC_SAWTOOTHDOWN:
  1181         return &GUID_SawtoothDown;
  1182 
  1183     case SDL_HAPTIC_SPRING:
  1184         return &GUID_Spring;
  1185 
  1186     case SDL_HAPTIC_DAMPER:
  1187         return &GUID_Damper;
  1188 
  1189     case SDL_HAPTIC_INERTIA:
  1190         return &GUID_Inertia;
  1191 
  1192     case SDL_HAPTIC_FRICTION:
  1193         return &GUID_Friction;
  1194 
  1195     case SDL_HAPTIC_CUSTOM:
  1196         return &GUID_CustomForce;
  1197 
  1198     default:
  1199         SDL_SetError("Haptic: Unknown effect type.");
  1200         return NULL;
  1201     }
  1202 }
  1203 
  1204 
  1205 /*
  1206  * Creates a new haptic effect.
  1207  */
  1208 int
  1209 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
  1210                         SDL_HapticEffect * base)
  1211 {
  1212     HRESULT ret;
  1213     REFGUID type = SDL_SYS_HapticEffectType(base);
  1214 
  1215     if ((type == NULL) && (!haptic->hwdata->bXInputHaptic)) {
  1216         goto err_hweffect;
  1217     }
  1218 
  1219     /* Alloc the effect. */
  1220     effect->hweffect = (struct haptic_hweffect *)
  1221         SDL_malloc(sizeof(struct haptic_hweffect));
  1222     if (effect->hweffect == NULL) {
  1223         SDL_OutOfMemory();
  1224         goto err_hweffect;
  1225     }
  1226 
  1227     SDL_zerop(effect->hweffect);
  1228 
  1229     if (haptic->hwdata->bXInputHaptic) {
  1230         SDL_assert(base->type == SDL_HAPTIC_LEFTRIGHT);  /* should catch this at higher level */
  1231         return SDL_SYS_HapticUpdateEffect(haptic, effect, base);
  1232     }
  1233 
  1234     /* Get the effect. */
  1235     if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
  1236         goto err_effectdone;
  1237     }
  1238 
  1239     /* Create the actual effect. */
  1240     ret = IDirectInputDevice8_CreateEffect(haptic->hwdata->device, type,
  1241                                            &effect->hweffect->effect,
  1242                                            &effect->hweffect->ref, NULL);
  1243     if (FAILED(ret)) {
  1244         DI_SetError("Unable to create effect", ret);
  1245         goto err_effectdone;
  1246     }
  1247 
  1248     return 0;
  1249 
  1250   err_effectdone:
  1251     SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, base->type);
  1252   err_hweffect:
  1253     if (effect->hweffect != NULL) {
  1254         SDL_free(effect->hweffect);
  1255         effect->hweffect = NULL;
  1256     }
  1257     return -1;
  1258 }
  1259 
  1260 
  1261 /*
  1262  * Updates an effect.
  1263  */
  1264 int
  1265 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
  1266                            struct haptic_effect *effect,
  1267                            SDL_HapticEffect * data)
  1268 {
  1269     HRESULT ret;
  1270     DWORD flags;
  1271     DIEFFECT temp;
  1272 
  1273     if (haptic->hwdata->bXInputHaptic) {
  1274         XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
  1275         SDL_assert(data->type == SDL_HAPTIC_LEFTRIGHT);
  1276         vib->wLeftMotorSpeed = data->leftright.large_magnitude;
  1277         vib->wRightMotorSpeed = data->leftright.small_magnitude;
  1278         SDL_LockMutex(haptic->hwdata->mutex);
  1279         if (haptic->hwdata->stopTicks) {  /* running right now? Update it. */
  1280             XINPUTSETSTATE(haptic->hwdata->userid, vib);
  1281         }
  1282         SDL_UnlockMutex(haptic->hwdata->mutex);
  1283         return 0;
  1284     }
  1285 
  1286     /* Get the effect. */
  1287     SDL_memset(&temp, 0, sizeof(DIEFFECT));
  1288     if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) {
  1289         goto err_update;
  1290     }
  1291 
  1292     /* Set the flags.  Might be worthwhile to diff temp with loaded effect and
  1293      *  only change those parameters. */
  1294     flags = DIEP_DIRECTION |
  1295         DIEP_DURATION |
  1296         DIEP_ENVELOPE |
  1297         DIEP_STARTDELAY |
  1298         DIEP_TRIGGERBUTTON |
  1299         DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS;
  1300 
  1301     /* Create the actual effect. */
  1302     ret =
  1303         IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags);
  1304     if (FAILED(ret)) {
  1305         DI_SetError("Unable to update effect", ret);
  1306         goto err_update;
  1307     }
  1308 
  1309     /* Copy it over. */
  1310     SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, data->type);
  1311     SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(DIEFFECT));
  1312 
  1313     return 0;
  1314 
  1315   err_update:
  1316     SDL_SYS_HapticFreeDIEFFECT(&temp, data->type);
  1317     return -1;
  1318 }
  1319 
  1320 
  1321 /*
  1322  * Runs an effect.
  1323  */
  1324 int
  1325 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
  1326                         Uint32 iterations)
  1327 {
  1328     HRESULT ret;
  1329     DWORD iter;
  1330 
  1331     if (haptic->hwdata->bXInputHaptic) {
  1332         XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
  1333         SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT);  /* should catch this at higher level */
  1334         SDL_LockMutex(haptic->hwdata->mutex);
  1335         haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations);
  1336         SDL_UnlockMutex(haptic->hwdata->mutex);
  1337         return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1;
  1338     }
  1339 
  1340     /* Check if it's infinite. */
  1341     if (iterations == SDL_HAPTIC_INFINITY) {
  1342         iter = INFINITE;
  1343     } else
  1344         iter = iterations;
  1345 
  1346     /* Run the effect. */
  1347     ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0);
  1348     if (FAILED(ret)) {
  1349         return DI_SetError("Running the effect", ret);
  1350     }
  1351 
  1352     return 0;
  1353 }
  1354 
  1355 
  1356 /*
  1357  * Stops an effect.
  1358  */
  1359 int
  1360 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1361 {
  1362     HRESULT ret;
  1363 
  1364     if (haptic->hwdata->bXInputHaptic) {
  1365         XINPUT_VIBRATION vibration = { 0, 0 };
  1366         SDL_LockMutex(haptic->hwdata->mutex);
  1367         haptic->hwdata->stopTicks = 0;
  1368         SDL_UnlockMutex(haptic->hwdata->mutex);
  1369         return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
  1370     }
  1371 
  1372     ret = IDirectInputEffect_Stop(effect->hweffect->ref);
  1373     if (FAILED(ret)) {
  1374         return DI_SetError("Unable to stop effect", ret);
  1375     }
  1376 
  1377     return 0;
  1378 }
  1379 
  1380 
  1381 /*
  1382  * Frees the effect.
  1383  */
  1384 void
  1385 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1386 {
  1387     HRESULT ret;
  1388 
  1389     if (haptic->hwdata->bXInputHaptic) {
  1390         SDL_SYS_HapticStopEffect(haptic, effect);
  1391     } else {
  1392         ret = IDirectInputEffect_Unload(effect->hweffect->ref);
  1393         if (FAILED(ret)) {
  1394             DI_SetError("Removing effect from the device", ret);
  1395         }
  1396         SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
  1397                                    effect->effect.type);
  1398     }
  1399     SDL_free(effect->hweffect);
  1400     effect->hweffect = NULL;
  1401 }
  1402 
  1403 
  1404 /*
  1405  * Gets the status of a haptic effect.
  1406  */
  1407 int
  1408 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
  1409                               struct haptic_effect *effect)
  1410 {
  1411     HRESULT ret;
  1412     DWORD status;
  1413 
  1414     ret = IDirectInputEffect_GetEffectStatus(effect->hweffect->ref, &status);
  1415     if (FAILED(ret)) {
  1416         return DI_SetError("Getting effect status", ret);
  1417     }
  1418 
  1419     if (status == 0)
  1420         return SDL_FALSE;
  1421     return SDL_TRUE;
  1422 }
  1423 
  1424 
  1425 /*
  1426  * Sets the gain.
  1427  */
  1428 int
  1429 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
  1430 {
  1431     HRESULT ret;
  1432     DIPROPDWORD dipdw;
  1433 
  1434     /* Create the weird structure thingy. */
  1435     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  1436     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1437     dipdw.diph.dwObj = 0;
  1438     dipdw.diph.dwHow = DIPH_DEVICE;
  1439     dipdw.dwData = gain * 100;  /* 0 to 10,000 */
  1440 
  1441     /* Try to set the autocenter. */
  1442     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
  1443                                           DIPROP_FFGAIN, &dipdw.diph);
  1444     if (FAILED(ret)) {
  1445         return DI_SetError("Setting gain", ret);
  1446     }
  1447 
  1448     return 0;
  1449 }
  1450 
  1451 
  1452 /*
  1453  * Sets the autocentering.
  1454  */
  1455 int
  1456 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
  1457 {
  1458     HRESULT ret;
  1459     DIPROPDWORD dipdw;
  1460 
  1461     /* Create the weird structure thingy. */
  1462     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  1463     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1464     dipdw.diph.dwObj = 0;
  1465     dipdw.diph.dwHow = DIPH_DEVICE;
  1466     dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF :
  1467         DIPROPAUTOCENTER_ON;
  1468 
  1469     /* Try to set the autocenter. */
  1470     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
  1471                                           DIPROP_AUTOCENTER, &dipdw.diph);
  1472     if (FAILED(ret)) {
  1473         return DI_SetError("Setting autocenter", ret);
  1474     }
  1475 
  1476     return 0;
  1477 }
  1478 
  1479 
  1480 /*
  1481  * Pauses the device.
  1482  */
  1483 int
  1484 SDL_SYS_HapticPause(SDL_Haptic * haptic)
  1485 {
  1486     HRESULT ret;
  1487 
  1488     /* Pause the device. */
  1489     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1490                                                        DISFFC_PAUSE);
  1491     if (FAILED(ret)) {
  1492         return DI_SetError("Pausing the device", ret);
  1493     }
  1494 
  1495     return 0;
  1496 }
  1497 
  1498 
  1499 /*
  1500  * Pauses the device.
  1501  */
  1502 int
  1503 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
  1504 {
  1505     HRESULT ret;
  1506 
  1507     /* Unpause the device. */
  1508     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1509                                                        DISFFC_CONTINUE);
  1510     if (FAILED(ret)) {
  1511         return DI_SetError("Pausing the device", ret);
  1512     }
  1513 
  1514     return 0;
  1515 }
  1516 
  1517 
  1518 /*
  1519  * Stops all the playing effects on the device.
  1520  */
  1521 int
  1522 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
  1523 {
  1524     HRESULT ret;
  1525 
  1526     if (haptic->hwdata->bXInputHaptic) {
  1527         XINPUT_VIBRATION vibration = { 0, 0 };
  1528         SDL_LockMutex(haptic->hwdata->mutex);
  1529         haptic->hwdata->stopTicks = 0;
  1530         SDL_UnlockMutex(haptic->hwdata->mutex);
  1531         return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
  1532     }
  1533 
  1534     /* Try to stop the effects. */
  1535     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1536                                                        DISFFC_STOPALL);
  1537     if (FAILED(ret)) {
  1538         return DI_SetError("Stopping the device", ret);
  1539     }
  1540 
  1541     return 0;
  1542 }
  1543 
  1544 
  1545 /* !!! FIXME: this is a hack, remove this later. */
  1546 /* Since XInput doesn't offer a way to vibrate for X time, we hook into
  1547  *  SDL_PumpEvents() to check if it's time to stop vibrating with some
  1548  *  frequency.
  1549  * In practice, this works for 99% of use cases. But in an ideal world,
  1550  *  we do this in a separate thread so that:
  1551  *    - we aren't bound to when the app chooses to pump the event queue.
  1552  *    - we aren't adding more polling to the event queue
  1553  *    - we can emulate all the haptic effects correctly (start on a delay,
  1554  *      mix multiple effects, etc).
  1555  *
  1556  * Mostly, this is here to get rumbling to work, and all the other features
  1557  *  are absent in the XInput path for now.  :(
  1558  */
  1559 static int SDLCALL
  1560 SDL_RunXInputHaptic(void *arg)
  1561 {
  1562     struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg;
  1563 
  1564     while (!hwdata->stopThread) {
  1565         SDL_Delay(50);
  1566         SDL_LockMutex(hwdata->mutex);
  1567         /* If we're currently running and need to stop... */
  1568         if ((hwdata->stopTicks) && (hwdata->stopTicks < SDL_GetTicks())) {
  1569             XINPUT_VIBRATION vibration = { 0, 0 };
  1570             hwdata->stopTicks = 0;
  1571             XINPUTSETSTATE(hwdata->userid, &vibration);
  1572         }
  1573         SDL_UnlockMutex(hwdata->mutex);
  1574     }
  1575 
  1576     return 0;
  1577 }
  1578 
  1579 #endif /* SDL_HAPTIC_DINPUT */
  1580 
  1581 /* vi: set ts=4 sw=4 expandtab: */