zenilib  0.5.3.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
SDL_sysjoystick.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
4 
5  This software is provided 'as-is', without any express or implied
6  warranty. In no event will the authors be held liable for any damages
7  arising from the use of this software.
8 
9  Permission is granted to anyone to use this software for any purpose,
10  including commercial applications, and to alter it and redistribute it
11  freely, subject to the following restrictions:
12 
13  1. The origin of this software must not be misrepresented; you must not
14  claim that you wrote the original software. If you use this software
15  in a product, an acknowledgment in the product documentation would be
16  appreciated but is not required.
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19  3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "SDL_config.h"
22 
23 #ifdef SDL_JOYSTICK_IOKIT
24 
25 /* SDL joystick driver for Darwin / Mac OS X, based on the IOKit HID API */
26 /* Written 2001 by Max Horn */
27 
28 #include <unistd.h>
29 #include <ctype.h>
30 #include <sysexits.h>
31 #include <mach/mach.h>
32 #include <mach/mach_error.h>
33 #include <IOKit/IOKitLib.h>
34 #include <IOKit/IOCFPlugIn.h>
35 #ifdef MACOS_10_0_4
36 #include <IOKit/hidsystem/IOHIDUsageTables.h>
37 #else
38 /* The header was moved here in Mac OS X 10.1 */
39 #include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
40 #endif
41 #include <IOKit/hid/IOHIDLib.h>
42 #include <IOKit/hid/IOHIDKeys.h>
43 #include <CoreFoundation/CoreFoundation.h>
44 #include <Carbon/Carbon.h> /* for NewPtrClear, DisposePtr */
45 #include <IOKit/IOMessage.h>
46 
47 /* For force feedback testing. */
48 #include <ForceFeedback/ForceFeedback.h>
49 #include <ForceFeedback/ForceFeedbackConstants.h>
50 
51 #include "SDL_joystick.h"
52 #include "../SDL_sysjoystick.h"
53 #include "../SDL_joystick_c.h"
54 #include "SDL_sysjoystick_c.h"
55 #include "SDL_events.h"
56 #if !SDL_EVENTS_DISABLED
57 #include "../../events/SDL_events_c.h"
58 #endif
59 
60 
61 /* Linked list of all available devices */
62 static recDevice *gpDeviceList = NULL;
63 /* OSX reference to the notification object that tells us about device insertion/removal */
64 IONotificationPortRef notificationPort = 0;
65 /* if 1 then a device was added since the last update call */
66 static SDL_bool s_bDeviceAdded = SDL_FALSE;
67 static SDL_bool s_bDeviceRemoved = SDL_FALSE;
68 
69 /* static incrementing counter for new joystick devices seen on the system. Devices should start with index 0 */
70 static int s_joystick_instance_id = -1;
71 
72 static void
73 HIDReportErrorNum(char *strError, long numError)
74 {
75  SDL_SetError(strError);
76 }
77 
78 static void HIDGetCollectionElements(CFMutableDictionaryRef deviceProperties,
79  recDevice * pDevice);
80 
81 /* returns current value for element, polling element
82  * will return 0 on error conditions which should be accounted for by application
83  */
84 
85 static SInt32
86 HIDGetElementValue(recDevice * pDevice, recElement * pElement)
87 {
88  IOReturn result = kIOReturnSuccess;
89  IOHIDEventStruct hidEvent;
90  hidEvent.value = 0;
91 
92  if (NULL != pDevice && NULL != pElement && NULL != pDevice->interface) {
93  result =
94  (*(pDevice->interface))->getElementValue(pDevice->interface,
95  pElement->cookie,
96  &hidEvent);
97  if (kIOReturnSuccess == result) {
98  /* record min and max for auto calibration */
99  if (hidEvent.value < pElement->minReport)
100  pElement->minReport = hidEvent.value;
101  if (hidEvent.value > pElement->maxReport)
102  pElement->maxReport = hidEvent.value;
103  }
104  }
105 
106  /* auto user scale */
107  return hidEvent.value;
108 }
109 
110 static SInt32
111 HIDScaledCalibratedValue(recDevice * pDevice, recElement * pElement,
112  long min, long max)
113 {
114  float deviceScale = max - min;
115  float readScale = pElement->maxReport - pElement->minReport;
116  SInt32 value = HIDGetElementValue(pDevice, pElement);
117  if (readScale == 0)
118  return value; /* no scaling at all */
119  else
120  return ((value - pElement->minReport) * deviceScale / readScale) +
121  min;
122 }
123 
124 
125 static void
126 HIDRemovalCallback(void *target, IOReturn result, void *refcon, void *sender)
127 {
128  recDevice *device = (recDevice *) refcon;
129  device->removed = 1;
130  s_bDeviceRemoved = SDL_TRUE;
131 }
132 
133 
134 /* Called by the io port notifier on removal of this device
135  */
136 void JoystickDeviceWasRemovedCallback( void * refcon, io_service_t service, natural_t messageType, void * messageArgument )
137 {
138  if( messageType == kIOMessageServiceIsTerminated && refcon )
139  {
140  recDevice *device = (recDevice *) refcon;
141  device->removed = 1;
142  s_bDeviceRemoved = SDL_TRUE;
143  }
144 }
145 
146 
147 /* Create and open an interface to device, required prior to extracting values or building queues.
148  * Note: application now owns the device and must close and release it prior to exiting
149  */
150 
151 static IOReturn
152 HIDCreateOpenDeviceInterface(io_object_t hidDevice, recDevice * pDevice)
153 {
154  IOReturn result = kIOReturnSuccess;
155  HRESULT plugInResult = S_OK;
156  SInt32 score = 0;
157  IOCFPlugInInterface **ppPlugInInterface = NULL;
158 
159  if (NULL == pDevice->interface) {
160  result =
161  IOCreatePlugInInterfaceForService(hidDevice,
162  kIOHIDDeviceUserClientTypeID,
163  kIOCFPlugInInterfaceID,
164  &ppPlugInInterface, &score);
165  if (kIOReturnSuccess == result) {
166  /* Call a method of the intermediate plug-in to create the device interface */
167  plugInResult =
168  (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
169  CFUUIDGetUUIDBytes
170  (kIOHIDDeviceInterfaceID),
171  (void *)
172  &(pDevice->interface));
173  if (S_OK != plugInResult)
174  HIDReportErrorNum
175  ("Couldn't query HID class device interface from plugInInterface",
176  plugInResult);
177  (*ppPlugInInterface)->Release(ppPlugInInterface);
178  } else
179  HIDReportErrorNum
180  ("Failed to create **plugInInterface via IOCreatePlugInInterfaceForService.",
181  result);
182  }
183  if (NULL != pDevice->interface) {
184  result = (*(pDevice->interface))->open(pDevice->interface, 0);
185  if (kIOReturnSuccess != result)
186  HIDReportErrorNum
187  ("Failed to open pDevice->interface via open.", result);
188  else
189  {
190  pDevice->portIterator = 0;
191 
192  /* It's okay if this fails, we have another detection method below */
193  (*(pDevice->interface))->setRemovalCallback(pDevice->interface,
194  HIDRemovalCallback,
195  pDevice, pDevice);
196 
197  /* now connect notification for new devices */
198  pDevice->notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
199 
200  CFRunLoopAddSource(CFRunLoopGetCurrent(),
201  IONotificationPortGetRunLoopSource(pDevice->notificationPort),
202  kCFRunLoopDefaultMode);
203 
204  /* Register for notifications when a serial port is added to the system */
205  result = IOServiceAddInterestNotification(pDevice->notificationPort,
206  hidDevice,
207  kIOGeneralInterest,
208  JoystickDeviceWasRemovedCallback,
209  pDevice,
210  &pDevice->portIterator);
211  if (kIOReturnSuccess != result) {
212  HIDReportErrorNum
213  ("Failed to register for removal callback.", result);
214  }
215  }
216 
217  }
218  return result;
219 }
220 
221 /* Closes and releases interface to device, should be done prior to exiting application
222  * Note: will have no affect if device or interface do not exist
223  * application will "own" the device if interface is not closed
224  * (device may have to be plug and re-plugged in different location to get it working again without a restart)
225  */
226 
227 static IOReturn
228 HIDCloseReleaseInterface(recDevice * pDevice)
229 {
230  IOReturn result = kIOReturnSuccess;
231 
232  if ((NULL != pDevice) && (NULL != pDevice->interface)) {
233  /* close the interface */
234  result = (*(pDevice->interface))->close(pDevice->interface);
235  if (kIOReturnNotOpen == result) {
236  /* do nothing as device was not opened, thus can't be closed */
237  } else if (kIOReturnSuccess != result)
238  HIDReportErrorNum("Failed to close IOHIDDeviceInterface.",
239  result);
240  /* release the interface */
241  result = (*(pDevice->interface))->Release(pDevice->interface);
242  if (kIOReturnSuccess != result)
243  HIDReportErrorNum("Failed to release IOHIDDeviceInterface.",
244  result);
245  pDevice->interface = NULL;
246 
247  if ( pDevice->portIterator )
248  {
249  IOObjectRelease( pDevice->portIterator );
250  pDevice->portIterator = 0;
251  }
252  }
253  return result;
254 }
255 
256 /* extracts actual specific element information from each element CF dictionary entry */
257 
258 static void
259 HIDGetElementInfo(CFTypeRef refElement, recElement * pElement)
260 {
261  long number;
262  CFTypeRef refType;
263 
264  refType = CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementCookieKey));
265  if (refType && CFNumberGetValue(refType, kCFNumberLongType, &number))
266  pElement->cookie = (IOHIDElementCookie) number;
267  refType = CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementMinKey));
268  if (refType && CFNumberGetValue(refType, kCFNumberLongType, &number))
269  pElement->minReport = pElement->min = number;
270  pElement->maxReport = pElement->min;
271  refType = CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementMaxKey));
272  if (refType && CFNumberGetValue(refType, kCFNumberLongType, &number))
273  pElement->maxReport = pElement->max = number;
274 /*
275  TODO: maybe should handle the following stuff somehow?
276 
277  refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMinKey));
278  if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
279  pElement->scaledMin = number;
280  refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMaxKey));
281  if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
282  pElement->scaledMax = number;
283  refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementSizeKey));
284  if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
285  pElement->size = number;
286  refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsRelativeKey));
287  if (refType)
288  pElement->relative = CFBooleanGetValue (refType);
289  refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsWrappingKey));
290  if (refType)
291  pElement->wrapping = CFBooleanGetValue (refType);
292  refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsNonLinearKey));
293  if (refType)
294  pElement->nonLinear = CFBooleanGetValue (refType);
295  refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasPreferedStateKey));
296  if (refType)
297  pElement->preferredState = CFBooleanGetValue (refType);
298  refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasNullStateKey));
299  if (refType)
300  pElement->nullState = CFBooleanGetValue (refType);
301 */
302 }
303 
304 /* examines CF dictionary value in device element hierarchy to determine if it is element of interest or a collection of more elements
305  * if element of interest allocate storage, add to list and retrieve element specific info
306  * if collection then pass on to deconstruction collection into additional individual elements
307  */
308 
309 static void
310 HIDAddElement(CFTypeRef refElement, recDevice * pDevice)
311 {
312  recElement *element = NULL;
313  recElement **headElement = NULL;
314  long elementType, usagePage, usage;
315  CFTypeRef refElementType =
316  CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementTypeKey));
317  CFTypeRef refUsagePage =
318  CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementUsagePageKey));
319  CFTypeRef refUsage =
320  CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementUsageKey));
321 
322 
323  if ((refElementType)
324  &&
325  (CFNumberGetValue(refElementType, kCFNumberLongType, &elementType))) {
326  /* look at types of interest */
327  if ((elementType == kIOHIDElementTypeInput_Misc)
328  || (elementType == kIOHIDElementTypeInput_Button)
329  || (elementType == kIOHIDElementTypeInput_Axis)) {
330  if (refUsagePage
331  && CFNumberGetValue(refUsagePage, kCFNumberLongType,
332  &usagePage) && refUsage
333  && CFNumberGetValue(refUsage, kCFNumberLongType, &usage)) {
334  switch (usagePage) { /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */
335  case kHIDPage_GenericDesktop:
336  {
337  switch (usage) { /* look at usage to determine function */
338  case kHIDUsage_GD_X:
339  case kHIDUsage_GD_Y:
340  case kHIDUsage_GD_Z:
341  case kHIDUsage_GD_Rx:
342  case kHIDUsage_GD_Ry:
343  case kHIDUsage_GD_Rz:
344  case kHIDUsage_GD_Slider:
345  case kHIDUsage_GD_Dial:
346  case kHIDUsage_GD_Wheel:
347  element = (recElement *)
348  NewPtrClear(sizeof(recElement));
349  if (element) {
350  pDevice->axes++;
351  headElement = &(pDevice->firstAxis);
352  }
353  break;
354  case kHIDUsage_GD_Hatswitch:
355  element = (recElement *)
356  NewPtrClear(sizeof(recElement));
357  if (element) {
358  pDevice->hats++;
359  headElement = &(pDevice->firstHat);
360  }
361  break;
362  }
363  }
364  break;
365  case kHIDPage_Simulation:
366  switch (usage) {
367  case kHIDUsage_Sim_Rudder:
368  case kHIDUsage_Sim_Throttle:
369  element = (recElement *)
370  NewPtrClear(sizeof(recElement));
371  if (element) {
372  pDevice->axes++;
373  headElement = &(pDevice->firstAxis);
374  }
375  break;
376 
377  default:
378  break;
379  }
380  break;
381  case kHIDPage_Button:
382  element = (recElement *)
383  NewPtrClear(sizeof(recElement));
384  if (element) {
385  pDevice->buttons++;
386  headElement = &(pDevice->firstButton);
387  }
388  break;
389  default:
390  break;
391  }
392  }
393  } else if (kIOHIDElementTypeCollection == elementType)
394  HIDGetCollectionElements((CFMutableDictionaryRef) refElement,
395  pDevice);
396  }
397 
398  if (element && headElement) { /* add to list */
399  recElement *elementPrevious = NULL;
400  recElement *elementCurrent = *headElement;
401  while (elementCurrent && usage >= elementCurrent->usage) {
402  elementPrevious = elementCurrent;
403  elementCurrent = elementCurrent->pNext;
404  }
405  if (elementPrevious) {
406  elementPrevious->pNext = element;
407  } else {
408  *headElement = element;
409  }
410  element->usagePage = usagePage;
411  element->usage = usage;
412  element->pNext = elementCurrent;
413  HIDGetElementInfo(refElement, element);
414  pDevice->elements++;
415  }
416 }
417 
418 /* collects information from each array member in device element list (each array member = element) */
419 
420 static void
421 HIDGetElementsCFArrayHandler(const void *value, void *parameter)
422 {
423  if (CFGetTypeID(value) == CFDictionaryGetTypeID())
424  HIDAddElement((CFTypeRef) value, (recDevice *) parameter);
425 }
426 
427 /* handles retrieval of element information from arrays of elements in device IO registry information */
428 
429 static void
430 HIDGetElements(CFTypeRef refElementCurrent, recDevice * pDevice)
431 {
432  CFTypeID type = CFGetTypeID(refElementCurrent);
433  if (type == CFArrayGetTypeID()) { /* if element is an array */
434  CFRange range = { 0, CFArrayGetCount(refElementCurrent) };
435  /* CountElementsCFArrayHandler called for each array member */
436  CFArrayApplyFunction(refElementCurrent, range,
437  HIDGetElementsCFArrayHandler, pDevice);
438  }
439 }
440 
441 /* handles extracting element information from element collection CF types
442  * used from top level element decoding and hierarchy deconstruction to flatten device element list
443  */
444 
445 static void
446 HIDGetCollectionElements(CFMutableDictionaryRef deviceProperties,
447  recDevice * pDevice)
448 {
449  CFTypeRef refElementTop =
450  CFDictionaryGetValue(deviceProperties, CFSTR(kIOHIDElementKey));
451  if (refElementTop)
452  HIDGetElements(refElementTop, pDevice);
453 }
454 
455 /* use top level element usage page and usage to discern device usage page and usage setting appropriate vlaues in device record */
456 
457 static void
458 HIDTopLevelElementHandler(const void *value, void *parameter)
459 {
460  CFTypeRef refCF = 0;
461  if (CFGetTypeID(value) != CFDictionaryGetTypeID())
462  return;
463  refCF = CFDictionaryGetValue(value, CFSTR(kIOHIDElementUsagePageKey));
464  if (!CFNumberGetValue
465  (refCF, kCFNumberLongType, &((recDevice *) parameter)->usagePage))
466  SDL_SetError("CFNumberGetValue error retrieving pDevice->usagePage.");
467  refCF = CFDictionaryGetValue(value, CFSTR(kIOHIDElementUsageKey));
468  if (!CFNumberGetValue
469  (refCF, kCFNumberLongType, &((recDevice *) parameter)->usage))
470  SDL_SetError("CFNumberGetValue error retrieving pDevice->usage.");
471 }
472 
473 /* extracts device info from CF dictionary records in IO registry */
474 
475 static void
476 HIDGetDeviceInfo(io_object_t hidDevice, CFMutableDictionaryRef hidProperties,
477  recDevice * pDevice)
478 {
479  CFMutableDictionaryRef usbProperties = 0;
480  io_registry_entry_t parent1, parent2;
481 
482  /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
483  * get dictionary for USB properties: step up two levels and get CF dictionary for USB properties
484  */
485  if ((KERN_SUCCESS == IORegistryEntryGetParentEntry(hidDevice, kIOServicePlane, &parent1))
486  && (KERN_SUCCESS == IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2))
487  && (KERN_SUCCESS == IORegistryEntryCreateCFProperties(parent2, &usbProperties, kCFAllocatorDefault, kNilOptions))) {
488  if (usbProperties) {
489  CFTypeRef refCF = 0;
490  /* get device info
491  * try hid dictionary first, if fail then go to usb dictionary
492  */
493 
494  /* get product name */
495  refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductKey));
496  if (!refCF) {
497  refCF = CFDictionaryGetValue(usbProperties, CFSTR("USB Product Name"));
498  }
499  if (refCF) {
500  if (!CFStringGetCString(refCF, pDevice->product, 256, CFStringGetSystemEncoding())) {
501  SDL_SetError("CFStringGetCString error retrieving pDevice->product.");
502  }
503  }
504 
505  /* get usage page and usage */
506  refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDPrimaryUsagePageKey));
507  if (refCF) {
508  if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->usagePage)) {
509  SDL_SetError("CFNumberGetValue error retrieving pDevice->usagePage.");
510  }
511 
512  refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDPrimaryUsageKey));
513  if (refCF) {
514  if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->usage)) {
515  SDL_SetError("CFNumberGetValue error retrieving pDevice->usage.");
516  }
517  }
518  }
519 
520  refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDVendorIDKey));
521  if (refCF) {
522  if (!CFNumberGetValue(refCF, kCFNumberLongType, &pDevice->guid.data[0])) {
523  SDL_SetError("CFNumberGetValue error retrieving pDevice->guid[0]");
524  }
525  }
526 
527  refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductIDKey));
528  if (refCF) {
529  if (!CFNumberGetValue(refCF, kCFNumberLongType, &pDevice->guid.data[8])) {
530  SDL_SetError("CFNumberGetValue error retrieving pDevice->guid[8]");
531  }
532  }
533 
534  /* Check to make sure we have a vendor and product ID
535  If we don't, use the same algorithm as the Linux code for Bluetooth devices */
536  {
537  Uint32 *guid32 = (Uint32*)pDevice->guid.data;
538  if (!guid32[0] && !guid32[1]) {
539  const Uint16 BUS_BLUETOOTH = 0x05;
540  Uint16 *guid16 = (Uint16 *)guid32;
541  *guid16++ = BUS_BLUETOOTH;
542  *guid16++ = 0;
543  SDL_strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4);
544  }
545  }
546 
547  /* If we don't have a vendor and product ID this is probably a Bluetooth device */
548 
549  if (NULL == refCF) { /* get top level element HID usage page or usage */
550  /* use top level element instead */
551  CFTypeRef refCFTopElement = 0;
552  refCFTopElement = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDElementKey));
553  {
554  /* refCFTopElement points to an array of element dictionaries */
555  CFRange range = { 0, CFArrayGetCount(refCFTopElement) };
556  CFArrayApplyFunction(refCFTopElement, range, HIDTopLevelElementHandler, pDevice);
557  }
558  }
559 
560  CFRelease(usbProperties);
561  } else {
562  SDL_SetError("IORegistryEntryCreateCFProperties failed to create usbProperties.");
563  }
564 
565  if (kIOReturnSuccess != IOObjectRelease(parent2)) {
566  SDL_SetError("IOObjectRelease error with parent2");
567  }
568  if (kIOReturnSuccess != IOObjectRelease(parent1)) {
569  SDL_SetError("IOObjectRelease error with parent1");
570  }
571  }
572 }
573 
574 
575 static recDevice *
576 HIDBuildDevice(io_object_t hidDevice)
577 {
578  recDevice *pDevice = (recDevice *) NewPtrClear(sizeof(recDevice));
579  if (pDevice) {
580  /* get dictionary for HID properties */
581  CFMutableDictionaryRef hidProperties = 0;
582  kern_return_t result =
583  IORegistryEntryCreateCFProperties(hidDevice, &hidProperties,
584  kCFAllocatorDefault,
585  kNilOptions);
586  if ((result == KERN_SUCCESS) && hidProperties) {
587  /* create device interface */
588  result = HIDCreateOpenDeviceInterface(hidDevice, pDevice);
589  if (kIOReturnSuccess == result) {
590  HIDGetDeviceInfo(hidDevice, hidProperties, pDevice); /* hidDevice used to find parents in registry tree */
591  HIDGetCollectionElements(hidProperties, pDevice);
592  pDevice->instance_id = ++s_joystick_instance_id;
593  } else {
594  DisposePtr((Ptr) pDevice);
595  pDevice = NULL;
596  }
597  CFRelease(hidProperties);
598  } else {
599  DisposePtr((Ptr) pDevice);
600  pDevice = NULL;
601  }
602  }
603  return pDevice;
604 }
605 
606 /* disposes of the element list associated with a device and the memory associated with the list
607  */
608 
609 static void
610 HIDDisposeElementList(recElement ** elementList)
611 {
612  recElement *pElement = *elementList;
613  while (pElement) {
614  recElement *pElementNext = pElement->pNext;
615  DisposePtr((Ptr) pElement);
616  pElement = pElementNext;
617  }
618  *elementList = NULL;
619 }
620 
621 /* disposes of a single device, closing and releaseing interface, freeing memory fro device and elements, setting device pointer to NULL
622  * all your device no longer belong to us... (i.e., you do not 'own' the device anymore)
623  */
624 
625 static recDevice *
626 HIDDisposeDevice(recDevice ** ppDevice)
627 {
628  kern_return_t result = KERN_SUCCESS;
629  recDevice *pDeviceNext = NULL;
630  if (*ppDevice) {
631  /* save next device prior to disposing of this device */
632  pDeviceNext = (*ppDevice)->pNext;
633 
634  /* free posible io_service_t */
635  if ((*ppDevice)->ffservice) {
636  IOObjectRelease((*ppDevice)->ffservice);
637  (*ppDevice)->ffservice = 0;
638  }
639 
640  /* free element lists */
641  HIDDisposeElementList(&(*ppDevice)->firstAxis);
642  HIDDisposeElementList(&(*ppDevice)->firstButton);
643  HIDDisposeElementList(&(*ppDevice)->firstHat);
644 
645  result = HIDCloseReleaseInterface(*ppDevice); /* function sanity checks interface value (now application does not own device) */
646  if (kIOReturnSuccess != result)
647  HIDReportErrorNum
648  ("HIDCloseReleaseInterface failed when trying to dipose device.",
649  result);
650  DisposePtr((Ptr) * ppDevice);
651  *ppDevice = NULL;
652  }
653  return pDeviceNext;
654 }
655 
656 
657 /* Given an io_object_t from OSX adds a joystick device to our list if appropriate
658  */
659 int
660 AddDeviceHelper( io_object_t ioHIDDeviceObject )
661 {
662  recDevice *device;
663 
664  /* build a device record */
665  device = HIDBuildDevice(ioHIDDeviceObject);
666  if (!device)
667  return 0;
668 
669  /* Filter device list to non-keyboard/mouse stuff */
670  if ((device->usagePage != kHIDPage_GenericDesktop) ||
671  ((device->usage != kHIDUsage_GD_Joystick &&
672  device->usage != kHIDUsage_GD_GamePad &&
673  device->usage != kHIDUsage_GD_MultiAxisController))) {
674 
675  /* release memory for the device */
676  HIDDisposeDevice(&device);
677  DisposePtr((Ptr) device);
678  return 0;
679  }
680 
681  /* We have to do some storage of the io_service_t for
682  * SDL_HapticOpenFromJoystick */
683  if (FFIsForceFeedback(ioHIDDeviceObject) == FF_OK) {
684  device->ffservice = ioHIDDeviceObject;
685  } else {
686  device->ffservice = 0;
687  }
688 
689  device->send_open_event = 1;
690  s_bDeviceAdded = SDL_TRUE;
691 
692  /* Add device to the end of the list */
693  if ( !gpDeviceList )
694  {
695  gpDeviceList = device;
696  }
697  else
698  {
699  recDevice *curdevice;
700 
701  curdevice = gpDeviceList;
702  while ( curdevice->pNext )
703  {
704  curdevice = curdevice->pNext;
705  }
706  curdevice->pNext = device;
707  }
708 
709  return 1;
710 }
711 
712 
713 /* Called by our IO port notifier on the master port when a HID device is inserted, we iterate
714  * and check for new joysticks
715  */
716 void JoystickDeviceWasAddedCallback( void *refcon, io_iterator_t iterator )
717 {
718  io_object_t ioHIDDeviceObject = 0;
719 
720  while ( ( ioHIDDeviceObject = IOIteratorNext(iterator) ) )
721  {
722  if ( ioHIDDeviceObject )
723  {
724  AddDeviceHelper( ioHIDDeviceObject );
725  }
726  }
727 }
728 
729 
730 /* Function to scan the system for joysticks.
731  * Joystick 0 should be the system default joystick.
732  * This function should return the number of available joysticks, or -1
733  * on an unrecoverable fatal error.
734  */
735 int
737 {
738  IOReturn result = kIOReturnSuccess;
739  mach_port_t masterPort = 0;
740  io_iterator_t hidObjectIterator = 0;
741  CFMutableDictionaryRef hidMatchDictionary = NULL;
742  io_object_t ioHIDDeviceObject = 0;
743  io_iterator_t portIterator = 0;
744 
745  if (gpDeviceList) {
746  return SDL_SetError("Joystick: Device list already inited.");
747  }
748 
749  result = IOMasterPort(bootstrap_port, &masterPort);
750  if (kIOReturnSuccess != result) {
751  return SDL_SetError("Joystick: IOMasterPort error with bootstrap_port.");
752  }
753 
754  /* Set up a matching dictionary to search I/O Registry by class name for all HID class devices. */
755  hidMatchDictionary = IOServiceMatching(kIOHIDDeviceKey);
756  if (hidMatchDictionary) {
757  /* Add key for device type (joystick, in this case) to refine the matching dictionary. */
758 
759  /* NOTE: we now perform this filtering later
760  UInt32 usagePage = kHIDPage_GenericDesktop;
761  UInt32 usage = kHIDUsage_GD_Joystick;
762  CFNumberRef refUsage = NULL, refUsagePage = NULL;
763 
764  refUsage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usage);
765  CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsageKey), refUsage);
766  refUsagePage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usagePage);
767  CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsagePageKey), refUsagePage);
768  */
769  } else {
770  return SDL_SetError
771  ("Joystick: Failed to get HID CFMutableDictionaryRef via IOServiceMatching.");
772  }
773 
774  /* Now search I/O Registry for matching devices. */
775  result =
776  IOServiceGetMatchingServices(masterPort, hidMatchDictionary,
777  &hidObjectIterator);
778  /* Check for errors */
779  if (kIOReturnSuccess != result) {
780  return SDL_SetError("Joystick: Couldn't create a HID object iterator.");
781  }
782  if (!hidObjectIterator) { /* there are no joysticks */
783  gpDeviceList = NULL;
784  return 0;
785  }
786  /* IOServiceGetMatchingServices consumes a reference to the dictionary, so we don't need to release the dictionary ref. */
787 
788  /* build flat linked list of devices from device iterator */
789 
790  gpDeviceList = NULL;
791 
792  while ((ioHIDDeviceObject = IOIteratorNext(hidObjectIterator))) {
793  AddDeviceHelper( ioHIDDeviceObject );
794  }
795  result = IOObjectRelease(hidObjectIterator); /* release the iterator */
796 
797  /* now connect notification for new devices */
798  notificationPort = IONotificationPortCreate(masterPort);
799  hidMatchDictionary = IOServiceMatching(kIOHIDDeviceKey);
800 
801  CFRunLoopAddSource(CFRunLoopGetCurrent(),
802  IONotificationPortGetRunLoopSource(notificationPort),
803  kCFRunLoopDefaultMode);
804 
805  /* Register for notifications when a serial port is added to the system */
806  result = IOServiceAddMatchingNotification(notificationPort,
807  kIOFirstMatchNotification,
808  hidMatchDictionary,
809  JoystickDeviceWasAddedCallback,
810  NULL,
811  &portIterator);
812  while (IOIteratorNext(portIterator)) {}; /* Run out the iterator or notifications won't start (you can also use it to iterate the available devices). */
813 
814  return SDL_SYS_NumJoysticks();
815 }
816 
817 /* Function to return the number of joystick devices plugged in right now */
818 int
820 {
821  recDevice *device = gpDeviceList;
822  int nJoySticks = 0;
823 
824  while ( device )
825  {
826  if ( !device->removed )
827  nJoySticks++;
828  device = device->pNext;
829  }
830 
831  return nJoySticks;
832 }
833 
834 /* Function to cause any queued joystick insertions to be processed
835  */
836 void
838 {
839  if ( s_bDeviceAdded || s_bDeviceRemoved )
840  {
841  recDevice *device = gpDeviceList;
842  s_bDeviceAdded = SDL_FALSE;
843  s_bDeviceRemoved = SDL_FALSE;
844  int device_index = 0;
845  /* send notifications */
846  while ( device )
847  {
848  if ( device->send_open_event )
849  {
850  device->send_open_event = 0;
851 #if !SDL_EVENTS_DISABLED
853  event.type = SDL_JOYDEVICEADDED;
854 
855  if (SDL_GetEventState(event.type) == SDL_ENABLE) {
856  event.jdevice.which = device_index;
857  if ((SDL_EventOK == NULL)
858  || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
859  SDL_PushEvent(&event);
860  }
861  }
862 #endif /* !SDL_EVENTS_DISABLED */
863 
864  }
865 
866  if ( device->removed )
867  {
868  recDevice *removeDevice = device;
869  if ( gpDeviceList == removeDevice )
870  {
871  device = device->pNext;
872  gpDeviceList = device;
873  }
874  else
875  {
876  device = gpDeviceList;
877  while ( device->pNext != removeDevice )
878  {
879  device = device->pNext;
880  }
881 
882  device->pNext = removeDevice->pNext;
883  }
884 
885 #if !SDL_EVENTS_DISABLED
887  event.type = SDL_JOYDEVICEREMOVED;
888 
889  if (SDL_GetEventState(event.type) == SDL_ENABLE) {
890  event.jdevice.which = removeDevice->instance_id;
891  if ((SDL_EventOK == NULL)
892  || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
893  SDL_PushEvent(&event);
894  }
895  }
896 
897  DisposePtr((Ptr) removeDevice);
898 #endif /* !SDL_EVENTS_DISABLED */
899 
900  }
901  else
902  {
903  device = device->pNext;
904  device_index++;
905  }
906  }
907  }
908 }
909 
910 SDL_bool
912 {
913  return s_bDeviceAdded || s_bDeviceRemoved;
914 }
915 
916 /* Function to get the device-dependent name of a joystick */
917 const char *
918 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
919 {
920  recDevice *device = gpDeviceList;
921 
922  for (; device_index > 0; device_index--)
923  device = device->pNext;
924 
925  return device->product;
926 }
927 
928 /* Function to return the instance id of the joystick at device_index
929  */
931 SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
932 {
933  recDevice *device = gpDeviceList;
934  int index;
935 
936  for (index = device_index; index > 0; index--)
937  device = device->pNext;
938 
939  return device->instance_id;
940 }
941 
942 /* Function to open a joystick for use.
943  * The joystick to open is specified by the index field of the joystick.
944  * This should fill the nbuttons and naxes fields of the joystick structure.
945  * It returns 0, or -1 if there is an error.
946  */
947 int
948 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
949 {
950  recDevice *device = gpDeviceList;
951  int index;
952 
953  for (index = device_index; index > 0; index--)
954  device = device->pNext;
955 
956  joystick->instance_id = device->instance_id;
957  joystick->hwdata = device;
958  joystick->name = device->product;
959 
960  joystick->naxes = device->axes;
961  joystick->nhats = device->hats;
962  joystick->nballs = 0;
963  joystick->nbuttons = device->buttons;
964  return 0;
965 }
966 
967 /* Function to query if the joystick is currently attached
968  * It returns 1 if attached, 0 otherwise.
969  */
970 SDL_bool
972 {
973  recDevice *device = gpDeviceList;
974 
975  while ( device )
976  {
977  if ( joystick->instance_id == device->instance_id )
978  return SDL_TRUE;
979 
980  device = device->pNext;
981  }
982 
983  return SDL_FALSE;
984 }
985 
986 /* Function to update the state of a joystick - called as a device poll.
987  * This function shouldn't update the joystick structure directly,
988  * but instead should call SDL_PrivateJoystick*() to deliver events
989  * and update joystick device state.
990  */
991 void
993 {
994  recDevice *device = joystick->hwdata;
995  recElement *element;
996  SInt32 value, range;
997  int i;
998 
999  if ( !device )
1000  return;
1001 
1002  if (device->removed) { /* device was unplugged; ignore it. */
1003  recDevice *devicelist = gpDeviceList;
1004  joystick->closed = 1;
1005  joystick->uncentered = 1;
1006 
1007  if ( devicelist == device )
1008  {
1009  gpDeviceList = device->pNext;
1010  }
1011  else
1012  {
1013  while ( devicelist->pNext != device )
1014  {
1015  devicelist = devicelist->pNext;
1016  }
1017 
1018  devicelist->pNext = device->pNext;
1019  }
1020 
1021  DisposePtr((Ptr) device);
1022  joystick->hwdata = NULL;
1023 
1024 #if !SDL_EVENTS_DISABLED
1025  SDL_Event event;
1026  event.type = SDL_JOYDEVICEREMOVED;
1027 
1028  if (SDL_GetEventState(event.type) == SDL_ENABLE) {
1029  event.jdevice.which = joystick->instance_id;
1030  if ((SDL_EventOK == NULL)
1031  || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
1032  SDL_PushEvent(&event);
1033  }
1034  }
1035 #endif /* !SDL_EVENTS_DISABLED */
1036 
1037  return;
1038  }
1039 
1040  element = device->firstAxis;
1041  i = 0;
1042  while (element) {
1043  value = HIDScaledCalibratedValue(device, element, -32768, 32767);
1044  if (value != joystick->axes[i])
1045  SDL_PrivateJoystickAxis(joystick, i, value);
1046  element = element->pNext;
1047  ++i;
1048  }
1049 
1050  element = device->firstButton;
1051  i = 0;
1052  while (element) {
1053  value = HIDGetElementValue(device, element);
1054  if (value > 1) /* handle pressure-sensitive buttons */
1055  value = 1;
1056  if (value != joystick->buttons[i])
1057  SDL_PrivateJoystickButton(joystick, i, value);
1058  element = element->pNext;
1059  ++i;
1060  }
1061 
1062  element = device->firstHat;
1063  i = 0;
1064  while (element) {
1065  Uint8 pos = 0;
1066 
1067  range = (element->max - element->min + 1);
1068  value = HIDGetElementValue(device, element) - element->min;
1069  if (range == 4) /* 4 position hatswitch - scale up value */
1070  value *= 2;
1071  else if (range != 8) /* Neither a 4 nor 8 positions - fall back to default position (centered) */
1072  value = -1;
1073  switch (value) {
1074  case 0:
1075  pos = SDL_HAT_UP;
1076  break;
1077  case 1:
1078  pos = SDL_HAT_RIGHTUP;
1079  break;
1080  case 2:
1081  pos = SDL_HAT_RIGHT;
1082  break;
1083  case 3:
1084  pos = SDL_HAT_RIGHTDOWN;
1085  break;
1086  case 4:
1087  pos = SDL_HAT_DOWN;
1088  break;
1089  case 5:
1090  pos = SDL_HAT_LEFTDOWN;
1091  break;
1092  case 6:
1093  pos = SDL_HAT_LEFT;
1094  break;
1095  case 7:
1096  pos = SDL_HAT_LEFTUP;
1097  break;
1098  default:
1099  /* Every other value is mapped to center. We do that because some
1100  * joysticks use 8 and some 15 for this value, and apparently
1101  * there are even more variants out there - so we try to be generous.
1102  */
1103  pos = SDL_HAT_CENTERED;
1104  break;
1105  }
1106  if (pos != joystick->hats[i])
1107  SDL_PrivateJoystickHat(joystick, i, pos);
1108  element = element->pNext;
1109  ++i;
1110  }
1111 
1112  return;
1113 }
1114 
1115 /* Function to close a joystick after use */
1116 void
1118 {
1119  joystick->closed = 1;
1120 }
1121 
1122 /* Function to perform any system-specific joystick related cleanup */
1123 void
1125 {
1126  while (NULL != gpDeviceList)
1127  gpDeviceList = HIDDisposeDevice(&gpDeviceList);
1128 
1129  if ( notificationPort )
1130  {
1131  IONotificationPortDestroy( notificationPort );
1132  notificationPort = 0;
1133  }
1134 }
1135 
1136 
1138 {
1139  recDevice *device = gpDeviceList;
1140  int index;
1141 
1142  for (index = device_index; index > 0; index--)
1143  device = device->pNext;
1144 
1145  return device->guid;
1146 }
1147 
1149 {
1150  return joystick->hwdata->guid;
1151 }
1152 
1153 #endif /* SDL_JOYSTICK_IOKIT */
1154 
1155 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_HAT_LEFTDOWN
Definition: SDL_joystick.h:199
DECLSPEC int SDLCALL SDL_PushEvent(SDL_Event *event)
Add an event to the event queue.
Definition: SDL_events.c:457
SDL_JoystickGUID guid
GLint GLenum GLsizei GLsizei GLsizei GLint GLenum GLenum type
Definition: gl2ext.h:845
IONotificationPortRef notificationPort
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
Definition: SDL_joystick.c:536
int SDL_SYS_NumJoysticks()
#define NULL
Definition: ftobjs.h:61
io_iterator_t portIterator
typedef HRESULT(WINAPI *LPD3DXIMTSIGNALCALLBACK)(CONST D3DXVECTOR2 *uv
SDL_bool
Definition: SDL_stdinc.h:116
#define SDL_HAT_RIGHTUP
Definition: SDL_joystick.h:196
Sint32 SDL_JoystickID
Definition: SDL_joystick.h:72
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
Definition: SDL_joystick.c:610
#define SDL_ENABLE
Definition: SDL_events.h:688
void SDL_SYS_JoystickQuit(void)
Uint8 data[16]
Definition: SDL_joystick.h:69
if(!yyg->yy_init)
struct joystick_hwdata * hwdata
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
Definition: SDL_joystick.c:496
struct recElement * pNext
IOHIDElementCookie cookie
EGLContext EGLenum target
Definition: eglext.h:87
GLenum GLint * range
Definition: glew.h:3391
uint32_t Uint32
An unsigned 32-bit integer type.
Definition: SDL_stdinc.h:145
#define SDL_HAT_RIGHT
Definition: SDL_joystick.h:193
io_service_t ffservice
GLuint64EXT * result
Definition: glew.h:12708
#define SDL_HAT_RIGHTDOWN
Definition: SDL_joystick.h:197
#define SDL_GetEventState(type)
Definition: SDL_events.h:701
#define SDL_HAT_LEFT
Definition: SDL_joystick.h:195
SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
SDL_EventFilter SDL_EventOK
Definition: SDL_events.c:40
DECLSPEC size_t SDLCALL SDL_strlcpy(char *dst, const char *src, size_t maxlen)
Definition: SDL_string.c:448
void SDL_SYS_JoystickDetect()
DECLSPEC int SDLCALL SDL_SetError(const char *fmt,...)
Definition: SDL_error.c:53
const char * SDL_SYS_JoystickNameForDeviceIndex(int device_index)
GLuint index
Definition: glew.h:1800
struct joystick_hwdata * pNext
void SDL_SYS_JoystickUpdate(SDL_Joystick *joystick)
SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
recElement * firstAxis
SDL_bool SDL_SYS_JoystickNeedsPolling()
int SDL_SYS_JoystickInit(void)
void * SDL_EventOKParam
Definition: SDL_events.c:41
recElement * firstHat
EGLSurface EGLint void ** value
Definition: eglext.h:301
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:129
#define SDL_HAT_LEFTUP
Definition: SDL_joystick.h:198
SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
int SDL_SYS_JoystickOpen(SDL_Joystick *joystick, int device_index)
uint16_t Uint16
An unsigned 16-bit integer type.
Definition: SDL_stdinc.h:137
General event structure.
Definition: SDL_events.h:495
#define min(x, y)
Definition: os.h:75
recElement * firstButton
int i
Definition: pngrutil.c:1377
#define SDL_HAT_CENTERED
Definition: SDL_joystick.h:191
void SDL_SYS_JoystickClose(SDL_Joystick *joystick)
#define max(x, y)
Definition: os.h:79
#define SDL_HAT_UP
Definition: SDL_joystick.h:192
IOHIDDeviceInterface ** interface
#define SDL_HAT_DOWN
Definition: SDL_joystick.h:194
GLsizeiptr const GLvoid GLenum usage
Definition: glew.h:1667
SDL_JoystickID instance_id
SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index)
Uint32 type
Definition: SDL_events.h:497
cl_event event
Definition: glew.h:3556