2 Simple DirectMedia Layer
3 Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "SDL_config.h"
23 #ifdef SDL_JOYSTICK_IOKIT
25 /* SDL joystick driver for Darwin / Mac OS X, based on the IOKit HID API */
26 /* Written 2001 by Max Horn */
31 #include <mach/mach.h>
32 #include <mach/mach_error.h>
33 #include <IOKit/IOKitLib.h>
34 #include <IOKit/IOCFPlugIn.h>
36 #include <IOKit/hidsystem/IOHIDUsageTables.h>
38 /* The header was moved here in Mac OS X 10.1 */
39 #include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
41 #include <IOKit/hid/IOHIDLib.h>
42 #include <IOKit/hid/IOHIDKeys.h>
43 #include <CoreFoundation/CoreFoundation.h>
44 #include <Carbon/Carbon.h> /* for NewPtrClear, DisposePtr */
46 /* For force feedback testing. */
47 #include <ForceFeedback/ForceFeedback.h>
48 #include <ForceFeedback/ForceFeedbackConstants.h>
50 #include "SDL_joystick.h"
51 #include "../SDL_sysjoystick.h"
52 #include "../SDL_joystick_c.h"
53 #include "SDL_sysjoystick_c.h"
56 /* Linked list of all available devices */
57 static recDevice *gpDeviceList = NULL;
61 HIDReportErrorNum(char *strError, long numError)
63 SDL_SetError(strError);
66 static void HIDGetCollectionElements(CFMutableDictionaryRef deviceProperties,
69 /* returns current value for element, polling element
70 * will return 0 on error conditions which should be accounted for by application
74 HIDGetElementValue(recDevice * pDevice, recElement * pElement)
76 IOReturn result = kIOReturnSuccess;
77 IOHIDEventStruct hidEvent;
80 if (NULL != pDevice && NULL != pElement && NULL != pDevice->interface) {
82 (*(pDevice->interface))->getElementValue(pDevice->interface,
85 if (kIOReturnSuccess == result) {
86 /* record min and max for auto calibration */
87 if (hidEvent.value < pElement->minReport)
88 pElement->minReport = hidEvent.value;
89 if (hidEvent.value > pElement->maxReport)
90 pElement->maxReport = hidEvent.value;
95 return hidEvent.value;
99 HIDScaledCalibratedValue(recDevice * pDevice, recElement * pElement,
102 float deviceScale = max - min;
103 float readScale = pElement->maxReport - pElement->minReport;
104 SInt32 value = HIDGetElementValue(pDevice, pElement);
106 return value; /* no scaling at all */
108 return ((value - pElement->minReport) * deviceScale / readScale) +
114 HIDRemovalCallback(void *target, IOReturn result, void *refcon, void *sender)
116 recDevice *device = (recDevice *) refcon;
118 device->uncentered = 1;
123 /* Create and open an interface to device, required prior to extracting values or building queues.
124 * Note: appliction now owns the device and must close and release it prior to exiting
128 HIDCreateOpenDeviceInterface(io_object_t hidDevice, recDevice * pDevice)
130 IOReturn result = kIOReturnSuccess;
131 HRESULT plugInResult = S_OK;
133 IOCFPlugInInterface **ppPlugInInterface = NULL;
135 if (NULL == pDevice->interface) {
137 IOCreatePlugInInterfaceForService(hidDevice,
138 kIOHIDDeviceUserClientTypeID,
139 kIOCFPlugInInterfaceID,
140 &ppPlugInInterface, &score);
141 if (kIOReturnSuccess == result) {
142 /* Call a method of the intermediate plug-in to create the device interface */
144 (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
146 (kIOHIDDeviceInterfaceID),
148 &(pDevice->interface));
149 if (S_OK != plugInResult)
151 ("CouldnÕt query HID class device interface from plugInInterface",
153 (*ppPlugInInterface)->Release(ppPlugInInterface);
156 ("Failed to create **plugInInterface via IOCreatePlugInInterfaceForService.",
159 if (NULL != pDevice->interface) {
160 result = (*(pDevice->interface))->open(pDevice->interface, 0);
161 if (kIOReturnSuccess != result)
163 ("Failed to open pDevice->interface via open.", result);
165 (*(pDevice->interface))->setRemovalCallback(pDevice->interface,
173 /* Closes and releases interface to device, should be done prior to exting application
174 * Note: will have no affect if device or interface do not exist
175 * application will "own" the device if interface is not closed
176 * (device may have to be plug and re-plugged in different location to get it working again without a restart)
180 HIDCloseReleaseInterface(recDevice * pDevice)
182 IOReturn result = kIOReturnSuccess;
184 if ((NULL != pDevice) && (NULL != pDevice->interface)) {
185 /* close the interface */
186 result = (*(pDevice->interface))->close(pDevice->interface);
187 if (kIOReturnNotOpen == result) {
188 /* do nothing as device was not opened, thus can't be closed */
189 } else if (kIOReturnSuccess != result)
190 HIDReportErrorNum("Failed to close IOHIDDeviceInterface.",
192 /* release the interface */
193 result = (*(pDevice->interface))->Release(pDevice->interface);
194 if (kIOReturnSuccess != result)
195 HIDReportErrorNum("Failed to release IOHIDDeviceInterface.",
197 pDevice->interface = NULL;
202 /* extracts actual specific element information from each element CF dictionary entry */
205 HIDGetElementInfo(CFTypeRef refElement, recElement * pElement)
210 refType = CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementCookieKey));
211 if (refType && CFNumberGetValue(refType, kCFNumberLongType, &number))
212 pElement->cookie = (IOHIDElementCookie) number;
213 refType = CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementMinKey));
214 if (refType && CFNumberGetValue(refType, kCFNumberLongType, &number))
215 pElement->minReport = pElement->min = number;
216 pElement->maxReport = pElement->min;
217 refType = CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementMaxKey));
218 if (refType && CFNumberGetValue(refType, kCFNumberLongType, &number))
219 pElement->maxReport = pElement->max = number;
221 TODO: maybe should handle the following stuff somehow?
223 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMinKey));
224 if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
225 pElement->scaledMin = number;
226 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMaxKey));
227 if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
228 pElement->scaledMax = number;
229 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementSizeKey));
230 if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
231 pElement->size = number;
232 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsRelativeKey));
234 pElement->relative = CFBooleanGetValue (refType);
235 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsWrappingKey));
237 pElement->wrapping = CFBooleanGetValue (refType);
238 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsNonLinearKey));
240 pElement->nonLinear = CFBooleanGetValue (refType);
241 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasPreferedStateKey));
243 pElement->preferredState = CFBooleanGetValue (refType);
244 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasNullStateKey));
246 pElement->nullState = CFBooleanGetValue (refType);
250 /* examines CF dictionary vlaue in device element hierarchy to determine if it is element of interest or a collection of more elements
251 * if element of interest allocate storage, add to list and retrieve element specific info
252 * if collection then pass on to deconstruction collection into additional individual elements
256 HIDAddElement(CFTypeRef refElement, recDevice * pDevice)
258 recElement *element = NULL;
259 recElement **headElement = NULL;
260 long elementType, usagePage, usage;
261 CFTypeRef refElementType =
262 CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementTypeKey));
263 CFTypeRef refUsagePage =
264 CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementUsagePageKey));
266 CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementUsageKey));
271 (CFNumberGetValue(refElementType, kCFNumberLongType, &elementType))) {
272 /* look at types of interest */
273 if ((elementType == kIOHIDElementTypeInput_Misc)
274 || (elementType == kIOHIDElementTypeInput_Button)
275 || (elementType == kIOHIDElementTypeInput_Axis)) {
277 && CFNumberGetValue(refUsagePage, kCFNumberLongType,
278 &usagePage) && refUsage
279 && CFNumberGetValue(refUsage, kCFNumberLongType, &usage)) {
280 switch (usagePage) { /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */
281 case kHIDPage_GenericDesktop:
283 switch (usage) { /* look at usage to determine function */
287 case kHIDUsage_GD_Rx:
288 case kHIDUsage_GD_Ry:
289 case kHIDUsage_GD_Rz:
290 case kHIDUsage_GD_Slider:
291 case kHIDUsage_GD_Dial:
292 case kHIDUsage_GD_Wheel:
293 element = (recElement *)
294 NewPtrClear(sizeof(recElement));
297 headElement = &(pDevice->firstAxis);
300 case kHIDUsage_GD_Hatswitch:
301 element = (recElement *)
302 NewPtrClear(sizeof(recElement));
305 headElement = &(pDevice->firstHat);
311 case kHIDPage_Button:
312 element = (recElement *)
313 NewPtrClear(sizeof(recElement));
316 headElement = &(pDevice->firstButton);
323 } else if (kIOHIDElementTypeCollection == elementType)
324 HIDGetCollectionElements((CFMutableDictionaryRef) refElement,
328 if (element && headElement) { /* add to list */
329 recElement *elementPrevious = NULL;
330 recElement *elementCurrent = *headElement;
331 while (elementCurrent && usage >= elementCurrent->usage) {
332 elementPrevious = elementCurrent;
333 elementCurrent = elementCurrent->pNext;
335 if (elementPrevious) {
336 elementPrevious->pNext = element;
338 *headElement = element;
340 element->usagePage = usagePage;
341 element->usage = usage;
342 element->pNext = elementCurrent;
343 HIDGetElementInfo(refElement, element);
348 /* collects information from each array member in device element list (each array memeber = element) */
351 HIDGetElementsCFArrayHandler(const void *value, void *parameter)
353 if (CFGetTypeID(value) == CFDictionaryGetTypeID())
354 HIDAddElement((CFTypeRef) value, (recDevice *) parameter);
357 /* handles retrieval of element information from arrays of elements in device IO registry information */
360 HIDGetElements(CFTypeRef refElementCurrent, recDevice * pDevice)
362 CFTypeID type = CFGetTypeID(refElementCurrent);
363 if (type == CFArrayGetTypeID()) { /* if element is an array */
364 CFRange range = { 0, CFArrayGetCount(refElementCurrent) };
365 /* CountElementsCFArrayHandler called for each array member */
366 CFArrayApplyFunction(refElementCurrent, range,
367 HIDGetElementsCFArrayHandler, pDevice);
371 /* handles extracting element information from element collection CF types
372 * used from top level element decoding and hierarchy deconstruction to flatten device element list
376 HIDGetCollectionElements(CFMutableDictionaryRef deviceProperties,
379 CFTypeRef refElementTop =
380 CFDictionaryGetValue(deviceProperties, CFSTR(kIOHIDElementKey));
382 HIDGetElements(refElementTop, pDevice);
385 /* use top level element usage page and usage to discern device usage page and usage setting appropriate vlaues in device record */
388 HIDTopLevelElementHandler(const void *value, void *parameter)
391 if (CFGetTypeID(value) != CFDictionaryGetTypeID())
393 refCF = CFDictionaryGetValue(value, CFSTR(kIOHIDElementUsagePageKey));
394 if (!CFNumberGetValue
395 (refCF, kCFNumberLongType, &((recDevice *) parameter)->usagePage))
396 SDL_SetError("CFNumberGetValue error retrieving pDevice->usagePage.");
397 refCF = CFDictionaryGetValue(value, CFSTR(kIOHIDElementUsageKey));
398 if (!CFNumberGetValue
399 (refCF, kCFNumberLongType, &((recDevice *) parameter)->usage))
400 SDL_SetError("CFNumberGetValue error retrieving pDevice->usage.");
403 /* extracts device info from CF dictionary records in IO registry */
406 HIDGetDeviceInfo(io_object_t hidDevice, CFMutableDictionaryRef hidProperties,
409 CFMutableDictionaryRef usbProperties = 0;
410 io_registry_entry_t parent1, parent2;
412 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
413 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
416 IORegistryEntryGetParentEntry(hidDevice, kIOServicePlane, &parent1))
418 IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2))
420 IORegistryEntryCreateCFProperties(parent2, &usbProperties,
426 * try hid dictionary first, if fail then go to usb dictionary
430 /* get product name */
432 CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductKey));
435 CFDictionaryGetValue(usbProperties,
436 CFSTR("USB Product Name"));
438 if (!CFStringGetCString
439 (refCF, pDevice->product, 256,
440 CFStringGetSystemEncoding()))
442 ("CFStringGetCString error retrieving pDevice->product.");
445 /* get usage page and usage */
447 CFDictionaryGetValue(hidProperties,
448 CFSTR(kIOHIDPrimaryUsagePageKey));
450 if (!CFNumberGetValue
451 (refCF, kCFNumberLongType, &pDevice->usagePage))
453 ("CFNumberGetValue error retrieving pDevice->usagePage.");
455 CFDictionaryGetValue(hidProperties,
456 CFSTR(kIOHIDPrimaryUsageKey));
458 if (!CFNumberGetValue
459 (refCF, kCFNumberLongType, &pDevice->usage))
461 ("CFNumberGetValue error retrieving pDevice->usage.");
464 if (NULL == refCF) { /* get top level element HID usage page or usage */
465 /* use top level element instead */
466 CFTypeRef refCFTopElement = 0;
468 CFDictionaryGetValue(hidProperties,
469 CFSTR(kIOHIDElementKey));
471 /* refCFTopElement points to an array of element dictionaries */
472 CFRange range = { 0, CFArrayGetCount(refCFTopElement) };
473 CFArrayApplyFunction(refCFTopElement, range,
474 HIDTopLevelElementHandler, pDevice);
478 CFRelease(usbProperties);
481 ("IORegistryEntryCreateCFProperties failed to create usbProperties.");
483 if (kIOReturnSuccess != IOObjectRelease(parent2))
484 SDL_SetError("IOObjectRelease error with parent2.");
485 if (kIOReturnSuccess != IOObjectRelease(parent1))
486 SDL_SetError("IOObjectRelease error with parent1.");
492 HIDBuildDevice(io_object_t hidDevice)
494 recDevice *pDevice = (recDevice *) NewPtrClear(sizeof(recDevice));
496 /* get dictionary for HID properties */
497 CFMutableDictionaryRef hidProperties = 0;
498 kern_return_t result =
499 IORegistryEntryCreateCFProperties(hidDevice, &hidProperties,
502 if ((result == KERN_SUCCESS) && hidProperties) {
503 /* create device interface */
504 result = HIDCreateOpenDeviceInterface(hidDevice, pDevice);
505 if (kIOReturnSuccess == result) {
506 HIDGetDeviceInfo(hidDevice, hidProperties, pDevice); /* hidDevice used to find parents in registry tree */
507 HIDGetCollectionElements(hidProperties, pDevice);
509 DisposePtr((Ptr) pDevice);
512 CFRelease(hidProperties);
514 DisposePtr((Ptr) pDevice);
521 /* disposes of the element list associated with a device and the memory associated with the list
525 HIDDisposeElementList(recElement ** elementList)
527 recElement *pElement = *elementList;
529 recElement *pElementNext = pElement->pNext;
530 DisposePtr((Ptr) pElement);
531 pElement = pElementNext;
536 /* disposes of a single device, closing and releaseing interface, freeing memory fro device and elements, setting device pointer to NULL
537 * all your device no longer belong to us... (i.e., you do not 'own' the device anymore)
541 HIDDisposeDevice(recDevice ** ppDevice)
543 kern_return_t result = KERN_SUCCESS;
544 recDevice *pDeviceNext = NULL;
546 /* save next device prior to disposing of this device */
547 pDeviceNext = (*ppDevice)->pNext;
549 /* free posible io_service_t */
550 if ((*ppDevice)->ffservice) {
551 IOObjectRelease((*ppDevice)->ffservice);
552 (*ppDevice)->ffservice = 0;
555 /* free element lists */
556 HIDDisposeElementList(&(*ppDevice)->firstAxis);
557 HIDDisposeElementList(&(*ppDevice)->firstButton);
558 HIDDisposeElementList(&(*ppDevice)->firstHat);
560 result = HIDCloseReleaseInterface(*ppDevice); /* function sanity checks interface value (now application does not own device) */
561 if (kIOReturnSuccess != result)
563 ("HIDCloseReleaseInterface failed when trying to dipose device.",
565 DisposePtr((Ptr) * ppDevice);
572 /* Function to scan the system for joysticks.
573 * Joystick 0 should be the system default joystick.
574 * This function should return the number of available joysticks, or -1
575 * on an unrecoverable fatal error.
578 SDL_SYS_JoystickInit(void)
580 IOReturn result = kIOReturnSuccess;
581 mach_port_t masterPort = 0;
582 io_iterator_t hidObjectIterator = 0;
583 CFMutableDictionaryRef hidMatchDictionary = NULL;
584 recDevice *device, *lastDevice;
585 io_object_t ioHIDDeviceObject = 0;
587 SDL_numjoysticks = 0;
590 SDL_SetError("Joystick: Device list already inited.");
594 result = IOMasterPort(bootstrap_port, &masterPort);
595 if (kIOReturnSuccess != result) {
596 SDL_SetError("Joystick: IOMasterPort error with bootstrap_port.");
600 /* Set up a matching dictionary to search I/O Registry by class name for all HID class devices. */
601 hidMatchDictionary = IOServiceMatching(kIOHIDDeviceKey);
602 if (hidMatchDictionary) {
603 /* Add key for device type (joystick, in this case) to refine the matching dictionary. */
605 /* NOTE: we now perform this filtering later
606 UInt32 usagePage = kHIDPage_GenericDesktop;
607 UInt32 usage = kHIDUsage_GD_Joystick;
608 CFNumberRef refUsage = NULL, refUsagePage = NULL;
610 refUsage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usage);
611 CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsageKey), refUsage);
612 refUsagePage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usagePage);
613 CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsagePageKey), refUsagePage);
617 ("Joystick: Failed to get HID CFMutableDictionaryRef via IOServiceMatching.");
621 /*/ Now search I/O Registry for matching devices. */
623 IOServiceGetMatchingServices(masterPort, hidMatchDictionary,
625 /* Check for errors */
626 if (kIOReturnSuccess != result) {
627 SDL_SetError("Joystick: Couldn't create a HID object iterator.");
630 if (!hidObjectIterator) { /* there are no joysticks */
632 SDL_numjoysticks = 0;
635 /* IOServiceGetMatchingServices consumes a reference to the dictionary, so we don't need to release the dictionary ref. */
637 /* build flat linked list of devices from device iterator */
639 gpDeviceList = lastDevice = NULL;
641 while ((ioHIDDeviceObject = IOIteratorNext(hidObjectIterator))) {
642 /* build a device record */
643 device = HIDBuildDevice(ioHIDDeviceObject);
647 /* Filter device list to non-keyboard/mouse stuff */
648 if ((device->usagePage != kHIDPage_GenericDesktop) ||
649 ((device->usage != kHIDUsage_GD_Joystick &&
650 device->usage != kHIDUsage_GD_GamePad &&
651 device->usage != kHIDUsage_GD_MultiAxisController))) {
653 /* release memory for the device */
654 HIDDisposeDevice(&device);
655 DisposePtr((Ptr) device);
659 /* We have to do some storage of the io_service_t for
660 * SDL_HapticOpenFromJoystick */
661 if (FFIsForceFeedback(ioHIDDeviceObject) == FF_OK) {
662 device->ffservice = ioHIDDeviceObject;
664 device->ffservice = 0;
667 /* Add device to the end of the list */
669 lastDevice->pNext = device;
671 gpDeviceList = device;
674 result = IOObjectRelease(hidObjectIterator); /* release the iterator */
676 /* Count the total number of devices we found */
677 device = gpDeviceList;
680 device = device->pNext;
683 return SDL_numjoysticks;
686 /* Function to get the device-dependent name of a joystick */
688 SDL_SYS_JoystickName(int index)
690 recDevice *device = gpDeviceList;
692 for (; index > 0; index--)
693 device = device->pNext;
695 return device->product;
698 /* Function to open a joystick for use.
699 * The joystick to open is specified by the index field of the joystick.
700 * This should fill the nbuttons and naxes fields of the joystick structure.
701 * It returns 0, or -1 if there is an error.
704 SDL_SYS_JoystickOpen(SDL_Joystick * joystick)
706 recDevice *device = gpDeviceList;
709 for (index = joystick->index; index > 0; index--)
710 device = device->pNext;
712 joystick->hwdata = device;
713 joystick->name = device->product;
715 joystick->naxes = device->axes;
716 joystick->nhats = device->hats;
717 joystick->nballs = 0;
718 joystick->nbuttons = device->buttons;
723 /* Function to update the state of a joystick - called as a device poll.
724 * This function shouldn't update the joystick structure directly,
725 * but instead should call SDL_PrivateJoystick*() to deliver events
726 * and update joystick device state.
729 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
731 recDevice *device = joystick->hwdata;
736 if (device->removed) { /* device was unplugged; ignore it. */
737 if (device->uncentered) {
738 device->uncentered = 0;
740 /* Tell the app that everything is centered/unpressed... */
741 for (i = 0; i < device->axes; i++)
742 SDL_PrivateJoystickAxis(joystick, i, 0);
744 for (i = 0; i < device->buttons; i++)
745 SDL_PrivateJoystickButton(joystick, i, 0);
747 for (i = 0; i < device->hats; i++)
748 SDL_PrivateJoystickHat(joystick, i, SDL_HAT_CENTERED);
754 element = device->firstAxis;
757 value = HIDScaledCalibratedValue(device, element, -32768, 32767);
758 if (value != joystick->axes[i])
759 SDL_PrivateJoystickAxis(joystick, i, value);
760 element = element->pNext;
764 element = device->firstButton;
767 value = HIDGetElementValue(device, element);
768 if (value > 1) /* handle pressure-sensitive buttons */
770 if (value != joystick->buttons[i])
771 SDL_PrivateJoystickButton(joystick, i, value);
772 element = element->pNext;
776 element = device->firstHat;
781 range = (element->max - element->min + 1);
782 value = HIDGetElementValue(device, element) - element->min;
783 if (range == 4) /* 4 position hatswitch - scale up value */
785 else if (range != 8) /* Neither a 4 nor 8 positions - fall back to default position (centered) */
792 pos = SDL_HAT_RIGHTUP;
798 pos = SDL_HAT_RIGHTDOWN;
804 pos = SDL_HAT_LEFTDOWN;
810 pos = SDL_HAT_LEFTUP;
813 /* Every other value is mapped to center. We do that because some
814 * joysticks use 8 and some 15 for this value, and apparently
815 * there are even more variants out there - so we try to be generous.
817 pos = SDL_HAT_CENTERED;
820 if (pos != joystick->hats[i])
821 SDL_PrivateJoystickHat(joystick, i, pos);
822 element = element->pNext;
829 /* Function to close a joystick after use */
831 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
833 /* Should we do anything here? */
837 /* Function to perform any system-specific joystick related cleanup */
839 SDL_SYS_JoystickQuit(void)
841 while (NULL != gpDeviceList)
842 gpDeviceList = HIDDisposeDevice(&gpDeviceList);
845 #endif /* SDL_JOYSTICK_IOKIT */
846 /* vi: set ts=4 sw=4 expandtab: */