FlightGear next
FGMouseInput.cxx
Go to the documentation of this file.
1// FGMouseInput.cxx -- handle user input from mouse devices
2//
3// Written by Torsten Dreyer, started August 2009
4// Based on work from David Megginson, started May 2001.
5//
6// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
7// Copyright (C) 2001 David Megginson, david@megginson.com
8//
9// This program is free software; you can redistribute it and/or
10// modify it under the terms of the GNU General Public License as
11// published by the Free Software Foundation; either version 2 of the
12// License, or (at your option) any later version.
13//
14// This program is distributed in the hope that it will be useful, but
15// WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17// General Public License for more details.
18//
19// You should have received a copy of the GNU General Public License
20// along with this program; if not, write to the Free Software
21// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22//
23// $Id$
24
25#ifdef HAVE_CONFIG_H
26# include "config.h"
27#endif
28
29#include <memory>
30
31#include "FGMouseInput.hxx"
32
33#include <osgGA/GUIEventAdapter>
34
35#include <simgear/scene/util/SGPickCallback.hxx>
36#include <simgear/timing/timestamp.hxx>
37#include <simgear/scene/model/SGPickAnimation.hxx>
38
39#include "FGButton.hxx"
40#include <Main/globals.hxx>
41#include <Main/fg_props.hxx>
42#include <Viewer/renderer.hxx>
43#include <Viewer/sview.hxx>
45#include <GUI/MouseCursor.hxx>
46
47using std::ios_base;
48
49const int MAX_MICE = 1;
50const int MAX_MOUSE_BUTTONS = 8;
51
52typedef std::vector<SGSceneryPick> SGSceneryPicks;
53typedef SGSharedPtr<SGPickCallback> SGPickCallbackPtr;
54typedef std::list<SGPickCallbackPtr> SGPickCallbackList;
55
57
62 public std::map<int, SGPickCallbackList>
63{
64 public:
65 void update( double dt, unsigned int keyModState );
66 void init( int button, const osgGA::GUIEventAdapter* ea );
67};
68
69
70void ActivePickCallbacks::init( int button, const osgGA::GUIEventAdapter* ea )
71{
72 osg::Vec2d windowPos;
73 flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
74
75 // Get the list of hit callbacks. Take the first callback that
76 // accepts the mouse button press and ignore the rest of them
77 // That is they get sorted by distance and by scenegraph depth.
78 // The nearest one is the first one and the deepest
79 // (the most specialized one in the scenegraph) is the first.
80 SGSceneryPicks pickList = globals->get_renderer()->pick(windowPos);
81 if (pickList.empty()) {
82 return;
83 }
84
85 SGSceneryPicks::const_iterator i;
86 for (i = pickList.begin(); i != pickList.end(); ++i) {
87 if (i->callback->buttonPressed(button, *ea, i->info)) {
88 (*this)[button].push_back(i->callback);
89 return;
90 }
91 }
92}
93
94void ActivePickCallbacks::update( double dt, unsigned int keyModState )
95{
96 // handle repeatable mouse press events
97 for( iterator mi = begin(); mi != end(); ++mi ) {
98 SGPickCallbackList::iterator li;
99 for (li = mi->second.begin(); li != mi->second.end(); ++li) {
100 (*li)->update(dt, keyModState);
101 }
102 }
103}
104
106
107
112 mouse_mode ();
113
117 std::unique_ptr<FGButton[]> buttons;
118 SGBindingList x_bindings[KEYMOD_MAX];
119 SGBindingList y_bindings[KEYMOD_MAX];
120};
121
122
126struct mouse {
127 mouse ();
128
129 int x, y;
130 SGPropertyNode_ptr mode_node;
134
135 SGTimeStamp timeSinceLastMove;
136 std::unique_ptr<mouse_mode[]> modes;
137};
138
139static
140const SGSceneryPick*
141getPick( const SGSceneryPicks& pick_list,
142 const SGPickCallback* cb )
143{
144 for(size_t i = 0; i < pick_list.size(); ++i)
145 if( pick_list[i].callback == cb )
146 return &pick_list[i];
147
148 return 0;
149}
150
152
153class FGMouseInput::FGMouseInputPrivate : public SGPropertyChangeListener
154{
155public:
157 haveWarped(false),
158 xSizeNode(fgGetNode("/sim/startup/xsize", false ) ),
159 ySizeNode(fgGetNode("/sim/startup/ysize", false ) ),
160 xAccelNode(fgGetNode("/devices/status/mice/mouse/accel-x", true ) ),
161 yAccelNode(fgGetNode("/devices/status/mice/mouse/accel-y", true ) ),
162 mouseXNode(fgGetNode("/devices/status/mice/mouse/x", true)),
163 mouseYNode(fgGetNode("/devices/status/mice/mouse/y", true))
164 {
165 tooltipTimeoutDone = false;
166 hoverPickScheduled = false;
167
168 fgGetNode("/sim/mouse/hide-cursor", true )->addChangeListener(this, true);
169 fgGetNode("/sim/mouse/cursor-timeout-sec", true )->addChangeListener(this, true);
170 fgGetNode("/sim/mouse/right-button-mode-cycle-enabled", true)->addChangeListener(this, true);
171 fgGetNode("/sim/mouse/tooltip-delay-msec", true)->addChangeListener(this, true);
172 fgGetNode("/sim/mouse/click-shows-tooltip", true)->addChangeListener(this, true);
173 fgGetNode("/sim/mouse/tooltips-enabled", true)->addChangeListener(this, true);
174 fgGetNode("/sim/mouse/tooltip-commands-registered", true)->addChangeListener(this, true);
175 fgGetNode("/sim/mouse/drag-sensitivity", true)->addChangeListener(this, true);
176 fgGetNode("/sim/mouse/invert-mouse-wheel", true)->addChangeListener(this, true);
177 }
178
180 {
182 }
183
185 {
186 // center the cursor
187 m.x = (xSizeNode ? xSizeNode->getIntValue() : 800) / 2;
188 m.y = (ySizeNode ? ySizeNode->getIntValue() : 600) / 2;
189 fgWarpMouse(m.x, m.y);
190 haveWarped = true;
191 }
192
193 void constrainMouse(int x, int y)
194 {
195 int new_x=x,new_y=y;
196 int xsize = xSizeNode ? xSizeNode->getIntValue() : 800;
197 int ysize = ySizeNode ? ySizeNode->getIntValue() : 600;
198
199 bool need_warp = false;
200 if (x <= (xsize * .25) || x >= (xsize * .75)) {
201 new_x = int(xsize * .5);
202 need_warp = true;
203 }
204
205 if (y <= (ysize * .25) || y >= (ysize * .75)) {
206 new_y = int(ysize * .5);
207 need_warp = true;
208 }
209
210 if (need_warp)
211 {
212 fgWarpMouse(new_x, new_y);
213 haveWarped = true;
214 }
215 }
216
217 void scheduleHoverPick(const osg::Vec2d& windowPos)
218 {
219 hoverPickScheduled = true;
220 hoverPos = windowPos;
221 }
222
223 void doHoverPick(const osg::Vec2d& windowPos)
224 {
226 bool explicitCursor = false;
227 bool didPick = false;
228
229 SGPickCallback::Priority priority = SGPickCallback::PriorityScenery;
230 SGSceneryPicks pickList = globals->get_renderer()->pick(windowPos);
231
232 SGSceneryPicks::const_iterator i;
233 for( i = pickList.begin(); i != pickList.end(); ++i )
234 {
235 bool done = i->callback->hover(windowPos, i->info);
236 std::string curName(i->callback->getCursor());
237 if (!curName.empty()) {
238 explicitCursor = true;
239 cur = FGMouseCursor::cursorFromString(curName.c_str());
240 }
241
242 // if the callback is of higher prioirty (lower enum index),
243 // record that.
244 if (i->callback->getPriority() < priority) {
245 priority = i->callback->getPriority();
246 }
247
248 if (done) {
249 didPick = true;
250 break;
251 }
252 } // of picks iteration
253
254 // Check if any pick from the previous iteration has disappeared. If so
255 // notify the callback that the mouse has left its element.
256 for( i = _previous_picks.begin(); i != _previous_picks.end(); ++i )
257 {
258 if( !getPick(pickList, i->callback) )
259 i->callback->mouseLeave(windowPos);
260 }
261 _previous_picks = pickList;
262
263 if (!explicitCursor && (priority == SGPickCallback::PriorityPanel)) {
265 }
266
268 if (!didPick && areTooltipsEnabled()) {
269 SGPropertyNode_ptr args(new SGPropertyNode);
270 globals->get_commands()->execute("update-hover", args, nullptr);
271 }
272 }
273
274 void doMouseMoveWithCallbacks(const osgGA::GUIEventAdapter* ea)
275 {
277
278 osg::Vec2d windowPos;
279 const bool ok = flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
280 if (!ok) {
281 SG_LOG(SG_GUI, SG_WARN, "doMouseMoveWithCallbacks: ignoring mouse move with missing context/traits");
282 return;
283 }
284
285 // Do not compute scenery picks unless a callback requests it, as it is costly.
286 SGSceneryPicks pickList;
287 bool did_pick = false;
288
289 for( ActivePickCallbacks::iterator mi = activePickCallbacks.begin();
290 mi != activePickCallbacks.end();
291 ++mi )
292 {
293 SGPickCallbackList::iterator li;
294 for( li = mi->second.begin(); li != mi->second.end(); ++li )
295 {
296 if (!did_pick && (*li)->needsDragPosition())
297 {
298 pickList = globals->get_renderer()->pick(windowPos);
299 did_pick = true;
300 }
301
302 const SGSceneryPick* pick = getPick(pickList, *li);
303 (*li)->mouseMoved(*ea, pick ? &pick->info : 0);
304
305 std::string curName((*li)->getCursor());
306 if( !curName.empty() )
307 cur = FGMouseCursor::cursorFromString(curName.c_str());
308 }
309 }
310
312 }
313
314 // implement the property-change-listener interfacee
315 void valueChanged(SGPropertyNode* node) override
316 {
317 if (node->getNameString() == "drag-sensitivity") {
318 SGKnobAnimation::setDragSensitivity(node->getDoubleValue());
319 } else if (node->getNameString() == "invert-mouse-wheel") {
320 SGKnobAnimation::setAlternateMouseWheelDirection(node->getBoolValue());
321 } else if (node->getNameString() == "hide-cursor") {
322 hideCursor = node->getBoolValue();
323 } else if (node->getNameString() == "cursor-timeout-sec") {
324 cursorTimeoutMsec = node->getDoubleValue() * 1000;
325 } else if (node->getNameString() == "tooltip-delay-msec") {
326 tooltipDelayMsec = node->getIntValue();
327 } else if (node->getNameString() == "right-button-mode-cycle-enabled") {
328 rightClickModeCycle = node->getBoolValue();
329 } else if (node->getNameString() == "click-shows-tooltip") {
330 clickTriggersTooltip = node->getBoolValue();
331 } else if (node->getNameString() == "tooltips-enabled") {
332 _tooltipsEnabled = node->getBoolValue();
333 } else if (node->getNameString() == "tooltip-commands-registered") {
334 _tooltipsCommandsRegistered = node->getBoolValue();
335 }
336 }
337
340
342
348
349 bool _tooltipsEnabled = false;
351
352 SGPropertyNode_ptr xSizeNode;
353 SGPropertyNode_ptr ySizeNode;
354 SGPropertyNode_ptr xAccelNode;
355 SGPropertyNode_ptr yAccelNode;
356 SGPropertyNode_ptr mouseXNode, mouseYNode;
357
359 osg::Vec2d hoverPos;
360};
361
362
364// The Mouse Input Implementation
366
368
369static void mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
370{
372 global_mouseInput->doMouseClick(button, updown, x, y, mainWindow, ea);
373}
374
375static void mouseMotionHandler(int x, int y, const osgGA::GUIEventAdapter* ea)
376{
377 if (global_mouseInput != 0)
378 global_mouseInput->doMouseMotion(x, y, ea);
379}
380
382
384{
385 SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse bindings");
386
387 d.reset(new FGMouseInputPrivate());
388 std::string module = "";
389
390 SGPropertyNode * mouse_nodes = fgGetNode("/input/mice");
391 if (mouse_nodes == 0) {
392 SG_LOG(SG_INPUT, SG_WARN, "No mouse bindings (/input/mice)!!");
393 mouse_nodes = fgGetNode("/input/mice", true);
394 }
395
396 int j;
397 for (int i = 0; i < MAX_MICE; i++) {
398 SGPropertyNode * mouse_node = mouse_nodes->getChild("mouse", i, true);
399 mouse &m = d->mice[i];
400
401 // Grab node pointers
402 std::ostringstream buf;
403 buf << "/devices/status/mice/mouse[" << i << "]/mode";
404 m.mode_node = fgGetNode(buf.str().c_str());
405 if (m.mode_node == NULL) {
406 m.mode_node = fgGetNode(buf.str().c_str(), true);
407 m.mode_node->setIntValue(0);
408 }
409 for (j = 0; j < MAX_MOUSE_BUTTONS; j++) {
410 // According to <https://stackoverflow.com/a/12112642/4756009> and other
411 // similar questions on stackoverflow.com, it seems safer not to try to
412 // reuse the 'buf' variable we have above.
413 std::ostringstream buf;
414 buf << "/devices/status/mice/mouse["<< i << "]/button[" << j << "]";
415 m.mouse_button_nodes[j] = fgGetNode(buf.str().c_str(), true);
416 m.mouse_button_nodes[j]->setBoolValue(false);
417 }
418
419 // Read all the modes
420 m.nModes = mouse_node->getIntValue("mode-count", 1);
421 m.modes.reset(new mouse_mode[m.nModes]);
422
423 for (int j = 0; j < m.nModes; j++) {
424 int k;
425 SGPropertyNode * mode_node = mouse_node->getChild("mode", j, true);
426
427 // Read the mouse cursor for this mode
428 m.modes[j].cursor = FGMouseCursor::cursorFromString(mode_node->getStringValue("cursor", "inherit").c_str());
429
430 // Read other properties for this mode
431 m.modes[j].constrained = mode_node->getBoolValue("constrained", false);
432 m.modes[j].pass_through = mode_node->getBoolValue("pass-through", false);
433
434 // Read the button bindings for this mode
435 m.modes[j].buttons.reset(new FGButton[MAX_MOUSE_BUTTONS]);
436 for (k = 0; k < MAX_MOUSE_BUTTONS; k++) {
437 std::ostringstream buf;
438 buf << "mouse button " << k;
439 m.modes[j].buttons[k].init( mode_node->getChild("button", k), buf.str(), module );
440 }
441
442 // Read the axis bindings for this mode
443 read_bindings(mode_node->getChild("x-axis", 0, true), m.modes[j].x_bindings, KEYMOD_NONE, module );
444 read_bindings(mode_node->getChild("y-axis", 0, true), m.modes[j].y_bindings, KEYMOD_NONE, module );
445
446 if (mode_node->hasChild("x-axis-ctrl")) {
447 read_bindings(mode_node->getChild("x-axis-ctrl"), m.modes[j].x_bindings, KEYMOD_CTRL, module );
448 }
449 if (mode_node->hasChild("x-axis-shift")) {
450 read_bindings(mode_node->getChild("x-axis-shift"), m.modes[j].x_bindings, KEYMOD_SHIFT, module );
451 }
452 if (mode_node->hasChild("x-axis-ctrl-shift")) {
453 read_bindings(mode_node->getChild("x-axis-ctrl-shift"), m.modes[j].x_bindings, KEYMOD_CTRL|KEYMOD_SHIFT, module );
454 }
455
456 if (mode_node->hasChild("y-axis-ctrl")) {
457 read_bindings(mode_node->getChild("y-axis-ctrl"), m.modes[j].y_bindings, KEYMOD_CTRL, module );
458 }
459 if (mode_node->hasChild("y-axis-shift")) {
460 read_bindings(mode_node->getChild("y-axis-shift"), m.modes[j].y_bindings, KEYMOD_SHIFT, module );
461 }
462 if (mode_node->hasChild("y-axis-ctrl-shift")) {
463 read_bindings(mode_node->getChild("y-axis-ctrl-shift"), m.modes[j].y_bindings, KEYMOD_CTRL|KEYMOD_SHIFT, module );
464 }
465 } // of modes iteration
466 }
467
470 global_mouseInput = this;
471}
472
474{
475 SG_LOG(SG_INPUT, SG_DEBUG, "Shutting down mouse bindings");
476
477 // This ensures that mouseClickHandler and mouseMotionHandler are no-ops.
478 global_mouseInput = nullptr;
479 // Reset the Pimpl
480 d.reset();
481}
482
484{
485 shutdown();
486 init();
487}
488
489void FGMouseInput::update ( double dt )
490{
491 if (!d) {
492 SG_LOG(SG_INPUT, SG_WARN, "update of mouse before init");
493 }
494
495 mouse &m = d->mice[0];
496 int mode = m.mode_node->getIntValue();
497 if (mode != m.current_mode) {
498 // current mode has changed
499 m.current_mode = mode;
500 m.timeSinceLastMove.stamp();
501
502 if (mode >= 0 && mode < m.nModes) {
503 FGMouseCursor::instance()->setCursor(m.modes[mode].cursor);
504 d->centerMouseCursor(m);
505 } else {
506 SG_LOG(SG_INPUT, SG_WARN, "Mouse mode " << mode << " out of range");
508 }
509 }
510
511 if ((mode == 0) && d->hoverPickScheduled) {
512 d->doHoverPick(d->hoverPos);
513 d->hoverPickScheduled = false;
514 }
515
516 if (!d->tooltipTimeoutDone &&
517 d->areTooltipsEnabled() &&
518 (m.timeSinceLastMove.elapsedMSec() > d->tooltipDelayMsec)) {
519 d->tooltipTimeoutDone = true;
520 SGPropertyNode_ptr arg(new SGPropertyNode);
521 globals->get_commands()->execute("tooltip-timeout", arg, nullptr);
522 }
523
524 if ( d->hideCursor ) {
525 if ( m.timeSinceLastMove.elapsedMSec() > d->cursorTimeoutMsec) {
527 m.timeSinceLastMove.stamp();
528 }
529 }
530
531 d->activePickCallbacks.update( dt, fgGetKeyModifiers() );
532}
533
535 : x(-1),
536 y(-1),
537 nModes(1),
538 current_mode(0),
539 modes()
540{
541}
542
544 : cursor(FGMouseCursor::CURSOR_ARROW),
545 constrained(false),
546 pass_through(false),
547 buttons()
548{
549}
550
551void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
552{
553 if (!d) {
554 // can occur during reset
555 return;
556 }
557
558 int modifiers = fgGetKeyModifiers();
559
560 mouse &m = d->mice[0];
561 mouse_mode &mode = m.modes[m.current_mode];
562 // Let the property manager know.
563 if (b >= 0 && b < MAX_MOUSE_BUTTONS)
564 m.mouse_button_nodes[b]->setBoolValue(updown == MOUSE_BUTTON_DOWN);
565
566 if (!d->rightClickModeCycle && (b == 2)) {
567 // in spring-loaded look mode, ignore right clicks entirely here
568 return;
569 }
570
571
572 // Pass on to PUI and the panel if
573 // requested, and return if one of
574 // them consumes the event.
575
576 osg::Vec2d windowPos;
577 bool ok = flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
578 if (!ok) {
579 SG_LOG(SG_GUI, SG_WARN, "Ignoring mouse click with null context/traits");
580 return;
581 }
582
583 SGSceneryPicks pickList;
584
585 if (isRightDragLookActive() && (updown == MOUSE_BUTTON_DOWN)) {
586 // when spring-loaded mode is active, don't do scene selection for picks
587 // https://sourceforge.net/p/flightgear/codetickets/2108/
588 } else {
589 pickList = globals->get_renderer()->pick(windowPos);
590 }
591
592 if( updown == MOUSE_BUTTON_UP )
593 {
594 // Execute the mouse up event in any case, may be we should
595 // stop processing here?
596
597 SGPickCallbackList& callbacks = d->activePickCallbacks[b];
598
599 while( !callbacks.empty() )
600 {
601 SGPickCallbackPtr& cb = callbacks.front();
602 const SGSceneryPick* pick = getPick(pickList, cb);
603 cb->buttonReleased(ea->getModKeyMask(), *ea, pick ? &pick->info : nullptr);
604
605 callbacks.pop_front();
606 }
607
608 if (ea->getHandled()) {
609 // for https://sourceforge.net/p/flightgear/codetickets/2347/
610 // we cleared the active picks, but don't do further processing
611 return;
612 }
613 }
614
615 if (mode.pass_through) {
616 // compute a
617 // scenegraph intersection point corresponding to the mouse click
618 if (updown == MOUSE_BUTTON_DOWN) {
619 d->activePickCallbacks.init( b, ea );
620
621 if (d->clickTriggersTooltip && d->areTooltipsEnabled()) {
622 SGPropertyNode_ptr args(new SGPropertyNode);
623 args->setStringValue("reason", "click");
624 globals->get_commands()->execute("tooltip-timeout", args, nullptr);
625 d->tooltipTimeoutDone = true;
626 }
627 } else {
628 // do a hover pick now, to fix up cursor
629 d->doHoverPick(windowPos);
630 } // mouse button was released
631 } // of pass-through mode
632
633 if (b >= MAX_MOUSE_BUTTONS) {
634 SG_LOG(SG_INPUT, SG_ALERT, "Mouse button " << b
635 << " where only " << MAX_MOUSE_BUTTONS << " expected");
636 return;
637 }
638
639 m.modes[m.current_mode].buttons[b].update( modifiers, 0 != updown, x, y);
640}
641
642void FGMouseInput::processMotion(int x, int y, const osgGA::GUIEventAdapter* ea)
643{
644 if (!d->activePickCallbacks[0].empty()) {
645 d->doMouseMoveWithCallbacks(ea);
646 return;
647 }
648
649 if (SviewMouseMotion(x, y, *ea)) {
650 return;
651 }
652
653 mouse &m = d->mice[0];
654 int modeIndex = m.current_mode;
655
656 if (isRightDragLookActive()) {
657 // right mouse is down, force look mode
658 modeIndex = 3;
659 }
660
661 if (modeIndex == 0) {
662 osg::Vec2d windowPos;
663 flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
664
665 // omly do hover picks if no buttons are down
666 if (ea->getButtonMask() == 0) {
667 d->scheduleHoverPick(windowPos);
668 }
669
670 // mouse has moved, so we may need to issue tooltip-timeout command again
671 d->tooltipTimeoutDone = false;
672 }
673
674 mouse_mode &mode = m.modes[modeIndex];
675
676 if (d->haveWarped)
677 {
678 // don't fire mouse-movement events at the first update after warping the mouse,
679 // just remember the new mouse position
680 d->haveWarped = false;
681 }
682 else
683 {
684 int modifiers = fgGetKeyModifiers();
685 int xsize = d->xSizeNode ? d->xSizeNode->getIntValue() : 800;
686 int ysize = d->ySizeNode ? d->ySizeNode->getIntValue() : 600;
687
688 // OK, PUI didn't want the event,
689 // so we can play with it.
690 if (x != m.x) {
691 int delta = x - m.x;
692 d->xAccelNode->setIntValue( delta );
693 for (unsigned int i = 0; i < mode.x_bindings[modifiers].size(); i++)
694 mode.x_bindings[modifiers][i]->fire(double(delta), double(xsize));
695 }
696 if (y != m.y) {
697 int delta = y - m.y;
698 d->yAccelNode->setIntValue( -delta );
699 for (unsigned int i = 0; i < mode.y_bindings[modifiers].size(); i++)
700 mode.y_bindings[modifiers][i]->fire(double(delta), double(ysize));
701 }
702 }
703
704 // Constrain the mouse if requested
705 if (mode.constrained) {
706 d->constrainMouse(x, y);
707 }
708}
709
710void FGMouseInput::doMouseMotion (int x, int y, const osgGA::GUIEventAdapter* ea)
711{
712 if (!d) {
713 // can occur during reset
714 return;
715 }
716
717 mouse &m = d->mice[0];
718
719 if (m.current_mode < 0 || m.current_mode >= m.nModes) {
720 m.x = x;
721 m.y = y;
722 return;
723 }
724
725 m.timeSinceLastMove.stamp();
727
728 // TODO Get rid of this as soon as soon as cursor hide timeout works globally
729
730 if (!ea->getHandled()) {
731 processMotion(x, y, ea);
732 }
733
734 m.x = x;
735 m.y = y;
736 d->mouseXNode->setIntValue(x);
737 d->mouseYNode->setIntValue(y);
738}
739
741{
742 if (!d) {
743 return false;
744 }
745
746 return (d->rightClickModeCycle == false);
747}
748
750{
751 if (!d) {
752 return false;
753 }
754
755 mouse &m = d->mice[0];
756 int mode = m.current_mode;
757 if (isRightDragToLookEnabled() && m.mouse_button_nodes[2]->getBoolValue()) {
758 mode = 3;
759 }
760
761 return m.modes[mode].pass_through;
762}
763
764bool FGMouseInput::isRightDragLookActive() const
765{
766 if (!d) {
767 return false;
768 }
769
770 const auto& m = d->mice[0];
771 if (!d->rightClickModeCycle && m.nModes > 3) {
772 return m.mouse_button_nodes[2]->getBoolValue();
773 }
774
775 return false;
776}
777
778
779// Register the subsystem.
780SGSubsystemMgr::Registrant<FGMouseInput> registrantFGMouseInput;
static const SGSceneryPick * getPick(const SGSceneryPicks &pick_list, const SGPickCallback *cb)
static void mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter *ea)
std::list< SGPickCallbackPtr > SGPickCallbackList
const int MAX_MICE
std::vector< SGSceneryPick > SGSceneryPicks
const int MAX_MOUSE_BUTTONS
SGSharedPtr< SGPickCallback > SGPickCallbackPtr
static FGMouseInput * global_mouseInput
SGSubsystemMgr::Registrant< FGMouseInput > registrantFGMouseInput
static void mouseMotionHandler(int x, int y, const osgGA::GUIEventAdapter *ea)
#define i(x)
List of currently pressed mouse button events.
void update(double dt, unsigned int keyModState)
void init(int button, const osgGA::GUIEventAdapter *ea)
static void read_bindings(const SGPropertyNode *base, binding_list_t *binding_list, int modifiers, const std::string &module)
virtual void mouseMoved()=0
virtual void setCursor(Cursor aCursor)=0
@ CURSOR_HAND
the browser 'link' cursor
virtual void hideCursorUntilMouseMove()=0
static FGMouseCursor * instance()
static Cursor cursorFromString(const char *str)
ActivePickCallbacks activePickCallbacks
void doMouseMoveWithCallbacks(const osgGA::GUIEventAdapter *ea)
void valueChanged(SGPropertyNode *node) override
bool _tooltipsCommandsRegistered
avoid errors if the mouse moves before Nasal init
void doHoverPick(const osg::Vec2d &windowPos)
void scheduleHoverPick(const osg::Vec2d &windowPos)
void doMouseMotion(int x, int y, const osgGA::GUIEventAdapter *)
void reinit() override
void update(double dt) override
bool isActiveModePassThrough() const
check if the active mode passes clicks through to the UI or not
bool isRightDragToLookEnabled() const
isRightDragToLookEnabled - test if we're in right-mouse-drag to adjust the view direction/position mo...
void shutdown() override
void doMouseClick(int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter *ea)
void init() override
void fgRegisterMouseMotionHandler(fgMouseMotionHandler func)
@ KEYMOD_CTRL
Definition fg_os.hxx:27
@ KEYMOD_NONE
Definition fg_os.hxx:24
@ KEYMOD_MAX
Definition fg_os.hxx:32
@ KEYMOD_SHIFT
Definition fg_os.hxx:26
@ MOUSE_BUTTON_DOWN
Definition fg_os.hxx:21
@ MOUSE_BUTTON_UP
Definition fg_os.hxx:22
void fgWarpMouse(int x, int y)
int fgGetKeyModifiers()
void fgRegisterMouseClickHandler(fgMouseClickHandler func)
FGGlobals * globals
Definition globals.cxx:142
bool eventToWindowCoords(const osgGA::GUIEventAdapter *ea, double &x, double &y)
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
Settings for a mouse mode.
SGBindingList x_bindings[KEYMOD_MAX]
SGBindingList y_bindings[KEYMOD_MAX]
FGMouseCursor::Cursor cursor
std::unique_ptr< FGButton[]> buttons
Settings for a mouse.
SGPropertyNode_ptr mouse_button_nodes[MAX_MOUSE_BUTTONS]
SGTimeStamp timeSinceLastMove
std::unique_ptr< mouse_mode[]> modes
int current_mode
SGPropertyNode_ptr mode_node
bool SviewMouseMotion(int x, int y, const osgGA::GUIEventAdapter &ea)
Definition sview.cxx:1849