17#ifdef PHAVE_LINUX_INPUT_H
24#include <sys/inotify.h>
32LinuxInputDeviceManager::
33LinuxInputDeviceManager() {
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";
83LinuxInputDeviceManager::
84~LinuxInputDeviceManager() {
85 if (_inotify_fd >= 0) {
98consider_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];
139 sprintf(path,
"/sys/class/input/event%zd/device", ev_index);
141 DIR *dir = opendir(path);
142 if (dir ==
nullptr) {
143 if (device_cat.is_debug()) {
145 <<
"Error opening directory " << path <<
": " << strerror(errno) <<
"\n";
150 dirent *entry = readdir(dir);
151 while (entry !=
nullptr) {
153 if (sscanf(entry->d_name,
"js%zd", &js_index) == 1) {
157 InputDevice *device = consider_add_js_device(js_index);
158 if (device !=
nullptr && device_cat.is_warning()) {
162 <<
"/dev/input/event" << ev_index <<
" is not readable, some "
163 "features will be unavailable.\n";
165 _evdev_devices[ev_index] = device;
168 entry = readdir(dir);
185consider_add_js_device(
size_t js_index) {
187 sprintf(path,
"/dev/input/js%zd", js_index);
189 if (access(path, R_OK) == 0) {
190 PT(LinuxJoystickDevice) device =
new LinuxJoystickDevice(
this, js_index);
191 if (device_cat.is_debug()) {
193 <<
"Discovered joydev input device " << *device <<
"\n";
197 if (device->is_connected()) {
198 _connected_devices.add_device(std::move(device));
201 _inactive_devices.add_device(std::move(device));
213bool LinuxInputDeviceManager::
214has_virtual_device(
unsigned short vendor_id,
unsigned short product_id)
const {
216 sprintf(path,
"/sys/devices/virtual/input");
218 DIR *dir = opendir(path);
219 if (dir !=
nullptr) {
220 dirent *entry = readdir(dir);
221 while (entry !=
nullptr) {
222 if (entry->d_name[0] !=
'i') {
223 entry = readdir(dir);
228 char vendor[5] = {0};
229 sprintf(path,
"/sys/devices/virtual/input/%s/id/vendor", entry->d_name);
230 f = fopen(path,
"r");
232 fgets(vendor,
sizeof(vendor), f);
236 char product[5] = {0};
237 sprintf(path,
"/sys/devices/virtual/input/%s/id/product", entry->d_name);
238 f = fopen(path,
"r");
240 fgets(product,
sizeof(product), f);
244 if (vendor[0] && std::stoi(std::string(vendor),
nullptr, 16) == (
int)vendor_id &&
245 product[0] && std::stoi(std::string(product),
nullptr, 16) == (
int)product_id) {
250 entry = readdir(dir);
262void LinuxInputDeviceManager::
269 inactive_devices = _inactive_devices;
271 for (
size_t i = 0; i < inactive_devices.
size(); ++i) {
280 unsigned int avail = 0;
281 ioctl(_inotify_fd, FIONREAD, &avail);
287 int n_read = read(_inotify_fd, buffer, avail);
289 if (errno == EAGAIN || errno == EWOULDBLOCK) {
293 device_cat.error() <<
"read: " << strerror(errno) <<
"\n";
301 bool removed_steam_virtual_device =
false;
303 char *end = buffer + avail;
305 inotify_event *
event = (inotify_event *)ptr;
307 std::string name(event->name);
309 if (event->mask & IN_DELETE) {
313 if (sscanf(event->name,
"event%zd", &index) == 1) {
315 if (index < _evdev_devices.size()) {
317 if (device !=
nullptr) {
319 _evdev_devices[index] =
nullptr;
320 _inactive_devices.remove_device(device);
321 if (_connected_devices.remove_device(device)) {
322 throw_event(
"disconnect-device", device.p());
325 if (device_cat.is_debug()) {
327 <<
"Removed input device " << *device <<
"\n";
333 removed_steam_virtual_device =
true;
339 }
else if (event->mask & (IN_CREATE | IN_ATTRIB)) {
345 if (sscanf(event->name,
"event%zd", &index) == 1) {
346 InputDevice *device = consider_add_evdev_device(index);
348 throw_event(
"connect-device", device);
353 ptr +=
sizeof(inotify_event) + event->len;
359 if (removed_steam_virtual_device) {
360 inactive_devices = _inactive_devices;
362 for (
size_t i = 0; i < inactive_devices.
size(); ++i) {
364 if (device !=
nullptr && device->
is_of_type(EvdevInputDevice::get_class_type())) {
365 PT(EvdevInputDevice) evdev_device = (EvdevInputDevice *)device;
366 if (evdev_device->reactivate_steam_controller()) {
367 _inactive_devices.remove_device(device);
368 _connected_devices.add_device(device);
369 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.