src/joystick/darwin/SDL_sysjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 25 Oct 2018 16:53:14 -0700
changeset 12359 691c32a30fb9
parent 12115 0d9277d27e2d
child 12503 806492103856
permissions -rw-r--r--
Generalized the XInput user index into a player index
slouken@172
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@11811
     3
  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
slouken@172
     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@172
     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@172
    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@172
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@172
    22
slouken@1635
    23
#ifdef SDL_JOYSTICK_IOKIT
slouken@1635
    24
slouken@12088
    25
#include "SDL_events.h"
slouken@172
    26
#include "SDL_joystick.h"
slouken@1361
    27
#include "../SDL_sysjoystick.h"
slouken@1361
    28
#include "../SDL_joystick_c.h"
slouken@2713
    29
#include "SDL_sysjoystick_c.h"
slouken@12088
    30
#include "../hidapi/SDL_hidapijoystick_c.h"
urkle@8173
    31
#include "../../haptic/darwin/SDL_syshaptic_c.h"    /* For haptic hot plugging */
slouken@172
    32
slouken@12088
    33
icculus@8739
    34
#define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick")
icculus@8739
    35
slouken@12088
    36
#define CONVERT_MAGNITUDE(x)    (((x)*10000) / 0x7FFF)
slouken@12088
    37
icculus@8230
    38
/* The base object of the HID Manager API */
icculus@8230
    39
static IOHIDManagerRef hidman = NULL;
slouken@172
    40
slouken@172
    41
/* Linked list of all available devices */
slouken@172
    42
static recDevice *gpDeviceList = NULL;
icculus@8230
    43
slouken@12088
    44
void FreeRumbleEffectData(FFEFFECT *effect)
slouken@12088
    45
{
slouken@12088
    46
    if (!effect) {
slouken@12088
    47
        return;
slouken@12088
    48
    }
slouken@12088
    49
    SDL_free(effect->rgdwAxes);
slouken@12088
    50
    SDL_free(effect->rglDirection);
slouken@12088
    51
    SDL_free(effect->lpvTypeSpecificParams);
slouken@12088
    52
    SDL_free(effect);
slouken@12088
    53
}
slouken@12088
    54
slouken@12088
    55
FFEFFECT *CreateRumbleEffectData(Sint16 magnitude, Uint32 duration_ms)
slouken@12088
    56
{
slouken@12088
    57
    FFEFFECT *effect;
slouken@12088
    58
    FFPERIODIC *periodic;
slouken@12088
    59
slouken@12088
    60
    /* Create the effect */
slouken@12088
    61
    effect = (FFEFFECT *)SDL_calloc(1, sizeof(*effect));
slouken@12088
    62
    if (!effect) {
slouken@12088
    63
        return NULL;
slouken@12088
    64
    }
slouken@12088
    65
    effect->dwSize = sizeof(*effect);
slouken@12088
    66
    effect->dwGain = 10000;
slouken@12088
    67
    effect->dwFlags = FFEFF_OBJECTOFFSETS;
slouken@12088
    68
    effect->dwDuration = duration_ms * 1000; /* In microseconds. */
slouken@12088
    69
    effect->dwTriggerButton = FFEB_NOTRIGGER;
slouken@12088
    70
slouken@12088
    71
    effect->cAxes = 2;
slouken@12088
    72
    effect->rgdwAxes = (DWORD *)SDL_calloc(effect->cAxes, sizeof(DWORD));
slouken@12088
    73
    if (!effect->rgdwAxes) {
slouken@12088
    74
        FreeRumbleEffectData(effect);
slouken@12088
    75
        return NULL;
slouken@12088
    76
    }
slouken@12088
    77
slouken@12088
    78
    effect->rglDirection = (LONG *)SDL_calloc(effect->cAxes, sizeof(LONG));
slouken@12088
    79
    if (!effect->rglDirection) {
slouken@12088
    80
        FreeRumbleEffectData(effect);
slouken@12088
    81
        return NULL;
slouken@12088
    82
    }
slouken@12088
    83
    effect->dwFlags |= FFEFF_CARTESIAN;
slouken@12088
    84
slouken@12088
    85
    periodic = (FFPERIODIC *)SDL_calloc(1, sizeof(*periodic));
slouken@12088
    86
    if (!periodic) {
slouken@12088
    87
        FreeRumbleEffectData(effect);
slouken@12088
    88
        return NULL;
slouken@12088
    89
    }
slouken@12088
    90
    periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
slouken@12088
    91
    periodic->dwPeriod = 1000000;
slouken@12088
    92
slouken@12088
    93
    effect->cbTypeSpecificParams = sizeof(*periodic);
slouken@12088
    94
    effect->lpvTypeSpecificParams = periodic;
slouken@12088
    95
slouken@12088
    96
    return effect;
slouken@12088
    97
}
slouken@172
    98
slouken@9629
    99
static recDevice *GetDeviceForIndex(int device_index)
slouken@9629
   100
{
slouken@9629
   101
    recDevice *device = gpDeviceList;
slouken@9629
   102
    while (device) {
slouken@9629
   103
        if (!device->removed) {
slouken@9629
   104
            if (device_index == 0)
slouken@9629
   105
                break;
slouken@9629
   106
slouken@9629
   107
            --device_index;
slouken@9629
   108
        }
slouken@9629
   109
        device = device->pNext;
slouken@9629
   110
    }
slouken@9629
   111
    return device;
slouken@9629
   112
}
icculus@8230
   113
slouken@1895
   114
static void
icculus@8230
   115
FreeElementList(recElement *pElement)
slouken@172
   116
{
icculus@8230
   117
    while (pElement) {
icculus@8230
   118
        recElement *pElementNext = pElement->pNext;
icculus@8230
   119
        SDL_free(pElement);
icculus@8230
   120
        pElement = pElementNext;
icculus@8230
   121
    }
slouken@172
   122
}
slouken@172
   123
icculus@8230
   124
static recDevice *
icculus@8230
   125
