src/haptic/darwin/SDL_syshaptic.c
author Sam Lantinga
Mon, 16 Mar 2020 13:28:38 -0700
changeset 13635 701fe6486077
parent 13422 fd6a12de91c7
child 13789 a359f4f93439
permissions -rw-r--r--
Fixed bug 3446 - The haptic API does not allow to select the direction axes

Mathieu Laurendeau

Consider a device supporting effects on multiple axes.
There's currently no way to play effects against a single-axis direction.


A device supporting effects against X and Y may not allow to play effects with a two-axis direction coordinate, even if one of the coordinates is null.

My current (ugly) work around for this is to add a direction type SDL_HAPTIC_X_FORCE to play effects against a X-axis only direction (patch attached).

This issue impacted two GIMX users using the following wheels:
- Leo Bodnar SimSteering force feedback wheel
- Accuforce direct drive wheel

Playing constant/spring/damper effects against a X-axis direction worked well for the first wheel, but not for the second one.

A better strategy seems to play the effects against the first axis reported by the DirectInput enumeration.

This strategy also works with Logitech wheels (at least the DFGT).

It's been more than a year that I have the latest patch (playing effects against the first axis only) in the GIMX software. It's being used by thousands of people, mostly for adapting their FFB wheel to the PS4. I had no report that proves this strategy to be wrong.
slouken@2713
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@13422
     3
  Copyright (C) 1997-2020 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
philipp@10617
   192
SDL_SYS_NumHaptics(void)
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@13379
   602
    if (joystick->driver != &SDL_DARWIN_JoystickDriver) {
slouken@13379
   603
        return SDL_FALSE;
slouken@13379
   604
    }
slouken@8986
   605
    if (joystick->hwdata->ffservice != 0) {
slouken@2713
   606
        return SDL_TRUE;
slouken@8986
   607
    }
slouken@2713
   608
    return SDL_FALSE;
slouken@2713
   609
}
slouken@2713
   610
slouken@2713
   611
slouken@2713
   612
/*
icculus@7708
   613
 * Checks to see if the haptic device and joystick are in reality the same.
slouken@2713
   614
 */
slouken@2713
   615
int
slouken@2713
   616
SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
slouken@2713
   617
{
slouken@13379
   618
    if (joystick->driver != &SDL_DARWIN_JoystickDriver) {
slouken@13379
   619
        return 0;
slouken@13379
   620
    }
icculus@5632
   621
    if (IOObjectIsEqualTo((io_object_t) ((size_t)haptic->hwdata->device),
slouken@8986
   622
                          joystick->hwdata->ffservice)) {
slouken@2713
   623
        return 1;
slouken@8986
   624
    }
slouken@2713
   625
    return 0;
slouken@2713
   626
}
slouken@2713
   627
slouken@2713
   628
slouken@2713
   629
/*
slouken@2713
   630
 * Opens a SDL_Haptic from a SDL_Joystick.
slouken@2713
   631
 */
slouken@2713
   632
int
slouken@2713
   633
SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
slouken@2713
   634
{
urkle@8173
   635
    int device_index = 0;
urkle@8173
   636
    SDL_hapticlist_item *item;
slouken@13379
   637
    
slouken@13379
   638
    if (joystick->driver != &SDL_DARWIN_JoystickDriver) {
slouken@13379
   639
        return -1;
slouken@13379
   640
    }
urkle@8173
   641
    for (item = SDL_hapticlist; item; item = item->next) {
urkle@8173
   642
        if (IOObjectIsEqualTo((io_object_t) item->dev,
slouken@5358
   643
                             joystick->hwdata->ffservice)) {
urkle@8173
   644
           haptic->index = device_index;
slouken@5358
   645
           break;
urkle@8173
   646
        }
urkle@8173
   647
        ++device_index;
slouken@5358
   648
    }
slouken@5358
   649
slouken@2713
   650
    return SDL_SYS_HapticOpenFromService(haptic, joystick->hwdata->ffservice);
slouken@2713
   651
}
slouken@2713
   652
slouken@2713
   653
slouken@2713
   654
/*
slouken@2713
   655
 * Closes the haptic device.
slouken@2713
   656
 */
slouken@2713
   657
void
slouken@2713
   658
SDL_SYS_HapticClose(SDL_Haptic * haptic)
slouken@2713
   659
{
slouken@2713
   660
    if (haptic->hwdata) {
slouken@2713
   661
slouken@2713
   662
        /* Free Effects. */
slouken@2713
   663
        SDL_free(haptic->effects);
slouken@2713
   664
        haptic->effects = NULL;
slouken@2713
   665
        haptic->neffects = 0;
slouken@2713
   666
slouken@2713
   667
        /* Clean up */
slouken@2713
   668
        FFReleaseDevice(haptic->hwdata->device);
slouken@2713
   669
slouken@2713
   670
        /* Free */
slouken@2713
   671
        SDL_free(haptic->hwdata);
slouken@2713
   672
        haptic->hwdata = NULL;
slouken@2713
   673
    }
slouken@2713
   674
}
slouken@2713
   675
slouken@2713
   676
slouken@7191
   677
/*
slouken@2713
   678
 * Clean up after system specific haptic stuff
slouken@2713
   679
 */
slouken@2713
   680
void
slouken@2713
   681
SDL_SYS_HapticQuit(void)
slouken@2713
   682
{
urkle@8173
   683
    SDL_hapticlist_item *item;
urkle@8173
   684
    SDL_hapticlist_item *next = NULL;
slouken@2713
   685
urkle@8173
   686
    for (item = SDL_hapticlist; item; item = next) {
urkle@8173
   687
        next = item->next;
slouken@2713
   688
        /* Opened and not closed haptics are leaked, this is on purpose.
slouken@2713
   689
         * Close your haptic devices after usage. */
slouken@2713
   690
slouken@2713
   691
        /* Free the io_service_t */
urkle@8173
   692
        IOObjectRelease(item->dev);
icculus@8178
   693
        SDL_free(item);
slouken@2713
   694
    }
slouken@9740
   695
urkle@8187
   696
    numhaptics = -1;
slouken@9740
   697
    SDL_hapticlist = NULL;
slouken@9740
   698
    SDL_hapticlist_tail = NULL;
slouken@2713
   699
}
slouken@2713
   700
