33#include <osgGA/GUIEventAdapter>
35#include <simgear/scene/util/SGPickCallback.hxx>
36#include <simgear/timing/timestamp.hxx>
37#include <simgear/scene/model/SGPickAnimation.hxx>
62 public std::map<int, SGPickCallbackList>
65 void update(
double dt,
unsigned int keyModState );
66 void init(
int button,
const osgGA::GUIEventAdapter* ea );
81 if (pickList.empty()) {
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);
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);
136 std::unique_ptr<mouse_mode[]>
modes;
142 const SGPickCallback* cb )
144 for(
size_t i = 0;
i < pick_list.size(); ++
i)
145 if( pick_list[
i].callback == cb )
146 return &pick_list[
i];
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);
199 bool need_warp =
false;
200 if (x <= (xsize * .25) || x >= (xsize * .75)) {
201 new_x = int(xsize * .5);
205 if (y <= (ysize * .25) || y >= (ysize * .75)) {
206 new_y = int(ysize * .5);
226 bool explicitCursor =
false;
227 bool didPick =
false;
229 SGPickCallback::Priority priority = SGPickCallback::PriorityScenery;
232 SGSceneryPicks::const_iterator
i;
233 for(
i = pickList.begin();
i != pickList.end(); ++
i )
235 bool done =
i->callback->hover(windowPos,
i->info);
236 std::string curName(
i->callback->getCursor());
237 if (!curName.empty()) {
238 explicitCursor =
true;
244 if (
i->callback->getPriority() < priority) {
245 priority =
i->callback->getPriority();
258 if( !
getPick(pickList,
i->callback) )
259 i->callback->mouseLeave(windowPos);
263 if (!explicitCursor && (priority == SGPickCallback::PriorityPanel)) {
269 SGPropertyNode_ptr args(
new SGPropertyNode);
270 globals->get_commands()->execute(
"update-hover", args,
nullptr);
278 osg::Vec2d windowPos;
281 SG_LOG(SG_GUI, SG_WARN,
"doMouseMoveWithCallbacks: ignoring mouse move with missing context/traits");
287 bool did_pick =
false;
293 SGPickCallbackList::iterator li;
294 for( li = mi->second.begin(); li != mi->second.end(); ++li )
296 if (!did_pick && (*li)->needsDragPosition())
298 pickList =
globals->get_renderer()->pick(windowPos);
302 const SGSceneryPick* pick =
getPick(pickList, *li);
303 (*li)->mouseMoved(*ea, pick ? &pick->info : 0);
305 std::string curName((*li)->getCursor());
306 if( !curName.empty() )
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") {
323 }
else if (node->getNameString() ==
"cursor-timeout-sec") {
325 }
else if (node->getNameString() ==
"tooltip-delay-msec") {
327 }
else if (node->getNameString() ==
"right-button-mode-cycle-enabled") {
329 }
else if (node->getNameString() ==
"click-shows-tooltip") {
331 }
else if (node->getNameString() ==
"tooltips-enabled") {
333 }
else if (node->getNameString() ==
"tooltip-commands-registered") {
369static void mouseClickHandler(
int button,
int updown,
int x,
int y,
bool mainWindow,
const osgGA::GUIEventAdapter* ea)
385 SG_LOG(SG_INPUT, SG_DEBUG,
"Initializing mouse bindings");
388 std::string module =
"";
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);
398 SGPropertyNode * mouse_node = mouse_nodes->getChild(
"mouse",
i,
true);
402 std::ostringstream buf;
403 buf <<
"/devices/status/mice/mouse[" <<
i <<
"]/mode";
413 std::ostringstream buf;
414 buf <<
"/devices/status/mice/mouse["<<
i <<
"]/button[" << j <<
"]";
420 m.
nModes = mouse_node->getIntValue(
"mode-count", 1);
423 for (
int j = 0; j < m.
nModes; j++) {
425 SGPropertyNode * mode_node = mouse_node->getChild(
"mode", j,
true);
431 m.
modes[j].constrained = mode_node->getBoolValue(
"constrained",
false);
432 m.
modes[j].pass_through = mode_node->getBoolValue(
"pass-through",
false);
437 std::ostringstream buf;
438 buf <<
"mouse button " << k;
439 m.
modes[j].buttons[k].init( mode_node->getChild(
"button", k), buf.str(), module );
446 if (mode_node->hasChild(
"x-axis-ctrl")) {
449 if (mode_node->hasChild(
"x-axis-shift")) {
452 if (mode_node->hasChild(
"x-axis-ctrl-shift")) {
456 if (mode_node->hasChild(
"y-axis-ctrl")) {
459 if (mode_node->hasChild(
"y-axis-shift")) {
462 if (mode_node->hasChild(
"y-axis-ctrl-shift")) {
475 SG_LOG(SG_INPUT, SG_DEBUG,
"Shutting down mouse bindings");
492 SG_LOG(SG_INPUT, SG_WARN,
"update of mouse before init");
495 mouse &m = d->mice[0];
502 if (mode >= 0 && mode < m.
nModes) {
504 d->centerMouseCursor(m);
506 SG_LOG(SG_INPUT, SG_WARN,
"Mouse mode " << mode <<
" out of range");
511 if ((mode == 0) && d->hoverPickScheduled) {
512 d->doHoverPick(d->hoverPos);
513 d->hoverPickScheduled =
false;
516 if (!d->tooltipTimeoutDone &&
517 d->areTooltipsEnabled() &&
519 d->tooltipTimeoutDone =
true;
520 SGPropertyNode_ptr arg(
new SGPropertyNode);
521 globals->get_commands()->execute(
"tooltip-timeout", arg,
nullptr);
524 if ( d->hideCursor ) {
560 mouse &m = d->mice[0];
566 if (!d->rightClickModeCycle && (b == 2)) {
576 osg::Vec2d windowPos;
579 SG_LOG(SG_GUI, SG_WARN,
"Ignoring mouse click with null context/traits");
589 pickList =
globals->get_renderer()->pick(windowPos);
599 while( !callbacks.empty() )
602 const SGSceneryPick* pick =
getPick(pickList, cb);
603 cb->buttonReleased(ea->getModKeyMask(), *ea, pick ? &pick->info :
nullptr);
605 callbacks.pop_front();
608 if (ea->getHandled()) {
619 d->activePickCallbacks.init( b, ea );
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;
629 d->doHoverPick(windowPos);
634 SG_LOG(SG_INPUT, SG_ALERT,
"Mouse button " << b
642void FGMouseInput::processMotion(
int x,
int y,
const osgGA::GUIEventAdapter* ea)
644 if (!d->activePickCallbacks[0].empty()) {
645 d->doMouseMoveWithCallbacks(ea);
653 mouse &m = d->mice[0];
656 if (isRightDragLookActive()) {
661 if (modeIndex == 0) {
662 osg::Vec2d windowPos;
666 if (ea->getButtonMask() == 0) {
667 d->scheduleHoverPick(windowPos);
671 d->tooltipTimeoutDone =
false;
674 mouse_mode &mode = m.
modes[modeIndex];
680 d->haveWarped =
false;
685 int xsize = d->xSizeNode ? d->xSizeNode->getIntValue() : 800;
686 int ysize = d->ySizeNode ? d->ySizeNode->getIntValue() : 600;
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));
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));
706 d->constrainMouse(x, y);
717 mouse &m = d->mice[0];
730 if (!ea->getHandled()) {
731 processMotion(x, y, ea);
736 d->mouseXNode->setIntValue(x);
737 d->mouseYNode->setIntValue(y);
746 return (d->rightClickModeCycle ==
false);
755 mouse &m = d->mice[0];
761 return m.
modes[mode].pass_through;
764bool FGMouseInput::isRightDragLookActive()
const
770 const auto& m = d->mice[0];
771 if (!d->rightClickModeCycle && m.
nModes > 3) {
List of currently pressed mouse button events.
void update(double dt, unsigned int keyModState)
void init(int button, const osgGA::GUIEventAdapter *ea)
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)
void fgRegisterMouseMotionHandler(fgMouseMotionHandler func)
void fgWarpMouse(int x, int y)
void fgRegisterMouseClickHandler(fgMouseClickHandler func)
bool eventToWindowCoords(const osgGA::GUIEventAdapter *ea, double &x, double &y)
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Settings for a mouse mode.
SGBindingList x_bindings[KEYMOD_MAX]
SGBindingList y_bindings[KEYMOD_MAX]
FGMouseCursor::Cursor cursor
std::unique_ptr< FGButton[]> buttons
SGPropertyNode_ptr mouse_button_nodes[MAX_MOUSE_BUTTONS]
SGTimeStamp timeSinceLastMove
std::unique_ptr< mouse_mode[]> modes
SGPropertyNode_ptr mode_node
bool SviewMouseMotion(int x, int y, const osgGA::GUIEventAdapter &ea)