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 std::sort(indices.begin(), indices.end());
65 _evdev_devices.resize(indices.back() + 1,
nullptr);
67 for (
size_t index : indices) {
68 consider_add_evdev_device(index);
72 <<
"Error opening directory /dev/input: " << strerror(errno) <<
"\n";
80 LinuxInputDeviceManager::
81 ~LinuxInputDeviceManager() {
82 if (_inotify_fd >= 0) {
95 consider_add_evdev_device(
size_t ev_index) {
96 if (ev_index < _evdev_devices.size()) {
97 if (_evdev_devices[ev_index] !=
nullptr) {
104 _evdev_devices.resize(ev_index + 1,
nullptr);
109 sprintf(path,
"/dev/input/event%zd", ev_index);
111 if (access(path, R_OK) == 0) {
112 PT(
InputDevice) device =
new EvdevInputDevice(
this, ev_index);
113 if (device_cat.is_debug()) {
115 <<
"Discovered evdev input device " << *device <<
"\n";
118 _evdev_devices[ev_index] = device;
120 if (device->is_connected()) {
121 _connected_devices.add_device(std::move(device));
124 _inactive_devices.add_device(std::move(device));
126 return _evdev_devices[ev_index];
135 sprintf(path,
"/sys/class/input/event%zd/device", ev_index);
137 DIR *dir = opendir(path);
138 if (dir ==
nullptr) {
139 if (device_cat.is_debug()) {
141 <<
"Error opening directory " << path <<
": " << strerror(errno) <<
"\n";
146 dirent *entry = readdir(dir);
147 while (entry !=
nullptr) {
149 if (sscanf(entry->d_name,
"js%zd", &js_index) == 1) {
153 InputDevice *device = consider_add_js_device(js_index);
154 if (device !=
nullptr && device_cat.is_warning()) {
158 <<
"/dev/input/event" << ev_index <<
" is not readable, some " 159 "features will be unavailable.\n";
161 _evdev_devices[ev_index] = device;
164 entry = readdir(dir);
179 consider_add_js_device(
size_t js_index) {
181 sprintf(path,
"/dev/input/js%zd", js_index);
183 if (access(path, R_OK) == 0) {
184 PT(LinuxJoystickDevice) device =
new LinuxJoystickDevice(
this, js_index);
185 if (device_cat.is_debug()) {
187 <<
"Discovered joydev input device " << *device <<
"\n";
191 if (device->is_connected()) {
192 _connected_devices.add_device(std::move(device));
195 _inactive_devices.add_device(std::move(device));
207 bool LinuxInputDeviceManager::
208 has_virtual_device(
unsigned short vendor_id,
unsigned short product_id)
const {
210 sprintf(path,
"/sys/devices/virtual/input");
212 DIR *dir = opendir(path);
213 if (dir !=
nullptr) {
214 dirent *entry = readdir(dir);
215 while (entry !=
nullptr) {
216 if (entry->d_name[0] !=
'i') {
217 entry = readdir(dir);
222 char vendor[5] = {0};
223 sprintf(path,
"/sys/devices/virtual/input/%s/id/vendor", entry->d_name);
224 f = fopen(path,
"r");
226 fgets(vendor,
sizeof(vendor), f);
230 char product[5] = {0};
231 sprintf(path,
"/sys/devices/virtual/input/%s/id/product", entry->d_name);
232 f = fopen(path,
"r");
234 fgets(product,
sizeof(product), f);
238 if (vendor[0] && std::stoi(std::string(vendor),
nullptr, 16) == (
int)vendor_id &&
239 product[0] && std::stoi(std::string(product),
nullptr, 16) == (
int)product_id) {
244 entry = readdir(dir);
256 void LinuxInputDeviceManager::
263 inactive_devices = _inactive_devices;
265 for (
size_t i = 0; i < inactive_devices.
size(); ++i) {
274 unsigned int avail = 0;
275 ioctl(_inotify_fd, FIONREAD, &avail);
281 int n_read = read(_inotify_fd, buffer, avail);
283 if (errno == EAGAIN || errno == EWOULDBLOCK) {
287 device_cat.error() <<
"read: " << strerror(errno) <<
"\n";
295 bool removed_steam_virtual_device =
false;
297 char *end = buffer + avail;
299 inotify_event *
event = (inotify_event *)ptr;
301 std::string name(event->name);
303 if (event->mask & IN_DELETE) {
307 if (sscanf(event->name,
"event%zd", &index) == 1) {
309 if (index < _evdev_devices.size()) {
311 if (device !=
nullptr) {
313 _evdev_devices[index] =
nullptr;
314 _inactive_devices.remove_device(device);
315 if (_connected_devices.remove_device(device)) {
316 throw_event(
"disconnect-device", device.p());
319 if (device_cat.is_debug()) {
321 <<
"Removed input device " << *device <<
"\n";
327 removed_steam_virtual_device =
true;
333 }
else if (event->mask & (IN_CREATE | IN_ATTRIB)) {
339 if (sscanf(event->name,
"event%zd", &index) == 1) {
340 InputDevice *device = consider_add_evdev_device(index);
342 throw_event(
"connect-device", device);
347 ptr +=
sizeof(inotify_event) + event->len;
353 if (removed_steam_virtual_device) {
354 inactive_devices = _inactive_devices;
356 for (
size_t i = 0; i < inactive_devices.
size(); ++i) {
358 if (device !=
nullptr && device->
is_of_type(EvdevInputDevice::get_class_type())) {
359 PT(EvdevInputDevice) evdev_device = (EvdevInputDevice *)device;
360 if (evdev_device->reactivate_steam_controller()) {
361 _inactive_devices.remove_device(device);
362 _connected_devices.add_device(device);
363 throw_event(
"connect-device", device);
370 #endif // PHAVE_LINUX_INPUT_H PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.