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