FreeDevice(recDevice *removeDevice)
icculus@8230
   126
{
icculus@8230
   127
    recDevice *pDeviceNext = NULL;
icculus@8230
   128
    if (removeDevice) {
icculus@8739
   129
        if (removeDevice->deviceRef) {
icculus@8739
   130
            IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
icculus@8739
   131
            removeDevice->deviceRef = NULL;
icculus@8739
   132
        }
icculus@8739
   133
icculus@8230
   134
        /* save next device prior to disposing of this device */
icculus@8230
   135
        pDeviceNext = removeDevice->pNext;
slouken@172
   136
icculus@8230
   137
        if ( gpDeviceList == removeDevice ) {
icculus@8230
   138
            gpDeviceList = pDeviceNext;
icculus@8230
   139
        } else {
icculus@8230
   140
            recDevice *device = gpDeviceList;
icculus@8230
   141
            while (device->pNext != removeDevice) {
icculus@8230
   142
                device = device->pNext;
icculus@8230
   143
            }
icculus@8230
   144
            device->pNext = pDeviceNext;
icculus@8230
   145
        }
icculus@8230
   146
        removeDevice->pNext = NULL;
icculus@8230
   147
icculus@8230
   148
        /* free element lists */
icculus@8230
   149
        FreeElementList(removeDevice->firstAxis);
icculus@8230
   150
        FreeElementList(removeDevice->firstButton);
icculus@8230
   151
        FreeElementList(removeDevice->firstHat);
icculus@8230
   152
icculus@8230
   153
        SDL_free(removeDevice);
icculus@8230
   154
    }
icculus@8230
   155
    return pDeviceNext;
icculus@8230
   156
}
slouken@172
   157
slouken@11843
   158
static SDL_bool
slouken@11843
   159
GetHIDElementState(recDevice *pDevice, recElement *pElement, SInt32 *pValue)
slouken@172
   160
{
icculus@8230
   161
    SInt32 value = 0;
slouken@11843
   162
    int returnValue = SDL_FALSE;
slouken@172
   163
icculus@8230
   164
    if (pDevice && pElement) {
icculus@8230
   165
        IOHIDValueRef valueRef;
icculus@8230
   166
        if (IOHIDDeviceGetValue(pDevice->deviceRef, pElement->elementRef, &valueRef) == kIOReturnSuccess) {
icculus@8230
   167
            value = (SInt32) IOHIDValueGetIntegerValue(valueRef);
icculus@8230
   168
slouken@1895
   169
            /* record min and max for auto calibration */
icculus@8230
   170
            if (value < pElement->minReport) {
icculus@8230
   171
                pElement->minReport = value;
icculus@8230
   172
            }
icculus@8230
   173
            if (value > pElement->maxReport) {
icculus@8230
   174
                pElement->maxReport = value;
icculus@8230
   175
            }
slouken@11843
   176
            *pValue = value;
slouken@11843
   177
slouken@11843
   178
            returnValue = SDL_TRUE;
slouken@1895
   179
        }
slouken@1895
   180
    }
slouken@11843
   181
    return returnValue;
slouken@172
   182
}
slouken@172
   183
slouken@11843
   184
static SDL_bool
slouken@11843
   185
GetHIDScaledCalibratedState(recDevice * pDevice, recElement * pElement, SInt32 min, SInt32 max, SInt32 *pValue)
slouken@172
   186
{
icculus@8230
   187
    const float deviceScale = max - min;
icculus@8230
   188
    const float readScale = pElement->maxReport - pElement->minReport;
slouken@11843
   189
    int returnValue = SDL_FALSE;
slouken@11843
   190
    if (GetHIDElementState(pDevice, pElement, pValue))
slouken@11843
   191
    {
slouken@11843
   192
        if (readScale == 0) {
slouken@11843
   193
            returnValue = SDL_TRUE;           /* no scaling at all */
slouken@11843
   194
        }
slouken@11843
   195
        else
slouken@11843
   196
        {
slouken@11843
   197
            *pValue = ((*pValue - pElement->minReport) * deviceScale / readScale) + min;
slouken@11843
   198
            returnValue = SDL_TRUE;
slouken@11843
   199
        }
slouken@11843
   200
    } 
slouken@11843
   201
    return returnValue;
slouken@172
   202
}
slouken@172
   203
slouken@1895
   204
static void
icculus@8230
   205
JoystickDeviceWasRemovedCallback(void *ctx, IOReturn result, void *sender)
slouken@858
   206
{
icculus@8230
   207
    recDevice *device = (recDevice *) ctx;
icculus@9433
   208
    device->removed = SDL_TRUE;
alfred@8858
   209
    device->deviceRef = NULL; // deviceRef was invalidated due to the remove
slouken@12088
   210
    if (device->ffeffect_ref) {
slouken@12088
   211
        FFDeviceReleaseEffect(device->ffdevice, device->ffeffect_ref);
slouken@12088
   212
        device->ffeffect_ref = NULL;
slouken@12088
   213
    }
slouken@12088
   214
    if (device->ffeffect) {
slouken@12088
   215
        FreeRumbleEffectData(device->ffeffect);
slouken@12088
   216
        device->ffeffect = NULL;
slouken@12088
   217
    }
slouken@12088
   218
    if (device->ffdevice) {
slouken@12088
   219
        FFReleaseDevice(device->ffdevice);
slouken@12088
   220
        device->ffdevice = NULL;
slouken@12088
   221
        device->ff_initialized = SDL_FALSE;
slouken@12088
   222
    }
urkle@8173
   223
#if SDL_HAPTIC_IOKIT
icculus@8176
   224
    MacHaptic_MaybeRemoveDevice(device->ffservice);
urkle@8173
   225
#endif
slouken@9629
   226
slouken@10226
   227
    SDL_PrivateJoystickRemoved(device->instance_id);
slouken@858
   228
}
slouken@858
   229
slouken@858
   230
icculus@8230
   231
static void AddHIDElement(const void *value, void *parameter);
icculus@8230
   232
icculus@8230
   233
/* Call AddHIDElement() on all elements in an array of IOHIDElementRefs */
icculus@8230
   234
static void
icculus@8230
   235
AddHIDElements(CFArrayRef array, recDevice *pDevice)
slouken@6690
   236
{
icculus@8230
   237
    const CFRange range = { 0, CFArrayGetCount(array) };
icculus@8230
   238
    CFArrayApplyFunction(array, range, AddHIDElement, pDevice);
slouken@6690
   239
}
slouken@6690
   240
icculus@8242
   241
static SDL_bool
icculus@8242
   242
ElementAlreadyAdded(const IOHIDElementCookie cookie, const recElement *listitem) {
icculus@8242
   243
    while (listitem) {
icculus@8242
   244
        if (listitem->cookie == cookie) {
icculus@8242
   245
            return SDL_TRUE;
icculus@8242
   246
        }
icculus@8242
   247
        listitem = listitem->pNext;
icculus@8242
   248
    }
icculus@8242
   249
    return SDL_FALSE;
icculus@8242
   250
}
icculus@8242
   251
icculus@8230
   252
/* See if we care about this HID element, and if so, note it in our recDevice. */
icculus@8230
   253
static void
icculus@8230
   254
