46 MouseWatcher(
const string &name) :
49 _pixel_xy_input = define_input(
"pixel_xy", EventStoreVec2::get_class_type());
50 _pixel_size_input = define_input(
"pixel_size", EventStoreVec2::get_class_type());
51 _xy_input = define_input(
"xy", EventStoreVec2::get_class_type());
52 _button_events_input = define_input(
"button_events", ButtonEventList::get_class_type());
53 _pointer_events_input = define_input(
"pointer_events", PointerEventList::get_class_type());
55 _pixel_xy_output = define_output(
"pixel_xy", EventStoreVec2::get_class_type());
56 _pixel_size_output = define_output(
"pixel_size", EventStoreVec2::get_class_type());
57 _xy_output = define_output(
"xy", EventStoreVec2::get_class_type());
58 _button_events_output = define_output(
"button_events", ButtonEventList::get_class_type());
66 _internal_suppress = 0;
67 _preferred_region =
nullptr;
68 _preferred_button_down_region =
nullptr;
71 _display_region =
nullptr;
72 _button_down_display_region =
nullptr;
74 _frame.set(-1.0f, 1.0f, -1.0f, 1.0f);
76 _inactivity_timeout = inactivity_timeout;
77 _has_inactivity_timeout = !IS_NEARLY_ZERO(_inactivity_timeout);
79 _num_trail_recent = 0;
80 _trail_log_duration = 0.0;
83 _inactivity_timeout_event =
"inactivity_timeout";
85 _inactivity_state = IS_active;
90 _enter_multiple =
false;
94 _implicit_click =
false;
112 remove_region_from(_current_regions, region);
113 if (region == _preferred_region) {
114 if (_preferred_region !=
nullptr) {
117 _preferred_region =
nullptr;
119 if (region == _preferred_button_down_region) {
120 _preferred_button_down_region =
nullptr;
123 return MouseWatcherBase::do_remove_region(region);
136 get_over_regions(regions, pos);
137 return get_preferred_region(regions);
158 Groups::const_iterator gi =
159 find(_groups.begin(), _groups.end(), pt);
160 if (gi != _groups.end()) {
166 if (!_show_regions_render2d.
is_empty()) {
167 group->
show_regions(_show_regions_render2d, _show_regions_bin_name,
168 _show_regions_draw_order);
173 _groups.push_back(pt);
187 group->do_sort_regions();
190 intersect_regions(only_a, only_b, both,
191 _current_regions, group->_regions);
192 set_current_regions(only_a);
194 if (has_region_in(both, _preferred_region)) {
195 if (_preferred_region !=
nullptr) {
198 _preferred_region =
nullptr;
200 if (has_region_in(both, _preferred_button_down_region)) {
201 _preferred_button_down_region =
nullptr;
205 if (!_show_regions_render2d.
is_empty()) {
206 group->do_hide_regions();
212 Groups::iterator gi =
213 find(_groups.begin(), _groups.end(), pt);
214 if (gi != _groups.end()) {
235 if (old_group == new_group) {
245 old_group->do_sort_regions();
246 new_group->do_sort_regions();
249 if (!_show_regions_render2d.
is_empty()) {
250 old_group->do_hide_regions();
251 new_group->do_show_regions(_show_regions_render2d, _show_regions_bin_name,
252 _show_regions_draw_order);
258 intersect_regions(remove, add, keep,
259 old_group->_regions, new_group->_regions);
262 bool any_new_current_regions =
false;
265 if (!remove.
empty()) {
267 intersect_regions(only_a, only_b, both,
268 _current_regions, remove);
269 new_current_regions.
swap(only_a);
270 any_new_current_regions =
true;
272 if (has_region_in(both, _preferred_region)) {
273 if (_preferred_region !=
nullptr) {
276 _preferred_region =
nullptr;
278 if (has_region_in(both, _preferred_button_down_region)) {
279 _preferred_button_down_region =
nullptr;
301 if (any_new_current_regions) {
302 set_current_regions(new_current_regions);
307 Groups::iterator gi =
308 find(_groups.begin(), _groups.end(), pt);
309 if (gi == _groups.end()) {
310 _groups.push_back(new_group);
314 if (!_show_regions_render2d.
is_empty()) {
315 new_group->do_update_regions();
321 gi = find(_groups.begin(), _groups.end(), pt);
322 if (gi != _groups.end()) {
339 return _groups.size();
348 nassertr(n >= 0 && n < (
int)_groups.size(),
nullptr);
359 if (duration < 0.0) {
362 _trail_log_duration = duration;
363 discard_excess_trail_log();
374 discard_excess_trail_log() {
375 if (_trail_log_duration == 0.0) {
378 if (_trail_log->get_num_events() > 2) {
380 while ((_trail_log->get_num_events() > 2)&&
381 (_trail_log->get_time(0) <= old)&&
382 (_trail_log->get_time(1) <= old)) {
383 _trail_log->pop_front();
402 if (_trail_node ==
nullptr) {
403 _trail_node =
new GeomNode(
"Mouse Trail Node");
417 _trail_node =
nullptr;
424 update_trail_node() {
425 if (_trail_node ==
nullptr) {
428 _trail_node->remove_all_geoms();
430 if (_trail_log->get_num_events() < 2) {
441 double xscale = 2.0 / _pixel_size->get_value().get_x();
442 double yscale = 2.0 / _pixel_size->get_value().get_y();
444 for (
int i=0; i<(int)_trail_log->get_num_events(); i++) {
445 double x = (_trail_log->get_xpos(i) * xscale) - 1.0;
446 double y = (_trail_log->get_ypos(i) * yscale) - 1.0;
447 vertex.add_data3(LVecBase3(x,0.0,-y));
448 lines->add_vertex(i);
450 lines->close_primitive();
453 l_geom->add_primitive(lines);
454 _trail_node->add_geom(l_geom);
470 switch (_inactivity_state) {
475 _inactivity_state = IS_inactive_to_active;
478 case IS_active_to_inactive:
479 _inactivity_state = IS_active;
482 case IS_inactive_to_active:
492 output(std::ostream &out)
const {
494 DataNode::output(out);
500 size_t count = _regions.size();
502 count += group->get_num_regions();
505 out <<
" (" << count <<
" regions)";
512 write(std::ostream &out,
int indent_level)
const {
514 <<
"MouseWatcher " << get_name() <<
":\n";
515 MouseWatcherBase::write(out, indent_level + 2);
519 indent(out, indent_level + 2)
521 group->write(out, indent_level + 4);
531 nassertv(_lock.debug_is_locked());
534 PN_stdfloat mx = (pos[0] + 1.0f) * 0.5f * (_frame[1] - _frame[0]) + _frame[0];
535 PN_stdfloat my = (pos[1] + 1.0f) * 0.5f * (_frame[3] - _frame[2]) + _frame[2];
549 const LVecBase4 &frame = region->get_frame();
551 if (region->get_active() &&
552 mx >= frame[0] && mx <= frame[1] &&
553 my >= frame[2] && my <= frame[3]) {
561 group->sort_regions();
564 const LVecBase4 &frame = region->get_frame();
567 mx >= frame[0] && mx <= frame[1] &&
568 my >= frame[2] && my <= frame[3]) {
578 sort(regions.
begin(), regions.
end());
588 if (regions.
empty()) {
592 Regions::const_iterator ri;
593 ri = regions.
begin();
596 while (ri != regions.
end()) {
599 if (*region < *preferred) {
617 nassertv(_lock.debug_is_locked());
625 Regions::const_iterator new_ri = regions.
begin();
626 Regions::const_iterator old_ri = _current_regions.
begin();
630 std::vector<MouseWatcherRegion *> new_regions;
632 bool any_changes =
false;
633 while (new_ri != regions.
end() && old_ri != _current_regions.
end()) {
634 if ((*new_ri) < (*old_ri)) {
637 new_regions.push_back(new_region);
641 }
else if ((*old_ri) < (*new_ri)) {
644 without_region(old_region, param);
655 while (new_ri != regions.
end()) {
658 new_regions.push_back(new_region);
663 while (old_ri != _current_regions.
end()) {
666 without_region(old_region, param);
674 _current_regions.
swap(regions);
677 std::vector<MouseWatcherRegion *>::const_iterator ri;
678 for (ri = new_regions.begin(); ri != new_regions.end(); ++ri) {
680 within_region(new_region, param);
684 if (!_enter_multiple) {
689 get_preferred_region(_current_regions);
691 if (_button_down && new_preferred_region != _preferred_button_down_region) {
694 new_preferred_region =
nullptr;
697 if (new_preferred_region != _preferred_region) {
698 if (_preferred_region !=
nullptr) {
699 exit_region(_preferred_region, param);
701 _preferred_region = new_preferred_region;
702 if (_preferred_region !=
nullptr) {
703 enter_region(_preferred_region, param);
713 clear_current_regions() {
714 nassertv(_lock.debug_is_locked());
716 if (!_current_regions.
empty()) {
722 Regions::const_iterator old_ri = _current_regions.
begin();
724 while (old_ri != _current_regions.
end()) {
728 throw_event_pattern(_leave_pattern, old_region, ButtonHandle::none());
732 _current_regions.
clear();
734 if (_preferred_region !=
nullptr) {
735 _preferred_region->exit_region(param);
736 throw_event_pattern(_leave_pattern, _preferred_region, ButtonHandle::none());
737 _preferred_region =
nullptr;
748 do_show_regions(
const NodePath &render2d,
const string &bin_name,
750 MouseWatcherBase::do_show_regions(render2d, bin_name, draw_order);
751 _show_regions_render2d = render2d;
752 _show_regions_bin_name = bin_name;
753 _show_regions_draw_order = draw_order;
756 group->show_regions(render2d, bin_name, draw_order);
768 MouseWatcherBase::do_hide_regions();
769 _show_regions_render2d =
NodePath();
770 _show_regions_bin_name = string();
771 _show_regions_draw_order = 0;
774 group->hide_regions();
795 Regions::const_iterator a_ri = regions_a.
begin();
796 Regions::const_iterator b_ri = regions_b.
begin();
798 while (a_ri != regions_a.
end() && b_ri != regions_b.
end()) {
799 if ((*a_ri) < (*b_ri)) {
804 }
else if ((*b_ri) < (*a_ri)) {
827 Regions::iterator ri = lower_bound(regions.
begin(), regions.
end(), ptr);
828 if (ri != regions.
end() && (*ri) == ptr) {
845 Regions::const_iterator ri = lower_bound(regions.
begin(), regions.
end(), ptr);
846 return (ri != regions.
end() && (*ri) == ptr);
856 if (pattern.empty()) {
860 if (region !=
nullptr) {
866 if (button != ButtonHandle::none()) {
876 for (
size_t p = 0; p < pattern.size(); ++p) {
877 if (pattern[p] ==
'%') {
878 string cmd = pattern.substr(p + 1, 1);
881 if (region !=
nullptr) {
882 event += region->get_name();
885 }
else if (cmd ==
"b") {
890 <<
"Invalid symbol in event_pattern: %" << cmd <<
"\n";
897 if (!event.empty()) {
911 nassertv(_lock.debug_is_locked());
917 if (_preferred_button_down_region !=
nullptr) {
918 _preferred_button_down_region->move(param);
927 nassertv(_lock.debug_is_locked());
939 _preferred_button_down_region = _preferred_region;
943 if (_preferred_button_down_region !=
nullptr) {
944 _preferred_button_down_region->press(param);
946 throw_event_pattern(_button_repeat_pattern,
947 _preferred_button_down_region, button);
949 throw_event_pattern(_button_down_pattern,
950 _preferred_button_down_region, button);
957 if (_preferred_region !=
nullptr) {
960 _preferred_region->press(param);
961 consider_keyboard_suppress(_preferred_region);
964 if ((_internal_suppress & MouseWatcherRegion::SF_other_button) == 0) {
969 global_keyboard_press(param);
979 nassertv(_lock.debug_is_locked());
993 if (_preferred_button_down_region !=
nullptr) {
994 param.
set_outside(_preferred_button_down_region != _preferred_region);
995 _preferred_button_down_region->release(param);
996 throw_event_pattern(_button_up_pattern,
997 _preferred_button_down_region, button);
1000 _button_down =
false;
1001 _preferred_button_down_region =
nullptr;
1006 if (_preferred_region !=
nullptr) {
1007 _preferred_region->release(param);
1011 global_keyboard_release(param);
1019 keystroke(
int keycode) {
1020 nassertv(_lock.debug_is_locked());
1041 consider_keyboard_suppress(region);
1047 group->sort_regions();
1053 consider_keyboard_suppress(region);
1064 candidate(
const std::wstring &candidate_string,
size_t highlight_start,
1065 size_t highlight_end,
size_t cursor_pos) {
1066 nassertv(_lock.debug_is_locked());
1069 param.
set_candidate(candidate_string, highlight_start, highlight_end, cursor_pos);
1090 group->sort_regions();
1108 nassertv(_lock.debug_is_locked());
1116 if (region != _preferred_region && region->
get_keyboard()) {
1117 region->
press(param);
1118 consider_keyboard_suppress(region);
1124 group->sort_regions();
1127 if (region != _preferred_region && region->
get_keyboard()) {
1128 region->
press(param);
1129 consider_keyboard_suppress(region);
1141 nassertv(_lock.debug_is_locked());
1149 if (region != _preferred_region && region->
get_keyboard()) {
1156 group->sort_regions();
1159 if (region != _preferred_region && region->
get_keyboard()) {
1172 nassertv(_lock.debug_is_locked());
1175 throw_event_pattern(_enter_pattern, region, ButtonHandle::none());
1176 if (_implicit_click) {
1179 region->
press(param1);
1189 nassertv(_lock.debug_is_locked());
1191 if (_implicit_click) {
1197 throw_event_pattern(_leave_pattern, region, ButtonHandle::none());
1206 nassertv(_lock.debug_is_locked());
1210 if (!_geometry.is_null()) {
1211 _geometry->set_overall_hidden(
true);
1216 clear_current_regions();
1224 set_mouse(
const LVecBase2 &xy,
const LVecBase2 &pixel_xy) {
1225 nassertv(_lock.debug_is_locked());
1227 if (!_geometry.is_null()) {
1229 _geometry->set_transform(TransformState::make_pos(LVecBase3(xy[0], 0, xy[1])));
1232 _geometry->set_overall_hidden(
false);
1238 _mouse_pixel = pixel_xy;
1241 get_over_regions(regions, _mouse);
1242 set_current_regions(regions);
1256 _external_suppress |= MouseWatcherRegion::SF_other_button;
1274 bool activity =
false;
1278 _internal_suppress = 0;
1279 _external_suppress = 0;
1283 DCAST_INTO_V(pixel_size, input.
get_data(_pixel_size_input).
get_ptr());
1284 output.
set_data(_pixel_size_output, pixel_size);
1285 _pixel_size = pixel_size;
1297 const LVecBase2 &last_f = _xy->get_value();
1303 if (_display_region !=
nullptr) {
1305 if (constrain_display_region(_display_region, f, p, current_thread)) {
1314 _internal_suppress |= MouseWatcherRegion::SF_mouse_button;
1324 _num_trail_recent = 0;
1325 if (input.
has_data(_pointer_events_input) && (_trail_log_duration > 0.0)) {
1327 DCAST_INTO_V(this_pointer_events, input.
get_data(_pointer_events_input).
get_ptr());
1329 for (
size_t i = 0; i < _num_trail_recent; i++) {
1331 int xpos = this_pointer_events->
get_xpos(i);
1332 int ypos = this_pointer_events->
get_ypos(i);
1334 double time = this_pointer_events->
get_time(i);
1335 _trail_log->add_event(in_win, xpos, ypos, sequence, time);
1338 if (_trail_log->get_num_events() > 0) {
1339 discard_excess_trail_log();
1340 update_trail_node();
1342 if (_num_trail_recent > _trail_log->get_num_events()) {
1343 _num_trail_recent = _trail_log->get_num_events();
1349 if (_preferred_region !=
nullptr) {
1350 _internal_suppress |= _preferred_region->get_suppress_flags();
1356 if (input.
has_data(_button_events_input)) {
1358 DCAST_INTO_V(this_button_events, input.
get_data(_button_events_input).
get_ptr());
1360 for (
int i = 0; i < num_events; i++) {
1365 case ButtonEvent::T_down:
1371 press(be._button,
false);
1378 case ButtonEvent::T_repeat:
1380 press(be._button,
true);
1385 case ButtonEvent::T_up:
1388 release(be._button);
1392 case ButtonEvent::T_keystroke:
1395 keystroke(be._keycode);
1399 case ButtonEvent::T_candidate:
1401 candidate(be._candidate_string, be._highlight_start, be._highlight_end, be._cursor_pos);
1405 case ButtonEvent::T_resume_down:
1411 case ButtonEvent::T_move:
1415 case ButtonEvent::T_raw_down:
1416 case ButtonEvent::T_raw_up:
1433 if (_has_inactivity_timeout) {
1439 double elapsed = now - _last_activity;
1442 if (elapsed > _inactivity_timeout) {
1443 switch (_inactivity_state) {
1445 _inactivity_state = IS_active_to_inactive;
1451 case IS_active_to_inactive:
1454 case IS_inactive_to_active:
1455 _inactivity_state = IS_inactive;
1462 switch (_inactivity_state) {
1467 case IS_active_to_inactive:
1469 if (tform_cat.is_debug()) {
1471 <<
"MouseWatcher detected " << _inactivity_timeout
1472 <<
" seconds of inactivity; releasing held buttons.\n";
1475 for (
size_t i = 0; i < _current_buttons_down.
get_num_bits(); ++i) {
1476 if (_current_buttons_down.
get_bit(i)) {
1482 _inactivity_state = IS_inactive;
1483 throw_event(_inactivity_timeout_event);
1486 case IS_inactive_to_active:
1489 for (
size_t i = 0; i < _current_buttons_down.
get_num_bits(); ++i) {
1490 if (_current_buttons_down.
get_bit(i)) {
1496 _inactivity_state = IS_active;
1501 (_internal_suppress & MouseWatcherRegion::SF_mouse_position) == 0) {
1503 _xy->set_value(_mouse);
1505 _pixel_xy->set_value(_mouse_pixel);
1510 int suppress_buttons = ((_internal_suppress | _external_suppress) & MouseWatcherRegion::SF_any_button);
1512 _button_events->clear();
1515 for (
int i = 0; i < num_events; i++) {
1517 bool suppress =
true;
1519 if (be._type != ButtonEvent::T_keystroke &&
1521 suppress = ((suppress_buttons & MouseWatcherRegion::SF_mouse_button) != 0);
1523 suppress = ((suppress_buttons & MouseWatcherRegion::SF_other_button) != 0);
1526 if (!suppress || be._type == ButtonEvent::T_up) {
1528 _button_events->add_event(be);
1532 if (_button_events->get_num_events() != 0) {
1545 LVecBase2 &f, LVecBase2 &p,
1546 Thread *current_thread) {
1547 if (!_button_down) {
1548 _button_down_display_region =
nullptr;
1550 if (_button_down_display_region !=
nullptr) {
1554 display_region = _button_down_display_region;
1561 DCAST_INTO_R(stereo_display_region, display_region,
false);
1562 return constrain_display_region(stereo_display_region->
get_left_eye(), f, p, current_thread) ||
1563 constrain_display_region(stereo_display_region->
get_right_eye(), f, p, current_thread);
1568 PN_stdfloat left, right, bottom, top;
1569 dr_reader.get_dimensions(left, right, bottom, top);
1572 PN_stdfloat x = (f[0] + 1.0f) / 2.0f;
1573 PN_stdfloat y = (f[1] + 1.0f) / 2.0f;
1575 if (_button_down_display_region ==
nullptr &&
1576 (x < left || x >= right || y < bottom || y >= top)) {
1583 _button_down_display_region = display_region;
1587 PN_stdfloat xp = (x - left) / (right - left);
1589 PN_stdfloat xpp = (xp * 2.0f) - 1.0f;
1591 PN_stdfloat yp = (y - bottom) / (top - bottom);
1592 PN_stdfloat ypp = (yp * 2.0f) - 1.0f;
1598 p.set(p[0] - xo, p[1] - yo);