sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_syshaptic.c (40911B)


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