]> git.mxchange.org Git - flightgear.git/blob - src/Input/FGMouseInput.cxx
Fix cursor hide timeout if hovering on canvas windows
[flightgear.git] / src / Input / FGMouseInput.cxx
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 "FGMouseInput.hxx"
30
31 #include <boost/foreach.hpp>
32 #include <osgGA/GUIEventAdapter>
33
34 #include <simgear/scene/util/SGPickCallback.hxx>
35 #include <simgear/timing/timestamp.hxx>
36 #include <simgear/scene/model/SGPickAnimation.hxx>
37
38 #include "FGButton.hxx"
39 #include "Main/globals.hxx"
40 #include <Viewer/renderer.hxx>
41 #include <plib/pu.h>
42 #include <Model/panelnode.hxx>
43 #include <Cockpit/panel.hxx>
44 #include <Viewer/FGEventHandler.hxx>
45 #include <GUI/MouseCursor.hxx>
46
47 using std::ios_base;
48
49 const int MAX_MICE = 1;
50 const int MAX_MOUSE_BUTTONS = 8;
51
52 typedef std::vector<SGSceneryPick> SGSceneryPicks;
53 typedef SGSharedPtr<SGPickCallback> SGPickCallbackPtr;
54 typedef std::list<SGPickCallbackPtr> SGPickCallbackList;
55
56 ////////////////////////////////////////////////////////////////////////
57
58 /**
59  * List of currently pressed mouse button events
60  */
61 class ActivePickCallbacks:
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
70 void 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;
81   if (!globals->get_renderer()->pick(pickList, windowPos)) {
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
94 void 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
105 ////////////////////////////////////////////////////////////////////////
106
107
108 /**
109  * Settings for a mouse mode.
110  */
111 struct mouse_mode {
112     mouse_mode ();
113     virtual ~mouse_mode ();
114     FGMouseCursor::Cursor cursor;
115     bool constrained;
116     bool pass_through;
117     FGButton * buttons;
118     SGBindingList x_bindings[KEYMOD_MAX];
119     SGBindingList y_bindings[KEYMOD_MAX];
120 };
121
122
123 /**
124  * Settings for a mouse.
125  */
126 struct mouse {
127     mouse ();
128     virtual ~mouse ();
129     int x, y;
130     SGPropertyNode_ptr mode_node;
131     SGPropertyNode_ptr mouse_button_nodes[MAX_MOUSE_BUTTONS];
132     int nModes;
133     int current_mode;
134     
135     SGTimeStamp timeSinceLastMove;
136     mouse_mode * modes;
137 };
138
139 static
140 const SGSceneryPick*
141 getPick( 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
151 ////////////////////////////////////////////////////////////////////////
152
153 class FGMouseInput::FGMouseInputPrivate : public SGPropertyChangeListener
154 {
155 public:
156     FGMouseInputPrivate() :
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/drag-sensitivity", true)->addChangeListener(this, true);
174         fgGetNode("/sim/mouse/invert-mouse-wheel", true)->addChangeListener(this, true);
175     }
176   
177     void centerMouseCursor(mouse& m)
178     {    
179       // center the cursor
180       m.x = (xSizeNode ? xSizeNode->getIntValue() : 800) / 2;
181       m.y = (ySizeNode ? ySizeNode->getIntValue() : 600) / 2;
182       fgWarpMouse(m.x, m.y);
183       haveWarped = true;
184     }
185   
186     void constrainMouse(int x, int y)
187     {
188         int new_x=x,new_y=y;
189         int xsize = xSizeNode ? xSizeNode->getIntValue() : 800;
190         int ysize = ySizeNode ? ySizeNode->getIntValue() : 600;
191         
192         bool need_warp = false;
193         if (x <= (xsize * .25) || x >= (xsize * .75)) {
194           new_x = int(xsize * .5);
195           need_warp = true;
196         }
197
198         if (y <= (ysize * .25) || y >= (ysize * .75)) {
199           new_y = int(ysize * .5);
200           need_warp = true;
201         }
202
203         if (need_warp)
204         {
205           fgWarpMouse(new_x, new_y);
206           haveWarped = true;
207         }
208     }
209
210     void scheduleHoverPick(const osg::Vec2d& windowPos)
211     {
212       hoverPickScheduled = true;
213       hoverPos = windowPos;
214     }
215   
216     void doHoverPick(const osg::Vec2d& windowPos)
217     {
218         FGMouseCursor::Cursor cur = FGMouseCursor::CURSOR_ARROW;
219         bool explicitCursor = false;
220         bool didPick = false;
221
222         SGPickCallback::Priority priority = SGPickCallback::PriorityScenery;
223         SGSceneryPicks pickList;
224         globals->get_renderer()->pick(pickList, windowPos);
225
226         SGSceneryPicks::const_iterator i;
227         for( i = pickList.begin(); i != pickList.end(); ++i )
228         {
229             bool done = i->callback->hover(windowPos, i->info);
230             std::string curName(i->callback->getCursor());
231             if (!curName.empty()) {
232                 explicitCursor = true;
233                 cur = FGMouseCursor::cursorFromString(curName.c_str());
234             }
235             
236             // if the callback is of higher prioirty (lower enum index),
237             // record that.
238             if (i->callback->getPriority() < priority) {
239                 priority = i->callback->getPriority();
240             }
241
242             if (done) {
243                 didPick = true;
244                 break;
245             }
246         } // of picks iteration
247
248         // Check if any pick from the previous iteration has disappeared. If so
249         // notify the callback that the mouse has left its element.
250         for( i = _previous_picks.begin(); i != _previous_picks.end(); ++i )
251         {
252           if( !getPick(pickList, i->callback) )
253             i->callback->mouseLeave(windowPos);
254         }
255         _previous_picks = pickList;
256       
257         if (!explicitCursor && (priority == SGPickCallback::PriorityPanel)) {
258             cur = FGMouseCursor::CURSOR_HAND;
259         }
260         
261         FGMouseCursor::instance()->setCursor(cur);
262         if (!didPick) {
263           SGPropertyNode_ptr args(new SGPropertyNode);
264           globals->get_commands()->execute("update-hover", args);
265
266         }
267     }
268     
269     void doMouseMoveWithCallbacks(const osgGA::GUIEventAdapter* ea)
270     {
271         FGMouseCursor::Cursor cur = FGMouseCursor::CURSOR_CLOSED_HAND;
272         
273         osg::Vec2d windowPos;
274         flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
275
276         SGSceneryPicks pickList;
277         if( !globals->get_renderer()->pick(pickList, windowPos) )
278           return;
279
280         for( ActivePickCallbacks::iterator mi = activePickCallbacks.begin();
281                                            mi != activePickCallbacks.end();
282                                          ++mi )
283         {
284           SGPickCallbackList::iterator li;
285           for( li = mi->second.begin(); li != mi->second.end(); ++li )
286           {
287             const SGSceneryPick* pick = getPick(pickList, *li);
288             (*li)->mouseMoved(*ea, pick ? &pick->info : 0);
289
290             std::string curName((*li)->getCursor());
291             if( !curName.empty() )
292               cur = FGMouseCursor::cursorFromString(curName.c_str());
293           }
294         }
295
296         FGMouseCursor::instance()->setCursor(cur);
297     }
298
299     // implement the property-change-listener interfacee
300     virtual void valueChanged( SGPropertyNode * node )
301     {
302         if (node->getNameString() == "drag-sensitivity") {
303             SGKnobAnimation::setDragSensitivity(node->getDoubleValue());
304         } else if (node->getNameString() == "invert-mouse-wheel") {
305             SGKnobAnimation::setAlternateMouseWheelDirection(node->getBoolValue());
306         } else if (node->getNameString() == "hide-cursor") {
307             hideCursor = node->getBoolValue();
308         } else if (node->getNameString() == "cursor-timeout-sec") {
309             cursorTimeoutMsec = node->getDoubleValue() * 1000;
310         } else if (node->getNameString() == "tooltip-delay-msec") {
311             tooltipDelayMsec = node->getIntValue();
312         } else if (node->getNameString() == "right-button-mode-cycle-enabled") {
313             rightClickModeCycle = node->getBoolValue();
314         } else if (node->getNameString() == "click-shows-tooltip") {
315             clickTriggersTooltip = node->getBoolValue();
316
317         }
318     }
319     
320     ActivePickCallbacks activePickCallbacks;
321     SGSceneryPicks _previous_picks;
322
323     mouse mice[MAX_MICE];
324     
325     bool hideCursor, haveWarped;
326     bool tooltipTimeoutDone;
327     bool clickTriggersTooltip;
328     int tooltipDelayMsec, cursorTimeoutMsec;
329     bool rightClickModeCycle;
330     
331     SGPropertyNode_ptr xSizeNode;
332     SGPropertyNode_ptr ySizeNode;
333     SGPropertyNode_ptr xAccelNode;
334     SGPropertyNode_ptr yAccelNode;
335     SGPropertyNode_ptr mouseXNode, mouseYNode;
336   
337     bool hoverPickScheduled;
338     osg::Vec2d hoverPos;
339 };
340
341
342 ////////////////////////////////////////////////////////////////////////
343 // The Mouse Input Implementation
344 ////////////////////////////////////////////////////////////////////////
345
346 static FGMouseInput* global_mouseInput = NULL;
347
348 static void mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
349 {
350     if(global_mouseInput)
351         global_mouseInput->doMouseClick(button, updown, x, y, mainWindow, ea);
352 }
353
354 static void mouseMotionHandler(int x, int y, const osgGA::GUIEventAdapter* ea)
355 {
356     if (global_mouseInput != 0)
357         global_mouseInput->doMouseMotion(x, y, ea);
358 }
359
360
361
362 FGMouseInput::FGMouseInput() :
363   d(new FGMouseInputPrivate)
364 {
365     global_mouseInput = this;
366 }
367
368 FGMouseInput::~FGMouseInput()
369 {
370     global_mouseInput = NULL;
371 }
372
373 void FGMouseInput::init()
374 {
375   SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse bindings");
376   string module = "";
377
378   SGPropertyNode * mouse_nodes = fgGetNode("/input/mice");
379   if (mouse_nodes == 0) {
380     SG_LOG(SG_INPUT, SG_WARN, "No mouse bindings (/input/mice)!!");
381     mouse_nodes = fgGetNode("/input/mice", true);
382   }
383
384   int j;
385   for (int i = 0; i < MAX_MICE; i++) {
386     SGPropertyNode * mouse_node = mouse_nodes->getChild("mouse", i, true);
387     mouse &m = d->mice[i];
388
389                                 // Grab node pointers
390     std::ostringstream buf;
391     buf <<  "/devices/status/mice/mouse[" << i << "]/mode";
392     m.mode_node = fgGetNode(buf.str().c_str());
393     if (m.mode_node == NULL) {
394       m.mode_node = fgGetNode(buf.str().c_str(), true);
395       m.mode_node->setIntValue(0);
396     }
397     for (j = 0; j < MAX_MOUSE_BUTTONS; j++) {
398       buf.seekp(ios_base::beg);
399       buf << "/devices/status/mice/mouse["<< i << "]/button[" << j << "]";
400       m.mouse_button_nodes[j] = fgGetNode(buf.str().c_str(), true);
401       m.mouse_button_nodes[j]->setBoolValue(false);
402     }
403
404    // Read all the modes
405     m.nModes = mouse_node->getIntValue("mode-count", 1);
406     m.modes = new mouse_mode[m.nModes];
407
408     for (int j = 0; j < m.nModes; j++) {
409       int k;
410       SGPropertyNode * mode_node = mouse_node->getChild("mode", j, true);
411
412     // Read the mouse cursor for this mode
413       m.modes[j].cursor = FGMouseCursor::cursorFromString(mode_node->getStringValue("cursor", "inherit"));
414         
415       // Read other properties for this mode
416       m.modes[j].constrained = mode_node->getBoolValue("constrained", false);
417       m.modes[j].pass_through = mode_node->getBoolValue("pass-through", false);
418
419       // Read the button bindings for this mode
420       m.modes[j].buttons = new FGButton[MAX_MOUSE_BUTTONS];
421       std::ostringstream buf;
422       for (k = 0; k < MAX_MOUSE_BUTTONS; k++) {
423         buf.seekp(ios_base::beg);
424         buf << "mouse button " << k;
425         m.modes[j].buttons[k].init( mode_node->getChild("button", k), buf.str(), module );
426       }
427
428       // Read the axis bindings for this mode
429       read_bindings(mode_node->getChild("x-axis", 0, true), m.modes[j].x_bindings, KEYMOD_NONE, module );
430       read_bindings(mode_node->getChild("y-axis", 0, true), m.modes[j].y_bindings, KEYMOD_NONE, module );
431       
432       if (mode_node->hasChild("x-axis-ctrl")) {
433         read_bindings(mode_node->getChild("x-axis-ctrl"), m.modes[j].x_bindings, KEYMOD_CTRL, module );
434       }
435       if (mode_node->hasChild("x-axis-shift")) {
436         read_bindings(mode_node->getChild("x-axis-shift"), m.modes[j].x_bindings, KEYMOD_SHIFT, module );
437       }
438       if (mode_node->hasChild("x-axis-ctrl-shift")) {
439         read_bindings(mode_node->getChild("x-axis-ctrl-shift"), m.modes[j].x_bindings, KEYMOD_CTRL|KEYMOD_SHIFT, module );
440       }
441       
442       if (mode_node->hasChild("y-axis-ctrl")) {
443         read_bindings(mode_node->getChild("y-axis-ctrl"), m.modes[j].y_bindings, KEYMOD_CTRL, module );
444       }
445       if (mode_node->hasChild("y-axis-shift")) {
446         read_bindings(mode_node->getChild("y-axis-shift"), m.modes[j].y_bindings, KEYMOD_SHIFT, module );
447       }
448       if (mode_node->hasChild("y-axis-ctrl-shift")) {
449         read_bindings(mode_node->getChild("y-axis-ctrl-shift"), m.modes[j].y_bindings, KEYMOD_CTRL|KEYMOD_SHIFT, module );
450       }
451     } // of modes iteration
452   }
453
454   fgRegisterMouseClickHandler(mouseClickHandler);
455   fgRegisterMouseMotionHandler(mouseMotionHandler);
456 }
457
458 void FGMouseInput::update ( double dt )
459 {
460   mouse &m = d->mice[0];
461   int mode =  m.mode_node->getIntValue();
462   if (mode != m.current_mode) {
463     // current mode has changed
464     m.current_mode = mode;
465     m.timeSinceLastMove.stamp();
466       
467     if (mode >= 0 && mode < m.nModes) {
468       FGMouseCursor::instance()->setCursor(m.modes[mode].cursor);
469       d->centerMouseCursor(m);
470     } else {
471       SG_LOG(SG_INPUT, SG_WARN, "Mouse mode " << mode << " out of range");
472       FGMouseCursor::instance()->setCursor(FGMouseCursor::CURSOR_ARROW);
473     }
474   }
475
476   if ((mode == 0) && d->hoverPickScheduled) {
477     d->doHoverPick(d->hoverPos);
478     d->hoverPickScheduled = false;
479   }
480   
481   // if delay is <= 0, disable tooltips
482   if ( !d->tooltipTimeoutDone &&
483       (d->tooltipDelayMsec > 0) &&
484       (m.timeSinceLastMove.elapsedMSec() > d->tooltipDelayMsec))
485   {
486       d->tooltipTimeoutDone = true;
487       SGPropertyNode_ptr arg(new SGPropertyNode);
488       globals->get_commands()->execute("tooltip-timeout", arg);
489   }
490   
491   if ( d->hideCursor ) {
492       if ( m.timeSinceLastMove.elapsedMSec() > d->cursorTimeoutMsec) {
493           FGMouseCursor::instance()->hideCursorUntilMouseMove();
494           m.timeSinceLastMove.stamp();
495       }
496   }
497     
498   d->activePickCallbacks.update( dt, fgGetKeyModifiers() );
499 }
500
501 mouse::mouse ()
502   : x(-1),
503     y(-1),
504     nModes(1),
505     current_mode(0),
506     modes(NULL)
507 {
508 }
509
510 mouse::~mouse ()
511 {
512   delete [] modes;
513 }
514
515 mouse_mode::mouse_mode ()
516   : cursor(FGMouseCursor::CURSOR_ARROW),
517     constrained(false),
518     pass_through(false),
519     buttons(NULL)
520 {
521 }
522
523 mouse_mode::~mouse_mode ()
524 {
525                                 // FIXME: memory leak
526 //   for (int i = 0; i < KEYMOD_MAX; i++) {
527 //     int j;
528 //     for (j = 0; i < x_bindings[i].size(); j++)
529 //       delete bindings[i][j];
530 //     for (j = 0; j < y_bindings[i].size(); j++)
531 //       delete bindings[i][j];
532 //   }
533   if (buttons) {
534     delete [] buttons;
535   }
536 }
537
538 void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
539 {
540   int modifiers = fgGetKeyModifiers();
541
542   mouse &m = d->mice[0];
543   mouse_mode &mode = m.modes[m.current_mode];
544                                 // Let the property manager know.
545   if (b >= 0 && b < MAX_MOUSE_BUTTONS)
546     m.mouse_button_nodes[b]->setBoolValue(updown == MOUSE_BUTTON_DOWN);
547
548   if (!d->rightClickModeCycle && (b == 2)) {
549     // in spring-loaded look mode, ignore right clicks entirely here
550     return;
551   }
552   
553   // Pass on to PUI and the panel if
554   // requested, and return if one of
555   // them consumes the event.
556
557   osg::Vec2d windowPos;
558   flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
559
560   SGSceneryPicks pickList;
561   globals->get_renderer()->pick(pickList, windowPos);
562
563   if( updown != MOUSE_BUTTON_DOWN )
564   {
565     // Execute the mouse up event in any case, may be we should
566     // stop processing here?
567
568     SGPickCallbackList& callbacks = d->activePickCallbacks[b];
569
570     while( !callbacks.empty() )
571     {
572       SGPickCallbackPtr& cb = callbacks.front();
573       const SGSceneryPick* pick = getPick(pickList, cb);
574       cb->buttonReleased(ea->getModKeyMask(), *ea, pick ? &pick->info : 0);
575
576       callbacks.pop_front();
577     }
578   }
579
580   if (mode.pass_through) {
581     // remove once PUI uses standard picking mechanism
582     if (0 <= x && 0 <= y && puMouse(b, updown, x, y))
583       return; // pui handled it
584
585     // pui didn't want the click event so compute a
586     // scenegraph intersection point corresponding to the mouse click
587     if (updown == MOUSE_BUTTON_DOWN) {
588       d->activePickCallbacks.init( b, ea );
589         
590       if (d->clickTriggersTooltip) {
591             SGPropertyNode_ptr args(new SGPropertyNode);
592             args->setStringValue("reason", "click");
593             globals->get_commands()->execute("tooltip-timeout", args);
594             d->tooltipTimeoutDone = true;
595       }
596     } else {
597       // do a hover pick now, to fix up cursor
598       d->doHoverPick(windowPos);
599     } // mouse button was released
600   } // of pass-through mode
601
602   // OK, PUI and the panel didn't want the click
603   if (b >= MAX_MOUSE_BUTTONS) {
604     SG_LOG(SG_INPUT, SG_ALERT, "Mouse button " << b
605            << " where only " << MAX_MOUSE_BUTTONS << " expected");
606     return;
607   }
608
609   m.modes[m.current_mode].buttons[b].update( modifiers, 0 != updown, x, y);  
610 }
611
612 void FGMouseInput::processMotion(int x, int y, const osgGA::GUIEventAdapter* ea)
613 {
614   if (!d->activePickCallbacks[0].empty()) {
615     d->doMouseMoveWithCallbacks(ea);
616     return;
617   }
618   
619   mouse &m = d->mice[0];
620   int modeIndex = m.current_mode;
621   // are we in spring-loaded look mode?
622   if (!d->rightClickModeCycle) {
623     if (m.mouse_button_nodes[2]->getBoolValue()) {
624       // right mouse is down, force look mode
625       modeIndex = 3;
626     }
627   }
628
629   if (modeIndex == 0) {
630     osg::Vec2d windowPos;
631     flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
632     d->scheduleHoverPick(windowPos);
633     // mouse has moved, so we may need to issue tooltip-timeout command again
634     d->tooltipTimeoutDone = false;
635   }
636   
637   mouse_mode &mode = m.modes[modeIndex];
638   
639   // Pass on to PUI if requested, and return
640   // if PUI consumed the event.
641   if (mode.pass_through && puMouse(x, y)) {
642     return;
643   }
644
645   if (d->haveWarped)
646   {
647     // don't fire mouse-movement events at the first update after warping the mouse,
648     // just remember the new mouse position
649     d->haveWarped = false;
650   }
651   else
652   {
653     int modifiers = fgGetKeyModifiers();
654     int xsize = d->xSizeNode ? d->xSizeNode->getIntValue() : 800;
655     int ysize = d->ySizeNode ? d->ySizeNode->getIntValue() : 600;
656       
657     // OK, PUI didn't want the event,
658     // so we can play with it.
659     if (x != m.x) {
660       int delta = x - m.x;
661       d->xAccelNode->setIntValue( delta );
662       for (unsigned int i = 0; i < mode.x_bindings[modifiers].size(); i++)
663         mode.x_bindings[modifiers][i]->fire(double(delta), double(xsize));
664     }
665     if (y != m.y) {
666       int delta = y - m.y;
667       d->yAccelNode->setIntValue( -delta );
668       for (unsigned int i = 0; i < mode.y_bindings[modifiers].size(); i++)
669         mode.y_bindings[modifiers][i]->fire(double(delta), double(ysize));
670     }
671   }
672   
673   // Constrain the mouse if requested
674   if (mode.constrained) {
675     d->constrainMouse(x, y);
676   }
677 }
678
679 void FGMouseInput::doMouseMotion (int x, int y, const osgGA::GUIEventAdapter* ea)
680 {
681   mouse &m = d->mice[0];
682
683   if (m.current_mode < 0 || m.current_mode >= m.nModes) {
684       m.x = x;
685       m.y = y;
686       return;
687   }
688
689   m.timeSinceLastMove.stamp();
690   FGMouseCursor::instance()->mouseMoved();
691
692   // TODO Get rid of this as soon as soon as cursor hide timeout works globally
693   if( ea->getHandled() )
694     return;
695
696   processMotion(x, y, ea);
697     
698   m.x = x;
699   m.y = y;
700   d->mouseXNode->setIntValue(x);
701   d->mouseYNode->setIntValue(y);
702 }
703
704
705