src/haptic/darwin/SDL_syshaptic.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 02 Feb 2014 00:53:27 -0800
changeset 8149 681eb46b8ac4
parent 8093 b43765095a6f
child 8173 23919d993046
permissions -rw-r--r--
Fixed bug 2374 - Update copyright for 2014...

Is it that time already??
slouken@2713
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@8149
     3
  Copyright (C) 1997-2014 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
*/
icculus@8093
    21
#include "../../SDL_internal.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 */