From e6077a603c549f22711de107fdf283c3ea8ad707 Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 15 Sep 2006 16:27:09 +0000 Subject: [PATCH] implement MouseWatcher::inactivity_timeout --- panda/src/event/buttonEvent.cxx | 6 + panda/src/event/buttonEvent.h | 6 + panda/src/event/buttonEventList.I | 21 +++ panda/src/event/buttonEventList.h | 2 + panda/src/pgui/pgItem.I | 26 +++ panda/src/pgui/pgItem.cxx | 7 +- panda/src/pgui/pgItem.h | 2 + panda/src/putil/buttonHandle.I | 11 ++ panda/src/putil/buttonHandle.h | 1 + panda/src/tform/buttonThrower.I | 38 +++++ panda/src/tform/buttonThrower.cxx | 12 +- panda/src/tform/buttonThrower.h | 3 + panda/src/tform/driveInterface.cxx | 2 +- panda/src/tform/mouseWatcher.I | 100 +++++++++++ panda/src/tform/mouseWatcher.cxx | 216 +++++++++++++++++++----- panda/src/tform/mouseWatcher.h | 29 +++- panda/src/tform/mouseWatcherParameter.I | 29 ++++ panda/src/tform/mouseWatcherParameter.h | 3 + 18 files changed, 468 insertions(+), 46 deletions(-) diff --git a/panda/src/event/buttonEvent.cxx b/panda/src/event/buttonEvent.cxx index 8e6aa26abf..54b6c91846 100644 --- a/panda/src/event/buttonEvent.cxx +++ b/panda/src/event/buttonEvent.cxx @@ -42,6 +42,10 @@ output(ostream &out) const { out << "button " << _button << " up"; break; + case T_repeat: + out << "button " << _button << " repeat"; + break; + case T_keystroke: out << "keystroke " << _keycode; break; @@ -70,6 +74,7 @@ write_datagram(Datagram &dg) const { case T_down: case T_resume_down: case T_up: + case T_repeat: // We write the button name. This is not particularly compact, but // presumably we don't get thousands of button events per frame, and // it is robust as the button index may change between sessions but @@ -108,6 +113,7 @@ read_datagram(DatagramIterator &scan) { case T_down: case T_resume_down: case T_up: + case T_repeat: _button = ButtonRegistry::ptr()->get_button(scan.get_string()); break; diff --git a/panda/src/event/buttonEvent.h b/panda/src/event/buttonEvent.h index 1fb0831352..1772be5a85 100644 --- a/panda/src/event/buttonEvent.h +++ b/panda/src/event/buttonEvent.h @@ -70,6 +70,12 @@ public: T_resume_down, T_up, + // T_repeat is sent for each a keyrepeat event generated by the + // system, for a button that is continually held down. If you + // want to respect keyrepeat, treat T_down and T_repeat + // equivalently. + T_repeat, + // T_keystroke is a special keystroke event, and is sent along // with a Unicode keycode value, not a ButtonHandle. T_keystroke, diff --git a/panda/src/event/buttonEventList.I b/panda/src/event/buttonEventList.I index f163b2c2b7..812bff20e4 100644 --- a/panda/src/event/buttonEventList.I +++ b/panda/src/event/buttonEventList.I @@ -26,6 +26,27 @@ INLINE ButtonEventList:: ButtonEventList() { } +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventList::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ButtonEventList:: +ButtonEventList(const ButtonEventList ©) : + _events(copy._events) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonEventList::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ButtonEventList:: +operator = (const ButtonEventList ©) { + _events = copy._events; +} + //////////////////////////////////////////////////////////////////// // Function: ButtonEventList::add_event // Access: Public diff --git a/panda/src/event/buttonEventList.h b/panda/src/event/buttonEventList.h index 82494dc43c..de733cf83c 100644 --- a/panda/src/event/buttonEventList.h +++ b/panda/src/event/buttonEventList.h @@ -41,6 +41,8 @@ class DatagramIterator; class EXPCL_PANDA ButtonEventList : public EventStoreValueBase { public: INLINE ButtonEventList(); + INLINE ButtonEventList(const ButtonEventList ©); + INLINE void operator = (const ButtonEventList ©); INLINE void add_event(ButtonEvent event); INLINE int get_num_events() const; diff --git a/panda/src/pgui/pgItem.I b/panda/src/pgui/pgItem.I index 6877b9e436..d204f25d1d 100644 --- a/panda/src/pgui/pgItem.I +++ b/panda/src/pgui/pgItem.I @@ -351,6 +351,19 @@ get_press_prefix() { return "press-"; } +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_repeat_prefix +// Access: Published, Static +// Description: Returns the prefix that is used to define the repeat +// event for all PGItems. The repeat event is the +// concatenation of this string followed by a button +// name, followed by a hyphen and get_id(). +//////////////////////////////////////////////////////////////////// +INLINE string PGItem:: +get_repeat_prefix() { + return "repeat-"; +} + //////////////////////////////////////////////////////////////////// // Function: PGItem::get_release_prefix // Access: Published, Static @@ -465,6 +478,19 @@ get_press_event(const ButtonHandle &button) const { return get_press_prefix() + button.get_name() + "-" + get_id(); } +//////////////////////////////////////////////////////////////////// +// Function: PGItem::get_repeat_event +// Access: Published +// Description: Returns the event name that will be thrown when the +// item is active and the indicated mouse or keyboard +// button is continuously held down while the mouse is +// within the frame. +//////////////////////////////////////////////////////////////////// +INLINE string PGItem:: +get_repeat_event(const ButtonHandle &button) const { + return get_repeat_prefix() + button.get_name() + "-" + get_id(); +} + //////////////////////////////////////////////////////////////////// // Function: PGItem::get_release_event // Access: Published diff --git a/panda/src/pgui/pgItem.cxx b/panda/src/pgui/pgItem.cxx index 932a5d5831..22b0d44409 100644 --- a/panda/src/pgui/pgItem.cxx +++ b/panda/src/pgui/pgItem.cxx @@ -583,7 +583,12 @@ void PGItem:: press(const MouseWatcherParameter ¶m, bool background) { if (!background) { PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param); - string event = get_press_event(param.get_button()); + string event; + if (param.is_keyrepeat()) { + event = get_repeat_event(param.get_button()); + } else { + event = get_press_event(param.get_button()); + } play_sound(event); throw_event(event, EventParameter(ep)); } diff --git a/panda/src/pgui/pgItem.h b/panda/src/pgui/pgItem.h index a672d9cdd2..f22f16152e 100644 --- a/panda/src/pgui/pgItem.h +++ b/panda/src/pgui/pgItem.h @@ -139,6 +139,7 @@ PUBLISHED: INLINE static string get_focus_in_prefix(); INLINE static string get_focus_out_prefix(); INLINE static string get_press_prefix(); + INLINE static string get_repeat_prefix(); INLINE static string get_release_prefix(); INLINE static string get_keystroke_prefix(); @@ -149,6 +150,7 @@ PUBLISHED: INLINE string get_focus_in_event() const; INLINE string get_focus_out_event() const; INLINE string get_press_event(const ButtonHandle &button) const; + INLINE string get_repeat_event(const ButtonHandle &button) const; INLINE string get_release_event(const ButtonHandle &button) const; INLINE string get_keystroke_event() const; diff --git a/panda/src/putil/buttonHandle.I b/panda/src/putil/buttonHandle.I index ff4a1ccdb4..ea43d9fb69 100644 --- a/panda/src/putil/buttonHandle.I +++ b/panda/src/putil/buttonHandle.I @@ -30,6 +30,17 @@ INLINE ButtonHandle:: ButtonHandle() { } +//////////////////////////////////////////////////////////////////// +// Function: ButtonHandle::Constructor +// Access: Published +// Description: Constructs a ButtonHandle with the corresponding +// index number, which may have been returned by an +// earlier call to ButtonHandle::get_index(). +//////////////////////////////////////////////////////////////////// +INLINE ButtonHandle:: +ButtonHandle(int index) : _index(index) { +} + //////////////////////////////////////////////////////////////////// // Function: ButtonHandle::Copy Constructor // Access: Published diff --git a/panda/src/putil/buttonHandle.h b/panda/src/putil/buttonHandle.h index d55aeab633..8e8fb129df 100644 --- a/panda/src/putil/buttonHandle.h +++ b/panda/src/putil/buttonHandle.h @@ -30,6 +30,7 @@ class EXPCL_PANDA ButtonHandle { PUBLISHED: INLINE ButtonHandle(); + INLINE ButtonHandle(int index); public: INLINE ButtonHandle(const ButtonHandle ©); diff --git a/panda/src/tform/buttonThrower.I b/panda/src/tform/buttonThrower.I index 39d36c2270..7b6af11ca8 100644 --- a/panda/src/tform/buttonThrower.I +++ b/panda/src/tform/buttonThrower.I @@ -72,6 +72,38 @@ get_button_up_event() const { return _button_up_event; } +//////////////////////////////////////////////////////////////////// +// Function: ButtonThrower::set_button_repeat_event +// Access: Published +// Description: Specifies the generic event that is generated (if +// any) repeatedly while a key or button is held down. +// Unlike the specific events that are unique to each +// key, this same event name is used for *all* button +// events, and the name of the button pressed (possibly +// with modifier prefixes) will be sent as a parameter. +// +// If this string is empty, no event is generated. It +// is possible to generate both generic events and +// specific events for the same button. +// +// See also set_keystroke_event(). +//////////////////////////////////////////////////////////////////// +INLINE void ButtonThrower:: +set_button_repeat_event(const string &button_repeat_event) { + _button_repeat_event = button_repeat_event; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonThrower::get_button_repeat_event +// Access: Published +// Description: Returns the button_repeat_event that has been set on +// this ButtonThrower. See set_button_repeat_event(). +//////////////////////////////////////////////////////////////////// +INLINE const string &ButtonThrower:: +get_button_repeat_event() const { + return _button_repeat_event; +} + //////////////////////////////////////////////////////////////////// // Function: ButtonThrower::set_keystroke_event // Access: Published @@ -83,6 +115,12 @@ get_button_up_event() const { // together will generate the button event "shift-4", // but it will generate the keystroke "$". // +// If a key is held down, keyrepeat will cause the same +// keystroke event to be generated repeatedly. This is +// different from the corresponding down event, which +// will only be generated once, followed by a number of +// button repeat events. +// // This event is generated with a single wstring // parameter, which is a one-character string that // contains the keystroke generated. If this event diff --git a/panda/src/tform/buttonThrower.cxx b/panda/src/tform/buttonThrower.cxx index 8799923ad9..65e8c8b30b 100644 --- a/panda/src/tform/buttonThrower.cxx +++ b/panda/src/tform/buttonThrower.cxx @@ -307,6 +307,10 @@ do_general_event(const ButtonEvent &button_event, const string &button_name) { event_name = _button_up_event; break; + case ButtonEvent::T_repeat: + event_name = _button_repeat_event; + break; + case ButtonEvent::T_keystroke: event_name = _keystroke_event; break; @@ -388,7 +392,7 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input, const ButtonEvent &be = button_events->get_event(i); string event_name = be._button.get_name(); - if (be._type == ButtonEvent::T_down) { + if (be._type == ButtonEvent::T_down || be._type == ButtonEvent::T_repeat) { // Button down. if (!_mods.button_down(be._button)) { // We only prepend modifier names on the button-down events, @@ -398,7 +402,11 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input, if (!_throw_buttons_active || has_throw_button(_mods, be._button)) { // Process this button. - do_specific_event(event_name, be._time); + if (be._type == ButtonEvent::T_repeat) { + do_specific_event(event_name + "-repeat", be._time); + } else { + do_specific_event(event_name, be._time); + } do_general_event(be, event_name); } else { diff --git a/panda/src/tform/buttonThrower.h b/panda/src/tform/buttonThrower.h index 6dd588ed17..27561086c2 100644 --- a/panda/src/tform/buttonThrower.h +++ b/panda/src/tform/buttonThrower.h @@ -49,6 +49,8 @@ PUBLISHED: INLINE const string &get_button_down_event() const; INLINE void set_button_up_event(const string &button_up_event); INLINE const string &get_button_up_event() const; + INLINE void set_button_repeat_event(const string &button_repeat_event); + INLINE const string &get_button_repeat_event() const; INLINE void set_keystroke_event(const string &keystroke_event); INLINE const string &get_keystroke_event() const; INLINE void set_candidate_event(const string &candidate_event); @@ -92,6 +94,7 @@ private: private: string _button_down_event; string _button_up_event; + string _button_repeat_event; string _keystroke_event; string _candidate_event; string _move_event; diff --git a/panda/src/tform/driveInterface.cxx b/panda/src/tform/driveInterface.cxx index 6b2b89717c..a3092a2555 100644 --- a/panda/src/tform/driveInterface.cxx +++ b/panda/src/tform/driveInterface.cxx @@ -422,7 +422,7 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input, for (int i = 0; i < num_events; i++) { const ButtonEvent &be = button_events->get_event(i); if (be._type != ButtonEvent::T_keystroke) { - bool down = (be._type == ButtonEvent::T_down); + bool down = (be._type != ButtonEvent::T_up); if (be._button == KeyboardButton::up()) { _up_arrow.set_key(down); diff --git a/panda/src/tform/mouseWatcher.I b/panda/src/tform/mouseWatcher.I index 9df02df54b..304a3821b4 100644 --- a/panda/src/tform/mouseWatcher.I +++ b/panda/src/tform/mouseWatcher.I @@ -138,6 +138,17 @@ get_over_region(float x, float y) const { return get_over_region(LPoint2f(x, y)); } +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::is_button_down +// Access: Published +// Description: Returns true if the indicated button is currently +// being held down, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool MouseWatcher:: +is_button_down(ButtonHandle button) const { + return _inactivity_state != IS_inactive && _current_buttons_down.get_bit(button.get_index()); +} + //////////////////////////////////////////////////////////////////// // Function: MouseWatcher::set_button_down_pattern // Access: Published @@ -193,6 +204,39 @@ get_button_up_pattern() const { return _button_up_pattern; } +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::set_button_repeat_pattern +// Access: Published +// Description: Sets the pattern string that indicates how the event +// names are generated when a button is continuously +// held and generates keyrepeat "down" events. This is +// a string that may contain any of the following: +// +// %r - the name of the region the mouse is over +// %b - the name of the button pressed. +// +// The event name will be based on the in_pattern +// string specified here, with all occurrences of the +// above strings replaced with the corresponding values. +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcher:: +set_button_repeat_pattern(const string &pattern) { + _button_repeat_pattern = pattern; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_button_repeat_pattern +// Access: Published +// Description: Returns the string that indicates how event names are +// names are generated when a button is continuously +// held and generates keyrepeat "down" events. See +// set_button_repeat_pattern(). +//////////////////////////////////////////////////////////////////// +INLINE const string &MouseWatcher:: +get_button_repeat_pattern() const { + return _button_repeat_pattern; +} + //////////////////////////////////////////////////////////////////// // Function: MouseWatcher::set_enter_pattern // Access: Published @@ -463,6 +507,62 @@ has_display_region() const { return (_display_region != (DisplayRegion *)NULL); } +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::set_inactivity_timeout +// Access: Published +// Description: Sets an inactivity timeout on the mouse activity. +// When this timeout (in seconds) is exceed with no +// keyboard or mouse activity, all currently-held +// buttons are automatically released. This is intended +// to help protect against people who inadvertently (or +// intentionally) leave a keyboard key stuck down and +// then wander away from the keyboard. +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcher:: +set_inactivity_timeout(double timeout) { + _has_inactivity_timeout = true; + _inactivity_timeout = timeout; + note_activity(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::has_inactivity_timeout +// Access: Published +// Description: Returns true if an inactivity timeout has been set, +// false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool MouseWatcher:: +has_inactivity_timeout() const { + return _has_inactivity_timeout; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_inactivity_timeout +// Access: Published +// Description: Returns the inactivity timeout that has been set. +// It is an error to call this if +// has_inactivity_timeout() returns false. +//////////////////////////////////////////////////////////////////// +INLINE double MouseWatcher:: +get_inactivity_timeout() const { + nassertr(_has_inactivity_timeout, 0.0); + return _inactivity_timeout; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::clear_inactivity_timeout +// Access: Published +// Description: Removes the inactivity timeout and restores the +// MouseWatcher to its default behavior of allowing a +// key to be held indefinitely. +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcher:: +clear_inactivity_timeout() { + _has_inactivity_timeout = false; + _inactivity_timeout = 0.0; + note_activity(); +} + //////////////////////////////////////////////////////////////////// // Function: MouseWatcher::within_region // Access: Protected diff --git a/panda/src/tform/mouseWatcher.cxx b/panda/src/tform/mouseWatcher.cxx index b9871eef14..c31101c70d 100644 --- a/panda/src/tform/mouseWatcher.cxx +++ b/panda/src/tform/mouseWatcher.cxx @@ -44,7 +44,7 @@ TypeHandle MouseWatcher::_type_handle; //////////////////////////////////////////////////////////////////// MouseWatcher:: MouseWatcher(const string &name) : - DataNode(name) + DataNode(name) { _pixel_xy_input = define_input("pixel_xy", EventStoreVec2::get_class_type()); _pixel_size_input = define_input("pixel_size", EventStoreVec2::get_class_type()); @@ -67,6 +67,10 @@ MouseWatcher(const string &name) : _button_down = false; _eh = (EventHandler *)NULL; _display_region = (DisplayRegion *)NULL; + _has_inactivity_timeout = false; + _inactivity_timeout = 0.0; + _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 @@ -336,6 +340,41 @@ get_group(int n) const { return _groups[n]; } +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::note_activity +// Access: Published +// Description: Can be used in conjunction with the inactivity +// timeout to inform the MouseWatcher that the user has +// just performed some action which proves he/she is +// present. It may be necessary to call this for +// external events, such as joystick action, that the +// MouseWatcher might otherwise not know about. This +// will reset the current inactivity timer. When the +// inactivity timer reaches the length of time specified +// by set_inactivity_timeout(), with no keyboard or +// mouse activity and no calls to note_activity(), then +// any buttons held will be automatically released. +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +note_activity() { + _last_activity = ClockObject::get_global_clock()->get_frame_time(); + switch (_inactivity_state) { + case IS_active: + break; + + case IS_inactive: + _inactivity_state = IS_inactive_to_active; + break; + + case IS_active_to_inactive: + _inactivity_state = IS_active; + break; + + case IS_inactive_to_active: + break; + } +} + //////////////////////////////////////////////////////////////////// // Function: MouseWatcher::output @@ -810,11 +849,12 @@ move() { // being depressed. //////////////////////////////////////////////////////////////////// void MouseWatcher:: -press(ButtonHandle button) { +press(ButtonHandle button, bool keyrepeat) { nassertv(_lock.debug_is_locked()); MouseWatcherParameter param; param.set_button(button); + param.set_keyrepeat(keyrepeat); param.set_modifier_buttons(_mods); param.set_mouse(_mouse); @@ -828,8 +868,13 @@ press(ButtonHandle button) { if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) { _preferred_button_down_region->press(param); - throw_event_pattern(_button_down_pattern, - _preferred_button_down_region, button); + if (keyrepeat) { + throw_event_pattern(_button_repeat_pattern, + _preferred_button_down_region, button); + } else { + throw_event_pattern(_button_down_pattern, + _preferred_button_down_region, button); + } } } else { @@ -1186,6 +1231,8 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input, Thread *current_thread = trav->get_current_thread(); MutexHolder holder(_lock); + bool activity = false; + // Initially, we do not suppress any events to objects below us in // the data graph. _internal_suppress = 0; @@ -1207,6 +1254,7 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input, // Asad: determine if mouse moved from last position const LVecBase2f &last_f = _xy->get_value(); if (f != last_f) { + activity = true; move(); } @@ -1260,34 +1308,63 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input, _internal_suppress |= _preferred_region->get_suppress_flags(); } - // Look for button events. + ButtonEventList new_button_events; + + // Look for new button events. if (input.has_data(_button_events_input)) { - const ButtonEventList *button_events; - DCAST_INTO_V(button_events, input.get_data(_button_events_input).get_ptr()); - int num_events = button_events->get_num_events(); + const ButtonEventList *this_button_events; + DCAST_INTO_V(this_button_events, input.get_data(_button_events_input).get_ptr()); + int num_events = this_button_events->get_num_events(); for (int i = 0; i < num_events; i++) { - const ButtonEvent &be = button_events->get_event(i); + const ButtonEvent &be = this_button_events->get_event(i); be.update_mods(_mods); switch (be._type) { case ButtonEvent::T_down: - press(be._button); + if (!_current_buttons_down.get_bit(be._button.get_index())) { + // The button was not already depressed; thus, this is not + // keyrepeat. + activity = true; + _current_buttons_down.set_bit(be._button.get_index()); + press(be._button, false); + new_button_events.add_event(be); + break; + } + // The button was already depressed, so this is really just + // keyrepeat. Fall through. + + case ButtonEvent::T_repeat: + _current_buttons_down.set_bit(be._button.get_index()); + press(be._button, true); + new_button_events.add_event(ButtonEvent(be._button, ButtonEvent::T_repeat, + be._time)); break; case ButtonEvent::T_up: + activity = true; + _current_buttons_down.clear_bit(be._button.get_index()); release(be._button); + new_button_events.add_event(be); break; case ButtonEvent::T_keystroke: + // We don't consider "keystroke" an activity event, because it + // might be just keyrepeat. keystroke(be._keycode); + new_button_events.add_event(be); break; case ButtonEvent::T_candidate: + activity = true; candidate(be._candidate_string, be._highlight_start, be._highlight_end, be._cursor_pos); + new_button_events.add_event(be); break; case ButtonEvent::T_resume_down: - // Ignore this, since the button wasn't pressed just now. + _current_buttons_down.set_bit(be._button.get_index()); + // Don't call press(), since the button wasn't actually + // pressed just now. + new_button_events.add_event(be); break; case ButtonEvent::T_move: @@ -1297,6 +1374,73 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input, } } + // Now check the inactivity timer. + if (_has_inactivity_timeout) { + if (activity) { + note_activity(); + + } else { + double now = ClockObject::get_global_clock()->get_frame_time(); + double elapsed = now - _last_activity; + + // Toggle the inactivity state to inactive. + if (elapsed > _inactivity_timeout) { + switch (_inactivity_state) { + case IS_active: + _inactivity_state = IS_active_to_inactive; + break; + + case IS_inactive: + break; + + case IS_active_to_inactive: + break; + + case IS_inactive_to_active: + _inactivity_state = IS_inactive; + break; + } + } + } + } + + switch (_inactivity_state) { + case IS_active: + case IS_inactive: + break; + + case IS_active_to_inactive: + // "Release" all of the currently-held buttons. + if (tform_cat.is_debug()) { + tform_cat.info() + << "MouseWatcher detected " << _inactivity_timeout + << " seconds of inactivity; releasing held buttons.\n"; + } + { + for (int i = 0; i < _current_buttons_down.get_num_bits(); ++i) { + if (_current_buttons_down.get_bit(i)) { + release(ButtonHandle(i)); + new_button_events.add_event(ButtonEvent(ButtonHandle(i), ButtonEvent::T_up)); + } + } + } + _inactivity_state = IS_inactive; + break; + + case IS_inactive_to_active: + // "Press" all of the buttons we "released" before. + { + for (int i = 0; i < _current_buttons_down.get_num_bits(); ++i) { + if (_current_buttons_down.get_bit(i)) { + press(ButtonHandle(i), false); + new_button_events.add_event(ButtonEvent(ButtonHandle(i), ButtonEvent::T_down)); + } + } + } + _inactivity_state = IS_active; + break; + } + if (_has_mouse && (_internal_suppress & MouseWatcherRegion::SF_mouse_position) == 0) { // Transmit the mouse position. @@ -1306,41 +1450,31 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input, output.set_data(_pixel_xy_output, EventParameter(_pixel_xy)); } + // Now transmit the buttons events down the graph. int suppress_buttons = ((_internal_suppress | _external_suppress) & MouseWatcherRegion::SF_any_button); - if (suppress_buttons != 0) { - // Suppress some buttons. - _button_events->clear(); + _button_events->clear(); - if (input.has_data(_button_events_input)) { - const ButtonEventList *button_events; - DCAST_INTO_V(button_events, input.get_data(_button_events_input).get_ptr()); - int num_events = button_events->get_num_events(); - for (int i = 0; i < num_events; i++) { - const ButtonEvent &be = button_events->get_event(i); - bool suppress = true; - - if (be._type != ButtonEvent::T_keystroke && - MouseButton::is_mouse_button(be._button)) { - suppress = ((suppress_buttons & MouseWatcherRegion::SF_mouse_button) != 0); - } else { - suppress = ((suppress_buttons & MouseWatcherRegion::SF_other_button) != 0); - } - - if (!suppress || be._type == ButtonEvent::T_up) { - // Don't suppress this button event; pass it through. - _button_events->add_event(be); - } - } + int num_events = new_button_events.get_num_events(); + for (int i = 0; i < num_events; i++) { + const ButtonEvent &be = new_button_events.get_event(i); + bool suppress = true; + + if (be._type != ButtonEvent::T_keystroke && + MouseButton::is_mouse_button(be._button)) { + suppress = ((suppress_buttons & MouseWatcherRegion::SF_mouse_button) != 0); + } else { + suppress = ((suppress_buttons & MouseWatcherRegion::SF_other_button) != 0); } - - if (_button_events->get_num_events() != 0) { - output.set_data(_button_events_output, EventParameter(_button_events)); + + if (!suppress || be._type == ButtonEvent::T_up) { + // Don't suppress this button event; pass it through. + _button_events->add_event(be); } - - } else { - // Transmit all buttons. - output.set_data(_button_events_output, input.get_data(_button_events_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. diff --git a/panda/src/tform/mouseWatcher.h b/panda/src/tform/mouseWatcher.h index e6e44caf2e..c98a777171 100644 --- a/panda/src/tform/mouseWatcher.h +++ b/panda/src/tform/mouseWatcher.h @@ -31,6 +31,8 @@ #include "buttonHandle.h" #include "buttonEventList.h" #include "linmath_events.h" +#include "bitArray.h" +#include "clockObject.h" #include "pvector.h" class MouseWatcherParameter; @@ -75,12 +77,17 @@ PUBLISHED: INLINE MouseWatcherRegion *get_over_region(float x, float y) const; MouseWatcherRegion *get_over_region(const LPoint2f &pos) const; + INLINE bool is_button_down(ButtonHandle button) const; + INLINE void set_button_down_pattern(const string &pattern); INLINE const string &get_button_down_pattern() const; INLINE void set_button_up_pattern(const string &pattern); INLINE const string &get_button_up_pattern() const; + INLINE void set_button_repeat_pattern(const string &pattern); + INLINE const string &get_button_repeat_pattern() const; + INLINE void set_enter_pattern(const string &pattern); INLINE const string &get_enter_pattern() const; @@ -115,6 +122,12 @@ PUBLISHED: int get_num_groups() const; MouseWatcherGroup *get_group(int n) const; + INLINE void set_inactivity_timeout(double timeout); + INLINE bool has_inactivity_timeout() const; + INLINE double get_inactivity_timeout() const; + INLINE void clear_inactivity_timeout(); + void note_activity(); + public: virtual void output(ostream &out) const; virtual void write(ostream &out, int indent_level = 0) const; @@ -147,7 +160,7 @@ protected: const ButtonHandle &button); void move(); - void press(ButtonHandle button); + void press(ButtonHandle button, bool keyrepeat); void release(ButtonHandle button); void keystroke(int keycode); void candidate(const wstring &candidate, size_t highlight_start, @@ -178,6 +191,7 @@ private: int _external_suppress; LPoint2f _mouse; LPoint2f _mouse_pixel; + BitArray _current_buttons_down; Regions _current_regions; PT(MouseWatcherRegion) _preferred_region; @@ -189,6 +203,7 @@ private: string _button_down_pattern; string _button_up_pattern; + string _button_repeat_pattern; string _enter_pattern; string _leave_pattern; string _within_pattern; @@ -200,6 +215,18 @@ private: ModifierButtons _mods; DisplayRegion *_display_region; + bool _has_inactivity_timeout; + double _inactivity_timeout; + double _last_activity; + + enum InactivityState { + IS_active, + IS_inactive, + IS_active_to_inactive, + IS_inactive_to_active, + }; + InactivityState _inactivity_state; + #ifndef NDEBUG NodePath _show_regions_render2d; string _show_regions_bin_name; diff --git a/panda/src/tform/mouseWatcherParameter.I b/panda/src/tform/mouseWatcherParameter.I index 467d2a6d92..89f47f9bc7 100644 --- a/panda/src/tform/mouseWatcherParameter.I +++ b/panda/src/tform/mouseWatcherParameter.I @@ -77,6 +77,23 @@ set_button(const ButtonHandle &button) { _flags |= F_has_button; } +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherParameter::set_keyrepeat +// Access: Public +// Description: Sets the state of the "keyrepeat" flag. This is true +// if a button-press event was generated due to +// keyrepeat, or false if it is an original button +// press. +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcherParameter:: +set_keyrepeat(bool flag) { + if (flag) { + _flags |= F_is_keyrepeat; + } else { + _flags &= ~F_is_keyrepeat; + } +} + //////////////////////////////////////////////////////////////////// // Function: MouseWatcherParameter::set_keycode // Access: Public @@ -168,6 +185,18 @@ get_button() const { return _button; } +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcherParameter::is_keyrepeat +// Access: Published +// Description: Returns true if the button-down even was generated +// due to keyrepeat, or false if it was an original +// button down. +//////////////////////////////////////////////////////////////////// +INLINE bool MouseWatcherParameter:: +is_keyrepeat() const { + return (_flags & F_is_keyrepeat) != 0; +} + //////////////////////////////////////////////////////////////////// // Function: MouseWatcherParameter::has_keycode // Access: Published diff --git a/panda/src/tform/mouseWatcherParameter.h b/panda/src/tform/mouseWatcherParameter.h index 26925e0a12..146eb29818 100644 --- a/panda/src/tform/mouseWatcherParameter.h +++ b/panda/src/tform/mouseWatcherParameter.h @@ -40,6 +40,7 @@ public: INLINE ~MouseWatcherParameter(); INLINE void set_button(const ButtonHandle &button); + INLINE void set_keyrepeat(bool flag); INLINE void set_keycode(int keycode); INLINE void set_candidate(const wstring &candidate_string, size_t highlight_start, @@ -52,6 +53,7 @@ public: PUBLISHED: INLINE bool has_button() const; INLINE ButtonHandle get_button() const; + INLINE bool is_keyrepeat() const; INLINE bool has_keycode() const; INLINE int get_keycode() const; @@ -93,6 +95,7 @@ public: F_is_outside = 0x004, F_has_keycode = 0x008, F_has_candidate = 0x010, + F_is_keyrepeat = 0x020, }; int _flags; };