1 // Copyright (C) 2008 Tim Moore
2 //
3 // This program is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU General Public License as
5 // published by the Free Software Foundation; either version 2 of the
6 // License, or (at your option) any later version.
7 //
8 // This program is distributed in the hope that it will be useful, but
9 // WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16
17 #include "config.h"
18
19 #include "WindowBuilder.hxx"
20 #include "WindowSystemAdapter.hxx"
21 #include <Main/fg_props.hxx>
22 #include <osg/Version>
23
24 #include <GUI/MessageBox.hxx>
25
26 #include <sstream>
27 #include <limits>
28
29 #if defined(SG_MAC)
30 #include <osgViewer/api/Cocoa/GraphicsWindowCocoa>
31 #endif
32
33 #if defined(HAVE_QT)
34 #include "GraphicsWindowQt5.hxx"
35 #endif
36
37
38 using namespace std;
39 using namespace osg;
40
41 namespace flightgear
42 {
makeName(const string & prefix,int num)43 string makeName(const string& prefix, int num)
44 {
45 stringstream stream;
46 stream << prefix << num;
47 return stream.str();
48 }
49
50 ref_ptr<WindowBuilder> WindowBuilder::windowBuilder;
51
52 const string WindowBuilder::defaultWindowName("FlightGear");
53
54 // default to true (historical behaviour), we will clear the flag if
55 // we run another GUI.
56 bool WindowBuilder::poseAsStandaloneApp = true;
57
initWindowBuilder(bool stencil)58 void WindowBuilder::initWindowBuilder(bool stencil)
59 {
60 windowBuilder = new WindowBuilder(stencil);
61 }
62
WindowBuilder(bool stencil)63 WindowBuilder::WindowBuilder(bool stencil) : defaultCounter(0)
64 {
65 #if defined (HAVE_QT)
66 usingQtGraphicsWindow = fgGetBool("/sim/rendering/graphics-window-qt", false);
67 #endif
68 makeDefaultTraits(stencil);
69 }
70
makeDefaultTraits(bool stencil)71 void WindowBuilder::makeDefaultTraits(bool stencil)
72 {
73 GraphicsContext::WindowingSystemInterface* wsi
74 = osg::GraphicsContext::getWindowingSystemInterface();
75 #if defined(HAVE_QT) && OSG_VERSION_GREATER_THAN(3, 5, 9)
76 if (usingQtGraphicsWindow) {
77 // use the correct WSI for OpenSceneGraph >= 3.6
78 wsi = osg::GraphicsContext::getWindowingSystemInterface("FlightGearQt5");
79 }
80 #endif
81
82
83 defaultTraits = new osg::GraphicsContext::Traits;
84 auto traits = defaultTraits.get();
85
86 traits->readDISPLAY();
87 if (traits->displayNum < 0)
88 traits->displayNum = 0;
89 if (traits->screenNum < 0)
90 traits->screenNum = 0;
91
92 int bpp = fgGetInt("/sim/rendering/bits-per-pixel");
93 int cbits = (bpp <= 16) ? 5 : 8;
94 int zbits = (bpp <= 16) ? 16 : 24;
95 traits->red = traits->green = traits->blue = cbits;
96 traits->depth = zbits;
97
98 if (stencil)
99 traits->stencil = 8;
100
101 traits->doubleBuffer = true;
102 traits->mipMapGeneration = true;
103 traits->windowName = "FlightGear";
104 // XXX should check per window too.
105 traits->sampleBuffers = fgGetInt("/sim/rendering/multi-sample-buffers", traits->sampleBuffers);
106 traits->samples = fgGetInt("/sim/rendering/multi-samples", traits->samples);
107 traits->vsync = fgGetBool("/sim/rendering/vsync-enable", traits->vsync);
108
109 const bool wantFullscreen = fgGetBool("/sim/startup/fullscreen");
110 if (usingQtGraphicsWindow) {
111 #if defined(HAVE_QT)
112 // fullscreen is handled by Qt natively
113 // we will check and set fullscreen mode when building
114 // the window instance
115 auto data = new GraphicsWindowQt5::WindowData;
116 data->createFullscreen = wantFullscreen;
117 data->isPrimaryWindow = true;
118
119 #if OSG_VERSION_GREATER_THAN(3, 5, 9)
120 traits->windowingSystemPreference = "FlightGearQt5";
121 #endif
122 traits->inheritedWindowData = data;
123 traits->windowDecoration = true;
124 traits->supportsResize = true;
125 traits->width = fgGetInt("/sim/startup/xsize");
126 traits->height = fgGetInt("/sim/startup/ysize");
127
128 // these are marker values to tell GraphicsWindowQt5 to use default x/y
129 traits->x = std::numeric_limits<int>::max();
130 traits->y = std::numeric_limits<int>::max();
131 #else
132 SG_LOG(SG_VIEW,SG_ALERT,"requested Qt GraphicsWindow in non-Qt build");
133 #endif
134 } else {
135 unsigned screenwidth = 0;
136 unsigned screenheight = 0;
137 // this is a deprecated method, should be screen-aware.
138 wsi->getScreenResolution(*traits, screenwidth, screenheight);
139
140 // handle fullscreen manually
141 traits->windowDecoration = !wantFullscreen;
142 if (!traits->windowDecoration) {
143 // fullscreen
144 traits->supportsResize = false;
145 traits->width = screenwidth;
146 traits->height = screenheight;
147 SG_LOG(SG_VIEW,SG_DEBUG,"Using full screen size for window: " << screenwidth << " x " << screenheight);
148 } else {
149 // window
150 int w = fgGetInt("/sim/startup/xsize");
151 int h = fgGetInt("/sim/startup/ysize");
152 traits->supportsResize = true;
153 traits->width = w;
154 traits->height = h;
155 if ((w>0)&&(h>0))
156 {
157 traits->x = ((unsigned)w>screenwidth) ? 0 : (screenwidth-w)/3;
158 traits->y = ((unsigned)h>screenheight) ? 0 : (screenheight-h)/3;
159 }
160 SG_LOG(SG_VIEW,SG_DEBUG,"Using initial window size: " << w << " x " << h);
161 }
162 }
163 }
164
165 } // of namespace flightgear
166
167 namespace
168 {
169 // Helper functions that set a value based on a property if it exists,
170 // returning 1 if the value was set.
171
setFromProperty(string & place,const SGPropertyNode * node,const char * name)172 inline int setFromProperty(string& place, const SGPropertyNode* node,
173 const char* name)
174 {
175 const SGPropertyNode* valNode = node->getNode(name);
176 if (valNode) {
177 place = valNode->getStringValue();
178 return 1;
179 }
180 return 0;
181 }
182
setFromProperty(int & place,const SGPropertyNode * node,const char * name)183 inline int setFromProperty(int& place, const SGPropertyNode* node,
184 const char* name)
185 {
186 const SGPropertyNode* valNode = node->getNode(name);
187 if (valNode) {
188 place = valNode->getIntValue();
189 return 1;
190 }
191 return 0;
192 }
193
setFromProperty(bool & place,const SGPropertyNode * node,const char * name)194 inline int setFromProperty(bool& place, const SGPropertyNode* node,
195 const char* name)
196 {
197 const SGPropertyNode* valNode = node->getNode(name);
198 if (valNode) {
199 place = valNode->getBoolValue();
200 return 1;
201 }
202 return 0;
203 }
204 }
205
206 namespace flightgear
207 {
208
setFullscreenTraits(const SGPropertyNode * winNode,GraphicsContext::Traits * traits)209 void WindowBuilder::setFullscreenTraits(const SGPropertyNode* winNode, GraphicsContext::Traits* traits)
210 {
211 const SGPropertyNode* orrNode = winNode->getNode("overrideRedirect");
212 bool overrideRedirect = orrNode && orrNode->getBoolValue();
213 traits->overrideRedirect = overrideRedirect;
214
215 #if defined(HAVE_QT)
216 if (usingQtGraphicsWindow) {
217 auto data = new GraphicsWindowQt5::WindowData;
218 data->createFullscreen = true;
219 traits->inheritedWindowData = data;
220 traits->windowDecoration = winNode->getBoolValue("decoration");
221 } else
222 #endif
223 // this codepath is mandatory on non-Qt builds
224 {
225 traits->windowDecoration = false;
226
227 unsigned int width = 0;
228 unsigned int height = 0;
229 auto wsi = osg::GraphicsContext::getWindowingSystemInterface();
230 wsi->getScreenResolution(*traits, width, height);
231 traits->width = width;
232 traits->height = height;
233 traits->supportsResize = false;
234 traits->x = 0;
235 traits->y = 0;
236 }
237 }
238
setWindowedTraits(const SGPropertyNode * winNode,GraphicsContext::Traits * traits)239 bool WindowBuilder::setWindowedTraits(const SGPropertyNode* winNode, GraphicsContext::Traits* traits)
240 {
241 bool customTraits = false;
242 #if defined(HAVE_QT)
243 if (usingQtGraphicsWindow) {
244 if (winNode->hasValue("fullscreen")) {
245 auto data = new GraphicsWindowQt5::WindowData;
246 data->createFullscreen = false;
247 traits->inheritedWindowData = data;
248 customTraits = true;
249 }
250 customTraits |= setFromProperty(traits->windowDecoration, winNode, "decoration");
251 customTraits |= setFromProperty(traits->width, winNode, "width");
252 customTraits |= setFromProperty(traits->height, winNode, "height");
253 } else
254 #endif
255 {
256 int resizable = 0;
257 const SGPropertyNode* fullscreenNode = winNode->getNode("fullscreen");
258 if (fullscreenNode && !fullscreenNode->getBoolValue())
259 {
260 traits->windowDecoration = true;
261 resizable = 1;
262 }
263 resizable |= setFromProperty(traits->windowDecoration, winNode, "decoration");
264 resizable |= setFromProperty(traits->width, winNode, "width");
265 resizable |= setFromProperty(traits->height, winNode, "height");
266 if (resizable) {
267 traits->supportsResize = true;
268 customTraits = true;
269 }
270 }
271
272 return customTraits;
273 }
274
setMacPoseAsStandaloneApp(GraphicsContext::Traits * traits)275 void WindowBuilder::setMacPoseAsStandaloneApp(GraphicsContext::Traits* traits)
276 {
277 #if defined(SG_MAC)
278 if (!usingQtGraphicsWindow) {
279 // this logic is unecessary if using a Qt window, since everything
280 // plays together nicely
281 int flags = osgViewer::GraphicsWindowCocoa::WindowData::CheckForEvents;
282
283 // avoid both QApplication and OSG::CocoaViewer doing single-application
284 // init (Apple menu, making front process, etc)
285 if (poseAsStandaloneApp) {
286 flags |= osgViewer::GraphicsWindowCocoa::WindowData::PoseAsStandaloneApp;
287 }
288 traits->inheritedWindowData = new osgViewer::GraphicsWindowCocoa::WindowData(flags);
289 }
290 #endif
291 }
292
buildWindow(const SGPropertyNode * winNode)293 GraphicsWindow* WindowBuilder::buildWindow(const SGPropertyNode* winNode)
294 {
295 WindowSystemAdapter* wsa = WindowSystemAdapter::getWSA();
296 string windowName;
297 if (winNode->hasChild("window-name"))
298 windowName = winNode->getStringValue("window-name");
299 else if (winNode->hasChild("name"))
300 windowName = winNode->getStringValue("name");
301 GraphicsWindow* result = 0;
302 if (!windowName.empty()) {
303 // look for an existing window and return that
304 result = wsa->findWindow(windowName);
305 if (result)
306 return result;
307 }
308 auto traits = new GraphicsContext::Traits(*defaultTraits);
309 int traitsSet = setFromProperty(traits->hostName, winNode, "host-name");
310 traitsSet |= setFromProperty(traits->displayNum, winNode, "display");
311 traitsSet |= setFromProperty(traits->screenNum, winNode, "screen");
312
313 const SGPropertyNode* fullscreenNode = winNode->getNode("fullscreen");
314 if (fullscreenNode && fullscreenNode->getBoolValue()) {
315 setFullscreenTraits(winNode, traits);
316 traitsSet = 1;
317 } else {
318 traitsSet |= setWindowedTraits(winNode, traits);
319 }
320 traitsSet |= setFromProperty(traits->x, winNode, "x");
321 traitsSet |= setFromProperty(traits->y, winNode, "y");
322 if (!windowName.empty() && windowName != traits->windowName) {
323 traits->windowName = windowName;
324 traitsSet = 1;
325 } else if (traitsSet) {
326 traits->windowName = makeName("FlightGear", defaultCounter++);
327 }
328
329 setMacPoseAsStandaloneApp(traits);
330
331 bool drawGUI = false;
332 traitsSet |= setFromProperty(drawGUI, winNode, "gui");
333 if (traitsSet) {
334 #if defined (HAVE_QT)
335 if (usingQtGraphicsWindow) {
336 // this assumes the user only sets the 'gui' flag on one window, not ideal
337 auto data = static_cast<GraphicsWindowQt5::WindowData*>(traits->inheritedWindowData.get());
338 data->isPrimaryWindow = drawGUI;
339 }
340 #endif
341
342 GraphicsContext* gc = GraphicsContext::createGraphicsContext(traits);
343 if (gc) {
344 GraphicsWindow* window = WindowSystemAdapter::getWSA()
345 ->registerWindow(gc, traits->windowName);
346 if (drawGUI)
347 window->flags |= GraphicsWindow::GUI;
348 return window;
349 } else {
350 return 0;
351 }
352 } else {
353 // XXX What if the window has no traits, but does have a name?
354 // We should create a "default window" registered with that name.
355 return getDefaultWindow();
356 }
357 }
358
getDefaultWindow()359 GraphicsWindow* WindowBuilder::getDefaultWindow()
360 {
361 GraphicsWindow* defaultWindow
362 = WindowSystemAdapter::getWSA()->findWindow(defaultWindowName);
363 if (defaultWindow)
364 return defaultWindow;
365
366 // create if it, if necessary
367 GraphicsContext::Traits* traits
368 = new GraphicsContext::Traits(*defaultTraits);
369 traits->windowName = "FlightGear";
370
371 setMacPoseAsStandaloneApp(traits);
372
373 // this may be the point, where we discover OpenGL is broken on the
374 // system.
375 GraphicsContext* gc = GraphicsContext::createGraphicsContext(traits);
376 if (!gc) {
377 flightgear::fatalMessageBoxThenExit("Unable to create window",
378 "FlightGear was unable to create a window supporting 3D rendering (OpenGL). "
379 "This is normally due to outdated graphics drivers, please check if updates are available. ",
380 "Depending on your OS and graphics chipset, updates might come from AMD, nVidia or Intel.");
381 return nullptr; // unreachable anyway
382 }
383
384 defaultWindow = WindowSystemAdapter::getWSA()->registerWindow(gc, defaultWindowName);
385 return defaultWindow;
386
387 }
388
setPoseAsStandaloneApp(bool b)389 void WindowBuilder::setPoseAsStandaloneApp(bool b)
390 {
391 poseAsStandaloneApp = b;
392 }
393
394 } // of namespace flightgear
395