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