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!
This commit is contained in:
Ryan C. Gordon 2014-04-24 23:24:48 -04:00
parent 8b28009105
commit 5d7562c7e2
1 changed files with 16 additions and 10 deletions

View File

@ -38,6 +38,8 @@
#include "../../events/SDL_events_c.h" #include "../../events/SDL_events_c.h"
#endif #endif
#define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick")
/* The base object of the HID Manager API */ /* The base object of the HID Manager API */
static IOHIDManagerRef hidman = NULL; static IOHIDManagerRef hidman = NULL;
@ -67,6 +69,11 @@ FreeDevice(recDevice *removeDevice)
{ {
recDevice *pDeviceNext = NULL; recDevice *pDeviceNext = NULL;
if (removeDevice) { if (removeDevice) {
if (removeDevice->deviceRef) {
IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
removeDevice->deviceRef = NULL;
}
/* save next device prior to disposing of this device */ /* save next device prior to disposing of this device */
pDeviceNext = removeDevice->pNext; pDeviceNext = removeDevice->pNext;
@ -378,7 +385,7 @@ JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDevic
/* Get notified when this device is disconnected. */ /* Get notified when this device is disconnected. */
IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device); IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
/* Allocate an instance ID for this device */ /* Allocate an instance ID for this device */
device->instance_id = ++s_joystick_instance_id; device->instance_id = ++s_joystick_instance_id;
@ -420,25 +427,19 @@ ConfigHIDManager(CFArrayRef matchingArray)
{ {
CFRunLoopRef runloop = CFRunLoopGetCurrent(); CFRunLoopRef runloop = CFRunLoopGetCurrent();
/* Run in a custom RunLoop mode just while initializing,
so we can detect sticks without messing with everything else. */
CFStringRef tempRunLoopMode = CFSTR("SDLJoystickInit");
if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) { if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
return SDL_FALSE; return SDL_FALSE;
} }
IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback, NULL); IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback, NULL);
IOHIDManagerScheduleWithRunLoop(hidman, runloop, tempRunLoopMode); IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray); IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
while (CFRunLoopRunInMode(tempRunLoopMode,0,TRUE)==kCFRunLoopRunHandledSource) { while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
/* no-op. Callback fires once per existing device. */ /* no-op. Callback fires once per existing device. */
} }
/* Put this in the normal RunLoop mode now, for future hotplug events. */ /* future hotplug events will come through SDL_JOYSTICK_RUNLOOP_MODE now. */
IOHIDManagerUnscheduleFromRunLoop(hidman, runloop, tempRunLoopMode);
IOHIDManagerScheduleWithRunLoop(hidman, runloop, kCFRunLoopDefaultMode);
return SDL_TRUE; /* good to go. */ return SDL_TRUE; /* good to go. */
} }
@ -544,6 +545,10 @@ SDL_SYS_NumJoysticks()
void void
SDL_SYS_JoystickDetect() SDL_SYS_JoystickDetect()
{ {
while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
/* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
}
if (s_bDeviceAdded || s_bDeviceRemoved) { if (s_bDeviceAdded || s_bDeviceRemoved) {
recDevice *device = gpDeviceList; recDevice *device = gpDeviceList;
s_bDeviceAdded = SDL_FALSE; s_bDeviceAdded = SDL_FALSE;
@ -793,6 +798,7 @@ SDL_SYS_JoystickQuit(void)
} }
if (hidman) { if (hidman) {
IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone); IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
CFRelease(hidman); CFRelease(hidman);
hidman = NULL; hidman = NULL;