21 #include "../../SDL_internal.h" 23 #ifdef SDL_JOYSTICK_IOKIT 25 #include <IOKit/hid/IOHIDLib.h> 28 #include <ForceFeedback/ForceFeedback.h> 29 #include <ForceFeedback/ForceFeedbackConstants.h> 32 #include "../SDL_sysjoystick.h" 33 #include "../SDL_joystick_c.h" 34 #include "SDL_sysjoystick_c.h" 36 #include "../../haptic/darwin/SDL_syshaptic_c.h" 38 #define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick") 41 static IOHIDManagerRef hidman =
NULL;
44 static recDevice *gpDeviceList =
NULL;
47 static int s_joystick_instance_id = -1;
51 recDevice *
device = gpDeviceList;
53 if (!device->removed) {
54 if (device_index == 0)
59 device = device->pNext;
70 pElement = pElementNext;
75 FreeDevice(recDevice *removeDevice)
77 recDevice *pDeviceNext =
NULL;
79 if (removeDevice->deviceRef) {
80 IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
81 removeDevice->deviceRef =
NULL;
85 pDeviceNext = removeDevice->pNext;
87 if ( gpDeviceList == removeDevice ) {
88 gpDeviceList = pDeviceNext;
90 recDevice *device = gpDeviceList;
91 while (device->pNext != removeDevice) {
92 device = device->pNext;
94 device->pNext = pDeviceNext;
96 removeDevice->pNext =
NULL;
99 FreeElementList(removeDevice->firstAxis);
100 FreeElementList(removeDevice->firstButton);
101 FreeElementList(removeDevice->firstHat);
109 GetHIDElementState(recDevice *pDevice,
recElement *pElement)
113 if (pDevice && pElement) {
114 IOHIDValueRef valueRef;
115 if (IOHIDDeviceGetValue(pDevice->deviceRef, pElement->
elementRef, &valueRef) == kIOReturnSuccess) {
116 value = (SInt32) IOHIDValueGetIntegerValue(valueRef);
132 GetHIDScaledCalibratedState(recDevice * pDevice,
recElement * pElement, SInt32 min, SInt32 max)
134 const float deviceScale = max - min;
136 const SInt32 value = GetHIDElementState(pDevice, pElement);
137 if (readScale == 0) {
140 return ((value - pElement->
minReport) * deviceScale / readScale) + min;
145 JoystickDeviceWasRemovedCallback(
void *
ctx, IOReturn
result,
void *sender)
147 recDevice *device = (recDevice *) ctx;
149 device->deviceRef =
NULL;
158 static void AddHIDElement(
const void *value,
void *parameter);
162 AddHIDElements(CFArrayRef
array, recDevice *pDevice)
164 const CFRange
range = { 0, CFArrayGetCount(array) };
165 CFArrayApplyFunction(array, range, AddHIDElement, pDevice);
169 ElementAlreadyAdded(
const IOHIDElementCookie cookie,
const recElement *listitem) {
171 if (listitem->
cookie == cookie) {
174 listitem = listitem->
pNext;
181 AddHIDElement(
const void *value,
void *parameter)
183 recDevice *pDevice = (recDevice *) parameter;
184 IOHIDElementRef refElement = (IOHIDElementRef) value;
185 const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0;
187 if (refElement && (elementTypeID == IOHIDElementGetTypeID())) {
188 const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement);
189 const uint32_t usagePage = IOHIDElementGetUsagePage(refElement);
195 switch (IOHIDElementGetType(refElement)) {
196 case kIOHIDElementTypeInput_Misc:
197 case kIOHIDElementTypeInput_Button:
198 case kIOHIDElementTypeInput_Axis: {
200 case kHIDPage_GenericDesktop:
205 case kHIDUsage_GD_Rx:
206 case kHIDUsage_GD_Ry:
207 case kHIDUsage_GD_Rz:
208 case kHIDUsage_GD_Slider:
209 case kHIDUsage_GD_Dial:
210 case kHIDUsage_GD_Wheel:
211 if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
215 headElement = &(pDevice->firstAxis);
220 case kHIDUsage_GD_Hatswitch:
221 if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) {
225 headElement = &(pDevice->firstHat);
229 case kHIDUsage_GD_DPadUp:
230 case kHIDUsage_GD_DPadDown:
231 case kHIDUsage_GD_DPadRight:
232 case kHIDUsage_GD_DPadLeft:
233 case kHIDUsage_GD_Start:
234 case kHIDUsage_GD_Select:
235 case kHIDUsage_GD_SystemMainMenu:
236 if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
240 headElement = &(pDevice->firstButton);
247 case kHIDPage_Simulation:
249 case kHIDUsage_Sim_Rudder:
250 case kHIDUsage_Sim_Throttle:
251 case kHIDUsage_Sim_Accelerator:
252 case kHIDUsage_Sim_Brake:
253 if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
257 headElement = &(pDevice->firstAxis);
267 case kHIDPage_Button:
268 case kHIDPage_Consumer:
269 if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
273 headElement = &(pDevice->firstButton);
284 case kIOHIDElementTypeCollection: {
285 CFArrayRef
array = IOHIDElementGetChildren(refElement);
287 AddHIDElements(array, pDevice);
296 if (element && headElement) {
299 while (elementCurrent && usage >= elementCurrent->
usage) {
300 elementPrevious = elementCurrent;
301 elementCurrent = elementCurrent->
pNext;
303 if (elementPrevious) {
304 elementPrevious->
pNext = element;
306 *headElement = element;
312 element->
pNext = elementCurrent;
314 element->
minReport = element->
min = (SInt32) IOHIDElementGetLogicalMin(refElement);
315 element->
maxReport = element->
max = (SInt32) IOHIDElementGetLogicalMax(refElement);
316 element->
cookie = IOHIDElementGetCookie(refElement);
324 GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
326 const Uint16 BUS_USB = 0x03;
327 const Uint16 BUS_BLUETOOTH = 0x05;
331 CFTypeRef refCF =
NULL;
336 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
338 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage);
340 if (pDevice->usagePage != kHIDPage_GenericDesktop) {
344 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey));
346 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage);
349 if ((pDevice->usage != kHIDUsage_GD_Joystick &&
350 pDevice->usage != kHIDUsage_GD_GamePad &&
351 pDevice->usage != kHIDUsage_GD_MultiAxisController)) {
355 pDevice->deviceRef = hidDevice;
358 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey));
361 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey));
363 if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) {
364 SDL_strlcpy(pDevice->product,
"Unidentified joystick", sizeof (pDevice->product));
367 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
369 CFNumberGetValue(refCF, kCFNumberSInt32Type, &vendor);
372 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey));
374 CFNumberGetValue(refCF, kCFNumberSInt32Type, &product);
377 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVersionNumberKey));
379 CFNumberGetValue(refCF, kCFNumberSInt32Type, &version);
382 SDL_memset(pDevice->guid.data, 0,
sizeof(pDevice->guid.data));
384 if (vendor && product) {
396 SDL_strlcpy((
char*)guid16, pDevice->product,
sizeof(pDevice->guid.data) - 4);
399 array = IOHIDDeviceCopyMatchingElements(hidDevice,
NULL, kIOHIDOptionsTypeNone);
401 AddHIDElements(array, pDevice);
409 JoystickAlreadyKnown(IOHIDDeviceRef ioHIDDeviceObject)
412 for (i = gpDeviceList; i !=
NULL; i = i->pNext) {
413 if (i->deviceRef == ioHIDDeviceObject) {
422 JoystickDeviceWasAddedCallback(
void *
ctx, IOReturn
res,
void *sender, IOHIDDeviceRef ioHIDDeviceObject)
425 int device_index = 0;
426 io_service_t ioservice;
428 if (res != kIOReturnSuccess) {
432 if (JoystickAlreadyKnown(ioHIDDeviceObject)) {
436 device = (recDevice *)
SDL_calloc(1,
sizeof(recDevice));
443 if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
455 IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
456 IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
459 device->instance_id = ++s_joystick_instance_id;
462 ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
464 if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
465 device->ffservice = ioservice;
471 if ( !gpDeviceList ) {
474 recDevice *curdevice;
476 curdevice = gpDeviceList;
477 while ( curdevice->pNext ) {
479 curdevice = curdevice->pNext;
481 curdevice->pNext =
device;
489 ConfigHIDManager(CFArrayRef matchingArray)
491 CFRunLoopRef runloop = CFRunLoopGetCurrent();
493 if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
497 IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
498 IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback,
NULL);
499 IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
501 while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,
TRUE) == kCFRunLoopRunHandledSource) {
511 static CFDictionaryRef
512 CreateHIDDeviceMatchDictionary(
const UInt32 page,
const UInt32 usage,
int *okay)
515 CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
516 CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
517 const void *keys[2] = { (
void *) CFSTR(kIOHIDDeviceUsagePageKey), (
void *) CFSTR(kIOHIDDeviceUsageKey) };
518 const void *vals[2] = { (
void *) pageNumRef, (
void *) usageNumRef };
520 if (pageNumRef && usageNumRef) {
521 retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
525 CFRelease(pageNumRef);
528 CFRelease(usageNumRef);
539 CreateHIDManager(
void)
543 const void *vals[] = {
544 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
545 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
546 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
549 CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) :
NULL;
552 for (i = 0; i < numElements; i++) {
554 CFRelease((CFTypeRef) vals[i]);
559 hidman = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
560 if (hidman !=
NULL) {
561 retval = ConfigHIDManager(array);
579 return SDL_SetError(
"Joystick: Device list already inited.");
582 if (!CreateHIDManager()) {
583 return SDL_SetError(
"Joystick: Couldn't initialize HID Manager");
593 recDevice *device = gpDeviceList;
597 if (!device->removed) {
600 device = device->pNext;
611 recDevice *device = gpDeviceList;
613 if (device->removed) {
614 device = FreeDevice(device);
616 device = device->pNext;
622 while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,
TRUE) == kCFRunLoopRunHandledSource) {
632 return device ? device->product :
"UNKNOWN";
641 return device ? device->instance_id : 0;
654 joystick->instance_id = device->instance_id;
655 joystick->hwdata =
device;
656 joystick->name = device->product;
658 joystick->naxes = device->axes;
659 joystick->nhats = device->hats;
660 joystick->nballs = 0;
661 joystick->nbuttons = device->buttons;
671 return joystick->hwdata !=
NULL;
682 recDevice *device = joystick->hwdata;
691 if (device->removed) {
692 if (joystick->hwdata) {
693 joystick->force_recentering =
SDL_TRUE;
694 joystick->hwdata =
NULL;
699 element = device->firstAxis;
702 value = GetHIDScaledCalibratedState(device, element, -32768, 32767);
704 element = element->
pNext;
708 element = device->firstButton;
711 value = GetHIDElementState(device, element);
716 element = element->
pNext;
720 element = device->firstHat;
725 range = (element->
max - element->
min + 1);
726 value = GetHIDElementState(device, element) - element->
min;
729 }
else if (range != 8) {
768 element = element->
pNext;
783 while (FreeDevice(gpDeviceList)) {
788 IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
789 IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
810 return joystick->hwdata->guid;
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
int MacHaptic_MaybeRemoveDevice(io_object_t device)
void SDL_SYS_JoystickQuit(void)
static SDL_JoystickDeviceItem * GetDeviceForIndex(int device_index)
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
struct recElement * pNext
IOHIDElementCookie cookie
static SDL_AudioDeviceID device
GLsizeiptr const void GLenum usage
void * SDL_calloc(size_t nmemb, size_t size)
#define SDL_HAT_RIGHTDOWN
uint8_t Uint8
An unsigned 8-bit integer type.
SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
IOHIDElementRef elementRef
SDL_bool SDL_ShouldIgnoreGameController(const char *name, SDL_JoystickGUID guid)
GLsizei const GLfloat * value
void SDL_PrivateJoystickAdded(int device_index)
const char * SDL_SYS_JoystickNameForDeviceIndex(int device_index)
int32_t Sint32
A signed 32-bit integer type.
void SDL_SYS_JoystickUpdate(SDL_Joystick *joystick)
SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
SDL_bool SDL_IsGameControllerNameAndGUID(const char *name, SDL_JoystickGUID guid)
int SDL_SYS_JoystickInit(void)
#define SDL_OutOfMemory()
int SDL_SYS_NumJoysticks(void)
int MacHaptic_MaybeAddDevice(io_object_t device)
SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
uint16_t Uint16
An unsigned 16-bit integer type.
#define SDL_arraysize(array)
int SDL_SYS_JoystickOpen(SDL_Joystick *joystick, int device_index)
void SDL_SYS_JoystickClose(SDL_Joystick *joystick)
SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index)
void SDL_SYS_JoystickDetect(void)