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 <IOKit/hid/IOHIDLib.h>
26 
27 /* For force feedback testing. */
28 #include <ForceFeedback/ForceFeedback.h>
29 #include <ForceFeedback/ForceFeedbackConstants.h>
30 
31 #include "SDL_joystick.h"
32 #include "../SDL_sysjoystick.h"
33 #include "../SDL_joystick_c.h"
34 #include "SDL_sysjoystick_c.h"
35 #include "SDL_events.h"
36 #include "../../haptic/darwin/SDL_syshaptic_c.h" /* For haptic hot plugging */
37 
38 #define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick")
39 
40 /* The base object of the HID Manager API */
41 static IOHIDManagerRef hidman = NULL;
42 
43 /* Linked list of all available devices */
44 static recDevice *gpDeviceList = NULL;
45 
46 /* static incrementing counter for new joystick devices seen on the system. Devices should start with index 0 */
47 static int s_joystick_instance_id = -1;
48 
49 static recDevice *GetDeviceForIndex(int device_index)
50 {
51  recDevice *device = gpDeviceList;
52  while (device) {
53  if (!device->removed) {
54  if (device_index == 0)
55  break;
56 
57  --device_index;
58  }
59  device = device->pNext;
60  }
61  return device;
62 }
63 
64 static void
65 FreeElementList(recElement *pElement)
66 {
67  while (pElement) {
68  recElement *pElementNext = pElement->pNext;
69  SDL_free(pElement);
70  pElement = pElementNext;
71  }
72 }
73 
74 static recDevice *
75 FreeDevice(recDevice *removeDevice)
76 {
77  recDevice *pDeviceNext = NULL;
78  if (removeDevice) {
79  if (removeDevice->deviceRef) {
80  IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
81  removeDevice->deviceRef = NULL;
82  }
83 
84  /* save next device prior to disposing of this device */
85  pDeviceNext = removeDevice->pNext;
86 
87  if ( gpDeviceList == removeDevice ) {
88  gpDeviceList = pDeviceNext;
89  } else {
90  recDevice *device = gpDeviceList;
91  while (device->pNext != removeDevice) {
92  device = device->pNext;
93  }
94  device->pNext = pDeviceNext;
95  }
96  removeDevice->pNext = NULL;
97 
98  /* free element lists */
99  FreeElementList(removeDevice->firstAxis);
100  FreeElementList(removeDevice->firstButton);
101  FreeElementList(removeDevice->firstHat);
102 
103  SDL_free(removeDevice);
104  }
105  return pDeviceNext;
106 }
107 
108 static SDL_bool
109 GetHIDElementState(recDevice *pDevice, recElement *pElement, SInt32 *pValue)
110 {
111  SInt32 value = 0;
112  int returnValue = SDL_FALSE;
113 
114  if (pDevice && pElement) {
115  IOHIDValueRef valueRef;
116  if (IOHIDDeviceGetValue(pDevice->deviceRef, pElement->elementRef, &valueRef) == kIOReturnSuccess) {
117  value = (SInt32) IOHIDValueGetIntegerValue(valueRef);
118 
119  /* record min and max for auto calibration */
120  if (value < pElement->minReport) {
121  pElement->minReport = value;
122  }
123  if (value > pElement->maxReport) {
124  pElement->maxReport = value;
125  }
126  *pValue = value;
127 
128  returnValue = SDL_TRUE;
129  }
130  }
131  return returnValue;
132 }
133 
134 static SDL_bool
135 GetHIDScaledCalibratedState(recDevice * pDevice, recElement * pElement, SInt32 min, SInt32 max, SInt32 *pValue)
136 {
137  const float deviceScale = max - min;
138  const float readScale = pElement->maxReport - pElement->minReport;
139  int returnValue = SDL_FALSE;
140  if (GetHIDElementState(pDevice, pElement, pValue))
141  {
142  if (readScale == 0) {
143  returnValue = SDL_TRUE; /* no scaling at all */
144  }
145  else
146  {
147  *pValue = ((*pValue - pElement->minReport) * deviceScale / readScale) + min;
148  returnValue = SDL_TRUE;
149  }
150  }
151  return returnValue;
152 }
153 
154 static void
155 JoystickDeviceWasRemovedCallback(void *ctx, IOReturn result, void *sender)
156 {
157  recDevice *device = (recDevice *) ctx;
158  device->removed = SDL_TRUE;
159  device->deviceRef = NULL; // deviceRef was invalidated due to the remove
160 #if SDL_HAPTIC_IOKIT
161  MacHaptic_MaybeRemoveDevice(device->ffservice);
162 #endif
163 
164  SDL_PrivateJoystickRemoved(device->instance_id);
165 }
166 
167 
168 static void AddHIDElement(const void *value, void *parameter);
169 
170 /* Call AddHIDElement() on all elements in an array of IOHIDElementRefs */
171 static void
172 AddHIDElements(CFArrayRef array, recDevice *pDevice)
173 {
174  const CFRange range = { 0, CFArrayGetCount(array) };
175  CFArrayApplyFunction(array, range, AddHIDElement, pDevice);
176 }
177 
178 static SDL_bool
179 ElementAlreadyAdded(const IOHIDElementCookie cookie, const recElement *listitem) {
180  while (listitem) {
181  if (listitem->cookie == cookie) {
182  return SDL_TRUE;
183  }
184  listitem = listitem->pNext;
185  }
186  return SDL_FALSE;
187 }
188 
189 /* See if we care about this HID element, and if so, note it in our recDevice. */
190 static void
191 AddHIDElement(const void *value, void *parameter)
192 {
193  recDevice *pDevice = (recDevice *) parameter;
194  IOHIDElementRef refElement = (IOHIDElementRef) value;
195  const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0;
196 
197  if (refElement && (elementTypeID == IOHIDElementGetTypeID())) {
198  const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement);
199  const uint32_t usagePage = IOHIDElementGetUsagePage(refElement);
200  const uint32_t usage = IOHIDElementGetUsage(refElement);
201  recElement *element = NULL;
202  recElement **headElement = NULL;
203 
204  /* look at types of interest */
205  switch (IOHIDElementGetType(refElement)) {
206  case kIOHIDElementTypeInput_Misc:
207  case kIOHIDElementTypeInput_Button:
208  case kIOHIDElementTypeInput_Axis: {
209  switch (usagePage) { /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */
210  case kHIDPage_GenericDesktop:
211  switch (usage) {
212  case kHIDUsage_GD_X:
213  case kHIDUsage_GD_Y:
214  case kHIDUsage_GD_Z:
215  case kHIDUsage_GD_Rx:
216  case kHIDUsage_GD_Ry:
217  case kHIDUsage_GD_Rz:
218  case kHIDUsage_GD_Slider:
219  case kHIDUsage_GD_Dial:
220  case kHIDUsage_GD_Wheel:
221  if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
222  element = (recElement *) SDL_calloc(1, sizeof (recElement));
223  if (element) {
224  pDevice->axes++;
225  headElement = &(pDevice->firstAxis);
226  }
227  }
228  break;
229 
230  case kHIDUsage_GD_Hatswitch:
231  if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) {
232  element = (recElement *) SDL_calloc(1, sizeof (recElement));
233  if (element) {
234  pDevice->hats++;
235  headElement = &(pDevice->firstHat);
236  }
237  }
238  break;
239  case kHIDUsage_GD_DPadUp:
240  case kHIDUsage_GD_DPadDown:
241  case kHIDUsage_GD_DPadRight:
242  case kHIDUsage_GD_DPadLeft:
243  case kHIDUsage_GD_Start:
244  case kHIDUsage_GD_Select:
245  case kHIDUsage_GD_SystemMainMenu:
246  if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
247  element = (recElement *) SDL_calloc(1, sizeof (recElement));
248  if (element) {
249  pDevice->buttons++;
250  headElement = &(pDevice->firstButton);
251  }
252  }
253  break;
254  }
255  break;
256 
257  case kHIDPage_Simulation:
258  switch (usage) {
259  case kHIDUsage_Sim_Rudder:
260  case kHIDUsage_Sim_Throttle:
261  case kHIDUsage_Sim_Accelerator:
262  case kHIDUsage_Sim_Brake:
263  if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
264  element = (recElement *) SDL_calloc(1, sizeof (recElement));
265  if (element) {
266  pDevice->axes++;
267  headElement = &(pDevice->firstAxis);
268  }
269  }
270  break;
271 
272  default:
273  break;
274  }
275  break;
276 
277  case kHIDPage_Button:
278  case kHIDPage_Consumer: /* e.g. 'pause' button on Steelseries MFi gamepads. */
279  if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
280  element = (recElement *) SDL_calloc(1, sizeof (recElement));
281  if (element) {
282  pDevice->buttons++;
283  headElement = &(pDevice->firstButton);
284  }
285  }
286  break;
287 
288  default:
289  break;
290  }
291  }
292  break;
293 
294  case kIOHIDElementTypeCollection: {
295  CFArrayRef array = IOHIDElementGetChildren(refElement);
296  if (array) {
297  AddHIDElements(array, pDevice);
298  }
299  }
300  break;
301 
302  default:
303  break;
304  }
305 
306  if (element && headElement) { /* add to list */
307  recElement *elementPrevious = NULL;
308  recElement *elementCurrent = *headElement;
309  while (elementCurrent && usage >= elementCurrent->usage) {
310  elementPrevious = elementCurrent;
311  elementCurrent = elementCurrent->pNext;
312  }
313  if (elementPrevious) {
314  elementPrevious->pNext = element;
315  } else {
316  *headElement = element;
317  }
318 
319  element->elementRef = refElement;
320  element->usagePage = usagePage;
321  element->usage = usage;
322  element->pNext = elementCurrent;
323 
324  element->minReport = element->min = (SInt32) IOHIDElementGetLogicalMin(refElement);
325  element->maxReport = element->max = (SInt32) IOHIDElementGetLogicalMax(refElement);
326  element->cookie = IOHIDElementGetCookie(refElement);
327 
328  pDevice->elements++;
329  }
330  }
331 }
332 
333 static SDL_bool
334 GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
335 {
336  const Uint16 BUS_USB = 0x03;
337  const Uint16 BUS_BLUETOOTH = 0x05;
338  Sint32 vendor = 0;
339  Sint32 product = 0;
340  Sint32 version = 0;
341  CFTypeRef refCF = NULL;
342  CFArrayRef array = NULL;
343  Uint16 *guid16 = (Uint16 *)pDevice->guid.data;
344 
345  /* get usage page and usage */
346  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
347  if (refCF) {
348  CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage);
349  }
350  if (pDevice->usagePage != kHIDPage_GenericDesktop) {
351  return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */
352  }
353 
354  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey));
355  if (refCF) {
356  CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage);
357  }
358 
359  if ((pDevice->usage != kHIDUsage_GD_Joystick &&
360  pDevice->usage != kHIDUsage_GD_GamePad &&
361  pDevice->usage != kHIDUsage_GD_MultiAxisController)) {
362  return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */
363  }
364 
365  pDevice->deviceRef = hidDevice;
366 
367  /* get device name */
368  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey));
369  if (!refCF) {
370  /* Maybe we can't get "AwesomeJoystick2000", but we can get "Logitech"? */
371  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey));
372  }
373  if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) {
374  SDL_strlcpy(pDevice->product, "Unidentified joystick", sizeof (pDevice->product));
375  }
376 
377  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
378  if (refCF) {
379  CFNumberGetValue(refCF, kCFNumberSInt32Type, &vendor);
380  }
381 
382  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey));
383  if (refCF) {
384  CFNumberGetValue(refCF, kCFNumberSInt32Type, &product);
385  }
386 
387  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVersionNumberKey));
388  if (refCF) {
389  CFNumberGetValue(refCF, kCFNumberSInt32Type, &version);
390  }
391 
392  SDL_memset(pDevice->guid.data, 0, sizeof(pDevice->guid.data));
393 
394  if (vendor && product) {
395  *guid16++ = SDL_SwapLE16(BUS_USB);
396  *guid16++ = 0;
397  *guid16++ = SDL_SwapLE16((Uint16)vendor);
398  *guid16++ = 0;
399  *guid16++ = SDL_SwapLE16((Uint16)product);
400  *guid16++ = 0;
401  *guid16++ = SDL_SwapLE16((Uint16)version);
402  *guid16++ = 0;
403  } else {
404  *guid16++ = SDL_SwapLE16(BUS_BLUETOOTH);
405  *guid16++ = 0;
406  SDL_strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4);
407  }
408 
409  array = IOHIDDeviceCopyMatchingElements(hidDevice, NULL, kIOHIDOptionsTypeNone);
410  if (array) {
411  AddHIDElements(array, pDevice);
412  CFRelease(array);
413  }
414 
415  return SDL_TRUE;
416 }
417 
418 static SDL_bool
419 JoystickAlreadyKnown(IOHIDDeviceRef ioHIDDeviceObject)
420 {
421  recDevice *i;
422  for (i = gpDeviceList; i != NULL; i = i->pNext) {
423  if (i->deviceRef == ioHIDDeviceObject) {
424  return SDL_TRUE;
425  }
426  }
427  return SDL_FALSE;
428 }
429 
430 
431 static void
432 JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject)
433 {
434  recDevice *device;
435  int device_index = 0;
436  io_service_t ioservice;
437 
438  if (res != kIOReturnSuccess) {
439  return;
440  }
441 
442  if (JoystickAlreadyKnown(ioHIDDeviceObject)) {
443  return; /* IOKit sent us a duplicate. */
444  }
445 
446  device = (recDevice *) SDL_calloc(1, sizeof(recDevice));
447 
448  if (!device) {
449  SDL_OutOfMemory();
450  return;
451  }
452 
453  if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
454  SDL_free(device);
455  return; /* not a device we care about, probably. */
456  }
457 
458  if (SDL_IsGameControllerNameAndGUID(device->product, device->guid) &&
459  SDL_ShouldIgnoreGameController(device->product, device->guid)) {
460  SDL_free(device);
461  return;
462  }
463 
464  /* Get notified when this device is disconnected. */
465  IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
466  IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
467 
468  /* Allocate an instance ID for this device */
469  device->instance_id = ++s_joystick_instance_id;
470 
471  /* We have to do some storage of the io_service_t for SDL_HapticOpenFromJoystick */
472  ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
473 #if SDL_HAPTIC_IOKIT
474  if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
475  device->ffservice = ioservice;
476  MacHaptic_MaybeAddDevice(ioservice);
477  }
478 #endif
479 
480  /* Add device to the end of the list */
481  if ( !gpDeviceList ) {
482  gpDeviceList = device;
483  } else {
484  recDevice *curdevice;
485 
486  curdevice = gpDeviceList;
487  while ( curdevice->pNext ) {
488  ++device_index;
489  curdevice = curdevice->pNext;
490  }
491  curdevice->pNext = device;
492  ++device_index; /* bump by one since we counted by pNext. */
493  }
494 
495  SDL_PrivateJoystickAdded(device_index);
496 }
497 
498 static SDL_bool
499 ConfigHIDManager(CFArrayRef matchingArray)
500 {
501  CFRunLoopRef runloop = CFRunLoopGetCurrent();
502 
503  if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
504  return SDL_FALSE;
505  }
506 
507  IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
508  IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback, NULL);
509  IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
510 
511  while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
512  /* no-op. Callback fires once per existing device. */
513  }
514 
515  /* future hotplug events will come through SDL_JOYSTICK_RUNLOOP_MODE now. */
516 
517  return SDL_TRUE; /* good to go. */
518 }
519 
520 
521 static CFDictionaryRef
522 CreateHIDDeviceMatchDictionary(const UInt32 page, const UInt32 usage, int *okay)
523 {
524  CFDictionaryRef retval = NULL;
525  CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
526  CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
527  const void *keys[2] = { (void *) CFSTR(kIOHIDDeviceUsagePageKey), (void *) CFSTR(kIOHIDDeviceUsageKey) };
528  const void *vals[2] = { (void *) pageNumRef, (void *) usageNumRef };
529 
530  if (pageNumRef && usageNumRef) {
531  retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
532  }
533 
534  if (pageNumRef) {
535  CFRelease(pageNumRef);
536  }
537  if (usageNumRef) {
538  CFRelease(usageNumRef);
539  }
540 
541  if (!retval) {
542  *okay = 0;
543  }
544 
545  return retval;
546 }
547 
548 static SDL_bool
549 CreateHIDManager(void)
550 {
551  SDL_bool retval = SDL_FALSE;
552  int okay = 1;
553  const void *vals[] = {
554  (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
555  (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
556  (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
557  };
558  const size_t numElements = SDL_arraysize(vals);
559  CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) : NULL;
560  size_t i;
561 
562  for (i = 0; i < numElements; i++) {
563  if (vals[i]) {
564  CFRelease((CFTypeRef) vals[i]);
565  }
566  }
567 
568  if (array) {
569  hidman = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
570  if (hidman != NULL) {
571  retval = ConfigHIDManager(array);
572  }
573  CFRelease(array);
574  }
575 
576  return retval;
577 }
578 
579 
580 /* Function to scan the system for joysticks.
581  * Joystick 0 should be the system default joystick.
582  * This function should return the number of available joysticks, or -1
583  * on an unrecoverable fatal error.
584  */
585 int
587 {
588  if (gpDeviceList) {
589  return SDL_SetError("Joystick: Device list already inited.");
590  }
591 
592  if (!CreateHIDManager()) {
593  return SDL_SetError("Joystick: Couldn't initialize HID Manager");
594  }
595 
596  return SDL_SYS_NumJoysticks();
597 }
598 
599 /* Function to return the number of joystick devices plugged in right now */
600 int
602 {
603  recDevice *device = gpDeviceList;
604  int nJoySticks = 0;
605 
606  while (device) {
607  if (!device->removed) {
608  nJoySticks++;
609  }
610  device = device->pNext;
611  }
612 
613  return nJoySticks;
614 }
615 
616 /* Function to cause any queued joystick insertions to be processed
617  */
618 void
620 {
621  recDevice *device = gpDeviceList;
622  while (device) {
623  if (device->removed) {
624  device = FreeDevice(device);
625  } else {
626  device = device->pNext;
627  }
628  }
629 
630  /* run this after the checks above so we don't set device->removed and delete the device before
631  SDL_SYS_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device */
632  while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
633  /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
634  }
635 }
636 
637 /* Function to get the device-dependent name of a joystick */
638 const char *
639 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
640 {
641  recDevice *device = GetDeviceForIndex(device_index);
642  return device ? device->product : "UNKNOWN";
643 }
644 
645 /* Function to return the instance id of the joystick at device_index
646  */
648 SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
649 {
650  recDevice *device = GetDeviceForIndex(device_index);
651  return device ? device->instance_id : 0;
652 }
653 
654 /* Function to open a joystick for use.
655  * The joystick to open is specified by the device index.
656  * This should fill the nbuttons and naxes fields of the joystick structure.
657  * It returns 0, or -1 if there is an error.
658  */
659 int
660 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
661 {
662  recDevice *device = GetDeviceForIndex(device_index);
663 
664  joystick->instance_id = device->instance_id;
665  joystick->hwdata = device;
666  joystick->name = device->product;
667 
668  joystick->naxes = device->axes;
669  joystick->nhats = device->hats;
670  joystick->nballs = 0;
671  joystick->nbuttons = device->buttons;
672  return 0;
673 }
674 
675 /* Function to query if the joystick is currently attached
676  * It returns SDL_TRUE if attached, SDL_FALSE otherwise.
677  */
678 SDL_bool
679 SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
680 {
681  return joystick->hwdata != NULL;
682 }
683 
684 /* Function to update the state of a joystick - called as a device poll.
685  * This function shouldn't update the joystick structure directly,
686  * but instead should call SDL_PrivateJoystick*() to deliver events
687  * and update joystick device state.
688  */
689 void
690 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
691 {
692  recDevice *device = joystick->hwdata;
693  recElement *element;
694  SInt32 value, range;
695  int i;
696 
697  if (!device) {
698  return;
699  }
700 
701  if (device->removed) { /* device was unplugged; ignore it. */
702  if (joystick->hwdata) {
703  joystick->force_recentering = SDL_TRUE;
704  joystick->hwdata = NULL;
705  }
706  return;
707  }
708 
709  element = device->firstAxis;
710  i = 0;
711 
712  int goodRead = SDL_FALSE;
713  while (element) {
714  goodRead = GetHIDScaledCalibratedState(device, element, -32768, 32767, &value);
715  if (goodRead) {
716  SDL_PrivateJoystickAxis(joystick, i, value);
717  }
718 
719  element = element->pNext;
720  ++i;
721  }
722 
723  element = device->firstButton;
724  i = 0;
725  while (element) {
726  goodRead = GetHIDElementState(device, element, &value);
727  if (goodRead) {
728  if (value > 1) { /* handle pressure-sensitive buttons */
729  value = 1;
730  }
731  SDL_PrivateJoystickButton(joystick, i, value);
732  }
733 
734  element = element->pNext;
735  ++i;
736  }
737 
738  element = device->firstHat;
739  i = 0;
740 
741  while (element) {
742  Uint8 pos = 0;
743 
744  range = (element->max - element->min + 1);
745  goodRead = GetHIDElementState(device, element, &value);
746  if (goodRead) {
747  value -= element->min;
748  if (range == 4) { /* 4 position hatswitch - scale up value */
749  value *= 2;
750  } else if (range != 8) { /* Neither a 4 nor 8 positions - fall back to default position (centered) */
751  value = -1;
752  }
753  switch (value) {
754  case 0:
755  pos = SDL_HAT_UP;
756  break;
757  case 1:
758  pos = SDL_HAT_RIGHTUP;
759  break;
760  case 2:
761  pos = SDL_HAT_RIGHT;
762  break;
763  case 3:
764  pos = SDL_HAT_RIGHTDOWN;
765  break;
766  case 4:
767  pos = SDL_HAT_DOWN;
768  break;
769  case 5:
770  pos = SDL_HAT_LEFTDOWN;
771  break;
772  case 6:
773  pos = SDL_HAT_LEFT;
774  break;
775  case 7:
776  pos = SDL_HAT_LEFTUP;
777  break;
778  default:
779  /* Every other value is mapped to center. We do that because some
780  * joysticks use 8 and some 15 for this value, and apparently
781  * there are even more variants out there - so we try to be generous.
782  */
783  pos = SDL_HAT_CENTERED;
784  break;
785  }
786 
787  SDL_PrivateJoystickHat(joystick, i, pos);
788  }
789 
790  element = element->pNext;
791  ++i;
792  }
793 }
794 
795 /* Function to close a joystick after use */
796 void
797 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
798 {
799 }
800 
801 /* Function to perform any system-specific joystick related cleanup */
802 void
804 {
805  while (FreeDevice(gpDeviceList)) {
806  /* spin */
807  }
808 
809  if (hidman) {
810  IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
811  IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
812  CFRelease(hidman);
813  hidman = NULL;
814  }
815 }
816 
817 
819 {
820  recDevice *device = GetDeviceForIndex(device_index);
821  SDL_JoystickGUID guid;
822  if (device) {
823  guid = device->guid;
824  } else {
825  SDL_zero(guid);
826  }
827  return guid;
828 }
829 
830 SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
831 {
832  return joystick->hwdata->guid;
833 }
834 
835 #endif /* SDL_JOYSTICK_IOKIT */
836 
837 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_HAT_LEFTDOWN
Definition: SDL_joystick.h:324
#define SDL_strlcpy
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:638
GLuint64EXT * result
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
Definition: SDL_joystick.c:712
#define SDL_HAT_RIGHTUP
Definition: SDL_joystick.h:321
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
Definition: SDL_joystick.c:788
int MacHaptic_MaybeRemoveDevice(io_object_t device)
void SDL_SYS_JoystickQuit(void)
static SDL_JoystickDeviceItem * GetDeviceForIndex(int device_index)
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
Definition: SDL_joystick.c:655
GLuint res
struct recElement * pNext
IOHIDElementCookie cookie
static SDL_AudioDeviceID device
Definition: loopwave.c:37
uint32_t usagePage
SDL_bool retval
#define SDL_HAT_RIGHT
Definition: SDL_joystick.h:318
GLsizeiptr const void GLenum usage
uint32_t usage
#define SDL_HAT_RIGHTDOWN
Definition: SDL_joystick.h:322
Sint32 SDL_JoystickID
Definition: SDL_joystick.h:81
#define SDL_HAT_LEFT
Definition: SDL_joystick.h:320
uint8_t Uint8
Definition: SDL_stdinc.h:157
#define SDL_free
SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
#define TRUE
Definition: edid-parse.c:33
IOHIDElementRef elementRef
GLenum GLint * range
SDL_bool SDL_ShouldIgnoreGameController(const char *name, SDL_JoystickGUID guid)
GLsizei const GLfloat * value
void SDL_PrivateJoystickAdded(int device_index)
Definition: SDL_joystick.c:595
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
const char * SDL_SYS_JoystickNameForDeviceIndex(int device_index)
int32_t Sint32
Definition: SDL_stdinc.h:175
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)
Definition: SDL_x11sym.h:50
SDL_bool SDL_IsGameControllerNameAndGUID(const char *name, SDL_JoystickGUID guid)
int SDL_SYS_JoystickInit(void)
#define NULL
Definition: begin_code.h:164
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:139
unsigned int uint32_t
int SDL_SYS_NumJoysticks(void)
int MacHaptic_MaybeAddDevice(io_object_t device)
#define SDL_SetError
#define SDL_calloc
#define SDL_HAT_LEFTUP
Definition: SDL_joystick.h:323
SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
uint16_t Uint16
Definition: SDL_stdinc.h:169
GLenum array
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:93
int SDL_SYS_JoystickOpen(SDL_Joystick *joystick, int device_index)
#define SDL_HAT_CENTERED
Definition: SDL_joystick.h:316
void SDL_SYS_JoystickClose(SDL_Joystick *joystick)
#define SDL_SwapLE16(X)
Definition: SDL_endian.h:232
#define SDL_HAT_UP
Definition: SDL_joystick.h:317
#define SDL_HAT_DOWN
Definition: SDL_joystick.h:319
SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index)
#define SDL_memset
void SDL_SYS_JoystickDetect(void)
EGLContext ctx
Definition: eglext.h:208