23 #ifdef SDL_JOYSTICK_IOKIT
31 #include <mach/mach.h>
32 #include <mach/mach_error.h>
33 #include <IOKit/IOKitLib.h>
34 #include <IOKit/IOCFPlugIn.h>
36 #include <IOKit/hidsystem/IOHIDUsageTables.h>
39 #include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
41 #include <IOKit/hid/IOHIDLib.h>
42 #include <IOKit/hid/IOHIDKeys.h>
43 #include <CoreFoundation/CoreFoundation.h>
44 #include <Carbon/Carbon.h>
45 #include <IOKit/IOMessage.h>
48 #include <ForceFeedback/ForceFeedback.h>
49 #include <ForceFeedback/ForceFeedbackConstants.h>
52 #include "../SDL_sysjoystick.h"
53 #include "../SDL_joystick_c.h"
54 #include "SDL_sysjoystick_c.h"
56 #if !SDL_EVENTS_DISABLED
57 #include "../../events/SDL_events_c.h"
64 IONotificationPortRef notificationPort = 0;
70 static int s_joystick_instance_id = -1;
73 HIDReportErrorNum(
char *strError,
long numError)
78 static void HIDGetCollectionElements(CFMutableDictionaryRef deviceProperties,
88 IOReturn
result = kIOReturnSuccess;
89 IOHIDEventStruct hidEvent;
97 if (kIOReturnSuccess == result) {
101 if (hidEvent.value > pElement->
maxReport)
107 return hidEvent.value;
114 float deviceScale = max -
min;
116 SInt32
value = HIDGetElementValue(pDevice, pElement);
120 return ((value - pElement->
minReport) * deviceScale / readScale) +
126 HIDRemovalCallback(
void *
target, IOReturn result,
void *refcon,
void *sender)
136 void JoystickDeviceWasRemovedCallback(
void * refcon, io_service_t service, natural_t messageType,
void * messageArgument )
138 if( messageType == kIOMessageServiceIsTerminated && refcon )
152 HIDCreateOpenDeviceInterface(io_object_t hidDevice,
recDevice * pDevice)
154 IOReturn result = kIOReturnSuccess;
157 IOCFPlugInInterface **ppPlugInInterface =
NULL;
161 IOCreatePlugInInterfaceForService(hidDevice,
162 kIOHIDDeviceUserClientTypeID,
163 kIOCFPlugInInterfaceID,
164 &ppPlugInInterface, &score);
165 if (kIOReturnSuccess == result) {
168 (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
170 (kIOHIDDeviceInterfaceID),
173 if (S_OK != plugInResult)
175 (
"Couldn't query HID class device interface from plugInInterface",
177 (*ppPlugInInterface)->Release(ppPlugInInterface);
180 (
"Failed to create **plugInInterface via IOCreatePlugInInterfaceForService.",
185 if (kIOReturnSuccess != result)
187 (
"Failed to open pDevice->interface via open.", result);
200 CFRunLoopAddSource(CFRunLoopGetCurrent(),
202 kCFRunLoopDefaultMode);
208 JoystickDeviceWasRemovedCallback,
211 if (kIOReturnSuccess != result) {
213 (
"Failed to register for removal callback.", result);
228 HIDCloseReleaseInterface(
recDevice * pDevice)
230 IOReturn result = kIOReturnSuccess;
235 if (kIOReturnNotOpen == result) {
237 }
else if (kIOReturnSuccess != result)
238 HIDReportErrorNum(
"Failed to close IOHIDDeviceInterface.",
242 if (kIOReturnSuccess != result)
243 HIDReportErrorNum(
"Failed to release IOHIDDeviceInterface.",
259 HIDGetElementInfo(CFTypeRef refElement,
recElement * pElement)
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))
271 refType = CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementMaxKey));
272 if (refType && CFNumberGetValue(refType, kCFNumberLongType, &number))
310 HIDAddElement(CFTypeRef refElement,
recDevice * pDevice)
314 long elementType, usagePage,
usage;
315 CFTypeRef refElementType =
316 CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementTypeKey));
317 CFTypeRef refUsagePage =
318 CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementUsagePageKey));
320 CFDictionaryGetValue(refElement, CFSTR(kIOHIDElementUsageKey));
325 (CFNumberGetValue(refElementType, kCFNumberLongType, &elementType))) {
327 if ((elementType == kIOHIDElementTypeInput_Misc)
328 || (elementType == kIOHIDElementTypeInput_Button)
329 || (elementType == kIOHIDElementTypeInput_Axis)) {
331 && CFNumberGetValue(refUsagePage, kCFNumberLongType,
332 &usagePage) && refUsage
333 && CFNumberGetValue(refUsage, kCFNumberLongType, &usage)) {
335 case kHIDPage_GenericDesktop:
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:
354 case kHIDUsage_GD_Hatswitch:
365 case kHIDPage_Simulation:
367 case kHIDUsage_Sim_Rudder:
368 case kHIDUsage_Sim_Throttle:
381 case kHIDPage_Button:
393 }
else if (kIOHIDElementTypeCollection == elementType)
394 HIDGetCollectionElements((CFMutableDictionaryRef) refElement,
398 if (element && headElement) {
401 while (elementCurrent && usage >= elementCurrent->
usage) {
402 elementPrevious = elementCurrent;
403 elementCurrent = elementCurrent->
pNext;
405 if (elementPrevious) {
406 elementPrevious->
pNext = element;
408 *headElement = element;
412 element->
pNext = elementCurrent;
413 HIDGetElementInfo(refElement, element);
421 HIDGetElementsCFArrayHandler(
const void *value,
void *parameter)
423 if (CFGetTypeID(value) == CFDictionaryGetTypeID())
424 HIDAddElement((CFTypeRef) value, (
recDevice *) parameter);
430 HIDGetElements(CFTypeRef refElementCurrent,
recDevice * pDevice)
432 CFTypeID
type = CFGetTypeID(refElementCurrent);
433 if (type == CFArrayGetTypeID()) {
434 CFRange
range = { 0, CFArrayGetCount(refElementCurrent) };
436 CFArrayApplyFunction(refElementCurrent, range,
437 HIDGetElementsCFArrayHandler, pDevice);
446 HIDGetCollectionElements(CFMutableDictionaryRef deviceProperties,
449 CFTypeRef refElementTop =
450 CFDictionaryGetValue(deviceProperties, CFSTR(kIOHIDElementKey));
452 HIDGetElements(refElementTop, pDevice);
458 HIDTopLevelElementHandler(
const void *value,
void *parameter)
461 if (CFGetTypeID(value) != CFDictionaryGetTypeID())
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.");
476 HIDGetDeviceInfo(io_object_t hidDevice, CFMutableDictionaryRef hidProperties,
479 CFMutableDictionaryRef usbProperties = 0;
480 io_registry_entry_t parent1, parent2;
485 if ((KERN_SUCCESS == IORegistryEntryGetParentEntry(hidDevice, kIOServicePlane, &parent1))
486 && (KERN_SUCCESS == IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2))
487 && (KERN_SUCCESS == IORegistryEntryCreateCFProperties(parent2, &usbProperties, kCFAllocatorDefault, kNilOptions))) {
495 refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductKey));
497 refCF = CFDictionaryGetValue(usbProperties, CFSTR(
"USB Product Name"));
500 if (!CFStringGetCString(refCF, pDevice->
product, 256, CFStringGetSystemEncoding())) {
501 SDL_SetError(
"CFStringGetCString error retrieving pDevice->product.");
506 refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDPrimaryUsagePageKey));
508 if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->
usagePage)) {
509 SDL_SetError(
"CFNumberGetValue error retrieving pDevice->usagePage.");
512 refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDPrimaryUsageKey));
514 if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->
usage)) {
515 SDL_SetError(
"CFNumberGetValue error retrieving pDevice->usage.");
520 refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDVendorIDKey));
522 if (!CFNumberGetValue(refCF, kCFNumberLongType, &pDevice->
guid.
data[0])) {
523 SDL_SetError(
"CFNumberGetValue error retrieving pDevice->guid[0]");
527 refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductIDKey));
529 if (!CFNumberGetValue(refCF, kCFNumberLongType, &pDevice->
guid.
data[8])) {
530 SDL_SetError(
"CFNumberGetValue error retrieving pDevice->guid[8]");
538 if (!guid32[0] && !guid32[1]) {
539 const Uint16 BUS_BLUETOOTH = 0x05;
541 *guid16++ = BUS_BLUETOOTH;
551 CFTypeRef refCFTopElement = 0;
552 refCFTopElement = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDElementKey));
555 CFRange
range = { 0, CFArrayGetCount(refCFTopElement) };
556 CFArrayApplyFunction(refCFTopElement, range, HIDTopLevelElementHandler, pDevice);
560 CFRelease(usbProperties);
562 SDL_SetError(
"IORegistryEntryCreateCFProperties failed to create usbProperties.");
565 if (kIOReturnSuccess != IOObjectRelease(parent2)) {
568 if (kIOReturnSuccess != IOObjectRelease(parent1)) {
576 HIDBuildDevice(io_object_t hidDevice)
581 CFMutableDictionaryRef hidProperties = 0;
582 kern_return_t result =
583 IORegistryEntryCreateCFProperties(hidDevice, &hidProperties,
586 if ((result == KERN_SUCCESS) && hidProperties) {
588 result = HIDCreateOpenDeviceInterface(hidDevice, pDevice);
589 if (kIOReturnSuccess == result) {
590 HIDGetDeviceInfo(hidDevice, hidProperties, pDevice);
591 HIDGetCollectionElements(hidProperties, pDevice);
594 DisposePtr((Ptr) pDevice);
597 CFRelease(hidProperties);
599 DisposePtr((Ptr) pDevice);
610 HIDDisposeElementList(
recElement ** elementList)
615 DisposePtr((Ptr) pElement);
616 pElement = pElementNext;
628 kern_return_t result = KERN_SUCCESS;
632 pDeviceNext = (*ppDevice)->
pNext;
635 if ((*ppDevice)->ffservice) {
636 IOObjectRelease((*ppDevice)->ffservice);
637 (*ppDevice)->ffservice = 0;
641 HIDDisposeElementList(&(*ppDevice)->firstAxis);
642 HIDDisposeElementList(&(*ppDevice)->firstButton);
643 HIDDisposeElementList(&(*ppDevice)->firstHat);
645 result = HIDCloseReleaseInterface(*ppDevice);
646 if (kIOReturnSuccess != result)
648 (
"HIDCloseReleaseInterface failed when trying to dipose device.",
650 DisposePtr((Ptr) * ppDevice);
660 AddDeviceHelper( io_object_t ioHIDDeviceObject )
665 device = HIDBuildDevice(ioHIDDeviceObject);
670 if ((device->
usagePage != kHIDPage_GenericDesktop) ||
671 ((device->
usage != kHIDUsage_GD_Joystick &&
672 device->
usage != kHIDUsage_GD_GamePad &&
673 device->
usage != kHIDUsage_GD_MultiAxisController))) {
676 HIDDisposeDevice(&device);
677 DisposePtr((Ptr) device);
683 if (FFIsForceFeedback(ioHIDDeviceObject) == FF_OK) {
695 gpDeviceList = device;
701 curdevice = gpDeviceList;
702 while ( curdevice->
pNext )
704 curdevice = curdevice->
pNext;
706 curdevice->
pNext = device;
716 void JoystickDeviceWasAddedCallback(
void *refcon, io_iterator_t iterator )
718 io_object_t ioHIDDeviceObject = 0;
720 while ( ( ioHIDDeviceObject = IOIteratorNext(iterator) ) )
722 if ( ioHIDDeviceObject )
724 AddDeviceHelper( ioHIDDeviceObject );
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;
746 return SDL_SetError(
"Joystick: Device list already inited.");
749 result = IOMasterPort(bootstrap_port, &masterPort);
750 if (kIOReturnSuccess != result) {
751 return SDL_SetError(
"Joystick: IOMasterPort error with bootstrap_port.");
755 hidMatchDictionary = IOServiceMatching(kIOHIDDeviceKey);
756 if (hidMatchDictionary) {
771 (
"Joystick: Failed to get HID CFMutableDictionaryRef via IOServiceMatching.");
776 IOServiceGetMatchingServices(masterPort, hidMatchDictionary,
779 if (kIOReturnSuccess != result) {
780 return SDL_SetError(
"Joystick: Couldn't create a HID object iterator.");
782 if (!hidObjectIterator) {
792 while ((ioHIDDeviceObject = IOIteratorNext(hidObjectIterator))) {
793 AddDeviceHelper( ioHIDDeviceObject );
795 result = IOObjectRelease(hidObjectIterator);
798 notificationPort = IONotificationPortCreate(masterPort);
799 hidMatchDictionary = IOServiceMatching(kIOHIDDeviceKey);
801 CFRunLoopAddSource(CFRunLoopGetCurrent(),
802 IONotificationPortGetRunLoopSource(notificationPort),
803 kCFRunLoopDefaultMode);
806 result = IOServiceAddMatchingNotification(notificationPort,
807 kIOFirstMatchNotification,
809 JoystickDeviceWasAddedCallback,
812 while (IOIteratorNext(portIterator)) {};
828 device = device->
pNext;
839 if ( s_bDeviceAdded || s_bDeviceRemoved )
844 int device_index = 0;
851 #if !SDL_EVENTS_DISABLED
856 event.jdevice.which = device_index;
869 if ( gpDeviceList == removeDevice )
871 device = device->
pNext;
872 gpDeviceList = device;
876 device = gpDeviceList;
877 while ( device->
pNext != removeDevice )
879 device = device->
pNext;
885 #if !SDL_EVENTS_DISABLED
897 DisposePtr((Ptr) removeDevice);
903 device = device->
pNext;
913 return s_bDeviceAdded || s_bDeviceRemoved;
922 for (; device_index > 0; device_index--)
923 device = device->
pNext;
936 for (index = device_index; index > 0; index--)
937 device = device->
pNext;
953 for (index = device_index; index > 0; index--)
954 device = device->
pNext;
957 joystick->
hwdata = device;
980 device = device->
pNext;
1007 if ( devicelist == device )
1009 gpDeviceList = device->
pNext;
1013 while ( devicelist->
pNext != device )
1015 devicelist = devicelist->
pNext;
1021 DisposePtr((Ptr) device);
1024 #if !SDL_EVENTS_DISABLED
1043 value = HIDScaledCalibratedValue(device, element, -32768, 32767);
1044 if (value != joystick->
axes[i])
1046 element = element->
pNext;
1053 value = HIDGetElementValue(device, element);
1056 if (value != joystick->
buttons[i])
1058 element = element->
pNext;
1067 range = (element->
max - element->
min + 1);
1068 value = HIDGetElementValue(device, element) - element->
min;
1071 else if (range != 8)
1106 if (pos != joystick->
hats[i])
1108 element = element->
pNext;
1126 while (
NULL != gpDeviceList)
1127 gpDeviceList = HIDDisposeDevice(&gpDeviceList);
1129 if ( notificationPort )
1131 IONotificationPortDestroy( notificationPort );
1132 notificationPort = 0;
1142 for (index = device_index; index > 0; index--)
1143 device = device->
pNext;
1145 return device->
guid;
DECLSPEC int SDLCALL SDL_PushEvent(SDL_Event *event)
Add an event to the event queue.
GLint GLenum GLsizei GLsizei GLsizei GLint GLenum GLenum type
IONotificationPortRef notificationPort
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
int SDL_SYS_NumJoysticks()
io_iterator_t portIterator
typedef HRESULT(WINAPI *LPD3DXIMTSIGNALCALLBACK)(CONST D3DXVECTOR2 *uv
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
void SDL_SYS_JoystickQuit(void)
struct joystick_hwdata * hwdata
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
struct recElement * pNext
IOHIDElementCookie cookie
EGLContext EGLenum target
uint32_t Uint32
An unsigned 32-bit integer type.
#define SDL_HAT_RIGHTDOWN
#define SDL_GetEventState(type)
SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
SDL_EventFilter SDL_EventOK
DECLSPEC size_t SDLCALL SDL_strlcpy(char *dst, const char *src, size_t maxlen)
void SDL_SYS_JoystickDetect()
DECLSPEC int SDLCALL SDL_SetError(const char *fmt,...)
const char * SDL_SYS_JoystickNameForDeviceIndex(int device_index)
struct joystick_hwdata * pNext
void SDL_SYS_JoystickUpdate(SDL_Joystick *joystick)
SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
SDL_bool SDL_SYS_JoystickNeedsPolling()
int SDL_SYS_JoystickInit(void)
EGLSurface EGLint void ** value
uint8_t Uint8
An unsigned 8-bit integer type.
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.
void SDL_SYS_JoystickClose(SDL_Joystick *joystick)
IOHIDDeviceInterface ** interface
GLsizeiptr const GLvoid GLenum usage
SDL_JoystickID instance_id
SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index)