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