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