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 if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
255 headElement = &(pDevice->firstAxis);
265 case kHIDPage_Button:
266 case kHIDPage_Consumer:
267 if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
271 headElement = &(pDevice->firstButton);
282 case kIOHIDElementTypeCollection: {
283 CFArrayRef
array = IOHIDElementGetChildren(refElement);
285 AddHIDElements(array, pDevice);
294 if (element && headElement) {
297 while (elementCurrent && usage >= elementCurrent->
usage) {
298 elementPrevious = elementCurrent;
299 elementCurrent = elementCurrent->
pNext;
301 if (elementPrevious) {
302 elementPrevious->
pNext = element;
304 *headElement = element;
310 element->
pNext = elementCurrent;
312 element->
minReport = element->
min = (SInt32) IOHIDElementGetLogicalMin(refElement);
313 element->
maxReport = element->
max = (SInt32) IOHIDElementGetLogicalMax(refElement);
314 element->
cookie = IOHIDElementGetCookie(refElement);
322 GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
325 CFTypeRef refCF =
NULL;
329 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
331 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage);
333 if (pDevice->usagePage != kHIDPage_GenericDesktop) {
337 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey));
339 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage);
342 if ((pDevice->usage != kHIDUsage_GD_Joystick &&
343 pDevice->usage != kHIDUsage_GD_GamePad &&
344 pDevice->usage != kHIDUsage_GD_MultiAxisController)) {
348 pDevice->deviceRef = hidDevice;
351 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey));
354 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey));
356 if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) {
357 SDL_strlcpy(pDevice->product,
"Unidentified joystick", sizeof (pDevice->product));
360 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
362 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->guid.data[0]);
365 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey));
367 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->guid.data[8]);
372 guid32 = (
Uint32*)pDevice->guid.data;
373 if (!guid32[0] && !guid32[1]) {
375 const Uint16 BUS_BLUETOOTH = 0x05;
377 *guid16++ = BUS_BLUETOOTH;
379 SDL_strlcpy((
char*)guid16, pDevice->product,
sizeof(pDevice->guid.data) - 4);
382 array = IOHIDDeviceCopyMatchingElements(hidDevice,
NULL, kIOHIDOptionsTypeNone);
384 AddHIDElements(array, pDevice);
392 JoystickAlreadyKnown(IOHIDDeviceRef ioHIDDeviceObject)
395 for (i = gpDeviceList; i !=
NULL; i = i->pNext) {
396 if (i->deviceRef == ioHIDDeviceObject) {
405 JoystickDeviceWasAddedCallback(
void *ctx, IOReturn
res,
void *sender, IOHIDDeviceRef ioHIDDeviceObject)
408 int device_index = 0;
409 io_service_t ioservice;
411 if (res != kIOReturnSuccess) {
415 if (JoystickAlreadyKnown(ioHIDDeviceObject)) {
419 device = (recDevice *)
SDL_calloc(1,
sizeof(recDevice));
426 if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
432 IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
433 IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
436 device->instance_id = ++s_joystick_instance_id;
439 ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
441 if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
442 device->ffservice = ioservice;
448 if ( !gpDeviceList ) {
449 gpDeviceList = device;
451 recDevice *curdevice;
453 curdevice = gpDeviceList;
454 while ( curdevice->pNext ) {
456 curdevice = curdevice->pNext;
458 curdevice->pNext = device;
466 ConfigHIDManager(CFArrayRef matchingArray)
468 CFRunLoopRef runloop = CFRunLoopGetCurrent();
470 if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
474 IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
475 IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback,
NULL);
476 IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
478 while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,
TRUE) == kCFRunLoopRunHandledSource) {
488 static CFDictionaryRef
489 CreateHIDDeviceMatchDictionary(
const UInt32 page,
const UInt32 usage,
int *okay)
492 CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
493 CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
494 const void *keys[2] = { (
void *) CFSTR(kIOHIDDeviceUsagePageKey), (
void *) CFSTR(kIOHIDDeviceUsageKey) };
495 const void *vals[2] = { (
void *) pageNumRef, (
void *) usageNumRef };
497 if (pageNumRef && usageNumRef) {
498 retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
502 CFRelease(pageNumRef);
505 CFRelease(usageNumRef);
516 CreateHIDManager(
void)
520 const void *vals[] = {
521 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
522 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
523 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
526 CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) :
NULL;
529 for (i = 0; i < numElements; i++) {
531 CFRelease((CFTypeRef) vals[i]);
536 hidman = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
537 if (hidman !=
NULL) {
538 retval = ConfigHIDManager(array);
556 return SDL_SetError(
"Joystick: Device list already inited.");
559 if (!CreateHIDManager()) {
560 return SDL_SetError(
"Joystick: Couldn't initialize HID Manager");
570 recDevice *device = gpDeviceList;
574 if (!device->removed) {
577 device = device->pNext;
588 recDevice *device = gpDeviceList;
590 if (device->removed) {
591 device = FreeDevice(device);
593 device = device->pNext;
599 while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,
TRUE) == kCFRunLoopRunHandledSource) {
609 return device ? device->product :
"UNKNOWN";
618 return device ? device->instance_id : 0;
631 joystick->instance_id = device->instance_id;
632 joystick->hwdata = device;
633 joystick->name = device->product;
635 joystick->naxes = device->axes;
636 joystick->nhats = device->hats;
637 joystick->nballs = 0;
638 joystick->nbuttons = device->buttons;
648 return joystick->hwdata !=
NULL;
659 recDevice *device = joystick->hwdata;
668 if (device->removed) {
669 if (joystick->hwdata) {
670 joystick->force_recentering =
SDL_TRUE;
671 joystick->hwdata =
NULL;
676 element = device->firstAxis;
679 value = GetHIDScaledCalibratedState(device, element, -32768, 32767);
680 if (value != joystick->axes[i]) {
683 element = element->
pNext;
687 element = device->firstButton;
690 value = GetHIDElementState(device, element);
694 if (value != joystick->buttons[i]) {
697 element = element->
pNext;
701 element = device->firstHat;
706 range = (element->
max - element->
min + 1);
707 value = GetHIDElementState(device, element) - element->
min;
710 }
else if (range != 8) {
747 if (pos != joystick->hats[i]) {
751 element = element->
pNext;
766 while (FreeDevice(gpDeviceList)) {
771 IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
772 IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
793 return joystick->hwdata->guid;
int MacHaptic_MaybeRemoveDevice(io_object_t device)
int MacHaptic_MaybeAddDevice(io_object_t device)
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
int SDL_SYS_NumJoysticks()
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
void SDL_SYS_JoystickQuit(void)
uint32_t Uint32
An unsigned 32-bit integer type.
static SDL_JoystickDeviceItem * GetDeviceForIndex(int device_index)
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
struct recElement * pNext
IOHIDElementCookie cookie
GLsizeiptr const void GLenum usage
void * SDL_calloc(size_t nmemb, size_t size)
#define SDL_HAT_RIGHTDOWN
GLsizei const GLfloat * value
uint8_t Uint8
An unsigned 8-bit integer type.
SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
IOHIDElementRef elementRef
void SDL_SYS_JoystickDetect()
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)
int SDL_SYS_JoystickInit(void)
#define SDL_OutOfMemory()
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)
struct joystick_hwdata * pNext