1
0
Fork 0
flightgear/src/GUI/MessageBox.cxx
Julian Smith f62e5b9ce3 CompositeViewer: Support for multiple view windows using osgViewer::CompositeViewer.
Overview:

    Previously Flightgear always used a single osgViewer::Viewer(), which
    inherits from both osgViewer::ViewerBase and osgViewer::View, giving a
    single view window.

    If CompositeViewer is enabled, we instead use a osgViewer::CompositeViewer
    which contains a list of osgViewer::View's. Each of these View's can have
    its own eye position, so we can have multiple different views of the same
    scene.

    Enable at runtime with: --composite-viewer=1

Changes to allow use of osgViewer::CompositeViewer:

    Previously FGRenderer had this method:

        osgViewer::Viewer* getViewer();

    This has been replaced by these two new methods:

        osgViewer::ViewerBase* getViewerBase();
        osgViewer::View* getView();

    If CompositeViewer is not enabled (the default), the actual runtime state
    is unchanged, and getViewerBase() and getView() both return a pointer to
    the singleton osgViewer::Viewer() object.

    If CompositeViewer is enabled, getViewerBase() returns a pointer to a
    singleton osgViewer::CompositeViewer object, and getView() returns a
    pointer to the first osgViewer::View in the osgViewer::CompositeViewer's
    list.

    The other significant change to FGRenderer() is the new method:

        osg::FrameStamp* getFrameStamp()

    If CompositeViewer is not enabled, this simply returns
    getView()->getFrameStamp(). If CompositeViewer is enabled it returns
    getViewerBase()->getFrameStamp(). It is important that code that previously
    called getView()->getFrameStamp() is changed to use the new method, because
    when CompositeViewer is enabled individual osgViewer::View's appear to
    return an osg::FrameStamp with zero frame number).

    All code that uses FGRenderer has been patched up to use the new methods so
    that things work as before regardless of whether CompositeViewer is enabled
    or not.

    We make FGRenderer::update() call SviewUpdate() which updates any extra
    views.

