src/haptic/windows/SDL_dinputhaptic.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 20 May 2020 16:58:33 -0400
changeset 13838 02e41b30186f
parent 13789 a359f4f93439
child 13914 348d0896fc9e
permissions -rw-r--r--
windows: Fix calls to CoCreateInstance() so last parameter is a LPVOID *.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2020 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     case SDL_HAPTIC_STEERING_AXIS:
   593         effect->dwFlags |= DIEFF_CARTESIAN;
   594         rglDir[0] = 0;
   595         return 0;
   596 
   597     default:
   598         return SDL_SetError("Haptic: Unknown direction type.");
   599     }
   600 }
   601 
   602 /* Clamps and converts. */
   603 #define CCONVERT(x)   (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
   604 /* Just converts. */
   605 #define CONVERT(x)    (((x)*10000) / 0x7FFF)
   606 /*
   607  * Creates the DIEFFECT from a SDL_HapticEffect.
   608  */
   609 static int
   610 SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
   611                    SDL_HapticEffect * src)
   612 {
   613     int i;
   614     DICONSTANTFORCE *constant;
   615     DIPERIODIC *periodic;
   616     DICONDITION *condition;     /* Actually an array of conditions - one per axis. */
   617     DIRAMPFORCE *ramp;
   618     DICUSTOMFORCE *custom;
   619     DIENVELOPE *envelope;
   620     SDL_HapticConstant *hap_constant;
   621     SDL_HapticPeriodic *hap_periodic;
   622     SDL_HapticCondition *hap_condition;
   623     SDL_HapticRamp *hap_ramp;
   624     SDL_HapticCustom *hap_custom;
   625     DWORD *axes;
   626 
   627     /* Set global stuff. */
   628     SDL_memset(dest, 0, sizeof(DIEFFECT));
   629     dest->dwSize = sizeof(DIEFFECT);    /* Set the structure size. */
   630     dest->dwSamplePeriod = 0;   /* Not used by us. */
   631     dest->dwGain = 10000;       /* Gain is set globally, not locally. */
   632     dest->dwFlags = DIEFF_OBJECTOFFSETS;        /* Seems obligatory. */
   633 
   634     /* Envelope. */
   635     envelope = SDL_malloc(sizeof(DIENVELOPE));
   636     if (envelope == NULL) {
   637         return SDL_OutOfMemory();
   638     }
   639     SDL_memset(envelope, 0, sizeof(DIENVELOPE));
   640     dest->lpEnvelope = envelope;
   641     envelope->dwSize = sizeof(DIENVELOPE);      /* Always should be this. */
   642 
   643     /* Axes. */
   644     if (src->constant.direction.type == SDL_HAPTIC_STEERING_AXIS) {
   645         dest->cAxes = 1;
   646     } else {
   647         dest->cAxes = haptic->naxes;
   648     }
   649     if (dest->cAxes > 0) {
   650         axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
   651         if (axes == NULL) {
   652             return SDL_OutOfMemory();
   653         }
   654         axes[0] = haptic->hwdata->axes[0];      /* Always at least one axis. */
   655         if (dest->cAxes > 1) {
   656             axes[1] = haptic->hwdata->axes[1];
   657         }
   658         if (dest->cAxes > 2) {
   659             axes[2] = haptic->hwdata->axes[2];
   660         }
   661         dest->rgdwAxes = axes;
   662     }
   663 
   664     /* The big type handling switch, even bigger than Linux's version. */
   665     switch (src->type) {
   666     case SDL_HAPTIC_CONSTANT:
   667         hap_constant = &src->constant;
   668         constant = SDL_malloc(sizeof(DICONSTANTFORCE));
   669         if (constant == NULL) {
   670             return SDL_OutOfMemory();
   671         }
   672         SDL_memset(constant, 0, sizeof(DICONSTANTFORCE));
   673 
   674         /* Specifics */
   675         constant->lMagnitude = CONVERT(hap_constant->level);
   676         dest->cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
   677         dest->lpvTypeSpecificParams = constant;
   678 
   679         /* Generics */
   680         dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
   681         dest->dwTriggerButton = DIGetTriggerButton(hap_constant->button);
   682         dest->dwTriggerRepeatInterval = hap_constant->interval;
   683         dest->dwStartDelay = hap_constant->delay * 1000;        /* In microseconds. */
   684 
   685         /* Direction. */
   686         if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes) < 0) {
   687             return -1;
   688         }
   689 
   690         /* Envelope */
   691         if ((hap_constant->attack_length == 0)
   692             && (hap_constant->fade_length == 0)) {
   693             SDL_free(dest->lpEnvelope);
   694             dest->lpEnvelope = NULL;
   695         } else {
   696             envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level);
   697             envelope->dwAttackTime = hap_constant->attack_length * 1000;
   698             envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level);
   699             envelope->dwFadeTime = hap_constant->fade_length * 1000;
   700         }
   701 
   702         break;
   703 
   704     case SDL_HAPTIC_SINE:
   705     /* !!! FIXME: put this back when we have more bits in 2.1 */
   706     /* case SDL_HAPTIC_SQUARE: */
   707     case SDL_HAPTIC_TRIANGLE:
   708     case SDL_HAPTIC_SAWTOOTHUP:
   709     case SDL_HAPTIC_SAWTOOTHDOWN:
   710         hap_periodic = &src->periodic;
   711         periodic = SDL_malloc(sizeof(DIPERIODIC));
   712         if (periodic == NULL) {
   713             return SDL_OutOfMemory();
   714         }
   715         SDL_memset(periodic, 0, sizeof(DIPERIODIC));
   716 
   717         /* Specifics */
   718         periodic->dwMagnitude = CONVERT(SDL_abs(hap_periodic->magnitude));
   719         periodic->lOffset = CONVERT(hap_periodic->offset);
   720         periodic->dwPhase = 
   721                 (hap_periodic->phase + (hap_periodic->magnitude < 0 ? 18000 : 0)) % 36000;
   722         periodic->dwPeriod = hap_periodic->period * 1000;
   723         dest->cbTypeSpecificParams = sizeof(DIPERIODIC);
   724         dest->lpvTypeSpecificParams = periodic;
   725 
   726         /* Generics */
   727         dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
   728         dest->dwTriggerButton = DIGetTriggerButton(hap_periodic->button);
   729         dest->dwTriggerRepeatInterval = hap_periodic->interval;
   730         dest->dwStartDelay = hap_periodic->delay * 1000;        /* In microseconds. */
   731 
   732         /* Direction. */
   733         if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
   734             < 0) {
   735             return -1;
   736         }
   737 
   738         /* Envelope */
   739         if ((hap_periodic->attack_length == 0)
   740             && (hap_periodic->fade_length == 0)) {
   741             SDL_free(dest->lpEnvelope);
   742             dest->lpEnvelope = NULL;
   743         } else {
   744             envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level);
   745             envelope->dwAttackTime = hap_periodic->attack_length * 1000;
   746             envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level);
   747             envelope->dwFadeTime = hap_periodic->fade_length * 1000;
   748         }
   749 
   750         break;
   751 
   752     case SDL_HAPTIC_SPRING:
   753     case SDL_HAPTIC_DAMPER:
   754     case SDL_HAPTIC_INERTIA:
   755     case SDL_HAPTIC_FRICTION:
   756         hap_condition = &src->condition;
   757         condition = SDL_malloc(sizeof(DICONDITION) * dest->cAxes);
   758         if (condition == NULL) {
   759             return SDL_OutOfMemory();
   760         }
   761         SDL_memset(condition, 0, sizeof(DICONDITION));
   762 
   763         /* Specifics */
   764         for (i = 0; i < (int) dest->cAxes; i++) {
   765             condition[i].lOffset = CONVERT(hap_condition->center[i]);
   766             condition[i].lPositiveCoefficient =
   767                 CONVERT(hap_condition->right_coeff[i]);
   768             condition[i].lNegativeCoefficient =
   769                 CONVERT(hap_condition->left_coeff[i]);
   770             condition[i].dwPositiveSaturation =
   771                 CCONVERT(hap_condition->right_sat[i] / 2);
   772             condition[i].dwNegativeSaturation =
   773                 CCONVERT(hap_condition->left_sat[i] / 2);
   774             condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i] / 2);
   775         }
   776         dest->cbTypeSpecificParams = sizeof(DICONDITION) * dest->cAxes;
   777         dest->lpvTypeSpecificParams = condition;
   778 
   779         /* Generics */
   780         dest->dwDuration = hap_condition->length * 1000;        /* In microseconds. */
   781         dest->dwTriggerButton = DIGetTriggerButton(hap_condition->button);
   782         dest->dwTriggerRepeatInterval = hap_condition->interval;
   783         dest->dwStartDelay = hap_condition->delay * 1000;       /* In microseconds. */
   784 
   785         /* Direction. */
   786         if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
   787             < 0) {
   788             return -1;
   789         }
   790 
   791         /* Envelope - Not actually supported by most CONDITION implementations. */
   792         SDL_free(dest->lpEnvelope);
   793         dest->lpEnvelope = NULL;
   794 
   795         break;
   796 
   797     case SDL_HAPTIC_RAMP:
   798         hap_ramp = &src->ramp;
   799         ramp = SDL_malloc(sizeof(DIRAMPFORCE));
   800         if (ramp == NULL) {
   801             return SDL_OutOfMemory();
   802         }
   803         SDL_memset(ramp, 0, sizeof(DIRAMPFORCE));
   804 
   805         /* Specifics */
   806         ramp->lStart = CONVERT(hap_ramp->start);
   807         ramp->lEnd = CONVERT(hap_ramp->end);
   808         dest->cbTypeSpecificParams = sizeof(DIRAMPFORCE);
   809         dest->lpvTypeSpecificParams = ramp;
   810 
   811         /* Generics */
   812         dest->dwDuration = hap_ramp->length * 1000;     /* In microseconds. */
   813         dest->dwTriggerButton = DIGetTriggerButton(hap_ramp->button);
   814         dest->dwTriggerRepeatInterval = hap_ramp->interval;
   815         dest->dwStartDelay = hap_ramp->delay * 1000;    /* In microseconds. */
   816 
   817         /* Direction. */
   818         if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
   819             return -1;
   820         }
   821 
   822         /* Envelope */
   823         if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
   824             SDL_free(dest->lpEnvelope);
   825             dest->lpEnvelope = NULL;
   826         } else {
   827             envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level);
   828             envelope->dwAttackTime = hap_ramp->attack_length * 1000;
   829             envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level);
   830             envelope->dwFadeTime = hap_ramp->fade_length * 1000;
   831         }
   832 
   833         break;
   834 
   835     case SDL_HAPTIC_CUSTOM:
   836         hap_custom = &src->custom;
   837         custom = SDL_malloc(sizeof(DICUSTOMFORCE));
   838         if (custom == NULL) {
   839             return SDL_OutOfMemory();
   840         }
   841         SDL_memset(custom, 0, sizeof(DICUSTOMFORCE));
   842 
   843         /* Specifics */
   844         custom->cChannels = hap_custom->channels;
   845         custom->dwSamplePeriod = hap_custom->period * 1000;
   846         custom->cSamples = hap_custom->samples;
   847         custom->rglForceData =
   848             SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
   849         for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) {      /* Copy data. */
   850             custom->rglForceData[i] = CCONVERT(hap_custom->data[i]);
   851         }
   852         dest->cbTypeSpecificParams = sizeof(DICUSTOMFORCE);
   853         dest->lpvTypeSpecificParams = custom;
   854 
   855         /* Generics */
   856         dest->dwDuration = hap_custom->length * 1000;   /* In microseconds. */
   857         dest->dwTriggerButton = DIGetTriggerButton(hap_custom->button);
   858         dest->dwTriggerRepeatInterval = hap_custom->interval;
   859         dest->dwStartDelay = hap_custom->delay * 1000;  /* In microseconds. */
   860 
   861         /* Direction. */
   862         if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) < 0) {
   863             return -1;
   864         }
   865 
   866         /* Envelope */
   867         if ((hap_custom->attack_length == 0)
   868             && (hap_custom->fade_length == 0)) {
   869             SDL_free(dest->lpEnvelope);
   870             dest->lpEnvelope = NULL;
   871         } else {
   872             envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level);
   873             envelope->dwAttackTime = hap_custom->attack_length * 1000;
   874             envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level);
   875             envelope->dwFadeTime = hap_custom->fade_length * 1000;
   876         }
   877 
   878         break;
   879 
   880     default:
   881         return SDL_SetError("Haptic: Unknown effect type.");
   882     }
   883 
   884     return 0;
   885 }
   886 
   887 
   888 /*
   889  * Frees an DIEFFECT allocated by SDL_SYS_ToDIEFFECT.
   890  */
   891 static void
   892 SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type)
   893 {
   894     DICUSTOMFORCE *custom;
   895 
   896     SDL_free(effect->lpEnvelope);
   897     effect->lpEnvelope = NULL;
   898     SDL_free(effect->rgdwAxes);
   899     effect->rgdwAxes = NULL;
   900     if (effect->lpvTypeSpecificParams != NULL) {
   901         if (type == SDL_HAPTIC_CUSTOM) {        /* Must free the custom data. */
   902             custom = (DICUSTOMFORCE *) effect->lpvTypeSpecificParams;
   903             SDL_free(custom->rglForceData);
   904             custom->rglForceData = NULL;
   905         }
   906         SDL_free(effect->lpvTypeSpecificParams);
   907         effect->lpvTypeSpecificParams = NULL;
   908     }
   909     SDL_free(effect->rglDirection);
   910     effect->rglDirection = NULL;
   911 }
   912 
   913 /*
   914  * Gets the effect type from the generic SDL haptic effect wrapper.
   915  */
   916 static REFGUID
   917 SDL_SYS_HapticEffectType(SDL_HapticEffect * effect)
   918 {
   919     switch (effect->type) {
   920     case SDL_HAPTIC_CONSTANT:
   921         return &GUID_ConstantForce;
   922 
   923     case SDL_HAPTIC_RAMP:
   924         return &GUID_RampForce;
   925 
   926     /* !!! FIXME: put this back when we have more bits in 2.1 */
   927     /* case SDL_HAPTIC_SQUARE:
   928         return &GUID_Square; */
   929 
   930     case SDL_HAPTIC_SINE:
   931         return &GUID_Sine;
   932 
   933     case SDL_HAPTIC_TRIANGLE:
   934         return &GUID_Triangle;
   935 
   936     case SDL_HAPTIC_SAWTOOTHUP:
   937         return &GUID_SawtoothUp;
   938 
   939     case SDL_HAPTIC_SAWTOOTHDOWN:
   940         return &GUID_SawtoothDown;
   941 
   942     case SDL_HAPTIC_SPRING:
   943         return &GUID_Spring;
   944 
   945     case SDL_HAPTIC_DAMPER:
   946         return &GUID_Damper;
   947 
   948     case SDL_HAPTIC_INERTIA:
   949         return &GUID_Inertia;
   950 
   951     case SDL_HAPTIC_FRICTION:
   952         return &GUID_Friction;
   953 
   954     case SDL_HAPTIC_CUSTOM:
   955         return &GUID_CustomForce;
   956 
   957     default:
   958         return NULL;
   959     }
   960 }
   961 int
   962 SDL_DINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base)
   963 {
   964     HRESULT ret;
   965     REFGUID type = SDL_SYS_HapticEffectType(base);
   966 
   967     if (type == NULL) {
   968         SDL_SetError("Haptic: Unknown effect type.");
   969         return -1;
   970     }
   971 
   972     /* Get the effect. */
   973     if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
   974         goto err_effectdone;
   975     }
   976 
   977     /* Create the actual effect. */
   978     ret = IDirectInputDevice8_CreateEffect(haptic->hwdata->device, type,
   979         &effect->hweffect->effect,
   980         &effect->hweffect->ref, NULL);
   981     if (FAILED(ret)) {
   982         DI_SetError("Unable to create effect", ret);
   983         goto err_effectdone;
   984     }
   985 
   986     return 0;
   987 
   988 err_effectdone:
   989     SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, base->type);
   990     return -1;
   991 }
   992 
   993 int
   994 SDL_DINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data)
   995 {
   996     HRESULT ret;
   997     DWORD flags;
   998     DIEFFECT temp;
   999 
  1000     /* Get the effect. */
  1001     SDL_memset(&temp, 0, sizeof(DIEFFECT));
  1002     if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) {
  1003         goto err_update;
  1004     }
  1005 
  1006     /* Set the flags.  Might be worthwhile to diff temp with loaded effect and
  1007     *  only change those parameters. */
  1008     flags = DIEP_DIRECTION |
  1009         DIEP_DURATION |
  1010         DIEP_ENVELOPE |
  1011         DIEP_STARTDELAY |
  1012         DIEP_TRIGGERBUTTON |
  1013         DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS;
  1014 
  1015     /* Create the actual effect. */
  1016     ret =
  1017         IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags);
  1018     if (ret == DIERR_NOTEXCLUSIVEACQUIRED) {
  1019         IDirectInputDevice8_Unacquire(haptic->hwdata->device);
  1020         ret = IDirectInputDevice8_SetCooperativeLevel(haptic->hwdata->device, SDL_HelperWindow, DISCL_EXCLUSIVE | DISCL_BACKGROUND);
  1021         if (SUCCEEDED(ret)) {
  1022             ret = DIERR_NOTACQUIRED;
  1023         }
  1024     }
  1025     if (ret == DIERR_INPUTLOST || ret == DIERR_NOTACQUIRED) {
  1026         ret = IDirectInputDevice8_Acquire(haptic->hwdata->device);
  1027         if (SUCCEEDED(ret)) {
  1028             ret = IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags);
  1029         }
  1030     }
  1031     if (FAILED(ret)) {
  1032         DI_SetError("Unable to update effect", ret);
  1033         goto err_update;
  1034     }
  1035 
  1036     /* Copy it over. */
  1037     SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, data->type);
  1038     SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(DIEFFECT));
  1039 
  1040     return 0;
  1041 
  1042 err_update:
  1043     SDL_SYS_HapticFreeDIEFFECT(&temp, data->type);
  1044     return -1;
  1045 }
  1046 
  1047 int
  1048 SDL_DINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations)
  1049 {
  1050     HRESULT ret;
  1051     DWORD iter;
  1052 
  1053     /* Check if it's infinite. */
  1054     if (iterations == SDL_HAPTIC_INFINITY) {
  1055         iter = INFINITE;
  1056     } else {
  1057         iter = iterations;
  1058     }
  1059 
  1060     /* Run the effect. */
  1061     ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0);
  1062     if (FAILED(ret)) {
  1063         return DI_SetError("Running the effect", ret);
  1064     }
  1065     return 0;
  1066 }
  1067 
  1068 int
  1069 SDL_DINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1070 {
  1071     HRESULT ret;
  1072 
  1073     ret = IDirectInputEffect_Stop(effect->hweffect->ref);
  1074     if (FAILED(ret)) {
  1075         return DI_SetError("Unable to stop effect", ret);
  1076     }
  1077     return 0;
  1078 }
  1079 
  1080 void
  1081 SDL_DINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1082 {
  1083     HRESULT ret;
  1084 
  1085     ret = IDirectInputEffect_Unload(effect->hweffect->ref);
  1086     if (FAILED(ret)) {
  1087         DI_SetError("Removing effect from the device", ret);
  1088     }
  1089     SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, effect->effect.type);
  1090 }
  1091 
  1092 int
  1093 SDL_DINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect)
  1094 {
  1095     HRESULT ret;
  1096     DWORD status;
  1097 
  1098     ret = IDirectInputEffect_GetEffectStatus(effect->hweffect->ref, &status);
  1099     if (FAILED(ret)) {
  1100         return DI_SetError("Getting effect status", ret);
  1101     }
  1102 
  1103     if (status == 0)
  1104         return SDL_FALSE;
  1105     return SDL_TRUE;
  1106 }
  1107 
  1108 int
  1109 SDL_DINPUT_HapticSetGain(SDL_Haptic * haptic, int gain)
  1110 {
  1111     HRESULT ret;
  1112     DIPROPDWORD dipdw;
  1113 
  1114     /* Create the weird structure thingy. */
  1115     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  1116     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1117     dipdw.diph.dwObj = 0;
  1118     dipdw.diph.dwHow = DIPH_DEVICE;
  1119     dipdw.dwData = gain * 100;  /* 0 to 10,000 */
  1120 
  1121     /* Try to set the autocenter. */
  1122     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
  1123         DIPROP_FFGAIN, &dipdw.diph);
  1124     if (FAILED(ret)) {
  1125         return DI_SetError("Setting gain", ret);
  1126     }
  1127     return 0;
  1128 }
  1129 
  1130 int
  1131 SDL_DINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
  1132 {
  1133     HRESULT ret;
  1134     DIPROPDWORD dipdw;
  1135 
  1136     /* Create the weird structure thingy. */
  1137     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  1138     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1139     dipdw.diph.dwObj = 0;
  1140     dipdw.diph.dwHow = DIPH_DEVICE;
  1141     dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF :
  1142         DIPROPAUTOCENTER_ON;
  1143 
  1144     /* Try to set the autocenter. */
  1145     ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
  1146         DIPROP_AUTOCENTER, &dipdw.diph);
  1147     if (FAILED(ret)) {
  1148         return DI_SetError("Setting autocenter", ret);
  1149     }
  1150     return 0;
  1151 }
  1152 
  1153 int
  1154 SDL_DINPUT_HapticPause(SDL_Haptic * haptic)
  1155 {
  1156     HRESULT ret;
  1157 
  1158     /* Pause the device. */
  1159     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1160         DISFFC_PAUSE);
  1161     if (FAILED(ret)) {
  1162         return DI_SetError("Pausing the device", ret);
  1163     }
  1164     return 0;
  1165 }
  1166 
  1167 int
  1168 SDL_DINPUT_HapticUnpause(SDL_Haptic * haptic)
  1169 {
  1170     HRESULT ret;
  1171 
  1172     /* Unpause the device. */
  1173     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1174         DISFFC_CONTINUE);
  1175     if (FAILED(ret)) {
  1176         return DI_SetError("Pausing the device", ret);
  1177     }
  1178     return 0;
  1179 }
  1180 
  1181 int
  1182 SDL_DINPUT_HapticStopAll(SDL_Haptic * haptic)
  1183 {
  1184     HRESULT ret;
  1185 
  1186     /* Try to stop the effects. */
  1187     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
  1188         DISFFC_STOPALL);
  1189     if (FAILED(ret)) {
  1190         return DI_SetError("Stopping the device", ret);
  1191     }
  1192     return 0;
  1193 }
  1194 
  1195 #else /* !SDL_HAPTIC_DINPUT */
  1196 
  1197 typedef struct DIDEVICEINSTANCE DIDEVICEINSTANCE;
  1198 typedef struct SDL_hapticlist_item SDL_hapticlist_item;
  1199 
  1200 int
  1201 SDL_DINPUT_HapticInit(void)
  1202 {
  1203     return 0;
  1204 }
  1205 
  1206 int
  1207 SDL_DINPUT_MaybeAddDevice(const DIDEVICEINSTANCE * pdidInstance)
  1208 {
  1209     return SDL_Unsupported();
  1210 }
  1211 
  1212 int
  1213 SDL_DINPUT_MaybeRemoveDevice(const DIDEVICEINSTANCE * pdidInstance)
  1214 {
  1215     return SDL_Unsupported();
  1216 }
  1217 
  1218 int
  1219 SDL_DINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item)
  1220 {
  1221     return SDL_Unsupported();
  1222 }
  1223 
  1224 int
  1225 SDL_DINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
  1226 {
  1227     return SDL_Unsupported();
  1228 }
  1229 
  1230 int
  1231 SDL_DINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
  1232 {
  1233     return SDL_Unsupported();
  1234 }
  1235 
  1236 void
  1237 SDL_DINPUT_HapticClose(SDL_Haptic * haptic)
  1238 {
  1239 }
  1240 
  1241 void
  1242 SDL_DINPUT_HapticQuit(void)
  1243 {
  1244 }
  1245 
  1246 int
  1247 SDL_DINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base)
  1248 {
  1249     return SDL_Unsupported();
  1250 }
  1251 
  1252 int
  1253 SDL_DINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data)
  1254 {
  1255     return SDL_Unsupported();
  1256 }
  1257 
  1258 int
  1259 SDL_DINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations)
  1260 {
  1261     return SDL_Unsupported();
  1262 }
  1263 
  1264 int
  1265 SDL_DINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1266 {
  1267     return SDL_Unsupported();
  1268 }
  1269 
  1270 void
  1271 SDL_DINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1272 {
  1273 }
  1274 
  1275 int
  1276 SDL_DINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect)
  1277 {
  1278     return SDL_Unsupported();
  1279 }
  1280 
  1281 int
  1282 SDL_DINPUT_HapticSetGain(SDL_Haptic * haptic, int gain)
  1283 {
  1284     return SDL_Unsupported();
  1285 }
  1286 
  1287 int
  1288 SDL_DINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
  1289 {
  1290     return SDL_Unsupported();
  1291 }
  1292 
  1293 int
  1294 SDL_DINPUT_HapticPause(SDL_Haptic * haptic)
  1295 {
  1296     return SDL_Unsupported();
  1297 }
  1298 
  1299 int
  1300 SDL_DINPUT_HapticUnpause(SDL_Haptic * haptic)
  1301 {
  1302     return SDL_Unsupported();
  1303 }
  1304 
  1305 int
  1306 SDL_DINPUT_HapticStopAll(SDL_Haptic * haptic)
  1307 {
  1308     return SDL_Unsupported();
  1309 }
  1310 
  1311 #endif /* SDL_HAPTIC_DINPUT */
  1312 
  1313 /* vi: set ts=4 sw=4 expandtab: */