21 #include "../../SDL_internal.h" 23 #ifdef SDL_JOYSTICK_IOKIT 27 #include "../SDL_sysjoystick.h" 28 #include "../SDL_joystick_c.h" 29 #include "SDL_sysjoystick_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,
Uint32 duration_ms)
61 effect = (FFEFFECT *)
SDL_calloc(1,
sizeof(*effect));
65 effect->dwSize =
sizeof(*effect);
66 effect->dwGain = 10000;
67 effect->dwFlags = FFEFF_OBJECTOFFSETS;
68 effect->dwDuration = duration_ms * 1000;
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;
103 if (!device->removed) {
104 if (device_index == 0)
109 device = device->pNext;
120 pElement = pElementNext;
125 FreeDevice(recDevice *removeDevice)
127 recDevice *pDeviceNext =
NULL;
129 if (removeDevice->deviceRef) {
130 IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
131 removeDevice->deviceRef =
NULL;
135 pDeviceNext = removeDevice->pNext;
137 if ( gpDeviceList == removeDevice ) {
138 gpDeviceList = pDeviceNext;
140 recDevice *device = gpDeviceList;
141 while (device->pNext != removeDevice) {
142 device = device->pNext;
144 device->pNext = pDeviceNext;
146 removeDevice->pNext =
NULL;
149 FreeElementList(removeDevice->firstAxis);
150 FreeElementList(removeDevice->firstButton);
151 FreeElementList(removeDevice->firstHat);
159 GetHIDElementState(recDevice *pDevice,
recElement *pElement, SInt32 *pValue)
164 if (pDevice && pElement) {
165 IOHIDValueRef valueRef;
166 if (IOHIDDeviceGetValue(pDevice->deviceRef, pElement->
elementRef, &valueRef) == kIOReturnSuccess) {
167 value = (SInt32) IOHIDValueGetIntegerValue(valueRef);
185 GetHIDScaledCalibratedState(recDevice * pDevice,
recElement * pElement, SInt32 min, SInt32 max, SInt32 *pValue)
187 const float deviceScale = max - min;
190 if (GetHIDElementState(pDevice, pElement, pValue))
192 if (readScale == 0) {
197 *pValue = ((*pValue - pElement->
minReport) * deviceScale / readScale) + min;
205 JoystickDeviceWasRemovedCallback(
void *
ctx, IOReturn
result,
void *sender)
207 recDevice *device = (recDevice *) ctx;
209 device->deviceRef =
NULL;
210 if (device->ffeffect_ref) {
211 FFDeviceReleaseEffect(device->ffdevice, device->ffeffect_ref);
212 device->ffeffect_ref =
NULL;
214 if (device->ffeffect) {
215 FreeRumbleEffectData(device->ffeffect);
216 device->ffeffect =
NULL;
218 if (device->ffdevice) {
219 FFReleaseDevice(device->ffdevice);
220 device->ffdevice =
NULL;
231 static void AddHIDElement(
const void *value,
void *parameter);
235 AddHIDElements(CFArrayRef
array, recDevice *pDevice)
237 const CFRange
range = { 0, CFArrayGetCount(array) };
238 CFArrayApplyFunction(array, range, AddHIDElement, pDevice);
242 ElementAlreadyAdded(
const IOHIDElementCookie cookie,
const recElement *listitem) {
244 if (listitem->
cookie == cookie) {
247 listitem = listitem->
pNext;
254 AddHIDElement(
const void *value,
void *parameter)
256 recDevice *pDevice = (recDevice *) parameter;
257 IOHIDElementRef refElement = (IOHIDElementRef) value;
258 const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0;
260 if (refElement && (elementTypeID == IOHIDElementGetTypeID())) {
261 const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement);
262 const uint32_t usagePage = IOHIDElementGetUsagePage(refElement);
268 switch (IOHIDElementGetType(refElement)) {
269 case kIOHIDElementTypeInput_Misc:
270 case kIOHIDElementTypeInput_Button:
271 case kIOHIDElementTypeInput_Axis: {
273 case kHIDPage_GenericDesktop:
278 case kHIDUsage_GD_Rx:
279 case kHIDUsage_GD_Ry:
280 case kHIDUsage_GD_Rz:
281 case kHIDUsage_GD_Slider:
282 case kHIDUsage_GD_Dial:
283 case kHIDUsage_GD_Wheel:
284 if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
288 headElement = &(pDevice->firstAxis);
293 case kHIDUsage_GD_Hatswitch:
294 if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) {
298 headElement = &(pDevice->firstHat);
302 case kHIDUsage_GD_DPadUp:
303 case kHIDUsage_GD_DPadDown:
304 case kHIDUsage_GD_DPadRight:
305 case kHIDUsage_GD_DPadLeft:
306 case kHIDUsage_GD_Start:
307 case kHIDUsage_GD_Select:
308 case kHIDUsage_GD_SystemMainMenu:
309 if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
313 headElement = &(pDevice->firstButton);
320 case kHIDPage_Simulation:
322 case kHIDUsage_Sim_Rudder:
323 case kHIDUsage_Sim_Throttle:
324 case kHIDUsage_Sim_Accelerator:
325 case kHIDUsage_Sim_Brake:
326 if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
330 headElement = &(pDevice->firstAxis);
340 case kHIDPage_Button:
341 case kHIDPage_Consumer:
342 if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
346 headElement = &(pDevice->firstButton);
357 case kIOHIDElementTypeCollection: {
358 CFArrayRef
array = IOHIDElementGetChildren(refElement);
360 AddHIDElements(array, pDevice);
369 if (element && headElement) {
372 while (elementCurrent && usage >= elementCurrent->
usage) {
373 elementPrevious = elementCurrent;
374 elementCurrent = elementCurrent->
pNext;
376 if (elementPrevious) {
377 elementPrevious->
pNext = element;
379 *headElement = element;
385 element->
pNext = elementCurrent;
387 element->
minReport = element->
min = (SInt32) IOHIDElementGetLogicalMin(refElement);
388 element->
maxReport = element->
max = (SInt32) IOHIDElementGetLogicalMax(refElement);
389 element->
cookie = IOHIDElementGetCookie(refElement);
397 GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
402 CFTypeRef refCF =
NULL;
407 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
409 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage);
411 if (pDevice->usagePage != kHIDPage_GenericDesktop) {
415 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey));
417 CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage);
420 if ((pDevice->usage != kHIDUsage_GD_Joystick &&
421 pDevice->usage != kHIDUsage_GD_GamePad &&
422 pDevice->usage != kHIDUsage_GD_MultiAxisController)) {
426 pDevice->deviceRef = hidDevice;
429 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey));
432 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey));
434 if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) {
435 SDL_strlcpy(pDevice->product,
"Unidentified joystick", sizeof (pDevice->product));
438 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
440 CFNumberGetValue(refCF, kCFNumberSInt32Type, &vendor);
443 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey));
445 CFNumberGetValue(refCF, kCFNumberSInt32Type, &product);
448 refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVersionNumberKey));
450 CFNumberGetValue(refCF, kCFNumberSInt32Type, &version);
453 #ifdef SDL_JOYSTICK_HIDAPI 460 SDL_memset(pDevice->guid.data, 0,
sizeof(pDevice->guid.data));
462 if (vendor && product) {
474 SDL_strlcpy((
char*)guid16, pDevice->product,
sizeof(pDevice->guid.data) - 4);
477 array = IOHIDDeviceCopyMatchingElements(hidDevice,
NULL, kIOHIDOptionsTypeNone);
479 AddHIDElements(array, pDevice);
487 JoystickAlreadyKnown(IOHIDDeviceRef ioHIDDeviceObject)
490 for (i = gpDeviceList; i !=
NULL; i = i->pNext) {
491 if (i->deviceRef == ioHIDDeviceObject) {
500 JoystickDeviceWasAddedCallback(
void *ctx, IOReturn
res,
void *sender, IOHIDDeviceRef ioHIDDeviceObject)
503 int device_index = 0;
504 io_service_t ioservice;
506 if (res != kIOReturnSuccess) {
510 if (JoystickAlreadyKnown(ioHIDDeviceObject)) {
514 device = (recDevice *)
SDL_calloc(1,
sizeof(recDevice));
520 if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
531 IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
532 IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
538 ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
539 if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
540 device->ffservice = ioservice;
547 if ( !gpDeviceList ) {
550 recDevice *curdevice;
552 curdevice = gpDeviceList;
553 while ( curdevice->pNext ) {
555 curdevice = curdevice->pNext;
557 curdevice->pNext =
device;
565 ConfigHIDManager(CFArrayRef matchingArray)
567 CFRunLoopRef runloop = CFRunLoopGetCurrent();
569 if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
573 IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
574 IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback,
NULL);
575 IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
577 while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,
TRUE) == kCFRunLoopRunHandledSource) {
587 static CFDictionaryRef
588 CreateHIDDeviceMatchDictionary(
const UInt32 page,
const UInt32 usage,
int *okay)
591 CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
592 CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
593 const void *keys[2] = { (
void *) CFSTR(kIOHIDDeviceUsagePageKey), (
void *) CFSTR(kIOHIDDeviceUsageKey) };
594 const void *vals[2] = { (
void *) pageNumRef, (
void *) usageNumRef };
596 if (pageNumRef && usageNumRef) {
597 retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
601 CFRelease(pageNumRef);
604 CFRelease(usageNumRef);
615 CreateHIDManager(
void)
619 const void *vals[] = {
620 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
621 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
622 (
void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
625 CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) :
NULL;
628 for (i = 0; i < numElements; i++) {
630 CFRelease((CFTypeRef) vals[i]);
635 hidman = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
636 if (hidman !=
NULL) {
637 retval = ConfigHIDManager(array);
647 DARWIN_JoystickInit(
void)
650 return SDL_SetError(
"Joystick: Device list already inited.");
653 if (!CreateHIDManager()) {
654 return SDL_SetError(
"Joystick: Couldn't initialize HID Manager");
661 DARWIN_JoystickGetCount(
void)
663 recDevice *device = gpDeviceList;
667 if (!device->removed) {
670 device = device->pNext;
677 DARWIN_JoystickDetect(
void)
679 recDevice *device = gpDeviceList;
681 if (device->removed) {
682 device = FreeDevice(device);
684 device = device->pNext;
690 while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,
TRUE) == kCFRunLoopRunHandledSource) {
697 DARWIN_JoystickGetDeviceName(
int device_index)
700 return device ? device->product :
"UNKNOWN";
704 DARWIN_JoystickGetDevicePlayerIndex(
int device_index)
710 DARWIN_JoystickGetDeviceGUID(
int device_index )
723 DARWIN_JoystickGetDeviceInstanceID(
int device_index)
726 return device ? device->instance_id : 0;
730 DARWIN_JoystickOpen(SDL_Joystick * joystick,
int device_index)
734 joystick->instance_id = device->instance_id;
735 joystick->hwdata =
device;
736 joystick->name = device->product;
738 joystick->naxes = device->axes;
739 joystick->nhats = device->hats;
740 joystick->nballs = 0;
741 joystick->nbuttons = device->buttons;
749 FFStrError(
unsigned int err)
752 case FFERR_DEVICEFULL:
753 return "device full";
757 case FFERR_DEVICEPAUSED:
758 return "device paused";
759 case FFERR_DEVICERELEASED:
760 return "device released";
761 case FFERR_EFFECTPLAYING:
762 return "effect playing";
763 case FFERR_EFFECTTYPEMISMATCH:
764 return "effect type mismatch";
765 case FFERR_EFFECTTYPENOTSUPPORTED:
766 return "effect type not supported";
768 return "undetermined error";
769 case FFERR_HASEFFECTS:
770 return "device has effects";
771 case FFERR_INCOMPLETEEFFECT:
772 return "incomplete effect";
774 return "internal fault";
775 case FFERR_INVALIDDOWNLOADID:
776 return "invalid download id";
777 case FFERR_INVALIDPARAM:
778 return "invalid parameter";
781 case FFERR_NOINTERFACE:
782 return "interface not supported";
783 case FFERR_NOTDOWNLOADED:
784 return "effect is not downloaded";
785 case FFERR_NOTINITIALIZED:
786 return "object has not been initialized";
787 case FFERR_OUTOFMEMORY:
788 return "out of memory";
789 case FFERR_UNPLUGGED:
790 return "device is unplugged";
791 case FFERR_UNSUPPORTED:
792 return "function call unsupported";
793 case FFERR_UNSUPPORTEDAXIS:
794 return "axis unsupported";
797 return "unknown error";
802 DARWIN_JoystickInitRumble(recDevice *device,
Sint16 magnitude,
Uint32 duration_ms)
806 if (!device->ffdevice) {
807 result = FFCreateDevice(device->ffservice, &device->ffdevice);
808 if (result != FF_OK) {
809 return SDL_SetError(
"Unable to create force feedback device from service: %s", FFStrError(result));
814 result = FFDeviceSendForceFeedbackCommand(device->ffdevice, FFSFFC_RESET);
815 if (result != FF_OK) {
816 return SDL_SetError(
"Unable to reset force feedback device: %s", FFStrError(result));
819 result = FFDeviceSendForceFeedbackCommand(device->ffdevice, FFSFFC_SETACTUATORSON);
820 if (result != FF_OK) {
821 return SDL_SetError(
"Unable to enable force feedback actuators: %s", FFStrError(result));
825 device->ffeffect = CreateRumbleEffectData(magnitude, duration_ms);
826 if (!device->ffeffect) {
830 result = FFDeviceCreateEffect(device->ffdevice, kFFEffectType_Sine_ID,
831 device->ffeffect, &device->ffeffect_ref);
832 if (result != FF_OK) {
833 return SDL_SetError(
"Haptic: Unable to create effect: %s", FFStrError(result));
839 DARWIN_JoystickRumble(SDL_Joystick * joystick,
Uint16 low_frequency_rumble,
Uint16 high_frequency_rumble,
Uint32 duration_ms)
842 recDevice *device = joystick->hwdata;
845 Sint16 magnitude = (
Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
847 if (!device->ffservice) {
851 if (device->ff_initialized) {
852 FFPERIODIC *periodic = ((FFPERIODIC *)device->ffeffect->lpvTypeSpecificParams);
853 device->ffeffect->dwDuration = duration_ms * 1000;
854 periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
856 result = FFEffectSetParameters(device->ffeffect_ref, device->ffeffect,
857 (FFEP_DURATION | FFEP_TYPESPECIFICPARAMS));
858 if (result != FF_OK) {
859 return SDL_SetError(
"Unable to update rumble effect: %s", FFStrError(result));
862 if (DARWIN_JoystickInitRumble(device, magnitude, duration_ms) < 0) {
868 result = FFEffectStart(device->ffeffect_ref, 1, 0);
869 if (result != FF_OK) {
870 return SDL_SetError(
"Unable to run the rumble effect: %s", FFStrError(result));
876 DARWIN_JoystickUpdate(SDL_Joystick * joystick)
878 recDevice *device = joystick->hwdata;
887 if (device->removed) {
888 if (joystick->hwdata) {
889 joystick->force_recentering =
SDL_TRUE;
890 joystick->hwdata =
NULL;
895 element = device->firstAxis;
900 goodRead = GetHIDScaledCalibratedState(device, element, -32768, 32767, &value);
905 element = element->
pNext;
909 element = device->firstButton;
912 goodRead = GetHIDElementState(device, element, &value);
920 element = element->
pNext;
924 element = device->firstHat;
930 range = (element->
max - element->
min + 1);
931 goodRead = GetHIDElementState(device, element, &value);
933 value -= element->
min;
936 }
else if (range != 8) {
976 element = element->
pNext;
982 DARWIN_JoystickClose(SDL_Joystick * joystick)
987 DARWIN_JoystickQuit(
void)
989 while (FreeDevice(gpDeviceList)) {
994 IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
995 IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
1003 DARWIN_JoystickInit,
1004 DARWIN_JoystickGetCount,
1005 DARWIN_JoystickDetect,
1006 DARWIN_JoystickGetDeviceName,
1007 DARWIN_JoystickGetDevicePlayerIndex,
1008 DARWIN_JoystickGetDeviceGUID,
1009 DARWIN_JoystickGetDeviceInstanceID,
1010 DARWIN_JoystickOpen,
1011 DARWIN_JoystickRumble,
1012 DARWIN_JoystickUpdate,
1013 DARWIN_JoystickClose,
1014 DARWIN_JoystickQuit,
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
#define SDL_HARDWARE_BUS_USB
SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid)
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
int MacHaptic_MaybeRemoveDevice(io_object_t device)
static SDL_JoystickDeviceItem * GetDeviceForIndex(int device_index)
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
struct recElement * pNext
IOHIDElementCookie cookie
SDL_JoystickDriver SDL_DARWIN_JoystickDriver
#define SDL_HARDWARE_BUS_BLUETOOTH
static SDL_AudioDeviceID device
GLsizeiptr const void GLenum usage
#define SDL_HAT_RIGHTDOWN
IOHIDElementRef elementRef
GLsizei const GLfloat * value
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)
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
#define SDL_OutOfMemory()
int MacHaptic_MaybeAddDevice(io_object_t device)
SDL_JoystickID SDL_GetNextJoystickInstanceID()
#define SDL_arraysize(array)
SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version)
#define SDL_Unsupported()