Extra view windows:

    If CompositeViewer is enabled, one can create top-level extra view windows
    by calling SviewCreate(). See src/Viewer/sview.hxx for details.

    Each extra view window has its own simgear::compositor::Compositor
    instance.

    Currently SviewCreate() can create extra view windows that clone the
    current view, or view from one point to another (e.g. from one multiplayer
    aircraft to the user's aircradt) or keep two aircraft in view, one at a
    fixed distance in the foreground.

    SviewCreate() can be called from nasal via new nasal commands "view-clone",
    "view-last-pair", "view-last-pair-double" and "view-push". Associated
    changes to fgdata gives access to these via the View menu. The "view-push"
    command tags the current view for later use by "view-last-pair" and
    "view-last-pair-double".

    Extra view windows created by SviewCreate() use a new view system called
    Sview, which allows views to be constructed at runtime instead of being
    hard-coded in *-set.xml files. This is work in progress and views aren't
    all fully implemented. For example Pilot view gets things slightly wrong
    with large roll values, Tower View AGL is not implemented, and we don't
    implement damping. See top of src/Viewer/sview.cxx for an overview.

OpenSceneGraph-3.4 issues:

    OSG-3.4's event handling seems to be incorrect with CompositeViewer -
    events get sent for the wrong window which causes issues with resize and
    closing. It doesn't seem to be possible to work around this, so closing
    extra view windows can end up closing the main window for example.

    OSG-3.6 seems to fix the problems.

    We warn if CompositeViewer is enabled and OpenSceneGraph is 3.4.
2020-11-21 13:27:02 +00:00

224 lines
5.8 KiB
C++

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <simgear/simgear_config.h>
#include <cstdlib>
#include "MessageBox.hxx"
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <Viewer/renderer.hxx>
#include <GUI/new_gui.hxx>
#include <Main/sentryIntegration.hxx>
#include <osgViewer/Viewer>
#include <simgear/structure/commands.hxx>
#ifdef SG_WINDOWS
#include <windows.h>
#include <osgViewer/GraphicsWindow>
#include <osgViewer/api/Win32/GraphicsWindowWin32>
#endif
#if defined(SG_MAC)
// externs from CocoaMessageBox.mm
flightgear::MessageBoxResult
cocoaFatalMessage(const std::string& msg, const std::string& text);
flightgear::MessageBoxResult
cocoaMessageBox(const std::string& msg, const std::string& text);
#endif
#ifdef HAVE_QT
#include "QtMessageBox.hxx"
#endif
using namespace simgear::strutils;
namespace {
static bool static_isHeadless = false;
bool isCanvasImplementationRegistered()
{
if (!globals) {
return false;
}
SGCommandMgr* cmd = globals->get_commands();
return (cmd->getCommand("canvas-message-box") != NULL);
}
#if defined(SG_WINDOWS)
HWND getMainViewerHWND()
{
osgViewer::Viewer::Windows windows;
if (!globals || !globals->get_renderer() || !globals->get_renderer()->getViewerBase()) {
return 0;
}
globals->get_renderer()->getViewerBase()->getWindows(windows);
osgViewer::Viewer::Windows::const_iterator it = windows.begin();
for(; it != windows.end(); ++it) {
if (strcmp((*it)->className(), "GraphicsWindowWin32")) {
continue;
}
osgViewer::GraphicsWindowWin32* platformWin =
static_cast<osgViewer::GraphicsWindowWin32*>(*it);
return platformWin->getHWND();
}
return 0;
}
flightgear::MessageBoxResult
win32MessageBox(const std::string& caption,
const std::string& msg,
const std::string& moreText)
{
// during early startup (aircraft / fg-data validation) there is no
// osgViewer so no HWND.
HWND ownerWindow = getMainViewerHWND();
std::string fullMsg(msg);
if (!moreText.empty()) {
fullMsg += "\n\n" + moreText;
}
UINT mbType = MB_OK;
std::wstring wMsg(convertUtf8ToWString(fullMsg)),
wCap(convertUtf8ToWString(caption));
::MessageBoxExW(ownerWindow, wMsg.c_str(), wCap.c_str(),
mbType, 0 /* system lang */);
return flightgear::MSG_BOX_OK;
}
#endif
} // anonymous namespace
namespace flightgear
{
void setHeadlessMode(bool headless)
{
static_isHeadless = headless;
}
bool isHeadlessMode()
{
return static_isHeadless;
}
MessageBoxResult modalMessageBox(const std::string& caption,
const std::string& msg,
const std::string& moreText)
{
// Headless mode.
if (static_isHeadless) {
SG_LOG(SG_HEADLESS, SG_ALERT, "ModalMessageBox Caption: \"" << caption << "\"");
SG_LOG(SG_HEADLESS, SG_ALERT, "ModalMessageBox Message: \"" << msg << "\"");
if (!moreText.empty())
SG_LOG(SG_HEADLESS, SG_ALERT, "ModalMessageBox More text: \"" << moreText << "\"");
return MSG_BOX_OK;
}
// prefer canvas
if (isCanvasImplementationRegistered()) {
SGPropertyNode_ptr args(new SGPropertyNode);
args->setStringValue("caption", caption);
args->setStringValue("message", msg);
args->setStringValue("more", moreText);
globals->get_commands()->execute("canvas-message-box", args, nullptr);
// how to make it modal?
return MSG_BOX_OK;
}
#if defined(SG_WINDOWS)
return win32MessageBox(caption, msg, moreText);
#elif defined(SG_MAC)
return cocoaMessageBox(msg, moreText);
#elif defined(HAVE_QT)
return QtMessageBox(caption, msg, moreText, false);
#else
std::string s = caption + ": "+ msg;
if (!moreText.empty()) {
s += "\n( " + moreText + ")";
}
NewGUI* gui = globals->get_subsystem<NewGUI>();
if (!gui || (fgGetBool("/sim/rendering/initialized", false) == false)) {
SG_LOG(SG_GENERAL, SG_POPUP, s);
} else {
SGPropertyNode_ptr dlg = gui->getDialogProperties("popup");
dlg->setStringValue("text/label", s );
gui->showDialog("popup");
}
return MSG_BOX_OK;
#endif
}
MessageBoxResult fatalMessageBoxWithoutExit(const std::string& caption,
const std::string& msg,
const std::string& moreText)
{
flightgear::sentryReportFatalError(msg, moreText);
// Headless mode.
if (static_isHeadless) {
SG_LOG(SG_HEADLESS, SG_ALERT, "Fatal Error: \"" << caption << "\"");
SG_LOG(SG_HEADLESS, SG_ALERT, "Error Message: \"" << msg << "\"");
if (!moreText.empty())
SG_LOG(SG_HEADLESS, SG_ALERT, "\tMore text: \"" << moreText << "\"");
return MSG_BOX_OK;
}
#if defined(SG_WINDOWS)
return win32MessageBox(caption, msg, moreText);
#elif defined(SG_MAC)
return cocoaFatalMessage(msg, moreText);
#elif defined(HAVE_QT)
return QtMessageBox(caption, msg, moreText, true);
#else
std::string s = "FATAL: "+ msg;
if (!moreText.empty()) {
s += "\n( " + moreText + ")";
}
if (fgGetBool("/sim/rendering/initialized", false) == false) {
std::cerr << s << std::endl;
} else {
NewGUI* _gui = (NewGUI *)globals->get_subsystem("gui");
SGPropertyNode_ptr dlg = _gui->getDialogProperties("popup");
dlg->setStringValue("text/label", s );
_gui->showDialog("popup");
}
return MSG_BOX_OK;
#endif
}
[[noreturn]] void fatalMessageBoxThenExit(
const std::string& caption,
const std::string& msg,
const std::string& moreText,
int exitStatus)
{
fatalMessageBoxWithoutExit(caption, msg, moreText);
// we can't use exit() here or QGuiApplication crashes
// let's instead throw a sepcial exception which we catch
// in boostrap.
throw FatalErrorException{};
}
} // of namespace flightgear