Prepare input API for multipointer support

This commit is contained in:
rdb 2018-11-30 20:29:20 +01:00
parent 447316c706
commit 585ba2d647
22 changed files with 281 additions and 131 deletions

View File

@ -557,6 +557,7 @@ init_device() {
if (test_bit(EV_REL, evtypes)) {
enable_feature(Feature::pointer);
add_pointer(PointerType::unknown, 0);
}
if (test_bit(EV_FF, evtypes)) {
@ -669,9 +670,8 @@ process_events() {
n_read /= sizeof(struct input_event);
int x = _pointer_data.get_x();
int y = _pointer_data.get_y();
bool have_pointer = false;
int rel_x = 0;
int rel_y = 0;
double time = ClockObject::get_global_clock()->get_frame_time();
int index;
@ -689,9 +689,8 @@ process_events() {
break;
case EV_REL:
if (code == REL_X) x += events[i].value;
if (code == REL_Y) y += events[i].value;
have_pointer = true;
if (code == REL_X) rel_x += events[i].value;
if (code == REL_Y) rel_y += events[i].value;
break;
case EV_ABS:
@ -728,8 +727,8 @@ process_events() {
}
}
if (have_pointer) {
set_pointer(true, x, y, time);
if (rel_x != 0 || rel_y != 0) {
pointer_moved(0, rel_x, rel_y, time);
}
return true;

View File

@ -19,6 +19,7 @@
INLINE InputDevice::
InputDevice() {
_button_events = new ButtonEventList;
_pointer_events = new PointerEventList;
}
/**
@ -142,16 +143,6 @@ has_battery() const {
return has_feature(Feature::battery);
}
/**
* Returns the PointerData associated with the input device's pointer. This
* only makes sense if has_pointer() also returns true.
*/
INLINE PointerData InputDevice::
get_pointer() const {
LightMutexHolder holder(_lock);
return _pointer_data;
}
/**
* Returns the TrackerData associated with the input device's tracker. This
* only makes sense if has_tracker() also returns true.

View File

@ -29,6 +29,7 @@ InputDevice(const std::string &name, DeviceClass dev_class) :
_is_connected(true)
{
_button_events = new ButtonEventList;
_pointer_events = new PointerEventList;
}
/**
@ -80,7 +81,7 @@ get_button_events() {
bool InputDevice::
has_pointer_event() const {
LightMutexHolder holder(_lock);
return (_pointer_events != 0);
return _pointer_events != nullptr && !_pointer_events->empty();
}
/**
@ -90,8 +91,8 @@ has_pointer_event() const {
PT(PointerEventList) InputDevice::
get_pointer_events() {
LightMutexHolder holder(_lock);
PT(PointerEventList) result = _pointer_events;
_pointer_events.clear();
PT(PointerEventList) result = new PointerEventList;
swap(_pointer_events, result);
return result;
}
@ -149,69 +150,116 @@ add_axis(Axis axis, int minimum, int maximum) {
}
/**
* Records that a mouse movement has taken place.
* Records that a new pointer was found.
*/
int InputDevice::
add_pointer(PointerType type, int id, bool primary) {
nassertr(_lock.debug_is_locked(), -1);
PointerData data;
data._id = id;
data._type = type;
int index = (int)_pointers.size();
if (_num_pointers == _pointers.size()) {
_pointers.push_back(data);
} else {
_pointers[index] = data;
}
++_num_pointers;
return index;
}
/**
* Removes a previously added pointer. If the current pressure is not zero,
* it will generate an event doing so.
*/
void InputDevice::
set_pointer(bool inwin, double x, double y, double time) {
remove_pointer(int id) {
nassertv(_lock.debug_is_locked());
_pointer_data._in_window = inwin;
_pointer_data._xpos = x;
_pointer_data._ypos = y;
if (_enable_pointer_events) {
int seq = _event_sequence++;
if (_pointer_events.is_null()) {
_pointer_events = new PointerEventList();
size_t i;
for (i = 0; i < _pointers.size(); ++i) {
if (_pointers[i]._id == id) {
break;
}
_pointer_events->add_event(_pointer_data._in_window,
_pointer_data._xpos,
_pointer_data._ypos,
seq, time);
}
if (i < _pointers.size()) {
if (_pointers[i]._pressure != 0.0) {
_pointers[i]._pressure = 0.0;
if (_enable_pointer_events) {
int seq = _event_sequence++;
double time = ClockObject::get_global_clock()->get_frame_time();
_pointer_events->add_event(_pointers[i], seq, time);
}
}
// Replace it with the last one.
if (i != _pointers.size() - 1) {
_pointers[i] = _pointers.back();
}
--_num_pointers;
}
}
/**
* Records that the mouse pointer has left the window.
* Records that pointer data for a pointer has changed. This can also be used
* to add a new pointer.
*/
void InputDevice::
set_pointer_out_of_window(double time) {
update_pointer(PointerData data, double time) {
nassertv(_lock.debug_is_locked());
_pointer_data._in_window = false;
PointerData *ptr = nullptr;
for (size_t i = 0; i < _pointers.size(); ++i) {
if (_pointers[i]._id == data._id) {
ptr = &_pointers[i];
*ptr = data;
break;
}
}
if (ptr == nullptr) {
_pointers.push_back(data);
ptr = &_pointers.back();
}
if (_enable_pointer_events) {
int seq = _event_sequence++;
if (_pointer_events.is_null()) {
_pointer_events = new PointerEventList();
}
_pointer_events->add_event(_pointer_data._in_window,
_pointer_data._xpos,
_pointer_data._ypos,
seq, time);
_pointer_events->add_event(*ptr, seq, time);
}
}
/**
* Records that a relative mouse movement has taken place.
* Records that a relative pointer movement has taken place.
*/
void InputDevice::
pointer_moved(double x, double y, double time) {
pointer_moved(int id, double x, double y, double time) {
nassertv(_lock.debug_is_locked());
_pointer_data._xpos += x;
_pointer_data._ypos += y;
PointerData *ptr = nullptr;
for (size_t i = 0; i < _pointers.size(); ++i) {
if (_pointers[i]._id == id) {
ptr = &_pointers[i];
_pointers[i]._xpos = x;
_pointers[i]._ypos = y;
break;
}
}
nassertv_always(ptr != nullptr);
if (device_cat.is_spam() && (x != 0 || y != 0)) {
device_cat.spam()
<< "Pointer moved by " << x << " x " << y << "\n";
<< "Pointer " << id << " moved by " << x << " x " << y << "\n";
}
if (_enable_pointer_events) {
int seq = _event_sequence++;
if (_pointer_events.is_null()) {
_pointer_events = new PointerEventList();
}
_pointer_events->add_event(_pointer_data._in_window,
_pointer_data._xpos,
_pointer_data._ypos,
_pointer_events->add_event(ptr->_in_window,
ptr->_xpos,
ptr->_ypos,
x, y, seq, time);
}
}

View File

@ -201,7 +201,6 @@ public:
INLINE bool has_vibration() const;
INLINE bool has_battery() const;
INLINE PointerData get_pointer() const;
INLINE TrackerData get_tracker() const;
INLINE BatteryData get_battery() const;
@ -244,7 +243,6 @@ PUBLISHED:
INLINE bool has_feature(Feature feature) const;
// Getters for the various types of device data.
MAKE_PROPERTY2(pointer, has_pointer, get_pointer);
MAKE_PROPERTY2(tracker, has_tracker, get_tracker);
MAKE_PROPERTY2(battery, has_battery, get_battery);
@ -285,10 +283,10 @@ protected:
int add_axis(Axis axis, int minimum, int maximum, bool centered);
int add_axis(Axis axis, int minimum, int maximum);
void set_pointer(bool inwin, double x, double y, double time);
void set_pointer_out_of_window(double time);
void pointer_moved(double x, double y, double time);
int add_pointer(PointerType type, int id, bool primary = false);
void remove_pointer(int id);
void update_pointer(PointerData data, double time);
void pointer_moved(int id, double x, double y, double time);
void button_changed(int index, bool down);
void axis_changed(int index, int value);
void set_axis_value(int index, double state);
@ -323,6 +321,10 @@ protected:
PT(ButtonEventList) _button_events;
PT(PointerEventList) _pointer_events;
size_t _num_pointers = 0;
typedef pvector<PointerData> Pointers;
Pointers _pointers;
PUBLISHED:
typedef pvector<ButtonState> Buttons;
typedef pvector<AxisState> Axes;

View File

@ -363,7 +363,7 @@ get_pointer(int device) const {
{
LightMutexHolder holder(_input_lock);
nassertr(device >= 0 && device < (int)_input_devices.size(), MouseData());
result = _input_devices[device]->get_pointer();
result = ((const GraphicsWindowInputDevice *)_input_devices[device].p())->get_pointer();
}
return result;
}

View File

@ -12,23 +12,27 @@
*/
/**
* To be called by a particular kind of GraphicsWindow to indicate that the
* pointer is within the window, at the given pixel coordinates.
* Returns the PointerData associated with the input device's pointer. This
* only makes sense if has_pointer() also returns true.
*/
INLINE void GraphicsWindowInputDevice::
set_pointer_in_window(double x, double y, double time) {
INLINE PointerData GraphicsWindowInputDevice::
get_pointer() const {
LightMutexHolder holder(_lock);
InputDevice::set_pointer(true, x, y, time);
if (!_pointers.empty()) {
return _pointers[0];
} else {
return PointerData();
}
}
/**
* To be called by a particular kind of GraphicsWindow to indicate that the
* pointer is no longer within the window.
* pointer data has changed.
*/
INLINE void GraphicsWindowInputDevice::
set_pointer_out_of_window(double time) {
update_pointer(PointerData data, double time) {
LightMutexHolder holder(_lock);
InputDevice::set_pointer_out_of_window(time);
InputDevice::update_pointer(std::move(data), time);
}
/**
@ -38,5 +42,15 @@ set_pointer_out_of_window(double time) {
INLINE void GraphicsWindowInputDevice::
pointer_moved(double x, double y, double time) {
LightMutexHolder holder(_lock);
InputDevice::pointer_moved(x, y, time);
InputDevice::pointer_moved(0, x, y, time);
}
/**
* To be called by a particular kind of GraphicsWindow to indicate that the
* pointer no longer exists.
*/
INLINE void GraphicsWindowInputDevice::
remove_pointer(int id) {
LightMutexHolder holder(_lock);
InputDevice::remove_pointer(id);
}

View File

@ -34,6 +34,7 @@ GraphicsWindowInputDevice(GraphicsWindow *host, const string &name, bool pointer
{
if (pointer) {
enable_feature(Feature::pointer);
add_pointer(PointerType::mouse, 0);
}
if (keyboard) {
enable_feature(Feature::keyboard);
@ -156,3 +157,45 @@ raw_button_up(ButtonHandle button, double time) {
LightMutexHolder holder(_lock);
_button_events->add_event(ButtonEvent(button, ButtonEvent::T_raw_up, time));
}
/**
* To be called by a particular kind of GraphicsWindow to indicate that the
* pointer is within the window, at the given pixel coordinates.
*/
void GraphicsWindowInputDevice::
set_pointer_in_window(double x, double y, double time) {
LightMutexHolder holder(_lock);
PointerData data = _pointers[0];
data._id = 0;
data._type = PointerType::mouse;
data._xpos = x;
data._ypos = y;
data._in_window = true;
InputDevice::update_pointer(data, time);
}
/**
* To be called by a particular kind of GraphicsWindow to indicate that the
* pointer is no longer within the window.
*/
void GraphicsWindowInputDevice::
set_pointer_out_of_window(double time) {
LightMutexHolder holder(_lock);
if (_pointers.empty()) {
return;
}
_pointers[0]._in_window = false;
_pointers[0]._pressure = 0.0;
if (_enable_pointer_events) {
int seq = _event_sequence++;
if (_pointer_events.is_null()) {
_pointer_events = new PointerEventList();
}
_pointer_events->add_event(_pointers[0]._in_window,
_pointers[0]._xpos,
_pointers[0]._ypos,
seq, time);
}
}

View File

@ -53,9 +53,12 @@ PUBLISHED:
void raw_button_down(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time());
void raw_button_up(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time());
INLINE void set_pointer_in_window(double x, double y, double time = ClockObject::get_global_clock()->get_frame_time());
INLINE void set_pointer_out_of_window(double time = ClockObject::get_global_clock()->get_frame_time());
INLINE PointerData get_pointer() const;
void set_pointer_in_window(double x, double y, double time = ClockObject::get_global_clock()->get_frame_time());
void set_pointer_out_of_window(double time = ClockObject::get_global_clock()->get_frame_time());
INLINE void update_pointer(PointerData data, double time = ClockObject::get_global_clock()->get_frame_time());
INLINE void pointer_moved(double x, double y, double time = ClockObject::get_global_clock()->get_frame_time());
INLINE void remove_pointer(int id);
private:
typedef pset<ButtonHandle> ButtonsHeld;

View File

@ -27,7 +27,7 @@ MouseAndKeyboard::
MouseAndKeyboard(GraphicsWindow *window, int device, const std::string &name) :
DataNode(name),
_window(window),
_device(device)
_device(window->get_input_device(device))
{
_pixel_xy_output = define_output("pixel_xy", EventStoreVec2::get_class_type());
_pixel_size_output = define_output("pixel_size", EventStoreVec2::get_class_type());
@ -38,6 +38,8 @@ MouseAndKeyboard(GraphicsWindow *window, int device, const std::string &name) :
_pixel_xy = new EventStoreVec2(LPoint2(0.0f, 0.0f));
_pixel_size = new EventStoreVec2(LPoint2(0.0f, 0.0f));
_xy = new EventStoreVec2(LPoint2(0.0f, 0.0f));
_device->enable_pointer_events();
}
/**
@ -47,23 +49,9 @@ MouseAndKeyboard(GraphicsWindow *window, int device, const std::string &name) :
void MouseAndKeyboard::
set_source(GraphicsWindow *window, int device) {
_window = window;
_device = device;
}
_device = window->get_input_device(device);
/**
* Returns the associated source window.
*/
PT(GraphicsWindow) MouseAndKeyboard::
get_source_window() const {
return _window;
}
/**
* Returns the associated source device.
*/
int MouseAndKeyboard::
get_source_device() const {
return _device;
//_device->enable_pointer_events();
}
/**
@ -78,7 +66,7 @@ void MouseAndKeyboard::
do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &,
DataNodeTransmit &output) {
PT(InputDevice) device = _window->get_input_device(_device);
GraphicsWindowInputDevice *device = (GraphicsWindowInputDevice *)_device.p();
if (device->has_button_event()) {
PT(ButtonEventList) bel = device->get_button_events();

View File

@ -43,9 +43,6 @@ PUBLISHED:
explicit MouseAndKeyboard(GraphicsWindow *window, int device, const std::string &name);
void set_source(GraphicsWindow *window, int device);
PT(GraphicsWindow) get_source_window() const;
int get_source_device() const;
protected:
// Inherited from DataNode
virtual void do_transmit_data(DataGraphTraverser *trav,
@ -65,7 +62,7 @@ private:
PT(EventStoreVec2) _xy;
PT(GraphicsWindow) _window;
int _device;
PT(InputDevice) _device;
public:
static TypeHandle get_class_type() {

View File

@ -11,24 +11,6 @@
* @date 2007-09-20
*/
/**
*
*/
INLINE PointerEvent::
PointerEvent() :
_in_window(false),
_xpos(0),
_ypos(0),
_dx(0),
_dy(0),
_length(0.0),
_direction(0.0),
_rotation(0.0),
_sequence(0),
_time(0.0)
{
}
/**
* The equality operator does not consider time significant.
*/

View File

@ -15,7 +15,7 @@
#define POINTEREVENT_H
#include "pandabase.h"
#include "mouseData.h"
#include "pointerData.h"
class Datagram;
class DatagramIterator;
@ -25,7 +25,7 @@ class DatagramIterator;
*/
class EXPCL_PANDA_EVENT PointerEvent {
public:
INLINE PointerEvent();
PointerEvent() = default;
INLINE bool operator == (const PointerEvent &other) const;
INLINE bool operator != (const PointerEvent &other) const;
@ -37,16 +37,19 @@ public:
void read_datagram(DatagramIterator &scan);
public:
bool _in_window;
int _xpos;
int _ypos;
double _dx;
double _dy;
double _length;
double _direction;
double _rotation;
int _sequence;
double _time;
bool _in_window = false;
int _id = 0;
PointerType _type = PointerType::unknown;
double _xpos = 0.0;
double _ypos = 0.0;
double _dx = 0.0;
double _dy = 0.0;
double _length = 0.0;
double _direction = 0.0;
double _pressure = 0.0;
double _rotation = 0.0;
int _sequence = 0;
double _time = 0.0;
};
INLINE std::ostream &operator << (std::ostream &out, const PointerEvent &pe) {

View File

@ -43,6 +43,22 @@ get_num_events() const {
return _events.size();
}
/**
* Returns true if the list is empty.
*/
INLINE bool PointerEventList::
empty() const {
return _events.empty();
}
/**
* Returns the nth event in the list.
*/
INLINE const PointerEvent &PointerEventList::
get_event(size_t n) const {
return _events[n];
}
/**
* Get the in-window flag of the nth event.
*/

View File

@ -76,6 +76,42 @@ write(std::ostream &out, int indent_level) const {
}
}
/**
* Adds a new event from the given PointerData object.
*/
void PointerEventList::
add_event(const PointerData &data, int seq, double time) {
PointerEvent pe;
pe._in_window = data._in_window;
pe._type = data._type;
pe._id = data._id;
pe._xpos = data._xpos;
pe._ypos = data._ypos;
pe._pressure = data._pressure;
pe._sequence = seq;
pe._time = time;
if (_events.size() > 0) {
pe._dx = data._xpos - _events.back()._xpos;
pe._dy = data._ypos - _events.back()._ypos;
double ddx = pe._dx;
double ddy = pe._dy;
pe._length = csqrt(ddx*ddx + ddy*ddy);
if (pe._length > 0.0) {
pe._direction = normalize_angle(rad_2_deg(catan2(-ddy,ddx)));
} else {
pe._direction = _events.back()._direction;
}
pe._rotation = delta_angle(_events.back()._direction, pe._direction);
} else {
pe._dx = 0;
pe._dy = 0;
pe._length = 0.0;
pe._direction = 0.0;
pe._rotation = 0.0;
}
_events.push_back(pe);
}
/**
* Adds a new event to the end of the list. Automatically calculates the dx,
* dy, length, direction, and rotation for all but the first event.

View File

@ -48,6 +48,7 @@ PUBLISHED:
INLINE void clear();
INLINE void pop_front();
void add_event(const PointerData &data, int seq, double time);
void add_event(bool in_win, int xpos, int ypos, int seq, double time);
void add_event(bool in_win, int xpos, int ypos, double xdelta, double ydelta,
int seq, double time);
@ -60,6 +61,9 @@ public:
INLINE PointerEventList(const PointerEventList &copy);
INLINE void operator = (const PointerEventList &copy);
INLINE bool empty() const;
INLINE const PointerEvent &get_event(size_t n) const;
virtual void output(std::ostream &out) const;
void write(std::ostream &out, int indent_level = 0) const;

View File

@ -47,6 +47,14 @@ get_id() const {
return _id;
}
/**
* Returns the type of pointing device.
*/
INLINE PointerType PointerData::
get_type() const {
return _type;
}
/**
* Returns the pressure of the pointer. For mice, this will be 1.0 if any
* button is pressed, 0.0 otherwise.

View File

@ -18,6 +18,19 @@
#include "modifierButtons.h"
BEGIN_PUBLISH
/**
* Contains the types of pointer device.
*/
enum class PointerType {
unknown,
mouse,
finger,
stylus,
eraser,
};
END_PUBLISH
/**
* Holds the data that might be generated by a 2-d pointer input device, such
* as the mouse in the GraphicsWindow.
@ -30,6 +43,7 @@ PUBLISHED:
public:
INLINE int get_id() const;
INLINE PointerType get_type() const;
INLINE double get_pressure() const;
void output(std::ostream &out) const;
@ -37,6 +51,7 @@ public:
PUBLISHED:
MAKE_PROPERTY(x, get_x);
MAKE_PROPERTY(y, get_y);
MAKE_PROPERTY(type, get_type);
MAKE_PROPERTY(id, get_id);
MAKE_PROPERTY(in_window, get_in_window);
MAKE_PROPERTY(pressure, get_pressure);
@ -46,6 +61,7 @@ public:
double _xpos = 0.0;
double _ypos = 0.0;
double _pressure = 0.0;
PointerType _type = PointerType::unknown;
int _id = 0;
};

View File

@ -542,7 +542,7 @@ get_trail_log() const {
* frame. The trail log is updated once per frame, during the process_events
* operation.
*/
INLINE int MouseWatcher::
INLINE size_t MouseWatcher::
num_trail_recent() const {
return _num_trail_recent;
}

View File

@ -1326,7 +1326,7 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
const PointerEventList *this_pointer_events;
DCAST_INTO_V(this_pointer_events, input.get_data(_pointer_events_input).get_ptr());
_num_trail_recent = this_pointer_events->get_num_events();
for (int i = 0; i < _num_trail_recent; i++) {
for (size_t i = 0; i < _num_trail_recent; i++) {
bool in_win = this_pointer_events->get_in_window(i);
int xpos = this_pointer_events->get_xpos(i);
int ypos = this_pointer_events->get_ypos(i);

View File

@ -138,7 +138,7 @@ PUBLISHED:
INLINE const std::string &get_inactivity_timeout_event() const;
INLINE CPT(PointerEventList) get_trail_log() const;
INLINE int num_trail_recent() const;
INLINE size_t num_trail_recent() const;
void set_trail_log_duration(double duration);
PT(GeomNode) get_trail_node();
void clear_trail_node();
@ -220,7 +220,7 @@ private:
LVecBase4 _frame;
PT(PointerEventList) _trail_log;
int _num_trail_recent;
size_t _num_trail_recent;
double _trail_log_duration;
PT(GeomNode) _trail_node;

View File

@ -130,7 +130,7 @@ get_pointer(int device) const {
LightMutexHolder holder(_input_lock);
nassertr(device >= 0 && device < (int)_input_devices.size(), MouseData());
result = _input_devices[device]->get_pointer();
result = ((const GraphicsWindowInputDevice *)_input_devices[device].p())->get_pointer();
// We recheck this immediately to get the most up-to-date value.
POINT cpos;

View File

@ -150,7 +150,7 @@ get_pointer(int device) const {
LightMutexHolder holder(_input_lock);
nassertr(device >= 0 && device < (int)_input_devices.size(), MouseData());
result = _input_devices[device]->get_pointer();
result = ((const GraphicsWindowInputDevice *)_input_devices[device].p())->get_pointer();
// We recheck this immediately to get the most up-to-date value, but we
// won't bother waiting for the lock if we can't.