cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_syshaptic.c (40020B)


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