slouken@2713
   701
slouken@2713
   702
/*
slouken@2713
   703
 * Converts an SDL trigger button to an FFEFFECT trigger button.
slouken@2713
   704
 */
slouken@2713
   705
static DWORD
slouken@2713
   706
FFGetTriggerButton(Uint16 button)
slouken@2713
   707
{
slouken@2713
   708
    DWORD dwTriggerButton;
slouken@2713
   709
slouken@2713
   710
    dwTriggerButton = FFEB_NOTRIGGER;
slouken@2713
   711
slouken@2713
   712
    if (button != 0) {
slouken@2713
   713
        dwTriggerButton = FFJOFS_BUTTON(button - 1);
slouken@2713
   714
    }
slouken@2713
   715
slouken@2713
   716
    return dwTriggerButton;
slouken@2713
   717
}
slouken@2713
   718
slouken@2713
   719
slouken@2713
   720
/*
slouken@2713
   721
 * Sets the direction.
slouken@2713
   722
 */
slouken@2713
   723
static int
slouken@2713
   724
SDL_SYS_SetDirection(FFEFFECT * effect, SDL_HapticDirection * dir, int naxes)
slouken@2713
   725
{
slouken@2713
   726
    LONG *rglDir;
slouken@2713
   727
slouken@2713
   728
    /* Handle no axes a part. */
slouken@2713
   729
    if (naxes == 0) {
slouken@2713
   730
        effect->dwFlags |= FFEFF_SPHERICAL;     /* Set as default. */
slouken@2713
   731
        effect->rglDirection = NULL;
slouken@2713
   732
        return 0;
slouken@2713
   733
    }
slouken@2713
   734
slouken@2713
   735
    /* Has axes. */
slouken@2713
   736
    rglDir = SDL_malloc(sizeof(LONG) * naxes);
slouken@2713
   737
    if (rglDir == NULL) {
icculus@7037
   738
        return SDL_OutOfMemory();
slouken@2713
   739
    }
slouken@2713
   740
    SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
slouken@2713
   741
    effect->rglDirection = rglDir;
slouken@2713
   742
slouken@2713
   743
    switch (dir->type) {
slouken@2713
   744
    case SDL_HAPTIC_POLAR:
slouken@2713
   745
        effect->dwFlags |= FFEFF_POLAR;
slouken@2713
   746
        rglDir[0] = dir->dir[0];
slouken@2713
   747
        return 0;
slouken@2713
   748
    case SDL_HAPTIC_CARTESIAN:
slouken@2713
   749
        effect->dwFlags |= FFEFF_CARTESIAN;
slouken@2713
   750
        rglDir[0] = dir->dir[0];
slouken@8986
   751
        if (naxes > 1) {
slouken@2713
   752
            rglDir[1] = dir->dir[1];
slouken@8986
   753
        }
slouken@8986
   754
        if (naxes > 2) {
slouken@2713
   755
            rglDir[2] = dir->dir[2];
slouken@8986
   756
        }
slouken@2713
   757
        return 0;
slouken@2713
   758
    case SDL_HAPTIC_SPHERICAL:
slouken@2713
   759
        effect->dwFlags |= FFEFF_SPHERICAL;
slouken@2713
   760
        rglDir[0] = dir->dir[0];
slouken@8986
   761
        if (naxes > 1) {
slouken@2713
   762
            rglDir[1] = dir->dir[1];
slouken@8986
   763
        }
slouken@8986
   764
        if (naxes > 2) {
slouken@2713
   765
            rglDir[2] = dir->dir[2];
slouken@8986
   766
        }
slouken@2713
   767
        return 0;
slouken@13635
   768
    case SDL_HAPTIC_FIRST_AXIS:
slouken@13635
   769
        effect->dwFlags |= FFEFF_CARTESIAN;
slouken@13635
   770
        rglDir[0] = 0;
slouken@13635
   771
        return 0;
slouken@2713
   772
slouken@2713
   773
    default:
icculus@7037
   774
        return SDL_SetError("Haptic: Unknown direction type.");
slouken@2713
   775
    }
slouken@2713
   776
}
slouken@2713
   777
slouken@2713
   778
slouken@2713
   779
/* Clamps and converts. */
slouken@2713
   780
#define CCONVERT(x)   (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
slouken@2713
   781
/* Just converts. */
slouken@2713
   782
#define CONVERT(x)    (((x)*10000) / 0x7FFF)
slouken@2713
   783
/*
slouken@2713
   784
 * Creates the FFEFFECT from a SDL_HapticEffect.
slouken@2713
   785
 */
slouken@2713
   786
static int
slouken@8986
   787
