src/haptic/darwin/SDL_syshaptic.c
author Ryan C. Gordon <icculus@icculus.org>
Sat, 16 Aug 2014 16:47:42 -0400
changeset 9070 8973a237f360
parent 8986 1c81316ac642
child 9147 6bf589c8d549
permissions -rw-r--r--
Haptic: Fix the saturation and deadband parameters' available range.

There was a misconception that Linux's saturation and deadband parameters -
on which the corresponding SDL parameters were based - use only half of the
possible range.

Thanks, Elias!

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