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