]> git.mxchange.org Git - flightgear.git/blob - src/Main/FGManipulator.cxx
header cleanups
[flightgear.git] / src / Main / FGManipulator.cxx
1 #ifdef HAVE_CONFIG_H
2 #  include <config.h>
3 #endif
4 #include <osg/Math>
5 #include <osgViewer/Viewer>
6 #include <plib/pu.h>
7 #include <Main/fg_props.hxx>
8 #include "FGManipulator.hxx"
9
10 #if !defined(X_DISPLAY_MISSING)
11 #define X_DOUBLE_SCROLL_BUG 1
12 #endif
13
14 const int displayStatsKey = 1;
15 const int printStatsKey = 2;
16
17
18 // The manipulator is responsible for updating a Viewer's camera. Its
19 // event handling method is also a convenient place to run the FG idle
20 // and draw handlers.
21
22 FGManipulator::FGManipulator() :
23     idleHandler(0),
24     drawHandler(0),
25     windowResizeHandler(0),
26     keyHandler(0),
27     mouseClickHandler(0),
28     mouseMotionHandler(0),
29     statsHandler(new osgViewer::StatsHandler),
30     statsEvent(new osgGA::GUIEventAdapter),
31     statsType(osgViewer::StatsHandler::NO_STATS),
32     currentModifiers(0),
33     resizable(true),
34     mouseWarped(false),
35     scrollButtonPressed(false)
36 {
37     using namespace osgGA;
38     statsHandler->setKeyEventTogglesOnScreenStats(displayStatsKey);
39     statsHandler->setKeyEventPrintsOutStats(printStatsKey);
40     statsEvent->setEventType(GUIEventAdapter::KEYDOWN);
41
42     // OSG reports NumPad keycodes independent of the NumLock modifier.
43     // Both KP-4 and KP-Left are reported as KEY_KP_Left (0xff96), so we
44     // have to generate the locked keys ourselves.
45     numlockKeyMap[GUIEventAdapter::KEY_KP_Insert]  = '0';
46     numlockKeyMap[GUIEventAdapter::KEY_KP_End] = '1';
47     numlockKeyMap[GUIEventAdapter::KEY_KP_Down] = '2';
48     numlockKeyMap[GUIEventAdapter::KEY_KP_Page_Down] = '3';
49     numlockKeyMap[GUIEventAdapter::KEY_KP_Left] = '4';
50     numlockKeyMap[GUIEventAdapter::KEY_KP_Begin] = '5';
51     numlockKeyMap[GUIEventAdapter::KEY_KP_Right] = '6';
52     numlockKeyMap[GUIEventAdapter::KEY_KP_Home] = '7';
53     numlockKeyMap[GUIEventAdapter::KEY_KP_Up] = '8';
54     numlockKeyMap[GUIEventAdapter::KEY_KP_Page_Up] = '9';
55
56     for (int i = 0; i < 128; i++)
57         release_keys[i] = i;
58 }
59
60 void FGManipulator::setByMatrix(const osg::Matrixd& matrix)
61 {
62     // Yuck
63     position = matrix.getTrans();
64     attitude = matrix.getRotate();
65 }
66
67 osg::Matrixd FGManipulator::getMatrix() const
68 {
69     return osg::Matrixd::rotate(attitude) * osg::Matrixd::translate(position);
70 }
71
72 osg::Matrixd FGManipulator::getInverseMatrix() const
73 {
74     return (osg::Matrixd::translate(-position)
75             * osg::Matrixd::rotate(attitude.inverse())) ;
76 }
77
78 // Not used, but part of the interface.
79 void FGManipulator::setNode(osg::Node* node)
80 {
81     _node = node;
82 }
83
84 const osg::Node* FGManipulator::getNode() const
85 {
86     return _node.get();
87 }
88
89 osg::Node* FGManipulator::getNode()
90 {
91     return _node.get();
92 }
93
94 // Translate OSG modifier mask to FG modifier mask.
95 static int osgToFGModifiers(int modifiers)
96 {
97     int result = 0;
98     if (modifiers & osgGA::GUIEventAdapter::MODKEY_SHIFT)
99         result |= KEYMOD_SHIFT;
100
101     if (modifiers & osgGA::GUIEventAdapter::MODKEY_CTRL)
102         result |= KEYMOD_CTRL;
103
104     if (modifiers & osgGA::GUIEventAdapter::MODKEY_ALT)
105         result |= KEYMOD_ALT;
106
107     if (modifiers & osgGA::GUIEventAdapter::MODKEY_META)
108         result |= KEYMOD_META;
109
110     if (modifiers & osgGA::GUIEventAdapter::MODKEY_SUPER)
111         result |= KEYMOD_SUPER;
112
113     if (modifiers & osgGA::GUIEventAdapter::MODKEY_HYPER)
114         result |= KEYMOD_HYPER;
115     return result;
116 }
117
118 void FGManipulator::init(const osgGA::GUIEventAdapter& ea,
119                          osgGA::GUIActionAdapter& us)
120 {
121     currentModifiers = osgToFGModifiers(ea.getModKeyMask());
122     (void)handle(ea, us);
123 }
124
125 static bool
126 eventToViewport(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us,
127                 int& x, int& y)
128 {
129     x = -1;
130     y = -1;
131
132     const osgViewer::Viewer* viewer;
133     viewer = dynamic_cast<const osgViewer::Viewer*>(&us);
134     if (!viewer)
135         return false;
136
137     float lx, ly;
138     const osg::Camera* camera;
139     camera = viewer->getCameraContainingPosition(ea.getX(), ea.getY(), lx, ly);
140
141     if (!(camera && fgOSIsMainCamera(camera)))
142         return false;
143
144     x = int(lx);
145     y = int(camera->getViewport()->height() - ly);
146
147     return true;
148 }
149
150 bool FGManipulator::handle(const osgGA::GUIEventAdapter& ea,
151                            osgGA::GUIActionAdapter& us)
152 {
153     int x = 0;
154     int y = 0;
155
156     switch (ea.getEventType()) {
157     case osgGA::GUIEventAdapter::FRAME:
158         if (idleHandler)
159             (*idleHandler)();
160         if (drawHandler)
161             (*drawHandler)();
162         mouseWarped = false;
163         handleStats(us);
164         return true;
165     case osgGA::GUIEventAdapter::KEYDOWN:
166     case osgGA::GUIEventAdapter::KEYUP:
167     {
168         int key, modmask;
169         handleKey(ea, key, modmask);
170         eventToViewport(ea, us, x, y);
171         if (keyHandler)
172             (*keyHandler)(key, modmask, x, y);
173         return true;
174     }
175     case osgGA::GUIEventAdapter::PUSH:
176     case osgGA::GUIEventAdapter::RELEASE:
177     {
178         bool mainWindow = eventToViewport(ea, us, x, y);
179         int button = 0;
180         switch (ea.getButton()) {
181         case osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON:
182             button = 0;
183             break;
184         case osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON:
185             button = 1;
186             break;
187         case osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON:
188             button = 2;
189             break;
190         }
191         if (mouseClickHandler)
192             (*mouseClickHandler)(button,
193                                  (ea.getEventType()
194                                   == osgGA::GUIEventAdapter::RELEASE), x, y, mainWindow, &ea);
195         return true;
196     }
197     case osgGA::GUIEventAdapter::SCROLL:
198     {
199         bool mainWindow = eventToViewport(ea, us, x, y);
200 #ifdef X_DOUBLE_SCROLL_BUG
201         scrollButtonPressed = !scrollButtonPressed;
202         if (!scrollButtonPressed) // Drop the button release event
203             return true;
204 #endif
205         int button;
206         if (ea.getScrollingMotion() == osgGA::GUIEventAdapter::SCROLL_UP)
207             button = 3;
208         else
209             button = 4;
210         if (mouseClickHandler) {
211             (*mouseClickHandler)(button, 0, x, y, mainWindow, &ea);
212             (*mouseClickHandler)(button, 1, x, y, mainWindow, &ea);
213         }
214         return true;
215     }
216     case osgGA::GUIEventAdapter::MOVE:
217     case osgGA::GUIEventAdapter::DRAG:
218         // If we warped the mouse, then disregard all pointer motion
219         // events for this frame. We really want to flush the event
220         // queue of mouse events, but don't have the ability to do
221         // that with osgViewer.
222         if (mouseWarped)
223             return true;
224         if (eventToViewport(ea, us, x, y) && mouseMotionHandler)
225             (*mouseMotionHandler)(x, y);
226         return true;
227     case osgGA::GUIEventAdapter::RESIZE:
228         if (resizable && windowResizeHandler)
229             (*windowResizeHandler)(ea.getWindowWidth(), ea.getWindowHeight());
230         return true;
231      case osgGA::GUIEventAdapter::CLOSE_WINDOW:
232     case osgGA::GUIEventAdapter::QUIT_APPLICATION:
233         fgOSExit(0);
234         return true;
235     default:
236         return false;
237     }
238 }
239
240 void FGManipulator::handleKey(const osgGA::GUIEventAdapter& ea, int& key,
241                               int& modifiers)
242 {
243     using namespace osgGA;
244     key = ea.getKey();
245     // XXX Probably other translations are needed too.
246     switch (key) {
247     case GUIEventAdapter::KEY_Escape:      key = 0x1b; break;
248     case GUIEventAdapter::KEY_Return:      key = '\n'; break;
249     case GUIEventAdapter::KEY_BackSpace:   key = '\b'; break;
250     case GUIEventAdapter::KEY_Delete:      key = 0x7f; break;
251     case GUIEventAdapter::KEY_Tab:         key = '\t'; break;
252     case GUIEventAdapter::KEY_Left:        key = PU_KEY_LEFT;      break;
253     case GUIEventAdapter::KEY_Up:          key = PU_KEY_UP;        break;
254     case GUIEventAdapter::KEY_Right:       key = PU_KEY_RIGHT;     break;
255     case GUIEventAdapter::KEY_Down:        key = PU_KEY_DOWN;      break;
256     case GUIEventAdapter::KEY_Page_Up:     key = PU_KEY_PAGE_UP;   break;
257     case GUIEventAdapter::KEY_Page_Down:   key = PU_KEY_PAGE_DOWN; break;
258     case GUIEventAdapter::KEY_Home:        key = PU_KEY_HOME;      break;
259     case GUIEventAdapter::KEY_End:         key = PU_KEY_END;       break;
260     case GUIEventAdapter::KEY_Insert:      key = PU_KEY_INSERT;    break;
261     case GUIEventAdapter::KEY_F1:          key = PU_KEY_F1;        break;
262     case GUIEventAdapter::KEY_F2:          key = PU_KEY_F2;        break;
263     case GUIEventAdapter::KEY_F3:          key = PU_KEY_F3;        break;
264     case GUIEventAdapter::KEY_F4:          key = PU_KEY_F4;        break;
265     case GUIEventAdapter::KEY_F5:          key = PU_KEY_F5;        break;
266     case GUIEventAdapter::KEY_F6:          key = PU_KEY_F6;        break;
267     case GUIEventAdapter::KEY_F7:          key = PU_KEY_F7;        break;
268     case GUIEventAdapter::KEY_F8:          key = PU_KEY_F8;        break;
269     case GUIEventAdapter::KEY_F9:          key = PU_KEY_F9;        break;
270     case GUIEventAdapter::KEY_F10:         key = PU_KEY_F10;       break;
271     case GUIEventAdapter::KEY_F11:         key = PU_KEY_F11;       break;
272     case GUIEventAdapter::KEY_F12:         key = PU_KEY_F12;       break;
273     case GUIEventAdapter::KEY_KP_Delete:   key = '.';  break;
274     case GUIEventAdapter::KEY_KP_Enter:    key = '\r'; break;
275     case GUIEventAdapter::KEY_KP_Add:      key = '+';  break;
276     case GUIEventAdapter::KEY_KP_Divide:   key = '/';  break;
277     case GUIEventAdapter::KEY_KP_Multiply: key = '*';  break;
278     case GUIEventAdapter::KEY_KP_Subtract: key = '-';  break;
279     }
280     osgGA::GUIEventAdapter::EventType eventType = ea.getEventType();
281
282     std::map<int, int>::iterator numPadIter = numlockKeyMap.find(key);
283
284     if (numPadIter != numlockKeyMap.end()) {
285         if (ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_NUM_LOCK) {
286             key = numPadIter->second;
287         }
288     }
289
290     modifiers = osgToFGModifiers(ea.getModKeyMask());
291     currentModifiers = modifiers;
292     if (eventType == osgGA::GUIEventAdapter::KEYUP)
293         modifiers |= KEYMOD_RELEASED;
294
295     // Release the letter key, for which the key press was reported. This
296     // is to deal with Ctrl-press -> a-press -> Ctrl-release -> a-release
297     // correctly.
298     if (key >= 0 && key < 128) {
299         if (modifiers & KEYMOD_RELEASED) {
300             key = release_keys[key];
301         } else {
302             release_keys[key] = key;
303             if (key >= 1 && key <= 26) {
304                 release_keys[key + '@'] = key;
305                 release_keys[key + '`'] = key;
306             } else if (key >= 'A' && key <= 'Z') {
307                 release_keys[key - '@'] = key;
308                 release_keys[tolower(key)] = key;
309             } else if (key >= 'a' && key <= 'z') {
310                 release_keys[key - '`'] = key;
311                 release_keys[toupper(key)] = key;
312             }
313         }
314     }
315 }
316
317 void FGManipulator::handleStats(osgGA::GUIActionAdapter& us)
318 {
319     static SGPropertyNode_ptr display = fgGetNode("/sim/rendering/on-screen-statistics", true);
320     static SGPropertyNode_ptr print = fgGetNode("/sim/rendering/print-statistics", true);
321
322     int type = display->getIntValue() % osgViewer::StatsHandler::LAST;
323     if (type != statsType) {
324         statsEvent->setKey(displayStatsKey);
325         do {
326             statsType = (statsType + 1) % osgViewer::StatsHandler::LAST;
327             statsHandler->handle(*statsEvent, us);
328         } while (statsType != type);
329
330         display->setIntValue(statsType);
331     }
332
333     if (print->getBoolValue()) {
334         statsEvent->setKey(printStatsKey);
335         statsHandler->handle(*statsEvent, us);
336         print->setBoolValue(false);
337     }
338 }
339