1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2012 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Sam Lantinga
20 slouken@libsdl.org
21 */
22 #include "SDL_config.h"
23
24 #ifdef SDL_JOYSTICK_IOKIT
25
26 /* SDL joystick driver for Darwin / Mac OS X, based on the IOKit HID API */
27 /* Written 2001 by Max Horn */
28
29 #include <unistd.h>
30 #include <ctype.h>
31 #include <sysexits.h>
32 #include <mach/mach.h>
33 #include <mach/mach_error.h>
34 #include <IOKit/IOKitLib.h>
35 #include <IOKit/IOCFPlugIn.h>
36 #ifdef MACOS_10_0_4
37 #include <IOKit/hidsystem/IOHIDUsageTables.h>
38 #else
39 /* The header was moved here in Mac OS X 10.1 */
40 #include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
41 #endif
42 #include <IOKit/hid/IOHIDLib.h>
43 #include <IOKit/hid/IOHIDKeys.h>
44 #include <CoreFoundation/CoreFoundation.h>
45 #include <Carbon/Carbon.h> /* for NewPtrClear, DisposePtr */
46
47 #include "SDL_joystick.h"
48 #include "../SDL_sysjoystick.h"
49 #include "../SDL_joystick_c.h"
50
51 struct recElement
52 {
53 IOHIDElementCookie cookie; /* unique value which identifies element, will NOT change */
54 long min; /* reported min value possible */
55 long max; /* reported max value possible */
56 #if 0
57 /* TODO: maybe should handle the following stuff somehow? */
58
59 long scaledMin; /* reported scaled min value possible */
60 long scaledMax; /* reported scaled max value possible */
61 long size; /* size in bits of data return from element */
62 Boolean relative; /* are reports relative to last report (deltas) */
63 Boolean wrapping; /* does element wrap around (one value higher than max is min) */
64 Boolean nonLinear; /* are the values reported non-linear relative to element movement */
65 Boolean preferredState; /* does element have a preferred state (such as a button) */
66 Boolean nullState; /* does element have null state */
67 #endif /* 0 */
68
69 /* runtime variables used for auto-calibration */
70 long minReport; /* min returned value */
71 long maxReport; /* max returned value */
72
73 struct recElement * pNext; /* next element in list */
74 };
75 typedef struct recElement recElement;
76
77 struct joystick_hwdata
78 {
79 IOHIDDeviceInterface ** interface; /* interface to device, NULL = no interface */
80
81 char product[256]; /* name of product */
82 long usage; /* usage page from IOUSBHID Parser.h which defines general usage */
83 long usagePage; /* usage within above page from IOUSBHID Parser.h which defines specific usage */
84
85 long axes; /* number of axis (calculated, not reported by device) */
86 long buttons; /* number of buttons (calculated, not reported by device) */
87 long hats; /* number of hat switches (calculated, not reported by device) */
88 long elements; /* number of total elements (shouldbe total of above) (calculated, not reported by device) */
89
90 recElement* firstAxis;
91 recElement* firstButton;
92 recElement* firstHat;
93
94 int removed;
95 int uncentered;
96
97 struct joystick_hwdata* pNext; /* next device */
98 };
99 typedef struct joystick_hwdata recDevice;
100
101
102 /* Linked list of all available devices */
103 static recDevice *gpDeviceList = NULL;
104
105
HIDReportErrorNum(char * strError,long numError)106 static void HIDReportErrorNum (char * strError, long numError)
107 {
108 SDL_SetError(strError);
109 }
110
111 static void HIDGetCollectionElements (CFMutableDictionaryRef deviceProperties, recDevice *pDevice);
112
113 /* returns current value for element, polling element
114 * will return 0 on error conditions which should be accounted for by application
115 */
116
HIDGetElementValue(recDevice * pDevice,recElement * pElement)117 static SInt32 HIDGetElementValue (recDevice *pDevice, recElement *pElement)
118 {
119 IOReturn result = kIOReturnSuccess;
120 IOHIDEventStruct hidEvent;
121 hidEvent.value = 0;
122
123 if (NULL != pDevice && NULL != pElement && NULL != pDevice->interface)
124 {
125 result = (*(pDevice->interface))->getElementValue(pDevice->interface, pElement->cookie, &hidEvent);
126 if (kIOReturnSuccess == result)
127 {
128 /* record min and max for auto calibration */
129 if (hidEvent.value < pElement->minReport)
130 pElement->minReport = hidEvent.value;
131 if (hidEvent.value > pElement->maxReport)
132 pElement->maxReport = hidEvent.value;
133 }
134 }
135
136 /* auto user scale */
137 return hidEvent.value;
138 }
139
HIDScaledCalibratedValue(recDevice * pDevice,recElement * pElement,long min,long max)140 static SInt32 HIDScaledCalibratedValue (recDevice *pDevice, recElement *pElement, long min, long max)
141 {
142 float deviceScale = max - min;
143 float readScale = pElement->maxReport - pElement->minReport;
144 SInt32 value = HIDGetElementValue(pDevice, pElement);
145 if (readScale == 0)
146 return value; /* no scaling at all */
147 else
148 return ((value - pElement->minReport) * deviceScale / readScale) + min;
149 }
150
151
HIDRemovalCallback(void * target,IOReturn result,void * refcon,void * sender)152 static void HIDRemovalCallback(void * target,
153 IOReturn result,
154 void * refcon,
155 void * sender)
156 {
157 recDevice *device = (recDevice *) refcon;
158 device->removed = 1;
159 device->uncentered = 1;
160 }
161
162
163
164 /* Create and open an interface to device, required prior to extracting values or building queues.
165 * Note: appliction now owns the device and must close and release it prior to exiting
166 */
167
HIDCreateOpenDeviceInterface(io_object_t hidDevice,recDevice * pDevice)168 static IOReturn HIDCreateOpenDeviceInterface (io_object_t hidDevice, recDevice *pDevice)
169 {
170 IOReturn result = kIOReturnSuccess;
171 HRESULT plugInResult = S_OK;
172 SInt32 score = 0;
173 IOCFPlugInInterface ** ppPlugInInterface = NULL;
174
175 if (NULL == pDevice->interface)
176 {
177 result = IOCreatePlugInInterfaceForService (hidDevice, kIOHIDDeviceUserClientTypeID,
178 kIOCFPlugInInterfaceID, &ppPlugInInterface, &score);
179 if (kIOReturnSuccess == result)
180 {
181 /* Call a method of the intermediate plug-in to create the device interface */
182 plugInResult = (*ppPlugInInterface)->QueryInterface (ppPlugInInterface,
183 CFUUIDGetUUIDBytes (kIOHIDDeviceInterfaceID), (void *) &(pDevice->interface));
184 if (S_OK != plugInResult)
185 HIDReportErrorNum ("Couldn�t query HID class device interface from plugInInterface", plugInResult);
186 (*ppPlugInInterface)->Release (ppPlugInInterface);
187 }
188 else
189 HIDReportErrorNum ("Failed to create **plugInInterface via IOCreatePlugInInterfaceForService.", result);
190 }
191 if (NULL != pDevice->interface)
192 {
193 result = (*(pDevice->interface))->open (pDevice->interface, 0);
194 if (kIOReturnSuccess != result)
195 HIDReportErrorNum ("Failed to open pDevice->interface via open.", result);
196 else
197 (*(pDevice->interface))->setRemovalCallback (pDevice->interface, HIDRemovalCallback, pDevice, pDevice);
198
199 }
200 return result;
201 }
202
203 /* Closes and releases interface to device, should be done prior to exting application
204 * Note: will have no affect if device or interface do not exist
205 * application will "own" the device if interface is not closed
206 * (device may have to be plug and re-plugged in different location to get it working again without a restart)
207 */
208
HIDCloseReleaseInterface(recDevice * pDevice)209 static IOReturn HIDCloseReleaseInterface (recDevice *pDevice)
210 {
211 IOReturn result = kIOReturnSuccess;
212
213 if ((NULL != pDevice) && (NULL != pDevice->interface))
214 {
215 /* close the interface */
216 result = (*(pDevice->interface))->close (pDevice->interface);
217 if (kIOReturnNotOpen == result)
218 {
219 /* do nothing as device was not opened, thus can't be closed */
220 }
221 else if (kIOReturnSuccess != result)
222 HIDReportErrorNum ("Failed to close IOHIDDeviceInterface.", result);
223 /* release the interface */
224 result = (*(pDevice->interface))->Release (pDevice->interface);
225 if (kIOReturnSuccess != result)
226 HIDReportErrorNum ("Failed to release IOHIDDeviceInterface.", result);
227 pDevice->interface = NULL;
228 }
229 return result;
230 }
231
232 /* extracts actual specific element information from each element CF dictionary entry */
233
HIDGetElementInfo(CFTypeRef refElement,recElement * pElement)234 static void HIDGetElementInfo (CFTypeRef refElement, recElement *pElement)
235 {
236 long number;
237 CFTypeRef refType;
238
239 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementCookieKey));
240 if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
241 pElement->cookie = (IOHIDElementCookie) number;
242 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementMinKey));
243 if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
244 pElement->minReport = pElement->min = number;
245 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementMaxKey));
246 if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
247 pElement->maxReport = pElement->max = number;
248 /*
249 TODO: maybe should handle the following stuff somehow?
250
251 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMinKey));
252 if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
253 pElement->scaledMin = number;
254 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMaxKey));
255 if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
256 pElement->scaledMax = number;
257 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementSizeKey));
258 if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
259 pElement->size = number;
260 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsRelativeKey));
261 if (refType)
262 pElement->relative = CFBooleanGetValue (refType);
263 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsWrappingKey));
264 if (refType)
265 pElement->wrapping = CFBooleanGetValue (refType);
266 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsNonLinearKey));
267 if (refType)
268 pElement->nonLinear = CFBooleanGetValue (refType);
269 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasPreferedStateKey));
270 if (refType)
271 pElement->preferredState = CFBooleanGetValue (refType);
272 refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasNullStateKey));
273 if (refType)
274 pElement->nullState = CFBooleanGetValue (refType);
275 */
276 }
277
278 /* examines CF dictionary vlaue in device element hierarchy to determine if it is element of interest or a collection of more elements
279 * if element of interest allocate storage, add to list and retrieve element specific info
280 * if collection then pass on to deconstruction collection into additional individual elements
281 */
282
HIDAddElement(CFTypeRef refElement,recDevice * pDevice)283 static void HIDAddElement (CFTypeRef refElement, recDevice* pDevice)
284 {
285 recElement* element = NULL;
286 recElement** headElement = NULL;
287 long elementType, usagePage, usage;
288 CFTypeRef refElementType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementTypeKey));
289 CFTypeRef refUsagePage = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUsagePageKey));
290 CFTypeRef refUsage = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUsageKey));
291
292
293 if ((refElementType) && (CFNumberGetValue (refElementType, kCFNumberLongType, &elementType)))
294 {
295 /* look at types of interest */
296 if ((elementType == kIOHIDElementTypeInput_Misc) || (elementType == kIOHIDElementTypeInput_Button) ||
297 (elementType == kIOHIDElementTypeInput_Axis))
298 {
299 if (refUsagePage && CFNumberGetValue (refUsagePage, kCFNumberLongType, &usagePage) &&
300 refUsage && CFNumberGetValue (refUsage, kCFNumberLongType, &usage))
301 {
302 switch (usagePage) /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */
303 {
304 case kHIDPage_GenericDesktop:
305 {
306 switch (usage) /* look at usage to determine function */
307 {
308 case kHIDUsage_GD_X:
309 case kHIDUsage_GD_Y:
310 case kHIDUsage_GD_Z:
311 case kHIDUsage_GD_Rx:
312 case kHIDUsage_GD_Ry:
313 case kHIDUsage_GD_Rz:
314 case kHIDUsage_GD_Slider:
315 case kHIDUsage_GD_Dial:
316 case kHIDUsage_GD_Wheel:
317 element = (recElement *) NewPtrClear (sizeof (recElement));
318 if (element)
319 {
320 pDevice->axes++;
321 headElement = &(pDevice->firstAxis);
322 }
323 break;
324 case kHIDUsage_GD_Hatswitch:
325 element = (recElement *) NewPtrClear (sizeof (recElement));
326 if (element)
327 {
328 pDevice->hats++;
329 headElement = &(pDevice->firstHat);
330 }
331 break;
332 }
333 }
334 break;
335 case kHIDPage_Button:
336 element = (recElement *) NewPtrClear (sizeof (recElement));
337 if (element)
338 {
339 pDevice->buttons++;
340 headElement = &(pDevice->firstButton);
341 }
342 break;
343 default:
344 break;
345 }
346 }
347 }
348 else if (kIOHIDElementTypeCollection == elementType)
349 HIDGetCollectionElements ((CFMutableDictionaryRef) refElement, pDevice);
350 }
351
352 if (element && headElement) /* add to list */
353 {
354 pDevice->elements++;
355 if (NULL == *headElement)
356 *headElement = element;
357 else
358 {
359 recElement *elementPrevious, *elementCurrent;
360 elementCurrent = *headElement;
361 while (elementCurrent)
362 {
363 elementPrevious = elementCurrent;
364 elementCurrent = elementPrevious->pNext;
365 }
366 elementPrevious->pNext = element;
367 }
368 element->pNext = NULL;
369 HIDGetElementInfo (refElement, element);
370 }
371 }
372
373 /* collects information from each array member in device element list (each array memeber = element) */
374
HIDGetElementsCFArrayHandler(const void * value,void * parameter)375 static void HIDGetElementsCFArrayHandler (const void * value, void * parameter)
376 {
377 if (CFGetTypeID (value) == CFDictionaryGetTypeID ())
378 HIDAddElement ((CFTypeRef) value, (recDevice *) parameter);
379 }
380
381 /* handles retrieval of element information from arrays of elements in device IO registry information */
382
HIDGetElements(CFTypeRef refElementCurrent,recDevice * pDevice)383 static void HIDGetElements (CFTypeRef refElementCurrent, recDevice *pDevice)
384 {
385 CFTypeID type = CFGetTypeID (refElementCurrent);
386 if (type == CFArrayGetTypeID()) /* if element is an array */
387 {
388 CFRange range = {0, CFArrayGetCount (refElementCurrent)};
389 /* CountElementsCFArrayHandler called for each array member */
390 CFArrayApplyFunction (refElementCurrent, range, HIDGetElementsCFArrayHandler, pDevice);
391 }
392 }
393
394 /* handles extracting element information from element collection CF types
395 * used from top level element decoding and hierarchy deconstruction to flatten device element list
396 */
397
HIDGetCollectionElements(CFMutableDictionaryRef deviceProperties,recDevice * pDevice)398 static void HIDGetCollectionElements (CFMutableDictionaryRef deviceProperties, recDevice *pDevice)
399 {
400 CFTypeRef refElementTop = CFDictionaryGetValue (deviceProperties, CFSTR(kIOHIDElementKey));
401 if (refElementTop)
402 HIDGetElements (refElementTop, pDevice);
403 }
404
405 /* use top level element usage page and usage to discern device usage page and usage setting appropriate vlaues in device record */
406
HIDTopLevelElementHandler(const void * value,void * parameter)407 static void HIDTopLevelElementHandler (const void * value, void * parameter)
408 {
409 CFTypeRef refCF = 0;
410 if (CFGetTypeID (value) != CFDictionaryGetTypeID ())
411 return;
412 refCF = CFDictionaryGetValue (value, CFSTR(kIOHIDElementUsagePageKey));
413 if (!CFNumberGetValue (refCF, kCFNumberLongType, &((recDevice *) parameter)->usagePage))
414 SDL_SetError ("CFNumberGetValue error retrieving pDevice->usagePage.");
415 refCF = CFDictionaryGetValue (value, CFSTR(kIOHIDElementUsageKey));
416 if (!CFNumberGetValue (refCF, kCFNumberLongType, &((recDevice *) parameter)->usage))
417 SDL_SetError ("CFNumberGetValue error retrieving pDevice->usage.");
418 }
419
420 /* extracts device info from CF dictionary records in IO registry */
421
HIDGetDeviceInfo(io_object_t hidDevice,CFMutableDictionaryRef hidProperties,recDevice * pDevice)422 static void HIDGetDeviceInfo (io_object_t hidDevice, CFMutableDictionaryRef hidProperties, recDevice *pDevice)
423 {
424 CFMutableDictionaryRef usbProperties = 0;
425 io_registry_entry_t parent1, parent2;
426
427 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
428 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
429 */
430 if ((KERN_SUCCESS == IORegistryEntryGetParentEntry (hidDevice, kIOServicePlane, &parent1)) &&
431 (KERN_SUCCESS == IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2)) &&
432 (KERN_SUCCESS == IORegistryEntryCreateCFProperties (parent2, &usbProperties, kCFAllocatorDefault, kNilOptions)))
433 {
434 if (usbProperties)
435 {
436 CFTypeRef refCF = 0;
437 /* get device info
438 * try hid dictionary first, if fail then go to usb dictionary
439 */
440
441
442 /* get product name */
443 refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDProductKey));
444 if (!refCF)
445 refCF = CFDictionaryGetValue (usbProperties, CFSTR("USB Product Name"));
446 if (refCF)
447 {
448 if (!CFStringGetCString (refCF, pDevice->product, 256, CFStringGetSystemEncoding ()))
449 SDL_SetError ("CFStringGetCString error retrieving pDevice->product.");
450 }
451
452 /* get usage page and usage */
453 refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsagePageKey));
454 if (refCF)
455 {
456 if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->usagePage))
457 SDL_SetError ("CFNumberGetValue error retrieving pDevice->usagePage.");
458 refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsageKey));
459 if (refCF)
460 if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->usage))
461 SDL_SetError ("CFNumberGetValue error retrieving pDevice->usage.");
462 }
463
464 if (NULL == refCF) /* get top level element HID usage page or usage */
465 {
466 /* use top level element instead */
467 CFTypeRef refCFTopElement = 0;
468 refCFTopElement = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDElementKey));
469 {
470 /* refCFTopElement points to an array of element dictionaries */
471 CFRange range = {0, CFArrayGetCount (refCFTopElement)};
472 CFArrayApplyFunction (refCFTopElement, range, HIDTopLevelElementHandler, pDevice);
473 }
474 }
475
476 CFRelease (usbProperties);
477 }
478 else
479 SDL_SetError ("IORegistryEntryCreateCFProperties failed to create usbProperties.");
480
481 if (kIOReturnSuccess != IOObjectRelease (parent2))
482 SDL_SetError ("IOObjectRelease error with parent2.");
483 if (kIOReturnSuccess != IOObjectRelease (parent1))
484 SDL_SetError ("IOObjectRelease error with parent1.");
485 }
486 }
487
488
HIDBuildDevice(io_object_t hidDevice)489 static recDevice *HIDBuildDevice (io_object_t hidDevice)
490 {
491 recDevice *pDevice = (recDevice *) NewPtrClear (sizeof (recDevice));
492 if (pDevice)
493 {
494 /* get dictionary for HID properties */
495 CFMutableDictionaryRef hidProperties = 0;
496 kern_return_t result = IORegistryEntryCreateCFProperties (hidDevice, &hidProperties, kCFAllocatorDefault, kNilOptions);
497 if ((result == KERN_SUCCESS) && hidProperties)
498 {
499 /* create device interface */
500 result = HIDCreateOpenDeviceInterface (hidDevice, pDevice);
501 if (kIOReturnSuccess == result)
502 {
503 HIDGetDeviceInfo (hidDevice, hidProperties, pDevice); /* hidDevice used to find parents in registry tree */
504 HIDGetCollectionElements (hidProperties, pDevice);
505 }
506 else
507 {
508 DisposePtr((Ptr)pDevice);
509 pDevice = NULL;
510 }
511 CFRelease (hidProperties);
512 }
513 else
514 {
515 DisposePtr((Ptr)pDevice);
516 pDevice = NULL;
517 }
518 }
519 return pDevice;
520 }
521
522 /* disposes of the element list associated with a device and the memory associated with the list
523 */
524
HIDDisposeElementList(recElement ** elementList)525 static void HIDDisposeElementList (recElement **elementList)
526 {
527 recElement *pElement = *elementList;
528 while (pElement)
529 {
530 recElement *pElementNext = pElement->pNext;
531 DisposePtr ((Ptr) pElement);
532 pElement = pElementNext;
533 }
534 *elementList = NULL;
535 }
536
537 /* disposes of a single device, closing and releaseing interface, freeing memory fro device and elements, setting device pointer to NULL
538 * all your device no longer belong to us... (i.e., you do not 'own' the device anymore)
539 */
540
HIDDisposeDevice(recDevice ** ppDevice)541 static recDevice *HIDDisposeDevice (recDevice **ppDevice)
542 {
543 kern_return_t result = KERN_SUCCESS;
544 recDevice *pDeviceNext = NULL;
545 if (*ppDevice)
546 {
547 /* save next device prior to disposing of this device */
548 pDeviceNext = (*ppDevice)->pNext;
549
550 /* free element lists */
551 HIDDisposeElementList (&(*ppDevice)->firstAxis);
552 HIDDisposeElementList (&(*ppDevice)->firstButton);
553 HIDDisposeElementList (&(*ppDevice)->firstHat);
554
555 result = HIDCloseReleaseInterface (*ppDevice); /* function sanity checks interface value (now application does not own device) */
556 if (kIOReturnSuccess != result)
557 HIDReportErrorNum ("HIDCloseReleaseInterface failed when trying to dipose device.", result);
558 DisposePtr ((Ptr)*ppDevice);
559 *ppDevice = NULL;
560 }
561 return pDeviceNext;
562 }
563
564
565 /* Function to scan the system for joysticks.
566 * Joystick 0 should be the system default joystick.
567 * This function should return the number of available joysticks, or -1
568 * on an unrecoverable fatal error.
569 */
SDL_SYS_JoystickInit(void)570 int SDL_SYS_JoystickInit(void)
571 {
572 IOReturn result = kIOReturnSuccess;
573 mach_port_t masterPort = 0;
574 io_iterator_t hidObjectIterator = 0;
575 CFMutableDictionaryRef hidMatchDictionary = NULL;
576 recDevice *device, *lastDevice;
577 io_object_t ioHIDDeviceObject = 0;
578
579 SDL_numjoysticks = 0;
580
581 if (gpDeviceList)
582 {
583 SDL_SetError("Joystick: Device list already inited.");
584 return -1;
585 }
586
587 result = IOMasterPort (bootstrap_port, &masterPort);
588 if (kIOReturnSuccess != result)
589 {
590 SDL_SetError("Joystick: IOMasterPort error with bootstrap_port.");
591 return -1;
592 }
593
594 /* Set up a matching dictionary to search I/O Registry by class name for all HID class devices. */
595 hidMatchDictionary = IOServiceMatching (kIOHIDDeviceKey);
596 if (hidMatchDictionary)
597 {
598 /* Add key for device type (joystick, in this case) to refine the matching dictionary. */
599
600 /* NOTE: we now perform this filtering later
601 UInt32 usagePage = kHIDPage_GenericDesktop;
602 UInt32 usage = kHIDUsage_GD_Joystick;
603 CFNumberRef refUsage = NULL, refUsagePage = NULL;
604
605 refUsage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usage);
606 CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsageKey), refUsage);
607 refUsagePage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usagePage);
608 CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsagePageKey), refUsagePage);
609 */
610 }
611 else
612 {
613 SDL_SetError("Joystick: Failed to get HID CFMutableDictionaryRef via IOServiceMatching.");
614 return -1;
615 }
616
617 /*/ Now search I/O Registry for matching devices. */
618 result = IOServiceGetMatchingServices (masterPort, hidMatchDictionary, &hidObjectIterator);
619 /* Check for errors */
620 if (kIOReturnSuccess != result)
621 {
622 SDL_SetError("Joystick: Couldn't create a HID object iterator.");
623 return -1;
624 }
625 if (!hidObjectIterator) /* there are no joysticks */
626 {
627 gpDeviceList = NULL;
628 SDL_numjoysticks = 0;
629 return 0;
630 }
631 /* IOServiceGetMatchingServices consumes a reference to the dictionary, so we don't need to release the dictionary ref. */
632
633 /* build flat linked list of devices from device iterator */
634
635 gpDeviceList = lastDevice = NULL;
636
637 while ((ioHIDDeviceObject = IOIteratorNext (hidObjectIterator)))
638 {
639 /* build a device record */
640 device = HIDBuildDevice (ioHIDDeviceObject);
641 if (!device)
642 continue;
643
644 /* dump device object, it is no longer needed */
645 result = IOObjectRelease (ioHIDDeviceObject);
646 /* if (KERN_SUCCESS != result)
647 HIDReportErrorNum ("IOObjectRelease error with ioHIDDeviceObject.", result);
648 */
649
650 /* Filter device list to non-keyboard/mouse stuff */
651 if ( (device->usagePage != kHIDPage_GenericDesktop) ||
652 ((device->usage != kHIDUsage_GD_Joystick &&
653 device->usage != kHIDUsage_GD_GamePad &&
654 device->usage != kHIDUsage_GD_MultiAxisController)) ) {
655
656 /* release memory for the device */
657 HIDDisposeDevice (&device);
658 DisposePtr((Ptr)device);
659 continue;
660 }
661
662 /* Add device to the end of the list */
663 if (lastDevice)
664 lastDevice->pNext = device;
665 else
666 gpDeviceList = device;
667 lastDevice = device;
668 }
669 result = IOObjectRelease (hidObjectIterator); /* release the iterator */
670
671 /* Count the total number of devices we found */
672 device = gpDeviceList;
673 while (device)
674 {
675 SDL_numjoysticks++;
676 device = device->pNext;
677 }
678
679 return SDL_numjoysticks;
680 }
681
682 /* Function to get the device-dependent name of a joystick */
SDL_SYS_JoystickName(int index)683 const char *SDL_SYS_JoystickName(int index)
684 {
685 recDevice *device = gpDeviceList;
686
687 for (; index > 0; index--)
688 device = device->pNext;
689
690 return device->product;
691 }
692
693 /* Function to open a joystick for use.
694 * The joystick to open is specified by the index field of the joystick.
695 * This should fill the nbuttons and naxes fields of the joystick structure.
696 * It returns 0, or -1 if there is an error.
697 */
SDL_SYS_JoystickOpen(SDL_Joystick * joystick)698 int SDL_SYS_JoystickOpen(SDL_Joystick *joystick)
699 {
700 recDevice *device = gpDeviceList;
701 int index;
702
703 for (index = joystick->index; index > 0; index--)
704 device = device->pNext;
705
706 joystick->hwdata = device;
707 joystick->name = device->product;
708
709 joystick->naxes = device->axes;
710 joystick->nhats = device->hats;
711 joystick->nballs = 0;
712 joystick->nbuttons = device->buttons;
713
714 return 0;
715 }
716
717 /* Function to update the state of a joystick - called as a device poll.
718 * This function shouldn't update the joystick structure directly,
719 * but instead should call SDL_PrivateJoystick*() to deliver events
720 * and update joystick device state.
721 */
SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)722 void SDL_SYS_JoystickUpdate(SDL_Joystick *joystick)
723 {
724 recDevice *device = joystick->hwdata;
725 recElement *element;
726 SInt32 value, range;
727 int i;
728
729 if (device->removed) /* device was unplugged; ignore it. */
730 {
731 if (device->uncentered)
732 {
733 device->uncentered = 0;
734
735 /* Tell the app that everything is centered/unpressed... */
736 for (i = 0; i < device->axes; i++)
737 SDL_PrivateJoystickAxis(joystick, i, 0);
738
739 for (i = 0; i < device->buttons; i++)
740 SDL_PrivateJoystickButton(joystick, i, 0);
741
742 for (i = 0; i < device->hats; i++)
743 SDL_PrivateJoystickHat(joystick, i, SDL_HAT_CENTERED);
744 }
745
746 return;
747 }
748
749 element = device->firstAxis;
750 i = 0;
751 while (element)
752 {
753 value = HIDScaledCalibratedValue(device, element, -32768, 32767);
754 if ( value != joystick->axes[i] )
755 SDL_PrivateJoystickAxis(joystick, i, value);
756 element = element->pNext;
757 ++i;
758 }
759
760 element = device->firstButton;
761 i = 0;
762 while (element)
763 {
764 value = HIDGetElementValue(device, element);
765 if (value > 1) /* handle pressure-sensitive buttons */
766 value = 1;
767 if ( value != joystick->buttons[i] )
768 SDL_PrivateJoystickButton(joystick, i, value);
769 element = element->pNext;
770 ++i;
771 }
772
773 element = device->firstHat;
774 i = 0;
775 while (element)
776 {
777 Uint8 pos = 0;
778
779 range = (element->max - element->min + 1);
780 value = HIDGetElementValue(device, element) - element->min;
781 if (range == 4) /* 4 position hatswitch - scale up value */
782 value *= 2;
783 else if (range != 8) /* Neither a 4 nor 8 positions - fall back to default position (centered) */
784 value = -1;
785 switch(value)
786 {
787 case 0:
788 pos = SDL_HAT_UP;
789 break;
790 case 1:
791 pos = SDL_HAT_RIGHTUP;
792 break;
793 case 2:
794 pos = SDL_HAT_RIGHT;
795 break;
796 case 3:
797 pos = SDL_HAT_RIGHTDOWN;
798 break;
799 case 4:
800 pos = SDL_HAT_DOWN;
801 break;
802 case 5:
803 pos = SDL_HAT_LEFTDOWN;
804 break;
805 case 6:
806 pos = SDL_HAT_LEFT;
807 break;
808 case 7:
809 pos = SDL_HAT_LEFTUP;
810 break;
811 default:
812 /* Every other value is mapped to center. We do that because some
813 * joysticks use 8 and some 15 for this value, and apparently
814 * there are even more variants out there - so we try to be generous.
815 */
816 pos = SDL_HAT_CENTERED;
817 break;
818 }
819 if ( pos != joystick->hats[i] )
820 SDL_PrivateJoystickHat(joystick, i, pos);
821 element = element->pNext;
822 ++i;
823 }
824
825 return;
826 }
827
828 /* Function to close a joystick after use */
SDL_SYS_JoystickClose(SDL_Joystick * joystick)829 void SDL_SYS_JoystickClose(SDL_Joystick *joystick)
830 {
831 /* Should we do anything here? */
832 return;
833 }
834
835 /* Function to perform any system-specific joystick related cleanup */
SDL_SYS_JoystickQuit(void)836 void SDL_SYS_JoystickQuit(void)
837 {
838 while (NULL != gpDeviceList)
839 gpDeviceList = HIDDisposeDevice (&gpDeviceList);
840 }
841
842 #endif /* SDL_JOYSTICK_IOKIT */
843