src/haptic/windows/SDL_syshaptic.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 20 Jan 2011 18:04:05 -0800
changeset 5062 e8916fe9cfc8
parent 3013 src/haptic/win32/SDL_syshaptic.c@8cc00819c8d6
child 5090 327f181542f1
permissions -rw-r--r--
Fixed bug #925

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