1 // Viewer.cxx -- alternative flightgear viewer application
2 //
3 // Copyright (C) 2009 - 2012  Mathias Froehlich
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 
23 #include "Viewer.hxx"
24 
25 #include <osg/Version>
26 #include <osg/ArgumentParser>
27 #include <osg/ProxyNode>
28 #include <osg/PagedLOD>
29 #include <osgDB/ReadFile>
30 
31 #ifdef __linux__
32 #include <X11/Xlib.h>
33 #include <osgViewer/api/X11/GraphicsWindowX11>
34 #endif
35 
36 #include "MEncoderCaptureOperation.hxx"
37 
38 #include <cassert>
39 
40 namespace fgviewer  {
41 
Viewer(osg::ArgumentParser & arguments)42 Viewer::Viewer(osg::ArgumentParser& arguments) :
43     osgViewer::Viewer(arguments),
44     _sceneDataGroup(new osg::Group),
45     _timeIncrement(SGTimeStamp::fromSec(0)),
46     _simTime(SGTimeStamp::fromSec(0))
47 {
48     /// Careful: this method really assigns the sceneDataGroup to all cameras!
49     /// FIXME the 'useMasterScene' flag at the slave is able to get around that!!!
50     osgViewer::Viewer::setSceneData(_sceneDataGroup.get());
51     /// The only changed default that is renderer independent ...
52     getCamera()->setClearColor(osg::Vec4(0, 0, 0, 0));
53 }
54 
~Viewer()55 Viewer::~Viewer()
56 {
57     stopThreading();
58 
59 #if FG_HAVE_HLA
60     if (_viewerFederate.valid())
61         _viewerFederate->shutdown();
62     _viewerFederate = 0;
63 #endif
64 }
65 
66 bool
readCameraConfig(const SGPropertyNode & viewerNode)67 Viewer::readCameraConfig(const SGPropertyNode& viewerNode)
68 {
69     // Collect and realize all windows
70     for (int i = 0; i < viewerNode.nChildren(); ++i) {
71         // FIXME support window, fullscreen, offscreen
72         const SGPropertyNode* windowNode = viewerNode.getChild(i);
73         if (!windowNode || windowNode->getNameString() != "window")
74             continue;
75 
76         std::string name = windowNode->getStringValue("name", "");
77         if (name.empty()) {
78             SG_LOG(SG_VIEW, SG_ALERT, "Ignoring unnamed window!");
79             return false;
80         }
81 
82         Drawable* drawable = getOrCreateDrawable(name);
83 
84         osg::GraphicsContext::ScreenIdentifier screenIdentifier;
85         screenIdentifier = getScreenIdentifier(windowNode->getStringValue("display", ""));
86         drawable->setScreenIdentifier(screenIdentifier.displayName());
87 
88         if (windowNode->getBoolValue("fullscreen", false)) {
89             osg::GraphicsContext::ScreenSettings screenSettings;
90             screenSettings = getScreenSettings(screenIdentifier);
91             drawable->setPosition(SGVec2i(0, 0));
92             drawable->setSize(SGVec2i(screenSettings.width, screenSettings.height));
93             drawable->setFullscreen(true);
94             drawable->setOffscreen(false);
95 
96         } else if (windowNode->getBoolValue("video", false)) {
97             drawable->setPosition(SGVec2i(0, 0));
98             SGVec2i size;
99             size[0] = windowNode->getIntValue("geometry/width", 1366);
100             size[1] = windowNode->getIntValue("geometry/height", 768);
101             drawable->setSize(size);
102             drawable->setFullscreen(true);
103             drawable->setOffscreen(true);
104 
105             std::string outputFile = windowNode->getStringValue("output-file", "fgviewer.avi");
106             unsigned fps = windowNode->getIntValue("frames-per-second", 30);
107 
108             /// This is the case for the video writers, have a fixed time increment
109             _timeIncrement = SGTimeStamp::fromSec(1.0/fps);
110 
111             MEncoderCaptureOperation* captureOperation;
112             captureOperation = new MEncoderCaptureOperation(outputFile, fps);
113             osgViewer::ScreenCaptureHandler* captureHandler;
114             captureHandler = new osgViewer::ScreenCaptureHandler(captureOperation, -1);
115             addEventHandler(captureHandler);
116             captureHandler->startCapture();
117 
118         } else {
119 
120             SGVec2i position;
121             position[0] = windowNode->getIntValue("geometry/x", 0);
122             position[1] = windowNode->getIntValue("geometry/y", 0);
123             drawable->setPosition(position);
124             SGVec2i size;
125             size[0] = windowNode->getIntValue("geometry/width", 1366);
126             size[1] = windowNode->getIntValue("geometry/height", 768);
127             drawable->setSize(size);
128             drawable->setFullscreen(false);
129             drawable->setOffscreen(false);
130         }
131     }
132 
133     for (int i = 0; i < viewerNode.nChildren(); ++i) {
134         const SGPropertyNode* cameraNode = viewerNode.getChild(i);
135         if (!cameraNode || cameraNode->getNameString() != "camera")
136             continue;
137 
138         std::string name = cameraNode->getStringValue("name", "");
139         if (name.empty()) {
140             SG_LOG(SG_VIEW, SG_ALERT, "Camera configuration needs a name!");
141             return false;
142         }
143 
144         SlaveCamera* slaveCamera = getOrCreateSlaveCamera(name);
145 
146         std::string drawableName = cameraNode->getStringValue("window", "");
147         if (drawableName.empty()) {
148             SG_LOG(SG_VIEW, SG_ALERT, "Camera configuration needs an assigned window!");
149             return false;
150         }
151         Drawable* drawable = getDrawable(drawableName);
152         if (!drawable) {
153             SG_LOG(SG_VIEW, SG_ALERT, "Camera configuration \"" << name << "\" needs a drawable configured!");
154             return false;
155         }
156         slaveCamera->setDrawableName(drawableName);
157         drawable->attachSlaveCamera(slaveCamera);
158 
159         SGVec2i size = drawable->getSize();
160         SGVec4i viewport(0, 0, size[0], size[1]);
161         viewport[0] = cameraNode->getIntValue("viewport/x", viewport[0]);
162         viewport[1] = cameraNode->getIntValue("viewport/y", viewport[1]);
163         viewport[2] = cameraNode->getIntValue("viewport/width", viewport[2]);
164         viewport[3] = cameraNode->getIntValue("viewport/height", viewport[3]);
165         slaveCamera->setViewport(viewport);
166 
167         double headingDeg = cameraNode->getDoubleValue("view-offset/heading-deg", 0);
168         double pitchDeg = cameraNode->getDoubleValue("view-offset/pitch-deg", 0);
169         double rollDeg = cameraNode->getDoubleValue("view-offset/roll-deg", 0);
170         slaveCamera->setViewOffsetDeg(headingDeg, pitchDeg, rollDeg);
171 
172         // Care for the reference points
173         if (const SGPropertyNode* referencePointsNode = cameraNode->getNode("reference-points")) {
174             for (int j = 0; j < referencePointsNode->nChildren(); ++j) {
175                 const SGPropertyNode* referencePointNode = cameraNode->getNode("reference-point");
176                 if (!referencePointNode)
177                     continue;
178                 std::string name = referencePointNode->getStringValue("name", "");
179                 if (name.empty())
180                     continue;
181                 osg::Vec2 point;
182                 point[0] = referencePointNode->getDoubleValue("x", 0);
183                 point[1] = referencePointNode->getDoubleValue("y", 0);
184                 slaveCamera->setProjectionReferencePoint(name, point);
185             }
186         }
187         // Define 4 reference points by monitor dimensions
188         else if (const SGPropertyNode* physicalDimensionsNode = cameraNode->getNode("physical-dimensions")) {
189             double physicalWidth = physicalDimensionsNode->getDoubleValue("width", viewport[2]);
190             double physicalHeight = physicalDimensionsNode->getDoubleValue("height", viewport[3]);
191             if (const SGPropertyNode* bezelNode = physicalDimensionsNode->getNode("bezel")) {
192                 double bezelHeightTop = bezelNode->getDoubleValue("top", 0);
193                 double bezelHeightBottom = bezelNode->getDoubleValue("bottom", 0);
194                 double bezelWidthLeft = bezelNode->getDoubleValue("left", 0);
195                 double bezelWidthRight = bezelNode->getDoubleValue("right", 0);
196                 slaveCamera->setMonitorProjectionReferences(physicalWidth, physicalHeight,
197                                                             bezelHeightTop, bezelHeightBottom,
198                                                             bezelWidthLeft, bezelWidthRight);
199             }
200         }
201 
202         // The frustum node takes precedence, as this is the most explicit one.
203         if (const SGPropertyNode* frustumNode = cameraNode->getNode("frustum")) {
204             Frustum frustum(slaveCamera->getAspectRatio());
205             frustum._near = frustumNode->getDoubleValue("near", frustum._near);
206             frustum._left = frustumNode->getDoubleValue("left", frustum._left);
207             frustum._right = frustumNode->getDoubleValue("right", frustum._right);
208             frustum._bottom = frustumNode->getDoubleValue("bottom", frustum._bottom);
209             frustum._top = frustumNode->getDoubleValue("top", frustum._top);
210             slaveCamera->setFrustum(frustum);
211 
212         } else if (const SGPropertyNode* perspectiveNode = cameraNode->getNode("perspective")) {
213             double fieldOfViewDeg = perspectiveNode->getDoubleValue("field-of-view-deg", 55);
214             slaveCamera->setFustumByFieldOfViewDeg(fieldOfViewDeg);
215 
216         } else if (const SGPropertyNode* monitorNode = cameraNode->getNode("monitor")) {
217 
218             std::string referenceCameraName;
219             std::string names[2];
220             std::string referenceNames[2];
221 
222             // FIXME??!!
223             if (const SGPropertyNode* leftOfNode = monitorNode->getNode("left-of")) {
224                 referenceCameraName = leftOfNode->getStringValue("");
225                 names[0] = "lowerRight";
226                 referenceNames[0] = "lowerLeft";
227                 names[1] = "upperRight";
228                 referenceNames[1] = "upperLeft";
229             } else if (const SGPropertyNode* rightOfNode = monitorNode->getNode("right-of")) {
230                 referenceCameraName = rightOfNode->getStringValue("");
231                 names[0] = "lowerLeft";
232                 referenceNames[0] = "lowerRight";
233                 names[1] = "upperLeft";
234                 referenceNames[1] = "upperRight";
235             } else if (const SGPropertyNode* aboveNode = monitorNode->getNode("above")) {
236                 referenceCameraName = aboveNode->getStringValue("");
237                 names[0] = "lowerLeft";
238                 referenceNames[0] = "upperLeft";
239                 names[1] = "lowerRight";
240                 referenceNames[1] = "upperRight";
241             } else if (const SGPropertyNode* belowNode = monitorNode->getNode("below")) {
242                 referenceCameraName = belowNode->getStringValue("");
243                 names[0] = "upperLeft";
244                 referenceNames[0] = "lowerLeft";
245                 names[1] = "upperRight";
246                 referenceNames[1] = "lowerRight";
247             } else {
248                 // names[0] = ;
249                 // referenceNames[0] = ;
250                 // names[1] = ;
251                 // referenceNames[1] = ;
252             }
253 
254             // If we finally found a set of reference points that should match,
255             // then create a relative frustum matching these references
256             if (SlaveCamera* referenceSlaveCamera = getSlaveCamera(referenceCameraName)) {
257                 slaveCamera->setRelativeFrustum(names, *referenceSlaveCamera, referenceNames);
258             } else {
259                 SG_LOG(SG_VIEW, SG_ALERT, "Unable to find reference camera \"" << referenceCameraName
260                        << "\" for camera \"" << name << "\"!");
261             }
262         } else {
263             // Set a proper default taking the current aspect ratio into account
264             slaveCamera->setFustumByFieldOfViewDeg(55);
265         }
266     }
267 
268     return true;
269 }
270 
271 void
setupDefaultCameraConfigIfUnset()272 Viewer::setupDefaultCameraConfigIfUnset()
273 {
274     if (getNumDrawables() || getNumSlaveCameras())
275         return;
276 
277     osg::GraphicsContext::ScreenIdentifier screenIdentifier;
278     screenIdentifier = getDefaultScreenIdentifier();
279 
280     Drawable* drawable = getOrCreateDrawable("fgviewer");
281     drawable->setScreenIdentifier(screenIdentifier.displayName());
282     drawable->setPosition(SGVec2i(0, 0));
283     SGVec2i size(800, 600);
284     drawable->setSize(size);
285     drawable->setFullscreen(false);
286     drawable->setOffscreen(false);
287 
288     SlaveCamera* slaveCamera = getOrCreateSlaveCamera(drawable->getName());
289     slaveCamera->setDrawableName(drawable->getName());
290     drawable->attachSlaveCamera(slaveCamera);
291     slaveCamera->setViewport(SGVec4i(0, 0, size[0], size[1]));
292     slaveCamera->setViewOffset(osg::Matrix::identity());
293     slaveCamera->setFustumByFieldOfViewDeg(55);
294 }
295 
296 bool
readConfiguration(const std::string &)297 Viewer::readConfiguration(const std::string&)
298 {
299     return false;
300 }
301 
302 void
setRenderer(Renderer * renderer)303 Viewer::setRenderer(Renderer* renderer)
304 {
305     if (!renderer) {
306         SG_LOG(SG_VIEW, SG_ALERT, "Viewer::setRenderer(): Setting the renderer to zero is not supported!");
307         return;
308     }
309     if (_renderer.valid()) {
310         SG_LOG(SG_VIEW, SG_ALERT, "Viewer::setRenderer(): Setting the renderer twice is not supported!");
311         return;
312     }
313     _renderer = renderer;
314 }
315 
316 Renderer*
getRenderer()317 Viewer::getRenderer()
318 {
319     return _renderer.get();
320 }
321 
322 Drawable*
getOrCreateDrawable(const std::string & name)323 Viewer::getOrCreateDrawable(const std::string& name)
324 {
325     Drawable* drawable = getDrawable(name);
326     if (drawable)
327         return drawable;
328     if (!_renderer.valid())
329         return 0;
330     drawable = _renderer->createDrawable(*this, name);
331     if (!drawable)
332         return 0;
333     _drawableVector.push_back(drawable);
334     return drawable;
335 }
336 
337 Drawable*
getDrawable(const std::string & name)338 Viewer::getDrawable(const std::string& name)
339 {
340     return getDrawable(getDrawableIndex(name));
341 }
342 
343 unsigned
getDrawableIndex(const std::string & name)344 Viewer::getDrawableIndex(const std::string& name)
345 {
346     for (DrawableVector::size_type i = 0; i < _drawableVector.size(); ++i) {
347         if (_drawableVector[i]->getName() == name)
348             return i;
349     }
350     return ~0u;
351 }
352 
353 Drawable*
getDrawable(unsigned index)354 Viewer::getDrawable(unsigned index)
355 {
356     if (_drawableVector.size() <= index)
357         return 0;
358     return _drawableVector[index].get();
359 }
360 
361 unsigned
getNumDrawables() const362 Viewer::getNumDrawables() const
363 {
364     return _drawableVector.size();
365 }
366 
367 SlaveCamera*
getOrCreateSlaveCamera(const std::string & name)368 Viewer::getOrCreateSlaveCamera(const std::string& name)
369 {
370     SlaveCamera* slaveCamera = getSlaveCamera(name);
371     if (slaveCamera)
372         return slaveCamera;
373     if (!_renderer.valid())
374         return 0;
375     slaveCamera = _renderer->createSlaveCamera(*this, name);
376     if (!slaveCamera)
377         return 0;
378     _slaveCameraVector.push_back(slaveCamera);
379     return slaveCamera;
380 }
381 
382 SlaveCamera*
getSlaveCamera(const std::string & name)383 Viewer::getSlaveCamera(const std::string& name)
384 {
385     return getSlaveCamera(getSlaveCameraIndex(name));
386 }
387 
388 unsigned
getSlaveCameraIndex(const std::string & name)389 Viewer::getSlaveCameraIndex(const std::string& name)
390 {
391     for (SlaveCameraVector::size_type i = 0; i < _slaveCameraVector.size(); ++i) {
392         if (_slaveCameraVector[i]->getName() == name)
393             return i;
394     }
395     return ~0u;
396 }
397 
398 SlaveCamera*
getSlaveCamera(unsigned index)399 Viewer::getSlaveCamera(unsigned index)
400 {
401     if (_slaveCameraVector.size() <= index)
402         return 0;
403     return _slaveCameraVector[index].get();
404 }
405 
406 unsigned
getNumSlaveCameras() const407 Viewer::getNumSlaveCameras() const
408 {
409     return _slaveCameraVector.size();
410 }
411 
412 void
realize()413 Viewer::realize()
414 {
415     if (isRealized())
416         return;
417 
418     if (!_renderer.valid())
419         return;
420 
421     // Setup a default config if there is none
422     setupDefaultCameraConfigIfUnset();
423 
424     // Realize
425     if (!_renderer->realize(*this)) {
426         SG_LOG(SG_VIEW, SG_ALERT, "Renderer::realize() failed!");
427         return;
428     }
429 
430     osgViewer::Viewer::realize();
431 }
432 
433 bool
realizeDrawables()434 Viewer::realizeDrawables()
435 {
436     for (DrawableVector::iterator i = _drawableVector.begin(); i != _drawableVector.end(); ++i) {
437         if (!(*i)->realize(*this)) {
438             SG_LOG(SG_VIEW, SG_ALERT, "Unable to realize drawable \"" << (*i)->getName() << "\"!");
439             return false;
440         }
441     }
442 
443     return true;
444 }
445 
446 bool
realizeSlaveCameras()447 Viewer::realizeSlaveCameras()
448 {
449     for (SlaveCameraVector::iterator i = _slaveCameraVector.begin(); i != _slaveCameraVector.end(); ++i) {
450         if (!(*i)->realize(*this)) {
451             SG_LOG(SG_VIEW, SG_ALERT, "Unable to realize camera \"" << (*i)->getName() << "\"!");
452             return false;
453         }
454     }
455 
456     return true;
457 }
458 
459 void
advance(double)460 Viewer::advance(double)
461 {
462     if (_timeIncrement == SGTimeStamp::fromSec(0)) {
463         // Flightgears current scheme - could be improoved
464         _simTime = SGTimeStamp::now();
465     } else {
466         // Giving an explicit time increment makes sense in presence
467         // of the video capture where we need deterministic
468         // frame times and object positions for each picture.
469         _simTime += _timeIncrement;
470     }
471 
472     // This sets the frame stamps simulation time to simTime
473     // and schedules a frame event
474     osgViewer::Viewer::advance(_simTime.toSecs());
475 }
476 
477 void
updateTraversal()478 Viewer::updateTraversal()
479 {
480 #if FG_HAVE_HLA
481     if (_viewerFederate.valid()) {
482         if (_timeIncrement == SGTimeStamp::fromSec(0)) {
483             if (!_viewerFederate->timeAdvanceAvailable()) {
484                 SG_LOG(SG_NETWORK, SG_ALERT, "Got error from federate update!");
485                 _viewerFederate->shutdown();
486                 _viewerFederate = 0;
487             }
488         } else {
489             osg::FrameStamp* frameStamp = getViewerFrameStamp();
490             SGTimeStamp timeStamp = SGTimeStamp::fromSec(frameStamp->getSimulationTime());
491             if (!_viewerFederate->timeAdvance(timeStamp)) {
492                 SG_LOG(SG_NETWORK, SG_ALERT, "Got error from federate update!");
493                 _viewerFederate->shutdown();
494                 _viewerFederate = 0;
495             }
496         }
497     }
498 #endif
499 
500     osgViewer::Viewer::updateTraversal();
501 
502     if (!_renderer->update(*this)) {
503         SG_LOG(SG_VIEW, SG_ALERT, "Renderer::update() failed!");
504     }
505 }
506 
507 bool
updateSlaveCameras()508 Viewer::updateSlaveCameras()
509 {
510     for (SlaveCameraVector::iterator i = _slaveCameraVector.begin(); i != _slaveCameraVector.end(); ++i) {
511         if (!(*i)->update(*this)) {
512             SG_LOG(SG_VIEW, SG_ALERT, "SlaveCamera::update() failed!");
513             return false;
514         }
515     }
516     return true;
517 }
518 
519 void
setReaderWriterOptions(simgear::SGReaderWriterOptions * readerWriterOptions)520 Viewer::setReaderWriterOptions(simgear::SGReaderWriterOptions* readerWriterOptions)
521 {
522     _readerWriterOptions = readerWriterOptions;
523 }
524 
525 simgear::SGReaderWriterOptions*
getReaderWriterOptions()526 Viewer::getReaderWriterOptions()
527 {
528     return _readerWriterOptions.get();
529 }
530 
531 void
setSceneData(osg::Node * node)532 Viewer::setSceneData(osg::Node* node)
533 {
534     _sceneDataGroup->removeChildren(0, _sceneDataGroup->getNumChildren());
535     insertSceneData(node);
536 }
537 
538 void
insertSceneData(osg::Node * node)539 Viewer::insertSceneData(osg::Node* node)
540 {
541     _sceneDataGroup->addChild(node);
542 }
543 
544 bool
insertSceneData(const std::string & fileName,const osgDB::Options * options)545 Viewer::insertSceneData(const std::string& fileName, const osgDB::Options* options)
546 {
547 #if 0
548     osg::ProxyNode* proxyNode = new osg::ProxyNode;
549     if (options)
550         proxyNode->setDatabaseOptions(options->clone(osg::CopyOp()));
551     else
552         proxyNode->setDatabaseOptions(_readerWriterOptions->clone(osg::CopyOp()));
553     proxyNode->setFileName(0, fileName);
554     insertSceneData(proxyNode);
555     return true;
556 #else
557 #if OSG_VERSION_LESS_THAN(3,4,0)
558     osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(fileName, options);
559 #else
560     osg::ref_ptr<osg::Node> node = osgDB::readRefNodeFile(fileName, options);
561 #endif
562     if (!node.valid())
563         return false;
564     insertSceneData(node.get());
565     return true;
566 #endif
567 }
568 
569 osg::Group*
getSceneDataGroup()570 Viewer::getSceneDataGroup()
571 {
572     return _sceneDataGroup.get();
573 }
574 
575 class Viewer::_PurgeLevelOfDetailNodesVisitor : public osg::NodeVisitor {
576 public:
_PurgeLevelOfDetailNodesVisitor()577     _PurgeLevelOfDetailNodesVisitor() :
578         osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
579     { }
~_PurgeLevelOfDetailNodesVisitor()580     virtual ~_PurgeLevelOfDetailNodesVisitor()
581     { }
582 
apply(osg::ProxyNode & node)583     virtual void apply(osg::ProxyNode& node)
584     {
585         for (unsigned i = 0; i < node.getNumChildren(); ++i) {
586             if (node.getFileName(i).empty())
587                 continue;
588             node.removeChildren(i, node.getNumChildren() - i);
589             break;
590         }
591 
592         osg::NodeVisitor::apply(static_cast<osg::Group&>(node));
593     }
apply(osg::PagedLOD & node)594     virtual void apply(osg::PagedLOD& node)
595     {
596         for (unsigned i = 0; i < node.getNumChildren(); ++i) {
597             if (node.getFileName(i).empty())
598                 continue;
599             node.removeChildren(i, node.getNumChildren() - i);
600             break;
601         }
602 
603         osg::NodeVisitor::apply(static_cast<osg::Group&>(node));
604     }
605 };
606 
607 void
purgeLevelOfDetailNodes()608 Viewer::purgeLevelOfDetailNodes()
609 {
610     _PurgeLevelOfDetailNodesVisitor purgeLevelOfDetailNodesVisitor;
611     _sceneDataGroup->accept(purgeLevelOfDetailNodesVisitor);
612 }
613 
614 osg::GraphicsContext::ScreenIdentifier
getDefaultScreenIdentifier()615 Viewer::getDefaultScreenIdentifier()
616 {
617     osg::GraphicsContext::ScreenIdentifier screenIdentifier;
618     screenIdentifier.readDISPLAY();
619     if (screenIdentifier.displayNum < 0)
620         screenIdentifier.displayNum = 0;
621     if (screenIdentifier.screenNum < 0)
622         screenIdentifier.screenNum = 0;
623     return screenIdentifier;
624 }
625 
626 osg::GraphicsContext::ScreenIdentifier
getScreenIdentifier(const std::string & display)627 Viewer::getScreenIdentifier(const std::string& display)
628 {
629     osg::GraphicsContext::ScreenIdentifier screenIdentifier;
630     screenIdentifier.setScreenIdentifier(display);
631 
632     osg::GraphicsContext::ScreenIdentifier defaultScreenIdentifier;
633     defaultScreenIdentifier = getDefaultScreenIdentifier();
634     if (screenIdentifier.hostName.empty())
635         screenIdentifier.hostName = defaultScreenIdentifier.hostName;
636     if (screenIdentifier.displayNum < 0)
637         screenIdentifier.displayNum = defaultScreenIdentifier.displayNum;
638     if (screenIdentifier.screenNum < 0)
639         screenIdentifier.screenNum = defaultScreenIdentifier.screenNum;
640 
641     return screenIdentifier;
642 }
643 
644 osg::GraphicsContext::ScreenSettings
getScreenSettings(const osg::GraphicsContext::ScreenIdentifier & screenIdentifier)645 Viewer::getScreenSettings(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier)
646 {
647     osg::GraphicsContext::ScreenSettings screenSettings;
648 
649     osg::GraphicsContext::WindowingSystemInterface* wsi;
650     wsi = osg::GraphicsContext::getWindowingSystemInterface();
651     if (!wsi) {
652         SG_LOG(SG_VIEW, SG_ALERT, "No windowing system interface defined!");
653         return screenSettings;
654     }
655 
656     wsi->getScreenSettings(screenIdentifier, screenSettings);
657     return screenSettings;
658 }
659 
660 osg::GraphicsContext::Traits*
getTraits(const osg::GraphicsContext::ScreenIdentifier & screenIdentifier)661 Viewer::getTraits(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier)
662 {
663     osg::DisplaySettings* ds = _displaySettings.get();
664     if (!ds)
665         ds = osg::DisplaySettings::instance().get();
666 
667     osg::GraphicsContext::Traits* traits = new osg::GraphicsContext::Traits(ds);
668 
669     traits->hostName = screenIdentifier.hostName;
670     traits->displayNum = screenIdentifier.displayNum;
671     traits->screenNum = screenIdentifier.screenNum;
672 
673     // not seriously consider something different
674     traits->doubleBuffer = true;
675 
676     osg::GraphicsContext::ScreenSettings screenSettings;
677     screenSettings = getScreenSettings(screenIdentifier);
678 
679     traits->x = 0;
680     traits->y = 0;
681     traits->width = screenSettings.width;
682     traits->height = screenSettings.height;
683 
684     return traits;
685 }
686 
687 #ifdef __linux__
688 class Viewer::_ResetScreenSaverSwapCallback : public osg::GraphicsContext::SwapCallback {
689 public:
_ResetScreenSaverSwapCallback()690     _ResetScreenSaverSwapCallback() :
691         _timeStamp(SGTimeStamp::fromSec(0))
692     {
693     }
~_ResetScreenSaverSwapCallback()694     virtual ~_ResetScreenSaverSwapCallback()
695     {
696     }
swapBuffersImplementation(osg::GraphicsContext * graphicsContext)697     virtual void swapBuffersImplementation(osg::GraphicsContext* graphicsContext)
698     {
699         graphicsContext->swapBuffersImplementation();
700 
701         // This callback must be attached to this type of graphics context
702         assert(dynamic_cast<osgViewer::GraphicsWindowX11*>(graphicsContext));
703 
704         // Reset the screen saver every 10 seconds
705         SGTimeStamp timeStamp = SGTimeStamp::now();
706         if (timeStamp < _timeStamp)
707             return;
708         _timeStamp = timeStamp + SGTimeStamp::fromSec(10);
709         // Obviously runs in the draw thread. Thus, use the draw display.
710         XResetScreenSaver(static_cast<osgViewer::GraphicsWindowX11*>(graphicsContext)->getDisplay());
711     }
712 private:
713     SGTimeStamp _timeStamp;
714 };
715 #endif
716 
717 osg::GraphicsContext*
createGraphicsContext(osg::GraphicsContext::Traits * traits)718 Viewer::createGraphicsContext(osg::GraphicsContext::Traits* traits)
719 {
720     osg::GraphicsContext::WindowingSystemInterface* wsi;
721     wsi = osg::GraphicsContext::getWindowingSystemInterface();
722     if (!wsi) {
723         SG_LOG(SG_VIEW, SG_ALERT, "No windowing system interface defined!");
724         return 0;
725     }
726 
727     osg::GraphicsContext* graphicsContext = wsi->createGraphicsContext(traits);
728     if (!graphicsContext) {
729         SG_LOG(SG_VIEW, SG_ALERT, "Unable to create window \"" << traits->windowName << "\"!");
730         return 0;
731     }
732 
733 #ifdef __linux__
734     if (dynamic_cast<osgViewer::GraphicsWindowX11*>(graphicsContext))
735         graphicsContext->setSwapCallback(new _ResetScreenSaverSwapCallback);
736 #endif
737 
738     return graphicsContext;
739 }
740 
741 #if FG_HAVE_HLA
742 const HLAViewerFederate*
getViewerFederate() const743 Viewer::getViewerFederate() const
744 {
745     return _viewerFederate.get();
746 }
747 
748 HLAViewerFederate*
getViewerFederate()749 Viewer::getViewerFederate()
750 {
751     return _viewerFederate.get();
752 }
753 
754 void
setViewerFederate(HLAViewerFederate * viewerFederate)755 Viewer::setViewerFederate(HLAViewerFederate* viewerFederate)
756 {
757     if (!viewerFederate) {
758         SG_LOG(SG_VIEW, SG_ALERT, "Viewer::setViewerFederate(): Setting the viewer federate to zero is not supported!");
759         return;
760     }
761     if (_viewerFederate.valid()) {
762         SG_LOG(SG_VIEW, SG_ALERT, "Viewer::setViewerFederate(): Setting the viewer federate twice is not supported!");
763         return;
764     }
765     _viewerFederate = viewerFederate;
766     _viewerFederate->attachToViewer(this);
767 }
768 #endif
769 
770 } // namespace fgviewer
771