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