SDL_SYS_ToFFEFFECT(SDL_Haptic * haptic, FFEFFECT * dest, SDL_HapticEffect * src)
slouken@2713
   788
{
slouken@2713
   789
    int i;
icculus@9631
   790
    FFCONSTANTFORCE *constant = NULL;
icculus@9631
   791
    FFPERIODIC *periodic = NULL;
icculus@9631
   792
    FFCONDITION *condition = NULL;     /* Actually an array of conditions - one per axis. */
icculus@9631
   793
    FFRAMPFORCE *ramp = NULL;
icculus@9631
   794
    FFCUSTOMFORCE *custom = NULL;
icculus@9631
   795
    FFENVELOPE *envelope = NULL;
icculus@9631
   796
    SDL_HapticConstant *hap_constant = NULL;
icculus@9631
   797
    SDL_HapticPeriodic *hap_periodic = NULL;
icculus@9631
   798
    SDL_HapticCondition *hap_condition = NULL;
icculus@9631
   799
    SDL_HapticRamp *hap_ramp = NULL;
icculus@9631
   800
    SDL_HapticCustom *hap_custom = NULL;
icculus@9631
   801
    DWORD *axes = NULL;
slouken@2713
   802
slouken@2713
   803
    /* Set global stuff. */
slouken@2713
   804
    SDL_memset(dest, 0, sizeof(FFEFFECT));
slouken@2713
   805
    dest->dwSize = sizeof(FFEFFECT);    /* Set the structure size. */
slouken@2713
   806
    dest->dwSamplePeriod = 0;   /* Not used by us. */
slouken@2713
   807
    dest->dwGain = 10000;       /* Gain is set globally, not locally. */
slouken@2713
   808
    dest->dwFlags = FFEFF_OBJECTOFFSETS;        /* Seems obligatory. */
slouken@2713
   809
slouken@2713
   810
    /* Envelope. */
slouken@2713
   811
    envelope = SDL_malloc(sizeof(FFENVELOPE));
slouken@2713
   812
    if (envelope == NULL) {
icculus@7037
   813
        return SDL_OutOfMemory();
slouken@2713
   814
    }
slouken@2713
   815
    SDL_memset(envelope, 0, sizeof(FFENVELOPE));
slouken@2713
   816
    dest->lpEnvelope = envelope;
slouken@2713
   817
    envelope->dwSize = sizeof(FFENVELOPE);      /* Always should be this. */
slouken@2713
   818
slouken@2713
   819
    /* Axes. */
slouken@13635
   820
    if (src->constant.direction.type == SDL_HAPTIC_FIRST_AXIS) {
slouken@13635
   821
        dest->cAxes = 1;
slouken@13635
   822
    } else {
slouken@13635
   823
        dest->cAxes = haptic->naxes;
slouken@13635
   824
    }
slouken@2713
   825
    if (dest->cAxes > 0) {
slouken@2713
   826
        axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
slouken@2713
   827
        if (axes == NULL) {
icculus@7037
   828
            return SDL_OutOfMemory();
slouken@2713
   829
        }
slouken@2713
   830
        axes[0] = haptic->hwdata->axes[0];      /* Always at least one axis. */
slouken@2713
   831
        if (dest->cAxes > 1) {
slouken@2713
   832
            axes[1] = haptic->hwdata->axes[1];
slouken@2713
   833
        }
slouken@2713
   834
        if (dest->cAxes > 2) {
slouken@2713
   835
            axes[2] = haptic->hwdata->axes[2];
slouken@2713
   836
        }
slouken@2713
   837
        dest->rgdwAxes = axes;
slouken@2713
   838
    }
slouken@2713
   839
slouken@2713
   840
philipp@7132
   841
    /* The big type handling switch, even bigger then Linux's version. */
slouken@2713
   842
    switch (src->type) {
slouken@2713
   843
    case SDL_HAPTIC_CONSTANT:
slouken@2713
   844
        hap_constant = &src->constant;
slouken@2713
   845
        constant = SDL_malloc(sizeof(FFCONSTANTFORCE));
slouken@2713
   846
        if (constant == NULL) {
icculus@7037
   847
            return SDL_OutOfMemory();
slouken@2713
   848
        }
slouken@2713
   849
        SDL_memset(constant, 0, sizeof(FFCONSTANTFORCE));
slouken@2713
   850
slouken@2713
   851
        /* Specifics */
slouken@2713
   852
        constant->lMagnitude = CONVERT(hap_constant->level);
slouken@2713
   853
        dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE);
slouken@2713
   854
        dest->lpvTypeSpecificParams = constant;
slouken@2713
   855
slouken@2713
   856
        /* Generics */
slouken@2713
   857
        dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
slouken@2713
   858
        dest->dwTriggerButton = FFGetTriggerButton(hap_constant->button);
slouken@2713
   859
        dest->dwTriggerRepeatInterval = hap_constant->interval;
slouken@2713
   860
        dest->dwStartDelay = hap_constant->delay * 1000;        /* In microseconds. */
slouken@2713
   861
slouken@2713
   862
        /* Direction. */
slouken@2713
   863
        if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
slouken@2713
   864
            < 0) {
slouken@2713
   865
            return -1;
slouken@2713
   866
        }
slouken@2713
   867
slouken@2713
   868
        /* Envelope */
slouken@2713
   869
        if ((hap_constant->attack_length == 0)
slouken@2713
   870
            && (hap_constant->fade_length == 0)) {
slouken@2713
   871
            SDL_free(envelope);
slouken@2713
   872
            dest->lpEnvelope = NULL;
slouken@2713
   873
        } else {
slouken@2713
   874
            envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level);
slouken@2713
   875
            envelope->dwAttackTime = hap_constant->attack_length * 1000;
slouken@2713
   876
            envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level);
slouken@2713
   877
            envelope->dwFadeTime = hap_constant->fade_length * 1000;
slouken@2713
   878
        }
slouken@2713
   879
slouken@2713
   880
        break;
slouken@2713
   881
slouken@2713
   882
    case SDL_HAPTIC_SINE:
icculus@7621
   883
    /* !!! FIXME: put this back when we have more bits in 2.1 */
gabomdq@7678
   884
    /* case SDL_HAPTIC_SQUARE: */
slouken@2713
   885
    case SDL_HAPTIC_TRIANGLE:
slouken@2713
   886
    case SDL_HAPTIC_SAWTOOTHUP:
slouken@2713
   887
    case SDL_HAPTIC_SAWTOOTHDOWN:
slouken@2713
   888
        hap_periodic = &src->periodic;
slouken@2713
   889
        periodic = SDL_malloc(sizeof(FFPERIODIC));
