1
0
Fork 0

UI handling tweaks

- add class to handle top-level windows
- remove requirement to use custom graphics-window
- order alongside PUI UI
This commit is contained in:
James Turner 2018-11-12 16:46:41 +01:00
parent af00b5e304
commit a9e5a27e55
22 changed files with 1060 additions and 200 deletions

View file

@ -25,7 +25,7 @@ if(COMMAND cmake_policy)
# OpenGL VND policy : use the old definition for now, until we can audit this # OpenGL VND policy : use the old definition for now, until we can audit this
if(POLICY CMP0072) if(POLICY CMP0072)
cmake_policy(SET CMP0071 OLD) cmake_policy(SET CMP0072 OLD)
endif() endif()
endif() endif()

View file

@ -166,6 +166,8 @@ if (HAVE_QT)
QmlNavCacheWrapper.cxx QmlNavCacheWrapper.cxx
QmlRadioButtonHelper.cxx QmlRadioButtonHelper.cxx
QmlRadioButtonHelper.hxx QmlRadioButtonHelper.hxx
QtHelpers.hxx
QtHelpers.cxx
UnitsModel.cxx UnitsModel.cxx
UnitsModel.hxx UnitsModel.hxx
NavaidSearchModel.hxx NavaidSearchModel.hxx
@ -178,6 +180,8 @@ if (HAVE_QT)
ModelDataExtractor.hxx ModelDataExtractor.hxx
HoverArea.cxx HoverArea.cxx
HoverArea.hxx HoverArea.hxx
FGQQWindowManager.cxx
FGQQWindowManager.hxx
) )
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON) set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)

View file

@ -23,7 +23,6 @@
#include <string> #include <string>
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
/** /**
* open a URL using the system's web-browser * open a URL using the system's web-browser
*/ */

View file

@ -34,6 +34,8 @@
#include <Foundation/NSLocale.h> #include <Foundation/NSLocale.h>
#include <AppKit/NSAlert.h> #include <AppKit/NSAlert.h>
#include <osgViewer/api/Cocoa/GraphicsWindowCocoa>
// simgear // simgear
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
@ -47,6 +49,9 @@
#if defined(HAVE_QT) #if defined(HAVE_QT)
# include <GUI/QtLauncher.hxx> # include <GUI/QtLauncher.hxx>
# include <QOpenGLContext>
# include <QtPlatformHeaders/QCocoaNativeContext>
# include <QWindow>
#endif #endif
NSString* stdStringToCocoa(const std::string& s) NSString* stdStringToCocoa(const std::string& s)
@ -151,6 +156,41 @@ SGPath Options::platformDefaultRoot() const
dataDir.append("data"); dataDir.append("data");
return dataDir; return dataDir;
} }
#if defined(HAVE_QT)
QOpenGLContext* qtContextFromOSG(osg::GraphicsContext* context)
{
osgViewer::GraphicsWindowCocoa* win = dynamic_cast<osgViewer::GraphicsWindowCocoa*>(context);
if (!win) {
return nullptr;
}
auto cnc = QCocoaNativeContext{win->getContext()};
QVariant nativeHandle = QVariant::fromValue(cnc);
if (nativeHandle.isNull())
return nullptr;
std::unique_ptr<QOpenGLContext> qtOpenGLContext(new QOpenGLContext);
qtOpenGLContext->setNativeHandle(nativeHandle);
if (!qtOpenGLContext->create()) {
return nullptr;
}
return qtOpenGLContext.release();
}
QWindow* qtWindowFromOSG(osgViewer::GraphicsWindow* graphicsWindow)
{
osgViewer::GraphicsWindowCocoa* win = dynamic_cast<osgViewer::GraphicsWindowCocoa*>(graphicsWindow);
if (!win) {
return nullptr;
}
NSView* osgView = [win->getContext() view];
return QWindow::fromWinId(reinterpret_cast<qlonglong>(osgView));
}
#endif
} // of namespace flightgear } // of namespace flightgear
@ -222,3 +262,5 @@ void cocoaRegisterTerminateHandler()
name:NSApplicationWillTerminateNotification object:app]; name:NSApplicationWillTerminateNotification object:app];
#endif #endif
} }

View file

