OSDN Git Service

remove ffmpeg from this project. i will put the ffmpeg to "external/ffmpeg" path
[android-x86/external-stagefright-plugins.git] / SDL-1.3 / android-project / jni / SDL / src / joystick / darwin / SDL_sysjoystick.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2012 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_config.h"
22
23 #ifdef SDL_JOYSTICK_IOKIT
24
25 /* SDL joystick driver for Darwin / Mac OS X, based on the IOKit HID API */
26 /* Written 2001 by Max Horn */
27
28 #include <unistd.h>
29 #include <ctype.h>
30 #include <sysexits.h>
31 #include <mach/mach.h>
32 #include <mach/mach_error.h>
33 #include <IOKit/IOKitLib.h>
34 #include <IOKit/IOCFPlugIn.h>
35 #ifdef MACOS_10_0_4
36 #include <IOKit/hidsystem/IOHIDUsageTables.h>
37 #else
38 /* The header was moved here in Mac OS X 10.1 */
39 #include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
40 #endif
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 */
45
46 /* For force feedback testing. */
47 #include <ForceFeedback/ForceFeedback.h>
48 #include <ForceFeedback/ForceFeedbackConstants.h>
49
50 #include "SDL_joystick.h"
51 #include "../SDL_sysjoystick.h"
52 #include "../SDL_joystick_c.h"
53 #include "SDL_sysjoystick_c.h"
54
55
56 /* Linked list of all available devices */
57 static recDevice *gpDeviceList = NULL;
58
59
60 static void
61 HIDReportErrorNum(char *strError, long numError)
62 {
63     SDL_SetError(strError);
64 }
65
66 static void HIDGetCollectionElements(CFMutableDictionaryRef deviceProperties,
67                                      recDevice * pDevice);
68
69 /* returns current value for element, polling element
70  * will return 0 on error conditions which should be accounted for by application
71  */
72
73 static SInt32
74 HIDGetElementValue(recDevice * pDevice, recElement * pElement)
75 {
76     IOReturn result = kIOReturnSuccess;
77     IOHIDEventStruct hidEvent;
78     hidEvent.value = 0;
79
80     if (NULL != pDevice && NULL != pElement && NULL != pDevice->interface) {
81         result =
82             (*(pDevice->interface))->getElementValue(pDevice->interface,
83                                                      pElement->cookie,
84                                                      &hidEvent);
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;
91         }
92     }
93
94     /* auto user scale */
95     return hidEvent.value;
96 }
97
98 static SInt32
99 HIDScaledCalibratedValue(recDevice * pDevice, recElement * pElement,
100                          long min, long max)
101 {
102     float deviceScale = max - min;
103     float readScale = pElement->maxReport - pElement->minReport;
104     SInt32 value = HIDGetElementValue(pDevice, pElement);
105     if (readScale == 0)
106         return value;           /* no scaling at all */
107     else
108         return ((value - pElement->minReport) * deviceScale / readScale) +
109             min;
110 }
111
112
113 static void
114 HIDRemovalCallback(void *target, IOReturn result, void *refcon, void *sender)
115 {
116     recDevice *device = (recDevice *) refcon;
117     device->removed = 1;
118     device->uncentered = 1;
119 }
120
121
122
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
125  */
126
127 static IOReturn
128 HIDCreateOpenDeviceInterface(io_object_t hidDevice, recDevice * pDevice)
129 {
130     IOReturn result = kIOReturnSuccess;
131     HRESULT plugInResult = S_OK;
132     SInt32 score = 0;
133     IOCFPlugInInterface **ppPlugInInterface = NULL;
134
135     if (NULL == pDevice->interface) {
136         result =
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 */
143             plugInResult =
144                 (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
145                                                      CFUUIDGetUUIDBytes
146                                                      (kIOHIDDeviceInterfaceID),
147                                                      (void *)
148                                                      &(pDevice->interface));
149             if (S_OK != plugInResult)
150                 HIDReportErrorNum
151                     ("CouldnĂ•t query HID class device interface from plugInInterface",
152                      plugInResult);
153             (*ppPlugInInterface)->Release(ppPlugInInterface);
154         } else
155             HIDReportErrorNum
156                 ("Failed to create **plugInInterface via IOCreatePlugInInterfaceForService.",
157                  result);
158     }
159     if (NULL != pDevice->interface) {
160         result = (*(pDevice->interface))->open(pDevice->interface, 0);
161         if (kIOReturnSuccess != result)
162             HIDReportErrorNum
163                 ("Failed to open pDevice->interface via open.", result);
164         else
165             (*(pDevice->interface))->setRemovalCallback(pDevice->interface,
166                                                         HIDRemovalCallback,
167                                                         pDevice, pDevice);
168
169     }
170     return result;
171 }
172
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)
177  */
178
179 static IOReturn
180 HIDCloseReleaseInterface(recDevice * pDevice)
181 {
182     IOReturn result = kIOReturnSuccess;
183
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.",
191                               result);
192         /* release the interface */
193         result = (*(pDevice->interface))->Release(pDevice->interface);
194         if (kIOReturnSuccess != result)
195             HIDReportErrorNum("Failed to release IOHIDDeviceInterface.",
196                               result);
197         pDevice->interface = NULL;
198     }
199     return result;
200 }
201
202 /* extracts actual specific element information from each element CF dictionary entry */
203
204 static void
205 HIDGetElementInfo(CFTypeRef refElement, recElement * pElement)
206 {
207     long number;
208     CFTypeRef refType;
209
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;
220 /*
221         TODO: maybe should handle the following stuff somehow?
222
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));
233         if (refType)
234                 pElement->relative = CFBooleanGetValue (refType);
235         refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsWrappingKey));
236         if (refType)
237                 pElement->wrapping = CFBooleanGetValue (refType);
238         refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsNonLinearKey));
239         if (refType)
240                 pElement->nonLinear = CFBooleanGetValue (refType);
241         refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasPreferedStateKey));
242         if (refType)
243                 pElement->preferredState = CFBooleanGetValue (refType);
244         refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasNullStateKey));
245         if (refType)
246                 pElement->nullState = CFBooleanGetValue (refType);
247 */
248 }
249
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
253  */
254
255 static void
256 HIDAddElement(CFTypeRef refElement, recDevice * pDevice)
257 {
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));
265     CFTypeRef refUsage =
266         CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementUsageKey));
267
268
269     if ((refElementType)
270         &&
271         (CFNumberGetValue(refElementType, kCFNumberLongType, &elementType))) {
272         /* look at types of interest */
273         if ((elementType == kIOHIDElementTypeInput_Misc)
274             || (elementType == kIOHIDElementTypeInput_Button)
275             || (elementType == kIOHIDElementTypeInput_Axis)) {
276             if (refUsagePage
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:
282                     {
283                         switch (usage) {        /* look at usage to determine function */
284                         case kHIDUsage_GD_X:
285                         case kHIDUsage_GD_Y:
286                         case kHIDUsage_GD_Z:
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));
295                             if (element) {
296                                 pDevice->axes++;
297                                 headElement = &(pDevice->firstAxis);
298                             }
299                             break;
300                         case kHIDUsage_GD_Hatswitch:
301                             element = (recElement *)
302                                 NewPtrClear(sizeof(recElement));
303                             if (element) {
304                                 pDevice->hats++;
305                                 headElement = &(pDevice->firstHat);
306                             }
307                             break;
308                         }
309                     }
310                     break;
311                 case kHIDPage_Button:
312                     element = (recElement *)
313                         NewPtrClear(sizeof(recElement));
314                     if (element) {
315                         pDevice->buttons++;
316                         headElement = &(pDevice->firstButton);
317                     }
318                     break;
319                 default:
320                     break;
321                 }
322             }
323         } else if (kIOHIDElementTypeCollection == elementType)
324             HIDGetCollectionElements((CFMutableDictionaryRef) refElement,
325                                      pDevice);
326     }
327
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;
334         }
335         if (elementPrevious) {
336             elementPrevious->pNext = element;
337         } else {
338             *headElement = element;
339         }
340         element->usagePage = usagePage;
341         element->usage = usage;
342         element->pNext = elementCurrent;
343         HIDGetElementInfo(refElement, element);
344         pDevice->elements++;
345     }
346 }
347
348 /* collects information from each array member in device element list (each array memeber = element) */
349
350 static void
351 HIDGetElementsCFArrayHandler(const void *value, void *parameter)
352 {
353     if (CFGetTypeID(value) == CFDictionaryGetTypeID())
354         HIDAddElement((CFTypeRef) value, (recDevice *) parameter);
355 }
356
357 /* handles retrieval of element information from arrays of elements in device IO registry information */
358
359 static void
360 HIDGetElements(CFTypeRef refElementCurrent, recDevice * pDevice)
361 {
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);
368     }
369 }
370
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
373  */
374
375 static void
376 HIDGetCollectionElements(CFMutableDictionaryRef deviceProperties,
377                          recDevice * pDevice)
378 {
379     CFTypeRef refElementTop =
380         CFDictionaryGetValue(deviceProperties, CFSTR(kIOHIDElementKey));
381     if (refElementTop)
382         HIDGetElements(refElementTop, pDevice);
383 }
384
385 /* use top level element usage page and usage to discern device usage page and usage setting appropriate vlaues in device record */
386
387 static void
388 HIDTopLevelElementHandler(const void *value, void *parameter)
389 {
390     CFTypeRef refCF = 0;
391     if (CFGetTypeID(value) != CFDictionaryGetTypeID())
392         return;
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.");
401 }
402
403 /* extracts device info from CF dictionary records in IO registry */
404
405 static void
406 HIDGetDeviceInfo(io_object_t hidDevice, CFMutableDictionaryRef hidProperties,
407                  recDevice * pDevice)
408 {
409     CFMutableDictionaryRef usbProperties = 0;
410     io_registry_entry_t parent1, parent2;
411
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
414      */
415     if ((KERN_SUCCESS ==
416          IORegistryEntryGetParentEntry(hidDevice, kIOServicePlane, &parent1))
417         && (KERN_SUCCESS ==
418             IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2))
419         && (KERN_SUCCESS ==
420             IORegistryEntryCreateCFProperties(parent2, &usbProperties,
421                                               kCFAllocatorDefault,
422                                               kNilOptions))) {
423         if (usbProperties) {
424             CFTypeRef refCF = 0;
425             /* get device info
426              * try hid dictionary first, if fail then go to usb dictionary
427              */
428
429
430             /* get product name */
431             refCF =
432                 CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductKey));
433             if (!refCF)
434                 refCF =
435                     CFDictionaryGetValue(usbProperties,
436                                          CFSTR("USB Product Name"));
437             if (refCF) {
438                 if (!CFStringGetCString
439                     (refCF, pDevice->product, 256,
440                      CFStringGetSystemEncoding()))
441                     SDL_SetError
442                         ("CFStringGetCString error retrieving pDevice->product.");
443             }
444
445             /* get usage page and usage */
446             refCF =
447                 CFDictionaryGetValue(hidProperties,
448                                      CFSTR(kIOHIDPrimaryUsagePageKey));
449             if (refCF) {
450                 if (!CFNumberGetValue
451                     (refCF, kCFNumberLongType, &pDevice->usagePage))
452                     SDL_SetError
453                         ("CFNumberGetValue error retrieving pDevice->usagePage.");
454                 refCF =
455                     CFDictionaryGetValue(hidProperties,
456                                          CFSTR(kIOHIDPrimaryUsageKey));
457                 if (refCF)
458                     if (!CFNumberGetValue
459                         (refCF, kCFNumberLongType, &pDevice->usage))
460                         SDL_SetError
461                             ("CFNumberGetValue error retrieving pDevice->usage.");
462             }
463
464             if (NULL == refCF) {        /* get top level element HID usage page or usage */
465                 /* use top level element instead */
466                 CFTypeRef refCFTopElement = 0;
467                 refCFTopElement =
468                     CFDictionaryGetValue(hidProperties,
469                                          CFSTR(kIOHIDElementKey));
470                 {
471                     /* refCFTopElement points to an array of element dictionaries */
472                     CFRange range = { 0, CFArrayGetCount(refCFTopElement) };
473                     CFArrayApplyFunction(refCFTopElement, range,
474                                          HIDTopLevelElementHandler, pDevice);
475                 }
476             }
477
478             CFRelease(usbProperties);
479         } else
480             SDL_SetError
481                 ("IORegistryEntryCreateCFProperties failed to create usbProperties.");
482
483         if (kIOReturnSuccess != IOObjectRelease(parent2))
484             SDL_SetError("IOObjectRelease error with parent2.");
485         if (kIOReturnSuccess != IOObjectRelease(parent1))
486             SDL_SetError("IOObjectRelease error with parent1.");
487     }
488 }
489
490
491 static recDevice *
492 HIDBuildDevice(io_object_t hidDevice)
493 {
494     recDevice *pDevice = (recDevice *) NewPtrClear(sizeof(recDevice));
495     if (pDevice) {
496         /* get dictionary for HID properties */
497         CFMutableDictionaryRef hidProperties = 0;
498         kern_return_t result =
499             IORegistryEntryCreateCFProperties(hidDevice, &hidProperties,
500                                               kCFAllocatorDefault,
501                                               kNilOptions);
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);
508             } else {
509                 DisposePtr((Ptr) pDevice);
510                 pDevice = NULL;
511             }
512             CFRelease(hidProperties);
513         } else {
514             DisposePtr((Ptr) pDevice);
515             pDevice = NULL;
516         }
517     }
518     return pDevice;
519 }
520
521 /* disposes of the element list associated with a device and the memory associated with the list
522  */
523
524 static void
525 HIDDisposeElementList(recElement ** elementList)
526 {
527     recElement *pElement = *elementList;
528     while (pElement) {
529         recElement *pElementNext = pElement->pNext;
530         DisposePtr((Ptr) pElement);
531         pElement = pElementNext;
532     }
533     *elementList = NULL;
534 }
535
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)
538  */
539
540 static recDevice *
541 HIDDisposeDevice(recDevice ** ppDevice)
542 {
543     kern_return_t result = KERN_SUCCESS;
544     recDevice *pDeviceNext = NULL;
545     if (*ppDevice) {
546         /* save next device prior to disposing of this device */
547         pDeviceNext = (*ppDevice)->pNext;
548
549         /* free posible io_service_t */
550         if ((*ppDevice)->ffservice) {
551             IOObjectRelease((*ppDevice)->ffservice);
552             (*ppDevice)->ffservice = 0;
553         }
554
555         /* free element lists */
556         HIDDisposeElementList(&(*ppDevice)->firstAxis);
557         HIDDisposeElementList(&(*ppDevice)->firstButton);
558         HIDDisposeElementList(&(*ppDevice)->firstHat);
559
560         result = HIDCloseReleaseInterface(*ppDevice);   /* function sanity checks interface value (now application does not own device) */
561         if (kIOReturnSuccess != result)
562             HIDReportErrorNum
563                 ("HIDCloseReleaseInterface failed when trying to dipose device.",
564                  result);
565         DisposePtr((Ptr) * ppDevice);
566         *ppDevice = NULL;
567     }
568     return pDeviceNext;
569 }
570
571
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.
576  */
577 int
578 SDL_SYS_JoystickInit(void)
579 {
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;
586
587     SDL_numjoysticks = 0;
588
589     if (gpDeviceList) {
590         SDL_SetError("Joystick: Device list already inited.");
591         return -1;
592     }
593
594     result = IOMasterPort(bootstrap_port, &masterPort);
595     if (kIOReturnSuccess != result) {
596         SDL_SetError("Joystick: IOMasterPort error with bootstrap_port.");
597         return -1;
598     }
599
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. */
604
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;
609
610            refUsage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usage);
611            CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsageKey), refUsage);
612            refUsagePage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usagePage);
613            CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsagePageKey), refUsagePage);
614          */
615     } else {
616         SDL_SetError
617             ("Joystick: Failed to get HID CFMutableDictionaryRef via IOServiceMatching.");
618         return -1;
619     }
620
621     /*/ Now search I/O Registry for matching devices. */
622     result =
623         IOServiceGetMatchingServices(masterPort, hidMatchDictionary,
624                                      &hidObjectIterator);
625     /* Check for errors */
626     if (kIOReturnSuccess != result) {
627         SDL_SetError("Joystick: Couldn't create a HID object iterator.");
628         return -1;
629     }
630     if (!hidObjectIterator) {   /* there are no joysticks */
631         gpDeviceList = NULL;
632         SDL_numjoysticks = 0;
633         return 0;
634     }
635     /* IOServiceGetMatchingServices consumes a reference to the dictionary, so we don't need to release the dictionary ref. */
636
637     /* build flat linked list of devices from device iterator */
638
639     gpDeviceList = lastDevice = NULL;
640
641     while ((ioHIDDeviceObject = IOIteratorNext(hidObjectIterator))) {
642         /* build a device record */
643         device = HIDBuildDevice(ioHIDDeviceObject);
644         if (!device)
645             continue;
646
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))) {
652
653             /* release memory for the device */
654             HIDDisposeDevice(&device);
655             DisposePtr((Ptr) device);
656             continue;
657         }
658
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;
663         } else {
664             device->ffservice = 0;
665         }
666
667         /* Add device to the end of the list */
668         if (lastDevice)
669             lastDevice->pNext = device;
670         else
671             gpDeviceList = device;
672         lastDevice = device;
673     }
674     result = IOObjectRelease(hidObjectIterator);        /* release the iterator */
675
676     /* Count the total number of devices we found */
677     device = gpDeviceList;
678     while (device) {
679         SDL_numjoysticks++;
680         device = device->pNext;
681     }
682
683     return SDL_numjoysticks;
684 }
685
686 /* Function to get the device-dependent name of a joystick */
687 const char *
688 SDL_SYS_JoystickName(int index)
689 {
690     recDevice *device = gpDeviceList;
691
692     for (; index > 0; index--)
693         device = device->pNext;
694
695     return device->product;
696 }
697
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.
702  */
703 int
704 SDL_SYS_JoystickOpen(SDL_Joystick * joystick)
705 {
706     recDevice *device = gpDeviceList;
707     int index;
708
709     for (index = joystick->index; index > 0; index--)
710         device = device->pNext;
711
712     joystick->hwdata = device;
713     joystick->name = device->product;
714
715     joystick->naxes = device->axes;
716     joystick->nhats = device->hats;
717     joystick->nballs = 0;
718     joystick->nbuttons = device->buttons;
719
720     return 0;
721 }
722
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.
727  */
728 void
729 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
730 {
731     recDevice *device = joystick->hwdata;
732     recElement *element;
733     SInt32 value, range;
734     int i;
735
736     if (device->removed) {      /* device was unplugged; ignore it. */
737         if (device->uncentered) {
738             device->uncentered = 0;
739
740             /* Tell the app that everything is centered/unpressed... */
741             for (i = 0; i < device->axes; i++)
742                 SDL_PrivateJoystickAxis(joystick, i, 0);
743
744             for (i = 0; i < device->buttons; i++)
745                 SDL_PrivateJoystickButton(joystick, i, 0);
746
747             for (i = 0; i < device->hats; i++)
748                 SDL_PrivateJoystickHat(joystick, i, SDL_HAT_CENTERED);
749         }
750
751         return;
752     }
753
754     element = device->firstAxis;
755     i = 0;
756     while (element) {
757         value = HIDScaledCalibratedValue(device, element, -32768, 32767);
758         if (value != joystick->axes[i])
759             SDL_PrivateJoystickAxis(joystick, i, value);
760         element = element->pNext;
761         ++i;
762     }
763
764     element = device->firstButton;
765     i = 0;
766     while (element) {
767         value = HIDGetElementValue(device, element);
768         if (value > 1)          /* handle pressure-sensitive buttons */
769             value = 1;
770         if (value != joystick->buttons[i])
771             SDL_PrivateJoystickButton(joystick, i, value);
772         element = element->pNext;
773         ++i;
774     }
775
776     element = device->firstHat;
777     i = 0;
778     while (element) {
779         Uint8 pos = 0;
780
781         range = (element->max - element->min + 1);
782         value = HIDGetElementValue(device, element) - element->min;
783         if (range == 4)         /* 4 position hatswitch - scale up value */
784             value *= 2;
785         else if (range != 8)    /* Neither a 4 nor 8 positions - fall back to default position (centered) */
786             value = -1;
787         switch (value) {
788         case 0:
789             pos = SDL_HAT_UP;
790             break;
791         case 1:
792             pos = SDL_HAT_RIGHTUP;
793             break;
794         case 2:
795             pos = SDL_HAT_RIGHT;
796             break;
797         case 3:
798             pos = SDL_HAT_RIGHTDOWN;
799             break;
800         case 4:
801             pos = SDL_HAT_DOWN;
802             break;
803         case 5:
804             pos = SDL_HAT_LEFTDOWN;
805             break;
806         case 6:
807             pos = SDL_HAT_LEFT;
808             break;
809         case 7:
810             pos = SDL_HAT_LEFTUP;
811             break;
812         default:
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.
816              */
817             pos = SDL_HAT_CENTERED;
818             break;
819         }
820         if (pos != joystick->hats[i])
821             SDL_PrivateJoystickHat(joystick, i, pos);
822         element = element->pNext;
823         ++i;
824     }
825
826     return;
827 }
828
829 /* Function to close a joystick after use */
830 void
831 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
832 {
833     /* Should we do anything here? */
834     return;
835 }
836
837 /* Function to perform any system-specific joystick related cleanup */
838 void
839 SDL_SYS_JoystickQuit(void)
840 {
841     while (NULL != gpDeviceList)
842         gpDeviceList = HIDDisposeDevice(&gpDeviceList);
843 }
844
845 #endif /* SDL_JOYSTICK_IOKIT */
846 /* vi: set ts=4 sw=4 expandtab: */