slouken@2713
   890
        if (periodic == NULL) {
icculus@7037
   891
            return SDL_OutOfMemory();
slouken@2713
   892
        }
slouken@2713
   893
        SDL_memset(periodic, 0, sizeof(FFPERIODIC));
slouken@2713
   894
slouken@2713
   895
        /* Specifics */
icculus@9147
   896
        periodic->dwMagnitude = CONVERT(SDL_abs(hap_periodic->magnitude));
slouken@2713
   897
        periodic->lOffset = CONVERT(hap_periodic->offset);
icculus@9147
   898
        periodic->dwPhase = 
icculus@9147
   899
                (hap_periodic->phase + (hap_periodic->magnitude < 0 ? 18000 : 0)) % 36000;
slouken@2713
   900
        periodic->dwPeriod = hap_periodic->period * 1000;
slouken@2713
   901
        dest->cbTypeSpecificParams = sizeof(FFPERIODIC);
slouken@2713
   902
        dest->lpvTypeSpecificParams = periodic;
slouken@2713
   903
slouken@2713
   904
        /* Generics */
slouken@2713
   905
        dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
slouken@2713
   906
        dest->dwTriggerButton = FFGetTriggerButton(hap_periodic->button);
slouken@2713
   907
        dest->dwTriggerRepeatInterval = hap_periodic->interval;
slouken@2713
   908
        dest->dwStartDelay = hap_periodic->delay * 1000;        /* In microseconds. */
slouken@2713
   909
slouken@2713
   910
        /* Direction. */
slouken@2713
   911
        if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
slouken@2713
   912
            < 0) {
slouken@2713
   913
            return -1;
slouken@2713
   914
        }
slouken@2713
   915
slouken@2713
   916
        /* Envelope */
slouken@2713
   917
        if ((hap_periodic->attack_length == 0)
slouken@2713
   918
            && (hap_periodic->fade_length == 0)) {
slouken@2713
   919
            SDL_free(envelope);
slouken@2713
   920
            dest->lpEnvelope = NULL;
slouken@2713
   921
        } else {
slouken@2713
   922
            envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level);
slouken@2713
   923
            envelope->dwAttackTime = hap_periodic->attack_length * 1000;
slouken@2713
   924
            envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level);
slouken@2713
   925
            envelope->dwFadeTime = hap_periodic->fade_length * 1000;
slouken@2713
   926
        }
slouken@2713
   927
slouken@2713
   928
        break;
slouken@2713
   929
slouken@2713
   930
    case SDL_HAPTIC_SPRING:
slouken@2713
   931
    case SDL_HAPTIC_DAMPER:
slouken@2713
   932
    case SDL_HAPTIC_INERTIA:
slouken@2713
   933
    case SDL_HAPTIC_FRICTION:
icculus@9633
   934
        hap_condition = &src->condition;
icculus@9631
   935
        if (dest->cAxes > 0) {
icculus@9631
   936
            condition = SDL_malloc(sizeof(FFCONDITION) * dest->cAxes);
icculus@9631
   937
            if (condition == NULL) {
icculus@9631
   938
                return SDL_OutOfMemory();
icculus@9631
   939
            }
icculus@9631
   940
            SDL_memset(condition, 0, sizeof(FFCONDITION));
slouken@2713
   941
icculus@9631
   942
            /* Specifics */
icculus@9631
   943
            for (i = 0; i < dest->cAxes; i++) {
icculus@9631
   944
                condition[i].lOffset = CONVERT(hap_condition->center[i]);
icculus@9631
   945
                condition[i].lPositiveCoefficient =
icculus@9631
   946
                    CONVERT(hap_condition->right_coeff[i]);
icculus@9631
   947
                condition[i].lNegativeCoefficient =
icculus@9631
   948
                    CONVERT(hap_condition->left_coeff[i]);
icculus@9631
   949
                condition[i].dwPositiveSaturation =
icculus@9631
   950
                    CCONVERT(hap_condition->right_sat[i] / 2);
icculus@9631
   951
                condition[i].dwNegativeSaturation =
icculus@9631
   952
                    CCONVERT(hap_condition->left_sat[i] / 2);
icculus@9631
   953
                condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i] / 2);
icculus@9631
   954
            }
slouken@2713
   955
        }
icculus@9631
   956
slouken@2713
   957
        dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes;
slouken@2713
   958
        dest->lpvTypeSpecificParams = condition;
slouken@2713
   959
slouken@2713
   960
        /* Generics */
slouken@2713
   961
        dest->dwDuration = hap_condition->length * 1000;        /* In microseconds. */
slouken@2713
   962
        dest->dwTriggerButton = FFGetTriggerButton(hap_condition->button);
slouken@2713
   963
        dest->dwTriggerRepeatInterval = hap_condition->interval;
slouken@2713
   964
        dest->dwStartDelay = hap_condition->delay * 1000;       /* In microseconds. */
slouken@2713
   965
slouken@2713
   966
        /* Direction. */
slouken@2713
   967
        if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
slouken@2713
   968
            < 0) {
slouken@2713
   969
            return -1;
slouken@2713
   970
        }
slouken@2713
   971
slouken@2713
   972
        /* Envelope - Not actually supported by most CONDITION implementations. */
slouken@2713
   973
        SDL_free(dest->lpEnvelope);
slouken@2713
   974
        dest->lpEnvelope = NULL;
slouken@2713
   975
slouken@2713
   976
        break;
slouken@2713
   977
slouken@2713
   978
    case SDL_HAPTIC_RAMP:
slouken@2713
   979
        hap_ramp = &src->ramp;
slouken@2713
   980
        ramp = SDL_malloc(sizeof(FFRAMPFORCE));
slouken@2713
   981
        if (ramp == NULL) {
icculus@7037
   982
            return SDL_OutOfMemory();
slouken@2713
   983
        }
slouken@2713
   984
        SDL_memset(ramp, 0, sizeof(FFRAMPFORCE));
