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
51struct 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};
75typedef struct recElement recElement;
76
77struct 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};
99typedef struct joystick_hwdata recDevice;
100
101
102/* Linked list of all available devices */
103static recDevice *gpDeviceList = NULL;
104
105
106static void HIDReportErrorNum (char * strError, long numError)
107{
108	SDL_SetError(strError);
109}
110
111static 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
117static 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
140static 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
152static 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
168static 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
209static 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
234static 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
283static 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
375static 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
383static 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
398static 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
407static 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
422static 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
489static 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
525static 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
541static 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 */
570int 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 */
683const 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 */
698int 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 */
722void 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 */
829void 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 */
836void SDL_SYS_JoystickQuit(void)
837{
838	while (NULL != gpDeviceList)
839		gpDeviceList = HIDDisposeDevice (&gpDeviceList);
840}
841
842#endif /* SDL_JOYSTICK_IOKIT */
843