Panda3D
Loading...
Searching...
No Matches
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
20TypeHandle InputDevice::_type_handle;
21
22/**
23 * Defines a new InputDevice.
24 */
25InputDevice::
26InputDevice(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 */
38InputDevice::
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 */
48poll() {
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 */
59has_button_event() const {
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 */
68PT(ButtonEventList) InputDevice::
69get_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 */
82has_pointer_event() const {
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 */
91PT(PointerEventList) InputDevice::
92get_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 */
102int InputDevice::
103add_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 */
112int InputDevice::
113add_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 */
134int InputDevice::
135add_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 */
155int InputDevice::
156add_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 */
178void InputDevice::
179remove_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 */
212void InputDevice::
213update_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 */
238void InputDevice::
239pointer_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 */
273void InputDevice::
274button_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 */
311void InputDevice::
312set_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 */
339void InputDevice::
340axis_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 */
367void InputDevice::
368tracker_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 */
380output(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 */
440output_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 */
470write_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 */
504write_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 */
530void InputDevice::
531do_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 */
539void InputDevice::
540do_poll() {
541}
542
543/**
544 * Returns a string describing the given device class enumerant.
545 */
546std::string InputDevice::
547format_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 case InputDevice::DeviceClass::digitizer:
583 return "digitizer";
584 }
585 return "**invalid**";
586}
587
588/**
589 * Returns a string describing the given axis enumerant.
590 */
591std::string InputDevice::
592format_axis(Axis axis) {
593 switch (axis) {
594 case InputDevice::Axis::none:
595 return "none";
596
597 case InputDevice::Axis::x:
598 return "x";
599
600 case InputDevice::Axis::y:
601 return "y";
602
603 case InputDevice::Axis::z:
604 return "z";
605
606 case InputDevice::Axis::yaw:
607 return "yaw";
608
609 case InputDevice::Axis::pitch:
610 return "pitch";
611
612 case InputDevice::Axis::roll:
613 return "roll";
614
615 case InputDevice::Axis::left_x:
616 return "left_x";
617
618 case InputDevice::Axis::left_y:
619 return "left_y";
620
621 case InputDevice::Axis::left_trigger:
622 return "left_trigger";
623
624 case InputDevice::Axis::right_x:
625 return "right_x";
626
627 case InputDevice::Axis::right_y:
628 return "right_y";
629
630 case InputDevice::Axis::right_trigger:
631 return "right_trigger";
632
633 //case InputDevice::Axis::trigger:
634 // return "trigger";
635
636 case InputDevice::Axis::throttle:
637 return "throttle";
638
639 case InputDevice::Axis::rudder:
640 return "rudder";
641
642 case InputDevice::Axis::wheel:
643 return "wheel";
644
645 case InputDevice::Axis::accelerator:
646 return "accelerator";
647
648 case InputDevice::Axis::brake:
649 return "brake";
650
651 case InputDevice::Axis::pressure:
652 return "pressure";
653 }
654 return "**invalid**";
655}
656
657std::ostream &
658operator << (std::ostream &out, InputDevice::DeviceClass dc) {
660 return out;
661}
662
663std::ostream &
664operator << (std::ostream &out, InputDevice::Axis axis) {
665 out << InputDevice::format_axis(axis);
666 return out;
667}
Records a set of button events that happened recently.
Records a button event of some kind.
Definition buttonEvent.h:49
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
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.
is_known
True if the button state is currently known.
void output_buttons(std::ostream &out) const
Writes a one-line string of all of the current button states.
static std::string format_axis(Axis axis)
Returns a string describing the given axis enumerant.
void write_buttons(std::ostream &out, int indent_level) const
Writes a multi-line description of the current button states.
virtual void output(std::ostream &out) const
Writes a one-line string describing the device.
void poll()
Polls the input device for new activity, to ensure it contains the latest events.
bool has_pointer_event() const
Returns true if this device has a pending pointer event (a mouse movement), or false otherwise.
static std::string format_device_class(DeviceClass dc)
Returns a string describing the given device class enumerant.
void write_axes(std::ostream &out, int indent_level) const
Writes a multi-line description of the current analog axis states.
bool has_button_event() const
Returns true if this device has a pending button event (a mouse button or keyboard button down/up),...
bool debug_is_locked() const
Returns true if the current thread has locked the LightMutex, false otherwise.
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
Records a set of pointer events that happened recently.
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
set_time
Indicates the time at which the position information (pos and orient) are effective.
Definition trackerData.h:48
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PointerType
Contains the types of pointer device.
Definition pointerData.h:25