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  if (code >= 0 && (size_t)code < _axis_indices.size()) {
810  index = _axis_indices[code];
811  if (index >= 0) {
812  axis_changed(index, events[i].value);
813  }
814  }
815  else if (device_cat.is_debug()) {
816  device_cat.debug()
817  << "Ignoring EV_ABS event with unknown code " << code << "\n";
818  }
819  break;
820 
821  case EV_KEY:
822  if (code >= 0 && (size_t)code < _button_indices.size()) {
823  index = _button_indices[code];
824  if (index >= 0) {
825  button_changed(index, events[i].value != 0);
826  }
827  if (code == _ltrigger_code) {
828  axis_changed(_ltrigger_axis, events[i].value);
829  } else if (code == _rtrigger_code) {
830  axis_changed(_ltrigger_axis + 1, events[i].value);
831  }
832  }
833  else if (device_cat.is_debug()) {
834  device_cat.debug()
835  << "Ignoring EV_KEY event with unknown code " << code << "\n";
836  }
837  break;
838 
839  default:
840  //cerr << "event " << events[i].type << " - " << events[i].code << " - " << events[i].value << "\n";
841  break;
842  }
843  }
844 
845  if (rel_x != 0 || rel_y != 0) {
846  pointer_moved(0, rel_x, rel_y, time);
847  }
848 
849  return true;
850 }
851 
852 /**
853  * Static function to map an evdev code to a ButtonHandle.
854  */
855 ButtonHandle EvdevInputDevice::
856 map_button(int code, DeviceClass device_class, int quirks) {
857  if (code >= 0 && code < 0x80) {
858  // See linux/input.h for the source of this mapping.
859  static const ButtonHandle keyboard_map[] = {
860  ButtonHandle::none(),
861  KeyboardButton::escape(),
874  KeyboardButton::backspace(),
875  KeyboardButton::tab(),
888  KeyboardButton::enter(),
889  KeyboardButton::lcontrol(),
902  KeyboardButton::lshift(),
914  KeyboardButton::rshift(),
916  KeyboardButton::lalt(),
917  KeyboardButton::space(),
918  KeyboardButton::caps_lock(),
919  KeyboardButton::f1(),
920  KeyboardButton::f2(),
921  KeyboardButton::f3(),
922  KeyboardButton::f4(),
923  KeyboardButton::f5(),
924  KeyboardButton::f6(),
925  KeyboardButton::f7(),
926  KeyboardButton::f8(),
927  KeyboardButton::f9(),
928  KeyboardButton::f10(),
929  KeyboardButton::num_lock(),
930  KeyboardButton::scroll_lock(),
944  ButtonHandle::none(),
945  ButtonHandle::none(),
946  ButtonHandle::none(),
947  KeyboardButton::f11(),
948  KeyboardButton::f12(),
949  ButtonHandle::none(),
950  ButtonHandle::none(),
951  ButtonHandle::none(),
952  ButtonHandle::none(),
953  ButtonHandle::none(),
954  ButtonHandle::none(),
955  ButtonHandle::none(),
956  KeyboardButton::enter(),
957  KeyboardButton::rcontrol(),
959  KeyboardButton::print_screen(),
960  KeyboardButton::ralt(),
961  ButtonHandle::none(),
962  KeyboardButton::home(),
963  KeyboardButton::up(),
964  KeyboardButton::page_up(),
965  KeyboardButton::left(),
966  KeyboardButton::right(),
967  KeyboardButton::end(),
968  KeyboardButton::down(),
969  KeyboardButton::page_down(),
970  KeyboardButton::insert(),
971  KeyboardButton::del(),
972  ButtonHandle::none(),
973  ButtonHandle::none(),
974  ButtonHandle::none(),
975  ButtonHandle::none(),
976  ButtonHandle::none(),
977  ButtonHandle::none(),
978  ButtonHandle::none(),
979  KeyboardButton::pause(),
980  ButtonHandle::none(),
981  ButtonHandle::none(),
982  ButtonHandle::none(),
983  ButtonHandle::none(),
984  ButtonHandle::none(),
985  KeyboardButton::lmeta(),
986  KeyboardButton::rmeta(),
987  KeyboardButton::menu(),
988  };
989  return keyboard_map[code];
990 
991  } else if (code == KEY_BACK) {
992  // Used by NVIDIA Shield Controller
993  return GamepadButton::back();
994 
995  } else if (code == KEY_SEARCH) {
996  // Used by NVIDIA Shield Controller
997  return GamepadButton::guide();
998 
999  } else if (code < 0x100) {
1000  return ButtonHandle::none();
1001 
1002  } else if ((code & 0xfff0) == BTN_MOUSE) {
1003  // The number for these is reversed in Panda.
1004  if (code == BTN_RIGHT) {
1005  return MouseButton::three();
1006  } else if (code == BTN_MIDDLE) {
1007  return MouseButton::two();
1008  } else {
1009  return MouseButton::button(code - BTN_MOUSE);
1010  }
1011 
1012  } else if ((code & 0xfff0) == BTN_JOYSTICK) {
1013  if (quirks & QB_steam_controller) {
1014  // BTN_THUMB and BTN_THUMB2 detect touching the touchpads.
1015  return ButtonHandle::none();
1016 
1017  } else if (device_class == DeviceClass::gamepad &&
1018  (quirks & QB_alt_button_mapping) != 0) {
1019  static const ButtonHandle mapping[] = {
1020  GamepadButton::face_y(),
1021  GamepadButton::face_b(),
1022  GamepadButton::face_a(),
1023  GamepadButton::face_x(),
1024  GamepadButton::lshoulder(),
1025  GamepadButton::rshoulder(),
1026  GamepadButton::ltrigger(),
1027  GamepadButton::rtrigger(),
1028  GamepadButton::back(),
1029  GamepadButton::start(),
1030  GamepadButton::lstick(),
1031  GamepadButton::rstick(),
1032  };
1033  if ((code & 0xf) < 12) {
1034  return mapping[code & 0xf];
1035  }
1036 
1037  } else if (device_class == DeviceClass::gamepad) {
1038  // Based on "Jess Tech Colour Rumble Pad"
1039  static const ButtonHandle mapping[] = {
1040  GamepadButton::face_x(),
1041  GamepadButton::face_y(),
1042  GamepadButton::face_a(),
1043  GamepadButton::face_b(),
1044  GamepadButton::lshoulder(),
1045  GamepadButton::ltrigger(),
1046  GamepadButton::rshoulder(),
1047  GamepadButton::rtrigger(),
1048  GamepadButton::back(),
1049  GamepadButton::start(),
1050  GamepadButton::lstick(),
1051  GamepadButton::rstick(),
1052  };
1053  if ((code & 0xf) < 12) {
1054  return mapping[code & 0xf];
1055  }
1056  } else {
1057  return GamepadButton::joystick(code & 0xf);
1058  }
1059  }
1060 
1061  switch (code) {
1062  case BTN_A:
1063  return GamepadButton::face_a();
1064 
1065  case BTN_B:
1066  return GamepadButton::face_b();
1067 
1068  case BTN_C:
1069  return GamepadButton::face_c();
1070 
1071  case BTN_X:
1072  return GamepadButton::face_x();
1073 
1074  case BTN_Y:
1075  return GamepadButton::face_y();
1076 
1077  case BTN_Z:
1078  return GamepadButton::face_z();
1079 
1080  case BTN_TL:
1081  return GamepadButton::lshoulder();
1082 
1083  case BTN_TR:
1084  return GamepadButton::rshoulder();
1085 
1086  case BTN_TL2:
1087  return GamepadButton::ltrigger();
1088 
1089  case BTN_TR2:
1090  return GamepadButton::rtrigger();
1091 
1092  case BTN_1:
1093  return GamepadButton::face_1();
1094 
1095  case BTN_2:
1096  return GamepadButton::face_2();
1097 
1098  case BTN_SELECT:
1099  case KEY_PREVIOUS:
1100  return GamepadButton::back();
1101 
1102  case BTN_START:
1103  case KEY_NEXT:
1104  return GamepadButton::start();
1105 
1106  case BTN_MODE:
1107  return GamepadButton::guide();
1108 
1109  case BTN_THUMBL:
1110  return GamepadButton::lstick();
1111 
1112  case BTN_THUMBR:
1113  return GamepadButton::rstick();
1114 
1115  case BTN_DPAD_LEFT:
1116  case BTN_TRIGGER_HAPPY1:
1117  return GamepadButton::dpad_left();
1118 
1119  case BTN_DPAD_RIGHT:
1120  case BTN_TRIGGER_HAPPY2:
1121  return GamepadButton::dpad_right();
1122 
1123  case BTN_DPAD_UP:
1124  case BTN_TRIGGER_HAPPY3:
1125  return GamepadButton::dpad_up();
1126 
1127  case BTN_DPAD_DOWN:
1128  case BTN_TRIGGER_HAPPY4:
1129  return GamepadButton::dpad_down();
1130 
1131  // The next two are for the Steam Controller's grip buttons.
1132  case BTN_GEAR_DOWN:
1133  return GamepadButton::lgrip();
1134 
1135  case BTN_GEAR_UP:
1136  return GamepadButton::rgrip();
1137 
1138  default:
1139  return ButtonHandle::none();
1140  }
1141 }
1142 
1143 #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.