slouken@2713
   985
slouken@2713
   986
        /* Specifics */
slouken@2713
   987
        ramp->lStart = CONVERT(hap_ramp->start);
slouken@2713
   988
        ramp->lEnd = CONVERT(hap_ramp->end);
slouken@2713
   989
        dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE);
slouken@2713
   990
        dest->lpvTypeSpecificParams = ramp;
slouken@2713
   991
slouken@2713
   992
        /* Generics */
slouken@2713
   993
        dest->dwDuration = hap_ramp->length * 1000;     /* In microseconds. */
slouken@2713
   994
        dest->dwTriggerButton = FFGetTriggerButton(hap_ramp->button);
slouken@2713
   995
        dest->dwTriggerRepeatInterval = hap_ramp->interval;
slouken@2713
   996
        dest->dwStartDelay = hap_ramp->delay * 1000;    /* In microseconds. */
slouken@2713
   997
slouken@2713
   998
        /* Direction. */
slouken@2713
   999
        if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
slouken@2713
  1000
            return -1;
slouken@2713
  1001
        }
slouken@2713
  1002
slouken@2713
  1003
        /* Envelope */
slouken@2713
  1004
        if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
slouken@2713
  1005
            SDL_free(envelope);
slouken@2713
  1006
            dest->lpEnvelope = NULL;
slouken@2713
  1007
        } else {
slouken@2713
  1008
            envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level);
slouken@2713
  1009
            envelope->dwAttackTime = hap_ramp->attack_length * 1000;
slouken@2713
  1010
            envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level);
slouken@2713
  1011
            envelope->dwFadeTime = hap_ramp->fade_length * 1000;
slouken@2713
  1012
        }
slouken@2713
  1013
slouken@2713
  1014
        break;
slouken@2713
  1015
slouken@2713
  1016
    case SDL_HAPTIC_CUSTOM:
slouken@2713
  1017
        hap_custom = &src->custom;
slouken@2713
  1018
        custom = SDL_malloc(sizeof(FFCUSTOMFORCE));
slouken@2713
  1019
        if (custom == NULL) {
icculus@7037
  1020
            return SDL_OutOfMemory();
slouken@2713
  1021
        }
slouken@2713
  1022
        SDL_memset(custom, 0, sizeof(FFCUSTOMFORCE));
slouken@2713
  1023
slouken@2713
  1024
        /* Specifics */
slouken@2713
  1025
        custom->cChannels = hap_custom->channels;
slouken@2713
  1026
        custom->dwSamplePeriod = hap_custom->period * 1000;
slouken@2713
  1027
        custom->cSamples = hap_custom->samples;
slouken@2713
  1028
        custom->rglForceData =
slouken@2713
  1029
            SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
slouken@2713
  1030
        for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) {      /* Copy data. */
slouken@2713
  1031
            custom->rglForceData[i] = CCONVERT(hap_custom->data[i]);
slouken@2713
  1032
        }
slouken@2713
  1033
        dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE);
slouken@2713
  1034
        dest->lpvTypeSpecificParams = custom;
slouken@2713
  1035
slouken@2713
  1036
        /* Generics */
slouken@2713
  1037
        dest->dwDuration = hap_custom->length * 1000;   /* In microseconds. */
slouken@2713
  1038
        dest->dwTriggerButton = FFGetTriggerButton(hap_custom->button);
slouken@2713
  1039
        dest->dwTriggerRepeatInterval = hap_custom->interval;
slouken@2713
  1040
        dest->dwStartDelay = hap_custom->delay * 1000;  /* In microseconds. */
slouken@2713
  1041
slouken@2713
  1042
        /* Direction. */
slouken@2713
  1043
        if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
slouken@2713
  1044
            0) {
slouken@2713
  1045
            return -1;
slouken@2713
  1046
        }
slouken@2713
  1047
slouken@2713
  1048
        /* Envelope */
slouken@2713
  1049
        if ((hap_custom->attack_length == 0)
slouken@2713
  1050
            && (hap_custom->fade_length == 0)) {
slouken@2713
  1051
            SDL_free(envelope);
slouken@2713
  1052
            dest->lpEnvelope = NULL;
slouken@2713
  1053
        } else {
slouken@2713
  1054
            envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level);
slouken@2713
  1055
            envelope->dwAttackTime = hap_custom->attack_length * 1000;
slouken@2713
  1056
            envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level);
slouken@2713
  1057
            envelope->dwFadeTime = hap_custom->fade_length * 1000;
slouken@2713
  1058
        }
slouken@2713
  1059
slouken@2713
  1060
        break;
slouken@2713
  1061
slouken@2713
  1062
slouken@2713
  1063
    default:
icculus@7037
  1064
        return SDL_SetError("Haptic: Unknown effect type.");
slouken@2713
  1065
    }
slouken@2713
  1066
slouken@2713
  1067
    return 0;
slouken@2713
  1068
}
slouken@2713
  1069
slouken@2713
  1070
slouken@2713
  1071
/*
slouken@2713
  1072
 * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT.
slouken@2713
  1073
 */
slouken@2713
  1074
static void
slouken@2713
  1075
SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type)
slouken@2713
  1076
{
slouken@2713
  1077
    FFCUSTOMFORCE *custom;
slouken@2713
  1078
slouken@7719
  1079
    SDL_free(effect->lpEnvelope);
slouken@7719
  1080
    effect->lpEnvelope = NULL;
slouken@7719
  1081
    SDL_free(effect->rgdwAxes);
slouken@7719
  1082
    effect->rgdwAxes = NULL;
slouken@2713
  1083
    if (effect->lpvTypeSpecificParams != NULL) {
slouken@2713
  1084
        if (type == SDL_HAPTIC_CUSTOM) {        /* Must free the custom data. */
slouken@2713
  1085
            custom = (FFCUSTOMFORCE *) effect->lpvTypeSpecificParams;
slouken@2713
  1086
            SDL_free(custom->rglForceData);
slouken@2713
  1087
            custom->rglForceData = NULL;
slouken@2713
  1088
        }
slouken@2713
  1089
        SDL_free(effect->lpvTypeSpecificParams);
slouken@2713
  1090
        effect->lpvTypeSpecificParams = NULL;
slouken@2713
  1091
    }
slouken@7719
  1092
    SDL_free(effect->rglDirection);
slouken@7719
  1093
    effect->rglDirection = NULL;
slouken@2713
  1094
}
slouken@2713
  1095
