From a9e5a27e551288872c55bf0cf1b2c161c6e97c72 Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 12 Nov 2018 16:46:41 +0100 Subject: [PATCH] UI handling tweaks - add class to handle top-level windows - remove requirement to use custom graphics-window - order alongside PUI UI --- CMakeLists.txt | 2 +- src/GUI/CMakeLists.txt | 4 + src/GUI/CocoaHelpers.h | 1 - src/GUI/CocoaHelpers.mm | 42 +++ src/GUI/FGQQWindowManager.cxx | 259 ++++++++++++++ src/GUI/FGQQWindowManager.hxx | 40 +++ src/GUI/QQuickDrawable.cxx | 537 +++++++++++++++++++++-------- src/GUI/QQuickDrawable.hxx | 32 +- src/GUI/QtHelpers.cxx | 29 ++ src/GUI/QtHelpers.hxx | 28 ++ src/GUI/qml/LauncherSettings.qml | 9 - src/GUI/resources.qrc | 1 - src/Main/CMakeLists.txt | 9 + src/Viewer/CMakeLists.txt | 6 +- src/Viewer/GraphicsWindowQt5.cpp | 5 + src/Viewer/OSGQtAdaption.cxx | 126 +++++++ src/Viewer/OSGQtAdaption.hxx | 53 +++ src/Viewer/PUICamera.cxx | 26 +- src/Viewer/PUICamera.hxx | 8 +- src/Viewer/WindowBuilder.cxx | 19 +- src/Viewer/renderer_compositor.cxx | 12 +- src/Viewer/renderer_legacy.cxx | 12 +- 22 files changed, 1060 insertions(+), 200 deletions(-) create mode 100644 src/GUI/FGQQWindowManager.cxx create mode 100644 src/GUI/FGQQWindowManager.hxx mode change 100644 => 100755 src/GUI/QQuickDrawable.cxx mode change 100644 => 100755 src/GUI/QQuickDrawable.hxx create mode 100644 src/GUI/QtHelpers.cxx create mode 100644 src/GUI/QtHelpers.hxx delete mode 100644 src/GUI/qml/LauncherSettings.qml create mode 100644 src/Viewer/OSGQtAdaption.cxx create mode 100644 src/Viewer/OSGQtAdaption.hxx diff --git a/CMakeLists.txt b/CMakeLists.txt index c753a3ea2..e061ba63c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ if(COMMAND cmake_policy) # OpenGL VND policy : use the old definition for now, until we can audit this if(POLICY CMP0072) - cmake_policy(SET CMP0071 OLD) + cmake_policy(SET CMP0072 OLD) endif() endif() diff --git a/src/GUI/CMakeLists.txt b/src/GUI/CMakeLists.txt index 186090339..5b5c0fdb9 100644 --- a/src/GUI/CMakeLists.txt +++ b/src/GUI/CMakeLists.txt @@ -166,6 +166,8 @@ if (HAVE_QT) QmlNavCacheWrapper.cxx QmlRadioButtonHelper.cxx QmlRadioButtonHelper.hxx + QtHelpers.hxx + QtHelpers.cxx UnitsModel.cxx UnitsModel.hxx NavaidSearchModel.hxx @@ -178,6 +180,8 @@ if (HAVE_QT) ModelDataExtractor.hxx HoverArea.cxx HoverArea.hxx + FGQQWindowManager.cxx + FGQQWindowManager.hxx ) set_property(TARGET fgqmlui PROPERTY AUTOMOC ON) diff --git a/src/GUI/CocoaHelpers.h b/src/GUI/CocoaHelpers.h index 8a23407a6..138d9c9ad 100644 --- a/src/GUI/CocoaHelpers.h +++ b/src/GUI/CocoaHelpers.h @@ -23,7 +23,6 @@ #include #include - /** * open a URL using the system's web-browser */ diff --git a/src/GUI/CocoaHelpers.mm b/src/GUI/CocoaHelpers.mm index 5ced7bd6c..eb03b2a53 100644 --- a/src/GUI/CocoaHelpers.mm +++ b/src/GUI/CocoaHelpers.mm @@ -34,6 +34,8 @@ #include #include +#include + // simgear #include #include @@ -47,6 +49,9 @@ #if defined(HAVE_QT) # include +# include +# include +# include #endif NSString* stdStringToCocoa(const std::string& s) @@ -151,6 +156,41 @@ SGPath Options::platformDefaultRoot() const dataDir.append("data"); return dataDir; } + +#if defined(HAVE_QT) +QOpenGLContext* qtContextFromOSG(osg::GraphicsContext* context) +{ + osgViewer::GraphicsWindowCocoa* win = dynamic_cast(context); + if (!win) { + return nullptr; + } + + auto cnc = QCocoaNativeContext{win->getContext()}; + QVariant nativeHandle = QVariant::fromValue(cnc); + + if (nativeHandle.isNull()) + return nullptr; + + std::unique_ptr qtOpenGLContext(new QOpenGLContext); + qtOpenGLContext->setNativeHandle(nativeHandle); + if (!qtOpenGLContext->create()) { + return nullptr; + } + + return qtOpenGLContext.release(); +} + +QWindow* qtWindowFromOSG(osgViewer::GraphicsWindow* graphicsWindow) +{ + osgViewer::GraphicsWindowCocoa* win = dynamic_cast(graphicsWindow); + if (!win) { + return nullptr; + } + + NSView* osgView = [win->getContext() view]; + return QWindow::fromWinId(reinterpret_cast(osgView)); +} +#endif } // of namespace flightgear @@ -222,3 +262,5 @@ void cocoaRegisterTerminateHandler() name:NSApplicationWillTerminateNotification object:app]; #endif } + + diff --git a/src/GUI/FGQQWindowManager.cxx b/src/GUI/FGQQWindowManager.cxx new file mode 100644 index 000000000..aa1cb5726 --- /dev/null +++ b/src/GUI/FGQQWindowManager.cxx @@ -0,0 +1,259 @@ +#include "FGQQWindowManager.hxx" + +#include + +#include +#include +#include +#include + +#include + +#include "QtHelpers.hxx" +#include
+ +//////////////////////////////////////////////////////////////////////////// + +/** + * @brief information on all the dialogs / windows we have registered + */ +struct DialogInfo { + DialogInfo(SGPropertyNode* node) + { + id = QString::fromStdString(node->getStringValue("id")); + if (id.isEmpty()) { + throw std::runtime_error("missing ID"); + } + + title = QString::fromStdString(node->getStringValue("title")); + + SGPath p = globals->get_fg_root() / "gui" / "quick" / node->getStringValue("file"); + if (!p.exists()) { + qWarning() << "missing dialog QML file: " << p; + } + resource = QUrl::fromLocalFile(QString::fromStdString(p.utf8Str())); + } + + QString id; + QString title; + QUrl resource; + // flags? +}; + +//////////////////////////////////////////////////////////////////////////// + +const int WindowContentUrl = Qt::UserRole + 1; +const int WindowSize = Qt::UserRole + 2; +const int WindowPosition = Qt::UserRole + 3; +const int WindowId = Qt::UserRole + 4; +const int WindowTitle = Qt::UserRole + 5; + +/** + * @brief data about an active window + */ +struct WindowData { + QString windowId; + QUrl source; + QRectF geometry; + QString title; + + // frame flags (close, popout, etc) +}; + +class WindowsModel : public QAbstractListModel +{ +public: + WindowsModel(FGQQWindowManager* wm) : QAbstractListModel(wm) + { + } + + int rowCount(const QModelIndex&) const override + { + return static_cast(_windowData.size()); + } + + QHash roleNames() const override + { + QHash r; + r[WindowId] = "windowId"; + r[WindowContentUrl] = "windowContentSource"; + r[WindowSize] = "windowSize"; + r[WindowPosition] = "windowPosition"; + r[WindowTitle] = "windowTitle"; + + return r; + } + + QVariant data(const QModelIndex& index, int role) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role) override; + + void show(const DialogInfo& dinfo) + { + QSettings settings; + QRectF geom = settings.value(dinfo.id + "-geometry").toRectF(); + if (!geom.isValid()) { + // fixme: define default window geometry + geom = QRectF{100, 100, 400, 400}; + } + + WindowData winfo = {dinfo.id, dinfo.resource, geom}; + + beginInsertRows({}, _windowData.size(), _windowData.size()); + _windowData.push_back(winfo); + endInsertRows(); + } + + void close(QString windowId) + { + auto it = findWindow(windowId); + if (it == _windowData.end()) { + SG_LOG(SG_GUI, SG_DEV_WARN, "trying to close non-open window:" << windowId.toStdString()); + return; + } + + const int index = std::distance(_windowData.begin(), it); + beginRemoveRows({}, index, index); + _windowData.erase(it); + endRemoveRows(); + } + + std::vector::const_iterator findWindow(QString windowId) const + { + return std::find_if(_windowData.begin(), _windowData.end(), + [windowId](const WindowData& d) { return d.windowId == windowId; }); + } + + std::vector::iterator findWindow(QString windowId) + { + return std::find_if(_windowData.begin(), _windowData.end(), + [windowId](const WindowData& d) { return d.windowId == windowId; }); + } + + std::vector _windowData; +}; + +QVariant WindowsModel::data(const QModelIndex& index, int role) const +{ + if ((index.row() < 0) || (index.row() >= _windowData.size())) + return {}; + + const auto& d = _windowData.at(static_cast(index.row())); + switch (role) { + case WindowId: + return d.windowId; + case WindowContentUrl: + return d.source; + case WindowSize: + return d.geometry.size(); + case WindowPosition: + return d.geometry.topLeft(); + case WindowTitle: + return d.title; + + default: break; + } + return {}; +} + +bool WindowsModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if ((index.row() < 0) || (index.row() >= _windowData.size())) + return false; + + auto& d = _windowData[static_cast(index.row())]; + QSettings settings; + if (role == WindowSize) { + d.geometry.setSize(value.toSizeF()); + settings.setValue(d.windowId + "-geometry", d.geometry); + emit dataChanged(index, index, {role}); + return true; + } else if (role == WindowPosition) { + d.geometry.setTopLeft(value.toPointF()); + settings.setValue(d.windowId + "-geometry", d.geometry); + emit dataChanged(index, index, {role}); + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////// + +class FGQQWindowManager::WindowManagerPrivate +{ +public: + WindowManagerPrivate(FGQQWindowManager* outer) : _p(outer), + _windows(new WindowsModel{outer}) + { + // scan dir to find built-in dialogs for now + SGPath p = globals->get_fg_root() / "gui" / "quick" / "dialogs.xml"; + if (p.exists()) { + SGPropertyNode_ptr dialogProps(new SGPropertyNode); + readProperties(p, dialogProps); + for (auto nd : dialogProps->getChildren("dialog")) { + try { + DialogInfo dinfo(nd); + _dialogDefinitions.push_back(dinfo); + } catch (std::exception&) { + // skip this dialog + SG_LOG(SG_GUI, SG_DEV_WARN, "Skipped bad QML dialog definition"); + } + } + } else { + SG_LOG(SG_GUI, SG_DEV_WARN, "No QML dialog definitions found"); + } + } + + FGQQWindowManager* _p; + WindowsModel* _windows; + std::vector _dialogDefinitions; +}; + +//////////////////////////////////////////////////////////////////////////// + +FGQQWindowManager::FGQQWindowManager(QQmlEngine* engine, QObject* parent) : QObject(parent), + _d(new WindowManagerPrivate(this)), + _engine(engine) +{ + // register commands to open / close / etc +} + +FGQQWindowManager::~FGQQWindowManager() +{ +} + +QAbstractItemModel* FGQQWindowManager::windows() const +{ + return _d->_windows; +} + +bool FGQQWindowManager::show(QString windowId) +{ + // find QML file corresponding to windowId + auto it = std::find_if(_d->_dialogDefinitions.begin(), + _d->_dialogDefinitions.end(), + [windowId](const DialogInfo& dd) { return dd.id == windowId; }); + if (it == _d->_dialogDefinitions.end()) { + SG_LOG(SG_GUI, SG_DEV_WARN, "Unknown dialog ID:" << windowId.toStdString()); + return false; + } + + _d->_windows->show(*it); + return true; +} + +bool FGQQWindowManager::requestPopout(QString windowId) +{ + return false; +} + +bool FGQQWindowManager::requestClose(QString windowId) +{ + _d->_windows->close(windowId); + return true; +} + +bool FGQQWindowManager::requestPopin(QString windowId) +{ + return false; +} diff --git a/src/GUI/FGQQWindowManager.hxx b/src/GUI/FGQQWindowManager.hxx new file mode 100644 index 000000000..b27885815 --- /dev/null +++ b/src/GUI/FGQQWindowManager.hxx @@ -0,0 +1,40 @@ +#ifndef FGQQWINDOWMANAGER_HXX +#define FGQQWINDOWMANAGER_HXX + +#include +#include + +// forward decls +class QAbstractItemModel; +class QQmlEngine; + +class FGQQWindowManager : public QObject +{ + Q_OBJECT +public: + explicit FGQQWindowManager(QQmlEngine* engine, QObject* parent = nullptr); + ~FGQQWindowManager(); + + Q_PROPERTY(QAbstractItemModel* windows READ windows CONSTANT) + + QAbstractItemModel* windows() const; + + Q_INVOKABLE bool show(QString windowId); + + Q_INVOKABLE bool requestPopout(QString windowId); + + Q_INVOKABLE bool requestClose(QString windowId); + + Q_INVOKABLE bool requestPopin(QString windowId); +signals: + +public slots: + +private: + class WindowManagerPrivate; + + std::unique_ptr _d; + QQmlEngine* _engine = nullptr; +}; + +#endif // FGQQWINDOWMANAGER_HXX diff --git a/src/GUI/QQuickDrawable.cxx b/src/GUI/QQuickDrawable.cxx old mode 100644 new mode 100755 index 40492c82c..36b8468e2 --- a/src/GUI/QQuickDrawable.cxx +++ b/src/GUI/QQuickDrawable.cxx @@ -1,56 +1,110 @@ +// QQuickDrawable.cxx - OSG Drawable using a QQuickRenderControl to draw +// +// Copyright (C) 2019 James Turner +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#include "config.h" #include "QQuickDrawable.hxx" -#include #include -#include #include +#include +#include -#include +#include #include -#include #include #include -#include +#include +#include +#include #include +#include +#include #include +#include +//#include +#include #include #include +#include -#include +#include
+#include
+#include -/** - * Helper run on Graphics thread to retrive its Qt wrapper - */ -class RetriveGraphicsThreadOperation : public osg::GraphicsOperation -{ -public: - RetriveGraphicsThreadOperation() - : osg::GraphicsOperation("RetriveGraphcisThread", false) - {} - - QThread* thread() const { return _result; } - QOpenGLContext* context() const { return _context; } - - virtual void operator () (osg::GraphicsContext* context) override - { - _result = QThread::currentThread(); - _context = QOpenGLContext::currentContext(); - if (!_context) { - qFatal("Use a Qt-based GraphicsWindow/Context"); - // TODO: use ability to create a QOpenGLContext from native - // however, this will also need event-handling to be re-written - //_context = qtContextFromOSG(context); - } - } -private: - QThread* _result = nullptr; - QOpenGLContext* _context = nullptr; +#if defined(HAVE_PUI) +#include +#endif + + +using namespace osgGA; + +struct QtKey { + QtKey(int _o, int _q, QString _s = {}) : osg(_o), qt(_q), s(_s) {} + + int osg; + int qt; + QString s; }; +const std::initializer_list keymapInit = { + // contains all the key mappings except 0..9 and A..Z which are generated + // programatically + + {GUIEventAdapter::KEY_Space, Qt::Key_Space, " "}, + {GUIEventAdapter::KEY_Escape, Qt::Key_Escape, "\x1B"}, + {GUIEventAdapter::KEY_Return, Qt::Key_Return, "\r"}, + {GUIEventAdapter::KEY_Tab, Qt::Key_Tab, "\t"}, + {GUIEventAdapter::KEY_BackSpace, Qt::Key_Backspace, "\x08"}, + {GUIEventAdapter::KEY_Delete, Qt::Key_Delete, "\x7f"}, + + {GUIEventAdapter::KEY_Period, Qt::Key_Period, "."}, + {GUIEventAdapter::KEY_Comma, Qt::Key_Comma, ","}, + {GUIEventAdapter::KEY_Colon, Qt::Key_Colon, ":"}, + {GUIEventAdapter::KEY_Quote, Qt::Key_QuoteLeft, "'"}, + {GUIEventAdapter::KEY_Quotedbl, Qt::Key_QuoteDbl, "\""}, + {GUIEventAdapter::KEY_Underscore, Qt::Key_Underscore, "_"}, + {GUIEventAdapter::KEY_Plus, Qt::Key_Plus, "+"}, + {GUIEventAdapter::KEY_Minus, Qt::Key_Minus, "-"}, + {GUIEventAdapter::KEY_Asterisk, Qt::Key_Asterisk, "*"}, + {GUIEventAdapter::KEY_Equals, Qt::Key_Equal, "="}, + {GUIEventAdapter::KEY_Slash, Qt::Key_Slash, "/"}, + + {GUIEventAdapter::KEY_Left, Qt::Key_Left}, + {GUIEventAdapter::KEY_Right, Qt::Key_Right}, + {GUIEventAdapter::KEY_Up, Qt::Key_Up}, + {GUIEventAdapter::KEY_Down, Qt::Key_Down}, + + {GUIEventAdapter::KEY_Shift_L, Qt::Key_Shift}, + {GUIEventAdapter::KEY_Shift_R, Qt::Key_Shift}, + {GUIEventAdapter::KEY_Control_L, Qt::Key_Control}, + {GUIEventAdapter::KEY_Control_R, Qt::Key_Control}, + {GUIEventAdapter::KEY_Meta_L, Qt::Key_Meta}, + {GUIEventAdapter::KEY_Meta_R, Qt::Key_Meta}, + + +}; + +std::vector global_keymap; + + #if 0 class SyncPolishOperation : public osg::Operation { @@ -80,18 +134,19 @@ class CustomRenderControl : public QQuickRenderControl { public: CustomRenderControl(QWindow* win) - : _qWindow(win) + : _qWindow(win) { - Q_ASSERT(win); + // Q_ASSERT(win); } - + QWindow* renderWindow(QPoint* offset) override { if (offset) { - *offset = QPoint(0,0); + *offset = QPoint(0, 0); } return _qWindow; } + private: QWindow* _qWindow = nullptr; }; @@ -100,98 +155,85 @@ class QQuickDrawablePrivate : public QObject { Q_OBJECT public: - CustomRenderControl* renderControl = nullptr; - - // the window our osgViewer is using - flightgear::GraphicsWindowQt5* graphicsWindow = nullptr; - + QQmlComponent* qmlComponent = nullptr; QQmlEngine* qmlEngine = nullptr; bool syncRequired = true; - + QQuickItem* rootItem = nullptr; - + // this window is neither created()-ed nor shown but is needed by // QQuickRenderControl for historical reasons, and gives us a place to // inject events from the OSG side. QQuickWindow* quickWindow = nullptr; - + + // window representing the OSG window, needed + // for making our adpoted context current + QWindow* foreignOSGWindow = nullptr; QOpenGLContext* qtContext = nullptr; - - public slots: + + void frameEvent() + { + if (syncRequired) { + renderControl->polishItems(); + syncRequired = false; + + if (QOpenGLContext::currentContext() != qtContext) { + bool ok = qtContext->makeCurrent(foreignOSGWindow); + if (!ok) { + SG_LOG(SG_GUI, SG_ALERT, "make current failed"); + } + } + renderControl->sync(); + } + } +public slots: void onComponentLoaded() { if (qmlComponent->isError()) { QList errorList = qmlComponent->errors(); - Q_FOREACH (const QQmlError &error, errorList) { + Q_FOREACH (const QQmlError& error, errorList) { qWarning() << error.url() << error.line() << error; } return; } - - QObject *rootObject = qmlComponent->create(); + + QObject* rootObject = qmlComponent->create(); if (qmlComponent->isError()) { QList errorList = qmlComponent->errors(); - Q_FOREACH (const QQmlError &error, errorList) { + Q_FOREACH (const QQmlError& error, errorList) { qWarning() << error.url() << error.line() << error; } return; } - - rootItem = qobject_cast(rootObject); + + rootItem = qobject_cast(rootObject); if (!rootItem) { qWarning() << Q_FUNC_INFO << "root object not a QQuickItem" << rootObject; delete rootObject; return; } - + // The root item is ready. Associate it with the window. rootItem->setParentItem(quickWindow->contentItem()); syncRequired = true; + rootItem->setWidth(quickWindow->width()); + rootItem->setHeight(quickWindow->height()); } - + void onSceneChanged() { syncRequired = true; } - + void onRenderRequested() { qWarning() << Q_FUNC_INFO; } - - bool eventFilter(QObject* obj, QEvent* event) override - { - if (obj != graphicsWindow->getGLWindow()) { - return false; - } - - const auto eventType = event->type(); - switch (eventType) { - case QEvent::MouseMove: - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - case QEvent::Wheel: - case QEvent::KeyPress: - case QEvent::KeyRelease: - { - bool result = QCoreApplication::sendEvent(quickWindow, event); - // result always seems to be true here, not useful - if (result && event->isAccepted()) { - return true; - } - } - default: - break; - } - - return false; - } }; -static QObject* fgqmlinstance_provider(QQmlEngine *engine, QJSEngine *scriptEngine) +static QObject* fgqmlinstance_provider(QQmlEngine* engine, QJSEngine* scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) @@ -200,73 +242,270 @@ static QObject* fgqmlinstance_provider(QQmlEngine *engine, QJSEngine *scriptEngi return instance; } -QQuickDrawable::QQuickDrawable() : - d(new QQuickDrawablePrivate) +static QObject* fgqq_windowManager_provider(QQmlEngine* engine, QJSEngine* scriptEngine) +{ + Q_UNUSED(scriptEngine) + + FGQQWindowManager* instance = new FGQQWindowManager(engine); + return instance; +} + +class ReloadCommand : public SGCommandMgr::Command +{ +public: + ReloadCommand(QQuickDrawable* qq) : _drawable(qq) + { + } + + bool operator()(const SGPropertyNode* aNode, SGPropertyNode* root) override + { + SG_UNUSED(aNode); + SG_UNUSED(root); + std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path"); + _drawable->reload(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath))); + return true; + } + +private: + QQuickDrawable* _drawable; +}; + +class QuickEventHandler : public osgGA::GUIEventHandler +{ +public: + QuickEventHandler(QQuickDrawablePrivate* p) : _drawable(p) + { + populateKeymap(); + } + + bool handle(const GUIEventAdapter& ea, GUIActionAdapter& aa, osg::Object*, osg::NodeVisitor*) override + { + Q_UNUSED(aa); + + if (ea.getHandled()) return false; + + // frame event, ho hum ... + if (ea.getEventType() == GUIEventAdapter::FRAME) { + _drawable->frameEvent(); + return false; + } + + // Qt expects increasing downward mouse coords + const float fixedY = (ea.getMouseYOrientation() == GUIEventAdapter::Y_INCREASING_UPWARDS) ? ea.getWindowHeight() - ea.getY() : ea.getY(); + const double pixelRatio = _drawable->foreignOSGWindow->devicePixelRatio(); + + QPointF pointInWindow{ea.getX() / pixelRatio, fixedY / pixelRatio}; + QPointF screenPt = pointInWindow + + QPointF{ea.getWindowX() / pixelRatio, ea.getWindowY() / pixelRatio}; + + // const int scaledX = static_cast(ea.getX() / static_pixelRatio); + // const int scaledY = static_cast(fixedY / static_pixelRatio); + + switch (ea.getEventType()) { + case (GUIEventAdapter::DRAG): + case (GUIEventAdapter::MOVE): { + QMouseEvent m(QEvent::MouseMove, pointInWindow, pointInWindow, screenPt, + Qt::NoButton, + osgButtonMaskToQt(ea), osgModifiersToQt(ea)); + QCoreApplication::sendEvent(_drawable->quickWindow, &m); + return m.isAccepted(); + } + + case (GUIEventAdapter::PUSH): + case (GUIEventAdapter::RELEASE): { + const bool isUp = (ea.getEventType() == GUIEventAdapter::RELEASE); + QMouseEvent m(isUp ? QEvent::MouseButtonRelease : QEvent::MouseButtonPress, + pointInWindow, pointInWindow, screenPt, + osgButtonToQt(ea), + osgButtonMaskToQt(ea), osgModifiersToQt(ea)); + QCoreApplication::sendEvent(_drawable->quickWindow, &m); + + if (!isUp) { + if (m.isAccepted()) { + // deactivate PUI + auto active = puActiveWidget(); + if (active) { + active->invokeDownCallback(); + } + } + + // on mouse downs which we don't accept, take focus back + // qInfo() << "Clearing QQ focus"; + // auto focused = _drawable->quickWindow->activeFocusItem(); + // if (focused) { + // focused->setFocus(false, Qt::MouseFocusReason); + // } + } + + return m.isAccepted(); + } + + case (GUIEventAdapter::KEYDOWN): + case (GUIEventAdapter::KEYUP): { + const bool isKeyRelease = (ea.getEventType() == GUIEventAdapter::KEYUP); + const auto& key = osgKeyToQt(ea.getKey()); + QString s = key.s; + + QKeyEvent k(isKeyRelease ? QEvent::KeyRelease : QEvent::KeyPress, + key.qt, osgModifiersToQt(ea), s); + QCoreApplication::sendEvent(_drawable->quickWindow, &k); + return k.isAccepted(); + } + + default: + return false; + } + + return false; + } +private: + Qt::MouseButtons osgButtonMaskToQt(const osgGA::GUIEventAdapter& ea) const + { + const int mask = ea.getButtonMask(); + Qt::MouseButtons result = Qt::NoButton; + if (mask & osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON) + result |= Qt::LeftButton; + + if (mask & osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON) + result |= Qt::MiddleButton; + + if (mask & osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON) + result |= Qt::RightButton; + + return result; + } + + Qt::MouseButton osgButtonToQt(const osgGA::GUIEventAdapter& ea) const + { + switch (ea.getButton()) { + case osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON: return Qt::LeftButton; + case osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON: return Qt::MiddleButton; + case osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON: return Qt::RightButton; + + default: + break; + } + + return Qt::NoButton; + } + + Qt::KeyboardModifiers osgModifiersToQt(const osgGA::GUIEventAdapter& ea) const + { + Qt::KeyboardModifiers result = Qt::NoModifier; + const int mask = ea.getModKeyMask(); + if (mask & osgGA::GUIEventAdapter::MODKEY_ALT) result |= Qt::AltModifier; + if (mask & osgGA::GUIEventAdapter::MODKEY_CTRL) result |= Qt::ControlModifier; + if (mask & osgGA::GUIEventAdapter::MODKEY_META) result |= Qt::MetaModifier; + if (mask & osgGA::GUIEventAdapter::MODKEY_SHIFT) result |= Qt::ShiftModifier; + return result; + } + + QtKey osgKeyToQt(int code) const + { + auto it = std::lower_bound(global_keymap.begin(), global_keymap.end(), + QtKey{code, 0, {}}, + [](const QtKey& a, const QtKey& b) { return a.osg < b.osg; }); + if ((it == global_keymap.end()) || (it->osg != code)) { + qWarning() << "no mapping defined for OSG key:" << code; + return {0, 0, ""}; + } + + return *it; + } + + void populateKeymap() + { + if (!global_keymap.empty()) + return; + + // regular keymappsing for A..Z and 0..9 + for (int i = 0; i < 10; ++i) { + global_keymap.emplace_back(GUIEventAdapter::KEY_0 + i, Qt::Key_0 + i, QString::number(i)); + } + + for (int i = 0; i < 26; ++i) { + global_keymap.emplace_back(GUIEventAdapter::KEY_A + i, Qt::Key_A + i, QChar::fromLatin1('a' + i)); + } + + for (int i = 0; i < 26; ++i) { + global_keymap.emplace_back('A' + i, Qt::Key_A + i, QChar::fromLatin1('A' + i)); + } + + // custom key mappsing + global_keymap.insert(global_keymap.end(), keymapInit); + + // sort by OSG code for fast lookups + std::sort(global_keymap.begin(), global_keymap.end(), + [](const QtKey& a, const QtKey& b) { return a.osg < b.osg; }); + } + + QQuickDrawablePrivate* _drawable; +}; + +QQuickDrawable::QQuickDrawable() : d(new QQuickDrawablePrivate) { setUseDisplayList(false); setDataVariance(Object::DYNAMIC); - + osg::StateSet* stateSet = getOrCreateStateSet(); stateSet->setRenderBinDetails(1001, "RenderBin"); -#if 0 - // speed optimization? - stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); - // We can do translucent menus, so why not. :-) - stateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA)); - stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); - stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF); - - stateSet->setTextureAttribute(0, new osg::TexEnv(osg::TexEnv::MODULATE)); - - stateSet->setMode(GL_FOG, osg::StateAttribute::OFF); - stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); -#endif + + QSurfaceFormat format; + format.setRenderableType(QSurfaceFormat::OpenGL); + QSurfaceFormat::setDefaultFormat(format); static bool doneQmlRegistration = false; if (!doneQmlRegistration) { doneQmlRegistration = true; // singleton system object - qmlRegisterSingletonType("flightgear.org", 1, 0, "System", fgqmlinstance_provider); + qmlRegisterSingletonType("FlightGear", 1, 0, "System", fgqmlinstance_provider); + qmlRegisterSingletonType("FlightGear", 1, 0, "WindowManager", fgqq_windowManager_provider); // QML types - qmlRegisterType("flightgear.org", 1, 0, "Property"); + qmlRegisterType("FlightGear", 1, 0, "Property"); + //qmlRegisterType("FlightGear", 1, 0, "DialogStateController"); } } -QQuickDrawable::~QQuickDrawable() -{ - delete d->qmlEngine; - delete d->renderControl; - delete d; -} - -void QQuickDrawable::setup(osgViewer::GraphicsWindow* gw) +void QQuickDrawable::setup(osgViewer::GraphicsWindow *gw, osgViewer::Viewer *viewer) { osg::GraphicsContext* gc = gw; - - // install operations on the graphics context to get the context - osg::ref_ptr op(new RetriveGraphicsThreadOperation); - + osg::ref_ptr op(new flightgear::RetriveGraphicsThreadOperation); gc->add(op); gc->runOperations(); - + // hopefully done now! - + d->qtContext = op->context(); - d->graphicsWindow = dynamic_cast(gw); - if (!d->graphicsWindow) { - qFatal("QQuick drawable requires a GraphicsWindowQt5 for now"); - } - d->renderControl = new CustomRenderControl(d->graphicsWindow->getGLWindow()); - + + // d->qtContext->setFormat(format); + + d->foreignOSGWindow = flightgear::qtWindowFromOSG(gw); + // d->foreignOSGWindow->setFormat(format); + d->foreignOSGWindow->setSurfaceType(QSurface::OpenGLSurface); + + d->renderControl = new CustomRenderControl(d->foreignOSGWindow); d->quickWindow = new QQuickWindow(d->renderControl); + + + bool ok = d->qtContext->makeCurrent(d->foreignOSGWindow); + if (!ok) { + SG_LOG(SG_GUI, SG_ALERT, "context not current"); + } + d->quickWindow->setClearBeforeRendering(false); - + d->qmlEngine = new QQmlEngine; + + SGPath rootQMLPath = SGPath::fromUtf8(fgGetString("/sim/gui/qml-root-path")); + SG_LOG(SG_GENERAL, SG_INFO, "Root QML dir:" << rootQMLPath.dir()); + d->qmlEngine->addImportPath(QString::fromStdString(rootQMLPath.dir())); + // d->qmlEngine->addImportPath(QStringLiteral("qrc:///")); + if (!d->qmlEngine->incubationController()) d->qmlEngine->setIncubationController(d->quickWindow->incubationController()); - + d->renderControl->initialize(d->qtContext); #if QT_VERSION < 0x050600 @@ -278,42 +517,43 @@ void QQuickDrawable::setup(osgViewer::GraphicsWindow* gw) d, &QQuickDrawablePrivate::onSceneChanged); QObject::connect(d->renderControl, &QQuickRenderControl::renderRequested, d, &QQuickDrawablePrivate::onRenderRequested); - - // insert ourselves as a filter on the GraphicsWindow, so we can intercept - // events directly and pass on to our window - d->graphicsWindow->getGLWindow()->installEventFilter(d); + + + viewer->getEventHandlers().push_front(new QuickEventHandler(d)); } void QQuickDrawable::drawImplementation(osg::RenderInfo& renderInfo) const { - if (d->syncRequired) { - // should happen on the main thread - d->renderControl->polishItems(); - - d->syncRequired = false; - // must ensure the GUI thread is blocked for this - d->renderControl->sync(); - // can unblock now - } - QOpenGLFunctions* glFuncs = d->qtContext->functions(); // prepare any state QQ2 needs d->quickWindow->resetOpenGLState(); - + // and reset these manually glFuncs->glPixelStorei(GL_PACK_ALIGNMENT, 4); glFuncs->glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glFuncs->glPixelStorei(GL_PACK_ROW_LENGTH, 0); glFuncs->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - + d->renderControl->render(); - - glFuncs->glFlush(); - + + // otherwise the PUI camera gets confused + d->quickWindow->resetOpenGLState(); +} + +void QQuickDrawable::reload(QUrl url) +{ + d->qmlEngine->clearComponentCache(); + setSource(url); } void QQuickDrawable::setSource(QUrl url) { + if (d->rootItem) + delete d->rootItem; + if (d->qmlComponent) + delete d->qmlComponent; + d->rootItem = nullptr; + d->qmlComponent = new QQmlComponent(d->qmlEngine, url); if (d->qmlComponent->isLoading()) { QObject::connect(d->qmlComponent, &QQmlComponent::statusChanged, @@ -326,15 +566,16 @@ void QQuickDrawable::setSource(QUrl url) void QQuickDrawable::resize(int width, int height) { // we need to unscale from physical pixels back to logical, otherwise we end up double-scaled. - const float currentPixelRatio = d->graphicsWindow->getGLWindow()->devicePixelRatio(); + const float currentPixelRatio = static_cast(d->foreignOSGWindow->devicePixelRatio()); const int logicalWidth = static_cast(width / currentPixelRatio); const int logicalHeight = static_cast(height / currentPixelRatio); - + if (d->rootItem) { d->rootItem->setWidth(logicalWidth); d->rootItem->setHeight(logicalHeight); } - + + SG_LOG(SG_GUI, SG_INFO, "Resize:, lw=" << logicalWidth << ", lh=" << logicalHeight); d->quickWindow->setGeometry(0, 0, logicalWidth, logicalHeight); } diff --git a/src/GUI/QQuickDrawable.hxx b/src/GUI/QQuickDrawable.hxx old mode 100644 new mode 100755 index 51dae888a..c3b4fc94e --- a/src/GUI/QQuickDrawable.hxx +++ b/src/GUI/QQuickDrawable.hxx @@ -1,3 +1,21 @@ +// QQuickDrawable.hxx - OSG Drawable using a QQuickRenderControl to draw +// +// Copyright (C) 2019 James Turner +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + #ifndef FG_VIEWER_QUICK_DRAWABLE_HXX #define FG_VIEWER_QUICK_DRAWABLE_HXX @@ -6,6 +24,10 @@ #include +namespace osgViewer { +class Viewer; +} + class QQuickDrawablePrivate; class QQuickDrawable : public osg::Drawable @@ -19,16 +41,14 @@ public: void drawImplementation(osg::RenderInfo& renderInfo) const override; - /** Return true, FGPanelNode does support accept(PrimitiveFunctor&). */ - // virtual bool supports(const osg::PrimitiveFunctor&) const { return true; } - - //virtual void accept(osg::PrimitiveFunctor& functor) const; - - void setup(osgViewer::GraphicsWindow* gw); + void setup(osgViewer::GraphicsWindow* gw, osgViewer::Viewer* viewer); void setSource(QUrl url); + void reload(QUrl url); + void resize(int width, int height); + private: QQuickDrawablePrivate* d; }; diff --git a/src/GUI/QtHelpers.cxx b/src/GUI/QtHelpers.cxx new file mode 100644 index 000000000..c1322ddea --- /dev/null +++ b/src/GUI/QtHelpers.cxx @@ -0,0 +1,29 @@ +// QtHelpers.cxx - make Qt and SimGear/FlightGear place nicely +// +// Copyright (C) 2019 James Turner +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "config.h" + +#include + +#include "QtHelpers.hxx" + +QDebug operator<<(QDebug debug, const SGPath& c) +{ + debug << QString::fromStdString(c.utf8Str()); + return debug; +} diff --git a/src/GUI/QtHelpers.hxx b/src/GUI/QtHelpers.hxx new file mode 100644 index 000000000..97199fad1 --- /dev/null +++ b/src/GUI/QtHelpers.hxx @@ -0,0 +1,28 @@ +// QtHelpers.hxx - make Qt and SimGear/FlightGear place nicely +//// +// Copyright (C) 2019 James Turner +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef QTGUI_HELPERS_H +#define QTGUI_HELPERS_H + +#include + +class SGPath; + +QDebug operator<<(QDebug debug, const SGPath& c); + +#endif // QTGUI_HELPERS_H diff --git a/src/GUI/qml/LauncherSettings.qml b/src/GUI/qml/LauncherSettings.qml deleted file mode 100644 index cd72ee3aa..000000000 --- a/src/GUI/qml/LauncherSettings.qml +++ /dev/null @@ -1,9 +0,0 @@ -import QtQuick 2.4 -import FlightGear.Launcher 1.0 - - -RenderSettings -{ -} - - diff --git a/src/GUI/resources.qrc b/src/GUI/resources.qrc index 97b9edbca..c371045ac 100644 --- a/src/GUI/resources.qrc +++ b/src/GUI/resources.qrc @@ -56,7 +56,6 @@ qml/Style.qml qml/qmldir qml/ClickableText.qml - qml/LauncherSettings.qml qml/Section.qml qml/SettingControl.qml qml/SettingCheckbox.qml diff --git a/src/Main/CMakeLists.txt b/src/Main/CMakeLists.txt index f4958c664..3435a392e 100644 --- a/src/Main/CMakeLists.txt +++ b/src/Main/CMakeLists.txt @@ -149,3 +149,12 @@ if (MSVC) set_property(TARGET fgfs PROPERTY VS_GLOBAL_LocalDebuggerEnvironment "PATH=${_install_bin_dir};${_msvc_3rdparty_bin_dir};${_qt5_bin_dir_native}") endif() + +if (TARGET Qt5::qmake) + get_target_property(_qt5_qmake_path Qt5::qmake LOCATION) + get_filename_component(_qt5_bin_dir ${_qt5_qmake_path} DIRECTORY) + get_filename_component(_qt5_root_dir ${_qt5_bin_dir} DIRECTORY) + + # for QtPlatformHeaders + target_include_directories(fgfs PRIVATE ${_qt5_root_dir}/include) +endif() diff --git a/src/Viewer/CMakeLists.txt b/src/Viewer/CMakeLists.txt index acc1b4422..2a4bcede4 100644 --- a/src/Viewer/CMakeLists.txt +++ b/src/Viewer/CMakeLists.txt @@ -25,8 +25,10 @@ set(HEADERS ) if (Qt5Core_FOUND) - list(APPEND HEADERS GraphicsWindowQt5.hxx) - list(APPEND SOURCES GraphicsWindowQt5.cpp) + list(APPEND HEADERS GraphicsWindowQt5.hxx + OSGQtAdaption.hxx) + list(APPEND SOURCES GraphicsWindowQt5.cpp + OSGQtAdaption.cxx) endif() if (YES) diff --git a/src/Viewer/GraphicsWindowQt5.cpp b/src/Viewer/GraphicsWindowQt5.cpp index a1520bd5f..dbd56f067 100644 --- a/src/Viewer/GraphicsWindowQt5.cpp +++ b/src/Viewer/GraphicsWindowQt5.cpp @@ -788,6 +788,9 @@ public: Qt5WindowingSystem() { +#if OSG_VERSION_GREATER_THAN(3,5,9) + setName("FlightGearQt5"); +#endif } ~Qt5WindowingSystem() @@ -899,6 +902,8 @@ void initQtWindowingSystem() { #if OSG_VERSION_LESS_THAN(3,5,2) osg::GraphicsContext::setWindowingSystemInterface(Qt5WindowingSystem::getInterface()); +#else + osg::GraphicsContext::getWindowingSystemInterfaces()->addWindowingSystemInterface(Qt5WindowingSystem::getInterface()); #endif } diff --git a/src/Viewer/OSGQtAdaption.cxx b/src/Viewer/OSGQtAdaption.cxx new file mode 100644 index 000000000..3bda4d123 --- /dev/null +++ b/src/Viewer/OSGQtAdaption.cxx @@ -0,0 +1,126 @@ +// Copyright (C) 2020 James Turner + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "config.h" + +#include "OSGQtAdaption.hxx" + +#include + +#include +#include +#include + +#include + +#if defined(SG_MAC) + // defined in CocoaUtils.mm +#elif defined(SG_WINDOWS) +# include +# include +#else +// assume X11 for now +# include +# include +#endif + +namespace flightgear +{ + +#if defined(SG_MAC) + +// defined in CocoaUtils.mm + +#elif defined(SG_WINDOWS) + + +QOpenGLContext* qtContextFromOSG(osg::GraphicsContext* context) +{ + auto win = dynamic_cast(context); + if (!win) { + return nullptr; + } + + auto wglnc = QWGLNativeContext{win->getWGLContext(), win->getHWND()}; + QVariant nativeHandle = QVariant::fromValue(wglnc); + + if (nativeHandle.isNull()) + return nullptr; + + std::unique_ptr qtOpenGLContext(new QOpenGLContext); + qtOpenGLContext->setNativeHandle(nativeHandle); + if (!qtOpenGLContext->create()) { + return nullptr; + } + + return qtOpenGLContext.release(); +} + +QWindow* qtWindowFromOSG(osgViewer::GraphicsWindow* graphicsWindow) +{ + auto win = dynamic_cast(graphicsWindow); + if (!win) { + return nullptr; + } + + return QWindow::fromWinId(reinterpret_cast(win->getHWND())); +} + +#else + +// assume X11 for now + +QOpenGLContext* qtContextFromOSG(osg::GraphicsContext* context) +{ + auto win = dynamic_cast(context); + if (!win) { + return nullptr; + } + + auto glxnc = QGLXNativeContext{win->getContext(), win->getDisplay(), win->getWindow(), 0}; + QVariant nativeHandle = QVariant::fromValue(glxnc); + + if (nativeHandle.isNull()) + return nullptr; + + std::unique_ptr qtOpenGLContext(new QOpenGLContext); + qtOpenGLContext->setNativeHandle(nativeHandle); + if (!qtOpenGLContext->create()) { + return nullptr; + } + + return qtOpenGLContext.release(); +} + +QWindow* qtWindowFromOSG(osgViewer::GraphicsWindow* graphicsWindow) +{ + auto win = dynamic_cast(graphicsWindow); + if (!win) { + return nullptr; + } + + return QWindow::fromWinId(static_cast(win->getWindow())); +} + +#endif + +void RetriveGraphicsThreadOperation::operator()(osg::GraphicsContext* context) +{ + _result = QThread::currentThread(); + _context = qtContextFromOSG(context); +} + +} // of namespace diff --git a/src/Viewer/OSGQtAdaption.hxx b/src/Viewer/OSGQtAdaption.hxx new file mode 100644 index 000000000..548c39179 --- /dev/null +++ b/src/Viewer/OSGQtAdaption.hxx @@ -0,0 +1,53 @@ +// Copyright (C) 2020 James Turner + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#pragma once + +#include +#include + +class QWindow; +class QOpenGLContext; +class QThread; + +namespace flightgear +{ + +QOpenGLContext* qtContextFromOSG(osg::GraphicsContext* context); +QWindow* qtWindowFromOSG(osgViewer::GraphicsWindow* graphicsWindow); + +/** + * Helper run on Graphics thread to retrive its Qt wrapper + */ +class RetriveGraphicsThreadOperation : public osg::GraphicsOperation +{ +public: + RetriveGraphicsThreadOperation() + : osg::GraphicsOperation("RetriveGraphicsThread", false) + { + } + + QThread* thread() const { return _result; } + QOpenGLContext* context() const { return _context; } + + void operator()(osg::GraphicsContext* context) override; +private: + QThread* _result = nullptr; + QOpenGLContext* _context = nullptr; +}; + + +} \ No newline at end of file diff --git a/src/Viewer/PUICamera.cxx b/src/Viewer/PUICamera.cxx index 2d3f86727..d198d93b0 100644 --- a/src/Viewer/PUICamera.cxx +++ b/src/Viewer/PUICamera.cxx @@ -29,9 +29,10 @@ #include #include -#include -#include #include +#include +#include +#include #include @@ -301,7 +302,7 @@ PUICamera::PUICamera() : { } -void PUICamera::init(osg::Group* parent) +void PUICamera::init(osg::Group* parent, osgViewer::Viewer* viewer) { setName("PUI FBO camera"); @@ -363,11 +364,6 @@ void PUICamera::init(osg::Group* parent) fsQuadGeode->addDrawable(_fullScreenQuad); fsQuadGeode->setName("PUI fullscreen Geode"); fsQuadGeode->setNodeMask(SG_NODEMASK_GUI_BIT); - - // set the event callback on the Geode; if we set it on the Drawable, - // osgGA calls it twice (as a NodeCallback and also a Drawable::EventCallback) - fsQuadGeode->setEventCallback(new PUIEventHandler(this)); - parent->addChild(this); parent->addChild(fsQuadGeode); @@ -376,6 +372,11 @@ void PUICamera::init(osg::Group* parent) osg::Viewport* vport = camera->getViewport(); resizeUi(vport->width(), vport->height()); } + + // push_front so we keep the order of event handlers the opposite of + // the rendering order (i.e top-most UI layer has the front-most event + // handler) + viewer->getEventHandlers().push_front(new PUIEventHandler(this)); } // remove once we require OSG 3.4 @@ -400,12 +401,13 @@ void PUICamera::resizeUi(int width, int height) resizeAttachments(scaledWidth, scaledHeight); #endif + const float puiZ = 1.0; // resize the full-screen quad osg::Vec3Array* fsQuadVertices = static_cast(_fullScreenQuad->getVertexArray()); - (*fsQuadVertices)[0] = osg::Vec3(0.0, height, 0.0); - (*fsQuadVertices)[1] = osg::Vec3(0.0, 0.0, 0.0); - (*fsQuadVertices)[2] = osg::Vec3(width, 0.0, 0.0); - (*fsQuadVertices)[3] = osg::Vec3(width, height, 0.0); + (*fsQuadVertices)[0] = osg::Vec3(0.0, height, puiZ); + (*fsQuadVertices)[1] = osg::Vec3(0.0, 0.0, puiZ); + (*fsQuadVertices)[2] = osg::Vec3(width, 0.0, puiZ); + (*fsQuadVertices)[3] = osg::Vec3(width, height, puiZ); fsQuadVertices->dirty(); } diff --git a/src/Viewer/PUICamera.hxx b/src/Viewer/PUICamera.hxx index afdb2c7dc..57ff41124 100644 --- a/src/Viewer/PUICamera.hxx +++ b/src/Viewer/PUICamera.hxx @@ -26,6 +26,10 @@ namespace osg class Geometry; } +namespace osgViewer { +class Viewer; +} + namespace flightgear { @@ -41,7 +45,9 @@ public: // osg::Camera already defines a resize() so use this name void resizeUi(int width, int height); - void init(osg::Group *parent); + + void init(osg::Group* parent, osgViewer::Viewer* viewer); + private: #if OSG_VERSION_LESS_THAN(3,4,0) class UpdateViewportAndFBOAfterTextureResizeCallback; diff --git a/src/Viewer/WindowBuilder.cxx b/src/Viewer/WindowBuilder.cxx index f4d3c3365..b1fcf1d62 100644 --- a/src/Viewer/WindowBuilder.cxx +++ b/src/Viewer/WindowBuilder.cxx @@ -14,14 +14,12 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif +#include "config.h" #include "WindowBuilder.hxx" - #include "WindowSystemAdapter.hxx" #include
+#include #include #include @@ -72,6 +70,14 @@ void WindowBuilder::makeDefaultTraits(bool stencil) { GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface(); +#if defined(HAVE_QT) && OSG_VERSION_GREATER_THAN(3, 5, 9) + if (usingQtGraphicsWindow) { + // use the correct WSI for OpenSceneGraph >= 3.6 + wsi = osg::GraphicsContext::getWindowingSystemInterface("FlightGearQt5"); + } +#endif + + defaultTraits = new osg::GraphicsContext::Traits; auto traits = defaultTraits.get(); @@ -107,7 +113,10 @@ void WindowBuilder::makeDefaultTraits(bool stencil) auto data = new GraphicsWindowQt5::WindowData; data->createFullscreen = wantFullscreen; data->isPrimaryWindow = true; - + +#if OSG_VERSION_GREATER_THAN(3, 5, 9) + traits->windowingSystemPreference = "FlightGearQt5"; +#endif traits->inheritedWindowData = data; traits->windowDecoration = true; traits->supportsResize = true; diff --git a/src/Viewer/renderer_compositor.cxx b/src/Viewer/renderer_compositor.cxx index 04429faee..ebb75125a 100644 --- a/src/Viewer/renderer_compositor.cxx +++ b/src/Viewer/renderer_compositor.cxx @@ -110,7 +110,6 @@ #if defined(HAVE_QT) #include -#include #endif #if defined(HAVE_PUI) @@ -609,23 +608,22 @@ FGRenderer::setupView( void ) #if defined(HAVE_PUI) _puiCamera = new flightgear::PUICamera; - _puiCamera->init(guiCamera); + _puiCamera->init(guiCamera, viewer); #endif #if defined(HAVE_QT) std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path"); - auto graphicsWindowQt = dynamic_cast(guiCamera->getGraphicsContext()); + auto graphicsWindow = dynamic_cast(guiCamera->getGraphicsContext()); - if (graphicsWindowQt && !rootQMLPath.empty()) { + if (!rootQMLPath.empty()) { _quickDrawable = new QQuickDrawable; - _quickDrawable->setup(graphicsWindowQt); + _quickDrawable->setup(graphicsWindow, viewer); _quickDrawable->setSource(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath))); geode->addDrawable(_quickDrawable); } #endif - // Draw first (eg. before Canvas GUI) - guiCamera->insertChild(0, geode); + guiCamera->addChild(geode); guiCamera->insertChild(0, FGPanelNode::create2DPanelNode()); } diff --git a/src/Viewer/renderer_legacy.cxx b/src/Viewer/renderer_legacy.cxx index dcb74ce56..a399a61ff 100755 --- a/src/Viewer/renderer_legacy.cxx +++ b/src/Viewer/renderer_legacy.cxx @@ -110,7 +110,6 @@ #if defined(HAVE_QT) #include -#include #endif #if defined(HAVE_PUI) @@ -1504,23 +1503,22 @@ FGRenderer::setupView( void ) #if defined(HAVE_PUI) _puiCamera = new flightgear::PUICamera; - _puiCamera->init(guiCamera); + _puiCamera->init(guiCamera, viewer); #endif #if defined(HAVE_QT) std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path"); - auto graphicsWindowQt = dynamic_cast(guiCamera->getGraphicsContext()); + auto graphicsWindow = dynamic_cast(guiCamera->getGraphicsContext()); - if (graphicsWindowQt && !rootQMLPath.empty()) { + if (!rootQMLPath.empty()) { _quickDrawable = new QQuickDrawable; - _quickDrawable->setup(graphicsWindowQt); + _quickDrawable->setup(graphicsWindow, viewer); _quickDrawable->setSource(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath))); geode->addDrawable(_quickDrawable); } #endif - // Draw first (eg. before Canvas GUI) - guiCamera->insertChild(0, geode); + guiCamera->addChild(geode); guiCamera->insertChild(0, FGPanelNode::create2DPanelNode()); }