1 // FGMouseInput.cxx -- handle user input from mouse devices
3 // Written by Torsten Dreyer, started August 2009
4 // Based on work from David Megginson, started May 2001.
6 // Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
7 // Copyright (C) 2001 David Megginson, david@megginson.com
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.
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.
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.
25 #include "FGMouseInput.hxx"
28 void ActivePickCallbacks::init( int b, const osgGA::GUIEventAdapter* ea )
30 // Get the list of hit callbacks. Take the first callback that
31 // accepts the mouse button press and ignore the rest of them
32 // That is they get sorted by distance and by scenegraph depth.
33 // The nearest one is the first one and the deepest
34 // (the most specialized one in the scenegraph) is the first.
35 std::vector<SGSceneryPick> pickList;
36 if (FGRenderer::pick(pickList, ea)) {
37 std::vector<SGSceneryPick>::const_iterator i;
38 for (i = pickList.begin(); i != pickList.end(); ++i) {
39 if (i->callback->buttonPressed(b, i->info)) {
40 (*this)[b].push_back(i->callback);
47 void ActivePickCallbacks::update( double dt )
49 // handle repeatable mouse press events
50 for( iterator mi = begin(); mi != end(); ++mi ) {
51 std::list<SGSharedPtr<SGPickCallback> >::iterator li;
52 for (li = mi->second.begin(); li != mi->second.end(); ++li) {
60 #include <Model/panelnode.hxx>
61 #include <Cockpit/panel.hxx>
62 ////////////////////////////////////////////////////////////////////////
63 // The Mouse Input Implementation
64 ////////////////////////////////////////////////////////////////////////
66 const FGMouseInput::MouseCursorMap FGMouseInput::mouse_cursor_map[] = {
67 { "none", MOUSE_CURSOR_NONE },
68 { "inherit", MOUSE_CURSOR_POINTER },
69 { "wait", MOUSE_CURSOR_WAIT },
70 { "crosshair", MOUSE_CURSOR_CROSSHAIR },
71 { "left-right", MOUSE_CURSOR_LEFTRIGHT },
75 FGMouseInput * FGMouseInput::mouseInput = NULL;
77 FGMouseInput::FGMouseInput()
79 if( mouseInput == NULL )
83 FGMouseInput::~FGMouseInput()
85 if( mouseInput == this )
89 void FGMouseInput::init()
91 SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse bindings");
94 SGPropertyNode * mouse_nodes = fgGetNode("/input/mice");
95 if (mouse_nodes == 0) {
96 SG_LOG(SG_INPUT, SG_WARN, "No mouse bindings (/input/mice)!!");
97 mouse_nodes = fgGetNode("/input/mice", true);
101 for (int i = 0; i < MAX_MICE; i++) {
102 SGPropertyNode * mouse_node = mouse_nodes->getChild("mouse", i, true);
103 mouse &m = bindings[i];
105 // Grab node pointers
107 sprintf(buf, "/devices/status/mice/mouse[%d]/mode", i);
108 m.mode_node = fgGetNode(buf);
109 if (m.mode_node == NULL) {
110 m.mode_node = fgGetNode(buf, true);
111 m.mode_node->setIntValue(0);
113 for (j = 0; j < MAX_MOUSE_BUTTONS; j++) {
114 sprintf(buf, "/devices/status/mice/mouse[%d]/button[%d]", i, j);
115 m.mouse_button_nodes[j] = fgGetNode(buf, true);
116 m.mouse_button_nodes[j]->setBoolValue(false);
119 // Read all the modes
120 m.nModes = mouse_node->getIntValue("mode-count", 1);
121 m.modes = new mouse_mode[m.nModes];
123 for (int j = 0; j < m.nModes; j++) {
126 // Read the mouse cursor for this mode
127 SGPropertyNode * mode_node = mouse_node->getChild("mode", j, true);
128 const char * cursor_name =
129 mode_node->getStringValue("cursor", "inherit");
130 m.modes[j].cursor = MOUSE_CURSOR_POINTER;
131 for (k = 0; mouse_cursor_map[k].name != 0; k++) {
132 if (!strcmp(mouse_cursor_map[k].name, cursor_name)) {
133 m.modes[j].cursor = mouse_cursor_map[k].cursor;
138 // Read other properties for this mode
139 m.modes[j].constrained = mode_node->getBoolValue("constrained", false);
140 m.modes[j].pass_through = mode_node->getBoolValue("pass-through", false);
142 // Read the button bindings for this mode
143 m.modes[j].buttons = new FGButton[MAX_MOUSE_BUTTONS];
145 for (k = 0; k < MAX_MOUSE_BUTTONS; k++) {
146 sprintf(buf, "mouse button %d", k);
147 SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse button " << k);
148 m.modes[j].buttons[k].init( mode_node->getChild("button", k), buf, module );
151 // Read the axis bindings for this mode
152 read_bindings(mode_node->getChild("x-axis", 0, true), m.modes[j].x_bindings, KEYMOD_NONE, module );
153 read_bindings(mode_node->getChild("y-axis", 0, true), m.modes[j].y_bindings, KEYMOD_NONE, module );
157 fgRegisterMouseClickHandler(mouseClickHandler);
158 fgRegisterMouseMotionHandler(mouseMotionHandler);
161 void FGMouseInput::update ( double dt )
163 mouse &m = bindings[0];
164 int mode = m.mode_node->getIntValue();
165 if (mode != m.current_mode) {
166 m.current_mode = mode;
167 m.timeout = fgGetDouble( "/sim/mouse/cursor-timeout-sec", 10.0 );
168 if (mode >= 0 && mode < m.nModes) {
169 fgSetMouseCursor(m.modes[mode].cursor);
170 m.x = fgGetInt("/sim/startup/xsize", 800) / 2;
171 m.y = fgGetInt("/sim/startup/ysize", 600) / 2;
172 fgWarpMouse(m.x, m.y);
174 SG_LOG(SG_INPUT, SG_DEBUG, "Mouse mode " << mode << " out of range");
175 fgSetMouseCursor(MOUSE_CURSOR_POINTER);
179 if ( fgGetBool( "/sim/mouse/hide-cursor", true ) ) {
180 if ( m.x != m.save_x || m.y != m.save_y ) {
181 m.timeout = fgGetDouble( "/sim/mouse/cursor-timeout-sec", 10.0 );
182 if (fgGetMouseCursor() == MOUSE_CURSOR_NONE)
183 fgSetMouseCursor(m.modes[mode].cursor);
186 if ( m.timeout <= 0.0 ) {
187 fgSetMouseCursor(MOUSE_CURSOR_NONE);
195 activePickCallbacks.update( dt );
198 FGMouseInput::mouse::mouse ()
209 FGMouseInput::mouse::~mouse ()
214 FGMouseInput::mouse_mode::mouse_mode ()
215 : cursor(MOUSE_CURSOR_POINTER),
222 FGMouseInput::mouse_mode::~mouse_mode ()
224 // FIXME: memory leak
225 // for (int i = 0; i < KEYMOD_MAX; i++) {
227 // for (j = 0; i < x_bindings[i].size(); j++)
228 // delete bindings[i][j];
229 // for (j = 0; j < y_bindings[i].size(); j++)
230 // delete bindings[i][j];
235 void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
237 int modifiers = fgGetKeyModifiers();
239 mouse &m = bindings[0];
240 mouse_mode &mode = m.modes[m.current_mode];
242 // Let the property manager know.
243 if (b >= 0 && b < MAX_MOUSE_BUTTONS)
244 m.mouse_button_nodes[b]->setBoolValue(updown == MOUSE_BUTTON_DOWN);
246 // Pass on to PUI and the panel if
247 // requested, and return if one of
248 // them consumes the event.
250 if (updown != MOUSE_BUTTON_DOWN) {
251 // Execute the mouse up event in any case, may be we should
252 // stop processing here?
253 while (!activePickCallbacks[b].empty()) {
254 activePickCallbacks[b].front()->buttonReleased();
255 activePickCallbacks[b].pop_front();
259 if (mode.pass_through) {
260 if (0 <= x && 0 <= y && puMouse(b, updown, x, y))
262 else if (0 <= x && 0 <= y && (globals->get_current_panel() != 0) &&
263 globals->get_current_panel()->getVisibility() &&
264 globals->get_current_panel()->doMouseAction(b, updown, x, y))
266 else if (0 <= x && 0 <= y && fgHandle3DPanelMouseEvent(b, updown, x, y))
269 // pui didn't want the click event so compute a
270 // scenegraph intersection point corresponding to the mouse click
271 if (updown == MOUSE_BUTTON_DOWN) {
272 activePickCallbacks.init( b, ea );
277 // OK, PUI and the panel didn't want the click
278 if (b >= MAX_MOUSE_BUTTONS) {
279 SG_LOG(SG_INPUT, SG_ALERT, "Mouse button " << b
280 << " where only " << MAX_MOUSE_BUTTONS << " expected");
284 m.modes[m.current_mode].buttons[b].update( modifiers, 0 != updown, x, y);
287 void FGMouseInput::doMouseMotion (int x, int y)
289 // Don't call fgGetKeyModifiers() here, until we are using a
290 // toolkit that supports getting the mods from outside a key
291 // callback. Glut doesn't.
292 int modifiers = KEYMOD_NONE;
294 int xsize = fgGetInt("/sim/startup/xsize", 800);
295 int ysize = fgGetInt("/sim/startup/ysize", 600);
297 mouse &m = bindings[0];
299 if (m.current_mode < 0 || m.current_mode >= m.nModes) {
304 mouse_mode &mode = m.modes[m.current_mode];
306 // Pass on to PUI if requested, and return
307 // if PUI consumed the event.
308 if (mode.pass_through && puMouse(x, y)) {
314 // OK, PUI didn't want the event,
315 // so we can play with it.
318 for (unsigned int i = 0; i < mode.x_bindings[modifiers].size(); i++)
319 mode.x_bindings[modifiers][i]->fire(double(delta), double(xsize));
323 for (unsigned int i = 0; i < mode.y_bindings[modifiers].size(); i++)
324 mode.y_bindings[modifiers][i]->fire(double(delta), double(ysize));
327 // Constrain the mouse if requested
328 if (mode.constrained) {
329 bool need_warp = false;
330 if (x <= (xsize * .25) || x >= (xsize * .75)) {
335 if (y <= (ysize * .25) || y >= (ysize * .75)) {
345 fgSetInt("/devices/status/mice/mouse/x", m.x = x);
348 fgSetInt("/devices/status/mice/mouse/y", m.y = y);
351 void FGMouseInput::mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
354 mouseInput->doMouseClick(button, updown, x, y, mainWindow, ea);
357 void FGMouseInput::mouseMotionHandler(int x, int y)
360 mouseInput->doMouseMotion(x, y);