comparison src/joystick/darwin/SDL_sysjoystick.c @ 172:37e3ca9254c7

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