src/haptic/darwin/SDL_syshaptic.c
author David Ludwig <dludwig@pobox.com>
Wed, 25 Dec 2013 21:39:48 -0500
changeset 8563 c0e68f3b6bbb
parent 7719 31b5f9ff36ca
child 8093 b43765095a6f
permissions -rw-r--r--
WinRT: compiled the d3d11 renderer's shaders into SDL itself

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