summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/SDL2/src/joystick/darwin/SDL_sysjoystick.c
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/SDL2/src/joystick/darwin/SDL_sysjoystick.c')
-rw-r--r--3rdparty/SDL2/src/joystick/darwin/SDL_sysjoystick.c836
1 files changed, 836 insertions, 0 deletions
diff --git a/3rdparty/SDL2/src/joystick/darwin/SDL_sysjoystick.c b/3rdparty/SDL2/src/joystick/darwin/SDL_sysjoystick.c
new file mode 100644
index 00000000000..6dd306aa8ee
--- /dev/null
+++ b/3rdparty/SDL2/src/joystick/darwin/SDL_sysjoystick.c
@@ -0,0 +1,836 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_IOKIT
+
+#include <IOKit/hid/IOHIDLib.h>
+
+/* For force feedback testing. */
+#include <ForceFeedback/ForceFeedback.h>
+#include <ForceFeedback/ForceFeedbackConstants.h>
+
+#include "SDL_joystick.h"
+#include "../SDL_sysjoystick.h"
+#include "../SDL_joystick_c.h"
+#include "SDL_sysjoystick_c.h"
+#include "SDL_events.h"
+#include "../../haptic/darwin/SDL_syshaptic_c.h" /* For haptic hot plugging */
+#if !SDL_EVENTS_DISABLED
+#include "../../events/SDL_events_c.h"
+#endif
+
+#define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick")
+
+/* The base object of the HID Manager API */
+static IOHIDManagerRef hidman = NULL;
+
+/* Linked list of all available devices */
+static recDevice *gpDeviceList = NULL;
+
+/* static incrementing counter for new joystick devices seen on the system. Devices should start with index 0 */
+static int s_joystick_instance_id = -1;
+
+static recDevice *GetDeviceForIndex(int device_index)
+{
+ recDevice *device = gpDeviceList;
+ while (device) {
+ if (!device->removed) {
+ if (device_index == 0)
+ break;
+
+ --device_index;
+ }
+ device = device->pNext;
+ }
+ return device;
+}
+
+static void
+FreeElementList(recElement *pElement)
+{
+ while (pElement) {
+ recElement *pElementNext = pElement->pNext;
+ SDL_free(pElement);
+ pElement = pElementNext;
+ }
+}
+
+static recDevice *
+FreeDevice(recDevice *removeDevice)
+{
+ recDevice *pDeviceNext = NULL;
+ if (removeDevice) {
+ if (removeDevice->deviceRef) {
+ IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
+ removeDevice->deviceRef = NULL;
+ }
+
+ /* save next device prior to disposing of this device */
+ pDeviceNext = removeDevice->pNext;
+
+ if ( gpDeviceList == removeDevice ) {
+ gpDeviceList = pDeviceNext;
+ } else {
+ recDevice *device = gpDeviceList;
+ while (device->pNext != removeDevice) {
+ device = device->pNext;
+ }
+ device->pNext = pDeviceNext;
+ }
+ removeDevice->pNext = NULL;
+
+ /* free element lists */
+ FreeElementList(removeDevice->firstAxis);
+ FreeElementList(removeDevice->firstButton);
+ FreeElementList(removeDevice->firstHat);
+
+ SDL_free(removeDevice);
+ }
+ return pDeviceNext;
+}
+
+static SInt32
+GetHIDElementState(recDevice *pDevice, recElement *pElement)
+{
+ SInt32 value = 0;
+
+ if (pDevice && pElement) {
+ IOHIDValueRef valueRef;
+ if (IOHIDDeviceGetValue(pDevice->deviceRef, pElement->elementRef, &valueRef) == kIOReturnSuccess) {
+ value = (SInt32) IOHIDValueGetIntegerValue(valueRef);
+
+ /* record min and max for auto calibration */
+ if (value < pElement->minReport) {
+ pElement->minReport = value;
+ }
+ if (value > pElement->maxReport) {
+ pElement->maxReport = value;
+ }
+ }
+ }
+
+ return value;
+}
+
+static SInt32
+GetHIDScaledCalibratedState(recDevice * pDevice, recElement * pElement, SInt32 min, SInt32 max)
+{
+ const float deviceScale = max - min;
+ const float readScale = pElement->maxReport - pElement->minReport;
+ const SInt32 value = GetHIDElementState(pDevice, pElement);
+ if (readScale == 0) {
+ return value; /* no scaling at all */
+ }
+ return ((value - pElement->minReport) * deviceScale / readScale) + min;
+}
+
+
+static void
+JoystickDeviceWasRemovedCallback(void *ctx, IOReturn result, void *sender)
+{
+ recDevice *device = (recDevice *) ctx;
+ device->removed = SDL_TRUE;
+ device->deviceRef = NULL; // deviceRef was invalidated due to the remove
+#if SDL_HAPTIC_IOKIT
+ MacHaptic_MaybeRemoveDevice(device->ffservice);
+#endif
+
+/* !!! FIXME: why isn't there an SDL_PrivateJoyDeviceRemoved()? */
+#if !SDL_EVENTS_DISABLED
+ {
+ SDL_Event event;
+ event.type = SDL_JOYDEVICEREMOVED;
+
+ if (SDL_GetEventState(event.type) == SDL_ENABLE) {
+ event.jdevice.which = device->instance_id;
+ if ((SDL_EventOK == NULL)
+ || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
+ SDL_PushEvent(&event);
+ }
+ }
+ }
+#endif /* !SDL_EVENTS_DISABLED */
+}
+
+
+static void AddHIDElement(const void *value, void *parameter);
+
+/* Call AddHIDElement() on all elements in an array of IOHIDElementRefs */
+static void
+AddHIDElements(CFArrayRef array, recDevice *pDevice)
+{
+ const CFRange range = { 0, CFArrayGetCount(array) };
+ CFArrayApplyFunction(array, range, AddHIDElement, pDevice);
+}
+
+static SDL_bool
+ElementAlreadyAdded(const IOHIDElementCookie cookie, const recElement *listitem) {
+ while (listitem) {
+ if (listitem->cookie == cookie) {
+ return SDL_TRUE;
+ }
+ listitem = listitem->pNext;
+ }
+ return SDL_FALSE;
+}
+
+/* See if we care about this HID element, and if so, note it in our recDevice. */
+static void
+AddHIDElement(const void *value, void *parameter)
+{
+ recDevice *pDevice = (recDevice *) parameter;
+ IOHIDElementRef refElement = (IOHIDElementRef) value;
+ const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0;
+
+ if (refElement && (elementTypeID == IOHIDElementGetTypeID())) {
+ const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement);
+ const uint32_t usagePage = IOHIDElementGetUsagePage(refElement);
+ const uint32_t usage = IOHIDElementGetUsage(refElement);
+ recElement *element = NULL;
+ recElement **headElement = NULL;
+
+ /* look at types of interest */
+ switch (IOHIDElementGetType(refElement)) {
+ case kIOHIDElementTypeInput_Misc:
+ case kIOHIDElementTypeInput_Button:
+ case kIOHIDElementTypeInput_Axis: {
+ switch (usagePage) { /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */
+ case kHIDPage_GenericDesktop:
+ switch (usage) {
+ case kHIDUsage_GD_X:
+ case kHIDUsage_GD_Y:
+ case kHIDUsage_GD_Z:
+ case kHIDUsage_GD_Rx:
+ case kHIDUsage_GD_Ry:
+ case kHIDUsage_GD_Rz:
+ case kHIDUsage_GD_Slider:
+ case kHIDUsage_GD_Dial:
+ case kHIDUsage_GD_Wheel:
+ if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
+ element = (recElement *) SDL_calloc(1, sizeof (recElement));
+ if (element) {
+ pDevice->axes++;
+ headElement = &(pDevice->firstAxis);
+ }
+ }
+ break;
+
+ case kHIDUsage_GD_Hatswitch:
+ if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) {
+ element = (recElement *) SDL_calloc(1, sizeof (recElement));
+ if (element) {
+ pDevice->hats++;
+ headElement = &(pDevice->firstHat);
+ }
+ }
+ break;
+ case kHIDUsage_GD_DPadUp:
+ case kHIDUsage_GD_DPadDown:
+ case kHIDUsage_GD_DPadRight:
+ case kHIDUsage_GD_DPadLeft:
+ case kHIDUsage_GD_Start:
+ case kHIDUsage_GD_Select:
+ if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
+ element = (recElement *) SDL_calloc(1, sizeof (recElement));
+ if (element) {
+ pDevice->buttons++;
+ headElement = &(pDevice->firstButton);
+ }
+ }
+ break;
+ }
+ break;
+
+ case kHIDPage_Simulation:
+ switch (usage) {
+ case kHIDUsage_Sim_Rudder:
+ case kHIDUsage_Sim_Throttle:
+ if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
+ element = (recElement *) SDL_calloc(1, sizeof (recElement));
+ if (element) {
+ pDevice->axes++;
+ headElement = &(pDevice->firstAxis);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case kHIDPage_Button:
+ case kHIDPage_Consumer: /* e.g. 'pause' button on Steelseries MFi gamepads. */
+ if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
+ element = (recElement *) SDL_calloc(1, sizeof (recElement));
+ if (element) {
+ pDevice->buttons++;
+ headElement = &(pDevice->firstButton);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+
+ case kIOHIDElementTypeCollection: {
+ CFArrayRef array = IOHIDElementGetChildren(refElement);
+ if (array) {
+ AddHIDElements(array, pDevice);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (element && headElement) { /* add to list */
+ recElement *elementPrevious = NULL;
+ recElement *elementCurrent = *headElement;
+ while (elementCurrent && usage >= elementCurrent->usage) {
+ elementPrevious = elementCurrent;
+ elementCurrent = elementCurrent->pNext;
+ }
+ if (elementPrevious) {
+ elementPrevious->pNext = element;
+ } else {
+ *headElement = element;
+ }
+
+ element->elementRef = refElement;
+ element->usagePage = usagePage;
+ element->usage = usage;
+ element->pNext = elementCurrent;
+
+ element->minReport = element->min = (SInt32) IOHIDElementGetLogicalMin(refElement);
+ element->maxReport = element->max = (SInt32) IOHIDElementGetLogicalMax(refElement);
+ element->cookie = IOHIDElementGetCookie(refElement);
+
+ pDevice->elements++;
+ }
+ }
+}
+
+static SDL_bool
+GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
+{
+ Uint32 *guid32 = NULL;
+ CFTypeRef refCF = NULL;
+ CFArrayRef array = NULL;
+
+ /* get usage page and usage */
+ refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
+ if (refCF) {
+ CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage);
+ }
+ if (pDevice->usagePage != kHIDPage_GenericDesktop) {
+ return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */
+ }
+
+ refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey));
+ if (refCF) {
+ CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage);
+ }
+
+ if ((pDevice->usage != kHIDUsage_GD_Joystick &&
+ pDevice->usage != kHIDUsage_GD_GamePad &&
+ pDevice->usage != kHIDUsage_GD_MultiAxisController)) {
+ return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */
+ }
+
+ pDevice->deviceRef = hidDevice;
+
+ /* get device name */
+ refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey));
+ if (!refCF) {
+ /* Maybe we can't get "AwesomeJoystick2000", but we can get "Logitech"? */
+ refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey));
+ }
+ if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) {
+ SDL_strlcpy(pDevice->product, "Unidentified joystick", sizeof (pDevice->product));
+ }
+
+ refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
+ if (refCF) {
+ CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->guid.data[0]);
+ }
+
+ refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey));
+ if (refCF) {
+ CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->guid.data[8]);
+ }
+
+ /* Check to make sure we have a vendor and product ID
+ If we don't, use the same algorithm as the Linux code for Bluetooth devices */
+ guid32 = (Uint32*)pDevice->guid.data;
+ if (!guid32[0] && !guid32[1]) {
+ /* If we don't have a vendor and product ID this is probably a Bluetooth device */
+ const Uint16 BUS_BLUETOOTH = 0x05;
+ Uint16 *guid16 = (Uint16 *)guid32;
+ *guid16++ = BUS_BLUETOOTH;
+ *guid16++ = 0;
+ SDL_strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4);
+ }
+
+ array = IOHIDDeviceCopyMatchingElements(hidDevice, NULL, kIOHIDOptionsTypeNone);
+ if (array) {
+ AddHIDElements(array, pDevice);
+ CFRelease(array);
+ }
+
+ return SDL_TRUE;
+}
+
+static SDL_bool
+JoystickAlreadyKnown(IOHIDDeviceRef ioHIDDeviceObject)
+{
+ recDevice *i;
+ for (i = gpDeviceList; i != NULL; i = i->pNext) {
+ if (i->deviceRef == ioHIDDeviceObject) {
+ return SDL_TRUE;
+ }
+ }
+ return SDL_FALSE;
+}
+
+
+static void
+JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject)
+{
+ recDevice *device;
+ int device_index = 0;
+
+ if (res != kIOReturnSuccess) {
+ return;
+ }
+
+ if (JoystickAlreadyKnown(ioHIDDeviceObject)) {
+ return; /* IOKit sent us a duplicate. */
+ }
+
+ device = (recDevice *) SDL_calloc(1, sizeof(recDevice));
+
+ if (!device) {
+ SDL_OutOfMemory();
+ return;
+ }
+
+ if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
+ SDL_free(device);
+ return; /* not a device we care about, probably. */
+ }
+
+ /* Get notified when this device is disconnected. */
+ IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
+ IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
+
+ /* Allocate an instance ID for this device */
+ device->instance_id = ++s_joystick_instance_id;
+
+ /* We have to do some storage of the io_service_t for SDL_HapticOpenFromJoystick */
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
+ if (IOHIDDeviceGetService != NULL) { /* weak reference: available in 10.6 and later. */
+#endif
+
+ const io_service_t ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
+#if SDL_HAPTIC_IOKIT
+ if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
+ device->ffservice = ioservice;
+ MacHaptic_MaybeAddDevice(ioservice);
+ }
+#endif
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
+ }
+#endif
+
+ /* Add device to the end of the list */
+ if ( !gpDeviceList ) {
+ gpDeviceList = device;
+ } else {
+ recDevice *curdevice;
+
+ curdevice = gpDeviceList;
+ while ( curdevice->pNext ) {
+ ++device_index;
+ curdevice = curdevice->pNext;
+ }
+ curdevice->pNext = device;
+ ++device_index; /* bump by one since we counted by pNext. */
+ }
+
+/* !!! FIXME: why isn't there an SDL_PrivateJoyDeviceAdded()? */
+#if !SDL_EVENTS_DISABLED
+ {
+ SDL_Event event;
+ event.type = SDL_JOYDEVICEADDED;
+
+ if (SDL_GetEventState(event.type) == SDL_ENABLE) {
+ event.jdevice.which = device_index;
+ if ((SDL_EventOK == NULL)
+ || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
+ SDL_PushEvent(&event);
+ }
+ }
+ }
+#endif /* !SDL_EVENTS_DISABLED */
+}
+
+static SDL_bool
+ConfigHIDManager(CFArrayRef matchingArray)
+{
+ CFRunLoopRef runloop = CFRunLoopGetCurrent();
+
+ if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
+ return SDL_FALSE;
+ }
+
+ IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
+ IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback, NULL);
+ IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
+
+ while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
+ /* no-op. Callback fires once per existing device. */
+ }
+
+ /* future hotplug events will come through SDL_JOYSTICK_RUNLOOP_MODE now. */
+
+ return SDL_TRUE; /* good to go. */
+}
+
+
+static CFDictionaryRef
+CreateHIDDeviceMatchDictionary(const UInt32 page, const UInt32 usage, int *okay)
+{
+ CFDictionaryRef retval = NULL;
+ CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
+ CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
+ const void *keys[2] = { (void *) CFSTR(kIOHIDDeviceUsagePageKey), (void *) CFSTR(kIOHIDDeviceUsageKey) };
+ const void *vals[2] = { (void *) pageNumRef, (void *) usageNumRef };
+
+ if (pageNumRef && usageNumRef) {
+ retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ }
+
+ if (pageNumRef) {
+ CFRelease(pageNumRef);
+ }
+ if (usageNumRef) {
+ CFRelease(usageNumRef);
+ }
+
+ if (!retval) {
+ *okay = 0;
+ }
+
+ return retval;
+}
+
+static SDL_bool
+CreateHIDManager(void)
+{
+ SDL_bool retval = SDL_FALSE;
+ int okay = 1;
+ const void *vals[] = {
+ (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
+ (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
+ (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
+ };
+ const size_t numElements = SDL_arraysize(vals);
+ CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) : NULL;
+ size_t i;
+
+ for (i = 0; i < numElements; i++) {
+ if (vals[i]) {
+ CFRelease((CFTypeRef) vals[i]);
+ }
+ }
+
+ if (array) {
+ hidman = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+ if (hidman != NULL) {
+ retval = ConfigHIDManager(array);
+ }
+ CFRelease(array);
+ }
+
+ return retval;
+}
+
+
+/* Function to scan the system for joysticks.
+ * Joystick 0 should be the system default joystick.
+ * This function should return the number of available joysticks, or -1
+ * on an unrecoverable fatal error.
+ */
+int
+SDL_SYS_JoystickInit(void)
+{
+ if (gpDeviceList) {
+ return SDL_SetError("Joystick: Device list already inited.");
+ }
+
+ if (!CreateHIDManager()) {
+ return SDL_SetError("Joystick: Couldn't initialize HID Manager");
+ }
+
+ return SDL_SYS_NumJoysticks();
+}
+
+/* Function to return the number of joystick devices plugged in right now */
+int
+SDL_SYS_NumJoysticks()
+{
+ recDevice *device = gpDeviceList;
+ int nJoySticks = 0;
+
+ while (device) {
+ if (!device->removed) {
+ nJoySticks++;
+ }
+ device = device->pNext;
+ }
+
+ return nJoySticks;
+}
+
+/* Function to cause any queued joystick insertions to be processed
+ */
+void
+SDL_SYS_JoystickDetect()
+{
+ recDevice *device = gpDeviceList;
+ while (device) {
+ if (device->removed) {
+ device = FreeDevice(device);
+ } else {
+ device = device->pNext;
+ }
+ }
+
+ // run this after the checks above so we don't set device->removed and delete the device before
+ // SDL_SYS_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device
+ while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
+ /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
+ }
+}
+
+/* Function to get the device-dependent name of a joystick */
+const char *
+SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+{
+ recDevice *device = GetDeviceForIndex(device_index);
+ return device ? device->product : "UNKNOWN";
+}
+
+/* Function to return the instance id of the joystick at device_index
+ */
+SDL_JoystickID
+SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+{
+ recDevice *device = GetDeviceForIndex(device_index);
+ return device ? device->instance_id : 0;
+}
+
+/* Function to open a joystick for use.
+ * The joystick to open is specified by the device index.
+ * This should fill the nbuttons and naxes fields of the joystick structure.
+ * It returns 0, or -1 if there is an error.
+ */
+int
+SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+{
+ recDevice *device = GetDeviceForIndex(device_index);
+
+ joystick->instance_id = device->instance_id;
+ joystick->hwdata = device;
+ joystick->name = device->product;
+
+ joystick->naxes = device->axes;
+ joystick->nhats = device->hats;
+ joystick->nballs = 0;
+ joystick->nbuttons = device->buttons;
+ return 0;
+}
+
+/* Function to query if the joystick is currently attached
+ * It returns SDL_TRUE if attached, SDL_FALSE otherwise.
+ */
+SDL_bool
+SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
+{
+ return joystick->hwdata != NULL;
+}
+
+/* Function to update the state of a joystick - called as a device poll.
+ * This function shouldn't update the joystick structure directly,
+ * but instead should call SDL_PrivateJoystick*() to deliver events
+ * and update joystick device state.
+ */
+void
+SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+{
+ recDevice *device = joystick->hwdata;
+ recElement *element;
+ SInt32 value, range;
+ int i;
+
+ if (!device) {
+ return;
+ }
+
+ if (device->removed) { /* device was unplugged; ignore it. */
+ if (joystick->hwdata) {
+ joystick->force_recentering = SDL_TRUE;
+ joystick->hwdata = NULL;
+ }
+ return;
+ }
+
+ element = device->firstAxis;
+ i = 0;
+ while (element) {
+ value = GetHIDScaledCalibratedState(device, element, -32768, 32767);
+ if (value != joystick->axes[i]) {
+ SDL_PrivateJoystickAxis(joystick, i, value);
+ }
+ element = element->pNext;
+ ++i;
+ }
+
+ element = device->firstButton;
+ i = 0;
+ while (element) {
+ value = GetHIDElementState(device, element);
+ if (value > 1) { /* handle pressure-sensitive buttons */
+ value = 1;
+ }
+ if (value != joystick->buttons[i]) {
+ SDL_PrivateJoystickButton(joystick, i, value);
+ }
+ element = element->pNext;
+ ++i;
+ }
+
+ element = device->firstHat;
+ i = 0;
+ while (element) {
+ Uint8 pos = 0;
+
+ range = (element->max - element->min + 1);
+ value = GetHIDElementState(device, element) - element->min;
+ if (range == 4) { /* 4 position hatswitch - scale up value */
+ value *= 2;
+ } else if (range != 8) { /* Neither a 4 nor 8 positions - fall back to default position (centered) */
+ value = -1;
+ }
+ switch (value) {
+ case 0:
+ pos = SDL_HAT_UP;
+ break;
+ case 1:
+ pos = SDL_HAT_RIGHTUP;
+ break;
+ case 2:
+ pos = SDL_HAT_RIGHT;
+ break;
+ case 3:
+ pos = SDL_HAT_RIGHTDOWN;
+ break;
+ case 4:
+ pos = SDL_HAT_DOWN;
+ break;
+ case 5:
+ pos = SDL_HAT_LEFTDOWN;
+ break;
+ case 6:
+ pos = SDL_HAT_LEFT;
+ break;
+ case 7:
+ pos = SDL_HAT_LEFTUP;
+ break;
+ default:
+ /* Every other value is mapped to center. We do that because some
+ * joysticks use 8 and some 15 for this value, and apparently
+ * there are even more variants out there - so we try to be generous.
+ */
+ pos = SDL_HAT_CENTERED;
+ break;
+ }
+
+ if (pos != joystick->hats[i]) {
+ SDL_PrivateJoystickHat(joystick, i, pos);
+ }
+
+ element = element->pNext;
+ ++i;
+ }
+}
+
+/* Function to close a joystick after use */
+void
+SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+{
+}
+
+/* Function to perform any system-specific joystick related cleanup */
+void
+SDL_SYS_JoystickQuit(void)
+{
+ while (FreeDevice(gpDeviceList)) {
+ /* spin */
+ }
+
+ if (hidman) {
+ IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
+ IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
+ CFRelease(hidman);
+ hidman = NULL;
+ }
+}
+
+
+SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
+{
+ recDevice *device = GetDeviceForIndex(device_index);
+ SDL_JoystickGUID guid;
+ if (device) {
+ guid = device->guid;
+ } else {
+ SDL_zero(guid);
+ }
+ return guid;
+}
+
+SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
+{
+ return joystick->hwdata->guid;
+}
+
+#endif /* SDL_JOYSTICK_IOKIT */
+
+/* vi: set ts=4 sw=4 expandtab: */