@ -0,0 +1,259 @@
#include "FGQQWindowManager.hxx"
#include <algorithm>
#include <QAbstractListModel>
#include <QQmlComponent>
#include <QRectF>
#include <QSettings>
#include <simgear/props/props_io.hxx>
#include "QtHelpers.hxx"
#include <Main/globals.hxx>
////////////////////////////////////////////////////////////////////////////
/**
* @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<int>(_windowData.size());
}
QHash<int, QByteArray> roleNames() const override
{
QHash<int, QByteArray> 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<WindowData>::const_iterator findWindow(QString windowId) const
{
return std::find_if(_windowData.begin(), _windowData.end(),
[windowId](const WindowData& d) { return d.windowId == windowId; });
}
std::vector<WindowData>::iterator findWindow(QString windowId)
{
return std::find_if(_windowData.begin(), _windowData.end(),
[windowId](const WindowData& d) { return d.windowId == windowId; });
}
std::vector<WindowData> _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<size_t>(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<size_t>(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<DialogInfo> _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;
}

View file

@ -0,0 +1,40 @@
#ifndef FGQQWINDOWMANAGER_HXX
#define FGQQWINDOWMANAGER_HXX
#include <QObject>
#include <memory>
// 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<WindowManagerPrivate> _d;
QQmlEngine* _engine = nullptr;
};
#endif // FGQQWINDOWMANAGER_HXX

537
src/GUI/QQuickDrawable.cxx Normal file → Executable file
View file

@ -1,56 +1,110 @@
// QQuickDrawable.cxx - OSG Drawable using a QQuickRenderControl to draw
//
// Copyright (C) 2019 James Turner <james@flightgear.org>
//
// 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 "QQuickDrawable.hxx"
#include <QQuickRenderControl>
#include <QQmlComponent> #include <QQmlComponent>
#include <QQmlEngine>
#include <QQmlContext> #include <QQmlContext>
#include <QQmlEngine>
#include <QQuickRenderControl>
#include <QThread> #include <QCoreApplication>
#include <QOpenGLContext> #include <QOpenGLContext>
#include <QQuickWindow>
#include <QOpenGLFunctions> #include <QOpenGLFunctions>
#include <QQuickItem> #include <QQuickItem>
#include <QCoreApplication> #include <QQuickWindow>
#include <QSurfaceFormat>
#include <QThread>
#include <osg/GraphicsContext> #include <osg/GraphicsContext>
#include <osgGA/GUIEventAdapter>
#include <osgGA/GUIEventHandler>
#include <osgViewer/GraphicsWindow> #include <osgViewer/GraphicsWindow>
#include <osgViewer/Viewer>
//#include <GUI/DialogStateController.hxx>
#include <GUI/FGQQWindowManager.hxx>
#include <GUI/FGQmlInstance.hxx> #include <GUI/FGQmlInstance.hxx>
#include <GUI/FGQmlPropertyNode.hxx> #include <GUI/FGQmlPropertyNode.hxx>
#include <Viewer/OSGQtAdaption.hxx>
#include <Viewer/GraphicsWindowQt5.hxx> #include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <simgear/structure/commands.hxx>
/** #if defined(HAVE_PUI)
* Helper run on Graphics thread to retrive its Qt wrapper #include <plib/pu.h>
*/ #endif
class RetriveGraphicsThreadOperation : public osg::GraphicsOperation
{
public: using namespace osgGA;
RetriveGraphicsThreadOperation()
: osg::GraphicsOperation("RetriveGraphcisThread", false) struct QtKey {
{} QtKey(int _o, int _q, QString _s = {}) : osg(_o), qt(_q), s(_s) {}
QThread* thread() const { return _result; } int osg;
QOpenGLContext* context() const { return _context; } int qt;
QString s;
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;
}; };
const std::initializer_list<QtKey> 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<QtKey> global_keymap;
#if 0 #if 0
class SyncPolishOperation : public osg::Operation class SyncPolishOperation : public osg::Operation
{ {
@ -80,18 +134,19 @@ class CustomRenderControl : public QQuickRenderControl
{ {
public: public:
CustomRenderControl(QWindow* win) CustomRenderControl(QWindow* win)
: _qWindow(win) : _qWindow(win)
{ {
Q_ASSERT(win); // Q_ASSERT(win);
} }
QWindow* renderWindow(QPoint* offset) override QWindow* renderWindow(QPoint* offset) override
{ {
if (offset) { if (offset) {
*offset = QPoint(0,0); *offset = QPoint(0, 0);
} }
return _qWindow; return _qWindow;
} }
private: private:
QWindow* _qWindow = nullptr; QWindow* _qWindow = nullptr;
}; };
@ -100,98 +155,85 @@ class QQuickDrawablePrivate : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
CustomRenderControl* renderControl = nullptr; CustomRenderControl* renderControl = nullptr;
// the window our osgViewer is using
flightgear::GraphicsWindowQt5* graphicsWindow = nullptr;
QQmlComponent* qmlComponent = nullptr; QQmlComponent* qmlComponent = nullptr;
QQmlEngine* qmlEngine = nullptr; QQmlEngine* qmlEngine = nullptr;
bool syncRequired = true; bool syncRequired = true;
QQuickItem* rootItem = nullptr; QQuickItem* rootItem = nullptr;
// this window is neither created()-ed nor shown but is needed by // this window is neither created()-ed nor shown but is needed by
// QQuickRenderControl for historical reasons, and gives us a place to // QQuickRenderControl for historical reasons, and gives us a place to
// inject events from the OSG side. // inject events from the OSG side.
QQuickWindow* quickWindow = nullptr; QQuickWindow* quickWindow = nullptr;
// window representing the OSG window, needed
// for making our adpoted context current
QWindow* foreignOSGWindow = nullptr;
QOpenGLContext* qtContext = 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() void onComponentLoaded()
{ {
if (qmlComponent->isError()) { if (qmlComponent->isError()) {
QList<QQmlError> errorList = qmlComponent->errors(); QList<QQmlError> errorList = qmlComponent->errors();
Q_FOREACH (const QQmlError &error, errorList) { Q_FOREACH (const QQmlError& error, errorList) {
qWarning() << error.url() << error.line() << error; qWarning() << error.url() << error.line() << error;
} }
return; return;
} }
QObject *rootObject = qmlComponent->create(); QObject* rootObject = qmlComponent->create();
if (qmlComponent->isError()) { if (qmlComponent->isError()) {
QList<QQmlError> errorList = qmlComponent->errors(); QList<QQmlError> errorList = qmlComponent->errors();
Q_FOREACH (const QQmlError &error, errorList) { Q_FOREACH (const QQmlError& error, errorList) {
qWarning() << error.url() << error.line() << error; qWarning() << error.url() << error.line() << error;
} }
return; return;
} }
rootItem = qobject_cast<QQuickItem *>(rootObject); rootItem = qobject_cast<QQuickItem*>(rootObject);
if (!rootItem) { if (!rootItem) {
qWarning() << Q_FUNC_INFO << "root object not a QQuickItem" << rootObject; qWarning() << Q_FUNC_INFO << "root object not a QQuickItem" << rootObject;
delete rootObject; delete rootObject;
return; return;
} }
// The root item is ready. Associate it with the window. // The root item is ready. Associate it with the window.
rootItem->setParentItem(quickWindow->contentItem()); rootItem->setParentItem(quickWindow->contentItem());
syncRequired = true; syncRequired = true;
rootItem->setWidth(quickWindow->width());
rootItem->setHeight(quickWindow->height());
} }
void onSceneChanged() void onSceneChanged()
{ {
syncRequired = true; syncRequired = true;
} }
void onRenderRequested() void onRenderRequested()
{ {
qWarning() << Q_FUNC_INFO; 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(engine)
Q_UNUSED(scriptEngine) Q_UNUSED(scriptEngine)
@ -200,73 +242,270 @@ static QObject* fgqmlinstance_provider(QQmlEngine *engine, QJSEngine *scriptEngi
return instance; return instance;
} }
QQuickDrawable::QQuickDrawable() : static QObject* fgqq_windowManager_provider(QQmlEngine* engine, QJSEngine* scriptEngine)
d(new QQuickDrawablePrivate) {
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<int>(ea.getX() / static_pixelRatio);
// const int scaledY = static_cast<int>(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); setUseDisplayList(false);
setDataVariance(Object::DYNAMIC); setDataVariance(Object::DYNAMIC);
osg::StateSet* stateSet = getOrCreateStateSet(); osg::StateSet* stateSet = getOrCreateStateSet();
stateSet->setRenderBinDetails(1001, "RenderBin"); stateSet->setRenderBinDetails(1001, "RenderBin");
#if 0
// speed optimization? QSurfaceFormat format;
stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); format.setRenderableType(QSurfaceFormat::OpenGL);
// We can do translucent menus, so why not. :-) QSurfaceFormat::setDefaultFormat(format);
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
static bool doneQmlRegistration = false; static bool doneQmlRegistration = false;
if (!doneQmlRegistration) { if (!doneQmlRegistration) {
doneQmlRegistration = true; doneQmlRegistration = true;
// singleton system object // singleton system object
qmlRegisterSingletonType<FGQmlInstance>("flightgear.org", 1, 0, "System", fgqmlinstance_provider); qmlRegisterSingletonType<FGQmlInstance>("FlightGear", 1, 0, "System", fgqmlinstance_provider);
qmlRegisterSingletonType<FGQmlInstance>("FlightGear", 1, 0, "WindowManager", fgqq_windowManager_provider);
// QML types // QML types
qmlRegisterType<FGQmlPropertyNode>("flightgear.org", 1, 0, "Property"); qmlRegisterType<FGQmlPropertyNode>("FlightGear", 1, 0, "Property");
//qmlRegisterType<DialogStateController>("FlightGear", 1, 0, "DialogStateController");
} }
} }
QQuickDrawable::~QQuickDrawable() void QQuickDrawable::setup(osgViewer::GraphicsWindow *gw, osgViewer::Viewer *viewer)
{
delete d->qmlEngine;
delete d->renderControl;
delete d;
}
void QQuickDrawable::setup(osgViewer::GraphicsWindow* gw)
{ {
osg::GraphicsContext* gc = gw; osg::GraphicsContext* gc = gw;
osg::ref_ptr<flightgear::RetriveGraphicsThreadOperation> op(new flightgear::RetriveGraphicsThreadOperation);
// install operations on the graphics context to get the context
osg::ref_ptr<RetriveGraphicsThreadOperation> op(new RetriveGraphicsThreadOperation);
gc->add(op); gc->add(op);
gc->runOperations(); gc->runOperations();
// hopefully done now! // hopefully done now!
d->qtContext = op->context(); d->qtContext = op->context();
d->graphicsWindow = dynamic_cast<flightgear::GraphicsWindowQt5*>(gw);
if (!d->graphicsWindow) { // d->qtContext->setFormat(format);
qFatal("QQuick drawable requires a GraphicsWindowQt5 for now");
} d->foreignOSGWindow = flightgear::qtWindowFromOSG(gw);
d->renderControl = new CustomRenderControl(d->graphicsWindow->getGLWindow()); // d->foreignOSGWindow->setFormat(format);
d->foreignOSGWindow->setSurfaceType(QSurface::OpenGLSurface);
d->renderControl = new CustomRenderControl(d->foreignOSGWindow);
d->quickWindow = new QQuickWindow(d->renderControl); 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->quickWindow->setClearBeforeRendering(false);
d->qmlEngine = new QQmlEngine; 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()) if (!d->qmlEngine->incubationController())
d->qmlEngine->setIncubationController(d->quickWindow->incubationController()); d->qmlEngine->setIncubationController(d->quickWindow->incubationController());
d->renderControl->initialize(d->qtContext); d->renderControl->initialize(d->qtContext);
#if QT_VERSION < 0x050600 #if QT_VERSION < 0x050600
@ -278,42 +517,43 @@ void QQuickDrawable::setup(osgViewer::GraphicsWindow* gw)
d, &QQuickDrawablePrivate::onSceneChanged); d, &QQuickDrawablePrivate::onSceneChanged);
QObject::connect(d->renderControl, &QQuickRenderControl::renderRequested, QObject::connect(d->renderControl, &QQuickRenderControl::renderRequested,
d, &QQuickDrawablePrivate::onRenderRequested); d, &QQuickDrawablePrivate::onRenderRequested);
// insert ourselves as a filter on the GraphicsWindow, so we can intercept
// events directly and pass on to our window viewer->getEventHandlers().push_front(new QuickEventHandler(d));
d->graphicsWindow->getGLWindow()->installEventFilter(d);
} }
void QQuickDrawable::drawImplementation(osg::RenderInfo& renderInfo) const 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(); QOpenGLFunctions* glFuncs = d->qtContext->functions();
// prepare any state QQ2 needs // prepare any state QQ2 needs
d->quickWindow->resetOpenGLState(); d->quickWindow->resetOpenGLState();
// and reset these manually // and reset these manually
glFuncs->glPixelStorei(GL_PACK_ALIGNMENT, 4); glFuncs->glPixelStorei(GL_PACK_ALIGNMENT, 4);
glFuncs->glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glFuncs->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glFuncs->glPixelStorei(GL_PACK_ROW_LENGTH, 0); glFuncs->glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glFuncs->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glFuncs->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
d->renderControl->render(); 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) 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); d->qmlComponent = new QQmlComponent(d->qmlEngine, url);
if (d->qmlComponent->isLoading()) { if (d->qmlComponent->isLoading()) {
QObject::connect(d->qmlComponent, &QQmlComponent::statusChanged, QObject::connect(d->qmlComponent, &QQmlComponent::statusChanged,
@ -326,15 +566,16 @@ void QQuickDrawable::setSource(QUrl url)
void QQuickDrawable::resize(int width, int height) void QQuickDrawable::resize(int width, int height)
{ {
// we need to unscale from physical pixels back to logical, otherwise we end up double-scaled. // 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<float>(d->foreignOSGWindow->devicePixelRatio());
const int logicalWidth = static_cast<int>(width / currentPixelRatio); const int logicalWidth = static_cast<int>(width / currentPixelRatio);
const int logicalHeight = static_cast<int>(height / currentPixelRatio); const int logicalHeight = static_cast<int>(height / currentPixelRatio);
if (d->rootItem) { if (d->rootItem) {
d->rootItem->setWidth(logicalWidth); d->rootItem->setWidth(logicalWidth);
d->rootItem->setHeight(logicalHeight); d->rootItem->setHeight(logicalHeight);
} }
SG_LOG(SG_GUI, SG_INFO, "Resize:, lw=" << logicalWidth << ", lh=" << logicalHeight);
d->quickWindow->setGeometry(0, 0, logicalWidth, logicalHeight); d->quickWindow->setGeometry(0, 0, logicalWidth, logicalHeight);
} }

32
src/GUI/QQuickDrawable.hxx Normal file → Executable file
View file

@ -1,3 +1,21 @@
// QQuickDrawable.hxx - OSG Drawable using a QQuickRenderControl to draw
//
// Copyright (C) 2019 James Turner <james@flightgear.org>
//
// 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 #ifndef FG_VIEWER_QUICK_DRAWABLE_HXX
#define FG_VIEWER_QUICK_DRAWABLE_HXX #define FG_VIEWER_QUICK_DRAWABLE_HXX
@ -6,6 +24,10 @@
#include <osgViewer/GraphicsWindow> #include <osgViewer/GraphicsWindow>
namespace osgViewer {
class Viewer;
}
class QQuickDrawablePrivate; class QQuickDrawablePrivate;
class QQuickDrawable : public osg::Drawable class QQuickDrawable : public osg::Drawable
@ -19,16 +41,14 @@ public:
void drawImplementation(osg::RenderInfo& renderInfo) const override; void drawImplementation(osg::RenderInfo& renderInfo) const override;
/** Return true, FGPanelNode does support accept(PrimitiveFunctor&). */ void setup(osgViewer::GraphicsWindow* gw, osgViewer::Viewer* viewer);
// virtual bool supports(const osg::PrimitiveFunctor&) const { return true; }
//virtual void accept(osg::PrimitiveFunctor& functor) const;
void setup(osgViewer::GraphicsWindow* gw);
void setSource(QUrl url); void setSource(QUrl url);
void reload(QUrl url);
void resize(int width, int height); void resize(int width, int height);
private: private:
QQuickDrawablePrivate* d; QQuickDrawablePrivate* d;
}; };

29
src/GUI/QtHelpers.cxx Normal file
View file

@ -0,0 +1,29 @@
// QtHelpers.cxx - make Qt and SimGear/FlightGear place nicely
//
// Copyright (C) 2019 James Turner <james@flightgear.org>
//
// 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 <simgear/misc/sg_path.hxx>
#include "QtHelpers.hxx"
QDebug operator<<(QDebug debug, const SGPath& c)
{
debug << QString::fromStdString(c.utf8Str());
return debug;
}

28
src/GUI/QtHelpers.hxx Normal file
View file

@ -0,0 +1,28 @@
// QtHelpers.hxx - make Qt and SimGear/FlightGear place nicely
////
// Copyright (C) 2019 James Turner <james@flightgear.org>
//
// 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 <QDebug>
class SGPath;
QDebug operator<<(QDebug debug, const SGPath& c);
#endif // QTGUI_HELPERS_H

View file

@ -1,9 +0,0 @@
import QtQuick 2.4
import FlightGear.Launcher 1.0
RenderSettings
{
}

View file

@ -56,7 +56,6 @@
<file>qml/Style.qml</file> <file>qml/Style.qml</file>
<file>qml/qmldir</file> <file>qml/qmldir</file>
<file>qml/ClickableText.qml</file> <file>qml/ClickableText.qml</file>
<file>qml/LauncherSettings.qml</file>
<file>qml/Section.qml</file> <file>qml/Section.qml</file>
<file>qml/SettingControl.qml</file> <file>qml/SettingControl.qml</file>
<file>qml/SettingCheckbox.qml</file> <file>qml/SettingCheckbox.qml</file>

View file

@ -149,3 +149,12 @@ if (MSVC)
set_property(TARGET fgfs PROPERTY set_property(TARGET fgfs PROPERTY
VS_GLOBAL_LocalDebuggerEnvironment "PATH=${_install_bin_dir};${_msvc_3rdparty_bin_dir};${_qt5_bin_dir_native}") VS_GLOBAL_LocalDebuggerEnvironment "PATH=${_install_bin_dir};${_msvc_3rdparty_bin_dir};${_qt5_bin_dir_native}")
endif() 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()

View file

@ -25,8 +25,10 @@ set(HEADERS
) )
if (Qt5Core_FOUND) if (Qt5Core_FOUND)
list(APPEND HEADERS GraphicsWindowQt5.hxx) list(APPEND HEADERS GraphicsWindowQt5.hxx
list(APPEND SOURCES GraphicsWindowQt5.cpp) OSGQtAdaption.hxx)
list(APPEND SOURCES GraphicsWindowQt5.cpp
OSGQtAdaption.cxx)
endif() endif()
if (YES) if (YES)

View file

@ -788,6 +788,9 @@ public:
Qt5WindowingSystem() Qt5WindowingSystem()
{ {
#if OSG_VERSION_GREATER_THAN(3,5,9)
setName("FlightGearQt5");
#endif
} }
~Qt5WindowingSystem() ~Qt5WindowingSystem()
@ -899,6 +902,8 @@ void initQtWindowingSystem()
{ {
#if OSG_VERSION_LESS_THAN(3,5,2) #if OSG_VERSION_LESS_THAN(3,5,2)
osg::GraphicsContext::setWindowingSystemInterface(Qt5WindowingSystem::getInterface()); osg::GraphicsContext::setWindowingSystemInterface(Qt5WindowingSystem::getInterface());
#else
osg::GraphicsContext::getWindowingSystemInterfaces()->addWindowingSystemInterface(Qt5WindowingSystem::getInterface());
#endif #endif
} }

View file

@ -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 <memory>
#include <QOpenGLContext>
#include <QWindow>
#include <QThread>
#include <simgear/compiler.h>
#if defined(SG_MAC)
// defined in CocoaUtils.mm
#elif defined(SG_WINDOWS)
# include <QtPlatformHeaders/QWGLNativeContext>
# include <osgViewer/api/Win32/GraphicsWindowWin32>
#else
// assume X11 for now
# include <osgViewer/api/X11/GraphicsWindowX11>
# include <QtPlatformHeaders/QGLXNativeContext>
#endif
namespace flightgear
{
#if defined(SG_MAC)
// defined in CocoaUtils.mm
#elif defined(SG_WINDOWS)
QOpenGLContext* qtContextFromOSG(osg::GraphicsContext* context)
{
auto win = dynamic_cast<osgViewer::GraphicsWindowWin32*>(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<QOpenGLContext> qtOpenGLContext(new QOpenGLContext);
qtOpenGLContext->setNativeHandle(nativeHandle);
if (!qtOpenGLContext->create()) {
return nullptr;
}
return qtOpenGLContext.release();
}
QWindow* qtWindowFromOSG(osgViewer::GraphicsWindow* graphicsWindow)
{
auto win = dynamic_cast<osgViewer::GraphicsWindowWin32*>(graphicsWindow);
if (!win) {
return nullptr;
}
return QWindow::fromWinId(reinterpret_cast<qlonglong>(win->getHWND()));
}
#else
// assume X11 for now
QOpenGLContext* qtContextFromOSG(osg::GraphicsContext* context)
{
auto win = dynamic_cast<osgViewer::GraphicsWindowX11*>(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<QOpenGLContext> qtOpenGLContext(new QOpenGLContext);
qtOpenGLContext->setNativeHandle(nativeHandle);
if (!qtOpenGLContext->create()) {
return nullptr;
}
return qtOpenGLContext.release();
}
QWindow* qtWindowFromOSG(osgViewer::GraphicsWindow* graphicsWindow)
{
auto win = dynamic_cast<osgViewer::GraphicsWindowX11*>(graphicsWindow);
if (!win) {
return nullptr;
}
return QWindow::fromWinId(static_cast<qlonglong>(win->getWindow()));
}
#endif
void RetriveGraphicsThreadOperation::operator()(osg::GraphicsContext* context)
{
_result = QThread::currentThread();
_context = qtContextFromOSG(context);
}
} // of namespace

View file

@ -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 <osgViewer/GraphicsWindow>
#include <osg/GraphicsContext>
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;
};
}

View file

@ -29,9 +29,10 @@
#include <osg/BlendFunc> #include <osg/BlendFunc>
#include <osg/NodeVisitor> #include <osg/NodeVisitor>
#include <osgUtil/CullVisitor>
#include <osgGA/GUIEventHandler>
#include <osgGA/GUIEventAdapter> #include <osgGA/GUIEventAdapter>
#include <osgGA/GUIEventHandler>
#include <osgUtil/CullVisitor>
#include <osgViewer/Viewer>
#include <plib/pu.h> #include <plib/pu.h>
@ -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"); setName("PUI FBO camera");
@ -363,11 +364,6 @@ void PUICamera::init(osg::Group* parent)
fsQuadGeode->addDrawable(_fullScreenQuad); fsQuadGeode->addDrawable(_fullScreenQuad);
fsQuadGeode->setName("PUI fullscreen Geode"); fsQuadGeode->setName("PUI fullscreen Geode");
fsQuadGeode->setNodeMask(SG_NODEMASK_GUI_BIT); 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(this);
parent->addChild(fsQuadGeode); parent->addChild(fsQuadGeode);
@ -376,6 +372,11 @@ void PUICamera::init(osg::Group* parent)
osg::Viewport* vport = camera->getViewport(); osg::Viewport* vport = camera->getViewport();
resizeUi(vport->width(), vport->height()); 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 // remove once we require OSG 3.4
@ -400,12 +401,13 @@ void PUICamera::resizeUi(int width, int height)
resizeAttachments(scaledWidth, scaledHeight); resizeAttachments(scaledWidth, scaledHeight);
#endif #endif
const float puiZ = 1.0;
// resize the full-screen quad // resize the full-screen quad
osg::Vec3Array* fsQuadVertices = static_cast<osg::Vec3Array*>(_fullScreenQuad->getVertexArray()); osg::Vec3Array* fsQuadVertices = static_cast<osg::Vec3Array*>(_fullScreenQuad->getVertexArray());
(*fsQuadVertices)[0] = osg::Vec3(0.0, height, 0.0); (*fsQuadVertices)[0] = osg::Vec3(0.0, height, puiZ);
(*fsQuadVertices)[1] = osg::Vec3(0.0, 0.0, 0.0); (*fsQuadVertices)[1] = osg::Vec3(0.0, 0.0, puiZ);
(*fsQuadVertices)[2] = osg::Vec3(width, 0.0, 0.0); (*fsQuadVertices)[2] = osg::Vec3(width, 0.0, puiZ);
(*fsQuadVertices)[3] = osg::Vec3(width, height, 0.0); (*fsQuadVertices)[3] = osg::Vec3(width, height, puiZ);
fsQuadVertices->dirty(); fsQuadVertices->dirty();
} }

