]> git.mxchange.org Git - flightgear.git/blob - src/Input/FGMouseInput.cxx
Fix (nearly) all the std:: namespace violations in headers, in preparation for fixing...
[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 #include "FGMouseInput.hxx"
26 #include "Main/globals.hxx"
27
28 using std::ios_base;
29
30 void ActivePickCallbacks::init( int b, const osgGA::GUIEventAdapter* ea )
31 {
32   // Get the list of hit callbacks. Take the first callback that
33   // accepts the mouse button press and ignore the rest of them
34   // That is they get sorted by distance and by scenegraph depth.
35   // The nearest one is the first one and the deepest
36   // (the most specialized one in the scenegraph) is the first.
37   std::vector<SGSceneryPick> pickList;
38   if (globals->get_renderer()->pick(pickList, ea)) {
39     std::vector<SGSceneryPick>::const_iterator i;
40     for (i = pickList.begin(); i != pickList.end(); ++i) {
41       if (i->callback->buttonPressed(b, i->info)) {
42           (*this)[b].push_back(i->callback);
43           return;
44       }
45     }
46   }
47 }
48
49 void ActivePickCallbacks::update( double dt )
50 {
51   // handle repeatable mouse press events
52   for( iterator mi = begin(); mi != end(); ++mi ) {
53     std::list<SGSharedPtr<SGPickCallback> >::iterator li;
54     for (li = mi->second.begin(); li != mi->second.end(); ++li) {
55       (*li)->update(dt);
56     }
57   }
58 }
59
60
61 #include <plib/pu.h>
62 #include <Model/panelnode.hxx>
63 #include <Cockpit/panel.hxx>
64 ////////////////////////////////////////////////////////////////////////
65 // The Mouse Input Implementation
66 ////////////////////////////////////////////////////////////////////////
67
68 const FGMouseInput::MouseCursorMap FGMouseInput::mouse_cursor_map[] = {
69     { "none", MOUSE_CURSOR_NONE },
70     { "inherit", MOUSE_CURSOR_POINTER },
71     { "wait", MOUSE_CURSOR_WAIT },
72     { "crosshair", MOUSE_CURSOR_CROSSHAIR },
73     { "left-right", MOUSE_CURSOR_LEFTRIGHT },
74     { 0, 0 }
75 };
76
77 FGMouseInput * FGMouseInput::mouseInput = NULL;
78
79 FGMouseInput::FGMouseInput() :
80   haveWarped(false),
81   xSizeNode(fgGetNode("/sim/startup/xsize", false ) ),
82   ySizeNode(fgGetNode("/sim/startup/ysize", false ) ),
83   xAccelNode(fgGetNode("/devices/status/mice/mouse/accel-x", true ) ),
84   yAccelNode(fgGetNode("/devices/status/mice/mouse/accel-y", true ) ),
85   hideCursorNode(fgGetNode("/sim/mouse/hide-cursor", true ) ),
86   cursorTimeoutNode(fgGetNode("/sim/mouse/cursor-timeout-sec", true ) )
87 {
88   if( mouseInput == NULL )
89     mouseInput = this;
90 }
91
92 FGMouseInput::~FGMouseInput()
93 {
94   if( mouseInput == this )
95     mouseInput = NULL;
96 }
97
98 void FGMouseInput::init()
99 {
100   SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse bindings");
101   string module = "";
102
103   SGPropertyNode * mouse_nodes = fgGetNode("/input/mice");
104   if (mouse_nodes == 0) {
105     SG_LOG(SG_INPUT, SG_WARN, "No mouse bindings (/input/mice)!!");
106     mouse_nodes = fgGetNode("/input/mice", true);
107   }
108
109   int j;
110   for (int i = 0; i < MAX_MICE; i++) {
111     SGPropertyNode * mouse_node = mouse_nodes->getChild("mouse", i, true);
112     mouse &m = bindings[i];
113
114                                 // Grab node pointers
115     std::ostringstream buf;
116     buf <<  "/devices/status/mice/mouse[" << i << "]/mode";
117     m.mode_node = fgGetNode(buf.str().c_str());
118     if (m.mode_node == NULL) {
119       m.mode_node = fgGetNode(buf.str().c_str(), true);
120       m.mode_node->setIntValue(0);
121     }
122     for (j = 0; j < MAX_MOUSE_BUTTONS; j++) {
123       buf.seekp(ios_base::beg);
124       buf << "/devices/status/mice/mouse["<< i << "]/button[" << j << "]";
125       m.mouse_button_nodes[j] = fgGetNode(buf.str().c_str(), true);
126       m.mouse_button_nodes[j]->setBoolValue(false);
127     }
128
129                                 // Read all the modes
130     m.nModes = mouse_node->getIntValue("mode-count", 1);
131     m.modes = new mouse_mode[m.nModes];
132
133     for (int j = 0; j < m.nModes; j++) {
134       int k;
135
136                                 // Read the mouse cursor for this mode
137       SGPropertyNode * mode_node = mouse_node->getChild("mode", j, true);
138       const char * cursor_name =
139         mode_node->getStringValue("cursor", "inherit");
140       m.modes[j].cursor = MOUSE_CURSOR_POINTER;
141       for (k = 0; mouse_cursor_map[k].name != 0; k++) {
142         if (!strcmp(mouse_cursor_map[k].name, cursor_name)) {
143           m.modes[j].cursor = mouse_cursor_map[k].cursor;
144           break;
145         }
146       }
147
148                                 // Read other properties for this mode
149       m.modes[j].constrained = mode_node->getBoolValue("constrained", false);
150       m.modes[j].pass_through = mode_node->getBoolValue("pass-through", false);
151
152                                 // Read the button bindings for this mode
153       m.modes[j].buttons = new FGButton[MAX_MOUSE_BUTTONS];
154       std::ostringstream buf;
155       for (k = 0; k < MAX_MOUSE_BUTTONS; k++) {
156         buf.seekp(ios_base::beg);
157         buf << "mouse button " << k;
158         SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse button " << k);
159         m.modes[j].buttons[k].init( mode_node->getChild("button", k), buf.str(), module );
160       }
161
162                                 // Read the axis bindings for this mode
163       read_bindings(mode_node->getChild("x-axis", 0, true), m.modes[j].x_bindings, KEYMOD_NONE, module );
164       read_bindings(mode_node->getChild("y-axis", 0, true), m.modes[j].y_bindings, KEYMOD_NONE, module );
165     }
166   }
167
168   fgRegisterMouseClickHandler(mouseClickHandler);
169   fgRegisterMouseMotionHandler(mouseMotionHandler);
170 }
171
172 void FGMouseInput::update ( double dt )
173 {
174   double cursorTimeout = cursorTimeoutNode ? cursorTimeoutNode->getDoubleValue() : 10.0;
175
176   mouse &m = bindings[0];
177   int mode =  m.mode_node->getIntValue();
178   if (mode != m.current_mode) {
179     m.current_mode = mode;
180     m.timeout = cursorTimeout;
181     if (mode >= 0 && mode < m.nModes) {
182       fgSetMouseCursor(m.modes[mode].cursor);
183       m.x = (xSizeNode ? xSizeNode->getIntValue() : 800) / 2;
184       m.y = (ySizeNode ? ySizeNode->getIntValue() : 600) / 2;
185       fgWarpMouse(m.x, m.y);
186       haveWarped = true;
187     } else {
188       SG_LOG(SG_INPUT, SG_DEBUG, "Mouse mode " << mode << " out of range");
189       fgSetMouseCursor(MOUSE_CURSOR_POINTER);
190     }
191   }
192
193   if ( hideCursorNode ==NULL || hideCursorNode->getBoolValue() ) {
194       if ( m.x != m.save_x || m.y != m.save_y ) {
195           m.timeout = cursorTimeout;
196           if (fgGetMouseCursor() == MOUSE_CURSOR_NONE)
197               fgSetMouseCursor(m.modes[mode].cursor);
198       } else {
199           m.timeout -= dt;
200           if ( m.timeout <= 0.0 ) {
201               fgSetMouseCursor(MOUSE_CURSOR_NONE);
202               m.timeout = 0.0;
203           }
204       }
205       m.save_x = m.x;
206       m.save_y = m.y;
207   }
208
209   activePickCallbacks.update( dt );
210 }
211
212 FGMouseInput::mouse::mouse ()
213   : x(-1),
214     y(-1),
215     save_x(-1),
216     save_y(-1),
217     nModes(1),
218     current_mode(0),
219     timeout(0),
220     modes(NULL)
221 {
222 }
223
224 FGMouseInput::mouse::~mouse ()
225 {
226   delete [] modes;
227 }
228
229 FGMouseInput::mouse_mode::mouse_mode ()
230   : cursor(MOUSE_CURSOR_POINTER),
231     constrained(false),
232     pass_through(false),
233     buttons(NULL)
234 {
235 }
236
237 FGMouseInput::mouse_mode::~mouse_mode ()
238 {
239                                 // FIXME: memory leak
240 //   for (int i = 0; i < KEYMOD_MAX; i++) {
241 //     int j;
242 //     for (j = 0; i < x_bindings[i].size(); j++)
243 //       delete bindings[i][j];
244 //     for (j = 0; j < y_bindings[i].size(); j++)
245 //       delete bindings[i][j];
246 //   }
247   delete [] buttons;
248 }
249
250 void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
251 {
252   int modifiers = fgGetKeyModifiers();
253
254   mouse &m = bindings[0];
255   mouse_mode &mode = m.modes[m.current_mode];
256
257                                 // Let the property manager know.
258   if (b >= 0 && b < MAX_MOUSE_BUTTONS)
259     m.mouse_button_nodes[b]->setBoolValue(updown == MOUSE_BUTTON_DOWN);
260
261                                 // Pass on to PUI and the panel if
262                                 // requested, and return if one of
263                                 // them consumes the event.
264
265   if (updown != MOUSE_BUTTON_DOWN) {
266     // Execute the mouse up event in any case, may be we should
267     // stop processing here?
268     while (!activePickCallbacks[b].empty()) {
269       activePickCallbacks[b].front()->buttonReleased();
270       activePickCallbacks[b].pop_front();
271     }
272   }
273
274   if (mode.pass_through) {
275     if (0 <= x && 0 <= y && puMouse(b, updown, x, y))
276       return;
277     else if (0 <= x && 0 <= y && (globals->get_current_panel() != 0) &&
278              globals->get_current_panel()->getVisibility() &&
279              globals->get_current_panel()->doMouseAction(b, updown, x, y))
280       return;
281     else if (0 <= x && 0 <= y && fgHandle3DPanelMouseEvent(b, updown, x, y))
282       return;
283     else {
284       // pui didn't want the click event so compute a
285       // scenegraph intersection point corresponding to the mouse click
286       if (updown == MOUSE_BUTTON_DOWN) {
287         activePickCallbacks.init( b, ea );
288       }
289     }
290   }
291
292   // OK, PUI and the panel didn't want the click
293   if (b >= MAX_MOUSE_BUTTONS) {
294     SG_LOG(SG_INPUT, SG_ALERT, "Mouse button " << b
295            << " where only " << MAX_MOUSE_BUTTONS << " expected");
296     return;
297   }
298
299   m.modes[m.current_mode].buttons[b].update( modifiers, 0 != updown, x, y);
300 }
301
302 void FGMouseInput::doMouseMotion (int x, int y)
303 {
304   // Don't call fgGetKeyModifiers() here, until we are using a
305   // toolkit that supports getting the mods from outside a key
306   // callback.  Glut doesn't.
307   int modifiers = KEYMOD_NONE;
308
309   int xsize = xSizeNode ? xSizeNode->getIntValue() : 800;
310   int ysize = ySizeNode ? ySizeNode->getIntValue() : 600;
311
312   mouse &m = bindings[0];
313
314   if (m.current_mode < 0 || m.current_mode >= m.nModes) {
315       m.x = x;
316       m.y = y;
317       return;
318   }
319   mouse_mode &mode = m.modes[m.current_mode];
320
321                                 // Pass on to PUI if requested, and return
322                                 // if PUI consumed the event.
323   if (mode.pass_through && puMouse(x, y)) {
324       m.x = x;
325       m.y = y;
326       return;
327   }
328   
329   if (haveWarped)
330   {
331       // don't fire mouse-movement events at the first update after warping the mouse,
332       // just remember the new mouse position
333       haveWarped = false;
334   }
335   else
336   {
337                                 // OK, PUI didn't want the event,
338                                 // so we can play with it.
339       if (x != m.x) {
340         int delta = x - m.x;
341         xAccelNode->setIntValue( delta );
342         for (unsigned int i = 0; i < mode.x_bindings[modifiers].size(); i++)
343           mode.x_bindings[modifiers][i]->fire(double(delta), double(xsize));
344       }
345       if (y != m.y) {
346         int delta = y - m.y;
347         yAccelNode->setIntValue( -delta );
348         for (unsigned int i = 0; i < mode.y_bindings[modifiers].size(); i++)
349           mode.y_bindings[modifiers][i]->fire(double(delta), double(ysize));
350       }
351   }
352                                 // Constrain the mouse if requested
353   if (mode.constrained) {
354     int new_x=x,new_y=y;
355     
356     bool need_warp = false;
357     if (x <= (xsize * .25) || x >= (xsize * .75)) {
358       new_x = int(xsize * .5);
359       need_warp = true;
360     }
361
362     if (y <= (ysize * .25) || y >= (ysize * .75)) {
363       new_y = int(ysize * .5);
364       need_warp = true;
365     }
366
367     if (need_warp)
368     {
369       fgWarpMouse(new_x, new_y);
370       haveWarped = true;
371       SG_LOG(SG_INPUT, SG_DEBUG, "Mouse warp: " << x << ", " << y << " => " << new_x << ", " << new_y);
372     }
373   }
374
375   if (m.x != x)
376       fgSetInt("/devices/status/mice/mouse/x", m.x = x);
377
378   if (m.y != y)
379       fgSetInt("/devices/status/mice/mouse/y", m.y = y);
380 }
381
382 void FGMouseInput::mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
383 {
384     if(mouseInput)
385       mouseInput->doMouseClick(button, updown, x, y, mainWindow, ea);
386 }
387
388 void FGMouseInput::mouseMotionHandler(int x, int y)
389 {
390     if (mouseInput != 0)
391         mouseInput->doMouseMotion(x, y);
392 }
393
394