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