SDL_sysjoystick.c
上传用户:sun1608
上传日期:2007-02-02
资源大小:6116k
文件大小:26k
源码类别:

流媒体/Mpeg4/MP4

开发平台:

Visual C++

  1. /*
  2. SDL - Simple DirectMedia Layer
  3. Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public
  6. License as published by the Free Software Foundation; either
  7. version 2 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11. Library General Public License for more details.
  12. You should have received a copy of the GNU Library General Public
  13. License along with this library; if not, write to the Free
  14. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  15. Sam Lantinga
  16. slouken@libsdl.org
  17. */
  18. /* SDL joystick driver for Darwin / MacOS X, based on the IOKit HID API */
  19. /* Written 2001 by Max Horn */
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <unistd.h>
  23. #include <ctype.h>
  24. #include <sys/errno.h>
  25. #include <sysexits.h>
  26. #include <mach/mach.h>
  27. #include <mach/mach_error.h>
  28. #include <IOKit/IOKitLib.h>
  29. #include <IOKit/IOCFPlugIn.h>
  30. #ifdef MACOS_10_0_4
  31. #include <IOKit/hidsystem/IOHIDUsageTables.h>
  32. #else
  33. /* The header was moved here in MacOS X 10.1 */
  34. #include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
  35. #endif
  36. #include <IOKit/hid/IOHIDLib.h>
  37. #include <IOKit/hid/IOHIDKeys.h>
  38. #include <CoreFoundation/CoreFoundation.h>
  39. #include "SDL_error.h"
  40. #include "SDL_joystick.h"
  41. #include "SDL_sysjoystick.h"
  42. #include "SDL_joystick_c.h"
  43. struct recElement
  44. {
  45. IOHIDElementCookie cookie; // unique value which identifies element, will NOT change
  46. long min; // reported min value possible
  47. long max; // reported max value possible
  48. /*
  49. TODO: maybe should handle the following stuff somehow?
  50. long scaledMin; // reported scaled min value possible
  51. long scaledMax; // reported scaled max value possible
  52. long size; // size in bits of data return from element
  53. Boolean relative; // are reports relative to last report (deltas)
  54. Boolean wrapping; // does element wrap around (one value higher than max is min)
  55. Boolean nonLinear; // are the values reported non-linear relative to element movement
  56. Boolean preferredState; // does element have a preferred state (such as a button)
  57. Boolean nullState; // does element have null state
  58. */
  59. /* runtime variables used for auto-calibration */
  60. long minReport; // min returned value
  61. long maxReport; // max returned value
  62. struct recElement * pNext; // next element in list
  63. };
  64. typedef struct recElement recElement;
  65. struct joystick_hwdata
  66. {
  67. IOHIDDeviceInterface ** interface; // interface to device, NULL = no interface
  68. char product[256]; // name of product
  69. long usage; // usage page from IOUSBHID Parser.h which defines general usage
  70. long usagePage; // usage within above page from IOUSBHID Parser.h which defines specific usage
  71. long axes; // number of axis (calculated, not reported by device)
  72. long buttons; // number of buttons (calculated, not reported by device)
  73. long hats; // number of hat switches (calculated, not reported by device)
  74. long elements; // number of total elements (shouldbe total of above) (calculated, not reported by device)
  75. recElement* firstAxis;
  76. recElement* firstButton;
  77. recElement* firstHat;
  78. struct joystick_hwdata* pNext; // next device
  79. };
  80. typedef struct joystick_hwdata recDevice;
  81. /* Linked list of all available devices */
  82. static recDevice *gpDeviceList = NULL;
  83. void HIDReportErrorNum (char * strError, long numError)
  84. {
  85. SDL_SetError(strError);
  86. }
  87. static void HIDGetCollectionElements (CFMutableDictionaryRef deviceProperties, recDevice *pDevice);
  88. /* returns current value for element, polling element
  89.  * will return 0 on error conditions which should be accounted for by application
  90.  */
  91. SInt32 HIDGetElementValue (recDevice *pDevice, recElement *pElement)
  92. {
  93. IOReturn result = kIOReturnSuccess;
  94. IOHIDEventStruct hidEvent;
  95. hidEvent.value = 0;
  96. if (NULL != pDevice && NULL != pElement && NULL != pDevice->interface)
  97. {
  98. result = (*(pDevice->interface))->getElementValue(pDevice->interface, pElement->cookie, &hidEvent);
  99. if (kIOReturnSuccess == result)
  100. {
  101. /* record min and max for auto calibration */
  102. if (hidEvent.value < pElement->minReport)
  103. pElement->minReport = hidEvent.value;
  104. if (hidEvent.value > pElement->maxReport)
  105. pElement->maxReport = hidEvent.value;
  106. }
  107. }
  108. // auto user scale
  109. return hidEvent.value;
  110. }
  111. /* similiar to HIDGetElementValue, but auto-calibrates the value before returning it */
  112. SInt32 HIDCalibratedValue (recDevice *pDevice, recElement *pElement)
  113. {
  114. float deviceScale = pElement->max - pElement->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) + pElement->min;
  121. }
  122. /* similiar to HIDCalibratedValue but calibrates to an arbitrary scale instead of the elements default scale */
  123. SInt32 HIDScaledCalibratedValue (recDevice *pDevice, recElement *pElement, long min, long max)
  124. {
  125. float deviceScale = max - min;
  126. float readScale = pElement->maxReport - pElement->minReport;
  127. SInt32 value = HIDGetElementValue(pDevice, pElement);
  128. if (readScale == 0)
  129. return value; // no scaling at all
  130. else
  131. return ((value - pElement->minReport) * deviceScale / readScale) + min;
  132. }
  133. /* Create and open an interface to device, required prior to extracting values or building queues.
  134.  * Note: appliction now owns the device and must close and release it prior to exiting
  135.  */
  136. IOReturn HIDCreateOpenDeviceInterface (io_object_t hidDevice, recDevice *pDevice)
  137. {
  138. IOReturn result = kIOReturnSuccess;
  139. HRESULT plugInResult = S_OK;
  140. SInt32 score = 0;
  141. IOCFPlugInInterface ** ppPlugInInterface = NULL;
  142. if (NULL == pDevice->interface)
  143. {
  144. result = IOCreatePlugInInterfaceForService (hidDevice, kIOHIDDeviceUserClientTypeID,
  145. kIOCFPlugInInterfaceID, &ppPlugInInterface, &score);
  146. if (kIOReturnSuccess == result)
  147. {
  148. // Call a method of the intermediate plug-in to create the device interface
  149. plugInResult = (*ppPlugInInterface)->QueryInterface (ppPlugInInterface,
  150. CFUUIDGetUUIDBytes (kIOHIDDeviceInterfaceID), (void *) &(pDevice->interface));
  151. if (S_OK != plugInResult)
  152. HIDReportErrorNum ("Couldn誸 query HID class device interface from plugInInterface", plugInResult);
  153. (*ppPlugInInterface)->Release (ppPlugInInterface);
  154. }
  155. else
  156. HIDReportErrorNum ("Failed to create **plugInInterface via IOCreatePlugInInterfaceForService.", result);
  157. }
  158. if (NULL != pDevice->interface)
  159. {
  160. result = (*(pDevice->interface))->open (pDevice->interface, 0);
  161. if (kIOReturnSuccess != result)
  162. HIDReportErrorNum ("Failed to open pDevice->interface via open.", result);
  163. }
  164. return result;
  165. }
  166. /* Closes and releases interface to device, should be done prior to exting application
  167.  * Note: will have no affect if device or interface do not exist
  168.  * application will "own" the device if interface is not closed
  169.  * (device may have to be plug and re-plugged in different location to get it working again without a restart)
  170.  */
  171. IOReturn HIDCloseReleaseInterface (recDevice *pDevice)
  172. {
  173. IOReturn result = kIOReturnSuccess;
  174. if ((NULL != pDevice) && (NULL != pDevice->interface))
  175. {
  176. // close the interface
  177. result = (*(pDevice->interface))->close (pDevice->interface);
  178. if (kIOReturnNotOpen == result)
  179. {
  180. //  do nothing as device was not opened, thus can't be closed
  181. }
  182. else if (kIOReturnSuccess != result)
  183. HIDReportErrorNum ("Failed to close IOHIDDeviceInterface.", result);
  184. //release the interface
  185. result = (*(pDevice->interface))->Release (pDevice->interface);
  186. if (kIOReturnSuccess != result)
  187. HIDReportErrorNum ("Failed to release IOHIDDeviceInterface.", result);
  188. pDevice->interface = NULL;
  189. }
  190. return result;
  191. }
  192. /* extracts actual specific element information from each element CF dictionary entry */
  193. static void HIDGetElementInfo (CFTypeRef refElement, recElement *pElement)
  194. {
  195. long number;
  196. CFTypeRef refType;
  197. refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementCookieKey));
  198. if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
  199. pElement->cookie = (IOHIDElementCookie) number;
  200. refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementMinKey));
  201. if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
  202. pElement->min = number;
  203. pElement->maxReport = pElement->min;
  204. refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementMaxKey));
  205. if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
  206. pElement->max = number;
  207. pElement->minReport = pElement->max;
  208. /*
  209. TODO: maybe should handle the following stuff somehow?
  210. refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMinKey));
  211. if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
  212. pElement->scaledMin = number;
  213. refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMaxKey));
  214. if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
  215. pElement->scaledMax = number;
  216. refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementSizeKey));
  217. if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number))
  218. pElement->size = number;
  219. refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsRelativeKey));
  220. if (refType)
  221. pElement->relative = CFBooleanGetValue (refType);
  222. refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsWrappingKey));
  223. if (refType)
  224. pElement->wrapping = CFBooleanGetValue (refType);
  225. refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsNonLinearKey));
  226. if (refType)
  227. pElement->nonLinear = CFBooleanGetValue (refType);
  228. refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasPreferedStateKey));
  229. if (refType)
  230. pElement->preferredState = CFBooleanGetValue (refType);
  231. refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasNullStateKey));
  232. if (refType)
  233. pElement->nullState = CFBooleanGetValue (refType);
  234. */
  235. }
  236. /* examines CF dictionary vlaue in device element hierarchy to determine if it is element of interest or a collection of more elements
  237.  * if element of interest allocate storage, add to list and retrieve element specific info
  238.  * if collection then pass on to deconstruction collection into additional individual elements
  239.  */
  240. static void HIDAddElement (CFTypeRef refElement, recDevice* pDevice)
  241. {
  242. recElement* element = NULL;
  243. recElement** headElement = NULL;
  244. long elementType, usagePage, usage;
  245. CFTypeRef refElementType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementTypeKey));
  246. CFTypeRef refUsagePage = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUsagePageKey));
  247. CFTypeRef refUsage = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUsageKey));
  248. if ((refElementType) && (CFNumberGetValue (refElementType, kCFNumberLongType, &elementType)))
  249. {
  250. /* look at types of interest */
  251. if ((elementType == kIOHIDElementTypeInput_Misc) || (elementType == kIOHIDElementTypeInput_Button) ||
  252. (elementType == kIOHIDElementTypeInput_Axis))
  253. {
  254. if (refUsagePage && CFNumberGetValue (refUsagePage, kCFNumberLongType, &usagePage) &&
  255. refUsage && CFNumberGetValue (refUsage, kCFNumberLongType, &usage))
  256. {
  257. switch (usagePage) /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */
  258. {
  259. case kHIDPage_GenericDesktop:
  260. {
  261. switch (usage) /* look at usage to determine function */
  262. {
  263. case kHIDUsage_GD_X:
  264. case kHIDUsage_GD_Y:
  265. case kHIDUsage_GD_Z:
  266. case kHIDUsage_GD_Rx:
  267. case kHIDUsage_GD_Ry:
  268. case kHIDUsage_GD_Rz:
  269. element = (recElement *) NewPtrClear (sizeof (recElement));
  270. if (element)
  271. {
  272. pDevice->axes++;
  273. headElement = &(pDevice->firstAxis);
  274. }
  275. break;
  276. case kHIDUsage_GD_Hatswitch:
  277. element = (recElement *) NewPtrClear (sizeof (recElement));
  278. if (element)
  279. {
  280. pDevice->hats++;
  281. headElement = &(pDevice->firstHat);
  282. }
  283. break;
  284. }
  285. }
  286. break;
  287. case kHIDPage_Button:
  288. element = (recElement *) NewPtrClear (sizeof (recElement));
  289. if (element)
  290. {
  291. pDevice->buttons++;
  292. headElement = &(pDevice->firstButton);
  293. }
  294. break;
  295. default:
  296. break;
  297. }
  298. }
  299. }
  300. else if (kIOHIDElementTypeCollection == elementType)
  301. HIDGetCollectionElements ((CFMutableDictionaryRef) refElement, pDevice);
  302. }
  303. if (element && headElement) /* add to list */
  304. {
  305. pDevice->elements++;
  306. if (NULL == *headElement)
  307. *headElement = element;
  308. else
  309. {
  310. recElement *elementPrevious, *elementCurrent;
  311. elementCurrent = *headElement;
  312. while (elementCurrent)
  313. {
  314. elementPrevious = elementCurrent;
  315. elementCurrent = elementPrevious->pNext;
  316. }
  317. elementPrevious->pNext = element;
  318. }
  319. element->pNext = NULL;
  320. HIDGetElementInfo (refElement, element);
  321. }
  322. }
  323. /* collects information from each array member in device element list (each array memeber = element) */
  324. static void HIDGetElementsCFArrayHandler (const void * value, void * parameter)
  325. {
  326. if (CFGetTypeID (value) == CFDictionaryGetTypeID ())
  327. HIDAddElement ((CFTypeRef) value, (recDevice *) parameter);
  328. }
  329. /* handles retrieval of element information from arrays of elements in device IO registry information */
  330. static void HIDGetElements (CFTypeRef refElementCurrent, recDevice *pDevice)
  331. {
  332. CFTypeID type = CFGetTypeID (refElementCurrent);
  333. if (type == CFArrayGetTypeID()) /* if element is an array */
  334. {
  335. CFRange range = {0, CFArrayGetCount (refElementCurrent)};
  336. /* CountElementsCFArrayHandler called for each array member */
  337. CFArrayApplyFunction (refElementCurrent, range, HIDGetElementsCFArrayHandler, pDevice);
  338. }
  339. }
  340. /* handles extracting element information from element collection CF types
  341.  * used from top level element decoding and hierarchy deconstruction to flatten device element list
  342.  */
  343. static void HIDGetCollectionElements (CFMutableDictionaryRef deviceProperties, recDevice *pDevice)
  344. {
  345. CFTypeRef refElementTop = CFDictionaryGetValue (deviceProperties, CFSTR(kIOHIDElementKey));
  346. if (refElementTop)
  347. HIDGetElements (refElementTop, pDevice);
  348. }
  349. /* use top level element usage page and usage to discern device usage page and usage setting appropriate vlaues in device record */
  350. static void HIDTopLevelElementHandler (const void * value, void * parameter)
  351. {
  352. CFTypeRef refCF = 0;
  353. if (CFGetTypeID (value) != CFDictionaryGetTypeID ())
  354. return;
  355. refCF = CFDictionaryGetValue (value, CFSTR(kIOHIDElementUsagePageKey));
  356. if (!CFNumberGetValue (refCF, kCFNumberLongType, &((recDevice *) parameter)->usagePage))
  357. SDL_SetError ("CFNumberGetValue error retrieving pDevice->usagePage.");
  358. refCF = CFDictionaryGetValue (value, CFSTR(kIOHIDElementUsageKey));
  359. if (!CFNumberGetValue (refCF, kCFNumberLongType, &((recDevice *) parameter)->usage))
  360. SDL_SetError ("CFNumberGetValue error retrieving pDevice->usage.");
  361. }
  362. /* extracts device info from CF dictionary records in IO registry */
  363. static void HIDGetDeviceInfo (io_object_t hidDevice, CFMutableDictionaryRef hidProperties, recDevice *pDevice)
  364. {
  365. CFMutableDictionaryRef usbProperties = 0;
  366. io_registry_entry_t parent1, parent2;
  367. /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
  368.  * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
  369.  */
  370. if ((KERN_SUCCESS == IORegistryEntryGetParentEntry (hidDevice, kIOServicePlane, &parent1)) &&
  371. (KERN_SUCCESS == IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2)) &&
  372. (KERN_SUCCESS == IORegistryEntryCreateCFProperties (parent2, &usbProperties, kCFAllocatorDefault, kNilOptions)))
  373. {
  374. if (usbProperties)
  375. {
  376. CFTypeRef refCF = 0;
  377. /* get device info
  378.  * try hid dictionary first, if fail then go to usb dictionary
  379.  */
  380. /* get product name */
  381. refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDProductKey));
  382. if (!refCF)
  383. refCF = CFDictionaryGetValue (usbProperties, CFSTR("USB Product Name"));
  384. if (refCF)
  385. {
  386. if (!CFStringGetCString (refCF, pDevice->product, 256, CFStringGetSystemEncoding ()))
  387. SDL_SetError ("CFStringGetCString error retrieving pDevice->product.");
  388. }
  389. /* get usage page and usage */
  390. refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsagePageKey));
  391. if (refCF)
  392. {
  393. if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->usagePage))
  394. SDL_SetError ("CFNumberGetValue error retrieving pDevice->usagePage.");
  395. refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsageKey));
  396. if (refCF)
  397. if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->usage))
  398. SDL_SetError ("CFNumberGetValue error retrieving pDevice->usage.");
  399. }
  400. if (NULL == refCF) /* get top level element HID usage page or usage */
  401. {
  402. /* use top level element instead */
  403. CFTypeRef refCFTopElement = 0;
  404. refCFTopElement = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDElementKey));
  405. {
  406. /* refCFTopElement points to an array of element dictionaries */
  407. CFRange range = {0, CFArrayGetCount (refCFTopElement)};
  408. CFArrayApplyFunction (refCFTopElement, range, HIDTopLevelElementHandler, pDevice);
  409. }
  410. }
  411. CFRelease (usbProperties);
  412. }
  413. else
  414. SDL_SetError ("IORegistryEntryCreateCFProperties failed to create usbProperties.");
  415. if (kIOReturnSuccess != IOObjectRelease (parent2))
  416. SDL_SetError ("IOObjectRelease error with parent2.");
  417. if (kIOReturnSuccess != IOObjectRelease (parent1))
  418. SDL_SetError ("IOObjectRelease error with parent1.");
  419. }
  420. }
  421. static recDevice *HIDBuildDevice (io_object_t hidDevice)
  422. {
  423. recDevice *pDevice = (recDevice *) NewPtrClear (sizeof (recDevice));
  424. if (pDevice)
  425. {
  426. /* get dictionary for HID properties */
  427. CFMutableDictionaryRef hidProperties = 0;
  428. kern_return_t result = IORegistryEntryCreateCFProperties (hidDevice, &hidProperties, kCFAllocatorDefault, kNilOptions);
  429. if ((result == KERN_SUCCESS) && hidProperties)
  430. {
  431. /* create device interface */
  432. result = HIDCreateOpenDeviceInterface (hidDevice, pDevice);
  433. if (kIOReturnSuccess == result)
  434. {
  435. HIDGetDeviceInfo (hidDevice, hidProperties, pDevice); /* hidDevice used to find parents in registry tree */
  436. HIDGetCollectionElements (hidProperties, pDevice);
  437. }
  438. else
  439. {
  440. DisposePtr((Ptr)pDevice);
  441. pDevice = NULL;
  442. }
  443. CFRelease (hidProperties);
  444. }
  445. else
  446. {
  447. DisposePtr((Ptr)pDevice);
  448. pDevice = NULL;
  449. }
  450. }
  451. return pDevice;
  452. }
  453. /* disposes of the element list associated with a device and the memory associated with the list
  454.  */
  455. static void HIDDisposeElementList (recElement **elementList)
  456. {
  457. recElement *pElement = *elementList;
  458. while (pElement)
  459. {
  460. recElement *pElementNext = pElement->pNext;
  461. DisposePtr ((Ptr) pElement);
  462. pElement = pElementNext;
  463. }
  464. *elementList = NULL;
  465. }
  466. /* disposes of a single device, closing and releaseing interface, freeing memory fro device and elements, setting device pointer to NULL
  467.  * all your device no longer belong to us... (i.e., you do not 'own' the device anymore)
  468.  */
  469. static recDevice *HIDDisposeDevice (recDevice **ppDevice)
  470. {
  471. kern_return_t result = KERN_SUCCESS;
  472. recDevice *pDeviceNext = NULL;
  473. if (*ppDevice)
  474. {
  475. // save next device prior to disposing of this device
  476. pDeviceNext = (*ppDevice)->pNext;
  477. /* free element lists */
  478. HIDDisposeElementList (&(*ppDevice)->firstAxis);
  479. HIDDisposeElementList (&(*ppDevice)->firstButton);
  480. HIDDisposeElementList (&(*ppDevice)->firstHat);
  481. result = HIDCloseReleaseInterface (*ppDevice); /* function sanity checks interface value (now application does not own device) */
  482. if (kIOReturnSuccess != result)
  483. HIDReportErrorNum ("HIDCloseReleaseInterface failed when trying to dipose device.", result);
  484. DisposePtr ((Ptr)*ppDevice);
  485. *ppDevice = NULL;
  486. }
  487. return pDeviceNext;
  488. }
  489. /* Function to scan the system for joysticks.
  490.  * Joystick 0 should be the system default joystick.
  491.  * This function should return the number of available joysticks, or -1
  492.  * on an unrecoverable fatal error.
  493.  */
  494. int SDL_SYS_JoystickInit(void)
  495. {
  496. IOReturn result = kIOReturnSuccess;
  497. mach_port_t masterPort = NULL;
  498. io_iterator_t hidObjectIterator = NULL;
  499. CFMutableDictionaryRef hidMatchDictionary = NULL;
  500. recDevice *device, *lastDevice;
  501. io_object_t ioHIDDeviceObject = NULL;
  502. UInt32 usagePage = kHIDPage_GenericDesktop;
  503. UInt32 usage = kHIDUsage_GD_Joystick; /* We probably also should check for gamepads? */
  504. SDL_numjoysticks = 0;
  505. if (NULL != gpDeviceList)
  506. {
  507. SDL_SetError("Joystick: Device list already inited.");
  508. return -1;
  509. }
  510. result = IOMasterPort (bootstrap_port, &masterPort);
  511. if (kIOReturnSuccess != result)
  512. {
  513. SDL_SetError("Joystick: IOMasterPort error with bootstrap_port.");
  514. return -1;
  515. }
  516. /* Set up a matching dictionary to search I/O Registry by class name for all HID class devices. */
  517. hidMatchDictionary = IOServiceMatching (kIOHIDDeviceKey);
  518. if ((hidMatchDictionary != NULL) && (usagePage) && (usage))
  519. {
  520. /* Add key for device type (joystick, in this case) to refine the matching dictionary. */
  521. CFNumberRef refUsage = NULL, refUsagePage = NULL;
  522. refUsage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usagePage);
  523. CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsageKey), refUsage);
  524. refUsagePage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usage);
  525. CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsagePageKey), refUsagePage);
  526. }
  527. else
  528. {
  529. SDL_SetError("Joystick: Failed to get HID CFMutableDictionaryRef via IOServiceMatching.");
  530. return -1;
  531. }
  532. /*/ Now search I/O Registry for matching devices. */
  533. result = IOServiceGetMatchingServices (masterPort, hidMatchDictionary, &hidObjectIterator);
  534. /* Check for errors */
  535. if ((kIOReturnSuccess != result) || (NULL == hidObjectIterator))
  536. {
  537. SDL_SetError("Joystick: Couldn't create a HID object iterator.");
  538. return -1;
  539. }
  540. /* IOServiceGetMatchingServices consumes a reference to the dictionary, so we don't need to release the dictionary ref. */
  541. /* build flat linked list of devices from device iterator */
  542. gpDeviceList = lastDevice = NULL;
  543. while ((ioHIDDeviceObject = IOIteratorNext (hidObjectIterator)))
  544. {
  545. /* build a device record */
  546. device = HIDBuildDevice (ioHIDDeviceObject);
  547. if (!device)
  548. continue;
  549. /* dump device object, it is no longer needed */
  550. result = IOObjectRelease (ioHIDDeviceObject);
  551. // if (KERN_SUCCESS != result)
  552. // HIDReportErrorNum ("IOObjectRelease error with ioHIDDeviceObject.", result);
  553. /* Add device to the end of the list */
  554. if (lastDevice)
  555. lastDevice->pNext = device;
  556. else
  557. gpDeviceList = device;
  558. lastDevice = device;
  559. }
  560. result = IOObjectRelease (hidObjectIterator); /* release the iterator */
  561. /* Count the total number of devices we found */
  562. device = gpDeviceList;
  563. while (device)
  564. {
  565. SDL_numjoysticks++;
  566. device = device->pNext;
  567. }
  568. return SDL_numjoysticks;
  569. }
  570. /* Function to get the device-dependent name of a joystick */
  571. const char *SDL_SYS_JoystickName(int index)
  572. {
  573. recDevice *device = gpDeviceList;
  574. for (; index > 0; index--)
  575. device = device->pNext;
  576. return device->product;
  577. }
  578. /* Function to open a joystick for use.
  579.  * The joystick to open is specified by the index field of the joystick.
  580.  * This should fill the nbuttons and naxes fields of the joystick structure.
  581.  * It returns 0, or -1 if there is an error.
  582.  */
  583. int SDL_SYS_JoystickOpen(SDL_Joystick *joystick)
  584. {
  585. recDevice *device = gpDeviceList;
  586. int index;
  587. for (index = joystick->index; index > 0; index--)
  588. device = device->pNext;
  589. joystick->hwdata = device;
  590. joystick->name = device->product;
  591. joystick->naxes = device->axes;
  592. joystick->nhats = device->hats;
  593. joystick->nballs = 0;
  594. joystick->nbuttons = device->buttons;
  595. return 0;
  596. }
  597. /* Function to update the state of a joystick - called as a device poll.
  598.  * This function shouldn't update the joystick structure directly,
  599.  * but instead should call SDL_PrivateJoystick*() to deliver events
  600.  * and update joystick device state.
  601.  */
  602. void SDL_SYS_JoystickUpdate(SDL_Joystick *joystick)
  603. {
  604. recDevice *device = joystick->hwdata;
  605. recElement *element;
  606. SInt32 value;
  607. int i;
  608. element = device->firstAxis;
  609. i = 0;
  610. while (element)
  611. {
  612. value = HIDScaledCalibratedValue(device, element, -32768, 32767);
  613. if ( value != joystick->axes[i] )
  614. SDL_PrivateJoystickAxis(joystick, i, value);
  615. element = element->pNext;
  616. ++i;
  617. }
  618. element = device->firstButton;
  619. i = 0;
  620. while (element)
  621. {
  622. value = HIDGetElementValue(device, element);
  623. if ( value != joystick->buttons[i] )
  624. SDL_PrivateJoystickButton(joystick, i, value);
  625. element = element->pNext;
  626. ++i;
  627. }
  628. element = device->firstHat;
  629. i = 0;
  630. while (element)
  631. {
  632. Uint8 pos = 0;
  633. value = HIDGetElementValue(device, element);
  634. switch(value)
  635. {
  636. case 0:
  637. pos = SDL_HAT_CENTERED;
  638. break;
  639. case 1:
  640. pos = SDL_HAT_UP;
  641. break;
  642. case 2:
  643. pos = SDL_HAT_RIGHTUP;
  644. break;
  645. case 3:
  646. pos = SDL_HAT_RIGHT;
  647. break;
  648. case 4:
  649. pos = SDL_HAT_RIGHTDOWN;
  650. break;
  651. case 5:
  652. pos = SDL_HAT_DOWN;
  653. break;
  654. case 6:
  655. pos = SDL_HAT_LEFTDOWN;
  656. break;
  657. case 7:
  658. pos = SDL_HAT_LEFT;
  659. break;
  660. case 8:
  661. pos = SDL_HAT_LEFTUP;
  662. break;
  663. }
  664. if ( pos != joystick->hats[i] )
  665. SDL_PrivateJoystickHat(joystick, i, pos);
  666. element = element->pNext;
  667. ++i;
  668. }
  669. return;
  670. }
  671. /* Function to close a joystick after use */
  672. void SDL_SYS_JoystickClose(SDL_Joystick *joystick)
  673. {
  674. /* Should we do anything here? */
  675. return;
  676. }
  677. /* Function to perform any system-specific joystick related cleanup */
  678. void SDL_SYS_JoystickQuit(void)
  679. {
  680. while (NULL != gpDeviceList)
  681. gpDeviceList = HIDDisposeDevice (&gpDeviceList);
  682. }