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