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