cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_sysjoystick.c (26307B)


      1/*
      2  Simple DirectMedia Layer
      3  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
      4
      5  This software is provided 'as-is', without any express or implied
      6  warranty.  In no event will the authors be held liable for any damages
      7  arising from the use of this software.
      8
      9  Permission is granted to anyone to use this software for any purpose,
     10  including commercial applications, and to alter it and redistribute it
     11  freely, subject to the following restrictions:
     12
     13  1. The origin of this software must not be misrepresented; you must not
     14     claim that you wrote the original software. If you use this software
     15     in a product, an acknowledgment in the product documentation would be
     16     appreciated but is not required.
     17  2. Altered source versions must be plainly marked as such, and must not be
     18     misrepresented as being the original software.
     19  3. This notice may not be removed or altered from any source distribution.
     20*/
     21#include "../../SDL_internal.h"
     22
     23#ifdef SDL_JOYSTICK_IOKIT
     24
     25#include <IOKit/hid/IOHIDLib.h>
     26
     27/* For force feedback testing. */
     28#include <ForceFeedback/ForceFeedback.h>
     29#include <ForceFeedback/ForceFeedbackConstants.h>
     30
     31#include "SDL_joystick.h"
     32#include "../SDL_sysjoystick.h"
     33#include "../SDL_joystick_c.h"
     34#include "SDL_sysjoystick_c.h"
     35#include "SDL_events.h"
     36#include "../../haptic/darwin/SDL_syshaptic_c.h"    /* For haptic hot plugging */
     37#if !SDL_EVENTS_DISABLED
     38#include "../../events/SDL_events_c.h"
     39#endif
     40
     41#define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick")
     42
     43/* The base object of the HID Manager API */
     44static IOHIDManagerRef hidman = NULL;
     45
     46/* Linked list of all available devices */
     47static recDevice *gpDeviceList = NULL;
     48
     49/* if SDL_TRUE then a device was added since the last update call */
     50static SDL_bool s_bDeviceAdded = SDL_FALSE;
     51static SDL_bool s_bDeviceRemoved = SDL_FALSE;
     52
     53/* static incrementing counter for new joystick devices seen on the system. Devices should start with index 0 */
     54static int s_joystick_instance_id = -1;
     55
     56
     57static void
     58FreeElementList(recElement *pElement)
     59{
     60    while (pElement) {
     61        recElement *pElementNext = pElement->pNext;
     62        SDL_free(pElement);
     63        pElement = pElementNext;
     64    }
     65}
     66
     67static recDevice *
     68FreeDevice(recDevice *removeDevice)
     69{
     70    recDevice *pDeviceNext = NULL;
     71    if (removeDevice) {
     72        if (removeDevice->deviceRef) {
     73            IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
     74            removeDevice->deviceRef = NULL;
     75        }
     76
     77        /* save next device prior to disposing of this device */
     78        pDeviceNext = removeDevice->pNext;
     79
     80        if ( gpDeviceList == removeDevice ) {
     81            gpDeviceList = pDeviceNext;
     82        } else {
     83            recDevice *device = gpDeviceList;
     84            while (device->pNext != removeDevice) {
     85                device = device->pNext;
     86            }
     87            device->pNext = pDeviceNext;
     88        }
     89        removeDevice->pNext = NULL;
     90
     91        /* free element lists */
     92        FreeElementList(removeDevice->firstAxis);
     93        FreeElementList(removeDevice->firstButton);
     94        FreeElementList(removeDevice->firstHat);
     95
     96        SDL_free(removeDevice);
     97    }
     98    return pDeviceNext;
     99}
    100
    101static SInt32
    102GetHIDElementState(recDevice *pDevice, recElement *pElement)
    103{
    104    SInt32 value = 0;
    105
    106    if (pDevice && pElement) {
    107        IOHIDValueRef valueRef;
    108        if (IOHIDDeviceGetValue(pDevice->deviceRef, pElement->elementRef, &valueRef) == kIOReturnSuccess) {
    109            value = (SInt32) IOHIDValueGetIntegerValue(valueRef);
    110
    111            /* record min and max for auto calibration */
    112            if (value < pElement->minReport) {
    113                pElement->minReport = value;
    114            }
    115            if (value > pElement->maxReport) {
    116                pElement->maxReport = value;
    117            }
    118        }
    119    }
    120
    121    return value;
    122}
    123
    124static SInt32
    125GetHIDScaledCalibratedState(recDevice * pDevice, recElement * pElement, SInt32 min, SInt32 max)
    126{
    127    const float deviceScale = max - min;
    128    const float readScale = pElement->maxReport - pElement->minReport;
    129    const SInt32 value = GetHIDElementState(pDevice, pElement);
    130    if (readScale == 0) {
    131        return value;           /* no scaling at all */
    132    }
    133    return ((value - pElement->minReport) * deviceScale / readScale) + min;
    134}
    135
    136
    137static void
    138JoystickDeviceWasRemovedCallback(void *ctx, IOReturn result, void *sender)
    139{
    140    recDevice *device = (recDevice *) ctx;
    141    device->removed = 1;
    142    device->deviceRef = NULL; // deviceRef was invalidated due to the remove
    143#if SDL_HAPTIC_IOKIT
    144    MacHaptic_MaybeRemoveDevice(device->ffservice);
    145#endif
    146    s_bDeviceRemoved = SDL_TRUE;
    147}
    148
    149
    150static void AddHIDElement(const void *value, void *parameter);
    151
    152/* Call AddHIDElement() on all elements in an array of IOHIDElementRefs */
    153static void
    154AddHIDElements(CFArrayRef array, recDevice *pDevice)
    155{
    156    const CFRange range = { 0, CFArrayGetCount(array) };
    157    CFArrayApplyFunction(array, range, AddHIDElement, pDevice);
    158}
    159
    160static SDL_bool
    161ElementAlreadyAdded(const IOHIDElementCookie cookie, const recElement *listitem) {
    162    while (listitem) {
    163        if (listitem->cookie == cookie) {
    164            return SDL_TRUE;
    165        }
    166        listitem = listitem->pNext;
    167    }
    168    return SDL_FALSE;
    169}
    170
    171/* See if we care about this HID element, and if so, note it in our recDevice. */
    172static void
    173AddHIDElement(const void *value, void *parameter)
    174{
    175    recDevice *pDevice = (recDevice *) parameter;
    176    IOHIDElementRef refElement = (IOHIDElementRef) value;
    177    const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0;
    178
    179    if (refElement && (elementTypeID == IOHIDElementGetTypeID())) {
    180        const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement);
    181        const uint32_t usagePage = IOHIDElementGetUsagePage(refElement);
    182        const uint32_t usage = IOHIDElementGetUsage(refElement);
    183        recElement *element = NULL;
    184        recElement **headElement = NULL;
    185
    186        /* look at types of interest */
    187        switch (IOHIDElementGetType(refElement)) {
    188            case kIOHIDElementTypeInput_Misc:
    189            case kIOHIDElementTypeInput_Button:
    190            case kIOHIDElementTypeInput_Axis: {
    191                switch (usagePage) {    /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */
    192                    case kHIDPage_GenericDesktop:
    193                        switch (usage) {
    194                            case kHIDUsage_GD_X:
    195                            case kHIDUsage_GD_Y:
    196                            case kHIDUsage_GD_Z:
    197                            case kHIDUsage_GD_Rx:
    198                            case kHIDUsage_GD_Ry:
    199                            case kHIDUsage_GD_Rz:
    200                            case kHIDUsage_GD_Slider:
    201                            case kHIDUsage_GD_Dial:
    202                            case kHIDUsage_GD_Wheel:
    203                                if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
    204                                    element = (recElement *) SDL_calloc(1, sizeof (recElement));
    205                                    if (element) {
    206                                        pDevice->axes++;
    207                                        headElement = &(pDevice->firstAxis);
    208                                    }
    209                                }
    210                                break;
    211
    212                            case kHIDUsage_GD_Hatswitch:
    213                                if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) {
    214                                    element = (recElement *) SDL_calloc(1, sizeof (recElement));
    215                                    if (element) {
    216                                        pDevice->hats++;
    217                                        headElement = &(pDevice->firstHat);
    218                                    }
    219                                }
    220                                break;
    221                        }
    222                        break;
    223
    224                    case kHIDPage_Simulation:
    225                        switch (usage) {
    226                            case kHIDUsage_Sim_Rudder:
    227                            case kHIDUsage_Sim_Throttle:
    228                                if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
    229                                    element = (recElement *) SDL_calloc(1, sizeof (recElement));
    230                                    if (element) {
    231                                        pDevice->axes++;
    232                                        headElement = &(pDevice->firstAxis);
    233                                    }
    234                                }
    235                                break;
    236
    237                            default:
    238                                break;
    239                        }
    240                        break;
    241
    242                    case kHIDPage_Button:
    243                        if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
    244                            element = (recElement *) SDL_calloc(1, sizeof (recElement));
    245                            if (element) {
    246                                pDevice->buttons++;
    247                                headElement = &(pDevice->firstButton);
    248                            }
    249                        }
    250                        break;
    251
    252                    default:
    253                        break;
    254                }
    255            }
    256            break;
    257
    258            case kIOHIDElementTypeCollection: {
    259                CFArrayRef array = IOHIDElementGetChildren(refElement);
    260                if (array) {
    261                    AddHIDElements(array, pDevice);
    262                }
    263            }
    264            break;
    265
    266            default:
    267                break;
    268        }
    269
    270        if (element && headElement) {       /* add to list */
    271            recElement *elementPrevious = NULL;
    272            recElement *elementCurrent = *headElement;
    273            while (elementCurrent && usage >= elementCurrent->usage) {
    274                elementPrevious = elementCurrent;
    275                elementCurrent = elementCurrent->pNext;
    276            }
    277            if (elementPrevious) {
    278                elementPrevious->pNext = element;
    279            } else {
    280                *headElement = element;
    281            }
    282
    283            element->elementRef = refElement;
    284            element->usagePage = usagePage;
    285            element->usage = usage;
    286            element->pNext = elementCurrent;
    287
    288            element->minReport = element->min = (SInt32) IOHIDElementGetLogicalMin(refElement);
    289            element->maxReport = element->max = (SInt32) IOHIDElementGetLogicalMax(refElement);
    290            element->cookie = IOHIDElementGetCookie(refElement);
    291
    292            pDevice->elements++;
    293        }
    294    }
    295}
    296
    297static SDL_bool
    298GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
    299{
    300    Uint32 *guid32 = NULL;
    301    CFTypeRef refCF = NULL;
    302    CFArrayRef array = NULL;
    303
    304    /* get usage page and usage */
    305    refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
    306    if (refCF) {
    307        CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage);
    308    }
    309    if (pDevice->usagePage != kHIDPage_GenericDesktop) {
    310        return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */
    311    }
    312
    313    refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey));
    314    if (refCF) {
    315        CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage);
    316    }
    317
    318    if ((pDevice->usage != kHIDUsage_GD_Joystick &&
    319         pDevice->usage != kHIDUsage_GD_GamePad &&
    320         pDevice->usage != kHIDUsage_GD_MultiAxisController)) {
    321        return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */
    322    }
    323
    324    pDevice->deviceRef = hidDevice;
    325
    326    /* get device name */
    327    refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey));
    328    if (!refCF) {
    329        /* Maybe we can't get "AwesomeJoystick2000", but we can get "Logitech"? */
    330        refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey));
    331    }
    332    if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) {
    333        SDL_strlcpy(pDevice->product, "Unidentified joystick", sizeof (pDevice->product));
    334    }
    335
    336    refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
    337    if (refCF) {
    338        CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->guid.data[0]);
    339    }
    340
    341    refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey));
    342    if (refCF) {
    343        CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->guid.data[8]);
    344    }
    345
    346    /* Check to make sure we have a vendor and product ID
    347       If we don't, use the same algorithm as the Linux code for Bluetooth devices */
    348    guid32 = (Uint32*)pDevice->guid.data;
    349    if (!guid32[0] && !guid32[1]) {
    350        /* If we don't have a vendor and product ID this is probably a Bluetooth device */
    351        const Uint16 BUS_BLUETOOTH = 0x05;
    352        Uint16 *guid16 = (Uint16 *)guid32;
    353        *guid16++ = BUS_BLUETOOTH;
    354        *guid16++ = 0;
    355        SDL_strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4);
    356    }
    357
    358    array = IOHIDDeviceCopyMatchingElements(hidDevice, NULL, kIOHIDOptionsTypeNone);
    359    if (array) {
    360        AddHIDElements(array, pDevice);
    361        CFRelease(array);
    362    }
    363
    364    return SDL_TRUE;
    365}
    366
    367static SDL_bool
    368JoystickAlreadyKnown(IOHIDDeviceRef ioHIDDeviceObject)
    369{
    370    recDevice *i;
    371    for (i = gpDeviceList; i != NULL; i = i->pNext) {
    372        if (i->deviceRef == ioHIDDeviceObject) {
    373            return SDL_TRUE;
    374        }
    375    }
    376    return SDL_FALSE;
    377}
    378
    379
    380static void
    381JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject)
    382{
    383    recDevice *device;
    384
    385    if (res != kIOReturnSuccess) {
    386        return;
    387    }
    388
    389    if (JoystickAlreadyKnown(ioHIDDeviceObject)) {
    390        return;  /* IOKit sent us a duplicate. */
    391    }
    392
    393    device = (recDevice *) SDL_calloc(1, sizeof(recDevice));
    394
    395    if (!device) {
    396        SDL_OutOfMemory();
    397        return;
    398    }
    399
    400    if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
    401        SDL_free(device);
    402        return;   /* not a device we care about, probably. */
    403    }
    404
    405    /* Get notified when this device is disconnected. */
    406    IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
    407    IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
    408
    409    /* Allocate an instance ID for this device */
    410    device->instance_id = ++s_joystick_instance_id;
    411
    412    /* We have to do some storage of the io_service_t for SDL_HapticOpenFromJoystick */
    413    if (IOHIDDeviceGetService != NULL) {  /* weak reference: available in 10.6 and later. */
    414        const io_service_t ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
    415        if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
    416            device->ffservice = ioservice;
    417#if SDL_HAPTIC_IOKIT
    418            MacHaptic_MaybeAddDevice(ioservice);
    419#endif
    420        }
    421    }
    422
    423    device->send_open_event = 1;
    424    s_bDeviceAdded = SDL_TRUE;
    425
    426    /* Add device to the end of the list */
    427    if ( !gpDeviceList ) {
    428        gpDeviceList = device;
    429    } else {
    430        recDevice *curdevice;
    431
    432        curdevice = gpDeviceList;
    433        while ( curdevice->pNext ) {
    434            curdevice = curdevice->pNext;
    435        }
    436        curdevice->pNext = device;
    437    }
    438}
    439
    440static SDL_bool
    441ConfigHIDManager(CFArrayRef matchingArray)
    442{
    443    CFRunLoopRef runloop = CFRunLoopGetCurrent();
    444
    445    if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
    446        return SDL_FALSE;
    447    }
    448
    449    IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback, NULL);
    450    IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
    451    IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
    452
    453    while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
    454        /* no-op. Callback fires once per existing device. */
    455    }
    456
    457    /* future hotplug events will come through SDL_JOYSTICK_RUNLOOP_MODE now. */
    458
    459    return SDL_TRUE;  /* good to go. */
    460}
    461
    462
    463static CFDictionaryRef
    464CreateHIDDeviceMatchDictionary(const UInt32 page, const UInt32 usage, int *okay)
    465{
    466    CFDictionaryRef retval = NULL;
    467    CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
    468    CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
    469    const void *keys[2] = { (void *) CFSTR(kIOHIDDeviceUsagePageKey), (void *) CFSTR(kIOHIDDeviceUsageKey) };
    470    const void *vals[2] = { (void *) pageNumRef, (void *) usageNumRef };
    471
    472    if (pageNumRef && usageNumRef) {
    473        retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    474    }
    475
    476    if (pageNumRef) {
    477        CFRelease(pageNumRef);
    478    }
    479    if (usageNumRef) {
    480        CFRelease(usageNumRef);
    481    }
    482
    483    if (!retval) {
    484        *okay = 0;
    485    }
    486
    487    return retval;
    488}
    489
    490static SDL_bool
    491CreateHIDManager(void)
    492{
    493    SDL_bool retval = SDL_FALSE;
    494    int okay = 1;
    495    const void *vals[] = {
    496        (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
    497        (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
    498        (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
    499    };
    500    const size_t numElements = SDL_arraysize(vals);
    501    CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) : NULL;
    502    size_t i;
    503
    504    for (i = 0; i < numElements; i++) {
    505        if (vals[i]) {
    506            CFRelease((CFTypeRef) vals[i]);
    507        }
    508    }
    509
    510    if (array) {
    511        hidman = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
    512        if (hidman != NULL) {
    513            retval = ConfigHIDManager(array);
    514        }
    515        CFRelease(array);
    516    }
    517
    518    return retval;
    519}
    520
    521
    522/* Function to scan the system for joysticks.
    523 * Joystick 0 should be the system default joystick.
    524 * This function should return the number of available joysticks, or -1
    525 * on an unrecoverable fatal error.
    526 */
    527int
    528SDL_SYS_JoystickInit(void)
    529{
    530    if (gpDeviceList) {
    531        return SDL_SetError("Joystick: Device list already inited.");
    532    }
    533
    534    if (!CreateHIDManager()) {
    535        return SDL_SetError("Joystick: Couldn't initialize HID Manager");
    536    }
    537
    538    return SDL_SYS_NumJoysticks();
    539}
    540
    541/* Function to return the number of joystick devices plugged in right now */
    542int
    543SDL_SYS_NumJoysticks()
    544{
    545    recDevice *device = gpDeviceList;
    546    int nJoySticks = 0;
    547
    548    while (device) {
    549        if (!device->removed) {
    550            nJoySticks++;
    551        }
    552        device = device->pNext;
    553    }
    554
    555    return nJoySticks;
    556}
    557
    558/* Function to cause any queued joystick insertions to be processed
    559 */
    560void
    561SDL_SYS_JoystickDetect()
    562{
    563    while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
    564        /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
    565    }
    566
    567    if (s_bDeviceAdded || s_bDeviceRemoved) {
    568        recDevice *device = gpDeviceList;
    569        s_bDeviceAdded = SDL_FALSE;
    570        s_bDeviceRemoved = SDL_FALSE;
    571        int device_index = 0;
    572        /* send notifications */
    573        while (device) {
    574            if (device->send_open_event) {
    575                device->send_open_event = 0;
    576/* !!! FIXME: why isn't there an SDL_PrivateJoyDeviceAdded()? */
    577#if !SDL_EVENTS_DISABLED
    578                SDL_Event event;
    579                event.type = SDL_JOYDEVICEADDED;
    580
    581                if (SDL_GetEventState(event.type) == SDL_ENABLE) {
    582                    event.jdevice.which = device_index;
    583                    if ((SDL_EventOK == NULL)
    584                        || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
    585                        SDL_PushEvent(&event);
    586                    }
    587                }
    588#endif /* !SDL_EVENTS_DISABLED */
    589
    590            }
    591
    592            if (device->removed) {
    593                const int instance_id = device->instance_id;
    594                device = FreeDevice(device);
    595
    596/* !!! FIXME: why isn't there an SDL_PrivateJoyDeviceRemoved()? */
    597#if !SDL_EVENTS_DISABLED
    598                SDL_Event event;
    599                event.type = SDL_JOYDEVICEREMOVED;
    600
    601                if (SDL_GetEventState(event.type) == SDL_ENABLE) {
    602                    event.jdevice.which = instance_id;
    603                    if ((SDL_EventOK == NULL)
    604                        || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
    605                        SDL_PushEvent(&event);
    606                    }
    607                }
    608#endif /* !SDL_EVENTS_DISABLED */
    609
    610            } else {
    611                device = device->pNext;
    612                device_index++;
    613            }
    614        }
    615    }
    616}
    617
    618/* Function to get the device-dependent name of a joystick */
    619const char *
    620SDL_SYS_JoystickNameForDeviceIndex(int device_index)
    621{
    622    recDevice *device = gpDeviceList;
    623
    624    while (device_index-- > 0) {
    625        device = device->pNext;
    626    }
    627
    628    return device->product;
    629}
    630
    631/* Function to return the instance id of the joystick at device_index
    632 */
    633SDL_JoystickID
    634SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
    635{
    636    recDevice *device = gpDeviceList;
    637    int index;
    638
    639    for (index = device_index; index > 0; index--) {
    640        device = device->pNext;
    641    }
    642
    643    return device->instance_id;
    644}
    645
    646/* Function to open a joystick for use.
    647 * The joystick to open is specified by the index field of the joystick.
    648 * This should fill the nbuttons and naxes fields of the joystick structure.
    649 * It returns 0, or -1 if there is an error.
    650 */
    651int
    652SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
    653{
    654    recDevice *device = gpDeviceList;
    655    int index;
    656
    657    for (index = device_index; index > 0; index--) {
    658        device = device->pNext;
    659    }
    660
    661    joystick->instance_id = device->instance_id;
    662    joystick->hwdata = device;
    663    joystick->name = device->product;
    664
    665    joystick->naxes = device->axes;
    666    joystick->nhats = device->hats;
    667    joystick->nballs = 0;
    668    joystick->nbuttons = device->buttons;
    669    return 0;
    670}
    671
    672/* Function to query if the joystick is currently attached
    673 *   It returns 1 if attached, 0 otherwise.
    674 */
    675SDL_bool
    676SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
    677{
    678    recDevice *device = gpDeviceList;
    679
    680    while (device) {
    681        if (joystick->instance_id == device->instance_id) {
    682            return SDL_TRUE;
    683        }
    684        device = device->pNext;
    685    }
    686
    687    return SDL_FALSE;
    688}
    689
    690/* Function to update the state of a joystick - called as a device poll.
    691 * This function shouldn't update the joystick structure directly,
    692 * but instead should call SDL_PrivateJoystick*() to deliver events
    693 * and update joystick device state.
    694 */
    695void
    696SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
    697{
    698    recDevice *device = joystick->hwdata;
    699    recElement *element;
    700    SInt32 value, range;
    701    int i;
    702
    703    if (!device) {
    704        return;
    705    }
    706
    707    if (device->removed) {      /* device was unplugged; ignore it. */
    708        joystick->closed = 1;
    709        joystick->uncentered = 1;
    710        joystick->hwdata = NULL;
    711        return;
    712    }
    713
    714    element = device->firstAxis;
    715    i = 0;
    716    while (element) {
    717        value = GetHIDScaledCalibratedState(device, element, -32768, 32767);
    718        if (value != joystick->axes[i]) {
    719            SDL_PrivateJoystickAxis(joystick, i, value);
    720        }
    721        element = element->pNext;
    722        ++i;
    723    }
    724
    725    element = device->firstButton;
    726    i = 0;
    727    while (element) {
    728        value = GetHIDElementState(device, element);
    729        if (value > 1) {          /* handle pressure-sensitive buttons */
    730            value = 1;
    731        }
    732        if (value != joystick->buttons[i]) {
    733            SDL_PrivateJoystickButton(joystick, i, value);
    734        }
    735        element = element->pNext;
    736        ++i;
    737    }
    738
    739    element = device->firstHat;
    740    i = 0;
    741    while (element) {
    742        Uint8 pos = 0;
    743
    744        range = (element->max - element->min + 1);
    745        value = GetHIDElementState(device, element) - element->min;
    746        if (range == 4) {         /* 4 position hatswitch - scale up value */
    747            value *= 2;
    748        } else if (range != 8) {    /* Neither a 4 nor 8 positions - fall back to default position (centered) */
    749            value = -1;
    750        }
    751        switch (value) {
    752        case 0:
    753            pos = SDL_HAT_UP;
    754            break;
    755        case 1:
    756            pos = SDL_HAT_RIGHTUP;
    757            break;
    758        case 2:
    759            pos = SDL_HAT_RIGHT;
    760            break;
    761        case 3:
    762            pos = SDL_HAT_RIGHTDOWN;
    763            break;
    764        case 4:
    765            pos = SDL_HAT_DOWN;
    766            break;
    767        case 5:
    768            pos = SDL_HAT_LEFTDOWN;
    769            break;
    770        case 6:
    771            pos = SDL_HAT_LEFT;
    772            break;
    773        case 7:
    774            pos = SDL_HAT_LEFTUP;
    775            break;
    776        default:
    777            /* Every other value is mapped to center. We do that because some
    778             * joysticks use 8 and some 15 for this value, and apparently
    779             * there are even more variants out there - so we try to be generous.
    780             */
    781            pos = SDL_HAT_CENTERED;
    782            break;
    783        }
    784
    785        if (pos != joystick->hats[i]) {
    786            SDL_PrivateJoystickHat(joystick, i, pos);
    787        }
    788
    789        element = element->pNext;
    790        ++i;
    791    }
    792}
    793
    794/* Function to close a joystick after use */
    795void
    796SDL_SYS_JoystickClose(SDL_Joystick * joystick)
    797{
    798    joystick->closed = 1;
    799}
    800
    801/* Function to perform any system-specific joystick related cleanup */
    802void
    803SDL_SYS_JoystickQuit(void)
    804{
    805    while (FreeDevice(gpDeviceList)) {
    806        /* spin */
    807    }
    808
    809    if (hidman) {
    810        IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
    811        IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
    812        CFRelease(hidman);
    813        hidman = NULL;
    814    }
    815
    816    s_bDeviceAdded = s_bDeviceRemoved = SDL_FALSE;
    817}
    818
    819
    820SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
    821{
    822    recDevice *device = gpDeviceList;
    823    int index;
    824
    825    for (index = device_index; index > 0; index--) {
    826        device = device->pNext;
    827    }
    828
    829    return device->guid;
    830}
    831
    832SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
    833{
    834    return joystick->hwdata->guid;
    835}
    836
    837#endif /* SDL_JOYSTICK_IOKIT */
    838
    839/* vi: set ts=4 sw=4 expandtab: */