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