implement MouseWatcher::inactivity_timeout

This commit is contained in:
David Rose 2006-09-15 16:27:09 +00:00
parent 8aaf640cd9
commit e6077a603c
18 changed files with 468 additions and 46 deletions

View File

@ -42,6 +42,10 @@ output(ostream &out) const {
out << "button " << _button << " up"; out << "button " << _button << " up";
break; break;
case T_repeat:
out << "button " << _button << " repeat";
break;
case T_keystroke: case T_keystroke:
out << "keystroke " << _keycode; out << "keystroke " << _keycode;
break; break;
@ -70,6 +74,7 @@ write_datagram(Datagram &dg) const {
case T_down: case T_down:
case T_resume_down: case T_resume_down:
case T_up: case T_up:
case T_repeat:
// We write the button name. This is not particularly compact, but // We write the button name. This is not particularly compact, but
// presumably we don't get thousands of button events per frame, and // presumably we don't get thousands of button events per frame, and
// it is robust as the button index may change between sessions but // 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_down:
case T_resume_down: case T_resume_down:
case T_up: case T_up:
case T_repeat:
_button = ButtonRegistry::ptr()->get_button(scan.get_string()); _button = ButtonRegistry::ptr()->get_button(scan.get_string());
break; break;

View File

@ -70,6 +70,12 @@ public:
T_resume_down, T_resume_down,
T_up, 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 // T_keystroke is a special keystroke event, and is sent along
// with a Unicode keycode value, not a ButtonHandle. // with a Unicode keycode value, not a ButtonHandle.
T_keystroke, T_keystroke,

View File

@ -26,6 +26,27 @@ INLINE ButtonEventList::
ButtonEventList() { ButtonEventList() {
} }
////////////////////////////////////////////////////////////////////
// Function: ButtonEventList::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE ButtonEventList::
ButtonEventList(const ButtonEventList &copy) :
_events(copy._events)
{
}
////////////////////////////////////////////////////////////////////
// Function: ButtonEventList::Copy Assignment Operator
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void ButtonEventList::
operator = (const ButtonEventList &copy) {
_events = copy._events;
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: ButtonEventList::add_event // Function: ButtonEventList::add_event
// Access: Public // Access: Public

View File

@ -41,6 +41,8 @@ class DatagramIterator;
class EXPCL_PANDA ButtonEventList : public EventStoreValueBase { class EXPCL_PANDA ButtonEventList : public EventStoreValueBase {
public: public:
INLINE ButtonEventList(); INLINE ButtonEventList();
INLINE ButtonEventList(const ButtonEventList &copy);
INLINE void operator = (const ButtonEventList &copy);
INLINE void add_event(ButtonEvent event); INLINE void add_event(ButtonEvent event);
INLINE int get_num_events() const; INLINE int get_num_events() const;

View File

@ -351,6 +351,19 @@ get_press_prefix() {
return "press-"; 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 // Function: PGItem::get_release_prefix
// Access: Published, Static // Access: Published, Static
@ -465,6 +478,19 @@ get_press_event(const ButtonHandle &button) const {
return get_press_prefix() + button.get_name() + "-" + get_id(); 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 // Function: PGItem::get_release_event
// Access: Published // Access: Published

View File

@ -583,7 +583,12 @@ void PGItem::
press(const MouseWatcherParameter &param, bool background) { press(const MouseWatcherParameter &param, bool background) {
if (!background) { if (!background) {
PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param); 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); play_sound(event);
throw_event(event, EventParameter(ep)); throw_event(event, EventParameter(ep));
} }

View File

@ -139,6 +139,7 @@ PUBLISHED:
INLINE static string get_focus_in_prefix(); INLINE static string get_focus_in_prefix();
INLINE static string get_focus_out_prefix(); INLINE static string get_focus_out_prefix();
INLINE static string get_press_prefix(); INLINE static string get_press_prefix();
INLINE static string get_repeat_prefix();
INLINE static string get_release_prefix(); INLINE static string get_release_prefix();
INLINE static string get_keystroke_prefix(); INLINE static string get_keystroke_prefix();
@ -149,6 +150,7 @@ PUBLISHED:
INLINE string get_focus_in_event() const; INLINE string get_focus_in_event() const;
INLINE string get_focus_out_event() const; INLINE string get_focus_out_event() const;
INLINE string get_press_event(const ButtonHandle &button) 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_release_event(const ButtonHandle &button) const;
INLINE string get_keystroke_event() const; INLINE string get_keystroke_event() const;

View File

@ -30,6 +30,17 @@ INLINE ButtonHandle::
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 // Function: ButtonHandle::Copy Constructor
// Access: Published // Access: Published

View File

@ -30,6 +30,7 @@
class EXPCL_PANDA ButtonHandle { class EXPCL_PANDA ButtonHandle {
PUBLISHED: PUBLISHED:
INLINE ButtonHandle(); INLINE ButtonHandle();
INLINE ButtonHandle(int index);
public: public:
INLINE ButtonHandle(const ButtonHandle &copy); INLINE ButtonHandle(const ButtonHandle &copy);

View File

@ -72,6 +72,38 @@ get_button_up_event() const {
return _button_up_event; 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 // Function: ButtonThrower::set_keystroke_event
// Access: Published // Access: Published
@ -83,6 +115,12 @@ get_button_up_event() const {
// together will generate the button event "shift-4", // together will generate the button event "shift-4",
// but it will generate the keystroke "$". // 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 // This event is generated with a single wstring
// parameter, which is a one-character string that // parameter, which is a one-character string that
// contains the keystroke generated. If this event // contains the keystroke generated. If this event

View File

@ -307,6 +307,10 @@ do_general_event(const ButtonEvent &button_event, const string &button_name) {
event_name = _button_up_event; event_name = _button_up_event;
break; break;
case ButtonEvent::T_repeat:
event_name = _button_repeat_event;
break;
case ButtonEvent::T_keystroke: case ButtonEvent::T_keystroke:
event_name = _keystroke_event; event_name = _keystroke_event;
break; break;
@ -388,7 +392,7 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input,
const ButtonEvent &be = button_events->get_event(i); const ButtonEvent &be = button_events->get_event(i);
string event_name = be._button.get_name(); 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. // Button down.
if (!_mods.button_down(be._button)) { if (!_mods.button_down(be._button)) {
// We only prepend modifier names on the button-down events, // 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)) { if (!_throw_buttons_active || has_throw_button(_mods, be._button)) {
// Process this button. // Process this button.
if (be._type == ButtonEvent::T_repeat) {
do_specific_event(event_name + "-repeat", be._time);
} else {
do_specific_event(event_name, be._time); do_specific_event(event_name, be._time);
}
do_general_event(be, event_name); do_general_event(be, event_name);
} else { } else {

View File

@ -49,6 +49,8 @@ PUBLISHED:
INLINE const string &get_button_down_event() const; INLINE const string &get_button_down_event() const;
INLINE void set_button_up_event(const string &button_up_event); INLINE void set_button_up_event(const string &button_up_event);
INLINE const string &get_button_up_event() const; 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 void set_keystroke_event(const string &keystroke_event);
INLINE const string &get_keystroke_event() const; INLINE const string &get_keystroke_event() const;
INLINE void set_candidate_event(const string &candidate_event); INLINE void set_candidate_event(const string &candidate_event);
@ -92,6 +94,7 @@ private:
private: private:
string _button_down_event; string _button_down_event;
string _button_up_event; string _button_up_event;
string _button_repeat_event;
string _keystroke_event; string _keystroke_event;
string _candidate_event; string _candidate_event;
string _move_event; string _move_event;

View File

@ -422,7 +422,7 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input,
for (int i = 0; i < num_events; i++) { for (int i = 0; i < num_events; i++) {
const ButtonEvent &be = button_events->get_event(i); const ButtonEvent &be = button_events->get_event(i);
if (be._type != ButtonEvent::T_keystroke) { 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()) { if (be._button == KeyboardButton::up()) {
_up_arrow.set_key(down); _up_arrow.set_key(down);

View File

@ -138,6 +138,17 @@ get_over_region(float x, float y) const {
return get_over_region(LPoint2f(x, y)); 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 // Function: MouseWatcher::set_button_down_pattern
// Access: Published // Access: Published
@ -193,6 +204,39 @@ get_button_up_pattern() const {
return _button_up_pattern; 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 // Function: MouseWatcher::set_enter_pattern
// Access: Published // Access: Published
@ -463,6 +507,62 @@ has_display_region() const {
return (_display_region != (DisplayRegion *)NULL); 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 // Function: MouseWatcher::within_region
// Access: Protected // Access: Protected

View File

@ -67,6 +67,10 @@ MouseWatcher(const string &name) :
_button_down = false; _button_down = false;
_eh = (EventHandler *)NULL; _eh = (EventHandler *)NULL;
_display_region = (DisplayRegion *)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 // When this flag is true, the mouse pointer is allowed to be
// "entered" into multiple regions simultaneously; when false, it // "entered" into multiple regions simultaneously; when false, it
@ -336,6 +340,41 @@ get_group(int n) const {
return _groups[n]; 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 // Function: MouseWatcher::output
@ -810,11 +849,12 @@ move() {
// being depressed. // being depressed.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void MouseWatcher:: void MouseWatcher::
press(ButtonHandle button) { press(ButtonHandle button, bool keyrepeat) {
nassertv(_lock.debug_is_locked()); nassertv(_lock.debug_is_locked());
MouseWatcherParameter param; MouseWatcherParameter param;
param.set_button(button); param.set_button(button);
param.set_keyrepeat(keyrepeat);
param.set_modifier_buttons(_mods); param.set_modifier_buttons(_mods);
param.set_mouse(_mouse); param.set_mouse(_mouse);
@ -828,9 +868,14 @@ press(ButtonHandle button) {
if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) { if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) {
_preferred_button_down_region->press(param); _preferred_button_down_region->press(param);
if (keyrepeat) {
throw_event_pattern(_button_repeat_pattern,
_preferred_button_down_region, button);
} else {
throw_event_pattern(_button_down_pattern, throw_event_pattern(_button_down_pattern,
_preferred_button_down_region, button); _preferred_button_down_region, button);
} }
}
} else { } else {
// It's a keyboard button; therefore, send the event to every // It's a keyboard button; therefore, send the event to every
@ -1186,6 +1231,8 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
Thread *current_thread = trav->get_current_thread(); Thread *current_thread = trav->get_current_thread();
MutexHolder holder(_lock); MutexHolder holder(_lock);
bool activity = false;
// Initially, we do not suppress any events to objects below us in // Initially, we do not suppress any events to objects below us in
// the data graph. // the data graph.
_internal_suppress = 0; _internal_suppress = 0;
@ -1207,6 +1254,7 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
// Asad: determine if mouse moved from last position // Asad: determine if mouse moved from last position
const LVecBase2f &last_f = _xy->get_value(); const LVecBase2f &last_f = _xy->get_value();
if (f != last_f) { if (f != last_f) {
activity = true;
move(); move();
} }
@ -1260,34 +1308,63 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
_internal_suppress |= _preferred_region->get_suppress_flags(); _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)) { if (input.has_data(_button_events_input)) {
const ButtonEventList *button_events; const ButtonEventList *this_button_events;
DCAST_INTO_V(button_events, input.get_data(_button_events_input).get_ptr()); DCAST_INTO_V(this_button_events, input.get_data(_button_events_input).get_ptr());
int num_events = button_events->get_num_events(); int num_events = this_button_events->get_num_events();
for (int i = 0; i < num_events; i++) { 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); be.update_mods(_mods);
switch (be._type) { switch (be._type) {
case ButtonEvent::T_down: 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; break;
case ButtonEvent::T_up: case ButtonEvent::T_up:
activity = true;
_current_buttons_down.clear_bit(be._button.get_index());
release(be._button); release(be._button);
new_button_events.add_event(be);
break; break;
case ButtonEvent::T_keystroke: case ButtonEvent::T_keystroke:
// We don't consider "keystroke" an activity event, because it
// might be just keyrepeat.
keystroke(be._keycode); keystroke(be._keycode);
new_button_events.add_event(be);
break; break;
case ButtonEvent::T_candidate: case ButtonEvent::T_candidate:
activity = true;
candidate(be._candidate_string, be._highlight_start, be._highlight_end, be._cursor_pos); candidate(be._candidate_string, be._highlight_start, be._highlight_end, be._cursor_pos);
new_button_events.add_event(be);
break; break;
case ButtonEvent::T_resume_down: 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; break;
case ButtonEvent::T_move: 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 && if (_has_mouse &&
(_internal_suppress & MouseWatcherRegion::SF_mouse_position) == 0) { (_internal_suppress & MouseWatcherRegion::SF_mouse_position) == 0) {
// Transmit the mouse position. // Transmit the mouse position.
@ -1306,18 +1450,14 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
output.set_data(_pixel_xy_output, EventParameter(_pixel_xy)); 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); 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)) { int num_events = new_button_events.get_num_events();
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++) { for (int i = 0; i < num_events; i++) {
const ButtonEvent &be = button_events->get_event(i); const ButtonEvent &be = new_button_events.get_event(i);
bool suppress = true; bool suppress = true;
if (be._type != ButtonEvent::T_keystroke && if (be._type != ButtonEvent::T_keystroke &&
@ -1332,17 +1472,11 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
_button_events->add_event(be); _button_events->add_event(be);
} }
} }
}
if (_button_events->get_num_events() != 0) { if (_button_events->get_num_events() != 0) {
output.set_data(_button_events_output, EventParameter(_button_events)); output.set_data(_button_events_output, EventParameter(_button_events));
} }
} else {
// Transmit all buttons.
output.set_data(_button_events_output, input.get_data(_button_events_input));
}
// We always pass the pixel_size data through. // We always pass the pixel_size data through.
output.set_data(_pixel_size_output, input.get_data(_pixel_size_input)); output.set_data(_pixel_size_output, input.get_data(_pixel_size_input));
} }

View File

@ -31,6 +31,8 @@
#include "buttonHandle.h" #include "buttonHandle.h"
#include "buttonEventList.h" #include "buttonEventList.h"
#include "linmath_events.h" #include "linmath_events.h"
#include "bitArray.h"
#include "clockObject.h"
#include "pvector.h" #include "pvector.h"
class MouseWatcherParameter; class MouseWatcherParameter;
@ -75,12 +77,17 @@ PUBLISHED:
INLINE MouseWatcherRegion *get_over_region(float x, float y) const; INLINE MouseWatcherRegion *get_over_region(float x, float y) const;
MouseWatcherRegion *get_over_region(const LPoint2f &pos) 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 void set_button_down_pattern(const string &pattern);
INLINE const string &get_button_down_pattern() const; INLINE const string &get_button_down_pattern() const;
INLINE void set_button_up_pattern(const string &pattern); INLINE void set_button_up_pattern(const string &pattern);
INLINE const string &get_button_up_pattern() const; 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 void set_enter_pattern(const string &pattern);
INLINE const string &get_enter_pattern() const; INLINE const string &get_enter_pattern() const;
@ -115,6 +122,12 @@ PUBLISHED:
int get_num_groups() const; int get_num_groups() const;
MouseWatcherGroup *get_group(int n) 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: public:
virtual void output(ostream &out) const; virtual void output(ostream &out) const;
virtual void write(ostream &out, int indent_level = 0) const; virtual void write(ostream &out, int indent_level = 0) const;
@ -147,7 +160,7 @@ protected:
const ButtonHandle &button); const ButtonHandle &button);
void move(); void move();
void press(ButtonHandle button); void press(ButtonHandle button, bool keyrepeat);
void release(ButtonHandle button); void release(ButtonHandle button);
void keystroke(int keycode); void keystroke(int keycode);
void candidate(const wstring &candidate, size_t highlight_start, void candidate(const wstring &candidate, size_t highlight_start,
@ -178,6 +191,7 @@ private:
int _external_suppress; int _external_suppress;
LPoint2f _mouse; LPoint2f _mouse;
LPoint2f _mouse_pixel; LPoint2f _mouse_pixel;
BitArray _current_buttons_down;
Regions _current_regions; Regions _current_regions;
PT(MouseWatcherRegion) _preferred_region; PT(MouseWatcherRegion) _preferred_region;
@ -189,6 +203,7 @@ private:
string _button_down_pattern; string _button_down_pattern;
string _button_up_pattern; string _button_up_pattern;
string _button_repeat_pattern;
string _enter_pattern; string _enter_pattern;
string _leave_pattern; string _leave_pattern;
string _within_pattern; string _within_pattern;
@ -200,6 +215,18 @@ private:
ModifierButtons _mods; ModifierButtons _mods;
DisplayRegion *_display_region; 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 #ifndef NDEBUG
NodePath _show_regions_render2d; NodePath _show_regions_render2d;
string _show_regions_bin_name; string _show_regions_bin_name;

View File

@ -77,6 +77,23 @@ set_button(const ButtonHandle &button) {
_flags |= F_has_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 // Function: MouseWatcherParameter::set_keycode
// Access: Public // Access: Public
@ -168,6 +185,18 @@ get_button() const {
return _button; 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 // Function: MouseWatcherParameter::has_keycode
// Access: Published // Access: Published

View File

@ -40,6 +40,7 @@ public:
INLINE ~MouseWatcherParameter(); INLINE ~MouseWatcherParameter();
INLINE void set_button(const ButtonHandle &button); INLINE void set_button(const ButtonHandle &button);
INLINE void set_keyrepeat(bool flag);
INLINE void set_keycode(int keycode); INLINE void set_keycode(int keycode);
INLINE void set_candidate(const wstring &candidate_string, INLINE void set_candidate(const wstring &candidate_string,
size_t highlight_start, size_t highlight_start,
@ -52,6 +53,7 @@ public:
PUBLISHED: PUBLISHED:
INLINE bool has_button() const; INLINE bool has_button() const;
INLINE ButtonHandle get_button() const; INLINE ButtonHandle get_button() const;
INLINE bool is_keyrepeat() const;
INLINE bool has_keycode() const; INLINE bool has_keycode() const;
INLINE int get_keycode() const; INLINE int get_keycode() const;
@ -93,6 +95,7 @@ public:
F_is_outside = 0x004, F_is_outside = 0x004,
F_has_keycode = 0x008, F_has_keycode = 0x008,
F_has_candidate = 0x010, F_has_candidate = 0x010,
F_is_keyrepeat = 0x020,
}; };
int _flags; int _flags;
}; };