Panda3D
inputDevice.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 inputDevice.cxx
10  * @author rdb
11  * @date 2015-12-11
12  */
13 
14 #include "inputDevice.h"
15 
16 #if defined(_MSC_VER) && _MSC_VER < 1700
17 #define fma(a, b, c) ((a) * (b) + (c))
18 #endif
19 
20 TypeHandle InputDevice::_type_handle;
21 
22 /**
23  * Defines a new InputDevice.
24  */
25 InputDevice::
26 InputDevice(const std::string &name, DeviceClass dev_class) :
27  _name(name),
28  _device_class(dev_class),
29  _is_connected(true)
30 {
31  _button_events = new ButtonEventList;
32  _pointer_events = new PointerEventList;
33 }
34 
35 /**
36  *
37  */
38 InputDevice::
39 ~InputDevice() {
40 }
41 
42 /**
43  * Polls the input device for new activity, to ensure it contains the latest
44  * events. This will only have any effect for some types of input devices;
45  * others may be updated automatically, and this method will be a no-op.
46  */
47 void InputDevice::
48 poll() {
49  LightMutexHolder holder(_lock);
50  do_poll();
51 }
52 
53 /**
54  * Returns true if this device has a pending button event (a mouse button or
55  * keyboard button down/up), false otherwise. If this returns true, the
56  * particular event may be extracted via get_button_event().
57  */
58 bool InputDevice::
60  LightMutexHolder holder(_lock);
61  return !_button_events.is_null() && _button_events->get_num_events() > 0;
62 }
63 
64 /**
65  * Returns the list of recently-generated ButtonEvents.
66  * The list is also cleared.
67  */
68 PT(ButtonEventList) InputDevice::
69 get_button_events() {
70  LightMutexHolder holder(_lock);
71  PT(ButtonEventList) result = new ButtonEventList;
72  swap(_button_events, result);
73  return result;
74 }
75 
76 /**
77  * Returns true if this device has a pending pointer event (a mouse movement),
78  * or false otherwise. If this returns true, the particular event may be
79  * extracted via get_pointer_event().
80  */
81 bool InputDevice::
83  LightMutexHolder holder(_lock);
84  return _pointer_events != nullptr && !_pointer_events->empty();
85 }
86 
87 /**
88  * Returns a PointerEventList containing all the recent pointer events.
89  * Clears the list.
90  */
91 PT(PointerEventList) InputDevice::
92 get_pointer_events() {
93  LightMutexHolder holder(_lock);
94  PT(PointerEventList) result = new PointerEventList;
95  swap(_pointer_events, result);
96  return result;
97 }
98 
99 /**
100  * Called by the implementation to add a new known button.
101  */
102 int InputDevice::
103 add_button(ButtonHandle button) {
104  int index = (int)_buttons.size();
105  _buttons.push_back(ButtonState(button));
106  return index;
107 }
108 
109 /**
110  * Called by the implementation to add a new known axis.
111  */
112 int InputDevice::
113 add_axis(Axis axis, int minimum, int maximum, bool centered) {
114  AxisState state;
115  state.axis = axis;
116  if (centered) {
117  // Centered, eg. for sticks.
118  state._scale = 2.0 / (maximum - minimum);
119  state._bias = (maximum + minimum) / (double)(minimum - maximum);
120  } else {
121  // 0-based, eg. for triggers.
122  state._scale = 1.0 / maximum;
123  state._bias = 0.0;
124  }
125  int index = (int)_axes.size();
126  _axes.push_back(state);
127  return index;
128 }
129 
130 /**
131  * Called by the implementation to add a new known axis. This version tries
132  * to guess whether the axis is centered or not.
133  */
134 int InputDevice::
135 add_axis(Axis axis, int minimum, int maximum) {
136  bool centered = (minimum < 0)
137  || axis == Axis::x
138  || axis == Axis::y
139  || axis == Axis::z
140  || axis == Axis::yaw
141  || axis == Axis::pitch
142  || axis == Axis::roll
143  || axis == Axis::left_x
144  || axis == Axis::left_y
145  || axis == Axis::right_x
146  || axis == Axis::right_y
147  || axis == Axis::wheel
148  || axis == Axis::rudder;
149  return add_axis(axis, minimum, maximum, centered);
150 }
151 
152 /**
153  * Records that a new pointer was found.
154  */
155 int InputDevice::
156 add_pointer(PointerType type, int id, bool primary) {
157  //nassertr(_lock.debug_is_locked(), -1);
158 
159  PointerData data;
160  data._id = id;
161  data._type = type;
162 
163  int index = (int)_pointers.size();
164  if (_num_pointers == _pointers.size()) {
165  _pointers.push_back(data);
166  } else {
167  _pointers[index] = data;
168  }
169  ++_num_pointers;
170 
171  return index;
172 }
173 
174 /**
175  * Removes a previously added pointer. If the current pressure is not zero,
176  * it will generate an event doing so.
177  */
178 void InputDevice::
179 remove_pointer(int id) {
180  nassertv(_lock.debug_is_locked());
181 
182  size_t i;
183  for (i = 0; i < _pointers.size(); ++i) {
184  if (_pointers[i]._id == id) {
185  break;
186  }
187  }
188 
189  if (i < _pointers.size()) {
190  if (_pointers[i]._pressure != 0.0) {
191  _pointers[i]._pressure = 0.0;
192 
193  if (_enable_pointer_events) {
194  int seq = _event_sequence++;
196  _pointer_events->add_event(_pointers[i], seq, time);
197  }
198  }
199 
200  // Replace it with the last one.
201  if (i != _pointers.size() - 1) {
202  _pointers[i] = _pointers.back();
203  }
204  --_num_pointers;
205  }
206 }
207 
208 /**
209  * Records that pointer data for a pointer has changed. This can also be used
210  * to add a new pointer.
211  */
212 void InputDevice::
213 update_pointer(PointerData data, double time) {
214  nassertv(_lock.debug_is_locked());
215 
216  PointerData *ptr = nullptr;
217  for (size_t i = 0; i < _pointers.size(); ++i) {
218  if (_pointers[i]._id == data._id) {
219  ptr = &_pointers[i];
220  *ptr = data;
221  break;
222  }
223  }
224  if (ptr == nullptr) {
225  _pointers.push_back(data);
226  ptr = &_pointers.back();
227  }
228 
229  if (_enable_pointer_events) {
230  int seq = _event_sequence++;
231  _pointer_events->add_event(*ptr, seq, time);
232  }
233 }
234 
235 /**
236  * Records that a relative pointer movement has taken place.
237  */
238 void InputDevice::
239 pointer_moved(int id, double x, double y, double time) {
240  nassertv(_lock.debug_is_locked());
241 
242  PointerData *ptr = nullptr;
243  for (size_t i = 0; i < _pointers.size(); ++i) {
244  if (_pointers[i]._id == id) {
245  ptr = &_pointers[i];
246  _pointers[i]._xpos = x;
247  _pointers[i]._ypos = y;
248  break;
249  }
250  }
251  nassertv_always(ptr != nullptr);
252 
253  if (device_cat.is_spam() && (x != 0 || y != 0)) {
254  device_cat.spam()
255  << "Pointer " << id << " moved by " << x << " x " << y << "\n";
256  }
257 
258  if (_enable_pointer_events) {
259  int seq = _event_sequence++;
260  _pointer_events->add_event(ptr->_in_window,
261  ptr->_xpos,
262  ptr->_ypos,
263  x, y, seq, time);
264  }
265 }
266 
267 /**
268  * Sets the state of the indicated button index, where true indicates down,
269  * and false indicates up. This may generate a ButtonEvent if the button has
270  * an associated ButtonHandle. The caller should ensure that the lock is held
271  * while this call is made.
272  */
273 void InputDevice::
274 button_changed(int index, bool down) {
275  nassertv(_lock.debug_is_locked());
276  nassertv(index >= 0);
277  if (index >= (int)_buttons.size()) {
278  _buttons.resize(index + 1, ButtonState());
279  }
280 
281  State new_state = down ? S_down : S_up;
282  if (_buttons[index]._state == new_state) {
283  return;
284  }
285  _buttons[index]._state = new_state;
286 
287  ButtonHandle handle = _buttons[index].handle;
288 
289  if (device_cat.is_spam()) {
290  device_cat.spam()
291  << "Changed button " << index;
292 
293  if (handle != ButtonHandle::none()) {
294  device_cat.spam(false) << " (" << handle << ")";
295  }
296 
297  device_cat.spam(false) << " to " << (down ? "down" : "up") << "\n";
298  }
299 
300  if (handle != ButtonHandle::none()) {
301  _button_events->add_event(ButtonEvent(handle, down ? ButtonEvent::T_down : ButtonEvent::T_up));
302  }
303 }
304 
305 /**
306  * Sets the state of the indicated analog index. The caller should ensure that
307  * the lock is held while this call is made. This should be a number in the
308  * range -1.0 to 1.0, representing the current position of the axis within its
309  * total range of movement.
310  */
311 void InputDevice::
312 set_axis_value(int index, double value) {
313  LightMutexHolder holder(_lock);
314 
315  nassertv(index >= 0);
316  if ((size_t)index >= _axes.size()) {
317  _axes.resize((size_t)index + 1u, AxisState());
318  }
319 
320  if (device_cat.is_spam() && _axes[index].value != value) {
321  device_cat.spam()
322  << "Changed axis " << index;
323 
324  if (_axes[index].axis != Axis::none) {
325  device_cat.spam(false) << " (" << _axes[index].axis << ")";
326  }
327 
328  device_cat.spam(false) << " to " << value << "\n";
329  }
330 
331  _axes[index].value = value;
332  _axes[index].known = true;
333 }
334 
335 /**
336  * Called by the implementation during do_poll to indicate that the indicated
337  * axis has received a new raw value. Assumes the lock is held.
338  */
339 void InputDevice::
340 axis_changed(int index, int state) {
341  nassertv(_lock.debug_is_locked());
342  nassertv(index >= 0);
343  if ((size_t)index >= _axes.size()) {
344  _axes.resize((size_t)index + 1u, AxisState());
345  }
346 
347  double value = fma((double)state, _axes[index]._scale, _axes[index]._bias);
348 
349  if (device_cat.is_spam() && !IS_NEARLY_EQUAL(_axes[index].value, value)) {
350  device_cat.spam()
351  << "Changed axis " << index;
352 
353  if (_axes[index].axis != Axis::none) {
354  device_cat.spam(false) << " (" << _axes[index].axis << ")";
355  }
356 
357  device_cat.spam(false) << " to " << value << " (raw value " << state << ")\n";
358  }
359 
360  _axes[index].value = value;
361  _axes[index].known = true;
362 }
363 
364 /**
365  * Records that a tracker movement has taken place.
366  */
367 void InputDevice::
368 tracker_changed(const LPoint3 &pos, const LOrientation &orient, double time) {
369  nassertv(_lock.debug_is_locked());
370 
371  _tracker_data.set_pos(pos);
372  _tracker_data.set_orient(orient);
373  _tracker_data.set_time(time);
374 }
375 
376 /**
377  * Writes a one-line string describing the device.
378  */
379 void InputDevice::
380 output(std::ostream &out) const {
381  LightMutexHolder holder(_lock);
382 
383  out << _name << " (";
384 
385  if (!_is_connected) {
386  out << "dis";
387  }
388 
389  out << "connected)";
390 
391  if (_device_class != DeviceClass::unknown) {
392  out << ", " << _device_class;
393  }
394 
395  if (_buttons.size() > 0) {
396  out << ", " << _buttons.size() << " button";
397  if (_buttons.size() != 1) {
398  out.put('s');
399  }
400  }
401 
402  if (_axes.size() > 0) {
403  out << ", " << _axes.size() << " ax"
404  << (_axes.size() != 1 ? 'e' : 'i') << 's';
405  }
406 
407  if (_features & (1 << (unsigned int)Feature::pointer)) {
408  out << ", pointer";
409  }
410  if (_features & (1 << (unsigned int)Feature::keyboard)) {
411  out << ", keyboard";
412  }
413  if (_features & (1 << (unsigned int)Feature::tracker)) {
414  out << ", tracker";
415  }
416  if (_features & (1 << (unsigned int)Feature::vibration)) {
417  out << ", vibration";
418  }
419  if (_features & (1 << (unsigned int)Feature::battery)) {
420  out << ", battery";
421 
422  if (_battery_data.level > 0 && _battery_data.max_level > 0) {
423  out << " [";
424  short i = 0;
425  for (; i < _battery_data.level; ++i) {
426  out << '=';
427  }
428  for (; i < _battery_data.max_level; ++i) {
429  out << ' ';
430  }
431  out << ']';
432  }
433  }
434 }
435 
436 /**
437  * Writes a one-line string of all of the current button states.
438  */
439 void InputDevice::
440 output_buttons(std::ostream &out) const {
441  LightMutexHolder holder(_lock);
442 
443  bool any_buttons = false;
444  Buttons::const_iterator bi;
445  for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) {
446  const ButtonState &state = (*bi);
447  if (state.is_known()) {
448  if (any_buttons) {
449  out << ", ";
450  }
451  any_buttons = true;
452  out << (int)(bi - _buttons.begin()) << "=";
453  if (state._state == S_up) {
454  out << "up";
455  } else {
456  out << "down";
457  }
458  }
459  }
460 
461  if (!any_buttons) {
462  out << "no known buttons";
463  }
464 }
465 
466 /**
467  * Writes a multi-line description of the current button states.
468  */
469 void InputDevice::
470 write_buttons(std::ostream &out, int indent_level) const {
471  bool any_buttons = false;
472  Buttons::const_iterator bi;
473  for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) {
474  const ButtonState &state = (*bi);
475  if (state.is_known()) {
476  any_buttons = true;
477 
478  indent(out, indent_level)
479  << (int)(bi - _buttons.begin()) << ". ";
480 
481  if (state.handle != ButtonHandle::none()) {
482  out << "(" << state.handle << ") ";
483  }
484 
485  if (state._state == S_up) {
486  out << "up";
487  } else {
488  out << "down";
489  }
490  out << "\n";
491  }
492  }
493 
494  if (!any_buttons) {
495  indent(out, indent_level)
496  << "(no known buttons)\n";
497  }
498 }
499 
500 /**
501  * Writes a multi-line description of the current analog axis states.
502  */
503 void InputDevice::
504 write_axes(std::ostream &out, int indent_level) const {
505  LightMutexHolder holder(_lock);
506 
507  bool any_axis = false;
508  Axes::const_iterator ai;
509  for (ai = _axes.begin(); ai != _axes.end(); ++ai) {
510  const AxisState &state = (*ai);
511  if (state.known) {
512  any_axis = true;
513 
514  indent(out, indent_level)
515  << (int)(ai - _axes.begin()) << ". " << state.value << "\n";
516  }
517  }
518 
519  if (!any_axis) {
520  indent(out, indent_level)
521  << "(no known analog axes)\n";
522  }
523 }
524 
525 /**
526  * Sets the vibration strength. The first argument controls a low-frequency
527  * motor, if present, and the latter controls a high-frequency motor.
528  * The values are within the 0-1 range.
529  */
530 void InputDevice::
531 do_set_vibration(double strong, double weak) {
532 }
533 
534 /**
535  * Polls the input device for new activity, to ensure it contains the latest
536  * events. This will only have any effect for some types of input devices;
537  * others may be updated automatically, and this method will be a no-op.
538  */
539 void InputDevice::
540 do_poll() {
541 }
542 
543 /**
544  * Returns a string describing the given device class enumerant.
545  */
546 std::string InputDevice::
547 format_device_class(DeviceClass dc) {
548  switch (dc) {
549  case InputDevice::DeviceClass::unknown:
550  return "unknown";
551 
552  case InputDevice::DeviceClass::virtual_device:
553  return "virtual_device";
554 
555  case InputDevice::DeviceClass::keyboard:
556  return "keyboard";
557 
558  case InputDevice::DeviceClass::mouse:
559  return "mouse";
560 
561  case InputDevice::DeviceClass::touch:
562  return "touch";
563 
564  case InputDevice::DeviceClass::gamepad:
565  return "gamepad";
566 
567  case InputDevice::DeviceClass::flight_stick:
568  return "flight_stick";
569 
570  case InputDevice::DeviceClass::steering_wheel:
571  return "steering_wheel";
572 
573  case InputDevice::DeviceClass::dance_pad:
574  return "dance_pad";
575 
576  case InputDevice::DeviceClass::hmd:
577  return "hmd";
578 
579  case InputDevice::DeviceClass::spatial_mouse:
580  return "spatial_mouse";
581  }
582  return "**invalid**";
583 }
584 
585 /**
586  * Returns a string describing the given axis enumerant.
587  */
588 std::string InputDevice::
589 format_axis(Axis axis) {
590  switch (axis) {
591  case InputDevice::Axis::none:
592  return "none";
593 
594  case InputDevice::Axis::x:
595  return "x";
596 
597  case InputDevice::Axis::y:
598  return "y";
599 
600  case InputDevice::Axis::z:
601  return "z";
602 
603  case InputDevice::Axis::yaw:
604  return "yaw";
605 
606  case InputDevice::Axis::pitch:
607  return "pitch";
608 
609  case InputDevice::Axis::roll:
610  return "roll";
611 
612  case InputDevice::Axis::left_x:
613  return "left_x";
614 
615  case InputDevice::Axis::left_y:
616  return "left_y";
617 
618  case InputDevice::Axis::left_trigger:
619  return "left_trigger";
620 
621  case InputDevice::Axis::right_x:
622  return "right_x";
623 
624  case InputDevice::Axis::right_y:
625  return "right_y";
626 
627  case InputDevice::Axis::right_trigger:
628  return "right_trigger";
629 
630  //case InputDevice::Axis::trigger:
631  // return "trigger";
632 
633  case InputDevice::Axis::throttle:
634  return "throttle";
635 
636  case InputDevice::Axis::rudder:
637  return "rudder";
638 
639  case InputDevice::Axis::wheel:
640  return "wheel";
641 
642  case InputDevice::Axis::accelerator:
643  return "accelerator";
644 
645  case InputDevice::Axis::brake:
646  return "brake";
647  }
648  return "**invalid**";
649 }
650 
651 std::ostream &
652 operator << (std::ostream &out, InputDevice::DeviceClass dc) {
654  return out;
655 }
656 
657 std::ostream &
658 operator << (std::ostream &out, InputDevice::Axis axis) {
659  out << InputDevice::format_axis(axis);
660  return out;
661 }
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
bool debug_is_locked() const
Returns true if the current thread has locked the LightMutex, false otherwise.
void output_buttons(std::ostream &out) const
Writes a one-line string of all of the current button states.
Records a set of pointer events that happened recently.
Records a button event of some kind.
Definition: buttonEvent.h:46
static std::string format_axis(Axis axis)
Returns a string describing the given axis enumerant.
Records a set of button events that happened recently.
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
Definition: buttonHandle.h:26
PointerType
Contains the types of pointer device.
Definition: pointerData.h:25
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
void write_axes(std::ostream &out, int indent_level) const
Writes a multi-line description of the current analog axis states.
void poll()
Polls the input device for new activity, to ensure it contains the latest events.
Definition: inputDevice.cxx:48
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
Similar to MutexHolder, but for a light mutex.
Holds the data that might be generated by a 2-d pointer input device, such as the mouse in the Graphi...
Definition: pointerData.h:38
bool has_pointer_event() const
Returns true if this device has a pending pointer event (a mouse movement), or false otherwise.
Definition: inputDevice.cxx:82
set_time
Indicates the time at which the position information (pos and orient) are effective.
Definition: trackerData.h:48
static std::string format_device_class(DeviceClass dc)
Returns a string describing the given device class enumerant.
virtual void output(std::ostream &out) const
Writes a one-line string describing the device.
void write_buttons(std::ostream &out, int indent_level) const
Writes a multi-line description of the current button states.
is_known
True if the button state is currently known.
Definition: inputDevice.h:149
bool has_button_event() const
Returns true if this device has a pending button event (a mouse button or keyboard button down/up),...
Definition: inputDevice.cxx:59
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
set_pos
Indicates the current position of the tracker sensor in space.
Definition: trackerData.h:49
set_orient
Indicates the current orientation of the tracker sensor in space.
Definition: trackerData.h:50
PT(ButtonEventList) InputDevice
Returns the list of recently-generated ButtonEvents.
Definition: inputDevice.cxx:68