Skip to content

Commit 17dddee

Browse files
treamologyrdb
authored andcommitted
device: Fix 10.15 segfault on USB hotplug
For some reason, IOHIDDeviceRegisterRemovalCallback() no longer works on 10.15+, so an app will crash once trying to poll a device that doesn't exist anymore. Thankfully, there is the alternative solution of using IOHIDManagerRegisterDeviceRemovalCallback(). This just required a little rearranging of the callback code, as well as keeping track of the connection between IOHIDDeviceRefs and IOKitInputDevices so we actually know which device to remove. Closes panda3d#847
1 parent 8222255 commit 17dddee

File tree

4 files changed

+41
-39
lines changed

4 files changed

+41
-39
lines changed

panda/src/device/ioKitInputDevice.cxx

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,6 @@
2121
#include "gamepadButton.h"
2222
#include "mouseButton.h"
2323

24-
static void removal_callback(void *ctx, IOReturn result, void *sender) {
25-
// We need to hold a reference to this because it may otherwise be destroyed
26-
// during the call to on_remove().
27-
PT(IOKitInputDevice) input_device = (IOKitInputDevice *)ctx;
28-
nassertv(input_device != nullptr);
29-
nassertv(input_device->test_ref_count_integrity());
30-
input_device->on_remove();
31-
}
32-
3324
/**
3425
* Protected constructor.
3526
*/
@@ -137,7 +128,6 @@ IOKitInputDevice(IOHIDDeviceRef device) :
137128
}
138129

139130
_is_connected = true;
140-
IOHIDDeviceRegisterRemovalCallback(device, removal_callback, this);
141131
}
142132

143133
/**
@@ -147,31 +137,6 @@ IOKitInputDevice::
147137
~IOKitInputDevice() {
148138
}
149139

150-
/**
151-
* The nonstatic version of on_remove_device.
152-
*/
153-
void IOKitInputDevice::
154-
on_remove() {
155-
{
156-
LightMutexHolder holder(_lock);
157-
if (!_is_connected) {
158-
return;
159-
}
160-
_is_connected = false;
161-
}
162-
163-
if (device_cat.is_debug()) {
164-
device_cat.debug()
165-
<< "Removed input device " << *this << "\n";
166-
}
167-
168-
IOHIDDeviceClose(_device, kIOHIDOptionsTypeNone);
169-
170-
InputDeviceManager *mgr = InputDeviceManager::get_global_ptr();
171-
nassertv(mgr != nullptr);
172-
mgr->remove_device(this);
173-
}
174-
175140
/**
176141
*
177142
*/

panda/src/device/ioKitInputDevice.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ class EXPCL_PANDA_DEVICE IOKitInputDevice final : public InputDevice {
3030
IOKitInputDevice(IOHIDDeviceRef device);
3131
~IOKitInputDevice();
3232

33-
void on_remove();
34-
3533
private:
3634
void parse_element(IOHIDElementRef element);
3735

panda/src/device/ioKitInputDeviceManager.cxx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ IOKitInputDeviceManager() {
5959
CFRelease(match);
6060

6161
IOHIDManagerRegisterDeviceMatchingCallback(_hid_manager, on_match_device, this);
62+
IOHIDManagerRegisterDeviceRemovalCallback(_hid_manager, on_remove_device, this);
6263
IOHIDManagerScheduleWithRunLoop(_hid_manager, CFRunLoopGetMain(), kCFRunLoopCommonModes);
6364
IOHIDManagerOpen(_hid_manager, kIOHIDOptionsTypeNone);
6465
}
@@ -78,16 +79,43 @@ IOKitInputDeviceManager::
7879
*/
7980
void IOKitInputDeviceManager::
8081
on_match_device(void *ctx, IOReturn result, void *sender, IOHIDDeviceRef device) {
81-
InputDeviceManager *mgr = (InputDeviceManager *)ctx;
82+
IOKitInputDeviceManager *mgr = (IOKitInputDeviceManager *)ctx;
8283
nassertv(mgr != nullptr);
8384
nassertv(device);
8485

85-
PT(InputDevice) input_device = new IOKitInputDevice(device);
86+
IOKitInputDevice *input_device = new IOKitInputDevice(device);
8687
if (device_cat.is_debug()) {
8788
device_cat.debug()
8889
<< "Discovered input device " << *input_device << "\n";
8990
}
9091
mgr->add_device(input_device);
92+
mgr->_devices_by_hidref[device] = input_device;
9193
}
9294

95+
/**
96+
* Called by IOKit when an input device has disappeared.
97+
*/
98+
void IOKitInputDeviceManager::
99+
on_remove_device(void *ctx, IOReturn result, void *sender, IOHIDDeviceRef device) {
100+
IOKitInputDeviceManager *mgr = (IOKitInputDeviceManager *)ctx;
101+
nassertv(mgr != nullptr);
102+
nassertv(device);
103+
104+
auto it = mgr->_devices_by_hidref.find(device);
105+
nassertv(it != mgr->_devices_by_hidref.end());
106+
IOKitInputDevice *input_device = it->second;
107+
108+
input_device->set_connected(false);
109+
110+
mgr->_devices_by_hidref.erase(device);
111+
112+
IOHIDDeviceClose(device, kIOHIDOptionsTypeNone);
113+
114+
if (device_cat.is_debug()) {
115+
device_cat.debug()
116+
<< "Removed input device " << *input_device << "\n";
117+
}
118+
119+
mgr->remove_device(input_device);
120+
}
93121
#endif

panda/src/device/ioKitInputDeviceManager.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#if defined(__APPLE__) && !defined(CPPPARSER)
2020
#include <IOKit/hid/IOHIDManager.h>
2121

22+
class IOKitInputDevice;
23+
2224
/**
2325
* The macOS implementation of InputDeviceManager.
2426
*/
@@ -30,7 +32,16 @@ class EXPCL_PANDA_DEVICE IOKitInputDeviceManager final : public InputDeviceManag
3032
protected:
3133
IOHIDManagerRef _hid_manager;
3234

35+
// The device removal callback method we need to use requires us to remember
36+
// which IOKitInputDevice corresponds to which IOHIDDeviceRef. This is the
37+
// same strategy used by winInputDevice and friends.
38+
//
39+
// We can make this a mapping to raw pointers since we know _devices will be
40+
// holding a reference until remove_device is called.
41+
pmap<IOHIDDeviceRef, IOKitInputDevice *> _devices_by_hidref;
42+
3343
static void on_match_device(void *ctx, IOReturn result, void *sender, IOHIDDeviceRef device);
44+
static void on_remove_device(void *ctx, IOReturn result, void *sender, IOHIDDeviceRef device);
3445

3546
friend class InputDeviceManager;
3647
};

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy