Mercurial > sdl-ios-xcode
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 } |