Fix for Bug 5034 - Replugging in a controller crashes on macOS in SDL 2.0.12 This is a multi-part fix, and is the 2nd attempt at a fix for Bug 5034. Here are the problems being addressed: 1. On macOS 10.14.x and earlier, trying to call IOHIDDeviceUnscheduleFromRunLoop without a prior, paired call to IOHIDDeviceScheduleWithRunLoop, appears to lead to a crash. A per-device flag has been added to make sure that these calls are paired. 2. DARWIN_JoystickDetect was free'ing its SDL_joystick's hwdata field (via FreeDevice) without setting it to NULL, and DARWIN_JoystickRumble wasn't checking for a NULL hwdata. FreeDevice will now set hwdata to NULL and DARWIN_JoystickRumble will check for a NULL hwdata.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
diff --git a/src/joystick/darwin/SDL_sysjoystick.c b/src/joystick/darwin/SDL_sysjoystick.c
index 44d4136..86debc3 100644
--- a/src/joystick/darwin/SDL_sysjoystick.c
+++ b/src/joystick/darwin/SDL_sysjoystick.c
@@ -127,11 +127,28 @@ FreeDevice(recDevice *removeDevice)
recDevice *pDeviceNext = NULL;
if (removeDevice) {
if (removeDevice->deviceRef) {
- IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
+ if (removeDevice->runLoopAttached) {
+ /* Calling IOHIDDeviceUnscheduleFromRunLoop without a prior,
+ * paired call to IOHIDDeviceScheduleWithRunLoop can lead
+ * to crashes in MacOS 10.14.x and earlier. This doesn't
+ * appear to be a problem in MacOS 10.15.x, but we'll
+ * do it anyways. (Part-of fix for Bug 5034)
+ */
+ IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
+ }
CFRelease(removeDevice->deviceRef);
removeDevice->deviceRef = NULL;
}
+ /* clear out any reference to removeDevice from an associated,
+ * live instance of SDL_Joystick (Part-of fix for Bug 5034)
+ */
+ SDL_LockJoysticks();
+ if (removeDevice->joystick) {
+ removeDevice->joystick->hwdata = NULL;
+ }
+ SDL_UnlockJoysticks();
+
/* save next device prior to disposing of this device */
pDeviceNext = removeDevice->pNext;
@@ -398,6 +415,7 @@ AddHIDElement(const void *value, void *parameter)
}
}
+
static SDL_bool
GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
{
@@ -552,6 +570,7 @@ JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDevic
/* Get notified when this device is disconnected. */
IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
+ device->runLoopAttached = SDL_TRUE;
/* Allocate an instance ID for this device */
device->instance_id = SDL_GetNextJoystickInstanceID();
@@ -760,6 +779,7 @@ DARWIN_JoystickOpen(SDL_Joystick * joystick, int device_index)
joystick->instance_id = device->instance_id;
joystick->hwdata = device;
+ device->joystick = joystick;
joystick->name = device->product;
joystick->naxes = device->axes;
@@ -870,6 +890,10 @@ DARWIN_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint
/* Scale and average the two rumble strengths */
Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
+
+ if (!device) {
+ return SDL_SetError("Rumble failed, device disconnected");
+ }
if (!device->ffservice) {
return SDL_Unsupported();
@@ -1007,6 +1031,10 @@ DARWIN_JoystickUpdate(SDL_Joystick * joystick)
static void
DARWIN_JoystickClose(SDL_Joystick * joystick)
{
+ recDevice *device = joystick->hwdata;
+ if (device) {
+ device->joystick = NULL;
+ }
}
static void
diff --git a/src/joystick/darwin/SDL_sysjoystick_c.h b/src/joystick/darwin/SDL_sysjoystick_c.h
index 45a5793..4505ecc 100644
--- a/src/joystick/darwin/SDL_sysjoystick_c.h
+++ b/src/joystick/darwin/SDL_sysjoystick_c.h
@@ -66,6 +66,8 @@ struct joystick_hwdata
recElement *firstHat;
SDL_bool removed;
+ SDL_Joystick *joystick;
+ SDL_bool runLoopAttached; /* is 'deviceRef' attached to a CFRunLoop? */
int instance_id;
SDL_JoystickGUID guid;