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