src/haptic/windows/SDL_syshaptic.c
author Ryan C. Gordon <icculus@icculus.org>
Sat, 10 Aug 2013 13:40:08 -0400
changeset 7622 d758150805de
parent 7621 5caa5fb3deb6
child 7623 fe1b8a78de0b
permissions -rw-r--r--
Removed no-longer-necessary comment.
     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         return 0;
  1279     }
  1280 
  1281     /* Get the effect. */
  1282     SDL_memset(&temp, 0, sizeof(DIEFFECT));
  1283     if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) {
  1284         goto err_update;
  1285     }
  1286 
  1287     /* Set the flags.  Might be worthwhile to diff temp with loaded effect and
  1288      *  only change those parameters. */
  1289     flags = DIEP_DIRECTION |
  1290         DIEP_DURATION |
  1291         DIEP_ENVELOPE |
  1292         DIEP_STARTDELAY |
  1293         DIEP_TRIGGERBUTTON |
  1294         DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS;
  1295 
  1296     /* Create the actual effect. */
  1297     ret =
  1298         IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags);
  1299     if (FAILED(ret)) {
  1300         DI_SetError("Unable to update effect", ret);
  1301         goto err_update;
  1302     }
  1303 
  1304     /* Copy it over. */
  1305     SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, data->type);
  1306     SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(DIEFFECT));
  1307 
  1308     return 0;
  1309 
  1310   err_update:
  1311     SDL_SYS_HapticFreeDIEFFECT(&temp, data->type);
  1312     return -1;
  1313 }
  1314 
  1315 
  1316 /*
  1317  * Runs an effect.
  1318  */
  1319 int
  1320 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
  1321                         Uint32 iterations)
  1322 {
  1323     HRESULT ret;
  1324     DWORD iter;
  1325 
  1326     if (haptic->hwdata->bXInputHaptic) {
  1327         XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
  1328         SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT);  /* should catch this at higher level */
  1329         SDL_LockMutex(haptic->hwdata->mutex);
  1330         haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations);
  1331         SDL_UnlockMutex(haptic->hwdata->mutex);
  1332         return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1;
  1333     }
  1334 
  1335     /* Check if it's infinite. */
  1336     if (iterations == SDL_HAPTIC_INFINITY) {
  1337         iter = INFINITE;
  1338     } else
  1339         iter = iterations;
  1340 
  1341     /* Run the effect. */
  1342     ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0);
  1343     if (FAILED(ret)) {
  1344         return DI_SetError("Running the effect", ret);
  1345     }
  1346 
  1347     return 0;
  1348 }
  1349 
  1350 
  1351 /*
  1352  * Stops an effect.
  1353  */
  1354 int
  1355 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1356 {
  1357     HRESULT ret;
  1358 
  1359     if (haptic->hwdata->bXInputHaptic) {
  1360         XINPUT_VIBRATION vibration = { 0, 0 };
  1361         SDL_LockMutex(haptic->hwdata->mutex);
  1362         haptic->hwdata->stopTicks = 0;
  1363         SDL_UnlockMutex(haptic->hwdata->mutex);
  1364         return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
  1365     }
  1366 
  1367     ret = IDirectInputEffect_Stop(effect->hweffect->ref);
  1368     if (FAILED(ret)) {
  1369         return DI_SetError("Unable to stop effect", ret);
  1370     }
  1371 
  1372     return 0;
  1373 }
  1374 
  1375 
  1376 /*
  1377  * Frees the effect.
  1378  */
  1379 void
  1380 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1381 {
  1382     HRESULT ret;
  1383 
  1384     if (haptic->hwdata->bXInputHaptic) {
  1385         SDL_SYS_HapticStopEffect(haptic, effect);
  1386     } else {
  1387         ret = IDirectInputEffect_Unload(effect->hweffect->ref);
  1388         if (FAILED(ret)) {
  1389             DI_SetError("Removing effect from the device", ret);
  1390         }
  1391         SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
  1392                                    effect->effect.type);
  1393     }
  1394     SDL_free(effect->hweffect);
  1395     effect->hweffect = NULL;
  1396 }
  1397 
  1398 
  1399 /*
  1400  * Gets the status of a haptic effect.
  1401  */
  1402 int
  1403 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
  1404                               struct haptic_effect *effect)
  1405 {
  1406     HRESULT ret;
  1407     DWORD status;
  1408 
  1409     ret = IDirectInputEffect_GetEffectStatus(effect->hweffect->ref, &status);
  1410     if (FAILED(ret)) {
  1411         return DI_SetError("Getting effect status", ret);
  1412     }
  1413 
  1414     if (status == 0)
  1415         return SDL_FALSE;
  1416     return SDL_TRUE;
  1417 }
  1418 
  1419 
  1420 /*
  1421  * Sets the gain.
  1422  */
  1423 int
  1424 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
  1425 {
  1426     HRESULT ret;
  1427     DIPROPDWORD dipdw;
  1428 
  1429     /* Create the weird structure thingy. */
  1430     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  1431     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1432     dipdw.diph.dwObj = 0;
  1433     dipdw.diph.dwHow = DIPH_DEVICE;
  1434     dipdw.dwData = gain * 100;  /* 0 to 10,000 */
  1435 
  1436     /* Try to set the autocenter. */
  1437     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
  1438                                           DIPROP_FFGAIN, &dipdw.diph);
  1439     if (FAILED(ret)) {
  1440         return DI_SetError("Setting gain", ret);
  1441     }
  1442 
  1443     return 0;
  1444 }
  1445 
  1446 
  1447 /*
  1448  * Sets the autocentering.
  1449  */
  1450 int
  1451 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
  1452 {
  1453     HRESULT ret;
  1454     DIPROPDWORD dipdw;
  1455 
  1456     /* Create the weird structure thingy. */
  1457     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  1458     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1459     dipdw.diph.dwObj = 0;
  1460     dipdw.diph.dwHow = DIPH_DEVICE;
  1461     dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF :
  1462         DIPROPAUTOCENTER_ON;
  1463 
  1464     /* Try to set the autocenter. */
  1465     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
  1466                                           DIPROP_AUTOCENTER, &dipdw.diph);
  1467     if (FAILED(ret)) {
  1468         return DI_SetError("Setting autocenter", ret);
  1469     }
  1470 
  1471     return 0;
  1472 }
  1473 
  1474 
  1475 /*
  1476  * Pauses the device.
  1477  */
  1478 int
  1479 SDL_SYS_HapticPause(SDL_Haptic * haptic)
  1480 {
  1481     HRESULT ret;
  1482 
  1483     /* Pause the device. */
  1484     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1485                                                        DISFFC_PAUSE);
  1486     if (FAILED(ret)) {
  1487         return DI_SetError("Pausing the device", ret);
  1488     }
  1489 
  1490     return 0;
  1491 }
  1492 
  1493 
  1494 /*
  1495  * Pauses the device.
  1496  */
  1497 int
  1498 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
  1499 {
  1500     HRESULT ret;
  1501 
  1502     /* Unpause the device. */
  1503     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1504                                                        DISFFC_CONTINUE);
  1505     if (FAILED(ret)) {
  1506         return DI_SetError("Pausing the device", ret);
  1507     }
  1508 
  1509     return 0;
  1510 }
  1511 
  1512 
  1513 /*
  1514  * Stops all the playing effects on the device.
  1515  */
  1516 int
  1517 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
  1518 {
  1519     HRESULT ret;
  1520 
  1521     if (haptic->hwdata->bXInputHaptic) {
  1522         XINPUT_VIBRATION vibration = { 0, 0 };
  1523         SDL_LockMutex(haptic->hwdata->mutex);
  1524         haptic->hwdata->stopTicks = 0;
  1525         SDL_UnlockMutex(haptic->hwdata->mutex);
  1526         return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
  1527     }
  1528 
  1529     /* Try to stop the effects. */
  1530     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1531                                                        DISFFC_STOPALL);
  1532     if (FAILED(ret)) {
  1533         return DI_SetError("Stopping the device", ret);
  1534     }
  1535 
  1536     return 0;
  1537 }
  1538 
  1539 
  1540 /* !!! FIXME: this is a hack, remove this later. */
  1541 /* Since XInput doesn't offer a way to vibrate for X time, we hook into
  1542  *  SDL_PumpEvents() to check if it's time to stop vibrating with some
  1543  *  frequency.
  1544  * In practice, this works for 99% of use cases. But in an ideal world,
  1545  *  we do this in a separate thread so that:
  1546  *    - we aren't bound to when the app chooses to pump the event queue.
  1547  *    - we aren't adding more polling to the event queue
  1548  *    - we can emulate all the haptic effects correctly (start on a delay,
  1549  *      mix multiple effects, etc).
  1550  *
  1551  * Mostly, this is here to get rumbling to work, and all the other features
  1552  *  are absent in the XInput path for now.  :(
  1553  */
  1554 static int SDLCALL
  1555 SDL_RunXInputHaptic(void *arg)
  1556 {
  1557     struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg;
  1558 
  1559     while (!hwdata->stopThread) {
  1560         SDL_Delay(50);
  1561         SDL_LockMutex(hwdata->mutex);
  1562         /* If we're currently running and need to stop... */
  1563         if ((hwdata->stopTicks) && (hwdata->stopTicks < SDL_GetTicks())) {
  1564             XINPUT_VIBRATION vibration = { 0, 0 };
  1565             hwdata->stopTicks = 0;
  1566             XINPUTSETSTATE(hwdata->userid, &vibration);
  1567         }
  1568         SDL_UnlockMutex(hwdata->mutex);
  1569     }
  1570 
  1571     return 0;
  1572 }
  1573 
  1574 #endif /* SDL_HAPTIC_DINPUT */
  1575 
  1576 /* vi: set ts=4 sw=4 expandtab: */