src/haptic/darwin/SDL_syshaptic.c
author Edgar Simo <bobbens@gmail.com>
Tue, 26 Aug 2008 11:17:16 +0000
changeset 2729 6c8bc4d87282
parent 2713 0906692aa6a4
child 3011 8f4ed5ec2b06
permissions -rw-r--r--
Missing an include.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 2008 Edgar Simo
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #ifdef SDL_HAPTIC_IOKIT
    25 
    26 #include "SDL_haptic.h"
    27 #include "../SDL_syshaptic.h"
    28 #include "SDL_joystick.h"
    29 #include "../../joystick/SDL_sysjoystick.h"     /* For the real SDL_Joystick */
    30 #include "../../joystick/darwin/SDL_sysjoystick_c.h"    /* For joystick hwdata */
    31 
    32 #include <IOKit/IOKitLib.h>
    33 #include <IOKit/hid/IOHIDKeys.h>
    34 #include <IOKit/hid/IOHIDUsageTables.h>
    35 #include <ForceFeedback/ForceFeedback.h>
    36 #include <ForceFeedback/ForceFeedbackConstants.h>
    37 
    38 
    39 #define MAX_HAPTICS  32
    40 
    41 
    42 /*
    43  * List of available haptic devices.
    44  */
    45 static struct
    46 {
    47     char name[256];             /* Name of the device. */
    48 
    49     io_service_t dev;           /* Node we use to create the device. */
    50     SDL_Haptic *haptic;         /* Haptic currently assosciated with it. */
    51 
    52     /* Usage pages for determining if it's a mouse or not. */
    53     long usage;
    54     long usagePage;
    55 } SDL_hapticlist[MAX_HAPTICS];
    56 
    57 
    58 /*
    59  * Haptic system hardware data.
    60  */
    61 struct haptic_hwdata
    62 {
    63     FFDeviceObjectReference device;     /* Hardware device. */
    64     UInt8 axes[3];
    65 };
    66 
    67 
    68 /*
    69  * Haptic system effect data.
    70  */
    71 struct haptic_hweffect
    72 {
    73     FFEffectObjectReference ref;        /* Reference. */
    74     struct FFEFFECT effect;     /* Hardware effect. */
    75 };
    76 
    77 /*
    78  * Prototypes.
    79  */
    80 static void SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type);
    81 static int HIDGetDeviceProduct(io_service_t dev, char *name);
    82 
    83 
    84 /* 
    85  * Like strerror but for force feedback errors.
    86  */
    87 static const char *
    88 FFStrError(HRESULT err)
    89 {
    90     switch (err) {
    91     case FFERR_DEVICEFULL:
    92         return "device full";
    93         /* This should be valid, but for some reason isn't defined... */
    94         /*case FFERR_DEVICENOTREG:
    95            return "device not registered"; */
    96     case FFERR_DEVICEPAUSED:
    97         return "device paused";
    98     case FFERR_DEVICERELEASED:
    99         return "device released";
   100     case FFERR_EFFECTPLAYING:
   101         return "effect playing";
   102     case FFERR_EFFECTTYPEMISMATCH:
   103         return "effect type mismatch";
   104     case FFERR_EFFECTTYPENOTSUPPORTED:
   105         return "effect type not supported";
   106     case FFERR_GENERIC:
   107         return "undetermined error";
   108     case FFERR_HASEFFECTS:
   109         return "device has effects";
   110     case FFERR_INCOMPLETEEFFECT:
   111         return "incomplete effect";
   112     case FFERR_INTERNAL:
   113         return "internal fault";
   114     case FFERR_INVALIDDOWNLOADID:
   115         return "invalid download id";
   116     case FFERR_INVALIDPARAM:
   117         return "invalid parameter";
   118     case FFERR_MOREDATA:
   119         return "more data";
   120     case FFERR_NOINTERFACE:
   121         return "interface not supported";
   122     case FFERR_NOTDOWNLOADED:
   123         return "effect is not downloaded";
   124     case FFERR_NOTINITIALIZED:
   125         return "object has not been initialized";
   126     case FFERR_OUTOFMEMORY:
   127         return "out of memory";
   128     case FFERR_UNPLUGGED:
   129         return "device is unplugged";
   130     case FFERR_UNSUPPORTED:
   131         return "function call unsupported";
   132     case FFERR_UNSUPPORTEDAXIS:
   133         return "axis unsupported";
   134 
   135     default:
   136         return "unknown error";
   137     }
   138 }
   139 
   140 
   141 /*
   142  * Initializes the haptic subsystem.
   143  */
   144 int
   145 SDL_SYS_HapticInit(void)
   146 {
   147     int numhaptics;
   148     IOReturn result;
   149     io_iterator_t iter;
   150     CFDictionaryRef match;
   151     io_service_t device;
   152     CFMutableDictionaryRef hidProperties;
   153     CFTypeRef refCF;
   154 
   155     /* Clear all the memory. */
   156     SDL_memset(SDL_hapticlist, 0, sizeof(SDL_hapticlist));
   157 
   158     /* Get HID devices. */
   159     match = IOServiceMatching(kIOHIDDeviceKey);
   160     if (match == NULL) {
   161         SDL_SetError("Haptic: Failed to get IOServiceMatching.");
   162         return -1;
   163     }
   164 
   165     /* Now search I/O Registry for matching devices. */
   166     result = IOServiceGetMatchingServices(kIOMasterPortDefault, match, &iter);
   167     if (result != kIOReturnSuccess) {
   168         SDL_SetError("Haptic: Couldn't create a HID object iterator.");
   169         return -1;
   170     }
   171     /* IOServiceGetMatchingServices consumes dictionary. */
   172 
   173     if (!IOIteratorIsValid(iter)) {     /* No iterator. */
   174         numhaptics = 0;
   175         return 0;
   176     }
   177 
   178     numhaptics = 0;
   179     while ((device = IOIteratorNext(iter)) != IO_OBJECT_NULL) {
   180 
   181         /* Check for force feedback. */
   182         if (FFIsForceFeedback(device) == FF_OK) {
   183 
   184             /* Set basic device data. */
   185             HIDGetDeviceProduct(device, SDL_hapticlist[numhaptics].name);
   186             SDL_hapticlist[numhaptics].dev = device;
   187             SDL_hapticlist[numhaptics].haptic = NULL;
   188 
   189             /* Set usage pages. */
   190             hidProperties = 0;
   191             refCF = 0;
   192             result = IORegistryEntryCreateCFProperties(device,
   193                                                        &hidProperties,
   194                                                        kCFAllocatorDefault,
   195                                                        kNilOptions);
   196             if ((result == KERN_SUCCESS) && hidProperties) {
   197                 refCF =
   198                     CFDictionaryGetValue(hidProperties,
   199                                          CFSTR(kIOHIDPrimaryUsagePageKey));
   200                 if (refCF) {
   201                     if (!CFNumberGetValue(refCF, kCFNumberLongType,
   202                                           &SDL_hapticlist[numhaptics].
   203                                           usagePage))
   204                         SDL_SetError
   205                             ("Haptic: Recieving device's usage page.");
   206                     refCF =
   207                         CFDictionaryGetValue(hidProperties,
   208                                              CFSTR(kIOHIDPrimaryUsageKey));
   209                     if (refCF) {
   210                         if (!CFNumberGetValue(refCF, kCFNumberLongType,
   211                                               &SDL_hapticlist[numhaptics].
   212                                               usage))
   213                             SDL_SetError("Haptic: Recieving device's usage.");
   214                     }
   215                 }
   216                 CFRelease(hidProperties);
   217             }
   218 
   219             /* Device has been added. */
   220             numhaptics++;
   221         } else {                /* Free the unused device. */
   222             IOObjectRelease(device);
   223         }
   224 
   225         /* Reached haptic limit. */
   226         if (numhaptics >= MAX_HAPTICS)
   227             break;
   228     }
   229     IOObjectRelease(iter);
   230 
   231     return numhaptics;
   232 }
   233 
   234 
   235 /*
   236  * Return the name of a haptic device, does not need to be opened.
   237  */
   238 const char *
   239 SDL_SYS_HapticName(int index)
   240 {
   241     return SDL_hapticlist[index].name;
   242 }
   243 
   244 /*
   245  * Gets the device's product name.
   246  */
   247 static int
   248 HIDGetDeviceProduct(io_service_t dev, char *name)
   249 {
   250     CFMutableDictionaryRef hidProperties, usbProperties;
   251     io_registry_entry_t parent1, parent2;
   252     kern_return_t ret;
   253 
   254     hidProperties = usbProperties = 0;
   255 
   256     ret = IORegistryEntryCreateCFProperties(dev, &hidProperties,
   257                                             kCFAllocatorDefault, kNilOptions);
   258     if ((ret != KERN_SUCCESS) || !hidProperties) {
   259         SDL_SetError("Haptic: Unable to create CFProperties.");
   260         return -1;
   261     }
   262 
   263     /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
   264      * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
   265      */
   266     if ((KERN_SUCCESS ==
   267          IORegistryEntryGetParentEntry(dev, kIOServicePlane, &parent1))
   268         && (KERN_SUCCESS ==
   269             IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2))
   270         && (KERN_SUCCESS ==
   271             IORegistryEntryCreateCFProperties(parent2, &usbProperties,
   272                                               kCFAllocatorDefault,
   273                                               kNilOptions))) {
   274         if (usbProperties) {
   275             CFTypeRef refCF = 0;
   276             /* get device info
   277              * try hid dictionary first, if fail then go to usb dictionary
   278              */
   279 
   280 
   281             /* Get product name */
   282             refCF =
   283                 CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductKey));
   284             if (!refCF)
   285                 refCF =
   286                     CFDictionaryGetValue(usbProperties,
   287                                          CFSTR("USB Product Name"));
   288             if (refCF) {
   289                 if (!CFStringGetCString(refCF, name, 256,
   290                                         CFStringGetSystemEncoding())) {
   291                     SDL_SetError
   292                         ("Haptic: CFStringGetCString error retrieving pDevice->product.");
   293                     return -1;
   294                 }
   295             }
   296 
   297             CFRelease(usbProperties);
   298         } else {
   299             SDL_SetError
   300                 ("Haptic: IORegistryEntryCreateCFProperties failed to create usbProperties.");
   301             return -1;
   302         }
   303 
   304         /* Release stuff. */
   305         if (kIOReturnSuccess != IOObjectRelease(parent2)) {
   306             SDL_SetError("Haptic: IOObjectRelease error with parent2.");
   307         }
   308         if (kIOReturnSuccess != IOObjectRelease(parent1)) {
   309             SDL_SetError("Haptic: IOObjectRelease error with parent1.");
   310         }
   311     } else {
   312         SDL_SetError("Haptic: Error getting registry entries.");
   313         return -1;
   314     }
   315 
   316     return 0;
   317 }
   318 
   319 
   320 #define FF_TEST(ff, s) \
   321 if (features.supportedEffects & (ff)) supported |= (s)
   322 /*
   323  * Gets supported features.
   324  */
   325 static unsigned int
   326 GetSupportedFeatures(SDL_Haptic * haptic)
   327 {
   328     HRESULT ret;
   329     FFDeviceObjectReference device;
   330     FFCAPABILITIES features;
   331     unsigned int supported;
   332     Uint32 val;
   333 
   334     device = haptic->hwdata->device;
   335 
   336     ret = FFDeviceGetForceFeedbackCapabilities(device, &features);
   337     if (ret != FF_OK) {
   338         SDL_SetError("Haptic: Unable to get device's supported features.");
   339         return -1;
   340     }
   341 
   342     supported = 0;
   343 
   344     /* Get maximum effects. */
   345     haptic->neffects = features.storageCapacity;
   346     haptic->nplaying = features.playbackCapacity;
   347 
   348     /* Test for effects. */
   349     FF_TEST(FFCAP_ET_CONSTANTFORCE, SDL_HAPTIC_CONSTANT);
   350     FF_TEST(FFCAP_ET_RAMPFORCE, SDL_HAPTIC_RAMP);
   351     FF_TEST(FFCAP_ET_SQUARE, SDL_HAPTIC_SQUARE);
   352     FF_TEST(FFCAP_ET_SINE, SDL_HAPTIC_SINE);
   353     FF_TEST(FFCAP_ET_TRIANGLE, SDL_HAPTIC_TRIANGLE);
   354     FF_TEST(FFCAP_ET_SAWTOOTHUP, SDL_HAPTIC_SAWTOOTHUP);
   355     FF_TEST(FFCAP_ET_SAWTOOTHDOWN, SDL_HAPTIC_SAWTOOTHDOWN);
   356     FF_TEST(FFCAP_ET_SPRING, SDL_HAPTIC_SPRING);
   357     FF_TEST(FFCAP_ET_DAMPER, SDL_HAPTIC_DAMPER);
   358     FF_TEST(FFCAP_ET_INERTIA, SDL_HAPTIC_INERTIA);
   359     FF_TEST(FFCAP_ET_FRICTION, SDL_HAPTIC_FRICTION);
   360     FF_TEST(FFCAP_ET_CUSTOMFORCE, SDL_HAPTIC_CUSTOM);
   361 
   362     /* Check if supports gain. */
   363     ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_FFGAIN,
   364                                            &val, sizeof(val));
   365     if (ret == FF_OK)
   366         supported |= SDL_HAPTIC_GAIN;
   367     else if (ret != FFERR_UNSUPPORTED) {
   368         SDL_SetError("Haptic: Unable to get if device supports gain: %s.",
   369                      FFStrError(ret));
   370         return -1;
   371     }
   372 
   373     /* Checks if supports autocenter. */
   374     ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_AUTOCENTER,
   375                                            &val, sizeof(val));
   376     if (ret == FF_OK)
   377         supported |= SDL_HAPTIC_AUTOCENTER;
   378     else if (ret != FFERR_UNSUPPORTED) {
   379         SDL_SetError
   380             ("Haptic: Unable to get if device supports autocenter: %s.",
   381              FFStrError(ret));
   382         return -1;
   383     }
   384 
   385     /* Check for axes, we have an artificial limit on axes */
   386     haptic->naxes = ((features.numFfAxes) > 3) ? 3 : features.numFfAxes;
   387     /* Actually store the axes we want to use */
   388     SDL_memcpy(haptic->hwdata->axes, features.ffAxes,
   389                haptic->naxes * sizeof(Uint8));
   390 
   391     /* Always supported features. */
   392     supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE;
   393 
   394     haptic->supported = supported;
   395     return 0;;
   396 }
   397 
   398 
   399 /*
   400  * Opens the haptic device from the file descriptor.
   401  */
   402 static int
   403 SDL_SYS_HapticOpenFromService(SDL_Haptic * haptic, io_service_t service)
   404 {
   405     HRESULT ret;
   406     int ret2;
   407 
   408     /* Allocate the hwdata */
   409     haptic->hwdata = (struct haptic_hwdata *)
   410         SDL_malloc(sizeof(*haptic->hwdata));
   411     if (haptic->hwdata == NULL) {
   412         SDL_OutOfMemory();
   413         goto creat_err;
   414     }
   415     SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
   416 
   417     /* Open the device */
   418     ret = FFCreateDevice(service, &haptic->hwdata->device);
   419     if (ret != FF_OK) {
   420         SDL_SetError("Haptic: Unable to create device from service: %s.",
   421                      FFStrError(ret));
   422         goto creat_err;
   423     }
   424 
   425     /* Get supported features. */
   426     ret2 = GetSupportedFeatures(haptic);
   427     if (haptic->supported < 0) {
   428         goto open_err;
   429     }
   430 
   431 
   432     /* Reset and then enable actuators. */
   433     ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
   434                                            FFSFFC_RESET);
   435     if (ret != FF_OK) {
   436         SDL_SetError("Haptic: Unable to reset device: %s.", FFStrError(ret));
   437         goto open_err;
   438     }
   439     ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
   440                                            FFSFFC_SETACTUATORSON);
   441     if (ret != FF_OK) {
   442         SDL_SetError("Haptic: Unable to enable actuators: %s.",
   443                      FFStrError(ret));
   444         goto open_err;
   445     }
   446 
   447 
   448     /* Allocate effects memory. */
   449     haptic->effects = (struct haptic_effect *)
   450         SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
   451     if (haptic->effects == NULL) {
   452         SDL_OutOfMemory();
   453         goto open_err;
   454     }
   455     /* Clear the memory */
   456     SDL_memset(haptic->effects, 0,
   457                sizeof(struct haptic_effect) * haptic->neffects);
   458 
   459     return 0;
   460 
   461     /* Error handling */
   462   open_err:
   463     FFReleaseDevice(haptic->hwdata->device);
   464   creat_err:
   465     if (haptic->hwdata != NULL) {
   466         free(haptic->hwdata);
   467         haptic->hwdata = NULL;
   468     }
   469     return -1;
   470 
   471 }
   472 
   473 
   474 /*
   475  * Opens a haptic device for usage.
   476  */
   477 int
   478 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
   479 {
   480     return SDL_SYS_HapticOpenFromService(haptic,
   481                                          SDL_hapticlist[haptic->index].dev);
   482 }
   483 
   484 
   485 /*
   486  * Opens a haptic device from first mouse it finds for usage.
   487  */
   488 int
   489 SDL_SYS_HapticMouse(void)
   490 {
   491     int i;
   492 
   493     for (i = 0; i < SDL_numhaptics; i++) {
   494         if ((SDL_hapticlist[i].usagePage == kHIDPage_GenericDesktop) &&
   495             (SDL_hapticlist[i].usage == kHIDUsage_GD_Mouse))
   496             return i;
   497     }
   498 
   499     return -1;
   500 }
   501 
   502 
   503 /*
   504  * Checks to see if a joystick has haptic features.
   505  */
   506 int
   507 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
   508 {
   509     if (joystick->hwdata->ffservice != 0)
   510         return SDL_TRUE;
   511     return SDL_FALSE;
   512 }
   513 
   514 
   515 /*
   516  * Checks to see if the haptic device and joystick and in reality the same.
   517  */
   518 int
   519 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
   520 {
   521     if (IOObjectIsEqualTo((io_object_t) haptic->hwdata->device,
   522                           joystick->hwdata->ffservice))
   523         return 1;
   524     return 0;
   525 }
   526 
   527 
   528 /*
   529  * Opens a SDL_Haptic from a SDL_Joystick.
   530  */
   531 int
   532 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
   533 {
   534     return SDL_SYS_HapticOpenFromService(haptic, joystick->hwdata->ffservice);
   535 }
   536 
   537 
   538 /*
   539  * Closes the haptic device.
   540  */
   541 void
   542 SDL_SYS_HapticClose(SDL_Haptic * haptic)
   543 {
   544     if (haptic->hwdata) {
   545 
   546         /* Free Effects. */
   547         SDL_free(haptic->effects);
   548         haptic->effects = NULL;
   549         haptic->neffects = 0;
   550 
   551         /* Clean up */
   552         FFReleaseDevice(haptic->hwdata->device);
   553 
   554         /* Free */
   555         SDL_free(haptic->hwdata);
   556         haptic->hwdata = NULL;
   557     }
   558 }
   559 
   560 
   561 /* 
   562  * Clean up after system specific haptic stuff
   563  */
   564 void
   565 SDL_SYS_HapticQuit(void)
   566 {
   567     int i;
   568 
   569     for (i = 0; i < SDL_numhaptics; i++) {
   570         /* Opened and not closed haptics are leaked, this is on purpose.
   571          * Close your haptic devices after usage. */
   572 
   573         /* Free the io_service_t */
   574         IOObjectRelease(SDL_hapticlist[i].dev);
   575     }
   576 }
   577 
   578 
   579 /*
   580  * Converts an SDL trigger button to an FFEFFECT trigger button.
   581  */
   582 static DWORD
   583 FFGetTriggerButton(Uint16 button)
   584 {
   585     DWORD dwTriggerButton;
   586 
   587     dwTriggerButton = FFEB_NOTRIGGER;
   588 
   589     if (button != 0) {
   590         dwTriggerButton = FFJOFS_BUTTON(button - 1);
   591     }
   592 
   593     return dwTriggerButton;
   594 }
   595 
   596 
   597 /*
   598  * Sets the direction.
   599  */
   600 static int
   601 SDL_SYS_SetDirection(FFEFFECT * effect, SDL_HapticDirection * dir, int naxes)
   602 {
   603     LONG *rglDir;
   604 
   605     /* Handle no axes a part. */
   606     if (naxes == 0) {
   607         effect->dwFlags |= FFEFF_SPHERICAL;     /* Set as default. */
   608         effect->rglDirection = NULL;
   609         return 0;
   610     }
   611 
   612     /* Has axes. */
   613     rglDir = SDL_malloc(sizeof(LONG) * naxes);
   614     if (rglDir == NULL) {
   615         SDL_OutOfMemory();
   616         return -1;
   617     }
   618     SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
   619     effect->rglDirection = rglDir;
   620 
   621     switch (dir->type) {
   622     case SDL_HAPTIC_POLAR:
   623         effect->dwFlags |= FFEFF_POLAR;
   624         rglDir[0] = dir->dir[0];
   625         return 0;
   626     case SDL_HAPTIC_CARTESIAN:
   627         effect->dwFlags |= FFEFF_CARTESIAN;
   628         rglDir[0] = dir->dir[0];
   629         if (naxes > 1)
   630             rglDir[1] = dir->dir[1];
   631         if (naxes > 2)
   632             rglDir[2] = dir->dir[2];
   633         return 0;
   634     case SDL_HAPTIC_SPHERICAL:
   635         effect->dwFlags |= FFEFF_SPHERICAL;
   636         rglDir[0] = dir->dir[0];
   637         if (naxes > 1)
   638             rglDir[1] = dir->dir[1];
   639         if (naxes > 2)
   640             rglDir[2] = dir->dir[2];
   641         return 0;
   642 
   643     default:
   644         SDL_SetError("Haptic: Unknown direction type.");
   645         return -1;
   646     }
   647 }
   648 
   649 
   650 /* Clamps and converts. */
   651 #define CCONVERT(x)   (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
   652 /* Just converts. */
   653 #define CONVERT(x)    (((x)*10000) / 0x7FFF)
   654 /*
   655  * Creates the FFEFFECT from a SDL_HapticEffect.
   656  */
   657 static int
   658 SDL_SYS_ToFFEFFECT(SDL_Haptic * haptic, FFEFFECT * dest,
   659                    SDL_HapticEffect * src)
   660 {
   661     int i;
   662     FFCONSTANTFORCE *constant;
   663     FFPERIODIC *periodic;
   664     FFCONDITION *condition;     /* Actually an array of conditions - one per axis. */
   665     FFRAMPFORCE *ramp;
   666     FFCUSTOMFORCE *custom;
   667     FFENVELOPE *envelope;
   668     SDL_HapticConstant *hap_constant;
   669     SDL_HapticPeriodic *hap_periodic;
   670     SDL_HapticCondition *hap_condition;
   671     SDL_HapticRamp *hap_ramp;
   672     SDL_HapticCustom *hap_custom;
   673     DWORD *axes;
   674 
   675     /* Set global stuff. */
   676     SDL_memset(dest, 0, sizeof(FFEFFECT));
   677     dest->dwSize = sizeof(FFEFFECT);    /* Set the structure size. */
   678     dest->dwSamplePeriod = 0;   /* Not used by us. */
   679     dest->dwGain = 10000;       /* Gain is set globally, not locally. */
   680     dest->dwFlags = FFEFF_OBJECTOFFSETS;        /* Seems obligatory. */
   681 
   682     /* Envelope. */
   683     envelope = SDL_malloc(sizeof(FFENVELOPE));
   684     if (envelope == NULL) {
   685         SDL_OutOfMemory();
   686         return -1;
   687     }
   688     SDL_memset(envelope, 0, sizeof(FFENVELOPE));
   689     dest->lpEnvelope = envelope;
   690     envelope->dwSize = sizeof(FFENVELOPE);      /* Always should be this. */
   691 
   692     /* Axes. */
   693     dest->cAxes = haptic->naxes;
   694     if (dest->cAxes > 0) {
   695         axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
   696         if (axes == NULL) {
   697             SDL_OutOfMemory();
   698             return -1;
   699         }
   700         axes[0] = haptic->hwdata->axes[0];      /* Always at least one axis. */
   701         if (dest->cAxes > 1) {
   702             axes[1] = haptic->hwdata->axes[1];
   703         }
   704         if (dest->cAxes > 2) {
   705             axes[2] = haptic->hwdata->axes[2];
   706         }
   707         dest->rgdwAxes = axes;
   708     }
   709 
   710 
   711     /* The big type handling switch, even bigger then linux's version. */
   712     switch (src->type) {
   713     case SDL_HAPTIC_CONSTANT:
   714         hap_constant = &src->constant;
   715         constant = SDL_malloc(sizeof(FFCONSTANTFORCE));
   716         if (constant == NULL) {
   717             SDL_OutOfMemory();
   718             return -1;
   719         }
   720         SDL_memset(constant, 0, sizeof(FFCONSTANTFORCE));
   721 
   722         /* Specifics */
   723         constant->lMagnitude = CONVERT(hap_constant->level);
   724         dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE);
   725         dest->lpvTypeSpecificParams = constant;
   726 
   727         /* Generics */
   728         dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
   729         dest->dwTriggerButton = FFGetTriggerButton(hap_constant->button);
   730         dest->dwTriggerRepeatInterval = hap_constant->interval;
   731         dest->dwStartDelay = hap_constant->delay * 1000;        /* In microseconds. */
   732 
   733         /* Direction. */
   734         if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
   735             < 0) {
   736             return -1;
   737         }
   738 
   739         /* Envelope */
   740         if ((hap_constant->attack_length == 0)
   741             && (hap_constant->fade_length == 0)) {
   742             SDL_free(envelope);
   743             dest->lpEnvelope = NULL;
   744         } else {
   745             envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level);
   746             envelope->dwAttackTime = hap_constant->attack_length * 1000;
   747             envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level);
   748             envelope->dwFadeTime = hap_constant->fade_length * 1000;
   749         }
   750 
   751         break;
   752 
   753     case SDL_HAPTIC_SINE:
   754     case SDL_HAPTIC_SQUARE:
   755     case SDL_HAPTIC_TRIANGLE:
   756     case SDL_HAPTIC_SAWTOOTHUP:
   757     case SDL_HAPTIC_SAWTOOTHDOWN:
   758         hap_periodic = &src->periodic;
   759         periodic = SDL_malloc(sizeof(FFPERIODIC));
   760         if (periodic == NULL) {
   761             SDL_OutOfMemory();
   762             return -1;
   763         }
   764         SDL_memset(periodic, 0, sizeof(FFPERIODIC));
   765 
   766         /* Specifics */
   767         periodic->dwMagnitude = CONVERT(hap_periodic->magnitude);
   768         periodic->lOffset = CONVERT(hap_periodic->offset);
   769         periodic->dwPhase = hap_periodic->phase;
   770         periodic->dwPeriod = hap_periodic->period * 1000;
   771         dest->cbTypeSpecificParams = sizeof(FFPERIODIC);
   772         dest->lpvTypeSpecificParams = periodic;
   773 
   774         /* Generics */
   775         dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
   776         dest->dwTriggerButton = FFGetTriggerButton(hap_periodic->button);
   777         dest->dwTriggerRepeatInterval = hap_periodic->interval;
   778         dest->dwStartDelay = hap_periodic->delay * 1000;        /* In microseconds. */
   779 
   780         /* Direction. */
   781         if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
   782             < 0) {
   783             return -1;
   784         }
   785 
   786         /* Envelope */
   787         if ((hap_periodic->attack_length == 0)
   788             && (hap_periodic->fade_length == 0)) {
   789             SDL_free(envelope);
   790             dest->lpEnvelope = NULL;
   791         } else {
   792             envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level);
   793             envelope->dwAttackTime = hap_periodic->attack_length * 1000;
   794             envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level);
   795             envelope->dwFadeTime = hap_periodic->fade_length * 1000;
   796         }
   797 
   798         break;
   799 
   800     case SDL_HAPTIC_SPRING:
   801     case SDL_HAPTIC_DAMPER:
   802     case SDL_HAPTIC_INERTIA:
   803     case SDL_HAPTIC_FRICTION:
   804         hap_condition = &src->condition;
   805         condition = SDL_malloc(sizeof(FFCONDITION) * dest->cAxes);
   806         if (condition == NULL) {
   807             SDL_OutOfMemory();
   808             return -1;
   809         }
   810         SDL_memset(condition, 0, sizeof(FFCONDITION));
   811 
   812         /* Specifics */
   813         for (i = 0; i < dest->cAxes; i++) {
   814             condition[i].lOffset = CONVERT(hap_condition->center[i]);
   815             condition[i].lPositiveCoefficient =
   816                 CONVERT(hap_condition->right_coeff[i]);
   817             condition[i].lNegativeCoefficient =
   818                 CONVERT(hap_condition->left_coeff[i]);
   819             condition[i].dwPositiveSaturation =
   820                 CCONVERT(hap_condition->right_sat[i]);
   821             condition[i].dwNegativeSaturation =
   822                 CCONVERT(hap_condition->left_sat[i]);
   823             condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i]);
   824         }
   825         dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes;
   826         dest->lpvTypeSpecificParams = condition;
   827 
   828         /* Generics */
   829         dest->dwDuration = hap_condition->length * 1000;        /* In microseconds. */
   830         dest->dwTriggerButton = FFGetTriggerButton(hap_condition->button);
   831         dest->dwTriggerRepeatInterval = hap_condition->interval;
   832         dest->dwStartDelay = hap_condition->delay * 1000;       /* In microseconds. */
   833 
   834         /* Direction. */
   835         if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
   836             < 0) {
   837             return -1;
   838         }
   839 
   840         /* Envelope - Not actually supported by most CONDITION implementations. */
   841         SDL_free(dest->lpEnvelope);
   842         dest->lpEnvelope = NULL;
   843 
   844         break;
   845 
   846     case SDL_HAPTIC_RAMP:
   847         hap_ramp = &src->ramp;
   848         ramp = SDL_malloc(sizeof(FFRAMPFORCE));
   849         if (ramp == NULL) {
   850             SDL_OutOfMemory();
   851             return -1;
   852         }
   853         SDL_memset(ramp, 0, sizeof(FFRAMPFORCE));
   854 
   855         /* Specifics */
   856         ramp->lStart = CONVERT(hap_ramp->start);
   857         ramp->lEnd = CONVERT(hap_ramp->end);
   858         dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE);
   859         dest->lpvTypeSpecificParams = ramp;
   860 
   861         /* Generics */
   862         dest->dwDuration = hap_ramp->length * 1000;     /* In microseconds. */
   863         dest->dwTriggerButton = FFGetTriggerButton(hap_ramp->button);
   864         dest->dwTriggerRepeatInterval = hap_ramp->interval;
   865         dest->dwStartDelay = hap_ramp->delay * 1000;    /* In microseconds. */
   866 
   867         /* Direction. */
   868         if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
   869             return -1;
   870         }
   871 
   872         /* Envelope */
   873         if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
   874             SDL_free(envelope);
   875             dest->lpEnvelope = NULL;
   876         } else {
   877             envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level);
   878             envelope->dwAttackTime = hap_ramp->attack_length * 1000;
   879             envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level);
   880             envelope->dwFadeTime = hap_ramp->fade_length * 1000;
   881         }
   882 
   883         break;
   884 
   885     case SDL_HAPTIC_CUSTOM:
   886         hap_custom = &src->custom;
   887         custom = SDL_malloc(sizeof(FFCUSTOMFORCE));
   888         if (custom == NULL) {
   889             SDL_OutOfMemory();
   890             return -1;
   891         }
   892         SDL_memset(custom, 0, sizeof(FFCUSTOMFORCE));
   893 
   894         /* Specifics */
   895         custom->cChannels = hap_custom->channels;
   896         custom->dwSamplePeriod = hap_custom->period * 1000;
   897         custom->cSamples = hap_custom->samples;
   898         custom->rglForceData =
   899             SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
   900         for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) {      /* Copy data. */
   901             custom->rglForceData[i] = CCONVERT(hap_custom->data[i]);
   902         }
   903         dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE);
   904         dest->lpvTypeSpecificParams = custom;
   905 
   906         /* Generics */
   907         dest->dwDuration = hap_custom->length * 1000;   /* In microseconds. */
   908         dest->dwTriggerButton = FFGetTriggerButton(hap_custom->button);
   909         dest->dwTriggerRepeatInterval = hap_custom->interval;
   910         dest->dwStartDelay = hap_custom->delay * 1000;  /* In microseconds. */
   911 
   912         /* Direction. */
   913         if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
   914             0) {
   915             return -1;
   916         }
   917 
   918         /* Envelope */
   919         if ((hap_custom->attack_length == 0)
   920             && (hap_custom->fade_length == 0)) {
   921             SDL_free(envelope);
   922             dest->lpEnvelope = NULL;
   923         } else {
   924             envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level);
   925             envelope->dwAttackTime = hap_custom->attack_length * 1000;
   926             envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level);
   927             envelope->dwFadeTime = hap_custom->fade_length * 1000;
   928         }
   929 
   930         break;
   931 
   932 
   933     default:
   934         SDL_SetError("Haptic: Unknown effect type.");
   935         return -1;
   936     }
   937 
   938     return 0;
   939 }
   940 
   941 
   942 /*
   943  * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT.
   944  */
   945 static void
   946 SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type)
   947 {
   948     FFCUSTOMFORCE *custom;
   949 
   950     if (effect->lpEnvelope != NULL) {
   951         SDL_free(effect->lpEnvelope);
   952         effect->lpEnvelope = NULL;
   953     }
   954     if (effect->rgdwAxes != NULL) {
   955         SDL_free(effect->rgdwAxes);
   956         effect->rgdwAxes = NULL;
   957     }
   958     if (effect->lpvTypeSpecificParams != NULL) {
   959         if (type == SDL_HAPTIC_CUSTOM) {        /* Must free the custom data. */
   960             custom = (FFCUSTOMFORCE *) effect->lpvTypeSpecificParams;
   961             SDL_free(custom->rglForceData);
   962             custom->rglForceData = NULL;
   963         }
   964         SDL_free(effect->lpvTypeSpecificParams);
   965         effect->lpvTypeSpecificParams = NULL;
   966     }
   967     if (effect->rglDirection != NULL) {
   968         SDL_free(effect->rglDirection);
   969         effect->rglDirection = NULL;
   970     }
   971 }
   972 
   973 
   974 /*
   975  * Gets the effect type from the generic SDL haptic effect wrapper.
   976  */
   977 CFUUIDRef
   978 SDL_SYS_HapticEffectType(Uint16 type)
   979 {
   980     switch (type) {
   981     case SDL_HAPTIC_CONSTANT:
   982         return kFFEffectType_ConstantForce_ID;
   983 
   984     case SDL_HAPTIC_RAMP:
   985         return kFFEffectType_RampForce_ID;
   986 
   987     case SDL_HAPTIC_SQUARE:
   988         return kFFEffectType_Square_ID;
   989 
   990     case SDL_HAPTIC_SINE:
   991         return kFFEffectType_Sine_ID;
   992 
   993     case SDL_HAPTIC_TRIANGLE:
   994         return kFFEffectType_Triangle_ID;
   995 
   996     case SDL_HAPTIC_SAWTOOTHUP:
   997         return kFFEffectType_SawtoothUp_ID;
   998 
   999     case SDL_HAPTIC_SAWTOOTHDOWN:
  1000         return kFFEffectType_SawtoothDown_ID;
  1001 
  1002     case SDL_HAPTIC_SPRING:
  1003         return kFFEffectType_Spring_ID;
  1004 
  1005     case SDL_HAPTIC_DAMPER:
  1006         return kFFEffectType_Damper_ID;
  1007 
  1008     case SDL_HAPTIC_INERTIA:
  1009         return kFFEffectType_Inertia_ID;
  1010 
  1011     case SDL_HAPTIC_FRICTION:
  1012         return kFFEffectType_Friction_ID;
  1013 
  1014     case SDL_HAPTIC_CUSTOM:
  1015         return kFFEffectType_CustomForce_ID;
  1016 
  1017     default:
  1018         SDL_SetError("Haptic: Unknown effect type.");
  1019         return NULL;
  1020     }
  1021 }
  1022 
  1023 
  1024 /*
  1025  * Creates a new haptic effect.
  1026  */
  1027 int
  1028 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
  1029                         SDL_HapticEffect * base)
  1030 {
  1031     HRESULT ret;
  1032     CFUUIDRef type;
  1033 
  1034     /* Alloc the effect. */
  1035     effect->hweffect = (struct haptic_hweffect *)
  1036         SDL_malloc(sizeof(struct haptic_hweffect));
  1037     if (effect->hweffect == NULL) {
  1038         SDL_OutOfMemory();
  1039         goto err_hweffect;
  1040     }
  1041 
  1042     /* Get the type. */
  1043     type = SDL_SYS_HapticEffectType(base->type);
  1044     if (type == NULL) {
  1045         goto err_hweffect;
  1046     }
  1047 
  1048     /* Get the effect. */
  1049     if (SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
  1050         goto err_effectdone;
  1051     }
  1052 
  1053     /* Create the actual effect. */
  1054     ret = FFDeviceCreateEffect(haptic->hwdata->device, type,
  1055                                &effect->hweffect->effect,
  1056                                &effect->hweffect->ref);
  1057     if (ret != FF_OK) {
  1058         SDL_SetError("Haptic: Unable to create effect: %s.", FFStrError(ret));
  1059         goto err_effectdone;
  1060     }
  1061 
  1062     return 0;
  1063 
  1064   err_effectdone:
  1065     SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, base->type);
  1066   err_hweffect:
  1067     if (effect->hweffect != NULL) {
  1068         SDL_free(effect->hweffect);
  1069         effect->hweffect = NULL;
  1070     }
  1071     return -1;
  1072 }
  1073 
  1074 
  1075 /*
  1076  * Updates an effect.
  1077  */
  1078 int
  1079 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
  1080                            struct haptic_effect *effect,
  1081                            SDL_HapticEffect * data)
  1082 {
  1083     HRESULT ret;
  1084     FFEffectParameterFlag flags;
  1085     FFEFFECT temp;
  1086 
  1087     /* Get the effect. */
  1088     SDL_memset(&temp, 0, sizeof(FFEFFECT));
  1089     if (SDL_SYS_ToFFEFFECT(haptic, &temp, data) < 0) {
  1090         goto err_update;
  1091     }
  1092 
  1093     /* Set the flags.  Might be worthwhile to diff temp with loaded effect and
  1094      *  only change those parameters. */
  1095     flags = FFEP_DIRECTION |
  1096         FFEP_DURATION |
  1097         FFEP_ENVELOPE |
  1098         FFEP_STARTDELAY |
  1099         FFEP_TRIGGERBUTTON |
  1100         FFEP_TRIGGERREPEATINTERVAL | FFEP_TYPESPECIFICPARAMS;
  1101 
  1102     /* Create the actual effect. */
  1103     ret = FFEffectSetParameters(effect->hweffect->ref, &temp, flags);
  1104     if (ret != FF_OK) {
  1105         SDL_SetError("Haptic: Unable to update effect: %s.", FFStrError(ret));
  1106         goto err_update;
  1107     }
  1108 
  1109     /* Copy it over. */
  1110     SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, data->type);
  1111     SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(FFEFFECT));
  1112 
  1113     return 0;
  1114 
  1115   err_update:
  1116     SDL_SYS_HapticFreeFFEFFECT(&temp, data->type);
  1117     return -1;
  1118 }
  1119 
  1120 
  1121 /*
  1122  * Runs an effect.
  1123  */
  1124 int
  1125 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
  1126                         Uint32 iterations)
  1127 {
  1128     HRESULT ret;
  1129     Uint32 iter;
  1130 
  1131     /* Check if it's infinite. */
  1132     if (iterations == SDL_HAPTIC_INFINITY) {
  1133         iter = FF_INFINITE;
  1134     } else
  1135         iter = iterations;
  1136 
  1137     /* Run the effect. */
  1138     ret = FFEffectStart(effect->hweffect->ref, iter, 0);
  1139     if (ret != FF_OK) {
  1140         SDL_SetError("Haptic: Unable to run the effect: %s.",
  1141                      FFStrError(ret));
  1142         return -1;
  1143     }
  1144 
  1145     return 0;
  1146 }
  1147 
  1148 
  1149 /*
  1150  * Stops an effect.
  1151  */
  1152 int
  1153 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1154 {
  1155     HRESULT ret;
  1156 
  1157     ret = FFEffectStop(effect->hweffect->ref);
  1158     if (ret != FF_OK) {
  1159         SDL_SetError("Haptic: Unable to stop the effect: %s.",
  1160                      FFStrError(ret));
  1161         return -1;
  1162     }
  1163 
  1164     return 0;
  1165 }
  1166 
  1167 
  1168 /*
  1169  * Frees the effect.
  1170  */
  1171 void
  1172 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1173 {
  1174     HRESULT ret;
  1175 
  1176     ret =
  1177         FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref);
  1178     if (ret != FF_OK) {
  1179         SDL_SetError("Haptic: Error removing the effect from the device: %s.",
  1180                      FFStrError(ret));
  1181     }
  1182     SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect,
  1183                                effect->effect.type);
  1184     SDL_free(effect->hweffect);
  1185     effect->hweffect = NULL;
  1186 }
  1187 
  1188 
  1189 /*
  1190  * Gets the status of a haptic effect.
  1191  */
  1192 int
  1193 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
  1194                               struct haptic_effect *effect)
  1195 {
  1196     HRESULT ret;
  1197     FFEffectStatusFlag status;
  1198 
  1199     ret = FFEffectGetEffectStatus(effect->hweffect->ref, &status);
  1200     if (ret != FF_OK) {
  1201         SDL_SetError("Haptic: Unable to get effect status: %s.",
  1202                      FFStrError(ret));
  1203         return -1;
  1204     }
  1205 
  1206     if (status == 0)
  1207         return SDL_FALSE;
  1208     return SDL_TRUE;            /* Assume it's playing or emulated. */
  1209 }
  1210 
  1211 
  1212 /*
  1213  * Sets the gain.
  1214  */
  1215 int
  1216 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
  1217 {
  1218     HRESULT ret;
  1219     Uint32 val;
  1220 
  1221     val = gain * 100;           /* Mac OS X uses 0 to 10,000 */
  1222     ret =
  1223         FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
  1224                                          FFPROP_FFGAIN, &val);
  1225     if (ret != FF_OK) {
  1226         SDL_SetError("Haptic: Error setting gain: %s.", FFStrError(ret));
  1227         return -1;
  1228     }
  1229 
  1230     return 0;
  1231 }
  1232 
  1233 
  1234 /*
  1235  * Sets the autocentering.
  1236  */
  1237 int
  1238 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
  1239 {
  1240     HRESULT ret;
  1241     Uint32 val;
  1242 
  1243     /* Mac OS X only has 0 (off) and 1 (on) */
  1244     if (autocenter == 0)
  1245         val = 0;
  1246     else
  1247         val = 1;
  1248 
  1249     ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
  1250                                            FFPROP_AUTOCENTER, &val);
  1251     if (ret != FF_OK) {
  1252         SDL_SetError("Haptic: Error setting autocenter: %s.",
  1253                      FFStrError(ret));
  1254         return -1;
  1255     }
  1256 
  1257     return 0;
  1258 }
  1259 
  1260 
  1261 /*
  1262  * Pauses the device.
  1263  */
  1264 int
  1265 SDL_SYS_HapticPause(SDL_Haptic * haptic)
  1266 {
  1267     HRESULT ret;
  1268 
  1269     ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
  1270                                            FFSFFC_PAUSE);
  1271     if (ret != FF_OK) {
  1272         SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
  1273         return -1;
  1274     }
  1275 
  1276     return 0;
  1277 }
  1278 
  1279 
  1280 /*
  1281  * Unpauses the device.
  1282  */
  1283 int
  1284 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
  1285 {
  1286     HRESULT ret;
  1287 
  1288     ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
  1289                                            FFSFFC_CONTINUE);
  1290     if (ret != FF_OK) {
  1291         SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
  1292         return -1;
  1293     }
  1294 
  1295     return 0;
  1296 }
  1297 
  1298 
  1299 /*
  1300  * Stops all currently playing effects.
  1301  */
  1302 int
  1303 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
  1304 {
  1305     HRESULT ret;
  1306 
  1307     ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
  1308                                            FFSFFC_STOPALL);
  1309     if (ret != FF_OK) {
  1310         SDL_SetError("Haptic: Error stopping device: %s.", FFStrError(ret));
  1311         return -1;
  1312     }
  1313 
  1314     return 0;
  1315 }
  1316 
  1317 
  1318 #endif /* SDL_HAPTIC_IOKIT */