slouken@2713
  1096
slouken@2713
  1097
/*
slouken@2713
  1098
 * Gets the effect type from the generic SDL haptic effect wrapper.
slouken@2713
  1099
 */
slouken@2713
  1100
CFUUIDRef
slouken@2713
  1101
SDL_SYS_HapticEffectType(Uint16 type)
slouken@2713
  1102
{
slouken@2713
  1103
    switch (type) {
slouken@2713
  1104
    case SDL_HAPTIC_CONSTANT:
slouken@2713
  1105
        return kFFEffectType_ConstantForce_ID;
slouken@2713
  1106
slouken@2713
  1107
    case SDL_HAPTIC_RAMP:
slouken@2713
  1108
        return kFFEffectType_RampForce_ID;
slouken@2713
  1109
icculus@7621
  1110
    /* !!! FIXME: put this back when we have more bits in 2.1 */
gabomdq@7678
  1111
    /* case SDL_HAPTIC_SQUARE:
gabomdq@7677
  1112
        return kFFEffectType_Square_ID; */
slouken@2713
  1113
slouken@2713
  1114
    case SDL_HAPTIC_SINE:
slouken@2713
  1115
        return kFFEffectType_Sine_ID;
slouken@2713
  1116
slouken@2713
  1117
    case SDL_HAPTIC_TRIANGLE:
slouken@2713
  1118
        return kFFEffectType_Triangle_ID;
slouken@2713
  1119
slouken@2713
  1120
    case SDL_HAPTIC_SAWTOOTHUP:
slouken@2713
  1121
        return kFFEffectType_SawtoothUp_ID;
slouken@2713
  1122
slouken@2713
  1123
    case SDL_HAPTIC_SAWTOOTHDOWN:
slouken@2713
  1124
        return kFFEffectType_SawtoothDown_ID;
slouken@2713
  1125
slouken@2713
  1126
    case SDL_HAPTIC_SPRING:
slouken@2713
  1127
        return kFFEffectType_Spring_ID;
slouken@2713
  1128
slouken@2713
  1129
    case SDL_HAPTIC_DAMPER:
slouken@2713
  1130
        return kFFEffectType_Damper_ID;
slouken@2713
  1131
slouken@2713
  1132
    case SDL_HAPTIC_INERTIA:
slouken@2713
  1133
        return kFFEffectType_Inertia_ID;
slouken@2713
  1134
slouken@2713
  1135
    case SDL_HAPTIC_FRICTION:
slouken@2713
  1136
        return kFFEffectType_Friction_ID;
slouken@2713
  1137
slouken@2713
  1138
    case SDL_HAPTIC_CUSTOM:
slouken@2713
  1139
        return kFFEffectType_CustomForce_ID;
slouken@2713
  1140
slouken@2713
  1141
    default:
slouken@2713
  1142
        SDL_SetError("Haptic: Unknown effect type.");
slouken@2713
  1143
        return NULL;
slouken@2713
  1144
    }
slouken@2713
  1145
}
slouken@2713
  1146
slouken@2713
  1147
slouken@2713
  1148
/*
slouken@2713
  1149
 * Creates a new haptic effect.
slouken@2713
  1150
 */
slouken@2713
  1151
int
slouken@2713
  1152
SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
slouken@2713
  1153
                        SDL_HapticEffect * base)
slouken@2713
  1154
{
slouken@2713
  1155
    HRESULT ret;
slouken@2713
  1156
    CFUUIDRef type;
slouken@2713
  1157
slouken@2713
  1158
    /* Alloc the effect. */
slouken@2713
  1159
    effect->hweffect = (struct haptic_hweffect *)
slouken@2713
  1160
        SDL_malloc(sizeof(struct haptic_hweffect));
slouken@2713
  1161
    if (effect->hweffect == NULL) {
slouken@2713
  1162
        SDL_OutOfMemory();
slouken@2713
  1163
        goto err_hweffect;
slouken@2713
  1164
    }
slouken@2713
  1165
slouken@2713
  1166
    /* Get the type. */
slouken@2713
  1167
    type = SDL_SYS_HapticEffectType(base->type);
slouken@2713
  1168
    if (type == NULL) {
slouken@2713
  1169
        goto err_hweffect;
slouken@2713
  1170
    }
slouken@2713
  1171
slouken@2713
  1172
    /* Get the effect. */
slouken@2713
  1173
    if (SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
slouken@2713
  1174
        goto err_effectdone;
slouken@2713
  1175
    }
slouken@2713
  1176
slouken@2713
  1177
    /* Create the actual effect. */
slouken@2713
  1178
    ret = FFDeviceCreateEffect(haptic->hwdata->device, type,
slouken@2713
  1179
                               &effect->hweffect->effect,
slouken@2713
  1180
                               &effect->hweffect->ref);
slouken@2713
  1181
    if (ret != FF_OK) {
slouken@2713
  1182
        SDL_SetError("Haptic: Unable to create effect: %s.", FFStrError(ret));
slouken@2713
  1183
        goto err_effectdone;
slouken@2713
  1184
    }
slouken@2713
  1185
slouken@2713
  1186
    return 0;
slouken@2713
  1187
slouken@2713
  1188
  err_effectdone:
slouken@2713
  1189
    SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, base->type);
slouken@2713
  1190
  err_hweffect:
