src/haptic/windows/SDL_dinputhaptic.c
author Ryan C. Gordon <icculus@icculus.org>
Sat, 16 Aug 2014 16:49:00 -0400
changeset 9071 8e04e17c7af3
parent 9070 8973a237f360
child 9147 6bf589c8d549
permissions -rw-r--r--
Haptic: Fix clamping bugs on Windows, by using the Darwin haptics code.

Thanks, Elias!

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