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" 37 #if !SDL_EVENTS_DISABLED 38 #include "../../events/SDL_events_c.h" 41 #define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick") 44 static IOHIDManagerRef hidman =
NULL;
47 static recDevice *gpDeviceList =
NULL;
50 static int s_joystick_instance_id = -1;
54 recDevice *device = gpDeviceList;
56 if (!device->removed) {
57 if (device_index == 0)
62 device = device->
pNext;
73 pElement = pElementNext;
78 FreeDevice(recDevice *removeDevice)
80 recDevice *pDeviceNext =
NULL;
82 if (removeDevice->deviceRef) {
83 IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
84 removeDevice->deviceRef =
NULL;
88 pDeviceNext = removeDevice->pNext;
90 if ( gpDeviceList == removeDevice ) {
91 gpDeviceList = pDeviceNext;
93 recDevice *device = gpDeviceList;
94 while (device->pNext != removeDevice) {
95 device = device->pNext;
97 device->pNext = pDeviceNext;
99 removeDevice->pNext =
NULL;
102 FreeElementList(removeDevice->firstAxis);
103 FreeElementList(removeDevice->firstButton);
104 FreeElementList(removeDevice->firstHat);
112 GetHIDElementState(recDevice *pDevice,
recElement *pElement)
116 if (pDevice && pElement) {
117 IOHIDValueRef valueRef;
118 if (IOHIDDeviceGetValue(pDevice->deviceRef, pElement->
elementRef, &valueRef) == kIOReturnSuccess) {
119 value = (SInt32) IOHIDValueGetIntegerValue(valueRef);
135 GetHIDScaledCalibratedState(recDevice * pDevice,
recElement * pElement, SInt32 min, SInt32 max)
137 const float deviceScale = max - min;
139 const SInt32 value = GetHIDElementState(pDevice, pElement);
140 if (readScale == 0) {
143 return ((value - pElement->
minReport) * deviceScale / readScale) + min;
148 JoystickDeviceWasRemovedCallback(
void *ctx, IOReturn
result,
void *sender)
150 recDevice *device = (recDevice *) ctx;
152 device->deviceRef =
NULL;
158 #if !SDL_EVENTS_DISABLED 164 event.jdevice.which = device->instance_id;
175 static void AddHIDElement(
const void *value,
void *parameter);
179 AddHIDElements(CFArrayRef
array, recDevice *pDevice)
181 const CFRange
range = { 0, CFArrayGetCount(array) };
182 CFArrayApplyFunction(array, range, AddHIDElement, pDevice);
186 ElementAlreadyAdded(
const IOHIDElementCookie cookie,
const recElement *listitem) {
188 if (listitem->
cookie == cookie) {
191 listitem = listitem->
pNext;
198 AddHIDElement(
const void *value,
void *parameter)
200 recDevice *pDevice = (recDevice *) parameter;
201 IOHIDElementRef refElement = (IOHIDElementRef) value;
202 const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0;
204 if (refElement && (elementTypeID == IOHIDElementGetTypeID())) {
205 const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement);
206 const uint32_t usagePage = IOHIDElementGetUsagePage(refElement);
212 switch (IOHIDElementGetType(refElement)) {
213 case kIOHIDElementTypeInput_Misc:
214 case kIOHIDElementTypeInput_Button:
215 case kIOHIDElementTypeInput_Axis: {
217 case kHIDPage_GenericDesktop:
222 case kHIDUsage_GD_Rx:
223 case kHIDUsage_GD_Ry:
224 case kHIDUsage_GD_Rz:
225 case kHIDUsage_GD_Slider:
226 case kHIDUsage_GD_Dial:
227 case kHIDUsage_GD_Wheel:
228 if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
232 headElement = &(pDevice->firstAxis);
237 case kHIDUsage_GD_Hatswitch:
238 if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) {
242 headElement = &(pDevice->firstHat);
246 case kHIDUsage_GD_DPadUp:
247 case kHIDUsage_GD_DPadDown:
248 case kHIDUsage_GD_DPadRight:
249 case kHIDUsage_GD_DPadLeft:
250 case kHIDUsage_GD_Start:
251 case kHIDUsage_GD_Select:
252 if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
256 headElement = &(pDevice->firstButton);
263 case kHIDPage_Simulation:
265 case kHIDUsage_Sim_Rudder:
266 case kHIDUsage_Sim_Throttle:
267 if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
271 headElement = &(pDevice->firstAxis);
281 case kHIDPage_Button:
282 case kHIDPage_Consumer:
283 if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
287 headElement = &(pDevice->firstButton);
298 case kIOHIDElementTypeCollection: {
299 CFArrayRef
array = IOHIDElementGetChildren(refElement);
301 AddHIDElements(array, pDevice);
310 if (element && headElement) {
313 while (elementCurrent && usage >= elementCurrent->
usage) {
314 elementPrevious = elementCurrent;
315 elementCurrent = elementCurrent->
pNext;
317 if (elementPrevious) {
318 elementPrevious->
pNext = element;
320 *headElement = element;
326 element->
pNext = elementCurrent;
328 element->
minReport = element->
min = (SInt32) IOHIDElementGetLogicalMin(refElement);
329 element->
maxReport = element->
max = (SInt32) IOHIDElementGetLogicalMax(refElement);
330 element->
cookie = IOHIDElementGetCookie(refElement);
338 GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
341 CFTypeRef refCF =
NULL;
345 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
347 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage);
349 if (pDevice->usagePage != kHIDPage_GenericDesktop) {
353 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey));
355 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage);
358 if ((pDevice->usage != kHIDUsage_GD_Joystick &&
359 pDevice->usage != kHIDUsage_GD_GamePad &&
360 pDevice->usage != kHIDUsage_GD_MultiAxisController)) {
364 pDevice->deviceRef = hidDevice;
367 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey));
370 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey));
372 if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) {
373 SDL_strlcpy(pDevice->product,
"Unidentified joystick", sizeof (pDevice->product));
376 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
378 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->guid.data[0]);
381 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey));
383 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->guid.data[8]);
388 guid32 = (
Uint32*)pDevice->guid.data;
389 if (!guid32[0] && !guid32[1]) {
391 const Uint16 BUS_BLUETOOTH = 0x05;
393 *guid16++ = BUS_BLUETOOTH;
395 SDL_strlcpy((
char*)guid16, pDevice->product,
sizeof(pDevice->guid.data) - 4);
398 array = IOHIDDeviceCopyMatchingElements(hidDevice,
NULL, kIOHIDOptionsTypeNone);
400 AddHIDElements(array, pDevice);
408 JoystickAlreadyKnown(IOHIDDeviceRef ioHIDDeviceObject)
411 for (i = gpDeviceList; i !=
NULL; i = i->pNext) {
412 if (i->deviceRef == ioHIDDeviceObject) {
421 JoystickDeviceWasAddedCallback(
void *ctx, IOReturn
res,
void *sender, IOHIDDeviceRef ioHIDDeviceObject)
424 int device_index = 0;
426 if (res != kIOReturnSuccess) {
430 if (JoystickAlreadyKnown(ioHIDDeviceObject)) {
434 device = (recDevice *)
SDL_calloc(1,
sizeof(recDevice));
441 if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
447 IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
448 IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
451 device->instance_id = ++s_joystick_instance_id;
455 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 456 if (IOHIDDeviceGetService !=
NULL) {
459 const io_service_t ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
461 if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
462 device->ffservice = ioservice;
467 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 472 if ( !gpDeviceList ) {
473 gpDeviceList = device;
475 recDevice *curdevice;
477 curdevice = gpDeviceList;
478 while ( curdevice->pNext ) {
480 curdevice = curdevice->pNext;
482 curdevice->pNext = device;
487 #if !SDL_EVENTS_DISABLED 493 event.jdevice.which = device_index;
504 ConfigHIDManager(CFArrayRef matchingArray)
506 CFRunLoopRef runloop = CFRunLoopGetCurrent();
508 if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
512 IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
513 IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback,
NULL);
514 IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
516 while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,
TRUE) == kCFRunLoopRunHandledSource) {
526 static CFDictionaryRef
527 CreateHIDDeviceMatchDictionary(
const UInt32 page,
const UInt32 usage,
int *okay)
530 CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
531 CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
532 const void *keys[2] = { (
void *) CFSTR(kIOHIDDeviceUsagePageKey), (
void *) CFSTR(kIOHIDDeviceUsageKey) };
533 const void *vals[2] = { (
void *) pageNumRef, (
void *) usageNumRef };
535 if (pageNumRef && usageNumRef) {
536 retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
540 CFRelease(pageNumRef);
543 CFRelease(usageNumRef);
554 CreateHIDManager(
void)
558 const void *vals[] = {
559 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
560 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
561 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
564 CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) :
NULL;
567 for (i = 0; i < numElements; i++) {
569 CFRelease((CFTypeRef) vals[i]);
574 hidman = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
575 if (hidman !=
NULL) {
576 retval = ConfigHIDManager(array);
594 return SDL_SetError(
"Joystick: Device list already inited.");
597 if (!CreateHIDManager()) {
598 return SDL_SetError(
"Joystick: Couldn't initialize HID Manager");
608 recDevice *device = gpDeviceList;
612 if (!device->removed) {
615 device = device->pNext;
626 recDevice *device = gpDeviceList;
628 if (device->removed) {
629 device = FreeDevice(device);
631 device = device->pNext;
637 while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,
TRUE) == kCFRunLoopRunHandledSource) {
647 return device ? device->product :
"UNKNOWN";
656 return device ? device->instance_id : 0;
669 joystick->instance_id = device->instance_id;
670 joystick->hwdata = device;
671 joystick->name = device->product;
673 joystick->naxes = device->axes;
674 joystick->nhats = device->hats;
675 joystick->nballs = 0;
676 joystick->nbuttons = device->buttons;
686 return joystick->hwdata !=
NULL;
697 recDevice *device = joystick->hwdata;
706 if (device->removed) {
707 if (joystick->hwdata) {
708 joystick->force_recentering =
SDL_TRUE;
709 joystick->hwdata =
NULL;
714 element = device->firstAxis;
717 value = GetHIDScaledCalibratedState(device, element, -32768, 32767);
718 if (value != joystick->axes[i]) {
721 element = element->
pNext;
725 element = device->firstButton;
728 value = GetHIDElementState(device, element);
732 if (value != joystick->buttons[i]) {
735 element = element->
pNext;
739 element = device->firstHat;
744 range = (element->
max - element->
min + 1);
745 value = GetHIDElementState(device, element) - element->
min;
748 }
else if (range != 8) {
785 if (pos != joystick->hats[i]) {
789 element = element->
pNext;
804 while (FreeDevice(gpDeviceList)) {
809 IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
810 IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
831 return joystick->hwdata->guid;
int MacHaptic_MaybeRemoveDevice(io_object_t device)
int MacHaptic_MaybeAddDevice(io_object_t device)
uint32_t Uint32
An unsigned 32-bit integer type.
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)
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
#define SDL_GetEventState(type)
GLsizei const GLfloat * value
uint8_t Uint8
An unsigned 8-bit integer type.
SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
SDL_EventFilter SDL_EventOK
IOHIDElementRef elementRef
void SDL_SYS_JoystickDetect()
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