src/haptic/windows/SDL_dinputhaptic.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 04 Oct 2016 03:50:28 -0700
changeset 10463 ac6a748250f4
parent 10442 80d5e7642a06
child 10737 3406a0f8b041
permissions -rw-r--r--
Fixed bug 3021 - HapticOpenFromJoystick() problems

Joe Thompson

With Direct Input device (MOMO Steering Wheel w/FF)
with SDL 2.0.3,
SDL_HapticOpenFromJoystick() would fail. (Can't set exclusive mode)
Now with 2.0.4 rc1,
SDL_HapticOpenFromJoystick() succeeds but the the returned SDL_Haptic* cannot be used. Calls to SDL_HapticNewEffect() fail with "Haptic error Unable to create effect"

If SDL_HapticOpen() is used instead of HapticOpenFromJoystick(), the device is usable. Calls to HapticNewEffect() succeed with the exact same parameters as the previous failing call.

I have attached a proposed patch for this issue.

When using SDL_HapticOpenFromJoystick(), the original code did not (re)enumerate the axes. This returned a new haptic device with 0 axes. Later, when a new effect is created, SDL_SYS_SetDirection() would set the flags to include DIEFF_SPHERICAL, regardless of what the caller actually set. (see Line 566 in SDL_dinputhaptic.c). This would cause the SDL_HapticNewEffect() to fail (or interpret the coordinates incorreclty.)

The patch moves the call to IDirectInputDevice8_EnumObjects() outside of the if() block so that the axes are (re)enumerated for the new haptic device.

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