Mac OS X: Look for joystick hotplug in its own CFRunLoop.
authorRyan C. Gordon <icculus@icculus.org>
Thu, 24 Apr 2014 23:24:48 -0400
changeset 8739153077041e4b
parent 8738 354dabd2cb58
child 8740 fbe394d76273
Mac OS X: Look for joystick hotplug in its own CFRunLoop.

This allows the joystick hotplug to function without the main event loop
(specifically: without SDL_INIT_VIDEO), and moves explicit polling for
joysticks where it belongs at the low-level: in SDL_SYS_JoystickDetect().

This lets apps call SDL_JoystickUpdate() to get hotplug events and keep
SDL_NumJoysticks() correct, as expected. As SDL_PumpEvents() (and
SDL_PollEvents, etc) calls SDL_JoystickUpdate(), existing apps will function
as before.

Thanks to "raskie" on the forums for pointing this out!
src/joystick/darwin/SDL_sysjoystick.c
     1.1 --- a/src/joystick/darwin/SDL_sysjoystick.c	Thu Apr 24 21:05:51 2014 -0700
     1.2 +++ b/src/joystick/darwin/SDL_sysjoystick.c	Thu Apr 24 23:24:48 2014 -0400
     1.3 @@ -38,6 +38,8 @@
     1.4  #include "../../events/SDL_events_c.h"
     1.5  #endif
     1.6  
     1.7 +#define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick")
     1.8 +
     1.9  /* The base object of the HID Manager API */
    1.10  static IOHIDManagerRef hidman = NULL;
    1.11  
    1.12 @@ -67,6 +69,11 @@
    1.13  {
    1.14      recDevice *pDeviceNext = NULL;
    1.15      if (removeDevice) {
    1.16 +        if (removeDevice->deviceRef) {
    1.17 +            IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
    1.18 +            removeDevice->deviceRef = NULL;
    1.19 +        }
    1.20 +
    1.21          /* save next device prior to disposing of this device */
    1.22          pDeviceNext = removeDevice->pNext;
    1.23  
    1.24 @@ -378,7 +385,7 @@
    1.25  
    1.26      /* Get notified when this device is disconnected. */
    1.27      IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
    1.28 -    IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    1.29 +    IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
    1.30  
    1.31      /* Allocate an instance ID for this device */
    1.32      device->instance_id = ++s_joystick_instance_id;
    1.33 @@ -420,25 +427,19 @@
    1.34  {
    1.35      CFRunLoopRef runloop = CFRunLoopGetCurrent();
    1.36  
    1.37 -    /* Run in a custom RunLoop mode just while initializing,
    1.38 -       so we can detect sticks without messing with everything else. */
    1.39 -    CFStringRef tempRunLoopMode = CFSTR("SDLJoystickInit");
    1.40 -
    1.41      if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
    1.42          return SDL_FALSE;
    1.43      }
    1.44  
    1.45      IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback, NULL);
    1.46 -    IOHIDManagerScheduleWithRunLoop(hidman, runloop, tempRunLoopMode);
    1.47 +    IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
    1.48      IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
    1.49  
    1.50 -    while (CFRunLoopRunInMode(tempRunLoopMode,0,TRUE)==kCFRunLoopRunHandledSource) {
    1.51 +    while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
    1.52          /* no-op. Callback fires once per existing device. */
    1.53      }
    1.54  
    1.55 -    /* Put this in the normal RunLoop mode now, for future hotplug events. */
    1.56 -    IOHIDManagerUnscheduleFromRunLoop(hidman, runloop, tempRunLoopMode);
    1.57 -    IOHIDManagerScheduleWithRunLoop(hidman, runloop, kCFRunLoopDefaultMode);
    1.58 +    /* future hotplug events will come through SDL_JOYSTICK_RUNLOOP_MODE now. */
    1.59  
    1.60      return SDL_TRUE;  /* good to go. */
    1.61  }
    1.62 @@ -544,6 +545,10 @@
    1.63  void
    1.64  SDL_SYS_JoystickDetect()
    1.65  {
    1.66 +    while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
    1.67 +        /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
    1.68 +    }
    1.69 +
    1.70      if (s_bDeviceAdded || s_bDeviceRemoved) {
    1.71          recDevice *device = gpDeviceList;
    1.72          s_bDeviceAdded = SDL_FALSE;
    1.73 @@ -793,6 +798,7 @@
    1.74      }
    1.75  
    1.76      if (hidman) {
    1.77 +        IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
    1.78          IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
    1.79          CFRelease(hidman);
    1.80          hidman = NULL;