AddHIDElement(const void *value, void *parameter)
icculus@8230
   255
{
icculus@8230
   256
    recDevice *pDevice = (recDevice *) parameter;
icculus@8230
   257
    IOHIDElementRef refElement = (IOHIDElementRef) value;
icculus@8230
   258
    const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0;
slouken@858
   259
icculus@8230
   260
    if (refElement && (elementTypeID == IOHIDElementGetTypeID())) {
icculus@8242
   261
        const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement);
icculus@8230
   262
        const uint32_t usagePage = IOHIDElementGetUsagePage(refElement);
icculus@8230
   263
        const uint32_t usage = IOHIDElementGetUsage(refElement);
icculus@8230
   264
        recElement *element = NULL;
icculus@8230
   265
        recElement **headElement = NULL;
slouken@172
   266
icculus@8230
   267
        /* look at types of interest */
icculus@8230
   268
        switch (IOHIDElementGetType(refElement)) {
icculus@8230
   269
            case kIOHIDElementTypeInput_Misc:
icculus@8230
   270
            case kIOHIDElementTypeInput_Button:
icculus@8230
   271
            case kIOHIDElementTypeInput_Axis: {
icculus@8230
   272
                switch (usagePage) {    /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */
icculus@8230
   273
                    case kHIDPage_GenericDesktop:
icculus@8230
   274
                        switch (usage) {
icculus@8230
   275
                            case kHIDUsage_GD_X:
icculus@8230
   276
                            case kHIDUsage_GD_Y:
icculus@8230
   277
                            case kHIDUsage_GD_Z:
icculus@8230
   278
                            case kHIDUsage_GD_Rx:
icculus@8230
   279
                            case kHIDUsage_GD_Ry:
icculus@8230
   280
                            case kHIDUsage_GD_Rz:
icculus@8230
   281
                            case kHIDUsage_GD_Slider:
icculus@8230
   282
                            case kHIDUsage_GD_Dial:
icculus@8230
   283
                            case kHIDUsage_GD_Wheel:
icculus@8242
   284
                                if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
icculus@8242
   285
                                    element = (recElement *) SDL_calloc(1, sizeof (recElement));
icculus@8242
   286
                                    if (element) {
icculus@8242
   287
                                        pDevice->axes++;
icculus@8242
   288
                                        headElement = &(pDevice->firstAxis);
icculus@8242
   289
                                    }
icculus@8230
   290
                                }
icculus@8230
   291
                                break;
slouken@858
   292
icculus@8230
   293
                            case kHIDUsage_GD_Hatswitch:
icculus@8242
   294
                                if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) {
icculus@8242
   295
                                    element = (recElement *) SDL_calloc(1, sizeof (recElement));
icculus@8242
   296
                                    if (element) {
icculus@8242
   297
                                        pDevice->hats++;
icculus@8242
   298
                                        headElement = &(pDevice->firstHat);
icculus@8242
   299
                                    }
icculus@8230
   300
                                }
icculus@8230
   301
                                break;
slime73@9912
   302
                            case kHIDUsage_GD_DPadUp:
slime73@9912
   303
                            case kHIDUsage_GD_DPadDown:
slime73@9912
   304
                            case kHIDUsage_GD_DPadRight:
slime73@9912
   305
                            case kHIDUsage_GD_DPadLeft:
slime73@9976
   306
                            case kHIDUsage_GD_Start:
slime73@9976
   307
                            case kHIDUsage_GD_Select:
slouken@10218
   308
                            case kHIDUsage_GD_SystemMainMenu:
slime73@9912
   309
                                if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
slime73@9912
   310
                                    element = (recElement *) SDL_calloc(1, sizeof (recElement));
slime73@9912
   311
                                    if (element) {
slime73@9912
   312
                                        pDevice->buttons++;
slime73@9912
   313
                                        headElement = &(pDevice->firstButton);
slime73@9912
   314
                                    }
slime73@9912
   315
                                }
slime73@9912
   316
                                break;
icculus@8230
   317
                        }
icculus@8230
   318
                        break;
slouken@6690
   319
icculus@8230
   320
                    case kHIDPage_Simulation:
icculus@8230
   321
                        switch (usage) {
icculus@8230
   322
                            case kHIDUsage_Sim_Rudder:
icculus@8230
   323
                            case kHIDUsage_Sim_Throttle:
mistydemeo@10853
   324
                            case kHIDUsage_Sim_Accelerator:
mistydemeo@10853
   325
                            case kHIDUsage_Sim_Brake:
icculus@8242
   326
                                if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
icculus@8242
   327
                                    element = (recElement *) SDL_calloc(1, sizeof (recElement));
icculus@8242
   328
                                    if (element) {
icculus@8242
   329
                                        pDevice->axes++;
icculus@8242
   330
                                        headElement = &(pDevice->firstAxis);
icculus@8242
   331
                                    }
icculus@8230
   332
                                }
icculus@8230
   333
                                break;
slouken@7191
   334
icculus@8230
   335
                            default:
icculus@8230
   336
                                break;
icculus@8230
   337
                        }
icculus@8230
   338
                        break;
slouken@7191
   339
icculus@8230
   340
                    case kHIDPage_Button:
slime73@9912
   341
                    case kHIDPage_Consumer: /* e.g. 'pause' button on Steelseries MFi gamepads. */
icculus@8242
   342
                        if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
icculus@8242
   343
                            element = (recElement *) SDL_calloc(1, sizeof (recElement));
icculus@8242
   344
                            if (element) {
icculus@8242
   345
                                pDevice->buttons++;
icculus@8242
   346
                                headElement = &(pDevice->firstButton);
icculus@8242
   347
                            }
icculus@8230
   348
                        }
icculus@8230
   349
                        break;
slouken@7191
   350
icculus@8230
   351
                    default:
icculus@8230
   352
                        break;
icculus@8230
   353
                }
slouken@7191
   354
            }
icculus@8230
   355
            break;
icculus@8230
   356
icculus@8230
   357
            case kIOHIDElementTypeCollection: {
icculus@8230
   358
                CFArrayRef array = IOHIDElementGetChildren(refElement);
icculus@8230
   359
                if (array) {
icculus@8230
   360
                    AddHIDElements(array, pDevice);
icculus@8230
   361
                }
icculus@8230
   362
            }
icculus@8230
   363
            break;
icculus@8230
   364
icculus@8230
   365
            default:
icculus@8230
   366
                break;
slouken@7191
   367
        }
slouken@1895
   368
