diff --git a/panda/src/device/mouseAndKeyboard.cxx b/panda/src/device/mouseAndKeyboard.cxx index 09f19f1e2e..ba51b5d8f6 100644 --- a/panda/src/device/mouseAndKeyboard.cxx +++ b/panda/src/device/mouseAndKeyboard.cxx @@ -40,11 +40,13 @@ MouseAndKeyboard(GraphicsWindow *window, int device, const string &name) : _pixel_size_output = define_output("pixel_size", EventStoreVec2::get_class_type()); _xy_output = define_output("xy", EventStoreVec2::get_class_type()); _button_events_output = define_output("button_events", ButtonEventList::get_class_type()); + _pointer_events_output = define_output("pointer_events", PointerEventList::get_class_type()); _pixel_xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f)); _pixel_size = new EventStoreVec2(LPoint2f(0.0f, 0.0f)); _xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f)); _button_events = new ButtonEventList; + _pointer_events = new PointerEventList; } //////////////////////////////////////////////////////////////////// @@ -60,6 +62,26 @@ set_source(GraphicsWindow *window, int device) { _device = device; } +//////////////////////////////////////////////////////////////////// +// Function: MouseAndKeyboard::get_source_window +// Access: Public +// Description: Returns the associated source window. +//////////////////////////////////////////////////////////////////// +PT(GraphicsWindow) MouseAndKeyboard:: +get_source_window() const { + return _window; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseAndKeyboard::get_source_device +// Access: Public +// Description: Returns the associated source device. +//////////////////////////////////////////////////////////////////// +int MouseAndKeyboard:: +get_source_device() const { + return _device; +} + //////////////////////////////////////////////////////////////////// // Function: MouseAndKeyboard::do_transmit_data // Access: Protected, Virtual @@ -85,6 +107,15 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &, } output.set_data(_button_events_output, EventParameter(_button_events)); } + if (_window->has_pointer_event(_device)) { + // Fill up the pointer events. + _pointer_events->clear(); + while (_window->has_pointer_event(_device)) { + PointerEvent be = _window->get_pointer_event(_device); + _pointer_events->add_event(be); + } + output.set_data(_pointer_events_output, EventParameter(_pointer_events)); + } // Get the window size. WindowProperties properties = _window->get_properties(); diff --git a/panda/src/device/mouseAndKeyboard.h b/panda/src/device/mouseAndKeyboard.h index 40dba3cd58..2402d6dc4f 100644 --- a/panda/src/device/mouseAndKeyboard.h +++ b/panda/src/device/mouseAndKeyboard.h @@ -23,6 +23,7 @@ #include "dataNode.h" #include "buttonEventList.h" +#include "pointerEventList.h" #include "linmath_events.h" #include "pointerTo.h" #include "graphicsWindow.h" @@ -52,6 +53,10 @@ PUBLISHED: MouseAndKeyboard(GraphicsWindow *window, int device, const string &name); void set_source(GraphicsWindow *window, int device); +public: + PT(GraphicsWindow) get_source_window() const; + int get_source_device() const; + protected: // Inherited from DataNode virtual void do_transmit_data(DataGraphTraverser *trav, @@ -64,11 +69,13 @@ private: int _pixel_size_output; int _xy_output; int _button_events_output; + int _pointer_events_output; PT(EventStoreVec2) _pixel_xy; PT(EventStoreVec2) _pixel_size; PT(EventStoreVec2) _xy; PT(ButtonEventList) _button_events; + PT(PointerEventList) _pointer_events; PT(GraphicsWindow) _window; int _device; diff --git a/panda/src/display/graphicsWindow.I b/panda/src/display/graphicsWindow.I index f45a0f7d82..7ec4049f83 100644 --- a/panda/src/display/graphicsWindow.I +++ b/panda/src/display/graphicsWindow.I @@ -44,7 +44,14 @@ is_fullscreen() const { return _properties.get_fullscreen(); } - - - +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::add_input_device +// Access: Protected +// Description: Adds a GraphicsWindowInputDevice to the vector. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindow:: +add_input_device(const GraphicsWindowInputDevice &device) { + _input_devices.push_back(device); + _input_devices.back().set_device_index(_input_devices.size()-1); +} diff --git a/panda/src/display/graphicsWindow.cxx b/panda/src/display/graphicsWindow.cxx index 708ed1e0b8..b693fcee81 100644 --- a/panda/src/display/graphicsWindow.cxx +++ b/panda/src/display/graphicsWindow.cxx @@ -337,6 +337,54 @@ has_keyboard(int device) const { return result; } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::enable_pointer_events +// Access: Published +// Description: Turn on the generation of pointer events. +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +enable_pointer_events(int device) { + MutexHolder holder(_input_lock); + nassertv(device >= 0 && device < (int)_input_devices.size()); + _input_devices[device].enable_pointer_events(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::disable_pointer_events +// Access: Published +// Description: Turn off the generation of pointer events. +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +disable_pointer_events(int device) { + MutexHolder holder(_input_lock); + nassertv(device >= 0 && device < (int)_input_devices.size()); + _input_devices[device].disable_pointer_events(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::enable_pointer_mode +// Access: Published +// Description: See GraphicsWindowInputDevice::enable_pointer_mode +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +enable_pointer_mode(int device, double speed) { + MutexHolder holder(_input_lock); + nassertv(device >= 0 && device < (int)_input_devices.size()); + _input_devices[device].enable_pointer_mode(speed); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::disable_pointer_events +// Access: Published +// Description: See GraphicsWindowInputDevice::disable_pointer_mode +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +disable_pointer_mode(int device) { + MutexHolder holder(_input_lock); + nassertv(device >= 0 && device < (int)_input_devices.size()); + _input_devices[device].disable_pointer_mode(); +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::get_pointer // Access: Published @@ -418,6 +466,43 @@ get_button_event(int device) { return result; } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::has_pointer_event +// Access: Public +// Description: Returns true if the indicated device has a pending +// pointer event (a mouse movement). If this returns +// true, the particular event may be extracted via +// get_pointer_event(). +//////////////////////////////////////////////////////////////////// +bool GraphicsWindow:: +has_pointer_event(int device) const { + bool result; + { + MutexHolder holder(_input_lock); + nassertr(device >= 0 && device < (int)_input_devices.size(), false); + result = _input_devices[device].has_pointer_event(); + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_pointer_event +// Access: Public +// Description: Assuming a previous call to has_pointer_event() +// returned true, this returns the pending pointer event. +//////////////////////////////////////////////////////////////////// +PointerEvent GraphicsWindow:: +get_pointer_event(int device) { + PointerEvent result; + { + MutexHolder holder(_input_lock); + nassertr(device >= 0 && device < (int)_input_devices.size(), PointerEvent()); + nassertr(_input_devices[device].has_pointer_event(), PointerEvent()); + result = _input_devices[device].get_pointer_event(); + } + return result; +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::verify_window_sizes // Access: Public, Virtual diff --git a/panda/src/display/graphicsWindow.h b/panda/src/display/graphicsWindow.h index 3ec9c0b4ec..0547fff55d 100644 --- a/panda/src/display/graphicsWindow.h +++ b/panda/src/display/graphicsWindow.h @@ -73,6 +73,11 @@ PUBLISHED: bool has_keyboard(int device) const; + void enable_pointer_events(int device); + void disable_pointer_events(int device); + void enable_pointer_mode(int device, double speed); + void disable_pointer_mode(int device); + MouseData get_pointer(int device) const; virtual bool move_pointer(int device, int x, int y); virtual void close_ime(); @@ -81,6 +86,8 @@ public: // No need to publish these. bool has_button_event(int device) const; ButtonEvent get_button_event(int device); + bool has_pointer_event(int device) const; + PointerEvent get_pointer_event(int device); virtual int verify_window_sizes(int numsizes, int *dimen); @@ -115,6 +122,7 @@ private: static unsigned int parse_color_mask(const string &word); protected: + INLINE void add_input_device(const GraphicsWindowInputDevice &device); typedef vector_GraphicsWindowInputDevice InputDevices; InputDevices _input_devices; Mutex _input_lock; diff --git a/panda/src/display/graphicsWindowInputDevice.I b/panda/src/display/graphicsWindowInputDevice.I index 51d41f9da6..d6edb09408 100644 --- a/panda/src/display/graphicsWindowInputDevice.I +++ b/panda/src/display/graphicsWindowInputDevice.I @@ -67,6 +67,51 @@ get_pointer() const { return _mouse_data; } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::get_raw_pointer +// Access: Public +// Description: Returns the MouseData associated with the input +// device's pointer, in raw form (ie, prior to any +// pointer_mode interpretation). +//////////////////////////////////////////////////////////////////// +INLINE const MouseData &GraphicsWindowInputDevice:: +get_raw_pointer() const { + return _true_mouse_data; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::set_device_index +// Access: Public +// Description: Set the device index. This is reported in pointer +// events. The device index will be equal to the position +// of the GraphicsWindowInputDevice in the window's list. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +set_device_index(int index) { + _device_index = index; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::enable_pointer_events +// Access: Public +// Description: Enables the generation of mouse-movement events. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +enable_pointer_events() { + _enable_pointer_events = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::disable_pointer_events +// Access: Public +// Description: Disables the generation of mouse-movement events. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindowInputDevice:: +disable_pointer_events() { + _enable_pointer_events = false; + _pointer_events.clear(); +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsWindowInputDevice::set_pointer_in_window // Access: Public @@ -75,10 +120,8 @@ get_pointer() const { // the given pixel coordinates. //////////////////////////////////////////////////////////////////// INLINE void GraphicsWindowInputDevice:: -set_pointer_in_window(int x, int y) { - _mouse_data._in_window = true; - _mouse_data._xpos = x; - _mouse_data._ypos = y; +set_pointer_in_window(int x, int y, double time) { + set_pointer(true, x, y, time); } //////////////////////////////////////////////////////////////////// @@ -89,8 +132,8 @@ set_pointer_in_window(int x, int y) { // window. //////////////////////////////////////////////////////////////////// INLINE void GraphicsWindowInputDevice:: -set_pointer_out_of_window() { - _mouse_data._in_window = false; +set_pointer_out_of_window(double time) { + set_pointer(false, _mouse_data._xpos, _mouse_data._ypos, time); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/graphicsWindowInputDevice.cxx b/panda/src/display/graphicsWindowInputDevice.cxx index 685bd6b983..21e79ceeb4 100644 --- a/panda/src/display/graphicsWindowInputDevice.cxx +++ b/panda/src/display/graphicsWindowInputDevice.cxx @@ -45,9 +45,17 @@ // the named constructors, below. //////////////////////////////////////////////////////////////////// GraphicsWindowInputDevice:: -GraphicsWindowInputDevice(const string &name, int flags) : +GraphicsWindowInputDevice(GraphicsWindow *host, const string &name, int flags) : + _host(host), _name(name), - _flags(flags) + _flags(flags), + _device_index(0), + _event_sequence(0), + _pointer_mode_enable(false), + _pointer_speed(1.0), + _pointer_true_x(0.0), + _pointer_true_y(0.0), + _enable_pointer_events(false) { } @@ -58,8 +66,8 @@ GraphicsWindowInputDevice(const string &name, int flags) : // only has a pointing device, no keyboard. //////////////////////////////////////////////////////////////////// GraphicsWindowInputDevice GraphicsWindowInputDevice:: -pointer_only(const string &name) { - return GraphicsWindowInputDevice(name, IDF_has_pointer); +pointer_only(GraphicsWindow *host, const string &name) { + return GraphicsWindowInputDevice(host, name, IDF_has_pointer); } //////////////////////////////////////////////////////////////////// @@ -69,8 +77,8 @@ pointer_only(const string &name) { // only has a keyboard, no pointing device. //////////////////////////////////////////////////////////////////// GraphicsWindowInputDevice GraphicsWindowInputDevice:: -keyboard_only(const string &name) { - return GraphicsWindowInputDevice(name, IDF_has_keyboard); +keyboard_only(GraphicsWindow *host, const string &name) { + return GraphicsWindowInputDevice(host, name, IDF_has_keyboard); } //////////////////////////////////////////////////////////////////// @@ -80,9 +88,9 @@ keyboard_only(const string &name) { // has both a keyboard and pointer. //////////////////////////////////////////////////////////////////// GraphicsWindowInputDevice GraphicsWindowInputDevice:: -pointer_and_keyboard(const string &name) { +pointer_and_keyboard(GraphicsWindow *host, const string &name) { return - GraphicsWindowInputDevice(name, IDF_has_pointer | IDF_has_keyboard); + GraphicsWindowInputDevice(host, name, IDF_has_pointer | IDF_has_keyboard); } //////////////////////////////////////////////////////////////////// @@ -92,10 +100,20 @@ pointer_and_keyboard(const string &name) { //////////////////////////////////////////////////////////////////// GraphicsWindowInputDevice:: GraphicsWindowInputDevice(const GraphicsWindowInputDevice ©) : + _host(copy._host), _name(copy._name), _flags(copy._flags), + _device_index(copy._device_index), + _event_sequence(copy._event_sequence), + _pointer_mode_enable(copy._pointer_mode_enable), + _pointer_speed(copy._pointer_speed), + _pointer_true_x(copy._pointer_true_x), + _pointer_true_y(copy._pointer_true_y), + _enable_pointer_events(copy._enable_pointer_events), _mouse_data(copy._mouse_data), - _button_events(copy._button_events) + _true_mouse_data(copy._true_mouse_data), + _button_events(copy._button_events), + _pointer_events(copy._pointer_events) { } @@ -106,10 +124,20 @@ GraphicsWindowInputDevice(const GraphicsWindowInputDevice ©) : //////////////////////////////////////////////////////////////////// void GraphicsWindowInputDevice:: operator = (const GraphicsWindowInputDevice ©) { + _host = copy._host; _name = copy._name; _flags = copy._flags; + _device_index = copy._device_index; + _event_sequence = copy._event_sequence; + _pointer_mode_enable = copy._pointer_mode_enable; + _pointer_speed = copy._pointer_speed; + _pointer_true_x = copy._pointer_true_x; + _pointer_true_y = copy._pointer_true_y; + _enable_pointer_events = copy._enable_pointer_events; _mouse_data = copy._mouse_data; + _true_mouse_data = copy._true_mouse_data; _button_events = copy._button_events; + _pointer_events = copy._pointer_events; } //////////////////////////////////////////////////////////////////// @@ -148,6 +176,112 @@ get_button_event() { return be; } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::has_pointer_event +// Access: Public +// Description: Returns true if this device has a pending pointer +// event (a mouse movement), or false otherwise. If +// this returns true, the particular event may be +// extracted via get_pointer_event(). +//////////////////////////////////////////////////////////////////// +bool GraphicsWindowInputDevice:: +has_pointer_event() const { + return !_pointer_events.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::get_pointer_event +// Access: Public +// Description: Assuming a previous call to has_pointer_event() +// returned true, this returns the pending pointer event. +//////////////////////////////////////////////////////////////////// +PointerEvent GraphicsWindowInputDevice:: +get_pointer_event() { + PointerEvent be = _pointer_events.front(); + _pointer_events.pop_front(); + return be; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::enable_pointer_mode +// Access: Public +// Description: There are two modes: raw mode, and pointer mode. +// In pointer mode, the mouse stops when it reaches the +// edges of the window. In raw mode, the mouse ignores +// the screen boundaries and can continue indefinitely, +// even into negative coordinates. In raw mode, each +// "blip" from the mouse hardware corresponds to a +// change of 1 unit in the mouse's (x,y) coordinate. +// In pointer mode, a variety of speed adjustment factors +// and concepts like "mouse acceleration" may be applied. +// +// Mouse zero represents the system mouse pointer. This +// is by definition a pointer, not a raw mouse. It is +// an error to try to enable or disable pointer mode on +// mouse zero. +//////////////////////////////////////////////////////////////////// +void GraphicsWindowInputDevice:: +enable_pointer_mode(double speed) { + nassertv(_device_index != 0); + _pointer_mode_enable = true; + _pointer_speed = speed; + _pointer_true_x = _host->get_x_size() * 0.5; + _pointer_true_y = _host->get_y_size() * 0.5; + _mouse_data._in_window = true; + _mouse_data._xpos = int(_pointer_true_x + 0.5); + _mouse_data._ypos = int(_pointer_true_y + 0.5); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::disable_pointer_mode +// Access: Public +// Description: see enable_pointer_mode. +//////////////////////////////////////////////////////////////////// +void GraphicsWindowInputDevice:: +disable_pointer_mode() { + nassertv(_device_index != 0); + _pointer_mode_enable = false; + _pointer_speed = 1.0; + _pointer_true_x = 0.0; + _pointer_true_y = 0.0; + _mouse_data = _true_mouse_data; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::set_pointer +// Access: Public +// Description: Records that a mouse movement has taken place. +//////////////////////////////////////////////////////////////////// +void GraphicsWindowInputDevice:: +set_pointer(bool inwin, int x, int y, double time) { + int delta_x = x - _true_mouse_data._xpos; + int delta_y = y - _true_mouse_data._ypos; + _true_mouse_data._in_window = inwin; + _true_mouse_data._xpos = x; + _true_mouse_data._ypos = y; + + if (_pointer_mode_enable) { + _pointer_true_x += (delta_x * _pointer_speed); + _pointer_true_y += (delta_y * _pointer_speed); + double xhi = _host->get_x_size(); + double yhi = _host->get_y_size(); + if (_pointer_true_x < 0.0) _pointer_true_x = 0.0; + if (_pointer_true_y < 0.0) _pointer_true_y = 0.0; + if (_pointer_true_x > xhi) _pointer_true_x = xhi; + if (_pointer_true_y > yhi) _pointer_true_y = yhi; + _mouse_data._in_window = true; + _mouse_data._xpos = int(_pointer_true_x + 0.5); + _mouse_data._ypos = int(_pointer_true_y + 0.5); + } else { + _mouse_data = _true_mouse_data; + } + + if (_enable_pointer_events) { + int seq = _event_sequence ++; + _pointer_events.push_back(PointerEvent(_device_index, _mouse_data, seq, time)); + } +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsWindowInputDevice::button_down // Access: Public diff --git a/panda/src/display/graphicsWindowInputDevice.h b/panda/src/display/graphicsWindowInputDevice.h index ba285f30ab..c07e6c623a 100644 --- a/panda/src/display/graphicsWindowInputDevice.h +++ b/panda/src/display/graphicsWindowInputDevice.h @@ -22,6 +22,7 @@ #include "pandabase.h" #include "buttonEvent.h" +#include "pointerEvent.h" #include "mouseData.h" #include "clockObject.h" @@ -38,26 +39,37 @@ //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_DISPLAY GraphicsWindowInputDevice { private: - GraphicsWindowInputDevice(const string &name, int flags); + GraphicsWindowInputDevice(GraphicsWindow *host, const string &name, int flags); public: - static GraphicsWindowInputDevice pointer_only(const string &name); - static GraphicsWindowInputDevice keyboard_only(const string &name); - static GraphicsWindowInputDevice pointer_and_keyboard(const string &name); - + static GraphicsWindowInputDevice pointer_only(GraphicsWindow *host, const string &name); + static GraphicsWindowInputDevice keyboard_only(GraphicsWindow *host, const string &name); + static GraphicsWindowInputDevice pointer_and_keyboard(GraphicsWindow *host, const string &name); + INLINE GraphicsWindowInputDevice(); GraphicsWindowInputDevice(const GraphicsWindowInputDevice ©); void operator = (const GraphicsWindowInputDevice ©); ~GraphicsWindowInputDevice(); - + INLINE string get_name() const; INLINE bool has_pointer() const; INLINE bool has_keyboard() const; + INLINE void set_device_index(int index); + INLINE const MouseData &get_pointer() const; - + INLINE const MouseData &get_raw_pointer() const; + + INLINE void enable_pointer_events(); + INLINE void disable_pointer_events(); + + INLINE void enable_pointer_mode(double speed); + INLINE void disable_pointer_mode(); + bool has_button_event() const; ButtonEvent get_button_event(); + bool has_pointer_event() const; + PointerEvent get_pointer_event(); public: // The following interface is for the various kinds of @@ -69,8 +81,9 @@ public: void candidate(const wstring &candidate_string, size_t highlight_start, size_t higlight_end, size_t cursor_pos); - INLINE void set_pointer_in_window(int x, int y); - INLINE void set_pointer_out_of_window(); + INLINE void set_pointer_in_window(int x, int 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()); + void set_pointer(bool inwin, int x, int y, double time); public: // We need these methods to make VC++ happy when we try to @@ -86,11 +99,25 @@ private: IDF_has_keyboard = 0x02 }; typedef pdeque ButtonEvents; - + typedef pdeque PointerEvents; + + GraphicsWindow *_host; + string _name; int _flags; + int _device_index; + int _event_sequence; + + bool _pointer_mode_enable; + double _pointer_speed; + double _pointer_true_x; + double _pointer_true_y; + + bool _enable_pointer_events; MouseData _mouse_data; + MouseData _true_mouse_data; ButtonEvents _button_events; + PointerEvents _pointer_events; }; #include "graphicsWindowInputDevice.I" diff --git a/panda/src/event/pointerEvent.I b/panda/src/event/pointerEvent.I index cb9aec23f7..05dd326aee 100644 --- a/panda/src/event/pointerEvent.I +++ b/panda/src/event/pointerEvent.I @@ -24,7 +24,8 @@ //////////////////////////////////////////////////////////////////// INLINE PointerEvent:: PointerEvent() : - _pointer(-1), + _device(-1), + _sequence(0), _time(0.0) { } @@ -35,9 +36,10 @@ PointerEvent() : // Description: //////////////////////////////////////////////////////////////////// INLINE PointerEvent:: -PointerEvent(int pointer, const MouseData &data, double time) : - _pointer(pointer), +PointerEvent(int dev, const MouseData &data, int seq, double time) : + _device(dev), _data(data), + _sequence(seq), _time(time) { } @@ -49,8 +51,9 @@ PointerEvent(int pointer, const MouseData &data, double time) : //////////////////////////////////////////////////////////////////// INLINE PointerEvent:: PointerEvent(const PointerEvent ©) : - _pointer(copy._pointer), + _device(copy._device), _data(copy._data), + _sequence(copy._sequence), _time(copy._time) { } @@ -62,8 +65,9 @@ PointerEvent(const PointerEvent ©) : //////////////////////////////////////////////////////////////////// INLINE void PointerEvent:: operator = (const PointerEvent ©) { - _pointer = copy._pointer; + _device = copy._device; _data = copy._data; + _sequence = copy._sequence; _time = copy._time; } @@ -75,9 +79,10 @@ operator = (const PointerEvent ©) { //////////////////////////////////////////////////////////////////// INLINE bool PointerEvent:: operator == (const PointerEvent &other) const { - return (_pointer == other._pointer && + return (_device == other._device && _data._xpos == other._data._xpos && _data._ypos == other._data._ypos && + _sequence == other._sequence && _data._in_window == other._data._in_window); } @@ -98,8 +103,11 @@ operator != (const PointerEvent &other) const { //////////////////////////////////////////////////////////////////// INLINE bool PointerEvent:: operator < (const PointerEvent &other) const { - if (_pointer != other._pointer) { - return _pointer < other._pointer; + if (_device != other._device) { + return _device < other._device; + } + if (_sequence != other._sequence) { + return _sequence < other._sequence; } if (_data._xpos != other._data._xpos) { return _data._xpos < other._data._xpos; diff --git a/panda/src/event/pointerEvent.cxx b/panda/src/event/pointerEvent.cxx index eca95e329d..c70d16dc3c 100644 --- a/panda/src/event/pointerEvent.cxx +++ b/panda/src/event/pointerEvent.cxx @@ -27,9 +27,8 @@ //////////////////////////////////////////////////////////////////// void PointerEvent:: output(ostream &out) const { - out << "pointer " << _pointer << " to " - << _data._xpos << "," << _data._ypos - << (_data._in_window ? " in" : " out"); + out << (_data._in_window ? "In@" : "Out@") + << _data._xpos << "," << _data._ypos << " "; } //////////////////////////////////////////////////////////////////// @@ -39,10 +38,11 @@ output(ostream &out) const { //////////////////////////////////////////////////////////////////// void PointerEvent:: write_datagram(Datagram &dg) const { - dg.add_int8(_pointer); + dg.add_int8(_device); dg.add_bool(_data._in_window); dg.add_int32(_data._xpos); dg.add_int32(_data._ypos); + dg.add_int32(_sequence); } //////////////////////////////////////////////////////////////////// @@ -52,9 +52,10 @@ write_datagram(Datagram &dg) const { //////////////////////////////////////////////////////////////////// void PointerEvent:: read_datagram(DatagramIterator &scan) { - _pointer = scan.get_int8(); + _device = scan.get_int8(); _data._in_window = scan.get_bool(); _data._xpos = scan.get_int32(); _data._ypos = scan.get_int32(); + _sequence = scan.get_int32(); _time = 0.0; } diff --git a/panda/src/event/pointerEvent.h b/panda/src/event/pointerEvent.h index 173d44dcae..7c208c1323 100644 --- a/panda/src/event/pointerEvent.h +++ b/panda/src/event/pointerEvent.h @@ -34,7 +34,7 @@ public: INLINE PointerEvent(); INLINE PointerEvent(const PointerEvent ©); - INLINE PointerEvent(int pointer, const MouseData &data, double time); + INLINE PointerEvent(int dev, const MouseData &data, int seq, double time); INLINE void operator = (const PointerEvent ©); INLINE bool operator == (const PointerEvent &other) const; @@ -47,8 +47,9 @@ public: void read_datagram(DatagramIterator &scan); public: - int _pointer; + int _device; MouseData _data; + int _sequence; double _time; }; diff --git a/panda/src/event/pointerEventList.I b/panda/src/event/pointerEventList.I index f2754eb627..61a229cf24 100644 --- a/panda/src/event/pointerEventList.I +++ b/panda/src/event/pointerEventList.I @@ -59,7 +59,7 @@ add_event(const PointerEvent &event) { //////////////////////////////////////////////////////////////////// // Function: PointerEventList::get_num_events -// Access: Public +// Access: Published // Description: Returns the number of events in the list. //////////////////////////////////////////////////////////////////// INLINE int PointerEventList:: @@ -67,6 +67,61 @@ get_num_events() const { return _events.size(); } +//////////////////////////////////////////////////////////////////// +// Function: PointerEventList::get_x +// Access: Published +// Description: Get the x-coordinate of the nth event. +//////////////////////////////////////////////////////////////////// +INLINE int PointerEventList:: +get_x(int evt) const { + nassertr((evt >= 0) && (evt < (int)_events.size()), 0); + return _events[evt]._data._xpos; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerEventList::get_y +// Access: Published +// Description: Get the y-coordinate of the nth event. +//////////////////////////////////////////////////////////////////// +INLINE int PointerEventList:: +get_y(int evt) const { + nassertr((evt >= 0) && (evt < (int)_events.size()), 0); + return _events[evt]._data._ypos; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerEventList::get_in_window +// Access: Published +// Description: Get the in-window flag of the nth event. +//////////////////////////////////////////////////////////////////// +INLINE bool PointerEventList:: +get_in_window(int evt) const { + nassertr((evt >= 0) && (evt < (int)_events.size()), 0); + return _events[evt]._data._in_window; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerEventList::get_sequence +// Access: Published +// Description: Get the sequence number of the nth event. +//////////////////////////////////////////////////////////////////// +INLINE int PointerEventList:: +get_sequence(int evt) const { + nassertr((evt >= 0) && (evt < (int)_events.size()), 0); + return _events[evt]._sequence; +} + +//////////////////////////////////////////////////////////////////// +// Function: PointerEventList::get_time +// Access: Published +// Description: Get the timestamp of the nth event. +//////////////////////////////////////////////////////////////////// +INLINE double PointerEventList:: +get_time(int evt) const { + nassertr((evt >= 0) && (evt < (int)_events.size()), 0); + return _events[evt]._time; +} + //////////////////////////////////////////////////////////////////// // Function: PointerEventList::get_event // Access: Public @@ -93,3 +148,13 @@ INLINE void PointerEventList:: clear() { _events.clear(); } + +//////////////////////////////////////////////////////////////////// +// Function: PointerEventList::pop_front +// Access: Public +// Description: Discards the first event on the list. +//////////////////////////////////////////////////////////////////// +INLINE void PointerEventList:: +pop_front() { + _events.pop_front(); +} diff --git a/panda/src/event/pointerEventList.cxx b/panda/src/event/pointerEventList.cxx index 41e9c071c4..392cfaae6f 100644 --- a/panda/src/event/pointerEventList.cxx +++ b/panda/src/event/pointerEventList.cxx @@ -29,7 +29,6 @@ TypeHandle PointerEventList::_type_handle; //////////////////////////////////////////////////////////////////// void PointerEventList:: add_events(const PointerEventList &other) { - _events.reserve(_events.size() + other._events.size()); Events::const_iterator ei; for (ei = other._events.begin(); ei != other._events.end(); ++ei) { _events.push_back(*ei); @@ -138,7 +137,6 @@ fillin(DatagramIterator &scan, BamReader *manager) { int num_events = scan.get_uint16(); _events.clear(); - _events.reserve(num_events); for (int i = 0; i < num_events; i++) { PointerEvent event; event.read_datagram(scan); diff --git a/panda/src/event/pointerEventList.h b/panda/src/event/pointerEventList.h index 420a2485e0..fd04de15d0 100644 --- a/panda/src/event/pointerEventList.h +++ b/panda/src/event/pointerEventList.h @@ -39,15 +39,23 @@ class DatagramIterator; // is desired. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_EVENT PointerEventList : public EventStoreValueBase { +PUBLISHED: + INLINE int get_num_events() const; + INLINE int get_x(int n) const; + INLINE int get_y(int n) const; + INLINE bool get_in_window(int n) const; + INLINE int get_sequence(int n) const; + INLINE double get_time(int n) const; + public: INLINE PointerEventList(); INLINE PointerEventList(const PointerEventList ©); INLINE void operator = (const PointerEventList ©); INLINE void add_event(const PointerEvent &event); - INLINE int get_num_events() const; INLINE const PointerEvent &get_event(int n) const; INLINE void clear(); + INLINE void pop_front(); void add_events(const PointerEventList &other); @@ -55,7 +63,7 @@ public: void write(ostream &out, int indent_level = 0) const; private: - typedef pvector Events; + typedef pdeque Events; Events _events; public: diff --git a/panda/src/glxdisplay/glxGraphicsWindow.cxx b/panda/src/glxdisplay/glxGraphicsWindow.cxx index 910de9e5a3..e5f66637c4 100644 --- a/panda/src/glxdisplay/glxGraphicsWindow.cxx +++ b/panda/src/glxdisplay/glxGraphicsWindow.cxx @@ -79,8 +79,8 @@ glxGraphicsWindow(GraphicsPipe *pipe, _net_wm_state_remove = glx_pipe->_net_wm_state_remove; GraphicsWindowInputDevice device = - GraphicsWindowInputDevice::pointer_and_keyboard("keyboard/mouse"); - _input_devices.push_back(device); + GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard/mouse"); + add_input_device(device); } //////////////////////////////////////////////////////////////////// @@ -1080,8 +1080,8 @@ open_raw_mice() inf._io_buffer = ""; _mouse_device_info.push_back(inf); GraphicsWindowInputDevice device = - GraphicsWindowInputDevice::pointer_only(full_id); - _input_devices.push_back(device); + GraphicsWindowInputDevice::pointer_only(this, full_id); + add_input_device(device); glxdisplay_cat.info() << "Raw mouse " << inf._input_device_index << " detected: " << full_id << "\n"; any_mice = true; @@ -1150,8 +1150,8 @@ poll_raw_mice() } const input_event *events = (const input_event *)(inf._io_buffer.c_str()); GraphicsWindowInputDevice &dev = _input_devices[inf._input_device_index]; - int x = dev.get_pointer().get_x(); - int y = dev.get_pointer().get_y(); + int x = _input_devices[i].get_raw_pointer().get_x(); + int y = _input_devices[i].get_raw_pointer().get_y(); for (int i=0; iwithout_region(param); throw_event_pattern(_without_pattern, region, ButtonHandle::none()); } + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_trail_log +// Access: Published +// Description: Obtain the mouse trail log. This is a PointerEventList. +// Does not make a copy, therefore, this PointerEventList +// will be updated each time process_events gets called. +// +// To use trail logging, you need to enable the +// generation of pointer events in the +// GraphicsWindowInputDevice and set the trail log +// duration in the MouseWatcher. Otherwise, the +// trail log will be empty. +//////////////////////////////////////////////////////////////////// +INLINE CPT(PointerEventList) MouseWatcher:: +get_trail_log() const { + return _trail_log; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::num_trail_recent +// Access: Published +// Description: This counter indicates how many events were added +// to the trail log this frame. The trail log is +// updated once per frame, during the process_events +// operation. +//////////////////////////////////////////////////////////////////// +INLINE int MouseWatcher:: +num_trail_recent() const { + return _num_trail_recent; +} diff --git a/panda/src/tform/mouseWatcher.cxx b/panda/src/tform/mouseWatcher.cxx index 73151299d0..fe4ca15500 100644 --- a/panda/src/tform/mouseWatcher.cxx +++ b/panda/src/tform/mouseWatcher.cxx @@ -29,6 +29,9 @@ #include "dataNodeTransmit.h" #include "transformState.h" #include "displayRegion.h" +#include "geomVertexWriter.h" +#include "geomLinestrips.h" +#include "geomPoints.h" #include "dcast.h" #include "indent.h" #include "mutexHolder.h" @@ -51,6 +54,7 @@ MouseWatcher(const string &name) : _pixel_size_input = define_input("pixel_size", EventStoreVec2::get_class_type()); _xy_input = define_input("xy", EventStoreVec2::get_class_type()); _button_events_input = define_input("button_events", ButtonEventList::get_class_type()); + _pointer_events_input = define_input("pointer_events", PointerEventList::get_class_type()); _pixel_xy_output = define_output("pixel_xy", EventStoreVec2::get_class_type()); _pixel_size_output = define_output("pixel_size", EventStoreVec2::get_class_type()); @@ -59,6 +63,7 @@ MouseWatcher(const string &name) : _pixel_xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f)); _xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f)); + _pixel_size = new EventStoreVec2(LPoint2f(0.0f, 0.0f)); _button_events = new ButtonEventList; _has_mouse = false; @@ -68,20 +73,24 @@ MouseWatcher(const string &name) : _button_down = false; _eh = (EventHandler *)NULL; _display_region = (DisplayRegion *)NULL; - + _inactivity_timeout = inactivity_timeout; _has_inactivity_timeout = !IS_NEARLY_ZERO(_inactivity_timeout); + _num_trail_recent = 0; + _trail_log_duration = 0.0; + _trail_log = new PointerEventList(); + _inactivity_timeout_event = "inactivity_timeout"; _last_activity = 0.0; _inactivity_state = IS_active; - + // When this flag is true, the mouse pointer is allowed to be // "entered" into multiple regions simultaneously; when false, it // will only be "within" multiple regions, but "entered" into the // topmost of those. _enter_multiple = false; - + // When this flag is true, moving the pointer into a region is // enough to click it. The click is simulated with mouse button // one. @@ -344,6 +353,128 @@ get_group(int n) const { return _groups[n]; } +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::set_trail_log_duration +// Access: Published +// Description: If the duration is nonzero, causes the MouseWatcher +// to log the mouse's trail. Events older than the +// specified duration are discarded. If the duration is +// zero, logging is disabled. +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +set_trail_log_duration(double duration) { + if (duration < 0.0) { + duration = 0.0; + } + _trail_log_duration = duration; + discard_excess_trail_log(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::discard_excess_trail_log +// Access: Private +// Description: Discards trail log events whose age exceed the +// desired log duration. Keeps one event that is beyond +// the specified age, because otherwise, it is not always +// possible to determine where the mouse was for the +// full logging duration. If the duration is zero, this +// method discards all trail events. +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +discard_excess_trail_log() { + if (_trail_log_duration == 0.0) { + _trail_log->clear(); + } else { + if (_trail_log->get_num_events() >= 2) { + double old = ClockObject::get_global_clock()->get_frame_time() - _trail_log_duration; + while ((_trail_log->get_num_events() >= 2)&& + (_trail_log->get_time(0) <= old)&& + (_trail_log->get_time(1) <= old)) { + _trail_log->pop_front(); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_trail_node +// Access: Published +// Description: Returns a GeomNode that represents the mouse trail. +// The intent is that you should reparent this GeomNode +// to Render2D, and then forget about it. The +// MouseWatcher will continually update the trail node. +// There is only one trail node, it does not create a +// new one each time you call get_trail_node. +// +// This is not a particularly beautiful way to render +// a mouse trail. It is intended more for debugging +// purposes than for finished applications. Even so, +// It is suggested that you might want to apply a line +// thickness and antialias mode to the line --- doing +// so makes it look a lot better. +//////////////////////////////////////////////////////////////////// +PT(GeomNode) MouseWatcher:: +get_trail_node() { + if (_trail_node == 0) { + _trail_node = new GeomNode("Mouse Trail Node"); + update_trail_node(); + } + return _trail_node; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::clear_trail_node +// Access: Published +// Description: If you have previously fetched the trail node +// using get_trail_node, then the MouseWatcher is +// continually updating the trail node every frame. +// Using clear_trail_node causes the MouseWatcher to +// forget the trail node and stop updating it. +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +clear_trail_node() { + _trail_node = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::update_trail_node +// Access: Private +// Description: Causes the trail node to represent the mouse trail. +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +update_trail_node() { + if (_trail_node == 0) { + return; + } + _trail_node->remove_all_geoms(); + + if (_trail_log->get_num_events() < 2) { + return; + } + + PT(GeomVertexData) data = new GeomVertexData + ("mouseTrailSegs", GeomVertexFormat::get_v3(), Geom::UH_static); + + GeomVertexWriter vertex(data, InternalName::get_vertex()); + + PT(GeomLinestrips) lines = new GeomLinestrips(Geom::UH_static); + + double xscale = 2.0 / _pixel_size->get_value().get_x(); + double yscale = 2.0 / _pixel_size->get_value().get_y(); + + for (int i=0; i<(int)_trail_log->get_num_events(); i++) { + double x = (_trail_log->get_x(i) * xscale) - 1.0; + double y = (_trail_log->get_y(i) * yscale) - 1.0; + vertex.add_data3f(LVecBase3f(x,0.0,-y)); + lines->add_vertex(i); + } + lines->close_primitive(); + + PT(Geom) l_geom = new Geom(data); + l_geom->add_primitive(lines); + _trail_node->add_geom(l_geom); +} + //////////////////////////////////////////////////////////////////// // Function: MouseWatcher::note_activity // Access: Published @@ -1242,6 +1373,12 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input, _internal_suppress = 0; _external_suppress = 0; + // We always pass the pixel_size data through. + EventStoreVec2 *pixel_size; + DCAST_INTO_V(pixel_size, input.get_data(_pixel_size_input).get_ptr()); + output.set_data(_pixel_size_output, pixel_size); + _pixel_size = pixel_size; + if (!input.has_data(_xy_input)) { // No mouse in the window. set_no_mouse(); @@ -1305,6 +1442,24 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input, } } + // Code for recording the mouse trail. + _num_trail_recent = 0; + if (input.has_data(_pointer_events_input) && (_trail_log_duration > 0.0)) { + 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++) { + _trail_log->add_event(this_pointer_events->get_event(i)); + } + } + if (_trail_log->get_num_events() > 0) { + discard_excess_trail_log(); + update_trail_node(); + } + if (_num_trail_recent > _trail_log->get_num_events()) { + _num_trail_recent = _trail_log->get_num_events(); + } + // If the mouse is over a particular region, or still considered // owned by a region because of a recent button-down event, that // region determines whether we suppress events below us. @@ -1481,7 +1636,4 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input, if (_button_events->get_num_events() != 0) { output.set_data(_button_events_output, EventParameter(_button_events)); } - - // We always pass the pixel_size data through. - output.set_data(_pixel_size_output, input.get_data(_pixel_size_input)); } diff --git a/panda/src/tform/mouseWatcher.h b/panda/src/tform/mouseWatcher.h index 12e8cdf830..37b57a5b61 100644 --- a/panda/src/tform/mouseWatcher.h +++ b/panda/src/tform/mouseWatcher.h @@ -30,6 +30,8 @@ #include "modifierButtons.h" #include "buttonHandle.h" #include "buttonEventList.h" +#include "pointerEvent.h" +#include "pointerEventList.h" #include "linmath_events.h" #include "bitArray.h" #include "clockObject.h" @@ -130,7 +132,13 @@ PUBLISHED: INLINE void set_inactivity_timeout_event(const string &event); INLINE const string &get_inactivity_timeout_event() const; - + + INLINE CPT(PointerEventList) get_trail_log() const; + INLINE int num_trail_recent() const; + void set_trail_log_duration(double duration); + PT(GeomNode) get_trail_node(); + void clear_trail_node(); + void note_activity(); public: @@ -184,7 +192,9 @@ protected: private: void consider_keyboard_suppress(const MouseWatcherRegion *region); - + void discard_excess_trail_log(); + void update_trail_node(); + private: // This wants to be a set, but because you cannot export sets across // dlls in windows, we will make it a vector instead @@ -198,6 +208,11 @@ private: LPoint2f _mouse_pixel; BitArray _current_buttons_down; + PT(PointerEventList) _trail_log; + int _num_trail_recent; + double _trail_log_duration; + PT(GeomNode) _trail_node; + Regions _current_regions; PT(MouseWatcherRegion) _preferred_region; PT(MouseWatcherRegion) _preferred_button_down_region; @@ -215,7 +230,7 @@ private: string _without_pattern; PT(PandaNode) _geometry; - + EventHandler *_eh; ModifierButtons _mods; DisplayRegion *_display_region; @@ -251,6 +266,7 @@ private: int _pixel_size_input; int _xy_input; int _button_events_input; + int _pointer_events_input; // outputs int _pixel_xy_output; @@ -260,6 +276,7 @@ private: PT(EventStoreVec2) _pixel_xy; PT(EventStoreVec2) _xy; + PT(EventStoreVec2) _pixel_size; PT(ButtonEventList) _button_events; public: diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index 82026cb6f7..a33e91986e 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -459,8 +459,8 @@ initialize_input_devices() { // Clear the handle array, and set up the system keyboard/mouse memset(_input_device_handle, 0, sizeof(_input_device_handle)); GraphicsWindowInputDevice device = - GraphicsWindowInputDevice::pointer_and_keyboard("keyboard/mouse"); - _input_devices.push_back(device); + GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard/mouse"); + add_input_device(device); // Try initializing the Raw Input function pointers. if (pRegisterRawInputDevices==0) { @@ -517,9 +517,9 @@ initialize_input_devices() { } if (pound2) *pound2 = '.'; _input_device_handle[_input_devices.size()] = pRawInputDeviceList[i].hDevice; - GraphicsWindowInputDevice device = GraphicsWindowInputDevice::pointer_only(psName); + GraphicsWindowInputDevice device = GraphicsWindowInputDevice::pointer_only(this, psName); device.set_pointer_in_window(0,0); - _input_devices.push_back(device); + add_input_device(device); } } } @@ -2183,8 +2183,8 @@ handle_raw_input(HRAWINPUT hraw) { if (raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { _input_devices[i].set_pointer_in_window(adjx, adjy); } else { - int oldx = _input_devices[i].get_pointer().get_x(); - int oldy = _input_devices[i].get_pointer().get_y(); + int oldx = _input_devices[i].get_raw_pointer().get_x(); + int oldy = _input_devices[i].get_raw_pointer().get_y(); _input_devices[i].set_pointer_in_window(oldx + adjx, oldy + adjy); }