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