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  int result = ioctl(_fd, EVIOCGBIT(EV_ABS, sizeof(axes)), axes);
332 #ifdef __FreeBSD__
333  // Older kernels had a bug where this would always return 0, see D28218
334  if (result == 0) {
335  for (int i = ABS_MAX; i >= 0; --i) {
336  if (test_bit(i, axes)) {
337  num_bits = i + 1;
338  break;
339  }
340  }
341  }
342  else
343 #endif
344  if (result > 0) {
345  num_bits = result << 3;
346  }
347  has_axes = true;
348  }
349 
350  // Do we have a preset device mapping?
351  int quirks = 0;
352  const DeviceMapping *mapping = mapping_presets;
353  while (mapping->vendor != 0) {
354  if (_vendor_id == mapping->vendor && _product_id == mapping->product) {
355  _device_class = mapping->device_class;
356  quirks = mapping->quirks;
357  break;
358  }
359  ++mapping;
360  }
361 
362  // The Steam Controller reports as multiple devices, one of which a gamepad.
363  if (quirks & QB_steam_controller) {
364  if (test_bit(BTN_GAMEPAD, keys)) {
365  _device_class = DeviceClass::gamepad;
366 
367  // If we have a virtual gamepad on the system, then careful: if Steam is
368  // running, it may disable its own gamepad in favour of the virtual
369  // device it registers. If the virtual device is present, we will only
370  // register this gamepad as connected when it registers input.
371  if (_manager->has_virtual_device(0x28de, 0x11ff)) {
372  device_cat.debug()
373  << "Detected Steam virtual gamepad, disabling Steam Controller\n";
374  quirks |= QB_connect_if_nonzero;
375  }
376  }
377  }
378  _quirks = quirks;
379 
380  // Try to detect which type of device we have here
381  if (_device_class == DeviceClass::unknown) {
382  int device_scores[(size_t)DeviceClass::digitizer + 1] = {0};
383 
384  // Test for specific keys
385  if (test_bit(BTN_GAMEPAD, keys) && test_bit(ABS_X, axes) && test_bit(ABS_RX, axes)) {
386  device_scores[(size_t)DeviceClass::gamepad] += 5;
387  device_scores[(size_t)DeviceClass::steering_wheel] += 5;
388  device_scores[(size_t)DeviceClass::flight_stick] += 5;
389  }
390 
391  if (test_bit(ABS_WHEEL, axes) && test_bit(ABS_GAS, axes) && test_bit(ABS_BRAKE, axes)) {
392  device_scores[(size_t)DeviceClass::steering_wheel] += 10;
393  }
394  if (test_bit(BTN_GEAR_DOWN, keys) && test_bit(BTN_GEAR_UP, keys)) {
395  device_scores[(size_t)DeviceClass::steering_wheel] += 10;
396  }
397  if (test_bit(BTN_JOYSTICK, keys) && test_bit(ABS_X, axes)) {
398  device_scores[(size_t)DeviceClass::flight_stick] += 10;
399  }
400  if (test_bit(BTN_MOUSE, keys) && test_bit(EV_REL, evtypes)) {
401  device_scores[(size_t)DeviceClass::mouse] += 20;
402  }
403  if (test_bit(BTN_DIGI, keys) && test_bit(EV_ABS, evtypes)) {
404  device_scores[(size_t)DeviceClass::digitizer] += 20;
405  }
406  uint8_t unknown_keys[] = {KEY_POWER};
407  for (int i = 0; i < 1; i++) {
408  if (test_bit(unknown_keys[i], keys)) {
409  if (unknown_keys[i] == KEY_POWER) {
410  }
411  device_scores[(size_t)DeviceClass::unknown] += 20;
412  }
413  }
414  if (_features & (unsigned int)Feature::keyboard) {
415  device_scores[(size_t)DeviceClass::keyboard] += 20;
416  }
417 
418  // Test for specific name tags
419  string lowercase_name = _name;
420  for(size_t x = 0; x < _name.length(); ++x) {
421  lowercase_name[x] = tolower(lowercase_name[x]);
422  }
423  if (lowercase_name.find("gamepad") != string::npos) {
424  device_scores[(size_t)DeviceClass::gamepad] += 10;
425  }
426  if (lowercase_name.find("wheel") != string::npos) {
427  device_scores[(size_t)DeviceClass::steering_wheel] += 10;
428  }
429  if (lowercase_name.find("mouse") != string::npos || lowercase_name.find("touchpad") != string::npos) {
430  device_scores[(size_t)DeviceClass::mouse] += 10;
431  }
432  if (lowercase_name.find("keyboard") != string::npos) {
433  device_scores[(size_t)DeviceClass::keyboard] += 10;
434  }
435  // List of lowercase names that occur in unknown devices
436  string unknown_names[] = {"video bus", "power button", "sleep button"};
437  for(int i = 0; i < 3; i++) {
438  if (lowercase_name.find(unknown_names[i]) != string::npos) {
439  device_scores[(size_t)DeviceClass::unknown] += 20;
440  }
441  }
442 
443  // Check which device type got the most points
444  int highest_score = 0;
445  for (size_t i = 0; i <= (size_t)DeviceClass::digitizer; i++) {
446  if (device_scores[i] > highest_score) {
447  highest_score = device_scores[i];
448  _device_class = (DeviceClass)i;
449  }
450  }
451  //std::cerr << "Found highscore class " << _device_class << " with this score: " << highest_score << "\n";
452  }
453 
454  if (has_keys) {
455  // Also check whether the buttons are currently pressed.
456  uint8_t states[(KEY_MAX + 8) >> 3] = {0};
457  ioctl(_fd, EVIOCGKEY(sizeof(states)), states);
458 
459  for (int i = 0; i <= KEY_MAX; ++i) {
460  if (test_bit(i, keys)) {
461  ButtonState button;
462  button.handle = map_button(i, _device_class, quirks);
463 
464  int button_index = (int)_buttons.size();
465  if (button.handle == ButtonHandle::none()) {
466  if (device_cat.is_debug()) {
467  device_cat.debug() << "Unmapped /dev/input/event" << _index
468  << " button " << button_index << ": 0x" << std::hex << i << std::dec << "\n";
469  }
470  }
471 
472  if (test_bit(i, states)) {
473  button._state = S_down;
474  all_values_zero = false;
475  } else {
476  button._state = S_up;
477  }
478  if (button.handle == GamepadButton::dpad_left()) {
479  emulate_dpad = false;
480  } else if (button.handle == GamepadButton::ltrigger()) {
481  _ltrigger_code = i;
482  } else if (button.handle == GamepadButton::rtrigger()) {
483  _rtrigger_code = i;
484  }
485 
486  _buttons.push_back(button);
487  if ((size_t)i >= _button_indices.size()) {
488  _button_indices.resize(i + 1, -1);
489  }
490  _button_indices[i] = button_index;
491  }
492  }
493  }
494 
495  if (has_axes) {
496  _axis_indices.resize(num_bits, -1);
497 
498  for (int i = 0; i < num_bits; ++i) {
499  if (test_bit(i, axes)) {
500  Axis axis = Axis::none;
501  switch (i) {
502  case ABS_X:
503  if (_device_class == DeviceClass::gamepad) {
504  axis = InputDevice::Axis::left_x;
505  } else if (_device_class == DeviceClass::flight_stick) {
506  axis = InputDevice::Axis::roll;
507  } else {
508  axis = InputDevice::Axis::x;
509  }
510  break;
511  case ABS_Y:
512  if (_device_class == DeviceClass::gamepad) {
513  axis = InputDevice::Axis::left_y;
514  } else if (_device_class == DeviceClass::flight_stick) {
515  axis = InputDevice::Axis::pitch;
516  } else {
517  axis = InputDevice::Axis::y;
518  }
519  break;
520  case ABS_Z:
521  if (quirks & QB_rstick_from_z) {
522  if (quirks & QB_right_axes_swapped) {
523  axis = InputDevice::Axis::right_y;
524  } else {
525  axis = InputDevice::Axis::right_x;
526  }
527  } else if (_device_class == DeviceClass::gamepad) {
528  if ((quirks & QB_no_analog_triggers) == 0) {
529  axis = InputDevice::Axis::left_trigger;
530  have_analog_triggers = true;
531  }
532  } else if (_device_class == DeviceClass::spatial_mouse) {
533  axis = InputDevice::Axis::z;
534  } else {
535  axis = InputDevice::Axis::throttle;
536  }
537  break;
538  case ABS_RX:
539  if (_device_class == DeviceClass::spatial_mouse) {
540  axis = InputDevice::Axis::pitch;
541  } else if ((quirks & QB_rstick_from_z) == 0) {
542  axis = InputDevice::Axis::right_x;
543  }
544  break;
545  case ABS_RY:
546  if (_device_class == DeviceClass::spatial_mouse) {
547  axis = InputDevice::Axis::roll;
548  } else if ((quirks & QB_rstick_from_z) == 0) {
549  axis = InputDevice::Axis::right_y;
550  }
551  break;
552  case ABS_RZ:
553  if (quirks & QB_rstick_from_z) {
554  if (quirks & QB_right_axes_swapped) {
555  axis = InputDevice::Axis::right_x;
556  } else {
557  axis = InputDevice::Axis::right_y;
558  }
559  } else if (_device_class == DeviceClass::gamepad) {
560  if ((quirks & QB_no_analog_triggers) == 0) {
561  axis = InputDevice::Axis::right_trigger;
562  have_analog_triggers = true;
563  } else {
564  // Special weird case for Trust GXT 24
565  axis = InputDevice::Axis::right_y;
566  }
567  } else {
568  axis = InputDevice::Axis::yaw;
569  }
570  break;
571  case ABS_THROTTLE:
572  if (quirks & QB_rudder_from_throttle) {
573  axis = InputDevice::Axis::rudder;
574  } else {
575  axis = InputDevice::Axis::throttle;
576  }
577  break;
578  case ABS_RUDDER:
579  axis = InputDevice::Axis::rudder;
580  break;
581  case ABS_WHEEL:
582  axis = InputDevice::Axis::wheel;
583  break;
584  case ABS_GAS:
585  if (_device_class == DeviceClass::gamepad) {
586  if ((quirks & QB_no_analog_triggers) == 0) {
587  axis = InputDevice::Axis::right_trigger;
588  have_analog_triggers = true;
589  }
590  } else {
591  axis = InputDevice::Axis::accelerator;
592  }
593  break;
594  case ABS_BRAKE:
595  if (_device_class == DeviceClass::gamepad) {
596  axis = InputDevice::Axis::left_trigger;
597  have_analog_triggers = true;
598  } else {
599  axis = InputDevice::Axis::brake;
600  }
601  break;
602  case ABS_HAT0X:
603  if (emulate_dpad) {
604  _dpad_x_axis = i;
605  _dpad_left_button = (int)_buttons.size();
606  if (_device_class == DeviceClass::gamepad) {
607  _buttons.push_back(ButtonState(GamepadButton::dpad_left()));
608  _buttons.push_back(ButtonState(GamepadButton::dpad_right()));
609  } else {
610  _buttons.push_back(ButtonState(GamepadButton::hat_left()));
611  _buttons.push_back(ButtonState(GamepadButton::hat_right()));
612  }
613  _buttons[_dpad_left_button]._state = S_up;
614  _buttons[_dpad_left_button+1]._state = S_up;
615  }
616  break;
617  case ABS_HAT0Y:
618  if (emulate_dpad) {
619  _dpad_y_axis = i;
620  _dpad_up_button = (int)_buttons.size();
621  if (_device_class == DeviceClass::gamepad) {
622  _buttons.push_back(ButtonState(GamepadButton::dpad_up()));
623  _buttons.push_back(ButtonState(GamepadButton::dpad_down()));
624  } else {
625  _buttons.push_back(ButtonState(GamepadButton::hat_up()));
626  _buttons.push_back(ButtonState(GamepadButton::hat_down()));
627  }
628  _buttons[_dpad_up_button]._state = S_up;
629  _buttons[_dpad_up_button+1]._state = S_up;
630  }
631  break;
632  case ABS_HAT2X:
633  if (quirks & QB_steam_controller) {
634  axis = InputDevice::Axis::right_trigger;
635  have_analog_triggers = true;
636  }
637  break;
638  case ABS_HAT2Y:
639  if (quirks & QB_steam_controller) {
640  axis = InputDevice::Axis::left_trigger;
641  have_analog_triggers = true;
642  }
643  break;
644  case ABS_PRESSURE:
645  axis = InputDevice::Axis::pressure;
646  break;
647  }
648 
649  // Check the initial value and ranges.
650  struct input_absinfo absinfo;
651  if (ioctl(_fd, EVIOCGABS(i), &absinfo) >= 0) {
652  int index;
653  // We'd like to reverse the Y axis to match the XInput behavior.
654  // Also reverse the yaw axis to match right-hand coordinate system.
655  // Also T.Flight Hotas X throttle is reversed and can go backwards.
656  if (axis == Axis::yaw || axis == Axis::rudder || axis == Axis::left_y || axis == Axis::right_y ||
657  (axis == Axis::throttle && (quirks & QB_reversed_throttle) != 0) ||
658  (_device_class == DeviceClass::spatial_mouse && (axis == Axis::y || axis == Axis::z || axis == Axis::roll)) ||
659  (_device_class == DeviceClass::digitizer && axis == Axis::y)) {
660  std::swap(absinfo.maximum, absinfo.minimum);
661  }
662  if (axis == Axis::throttle && (quirks & QB_centered_throttle) != 0) {
663  index = add_axis(axis, absinfo.minimum, absinfo.maximum, true);
664  } else {
665  index = add_axis(axis, absinfo.minimum, absinfo.maximum);
666  }
667  axis_changed(index, absinfo.value);
668  _axis_indices[i] = index;
669 
670  if (absinfo.value != 0) {
671  all_values_zero = false;
672  }
673  }
674  }
675  }
676  }
677 
678  if (test_bit(EV_REL, evtypes)) {
679  enable_feature(Feature::pointer);
680  add_pointer(PointerType::unknown, 0);
681  }
682 
683  if (test_bit(EV_FF, evtypes)) {
684  uint8_t effects[(FF_MAX + 8) >> 3] = {0};
685  ioctl(_fd, EVIOCGBIT(EV_FF, sizeof(effects)), effects);
686 
687  if (test_bit(FF_RUMBLE, effects)) {
688  if (_can_write) {
689  enable_feature(Feature::vibration);
690  } else {
691  // Let the user know what he's missing out on.
692  device_cat.warning()
693  << "/dev/input/event" << _index << " is not writable, vibration "
694  << "effects will be unavailable.\n";
695  }
696  }
697  }
698 
699  if (_ltrigger_code >= 0 && _rtrigger_code >= 0 && !have_analog_triggers) {
700  // Emulate analog triggers.
701  _ltrigger_axis = (int)_axes.size();
702  add_axis(Axis::left_trigger, 0, 1, false);
703  add_axis(Axis::right_trigger, 0, 1, false);
704  } else {
705  _ltrigger_code = -1;
706  _rtrigger_code = -1;
707  }
708 
709 #ifndef __FreeBSD__
710  char path[64];
711  char buffer[256];
712  const char *parent = "";
713  sprintf(path, "/sys/class/input/event%zd/device/device/../product", _index);
714  FILE *f = fopen(path, "r");
715  if (!f) {
716  parent = "../";
717  sprintf(path, "/sys/class/input/event%zd/device/device/%s../product", _index, parent);
718  f = fopen(path, "r");
719  }
720  if (f) {
721  if (fgets(buffer, sizeof(buffer), f) != nullptr) {
722  buffer[strcspn(buffer, "\r\n")] = 0;
723  if (buffer[0] != 0) {
724  _name.assign(buffer);
725  }
726  }
727  fclose(f);
728  }
729  sprintf(path, "/sys/class/input/event%zd/device/device/%s../manufacturer", _index, parent);
730  f = fopen(path, "r");
731  if (f) {
732  if (fgets(buffer, sizeof(buffer), f) != nullptr) {
733  buffer[strcspn(buffer, "\r\n")] = 0;
734  _manufacturer.assign(buffer);
735  }
736  fclose(f);
737  }
738  sprintf(path, "/sys/class/input/event%zd/device/device/%s../serial", _index, parent);
739  f = fopen(path, "r");
740  if (f) {
741  if (fgets(buffer, sizeof(buffer), f) != nullptr) {
742  buffer[strcspn(buffer, "\r\n")] = 0;
743  _serial_number.assign(buffer);
744  }
745  fclose(f);
746  }
747 #endif
748 
749  // Special-case fix for Xbox 360 Wireless Receiver: the Linux kernel
750  // driver always reports 4 connected gamepads, regardless of the number
751  // of gamepads actually present. This hack partially remedies this.
752  if (all_values_zero && (quirks & QB_connect_if_nonzero) != 0) {
753  _is_connected = false;
754  } else {
755  _is_connected = true;
756  }
757  return true;
758 }
759 
760 /**
761  * Reads a number of events from the device. Returns true if events were read,
762  * meaning this function should keep being called until it returns false.
763  */
764 bool EvdevInputDevice::
765 process_events() {
766  // Read 8 events at a time.
767  struct input_event events[8];
768 
769  int n_read = read(_fd, events, sizeof(events));
770  if (n_read < 0) {
771  if (errno == EAGAIN || errno == EWOULDBLOCK) {
772  // No data available for now.
773 
774  } else if (errno == ENODEV || errno == EINVAL) {
775  // The device ceased to exist, so we better close it. No need
776  // to worry about removing it from the InputDeviceManager, as it
777  // will get an inotify event sooner or later about this.
778  close(_fd);
779  _fd = -1;
780  //_is_connected = false;
781  errno = 0;
782 
783  } else {
784  device_cat.error() << "read: " << strerror(errno) << "\n";
785  }
786  return false;
787  }
788 
789  if (n_read == 0) {
790  return false;
791  }
792 
793  n_read /= sizeof(struct input_event);
794 
795  int rel_x = 0;
796  int rel_y = 0;
798  int index;
799 
800  // It seems that some devices send a single EV_SYN event when being
801  // unplugged. Boo. Ignore it.
802  if (n_read == 1 && events[0].code == EV_SYN) {
803  return false;
804  }
805 
806  for (int i = 0; i < n_read; ++i) {
807  int code = events[i].code;
808 
809  switch (events[i].type) {
810  case EV_SYN:
811  break;
812 
813  case EV_REL:
814  if (code == REL_X) rel_x += events[i].value;
815  if (code == REL_Y) rel_y += events[i].value;
816  break;
817 
818  case EV_ABS:
819  if (code == _dpad_x_axis) {
820  button_changed(_dpad_left_button, events[i].value < 0);
821  button_changed(_dpad_left_button+1, events[i].value > 0);
822  } else if (code == _dpad_y_axis) {
823  button_changed(_dpad_up_button, events[i].value < 0);
824  button_changed(_dpad_up_button+1, events[i].value > 0);
825  }
826  if (code >= 0 && (size_t)code < _axis_indices.size()) {
827  index = _axis_indices[code];
828  if (index >= 0) {
829  axis_changed(index, events[i].value);
830  }
831  }
832  else if (device_cat.is_debug()) {
833  device_cat.debug()
834  << "Ignoring EV_ABS event with unknown code " << code << "\n";
835  }
836  break;
837 
838  case EV_KEY:
839  if (code >= 0 && (size_t)code < _button_indices.size()) {
840  index = _button_indices[code];
841  if (index >= 0) {
842  button_changed(index, events[i].value != 0);
843  }
844  if (code == _ltrigger_code) {
845  axis_changed(_ltrigger_axis, events[i].value);
846  } else if (code == _rtrigger_code) {
847  axis_changed(_ltrigger_axis + 1, events[i].value);
848  }
849  }
850  else if (device_cat.is_debug()) {
851  device_cat.debug()
852  << "Ignoring EV_KEY event with unknown code " << code << "\n";
853  }
854  break;
855 
856  default:
857  //cerr << "event " << events[i].type << " - " << events[i].code << " - " << events[i].value << "\n";
858  break;
859  }
860  }
861 
862  if (rel_x != 0 || rel_y != 0) {
863  pointer_moved(0, rel_x, rel_y, time);
864  }
865 
866  return true;
867 }
868 
869 /**
870  * Static function to map an evdev code to a ButtonHandle.
871  */
872 ButtonHandle EvdevInputDevice::
873 map_button(int code, DeviceClass device_class, int quirks) {
874  if (code >= 0 && code < 0x80) {
875  // See linux/input.h for the source of this mapping.
876  static const ButtonHandle keyboard_map[] = {
877  ButtonHandle::none(),
878  KeyboardButton::escape(),
891  KeyboardButton::backspace(),
892  KeyboardButton::tab(),
905  KeyboardButton::enter(),
906  KeyboardButton::lcontrol(),
919  KeyboardButton::lshift(),
931  KeyboardButton::rshift(),
933  KeyboardButton::lalt(),
934  KeyboardButton::space(),
935  KeyboardButton::caps_lock(),
936  KeyboardButton::f1(),
937  KeyboardButton::f2(),
938  KeyboardButton::f3(),
939  KeyboardButton::f4(),
940  KeyboardButton::f5(),
941  KeyboardButton::f6(),
942  KeyboardButton::f7(),
943  KeyboardButton::f8(),
944  KeyboardButton::f9(),
945  KeyboardButton::f10(),
946  KeyboardButton::num_lock(),
947  KeyboardButton::scroll_lock(),
961  ButtonHandle::none(),
962  ButtonHandle::none(),
963  ButtonHandle::none(),
964  KeyboardButton::f11(),
965  KeyboardButton::f12(),
966  ButtonHandle::none(),
967  ButtonHandle::none(),
968  ButtonHandle::none(),
969  ButtonHandle::none(),
970  ButtonHandle::none(),
971  ButtonHandle::none(),
972  ButtonHandle::none(),
973  KeyboardButton::enter(),
974  KeyboardButton::rcontrol(),
976  KeyboardButton::print_screen(),
977  KeyboardButton::ralt(),
978  ButtonHandle::none(),
979  KeyboardButton::home(),
980  KeyboardButton::up(),
981  KeyboardButton::page_up(),
982  KeyboardButton::left(),
983  KeyboardButton::right(),
984  KeyboardButton::end(),
985  KeyboardButton::down(),
986  KeyboardButton::page_down(),
987  KeyboardButton::insert(),
988  KeyboardButton::del(),
989  ButtonHandle::none(),
990  ButtonHandle::none(),
991  ButtonHandle::none(),
992  ButtonHandle::none(),
993  ButtonHandle::none(),
994  ButtonHandle::none(),
995  ButtonHandle::none(),
996  KeyboardButton::pause(),
997  ButtonHandle::none(),
998  ButtonHandle::none(),
999  ButtonHandle::none(),
1000  ButtonHandle::none(),
1001  ButtonHandle::none(),
1002  KeyboardButton::lmeta(),
1003  KeyboardButton::rmeta(),
1004  KeyboardButton::menu(),
1005  };
1006  return keyboard_map[code];
1007 
1008  } else if (code == KEY_BACK) {
1009  // Used by NVIDIA Shield Controller
1010  return GamepadButton::back();
1011 
1012  } else if (code == KEY_SEARCH) {
1013  // Used by NVIDIA Shield Controller
1014  return GamepadButton::guide();
1015 
1016  } else if (code < 0x100) {
1017  return ButtonHandle::none();
1018 
1019  } else if ((code & 0xfff0) == BTN_MOUSE) {
1020  // The number for these is reversed in Panda.
1021  if (code == BTN_RIGHT) {
1022  return MouseButton::three();
1023  } else if (code == BTN_MIDDLE) {
1024  return MouseButton::two();
1025  } else {
1026  return MouseButton::button(code - BTN_MOUSE);
1027  }
1028 
1029  } else if ((code & 0xfff0) == BTN_JOYSTICK) {
1030  if (quirks & QB_steam_controller) {
1031  // BTN_THUMB and BTN_THUMB2 detect touching the touchpads.
1032  return ButtonHandle::none();
1033 
1034  } else if (device_class == DeviceClass::gamepad &&
1035  (quirks & QB_alt_button_mapping) != 0) {
1036  static const ButtonHandle mapping[] = {
1037  GamepadButton::face_y(),
1038  GamepadButton::face_b(),
1039  GamepadButton::face_a(),
1040  GamepadButton::face_x(),
1041  GamepadButton::lshoulder(),
1042  GamepadButton::rshoulder(),
1043  GamepadButton::ltrigger(),
1044  GamepadButton::rtrigger(),
1045  GamepadButton::back(),
1046  GamepadButton::start(),
1047  GamepadButton::lstick(),
1048  GamepadButton::rstick(),
1049  };
1050  if ((code & 0xf) < 12) {
1051  return mapping[code & 0xf];
1052  }
1053 
1054  } else if (device_class == DeviceClass::gamepad) {
1055  // Based on "Jess Tech Colour Rumble Pad"
1056  static const ButtonHandle mapping[] = {
1057  GamepadButton::face_x(),
1058  GamepadButton::face_y(),
1059  GamepadButton::face_a(),
1060  GamepadButton::face_b(),
1061  GamepadButton::lshoulder(),
1062  GamepadButton::ltrigger(),
1063  GamepadButton::rshoulder(),
1064  GamepadButton::rtrigger(),
1065  GamepadButton::back(),
1066  GamepadButton::start(),
1067  GamepadButton::lstick(),
1068  GamepadButton::rstick(),
1069  };
1070  if ((code & 0xf) < 12) {
1071  return mapping[code & 0xf];
1072  }
1073  } else {
1074  return GamepadButton::joystick(code & 0xf);
1075  }
1076  }
1077 
1078  switch (code) {
1079  case BTN_A:
1080  return GamepadButton::face_a();
1081 
1082  case BTN_B:
1083  return GamepadButton::face_b();
1084 
1085  case BTN_C:
1086  return GamepadButton::face_c();
1087 
1088  case BTN_X:
1089  return GamepadButton::face_x();
1090 
1091  case BTN_Y:
1092  return GamepadButton::face_y();
1093 
1094  case BTN_Z:
1095  return GamepadButton::face_z();
1096 
1097  case BTN_TL:
1098  return GamepadButton::lshoulder();
1099 
1100  case BTN_TR:
1101  return GamepadButton::rshoulder();
1102 
1103  case BTN_TL2:
1104  return GamepadButton::ltrigger();
1105 
1106  case BTN_TR2:
1107  return GamepadButton::rtrigger();
1108 
1109  case BTN_1:
1110  return GamepadButton::face_1();
1111 
1112  case BTN_2:
1113  return GamepadButton::face_2();
1114 
1115  case BTN_SELECT:
1116  case KEY_PREVIOUS:
1117  return GamepadButton::back();
1118 
1119  case BTN_START:
1120  case KEY_NEXT:
1121  return GamepadButton::start();
1122 
1123  case BTN_MODE:
1124  return GamepadButton::guide();
1125 
1126  case BTN_THUMBL:
1127  return GamepadButton::lstick();
1128 
1129  case BTN_THUMBR:
1130  return GamepadButton::rstick();
1131 
1132  case BTN_DPAD_LEFT:
1133  case BTN_TRIGGER_HAPPY1:
1134  return GamepadButton::dpad_left();
1135 
1136  case BTN_DPAD_RIGHT:
1137  case BTN_TRIGGER_HAPPY2:
1138  return GamepadButton::dpad_right();
1139 
1140  case BTN_DPAD_UP:
1141  case BTN_TRIGGER_HAPPY3:
1142  return GamepadButton::dpad_up();
1143 
1144  case BTN_DPAD_DOWN:
1145  case BTN_TRIGGER_HAPPY4:
1146  return GamepadButton::dpad_down();
1147 
1148  // The next two are for the Steam Controller's grip buttons.
1149  case BTN_GEAR_DOWN:
1150  return GamepadButton::lgrip();
1151 
1152  case BTN_GEAR_UP:
1153  return GamepadButton::rgrip();
1154 
1155  default:
1156  return ButtonHandle::none();
1157  }
1158 }
1159 
1160 #endif
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 ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
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),...
Definition: mouseButton.cxx:32
static ButtonHandle two()
Returns the ButtonHandle associated with the second mouse button.
Definition: mouseButton.cxx:51
static ButtonHandle three()
Returns the ButtonHandle associated with the third mouse button.
Definition: mouseButton.cxx:59
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.