SDL  2.0
SDL_sysjoystick.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
4 
5  This software is provided 'as-is', without any express or implied
6  warranty. In no event will the authors be held liable for any damages
7  arising from the use of this software.
8 
9  Permission is granted to anyone to use this software for any purpose,
10  including commercial applications, and to alter it and redistribute it
11  freely, subject to the following restrictions:
12 
13  1. The origin of this software must not be misrepresented; you must not
14  claim that you wrote the original software. If you use this software
15  in a product, an acknowledgment in the product documentation would be
16  appreciated but is not required.
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19  3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 #ifdef SDL_JOYSTICK_IOKIT
24 
25 #include "SDL_events.h"
26 #include "SDL_joystick.h"
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" /* For haptic hot plugging */
32 
33 
34 #define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick")
35 
36 #define CONVERT_MAGNITUDE(x) (((x)*10000) / 0x7FFF)
37 
38 /* The base object of the HID Manager API */
39 static IOHIDManagerRef hidman = NULL;
40 
41 /* Linked list of all available devices */
42 static recDevice *gpDeviceList = NULL;
43 
44 void FreeRumbleEffectData(FFEFFECT *effect)
45 {
46  if (!effect) {
47  return;
48  }
49  SDL_free(effect->rgdwAxes);
50  SDL_free(effect->rglDirection);
51  SDL_free(effect->lpvTypeSpecificParams);
52  SDL_free(effect);
53 }
54 
55 FFEFFECT *CreateRumbleEffectData(Sint16 magnitude, Uint32 duration_ms)
56 {
57  FFEFFECT *effect;
58  FFPERIODIC *periodic;
59 
60  /* Create the effect */
61  effect = (FFEFFECT *)SDL_calloc(1, sizeof(*effect));
62  if (!effect) {
63  return NULL;
64  }
65  effect->dwSize = sizeof(*effect);
66  effect->dwGain = 10000;
67  effect->dwFlags = FFEFF_OBJECTOFFSETS;
68  effect->dwDuration = duration_ms * 1000; /* In microseconds. */
69  effect->dwTriggerButton = FFEB_NOTRIGGER;
70 
71  effect->cAxes = 2;
72  effect->rgdwAxes = (DWORD *)SDL_calloc(effect->cAxes, sizeof(DWORD));
73  if (!effect->rgdwAxes) {
74  FreeRumbleEffectData(effect);
75  return NULL;
76  }
77 
78  effect->rglDirection = (LONG *)SDL_calloc(effect->cAxes, sizeof(LONG));
79  if (!effect->rglDirection) {
80  FreeRumbleEffectData(effect);
81  return NULL;
82  }
83  effect->dwFlags |= FFEFF_CARTESIAN;
84 
85  periodic = (FFPERIODIC *)SDL_calloc(1, sizeof(*periodic));
86  if (!periodic) {
87  FreeRumbleEffectData(effect);
88  return NULL;
89  }
90  periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
91  periodic->dwPeriod = 1000000;
92 
93  effect->cbTypeSpecificParams = sizeof(*periodic);
94  effect->lpvTypeSpecificParams = periodic;
95 
96  return effect;
97 }
98 
99 static recDevice *GetDeviceForIndex(int device_index)
100 {
101  recDevice *device = gpDeviceList;
102  while (device) {
103  if (!device->removed) {
104  if (device_index == 0)
105  break;
106 
107  --device_index;
108  }
109  device = device->pNext;
110  }
111  return device;
112 }
113 
114 static void
115 FreeElementList(recElement *pElement)
116 {
117  while (pElement) {
118  recElement *pElementNext = pElement->pNext;
119  SDL_free(pElement);
120  pElement = pElementNext;
121  }
122 }
123 
124 static recDevice *
125 FreeDevice(recDevice *removeDevice)
126 {
127  recDevice *pDeviceNext = NULL;
128  if (removeDevice) {
129  if (removeDevice->deviceRef) {
130  IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
131  removeDevice->deviceRef = NULL;
132  }
133 
134  /* save next device prior to disposing of this device */
135  pDeviceNext = removeDevice->pNext;
136 
137  if ( gpDeviceList == removeDevice ) {
138  gpDeviceList = pDeviceNext;
139  } else {
140  recDevice *device = gpDeviceList;
141  while (device->pNext != removeDevice) {
142  device = device->pNext;
143  }
144  device->pNext = pDeviceNext;
145  }
146  removeDevice->pNext = NULL;
147 
148  /* free element lists */
149  FreeElementList(removeDevice->firstAxis);
150  FreeElementList(removeDevice->firstButton);
151  FreeElementList(removeDevice->firstHat);
152 
153  SDL_free(removeDevice);
154  }
155  return pDeviceNext;
156 }
157 
158 static SDL_bool
159 GetHIDElementState(recDevice *pDevice, recElement *pElement, SInt32 *pValue)
160 {
161  SInt32 value = 0;
162  int returnValue = SDL_FALSE;
163 
164  if (pDevice && pElement) {
165  IOHIDValueRef valueRef;
166  if (IOHIDDeviceGetValue(pDevice->deviceRef, pElement->elementRef, &valueRef) == kIOReturnSuccess) {
167  value = (SInt32) IOHIDValueGetIntegerValue(valueRef);
168 
169  /* record min and max for auto calibration */
170  if (value < pElement->minReport) {
171  pElement->minReport = value;
172  }
173  if (value > pElement->maxReport) {
174  pElement->maxReport = value;
175  }
176  *pValue = value;
177 
178  returnValue = SDL_TRUE;
179  }
180  }
181  return returnValue;
182 }
183 
184 static SDL_bool
185 GetHIDScaledCalibratedState(recDevice * pDevice, recElement * pElement, SInt32 min, SInt32 max, SInt32 *pValue)
186 {
187  const float deviceScale = max - min;
188  const float readScale = pElement->maxReport - pElement->minReport;
189  int returnValue = SDL_FALSE;
190  if (GetHIDElementState(pDevice, pElement, pValue))
191  {
192  if (readScale == 0) {
193  returnValue = SDL_TRUE; /* no scaling at all */
194  }
195  else
196  {
197  *pValue = ((*pValue - pElement->minReport) * deviceScale / readScale) + min;
198  returnValue = SDL_TRUE;
199  }
200  }
201  return returnValue;
202 }
203 
204 static void
205 JoystickDeviceWasRemovedCallback(void *ctx, IOReturn result, void *sender)
206 {
207  recDevice *device = (recDevice *) ctx;
208  device->removed = SDL_TRUE;
209  device->deviceRef = NULL; // deviceRef was invalidated due to the remove
210  if (device->ffeffect_ref) {
211  FFDeviceReleaseEffect(device->ffdevice, device->ffeffect_ref);
212  device->ffeffect_ref = NULL;
213  }
214  if (device->ffeffect) {
215  FreeRumbleEffectData(device->ffeffect);
216  device->ffeffect = NULL;
217  }
218  if (device->ffdevice) {
219  FFReleaseDevice(device->ffdevice);
220  device->ffdevice = NULL;
221  device->ff_initialized = SDL_FALSE;
222  }
223 #if SDL_HAPTIC_IOKIT
224  MacHaptic_MaybeRemoveDevice(device->ffservice);
225 #endif
226 
227  SDL_PrivateJoystickRemoved(device->instance_id);
228 }
229 
230 
231 static void AddHIDElement(const void *value, void *parameter);
232 
233 /* Call AddHIDElement() on all elements in an array of IOHIDElementRefs */
234 static void
235 AddHIDElements(CFArrayRef array, recDevice *pDevice)
236 {
237  const CFRange range = { 0, CFArrayGetCount(array) };
238  CFArrayApplyFunction(array, range, AddHIDElement, pDevice);
239 }
240 
241 static SDL_bool
242 ElementAlreadyAdded(const IOHIDElementCookie cookie, const recElement *listitem) {
243  while (listitem) {
244  if (listitem->cookie == cookie) {
245  return SDL_TRUE;
246  }
247  listitem = listitem->pNext;
248  }
249  return SDL_FALSE;
250 }
251 
252 /* See if we care about this HID element, and if so, note it in our recDevice. */
253 static void
254 AddHIDElement(const void *value, void *parameter)
255 {
256  recDevice *pDevice = (recDevice *) parameter;
257  IOHIDElementRef refElement = (IOHIDElementRef) value;
258  const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0;
259 
260  if (refElement && (elementTypeID == IOHIDElementGetTypeID())) {
261  const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement);
262  const uint32_t usagePage = IOHIDElementGetUsagePage(refElement);
263  const uint32_t usage = IOHIDElementGetUsage(refElement);
264  recElement *element = NULL;
265  recElement **headElement = NULL;
266 
267  /* look at types of interest */
268  switch (IOHIDElementGetType(refElement)) {
269  case kIOHIDElementTypeInput_Misc:
270  case kIOHIDElementTypeInput_Button:
271  case kIOHIDElementTypeInput_Axis: {
272  switch (usagePage) { /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */
273  case kHIDPage_GenericDesktop:
274  switch (usage) {
275  case kHIDUsage_GD_X:
276  case kHIDUsage_GD_Y:
277  case kHIDUsage_GD_Z:
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)) {
285  element = (recElement *) SDL_calloc(1, sizeof (recElement));
286  if (element) {
287  pDevice->axes++;
288  headElement = &(pDevice->firstAxis);
289  }
290  }
291  break;
292 
293  case kHIDUsage_GD_Hatswitch:
294  if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) {
295  element = (recElement *) SDL_calloc(1, sizeof (recElement));
296  if (element) {
297  pDevice->hats++;
298  headElement = &(pDevice->firstHat);
299  }
300  }
301  break;
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)) {
310  element = (recElement *) SDL_calloc(1, sizeof (recElement));
311  if (element) {
312  pDevice->buttons++;
313  headElement = &(pDevice->firstButton);
314  }
315  }
316  break;
317  }
318  break;
319 
320  case kHIDPage_Simulation:
321  switch (usage) {
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)) {
327  element = (recElement *) SDL_calloc(1, sizeof (recElement));
328  if (element) {
329  pDevice->axes++;
330  headElement = &(pDevice->firstAxis);
331  }
332  }
333  break;
334 
335  default:
336  break;
337  }
338  break;
339 
340  case kHIDPage_Button:
341  case kHIDPage_Consumer: /* e.g. 'pause' button on Steelseries MFi gamepads. */
342  if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
343  element = (recElement *) SDL_calloc(1, sizeof (recElement));
344  if (element) {
345  pDevice->buttons++;
346  headElement = &(pDevice->firstButton);
347  }
348  }
349  break;
350 
351  default:
352  break;
353  }
354  }
355  break;
356 
357  case kIOHIDElementTypeCollection: {
358  CFArrayRef array = IOHIDElementGetChildren(refElement);
359  if (array) {
360  AddHIDElements(array, pDevice);
361  }
362  }
363  break;
364 
365  default:
366  break;
367  }
368 
369  if (element && headElement) { /* add to list */
370  recElement *elementPrevious = NULL;
371  recElement *elementCurrent = *headElement;
372  while (elementCurrent && usage >= elementCurrent->usage) {
373  elementPrevious = elementCurrent;
374  elementCurrent = elementCurrent->pNext;
375  }
376  if (elementPrevious) {
377  elementPrevious->pNext = element;
378  } else {
379  *headElement = element;
380  }
381 
382  element->elementRef = refElement;
383  element->usagePage = usagePage;
384  element->usage = usage;
385  element->pNext = elementCurrent;
386 
387  element->minReport = element->min = (SInt32) IOHIDElementGetLogicalMin(refElement);
388  element->maxReport = element->max = (SInt32) IOHIDElementGetLogicalMax(refElement);
389  element->cookie = IOHIDElementGetCookie(refElement);
390 
391  pDevice->elements++;
392  }
393  }
394 }
395 
396 static SDL_bool
397 GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
398 {
399  Sint32 vendor = 0;
400  Sint32 product = 0;
401  Sint32 version = 0;
402  CFTypeRef refCF = NULL;
403  CFArrayRef array = NULL;
404  Uint16 *guid16 = (Uint16 *)pDevice->guid.data;
405 
406  /* get usage page and usage */
407  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
408  if (refCF) {
409  CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage);
410  }
411  if (pDevice->usagePage != kHIDPage_GenericDesktop) {
412  return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */
413  }
414 
415  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey));
416  if (refCF) {
417  CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage);
418  }
419 
420  if ((pDevice->usage != kHIDUsage_GD_Joystick &&
421  pDevice->usage != kHIDUsage_GD_GamePad &&
422  pDevice->usage != kHIDUsage_GD_MultiAxisController)) {
423  return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */
424  }
425 
426  pDevice->deviceRef = hidDevice;
427 
428  /* get device name */
429  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey));
430  if (!refCF) {
431  /* Maybe we can't get "AwesomeJoystick2000", but we can get "Logitech"? */
432  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey));
433  }
434  if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) {
435  SDL_strlcpy(pDevice->product, "Unidentified joystick", sizeof (pDevice->product));
436  }
437 
438  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
439  if (refCF) {
440  CFNumberGetValue(refCF, kCFNumberSInt32Type, &vendor);
441  }
442 
443  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey));
444  if (refCF) {
445  CFNumberGetValue(refCF, kCFNumberSInt32Type, &product);
446  }
447 
448  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVersionNumberKey));
449  if (refCF) {
450  CFNumberGetValue(refCF, kCFNumberSInt32Type, &version);
451  }
452 
453 #ifdef SDL_JOYSTICK_HIDAPI
454  if (HIDAPI_IsDevicePresent(vendor, product, version)) {
455  /* The HIDAPI driver is taking care of this device */
456  return 0;
457  }
458 #endif
459 
460  SDL_memset(pDevice->guid.data, 0, sizeof(pDevice->guid.data));
461 
462  if (vendor && product) {
463  *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
464  *guid16++ = 0;
465  *guid16++ = SDL_SwapLE16((Uint16)vendor);
466  *guid16++ = 0;
467  *guid16++ = SDL_SwapLE16((Uint16)product);
468  *guid16++ = 0;
469  *guid16++ = SDL_SwapLE16((Uint16)version);
470  *guid16++ = 0;
471  } else {
473  *guid16++ = 0;
474  SDL_strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4);
475  }
476 
477  array = IOHIDDeviceCopyMatchingElements(hidDevice, NULL, kIOHIDOptionsTypeNone);
478  if (array) {
479  AddHIDElements(array, pDevice);
480  CFRelease(array);
481  }
482 
483  return SDL_TRUE;
484 }
485 
486 static SDL_bool
487 JoystickAlreadyKnown(IOHIDDeviceRef ioHIDDeviceObject)
488 {
489  recDevice *i;
490  for (i = gpDeviceList; i != NULL; i = i->pNext) {
491  if (i->deviceRef == ioHIDDeviceObject) {
492  return SDL_TRUE;
493  }
494  }
495  return SDL_FALSE;
496 }
497 
498 
499 static void
500 JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject)
501 {
502  recDevice *device;
503  int device_index = 0;
504  io_service_t ioservice;
505 
506  if (res != kIOReturnSuccess) {
507  return;
508  }
509 
510  if (JoystickAlreadyKnown(ioHIDDeviceObject)) {
511  return; /* IOKit sent us a duplicate. */
512  }
513 
514  device = (recDevice *) SDL_calloc(1, sizeof(recDevice));
515  if (!device) {
516  SDL_OutOfMemory();
517  return;
518  }
519 
520  if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
521  SDL_free(device);
522  return; /* not a device we care about, probably. */
523  }
524 
525  if (SDL_ShouldIgnoreJoystick(device->product, device->guid)) {
526  SDL_free(device);
527  return;
528  }
529 
530  /* Get notified when this device is disconnected. */
531  IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
532  IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
533 
534  /* Allocate an instance ID for this device */
535  device->instance_id = SDL_GetNextJoystickInstanceID();
536 
537  /* We have to do some storage of the io_service_t for SDL_HapticOpenFromJoystick */
538  ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
539  if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
540  device->ffservice = ioservice;
541 #if SDL_HAPTIC_IOKIT
542  MacHaptic_MaybeAddDevice(ioservice);
543 #endif
544  }
545 
546  /* Add device to the end of the list */
547  if ( !gpDeviceList ) {
548  gpDeviceList = device;
549  } else {
550  recDevice *curdevice;
551 
552  curdevice = gpDeviceList;
553  while ( curdevice->pNext ) {
554  ++device_index;
555  curdevice = curdevice->pNext;
556  }
557  curdevice->pNext = device;
558  ++device_index; /* bump by one since we counted by pNext. */
559  }
560 
561  SDL_PrivateJoystickAdded(device->instance_id);
562 }
563 
564 static SDL_bool
565 ConfigHIDManager(CFArrayRef matchingArray)
566 {
567  CFRunLoopRef runloop = CFRunLoopGetCurrent();
568 
569  if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
570  return SDL_FALSE;
571  }
572 
573  IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
574  IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback, NULL);
575  IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
576 
577  while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
578  /* no-op. Callback fires once per existing device. */
579  }
580 
581  /* future hotplug events will come through SDL_JOYSTICK_RUNLOOP_MODE now. */
582 
583  return SDL_TRUE; /* good to go. */
584 }
585 
586 
587 static CFDictionaryRef
588 CreateHIDDeviceMatchDictionary(const UInt32 page, const UInt32 usage, int *okay)
589 {
590  CFDictionaryRef retval = NULL;
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 };
595 
596  if (pageNumRef && usageNumRef) {
597  retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
598  }
599 
600  if (pageNumRef) {
601  CFRelease(pageNumRef);
602  }
603  if (usageNumRef) {
604  CFRelease(usageNumRef);
605  }
606 
607  if (!retval) {
608  *okay = 0;
609  }
610 
611  return retval;
612 }
613 
614 static SDL_bool
615 CreateHIDManager(void)
616 {
617  SDL_bool retval = SDL_FALSE;
618  int okay = 1;
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),
623  };
624  const size_t numElements = SDL_arraysize(vals);
625  CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) : NULL;
626  size_t i;
627 
628  for (i = 0; i < numElements; i++) {
629  if (vals[i]) {
630  CFRelease((CFTypeRef) vals[i]);
631  }
632  }
633 
634  if (array) {
635  hidman = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
636  if (hidman != NULL) {
637  retval = ConfigHIDManager(array);
638  }
639  CFRelease(array);
640  }
641 
642  return retval;
643 }
644 
645 
646 static int
647 DARWIN_JoystickInit(void)
648 {
649  if (gpDeviceList) {
650  return SDL_SetError("Joystick: Device list already inited.");
651  }
652 
653  if (!CreateHIDManager()) {
654  return SDL_SetError("Joystick: Couldn't initialize HID Manager");
655  }
656 
657  return 0;
658 }
659 
660 static int
661 DARWIN_JoystickGetCount(void)
662 {
663  recDevice *device = gpDeviceList;
664  int nJoySticks = 0;
665 
666  while (device) {
667  if (!device->removed) {
668  nJoySticks++;
669  }
670  device = device->pNext;
671  }
672 
673  return nJoySticks;
674 }
675 
676 static void
677 DARWIN_JoystickDetect(void)
678 {
679  recDevice *device = gpDeviceList;
680  while (device) {
681  if (device->removed) {
682  device = FreeDevice(device);
683  } else {
684  device = device->pNext;
685  }
686  }
687 
688  /* run this after the checks above so we don't set device->removed and delete the device before
689  DARWIN_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device */
690  while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
691  /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
692  }
693 }
694 
695 /* Function to get the device-dependent name of a joystick */
696 const char *
697 DARWIN_JoystickGetDeviceName(int device_index)
698 {
699  recDevice *device = GetDeviceForIndex(device_index);
700  return device ? device->product : "UNKNOWN";
701 }
702 
703 static int
704 DARWIN_JoystickGetDevicePlayerIndex(int device_index)
705 {
706  return -1;
707 }
708 
709 static SDL_JoystickGUID
710 DARWIN_JoystickGetDeviceGUID( int device_index )
711 {
712  recDevice *device = GetDeviceForIndex(device_index);
713  SDL_JoystickGUID guid;
714  if (device) {
715  guid = device->guid;
716  } else {
717  SDL_zero(guid);
718  }
719  return guid;
720 }
721 
722 static SDL_JoystickID
723 DARWIN_JoystickGetDeviceInstanceID(int device_index)
724 {
725  recDevice *device = GetDeviceForIndex(device_index);
726  return device ? device->instance_id : 0;
727 }
728 
729 static int
730 DARWIN_JoystickOpen(SDL_Joystick * joystick, int device_index)
731 {
732  recDevice *device = GetDeviceForIndex(device_index);
733 
734  joystick->instance_id = device->instance_id;
735  joystick->hwdata = device;
736  joystick->name = device->product;
737 
738  joystick->naxes = device->axes;
739  joystick->nhats = device->hats;
740  joystick->nballs = 0;
741  joystick->nbuttons = device->buttons;
742  return 0;
743 }
744 
745 /*
746  * Like strerror but for force feedback errors.
747  */
748 static const char *
749 FFStrError(unsigned int err)
750 {
751  switch (err) {
752  case FFERR_DEVICEFULL:
753  return "device full";
754  /* This should be valid, but for some reason isn't defined... */
755  /* case FFERR_DEVICENOTREG:
756  return "device not registered"; */
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";
767  case FFERR_GENERIC:
768  return "undetermined error";
769  case FFERR_HASEFFECTS:
770  return "device has effects";
771  case FFERR_INCOMPLETEEFFECT:
772  return "incomplete effect";
773  case FFERR_INTERNAL:
774  return "internal fault";
775  case FFERR_INVALIDDOWNLOADID:
776  return "invalid download id";
777  case FFERR_INVALIDPARAM:
778  return "invalid parameter";
779  case FFERR_MOREDATA:
780  return "more data";
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";
795 
796  default:
797  return "unknown error";
798  }
799 }
800 
801 static int
802 DARWIN_JoystickInitRumble(recDevice *device, Sint16 magnitude, Uint32 duration_ms)
803 {
804  HRESULT result;
805 
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));
810  }
811  }
812 
813  /* Reset and then enable actuators */
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));
817  }
818 
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));
822  }
823 
824  /* Create the effect */
825  device->ffeffect = CreateRumbleEffectData(magnitude, duration_ms);
826  if (!device->ffeffect) {
827  return SDL_OutOfMemory();
828  }
829 
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));
834  }
835  return 0;
836 }
837 
838 static int
839 DARWIN_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
840 {
841  HRESULT result;
842  recDevice *device = joystick->hwdata;
843 
844  /* Scale and average the two rumble strengths */
845  Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
846 
847  if (!device->ffservice) {
848  return SDL_Unsupported();
849  }
850 
851  if (device->ff_initialized) {
852  FFPERIODIC *periodic = ((FFPERIODIC *)device->ffeffect->lpvTypeSpecificParams);
853  device->ffeffect->dwDuration = duration_ms * 1000; /* In microseconds. */
854  periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
855 
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));
860  }
861  } else {
862  if (DARWIN_JoystickInitRumble(device, magnitude, duration_ms) < 0) {
863  return -1;
864  }
865  device->ff_initialized = SDL_TRUE;
866  }
867 
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));
871  }
872  return 0;
873 }
874 
875 static void
876 DARWIN_JoystickUpdate(SDL_Joystick * joystick)
877 {
878  recDevice *device = joystick->hwdata;
879  recElement *element;
880  SInt32 value, range;
881  int i;
882 
883  if (!device) {
884  return;
885  }
886 
887  if (device->removed) { /* device was unplugged; ignore it. */
888  if (joystick->hwdata) {
889  joystick->force_recentering = SDL_TRUE;
890  joystick->hwdata = NULL;
891  }
892  return;
893  }
894 
895  element = device->firstAxis;
896  i = 0;
897 
898  int goodRead = SDL_FALSE;
899  while (element) {
900  goodRead = GetHIDScaledCalibratedState(device, element, -32768, 32767, &value);
901  if (goodRead) {
902  SDL_PrivateJoystickAxis(joystick, i, value);
903  }
904 
905  element = element->pNext;
906  ++i;
907  }
908 
909  element = device->firstButton;
910  i = 0;
911  while (element) {
912  goodRead = GetHIDElementState(device, element, &value);
913  if (goodRead) {
914  if (value > 1) { /* handle pressure-sensitive buttons */
915  value = 1;
916  }
917  SDL_PrivateJoystickButton(joystick, i, value);
918  }
919 
920  element = element->pNext;
921  ++i;
922  }
923 
924  element = device->firstHat;
925  i = 0;
926 
927  while (element) {
928  Uint8 pos = 0;
929 
930  range = (element->max - element->min + 1);
931  goodRead = GetHIDElementState(device, element, &value);
932  if (goodRead) {
933  value -= element->min;
934  if (range == 4) { /* 4 position hatswitch - scale up value */
935  value *= 2;
936  } else if (range != 8) { /* Neither a 4 nor 8 positions - fall back to default position (centered) */
937  value = -1;
938  }
939  switch (value) {
940  case 0:
941  pos = SDL_HAT_UP;
942  break;
943  case 1:
944  pos = SDL_HAT_RIGHTUP;
945  break;
946  case 2:
947  pos = SDL_HAT_RIGHT;
948  break;
949  case 3:
950  pos = SDL_HAT_RIGHTDOWN;
951  break;
952  case 4:
953  pos = SDL_HAT_DOWN;
954  break;
955  case 5:
956  pos = SDL_HAT_LEFTDOWN;
957  break;
958  case 6:
959  pos = SDL_HAT_LEFT;
960  break;
961  case 7:
962  pos = SDL_HAT_LEFTUP;
963  break;
964  default:
965  /* Every other value is mapped to center. We do that because some
966  * joysticks use 8 and some 15 for this value, and apparently
967  * there are even more variants out there - so we try to be generous.
968  */
969  pos = SDL_HAT_CENTERED;
970  break;
971  }
972 
973  SDL_PrivateJoystickHat(joystick, i, pos);
974  }
975 
976  element = element->pNext;
977  ++i;
978  }
979 }
980 
981 static void
982 DARWIN_JoystickClose(SDL_Joystick * joystick)
983 {
984 }
985 
986 static void
987 DARWIN_JoystickQuit(void)
988 {
989  while (FreeDevice(gpDeviceList)) {
990  /* spin */
991  }
992 
993  if (hidman) {
994  IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
995  IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
996  CFRelease(hidman);
997  hidman = NULL;
998  }
999 }
1000 
1002 {
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,
1015 };
1016 
1017 #endif /* SDL_JOYSTICK_IOKIT */
1018 
1019 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_HAT_LEFTDOWN
Definition: SDL_joystick.h:337
#define SDL_strlcpy
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:800
GLuint64EXT * result
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
Definition: SDL_joystick.c:885
#define SDL_HARDWARE_BUS_USB
int16_t Sint16
Definition: SDL_stdinc.h:185
#define SDL_HAT_RIGHTUP
Definition: SDL_joystick.h:334
uint8_t Uint8
Definition: SDL_stdinc.h:179
SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid)
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
Definition: SDL_joystick.c:961
int MacHaptic_MaybeRemoveDevice(io_object_t device)
static SDL_JoystickDeviceItem * GetDeviceForIndex(int device_index)
uint32_t Uint32
Definition: SDL_stdinc.h:203
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
Definition: SDL_joystick.c:828
GLuint res
struct recElement * pNext
IOHIDElementCookie cookie
SDL_JoystickDriver SDL_DARWIN_JoystickDriver
#define SDL_HARDWARE_BUS_BLUETOOTH
static SDL_AudioDeviceID device
Definition: loopwave.c:37
uint32_t usagePage
SDL_bool retval
#define SDL_HAT_RIGHT
Definition: SDL_joystick.h:331
GLsizeiptr const void GLenum usage
uint32_t usage
#define SDL_HAT_RIGHTDOWN
Definition: SDL_joystick.h:335
Sint32 SDL_JoystickID
Definition: SDL_joystick.h:81
#define SDL_HAT_LEFT
Definition: SDL_joystick.h:333
#define SDL_free
#define TRUE
Definition: edid-parse.c:33
IOHIDElementRef elementRef
GLenum GLint * range
GLsizei const GLfloat * value
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
int32_t Sint32
Definition: SDL_stdinc.h:197
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)
Definition: SDL_x11sym.h:50
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:751
#define NULL
Definition: begin_code.h:164
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:161
unsigned int uint32_t
int MacHaptic_MaybeAddDevice(io_object_t device)
#define SDL_SetError
#define SDL_calloc
SDL_JoystickID SDL_GetNextJoystickInstanceID()
Definition: SDL_joystick.c:163
#define SDL_HAT_LEFTUP
Definition: SDL_joystick.h:336
uint16_t Uint16
Definition: SDL_stdinc.h:191
GLenum array
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
#define SDL_HAT_CENTERED
Definition: SDL_joystick.h:329
#define SDL_SwapLE16(X)
Definition: SDL_endian.h:232
SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version)
#define SDL_HAT_UP
Definition: SDL_joystick.h:330
#define SDL_HAT_DOWN
Definition: SDL_joystick.h:332
#define SDL_Unsupported()
Definition: SDL_error.h:53
#define SDL_memset
EGLContext ctx
Definition: eglext.h:208