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