SDL  2.0
SDL_sysjoystick.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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  if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
252  element = (recElement *) SDL_calloc(1, sizeof (recElement));
253  if (element) {
254  pDevice->axes++;
255  headElement = &(pDevice->firstAxis);
256  }
257  }
258  break;
259 
260  default:
261  break;
262  }
263  break;
264 
265  case kHIDPage_Button:
266  case kHIDPage_Consumer: /* e.g. 'pause' button on Steelseries MFi gamepads. */
267  if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
268  element = (recElement *) SDL_calloc(1, sizeof (recElement));
269  if (element) {
270  pDevice->buttons++;
271  headElement = &(pDevice->firstButton);
272  }
273  }
274  break;
275 
276  default:
277  break;
278  }
279  }
280  break;
281 
282  case kIOHIDElementTypeCollection: {
283  CFArrayRef array = IOHIDElementGetChildren(refElement);
284  if (array) {
285  AddHIDElements(array, pDevice);
286  }
287  }
288  break;
289 
290  default:
291  break;
292  }
293 
294  if (element && headElement) { /* add to list */
295  recElement *elementPrevious = NULL;
296  recElement *elementCurrent = *headElement;
297  while (elementCurrent && usage >= elementCurrent->usage) {
298  elementPrevious = elementCurrent;
299  elementCurrent = elementCurrent->pNext;
300  }
301  if (elementPrevious) {
302  elementPrevious->pNext = element;
303  } else {
304  *headElement = element;
305  }
306 
307  element->elementRef = refElement;
308  element->usagePage = usagePage;
309  element->usage = usage;
310  element->pNext = elementCurrent;
311 
312  element->minReport = element->min = (SInt32) IOHIDElementGetLogicalMin(refElement);
313  element->maxReport = element->max = (SInt32) IOHIDElementGetLogicalMax(refElement);
314  element->cookie = IOHIDElementGetCookie(refElement);
315 
316  pDevice->elements++;
317  }
318  }
319 }
320 
321 static SDL_bool
322 GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
323 {
324  Uint32 *guid32 = NULL;
325  CFTypeRef refCF = NULL;
326  CFArrayRef array = NULL;
327 
328  /* get usage page and usage */
329  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
330  if (refCF) {
331  CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage);
332  }
333  if (pDevice->usagePage != kHIDPage_GenericDesktop) {
334  return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */
335  }
336 
337  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey));
338  if (refCF) {
339  CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage);
340  }
341 
342  if ((pDevice->usage != kHIDUsage_GD_Joystick &&
343  pDevice->usage != kHIDUsage_GD_GamePad &&
344  pDevice->usage != kHIDUsage_GD_MultiAxisController)) {
345  return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */
346  }
347 
348  pDevice->deviceRef = hidDevice;
349 
350  /* get device name */
351  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey));
352  if (!refCF) {
353  /* Maybe we can't get "AwesomeJoystick2000", but we can get "Logitech"? */
354  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey));
355  }
356  if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) {
357  SDL_strlcpy(pDevice->product, "Unidentified joystick", sizeof (pDevice->product));
358  }
359 
360  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
361  if (refCF) {
362  CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->guid.data[0]);
363  }
364 
365  refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey));
366  if (refCF) {
367  CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->guid.data[8]);
368  }
369 
370  /* Check to make sure we have a vendor and product ID
371  If we don't, use the same algorithm as the Linux code for Bluetooth devices */
372  guid32 = (Uint32*)pDevice->guid.data;
373  if (!guid32[0] && !guid32[1]) {
374  /* If we don't have a vendor and product ID this is probably a Bluetooth device */
375  const Uint16 BUS_BLUETOOTH = 0x05;
376  Uint16 *guid16 = (Uint16 *)guid32;
377  *guid16++ = BUS_BLUETOOTH;
378  *guid16++ = 0;
379  SDL_strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4);
380  }
381 
382  array = IOHIDDeviceCopyMatchingElements(hidDevice, NULL, kIOHIDOptionsTypeNone);
383  if (array) {
384  AddHIDElements(array, pDevice);
385  CFRelease(array);
386  }
387 
388  return SDL_TRUE;
389 }
390 
391 static SDL_bool
392 JoystickAlreadyKnown(IOHIDDeviceRef ioHIDDeviceObject)
393 {
394  recDevice *i;
395  for (i = gpDeviceList; i != NULL; i = i->pNext) {
396  if (i->deviceRef == ioHIDDeviceObject) {
397  return SDL_TRUE;
398  }
399  }
400  return SDL_FALSE;
401 }
402 
403 
404 static void
405 JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject)
406 {
407  recDevice *device;
408  int device_index = 0;
409  io_service_t ioservice;
410 
411  if (res != kIOReturnSuccess) {
412  return;
413  }
414 
415  if (JoystickAlreadyKnown(ioHIDDeviceObject)) {
416  return; /* IOKit sent us a duplicate. */
417  }
418 
419  device = (recDevice *) SDL_calloc(1, sizeof(recDevice));
420 
421  if (!device) {
422  SDL_OutOfMemory();
423  return;
424  }
425 
426  if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
427  SDL_free(device);
428  return; /* not a device we care about, probably. */
429  }
430 
431  /* Get notified when this device is disconnected. */
432  IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
433  IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
434 
435  /* Allocate an instance ID for this device */
436  device->instance_id = ++s_joystick_instance_id;
437 
438  /* We have to do some storage of the io_service_t for SDL_HapticOpenFromJoystick */
439  ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
440 #if SDL_HAPTIC_IOKIT
441  if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
442  device->ffservice = ioservice;
443  MacHaptic_MaybeAddDevice(ioservice);
444  }
445 #endif
446 
447  /* Add device to the end of the list */
448  if ( !gpDeviceList ) {
449  gpDeviceList = device;
450  } else {
451  recDevice *curdevice;
452 
453  curdevice = gpDeviceList;
454  while ( curdevice->pNext ) {
455  ++device_index;
456  curdevice = curdevice->pNext;
457  }
458  curdevice->pNext = device;
459  ++device_index; /* bump by one since we counted by pNext. */
460  }
461 
462  SDL_PrivateJoystickAdded(device_index);
463 }
464 
465 static SDL_bool
466 ConfigHIDManager(CFArrayRef matchingArray)
467 {
468  CFRunLoopRef runloop = CFRunLoopGetCurrent();
469 
470  if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
471  return SDL_FALSE;
472  }
473 
474  IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
475  IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback, NULL);
476  IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
477 
478  while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
479  /* no-op. Callback fires once per existing device. */
480  }
481 
482  /* future hotplug events will come through SDL_JOYSTICK_RUNLOOP_MODE now. */
483 
484  return SDL_TRUE; /* good to go. */
485 }
486 
487 
488 static CFDictionaryRef
489 CreateHIDDeviceMatchDictionary(const UInt32 page, const UInt32 usage, int *okay)
490 {
491  CFDictionaryRef retval = NULL;
492  CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
493  CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
494  const void *keys[2] = { (void *) CFSTR(kIOHIDDeviceUsagePageKey), (void *) CFSTR(kIOHIDDeviceUsageKey) };
495  const void *vals[2] = { (void *) pageNumRef, (void *) usageNumRef };
496 
497  if (pageNumRef && usageNumRef) {
498  retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
499  }
500 
501  if (pageNumRef) {
502  CFRelease(pageNumRef);
503  }
504  if (usageNumRef) {
505  CFRelease(usageNumRef);
506  }
507 
508  if (!retval) {
509  *okay = 0;
510  }
511 
512  return retval;
513 }
514 
515 static SDL_bool
516 CreateHIDManager(void)
517 {
518  SDL_bool retval = SDL_FALSE;
519  int okay = 1;
520  const void *vals[] = {
521  (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
522  (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
523  (void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
524  };
525  const size_t numElements = SDL_arraysize(vals);
526  CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) : NULL;
527  size_t i;
528 
529  for (i = 0; i < numElements; i++) {
530  if (vals[i]) {
531  CFRelease((CFTypeRef) vals[i]);
532  }
533  }
534 
535  if (array) {
536  hidman = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
537  if (hidman != NULL) {
538  retval = ConfigHIDManager(array);
539  }
540  CFRelease(array);
541  }
542 
543  return retval;
544 }
545 
546 
547 /* Function to scan the system for joysticks.
548  * Joystick 0 should be the system default joystick.
549  * This function should return the number of available joysticks, or -1
550  * on an unrecoverable fatal error.
551  */
552 int
554 {
555  if (gpDeviceList) {
556  return SDL_SetError("Joystick: Device list already inited.");
557  }
558 
559  if (!CreateHIDManager()) {
560  return SDL_SetError("Joystick: Couldn't initialize HID Manager");
561  }
562 
563  return SDL_SYS_NumJoysticks();
564 }
565 
566 /* Function to return the number of joystick devices plugged in right now */
567 int
569 {
570  recDevice *device = gpDeviceList;
571  int nJoySticks = 0;
572 
573  while (device) {
574  if (!device->removed) {
575  nJoySticks++;
576  }
577  device = device->pNext;
578  }
579 
580  return nJoySticks;
581 }
582 
583 /* Function to cause any queued joystick insertions to be processed
584  */
585 void
587 {
588  recDevice *device = gpDeviceList;
589  while (device) {
590  if (device->removed) {
591  device = FreeDevice(device);
592  } else {
593  device = device->pNext;
594  }
595  }
596 
597  // run this after the checks above so we don't set device->removed and delete the device before
598  // SDL_SYS_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device
599  while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
600  /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
601  }
602 }
603 
604 /* Function to get the device-dependent name of a joystick */
605 const char *
606 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
607 {
608  recDevice *device = GetDeviceForIndex(device_index);
609  return device ? device->product : "UNKNOWN";
610 }
611 
612 /* Function to return the instance id of the joystick at device_index
613  */
615 SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
616 {
617  recDevice *device = GetDeviceForIndex(device_index);
618  return device ? device->instance_id : 0;
619 }
620 
621 /* Function to open a joystick for use.
622  * The joystick to open is specified by the device index.
623  * This should fill the nbuttons and naxes fields of the joystick structure.
624  * It returns 0, or -1 if there is an error.
625  */
626 int
627 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
628 {
629  recDevice *device = GetDeviceForIndex(device_index);
630 
631  joystick->instance_id = device->instance_id;
632  joystick->hwdata = device;
633  joystick->name = device->product;
634 
635  joystick->naxes = device->axes;
636  joystick->nhats = device->hats;
637  joystick->nballs = 0;
638  joystick->nbuttons = device->buttons;
639  return 0;
640 }
641 
642 /* Function to query if the joystick is currently attached
643  * It returns SDL_TRUE if attached, SDL_FALSE otherwise.
644  */
645 SDL_bool
646 SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
647 {
648  return joystick->hwdata != NULL;
649 }
650 
651 /* Function to update the state of a joystick - called as a device poll.
652  * This function shouldn't update the joystick structure directly,
653  * but instead should call SDL_PrivateJoystick*() to deliver events
654  * and update joystick device state.
655  */
656 void
657 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
658 {
659  recDevice *device = joystick->hwdata;
660  recElement *element;
661  SInt32 value, range;
662  int i;
663 
664  if (!device) {
665  return;
666  }
667 
668  if (device->removed) { /* device was unplugged; ignore it. */
669  if (joystick->hwdata) {
670  joystick->force_recentering = SDL_TRUE;
671  joystick->hwdata = NULL;
672  }
673  return;
674  }
675 
676  element = device->firstAxis;
677  i = 0;
678  while (element) {
679  value = GetHIDScaledCalibratedState(device, element, -32768, 32767);
680  if (value != joystick->axes[i]) {
681  SDL_PrivateJoystickAxis(joystick, i, value);
682  }
683  element = element->pNext;
684  ++i;
685  }
686 
687  element = device->firstButton;
688  i = 0;
689  while (element) {
690  value = GetHIDElementState(device, element);
691  if (value > 1) { /* handle pressure-sensitive buttons */
692  value = 1;
693  }
694  if (value != joystick->buttons[i]) {
695  SDL_PrivateJoystickButton(joystick, i, value);
696  }
697  element = element->pNext;
698  ++i;
699  }
700 
701  element = device->firstHat;
702  i = 0;
703  while (element) {
704  Uint8 pos = 0;
705 
706  range = (element->max - element->min + 1);
707  value = GetHIDElementState(device, element) - element->min;
708  if (range == 4) { /* 4 position hatswitch - scale up value */
709  value *= 2;
710  } else if (range != 8) { /* Neither a 4 nor 8 positions - fall back to default position (centered) */
711  value = -1;
712  }
713  switch (value) {
714  case 0:
715  pos = SDL_HAT_UP;
716  break;
717  case 1:
718  pos = SDL_HAT_RIGHTUP;
719  break;
720  case 2:
721  pos = SDL_HAT_RIGHT;
722  break;
723  case 3:
724  pos = SDL_HAT_RIGHTDOWN;
725  break;
726  case 4:
727  pos = SDL_HAT_DOWN;
728  break;
729  case 5:
730  pos = SDL_HAT_LEFTDOWN;
731  break;
732  case 6:
733  pos = SDL_HAT_LEFT;
734  break;
735  case 7:
736  pos = SDL_HAT_LEFTUP;
737  break;
738  default:
739  /* Every other value is mapped to center. We do that because some
740  * joysticks use 8 and some 15 for this value, and apparently
741  * there are even more variants out there - so we try to be generous.
742  */
743  pos = SDL_HAT_CENTERED;
744  break;
745  }
746 
747  if (pos != joystick->hats[i]) {
748  SDL_PrivateJoystickHat(joystick, i, pos);
749  }
750 
751  element = element->pNext;
752  ++i;
753  }
754 }
755 
756 /* Function to close a joystick after use */
757 void
758 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
759 {
760 }
761 
762 /* Function to perform any system-specific joystick related cleanup */
763 void
765 {
766  while (FreeDevice(gpDeviceList)) {
767  /* spin */
768  }
769 
770  if (hidman) {
771  IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
772  IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
773  CFRelease(hidman);
774  hidman = NULL;
775  }
776 }
777 
778 
780 {
781  recDevice *device = GetDeviceForIndex(device_index);
782  SDL_JoystickGUID guid;
783  if (device) {
784  guid = device->guid;
785  } else {
786  SDL_zero(guid);
787  }
788  return guid;
789 }
790 
791 SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
792 {
793  return joystick->hwdata->guid;
794 }
795 
796 #endif /* SDL_JOYSTICK_IOKIT */
797 
798 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_HAT_LEFTDOWN
Definition: SDL_joystick.h:215
int MacHaptic_MaybeRemoveDevice(io_object_t device)
#define SDL_strlcpy
int MacHaptic_MaybeAddDevice(io_object_t device)
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:547
GLuint64EXT * result
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
Definition: SDL_joystick.c:608
int SDL_SYS_NumJoysticks()
#define SDL_HAT_RIGHTUP
Definition: SDL_joystick.h:212
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
Definition: SDL_joystick.c:684
void SDL_SYS_JoystickQuit(void)
uint32_t Uint32
An unsigned 32-bit integer type.
Definition: SDL_stdinc.h:161
static SDL_JoystickDeviceItem * GetDeviceForIndex(int device_index)
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
Definition: SDL_joystick.c:567
GLuint res
struct recElement * pNext
IOHIDElementCookie cookie
uint32_t usagePage
SDL_bool retval
#define SDL_HAT_RIGHT
Definition: SDL_joystick.h:209
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:213
Sint32 SDL_JoystickID
Definition: SDL_joystick.h:72
#define SDL_HAT_LEFT
Definition: SDL_joystick.h:211
GLsizei const GLfloat * value
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:143
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
void SDL_SYS_JoystickDetect()
void SDL_PrivateJoystickAdded(int device_index)
Definition: SDL_joystick.c:501
#define SDL_zero(x)
Definition: SDL_stdinc.h:361
const char * SDL_SYS_JoystickNameForDeviceIndex(int device_index)
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
int SDL_SYS_JoystickInit(void)
#define NULL
Definition: begin_code.h:143
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:130
unsigned int uint32_t
#define SDL_SetError
#define SDL_HAT_LEFTUP
Definition: SDL_joystick.h:214
SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
uint16_t Uint16
An unsigned 16-bit integer type.
Definition: SDL_stdinc.h:151
GLenum array
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:90
int SDL_SYS_JoystickOpen(SDL_Joystick *joystick, int device_index)
#define SDL_HAT_CENTERED
Definition: SDL_joystick.h:207
void SDL_SYS_JoystickClose(SDL_Joystick *joystick)
#define SDL_HAT_UP
Definition: SDL_joystick.h:208
#define SDL_HAT_DOWN
Definition: SDL_joystick.h:210
SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index)
struct joystick_hwdata * pNext