src/haptic/windows/SDL_syshaptic.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 24 Jan 2011 21:20:30 -0800
changeset 5090 327f181542f1
parent 5062 e8916fe9cfc8
child 5358 5b330ef7b8dd
permissions -rw-r--r--
Include windows.h in a single point in the source, so we can be consistent about the definition of UNICODE and have core utility functions for Windows that all modules can share.

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