소스 검색

make PGui more thread-safe

David Rose 20 년 전
부모
커밋
8fbc824aab

+ 6 - 1
panda/src/gobj/geom.cxx

@@ -37,7 +37,12 @@ TypeHandle Geom::_type_handle;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 Geom::
 Geom::
 Geom(const GeomVertexData *data) {
 Geom(const GeomVertexData *data) {
-  set_vertex_data(data);
+  // Let's ensure the vertex data gets set on all stages at once.
+  OPEN_ITERATE_ALL_STAGES(_cycler) {
+    CDStageWriter cdata(_cycler, pipeline_stage);
+    cdata->_data = (GeomVertexData *)data;
+  }
+  CLOSE_ITERATE_ALL_STAGES(_cycler);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 6 - 1
panda/src/gobj/geomVertexArrayData.cxx

@@ -47,7 +47,12 @@ GeomVertexArrayData(const GeomVertexArrayFormat *array_format,
                     GeomVertexArrayData::UsageHint usage_hint) :
                     GeomVertexArrayData::UsageHint usage_hint) :
   _array_format(array_format)
   _array_format(array_format)
 {
 {
-  set_usage_hint(usage_hint);
+  OPEN_ITERATE_ALL_STAGES(_cycler) {
+    CDStageWriter cdata(_cycler, pipeline_stage);
+    cdata->_usage_hint = usage_hint;
+  }
+  CLOSE_ITERATE_ALL_STAGES(_cycler);
+
   _endian_reversed = false;
   _endian_reversed = false;
   nassertv(_array_format->is_registered());
   nassertv(_array_format->is_registered());
 }
 }

+ 10 - 7
panda/src/gobj/geomVertexData.cxx

@@ -67,14 +67,17 @@ GeomVertexData(const string &name,
   set_usage_hint(usage_hint);
   set_usage_hint(usage_hint);
 
 
   // Create some empty arrays as required by the format.
   // Create some empty arrays as required by the format.
-  CDWriter cdata(_cycler, true);
-
-  int num_arrays = _format->get_num_arrays();
-  for (int i = 0; i < num_arrays; i++) {
-    PT(GeomVertexArrayData) array = new GeomVertexArrayData
-      (_format->get_array(i), usage_hint);
-    cdata->_arrays.push_back(array);
+  // Let's ensure the vertex data gets set on all stages at once.
+  OPEN_ITERATE_ALL_STAGES(_cycler) {
+    CDStageWriter cdata(_cycler, pipeline_stage);
+    int num_arrays = _format->get_num_arrays();
+    for (int i = 0; i < num_arrays; i++) {
+      PT(GeomVertexArrayData) array = new GeomVertexArrayData
+        (_format->get_array(i), usage_hint);
+      cdata->_arrays.push_back(array);
+    }
   }
   }
+  CLOSE_ITERATE_ALL_STAGES(_cycler);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 0 - 13
panda/src/pgui/pgTop.I

@@ -102,16 +102,3 @@ add_region(MouseWatcherRegion *region) {
   nassertv(_watcher_group != (PGMouseWatcherGroup *)NULL);
   nassertv(_watcher_group != (PGMouseWatcherGroup *)NULL);
   _watcher_group->add_region(region);
   _watcher_group->add_region(region);
 }
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: PGTop::clear_regions
-//       Access: Public
-//  Description: Removes all the regions from the group.
-////////////////////////////////////////////////////////////////////
-INLINE void PGTop::
-clear_regions() {
-  if (_watcher_group == (PGMouseWatcherGroup *)NULL) {
-    return;
-  }
-  _watcher_group->clear_regions();
-}

+ 18 - 6
panda/src/pgui/pgTop.cxx

@@ -34,7 +34,6 @@ PGTop::
 PGTop(const string &name) : 
 PGTop(const string &name) : 
   PandaNode(name)
   PandaNode(name)
 {
 {
-  _watcher_group = (PGMouseWatcherGroup *)NULL;
   _start_sort = 0;
   _start_sort = 0;
 
 
   // A PGTop node normally has an infinite bounding volume.  Screw
   // A PGTop node normally has an infinite bounding volume.  Screw
@@ -107,9 +106,14 @@ has_cull_callback() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PGTop::
 bool PGTop::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
-  // Empty our set of regions in preparation for re-adding whichever
-  // ones we encounter in the traversal that are current.
-  clear_regions();
+  // We create a new MouseWatcherGroup for the purposes of collecting
+  // a new set of regions visible onscreen.
+  PT(PGMouseWatcherGroup) old_watcher_group;
+  if (_watcher_group != (PGMouseWatcherGroup *)NULL) {
+    _watcher_group->clear_top(this);
+    old_watcher_group = _watcher_group;
+    _watcher_group = new PGMouseWatcherGroup(this);
+  }
 
 
   // Now subsitute for the normal CullTraverser a special one of our
   // Now subsitute for the normal CullTraverser a special one of our
   // own choosing.  This just carries around a pointer back to the
   // own choosing.  This just carries around a pointer back to the
@@ -119,6 +123,16 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   pg_trav._sort_index = _start_sort;
   pg_trav._sort_index = _start_sort;
   pg_trav.traverse_below(data);
   pg_trav.traverse_below(data);
 
 
+  // Now tell the watcher about the new set of regions.  Strictly
+  // speaking, we shouldn't do this until the frame that we're about
+  // to render has been presented; otherwise, we may make regions
+  // active before they are actually visible.  But no one has
+  // complained about this so far.
+  if (_watcher_group != (PGMouseWatcherGroup *)NULL) {
+    nassertr(_watcher != (MouseWatcher *)NULL, false);
+    _watcher->replace_group(old_watcher_group, _watcher_group);
+  }
+
   // We've taken care of the traversal, thank you.
   // We've taken care of the traversal, thank you.
   return false;
   return false;
 }
 }
@@ -143,8 +157,6 @@ set_mouse_watcher(MouseWatcher *watcher) {
   _watcher_group = (PGMouseWatcherGroup *)NULL;
   _watcher_group = (PGMouseWatcherGroup *)NULL;
 
 
   if (_watcher != (MouseWatcher *)NULL) {
   if (_watcher != (MouseWatcher *)NULL) {
-    // We create a new PGMouseWatcherGroup, but we don't own the
-    // reference count; the watcher will own this for us.
     _watcher_group = new PGMouseWatcherGroup(this);
     _watcher_group = new PGMouseWatcherGroup(this);
     _watcher->add_group(_watcher_group);
     _watcher->add_group(_watcher_group);
   }
   }

+ 1 - 1
panda/src/pgui/pgTop.h

@@ -72,7 +72,7 @@ public:
 
 
 private:
 private:
   PT(MouseWatcher) _watcher;
   PT(MouseWatcher) _watcher;
-  PGMouseWatcherGroup *_watcher_group;
+  PT(PGMouseWatcherGroup) _watcher_group;
   int _start_sort;
   int _start_sort;
   
   
 public:
 public:

+ 3 - 1
panda/src/text/textGlyph.I

@@ -103,7 +103,9 @@ get_geom(Geom::UsageHint usage_hint) const {
   // this behavior to properly count references to this glyph.
   // this behavior to properly count references to this glyph.
   PT(Geom) new_geom = _geom->make_copy();
   PT(Geom) new_geom = _geom->make_copy();
   new_geom->set_usage_hint(usage_hint);
   new_geom->set_usage_hint(usage_hint);
-  if (new_geom->get_vertex_data()->get_usage_hint() != usage_hint) {
+  const GeomVertexData *vdata = new_geom->get_vertex_data();
+  nassertr(vdata != NULL, new_geom);
+  if (vdata->get_usage_hint() != usage_hint) {
     new_geom->modify_vertex_data()->set_usage_hint(usage_hint);
     new_geom->modify_vertex_data()->set_usage_hint(usage_hint);
   }
   }
   return new_geom;
   return new_geom;

+ 179 - 74
panda/src/tform/mouseWatcher.cxx

@@ -30,6 +30,7 @@
 #include "displayRegion.h"
 #include "displayRegion.h"
 #include "dcast.h"
 #include "dcast.h"
 #include "indent.h"
 #include "indent.h"
+#include "mutexHolder.h"
 
 
 #include <algorithm>
 #include <algorithm>
 
 
@@ -96,6 +97,8 @@ MouseWatcher::
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool MouseWatcher::
 bool MouseWatcher::
 remove_region(MouseWatcherRegion *region) {
 remove_region(MouseWatcherRegion *region) {
+  MutexHolder holder(_lock);
+
   remove_region_from(_current_regions, region);
   remove_region_from(_current_regions, region);
   if (region == _preferred_region) {
   if (region == _preferred_region) {
     _preferred_region = (MouseWatcherRegion *)NULL;
     _preferred_region = (MouseWatcherRegion *)NULL;
@@ -118,7 +121,9 @@ remove_region(MouseWatcherRegion *region) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 MouseWatcherRegion *MouseWatcher::
 MouseWatcherRegion *MouseWatcher::
 get_over_region(const LPoint2f &pos) const {
 get_over_region(const LPoint2f &pos) const {
-  VRegions regions;
+  MutexHolder holder(_lock);
+
+  Regions regions;
   get_over_regions(regions, pos);
   get_over_regions(regions, pos);
   return get_preferred_region(regions);
   return get_preferred_region(regions);
 }
 }
@@ -141,7 +146,7 @@ get_over_region(const LPoint2f &pos) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool MouseWatcher::
 bool MouseWatcher::
 add_group(MouseWatcherGroup *group) {
 add_group(MouseWatcherGroup *group) {
-  // return _groups.insert(group).second;
+  MutexHolder holder(_lock);
 
 
   // See if the group is in the set/vector already
   // See if the group is in the set/vector already
   PT(MouseWatcherGroup) pt = group;
   PT(MouseWatcherGroup) pt = group;
@@ -167,11 +172,18 @@ add_group(MouseWatcherGroup *group) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool MouseWatcher::
 bool MouseWatcher::
 remove_group(MouseWatcherGroup *group) {
 remove_group(MouseWatcherGroup *group) {
-  remove_regions_from(_current_regions, group);
-  if (group->has_region(_preferred_region)) {
+  MutexHolder holder(_lock);
+  MutexHolder holder2(group->_lock);
+
+  Regions only_a, only_b, both;
+  intersect_regions(only_a, only_b, both,
+                    _current_regions, group->_regions);
+  _current_regions.swap(only_a);
+
+  if (has_region_in(both, _preferred_region)) {
     _preferred_region = (MouseWatcherRegion *)NULL;
     _preferred_region = (MouseWatcherRegion *)NULL;
   }
   }
-  if (group->has_region(_preferred_button_down_region)) {
+  if (has_region_in(both, _preferred_button_down_region)) {
     _preferred_button_down_region = (MouseWatcherRegion *)NULL;
     _preferred_button_down_region = (MouseWatcherRegion *)NULL;
   }
   }
 
 
@@ -189,6 +201,79 @@ remove_group(MouseWatcherGroup *group) {
   return false;
   return false;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcher::replace_group
+//       Access: Published
+//  Description: Atomically removes old_group fom the MouseWatcher,
+//               and replaces it with new_group.  Presumably old_group
+//               and new_group might have some regions in common;
+//               these are handled properly.
+//
+//               If old_group is not already present, simply adds
+//               new_group and returns false.  Otherwise, removes
+//               old_group and adds new_group, and then returns true.
+////////////////////////////////////////////////////////////////////
+bool MouseWatcher::
+replace_group(MouseWatcherGroup *old_group, MouseWatcherGroup *new_group) {
+  if (old_group == new_group) {
+    // Trivial.
+    return true;
+  }
+
+  MutexHolder holder(_lock);
+  MutexHolder holder2(old_group->_lock);
+  MutexHolder holder3(new_group->_lock);
+
+  // Figure out the list of regions that change
+  Regions remove, add, keep;
+  intersect_regions(remove, add, keep,
+                    old_group->_regions, new_group->_regions);
+
+  // Remove the old regions
+  if (!remove.empty()) {
+    Regions only_a, only_b, both;
+    intersect_regions(only_a, only_b, both,
+                      _current_regions, remove);
+    _current_regions.swap(only_a);
+
+    if (has_region_in(both, _preferred_region)) {
+      _preferred_region = (MouseWatcherRegion *)NULL;
+    }
+    if (has_region_in(both, _preferred_button_down_region)) {
+      _preferred_button_down_region = (MouseWatcherRegion *)NULL;
+    }
+  }
+
+  // And add the new regions
+  if (!add.empty()) {
+    Regions new_list;
+    intersect_regions(new_list, new_list, new_list,
+                      _current_regions, add);
+    _current_regions.swap(new_list);
+  }
+
+  // Add the new group, if it's not already there.
+  PT(MouseWatcherGroup) pt = new_group;
+  Groups::iterator gi = 
+    find(_groups.begin(), _groups.end(), pt);
+  if (gi == _groups.end()) {
+    _groups.push_back(new_group);
+  }
+    
+  // Remove the old group, if it is already there.
+  pt = old_group;
+  gi = find(_groups.begin(), _groups.end(), pt);
+  if (gi != _groups.end()) {
+    // Found it, now erase it
+    _groups.erase(gi);
+    return true;
+  }
+
+  // Did not find the group to erase
+  return false;
+
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MouseWatcher::get_num_groups
 //     Function: MouseWatcher::get_num_groups
 //       Access: Published
 //       Access: Published
@@ -197,6 +282,7 @@ remove_group(MouseWatcherGroup *group) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int MouseWatcher::
 int MouseWatcher::
 get_num_groups() const {
 get_num_groups() const {
+  MutexHolder holder(_lock);
   return _groups.size();
   return _groups.size();
 }
 }
 
 
@@ -208,6 +294,7 @@ get_num_groups() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 MouseWatcherGroup *MouseWatcher::
 MouseWatcherGroup *MouseWatcher::
 get_group(int n) const {
 get_group(int n) const {
+  MutexHolder holder(_lock);
   nassertr(n >= 0 && n < (int)_groups.size(), NULL);
   nassertr(n >= 0 && n < (int)_groups.size(), NULL);
   return _groups[n];
   return _groups[n];
 }
 }
@@ -220,6 +307,7 @@ get_group(int n) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
 output(ostream &out) const {
 output(ostream &out) const {
+  MutexHolder holder(_lock);
   DataNode::output(out);
   DataNode::output(out);
 
 
   int count = _regions.size();
   int count = _regions.size();
@@ -243,6 +331,7 @@ write(ostream &out, int indent_level) const {
     << "MouseWatcher " << get_name() << ":\n";
     << "MouseWatcher " << get_name() << ":\n";
   MouseWatcherGroup::write(out, indent_level + 2);
   MouseWatcherGroup::write(out, indent_level + 2);
 
 
+  MutexHolder holder(_lock);
   if (!_groups.empty()) {
   if (!_groups.empty()) {
     Groups::const_iterator gi;
     Groups::const_iterator gi;
     for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
     for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
@@ -259,10 +348,12 @@ write(ostream &out, int indent_level) const {
 //       Access: Protected
 //       Access: Protected
 //  Description: Fills up the "regions" list with the set of regions
 //  Description: Fills up the "regions" list with the set of regions
 //               that the indicated point is over, sorted in order by
 //               that the indicated point is over, sorted in order by
-//               pointer.
+//               pointer.  Assumes the lock is held.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
-get_over_regions(MouseWatcher::VRegions &regions, const LPoint2f &pos) const {
+get_over_regions(MouseWatcher::Regions &regions, const LPoint2f &pos) const {
+  nassertv(_lock.debug_is_locked());
+
   // Ensure the vector is empty before we begin.
   // Ensure the vector is empty before we begin.
   regions.clear();
   regions.clear();
 
 
@@ -308,15 +399,16 @@ get_over_regions(MouseWatcher::VRegions &regions, const LPoint2f &pos) const {
 //  Description: Returns the innermost region of all the regions
 //  Description: Returns the innermost region of all the regions
 //               indicated in the given vector (usually, the regions
 //               indicated in the given vector (usually, the regions
 //               the mouse is over).  This is the "preferred" region
 //               the mouse is over).  This is the "preferred" region
-//               that gets some special treatment.
+//               that gets some special treatment.  Assumes the lock
+//               is already held.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 MouseWatcherRegion *MouseWatcher::
 MouseWatcherRegion *MouseWatcher::
-get_preferred_region(const MouseWatcher::VRegions &regions) {
+get_preferred_region(const MouseWatcher::Regions &regions) {
   if (regions.empty()) {
   if (regions.empty()) {
     return (MouseWatcherRegion *)NULL;
     return (MouseWatcherRegion *)NULL;
   }
   }
 
 
-  VRegions::const_iterator ri;
+  Regions::const_iterator ri;
   ri = regions.begin();
   ri = regions.begin();
   MouseWatcherRegion *preferred = *ri;
   MouseWatcherRegion *preferred = *ri;
   ++ri;
   ++ri;
@@ -339,18 +431,21 @@ get_preferred_region(const MouseWatcher::VRegions &regions) {
 //               mouse to be over--to the indicated list, and throws
 //               mouse to be over--to the indicated list, and throws
 //               whatever events are appropriate because of that.
 //               whatever events are appropriate because of that.
 //
 //
-//               The list passed in is destroyed.
+//               The list passed in is destroyed.  Assumes the lock is
+//               already held.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
-set_current_regions(MouseWatcher::VRegions &regions) {
+set_current_regions(MouseWatcher::Regions &regions) {
+  nassertv(_lock.debug_is_locked());
+
   // Set up a parameter for passing through any change events.
   // Set up a parameter for passing through any change events.
   MouseWatcherParameter param;
   MouseWatcherParameter param;
   param.set_modifier_buttons(_mods);
   param.set_modifier_buttons(_mods);
   param.set_mouse(_mouse);
   param.set_mouse(_mouse);
 
 
   // Now do a standard sorted comparison between the two vectors.
   // Now do a standard sorted comparison between the two vectors.
-  VRegions::const_iterator new_ri = regions.begin();
-  VRegions::const_iterator old_ri = _current_regions.begin();
+  Regions::const_iterator new_ri = regions.begin();
+  Regions::const_iterator old_ri = _current_regions.begin();
 
 
   // Queue up all the new regions so we can send the within patterns
   // Queue up all the new regions so we can send the within patterns
   // all at once, after all of the without patterns have been thrown.
   // all at once, after all of the without patterns have been thrown.
@@ -436,17 +531,20 @@ set_current_regions(MouseWatcher::VRegions &regions) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MouseWatcher::clear_current_regions
 //     Function: MouseWatcher::clear_current_regions
 //       Access: Protected
 //       Access: Protected
-//  Description: Empties the set of current regions.
+//  Description: Empties the set of current regions.  Assumes the lock
+//               is already held.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
 clear_current_regions() {
 clear_current_regions() {
+  nassertv(_lock.debug_is_locked());
+
   if (!_current_regions.empty()) {
   if (!_current_regions.empty()) {
     // Set up a parameter for passing through any change events.
     // Set up a parameter for passing through any change events.
     MouseWatcherParameter param;
     MouseWatcherParameter param;
     param.set_modifier_buttons(_mods);
     param.set_modifier_buttons(_mods);
     param.set_mouse(_mouse);
     param.set_mouse(_mouse);
     
     
-    VRegions::const_iterator old_ri = _current_regions.begin();
+    Regions::const_iterator old_ri = _current_regions.begin();
     
     
     while (old_ri != _current_regions.end()) {
     while (old_ri != _current_regions.end()) {
       // Here's a region we don't have any more.
       // Here's a region we don't have any more.
@@ -469,43 +567,45 @@ clear_current_regions() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MouseWatcher::intersect_regions
 //     Function: MouseWatcher::intersect_regions
 //       Access: Protected, Static
 //       Access: Protected, Static
-//  Description: Sets result to be the intersection of the list of
-//               regions in regions_a and regions_b.  It is assumed
-//               that both vectors are already sorted in pointer
-//               order.
+//  Description: Computes the list of regions that are in both
+//               regions_a and regions_b, as well as the list of
+//               regions only in regions_a, and the list of regions
+//               only in regions_b.  Any or all of the three output
+//               lists may be the same object, but they must be
+//               different objects from both of the input lists.
+//
+//               It is assumed that both vectors are already sorted in
+//               pointer order.  It is also assumed that any relevant
+//               locks are already held.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
-intersect_regions(MouseWatcher::VRegions &result,
-                  const MouseWatcher::VRegions &regions_a,
-                  const MouseWatcher::VRegions &regions_b) {
-  // Get a temporary vector for storing the result in.  We don't use
-  // result directly, because it might be the same vector as one of a
-  // or b.
-  VRegions temp;
-
+intersect_regions(MouseWatcher::Regions &only_a,
+                  MouseWatcher::Regions &only_b,
+                  MouseWatcher::Regions &both,
+                  const MouseWatcher::Regions &regions_a,
+                  const MouseWatcher::Regions &regions_b) {
   // Now do a standard sorted intersection between the two vectors.
   // Now do a standard sorted intersection between the two vectors.
-  VRegions::const_iterator a_ri = regions_a.begin();
-  VRegions::const_iterator b_ri = regions_b.begin();
+  Regions::const_iterator a_ri = regions_a.begin();
+  Regions::const_iterator b_ri = regions_b.begin();
 
 
   while (a_ri != regions_a.end() && b_ri != regions_b.end()) {
   while (a_ri != regions_a.end() && b_ri != regions_b.end()) {
     if ((*a_ri) < (*b_ri)) {
     if ((*a_ri) < (*b_ri)) {
       // Here's a region in a, not in b.
       // Here's a region in a, not in b.
+      only_a.push_back(*a_ri);
       ++a_ri;
       ++a_ri;
 
 
     } else if ((*b_ri) < (*a_ri)) {
     } else if ((*b_ri) < (*a_ri)) {
       // Here's a region in b, not in a.
       // Here's a region in b, not in a.
+      only_b.push_back(*b_ri);
       ++b_ri;
       ++b_ri;
 
 
     } else {
     } else {
       // Here's a region in both vectors.
       // Here's a region in both vectors.
-      temp.push_back(*a_ri);
+      both.push_back(*a_ri);
       ++a_ri;
       ++a_ri;
       ++b_ri;
       ++b_ri;
     }
     }
   }
   }
-
-  // Now store the result!
-  result.swap(temp);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -513,58 +613,39 @@ intersect_regions(MouseWatcher::VRegions &result,
 //       Access: Protected, Static
 //       Access: Protected, Static
 //  Description: Removes the indicated region from the given vector.
 //  Description: Removes the indicated region from the given vector.
 //               Assumes the vector is sorted in pointer order.
 //               Assumes the vector is sorted in pointer order.
+//               Returns true if removed, false if it wasn't there.
+//               Assumes any relevent locks are already held.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void MouseWatcher::
-remove_region_from(MouseWatcher::VRegions &regions,
+bool MouseWatcher::
+remove_region_from(MouseWatcher::Regions &regions,
                    MouseWatcherRegion *region) {
                    MouseWatcherRegion *region) {
   PT(MouseWatcherRegion) ptr = region;
   PT(MouseWatcherRegion) ptr = region;
-  VRegions::iterator ri = lower_bound(regions.begin(), regions.end(), ptr);
+  Regions::iterator ri = lower_bound(regions.begin(), regions.end(), ptr);
   if (ri != regions.end() && (*ri) == ptr) {
   if (ri != regions.end() && (*ri) == ptr) {
     // The region is in the vector.  Remove it.
     // The region is in the vector.  Remove it.
     regions.erase(ri);
     regions.erase(ri);
+    return true;
   }
   }
+
+  return false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcher::remove_regions_from
+//     Function: MouseWatcher::has_region_in
 //       Access: Protected, Static
 //       Access: Protected, Static
-//  Description: Removes all the regions in the indicated group from
-//               the given vector.  Assumes the vector is sorted in
-//               pointer order.
+//  Description: Returns true if the indicated region is a member of
+//               the given sorted list, false otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void MouseWatcher::
-remove_regions_from(MouseWatcher::VRegions &regions,
-                    MouseWatcherGroup *group) {
-  // Since the group stores a set of regions, which are also sorted in
-  // pointer order, we can just do an intersection operation here.
-  VRegions temp;
-
-  VRegions::const_iterator a_ri = regions.begin();
-  MouseWatcherGroup::Regions::const_iterator b_ri = group->_regions.begin();
-
-  while (a_ri != regions.end() && b_ri != group->_regions.end()) {
-    if ((*a_ri) < (*b_ri)) {
-      // Here's a region in the group, not in regions.
-      ++a_ri;
-
-    } else if ((*b_ri) < (*a_ri)) {
-      // Here's a region in regions, not in the group.
-      temp.push_back(*b_ri);
-      ++b_ri;
-
-    } else {
-      // Here's a region in the group and in regions.
-      ++a_ri;
-      ++b_ri;
-    }
-  }
-
-  // Now store the result!
-  regions.swap(temp);
+bool MouseWatcher::
+has_region_in(const MouseWatcher::Regions &regions,
+              MouseWatcherRegion *region) {
+  PT(MouseWatcherRegion) ptr = region;
+  Regions::const_iterator ri = lower_bound(regions.begin(), regions.end(), ptr);
+  return (ri != regions.end() && (*ri) == ptr);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: MouseWatcher::throw_event_for
+//     Function: MouseWatcher::throw_event_pattern
 //       Access: Protected
 //       Access: Protected
 //  Description: Throws an event associated with the indicated region,
 //  Description: Throws an event associated with the indicated region,
 //               using the given pattern.
 //               using the given pattern.
@@ -629,6 +710,8 @@ throw_event_pattern(const string &pattern, const MouseWatcherRegion *region,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
 move() {
 move() {
+  nassertv(_lock.debug_is_locked());
+
   MouseWatcherParameter param;
   MouseWatcherParameter param;
   param.set_modifier_buttons(_mods);
   param.set_modifier_buttons(_mods);
   param.set_mouse(_mouse);
   param.set_mouse(_mouse);
@@ -646,6 +729,8 @@ move() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
 press(ButtonHandle button) {
 press(ButtonHandle button) {
+  nassertv(_lock.debug_is_locked());
+
   MouseWatcherParameter param;
   MouseWatcherParameter param;
   param.set_button(button);
   param.set_button(button);
   param.set_modifier_buttons(_mods);
   param.set_modifier_buttons(_mods);
@@ -695,6 +780,8 @@ press(ButtonHandle button) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
 release(ButtonHandle button) {
 release(ButtonHandle button) {
+  nassertv(_lock.debug_is_locked());
+
   MouseWatcherParameter param;
   MouseWatcherParameter param;
   param.set_button(button);
   param.set_button(button);
   param.set_modifier_buttons(_mods);
   param.set_modifier_buttons(_mods);
@@ -738,6 +825,8 @@ release(ButtonHandle button) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
 keystroke(int keycode) {
 keystroke(int keycode) {
+  nassertv(_lock.debug_is_locked());
+
   MouseWatcherParameter param;
   MouseWatcherParameter param;
   param.set_keycode(keycode);
   param.set_keycode(keycode);
   param.set_modifier_buttons(_mods);
   param.set_modifier_buttons(_mods);
@@ -785,6 +874,8 @@ keystroke(int keycode) {
 void MouseWatcher::
 void MouseWatcher::
 candidate(const wstring &candidate_string, size_t highlight_start, 
 candidate(const wstring &candidate_string, size_t highlight_start, 
           size_t highlight_end, size_t cursor_pos) {
           size_t highlight_end, size_t cursor_pos) {
+  nassertv(_lock.debug_is_locked());
+
   MouseWatcherParameter param;
   MouseWatcherParameter param;
   param.set_candidate(candidate_string, highlight_start, highlight_end, cursor_pos);
   param.set_candidate(candidate_string, highlight_start, highlight_end, cursor_pos);
   param.set_modifier_buttons(_mods);
   param.set_modifier_buttons(_mods);
@@ -827,6 +918,8 @@ candidate(const wstring &candidate_string, size_t highlight_start,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
 global_keyboard_press(const MouseWatcherParameter &param) {
 global_keyboard_press(const MouseWatcherParameter &param) {
+  nassertv(_lock.debug_is_locked());
+
   Regions::const_iterator ri;
   Regions::const_iterator ri;
   for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
   for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
     MouseWatcherRegion *region = (*ri);
     MouseWatcherRegion *region = (*ri);
@@ -860,6 +953,8 @@ global_keyboard_press(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
 global_keyboard_release(const MouseWatcherParameter &param) {
 global_keyboard_release(const MouseWatcherParameter &param) {
+  nassertv(_lock.debug_is_locked());
+
   Regions::const_iterator ri;
   Regions::const_iterator ri;
   for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
   for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
     MouseWatcherRegion *region = (*ri);
     MouseWatcherRegion *region = (*ri);
@@ -886,11 +981,13 @@ global_keyboard_release(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MouseWatcher::enter_region
 //     Function: MouseWatcher::enter_region
 //       Access: Protected
 //       Access: Protected
-//  Description: Called internally to indicate the mouse pointer is no
-//               longer favoring the indicated region.
+//  Description: Called internally to indicate the mouse pointer is 
+//               favoring the indicated region.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
 enter_region(MouseWatcherRegion *region, const MouseWatcherParameter &param) {
 enter_region(MouseWatcherRegion *region, const MouseWatcherParameter &param) {
+  nassertv(_lock.debug_is_locked());
+
   region->enter(param);
   region->enter(param);
   throw_event_pattern(_enter_pattern, region, ButtonHandle::none());
   throw_event_pattern(_enter_pattern, region, ButtonHandle::none());
   if (_implicit_click) {
   if (_implicit_click) {
@@ -908,6 +1005,8 @@ enter_region(MouseWatcherRegion *region, const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
 exit_region(MouseWatcherRegion *region, const MouseWatcherParameter &param) {
 exit_region(MouseWatcherRegion *region, const MouseWatcherParameter &param) {
+  nassertv(_lock.debug_is_locked());
+
   if (_implicit_click) {
   if (_implicit_click) {
     MouseWatcherParameter param1(param);
     MouseWatcherParameter param1(param);
     param1.set_button(MouseButton::one());
     param1.set_button(MouseButton::one());
@@ -925,6 +1024,8 @@ exit_region(MouseWatcherRegion *region, const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
 set_no_mouse() {
 set_no_mouse() {
+  nassertv(_lock.debug_is_locked());
+
   if (_has_mouse) {
   if (_has_mouse) {
     // Hide the mouse pointer.
     // Hide the mouse pointer.
     if (!_geometry.is_null()) {
     if (!_geometry.is_null()) {
@@ -945,6 +1046,8 @@ set_no_mouse() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
 set_mouse(const LVecBase2f &xy, const LVecBase2f &pixel_xy) {
 set_mouse(const LVecBase2f &xy, const LVecBase2f &pixel_xy) {
+  nassertv(_lock.debug_is_locked());
+
   if (!_geometry.is_null()) {
   if (!_geometry.is_null()) {
     // Transform the mouse pointer.
     // Transform the mouse pointer.
     _geometry->set_transform(TransformState::make_pos(LVecBase3f(xy[0], 0, xy[1])));
     _geometry->set_transform(TransformState::make_pos(LVecBase3f(xy[0], 0, xy[1])));
@@ -958,7 +1061,7 @@ set_mouse(const LVecBase2f &xy, const LVecBase2f &pixel_xy) {
   _mouse = xy;
   _mouse = xy;
   _mouse_pixel = pixel_xy;
   _mouse_pixel = pixel_xy;
     
     
-  VRegions regions;
+  Regions regions;
   get_over_regions(regions, _mouse);
   get_over_regions(regions, _mouse);
   set_current_regions(regions);
   set_current_regions(regions);
 }
 }
@@ -997,6 +1100,8 @@ consider_keyboard_suppress(const MouseWatcherRegion *region) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
 void MouseWatcher::
 do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
 do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
+  MutexHolder holder(_lock);
+
   // 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;

+ 14 - 12
panda/src/tform/mouseWatcher.h

@@ -111,6 +111,7 @@ PUBLISHED:
 
 
   bool add_group(MouseWatcherGroup *group);
   bool add_group(MouseWatcherGroup *group);
   bool remove_group(MouseWatcherGroup *group);
   bool remove_group(MouseWatcherGroup *group);
+  bool replace_group(MouseWatcherGroup *old_group, MouseWatcherGroup *new_group);
   int get_num_groups() const;
   int get_num_groups() const;
   MouseWatcherGroup *get_group(int n) const;
   MouseWatcherGroup *get_group(int n) const;
 
 
@@ -119,20 +120,21 @@ public:
   virtual void write(ostream &out, int indent_level = 0) const;
   virtual void write(ostream &out, int indent_level = 0) const;
 
 
 protected:
 protected:
-  typedef pvector< PT(MouseWatcherRegion) > VRegions;
-  void get_over_regions(VRegions &regions, const LPoint2f &pos) const;
-  static MouseWatcherRegion *get_preferred_region(const VRegions &regions);
+  void get_over_regions(Regions &regions, const LPoint2f &pos) const;
+  static MouseWatcherRegion *get_preferred_region(const Regions &regions);
 
 
-  void set_current_regions(VRegions &regions);
+  void set_current_regions(Regions &regions);
   void clear_current_regions();
   void clear_current_regions();
-  static void intersect_regions(MouseWatcher::VRegions &result,
-                                const MouseWatcher::VRegions &regions_a,
-                                const MouseWatcher::VRegions &regions_b);
-  static void remove_region_from(MouseWatcher::VRegions &regions,
-                                 MouseWatcherRegion *region);
-  static void remove_regions_from(MouseWatcher::VRegions &regions,
-                                  MouseWatcherGroup *group);
 
 
+  static void intersect_regions(Regions &only_a,
+                                Regions &only_b,
+                                Regions &both,
+                                const Regions &regions_a,
+                                const Regions &regions_b);
+  static bool remove_region_from(Regions &regions,
+                                 MouseWatcherRegion *region);
+  static bool has_region_in(const Regions &regions,
+                            MouseWatcherRegion *region);
     
     
   void throw_event_pattern(const string &pattern,
   void throw_event_pattern(const string &pattern,
                            const MouseWatcherRegion *region,
                            const MouseWatcherRegion *region,
@@ -171,7 +173,7 @@ protected:
   LPoint2f _mouse;
   LPoint2f _mouse;
   LPoint2f _mouse_pixel;
   LPoint2f _mouse_pixel;
 
 
-  VRegions _current_regions;
+  Regions _current_regions;
   PT(MouseWatcherRegion) _preferred_region;
   PT(MouseWatcherRegion) _preferred_region;
   PT(MouseWatcherRegion) _preferred_button_down_region;
   PT(MouseWatcherRegion) _preferred_button_down_region;
   bool _button_down;
   bool _button_down;

+ 36 - 5
panda/src/tform/mouseWatcherGroup.cxx

@@ -19,6 +19,7 @@
 #include "mouseWatcherGroup.h"
 #include "mouseWatcherGroup.h"
 #include "lineSegs.h"
 #include "lineSegs.h"
 #include "indent.h"
 #include "indent.h"
+#include "mutexHolder.h"
 
 
 TypeHandle MouseWatcherGroup::_type_handle;
 TypeHandle MouseWatcherGroup::_type_handle;
 
 
@@ -55,6 +56,8 @@ void MouseWatcherGroup::
 add_region(MouseWatcherRegion *region) {
 add_region(MouseWatcherRegion *region) {
   PT(MouseWatcherRegion) pt = region;
   PT(MouseWatcherRegion) pt = region;
 
 
+  MutexHolder holder(_lock);
+
   // We will only bother to check for duplicates in the region list if
   // We will only bother to check for duplicates in the region list if
   // we are building a development Panda.  The overhead for doing this
   // we are building a development Panda.  The overhead for doing this
   // may be too high if we have many regions.
   // may be too high if we have many regions.
@@ -82,6 +85,8 @@ add_region(MouseWatcherRegion *region) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool MouseWatcherGroup::
 bool MouseWatcherGroup::
 has_region(MouseWatcherRegion *region) const {
 has_region(MouseWatcherRegion *region) const {
+  MutexHolder holder(_lock);
+
   // See if the region is in the vector.
   // See if the region is in the vector.
   PT(MouseWatcherRegion) pt = region;
   PT(MouseWatcherRegion) pt = region;
   Regions::const_iterator ri = 
   Regions::const_iterator ri = 
@@ -103,6 +108,8 @@ has_region(MouseWatcherRegion *region) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool MouseWatcherGroup::
 bool MouseWatcherGroup::
 remove_region(MouseWatcherRegion *region) {
 remove_region(MouseWatcherRegion *region) {
+  MutexHolder holder(_lock);
+
   // See if the region is in the vector.
   // See if the region is in the vector.
   PT(MouseWatcherRegion) pt = region;
   PT(MouseWatcherRegion) pt = region;
   Regions::iterator ri = 
   Regions::iterator ri = 
@@ -137,6 +144,8 @@ remove_region(MouseWatcherRegion *region) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 MouseWatcherRegion *MouseWatcherGroup::
 MouseWatcherRegion *MouseWatcherGroup::
 find_region(const string &name) const {
 find_region(const string &name) const {
+  MutexHolder holder(_lock);
+
   Regions::const_iterator ri;
   Regions::const_iterator ri;
   for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
   for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
     MouseWatcherRegion *region = (*ri);
     MouseWatcherRegion *region = (*ri);
@@ -155,6 +164,8 @@ find_region(const string &name) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcherGroup::
 void MouseWatcherGroup::
 clear_regions() {
 clear_regions() {
+  MutexHolder holder(_lock);
+
   _regions.clear();
   _regions.clear();
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
@@ -172,18 +183,26 @@ clear_regions() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int MouseWatcherGroup::
 int MouseWatcherGroup::
 get_num_regions() const {
 get_num_regions() const {
+  MutexHolder holder(_lock);
+
   return _regions.size();
   return _regions.size();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MouseWatcherGroup::get_region
 //     Function: MouseWatcherGroup::get_region
 //       Access: Published
 //       Access: Published
-//  Description: Returns the nth regions in the group.
+//  Description: Returns the nth region of the group; returns NULL if
+//               there is no nth region.  Note that this is not
+//               thread-safe; another thread might have removed the
+//               nth region before you called this method.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 MouseWatcherRegion *MouseWatcherGroup::
 MouseWatcherRegion *MouseWatcherGroup::
 get_region(int n) const {
 get_region(int n) const {
-  nassertr(n >= 0 && n < (int)_regions.size(), NULL);
-  return _regions[n];
+  MutexHolder holder(_lock);
+  if (n >= 0 && n < (int)_regions.size()) {
+    return _regions[n];
+  }
+  return NULL;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -203,6 +222,8 @@ output(ostream &out) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcherGroup::
 void MouseWatcherGroup::
 write(ostream &out, int indent_level) const {
 write(ostream &out, int indent_level) const {
+  MutexHolder holder(_lock);
+
   Regions::const_iterator ri;
   Regions::const_iterator ri;
   for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
   for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
     MouseWatcherRegion *region = (*ri);
     MouseWatcherRegion *region = (*ri);
@@ -221,6 +242,8 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcherGroup::
 void MouseWatcherGroup::
 show_regions(const NodePath &render2d) {
 show_regions(const NodePath &render2d) {
+  MutexHolder holder(_lock);
+
   _show_regions = true;
   _show_regions = true;
   _show_regions_root = render2d.attach_new_node("show_regions");
   _show_regions_root = render2d.attach_new_node("show_regions");
   _show_regions_root.set_bin("unsorted", 0);
   _show_regions_root.set_bin("unsorted", 0);
@@ -238,6 +261,8 @@ show_regions(const NodePath &render2d) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcherGroup::
 void MouseWatcherGroup::
 set_color(const Colorf &color) {
 set_color(const Colorf &color) {
+  MutexHolder holder(_lock);
+
   _color = color;
   _color = color;
   update_regions();
   update_regions();
 }
 }
@@ -252,6 +277,8 @@ set_color(const Colorf &color) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcherGroup::
 void MouseWatcherGroup::
 hide_regions() {
 hide_regions() {
+  MutexHolder holder(_lock);
+
   _show_regions_root.remove_node();
   _show_regions_root.remove_node();
   _show_regions = false;
   _show_regions = false;
   _vizzes.clear();
   _vizzes.clear();
@@ -263,10 +290,12 @@ hide_regions() {
 //     Function: MouseWatcherGroup::update_regions
 //     Function: MouseWatcherGroup::update_regions
 //       Access: Private
 //       Access: Private
 //  Description: Internally regenerates the show_regions()
 //  Description: Internally regenerates the show_regions()
-//               visualization.
+//               visualization.  Assumes the lock is already held.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MouseWatcherGroup::
 void MouseWatcherGroup::
 update_regions() {
 update_regions() {
+  nassertv(_lock.debug_is_locked());
+
   _show_regions_root.node()->remove_all_children();
   _show_regions_root.node()->remove_all_children();
   _vizzes.clear();
   _vizzes.clear();
   _vizzes.reserve(_regions.size());
   _vizzes.reserve(_regions.size());
@@ -284,10 +313,12 @@ update_regions() {
 //       Access: Private
 //       Access: Private
 //  Description: Creates a node to represent the indicated region, and
 //  Description: Creates a node to represent the indicated region, and
 //               attaches it to the _show_regions_root.  Does not add
 //               attaches it to the _show_regions_root.  Does not add
-//               it to _vizzes.
+//               it to _vizzes.  Assumes the lock is already held.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PandaNode *MouseWatcherGroup::
 PandaNode *MouseWatcherGroup::
 make_viz_region(MouseWatcherRegion *region) {
 make_viz_region(MouseWatcherRegion *region) {
+  nassertr(_lock.debug_is_locked(), NULL);
+
   LineSegs ls("show_regions");
   LineSegs ls("show_regions");
   ls.set_color(_color);
   ls.set_color(_color);
 
 

+ 6 - 0
panda/src/tform/mouseWatcherGroup.h

@@ -26,6 +26,7 @@
 #include "referenceCount.h"
 #include "referenceCount.h"
 #include "pvector.h"
 #include "pvector.h"
 #include "nodePath.h"
 #include "nodePath.h"
+#include "pmutex.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : MouseWatcherGroup
 //       Class : MouseWatcherGroup
@@ -60,6 +61,11 @@ protected:
   typedef pvector< PT(MouseWatcherRegion) > Regions;
   typedef pvector< PT(MouseWatcherRegion) > Regions;
   Regions _regions;
   Regions _regions;
 
 
+  // This mutex protects the above list of regions, as well as the
+  // below list of vizzes.  It is also referenced directly by
+  // MouseWatcher, a derived class.
+  Mutex _lock;
+
 private:
 private:
 #ifndef NDEBUG
 #ifndef NDEBUG
   void update_regions();
   void update_regions();