Panda3D
|
00001 // Filename: mouseWatcher.cxx 00002 // Created by: drose (12Mar02) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "mouseWatcher.h" 00016 #include "config_tform.h" 00017 #include "dataGraphTraverser.h" 00018 #include "mouseWatcherParameter.h" 00019 #include "mouseAndKeyboard.h" 00020 #include "mouseData.h" 00021 #include "buttonEventList.h" 00022 #include "mouseButton.h" 00023 #include "throw_event.h" 00024 #include "eventParameter.h" 00025 #include "dataNodeTransmit.h" 00026 #include "transformState.h" 00027 #include "displayRegion.h" 00028 #include "stereoDisplayRegion.h" 00029 #include "geomVertexWriter.h" 00030 #include "geomLinestrips.h" 00031 #include "geomPoints.h" 00032 #include "dcast.h" 00033 #include "indent.h" 00034 #include "lightMutexHolder.h" 00035 #include "nearly_zero.h" 00036 00037 #include <algorithm> 00038 00039 TypeHandle MouseWatcher::_type_handle; 00040 00041 //////////////////////////////////////////////////////////////////// 00042 // Function: MouseWatcher::Constructor 00043 // Access: Published 00044 // Description: 00045 //////////////////////////////////////////////////////////////////// 00046 MouseWatcher:: 00047 MouseWatcher(const string &name) : 00048 DataNode(name) 00049 { 00050 _pixel_xy_input = define_input("pixel_xy", EventStoreVec2::get_class_type()); 00051 _pixel_size_input = define_input("pixel_size", EventStoreVec2::get_class_type()); 00052 _xy_input = define_input("xy", EventStoreVec2::get_class_type()); 00053 _button_events_input = define_input("button_events", ButtonEventList::get_class_type()); 00054 _pointer_events_input = define_input("pointer_events", PointerEventList::get_class_type()); 00055 00056 _pixel_xy_output = define_output("pixel_xy", EventStoreVec2::get_class_type()); 00057 _pixel_size_output = define_output("pixel_size", EventStoreVec2::get_class_type()); 00058 _xy_output = define_output("xy", EventStoreVec2::get_class_type()); 00059 _button_events_output = define_output("button_events", ButtonEventList::get_class_type()); 00060 00061 _pixel_xy = new EventStoreVec2(LPoint2(0.0f, 0.0f)); 00062 _xy = new EventStoreVec2(LPoint2(0.0f, 0.0f)); 00063 _pixel_size = new EventStoreVec2(LPoint2(0.0f, 0.0f)); 00064 _button_events = new ButtonEventList; 00065 00066 _has_mouse = false; 00067 _internal_suppress = 0; 00068 _preferred_region = (MouseWatcherRegion *)NULL; 00069 _preferred_button_down_region = (MouseWatcherRegion *)NULL; 00070 _button_down = false; 00071 _eh = (EventHandler *)NULL; 00072 _display_region = (DisplayRegion *)NULL; 00073 00074 _frame.set(-1.0f, 1.0f, -1.0f, 1.0f); 00075 00076 _inactivity_timeout = inactivity_timeout; 00077 _has_inactivity_timeout = !IS_NEARLY_ZERO(_inactivity_timeout); 00078 00079 _num_trail_recent = 0; 00080 _trail_log_duration = 0.0; 00081 _trail_log = new PointerEventList(); 00082 00083 _inactivity_timeout_event = "inactivity_timeout"; 00084 _last_activity = 0.0; 00085 _inactivity_state = IS_active; 00086 00087 // When this flag is true, the mouse pointer is allowed to be 00088 // "entered" into multiple regions simultaneously; when false, it 00089 // will only be "within" multiple regions, but "entered" into the 00090 // topmost of those. 00091 _enter_multiple = false; 00092 00093 // When this flag is true, moving the pointer into a region is 00094 // enough to click it. The click is simulated with mouse button 00095 // one. 00096 _implicit_click = false; 00097 } 00098 00099 //////////////////////////////////////////////////////////////////// 00100 // Function: MouseWatcher::Destructor 00101 // Access: Published 00102 // Description: 00103 //////////////////////////////////////////////////////////////////// 00104 MouseWatcher:: 00105 ~MouseWatcher() { 00106 } 00107 00108 //////////////////////////////////////////////////////////////////// 00109 // Function: MouseWatcher::remove_region 00110 // Access: Published 00111 // Description: Removes the indicated region from the group. 00112 // Returns true if it was successfully removed, or false 00113 // if it wasn't there in the first place. 00114 //////////////////////////////////////////////////////////////////// 00115 bool MouseWatcher:: 00116 remove_region(MouseWatcherRegion *region) { 00117 LightMutexHolder holder(_lock); 00118 00119 remove_region_from(_current_regions, region); 00120 if (region == _preferred_region) { 00121 if (_preferred_region != (MouseWatcherRegion *)NULL) { 00122 exit_region(_preferred_region, MouseWatcherParameter()); 00123 } 00124 _preferred_region = (MouseWatcherRegion *)NULL; 00125 } 00126 if (region == _preferred_button_down_region) { 00127 _preferred_button_down_region = (MouseWatcherRegion *)NULL; 00128 } 00129 00130 return MouseWatcherGroup::do_remove_region(region); 00131 } 00132 00133 //////////////////////////////////////////////////////////////////// 00134 // Function: MouseWatcher::get_over_region 00135 // Access: Published 00136 // Description: Returns the preferred region the mouse is over. In 00137 // the case of overlapping regions, the region with the 00138 // largest sort order is preferred; if two regions have 00139 // the same sort order, then the smaller region is 00140 // preferred. 00141 //////////////////////////////////////////////////////////////////// 00142 MouseWatcherRegion *MouseWatcher:: 00143 get_over_region(const LPoint2 &pos) const { 00144 LightMutexHolder holder(_lock); 00145 00146 Regions regions; 00147 get_over_regions(regions, pos); 00148 return get_preferred_region(regions); 00149 } 00150 00151 //////////////////////////////////////////////////////////////////// 00152 // Function: MouseWatcher::add_group 00153 // Access: Published 00154 // Description: Adds the indicated group of regions to the set of 00155 // regions the MouseWatcher will monitor each frame. 00156 // 00157 // Since the MouseWatcher itself inherits from 00158 // MouseWatcherGroup, this operation is normally not 00159 // necessary--you can simply add the Regions you care 00160 // about one at a time. Adding a complete group is 00161 // useful when you may want to explicitly remove the 00162 // regions as a group later. 00163 // 00164 // Returns true if the group was successfully added, or 00165 // false if it was already on the list. 00166 //////////////////////////////////////////////////////////////////// 00167 bool MouseWatcher:: 00168 add_group(MouseWatcherGroup *group) { 00169 LightMutexHolder holder(_lock); 00170 00171 // See if the group is in the set/vector already 00172 PT(MouseWatcherGroup) pt = group; 00173 Groups::const_iterator gi = 00174 find(_groups.begin(), _groups.end(), pt); 00175 if (gi != _groups.end()) { 00176 // Already in the set, return false 00177 return false; 00178 } 00179 00180 #ifndef NDEBUG 00181 if (!_show_regions_render2d.is_empty()) { 00182 group->show_regions(_show_regions_render2d, _show_regions_bin_name, 00183 _show_regions_draw_order); 00184 } 00185 #endif // NDEBUG 00186 00187 // Not in the set, add it and return true 00188 _groups.push_back(pt); 00189 return true; 00190 } 00191 00192 //////////////////////////////////////////////////////////////////// 00193 // Function: MouseWatcher::remove_group 00194 // Access: Published 00195 // Description: Removes the indicated group from the set of extra 00196 // groups associated with the MouseWatcher. Returns 00197 // true if successful, or false if the group was already 00198 // removed or was never added via add_group(). 00199 //////////////////////////////////////////////////////////////////// 00200 bool MouseWatcher:: 00201 remove_group(MouseWatcherGroup *group) { 00202 LightMutexHolder holder(_lock); 00203 LightMutexHolder holder2(group->_lock); 00204 00205 group->do_sort_regions(); 00206 00207 Regions only_a, only_b, both; 00208 intersect_regions(only_a, only_b, both, 00209 _current_regions, group->_regions); 00210 set_current_regions(only_a); 00211 00212 if (has_region_in(both, _preferred_region)) { 00213 if (_preferred_region != (MouseWatcherRegion *)NULL) { 00214 exit_region(_preferred_region, MouseWatcherParameter()); 00215 } 00216 _preferred_region = (MouseWatcherRegion *)NULL; 00217 } 00218 if (has_region_in(both, _preferred_button_down_region)) { 00219 _preferred_button_down_region = (MouseWatcherRegion *)NULL; 00220 } 00221 00222 #ifndef NDEBUG 00223 if (!_show_regions_render2d.is_empty()) { 00224 group->do_hide_regions(); 00225 } 00226 #endif // NDEBUG 00227 00228 // See if the group is in the set/vector 00229 PT(MouseWatcherGroup) pt = group; 00230 Groups::iterator gi = 00231 find(_groups.begin(), _groups.end(), pt); 00232 if (gi != _groups.end()) { 00233 // Found it, now erase it 00234 _groups.erase(gi); 00235 return true; 00236 } 00237 00238 // Did not find the group to erase 00239 return false; 00240 } 00241 00242 //////////////////////////////////////////////////////////////////// 00243 // Function: MouseWatcher::replace_group 00244 // Access: Published 00245 // Description: Atomically removes old_group from the MouseWatcher, 00246 // and replaces it with new_group. Presumably old_group 00247 // and new_group might have some regions in common; 00248 // these are handled properly. 00249 // 00250 // If old_group is not already present, simply adds 00251 // new_group and returns false. Otherwise, removes 00252 // old_group and adds new_group, and then returns true. 00253 //////////////////////////////////////////////////////////////////// 00254 bool MouseWatcher:: 00255 replace_group(MouseWatcherGroup *old_group, MouseWatcherGroup *new_group) { 00256 if (old_group == new_group) { 00257 // Trivial. 00258 return true; 00259 } 00260 00261 LightMutexHolder holder(_lock); 00262 00263 LightMutexHolder holder2(old_group->_lock); 00264 LightMutexHolder holder3(new_group->_lock); 00265 00266 old_group->do_sort_regions(); 00267 new_group->do_sort_regions(); 00268 00269 #ifndef NDEBUG 00270 if (!_show_regions_render2d.is_empty()) { 00271 old_group->do_hide_regions(); 00272 new_group->do_show_regions(_show_regions_render2d, _show_regions_bin_name, 00273 _show_regions_draw_order); 00274 } 00275 #endif // NDEBUG 00276 00277 // Figure out the list of regions that change 00278 Regions remove, add, keep; 00279 intersect_regions(remove, add, keep, 00280 old_group->_regions, new_group->_regions); 00281 00282 Regions new_current_regions; 00283 bool any_new_current_regions = false; 00284 00285 // Remove the old regions 00286 if (!remove.empty()) { 00287 Regions only_a, only_b, both; 00288 intersect_regions(only_a, only_b, both, 00289 _current_regions, remove); 00290 new_current_regions.swap(only_a); 00291 any_new_current_regions = true; 00292 00293 if (has_region_in(both, _preferred_region)) { 00294 if (_preferred_region != (MouseWatcherRegion *)NULL) { 00295 exit_region(_preferred_region, MouseWatcherParameter()); 00296 } 00297 _preferred_region = (MouseWatcherRegion *)NULL; 00298 } 00299 if (has_region_in(both, _preferred_button_down_region)) { 00300 _preferred_button_down_region = (MouseWatcherRegion *)NULL; 00301 } 00302 } 00303 00304 // Don't add the new regions--we have no reason to believe these 00305 // should become current; some of them may not even be under the 00306 // mouse. 00307 /* 00308 // And add the new regions 00309 if (!add.empty()) { 00310 Regions new_list; 00311 if (any_new_current_regions) { 00312 intersect_regions(new_list, new_list, new_list, 00313 new_current_regions, add); 00314 } else { 00315 intersect_regions(new_list, new_list, new_list, 00316 _current_regions, add); 00317 } 00318 new_current_regions.swap(new_list); 00319 any_new_current_regions = true; 00320 } 00321 */ 00322 00323 if (any_new_current_regions) { 00324 set_current_regions(new_current_regions); 00325 } 00326 00327 // Add the new group, if it's not already there. 00328 PT(MouseWatcherGroup) pt = new_group; 00329 Groups::iterator gi = 00330 find(_groups.begin(), _groups.end(), pt); 00331 if (gi == _groups.end()) { 00332 _groups.push_back(new_group); 00333 } 00334 00335 #ifndef NDEBUG 00336 if (!_show_regions_render2d.is_empty()) { 00337 new_group->do_update_regions(); 00338 } 00339 #endif // NDEBUG 00340 00341 // Remove the old group, if it is already there. 00342 pt = old_group; 00343 gi = find(_groups.begin(), _groups.end(), pt); 00344 if (gi != _groups.end()) { 00345 // Found it, now erase it 00346 _groups.erase(gi); 00347 return true; 00348 } 00349 00350 // Did not find the group to erase 00351 return false; 00352 } 00353 00354 //////////////////////////////////////////////////////////////////// 00355 // Function: MouseWatcher::get_num_groups 00356 // Access: Published 00357 // Description: Returns the number of separate groups added to the 00358 // MouseWatcher via add_group(). 00359 //////////////////////////////////////////////////////////////////// 00360 int MouseWatcher:: 00361 get_num_groups() const { 00362 LightMutexHolder holder(_lock); 00363 return _groups.size(); 00364 } 00365 00366 //////////////////////////////////////////////////////////////////// 00367 // Function: MouseWatcher::get_group 00368 // Access: Published 00369 // Description: Returns the nth group added to the MouseWatcher via 00370 // add_group(). 00371 //////////////////////////////////////////////////////////////////// 00372 MouseWatcherGroup *MouseWatcher:: 00373 get_group(int n) const { 00374 LightMutexHolder holder(_lock); 00375 nassertr(n >= 0 && n < (int)_groups.size(), NULL); 00376 return _groups[n]; 00377 } 00378 00379 //////////////////////////////////////////////////////////////////// 00380 // Function: MouseWatcher::set_trail_log_duration 00381 // Access: Published 00382 // Description: If the duration is nonzero, causes the MouseWatcher 00383 // to log the mouse's trail. Events older than the 00384 // specified duration are discarded. If the duration is 00385 // zero, logging is disabled. 00386 //////////////////////////////////////////////////////////////////// 00387 void MouseWatcher:: 00388 set_trail_log_duration(double duration) { 00389 if (duration < 0.0) { 00390 duration = 0.0; 00391 } 00392 _trail_log_duration = duration; 00393 discard_excess_trail_log(); 00394 } 00395 00396 //////////////////////////////////////////////////////////////////// 00397 // Function: MouseWatcher::discard_excess_trail_log 00398 // Access: Private 00399 // Description: Discards trail log events whose age exceed the 00400 // desired log duration. Keeps one event that is beyond 00401 // the specified age, because otherwise, it is not always 00402 // possible to determine where the mouse was for the 00403 // full logging duration. Also, keeps a minimum of two 00404 // events in the queue. If the duration is zero, this 00405 // method discards all trail events. 00406 //////////////////////////////////////////////////////////////////// 00407 void MouseWatcher:: 00408 discard_excess_trail_log() { 00409 if (_trail_log_duration == 0.0) { 00410 _trail_log->clear(); 00411 } else { 00412 if (_trail_log->get_num_events() > 2) { 00413 double old = ClockObject::get_global_clock()->get_frame_time() - _trail_log_duration; 00414 while ((_trail_log->get_num_events() > 2)&& 00415 (_trail_log->get_time(0) <= old)&& 00416 (_trail_log->get_time(1) <= old)) { 00417 _trail_log->pop_front(); 00418 } 00419 } 00420 } 00421 } 00422 00423 //////////////////////////////////////////////////////////////////// 00424 // Function: MouseWatcher::get_trail_node 00425 // Access: Published 00426 // Description: Returns a GeomNode that represents the mouse trail. 00427 // The intent is that you should reparent this GeomNode 00428 // to Render2D, and then forget about it. The 00429 // MouseWatcher will continually update the trail node. 00430 // There is only one trail node, it does not create a 00431 // new one each time you call get_trail_node. 00432 // 00433 // This is not a particularly beautiful way to render 00434 // a mouse trail. It is intended more for debugging 00435 // purposes than for finished applications. Even so, 00436 // It is suggested that you might want to apply a line 00437 // thickness and antialias mode to the line --- doing 00438 // so makes it look a lot better. 00439 //////////////////////////////////////////////////////////////////// 00440 PT(GeomNode) MouseWatcher:: 00441 get_trail_node() { 00442 if (_trail_node == 0) { 00443 _trail_node = new GeomNode("Mouse Trail Node"); 00444 update_trail_node(); 00445 } 00446 return _trail_node; 00447 } 00448 00449 //////////////////////////////////////////////////////////////////// 00450 // Function: MouseWatcher::clear_trail_node 00451 // Access: Published 00452 // Description: If you have previously fetched the trail node 00453 // using get_trail_node, then the MouseWatcher is 00454 // continually updating the trail node every frame. 00455 // Using clear_trail_node causes the MouseWatcher to 00456 // forget the trail node and stop updating it. 00457 //////////////////////////////////////////////////////////////////// 00458 void MouseWatcher:: 00459 clear_trail_node() { 00460 _trail_node = 0; 00461 } 00462 00463 //////////////////////////////////////////////////////////////////// 00464 // Function: MouseWatcher::update_trail_node 00465 // Access: Private 00466 // Description: Causes the trail node to represent the mouse trail. 00467 //////////////////////////////////////////////////////////////////// 00468 void MouseWatcher:: 00469 update_trail_node() { 00470 if (_trail_node == 0) { 00471 return; 00472 } 00473 _trail_node->remove_all_geoms(); 00474 00475 if (_trail_log->get_num_events() < 2) { 00476 return; 00477 } 00478 00479 PT(GeomVertexData) data = new GeomVertexData 00480 ("mouseTrailSegs", GeomVertexFormat::get_v3(), Geom::UH_static); 00481 00482 GeomVertexWriter vertex(data, InternalName::get_vertex()); 00483 00484 PT(GeomLinestrips) lines = new GeomLinestrips(Geom::UH_static); 00485 00486 double xscale = 2.0 / _pixel_size->get_value().get_x(); 00487 double yscale = 2.0 / _pixel_size->get_value().get_y(); 00488 00489 for (int i=0; i<(int)_trail_log->get_num_events(); i++) { 00490 double x = (_trail_log->get_xpos(i) * xscale) - 1.0; 00491 double y = (_trail_log->get_ypos(i) * yscale) - 1.0; 00492 vertex.add_data3(LVecBase3(x,0.0,-y)); 00493 lines->add_vertex(i); 00494 } 00495 lines->close_primitive(); 00496 00497 PT(Geom) l_geom = new Geom(data); 00498 l_geom->add_primitive(lines); 00499 _trail_node->add_geom(l_geom); 00500 } 00501 00502 //////////////////////////////////////////////////////////////////// 00503 // Function: MouseWatcher::note_activity 00504 // Access: Published 00505 // Description: Can be used in conjunction with the inactivity 00506 // timeout to inform the MouseWatcher that the user has 00507 // just performed some action which proves he/she is 00508 // present. It may be necessary to call this for 00509 // external events, such as joystick action, that the 00510 // MouseWatcher might otherwise not know about. This 00511 // will reset the current inactivity timer. When the 00512 // inactivity timer reaches the length of time specified 00513 // by set_inactivity_timeout(), with no keyboard or 00514 // mouse activity and no calls to note_activity(), then 00515 // any buttons held will be automatically released. 00516 //////////////////////////////////////////////////////////////////// 00517 void MouseWatcher:: 00518 note_activity() { 00519 _last_activity = ClockObject::get_global_clock()->get_frame_time(); 00520 switch (_inactivity_state) { 00521 case IS_active: 00522 break; 00523 00524 case IS_inactive: 00525 _inactivity_state = IS_inactive_to_active; 00526 break; 00527 00528 case IS_active_to_inactive: 00529 _inactivity_state = IS_active; 00530 break; 00531 00532 case IS_inactive_to_active: 00533 break; 00534 } 00535 } 00536 00537 00538 //////////////////////////////////////////////////////////////////// 00539 // Function: MouseWatcher::output 00540 // Access: Public, Virtual 00541 // Description: 00542 //////////////////////////////////////////////////////////////////// 00543 void MouseWatcher:: 00544 output(ostream &out) const { 00545 LightMutexHolder holder(_lock); 00546 DataNode::output(out); 00547 00548 int count = _regions.size(); 00549 Groups::const_iterator gi; 00550 for (gi = _groups.begin(); gi != _groups.end(); ++gi) { 00551 MouseWatcherGroup *group = (*gi); 00552 count += group->_regions.size(); 00553 } 00554 00555 out << " (" << count << " regions)"; 00556 } 00557 00558 //////////////////////////////////////////////////////////////////// 00559 // Function: MouseWatcher::write 00560 // Access: Public, Virtual 00561 // Description: 00562 //////////////////////////////////////////////////////////////////// 00563 void MouseWatcher:: 00564 write(ostream &out, int indent_level) const { 00565 indent(out, indent_level) 00566 << "MouseWatcher " << get_name() << ":\n"; 00567 MouseWatcherGroup::write(out, indent_level + 2); 00568 00569 LightMutexHolder holder(_lock); 00570 if (!_groups.empty()) { 00571 Groups::const_iterator gi; 00572 for (gi = _groups.begin(); gi != _groups.end(); ++gi) { 00573 MouseWatcherGroup *group = (*gi); 00574 indent(out, indent_level + 2) 00575 << "Subgroup:\n"; 00576 group->write(out, indent_level + 4); 00577 } 00578 } 00579 } 00580 00581 //////////////////////////////////////////////////////////////////// 00582 // Function: MouseWatcher::get_over_regions 00583 // Access: Protected 00584 // Description: Fills up the "regions" list with the set of regions 00585 // that the indicated point is over, sorted in order by 00586 // pointer. Assumes the lock is held. 00587 //////////////////////////////////////////////////////////////////// 00588 void MouseWatcher:: 00589 get_over_regions(MouseWatcher::Regions ®ions, const LPoint2 &pos) const { 00590 nassertv(_lock.debug_is_locked()); 00591 00592 // Scale the mouse coordinates into the frame. 00593 PN_stdfloat mx = (pos[0] + 1.0f) * 0.5f * (_frame[1] - _frame[0]) + _frame[0]; 00594 PN_stdfloat my = (pos[1] + 1.0f) * 0.5f * (_frame[3] - _frame[2]) + _frame[2]; 00595 00596 // pos[0] = 2.0f * (mx - _frame[0]) / (_frame[1] - _frame[0]) - 1.0f; 00597 // pos[1] = 2.0f * (my - _frame[2]) / (_frame[3] - _frame[2]) - 1.0f; 00598 00599 // Ensure the vector is empty before we begin. 00600 regions.clear(); 00601 00602 Regions::const_iterator ri; 00603 for (ri = _regions.begin(); ri != _regions.end(); ++ri) { 00604 MouseWatcherRegion *region = (*ri); 00605 const LVecBase4 &frame = region->get_frame(); 00606 00607 if (region->get_active() && 00608 mx >= frame[0] && mx <= frame[1] && 00609 my >= frame[2] && my <= frame[3]) { 00610 00611 regions.push_back(region); 00612 } 00613 } 00614 00615 // Also check all of our sub-groups. 00616 Groups::const_iterator gi; 00617 for (gi = _groups.begin(); gi != _groups.end(); ++gi) { 00618 MouseWatcherGroup *group = (*gi); 00619 for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) { 00620 MouseWatcherRegion *region = (*ri); 00621 const LVecBase4 &frame = region->get_frame(); 00622 00623 if (region->get_active() && 00624 mx >= frame[0] && mx <= frame[1] && 00625 my >= frame[2] && my <= frame[3]) { 00626 00627 regions.push_back(region); 00628 } 00629 } 00630 } 00631 00632 // Now sort the regions by pointer. By convention, the Regions 00633 // vectors are always kept in order by pointer, so we can do easy 00634 // linear comparison and intersection operations. 00635 sort(regions.begin(), regions.end()); 00636 } 00637 00638 //////////////////////////////////////////////////////////////////// 00639 // Function: MouseWatcher::get_preferred_region 00640 // Access: Protected, Static 00641 // Description: Returns the innermost region of all the regions 00642 // indicated in the given vector (usually, the regions 00643 // the mouse is over). This is the "preferred" region 00644 // that gets some special treatment. Assumes the lock 00645 // is already held. 00646 //////////////////////////////////////////////////////////////////// 00647 MouseWatcherRegion *MouseWatcher:: 00648 get_preferred_region(const MouseWatcher::Regions ®ions) { 00649 if (regions.empty()) { 00650 return (MouseWatcherRegion *)NULL; 00651 } 00652 00653 Regions::const_iterator ri; 00654 ri = regions.begin(); 00655 MouseWatcherRegion *preferred = *ri; 00656 ++ri; 00657 while (ri != regions.end()) { 00658 MouseWatcherRegion *region = *ri; 00659 00660 if (*region < *preferred) { 00661 preferred = region; 00662 } 00663 ++ri; 00664 } 00665 00666 return preferred; 00667 } 00668 00669 //////////////////////////////////////////////////////////////////// 00670 // Function: MouseWatcher::set_current_regions 00671 // Access: Protected 00672 // Description: Changes the "current" regions--the one we consider the 00673 // mouse to be over--to the indicated list, and throws 00674 // whatever events are appropriate because of that. 00675 // 00676 // The list passed in is destroyed. Assumes the lock is 00677 // already held. 00678 //////////////////////////////////////////////////////////////////// 00679 void MouseWatcher:: 00680 set_current_regions(MouseWatcher::Regions ®ions) { 00681 nassertv(_lock.debug_is_locked()); 00682 00683 // Set up a parameter for passing through any change events. 00684 MouseWatcherParameter param; 00685 param.set_modifier_buttons(_mods); 00686 param.set_mouse(_mouse); 00687 00688 // Now do a standard sorted comparison between the two vectors. 00689 Regions::const_iterator new_ri = regions.begin(); 00690 Regions::const_iterator old_ri = _current_regions.begin(); 00691 00692 // Queue up all the new regions so we can send the within patterns 00693 // all at once, after all of the without patterns have been thrown. 00694 vector<MouseWatcherRegion *> new_regions; 00695 00696 bool any_changes = false; 00697 while (new_ri != regions.end() && old_ri != _current_regions.end()) { 00698 if ((*new_ri) < (*old_ri)) { 00699 // Here's a new region that we didn't have last frame. 00700 MouseWatcherRegion *new_region = (*new_ri); 00701 new_regions.push_back(new_region); 00702 any_changes = true; 00703 ++new_ri; 00704 00705 } else if ((*old_ri) < (*new_ri)) { 00706 // Here's a region we don't have any more. 00707 MouseWatcherRegion *old_region = (*old_ri); 00708 without_region(old_region, param); 00709 any_changes = true; 00710 ++old_ri; 00711 00712 } else { 00713 // Here's a region that hasn't changed. 00714 ++new_ri; 00715 ++old_ri; 00716 } 00717 } 00718 00719 while (new_ri != regions.end()) { 00720 // Here's a new region that we didn't have last frame. 00721 MouseWatcherRegion *new_region = (*new_ri); 00722 new_regions.push_back(new_region); 00723 any_changes = true; 00724 ++new_ri; 00725 } 00726 00727 while (old_ri != _current_regions.end()) { 00728 // Here's a region we don't have any more. 00729 MouseWatcherRegion *old_region = (*old_ri); 00730 without_region(old_region, param); 00731 any_changes = true; 00732 ++old_ri; 00733 } 00734 00735 if (any_changes) { 00736 // Now that we've compared the two vectors, simply swap them to set 00737 // the new vector. 00738 _current_regions.swap(regions); 00739 00740 // And don't forget to throw all of the new regions' "within" events. 00741 vector<MouseWatcherRegion *>::const_iterator ri; 00742 for (ri = new_regions.begin(); ri != new_regions.end(); ++ri) { 00743 MouseWatcherRegion *new_region = (*ri); 00744 within_region(new_region, param); 00745 } 00746 } 00747 00748 if (!_enter_multiple) { 00749 // Determine which is the "preferred region", if any. This is the 00750 // topmost region that the mouse cursor is over, and the one that 00751 // we are considered "entered" into. 00752 MouseWatcherRegion *new_preferred_region = 00753 get_preferred_region(_current_regions); 00754 00755 if (_button_down && new_preferred_region != _preferred_button_down_region) { 00756 // If the button's being held down, we're only allowed to select 00757 // the preferred button down region. 00758 new_preferred_region = (MouseWatcherRegion *)NULL; 00759 } 00760 00761 if (new_preferred_region != _preferred_region) { 00762 if (_preferred_region != (MouseWatcherRegion *)NULL) { 00763 exit_region(_preferred_region, param); 00764 } 00765 _preferred_region = new_preferred_region; 00766 if (_preferred_region != (MouseWatcherRegion *)NULL) { 00767 enter_region(_preferred_region, param); 00768 } 00769 } 00770 } 00771 } 00772 00773 //////////////////////////////////////////////////////////////////// 00774 // Function: MouseWatcher::clear_current_regions 00775 // Access: Protected 00776 // Description: Empties the set of current regions. Assumes the lock 00777 // is already held. 00778 //////////////////////////////////////////////////////////////////// 00779 void MouseWatcher:: 00780 clear_current_regions() { 00781 nassertv(_lock.debug_is_locked()); 00782 00783 if (!_current_regions.empty()) { 00784 // Set up a parameter for passing through any change events. 00785 MouseWatcherParameter param; 00786 param.set_modifier_buttons(_mods); 00787 param.set_mouse(_mouse); 00788 00789 Regions::const_iterator old_ri = _current_regions.begin(); 00790 00791 while (old_ri != _current_regions.end()) { 00792 // Here's a region we don't have any more. 00793 MouseWatcherRegion *old_region = (*old_ri); 00794 old_region->exit_region(param); 00795 throw_event_pattern(_leave_pattern, old_region, ButtonHandle::none()); 00796 ++old_ri; 00797 } 00798 00799 _current_regions.clear(); 00800 00801 if (_preferred_region != (MouseWatcherRegion *)NULL) { 00802 _preferred_region->exit_region(param); 00803 throw_event_pattern(_leave_pattern, _preferred_region, ButtonHandle::none()); 00804 _preferred_region = (MouseWatcherRegion *)NULL; 00805 } 00806 } 00807 } 00808 00809 #ifndef NDEBUG 00810 //////////////////////////////////////////////////////////////////// 00811 // Function: MouseWatcher::do_show_regions 00812 // Access: Protected, Virtual 00813 // Description: The protected implementation of show_regions(). This 00814 // assumes the lock is already held. 00815 //////////////////////////////////////////////////////////////////// 00816 void MouseWatcher:: 00817 do_show_regions(const NodePath &render2d, const string &bin_name, 00818 int draw_order) { 00819 MouseWatcherGroup::do_show_regions(render2d, bin_name, draw_order); 00820 _show_regions_render2d = render2d; 00821 _show_regions_bin_name = bin_name; 00822 _show_regions_draw_order = draw_order; 00823 00824 Groups::const_iterator gi; 00825 for (gi = _groups.begin(); gi != _groups.end(); ++gi) { 00826 MouseWatcherGroup *group = (*gi); 00827 group->show_regions(render2d, bin_name, draw_order); 00828 } 00829 } 00830 #endif // NDEBUG 00831 00832 #ifndef NDEBUG 00833 //////////////////////////////////////////////////////////////////// 00834 // Function: MouseWatcher::do_hide_regions 00835 // Access: Protected, Virtual 00836 // Description: The protected implementation of hide_regions(). This 00837 // assumes the lock is already held. 00838 //////////////////////////////////////////////////////////////////// 00839 void MouseWatcher:: 00840 do_hide_regions() { 00841 MouseWatcherGroup::do_hide_regions(); 00842 _show_regions_render2d = NodePath(); 00843 _show_regions_bin_name = string(); 00844 _show_regions_draw_order = 0; 00845 00846 Groups::const_iterator gi; 00847 for (gi = _groups.begin(); gi != _groups.end(); ++gi) { 00848 MouseWatcherGroup *group = (*gi); 00849 group->hide_regions(); 00850 } 00851 } 00852 #endif // NDEBUG 00853 00854 //////////////////////////////////////////////////////////////////// 00855 // Function: MouseWatcher::intersect_regions 00856 // Access: Protected, Static 00857 // Description: Computes the list of regions that are in both 00858 // regions_a and regions_b, as well as the list of 00859 // regions only in regions_a, and the list of regions 00860 // only in regions_b. Any or all of the three output 00861 // lists may be the same object, but they must be 00862 // different objects from both of the input lists. 00863 // 00864 // It is assumed that both vectors are already sorted in 00865 // pointer order. It is also assumed that any relevant 00866 // locks are already held. 00867 //////////////////////////////////////////////////////////////////// 00868 void MouseWatcher:: 00869 intersect_regions(MouseWatcher::Regions &only_a, 00870 MouseWatcher::Regions &only_b, 00871 MouseWatcher::Regions &both, 00872 const MouseWatcher::Regions ®ions_a, 00873 const MouseWatcher::Regions ®ions_b) { 00874 // Now do a standard sorted intersection between the two vectors. 00875 Regions::const_iterator a_ri = regions_a.begin(); 00876 Regions::const_iterator b_ri = regions_b.begin(); 00877 00878 while (a_ri != regions_a.end() && b_ri != regions_b.end()) { 00879 if ((*a_ri) < (*b_ri)) { 00880 // Here's a region in a, not in b. 00881 only_a.push_back(*a_ri); 00882 ++a_ri; 00883 00884 } else if ((*b_ri) < (*a_ri)) { 00885 // Here's a region in b, not in a. 00886 only_b.push_back(*b_ri); 00887 ++b_ri; 00888 00889 } else { 00890 // Here's a region in both vectors. 00891 both.push_back(*a_ri); 00892 ++a_ri; 00893 ++b_ri; 00894 } 00895 } 00896 } 00897 00898 //////////////////////////////////////////////////////////////////// 00899 // Function: MouseWatcher::remove_region_from 00900 // Access: Protected, Static 00901 // Description: Removes the indicated region from the given vector. 00902 // Assumes the vector is sorted in pointer order. 00903 // Returns true if removed, false if it wasn't there. 00904 // Assumes any relevent locks are already held. 00905 //////////////////////////////////////////////////////////////////// 00906 bool MouseWatcher:: 00907 remove_region_from(MouseWatcher::Regions ®ions, 00908 MouseWatcherRegion *region) { 00909 PT(MouseWatcherRegion) ptr = region; 00910 Regions::iterator ri = lower_bound(regions.begin(), regions.end(), ptr); 00911 if (ri != regions.end() && (*ri) == ptr) { 00912 // The region is in the vector. Remove it. 00913 regions.erase(ri); 00914 return true; 00915 } 00916 00917 return false; 00918 } 00919 00920 //////////////////////////////////////////////////////////////////// 00921 // Function: MouseWatcher::has_region_in 00922 // Access: Protected, Static 00923 // Description: Returns true if the indicated region is a member of 00924 // the given sorted list, false otherwise. 00925 //////////////////////////////////////////////////////////////////// 00926 bool MouseWatcher:: 00927 has_region_in(const MouseWatcher::Regions ®ions, 00928 MouseWatcherRegion *region) { 00929 PT(MouseWatcherRegion) ptr = region; 00930 Regions::const_iterator ri = lower_bound(regions.begin(), regions.end(), ptr); 00931 return (ri != regions.end() && (*ri) == ptr); 00932 } 00933 00934 //////////////////////////////////////////////////////////////////// 00935 // Function: MouseWatcher::throw_event_pattern 00936 // Access: Protected 00937 // Description: Throws an event associated with the indicated region, 00938 // using the given pattern. 00939 //////////////////////////////////////////////////////////////////// 00940 void MouseWatcher:: 00941 throw_event_pattern(const string &pattern, const MouseWatcherRegion *region, 00942 const ButtonHandle &button) { 00943 if (pattern.empty()) { 00944 return; 00945 } 00946 #ifndef NDEBUG 00947 if (region != (MouseWatcherRegion *)NULL) { 00948 region->test_ref_count_integrity(); 00949 } 00950 #endif 00951 00952 string button_name; 00953 if (button != ButtonHandle::none()) { 00954 if (!_mods.has_button(button)) { 00955 // We only prepend modifier names for buttons which are not 00956 // themselves modifiers. 00957 button_name = _mods.get_prefix(); 00958 } 00959 button_name += button.get_name(); 00960 } 00961 00962 string event; 00963 for (size_t p = 0; p < pattern.size(); ++p) { 00964 if (pattern[p] == '%') { 00965 string cmd = pattern.substr(p + 1, 1); 00966 p++; 00967 if (cmd == "r") { 00968 if (region != (MouseWatcherRegion *)NULL) { 00969 event += region->get_name(); 00970 } 00971 00972 } else if (cmd == "b") { 00973 event += button.get_name(); 00974 00975 } else { 00976 tform_cat.error() 00977 << "Invalid symbol in event_pattern: %" << cmd << "\n"; 00978 } 00979 } else { 00980 event += pattern[p]; 00981 } 00982 } 00983 00984 if (!event.empty()) { 00985 throw_event(event, EventParameter(region), EventParameter(button_name)); 00986 if (_eh != (EventHandler*)0L) 00987 throw_event_directly(*_eh, event, EventParameter(region), 00988 EventParameter(button_name)); 00989 } 00990 } 00991 00992 //////////////////////////////////////////////////////////////////// 00993 // Function: MouseWatcher::move 00994 // Access: Protected 00995 // Description: Records the indicated mouse or keyboard button as 00996 // being moved from last position. 00997 //////////////////////////////////////////////////////////////////// 00998 void MouseWatcher:: 00999 move() { 01000 nassertv(_lock.debug_is_locked()); 01001 01002 MouseWatcherParameter param; 01003 param.set_modifier_buttons(_mods); 01004 param.set_mouse(_mouse); 01005 01006 if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) { 01007 _preferred_button_down_region->move(param); 01008 } 01009 } 01010 01011 //////////////////////////////////////////////////////////////////// 01012 // Function: MouseWatcher::press 01013 // Access: Protected 01014 // Description: Records the indicated mouse or keyboard button as 01015 // being depressed. 01016 //////////////////////////////////////////////////////////////////// 01017 void MouseWatcher:: 01018 press(ButtonHandle button, bool keyrepeat) { 01019 nassertv(_lock.debug_is_locked()); 01020 01021 MouseWatcherParameter param; 01022 param.set_button(button); 01023 param.set_keyrepeat(keyrepeat); 01024 param.set_modifier_buttons(_mods); 01025 param.set_mouse(_mouse); 01026 01027 if (MouseButton::is_mouse_button(button)) { 01028 // Mouse buttons are inextricably linked to the mouse position. 01029 01030 if (!_button_down) { 01031 _preferred_button_down_region = _preferred_region; 01032 } 01033 _button_down = true; 01034 01035 if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) { 01036 _preferred_button_down_region->press(param); 01037 if (keyrepeat) { 01038 throw_event_pattern(_button_repeat_pattern, 01039 _preferred_button_down_region, button); 01040 } else { 01041 throw_event_pattern(_button_down_pattern, 01042 _preferred_button_down_region, button); 01043 } 01044 } 01045 01046 } else { 01047 // It's a keyboard button; therefore, send the event to every 01048 // region that wants keyboard buttons, regardless of the mouse 01049 // position. 01050 if (_preferred_region != (MouseWatcherRegion *)NULL) { 01051 // Our current region, the one under the mouse, always get 01052 // all the keyboard events, even if it doesn't set its 01053 // keyboard flag. 01054 _preferred_region->press(param); 01055 consider_keyboard_suppress(_preferred_region); 01056 } 01057 01058 if ((_internal_suppress & MouseWatcherRegion::SF_other_button) == 0) { 01059 // All the other regions only get the keyboard events if they 01060 // set their global keyboard flag, *and* the current region does 01061 // not suppress keyboard buttons. 01062 param.set_outside(true); 01063 global_keyboard_press(param); 01064 } 01065 } 01066 } 01067 01068 //////////////////////////////////////////////////////////////////// 01069 // Function: MouseWatcher::release 01070 // Access: Protected 01071 // Description: Records the indicated mouse or keyboard button as 01072 // being released. 01073 //////////////////////////////////////////////////////////////////// 01074 void MouseWatcher:: 01075 release(ButtonHandle button) { 01076 nassertv(_lock.debug_is_locked()); 01077 01078 MouseWatcherParameter param; 01079 param.set_button(button); 01080 param.set_modifier_buttons(_mods); 01081 param.set_mouse(_mouse); 01082 01083 if (MouseButton::is_mouse_button(button)) { 01084 // Button up. Send the up event associated with the region(s) we 01085 // were over when the button went down. 01086 01087 // There is some danger of losing button-up events here. If 01088 // more than one button goes down together, we won't detect 01089 // both of the button-up events properly. 01090 if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) { 01091 param.set_outside(_preferred_button_down_region != _preferred_region); 01092 _preferred_button_down_region->release(param); 01093 throw_event_pattern(_button_up_pattern, 01094 _preferred_button_down_region, button); 01095 } 01096 01097 _button_down = false; 01098 _preferred_button_down_region = (MouseWatcherRegion *)NULL; 01099 01100 } else { 01101 // It's a keyboard button; therefore, send the event to every 01102 // region that wants keyboard buttons, regardless of the mouse 01103 // position. 01104 if (_preferred_region != (MouseWatcherRegion *)NULL) { 01105 _preferred_region->release(param); 01106 } 01107 01108 param.set_outside(true); 01109 global_keyboard_release(param); 01110 } 01111 } 01112 01113 //////////////////////////////////////////////////////////////////// 01114 // Function: MouseWatcher::keystroke 01115 // Access: Protected 01116 // Description: Records that the indicated keystroke has been 01117 // generated. 01118 //////////////////////////////////////////////////////////////////// 01119 void MouseWatcher:: 01120 keystroke(int keycode) { 01121 nassertv(_lock.debug_is_locked()); 01122 01123 MouseWatcherParameter param; 01124 param.set_keycode(keycode); 01125 param.set_modifier_buttons(_mods); 01126 param.set_mouse(_mouse); 01127 01128 // Keystrokes go to all those regions that want keyboard events, 01129 // regardless of which is the "preferred" region (that is, without 01130 // respect to the mouse position). However, we do set the outside 01131 // flag according to whether the given region is the preferred 01132 // region or not. 01133 01134 Regions::const_iterator ri; 01135 for (ri = _regions.begin(); ri != _regions.end(); ++ri) { 01136 MouseWatcherRegion *region = (*ri); 01137 01138 if (region->get_keyboard()) { 01139 param.set_outside(region != _preferred_region); 01140 region->keystroke(param); 01141 consider_keyboard_suppress(region); 01142 } 01143 } 01144 01145 // Also check all of our sub-groups. 01146 Groups::const_iterator gi; 01147 for (gi = _groups.begin(); gi != _groups.end(); ++gi) { 01148 MouseWatcherGroup *group = (*gi); 01149 for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) { 01150 MouseWatcherRegion *region = (*ri); 01151 01152 if (region->get_keyboard()) { 01153 param.set_outside(region != _preferred_region); 01154 region->keystroke(param); 01155 consider_keyboard_suppress(region); 01156 } 01157 } 01158 } 01159 } 01160 01161 //////////////////////////////////////////////////////////////////// 01162 // Function: MouseWatcher::candidate 01163 // Access: Protected 01164 // Description: Records that the indicated candidate string has been 01165 // highlighted in the IME. 01166 //////////////////////////////////////////////////////////////////// 01167 void MouseWatcher:: 01168 candidate(const wstring &candidate_string, size_t highlight_start, 01169 size_t highlight_end, size_t cursor_pos) { 01170 nassertv(_lock.debug_is_locked()); 01171 01172 MouseWatcherParameter param; 01173 param.set_candidate(candidate_string, highlight_start, highlight_end, cursor_pos); 01174 param.set_modifier_buttons(_mods); 01175 param.set_mouse(_mouse); 01176 01177 // Candidate strings go to all those regions that want keyboard 01178 // events, exactly like keystrokes, above. 01179 01180 Regions::const_iterator ri; 01181 for (ri = _regions.begin(); ri != _regions.end(); ++ri) { 01182 MouseWatcherRegion *region = (*ri); 01183 01184 if (region->get_keyboard()) { 01185 param.set_outside(region != _preferred_region); 01186 region->candidate(param); 01187 } 01188 } 01189 01190 // Also check all of our sub-groups. 01191 Groups::const_iterator gi; 01192 for (gi = _groups.begin(); gi != _groups.end(); ++gi) { 01193 MouseWatcherGroup *group = (*gi); 01194 for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) { 01195 MouseWatcherRegion *region = (*ri); 01196 01197 if (region->get_keyboard()) { 01198 param.set_outside(region != _preferred_region); 01199 region->candidate(param); 01200 } 01201 } 01202 } 01203 } 01204 01205 //////////////////////////////////////////////////////////////////// 01206 // Function: MouseWatcher::global_keyboard_press 01207 // Access: Protected 01208 // Description: Calls press() on all regions that are interested in 01209 // receiving global keyboard events, except for the 01210 // current region (which already received this one). 01211 //////////////////////////////////////////////////////////////////// 01212 void MouseWatcher:: 01213 global_keyboard_press(const MouseWatcherParameter ¶m) { 01214 nassertv(_lock.debug_is_locked()); 01215 01216 Regions::const_iterator ri; 01217 for (ri = _regions.begin(); ri != _regions.end(); ++ri) { 01218 MouseWatcherRegion *region = (*ri); 01219 01220 if (region != _preferred_region && region->get_keyboard()) { 01221 region->press(param); 01222 consider_keyboard_suppress(region); 01223 } 01224 } 01225 01226 // Also check all of our sub-groups. 01227 Groups::const_iterator gi; 01228 for (gi = _groups.begin(); gi != _groups.end(); ++gi) { 01229 MouseWatcherGroup *group = (*gi); 01230 for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) { 01231 MouseWatcherRegion *region = (*ri); 01232 01233 if (region != _preferred_region && region->get_keyboard()) { 01234 region->press(param); 01235 consider_keyboard_suppress(region); 01236 } 01237 } 01238 } 01239 } 01240 //////////////////////////////////////////////////////////////////// 01241 // Function: MouseWatcher::global_keyboard_release 01242 // Access: Protected 01243 // Description: Calls release() on all regions that are interested in 01244 // receiving global keyboard events, except for the 01245 // current region (which already received this one). 01246 //////////////////////////////////////////////////////////////////// 01247 void MouseWatcher:: 01248 global_keyboard_release(const MouseWatcherParameter ¶m) { 01249 nassertv(_lock.debug_is_locked()); 01250 01251 Regions::const_iterator ri; 01252 for (ri = _regions.begin(); ri != _regions.end(); ++ri) { 01253 MouseWatcherRegion *region = (*ri); 01254 01255 if (region != _preferred_region && region->get_keyboard()) { 01256 region->release(param); 01257 } 01258 } 01259 01260 // Also check all of our sub-groups. 01261 Groups::const_iterator gi; 01262 for (gi = _groups.begin(); gi != _groups.end(); ++gi) { 01263 MouseWatcherGroup *group = (*gi); 01264 for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) { 01265 MouseWatcherRegion *region = (*ri); 01266 01267 if (region != _preferred_region && region->get_keyboard()) { 01268 region->release(param); 01269 } 01270 } 01271 } 01272 } 01273 01274 //////////////////////////////////////////////////////////////////// 01275 // Function: MouseWatcher::enter_region 01276 // Access: Protected 01277 // Description: Called internally to indicate the mouse pointer is 01278 // favoring the indicated region. 01279 //////////////////////////////////////////////////////////////////// 01280 void MouseWatcher:: 01281 enter_region(MouseWatcherRegion *region, const MouseWatcherParameter ¶m) { 01282 nassertv(_lock.debug_is_locked()); 01283 01284 region->enter_region(param); 01285 throw_event_pattern(_enter_pattern, region, ButtonHandle::none()); 01286 if (_implicit_click) { 01287 MouseWatcherParameter param1(param); 01288 param1.set_button(MouseButton::one()); 01289 region->press(param1); 01290 } 01291 } 01292 01293 //////////////////////////////////////////////////////////////////// 01294 // Function: MouseWatcher::exit_region 01295 // Access: Protected 01296 // Description: Called internally to indicate the mouse pointer is no 01297 // longer favoring the indicated region. 01298 //////////////////////////////////////////////////////////////////// 01299 void MouseWatcher:: 01300 exit_region(MouseWatcherRegion *region, const MouseWatcherParameter ¶m) { 01301 nassertv(_lock.debug_is_locked()); 01302 01303 if (_implicit_click) { 01304 MouseWatcherParameter param1(param); 01305 param1.set_button(MouseButton::one()); 01306 region->release(param1); 01307 } 01308 region->exit_region(param); 01309 throw_event_pattern(_leave_pattern, region, ButtonHandle::none()); 01310 } 01311 01312 //////////////////////////////////////////////////////////////////// 01313 // Function: MouseWatcher::set_no_mouse 01314 // Access: Protected 01315 // Description: Called from do_transmit_data() to indicate the mouse 01316 // is not within the window. 01317 //////////////////////////////////////////////////////////////////// 01318 void MouseWatcher:: 01319 set_no_mouse() { 01320 nassertv(_lock.debug_is_locked()); 01321 01322 if (_has_mouse) { 01323 // Hide the mouse pointer. 01324 if (!_geometry.is_null()) { 01325 _geometry->set_overall_hidden(true); 01326 } 01327 } 01328 01329 _has_mouse = false; 01330 clear_current_regions(); 01331 } 01332 01333 //////////////////////////////////////////////////////////////////// 01334 // Function: MouseWatcher::set_mouse 01335 // Access: Protected 01336 // Description: Called from do_transmit_data() to indicate the mouse 01337 // is within the window, and to specify its current 01338 // position. 01339 //////////////////////////////////////////////////////////////////// 01340 void MouseWatcher:: 01341 set_mouse(const LVecBase2 &xy, const LVecBase2 &pixel_xy) { 01342 nassertv(_lock.debug_is_locked()); 01343 01344 if (!_geometry.is_null()) { 01345 // Transform the mouse pointer. 01346 _geometry->set_transform(TransformState::make_pos(LVecBase3(xy[0], 0, xy[1]))); 01347 if (!_has_mouse) { 01348 // Show the mouse pointer. 01349 _geometry->set_overall_hidden(false); 01350 } 01351 } 01352 01353 _has_mouse = true; 01354 _mouse = xy; 01355 _mouse_pixel = pixel_xy; 01356 01357 Regions regions; 01358 get_over_regions(regions, _mouse); 01359 set_current_regions(regions); 01360 } 01361 01362 //////////////////////////////////////////////////////////////////// 01363 // Function: MouseWatcher::consider_keyboard_suppress 01364 // Access: Private 01365 // Description: If we send any keyboard events to a region that has 01366 // the SF_other_button suppress flag set, that means we 01367 // should not send the keyboard event along the data 01368 // graph. 01369 // 01370 // This method is called as each keyboard event is sent 01371 // to a region; it should update the internal 01372 // _keyboard_suppress bitmask to indicate this. 01373 //////////////////////////////////////////////////////////////////// 01374 void MouseWatcher:: 01375 consider_keyboard_suppress(const MouseWatcherRegion *region) { 01376 if ((region->get_suppress_flags() & MouseWatcherRegion::SF_other_button) != 0) { 01377 _external_suppress |= MouseWatcherRegion::SF_other_button; 01378 } 01379 } 01380 01381 //////////////////////////////////////////////////////////////////// 01382 // Function: MouseWatcher::do_transmit_data 01383 // Access: Protected, Virtual 01384 // Description: The virtual implementation of transmit_data(). This 01385 // function receives an array of input parameters and 01386 // should generate an array of output parameters. The 01387 // input parameters may be accessed with the index 01388 // numbers returned by the define_input() calls that 01389 // were made earlier (presumably in the constructor); 01390 // likewise, the output parameters should be set with 01391 // the index numbers returned by the define_output() 01392 // calls. 01393 //////////////////////////////////////////////////////////////////// 01394 void MouseWatcher:: 01395 do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input, 01396 DataNodeTransmit &output) { 01397 Thread *current_thread = trav->get_current_thread(); 01398 LightMutexHolder holder(_lock); 01399 01400 bool activity = false; 01401 01402 // Initially, we do not suppress any events to objects below us in 01403 // the data graph. 01404 _internal_suppress = 0; 01405 _external_suppress = 0; 01406 01407 // We always pass the pixel_size data through. 01408 EventStoreVec2 *pixel_size; 01409 DCAST_INTO_V(pixel_size, input.get_data(_pixel_size_input).get_ptr()); 01410 output.set_data(_pixel_size_output, pixel_size); 01411 _pixel_size = pixel_size; 01412 01413 if (input.has_data(_xy_input)) { 01414 // The mouse is within the window. Get the current mouse position. 01415 const EventStoreVec2 *xy, *pixel_xy; 01416 DCAST_INTO_V(xy, input.get_data(_xy_input).get_ptr()); 01417 DCAST_INTO_V(pixel_xy, input.get_data(_pixel_xy_input).get_ptr()); 01418 01419 LVecBase2 f = xy->get_value(); 01420 LVecBase2 p = pixel_xy->get_value(); 01421 01422 // Asad: determine if mouse moved from last position 01423 const LVecBase2 &last_f = _xy->get_value(); 01424 if (f != last_f) { 01425 activity = true; 01426 move(); 01427 } 01428 01429 if (_display_region != (DisplayRegion *)NULL) { 01430 // If we've got a display region, constrain the mouse to it. 01431 if (constrain_display_region(_display_region, f, p, current_thread)) { 01432 set_mouse(f, p); 01433 01434 } else { 01435 // The mouse is outside the display region, even though it's 01436 // within the window. This is considered not having a mouse. 01437 set_no_mouse(); 01438 01439 // This also means we should suppress mouse button events below us. 01440 _internal_suppress |= MouseWatcherRegion::SF_mouse_button; 01441 } 01442 01443 } else { 01444 // No display region; respect the whole window. 01445 set_mouse(f, p); 01446 } 01447 } 01448 01449 // Code for recording the mouse trail. 01450 _num_trail_recent = 0; 01451 if (input.has_data(_pointer_events_input) && (_trail_log_duration > 0.0)) { 01452 const PointerEventList *this_pointer_events; 01453 DCAST_INTO_V(this_pointer_events, input.get_data(_pointer_events_input).get_ptr()); 01454 _num_trail_recent = this_pointer_events->get_num_events(); 01455 for (int i = 0; i < _num_trail_recent; i++) { 01456 bool in_win = this_pointer_events->get_in_window(i); 01457 int xpos = this_pointer_events->get_xpos(i); 01458 int ypos = this_pointer_events->get_ypos(i); 01459 int sequence = this_pointer_events->get_sequence(i); 01460 double time = this_pointer_events->get_time(i); 01461 _trail_log->add_event(in_win, xpos, ypos, sequence, time); 01462 } 01463 } 01464 if (_trail_log->get_num_events() > 0) { 01465 discard_excess_trail_log(); 01466 update_trail_node(); 01467 } 01468 if (_num_trail_recent > _trail_log->get_num_events()) { 01469 _num_trail_recent = _trail_log->get_num_events(); 01470 } 01471 01472 // If the mouse is over a particular region, or still considered 01473 // owned by a region because of a recent button-down event, that 01474 // region determines whether we suppress events below us. 01475 if (_preferred_region != (MouseWatcherRegion *)NULL) { 01476 _internal_suppress |= _preferred_region->get_suppress_flags(); 01477 } 01478 01479 ButtonEventList new_button_events; 01480 01481 // Look for new button events. 01482 if (input.has_data(_button_events_input)) { 01483 const ButtonEventList *this_button_events; 01484 DCAST_INTO_V(this_button_events, input.get_data(_button_events_input).get_ptr()); 01485 int num_events = this_button_events->get_num_events(); 01486 for (int i = 0; i < num_events; i++) { 01487 const ButtonEvent &be = this_button_events->get_event(i); 01488 be.update_mods(_mods); 01489 01490 switch (be._type) { 01491 case ButtonEvent::T_down: 01492 if (!_current_buttons_down.get_bit(be._button.get_index())) { 01493 // The button was not already depressed; thus, this is not 01494 // keyrepeat. 01495 activity = true; 01496 _current_buttons_down.set_bit(be._button.get_index()); 01497 press(be._button, false); 01498 new_button_events.add_event(be); 01499 break; 01500 } 01501 // The button was already depressed, so this is really just 01502 // keyrepeat. Fall through. 01503 01504 case ButtonEvent::T_repeat: 01505 _current_buttons_down.set_bit(be._button.get_index()); 01506 press(be._button, true); 01507 new_button_events.add_event(ButtonEvent(be._button, ButtonEvent::T_repeat, 01508 be._time)); 01509 break; 01510 01511 case ButtonEvent::T_up: 01512 activity = true; 01513 _current_buttons_down.clear_bit(be._button.get_index()); 01514 release(be._button); 01515 new_button_events.add_event(be); 01516 break; 01517 01518 case ButtonEvent::T_keystroke: 01519 // We don't consider "keystroke" an activity event, because it 01520 // might be just keyrepeat. 01521 keystroke(be._keycode); 01522 new_button_events.add_event(be); 01523 break; 01524 01525 case ButtonEvent::T_candidate: 01526 activity = true; 01527 candidate(be._candidate_string, be._highlight_start, be._highlight_end, be._cursor_pos); 01528 new_button_events.add_event(be); 01529 break; 01530 01531 case ButtonEvent::T_resume_down: 01532 //_current_buttons_down.set_bit(be._button.get_index()); 01533 // Don't call press(), since the button wasn't actually 01534 // pressed just now. 01535 new_button_events.add_event(be); 01536 break; 01537 01538 case ButtonEvent::T_move: 01539 // This is handled below. 01540 break; 01541 } 01542 } 01543 } 01544 01545 if (!input.has_data(_xy_input)) { 01546 // No mouse in the window. We check this down here, below the 01547 // button checking, in case the mouse left the window in the same 01548 // frame it released a button (particularly likely with a 01549 // touchscreen input that's emulating a mouse). 01550 set_no_mouse(); 01551 } 01552 01553 // Now check the inactivity timer. 01554 if (_has_inactivity_timeout) { 01555 if (activity) { 01556 note_activity(); 01557 01558 } else { 01559 double now = ClockObject::get_global_clock()->get_frame_time(); 01560 double elapsed = now - _last_activity; 01561 01562 // Toggle the inactivity state to inactive. 01563 if (elapsed > _inactivity_timeout) { 01564 switch (_inactivity_state) { 01565 case IS_active: 01566 _inactivity_state = IS_active_to_inactive; 01567 break; 01568 01569 case IS_inactive: 01570 break; 01571 01572 case IS_active_to_inactive: 01573 break; 01574 01575 case IS_inactive_to_active: 01576 _inactivity_state = IS_inactive; 01577 break; 01578 } 01579 } 01580 } 01581 } 01582 01583 switch (_inactivity_state) { 01584 case IS_active: 01585 case IS_inactive: 01586 break; 01587 01588 case IS_active_to_inactive: 01589 // "Release" all of the currently-held buttons. 01590 if (tform_cat.is_debug()) { 01591 tform_cat.info() 01592 << "MouseWatcher detected " << _inactivity_timeout 01593 << " seconds of inactivity; releasing held buttons.\n"; 01594 } 01595 { 01596 for (int i = 0; i < _current_buttons_down.get_num_bits(); ++i) { 01597 if (_current_buttons_down.get_bit(i)) { 01598 release(ButtonHandle(i)); 01599 new_button_events.add_event(ButtonEvent(ButtonHandle(i), ButtonEvent::T_up)); 01600 } 01601 } 01602 } 01603 _inactivity_state = IS_inactive; 01604 throw_event(_inactivity_timeout_event); 01605 break; 01606 01607 case IS_inactive_to_active: 01608 // "Press" all of the buttons we "released" before. 01609 { 01610 for (int i = 0; i < _current_buttons_down.get_num_bits(); ++i) { 01611 if (_current_buttons_down.get_bit(i)) { 01612 press(ButtonHandle(i), false); 01613 new_button_events.add_event(ButtonEvent(ButtonHandle(i), ButtonEvent::T_down)); 01614 } 01615 } 01616 } 01617 _inactivity_state = IS_active; 01618 break; 01619 } 01620 01621 if (_has_mouse && 01622 (_internal_suppress & MouseWatcherRegion::SF_mouse_position) == 0) { 01623 // Transmit the mouse position. 01624 _xy->set_value(_mouse); 01625 output.set_data(_xy_output, EventParameter(_xy)); 01626 _pixel_xy->set_value(_mouse_pixel); 01627 output.set_data(_pixel_xy_output, EventParameter(_pixel_xy)); 01628 } 01629 01630 // Now transmit the buttons events down the graph. 01631 int suppress_buttons = ((_internal_suppress | _external_suppress) & MouseWatcherRegion::SF_any_button); 01632 01633 _button_events->clear(); 01634 01635 int num_events = new_button_events.get_num_events(); 01636 for (int i = 0; i < num_events; i++) { 01637 const ButtonEvent &be = new_button_events.get_event(i); 01638 bool suppress = true; 01639 01640 if (be._type != ButtonEvent::T_keystroke && 01641 MouseButton::is_mouse_button(be._button)) { 01642 suppress = ((suppress_buttons & MouseWatcherRegion::SF_mouse_button) != 0); 01643 } else { 01644 suppress = ((suppress_buttons & MouseWatcherRegion::SF_other_button) != 0); 01645 } 01646 01647 if (!suppress || be._type == ButtonEvent::T_up) { 01648 // Don't suppress this button event; pass it through. 01649 _button_events->add_event(be); 01650 } 01651 } 01652 01653 if (_button_events->get_num_events() != 0) { 01654 output.set_data(_button_events_output, EventParameter(_button_events)); 01655 } 01656 } 01657 01658 //////////////////////////////////////////////////////////////////// 01659 // Function: MouseWatcher::constrain_display_region 01660 // Access: Private, Static 01661 // Description: Constrains the mouse coordinates to within the 01662 // indicated DisplayRegion. If the mouse pointer does 01663 // indeed fall within the DisplayRegion, rescales f and 01664 // p correspondingly, and returns true. If the mouse 01665 // pointer does not fall within the DisplayRegion, 01666 // leaves f and p unchanged, and returns false. 01667 //////////////////////////////////////////////////////////////////// 01668 bool MouseWatcher:: 01669 constrain_display_region(DisplayRegion *display_region, 01670 LVecBase2 &f, LVecBase2 &p, 01671 Thread *current_thread) { 01672 // If it's a stereo DisplayRegion, we should actually call this 01673 // method twice, once for each eye, in case we have side-by-side 01674 // stereo. 01675 if (display_region->is_stereo()) { 01676 StereoDisplayRegion *stereo_display_region; 01677 DCAST_INTO_R(stereo_display_region, display_region, false); 01678 return constrain_display_region(stereo_display_region->get_left_eye(), f, p, current_thread) || 01679 constrain_display_region(stereo_display_region->get_right_eye(), f, p, current_thread); 01680 } 01681 01682 DisplayRegionPipelineReader dr_reader(display_region, current_thread); 01683 PN_stdfloat left, right, bottom, top; 01684 dr_reader.get_dimensions(left, right, bottom, top); 01685 01686 // Need to translate this into DisplayRegion [0, 1] space 01687 PN_stdfloat x = (f[0] + 1.0f) / 2.0f; 01688 PN_stdfloat y = (f[1] + 1.0f) / 2.0f; 01689 01690 if (x < left || x >= right || 01691 y < bottom || y >= top) { 01692 // The mouse is outside the display region. 01693 return false; 01694 } 01695 01696 // The mouse is within the display region; rescale it. 01697 01698 // Scale in DR space 01699 PN_stdfloat xp = (x - left) / (right - left); 01700 // Translate back into [-1, 1] space 01701 PN_stdfloat xpp = (xp * 2.0f) - 1.0f; 01702 01703 PN_stdfloat yp = (y - bottom) / (top - bottom); 01704 PN_stdfloat ypp = (yp * 2.0f) - 1.0f; 01705 01706 int xo, yo, w, h; 01707 dr_reader.get_region_pixels_i(xo, yo, w, h); 01708 01709 f.set(xpp, ypp); 01710 p.set(p[0] - xo, p[1] - yo); 01711 return true; 01712 }