src/haptic/windows/SDL_syshaptic.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 20 Oct 2013 16:01:10 -0400
changeset 7848 4f77ff668405
parent 7847 038da46576e6
child 7849 99607295ac82
permissions -rw-r--r--
Patched to compile on Windows.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 #ifdef SDL_HAPTIC_DINPUT
    24 
    25 #include "SDL_assert.h"
    26 #include "SDL_thread.h"
    27 #include "SDL_mutex.h"
    28 #include "SDL_timer.h"
    29 #include "SDL_hints.h"
    30 #include "SDL_haptic.h"
    31 #include "../SDL_syshaptic.h"
    32 #include "SDL_joystick.h"
    33 #include "../../joystick/SDL_sysjoystick.h"     /* For the real SDL_Joystick */
    34 #include "../../joystick/windows/SDL_dxjoystick_c.h"      /* For joystick hwdata */
    35 
    36 #define MAX_HAPTICS  32
    37 
    38 /*
    39  * List of available haptic devices.
    40  */
    41 static struct
    42 {
    43     DIDEVICEINSTANCE instance;
    44     char *name;
    45     SDL_Haptic *haptic;
    46     DIDEVCAPS capabilities;
    47     Uint8 bXInputHaptic; /* Supports force feedback via XInput. */
    48     Uint8 userid; /* XInput userid index for this joystick */
    49 } SDL_hapticlist[MAX_HAPTICS];
    50 
    51 
    52 /*
    53  * Haptic system hardware data.
    54  */
    55 struct haptic_hwdata
    56 {
    57     LPDIRECTINPUTDEVICE8 device;
    58     DWORD axes[3];              /* Axes to use. */
    59     SDL_bool is_joystick;       /* Device is loaded as joystick. */
    60     Uint8 bXInputHaptic; /* Supports force feedback via XInput. */
    61     Uint8 userid; /* XInput userid index for this joystick */
    62     SDL_Thread *thread;
    63     SDL_mutex *mutex;
    64     volatile Uint32 stopTicks;
    65     volatile int stopThread;
    66 };
    67 
    68 
    69 /*
    70  * Haptic system effect data.
    71  */
    72 struct haptic_hweffect
    73 {
    74     DIEFFECT effect;
    75     LPDIRECTINPUTEFFECT ref;
    76     XINPUT_VIBRATION vibration;
    77 };
    78 
    79 
    80 /*
    81  * Internal stuff.
    82  */
    83 static SDL_bool coinitialized = SDL_FALSE;
    84 static LPDIRECTINPUT8 dinput = NULL;
    85 static SDL_bool loaded_xinput = SDL_FALSE;
    86 
    87 
    88 /*
    89  * External stuff.
    90  */
    91 extern HWND SDL_HelperWindow;
    92 
    93 
    94 /*
    95  * Prototypes.
    96  */
    97 static int DI_SetError(const char *str, HRESULT err);
    98 static int DI_GUIDIsSame(const GUID * a, const GUID * b);
    99 static int SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic,
   100                                           DIDEVICEINSTANCE instance);
   101 static int SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic,
   102                                          LPDIRECTINPUTDEVICE8 device8,
   103                                          SDL_bool is_joystick);
   104 static int SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, Uint8 userid);
   105 static DWORD DIGetTriggerButton(Uint16 button);
   106 static int SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir,
   107                                 int naxes);
   108 static int SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
   109                               SDL_HapticEffect * src);
   110 static void SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type);
   111 static REFGUID SDL_SYS_HapticEffectType(SDL_HapticEffect * effect);
   112 static int SDLCALL SDL_RunXInputHaptic(void *arg);
   113 
   114 /* Callbacks. */
   115 static BOOL CALLBACK EnumHapticsCallback(const DIDEVICEINSTANCE *
   116                                          pdidInstance, VOID * pContext);
   117 static BOOL CALLBACK DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv);
   118 
   119 
   120 /*
   121  * Like SDL_SetError but for DX error codes.
   122  */
   123 static int
   124 DI_SetError(const char *str, HRESULT err)
   125 {
   126     /*
   127        SDL_SetError("Haptic: %s - %s: %s", str,
   128        DXGetErrorString8A(err), DXGetErrorDescription8A(err));
   129      */
   130     return SDL_SetError("Haptic error %s", str);
   131 }
   132 
   133 
   134 /*
   135  * Checks to see if two GUID are the same.
   136  */
   137 static int
   138 DI_GUIDIsSame(const GUID * a, const GUID * b)
   139 {
   140     return (SDL_memcmp(a, b, sizeof (GUID)) == 0);
   141 }
   142 
   143 
   144 /*
   145  * Initializes the haptic subsystem.
   146  */
   147 int
   148 SDL_SYS_HapticInit(void)
   149 {
   150     const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
   151     HRESULT ret;
   152     HINSTANCE instance;
   153 
   154     if (dinput != NULL) {       /* Already open. */
   155         return SDL_SetError("Haptic: SubSystem already open.");
   156     }
   157 
   158     /* Clear all the memory. */
   159     SDL_memset(SDL_hapticlist, 0, sizeof(SDL_hapticlist));
   160 
   161     SDL_numhaptics = 0;
   162 
   163     ret = WIN_CoInitialize();
   164     if (FAILED(ret)) {
   165         return DI_SetError("Coinitialize", ret);
   166     }
   167 
   168     coinitialized = SDL_TRUE;
   169 
   170     ret = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER,
   171                            &IID_IDirectInput8, (LPVOID) & dinput);
   172     if (FAILED(ret)) {
   173         SDL_SYS_HapticQuit();
   174         return DI_SetError("CoCreateInstance", ret);
   175     }
   176 
   177     /* Because we used CoCreateInstance, we need to Initialize it, first. */
   178     instance = GetModuleHandle(NULL);
   179     if (instance == NULL) {
   180         SDL_SYS_HapticQuit();
   181         return SDL_SetError("GetModuleHandle() failed with error code %d.",
   182                             GetLastError());
   183     }
   184     ret = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION);
   185     if (FAILED(ret)) {
   186         SDL_SYS_HapticQuit();
   187         return DI_SetError("Initializing DirectInput device", ret);
   188     }
   189 
   190     /* Look for haptic devices. */
   191     ret = IDirectInput8_EnumDevices(dinput,
   192                                    0,
   193                                    EnumHapticsCallback,
   194                                    NULL,
   195                                    DIEDFL_FORCEFEEDBACK |
   196                                    DIEDFL_ATTACHEDONLY);
   197     if (FAILED(ret)) {
   198         SDL_SYS_HapticQuit();
   199         return DI_SetError("Enumerating DirectInput devices", ret);
   200     }
   201 
   202     if (!env || SDL_atoi(env)) {
   203         loaded_xinput = (WIN_LoadXInputDLL() == 0);
   204     }
   205 
   206     if (loaded_xinput) {
   207         DWORD i;
   208         const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
   209 
   210         for (i = 0; (i < SDL_XINPUT_MAX_DEVICES) && (SDL_numhaptics < MAX_HAPTICS); i++) {
   211             XINPUT_CAPABILITIES caps;
   212             if (XINPUTGETCAPABILITIES(i, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) {
   213                 if ((!bIs14OrLater) || (caps.Flags & XINPUT_CAPS_FFB_SUPPORTED)) {
   214                     /* !!! FIXME: I'm not bothering to query for a real name right now. */
   215                     char buf[64];
   216                     SDL_snprintf(buf, sizeof (buf), "XInput Controller #%u", i+1);
   217                     SDL_hapticlist[SDL_numhaptics].name = SDL_strdup(buf);
   218                     SDL_hapticlist[SDL_numhaptics].bXInputHaptic = 1;
   219                     SDL_hapticlist[SDL_numhaptics].userid = (Uint8) i;
   220                     SDL_numhaptics++;
   221                 }
   222             }
   223         }
   224     }
   225 
   226     return SDL_numhaptics;
   227 }
   228 
   229 /*
   230  * Callback to find the haptic devices.
   231  */
   232 static BOOL CALLBACK
   233 EnumHapticsCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
   234 {
   235     HRESULT ret;
   236     LPDIRECTINPUTDEVICE8 device;
   237 
   238     /* Copy the instance over, useful for creating devices. */
   239     SDL_memcpy(&SDL_hapticlist[SDL_numhaptics].instance, pdidInstance,
   240                sizeof(DIDEVICEINSTANCE));
   241 
   242     /* Open the device */
   243     ret = IDirectInput8_CreateDevice(dinput, &pdidInstance->guidInstance,
   244                                     &device, NULL);
   245     if (FAILED(ret)) {
   246         /* DI_SetError("Creating DirectInput device",ret); */
   247         return DIENUM_CONTINUE;
   248     }
   249 
   250     /* Get capabilities. */
   251     SDL_hapticlist[SDL_numhaptics].capabilities.dwSize = sizeof(DIDEVCAPS);
   252     ret = IDirectInputDevice8_GetCapabilities(device,
   253                                              &SDL_hapticlist[SDL_numhaptics].
   254                                              capabilities);
   255     if (FAILED(ret)) {
   256         /* DI_SetError("Getting device capabilities",ret); */
   257         IDirectInputDevice8_Release(device);
   258         return DIENUM_CONTINUE;
   259     }
   260 
   261     /* Copy the name */
   262     SDL_hapticlist[SDL_numhaptics].name = WIN_StringToUTF8(SDL_hapticlist[SDL_numhaptics].instance.tszProductName);
   263 
   264     /* Close up device and count it. */
   265     IDirectInputDevice8_Release(device);
   266     SDL_numhaptics++;
   267 
   268     /* Watch out for hard limit. */
   269     if (SDL_numhaptics >= MAX_HAPTICS)
   270         return DIENUM_STOP;
   271 
   272     return DIENUM_CONTINUE;
   273 }
   274 
   275 
   276 /*
   277  * Return the name of a haptic device, does not need to be opened.
   278  */
   279 const char *
   280 SDL_SYS_HapticName(int index)
   281 {
   282     return SDL_hapticlist[index].name;
   283 }
   284 
   285 
   286 /*
   287  * Callback to get all supported effects.
   288  */
   289 #define EFFECT_TEST(e,s)               \
   290 if (DI_GUIDIsSame(&pei->guid, &(e)))   \
   291    haptic->supported |= (s)
   292 static BOOL CALLBACK
   293 DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv)
   294 {
   295     /* Prepare the haptic device. */
   296     SDL_Haptic *haptic = (SDL_Haptic *) pv;
   297 
   298     /* Get supported. */
   299     EFFECT_TEST(GUID_Spring, SDL_HAPTIC_SPRING);
   300     EFFECT_TEST(GUID_Damper, SDL_HAPTIC_DAMPER);
   301     EFFECT_TEST(GUID_Inertia, SDL_HAPTIC_INERTIA);
   302     EFFECT_TEST(GUID_Friction, SDL_HAPTIC_FRICTION);
   303     EFFECT_TEST(GUID_ConstantForce, SDL_HAPTIC_CONSTANT);
   304     EFFECT_TEST(GUID_CustomForce, SDL_HAPTIC_CUSTOM);
   305     EFFECT_TEST(GUID_Sine, SDL_HAPTIC_SINE);
   306     /* !!! FIXME: put this back when we have more bits in 2.1 */
   307     /* EFFECT_TEST(GUID_Square, SDL_HAPTIC_SQUARE); */
   308     EFFECT_TEST(GUID_Triangle, SDL_HAPTIC_TRIANGLE);
   309     EFFECT_TEST(GUID_SawtoothUp, SDL_HAPTIC_SAWTOOTHUP);
   310     EFFECT_TEST(GUID_SawtoothDown, SDL_HAPTIC_SAWTOOTHDOWN);
   311     EFFECT_TEST(GUID_RampForce, SDL_HAPTIC_RAMP);
   312 
   313     /* Check for more. */
   314     return DIENUM_CONTINUE;
   315 }
   316 
   317 
   318 /*
   319  * Callback to get supported axes.
   320  */
   321 static BOOL CALLBACK
   322 DI_DeviceObjectCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
   323 {
   324     SDL_Haptic *haptic = (SDL_Haptic *) pvRef;
   325 
   326     if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) {
   327 
   328         haptic->hwdata->axes[haptic->naxes] = dev->dwOfs;
   329         haptic->naxes++;
   330 
   331         /* Currently using the artificial limit of 3 axes. */
   332         if (haptic->naxes >= 3) {
   333             return DIENUM_STOP;
   334         }
   335     }
   336 
   337     return DIENUM_CONTINUE;
   338 }
   339 
   340 
   341 /*
   342  * Opens the haptic device from the file descriptor.
   343  *
   344  *    Steps:
   345  *       - Open temporary DirectInputDevice interface.
   346  *       - Create DirectInputDevice8 interface.
   347  *       - Release DirectInputDevice interface.
   348  *       - Call SDL_SYS_HapticOpenFromDevice8
   349  */
   350 static int
   351 SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic, DIDEVICEINSTANCE instance)
   352 {
   353     HRESULT ret;
   354     int ret2;
   355     LPDIRECTINPUTDEVICE8 device;
   356     LPDIRECTINPUTDEVICE8 device8;
   357 
   358     /* Open the device */
   359     ret = IDirectInput8_CreateDevice(dinput, &instance.guidInstance,
   360                                     &device, NULL);
   361     if (FAILED(ret)) {
   362         DI_SetError("Creating DirectInput device", ret);
   363         return -1;
   364     }
   365 
   366     /* Now get the IDirectInputDevice8 interface, instead. */
   367     ret = IDirectInputDevice8_QueryInterface(device,
   368                                             &IID_IDirectInputDevice8,
   369                                             (LPVOID *) &device8);
   370     /* Done with the temporary one now. */
   371     IDirectInputDevice8_Release(device);
   372     if (FAILED(ret)) {
   373         DI_SetError("Querying DirectInput interface", ret);
   374         return -1;
   375     }
   376 
   377     ret2 = SDL_SYS_HapticOpenFromDevice8(haptic, device8, SDL_FALSE);
   378     if (ret2 < 0) {
   379         IDirectInputDevice8_Release(device8);
   380         return -1;
   381     }
   382 
   383     return 0;
   384 }
   385 
   386 static int
   387 SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, Uint8 userid)
   388 {
   389     char threadName[32];
   390     XINPUT_VIBRATION vibration = { 0, 0 };  /* stop any current vibration */
   391     XINPUTSETSTATE(userid, &vibration);
   392 
   393     haptic->supported = SDL_HAPTIC_LEFTRIGHT;
   394 
   395     haptic->neffects = 1;
   396     haptic->nplaying = 1;
   397 
   398     /* Prepare effects memory. */
   399     haptic->effects = (struct haptic_effect *)
   400         SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
   401     if (haptic->effects == NULL) {
   402         return SDL_OutOfMemory();
   403     }
   404     /* Clear the memory */
   405     SDL_memset(haptic->effects, 0,
   406                sizeof(struct haptic_effect) * haptic->neffects);
   407 
   408     haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata));
   409     if (haptic->hwdata == NULL) {
   410         SDL_free(haptic->effects);
   411         haptic->effects = NULL;
   412         return SDL_OutOfMemory();
   413     }
   414     SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
   415 
   416     haptic->hwdata->bXInputHaptic = 1;
   417     haptic->hwdata->userid = userid;
   418 
   419     haptic->hwdata->mutex = SDL_CreateMutex();
   420     if (haptic->hwdata->mutex == NULL) {
   421         SDL_free(haptic->effects);
   422         SDL_free(haptic->hwdata);
   423         haptic->effects = NULL;
   424         return SDL_SetError("Couldn't create XInput haptic mutex");
   425     }
   426 
   427     SDL_snprintf(threadName, sizeof (threadName), "SDLXInputDev%d", (int) userid);
   428 
   429 #if defined(__WIN32__) && !defined(HAVE_LIBC)  /* !!! FIXME: this is nasty. */
   430     #undef SDL_CreateThread
   431     haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL);
   432 #else
   433     haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata);
   434 #endif
   435     if (haptic->hwdata->thread == NULL) {
   436         SDL_DestroyMutex(haptic->hwdata->mutex);
   437         SDL_free(haptic->effects);
   438         SDL_free(haptic->hwdata);
   439         haptic->effects = NULL;
   440         return SDL_SetError("Couldn't create XInput haptic thread");
   441     }
   442 
   443     return 0;
   444  }
   445 
   446 /*
   447  * Opens the haptic device from the file descriptor.
   448  *
   449  *    Steps:
   450  *       - Set cooperative level.
   451  *       - Set data format.
   452  *       - Acquire exclusiveness.
   453  *       - Reset actuators.
   454  *       - Get supported features.
   455  */
   456 static int
   457 SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic,
   458                               LPDIRECTINPUTDEVICE8 device8, SDL_bool is_joystick)
   459 {
   460     HRESULT ret;
   461     DIPROPDWORD dipdw;
   462 
   463     /* Allocate the hwdata */
   464     haptic->hwdata = (struct haptic_hwdata *)SDL_malloc(sizeof(*haptic->hwdata));
   465     if (haptic->hwdata == NULL) {
   466         return SDL_OutOfMemory();
   467     }
   468     SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
   469 
   470     /* We'll use the device8 from now on. */
   471     haptic->hwdata->device = device8;
   472     haptic->hwdata->is_joystick = is_joystick;
   473 
   474     /* Grab it exclusively to use force feedback stuff. */
   475     ret = IDirectInputDevice8_SetCooperativeLevel(haptic->hwdata->device,
   476                                                   SDL_HelperWindow,
   477                                                   DISCL_EXCLUSIVE |
   478                                                   DISCL_BACKGROUND);
   479     if (FAILED(ret)) {
   480         DI_SetError("Setting cooperative level to exclusive", ret);
   481         goto acquire_err;
   482     }
   483 
   484     /* Set data format. */
   485     ret = IDirectInputDevice8_SetDataFormat(haptic->hwdata->device,
   486                                             &c_dfDIJoystick2);
   487     if (FAILED(ret)) {
   488         DI_SetError("Setting data format", ret);
   489         goto acquire_err;
   490     }
   491 
   492     /* Get number of axes. */
   493     ret = IDirectInputDevice8_EnumObjects(haptic->hwdata->device,
   494                                           DI_DeviceObjectCallback,
   495                                           haptic, DIDFT_AXIS);
   496     if (FAILED(ret)) {
   497         DI_SetError("Getting device axes", ret);
   498         goto acquire_err;
   499     }
   500 
   501     /* Acquire the device. */
   502     ret = IDirectInputDevice8_Acquire(haptic->hwdata->device);
   503     if (FAILED(ret)) {
   504         DI_SetError("Acquiring DirectInput device", ret);
   505         goto acquire_err;
   506     }
   507 
   508     /* Reset all actuators - just in case. */
   509     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
   510                                                        DISFFC_RESET);
   511     if (FAILED(ret)) {
   512         DI_SetError("Resetting device", ret);
   513         goto acquire_err;
   514     }
   515 
   516     /* Enabling actuators. */
   517     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
   518                                                        DISFFC_SETACTUATORSON);
   519     if (FAILED(ret)) {
   520         DI_SetError("Enabling actuators", ret);
   521         goto acquire_err;
   522     }
   523 
   524     /* Get supported effects. */
   525     ret = IDirectInputDevice8_EnumEffects(haptic->hwdata->device,
   526                                           DI_EffectCallback, haptic,
   527                                           DIEFT_ALL);
   528     if (FAILED(ret)) {
   529         DI_SetError("Enumerating supported effects", ret);
   530         goto acquire_err;
   531     }
   532     if (haptic->supported == 0) {       /* Error since device supports nothing. */
   533         SDL_SetError("Haptic: Internal error on finding supported effects.");
   534         goto acquire_err;
   535     }
   536 
   537     /* Check autogain and autocenter. */
   538     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
   539     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
   540     dipdw.diph.dwObj = 0;
   541     dipdw.diph.dwHow = DIPH_DEVICE;
   542     dipdw.dwData = 10000;
   543     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
   544                                           DIPROP_FFGAIN, &dipdw.diph);
   545     if (!FAILED(ret)) {         /* Gain is supported. */
   546         haptic->supported |= SDL_HAPTIC_GAIN;
   547     }
   548     dipdw.diph.dwObj = 0;
   549     dipdw.diph.dwHow = DIPH_DEVICE;
   550     dipdw.dwData = DIPROPAUTOCENTER_OFF;
   551     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
   552                                           DIPROP_AUTOCENTER, &dipdw.diph);
   553     if (!FAILED(ret)) {         /* Autocenter is supported. */
   554         haptic->supported |= SDL_HAPTIC_AUTOCENTER;
   555     }
   556 
   557     /* Status is always supported. */
   558     haptic->supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE;
   559 
   560     /* Check maximum effects. */
   561     haptic->neffects = 128;     /* This is not actually supported as thus under windows,
   562                                    there is no way to tell the number of EFFECTS that a
   563                                    device can hold, so we'll just use a "random" number
   564                                    instead and put warnings in SDL_haptic.h */
   565     haptic->nplaying = 128;     /* Even more impossible to get this then neffects. */
   566 
   567     /* Prepare effects memory. */
   568     haptic->effects = (struct haptic_effect *)
   569         SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
   570     if (haptic->effects == NULL) {
   571         SDL_OutOfMemory();
   572         goto acquire_err;
   573     }
   574     /* Clear the memory */
   575     SDL_memset(haptic->effects, 0,
   576                sizeof(struct haptic_effect) * haptic->neffects);
   577 
   578     return 0;
   579 
   580     /* Error handling */
   581   acquire_err:
   582     IDirectInputDevice8_Unacquire(haptic->hwdata->device);
   583     return -1;
   584 
   585 }
   586 
   587 
   588 /*
   589  * Opens a haptic device for usage.
   590  */
   591 int
   592 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
   593 {
   594     if (SDL_hapticlist[haptic->index].bXInputHaptic) {
   595         return SDL_SYS_HapticOpenFromXInput(haptic, SDL_hapticlist[haptic->index].userid);
   596     }
   597 
   598     return SDL_SYS_HapticOpenFromInstance(haptic, SDL_hapticlist[haptic->index].instance);
   599 }
   600 
   601 
   602 /*
   603  * Opens a haptic device from first mouse it finds for usage.
   604  */
   605 int
   606 SDL_SYS_HapticMouse(void)
   607 {
   608     int i;
   609 
   610     /* Grab the first mouse haptic device we find. */
   611     for (i = 0; i < SDL_numhaptics; i++) {
   612         if (SDL_hapticlist[i].capabilities.dwDevType == DI8DEVCLASS_POINTER ) {
   613             return i;
   614         }
   615     }
   616 
   617     return -1;
   618 }
   619 
   620 
   621 /*
   622  * Checks to see if a joystick has haptic features.
   623  */
   624 int
   625 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
   626 {
   627     const struct joystick_hwdata *hwdata = joystick->hwdata;
   628     return ( (hwdata->bXInputHaptic) ||
   629              ((hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) != 0) );
   630 }
   631 
   632 
   633 /*
   634  * Checks to see if the haptic device and joystick are in reality the same.
   635  */
   636 int
   637 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
   638 {
   639     if (joystick->hwdata->bXInputHaptic != haptic->hwdata->bXInputHaptic) {
   640         return 0;  /* one is XInput, one is not; not the same device. */
   641     } else if (joystick->hwdata->bXInputHaptic) {  /* XInput */
   642         return (haptic->hwdata->userid == joystick->hwdata->userid);
   643     } else {  /* DirectInput */
   644         HRESULT ret;
   645         DIDEVICEINSTANCE hap_instance, joy_instance;
   646 
   647         hap_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   648         joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   649 
   650         /* Get the device instances. */
   651         ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device,
   652                                             &hap_instance);
   653         if (FAILED(ret)) {
   654             return 0;
   655         }
   656         ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
   657                                                 &joy_instance);
   658         if (FAILED(ret)) {
   659             return 0;
   660         }
   661 
   662         if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance))
   663             return 1;
   664     }
   665 
   666     return 0;
   667 }
   668 
   669 
   670 /*
   671  * Opens a SDL_Haptic from a SDL_Joystick.
   672  */
   673 int
   674 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
   675 {
   676     int i;
   677     HRESULT idret;
   678     DIDEVICEINSTANCE joy_instance;
   679     joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   680 
   681     /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */
   682     if (joystick->hwdata->bXInputDevice) {
   683         const Uint8 userid = joystick->hwdata->userid;
   684         for (i=0; i<SDL_numhaptics; i++) {
   685             if ((SDL_hapticlist[i].bXInputHaptic) && (SDL_hapticlist[i].userid == userid)) {
   686                 SDL_assert(joystick->hwdata->bXInputHaptic);
   687                 haptic->index = i;
   688                 return SDL_SYS_HapticOpenFromXInput(haptic, SDL_hapticlist[haptic->index].userid);
   689             }
   690         }
   691     } else {
   692         for (i=0; i<SDL_numhaptics; i++) {
   693             idret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, &joy_instance);
   694             if (FAILED(idret)) {
   695                 return -1;
   696             }
   697             if (DI_GUIDIsSame(&SDL_hapticlist[i].instance.guidInstance,
   698                               &joy_instance.guidInstance)) {
   699                 haptic->index = i;
   700                 return SDL_SYS_HapticOpenFromDevice8(haptic, joystick->hwdata->InputDevice, SDL_TRUE);
   701             }
   702         }
   703     }
   704     /* No match to our haptic list */
   705     return -1;
   706 }
   707 
   708 
   709 /*
   710  * Closes the haptic device.
   711  */
   712 void
   713 SDL_SYS_HapticClose(SDL_Haptic * haptic)
   714 {
   715     if (haptic->hwdata) {
   716 
   717         /* Free effects. */
   718         SDL_free(haptic->effects);
   719         haptic->effects = NULL;
   720         haptic->neffects = 0;
   721 
   722         /* Clean up */
   723         if (haptic->hwdata->bXInputHaptic) {
   724             haptic->hwdata->stopThread = 1;
   725             SDL_WaitThread(haptic->hwdata->thread, NULL);
   726             SDL_DestroyMutex(haptic->hwdata->mutex);
   727         } else {
   728             IDirectInputDevice8_Unacquire(haptic->hwdata->device);
   729             /* Only release if isn't grabbed by a joystick. */
   730             if (haptic->hwdata->is_joystick == 0) {
   731                 IDirectInputDevice8_Release(haptic->hwdata->device);
   732             }
   733         }
   734 
   735         /* Free */
   736         SDL_free(haptic->hwdata);
   737         haptic->hwdata = NULL;
   738     }
   739 }
   740 
   741 
   742 /*
   743  * Clean up after system specific haptic stuff
   744  */
   745 void
   746 SDL_SYS_HapticQuit(void)
   747 {
   748     int i;
   749 
   750     if (loaded_xinput) {
   751         WIN_UnloadXInputDLL();
   752         loaded_xinput = SDL_FALSE;
   753     }
   754 
   755     for (i = 0; i < SDL_arraysize(SDL_hapticlist); ++i) {
   756         SDL_free(SDL_hapticlist[i].name);
   757         SDL_hapticlist[i].name = NULL;
   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     SDL_free(effect->lpEnvelope);
  1131     effect->lpEnvelope = NULL;
  1132     SDL_free(effect->rgdwAxes);
  1133     effect->rgdwAxes = NULL;
  1134     if (effect->lpvTypeSpecificParams != NULL) {
  1135         if (type == SDL_HAPTIC_CUSTOM) {        /* Must free the custom data. */
  1136             custom = (DICUSTOMFORCE *) effect->lpvTypeSpecificParams;
  1137             SDL_free(custom->rglForceData);
  1138             custom->rglForceData = NULL;
  1139         }
  1140         SDL_free(effect->lpvTypeSpecificParams);
  1141         effect->lpvTypeSpecificParams = NULL;
  1142     }
  1143     SDL_free(effect->rglDirection);
  1144     effect->rglDirection = NULL;
  1145 }
  1146 
  1147 
  1148 /*
  1149  * Gets the effect type from the generic SDL haptic effect wrapper.
  1150  */
  1151 static REFGUID
  1152 SDL_SYS_HapticEffectType(SDL_HapticEffect * effect)
  1153 {
  1154     switch (effect->type) {
  1155     case SDL_HAPTIC_CONSTANT:
  1156         return &GUID_ConstantForce;
  1157 
  1158     case SDL_HAPTIC_RAMP:
  1159         return &GUID_RampForce;
  1160 
  1161     /* !!! FIXME: put this back when we have more bits in 2.1 */
  1162     /* case SDL_HAPTIC_SQUARE:
  1163         return &GUID_Square; */
  1164 
  1165     case SDL_HAPTIC_SINE:
  1166         return &GUID_Sine;
  1167 
  1168     case SDL_HAPTIC_TRIANGLE:
  1169         return &GUID_Triangle;
  1170 
  1171     case SDL_HAPTIC_SAWTOOTHUP:
  1172         return &GUID_SawtoothUp;
  1173 
  1174     case SDL_HAPTIC_SAWTOOTHDOWN:
  1175         return &GUID_SawtoothDown;
  1176 
  1177     case SDL_HAPTIC_SPRING:
  1178         return &GUID_Spring;
  1179 
  1180     case SDL_HAPTIC_DAMPER:
  1181         return &GUID_Damper;
  1182 
  1183     case SDL_HAPTIC_INERTIA:
  1184         return &GUID_Inertia;
  1185 
  1186     case SDL_HAPTIC_FRICTION:
  1187         return &GUID_Friction;
  1188 
  1189     case SDL_HAPTIC_CUSTOM:
  1190         return &GUID_CustomForce;
  1191 
  1192     default:
  1193         SDL_SetError("Haptic: Unknown effect type.");
  1194         return NULL;
  1195     }
  1196 }
  1197 
  1198 
  1199 /*
  1200  * Creates a new haptic effect.
  1201  */
  1202 int
  1203 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
  1204                         SDL_HapticEffect * base)
  1205 {
  1206     HRESULT ret;
  1207     REFGUID type = NULL;
  1208 
  1209     if (!haptic->hwdata->bXInputHaptic) {
  1210         type = SDL_SYS_HapticEffectType(base);
  1211         if (type == NULL) {
  1212             goto err_hweffect;
  1213         }
  1214     }
  1215 
  1216     /* Alloc the effect. */
  1217     effect->hweffect = (struct haptic_hweffect *)
  1218         SDL_malloc(sizeof(struct haptic_hweffect));
  1219     if (effect->hweffect == NULL) {
  1220         SDL_OutOfMemory();
  1221         goto err_hweffect;
  1222     }
  1223 
  1224     SDL_zerop(effect->hweffect);
  1225 
  1226     if (haptic->hwdata->bXInputHaptic) {
  1227         SDL_assert(base->type == SDL_HAPTIC_LEFTRIGHT);  /* should catch this at higher level */
  1228         return SDL_SYS_HapticUpdateEffect(haptic, effect, base);
  1229     }
  1230 
  1231     /* Get the effect. */
  1232     if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
  1233         goto err_effectdone;
  1234     }
  1235 
  1236     /* Create the actual effect. */
  1237     ret = IDirectInputDevice8_CreateEffect(haptic->hwdata->device, type,
  1238                                            &effect->hweffect->effect,
  1239                                            &effect->hweffect->ref, NULL);
  1240     if (FAILED(ret)) {
  1241         DI_SetError("Unable to create effect", ret);
  1242         goto err_effectdone;
  1243     }
  1244 
  1245     return 0;
  1246 
  1247   err_effectdone:
  1248     SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, base->type);
  1249   err_hweffect:
  1250     SDL_free(effect->hweffect);
  1251     effect->hweffect = NULL;
  1252     return -1;
  1253 }
  1254 
  1255 
  1256 /*
  1257  * Updates an effect.
  1258  */
  1259 int
  1260 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
  1261                            struct haptic_effect *effect,
  1262                            SDL_HapticEffect * data)
  1263 {
  1264     HRESULT ret;
  1265     DWORD flags;
  1266     DIEFFECT temp;
  1267 
  1268     if (haptic->hwdata->bXInputHaptic) {
  1269         XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
  1270         SDL_assert(data->type == SDL_HAPTIC_LEFTRIGHT);
  1271         vib->wLeftMotorSpeed = data->leftright.large_magnitude;
  1272         vib->wRightMotorSpeed = data->leftright.small_magnitude;
  1273         SDL_LockMutex(haptic->hwdata->mutex);
  1274         if (haptic->hwdata->stopTicks) {  /* running right now? Update it. */
  1275             XINPUTSETSTATE(haptic->hwdata->userid, vib);
  1276         }
  1277         SDL_UnlockMutex(haptic->hwdata->mutex);
  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         if(effect->effect.leftright.length == SDL_HAPTIC_INFINITY || iterations == SDL_HAPTIC_INFINITY) {
  1331             haptic->hwdata->stopTicks = SDL_HAPTIC_INFINITY;
  1332         } else {
  1333             haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations);
  1334         }
  1335         SDL_UnlockMutex(haptic->hwdata->mutex);
  1336         return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1;
  1337     }
  1338 
  1339     /* Check if it's infinite. */
  1340     if (iterations == SDL_HAPTIC_INFINITY) {
  1341         iter = INFINITE;
  1342     } else
  1343         iter = iterations;
  1344 
  1345     /* Run the effect. */
  1346     ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0);
  1347     if (FAILED(ret)) {
  1348         return DI_SetError("Running the effect", ret);
  1349     }
  1350 
  1351     return 0;
  1352 }
  1353 
  1354 
  1355 /*
  1356  * Stops an effect.
  1357  */
  1358 int
  1359 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1360 {
  1361     HRESULT ret;
  1362 
  1363     if (haptic->hwdata->bXInputHaptic) {
  1364         XINPUT_VIBRATION vibration = { 0, 0 };
  1365         SDL_LockMutex(haptic->hwdata->mutex);
  1366         haptic->hwdata->stopTicks = 0;
  1367         SDL_UnlockMutex(haptic->hwdata->mutex);
  1368         return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
  1369     }
  1370 
  1371     ret = IDirectInputEffect_Stop(effect->hweffect->ref);
  1372     if (FAILED(ret)) {
  1373         return DI_SetError("Unable to stop effect", ret);
  1374     }
  1375 
  1376     return 0;
  1377 }
  1378 
  1379 
  1380 /*
  1381  * Frees the effect.
  1382  */
  1383 void
  1384 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1385 {
  1386     HRESULT ret;
  1387 
  1388     if (haptic->hwdata->bXInputHaptic) {
  1389         SDL_SYS_HapticStopEffect(haptic, effect);
  1390     } else {
  1391         ret = IDirectInputEffect_Unload(effect->hweffect->ref);
  1392         if (FAILED(ret)) {
  1393             DI_SetError("Removing effect from the device", ret);
  1394         }
  1395         SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
  1396                                    effect->effect.type);
  1397     }
  1398     SDL_free(effect->hweffect);
  1399     effect->hweffect = NULL;
  1400 }
  1401 
  1402 
  1403 /*
  1404  * Gets the status of a haptic effect.
  1405  */
  1406 int
  1407 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
  1408                               struct haptic_effect *effect)
  1409 {
  1410     HRESULT ret;
  1411     DWORD status;
  1412 
  1413     ret = IDirectInputEffect_GetEffectStatus(effect->hweffect->ref, &status);
  1414     if (FAILED(ret)) {
  1415         return DI_SetError("Getting effect status", ret);
  1416     }
  1417 
  1418     if (status == 0)
  1419         return SDL_FALSE;
  1420     return SDL_TRUE;
  1421 }
  1422 
  1423 
  1424 /*
  1425  * Sets the gain.
  1426  */
  1427 int
  1428 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
  1429 {
  1430     HRESULT ret;
  1431     DIPROPDWORD dipdw;
  1432 
  1433     /* Create the weird structure thingy. */
  1434     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  1435     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1436     dipdw.diph.dwObj = 0;
  1437     dipdw.diph.dwHow = DIPH_DEVICE;
  1438     dipdw.dwData = gain * 100;  /* 0 to 10,000 */
  1439 
  1440     /* Try to set the autocenter. */
  1441     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
  1442                                           DIPROP_FFGAIN, &dipdw.diph);
  1443     if (FAILED(ret)) {
  1444         return DI_SetError("Setting gain", ret);
  1445     }
  1446 
  1447     return 0;
  1448 }
  1449 
  1450 
  1451 /*
  1452  * Sets the autocentering.
  1453  */
  1454 int
  1455 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
  1456 {
  1457     HRESULT ret;
  1458     DIPROPDWORD dipdw;
  1459 
  1460     /* Create the weird structure thingy. */
  1461     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  1462     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1463     dipdw.diph.dwObj = 0;
  1464     dipdw.diph.dwHow = DIPH_DEVICE;
  1465     dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF :
  1466         DIPROPAUTOCENTER_ON;
  1467 
  1468     /* Try to set the autocenter. */
  1469     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
  1470                                           DIPROP_AUTOCENTER, &dipdw.diph);
  1471     if (FAILED(ret)) {
  1472         return DI_SetError("Setting autocenter", ret);
  1473     }
  1474 
  1475     return 0;
  1476 }
  1477 
  1478 
  1479 /*
  1480  * Pauses the device.
  1481  */
  1482 int
  1483 SDL_SYS_HapticPause(SDL_Haptic * haptic)
  1484 {
  1485     HRESULT ret;
  1486 
  1487     /* Pause the device. */
  1488     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1489                                                        DISFFC_PAUSE);
  1490     if (FAILED(ret)) {
  1491         return DI_SetError("Pausing the device", ret);
  1492     }
  1493 
  1494     return 0;
  1495 }
  1496 
  1497 
  1498 /*
  1499  * Pauses the device.
  1500  */
  1501 int
  1502 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
  1503 {
  1504     HRESULT ret;
  1505 
  1506     /* Unpause the device. */
  1507     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1508                                                        DISFFC_CONTINUE);
  1509     if (FAILED(ret)) {
  1510         return DI_SetError("Pausing the device", ret);
  1511     }
  1512 
  1513     return 0;
  1514 }
  1515 
  1516 
  1517 /*
  1518  * Stops all the playing effects on the device.
  1519  */
  1520 int
  1521 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
  1522 {
  1523     HRESULT ret;
  1524 
  1525     if (haptic->hwdata->bXInputHaptic) {
  1526         XINPUT_VIBRATION vibration = { 0, 0 };
  1527         SDL_LockMutex(haptic->hwdata->mutex);
  1528         haptic->hwdata->stopTicks = 0;
  1529         SDL_UnlockMutex(haptic->hwdata->mutex);
  1530         return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
  1531     }
  1532 
  1533     /* Try to stop the effects. */
  1534     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1535                                                        DISFFC_STOPALL);
  1536     if (FAILED(ret)) {
  1537         return DI_SetError("Stopping the device", ret);
  1538     }
  1539 
  1540     return 0;
  1541 }
  1542 
  1543 
  1544 /* !!! FIXME: this is a hack, remove this later. */
  1545 /* Since XInput doesn't offer a way to vibrate for X time, we hook into
  1546  *  SDL_PumpEvents() to check if it's time to stop vibrating with some
  1547  *  frequency.
  1548  * In practice, this works for 99% of use cases. But in an ideal world,
  1549  *  we do this in a separate thread so that:
  1550  *    - we aren't bound to when the app chooses to pump the event queue.
  1551  *    - we aren't adding more polling to the event queue
  1552  *    - we can emulate all the haptic effects correctly (start on a delay,
  1553  *      mix multiple effects, etc).
  1554  *
  1555  * Mostly, this is here to get rumbling to work, and all the other features
  1556  *  are absent in the XInput path for now.  :(
  1557  */
  1558 static int SDLCALL
  1559 SDL_RunXInputHaptic(void *arg)
  1560 {
  1561     struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg;
  1562 
  1563     while (!hwdata->stopThread) {
  1564         SDL_Delay(50);
  1565         SDL_LockMutex(hwdata->mutex);
  1566         /* If we're currently running and need to stop... */
  1567         const Uint32 stopTicks = hwdata->stopTicks;
  1568         if (stopTicks) {
  1569             if ((stopTicks != SDL_HAPTIC_INFINITY) && (stopTicks < SDL_GetTicks())) {
  1570                 XINPUT_VIBRATION vibration = { 0, 0 };
  1571                 hwdata->stopTicks = 0;
  1572                 XINPUTSETSTATE(hwdata->userid, &vibration);
  1573             }
  1574         }
  1575         SDL_UnlockMutex(hwdata->mutex);
  1576     }
  1577 
  1578     return 0;
  1579 }
  1580 
  1581 #endif /* SDL_HAPTIC_DINPUT */
  1582 
  1583 /* vi: set ts=4 sw=4 expandtab: */