diff options
author | Franklin Wei <git@fwei.tk> | 2017-01-21 15:18:31 -0500 |
---|---|---|
committer | Franklin Wei <git@fwei.tk> | 2017-12-23 21:01:26 -0500 |
commit | a855d6202536ff28e5aae4f22a0f31d8f5b325d0 (patch) | |
tree | 8c75f224dd64ed360505afa8843d016b0d75000b /apps/plugins/sdl/src/joystick/darwin | |
parent | 01c6dcf6c7b9bb1ad2fa0450f99bacc5f3d3e04b (diff) | |
download | rockbox-a855d6202536ff28e5aae4f22a0f31d8f5b325d0.tar.gz rockbox-a855d6202536ff28e5aae4f22a0f31d8f5b325d0.zip |
Port of Duke Nukem 3D
This ports Fabien Sanglard's Chocolate Duke to run on a version of SDL
for Rockbox.
Change-Id: I8f2c4c78af19de10c1633ed7bb7a997b43256dd9
Diffstat (limited to 'apps/plugins/sdl/src/joystick/darwin')
-rw-r--r-- | apps/plugins/sdl/src/joystick/darwin/SDL_sysjoystick.c | 842 |
1 files changed, 842 insertions, 0 deletions
diff --git a/apps/plugins/sdl/src/joystick/darwin/SDL_sysjoystick.c b/apps/plugins/sdl/src/joystick/darwin/SDL_sysjoystick.c new file mode 100644 index 0000000000..a9ccb35fad --- /dev/null +++ b/apps/plugins/sdl/src/joystick/darwin/SDL_sysjoystick.c | |||
@@ -0,0 +1,842 @@ | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | */ | ||
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 */ | ||
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 | */ | ||
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 | */ | ||
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 */ | ||
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 */ | ||
836 | void SDL_SYS_JoystickQuit(void) | ||
837 | { | ||
838 | while (NULL != gpDeviceList) | ||
839 | gpDeviceList = HIDDisposeDevice (&gpDeviceList); | ||
840 | } | ||
841 | |||
842 | #endif /* SDL_JOYSTICK_IOKIT */ | ||