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