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, SInt32 *pValue)
114 if (pDevice && pElement) {
115 IOHIDValueRef valueRef;
116 if (IOHIDDeviceGetValue(pDevice->deviceRef, pElement->
elementRef, &valueRef) == kIOReturnSuccess) {
117 value = (SInt32) IOHIDValueGetIntegerValue(valueRef);
135 GetHIDScaledCalibratedState(recDevice * pDevice,
recElement * pElement, SInt32 min, SInt32 max, SInt32 *pValue)
137 const float deviceScale = max - min;
140 if (GetHIDElementState(pDevice, pElement, pValue))
142 if (readScale == 0) {
147 *pValue = ((*pValue - pElement->
minReport) * deviceScale / readScale) + min;
155 JoystickDeviceWasRemovedCallback(
void *
ctx, IOReturn
result,
void *sender)
157 recDevice *device = (recDevice *) ctx;
159 device->deviceRef =
NULL;
168 static void AddHIDElement(
const void *value,
void *parameter);
172 AddHIDElements(CFArrayRef
array, recDevice *pDevice)
174 const CFRange
range = { 0, CFArrayGetCount(array) };
175 CFArrayApplyFunction(array, range, AddHIDElement, pDevice);
179 ElementAlreadyAdded(
const IOHIDElementCookie cookie,
const recElement *listitem) {
181 if (listitem->
cookie == cookie) {
184 listitem = listitem->
pNext;
191 AddHIDElement(
const void *value,
void *parameter)
193 recDevice *pDevice = (recDevice *) parameter;
194 IOHIDElementRef refElement = (IOHIDElementRef) value;
195 const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0;
197 if (refElement && (elementTypeID == IOHIDElementGetTypeID())) {
198 const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement);
199 const uint32_t usagePage = IOHIDElementGetUsagePage(refElement);
205 switch (IOHIDElementGetType(refElement)) {
206 case kIOHIDElementTypeInput_Misc:
207 case kIOHIDElementTypeInput_Button:
208 case kIOHIDElementTypeInput_Axis: {
210 case kHIDPage_GenericDesktop:
215 case kHIDUsage_GD_Rx:
216 case kHIDUsage_GD_Ry:
217 case kHIDUsage_GD_Rz:
218 case kHIDUsage_GD_Slider:
219 case kHIDUsage_GD_Dial:
220 case kHIDUsage_GD_Wheel:
221 if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
225 headElement = &(pDevice->firstAxis);
230 case kHIDUsage_GD_Hatswitch:
231 if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) {
235 headElement = &(pDevice->firstHat);
239 case kHIDUsage_GD_DPadUp:
240 case kHIDUsage_GD_DPadDown:
241 case kHIDUsage_GD_DPadRight:
242 case kHIDUsage_GD_DPadLeft:
243 case kHIDUsage_GD_Start:
244 case kHIDUsage_GD_Select:
245 case kHIDUsage_GD_SystemMainMenu:
246 if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
250 headElement = &(pDevice->firstButton);
257 case kHIDPage_Simulation:
259 case kHIDUsage_Sim_Rudder:
260 case kHIDUsage_Sim_Throttle:
261 case kHIDUsage_Sim_Accelerator:
262 case kHIDUsage_Sim_Brake:
263 if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
267 headElement = &(pDevice->firstAxis);
277 case kHIDPage_Button:
278 case kHIDPage_Consumer:
279 if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
283 headElement = &(pDevice->firstButton);
294 case kIOHIDElementTypeCollection: {
295 CFArrayRef
array = IOHIDElementGetChildren(refElement);
297 AddHIDElements(array, pDevice);
306 if (element && headElement) {
309 while (elementCurrent && usage >= elementCurrent->
usage) {
310 elementPrevious = elementCurrent;
311 elementCurrent = elementCurrent->
pNext;
313 if (elementPrevious) {
314 elementPrevious->
pNext = element;
316 *headElement = element;
322 element->
pNext = elementCurrent;
324 element->
minReport = element->
min = (SInt32) IOHIDElementGetLogicalMin(refElement);
325 element->
maxReport = element->
max = (SInt32) IOHIDElementGetLogicalMax(refElement);
326 element->
cookie = IOHIDElementGetCookie(refElement);
334 GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
336 const Uint16 BUS_USB = 0x03;
337 const Uint16 BUS_BLUETOOTH = 0x05;
341 CFTypeRef refCF =
NULL;
346 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
348 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage);
350 if (pDevice->usagePage != kHIDPage_GenericDesktop) {
354 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey));
356 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage);
359 if ((pDevice->usage != kHIDUsage_GD_Joystick &&
360 pDevice->usage != kHIDUsage_GD_GamePad &&
361 pDevice->usage != kHIDUsage_GD_MultiAxisController)) {
365 pDevice->deviceRef = hidDevice;
368 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey));
371 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey));
373 if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) {
374 SDL_strlcpy(pDevice->product,
"Unidentified joystick", sizeof (pDevice->product));
377 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
379 CFNumberGetValue(refCF, kCFNumberSInt32Type, &vendor);
382 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey));
384 CFNumberGetValue(refCF, kCFNumberSInt32Type, &product);
387 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVersionNumberKey));
389 CFNumberGetValue(refCF, kCFNumberSInt32Type, &version);
392 SDL_memset(pDevice->guid.data, 0,
sizeof(pDevice->guid.data));
394 if (vendor && product) {
406 SDL_strlcpy((
char*)guid16, pDevice->product,
sizeof(pDevice->guid.data) - 4);
409 array = IOHIDDeviceCopyMatchingElements(hidDevice,
NULL, kIOHIDOptionsTypeNone);
411 AddHIDElements(array, pDevice);
419 JoystickAlreadyKnown(IOHIDDeviceRef ioHIDDeviceObject)
422 for (i = gpDeviceList; i !=
NULL; i = i->pNext) {
423 if (i->deviceRef == ioHIDDeviceObject) {
432 JoystickDeviceWasAddedCallback(
void *
ctx, IOReturn
res,
void *sender, IOHIDDeviceRef ioHIDDeviceObject)
435 int device_index = 0;
436 io_service_t ioservice;
438 if (res != kIOReturnSuccess) {
442 if (JoystickAlreadyKnown(ioHIDDeviceObject)) {
446 device = (recDevice *)
SDL_calloc(1,
sizeof(recDevice));
453 if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
465 IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
466 IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
469 device->instance_id = ++s_joystick_instance_id;
472 ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
474 if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
475 device->ffservice = ioservice;
481 if ( !gpDeviceList ) {
484 recDevice *curdevice;
486 curdevice = gpDeviceList;
487 while ( curdevice->pNext ) {
489 curdevice = curdevice->pNext;
491 curdevice->pNext =
device;
499 ConfigHIDManager(CFArrayRef matchingArray)
501 CFRunLoopRef runloop = CFRunLoopGetCurrent();
503 if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
507 IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
508 IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback,
NULL);
509 IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
511 while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,
TRUE) == kCFRunLoopRunHandledSource) {
521 static CFDictionaryRef
522 CreateHIDDeviceMatchDictionary(
const UInt32 page,
const UInt32 usage,
int *okay)
525 CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
526 CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
527 const void *keys[2] = { (
void *) CFSTR(kIOHIDDeviceUsagePageKey), (
void *) CFSTR(kIOHIDDeviceUsageKey) };
528 const void *vals[2] = { (
void *) pageNumRef, (
void *) usageNumRef };
530 if (pageNumRef && usageNumRef) {
531 retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
535 CFRelease(pageNumRef);
538 CFRelease(usageNumRef);
549 CreateHIDManager(
void)
553 const void *vals[] = {
554 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
555 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
556 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
559 CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) :
NULL;
562 for (i = 0; i < numElements; i++) {
564 CFRelease((CFTypeRef) vals[i]);
569 hidman = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
570 if (hidman !=
NULL) {
571 retval = ConfigHIDManager(array);
589 return SDL_SetError(
"Joystick: Device list already inited.");
592 if (!CreateHIDManager()) {
593 return SDL_SetError(
"Joystick: Couldn't initialize HID Manager");
603 recDevice *device = gpDeviceList;
607 if (!device->removed) {
610 device = device->pNext;
621 recDevice *device = gpDeviceList;
623 if (device->removed) {
624 device = FreeDevice(device);
626 device = device->pNext;
632 while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,
TRUE) == kCFRunLoopRunHandledSource) {
642 return device ? device->product :
"UNKNOWN";
651 return device ? device->instance_id : 0;
664 joystick->instance_id = device->instance_id;
665 joystick->hwdata =
device;
666 joystick->name = device->product;
668 joystick->naxes = device->axes;
669 joystick->nhats = device->hats;
670 joystick->nballs = 0;
671 joystick->nbuttons = device->buttons;
681 return joystick->hwdata !=
NULL;
692 recDevice *device = joystick->hwdata;
701 if (device->removed) {
702 if (joystick->hwdata) {
703 joystick->force_recentering =
SDL_TRUE;
704 joystick->hwdata =
NULL;
709 element = device->firstAxis;
714 goodRead = GetHIDScaledCalibratedState(device, element, -32768, 32767, &value);
719 element = element->
pNext;
723 element = device->firstButton;
726 goodRead = GetHIDElementState(device, element, &value);
734 element = element->
pNext;
738 element = device->firstHat;
744 range = (element->
max - element->
min + 1);
745 goodRead = GetHIDElementState(device, element, &value);
747 value -= element->
min;
750 }
else if (range != 8) {
790 element = element->
pNext;
805 while (FreeDevice(gpDeviceList)) {
810 IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
811 IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
832 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
#define SDL_HAT_RIGHTDOWN
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)
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)
#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)