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