src/haptic/windows/SDL_dinputhaptic.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 21 May 2018 12:00:21 -0400
changeset 11994 8e094f91ddab
parent 11811 5d94cb6b24d3
child 12503 806492103856
permissions -rw-r--r--
thread: fixed compiler warnings on non-Linux systems that use pthread.

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