Panda3D
Loading...
Searching...
No Matches
evdevInputDevice.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file evdevInputDevice.cxx
10 * @author rdb
11 * @date 2015-08-24
12 */
13
14#include "evdevInputDevice.h"
15
16#ifdef PHAVE_LINUX_INPUT_H
17
18#include "gamepadButton.h"
19#include "keyboardButton.h"
20#include "mouseButton.h"
22
23#include <fcntl.h>
24#include <linux/input.h>
25
26#ifndef BTN_DPAD_UP
27#define BTN_DPAD_UP 0x220
28#define BTN_DPAD_DOWN 0x221
29#define BTN_DPAD_LEFT 0x222
30#define BTN_DPAD_RIGHT 0x223
31#endif
32
33
34// Android introduces these in API level 21.
35#ifndef BTN_TRIGGER_HAPPY
36#define BTN_TRIGGER_HAPPY 0x2c0
37#define BTN_TRIGGER_HAPPY1 0x2c0
38#define BTN_TRIGGER_HAPPY2 0x2c1
39#define BTN_TRIGGER_HAPPY3 0x2c2
40#define BTN_TRIGGER_HAPPY4 0x2c3
41#endif
42
43#define test_bit(bit, array) ((array)[(bit)>>3] & (1<<((bit)&7)))
44
45enum QuirkBits {
46 // Right stick uses Z and Rz inputs.
47 QB_rstick_from_z = 1,
48
49 // Throttle goes from -1 to 1 rather than from 0 to 1.
50 QB_centered_throttle = 2,
51
52 // Throttle is reversed.
53 QB_reversed_throttle = 4,
54
55 // Only consider the device "connected" if all axes are non-zero.
56 QB_connect_if_nonzero = 8,
57
58 // ABS_THROTTLE maps to rudder
59 QB_rudder_from_throttle = 16,
60
61 // Special handling for Steam Controller, which has many peculiarities.
62 // We only connect it if it is reporting any events, because when Steam is
63 // running, the Steam controller is muted in favour of a dummy Xbox device.
64 QB_steam_controller = 32,
65
66 // Axes on the right stick are swapped, using x for y and vice versa.
67 QB_right_axes_swapped = 64,
68
69 // Has no trigger axes.
70 QB_no_analog_triggers = 128,
71
72 // Alternate button mapping.
73 QB_alt_button_mapping = 256,
74};
75
76static const struct DeviceMapping {
77 unsigned short vendor;
78 unsigned short product;
79 InputDevice::DeviceClass device_class;
80 int quirks;
81} mapping_presets[] = {
82 // NVIDIA Shield Controller
83 {0x0955, 0x7214, InputDevice::DeviceClass::gamepad, QB_rstick_from_z},
84 // T.Flight Hotas X
85 {0x044f, 0xb108, InputDevice::DeviceClass::flight_stick, QB_centered_throttle | QB_reversed_throttle | QB_rudder_from_throttle},
86 // Xbox 360 Wireless Controller
87 {0x045e, 0x0719, InputDevice::DeviceClass::gamepad, QB_connect_if_nonzero},
88 // Steam Controller (wired)
89 {0x28de, 0x1102, InputDevice::DeviceClass::unknown, QB_steam_controller},
90 // Steam Controller (wireless)
91 {0x28de, 0x1142, InputDevice::DeviceClass::unknown, QB_steam_controller},
92 // Jess Tech Colour Rumble Pad
93 {0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, QB_rstick_from_z | QB_right_axes_swapped},
94 // Trust GXT 24
95 {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers | QB_alt_button_mapping},
96 // 8bitdo N30 Pro Controller
97 {0x2dc8, 0x9001, InputDevice::DeviceClass::gamepad, QB_rstick_from_z},
98 // Generic gamepad
99 {0x0810, 0x0001, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers | QB_alt_button_mapping | QB_rstick_from_z | QB_right_axes_swapped},
100 // Generic gamepad without sticks
101 {0x0810, 0xe501, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers | QB_alt_button_mapping},
102 // 3Dconnexion Space Traveller 3D Mouse
103 {0x046d, 0xc623, InputDevice::DeviceClass::spatial_mouse, 0},
104 // 3Dconnexion Space Pilot 3D Mouse
105 {0x046d, 0xc625, InputDevice::DeviceClass::spatial_mouse, 0},
106 // 3Dconnexion Space Navigator 3D Mouse
107 {0x046d, 0xc626, InputDevice::DeviceClass::spatial_mouse, 0},
108 // 3Dconnexion Space Explorer 3D Mouse
109 {0x046d, 0xc627, InputDevice::DeviceClass::spatial_mouse, 0},
110 // 3Dconnexion Space Navigator for Notebooks
111 {0x046d, 0xc628, InputDevice::DeviceClass::spatial_mouse, 0},
112 // 3Dconnexion SpacePilot Pro 3D Mouse
113 {0x046d, 0xc629, InputDevice::DeviceClass::spatial_mouse, 0},
114 // 3Dconnexion Space Mouse Pro
115 {0x046d, 0xc62b, InputDevice::DeviceClass::spatial_mouse, 0},
116 // FrSky Simulator
117 {0x0483, 0x5720, InputDevice::DeviceClass::flight_stick, 0},
118 {0},
119};
120
121TypeHandle EvdevInputDevice::_type_handle;
122
123/**
124 * Creates a new device representing the evdev device with the given index.
125 */
126EvdevInputDevice::
127EvdevInputDevice(LinuxInputDeviceManager *manager, size_t index) :
128 _manager(manager),
129 _index(index),
130 _fd(-1),
131 _can_write(false),
132 _ff_id(-1),
133 _ff_playing(false),
134 _ff_strong(-1),
135 _ff_weak(-1),
136 _dpad_x_axis(-1),
137 _dpad_y_axis(-1),
138 _dpad_left_button(-1),
139 _dpad_up_button(-1),
140 _ltrigger_code(-1),
141 _rtrigger_code(-1),
142 _quirks(0) {
143
144 char path[64];
145 sprintf(path, "/dev/input/event%zd", index);
146
147 _fd = open(path, O_RDWR | O_NONBLOCK);
148 if (_fd >= 0) {
149 _can_write = true;
150 } else {
151 // On failure, open device as read-only.
152 _fd = open(path, O_RDONLY | O_NONBLOCK);
153 }
154
155 if (_fd >= 0) {
156 init_device();
157 } else {
158 _is_connected = false;
159 device_cat.error()
160 << "Opening raw input device: " << strerror(errno) << " " << path << "\n";
161 }
162}
163
164/**
165 *
166 */
167EvdevInputDevice::
168~EvdevInputDevice() {
169 if (_fd != -1) {
170 if (_ff_id != -1) {
171 // Remove force-feedback effect.
172 do_set_vibration(0, 0);
173 ioctl(_fd, EVIOCRMFF, _ff_id);
174 _ff_id = -1;
175 }
176
177 close(_fd);
178 _fd = -1;
179 }
180}
181
182/**
183 * Sets the vibration strength. The first argument controls a low-frequency
184 * motor, if present, and the latter controls a high-frequency motor.
185 * The values are within the 0-1 range.
186 */
187void EvdevInputDevice::
188do_set_vibration(double strong, double weak) {
189 if (_fd == -1 || !_can_write) {
190 return;
191 }
192
193 int strong_level = strong * 0xffff;
194 int weak_level = weak * 0xffff;
195
196 if (strong_level == _ff_strong && weak_level == _ff_weak) {
197 // No change.
198 return;
199 }
200
201 // Upload the new effect parameters. Do this even if we are about
202 // to stop the effect, because some drivers don't respond to simply
203 // stopping the effect.
204 struct ff_effect effect;
205 effect.type = FF_RUMBLE;
206 effect.id = _ff_id;
207 effect.direction = 0;
208 effect.trigger.button = 0;
209 effect.trigger.interval = 0;
210 effect.replay.length = 0;
211 effect.replay.delay = 0;
212 effect.u.rumble.strong_magnitude = strong_level;
213 effect.u.rumble.weak_magnitude = weak_level;
214
215 if (ioctl(_fd, EVIOCSFF, &effect) < 0) {
216 return;
217 } else {
218 _ff_id = effect.id;
219 _ff_strong = strong_level;
220 _ff_weak = weak_level;
221 }
222
223 if (!_ff_playing) {
224 // Start the effect. We could pass 0 as value to stop the effect
225 // when a level of 0 is requested, but my driver seems to ignore it.
226 _ff_playing = true;
227
228 struct input_event play;
229 play.type = EV_FF;
230 play.code = _ff_id;
231 play.value = 1;
232
233 if (write(_fd, &play, sizeof(play)) < 0) {
234 device_cat.warning()
235 << "Failed to write force-feedback event: " << strerror(errno) << "\n";
236 }
237 }
238}
239
240/**
241 * Special case for Steam controllers; called if a Steam virtual device has
242 * just been disconnected, and this is currently an inactive Steam Controller
243 * previously blocked by Steam, waiting to be reactivated.
244 * Returns true if the device has just been reconnected.
245 */
246bool EvdevInputDevice::
247reactivate_steam_controller() {
248 LightMutexHolder holder(_lock);
249 if (!_is_connected && (_quirks & QB_steam_controller) != 0) {
250 // Just check to make sure the device is still readable.
251 process_events();
252 if (_fd != -1) {
253 _is_connected = true;
254 return true;
255 }
256 }
257 return false;
258}
259
260/**
261 * Polls the input device for new activity, to ensure it contains the latest
262 * events. This will only have any effect for some types of input devices;
263 * others may be updated automatically, and this method will be a no-op.
264 */
265void EvdevInputDevice::
266do_poll() {
267 if (_fd != -1 && process_events()) {
268 while (process_events()) {}
269
270 // If we got events, we are obviously connected. Mark us so.
271 if (!_is_connected && _fd != -1) {
272 _is_connected = true;
273 if (_manager != nullptr) {
274 _manager->add_device(this);
275 }
276 }
277 }
278}
279
280/**
281 * Reads basic properties from the device.
282 */
283bool EvdevInputDevice::
284init_device() {
285 using std::string;
286
287 nassertr(_fd >= 0, false);
288
289 LightMutexHolder holder(_lock);
290
291 uint8_t evtypes[(EV_MAX + 8) >> 3] = {0};
292 char name[128];
293 if (ioctl(_fd, EVIOCGNAME(sizeof(name)), name) < 0 ||
294 ioctl(_fd, EVIOCGBIT(0, sizeof(evtypes)), evtypes) < 0) {
295 close(_fd);
296 _fd = -1;
297 _is_connected = false;
298 device_cat.error() << "Opening raw input device: ioctl failed\n";
299 return false;
300 }
301
302 _name.assign(name);
303 //cerr << "##### Now initializing device " << name << "\n";
304
305 struct input_id id;
306 if (ioctl(_fd, EVIOCGID, &id) >= 0) {
307 _vendor_id = id.vendor;
308 _product_id = id.product;
309 }
310
311 bool all_values_zero = true;
312 bool emulate_dpad = true;
313 bool have_analog_triggers = false;
314
315 bool has_keys = false;
316 bool has_axes = false;
317
318 uint8_t keys[(KEY_MAX + 8) >> 3] = {0};
319 if (test_bit(EV_KEY, evtypes)) {
320 // Check which buttons are on the device.
321 ioctl(_fd, EVIOCGBIT(EV_KEY, sizeof(keys)), keys);
322 has_keys = true;
323
324 if (test_bit(KEY_A, keys) && test_bit(KEY_Z, keys)) {
325 enable_feature(Feature::keyboard);
326 }
327 }
328
329 int num_bits = 0;
330 uint8_t axes[(ABS_MAX + 8) >> 3] = {0};
331 if (test_bit(EV_ABS, evtypes)) {
332 // Check which axes are on the device.
333 int result = ioctl(_fd, EVIOCGBIT(EV_ABS, sizeof(axes)), axes);
334#ifdef __FreeBSD__
335 // Older kernels had a bug where this would always return 0, see D28218
336 if (result == 0) {
337 for (int i = ABS_MAX; i >= 0; --i) {
338 if (test_bit(i, axes)) {
339 num_bits = i + 1;
340 break;
341 }
342 }
343 }
344 else
345#endif
346 if (result > 0) {
347 num_bits = result << 3;
348 }
349 has_axes = true;
350 }
351
352 // Do we have a preset device mapping?
353 int quirks = 0;
354 const DeviceMapping *mapping = mapping_presets;
355 while (mapping->vendor != 0) {
356 if (_vendor_id == mapping->vendor && _product_id == mapping->product) {
357 _device_class = mapping->device_class;
358 quirks = mapping->quirks;
359 break;
360 }
361 ++mapping;
362 }
363
364 // The Steam Controller reports as multiple devices, one of which a gamepad.
365 if (quirks & QB_steam_controller) {
366 if (test_bit(BTN_GAMEPAD, keys)) {
367 _device_class = DeviceClass::gamepad;
368
369 // If we have a virtual gamepad on the system, then careful: if Steam is
370 // running, it may disable its own gamepad in favour of the virtual
371 // device it registers. If the virtual device is present, we will only
372 // register this gamepad as connected when it registers input.
373 if (_manager->has_virtual_device(0x28de, 0x11ff)) {
374 device_cat.debug()
375 << "Detected Steam virtual gamepad, disabling Steam Controller\n";
376 quirks |= QB_connect_if_nonzero;
377 }
378 }
379 }
380 _quirks = quirks;
381
382 // Try to detect which type of device we have here
383 if (_device_class == DeviceClass::unknown) {
384 int device_scores[(size_t)DeviceClass::digitizer + 1] = {0};
385
386 // Test for specific keys
387 if (test_bit(BTN_GAMEPAD, keys) && test_bit(ABS_X, axes) && test_bit(ABS_RX, axes)) {
388 device_scores[(size_t)DeviceClass::gamepad] += 5;
389 device_scores[(size_t)DeviceClass::steering_wheel] += 5;
390 device_scores[(size_t)DeviceClass::flight_stick] += 5;
391 }
392
393 if (test_bit(ABS_WHEEL, axes) && test_bit(ABS_GAS, axes) && test_bit(ABS_BRAKE, axes)) {
394 device_scores[(size_t)DeviceClass::steering_wheel] += 10;
395 }
396 if (test_bit(BTN_GEAR_DOWN, keys) && test_bit(BTN_GEAR_UP, keys)) {
397 device_scores[(size_t)DeviceClass::steering_wheel] += 10;
398 }
399 if (test_bit(BTN_JOYSTICK, keys) && test_bit(ABS_X, axes)) {
400 device_scores[(size_t)DeviceClass::flight_stick] += 10;
401 }
402 if (test_bit(BTN_MOUSE, keys) && test_bit(EV_REL, evtypes)) {
403 device_scores[(size_t)DeviceClass::mouse] += 20;
404 }
405 if (test_bit(BTN_DIGI, keys) && test_bit(EV_ABS, evtypes)) {
406 device_scores[(size_t)DeviceClass::digitizer] += 20;
407 }
408 uint8_t unknown_keys[] = {KEY_POWER};
409 for (int i = 0; i < 1; i++) {
410 if (test_bit(unknown_keys[i], keys)) {
411 if (unknown_keys[i] == KEY_POWER) {
412 }
413 device_scores[(size_t)DeviceClass::unknown] += 20;
414 }
415 }
416 if (_features & (unsigned int)Feature::keyboard) {
417 device_scores[(size_t)DeviceClass::keyboard] += 20;
418 }
419
420 // Test for specific name tags
421 string lowercase_name = _name;
422 for(size_t x = 0; x < _name.length(); ++x) {
423 lowercase_name[x] = tolower(lowercase_name[x]);
424 }
425 if (lowercase_name.find("gamepad") != string::npos) {
426 device_scores[(size_t)DeviceClass::gamepad] += 10;
427 }
428 if (lowercase_name.find("wheel") != string::npos) {
429 device_scores[(size_t)DeviceClass::steering_wheel] += 10;
430 }
431 if (lowercase_name.find("mouse") != string::npos || lowercase_name.find("touchpad") != string::npos) {
432 device_scores[(size_t)DeviceClass::mouse] += 10;
433 }
434 if (lowercase_name.find("keyboard") != string::npos) {
435 device_scores[(size_t)DeviceClass::keyboard] += 10;
436 }
437 // List of lowercase names that occur in unknown devices
438 string unknown_names[] = {"video bus", "power button", "sleep button"};
439 for(int i = 0; i < 3; i++) {
440 if (lowercase_name.find(unknown_names[i]) != string::npos) {
441 device_scores[(size_t)DeviceClass::unknown] += 20;
442 }
443 }
444
445 // Check which device type got the most points
446 int highest_score = 0;
447 for (size_t i = 0; i <= (size_t)DeviceClass::digitizer; i++) {
448 if (device_scores[i] > highest_score) {
449 highest_score = device_scores[i];
450 _device_class = (DeviceClass)i;
451 }
452 }
453 //std::cerr << "Found highscore class " << _device_class << " with this score: " << highest_score << "\n";
454 }
455
456 if (has_keys) {
457 // Also check whether the buttons are currently pressed.
458 uint8_t states[(KEY_MAX + 8) >> 3] = {0};
459 ioctl(_fd, EVIOCGKEY(sizeof(states)), states);
460
461 for (int i = 0; i <= KEY_MAX; ++i) {
462 if (test_bit(i, keys)) {
463 ButtonState button;
464 button.handle = map_button(i, _device_class, quirks);
465
466 int button_index = (int)_buttons.size();
467 if (button.handle == ButtonHandle::none()) {
468 if (device_cat.is_debug()) {
469 device_cat.debug() << "Unmapped /dev/input/event" << _index
470 << " button " << button_index << ": 0x" << std::hex << i << std::dec << "\n";
471 }
472 }
473
474 if (test_bit(i, states)) {
475 button._state = S_down;
476 all_values_zero = false;
477 } else {
478 button._state = S_up;
479 }
480 if (button.handle == GamepadButton::dpad_left()) {
481 emulate_dpad = false;
482 } else if (button.handle == GamepadButton::ltrigger()) {
483 _ltrigger_code = i;
484 } else if (button.handle == GamepadButton::rtrigger()) {
485 _rtrigger_code = i;
486 }
487
488 _buttons.push_back(button);
489 if ((size_t)i >= _button_indices.size()) {
490 _button_indices.resize(i + 1, -1);
491 }
492 _button_indices[i] = button_index;
493 }
494 }
495 }
496
497 if (has_axes) {
498 _axis_indices.resize(num_bits, -1);
499
500 for (int i = 0; i < num_bits; ++i) {
501 if (test_bit(i, axes)) {
502 Axis axis = Axis::none;
503 switch (i) {
504 case ABS_X:
505 if (_device_class == DeviceClass::gamepad) {
506 axis = InputDevice::Axis::left_x;
507 } else if (_device_class == DeviceClass::flight_stick) {
508 axis = InputDevice::Axis::roll;
509 } else {
510 axis = InputDevice::Axis::x;
511 }
512 break;
513 case ABS_Y:
514 if (_device_class == DeviceClass::gamepad) {
515 axis = InputDevice::Axis::left_y;
516 } else if (_device_class == DeviceClass::flight_stick) {
517 axis = InputDevice::Axis::pitch;
518 } else {
519 axis = InputDevice::Axis::y;
520 }
521 break;
522 case ABS_Z:
523 if (quirks & QB_rstick_from_z) {
524 if (quirks & QB_right_axes_swapped) {
525 axis = InputDevice::Axis::right_y;
526 } else {
527 axis = InputDevice::Axis::right_x;
528 }
529 } else if (_device_class == DeviceClass::gamepad) {
530 if ((quirks & QB_no_analog_triggers) == 0) {
531 axis = InputDevice::Axis::left_trigger;
532 have_analog_triggers = true;
533 }
534 } else if (_device_class == DeviceClass::spatial_mouse) {
535 axis = InputDevice::Axis::z;
536 } else {
537 axis = InputDevice::Axis::throttle;
538 }
539 break;
540 case ABS_RX:
541 if (_device_class == DeviceClass::spatial_mouse) {
542 axis = InputDevice::Axis::pitch;
543 } else if ((quirks & QB_rstick_from_z) == 0) {
544 axis = InputDevice::Axis::right_x;
545 }
546 break;
547 case ABS_RY:
548 if (_device_class == DeviceClass::spatial_mouse) {
549 axis = InputDevice::Axis::roll;
550 } else if ((quirks & QB_rstick_from_z) == 0) {
551 axis = InputDevice::Axis::right_y;
552 }
553 break;
554 case ABS_RZ:
555 if (quirks & QB_rstick_from_z) {
556 if (quirks & QB_right_axes_swapped) {
557 axis = InputDevice::Axis::right_x;
558 } else {
559 axis = InputDevice::Axis::right_y;
560 }
561 } else if (_device_class == DeviceClass::gamepad) {
562 if ((quirks & QB_no_analog_triggers) == 0) {
563 axis = InputDevice::Axis::right_trigger;
564 have_analog_triggers = true;
565 } else {
566 // Special weird case for Trust GXT 24
567 axis = InputDevice::Axis::right_y;
568 }
569 } else {
570 axis = InputDevice::Axis::yaw;
571 }
572 break;
573 case ABS_THROTTLE:
574 if (quirks & QB_rudder_from_throttle) {
575 axis = InputDevice::Axis::rudder;
576 } else {
577 axis = InputDevice::Axis::throttle;
578 }
579 break;
580 case ABS_RUDDER:
581 axis = InputDevice::Axis::rudder;
582 break;
583 case ABS_WHEEL:
584 axis = InputDevice::Axis::wheel;
585 break;
586 case ABS_GAS:
587 if (_device_class == DeviceClass::gamepad) {
588 if ((quirks & QB_no_analog_triggers) == 0) {
589 axis = InputDevice::Axis::right_trigger;
590 have_analog_triggers = true;
591 }
592 } else {
593 axis = InputDevice::Axis::accelerator;
594 }
595 break;
596 case ABS_BRAKE:
597 if (_device_class == DeviceClass::gamepad) {
598 axis = InputDevice::Axis::left_trigger;
599 have_analog_triggers = true;
600 } else {
601 axis = InputDevice::Axis::brake;
602 }
603 break;
604 case ABS_HAT0X:
605 if (emulate_dpad) {
606 _dpad_x_axis = i;
607 _dpad_left_button = (int)_buttons.size();
608 if (_device_class == DeviceClass::gamepad) {
609 _buttons.push_back(ButtonState(GamepadButton::dpad_left()));
610 _buttons.push_back(ButtonState(GamepadButton::dpad_right()));
611 } else {
612 _buttons.push_back(ButtonState(GamepadButton::hat_left()));
613 _buttons.push_back(ButtonState(GamepadButton::hat_right()));
614 }
615 _buttons[_dpad_left_button]._state = S_up;
616 _buttons[_dpad_left_button+1]._state = S_up;
617 }
618 break;
619 case ABS_HAT0Y:
620 if (emulate_dpad) {
621 _dpad_y_axis = i;
622 _dpad_up_button = (int)_buttons.size();
623 if (_device_class == DeviceClass::gamepad) {
624 _buttons.push_back(ButtonState(GamepadButton::dpad_up()));
625 _buttons.push_back(ButtonState(GamepadButton::dpad_down()));
626 } else {
627 _buttons.push_back(ButtonState(GamepadButton::hat_up()));
628 _buttons.push_back(ButtonState(GamepadButton::hat_down()));
629 }
630 _buttons[_dpad_up_button]._state = S_up;
631 _buttons[_dpad_up_button+1]._state = S_up;
632 }
633 break;
634 case ABS_HAT2X:
635 if (quirks & QB_steam_controller) {
636 axis = InputDevice::Axis::right_trigger;
637 have_analog_triggers = true;
638 }
639 break;
640 case ABS_HAT2Y:
641 if (quirks & QB_steam_controller) {
642 axis = InputDevice::Axis::left_trigger;
643 have_analog_triggers = true;
644 }
645 break;
646 case ABS_PRESSURE:
647 axis = InputDevice::Axis::pressure;
648 break;
649 }
650
651 // Check the initial value and ranges.
652 struct input_absinfo absinfo;
653 if (ioctl(_fd, EVIOCGABS(i), &absinfo) >= 0) {
654 int index;
655 // We'd like to reverse the Y axis to match the XInput behavior.
656 // Also reverse the yaw axis to match right-hand coordinate system.
657 // Also T.Flight Hotas X throttle is reversed and can go backwards.
658 if (axis == Axis::yaw || axis == Axis::rudder || axis == Axis::left_y || axis == Axis::right_y ||
659 (axis == Axis::throttle && (quirks & QB_reversed_throttle) != 0) ||
660 (_device_class == DeviceClass::spatial_mouse && (axis == Axis::y || axis == Axis::z || axis == Axis::roll)) ||
661 (_device_class == DeviceClass::digitizer && axis == Axis::y)) {
662 std::swap(absinfo.maximum, absinfo.minimum);
663 }
664 if (axis == Axis::throttle && (quirks & QB_centered_throttle) != 0) {
665 index = add_axis(axis, absinfo.minimum, absinfo.maximum, true);
666 } else {
667 index = add_axis(axis, absinfo.minimum, absinfo.maximum);
668 }
669 axis_changed(index, absinfo.value);
670 _axis_indices[i] = index;
671
672 if (absinfo.value != 0) {
673 all_values_zero = false;
674 }
675 }
676 }
677 }
678 }
679
680 if (test_bit(EV_REL, evtypes)) {
681 enable_feature(Feature::pointer);
682 add_pointer(PointerType::unknown, 0);
683 }
684
685 if (test_bit(EV_FF, evtypes)) {
686 uint8_t effects[(FF_MAX + 8) >> 3] = {0};
687 ioctl(_fd, EVIOCGBIT(EV_FF, sizeof(effects)), effects);
688
689 if (test_bit(FF_RUMBLE, effects)) {
690 if (_can_write) {
691 enable_feature(Feature::vibration);
692 } else {
693 // Let the user know what he's missing out on.
694 device_cat.warning()
695 << "/dev/input/event" << _index << " is not writable, vibration "
696 << "effects will be unavailable.\n";
697 }
698 }
699 }
700
701 if (_ltrigger_code >= 0 && _rtrigger_code >= 0 && !have_analog_triggers) {
702 // Emulate analog triggers.
703 _ltrigger_axis = (int)_axes.size();
704 add_axis(Axis::left_trigger, 0, 1, false);
705 add_axis(Axis::right_trigger, 0, 1, false);
706 } else {
707 _ltrigger_code = -1;
708 _rtrigger_code = -1;
709 }
710
711#ifndef __FreeBSD__
712 char path[64];
713 char buffer[256];
714 const char *parent = "";
715 sprintf(path, "/sys/class/input/event%zd/device/device/../product", _index);
716 FILE *f = fopen(path, "r");
717 if (!f) {
718 parent = "../";
719 sprintf(path, "/sys/class/input/event%zd/device/device/%s../product", _index, parent);
720 f = fopen(path, "r");
721 }
722 if (f) {
723 if (fgets(buffer, sizeof(buffer), f) != nullptr) {
724 buffer[strcspn(buffer, "\r\n")] = 0;
725 if (buffer[0] != 0) {
726 _name.assign(buffer);
727 }
728 }
729 fclose(f);
730 }
731 sprintf(path, "/sys/class/input/event%zd/device/device/%s../manufacturer", _index, parent);
732 f = fopen(path, "r");
733 if (f) {
734 if (fgets(buffer, sizeof(buffer), f) != nullptr) {
735 buffer[strcspn(buffer, "\r\n")] = 0;
736 _manufacturer.assign(buffer);
737 }
738 fclose(f);
739 }
740 sprintf(path, "/sys/class/input/event%zd/device/device/%s../serial", _index, parent);
741 f = fopen(path, "r");
742 if (f) {
743 if (fgets(buffer, sizeof(buffer), f) != nullptr) {
744 buffer[strcspn(buffer, "\r\n")] = 0;
745 _serial_number.assign(buffer);
746 }
747 fclose(f);
748 }
749#endif
750
751 // Special-case fix for Xbox 360 Wireless Receiver: the Linux kernel
752 // driver always reports 4 connected gamepads, regardless of the number
753 // of gamepads actually present. This hack partially remedies this.
754 if (all_values_zero && (quirks & QB_connect_if_nonzero) != 0) {
755 _is_connected = false;
756 } else {
757 _is_connected = true;
758 }
759 return true;
760}
761
762/**
763 * Reads a number of events from the device. Returns true if events were read,
764 * meaning this function should keep being called until it returns false.
765 */
766bool EvdevInputDevice::
767process_events() {
768 // Read 8 events at a time.
769 struct input_event events[8];
770
771 int n_read = read(_fd, events, sizeof(events));
772 if (n_read < 0) {
773 if (errno == EAGAIN || errno == EWOULDBLOCK) {
774 // No data available for now.
775
776 } else if (errno == ENODEV || errno == EINVAL) {
777 // The device ceased to exist, so we better close it. No need
778 // to worry about removing it from the InputDeviceManager, as it
779 // will get an inotify event sooner or later about this.
780 close(_fd);
781 _fd = -1;
782 //_is_connected = false;
783 errno = 0;
784
785 } else {
786 device_cat.error() << "read: " << strerror(errno) << "\n";
787 }
788 return false;
789 }
790
791 if (n_read == 0) {
792 return false;
793 }
794
795 n_read /= sizeof(struct input_event);
796
797 int rel_x = 0;
798 int rel_y = 0;
800 int index;
801
802 // It seems that some devices send a single EV_SYN event when being
803 // unplugged. Boo. Ignore it.
804 if (n_read == 1 && events[0].code == EV_SYN) {
805 return false;
806 }
807
808 for (int i = 0; i < n_read; ++i) {
809 int code = events[i].code;
810
811 switch (events[i].type) {
812 case EV_SYN:
813 break;
814
815 case EV_REL:
816 if (code == REL_X) rel_x += events[i].value;
817 if (code == REL_Y) rel_y += events[i].value;
818 break;
819
820 case EV_ABS:
821 if (code == _dpad_x_axis) {
822 button_changed(_dpad_left_button, events[i].value < 0);
823 button_changed(_dpad_left_button+1, events[i].value > 0);
824 } else if (code == _dpad_y_axis) {
825 button_changed(_dpad_up_button, events[i].value < 0);
826 button_changed(_dpad_up_button+1, events[i].value > 0);
827 }
828 if (code >= 0 && (size_t)code < _axis_indices.size()) {
829 index = _axis_indices[code];
830 if (index >= 0) {
831 axis_changed(index, events[i].value);
832 }
833 }
834 else if (device_cat.is_debug()) {
835 device_cat.debug()
836 << "Ignoring EV_ABS event with unknown code " << code << "\n";
837 }
838 break;
839
840 case EV_KEY:
841 if (code >= 0 && (size_t)code < _button_indices.size()) {
842 index = _button_indices[code];
843 if (index >= 0) {
844 button_changed(index, events[i].value != 0);
845 }
846 if (code == _ltrigger_code) {
847 axis_changed(_ltrigger_axis, events[i].value);
848 } else if (code == _rtrigger_code) {
849 axis_changed(_ltrigger_axis + 1, events[i].value);
850 }
851 }
852 else if (device_cat.is_debug()) {
853 device_cat.debug()
854 << "Ignoring EV_KEY event with unknown code " << code << "\n";
855 }
856 break;
857
858 default:
859 //cerr << "event " << events[i].type << " - " << events[i].code << " - " << events[i].value << "\n";
860 break;
861 }
862 }
863
864 if (rel_x != 0 || rel_y != 0) {
865 pointer_moved(0, rel_x, rel_y, time);
866 }
867
868 return true;
869}
870
871/**
872 * Static function to map an evdev code to a ButtonHandle.
873 */
874ButtonHandle EvdevInputDevice::
875map_button(int code, DeviceClass device_class, int quirks) {
876 if (code >= 0 && code < 0x80) {
877 // See linux/input.h for the source of this mapping.
878 static const ButtonHandle keyboard_map[] = {
879 ButtonHandle::none(),
880 KeyboardButton::escape(),
893 KeyboardButton::backspace(),
894 KeyboardButton::tab(),
907 KeyboardButton::enter(),
908 KeyboardButton::lcontrol(),
921 KeyboardButton::lshift(),
933 KeyboardButton::rshift(),
935 KeyboardButton::lalt(),
936 KeyboardButton::space(),
937 KeyboardButton::caps_lock(),
938 KeyboardButton::f1(),
939 KeyboardButton::f2(),
940 KeyboardButton::f3(),
941 KeyboardButton::f4(),
942 KeyboardButton::f5(),
943 KeyboardButton::f6(),
944 KeyboardButton::f7(),
945 KeyboardButton::f8(),
946 KeyboardButton::f9(),
947 KeyboardButton::f10(),
948 KeyboardButton::num_lock(),
949 KeyboardButton::scroll_lock(),
963 ButtonHandle::none(),
964 ButtonHandle::none(),
966 KeyboardButton::f11(),
967 KeyboardButton::f12(),
968 ButtonHandle::none(),
969 ButtonHandle::none(),
970 ButtonHandle::none(),
971 ButtonHandle::none(),
972 ButtonHandle::none(),
973 ButtonHandle::none(),
974 ButtonHandle::none(),
975 KeyboardButton::enter(),
976 KeyboardButton::rcontrol(),
978 KeyboardButton::print_screen(),
979 KeyboardButton::ralt(),
980 ButtonHandle::none(),
981 KeyboardButton::home(),
982 KeyboardButton::up(),
983 KeyboardButton::page_up(),
984 KeyboardButton::left(),
985 KeyboardButton::right(),
986 KeyboardButton::end(),
987 KeyboardButton::down(),
988 KeyboardButton::page_down(),
989 KeyboardButton::insert(),
990 KeyboardButton::del(),
991 ButtonHandle::none(),
992 ButtonHandle::none(),
993 ButtonHandle::none(),
994 ButtonHandle::none(),
995 ButtonHandle::none(),
996 ButtonHandle::none(),
997 ButtonHandle::none(),
998 KeyboardButton::pause(),
999 ButtonHandle::none(),
1000 ButtonHandle::none(),
1001 ButtonHandle::none(),
1002 ButtonHandle::none(),
1003 ButtonHandle::none(),
1004 KeyboardButton::lmeta(),
1005 KeyboardButton::rmeta(),
1006 KeyboardButton::menu(),
1007 };
1008 return keyboard_map[code];
1009
1010 } else if (code == KEY_BACK) {
1011 // Used by NVIDIA Shield Controller
1012 return GamepadButton::back();
1013
1014 } else if (code == KEY_SEARCH) {
1015 // Used by NVIDIA Shield Controller
1016 return GamepadButton::guide();
1017
1018 } else if (code < 0x100) {
1019 return ButtonHandle::none();
1020
1021 } else if ((code & 0xfff0) == BTN_MOUSE) {
1022 // The number for these is reversed in Panda.
1023 if (code == BTN_RIGHT) {
1024 return MouseButton::three();
1025 } else if (code == BTN_MIDDLE) {
1026 return MouseButton::two();
1027 } else {
1028 return MouseButton::button(code - BTN_MOUSE);
1029 }
1030
1031 } else if ((code & 0xfff0) == BTN_JOYSTICK) {
1032 if (quirks & QB_steam_controller) {
1033 // BTN_THUMB and BTN_THUMB2 detect touching the touchpads.
1034 return ButtonHandle::none();
1035
1036 } else if (device_class == DeviceClass::gamepad &&
1037 (quirks & QB_alt_button_mapping) != 0) {
1038 static const ButtonHandle mapping[] = {
1039 GamepadButton::face_y(),
1040 GamepadButton::face_b(),
1041 GamepadButton::face_a(),
1042 GamepadButton::face_x(),
1043 GamepadButton::lshoulder(),
1044 GamepadButton::rshoulder(),
1045 GamepadButton::ltrigger(),
1046 GamepadButton::rtrigger(),
1047 GamepadButton::back(),
1048 GamepadButton::start(),
1049 GamepadButton::lstick(),
1050 GamepadButton::rstick(),
1051 };
1052 if ((code & 0xf) < 12) {
1053 return mapping[code & 0xf];
1054 }
1055
1056 } else if (device_class == DeviceClass::gamepad) {
1057 // Based on "Jess Tech Colour Rumble Pad"
1058 static const ButtonHandle mapping[] = {
1059 GamepadButton::face_x(),
1060 GamepadButton::face_y(),
1061 GamepadButton::face_a(),
1062 GamepadButton::face_b(),
1063 GamepadButton::lshoulder(),
1064 GamepadButton::ltrigger(),
1065 GamepadButton::rshoulder(),
1066 GamepadButton::rtrigger(),
1067 GamepadButton::back(),
1068 GamepadButton::start(),
1069 GamepadButton::lstick(),
1070 GamepadButton::rstick(),
1071 };
1072 if ((code & 0xf) < 12) {
1073 return mapping[code & 0xf];
1074 }
1075 } else {
1076 return GamepadButton::joystick(code & 0xf);
1077 }
1078 }
1079
1080 switch (code) {
1081 case BTN_A:
1082 return GamepadButton::face_a();
1083
1084 case BTN_B:
1085 return GamepadButton::face_b();
1086
1087 case BTN_C:
1088 return GamepadButton::face_c();
1089
1090 case BTN_X:
1091 return GamepadButton::face_x();
1092
1093 case BTN_Y:
1094 return GamepadButton::face_y();
1095
1096 case BTN_Z:
1097 return GamepadButton::face_z();
1098
1099 case BTN_TL:
1100 return GamepadButton::lshoulder();
1101
1102 case BTN_TR:
1103 return GamepadButton::rshoulder();
1104
1105 case BTN_TL2:
1106 return GamepadButton::ltrigger();
1107
1108 case BTN_TR2:
1109 return GamepadButton::rtrigger();
1110
1111 case BTN_1:
1112 return GamepadButton::face_1();
1113
1114 case BTN_2:
1115 return GamepadButton::face_2();
1116
1117 case BTN_SELECT:
1118 case KEY_PREVIOUS:
1119 return GamepadButton::back();
1120
1121 case BTN_START:
1122 case KEY_NEXT:
1123 return GamepadButton::start();
1124
1125 case BTN_MODE:
1126 return GamepadButton::guide();
1127
1128 case BTN_THUMBL:
1129 return GamepadButton::lstick();
1130
1131 case BTN_THUMBR:
1132 return GamepadButton::rstick();
1133
1134 case BTN_DPAD_LEFT:
1135 case BTN_TRIGGER_HAPPY1:
1136 return GamepadButton::dpad_left();
1137
1138 case BTN_DPAD_RIGHT:
1139 case BTN_TRIGGER_HAPPY2:
1140 return GamepadButton::dpad_right();
1141
1142 case BTN_DPAD_UP:
1143 case BTN_TRIGGER_HAPPY3:
1144 return GamepadButton::dpad_up();
1145
1146 case BTN_DPAD_DOWN:
1147 case BTN_TRIGGER_HAPPY4:
1148 return GamepadButton::dpad_down();
1149
1150 // The next two are for the Steam Controller's grip buttons.
1151 case BTN_GEAR_DOWN:
1152 return GamepadButton::lgrip();
1153
1154 case BTN_GEAR_UP:
1155 return GamepadButton::rgrip();
1156
1157 default:
1158 return ButtonHandle::none();
1159 }
1160}
1161
1162#endif
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
get_frame_time
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition clockObject.h:91
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
static ButtonHandle joystick(int button_number)
Returns the ButtonHandle associated with the particular numbered joystick button (zero-based),...
static ButtonHandle ascii_key(char ascii_equivalent)
Returns the ButtonHandle associated with the particular ASCII character, if there is one,...
Similar to MutexHolder, but for a light mutex.
static ButtonHandle button(int button_number)
Returns the ButtonHandle associated with the particular numbered mouse button (zero-based),...
static ButtonHandle two()
Returns the ButtonHandle associated with the second mouse button.
static ButtonHandle three()
Returns the ButtonHandle associated with the third mouse button.
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.