icculus@8230
   369
        if (element && headElement) {       /* add to list */
icculus@8230
   370
            recElement *elementPrevious = NULL;
icculus@8230
   371
            recElement *elementCurrent = *headElement;
icculus@8230
   372
            while (elementCurrent && usage >= elementCurrent->usage) {
icculus@8230
   373
                elementPrevious = elementCurrent;
icculus@8230
   374
                elementCurrent = elementCurrent->pNext;
slouken@1895
   375
            }
icculus@8230
   376
            if (elementPrevious) {
icculus@8230
   377
                elementPrevious->pNext = element;
icculus@8230
   378
            } else {
icculus@8230
   379
                *headElement = element;
slouken@1895
   380
            }
slouken@1895
   381
icculus@8230
   382
            element->elementRef = refElement;
icculus@8230
   383
            element->usagePage = usagePage;
icculus@8230
   384
            element->usage = usage;
icculus@8230
   385
            element->pNext = elementCurrent;
slouken@7026
   386
icculus@8230
   387
            element->minReport = element->min = (SInt32) IOHIDElementGetLogicalMin(refElement);
icculus@8230
   388
            element->maxReport = element->max = (SInt32) IOHIDElementGetLogicalMax(refElement);
icculus@8242
   389
            element->cookie = IOHIDElementGetCookie(refElement);
slouken@1895
   390
icculus@8230
   391
            pDevice->elements++;
slouken@7026
   392
        }
slouken@1895
   393
    }
slouken@172
   394
}
slouken@172
   395
icculus@8230
   396
static SDL_bool
icculus@8230
   397
GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
icculus@8230
   398
{
slouken@10595
   399
    Sint32 vendor = 0;
slouken@10595
   400
    Sint32 product = 0;
slouken@10595
   401
    Sint32 version = 0;
icculus@8230
   402
    CFTypeRef refCF = NULL;
icculus@8230
   403
    CFArrayRef array = NULL;
slouken@10598
   404
    Uint16 *guid16 = (Uint16 *)pDevice->guid.data;
slouken@172
   405
icculus@8230
   406
    /* get usage page and usage */
icculus@8230
   407
    refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
icculus@8230
   408
    if (refCF) {
icculus@8230
   409
        CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage);
slouken@1895
   410
    }
icculus@8230
   411
    if (pDevice->usagePage != kHIDPage_GenericDesktop) {
icculus@8230
   412
        return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */
icculus@8230
   413
    }
icculus@8230
   414
icculus@8230
   415
    refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey));
icculus@8230
   416
    if (refCF) {
icculus@8230
   417
        CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage);
icculus@8230
   418
    }
icculus@8230
   419
icculus@8230
   420
    if ((pDevice->usage != kHIDUsage_GD_Joystick &&
icculus@8230
   421
         pDevice->usage != kHIDUsage_GD_GamePad &&
icculus@8230
   422
         pDevice->usage != kHIDUsage_GD_MultiAxisController)) {
icculus@8230
   423
        return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */
icculus@8230
   424
    }
icculus@8230
   425
icculus@8230
   426
    pDevice->deviceRef = hidDevice;
icculus@8230
   427
icculus@8230
   428
    /* get device name */
icculus@8230
   429
    refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey));
icculus@8230
   430
    if (!refCF) {
icculus@8230
   431
        /* Maybe we can't get "AwesomeJoystick2000", but we can get "Logitech"? */
icculus@8230
   432
        refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey));
icculus@8230
   433
    }
icculus@8230
   434
    if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) {
icculus@8230
   435
        SDL_strlcpy(pDevice->product, "Unidentified joystick", sizeof (pDevice->product));
icculus@8230
   436
    }
icculus@8230
   437
icculus@8230
   438
    refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
icculus@8230
   439
    if (refCF) {
slouken@10595
   440
        CFNumberGetValue(refCF, kCFNumberSInt32Type, &vendor);
icculus@8230
   441
    }
icculus@8230
   442
icculus@8230
   443
    refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey));
icculus@8230
   444
    if (refCF) {
slouken@10595
   445
        CFNumberGetValue(refCF, kCFNumberSInt32Type, &product);
icculus@8230
   446
    }
icculus@8230
   447
slouken@10595
   448
    refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVersionNumberKey));
slouken@10595
   449
    if (refCF) {
slouken@10595
   450
        CFNumberGetValue(refCF, kCFNumberSInt32Type, &version);
slouken@10595
   451
    }
slouken@10595
   452
slouken@12088
   453
#ifdef SDL_JOYSTICK_HIDAPI
slouken@12115
   454
    if (HIDAPI_IsDevicePresent(vendor, product, version)) {
slouken@12088
   455
        /* The HIDAPI driver is taking care of this device */
slouken@12088
   456
        return 0;
slouken@12088
   457
    }
slouken@12088
   458
#endif
slouken@12088
   459
slouken@10598
   460
    SDL_memset(pDevice->guid.data, 0, sizeof(pDevice->guid.data));
slouken@10595
   461
slouken@10595
   462
    if (vendor && product) {
slouken@12088
   463
        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
slouken@10595
   464
        *guid16++ = 0;
slouken@10595
   465
        *guid16++ = SDL_SwapLE16((Uint16)vendor);
slouken@10595
   466
        *guid16++ = 0;
slouken@10595
   467
        *guid16++ = SDL_SwapLE16((Uint16)product);
slouken@10595
   468
        *guid16++ = 0;
slouken@10595
   469
        *guid16++ = SDL_SwapLE16((Uint16)version);
slouken@10595
   470
        *guid16++ = 0;
slouken@10595
   471
    } else {
slouken@12088
   472
        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
icculus@8230
   473
        *guid16++ = 0;
icculus@8230
   474
        SDL_strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4);
icculus@8230
   475
    }
icculus@8230
   476
icculus@8230
   477
    array = IOHIDDeviceCopyMatchingElements(hidDevice, NULL, kIOHIDOptionsTypeNone);
icculus@8230
   478
    if (array) {
icculus@8230
   479
        AddHIDElements(array, pDevice);
icculus@8230
   480
        CFRelease(array);
icculus@8230
   481
    }
icculus@8230
   482
icculus@8230
   483
    return SDL_TRUE;
slouken@172
   484
}
slouken@172
   485
icculus@9120
   486
static SDL_bool
icculus@9120
   487
JoystickAlreadyKnown(IOHIDDeviceRef ioHIDDeviceObject)
icculus@9120
   488
{
icculus@9120
   489
    recDevice *i;
icculus@9120
   490
    for (i = gpDeviceList; i != NULL; i = i->pNext) {
icculus@9120
   491
        if (i->deviceRef == ioHIDDeviceObject) {
icculus@9120
   492
            return SDL_TRUE;
icculus@9120
   493
        }
icculus@9120
   494
    }
icculus@9120
   495
    return SDL_FALSE;
icculus@9120
   496
}
icculus@9120
   497
slouken@172
   498
