Panda3D
 All Classes Functions Variables Enumerations
mouseWatcher.cxx
1 // Filename: mouseWatcher.cxx
2 // Created by: drose (12Mar02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "mouseWatcher.h"
16 #include "config_tform.h"
17 #include "dataGraphTraverser.h"
18 #include "mouseWatcherParameter.h"
19 #include "mouseAndKeyboard.h"
20 #include "mouseData.h"
21 #include "buttonEventList.h"
22 #include "mouseButton.h"
23 #include "throw_event.h"
24 #include "eventParameter.h"
25 #include "dataNodeTransmit.h"
26 #include "transformState.h"
27 #include "displayRegion.h"
28 #include "stereoDisplayRegion.h"
29 #include "geomVertexWriter.h"
30 #include "geomLinestrips.h"
31 #include "geomPoints.h"
32 #include "dcast.h"
33 #include "indent.h"
34 #include "lightMutexHolder.h"
35 #include "nearly_zero.h"
36 
37 #include <algorithm>
38 
39 TypeHandle MouseWatcher::_type_handle;
40 
41 ////////////////////////////////////////////////////////////////////
42 // Function: MouseWatcher::Constructor
43 // Access: Published
44 // Description:
45 ////////////////////////////////////////////////////////////////////
46 MouseWatcher::
47 MouseWatcher(const string &name) :
48  DataNode(name)
49 {
50  _pixel_xy_input = define_input("pixel_xy", EventStoreVec2::get_class_type());
51  _pixel_size_input = define_input("pixel_size", EventStoreVec2::get_class_type());
52  _xy_input = define_input("xy", EventStoreVec2::get_class_type());
53  _button_events_input = define_input("button_events", ButtonEventList::get_class_type());
54  _pointer_events_input = define_input("pointer_events", PointerEventList::get_class_type());
55 
56  _pixel_xy_output = define_output("pixel_xy", EventStoreVec2::get_class_type());
57  _pixel_size_output = define_output("pixel_size", EventStoreVec2::get_class_type());
58  _xy_output = define_output("xy", EventStoreVec2::get_class_type());
59  _button_events_output = define_output("button_events", ButtonEventList::get_class_type());
60 
61  _pixel_xy = new EventStoreVec2(LPoint2(0.0f, 0.0f));
62  _xy = new EventStoreVec2(LPoint2(0.0f, 0.0f));
63  _pixel_size = new EventStoreVec2(LPoint2(0.0f, 0.0f));
64  _button_events = new ButtonEventList;
65 
66  _has_mouse = false;
67  _internal_suppress = 0;
68  _preferred_region = (MouseWatcherRegion *)NULL;
69  _preferred_button_down_region = (MouseWatcherRegion *)NULL;
70  _button_down = false;
71  _eh = (EventHandler *)NULL;
72  _display_region = (DisplayRegion *)NULL;
73  _button_down_display_region = (DisplayRegion *)NULL;
74 
75  _frame.set(-1.0f, 1.0f, -1.0f, 1.0f);
76 
77  _inactivity_timeout = inactivity_timeout;
78  _has_inactivity_timeout = !IS_NEARLY_ZERO(_inactivity_timeout);
79 
80  _num_trail_recent = 0;
81  _trail_log_duration = 0.0;
82  _trail_log = new PointerEventList();
83 
84  _inactivity_timeout_event = "inactivity_timeout";
85  _last_activity = 0.0;
86  _inactivity_state = IS_active;
87 
88  // When this flag is true, the mouse pointer is allowed to be
89  // "entered" into multiple regions simultaneously; when false, it
90  // will only be "within" multiple regions, but "entered" into the
91  // topmost of those.
92  _enter_multiple = false;
93 
94  // When this flag is true, moving the pointer into a region is
95  // enough to click it. The click is simulated with mouse button
96  // one.
97  _implicit_click = false;
98 }
99 
100 ////////////////////////////////////////////////////////////////////
101 // Function: MouseWatcher::Destructor
102 // Access: Published
103 // Description:
104 ////////////////////////////////////////////////////////////////////
105 MouseWatcher::
106 ~MouseWatcher() {
107 }
108 
109 ////////////////////////////////////////////////////////////////////
110 // Function: MouseWatcher::remove_region
111 // Access: Published
112 // Description: Removes the indicated region from the group.
113 // Returns true if it was successfully removed, or false
114 // if it wasn't there in the first place.
115 ////////////////////////////////////////////////////////////////////
116 bool MouseWatcher::
118  LightMutexHolder holder(_lock);
119 
120  remove_region_from(_current_regions, region);
121  if (region == _preferred_region) {
122  if (_preferred_region != (MouseWatcherRegion *)NULL) {
123  exit_region(_preferred_region, MouseWatcherParameter());
124  }
125  _preferred_region = (MouseWatcherRegion *)NULL;
126  }
127  if (region == _preferred_button_down_region) {
128  _preferred_button_down_region = (MouseWatcherRegion *)NULL;
129  }
130 
131  return MouseWatcherBase::do_remove_region(region);
132 }
133 
134 ////////////////////////////////////////////////////////////////////
135 // Function: MouseWatcher::get_over_region
136 // Access: Published
137 // Description: Returns the preferred region the mouse is over. In
138 // the case of overlapping regions, the region with the
139 // largest sort order is preferred; if two regions have
140 // the same sort order, then the smaller region is
141 // preferred.
142 ////////////////////////////////////////////////////////////////////
144 get_over_region(const LPoint2 &pos) const {
145  LightMutexHolder holder(_lock);
146 
147  Regions regions;
148  get_over_regions(regions, pos);
149  return get_preferred_region(regions);
150 }
151 
152 ////////////////////////////////////////////////////////////////////
153 // Function: MouseWatcher::add_group
154 // Access: Published
155 // Description: Adds the indicated group of regions to the set of
156 // regions the MouseWatcher will monitor each frame.
157 //
158 // Since the MouseWatcher itself inherits from
159 // MouseWatcherBase, this operation is normally not
160 // necessary--you can simply add the Regions you care
161 // about one at a time. Adding a complete group is
162 // useful when you may want to explicitly remove the
163 // regions as a group later.
164 //
165 // Returns true if the group was successfully added, or
166 // false if it was already on the list.
167 ////////////////////////////////////////////////////////////////////
168 bool MouseWatcher::
170  LightMutexHolder holder(_lock);
171 
172  // See if the group is in the set/vector already
173  PT(MouseWatcherGroup) pt = group;
174  Groups::const_iterator gi =
175  find(_groups.begin(), _groups.end(), pt);
176  if (gi != _groups.end()) {
177  // Already in the set, return false
178  return false;
179  }
180 
181 #ifndef NDEBUG
182  if (!_show_regions_render2d.is_empty()) {
183  group->show_regions(_show_regions_render2d, _show_regions_bin_name,
184  _show_regions_draw_order);
185  }
186 #endif // NDEBUG
187 
188  // Not in the set, add it and return true
189  _groups.push_back(pt);
190  return true;
191 }
192 
193 ////////////////////////////////////////////////////////////////////
194 // Function: MouseWatcher::remove_group
195 // Access: Published
196 // Description: Removes the indicated group from the set of extra
197 // groups associated with the MouseWatcher. Returns
198 // true if successful, or false if the group was already
199 // removed or was never added via add_group().
200 ////////////////////////////////////////////////////////////////////
201 bool MouseWatcher::
203  LightMutexHolder holder(_lock);
204  LightMutexHolder holder2(group->_lock);
205 
206  group->do_sort_regions();
207 
208  Regions only_a, only_b, both;
209  intersect_regions(only_a, only_b, both,
210  _current_regions, group->_regions);
211  set_current_regions(only_a);
212 
213  if (has_region_in(both, _preferred_region)) {
214  if (_preferred_region != (MouseWatcherRegion *)NULL) {
215  exit_region(_preferred_region, MouseWatcherParameter());
216  }
217  _preferred_region = (MouseWatcherRegion *)NULL;
218  }
219  if (has_region_in(both, _preferred_button_down_region)) {
220  _preferred_button_down_region = (MouseWatcherRegion *)NULL;
221  }
222 
223 #ifndef NDEBUG
224  if (!_show_regions_render2d.is_empty()) {
225  group->do_hide_regions();
226  }
227 #endif // NDEBUG
228 
229  // See if the group is in the set/vector
230  PT(MouseWatcherGroup) pt = group;
231  Groups::iterator gi =
232  find(_groups.begin(), _groups.end(), pt);
233  if (gi != _groups.end()) {
234  // Found it, now erase it
235  _groups.erase(gi);
236  return true;
237  }
238 
239  // Did not find the group to erase
240  return false;
241 }
242 
243 ////////////////////////////////////////////////////////////////////
244 // Function: MouseWatcher::replace_group
245 // Access: Published
246 // Description: Atomically removes old_group from the MouseWatcher,
247 // and replaces it with new_group. Presumably old_group
248 // and new_group might have some regions in common;
249 // these are handled properly.
250 //
251 // If old_group is not already present, simply adds
252 // new_group and returns false. Otherwise, removes
253 // old_group and adds new_group, and then returns true.
254 ////////////////////////////////////////////////////////////////////
255 bool MouseWatcher::
257  if (old_group == new_group) {
258  // Trivial.
259  return true;
260  }
261 
262  LightMutexHolder holder(_lock);
263 
264  LightMutexHolder holder2(old_group->_lock);
265  LightMutexHolder holder3(new_group->_lock);
266 
267  old_group->do_sort_regions();
268  new_group->do_sort_regions();
269 
270 #ifndef NDEBUG
271  if (!_show_regions_render2d.is_empty()) {
272  old_group->do_hide_regions();
273  new_group->do_show_regions(_show_regions_render2d, _show_regions_bin_name,
274  _show_regions_draw_order);
275  }
276 #endif // NDEBUG
277 
278  // Figure out the list of regions that change
279  Regions remove, add, keep;
280  intersect_regions(remove, add, keep,
281  old_group->_regions, new_group->_regions);
282 
283  Regions new_current_regions;
284  bool any_new_current_regions = false;
285 
286  // Remove the old regions
287  if (!remove.empty()) {
288  Regions only_a, only_b, both;
289  intersect_regions(only_a, only_b, both,
290  _current_regions, remove);
291  new_current_regions.swap(only_a);
292  any_new_current_regions = true;
293 
294  if (has_region_in(both, _preferred_region)) {
295  if (_preferred_region != (MouseWatcherRegion *)NULL) {
296  exit_region(_preferred_region, MouseWatcherParameter());
297  }
298  _preferred_region = (MouseWatcherRegion *)NULL;
299  }
300  if (has_region_in(both, _preferred_button_down_region)) {
301  _preferred_button_down_region = (MouseWatcherRegion *)NULL;
302  }
303  }
304 
305  // Don't add the new regions--we have no reason to believe these
306  // should become current; some of them may not even be under the
307  // mouse.
308  /*
309  // And add the new regions
310  if (!add.empty()) {
311  Regions new_list;
312  if (any_new_current_regions) {
313  intersect_regions(new_list, new_list, new_list,
314  new_current_regions, add);
315  } else {
316  intersect_regions(new_list, new_list, new_list,
317  _current_regions, add);
318  }
319  new_current_regions.swap(new_list);
320  any_new_current_regions = true;
321  }
322  */
323 
324  if (any_new_current_regions) {
325  set_current_regions(new_current_regions);
326  }
327 
328  // Add the new group, if it's not already there.
329  PT(MouseWatcherGroup) pt = new_group;
330  Groups::iterator gi =
331  find(_groups.begin(), _groups.end(), pt);
332  if (gi == _groups.end()) {
333  _groups.push_back(new_group);
334  }
335 
336 #ifndef NDEBUG
337  if (!_show_regions_render2d.is_empty()) {
338  new_group->do_update_regions();
339  }
340 #endif // NDEBUG
341 
342  // Remove the old group, if it is already there.
343  pt = old_group;
344  gi = find(_groups.begin(), _groups.end(), pt);
345  if (gi != _groups.end()) {
346  // Found it, now erase it
347  _groups.erase(gi);
348  return true;
349  }
350 
351  // Did not find the group to erase
352  return false;
353 }
354 
355 ////////////////////////////////////////////////////////////////////
356 // Function: MouseWatcher::get_num_groups
357 // Access: Published
358 // Description: Returns the number of separate groups added to the
359 // MouseWatcher via add_group().
360 ////////////////////////////////////////////////////////////////////
361 int MouseWatcher::
362 get_num_groups() const {
363  LightMutexHolder holder(_lock);
364  return _groups.size();
365 }
366 
367 ////////////////////////////////////////////////////////////////////
368 // Function: MouseWatcher::get_group
369 // Access: Published
370 // Description: Returns the nth group added to the MouseWatcher via
371 // add_group().
372 ////////////////////////////////////////////////////////////////////
374 get_group(int n) const {
375  LightMutexHolder holder(_lock);
376  nassertr(n >= 0 && n < (int)_groups.size(), NULL);
377  return _groups[n];
378 }
379 
380 ////////////////////////////////////////////////////////////////////
381 // Function: MouseWatcher::set_trail_log_duration
382 // Access: Published
383 // Description: If the duration is nonzero, causes the MouseWatcher
384 // to log the mouse's trail. Events older than the
385 // specified duration are discarded. If the duration is
386 // zero, logging is disabled.
387 ////////////////////////////////////////////////////////////////////
388 void MouseWatcher::
389 set_trail_log_duration(double duration) {
390  if (duration < 0.0) {
391  duration = 0.0;
392  }
393  _trail_log_duration = duration;
394  discard_excess_trail_log();
395 }
396 
397 ////////////////////////////////////////////////////////////////////
398 // Function: MouseWatcher::discard_excess_trail_log
399 // Access: Private
400 // Description: Discards trail log events whose age exceed the
401 // desired log duration. Keeps one event that is beyond
402 // the specified age, because otherwise, it is not always
403 // possible to determine where the mouse was for the
404 // full logging duration. Also, keeps a minimum of two
405 // events in the queue. If the duration is zero, this
406 // method discards all trail events.
407 ////////////////////////////////////////////////////////////////////
408 void MouseWatcher::
409 discard_excess_trail_log() {
410  if (_trail_log_duration == 0.0) {
411  _trail_log->clear();
412  } else {
413  if (_trail_log->get_num_events() > 2) {
414  double old = ClockObject::get_global_clock()->get_frame_time() - _trail_log_duration;
415  while ((_trail_log->get_num_events() > 2)&&
416  (_trail_log->get_time(0) <= old)&&
417  (_trail_log->get_time(1) <= old)) {
418  _trail_log->pop_front();
419  }
420  }
421  }
422 }
423 
424 ////////////////////////////////////////////////////////////////////
425 // Function: MouseWatcher::get_trail_node
426 // Access: Published
427 // Description: Returns a GeomNode that represents the mouse trail.
428 // The intent is that you should reparent this GeomNode
429 // to Render2D, and then forget about it. The
430 // MouseWatcher will continually update the trail node.
431 // There is only one trail node, it does not create a
432 // new one each time you call get_trail_node.
433 //
434 // This is not a particularly beautiful way to render
435 // a mouse trail. It is intended more for debugging
436 // purposes than for finished applications. Even so,
437 // It is suggested that you might want to apply a line
438 // thickness and antialias mode to the line --- doing
439 // so makes it look a lot better.
440 ////////////////////////////////////////////////////////////////////
442 get_trail_node() {
443  if (_trail_node == 0) {
444  _trail_node = new GeomNode("Mouse Trail Node");
445  update_trail_node();
446  }
447  return _trail_node;
448 }
449 
450 ////////////////////////////////////////////////////////////////////
451 // Function: MouseWatcher::clear_trail_node
452 // Access: Published
453 // Description: If you have previously fetched the trail node
454 // using get_trail_node, then the MouseWatcher is
455 // continually updating the trail node every frame.
456 // Using clear_trail_node causes the MouseWatcher to
457 // forget the trail node and stop updating it.
458 ////////////////////////////////////////////////////////////////////
459 void MouseWatcher::
461  _trail_node = 0;
462 }
463 
464 ////////////////////////////////////////////////////////////////////
465 // Function: MouseWatcher::update_trail_node
466 // Access: Private
467 // Description: Causes the trail node to represent the mouse trail.
468 ////////////////////////////////////////////////////////////////////
469 void MouseWatcher::
470 update_trail_node() {
471  if (_trail_node == 0) {
472  return;
473  }
474  _trail_node->remove_all_geoms();
475 
476  if (_trail_log->get_num_events() < 2) {
477  return;
478  }
479 
480  PT(GeomVertexData) data = new GeomVertexData
481  ("mouseTrailSegs", GeomVertexFormat::get_v3(), Geom::UH_static);
482 
483  GeomVertexWriter vertex(data, InternalName::get_vertex());
484 
485  PT(GeomLinestrips) lines = new GeomLinestrips(Geom::UH_static);
486 
487  double xscale = 2.0 / _pixel_size->get_value().get_x();
488  double yscale = 2.0 / _pixel_size->get_value().get_y();
489 
490  for (int i=0; i<(int)_trail_log->get_num_events(); i++) {
491  double x = (_trail_log->get_xpos(i) * xscale) - 1.0;
492  double y = (_trail_log->get_ypos(i) * yscale) - 1.0;
493  vertex.add_data3(LVecBase3(x,0.0,-y));
494  lines->add_vertex(i);
495  }
496  lines->close_primitive();
497 
498  PT(Geom) l_geom = new Geom(data);
499  l_geom->add_primitive(lines);
500  _trail_node->add_geom(l_geom);
501 }
502 
503 ////////////////////////////////////////////////////////////////////
504 // Function: MouseWatcher::note_activity
505 // Access: Published
506 // Description: Can be used in conjunction with the inactivity
507 // timeout to inform the MouseWatcher that the user has
508 // just performed some action which proves he/she is
509 // present. It may be necessary to call this for
510 // external events, such as joystick action, that the
511 // MouseWatcher might otherwise not know about. This
512 // will reset the current inactivity timer. When the
513 // inactivity timer reaches the length of time specified
514 // by set_inactivity_timeout(), with no keyboard or
515 // mouse activity and no calls to note_activity(), then
516 // any buttons held will be automatically released.
517 ////////////////////////////////////////////////////////////////////
518 void MouseWatcher::
520  _last_activity = ClockObject::get_global_clock()->get_frame_time();
521  switch (_inactivity_state) {
522  case IS_active:
523  break;
524 
525  case IS_inactive:
526  _inactivity_state = IS_inactive_to_active;
527  break;
528 
529  case IS_active_to_inactive:
530  _inactivity_state = IS_active;
531  break;
532 
533  case IS_inactive_to_active:
534  break;
535  }
536 }
537 
538 
539 ////////////////////////////////////////////////////////////////////
540 // Function: MouseWatcher::output
541 // Access: Public, Virtual
542 // Description:
543 ////////////////////////////////////////////////////////////////////
544 void MouseWatcher::
545 output(ostream &out) const {
546  LightMutexHolder holder(_lock);
547  DataNode::output(out);
548 
549  int count = _regions.size();
550  Groups::const_iterator gi;
551  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
552  MouseWatcherGroup *group = (*gi);
553  count += group->_regions.size();
554  }
555 
556  out << " (" << count << " regions)";
557 }
558 
559 ////////////////////////////////////////////////////////////////////
560 // Function: MouseWatcher::write
561 // Access: Public, Virtual
562 // Description:
563 ////////////////////////////////////////////////////////////////////
564 void MouseWatcher::
565 write(ostream &out, int indent_level) const {
566  indent(out, indent_level)
567  << "MouseWatcher " << get_name() << ":\n";
568  MouseWatcherBase::write(out, indent_level + 2);
569 
570  LightMutexHolder holder(_lock);
571  if (!_groups.empty()) {
572  Groups::const_iterator gi;
573  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
574  MouseWatcherGroup *group = (*gi);
575  indent(out, indent_level + 2)
576  << "Subgroup:\n";
577  group->write(out, indent_level + 4);
578  }
579  }
580 }
581 
582 ////////////////////////////////////////////////////////////////////
583 // Function: MouseWatcher::get_over_regions
584 // Access: Protected
585 // Description: Fills up the "regions" list with the set of regions
586 // that the indicated point is over, sorted in order by
587 // pointer. Assumes the lock is held.
588 ////////////////////////////////////////////////////////////////////
589 void MouseWatcher::
590 get_over_regions(MouseWatcher::Regions &regions, const LPoint2 &pos) const {
591  nassertv(_lock.debug_is_locked());
592 
593  // Scale the mouse coordinates into the frame.
594  PN_stdfloat mx = (pos[0] + 1.0f) * 0.5f * (_frame[1] - _frame[0]) + _frame[0];
595  PN_stdfloat my = (pos[1] + 1.0f) * 0.5f * (_frame[3] - _frame[2]) + _frame[2];
596 
597  // pos[0] = 2.0f * (mx - _frame[0]) / (_frame[1] - _frame[0]) - 1.0f;
598  // pos[1] = 2.0f * (my - _frame[2]) / (_frame[3] - _frame[2]) - 1.0f;
599 
600  // Ensure the vector is empty before we begin.
601  regions.clear();
602 
603  Regions::const_iterator ri;
604  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
605  MouseWatcherRegion *region = (*ri);
606  const LVecBase4 &frame = region->get_frame();
607 
608  if (region->get_active() &&
609  mx >= frame[0] && mx <= frame[1] &&
610  my >= frame[2] && my <= frame[3]) {
611 
612  regions.push_back(region);
613  }
614  }
615 
616  // Also check all of our sub-groups.
617  Groups::const_iterator gi;
618  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
619  MouseWatcherGroup *group = (*gi);
620  for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
621  MouseWatcherRegion *region = (*ri);
622  const LVecBase4 &frame = region->get_frame();
623 
624  if (region->get_active() &&
625  mx >= frame[0] && mx <= frame[1] &&
626  my >= frame[2] && my <= frame[3]) {
627 
628  regions.push_back(region);
629  }
630  }
631  }
632 
633  // Now sort the regions by pointer. By convention, the Regions
634  // vectors are always kept in order by pointer, so we can do easy
635  // linear comparison and intersection operations.
636  sort(regions.begin(), regions.end());
637 }
638 
639 ////////////////////////////////////////////////////////////////////
640 // Function: MouseWatcher::get_preferred_region
641 // Access: Protected, Static
642 // Description: Returns the innermost region of all the regions
643 // indicated in the given vector (usually, the regions
644 // the mouse is over). This is the "preferred" region
645 // that gets some special treatment. Assumes the lock
646 // is already held.
647 ////////////////////////////////////////////////////////////////////
648 MouseWatcherRegion *MouseWatcher::
649 get_preferred_region(const MouseWatcher::Regions &regions) {
650  if (regions.empty()) {
651  return (MouseWatcherRegion *)NULL;
652  }
653 
654  Regions::const_iterator ri;
655  ri = regions.begin();
656  MouseWatcherRegion *preferred = *ri;
657  ++ri;
658  while (ri != regions.end()) {
659  MouseWatcherRegion *region = *ri;
660 
661  if (*region < *preferred) {
662  preferred = region;
663  }
664  ++ri;
665  }
666 
667  return preferred;
668 }
669 
670 ////////////////////////////////////////////////////////////////////
671 // Function: MouseWatcher::set_current_regions
672 // Access: Protected
673 // Description: Changes the "current" regions--the one we consider the
674 // mouse to be over--to the indicated list, and throws
675 // whatever events are appropriate because of that.
676 //
677 // The list passed in is destroyed. Assumes the lock is
678 // already held.
679 ////////////////////////////////////////////////////////////////////
680 void MouseWatcher::
681 set_current_regions(MouseWatcher::Regions &regions) {
682  nassertv(_lock.debug_is_locked());
683 
684  // Set up a parameter for passing through any change events.
685  MouseWatcherParameter param;
686  param.set_modifier_buttons(_mods);
687  param.set_mouse(_mouse);
688 
689  // Now do a standard sorted comparison between the two vectors.
690  Regions::const_iterator new_ri = regions.begin();
691  Regions::const_iterator old_ri = _current_regions.begin();
692 
693  // Queue up all the new regions so we can send the within patterns
694  // all at once, after all of the without patterns have been thrown.
695  vector<MouseWatcherRegion *> new_regions;
696 
697  bool any_changes = false;
698  while (new_ri != regions.end() && old_ri != _current_regions.end()) {
699  if ((*new_ri) < (*old_ri)) {
700  // Here's a new region that we didn't have last frame.
701  MouseWatcherRegion *new_region = (*new_ri);
702  new_regions.push_back(new_region);
703  any_changes = true;
704  ++new_ri;
705 
706  } else if ((*old_ri) < (*new_ri)) {
707  // Here's a region we don't have any more.
708  MouseWatcherRegion *old_region = (*old_ri);
709  without_region(old_region, param);
710  any_changes = true;
711  ++old_ri;
712 
713  } else {
714  // Here's a region that hasn't changed.
715  ++new_ri;
716  ++old_ri;
717  }
718  }
719 
720  while (new_ri != regions.end()) {
721  // Here's a new region that we didn't have last frame.
722  MouseWatcherRegion *new_region = (*new_ri);
723  new_regions.push_back(new_region);
724  any_changes = true;
725  ++new_ri;
726  }
727 
728  while (old_ri != _current_regions.end()) {
729  // Here's a region we don't have any more.
730  MouseWatcherRegion *old_region = (*old_ri);
731  without_region(old_region, param);
732  any_changes = true;
733  ++old_ri;
734  }
735 
736  if (any_changes) {
737  // Now that we've compared the two vectors, simply swap them to set
738  // the new vector.
739  _current_regions.swap(regions);
740 
741  // And don't forget to throw all of the new regions' "within" events.
742  vector<MouseWatcherRegion *>::const_iterator ri;
743  for (ri = new_regions.begin(); ri != new_regions.end(); ++ri) {
744  MouseWatcherRegion *new_region = (*ri);
745  within_region(new_region, param);
746  }
747  }
748 
749  if (!_enter_multiple) {
750  // Determine which is the "preferred region", if any. This is the
751  // topmost region that the mouse cursor is over, and the one that
752  // we are considered "entered" into.
753  MouseWatcherRegion *new_preferred_region =
754  get_preferred_region(_current_regions);
755 
756  if (_button_down && new_preferred_region != _preferred_button_down_region) {
757  // If the button's being held down, we're only allowed to select
758  // the preferred button down region.
759  new_preferred_region = (MouseWatcherRegion *)NULL;
760  }
761 
762  if (new_preferred_region != _preferred_region) {
763  if (_preferred_region != (MouseWatcherRegion *)NULL) {
764  exit_region(_preferred_region, param);
765  }
766  _preferred_region = new_preferred_region;
767  if (_preferred_region != (MouseWatcherRegion *)NULL) {
768  enter_region(_preferred_region, param);
769  }
770  }
771  }
772 }
773 
774 ////////////////////////////////////////////////////////////////////
775 // Function: MouseWatcher::clear_current_regions
776 // Access: Protected
777 // Description: Empties the set of current regions. Assumes the lock
778 // is already held.
779 ////////////////////////////////////////////////////////////////////
780 void MouseWatcher::
781 clear_current_regions() {
782  nassertv(_lock.debug_is_locked());
783 
784  if (!_current_regions.empty()) {
785  // Set up a parameter for passing through any change events.
786  MouseWatcherParameter param;
787  param.set_modifier_buttons(_mods);
788  param.set_mouse(_mouse);
789 
790  Regions::const_iterator old_ri = _current_regions.begin();
791 
792  while (old_ri != _current_regions.end()) {
793  // Here's a region we don't have any more.
794  MouseWatcherRegion *old_region = (*old_ri);
795  old_region->exit_region(param);
796  throw_event_pattern(_leave_pattern, old_region, ButtonHandle::none());
797  ++old_ri;
798  }
799 
800  _current_regions.clear();
801 
802  if (_preferred_region != (MouseWatcherRegion *)NULL) {
803  _preferred_region->exit_region(param);
804  throw_event_pattern(_leave_pattern, _preferred_region, ButtonHandle::none());
805  _preferred_region = (MouseWatcherRegion *)NULL;
806  }
807  }
808 }
809 
810 #ifndef NDEBUG
811 ////////////////////////////////////////////////////////////////////
812 // Function: MouseWatcher::do_show_regions
813 // Access: Protected, Virtual
814 // Description: The protected implementation of show_regions(). This
815 // assumes the lock is already held.
816 ////////////////////////////////////////////////////////////////////
817 void MouseWatcher::
818 do_show_regions(const NodePath &render2d, const string &bin_name,
819  int draw_order) {
820  MouseWatcherBase::do_show_regions(render2d, bin_name, draw_order);
821  _show_regions_render2d = render2d;
822  _show_regions_bin_name = bin_name;
823  _show_regions_draw_order = draw_order;
824 
825  Groups::const_iterator gi;
826  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
827  MouseWatcherGroup *group = (*gi);
828  group->show_regions(render2d, bin_name, draw_order);
829  }
830 }
831 #endif // NDEBUG
832 
833 #ifndef NDEBUG
834 ////////////////////////////////////////////////////////////////////
835 // Function: MouseWatcher::do_hide_regions
836 // Access: Protected, Virtual
837 // Description: The protected implementation of hide_regions(). This
838 // assumes the lock is already held.
839 ////////////////////////////////////////////////////////////////////
840 void MouseWatcher::
841 do_hide_regions() {
842  MouseWatcherBase::do_hide_regions();
843  _show_regions_render2d = NodePath();
844  _show_regions_bin_name = string();
845  _show_regions_draw_order = 0;
846 
847  Groups::const_iterator gi;
848  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
849  MouseWatcherGroup *group = (*gi);
850  group->hide_regions();
851  }
852 }
853 #endif // NDEBUG
854 
855 ////////////////////////////////////////////////////////////////////
856 // Function: MouseWatcher::intersect_regions
857 // Access: Protected, Static
858 // Description: Computes the list of regions that are in both
859 // regions_a and regions_b, as well as the list of
860 // regions only in regions_a, and the list of regions
861 // only in regions_b. Any or all of the three output
862 // lists may be the same object, but they must be
863 // different objects from both of the input lists.
864 //
865 // It is assumed that both vectors are already sorted in
866 // pointer order. It is also assumed that any relevant
867 // locks are already held.
868 ////////////////////////////////////////////////////////////////////
869 void MouseWatcher::
870 intersect_regions(MouseWatcher::Regions &only_a,
871  MouseWatcher::Regions &only_b,
872  MouseWatcher::Regions &both,
873  const MouseWatcher::Regions &regions_a,
874  const MouseWatcher::Regions &regions_b) {
875  // Now do a standard sorted intersection between the two vectors.
876  Regions::const_iterator a_ri = regions_a.begin();
877  Regions::const_iterator b_ri = regions_b.begin();
878 
879  while (a_ri != regions_a.end() && b_ri != regions_b.end()) {
880  if ((*a_ri) < (*b_ri)) {
881  // Here's a region in a, not in b.
882  only_a.push_back(*a_ri);
883  ++a_ri;
884 
885  } else if ((*b_ri) < (*a_ri)) {
886  // Here's a region in b, not in a.
887  only_b.push_back(*b_ri);
888  ++b_ri;
889 
890  } else {
891  // Here's a region in both vectors.
892  both.push_back(*a_ri);
893  ++a_ri;
894  ++b_ri;
895  }
896  }
897 }
898 
899 ////////////////////////////////////////////////////////////////////
900 // Function: MouseWatcher::remove_region_from
901 // Access: Protected, Static
902 // Description: Removes the indicated region from the given vector.
903 // Assumes the vector is sorted in pointer order.
904 // Returns true if removed, false if it wasn't there.
905 // Assumes any relevent locks are already held.
906 ////////////////////////////////////////////////////////////////////
907 bool MouseWatcher::
908 remove_region_from(MouseWatcher::Regions &regions,
909  MouseWatcherRegion *region) {
910  PT(MouseWatcherRegion) ptr = region;
911  Regions::iterator ri = lower_bound(regions.begin(), regions.end(), ptr);
912  if (ri != regions.end() && (*ri) == ptr) {
913  // The region is in the vector. Remove it.
914  regions.erase(ri);
915  return true;
916  }
917 
918  return false;
919 }
920 
921 ////////////////////////////////////////////////////////////////////
922 // Function: MouseWatcher::has_region_in
923 // Access: Protected, Static
924 // Description: Returns true if the indicated region is a member of
925 // the given sorted list, false otherwise.
926 ////////////////////////////////////////////////////////////////////
927 bool MouseWatcher::
928 has_region_in(const MouseWatcher::Regions &regions,
929  MouseWatcherRegion *region) {
930  PT(MouseWatcherRegion) ptr = region;
931  Regions::const_iterator ri = lower_bound(regions.begin(), regions.end(), ptr);
932  return (ri != regions.end() && (*ri) == ptr);
933 }
934 
935 ////////////////////////////////////////////////////////////////////
936 // Function: MouseWatcher::throw_event_pattern
937 // Access: Protected
938 // Description: Throws an event associated with the indicated region,
939 // using the given pattern.
940 ////////////////////////////////////////////////////////////////////
941 void MouseWatcher::
942 throw_event_pattern(const string &pattern, const MouseWatcherRegion *region,
943  const ButtonHandle &button) {
944  if (pattern.empty()) {
945  return;
946  }
947 #ifndef NDEBUG
948  if (region != (MouseWatcherRegion *)NULL) {
949  region->test_ref_count_integrity();
950  }
951 #endif
952 
953  string button_name;
954  if (button != ButtonHandle::none()) {
955  if (!_mods.has_button(button)) {
956  // We only prepend modifier names for buttons which are not
957  // themselves modifiers.
958  button_name = _mods.get_prefix();
959  }
960  button_name += button.get_name();
961  }
962 
963  string event;
964  for (size_t p = 0; p < pattern.size(); ++p) {
965  if (pattern[p] == '%') {
966  string cmd = pattern.substr(p + 1, 1);
967  p++;
968  if (cmd == "r") {
969  if (region != (MouseWatcherRegion *)NULL) {
970  event += region->get_name();
971  }
972 
973  } else if (cmd == "b") {
974  event += button.get_name();
975 
976  } else {
977  tform_cat.error()
978  << "Invalid symbol in event_pattern: %" << cmd << "\n";
979  }
980  } else {
981  event += pattern[p];
982  }
983  }
984 
985  if (!event.empty()) {
986  throw_event(event, EventParameter(region), EventParameter(button_name));
987  if (_eh != (EventHandler*)0L)
988  throw_event_directly(*_eh, event, EventParameter(region),
989  EventParameter(button_name));
990  }
991 }
992 
993 ////////////////////////////////////////////////////////////////////
994 // Function: MouseWatcher::move
995 // Access: Protected
996 // Description: Records the indicated mouse or keyboard button as
997 // being moved from last position.
998 ////////////////////////////////////////////////////////////////////
999 void MouseWatcher::
1000 move() {
1001  nassertv(_lock.debug_is_locked());
1002 
1003  MouseWatcherParameter param;
1004  param.set_modifier_buttons(_mods);
1005  param.set_mouse(_mouse);
1006 
1007  if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) {
1008  _preferred_button_down_region->move(param);
1009  }
1010 }
1011 
1012 ////////////////////////////////////////////////////////////////////
1013 // Function: MouseWatcher::press
1014 // Access: Protected
1015 // Description: Records the indicated mouse or keyboard button as
1016 // being depressed.
1017 ////////////////////////////////////////////////////////////////////
1018 void MouseWatcher::
1019 press(ButtonHandle button, bool keyrepeat) {
1020  nassertv(_lock.debug_is_locked());
1021 
1022  MouseWatcherParameter param;
1023  param.set_button(button);
1024  param.set_keyrepeat(keyrepeat);
1025  param.set_modifier_buttons(_mods);
1026  param.set_mouse(_mouse);
1027 
1028  if (MouseButton::is_mouse_button(button)) {
1029  // Mouse buttons are inextricably linked to the mouse position.
1030 
1031  if (!_button_down) {
1032  _preferred_button_down_region = _preferred_region;
1033  }
1034  _button_down = true;
1035 
1036  if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) {
1037  _preferred_button_down_region->press(param);
1038  if (keyrepeat) {
1039  throw_event_pattern(_button_repeat_pattern,
1040  _preferred_button_down_region, button);
1041  } else {
1042  throw_event_pattern(_button_down_pattern,
1043  _preferred_button_down_region, button);
1044  }
1045  }
1046 
1047  } else {
1048  // It's a keyboard button; therefore, send the event to every
1049  // region that wants keyboard buttons, regardless of the mouse
1050  // position.
1051  if (_preferred_region != (MouseWatcherRegion *)NULL) {
1052  // Our current region, the one under the mouse, always get
1053  // all the keyboard events, even if it doesn't set its
1054  // keyboard flag.
1055  _preferred_region->press(param);
1056  consider_keyboard_suppress(_preferred_region);
1057  }
1058 
1059  if ((_internal_suppress & MouseWatcherRegion::SF_other_button) == 0) {
1060  // All the other regions only get the keyboard events if they
1061  // set their global keyboard flag, *and* the current region does
1062  // not suppress keyboard buttons.
1063  param.set_outside(true);
1064  global_keyboard_press(param);
1065  }
1066  }
1067 }
1068 
1069 ////////////////////////////////////////////////////////////////////
1070 // Function: MouseWatcher::release
1071 // Access: Protected
1072 // Description: Records the indicated mouse or keyboard button as
1073 // being released.
1074 ////////////////////////////////////////////////////////////////////
1075 void MouseWatcher::
1076 release(ButtonHandle button) {
1077  nassertv(_lock.debug_is_locked());
1078 
1079  MouseWatcherParameter param;
1080  param.set_button(button);
1081  param.set_modifier_buttons(_mods);
1082  param.set_mouse(_mouse);
1083 
1084  if (MouseButton::is_mouse_button(button)) {
1085  // Button up. Send the up event associated with the region(s) we
1086  // were over when the button went down.
1087 
1088  // There is some danger of losing button-up events here. If
1089  // more than one button goes down together, we won't detect
1090  // both of the button-up events properly.
1091  if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) {
1092  param.set_outside(_preferred_button_down_region != _preferred_region);
1093  _preferred_button_down_region->release(param);
1094  throw_event_pattern(_button_up_pattern,
1095  _preferred_button_down_region, button);
1096  }
1097 
1098  _button_down = false;
1099  _preferred_button_down_region = (MouseWatcherRegion *)NULL;
1100 
1101  } else {
1102  // It's a keyboard button; therefore, send the event to every
1103  // region that wants keyboard buttons, regardless of the mouse
1104  // position.
1105  if (_preferred_region != (MouseWatcherRegion *)NULL) {
1106  _preferred_region->release(param);
1107  }
1108 
1109  param.set_outside(true);
1110  global_keyboard_release(param);
1111  }
1112 }
1113 
1114 ////////////////////////////////////////////////////////////////////
1115 // Function: MouseWatcher::keystroke
1116 // Access: Protected
1117 // Description: Records that the indicated keystroke has been
1118 // generated.
1119 ////////////////////////////////////////////////////////////////////
1120 void MouseWatcher::
1121 keystroke(int keycode) {
1122  nassertv(_lock.debug_is_locked());
1123 
1124  MouseWatcherParameter param;
1125  param.set_keycode(keycode);
1126  param.set_modifier_buttons(_mods);
1127  param.set_mouse(_mouse);
1128 
1129  // Keystrokes go to all those regions that want keyboard events,
1130  // regardless of which is the "preferred" region (that is, without
1131  // respect to the mouse position). However, we do set the outside
1132  // flag according to whether the given region is the preferred
1133  // region or not.
1134 
1135  Regions::const_iterator ri;
1136  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
1137  MouseWatcherRegion *region = (*ri);
1138 
1139  if (region->get_keyboard()) {
1140  param.set_outside(region != _preferred_region);
1141  region->keystroke(param);
1142  consider_keyboard_suppress(region);
1143  }
1144  }
1145 
1146  // Also check all of our sub-groups.
1147  Groups::const_iterator gi;
1148  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
1149  MouseWatcherGroup *group = (*gi);
1150  for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
1151  MouseWatcherRegion *region = (*ri);
1152 
1153  if (region->get_keyboard()) {
1154  param.set_outside(region != _preferred_region);
1155  region->keystroke(param);
1156  consider_keyboard_suppress(region);
1157  }
1158  }
1159  }
1160 }
1161 
1162 ////////////////////////////////////////////////////////////////////
1163 // Function: MouseWatcher::candidate
1164 // Access: Protected
1165 // Description: Records that the indicated candidate string has been
1166 // highlighted in the IME.
1167 ////////////////////////////////////////////////////////////////////
1168 void MouseWatcher::
1169 candidate(const wstring &candidate_string, size_t highlight_start,
1170  size_t highlight_end, size_t cursor_pos) {
1171  nassertv(_lock.debug_is_locked());
1172 
1173  MouseWatcherParameter param;
1174  param.set_candidate(candidate_string, highlight_start, highlight_end, cursor_pos);
1175  param.set_modifier_buttons(_mods);
1176  param.set_mouse(_mouse);
1177 
1178  // Candidate strings go to all those regions that want keyboard
1179  // events, exactly like keystrokes, above.
1180 
1181  Regions::const_iterator ri;
1182  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
1183  MouseWatcherRegion *region = (*ri);
1184 
1185  if (region->get_keyboard()) {
1186  param.set_outside(region != _preferred_region);
1187  region->candidate(param);
1188  }
1189  }
1190 
1191  // Also check all of our sub-groups.
1192  Groups::const_iterator gi;
1193  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
1194  MouseWatcherGroup *group = (*gi);
1195  for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
1196  MouseWatcherRegion *region = (*ri);
1197 
1198  if (region->get_keyboard()) {
1199  param.set_outside(region != _preferred_region);
1200  region->candidate(param);
1201  }
1202  }
1203  }
1204 }
1205 
1206 ////////////////////////////////////////////////////////////////////
1207 // Function: MouseWatcher::global_keyboard_press
1208 // Access: Protected
1209 // Description: Calls press() on all regions that are interested in
1210 // receiving global keyboard events, except for the
1211 // current region (which already received this one).
1212 ////////////////////////////////////////////////////////////////////
1213 void MouseWatcher::
1214 global_keyboard_press(const MouseWatcherParameter &param) {
1215  nassertv(_lock.debug_is_locked());
1216 
1217  Regions::const_iterator ri;
1218  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
1219  MouseWatcherRegion *region = (*ri);
1220 
1221  if (region != _preferred_region && region->get_keyboard()) {
1222  region->press(param);
1223  consider_keyboard_suppress(region);
1224  }
1225  }
1226 
1227  // Also check all of our sub-groups.
1228  Groups::const_iterator gi;
1229  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
1230  MouseWatcherGroup *group = (*gi);
1231  for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
1232  MouseWatcherRegion *region = (*ri);
1233 
1234  if (region != _preferred_region && region->get_keyboard()) {
1235  region->press(param);
1236  consider_keyboard_suppress(region);
1237  }
1238  }
1239  }
1240 }
1241 ////////////////////////////////////////////////////////////////////
1242 // Function: MouseWatcher::global_keyboard_release
1243 // Access: Protected
1244 // Description: Calls release() on all regions that are interested in
1245 // receiving global keyboard events, except for the
1246 // current region (which already received this one).
1247 ////////////////////////////////////////////////////////////////////
1248 void MouseWatcher::
1249 global_keyboard_release(const MouseWatcherParameter &param) {
1250  nassertv(_lock.debug_is_locked());
1251 
1252  Regions::const_iterator ri;
1253  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
1254  MouseWatcherRegion *region = (*ri);
1255 
1256  if (region != _preferred_region && region->get_keyboard()) {
1257  region->release(param);
1258  }
1259  }
1260 
1261  // Also check all of our sub-groups.
1262  Groups::const_iterator gi;
1263  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
1264  MouseWatcherGroup *group = (*gi);
1265  for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
1266  MouseWatcherRegion *region = (*ri);
1267 
1268  if (region != _preferred_region && region->get_keyboard()) {
1269  region->release(param);
1270  }
1271  }
1272  }
1273 }
1274 
1275 ////////////////////////////////////////////////////////////////////
1276 // Function: MouseWatcher::enter_region
1277 // Access: Protected
1278 // Description: Called internally to indicate the mouse pointer is
1279 // favoring the indicated region.
1280 ////////////////////////////////////////////////////////////////////
1281 void MouseWatcher::
1282 enter_region(MouseWatcherRegion *region, const MouseWatcherParameter &param) {
1283  nassertv(_lock.debug_is_locked());
1284 
1285  region->enter_region(param);
1286  throw_event_pattern(_enter_pattern, region, ButtonHandle::none());
1287  if (_implicit_click) {
1288  MouseWatcherParameter param1(param);
1289  param1.set_button(MouseButton::one());
1290  region->press(param1);
1291  }
1292 }
1293 
1294 ////////////////////////////////////////////////////////////////////
1295 // Function: MouseWatcher::exit_region
1296 // Access: Protected
1297 // Description: Called internally to indicate the mouse pointer is no
1298 // longer favoring the indicated region.
1299 ////////////////////////////////////////////////////////////////////
1300 void MouseWatcher::
1301 exit_region(MouseWatcherRegion *region, const MouseWatcherParameter &param) {
1302  nassertv(_lock.debug_is_locked());
1303 
1304  if (_implicit_click) {
1305  MouseWatcherParameter param1(param);
1306  param1.set_button(MouseButton::one());
1307  region->release(param1);
1308  }
1309  region->exit_region(param);
1310  throw_event_pattern(_leave_pattern, region, ButtonHandle::none());
1311 }
1312 
1313 ////////////////////////////////////////////////////////////////////
1314 // Function: MouseWatcher::set_no_mouse
1315 // Access: Protected
1316 // Description: Called from do_transmit_data() to indicate the mouse
1317 // is not within the window.
1318 ////////////////////////////////////////////////////////////////////
1319 void MouseWatcher::
1320 set_no_mouse() {
1321  nassertv(_lock.debug_is_locked());
1322 
1323  if (_has_mouse) {
1324  // Hide the mouse pointer.
1325  if (!_geometry.is_null()) {
1326  _geometry->set_overall_hidden(true);
1327  }
1328  }
1329 
1330  _has_mouse = false;
1331  clear_current_regions();
1332 }
1333 
1334 ////////////////////////////////////////////////////////////////////
1335 // Function: MouseWatcher::set_mouse
1336 // Access: Protected
1337 // Description: Called from do_transmit_data() to indicate the mouse
1338 // is within the window, and to specify its current
1339 // position.
1340 ////////////////////////////////////////////////////////////////////
1341 void MouseWatcher::
1342 set_mouse(const LVecBase2 &xy, const LVecBase2 &pixel_xy) {
1343  nassertv(_lock.debug_is_locked());
1344 
1345  if (!_geometry.is_null()) {
1346  // Transform the mouse pointer.
1347  _geometry->set_transform(TransformState::make_pos(LVecBase3(xy[0], 0, xy[1])));
1348  if (!_has_mouse) {
1349  // Show the mouse pointer.
1350  _geometry->set_overall_hidden(false);
1351  }
1352  }
1353 
1354  _has_mouse = true;
1355  _mouse = xy;
1356  _mouse_pixel = pixel_xy;
1357 
1358  Regions regions;
1359  get_over_regions(regions, _mouse);
1360  set_current_regions(regions);
1361 }
1362 
1363 ////////////////////////////////////////////////////////////////////
1364 // Function: MouseWatcher::consider_keyboard_suppress
1365 // Access: Private
1366 // Description: If we send any keyboard events to a region that has
1367 // the SF_other_button suppress flag set, that means we
1368 // should not send the keyboard event along the data
1369 // graph.
1370 //
1371 // This method is called as each keyboard event is sent
1372 // to a region; it should update the internal
1373 // _keyboard_suppress bitmask to indicate this.
1374 ////////////////////////////////////////////////////////////////////
1375 void MouseWatcher::
1376 consider_keyboard_suppress(const MouseWatcherRegion *region) {
1377  if ((region->get_suppress_flags() & MouseWatcherRegion::SF_other_button) != 0) {
1378  _external_suppress |= MouseWatcherRegion::SF_other_button;
1379  }
1380 }
1381 
1382 ////////////////////////////////////////////////////////////////////
1383 // Function: MouseWatcher::do_transmit_data
1384 // Access: Protected, Virtual
1385 // Description: The virtual implementation of transmit_data(). This
1386 // function receives an array of input parameters and
1387 // should generate an array of output parameters. The
1388 // input parameters may be accessed with the index
1389 // numbers returned by the define_input() calls that
1390 // were made earlier (presumably in the constructor);
1391 // likewise, the output parameters should be set with
1392 // the index numbers returned by the define_output()
1393 // calls.
1394 ////////////////////////////////////////////////////////////////////
1395 void MouseWatcher::
1396 do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
1397  DataNodeTransmit &output) {
1398  Thread *current_thread = trav->get_current_thread();
1399  LightMutexHolder holder(_lock);
1400 
1401  bool activity = false;
1402 
1403  // Initially, we do not suppress any events to objects below us in
1404  // the data graph.
1405  _internal_suppress = 0;
1406  _external_suppress = 0;
1407 
1408  // We always pass the pixel_size data through.
1409  EventStoreVec2 *pixel_size;
1410  DCAST_INTO_V(pixel_size, input.get_data(_pixel_size_input).get_ptr());
1411  output.set_data(_pixel_size_output, pixel_size);
1412  _pixel_size = pixel_size;
1413 
1414  if (input.has_data(_xy_input)) {
1415  // The mouse is within the window. Get the current mouse position.
1416  const EventStoreVec2 *xy, *pixel_xy;
1417  DCAST_INTO_V(xy, input.get_data(_xy_input).get_ptr());
1418  DCAST_INTO_V(pixel_xy, input.get_data(_pixel_xy_input).get_ptr());
1419 
1420  LVecBase2 f = xy->get_value();
1421  LVecBase2 p = pixel_xy->get_value();
1422 
1423  // Asad: determine if mouse moved from last position
1424  const LVecBase2 &last_f = _xy->get_value();
1425  if (f != last_f) {
1426  activity = true;
1427  move();
1428  }
1429 
1430  if (_display_region != (DisplayRegion *)NULL) {
1431  // If we've got a display region, constrain the mouse to it.
1432  if (constrain_display_region(_display_region, f, p, current_thread)) {
1433  set_mouse(f, p);
1434 
1435  } else {
1436  // The mouse is outside the display region, even though it's
1437  // within the window. This is considered not having a mouse.
1438  set_no_mouse();
1439 
1440  // This also means we should suppress mouse button events below us.
1441  _internal_suppress |= MouseWatcherRegion::SF_mouse_button;
1442  }
1443 
1444  } else {
1445  // No display region; respect the whole window.
1446  set_mouse(f, p);
1447  }
1448  }
1449 
1450  // Code for recording the mouse trail.
1451  _num_trail_recent = 0;
1452  if (input.has_data(_pointer_events_input) && (_trail_log_duration > 0.0)) {
1453  const PointerEventList *this_pointer_events;
1454  DCAST_INTO_V(this_pointer_events, input.get_data(_pointer_events_input).get_ptr());
1455  _num_trail_recent = this_pointer_events->get_num_events();
1456  for (int i = 0; i < _num_trail_recent; i++) {
1457  bool in_win = this_pointer_events->get_in_window(i);
1458  int xpos = this_pointer_events->get_xpos(i);
1459  int ypos = this_pointer_events->get_ypos(i);
1460  int sequence = this_pointer_events->get_sequence(i);
1461  double time = this_pointer_events->get_time(i);
1462  _trail_log->add_event(in_win, xpos, ypos, sequence, time);
1463  }
1464  }
1465  if (_trail_log->get_num_events() > 0) {
1466  discard_excess_trail_log();
1467  update_trail_node();
1468  }
1469  if (_num_trail_recent > _trail_log->get_num_events()) {
1470  _num_trail_recent = _trail_log->get_num_events();
1471  }
1472 
1473  // If the mouse is over a particular region, or still considered
1474  // owned by a region because of a recent button-down event, that
1475  // region determines whether we suppress events below us.
1476  if (_preferred_region != (MouseWatcherRegion *)NULL) {
1477  _internal_suppress |= _preferred_region->get_suppress_flags();
1478  }
1479 
1480  ButtonEventList new_button_events;
1481 
1482  // Look for new button events.
1483  if (input.has_data(_button_events_input)) {
1484  const ButtonEventList *this_button_events;
1485  DCAST_INTO_V(this_button_events, input.get_data(_button_events_input).get_ptr());
1486  int num_events = this_button_events->get_num_events();
1487  for (int i = 0; i < num_events; i++) {
1488  const ButtonEvent &be = this_button_events->get_event(i);
1489  be.update_mods(_mods);
1490 
1491  switch (be._type) {
1492  case ButtonEvent::T_down:
1493  if (!_current_buttons_down.get_bit(be._button.get_index())) {
1494  // The button was not already depressed; thus, this is not
1495  // keyrepeat.
1496  activity = true;
1497  _current_buttons_down.set_bit(be._button.get_index());
1498  press(be._button, false);
1499  new_button_events.add_event(be);
1500  break;
1501  }
1502  // The button was already depressed, so this is really just
1503  // keyrepeat. Fall through.
1504 
1505  case ButtonEvent::T_repeat:
1506  _current_buttons_down.set_bit(be._button.get_index());
1507  press(be._button, true);
1508  new_button_events.add_event(ButtonEvent(be._button, ButtonEvent::T_repeat,
1509  be._time));
1510  break;
1511 
1512  case ButtonEvent::T_up:
1513  activity = true;
1514  _current_buttons_down.clear_bit(be._button.get_index());
1515  release(be._button);
1516  new_button_events.add_event(be);
1517  break;
1518 
1519  case ButtonEvent::T_keystroke:
1520  // We don't consider "keystroke" an activity event, because it
1521  // might be just keyrepeat.
1522  keystroke(be._keycode);
1523  new_button_events.add_event(be);
1524  break;
1525 
1526  case ButtonEvent::T_candidate:
1527  activity = true;
1528  candidate(be._candidate_string, be._highlight_start, be._highlight_end, be._cursor_pos);
1529  new_button_events.add_event(be);
1530  break;
1531 
1532  case ButtonEvent::T_resume_down:
1533  //_current_buttons_down.set_bit(be._button.get_index());
1534  // Don't call press(), since the button wasn't actually
1535  // pressed just now.
1536  new_button_events.add_event(be);
1537  break;
1538 
1539  case ButtonEvent::T_move:
1540  // This is handled below.
1541  break;
1542 
1543  case ButtonEvent::T_raw_down:
1544  case ButtonEvent::T_raw_up:
1545  // These are passed through.
1546  new_button_events.add_event(be);
1547  break;
1548  }
1549  }
1550  }
1551 
1552  if (!input.has_data(_xy_input)) {
1553  // No mouse in the window. We check this down here, below the
1554  // button checking, in case the mouse left the window in the same
1555  // frame it released a button (particularly likely with a
1556  // touchscreen input that's emulating a mouse).
1557  set_no_mouse();
1558  }
1559 
1560  // Now check the inactivity timer.
1561  if (_has_inactivity_timeout) {
1562  if (activity) {
1563  note_activity();
1564 
1565  } else {
1567  double elapsed = now - _last_activity;
1568 
1569  // Toggle the inactivity state to inactive.
1570  if (elapsed > _inactivity_timeout) {
1571  switch (_inactivity_state) {
1572  case IS_active:
1573  _inactivity_state = IS_active_to_inactive;
1574  break;
1575 
1576  case IS_inactive:
1577  break;
1578 
1579  case IS_active_to_inactive:
1580  break;
1581 
1582  case IS_inactive_to_active:
1583  _inactivity_state = IS_inactive;
1584  break;
1585  }
1586  }
1587  }
1588  }
1589 
1590  switch (_inactivity_state) {
1591  case IS_active:
1592  case IS_inactive:
1593  break;
1594 
1595  case IS_active_to_inactive:
1596  // "Release" all of the currently-held buttons.
1597  if (tform_cat.is_debug()) {
1598  tform_cat.info()
1599  << "MouseWatcher detected " << _inactivity_timeout
1600  << " seconds of inactivity; releasing held buttons.\n";
1601  }
1602  {
1603  for (int i = 0; i < _current_buttons_down.get_num_bits(); ++i) {
1604  if (_current_buttons_down.get_bit(i)) {
1605  release(ButtonHandle(i));
1606  new_button_events.add_event(ButtonEvent(ButtonHandle(i), ButtonEvent::T_up));
1607  }
1608  }
1609  }
1610  _inactivity_state = IS_inactive;
1611  throw_event(_inactivity_timeout_event);
1612  break;
1613 
1614  case IS_inactive_to_active:
1615  // "Press" all of the buttons we "released" before.
1616  {
1617  for (int i = 0; i < _current_buttons_down.get_num_bits(); ++i) {
1618  if (_current_buttons_down.get_bit(i)) {
1619  press(ButtonHandle(i), false);
1620  new_button_events.add_event(ButtonEvent(ButtonHandle(i), ButtonEvent::T_down));
1621  }
1622  }
1623  }
1624  _inactivity_state = IS_active;
1625  break;
1626  }
1627 
1628  if (_has_mouse &&
1629  (_internal_suppress & MouseWatcherRegion::SF_mouse_position) == 0) {
1630  // Transmit the mouse position.
1631  _xy->set_value(_mouse);
1632  output.set_data(_xy_output, EventParameter(_xy));
1633  _pixel_xy->set_value(_mouse_pixel);
1634  output.set_data(_pixel_xy_output, EventParameter(_pixel_xy));
1635  }
1636 
1637  // Now transmit the buttons events down the graph.
1638  int suppress_buttons = ((_internal_suppress | _external_suppress) & MouseWatcherRegion::SF_any_button);
1639 
1640  _button_events->clear();
1641 
1642  int num_events = new_button_events.get_num_events();
1643  for (int i = 0; i < num_events; i++) {
1644  const ButtonEvent &be = new_button_events.get_event(i);
1645  bool suppress = true;
1646 
1647  if (be._type != ButtonEvent::T_keystroke &&
1648  MouseButton::is_mouse_button(be._button)) {
1649  suppress = ((suppress_buttons & MouseWatcherRegion::SF_mouse_button) != 0);
1650  } else {
1651  suppress = ((suppress_buttons & MouseWatcherRegion::SF_other_button) != 0);
1652  }
1653 
1654  if (!suppress || be._type == ButtonEvent::T_up) {
1655  // Don't suppress this button event; pass it through.
1656  _button_events->add_event(be);
1657  }
1658  }
1659 
1660  if (_button_events->get_num_events() != 0) {
1661  output.set_data(_button_events_output, EventParameter(_button_events));
1662  }
1663 }
1664 
1665 ////////////////////////////////////////////////////////////////////
1666 // Function: MouseWatcher::constrain_display_region
1667 // Access: Private
1668 // Description: Constrains the mouse coordinates to within the
1669 // indicated DisplayRegion. If the mouse pointer does
1670 // indeed fall within the DisplayRegion, rescales f and
1671 // p correspondingly, and returns true. If the mouse
1672 // pointer does not fall within the DisplayRegion,
1673 // leaves f and p unchanged, and returns false.
1674 ////////////////////////////////////////////////////////////////////
1675 bool MouseWatcher::
1676 constrain_display_region(DisplayRegion *display_region,
1677  LVecBase2 &f, LVecBase2 &p,
1678  Thread *current_thread) {
1679  if (!_button_down) {
1680  _button_down_display_region = NULL;
1681  }
1682  if (_button_down_display_region != NULL) {
1683  // If the button went down over this DisplayRegion, we consider
1684  // the button within the same DisplayRegion until it is released
1685  // (even if it wanders outside the borders).
1686  display_region = _button_down_display_region;
1687 
1688  } else {
1689  // If it's a stereo DisplayRegion, we should actually call this
1690  // method twice, once for each eye, in case we have side-by-side
1691  // stereo.
1692  if (display_region->is_stereo()) {
1693  StereoDisplayRegion *stereo_display_region;
1694  DCAST_INTO_R(stereo_display_region, display_region, false);
1695  return constrain_display_region(stereo_display_region->get_left_eye(), f, p, current_thread) ||
1696  constrain_display_region(stereo_display_region->get_right_eye(), f, p, current_thread);
1697  }
1698  }
1699 
1700  DisplayRegionPipelineReader dr_reader(display_region, current_thread);
1701  PN_stdfloat left, right, bottom, top;
1702  dr_reader.get_dimensions(left, right, bottom, top);
1703 
1704  // Need to translate this into DisplayRegion [0, 1] space
1705  PN_stdfloat x = (f[0] + 1.0f) / 2.0f;
1706  PN_stdfloat y = (f[1] + 1.0f) / 2.0f;
1707 
1708  if (_button_down_display_region == NULL &&
1709  (x < left || x >= right || y < bottom || y >= top)) {
1710  // The mouse is outside the display region.
1711  return false;
1712  }
1713 
1714  // The mouse is within the display region; rescale it.
1715  if (_button_down) {
1716  _button_down_display_region = display_region;
1717  }
1718 
1719  // Scale in DR space
1720  PN_stdfloat xp = (x - left) / (right - left);
1721  // Translate back into [-1, 1] space
1722  PN_stdfloat xpp = (xp * 2.0f) - 1.0f;
1723 
1724  PN_stdfloat yp = (y - bottom) / (top - bottom);
1725  PN_stdfloat ypp = (yp * 2.0f) - 1.0f;
1726 
1727  int xo, yo, w, h;
1728  dr_reader.get_region_pixels_i(xo, yo, w, h);
1729 
1730  f.set(xpp, ypp);
1731  p.set(p[0] - xo, p[1] - yo);
1732  return true;
1733 }
bool get_bit(int index) const
Returns true if the nth bit is set, false if it is cleared.
Definition: bitArray.I:207
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:271
bool test_ref_count_integrity() const
Does some easy checks to make sure that the reference count isn&#39;t completely bogus.
This TFormer maintains a list of rectangular regions on the screen that are considered special mouse ...
Definition: mouseWatcher.h:68
static ButtonHandle none()
Returns a special zero-valued ButtonHandle that is used to indicate no button.
Definition: buttonHandle.I:205
bool get_in_window(int n) const
Get the in-window flag of the nth event.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
The fundamental type of node for the data graph.
Definition: dataNode.h:64
Encapsulates the data from a DisplayRegion, pre-fetched for one stage of the pipeline.
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
int get_index() const
Returns the integer index associated with this ButtonHandle.
Definition: buttonHandle.I:184
void add_event(ButtonEvent event)
Adds a new event to the end of the list.
This represents a collection of MouseWatcherRegions that may be managed as a group.
void set_modifier_buttons(const ModifierButtons &mods)
Sets the modifier buttons that were being held while this event was generated.
int get_xpos(int n) const
Get the x-coordinate of the nth event.
A class to monitor events from the C++ side of things.
Definition: eventHandler.h:41
An optional parameter associated with an event.
int get_sequence(int n) const
Get the sequence number of the nth event.
virtual bool is_stereo() const
Returns true if this is a StereoDisplayRegion, false otherwise.
virtual void exit_region(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the mouse exits the region.
virtual void release(const MouseWatcherParameter &param)
This is a callback hook function, called whenever a mouse or keyboard button previously depressed wit...
bool remove_region(MouseWatcherRegion *region)
Removes the indicated region from the group.
void set_trail_log_duration(double duration)
If the duration is nonzero, causes the MouseWatcher to log the mouse&#39;s trail.
void set_button(const ButtonHandle &button)
Sets the mouse or keyboard button that generated this event, if any.
static ButtonHandle one()
Returns the ButtonHandle associated with the first mouse button.
Definition: mouseButton.cxx:50
virtual void enter_region(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the mouse enters the region.
Records a set of pointer events that happened recently.
Records a button event of some kind.
Definition: buttonEvent.h:53
A handy class object for storing simple values (like integers or strings) passed along with an Event ...
Definition: paramValue.h:109
Records a set of button events that happened recently.
bool update_mods(ModifierButtons &mods) const
Calls button_down() or button_up(), as appropriate, according to the ButtonEvent. ...
Definition: buttonEvent.I:163
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
Definition: buttonHandle.h:28
void clear_bit(int index)
Sets the nth bit off.
Definition: bitArray.I:245
const ButtonEvent & get_event(int n) const
Returns the nth event in the list.
DisplayRegion * get_right_eye()
Returns a pointer to the right DisplayRegion managed by this stereo object.
void show_regions(const NodePath &render2d, const string &bin_name, int draw_order)
Enables the visualization of all of the regions handled by this MouseWatcherBase. ...
void hide_regions()
Stops the visualization created by a previous call to show_regions().
virtual void press(const MouseWatcherParameter &param)
This is a callback hook function, called whenever a mouse or keyboard button is depressed while the m...
virtual void candidate(const MouseWatcherParameter &param)
This is a callback hook function, called whenever an IME candidate is highlighted by the user...
void set_data(int index, const EventParameter &data)
Sets the data for the indicated parameter.
int get_num_groups() const
Returns the number of separate groups added to the MouseWatcher via add_group().
void set_mouse(const LPoint2 &mouse)
Sets the mouse position that was current at the time the event was generated.
double get_frame_time(Thread *current_thread=Thread::get_current_thread()) const
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition: clockObject.I:48
int get_num_events() const
Returns the number of events in the list.
Similar to MutexHolder, but for a light mutex.
This is the class that defines a rectangular region on the screen for the MouseWatcher.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
A container for geometry primitives.
Definition: geom.h:58
const EventParameter & get_data(int index) const
Extracts the data for the indicated index, if it has been stored, or the empty parameter if it has no...
This is the base class for all two-component vectors and points.
Definition: lvecBase2.h:105
int get_suppress_flags() const
Returns the current suppress_flags.
MouseWatcherGroup * get_group(int n) const
Returns the nth group added to the MouseWatcher via add_group().
void set_keycode(int keycode)
Sets the keycode associated with this event, if any.
bool get_keyboard() const
Returns whether the region is interested in global keyboard events; see set_keyboard().
This is a special DisplayRegion wrapper that actually includes a pair of DisplayRegions internally: t...
int get_num_events() const
Returns the number of events in the list.
const Type & get_value() const
Retrieves the value stored in the parameter.
Definition: paramValue.I:136
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
void set_bit(int index)
Sets the nth bit on.
Definition: bitArray.I:225
bool add_group(MouseWatcherGroup *group)
Adds the indicated group of regions to the set of regions the MouseWatcher will monitor each frame...
int get_num_bits() const
Returns the current number of possibly different bits in this array.
Definition: bitArray.I:194
MouseWatcherRegion * get_over_region() const
Returns the smallest region the mouse is currently over, or NULL if it is over no region...
Definition: mouseWatcher.I:165
virtual void keystroke(const MouseWatcherParameter &param)
This is a callback hook function, called whenever a keystroke is generated by the user...
void note_activity()
Can be used in conjunction with the inactivity timeout to inform the MouseWatcher that the user has j...
A thread; that is, a lightweight process.
Definition: thread.h:51
Defines a series of line strips.
bool get_active() const
Returns whether the region is active or not.
TypedWritableReferenceCount * get_ptr() const
Retrieves a pointer to the actual value stored in the parameter.
string get_prefix() const
Returns a string which can be used to prefix any button name or event name with the unique set of mod...
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:236
void clear_trail_node()
If you have previously fetched the trail node using get_trail_node, then the MouseWatcher is continua...
A rectangular subregion within a window for rendering into.
Definition: displayRegion.h:61
bool remove_group(MouseWatcherGroup *group)
Removes the indicated group from the set of extra groups associated with the MouseWatcher.
static bool is_mouse_button(ButtonHandle button)
Returns true if the indicated ButtonHandle is a mouse button, false if it is some other kind of butto...
void set_candidate(const wstring &candidate_string, size_t highlight_start, size_t higlight_end, size_t cursor_pos)
Sets the candidate string associated with this event, if any.
This is a two-component point in space.
Definition: lpoint2.h:92
int get_ypos(int n) const
Get the y-coordinate of the nth event.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
DisplayRegion * get_left_eye()
Returns a pointer to the left DisplayRegion managed by this stereo object.
This is sent along as a parameter to most events generated for a region to indicate the mouse and but...
bool has_data(int index) const
Returns true if the indicated parameter has been stored, false otherwise.
bool has_button(ButtonHandle button) const
Returns true if the indicated button is in the set of buttons being monitored, false otherwise...
double get_time(int n) const
Get the timestamp of the nth event.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:37
Encapsulates the data generated from (or sent into) any particular DataNode.
Thread * get_current_thread() const
Returns the currently-executing thread object, as passed to the DataGraphTraverser constructor...
static int size()
Returns 3: the number of components of a LVecBase3.
Definition: lvecBase3.h:452
This object supervises the traversal of the data graph and the moving of data from one DataNode to it...
bool replace_group(MouseWatcherGroup *old_group, MouseWatcherGroup *new_group)
Atomically removes old_group from the MouseWatcher, and replaces it with new_group.