src/haptic/windows/SDL_syshaptic.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 20 Feb 2011 09:28:13 -0800
changeset 5358 5b330ef7b8dd
parent 5090 327f181542f1
child 5535 96594ac5fd1a
permissions -rw-r--r--
Untested fix for bug 946 (SDL_HapticIndex returns 0 for all devices)

Edgar Simo 2011-02-20 09:02:31 PST

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