src/haptic/windows/SDL_syshaptic.c
author Sam Lantinga
Tue, 19 Apr 2011 08:08:50 -0700
changeset 5539 b305e3c2d2e7
parent 5535 96594ac5fd1a
child 5591 17beb16fa838
permissions -rw-r--r--
Fixed: Windows always fails with SDL_HapticOpenFromJoystick

The windows function SDL_SYS_HapticOpenFromJoystick fails because DIDEVICEINSTANCE joy_instance does not have its dwSize field initialized. The attached patch includes the fix along with a fix for two similar problems.

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