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

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