src/joystick/darwin/SDL_sysjoystick.c
author David Ludwig <dludwig@pobox.com>
Tue, 17 Mar 2020 02:31:47 -0400
changeset 13642 f4ae4b91cf38
parent 13638 ca87b62e4e17
child 13646 784ce9766fb9
permissions -rw-r--r--
Backout prior fix for Bug 5034, which needs more research

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