slouken@7719
  1191
    SDL_free(effect->hweffect);
slouken@7719
  1192
    effect->hweffect = NULL;
slouken@2713
  1193
    return -1;
slouken@2713
  1194
}
slouken@2713
  1195
slouken@2713
  1196
slouken@2713
  1197
/*
slouken@2713
  1198
 * Updates an effect.
slouken@2713
  1199
 */
slouken@2713
  1200
int
slouken@2713
  1201
SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
slouken@2713
  1202
                           struct haptic_effect *effect,
slouken@2713
  1203
                           SDL_HapticEffect * data)
slouken@2713
  1204
{
slouken@2713
  1205
    HRESULT ret;
slouken@2713
  1206
    FFEffectParameterFlag flags;
slouken@2713
  1207
    FFEFFECT temp;
slouken@2713
  1208
slouken@2713
  1209
    /* Get the effect. */
slouken@2713
  1210
    SDL_memset(&temp, 0, sizeof(FFEFFECT));
slouken@2713
  1211
    if (SDL_SYS_ToFFEFFECT(haptic, &temp, data) < 0) {
slouken@2713
  1212
        goto err_update;
slouken@2713
  1213
    }
slouken@2713
  1214
slouken@2713
  1215
    /* Set the flags.  Might be worthwhile to diff temp with loaded effect and
slouken@2713
  1216
     *  only change those parameters. */
slouken@2713
  1217
    flags = FFEP_DIRECTION |
slouken@2713
  1218
        FFEP_DURATION |
slouken@2713
  1219
        FFEP_ENVELOPE |
slouken@2713
  1220
        FFEP_STARTDELAY |
slouken@2713
  1221
        FFEP_TRIGGERBUTTON |
slouken@2713
  1222
        FFEP_TRIGGERREPEATINTERVAL | FFEP_TYPESPECIFICPARAMS;
slouken@2713
  1223
slouken@2713
  1224
    /* Create the actual effect. */
slouken@2713
  1225
    ret = FFEffectSetParameters(effect->hweffect->ref, &temp, flags);
slouken@2713
  1226
    if (ret != FF_OK) {
slouken@2713
  1227
        SDL_SetError("Haptic: Unable to update effect: %s.", FFStrError(ret));
slouken@2713
  1228
        goto err_update;
slouken@2713
  1229
    }
slouken@2713
  1230
slouken@2713
  1231
    /* Copy it over. */
slouken@2713
  1232
    SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, data->type);
slouken@2713
  1233
    SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(FFEFFECT));
slouken@2713
  1234
slouken@2713
  1235
    return 0;
slouken@2713
  1236
slouken@2713
  1237
  err_update:
slouken@2713
  1238
    SDL_SYS_HapticFreeFFEFFECT(&temp, data->type);
slouken@2713
  1239
    return -1;
slouken@2713
  1240
}
slouken@2713
  1241
slouken@2713
  1242
slouken@2713
  1243
/*
slouken@2713
  1244
 * Runs an effect.
slouken@2713
  1245
 */
slouken@2713
  1246
int
slouken@2713
  1247
SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
slouken@2713
  1248
                        Uint32 iterations)
slouken@2713
  1249
{
slouken@2713
  1250
    HRESULT ret;
slouken@2713
  1251
    Uint32 iter;
slouken@2713
  1252
slouken@2713
  1253
    /* Check if it's infinite. */
slouken@2713
  1254
    if (iterations == SDL_HAPTIC_INFINITY) {
slouken@2713
  1255
        iter = FF_INFINITE;
slouken@2713
  1256
    } else
slouken@2713
  1257
        iter = iterations;
slouken@2713
  1258
slouken@2713
  1259
    /* Run the effect. */
slouken@2713
  1260
    ret = FFEffectStart(effect->hweffect->ref, iter, 0);
slouken@2713
  1261
    if (ret != FF_OK) {
icculus@7037
  1262
        return SDL_SetError("Haptic: Unable to run the effect: %s.",
icculus@7037
  1263
                            FFStrError(ret));
slouken@2713
  1264
    }
slouken@2713
  1265
slouken@2713
  1266
    return 0;
slouken@2713
  1267
}
slouken@2713
  1268
slouken@2713
  1269
slouken@2713
  1270
/*
slouken@2713
  1271
 * Stops an effect.
slouken@2713
  1272
 */
slouken@2713
  1273
int
slouken@2713
  1274
SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
slouken@2713
  1275
{
slouken@2713
  1276
    HRESULT ret;
slouken@2713
  1277
slouken@2713
  1278
    ret = FFEffectStop(effect->hweffect->ref);
slouken@2713
  1279
    if (ret != FF_OK) {
icculus@7037
  1280
        return SDL_SetError("Haptic: Unable to stop the effect: %s.",
icculus@7037
  1281
                            FFStrError(ret));
slouken@2713
  1282
    }
slouken@2713
  1283
slouken@2713
  1284
    return 0;
slouken@2713
  1285
}
slouken@2713
  1286
slouken@2713
  1287
slouken@2713
  1288
/*
slouken@2713
  1289
 * Frees the effect.
slouken@2713
  1290
 */
slouken@2713
  1291
void
slouken@2713
  1292
SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
slouken@2713
  1293
{
slouken@2713
  1294
    HRESULT ret;
slouken@2713
  1295
slouken@8986
  1296
    ret = FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref);
slouken@2713
  1297
    if (ret != FF_OK) {
slouken@2713
  1298
        SDL_SetError("Haptic: Error removing the effect from the device: %s.",
slouken@2713
  1299
                     FFStrError(ret));
slouken@2713
  1300
    }
slouken@2713
  1301
    SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect,
slouken@2713
  1302
                               effect->effect.type);
slouken@2713
  1303
    SDL_free(effect->hweffect);
slouken@2713
  1304
    effect->hweffect = NULL;
slouken@2713
  1305
}
slouken@2713
  1306
