mirror of https://github.com/encounter/SDL.git
Use IOHIDManagerRegisterDeviceRemovalCallback() to monitor for HID removal
The function we currently use, IOHIDDeviceRegisterRemovalCallback(), often fails on Catalina with a "__CFRunLoopModeFindSourceForMachPort returned NULL" error message. Once a removal callback is missed, we will eventually crash when the joystick is closed attempting to use the invalid IOHIDDeviceRef. https://forums.developer.apple.com/thread/124444
This commit is contained in:
parent
dd5987e80a
commit
f21e172767
|
@ -372,6 +372,27 @@ static int make_path(IOHIDDeviceRef device, char *buf, size_t len)
|
|||
return res+1;
|
||||
}
|
||||
|
||||
static void hid_device_removal_callback(void *context, IOReturn result,
|
||||
void *sender, IOHIDDeviceRef hid_ref)
|
||||
{
|
||||
// The device removal callback is sometimes called even after being
|
||||
// unregistered, leading to a crash when trying to access fields in
|
||||
// the already freed hid_device. We keep a linked list of all created
|
||||
// hid_device's so that the one being removed can be checked against
|
||||
// the list to see if it really hasn't been closed yet and needs to
|
||||
// be dealt with here.
|
||||
struct hid_device_list_node *node = device_list;
|
||||
while (node) {
|
||||
if (node->dev->device_handle == hid_ref) {
|
||||
node->dev->disconnected = 1;
|
||||
CFRunLoopStop(node->dev->run_loop);
|
||||
break;
|
||||
}
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */
|
||||
static int init_hid_manager(void)
|
||||
{
|
||||
|
@ -381,6 +402,7 @@ static int init_hid_manager(void)
|
|||
if (hid_mgr) {
|
||||
IOHIDManagerSetDeviceMatching(hid_mgr, NULL);
|
||||
IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
IOHIDManagerRegisterDeviceRemovalCallback(hid_mgr, hid_device_removal_callback, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -569,30 +591,6 @@ hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short pr
|
|||
return handle;
|
||||
}
|
||||
|
||||
static void hid_device_removal_callback(void *context, IOReturn result,
|
||||
void *sender)
|
||||
{
|
||||
/* Stop the Run Loop for this device. */
|
||||
hid_device *dev = (hid_device *)context;
|
||||
|
||||
// The device removal callback is sometimes called even after being
|
||||
// unregistered, leading to a crash when trying to access fields in
|
||||
// the already freed hid_device. We keep a linked list of all created
|
||||
// hid_device's so that the one being removed can be checked against
|
||||
// the list to see if it really hasn't been closed yet and needs to
|
||||
// be dealt with here.
|
||||
struct hid_device_list_node *node = device_list;
|
||||
while (node) {
|
||||
if (node->dev == dev) {
|
||||
dev->disconnected = 1;
|
||||
CFRunLoopStop(dev->run_loop);
|
||||
break;
|
||||
}
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* The Run Loop calls this function for each input report received.
|
||||
This function puts the data into a linked list to be picked up by
|
||||
hid_read(). */
|
||||
|
@ -766,7 +764,6 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive)
|
|||
IOHIDDeviceRegisterInputReportCallback(
|
||||
os_dev, dev->input_report_buf, dev->max_input_report_len,
|
||||
&hid_report_callback, dev);
|
||||
IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev);
|
||||
|
||||
struct hid_device_list_node *node = (struct hid_device_list_node *)calloc(1, sizeof(struct hid_device_list_node));
|
||||
node->dev = dev;
|
||||
|
@ -1042,7 +1039,6 @@ void HID_API_EXPORT hid_close(hid_device *dev)
|
|||
IOHIDDeviceRegisterInputReportCallback(
|
||||
dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
|
||||
NULL, dev);
|
||||
IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev);
|
||||
IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode);
|
||||
IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue