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);
373 #endif // PHAVE_LINUX_INPUT_H