View file

@ -26,6 +26,10 @@ namespace osg
class Geometry; class Geometry;
} }
namespace osgViewer {
class Viewer;
}
namespace flightgear namespace flightgear
{ {
@ -41,7 +45,9 @@ public:
// osg::Camera already defines a resize() so use this name // osg::Camera already defines a resize() so use this name
void resizeUi(int width, int height); void resizeUi(int width, int height);
void init(osg::Group *parent);
void init(osg::Group* parent, osgViewer::Viewer* viewer);
private: private:
#if OSG_VERSION_LESS_THAN(3,4,0) #if OSG_VERSION_LESS_THAN(3,4,0)
class UpdateViewportAndFBOAfterTextureResizeCallback; class UpdateViewportAndFBOAfterTextureResizeCallback;

View file

@ -14,14 +14,12 @@
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H #include "config.h"
# include "config.h"
#endif
#include "WindowBuilder.hxx" #include "WindowBuilder.hxx"
#include "WindowSystemAdapter.hxx" #include "WindowSystemAdapter.hxx"
#include <Main/fg_props.hxx> #include <Main/fg_props.hxx>
#include <osg/Version>
#include <sstream> #include <sstream>
#include <limits> #include <limits>
@ -72,6 +70,14 @@ void WindowBuilder::makeDefaultTraits(bool stencil)
{ {
GraphicsContext::WindowingSystemInterface* wsi GraphicsContext::WindowingSystemInterface* wsi
= osg::GraphicsContext::getWindowingSystemInterface(); = 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; defaultTraits = new osg::GraphicsContext::Traits;
auto traits = defaultTraits.get(); auto traits = defaultTraits.get();
@ -107,7 +113,10 @@ void WindowBuilder::makeDefaultTraits(bool stencil)
auto data = new GraphicsWindowQt5::WindowData; auto data = new GraphicsWindowQt5::WindowData;
data->createFullscreen = wantFullscreen; data->createFullscreen = wantFullscreen;
data->isPrimaryWindow = true; data->isPrimaryWindow = true;
#if OSG_VERSION_GREATER_THAN(3, 5, 9)
traits->windowingSystemPreference = "FlightGearQt5";
#endif
traits->inheritedWindowData = data; traits->inheritedWindowData = data;
traits->windowDecoration = true; traits->windowDecoration = true;
traits->supportsResize = true; traits->supportsResize = true;

View file

@ -110,7 +110,6 @@
#if defined(HAVE_QT) #if defined(HAVE_QT)
#include <GUI/QQuickDrawable.hxx> #include <GUI/QQuickDrawable.hxx>
#include <Viewer/GraphicsWindowQt5.hxx>
#endif #endif
#if defined(HAVE_PUI) #if defined(HAVE_PUI)
@ -609,23 +608,22 @@ FGRenderer::setupView( void )
#if defined(HAVE_PUI) #if defined(HAVE_PUI)
_puiCamera = new flightgear::PUICamera; _puiCamera = new flightgear::PUICamera;
_puiCamera->init(guiCamera); _puiCamera->init(guiCamera, viewer);
#endif #endif
#if defined(HAVE_QT) #if defined(HAVE_QT)
std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path"); std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path");
auto graphicsWindowQt = dynamic_cast<GraphicsWindowQt5*>(guiCamera->getGraphicsContext()); auto graphicsWindow = dynamic_cast<osgViewer::GraphicsWindow*>(guiCamera->getGraphicsContext());
if (graphicsWindowQt && !rootQMLPath.empty()) { if (!rootQMLPath.empty()) {
_quickDrawable = new QQuickDrawable; _quickDrawable = new QQuickDrawable;
_quickDrawable->setup(graphicsWindowQt); _quickDrawable->setup(graphicsWindow, viewer);
_quickDrawable->setSource(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath))); _quickDrawable->setSource(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath)));
geode->addDrawable(_quickDrawable); geode->addDrawable(_quickDrawable);
} }
#endif #endif
// Draw first (eg. before Canvas GUI) guiCamera->addChild(geode);
guiCamera->insertChild(0, geode);
guiCamera->insertChild(0, FGPanelNode::create2DPanelNode()); guiCamera->insertChild(0, FGPanelNode::create2DPanelNode());
} }

