Skip to content
Permalink
Browse files

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!
  • Loading branch information
icculus committed Apr 25, 2014
1 parent 8b28009 commit 5d7562c7e26b0970c2db38cbda69b96526115612
Showing with 16 additions and 10 deletions.
  1. +16 −10 src/joystick/darwin/SDL_sysjoystick.c
@@ -38,6 +38,8 @@
#include "../../events/SDL_events_c.h"
#endif

#define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick")

/* The base object of the HID Manager API */
static IOHIDManagerRef hidman = NULL;

@@ -67,6 +69,11 @@ FreeDevice(recDevice *removeDevice)
{
recDevice *pDeviceNext = NULL;
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 */
pDeviceNext = removeDevice->pNext;

@@ -378,7 +385,7 @@ JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDevic

/* Get notified when this device is disconnected. */
IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);

/* Allocate an instance ID for this device */
device->instance_id = ++s_joystick_instance_id;
@@ -420,25 +427,19 @@ ConfigHIDManager(CFArrayRef matchingArray)
{
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) {
return SDL_FALSE;
}

IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback, NULL);
IOHIDManagerScheduleWithRunLoop(hidman, runloop, tempRunLoopMode);
IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
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. */
}

/* Put this in the normal RunLoop mode now, for future hotplug events. */
IOHIDManagerUnscheduleFromRunLoop(hidman, runloop, tempRunLoopMode);
IOHIDManagerScheduleWithRunLoop(hidman, runloop, kCFRunLoopDefaultMode);
/* future hotplug events will come through SDL_JOYSTICK_RUNLOOP_MODE now. */

return SDL_TRUE; /* good to go. */
}
@@ -544,6 +545,10 @@ SDL_SYS_NumJoysticks()
void
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) {
recDevice *device = gpDeviceList;
s_bDeviceAdded = SDL_FALSE;
@@ -793,6 +798,7 @@ SDL_SYS_JoystickQuit(void)
}

if (hidman) {
IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
CFRelease(hidman);
hidman = NULL;

0 comments on commit 5d7562c

Please sign in to comment.