slouken@2713
  1307
slouken@2713
  1308
/*
slouken@2713
  1309
 * Gets the status of a haptic effect.
slouken@2713
  1310
 */
slouken@2713
  1311
int
slouken@2713
  1312
SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
slouken@2713
  1313
                              struct haptic_effect *effect)
slouken@2713
  1314
{
slouken@2713
  1315
    HRESULT ret;
slouken@2713
  1316
    FFEffectStatusFlag status;
slouken@2713
  1317
slouken@2713
  1318
    ret = FFEffectGetEffectStatus(effect->hweffect->ref, &status);
slouken@2713
  1319
    if (ret != FF_OK) {
slouken@2713
  1320
        SDL_SetError("Haptic: Unable to get effect status: %s.",
slouken@2713
  1321
                     FFStrError(ret));
slouken@2713
  1322
        return -1;
slouken@2713
  1323
    }
slouken@2713
  1324
slouken@8986
  1325
    if (status == 0) {
slouken@2713
  1326
        return SDL_FALSE;
slouken@8986
  1327
    }
slouken@2713
  1328
    return SDL_TRUE;            /* Assume it's playing or emulated. */
slouken@2713
  1329
}
slouken@2713
  1330
slouken@2713
  1331
slouken@2713
  1332
/*
slouken@2713
  1333
 * Sets the gain.
slouken@2713
  1334
 */
slouken@2713
  1335
int
slouken@2713
  1336
SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
slouken@2713
  1337
{
slouken@2713
  1338
    HRESULT ret;
slouken@2713
  1339
    Uint32 val;
slouken@2713
  1340
slouken@2713
  1341
    val = gain * 100;           /* Mac OS X uses 0 to 10,000 */
slouken@8986
  1342
    ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
slouken@8986
  1343
                                           FFPROP_FFGAIN, &val);
slouken@2713
  1344
    if (ret != FF_OK) {
icculus@7037
  1345
        return SDL_SetError("Haptic: Error setting gain: %s.", FFStrError(ret));
slouken@2713
  1346
    }
slouken@2713
  1347
slouken@2713
  1348
    return 0;
slouken@2713
  1349
}
slouken@2713
  1350
slouken@2713
  1351
slouken@2713
  1352
/*
slouken@2713
  1353
 * Sets the autocentering.
slouken@2713
  1354
 */
slouken@2713
  1355
int
slouken@2713
  1356
SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
slouken@2713
  1357
{
slouken@2713
  1358
    HRESULT ret;
slouken@2713
  1359
    Uint32 val;
slouken@2713
  1360
slouken@2713
  1361
    /* Mac OS X only has 0 (off) and 1 (on) */
slouken@8986
  1362
    if (autocenter == 0) {
slouken@2713
  1363
        val = 0;
slouken@8986
  1364
    } else {
slouken@2713
  1365
        val = 1;
slouken@8986
  1366
    }
slouken@2713
  1367
slouken@2713
  1368
    ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
slouken@2713
  1369
                                           FFPROP_AUTOCENTER, &val);
slouken@2713
  1370
    if (ret != FF_OK) {
icculus@7037
  1371
        return SDL_SetError("Haptic: Error setting autocenter: %s.",
icculus@7037
  1372
                            FFStrError(ret));
slouken@2713
  1373
    }
slouken@2713
  1374
slouken@2713
  1375
    return 0;
slouken@2713
  1376
}
slouken@2713
  1377
slouken@2713
  1378
slouken@2713
  1379
/*
slouken@2713
  1380
 * Pauses the device.
slouken@2713
  1381
 */
slouken@2713
  1382
int
slouken@2713
  1383
SDL_SYS_HapticPause(SDL_Haptic * haptic)
slouken@2713
  1384
{
slouken@2713
  1385
    HRESULT ret;
slouken@2713
  1386
slouken@2713
  1387
    ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
slouken@2713
  1388
                                           FFSFFC_PAUSE);
slouken@2713
  1389
    if (ret != FF_OK) {
icculus@7037
  1390
        return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
slouken@2713
  1391
    }
slouken@2713
  1392
slouken@2713
  1393
    return 0;
slouken@2713
  1394
}
slouken@2713
  1395
slouken@2713
  1396
slouken@2713
  1397
/*
slouken@2713
  1398
 * Unpauses the device.
slouken@2713
  1399
 */
slouken@2713
  1400
int
slouken@2713
  1401
SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
slouken@2713
  1402
{
slouken@2713
  1403
    HRESULT ret;
slouken@2713
  1404
slouken@2713
  1405
    ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
slouken@2713
  1406
                                           FFSFFC_CONTINUE);
slouken@2713
  1407
    if (ret != FF_OK) {
icculus@7037
  1408
        return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
slouken@2713
  1409
    }
slouken@2713
  1410
slouken@2713
  1411
    return 0;
slouken@2713
  1412
}
slouken@2713
  1413
slouken@2713
  1414
slouken@2713
  1415
/*
slouken@2713
  1416
 * Stops all currently playing effects.
slouken@2713
  1417
 */
slouken@2713
  1418
int
slouken@2713
  1419
SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
slouken@2713
  1420
{
slouken@2713
  1421
    HRESULT ret;
slouken@2713
  1422
slouken@2713
  1423
    ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
slouken@2713
  1424
                                           FFSFFC_STOPALL);
slouken@2713
  1425
    if (ret != FF_OK) {
icculus@7037
  1426
        return SDL_SetError("Haptic: Error stopping device: %s.", FFStrError(ret));
slouken@2713
  1427
    }
slouken@2713
  1428
slouken@2713
  1429
    return 0;
slouken@2713
  1430
}
slouken@2713
  1431
slouken@9740
  1432
#endif /* SDL_HAPTIC_IOKIT */
slouken@2713
  1433
slouken@9740
  1434
/* vi: set ts=4 sw=4 expandtab: */