21 #include "../../SDL_internal.h"
23 #ifdef SDL_JOYSTICK_IOKIT
27 #include "../SDL_sysjoystick.h"
28 #include "../SDL_joystick_c.h"
30 #include "../hidapi/SDL_hidapijoystick_c.h"
31 #include "../../haptic/darwin/SDL_syshaptic_c.h"
34 #define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick")
36 #define CONVERT_MAGNITUDE(x) (((x)*10000) / 0x7FFF)
39 static IOHIDManagerRef hidman =
NULL;
42 static recDevice *gpDeviceList =
NULL;
44 void FreeRumbleEffectData(FFEFFECT *effect)
51 SDL_free(effect->lpvTypeSpecificParams);
55 FFEFFECT *CreateRumbleEffectData(
Sint16 magnitude)
61 effect = (FFEFFECT *)
SDL_calloc(1,
sizeof(*effect));
65 effect->dwSize =
sizeof(*effect);
66 effect->dwGain = 10000;
67 effect->dwFlags = FFEFF_OBJECTOFFSETS;
69 effect->dwTriggerButton = FFEB_NOTRIGGER;
72 effect->rgdwAxes = (DWORD *)
SDL_calloc(effect->cAxes,
sizeof(DWORD));
73 if (!effect->rgdwAxes) {
74 FreeRumbleEffectData(effect);
78 effect->rglDirection = (LONG *)
SDL_calloc(effect->cAxes,
sizeof(LONG));
79 if (!effect->rglDirection) {
80 FreeRumbleEffectData(effect);
83 effect->dwFlags |= FFEFF_CARTESIAN;
85 periodic = (FFPERIODIC *)
SDL_calloc(1,
sizeof(*periodic));
87 FreeRumbleEffectData(effect);
90 periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
91 periodic->dwPeriod = 1000000;
93 effect->cbTypeSpecificParams =
sizeof(*periodic);
94 effect->lpvTypeSpecificParams = periodic;
101 recDevice *
device = gpDeviceList;
104 if (device_index == 0)
120 pElement = pElementNext;
125 FreeDevice(recDevice *removeDevice)
127 recDevice *pDeviceNext =
NULL;
129 if (removeDevice->deviceRef) {
130 if (removeDevice->runLoopAttached) {
137 IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
139 CFRelease(removeDevice->deviceRef);
140 removeDevice->deviceRef =
NULL;
147 if (removeDevice->joystick) {
148 removeDevice->joystick->hwdata =
NULL;
153 pDeviceNext = removeDevice->pNext;
155 if ( gpDeviceList == removeDevice ) {
156 gpDeviceList = pDeviceNext;
157 }
else if (gpDeviceList) {
158 recDevice *
device = gpDeviceList;
159 while (
device->pNext != removeDevice) {
162 device->pNext = pDeviceNext;
164 removeDevice->pNext =
NULL;
167 FreeElementList(removeDevice->firstAxis);
168 FreeElementList(removeDevice->firstButton);
169 FreeElementList(removeDevice->firstHat);
177 GetHIDElementState(recDevice *pDevice,
recElement *pElement, SInt32 *pValue)
182 if (pDevice && pDevice->deviceRef && pElement) {
183 IOHIDValueRef valueRef;
184 if (IOHIDDeviceGetValue(pDevice->deviceRef, pElement->
elementRef, &valueRef) == kIOReturnSuccess) {
185 value = (SInt32) IOHIDValueGetIntegerValue(valueRef);
203 GetHIDScaledCalibratedState(recDevice * pDevice,
recElement * pElement, SInt32
min, SInt32 max, SInt32 *pValue)
205 const float deviceScale = max -
min;
208 if (GetHIDElementState(pDevice, pElement, pValue))
210 if (readScale == 0) {
215 *pValue = ((*pValue - pElement->
minReport) * deviceScale / readScale) +
min;
223 JoystickDeviceWasRemovedCallback(
void *
ctx, IOReturn
result,
void *sender)
229 CFRelease(
device->deviceRef);
232 if (
device->ffeffect_ref) {
233 FFDeviceReleaseEffect(
device->ffdevice,
device->ffeffect_ref);
237 FreeRumbleEffectData(
device->ffeffect);
241 FFReleaseDevice(
device->ffdevice);
253 static void AddHIDElement(
const void *
value,
void *parameter);
257 AddHIDElements(CFArrayRef
array, recDevice *pDevice)
259 const CFRange
range = { 0, CFArrayGetCount(
array) };
260 CFArrayApplyFunction(
array,
range, AddHIDElement, pDevice);
264 ElementAlreadyAdded(
const IOHIDElementCookie cookie,
const recElement *listitem) {
266 if (listitem->
cookie == cookie) {
269 listitem = listitem->
pNext;
276 AddHIDElement(
const void *
value,
void *parameter)
278 recDevice *pDevice = (recDevice *) parameter;
279 IOHIDElementRef refElement = (IOHIDElementRef)
value;
280 const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0;
282 if (refElement && (elementTypeID == IOHIDElementGetTypeID())) {
283 const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement);
284 const uint32_t usagePage = IOHIDElementGetUsagePage(refElement);
290 switch (IOHIDElementGetType(refElement)) {
291 case kIOHIDElementTypeInput_Misc:
292 case kIOHIDElementTypeInput_Button:
293 case kIOHIDElementTypeInput_Axis: {
295 case kHIDPage_GenericDesktop:
300 case kHIDUsage_GD_Rx:
301 case kHIDUsage_GD_Ry:
302 case kHIDUsage_GD_Rz:
303 case kHIDUsage_GD_Slider:
304 case kHIDUsage_GD_Dial:
305 case kHIDUsage_GD_Wheel:
306 if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
310 headElement = &(pDevice->firstAxis);
315 case kHIDUsage_GD_Hatswitch:
316 if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) {
320 headElement = &(pDevice->firstHat);
324 case kHIDUsage_GD_DPadUp:
325 case kHIDUsage_GD_DPadDown:
326 case kHIDUsage_GD_DPadRight:
327 case kHIDUsage_GD_DPadLeft:
328 case kHIDUsage_GD_Start:
329 case kHIDUsage_GD_Select:
330 case kHIDUsage_GD_SystemMainMenu:
331 if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
335 headElement = &(pDevice->firstButton);
342 case kHIDPage_Simulation:
344 case kHIDUsage_Sim_Rudder:
345 case kHIDUsage_Sim_Throttle:
346 case kHIDUsage_Sim_Accelerator:
347 case kHIDUsage_Sim_Brake:
348 if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
352 headElement = &(pDevice->firstAxis);
362 case kHIDPage_Button:
363 case kHIDPage_Consumer:
364 if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
368 headElement = &(pDevice->firstButton);
379 case kIOHIDElementTypeCollection: {
380 CFArrayRef
array = IOHIDElementGetChildren(refElement);
382 AddHIDElements(
array, pDevice);
391 if (element && headElement) {
394 while (elementCurrent &&
usage >= elementCurrent->
usage) {
395 elementPrevious = elementCurrent;
396 elementCurrent = elementCurrent->
pNext;
398 if (elementPrevious) {
399 elementPrevious->
pNext = element;
401 *headElement = element;
407 element->
pNext = elementCurrent;
409 element->
minReport = element->
min = (SInt32) IOHIDElementGetLogicalMin(refElement);
410 element->
maxReport = element->
max = (SInt32) IOHIDElementGetLogicalMax(refElement);
411 element->
cookie = IOHIDElementGetCookie(refElement);
420 GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
426 char manufacturer_string[256];
427 char product_string[256];
428 CFTypeRef refCF =
NULL;
433 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
435 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage);
437 if (pDevice->usagePage != kHIDPage_GenericDesktop) {
441 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey));
443 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage);
446 if ((pDevice->usage != kHIDUsage_GD_Joystick &&
447 pDevice->usage != kHIDUsage_GD_GamePad &&
448 pDevice->usage != kHIDUsage_GD_MultiAxisController)) {
461 pDevice->deviceRef = hidDevice;
463 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
465 CFNumberGetValue(refCF, kCFNumberSInt32Type, &vendor);
468 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey));
470 CFNumberGetValue(refCF, kCFNumberSInt32Type, &product);
473 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVersionNumberKey));
475 CFNumberGetValue(refCF, kCFNumberSInt32Type, &version);
479 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey));
480 if ((!refCF) || (!CFStringGetCString(refCF, manufacturer_string,
sizeof(manufacturer_string), kCFStringEncodingUTF8))) {
481 manufacturer_string[0] =
'\0';
483 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey));
484 if ((!refCF) || (!CFStringGetCString(refCF, product_string,
sizeof(product_string), kCFStringEncodingUTF8))) {
485 product_string[0] =
'\0';
493 #ifdef SDL_JOYSTICK_HIDAPI
500 SDL_memset(pDevice->guid.data, 0,
sizeof(pDevice->guid.data));
502 if (vendor && product) {
514 SDL_strlcpy((
char*)guid16, pDevice->product,
sizeof(pDevice->guid.data) - 4);
517 array = IOHIDDeviceCopyMatchingElements(hidDevice,
NULL, kIOHIDOptionsTypeNone);
519 AddHIDElements(
array, pDevice);
527 JoystickAlreadyKnown(IOHIDDeviceRef ioHIDDeviceObject)
531 #if defined(SDL_JOYSTICK_MFI)
533 if (IOS_SupportedHIDDevice(ioHIDDeviceObject)) {
538 for (
i = gpDeviceList;
i !=
NULL;
i =
i->pNext) {
539 if (
i->deviceRef == ioHIDDeviceObject) {
548 JoystickDeviceWasAddedCallback(
void *
ctx, IOReturn
res,
void *sender, IOHIDDeviceRef ioHIDDeviceObject)
551 int device_index = 0;
552 io_service_t ioservice;
554 if (
res != kIOReturnSuccess) {
558 if (JoystickAlreadyKnown(ioHIDDeviceObject)) {
568 if (!GetDeviceInfo(ioHIDDeviceObject,
device)) {
579 IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback,
device);
580 IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
587 ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
588 if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
589 device->ffservice = ioservice;
596 if ( !gpDeviceList ) {
599 recDevice *curdevice;
601 curdevice = gpDeviceList;
602 while ( curdevice->pNext ) {
604 curdevice = curdevice->pNext;
606 curdevice->pNext =
device;
614 ConfigHIDManager(CFArrayRef matchingArray)
616 CFRunLoopRef runloop = CFRunLoopGetCurrent();
618 if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
622 IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
623 IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback,
NULL);
624 IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
626 while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,
TRUE) == kCFRunLoopRunHandledSource) {
636 static CFDictionaryRef
637 CreateHIDDeviceMatchDictionary(
const UInt32 page,
const UInt32
usage,
int *okay)
640 CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
641 CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &
usage);
642 const void *keys[2] = { (
void *) CFSTR(kIOHIDDeviceUsagePageKey), (
void *) CFSTR(kIOHIDDeviceUsageKey) };
643 const void *vals[2] = { (
void *) pageNumRef, (
void *) usageNumRef };
645 if (pageNumRef && usageNumRef) {
646 retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
650 CFRelease(pageNumRef);
653 CFRelease(usageNumRef);
664 CreateHIDManager(
void)
668 const void *vals[] = {
669 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
670 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
671 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
674 CFArrayRef
array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) :
NULL;
677 for (
i = 0;
i < numElements;
i++) {
679 CFRelease((CFTypeRef) vals[
i]);
684 hidman = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
685 if (hidman !=
NULL) {
696 DARWIN_JoystickInit(
void)
699 return SDL_SetError(
"Joystick: Device list already inited.");
702 if (!CreateHIDManager()) {
703 return SDL_SetError(
"Joystick: Couldn't initialize HID Manager");
710 DARWIN_JoystickGetCount(
void)
712 recDevice *
device = gpDeviceList;
726 DARWIN_JoystickDetect(
void)
728 recDevice *
device = gpDeviceList;
739 while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,
TRUE) == kCFRunLoopRunHandledSource) {
746 DARWIN_JoystickGetDeviceName(
int device_index)
753 DARWIN_JoystickGetDevicePlayerIndex(
int device_index)
759 DARWIN_JoystickSetDevicePlayerIndex(
int device_index,
int player_index)
764 DARWIN_JoystickGetDeviceGUID(
int device_index )
777 DARWIN_JoystickGetDeviceInstanceID(
int device_index)
784 DARWIN_JoystickOpen(SDL_Joystick *
joystick,
int device_index)
804 FFStrError(
unsigned int err)
807 case FFERR_DEVICEFULL:
808 return "device full";
812 case FFERR_DEVICEPAUSED:
813 return "device paused";
814 case FFERR_DEVICERELEASED:
815 return "device released";
816 case FFERR_EFFECTPLAYING:
817 return "effect playing";
818 case FFERR_EFFECTTYPEMISMATCH:
819 return "effect type mismatch";
820 case FFERR_EFFECTTYPENOTSUPPORTED:
821 return "effect type not supported";
823 return "undetermined error";
824 case FFERR_HASEFFECTS:
825 return "device has effects";
826 case FFERR_INCOMPLETEEFFECT:
827 return "incomplete effect";
829 return "internal fault";
830 case FFERR_INVALIDDOWNLOADID:
831 return "invalid download id";
832 case FFERR_INVALIDPARAM:
833 return "invalid parameter";
836 case FFERR_NOINTERFACE:
837 return "interface not supported";
838 case FFERR_NOTDOWNLOADED:
839 return "effect is not downloaded";
840 case FFERR_NOTINITIALIZED:
841 return "object has not been initialized";
842 case FFERR_OUTOFMEMORY:
843 return "out of memory";
844 case FFERR_UNPLUGGED:
845 return "device is unplugged";
846 case FFERR_UNSUPPORTED:
847 return "function call unsupported";
848 case FFERR_UNSUPPORTEDAXIS:
849 return "axis unsupported";
852 return "unknown error";
857 DARWIN_JoystickInitRumble(recDevice *
device,
Sint16 magnitude)
864 return SDL_SetError(
"Unable to create force feedback device from service: %s", FFStrError(
result));
869 result = FFDeviceSendForceFeedbackCommand(
device->ffdevice, FFSFFC_RESET);
874 result = FFDeviceSendForceFeedbackCommand(
device->ffdevice, FFSFFC_SETACTUATORSON);
876 return SDL_SetError(
"Unable to enable force feedback actuators: %s", FFStrError(
result));
880 device->ffeffect = CreateRumbleEffectData(magnitude);
885 result = FFDeviceCreateEffect(
device->ffdevice, kFFEffectType_Sine_ID,
894 DARWIN_JoystickRumble(SDL_Joystick *
joystick,
Uint16 low_frequency_rumble,
Uint16 high_frequency_rumble)
900 Sint16 magnitude = (
Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
903 return SDL_SetError(
"Rumble failed, device disconnected");
910 if (
device->ff_initialized) {
911 FFPERIODIC *periodic = ((FFPERIODIC *)
device->ffeffect->lpvTypeSpecificParams);
912 periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
915 (FFEP_DURATION | FFEP_TYPESPECIFICPARAMS));
920 if (DARWIN_JoystickInitRumble(
device, magnitude) < 0) {
940 DARWIN_JoystickHasLED(SDL_Joystick *
joystick)
958 DARWIN_JoystickUpdate(SDL_Joystick *
joystick)
976 element =
device->firstAxis;
981 goodRead = GetHIDScaledCalibratedState(
device, element, -32768, 32767, &
value);
986 element = element->
pNext;
990 element =
device->firstButton;
993 goodRead = GetHIDElementState(
device, element, &
value);
1001 element = element->
pNext;
1005 element =
device->firstHat;
1012 goodRead = GetHIDElementState(
device, element, &
value);
1017 }
else if (
range != 8) {
1057 element = element->
pNext;
1063 DARWIN_JoystickClose(SDL_Joystick *
joystick)
1072 DARWIN_JoystickQuit(
void)
1074 while (FreeDevice(gpDeviceList)) {
1079 IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
1080 IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
1094 DARWIN_JoystickInit,
1095 DARWIN_JoystickGetCount,
1096 DARWIN_JoystickDetect,
1097 DARWIN_JoystickGetDeviceName,
1098 DARWIN_JoystickGetDevicePlayerIndex,
1099 DARWIN_JoystickSetDevicePlayerIndex,
1100 DARWIN_JoystickGetDeviceGUID,
1101 DARWIN_JoystickGetDeviceInstanceID,
1102 DARWIN_JoystickOpen,
1103 DARWIN_JoystickRumble,
1104 DARWIN_JoystickRumbleTriggers,
1105 DARWIN_JoystickHasLED,
1106 DARWIN_JoystickSetLED,
1107 DARWIN_JoystickSetSensorsEnabled,
1108 DARWIN_JoystickUpdate,
1109 DARWIN_JoystickClose,
1110 DARWIN_JoystickQuit,
1111 DARWIN_JoystickGetGamepadMapping
#define SDL_LockJoysticks
#define SDL_UnlockJoysticks
#define SDL_OutOfMemory()
#define SDL_Unsupported()
SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid)
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
SDL_JoystickID SDL_GetNextJoystickInstanceID()
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
char * SDL_CreateJoystickName(Uint16 vendor, Uint16 product, const char *vendor_name, const char *product_name)
#define SDL_HAT_RIGHTDOWN
static SDL_JoystickDeviceItem * GetDeviceForIndex(int device_index)
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLuint const GLchar * name
GLsizei const GLfloat * value
GLsizeiptr const void GLenum usage
#define SDL_arraysize(array)
#define SDL_HARDWARE_BUS_USB
#define SDL_MAX_RUMBLE_DURATION_MS
#define SDL_HARDWARE_BUS_BLUETOOTH
SDL_JoystickDriver SDL_DARWIN_JoystickDriver
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 MacHaptic_MaybeRemoveDevice(io_object_t device)
int MacHaptic_MaybeAddDevice(io_object_t device)
static SDL_AudioDeviceID device
IOHIDElementCookie cookie
IOHIDElementRef elementRef
struct recElement * pNext
static SDL_Joystick * joystick