slouken@1895
   499
static void
icculus@8230
   500
JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject)
slouken@172
   501
{
icculus@9120
   502
    recDevice *device;
slouken@9629
   503
    int device_index = 0;
slime73@10176
   504
    io_service_t ioservice;
icculus@9120
   505
icculus@8230
   506
    if (res != kIOReturnSuccess) {
icculus@8230
   507
        return;
slouken@1895
   508
    }
slouken@172
   509
icculus@9120
   510
    if (JoystickAlreadyKnown(ioHIDDeviceObject)) {
icculus@9120
   511
        return;  /* IOKit sent us a duplicate. */
icculus@9120
   512
    }
icculus@9120
   513
icculus@9120
   514
    device = (recDevice *) SDL_calloc(1, sizeof(recDevice));
icculus@8230
   515
    if (!device) {
icculus@8230
   516
        SDL_OutOfMemory();
icculus@8230
   517
        return;
icculus@8230
   518
    }
slouken@1895
   519
icculus@8230
   520
    if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
icculus@8230
   521
        SDL_free(device);
icculus@8230
   522
        return;   /* not a device we care about, probably. */
icculus@8230
   523
    }
slouken@2713
   524
slouken@12088
   525
    if (SDL_ShouldIgnoreJoystick(device->product, device->guid)) {
slouken@11201
   526
        SDL_free(device);
slouken@11201
   527
        return;
slouken@11201
   528
    }
slouken@11201
   529
icculus@8230
   530
    /* Get notified when this device is disconnected. */
icculus@8230
   531
    IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
icculus@8739
   532
    IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
slouken@7191
   533
slouken@7791
   534
    /* Allocate an instance ID for this device */
slouken@12088
   535
    device->instance_id = SDL_GetNextJoystickInstanceID();
slouken@7791
   536
icculus@8230
   537
    /* We have to do some storage of the io_service_t for SDL_HapticOpenFromJoystick */
slime73@10176
   538
    ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
slime73@10176
   539
    if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
slime73@10176
   540
        device->ffservice = ioservice;
slouken@12088
   541
#if SDL_HAPTIC_IOKIT
slime73@10176
   542
        MacHaptic_MaybeAddDevice(ioservice);
slouken@12088
   543
#endif
slouken@7191
   544
    }
slouken@7191
   545
slouken@7191
   546
    /* Add device to the end of the list */
slouken@8986
   547
    if ( !gpDeviceList ) {
slouken@7191
   548
        gpDeviceList = device;
slouken@8986
   549
    } else {
slouken@7191
   550
        recDevice *curdevice;
slouken@7191
   551
slouken@7191
   552
        curdevice = gpDeviceList;
slouken@8986
   553
        while ( curdevice->pNext ) {
slouken@9629
   554
            ++device_index;
slouken@7191
   555
            curdevice = curdevice->pNext;
slouken@7191
   556
        }
slouken@7191
   557
        curdevice->pNext = device;
icculus@9862
   558
        ++device_index;  /* bump by one since we counted by pNext. */
slouken@7191
   559
    }
slouken@9629
   560
slouken@12088
   561
    SDL_PrivateJoystickAdded(device->instance_id);
slouken@6690
   562
}
slouken@6690
   563
icculus@8230
   564
static SDL_bool
icculus@8230
   565
ConfigHIDManager(CFArrayRef matchingArray)
icculus@8230
   566
{
icculus@8230
   567
    CFRunLoopRef runloop = CFRunLoopGetCurrent();
slouken@6690
   568
icculus@8230
   569
    if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
icculus@8230
   570
        return SDL_FALSE;
icculus@8230
   571
    }
icculus@8230
   572
icculus@9370
   573
    IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
icculus@8230
   574
    IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback, NULL);
icculus@8739
   575
    IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
icculus@8230
   576
icculus@8739
   577
    while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
icculus@8230
   578
        /* no-op. Callback fires once per existing device. */
icculus@8230
   579
    }
icculus@8230
   580
icculus@8739
   581
    /* future hotplug events will come through SDL_JOYSTICK_RUNLOOP_MODE now. */
icculus@8230
   582
icculus@8230
   583
    return SDL_TRUE;  /* good to go. */
icculus@8230
   584
}
icculus@8230
   585
icculus@8230
   586
icculus@8230
   587
static CFDictionaryRef
icculus@8230
   588
CreateHIDDeviceMatchDictionary(const UInt32 page, const UInt32 usage, int *okay)
slouken@6690
   589
{
icculus@8230
   590
    CFDictionaryRef retval = NULL;
icculus@8230
   591
    CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
icculus@8230
   592
    CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
icculus@8230
   593
    const void *keys[2] = { (void *) CFSTR(kIOHIDDeviceUsagePageKey), (void *) CFSTR(kIOHIDDeviceUsageKey) };
icculus@8230
   594
    const void *vals[2] = { (void *) pageNumRef, (void *) usageNumRef };
slouken@7191
   595
icculus@8230
   596
    if (pageNumRef && usageNumRef) {
icculus@8230
   597
        retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
icculus@8230
   598
    }
icculus@8230
   599
icculus@8230
   600
    if (pageNumRef) {
icculus@8230
   601
        CFRelease(pageNumRef);
icculus@8230
   602
    }
icculus@8230
   603
    if (usageNumRef) {
icculus@8245
   604
        CFRelease(usageNumRef);
icculus@8230
   605
    }
icculus@8230
   606
icculus@8230
   607
    if (!retval) {
icculus@8230
   608
        *okay = 0;
icculus@8230
   609
    }
icculus@8230
   610
icculus@8230
   611
    return retval;
icculus@8230
   612
}
icculus@8230
   613
icculus@8230
   614
static SDL_bool
icculus@8230
   615
