17 #ifdef PHAVE_LINUX_INPUT_H
24 #include <sys/inotify.h>
25 #include <sys/ioctl.h>
32 LinuxInputDeviceManager::
33 LinuxInputDeviceManager() {
35 _inotify_fd = inotify_init();
36 fcntl(_inotify_fd, F_SETFL, O_NONBLOCK);
37 fcntl(_inotify_fd, F_SETFD, FD_CLOEXEC);
39 if (_inotify_fd < 0) {
41 <<
"Error initializing inotify: " << strerror(errno) <<
"\n";
43 }
else if (inotify_add_watch(_inotify_fd,
"/dev/input", IN_CREATE | IN_ATTRIB | IN_DELETE) < 0) {
45 <<
"Error adding inotify watch on /dev/input: " << strerror(errno) <<
"\n";
49 DIR *dir = opendir(
"/dev/input");
51 std::vector<size_t> indices;
52 dirent *entry = readdir(dir);
53 while (entry !=
nullptr) {
55 if (entry->d_type == DT_CHR && sscanf(entry->d_name,
"event%zd", &index) == 1) {
56 indices.push_back(index);
64 if (indices.empty()) {
67 std::sort(indices.begin(), indices.end());
68 _evdev_devices.resize(indices.back() + 1,
nullptr);
70 for (
size_t index : indices) {
71 consider_add_evdev_device(index);
75 <<
"Error opening directory /dev/input: " << strerror(errno) <<
"\n";
83 LinuxInputDeviceManager::
84 ~LinuxInputDeviceManager() {
85 if (_inotify_fd >= 0) {
98 consider_add_evdev_device(
size_t ev_index) {
99 if (ev_index < _evdev_devices.size()) {
100 if (_evdev_devices[ev_index] !=
nullptr) {
107 _evdev_devices.resize(ev_index + 1,
nullptr);
112 sprintf(path,
"/dev/input/event%zd", ev_index);
114 if (access(path, R_OK) == 0) {
115 PT(
InputDevice) device =
new EvdevInputDevice(
this, ev_index);
116 if (device_cat.is_debug()) {
118 <<
"Discovered evdev input device " << *device <<
"\n";
121 _evdev_devices[ev_index] = device;
123 if (device->is_connected()) {
124 _connected_devices.add_device(std::move(device));
127 _inactive_devices.add_device(std::move(device));
129 return _evdev_devices[ev_index];
138 sprintf(path,
"/sys/class/input/event%zd/device", ev_index);
140 DIR *dir = opendir(path);
141 if (dir ==
nullptr) {
142 if (device_cat.is_debug()) {
144 <<
"Error opening directory " << path <<
": " << strerror(errno) <<
"\n";
149 dirent *entry = readdir(dir);
150 while (entry !=
nullptr) {
152 if (sscanf(entry->d_name,
"js%zd", &js_index) == 1) {
156 InputDevice *device = consider_add_js_device(js_index);
157 if (device !=
nullptr && device_cat.is_warning()) {
161 <<
"/dev/input/event" << ev_index <<
" is not readable, some "
162 "features will be unavailable.\n";
164 _evdev_devices[ev_index] = device;
167 entry = readdir(dir);
182 consider_add_js_device(
size_t js_index) {
184 sprintf(path,
"/dev/input/js%zd", js_index);
186 if (access(path, R_OK) == 0) {
187 PT(LinuxJoystickDevice) device =
new LinuxJoystickDevice(
this, js_index);
188 if (device_cat.is_debug()) {
190 <<
"Discovered joydev input device " << *device <<
"\n";
194 if (device->is_connected()) {
195 _connected_devices.add_device(std::move(device));
198 _inactive_devices.add_device(std::move(device));
210 bool LinuxInputDeviceManager::
211 has_virtual_device(
unsigned short vendor_id,
unsigned short product_id)
const {
213 sprintf(path,
"/sys/devices/virtual/input");
215 DIR *dir = opendir(path);
216 if (dir !=
nullptr) {
217 dirent *entry = readdir(dir);
218 while (entry !=
nullptr) {
219 if (entry->d_name[0] !=
'i') {
220 entry = readdir(dir);
225 char vendor[5] = {0};
226 sprintf(path,
"/sys/devices/virtual/input/%s/id/vendor", entry->d_name);
227 f = fopen(path,
"r");
229 fgets(vendor,
sizeof(vendor), f);
233 char product[5] = {0};
234 sprintf(path,
"/sys/devices/virtual/input/%s/id/product", entry->d_name);
235 f = fopen(path,
"r");
237 fgets(product,
sizeof(product), f);
241 if (vendor[0] && std::stoi(std::string(vendor),
nullptr, 16) == (
int)vendor_id &&
242 product[0] && std::stoi(std::string(product),
nullptr, 16) == (
int)product_id) {
247 entry = readdir(dir);
259 void LinuxInputDeviceManager::
266 inactive_devices = _inactive_devices;
268 for (
size_t i = 0; i < inactive_devices.
size(); ++i) {
277 unsigned int avail = 0;
278 ioctl(_inotify_fd, FIONREAD, &avail);
284 int n_read = read(_inotify_fd, buffer, avail);
286 if (errno == EAGAIN || errno == EWOULDBLOCK) {
290 device_cat.error() <<
"read: " << strerror(errno) <<
"\n";
298 bool removed_steam_virtual_device =
false;
300 char *end = buffer + avail;
302 inotify_event *
event = (inotify_event *)ptr;
304 std::string name(event->name);
306 if (event->mask & IN_DELETE) {
310 if (sscanf(event->name,
"event%zd", &index) == 1) {
312 if (index < _evdev_devices.size()) {
314 if (device !=
nullptr) {
316 _evdev_devices[index] =
nullptr;
317 _inactive_devices.remove_device(device);
318 if (_connected_devices.remove_device(device)) {
319 throw_event(
"disconnect-device", device.p());
322 if (device_cat.is_debug()) {
324 <<
"Removed input device " << *device <<
"\n";
330 removed_steam_virtual_device =
true;
336 }
else if (event->mask & (IN_CREATE | IN_ATTRIB)) {
342 if (sscanf(event->name,
"event%zd", &index) == 1) {
343 InputDevice *device = consider_add_evdev_device(index);
345 throw_event(
"connect-device", device);
350 ptr +=
sizeof(inotify_event) + event->len;
356 if (removed_steam_virtual_device) {
357 inactive_devices = _inactive_devices;
359 for (
size_t i = 0; i < inactive_devices.
size(); ++i) {
361 if (device !=
nullptr && device->
is_of_type(EvdevInputDevice::get_class_type())) {
362 PT(EvdevInputDevice) evdev_device = (EvdevInputDevice *)device;
363 if (evdev_device->reactivate_steam_controller()) {
364 _inactive_devices.remove_device(device);
365 _connected_devices.add_device(device);
366 throw_event(
"connect-device", device);
Similar to MutexHolder, but for a light mutex.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.