View file

@ -110,7 +110,6 @@
#if defined(HAVE_QT) #if defined(HAVE_QT)
#include <GUI/QQuickDrawable.hxx> #include <GUI/QQuickDrawable.hxx>
#include <Viewer/GraphicsWindowQt5.hxx>
#endif #endif
#if defined(HAVE_PUI) #if defined(HAVE_PUI)
@ -1504,23 +1503,22 @@ FGRenderer::setupView( void )
#if defined(HAVE_PUI) #if defined(HAVE_PUI)
_puiCamera = new flightgear::PUICamera; _puiCamera = new flightgear::PUICamera;
_puiCamera->init(guiCamera); _puiCamera->init(guiCamera, viewer);
#endif #endif
#if defined(HAVE_QT) #if defined(HAVE_QT)
std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path"); std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path");
auto graphicsWindowQt = dynamic_cast<GraphicsWindowQt5*>(guiCamera->getGraphicsContext()); auto graphicsWindow = dynamic_cast<osgViewer::GraphicsWindow*>(guiCamera->getGraphicsContext());
if (graphicsWindowQt && !rootQMLPath.empty()) { if (!rootQMLPath.empty()) {
_quickDrawable = new QQuickDrawable; _quickDrawable = new QQuickDrawable;
_quickDrawable->setup(graphicsWindowQt); _quickDrawable->setup(graphicsWindow, viewer);
_quickDrawable->setSource(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath))); _quickDrawable->setSource(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath)));
geode->addDrawable(_quickDrawable); geode->addDrawable(_quickDrawable);
} }
#endif #endif
// Draw first (eg. before Canvas GUI) guiCamera->addChild(geode);
guiCamera->insertChild(0, geode);
guiCamera->insertChild(0, FGPanelNode::create2DPanelNode()); guiCamera->insertChild(0, FGPanelNode::create2DPanelNode());
} }