CreateHIDManager(void)
icculus@8230
   616
{
icculus@8230
   617
    SDL_bool retval = SDL_FALSE;
icculus@8230
   618
    int okay = 1;
icculus@8230
   619
    const void *vals[] = {
icculus@8230
   620
        (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
icculus@8230
   621
        (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
icculus@8230
   622
        (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
icculus@8230
   623
    };
icculus@8230
   624
    const size_t numElements = SDL_arraysize(vals);
icculus@8230
   625
    CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) : NULL;
icculus@8230
   626
    size_t i;
icculus@8230
   627
icculus@8230
   628
    for (i = 0; i < numElements; i++) {
icculus@8230
   629
        if (vals[i]) {
icculus@8230
   630
            CFRelease((CFTypeRef) vals[i]);
slouken@7191
   631
        }
slouken@7191
   632
    }
icculus@8230
   633
icculus@8230
   634
    if (array) {
icculus@8230
   635
        hidman = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
icculus@8230
   636
        if (hidman != NULL) {
icculus@8230
   637
            retval = ConfigHIDManager(array);
icculus@8230
   638
        }
icculus@8230
   639
        CFRelease(array);
icculus@8230
   640
    }
icculus@8230
   641
icculus@8230
   642
    return retval;
slouken@6690
   643
}
slouken@7191
   644
slouken@6690
   645
slouken@12088
   646
static int
slouken@12088
   647
DARWIN_JoystickInit(void)
slouken@172
   648
{
slouken@1895
   649
    if (gpDeviceList) {
icculus@7037
   650
        return SDL_SetError("Joystick: Device list already inited.");
slouken@1895
   651
    }
slouken@172
   652
icculus@8230
   653
    if (!CreateHIDManager()) {
icculus@8230
   654
        return SDL_SetError("Joystick: Couldn't initialize HID Manager");
slouken@1895
   655
    }
slouken@172
   656
slouken@12088
   657
    return 0;
slouken@172
   658
}
slouken@172
   659
slouken@12088
   660
static int
slouken@12088
   661
DARWIN_JoystickGetCount(void)
slouken@6707
   662
{
slouken@7191
   663
    recDevice *device = gpDeviceList;
slouken@6707
   664
    int nJoySticks = 0;
slouken@7191
   665
icculus@8230
   666
    while (device) {
icculus@8230
   667
        if (!device->removed) {
slouken@7191
   668
            nJoySticks++;
icculus@8230
   669
        }
slouken@6707
   670
        device = device->pNext;
slouken@7191
   671
    }
slouken@6707
   672
slouken@7191
   673
    return nJoySticks;
slouken@6707
   674
}
slouken@6707
   675
slouken@12088
   676
static void
slouken@12088
   677
DARWIN_JoystickDetect(void)
slouken@6707
   678
{
slouken@9629
   679
    recDevice *device = gpDeviceList;
slouken@9629
   680
    while (device) {
slouken@9629
   681
        if (device->removed) {
slouken@9629
   682
            device = FreeDevice(device);
slouken@9629
   683
        } else {
slouken@9629
   684
            device = device->pNext;
slouken@7191
   685
        }
slouken@7191
   686
    }
alfred@9375
   687
slouken@12088
   688
    /* run this after the checks above so we don't set device->removed and delete the device before
slouken@12088
   689
       DARWIN_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device */
slouken@12088
   690
    while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
slouken@12088
   691
        /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
slouken@12088
   692
    }
slouken@6707
   693
}
slouken@6707
   694
slouken@172
   695
/* Function to get the device-dependent name of a joystick */
slouken@1895
   696
const char *
slouken@12088
   697
DARWIN_JoystickGetDeviceName(int device_index)
slouken@172
   698
{
slouken@9629
   699
    recDevice *device = GetDeviceForIndex(device_index);
slouken@9629
   700
    return device ? device->product : "UNKNOWN";
slouken@172
   701
}
slouken@172
   702
slouken@12359
   703
static int
slouken@12359
   704
DARWIN_JoystickGetDevicePlayerIndex(int device_index)
slouken@12359
   705
{
slouken@12359
   706
    return -1;
slouken@12359
   707
}
slouken@12359
   708
slouken@12088
   709
static SDL_JoystickGUID
slouken@12088
   710
DARWIN_JoystickGetDeviceGUID( int device_index )
slouken@12088
   711
{
slouken@12088
   712
    recDevice *device = GetDeviceForIndex(device_index);
slouken@12088
   713
    SDL_JoystickGUID guid;
slouken@12088
   714
    if (device) {
slouken@12088
   715
        guid = device->guid;
slouken@12088
   716
    } else {
slouken@12088
   717
        SDL_zero(guid);
slouken@12088
   718
    }
slouken@12088
   719
    return guid;
slouken@12088
   720
}
slouken@12088
   721
slouken@12088
   722
static SDL_JoystickID
slouken@12088
   723
DARWIN_JoystickGetDeviceInstanceID(int device_index)
slouken@6707
   724
{
slouken@9629
   725
    recDevice *device = GetDeviceForIndex(device_index);
slouken@9629
   726
    return device ? device->instance_id : 0;
slouken@6707
   727
}
slouken@6707
   728
slouken@12088
   729
static int
slouken@12088
   730
DARWIN_JoystickOpen(SDL_Joystick * joystick, int device_index)
slouken@172
   731
{
slouken@9629
   732
    recDevice *device = GetDeviceForIndex(device_index);
slouken@172
   733
slouken@7191
   734
    joystick->instance_id = device->instance_id;
slouken@1895
   735
    joystick->hwdata = device;
slouken@7191
   736
    joystick->name = device->product;
slouken@172
   737
slouken@7191
   738
    joystick->naxes = device->axes;
slouken@7191
   739
    joystick->nhats = device->hats;
slouken@7191
   740
    joystick->nballs = 0;
slouken@7191
   741
    joystick->nbuttons = device->buttons;
slouken@1895
   742
    return 0;
slouken@172
   743
}
slouken@172
   744
slouken@12088
   745
/*
slouken@12088
   746
 * Like strerror but for force feedback errors.
slouken@172
   747
 */
slouken@12088
   748
static const char *
slouken@12088
   749
FFStrError(unsigned int err)
slouken@12088
   750
{
slouken@12088
   751
    switch (err) {
slouken@12088
   752
    case FFERR_DEVICEFULL:
slouken@12088
   753
        return "device full";
slouken@12088
   754
    /* This should be valid, but for some reason isn't defined... */
slouken@12088
   755
    /* case FFERR_DEVICENOTREG:
slouken@12088
   756
        return "device not registered"; */
slouken@12088
   757
    case FFERR_DEVICEPAUSED:
slouken@12088
   758
        return "device paused";
slouken@12088
   759
    case FFERR_DEVICERELEASED:
slouken@12088
   760
        return "device released";
slouken@12088
   761
    case FFERR_EFFECTPLAYING:
slouken@12088
   762
        return "effect playing";
slouken@12088
   763
    case FFERR_EFFECTTYPEMISMATCH:
slouken@12088
   764
        return "effect type mismatch";
slouken@12088
   765
    case FFERR_EFFECTTYPENOTSUPPORTED:
slouken@12088
   766
        return "effect type not supported";
slouken@12088
   767
    case FFERR_GENERIC:
slouken@12088
   768
        return "undetermined error";
slouken@12088
   769
    case FFERR_HASEFFECTS:
slouken@12088
   770
        return "device has effects";
slouken@12088
   771
    case FFERR_INCOMPLETEEFFECT:
slouken@12088
   772
        return "incomplete effect";
slouken@12088
   773
    case FFERR_INTERNAL:
slouken@12088
   774
        return "internal fault";
slouken@12088
   775
    case FFERR_INVALIDDOWNLOADID:
slouken@12088
   776
        return "invalid download id";
slouken@12088
   777
    case FFERR_INVALIDPARAM:
slouken@12088
   778
        return "invalid parameter";
slouken@12088
   779
    case FFERR_MOREDATA:
slouken@12088
   780
        return "more data";
slouken@12088
   781
    case FFERR_NOINTERFACE:
slouken@12088
   782
        return "interface not supported";
slouken@12088
   783
    case FFERR_NOTDOWNLOADED:
slouken@12088
   784
        return "effect is not downloaded";
slouken@12088
   785
    case FFERR_NOTINITIALIZED:
slouken@12088
   786
        return "object has not been initialized";
slouken@12088
   787
    case FFERR_OUTOFMEMORY:
slouken@12088
   788
        return "out of memory";
slouken@12088
   789
    case FFERR_UNPLUGGED:
slouken@12088
   790
        return "device is unplugged";
slouken@12088
   791
    case FFERR_UNSUPPORTED:
slouken@12088
   792
        return "function call unsupported";
slouken@12088
   793
    case FFERR_UNSUPPORTEDAXIS:
slouken@12088
   794
        return "axis unsupported";
slouken@12088
   795
slouken@12088
   796
    default:
slouken@12088
   797
        return "unknown error";
slouken@12088
   798
    }
slouken@12088
   799
}
slouken@12088
   800
slouken@12088
   801
static int
slouken@12088
   802
DARWIN_JoystickInitRumble(recDevice *device, Sint16 magnitude, Uint32 duration_ms)
slouken@12088
   803
{
slouken@12088
   804
    HRESULT result;
slouken@12088
   805
slouken@12088
   806
    if (!device->ffdevice) {
slouken@12088
   807
        result = FFCreateDevice(device->ffservice, &device->ffdevice);
slouken@12088
   808
        if (result != FF_OK) {
slouken@12088
   809
            return SDL_SetError("Unable to create force feedback device from service: %s", FFStrError(result));
slouken@12088
   810
        }
slouken@12088
   811
    }
slouken@12088
   812
slouken@12088
   813
    /* Reset and then enable actuators */
slouken@12088
   814
    result = FFDeviceSendForceFeedbackCommand(device->ffdevice, FFSFFC_RESET);
slouken@12088
   815
    if (result != FF_OK) {
slouken@12088
   816
        return SDL_SetError("Unable to reset force feedback device: %s", FFStrError(result));
slouken@12088
   817
    }
slouken@12088
   818
slouken@12088
   819
    result = FFDeviceSendForceFeedbackCommand(device->ffdevice, FFSFFC_SETACTUATORSON);
slouken@12088
   820
    if (result != FF_OK) {
slouken@12088
   821
        return SDL_SetError("Unable to enable force feedback actuators: %s", FFStrError(result));
slouken@12088
   822
    }
slouken@12088
   823
slouken@12088
   824
    /* Create the effect */
slouken@12088
   825
    device->ffeffect = CreateRumbleEffectData(magnitude, duration_ms);
slouken@12088
   826
    if (!device->ffeffect) {
slouken@12088
   827
        return SDL_OutOfMemory();
slouken@12088
   828
    }
slouken@12088
   829
slouken@12088
   830
    result = FFDeviceCreateEffect(device->ffdevice, kFFEffectType_Sine_ID,
slouken@12088
   831
                               device->ffeffect, &device->ffeffect_ref);
slouken@12088
   832
    if (result != FF_OK) {
slouken@12088
   833
        return SDL_SetError("Haptic: Unable to create effect: %s", FFStrError(result));
slouken@12088
   834
    }
slouken@12088
   835
    return 0;
slouken@12088
   836
}
slouken@12088
   837
slouken@12088
   838
static int
slouken@12088
   839
DARWIN_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
slouken@12088
   840
{
slouken@12088
   841
    HRESULT result;
slouken@12088
   842
    recDevice *device = joystick->hwdata;
slouken@12088
   843
slouken@12088
   844
    /* Scale and average the two rumble strengths */
slouken@12088
   845
    Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
slouken@12088
   846
slouken@12088
   847
    if (!device->ffservice) {
slouken@12088
   848
        return SDL_Unsupported();
slouken@12088
   849
    }
slouken@12088
   850
slouken@12088
   851
    if (device->ff_initialized) {
slouken@12088
   852
        FFPERIODIC *periodic = ((FFPERIODIC *)device->ffeffect->lpvTypeSpecificParams);
slouken@12088
   853
        device->ffeffect->dwDuration = duration_ms * 1000; /* In microseconds. */
slouken@12088
   854
        periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
slouken@12088
   855
slouken@12088
   856
        result = FFEffectSetParameters(device->ffeffect_ref, device->ffeffect,
slouken@12088
   857
                                    (FFEP_DURATION | FFEP_TYPESPECIFICPARAMS));
slouken@12088
   858
        if (result != FF_OK) {
slouken@12088
   859
            return SDL_SetError("Unable to update rumble effect: %s", FFStrError(result));
slouken@12088
   860
        }
slouken@12088
   861
    } else {
slouken@12088
   862
        if (DARWIN_JoystickInitRumble(device, magnitude, duration_ms) < 0) {
slouken@12088
   863
            return -1;
slouken@12088
   864
        }
slouken@12088
   865
        device->ff_initialized = SDL_TRUE;
slouken@12088
   866
    }
slouken@12088
   867
slouken@12088
   868
    result = FFEffectStart(device->ffeffect_ref, 1, 0);
slouken@12088
   869
    if (result != FF_OK) {
slouken@12088
   870
        return SDL_SetError("Unable to run the rumble effect: %s", FFStrError(result));
slouken@12088
   871
    }
slouken@12088
   872
    return 0;
slouken@12088
   873
}
slouken@12088
   874
slouken@12088
   875
static void
slouken@12088
   876
DARWIN_JoystickUpdate(SDL_Joystick * joystick)
slouken@172
   877
{
slouken@7191
   878
    recDevice *device = joystick->hwdata;
slouken@1895
   879
    recElement *element;
slouken@2287
   880
    SInt32 value, range;
slouken@1895
   881
    int i;
slouken@858
   882
icculus@8230
   883
    if (!device) {
slouken@7191
   884
        return;
icculus@8230
   885
    }
slouken@6690
   886
slouken@1895
   887
    if (device->removed) {      /* device was unplugged; ignore it. */
icculus@9433
   888
        if (joystick->hwdata) {
icculus@9433
   889
            joystick->force_recentering = SDL_TRUE;
icculus@9433
   890
            joystick->hwdata = NULL;
icculus@9433
   891
        }
slouken@1895
   892
        return;
slouken@1895
   893
    }
slouken@858
   894
slouken@1895
   895
    element = device->firstAxis;
slouken@1895
   896
    i = 0;
slouken@11843
   897
slouken@11843
   898
    int goodRead = SDL_FALSE;
slouken@1895
   899
    while (element) {
slouken@11843
   900
        goodRead = GetHIDScaledCalibratedState(device, element, -32768, 32767, &value);
slouken@11843
   901
        if (goodRead) {
slouken@11843
   902
            SDL_PrivateJoystickAxis(joystick, i, value);
slouken@11843
   903
        }
slouken@11843
   904
slouken@1895
   905
        element = element->pNext;
slouken@1895
   906
        ++i;
slouken@1895
   907
    }
slouken@1895
   908
slouken@1895
   909
    element = device->firstButton;
slouken@1895
   910
    i = 0;
slouken@1895
   911
    while (element) {
slouken@11843
   912
        goodRead = GetHIDElementState(device, element, &value);
slouken@11843
   913
        if (goodRead) {
slouken@11843
   914
            if (value > 1) {          /* handle pressure-sensitive buttons */
slouken@11843
   915
                value = 1;
slouken@11843
   916
            }
slouken@11843
   917
            SDL_PrivateJoystickButton(joystick, i, value);
icculus@8230
   918
        }
slouken@11843
   919
slouken@1895
   920
        element = element->pNext;
slouken@1895
   921
        ++i;
slouken@1895
   922
    }
slouken@172
   923
slouken@1895
   924
    element = device->firstHat;
slouken@1895
   925
    i = 0;
slouken@11843
   926
    
slouken@1895
   927
    while (element) {
slouken@1895
   928
        Uint8 pos = 0;
slouken@1895
   929
slouken@2287
   930
        range = (element->max - element->min + 1);
slouken@11843
   931
        goodRead = GetHIDElementState(device, element, &value);
slouken@11843
   932
        if (goodRead) {
slouken@11843
   933
            value -= element->min;
slouken@11843
   934
            if (range == 4) {         /* 4 position hatswitch - scale up value */
slouken@11843
   935
                value *= 2;
slouken@11843
   936
            } else if (range != 8) {    /* Neither a 4 nor 8 positions - fall back to default position (centered) */
slouken@11843
   937
                value = -1;
slouken@11843
   938
            }
slouken@11843
   939
            switch (value) {
slouken@11843
   940
            case 0:
slouken@11843
   941
                pos = SDL_HAT_UP;
slouken@11843
   942
                break;
slouken@11843
   943
            case 1:
slouken@11843
   944
                pos = SDL_HAT_RIGHTUP;
slouken@11843
   945
                break;
slouken@11843
   946
            case 2:
slouken@11843
   947
                pos = SDL_HAT_RIGHT;
slouken@11843
   948
                break;
slouken@11843
   949
            case 3:
slouken@11843
   950
                pos = SDL_HAT_RIGHTDOWN;
slouken@11843
   951
                break;
slouken@11843
   952
            case 4:
slouken@11843
   953
                pos = SDL_HAT_DOWN;
slouken@11843
   954
                break;
slouken@11843
   955
            case 5:
slouken@11843
   956
                pos = SDL_HAT_LEFTDOWN;
slouken@11843
   957
                break;
slouken@11843
   958
            case 6:
slouken@11843
   959
                pos = SDL_HAT_LEFT;
slouken@11843
   960
                break;
slouken@11843
   961
            case 7:
slouken@11843
   962
                pos = SDL_HAT_LEFTUP;
slouken@11843
   963
                break;
slouken@11843
   964
            default:
slouken@11843
   965
                /* Every other value is mapped to center. We do that because some
slouken@11843
   966
                 * joysticks use 8 and some 15 for this value, and apparently
slouken@11843
   967
                 * there are even more variants out there - so we try to be generous.
slouken@11843
   968
                 */
slouken@11843
   969
                pos = SDL_HAT_CENTERED;
slouken@11843
   970
                break;
slouken@11843
   971
            }
slouken@11843
   972
slouken@11843
   973
            SDL_PrivateJoystickHat(joystick, i, pos);
icculus@8230
   974
        }
slouken@11843
   975
        
slouken@1895
   976
        element = element->pNext;
slouken@1895
   977
        ++i;
slouken@1895
   978
    }
slouken@172
   979
}
slouken@172
   980
slouken@12088
   981
static void
slouken@12088
   982
DARWIN_JoystickClose(SDL_Joystick * joystick)
slouken@7191
   983
{
slouken@172
   984
}
slouken@172
   985
slouken@12088
   986
static void
slouken@12088
   987
DARWIN_JoystickQuit(void)
slouken@172
   988
{
icculus@8230
   989
    while (FreeDevice(gpDeviceList)) {
icculus@8230
   990
        /* spin */
icculus@8230
   991
    }
slouken@7191
   992
icculus@8230
   993
    if (hidman) {
icculus@8739
   994
        IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
icculus@8230
   995
        IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
icculus@8230
   996
        CFRelease(hidman);
icculus@8230
   997
        hidman = NULL;
slouken@7191
   998
    }
slouken@6690
   999
}
slouken@6690
  1000
slouken@12088
  1001
SDL_JoystickDriver SDL_DARWIN_JoystickDriver =
slouken@6690
  1002
{
slouken@12088
  1003
    DARWIN_JoystickInit,
slouken@12088
  1004
    DARWIN_JoystickGetCount,
slouken@12088
  1005
    DARWIN_JoystickDetect,
slouken@12088
  1006
    DARWIN_JoystickGetDeviceName,
slouken@12359
  1007
    DARWIN_JoystickGetDevicePlayerIndex,
slouken@12088
  1008
    DARWIN_JoystickGetDeviceGUID,
slouken@12088
  1009
    DARWIN_JoystickGetDeviceInstanceID,
slouken@12088
  1010
    DARWIN_JoystickOpen,
slouken@12088
  1011
    DARWIN_JoystickRumble,
slouken@12088
  1012
    DARWIN_JoystickUpdate,
slouken@12088
  1013
    DARWIN_JoystickClose,
slouken@12088
  1014
    DARWIN_JoystickQuit,
slouken@12088
  1015
};
slouken@1635
  1016
slouken@1635
  1017
#endif /* SDL_JOYSTICK_IOKIT */
slouken@7026
  1018
slouken@1895
  1019
/* vi: set ts=4 sw=4 expandtab: */