diff options
author | Robert Griebl <robert.griebl@pelagicore.com> | 2017-12-06 12:43:08 +0100 |
---|---|---|
committer | Dominik Holland <dominik.holland@pelagicore.com> | 2017-12-15 15:47:53 +0000 |
commit | 5fad90789b756ad8d6ec88db3ef71d354d0c7323 (patch) | |
tree | f894eb59441e35329a5985b88db584fa4f7f6d6c /src | |
parent | c77ac0226c7f323bdb93fe214bc335a5d1e386cd (diff) |
Refactor the interface between the AM and runtime launchers
* A lot of shared code was moved to the new shared-main library, making it
easier to write custom runtime launchers
* Simplified the qml runtime launcher
* The new OpenGL versioning was extended to apps using the QML runtime
* Settings are communicated to the apps as plain text YAML documents now,
which makes it very easy to verify and extended this interface.
Task-number: AUTOSUITE-162
Change-Id: I0c1be3ac2e38ab492e69182058098cf31d34484b
Reviewed-by: Dominik Holland <dominik.holland@pelagicore.com>
Diffstat (limited to 'src')
33 files changed, 880 insertions, 435 deletions
diff --git a/src/application-lib/application.cpp b/src/application-lib/application.cpp index 646f6cf5..8c5834d2 100644 --- a/src/application-lib/application.cpp +++ b/src/application-lib/application.cpp @@ -512,6 +512,11 @@ QString Application::version() const return m_nonAliased ? m_nonAliased->m_version : m_version; } +QVariantMap Application::openGLConfiguration() const +{ + return m_nonAliased ? m_nonAliased->m_openGLConfiguration : m_openGLConfiguration; +} + void Application::validate() const Q_DECL_NOEXCEPT_EXPR(false) { if (isAlias()) { @@ -564,6 +569,7 @@ void Application::mergeInto(Application *app) const app->m_mimeTypes = m_mimeTypes; app->m_backgroundMode = m_backgroundMode; app->m_version = m_version; + app->m_openGLConfiguration = m_openGLConfiguration; emit app->bulkChange(); } @@ -689,6 +695,7 @@ Application *Application::readFromDataStream(QDataStream &ds, const QVector<cons >> app->m_mimeTypes >> backgroundMode >> app->m_version + >> app->m_openGLConfiguration >> codeDir >> manifestDir >> app->m_uid @@ -759,6 +766,7 @@ void Application::writeToDataStream(QDataStream &ds, const QVector<const Applica << m_mimeTypes << qint32(m_backgroundMode) << m_version + << m_openGLConfiguration << m_codeDir.absolutePath() << m_manifestDir.absolutePath() << m_uid diff --git a/src/application-lib/application.h b/src/application-lib/application.h index cfc09ac8..ff436952 100644 --- a/src/application-lib/application.h +++ b/src/application-lib/application.h @@ -131,6 +131,8 @@ public: QString version() const; + QVariantMap openGLConfiguration() const; + void validate() const Q_DECL_NOEXCEPT_EXPR(false); QVariantMap toVariantMap() const; static Application *fromVariantMap(const QVariantMap &map, QString *error = 0); @@ -208,6 +210,8 @@ private: QString m_version; + QVariantMap m_openGLConfiguration; + // added by installer QScopedPointer<InstallationReport> m_installationReport; QDir m_manifestDir; diff --git a/src/application-lib/yamlapplicationscanner.cpp b/src/application-lib/yamlapplicationscanner.cpp index 11f26225..89d551d1 100644 --- a/src/application-lib/yamlapplicationscanner.cpp +++ b/src/application-lib/yamlapplicationscanner.cpp @@ -182,12 +182,12 @@ Application *YamlApplicationScanner::scanInternal(const QString &filePath, bool { "audio", Application::PlaysAudio }, { "location", Application::TracksLocation }, { "auto", Application::Auto }, - { 0, Application::Auto } + { nullptr, Application::Auto } }; QByteArray enumValue = v.toString().toLatin1(); bool found = false; - for (auto it = backgroundMap; backgroundMap->first; ++it) { + for (auto it = backgroundMap; it->first; ++it) { if (enumValue == it->first) { app->m_backgroundMode = it->second; found = true; @@ -196,6 +196,19 @@ Application *YamlApplicationScanner::scanInternal(const QString &filePath, bool } if (!found) throw Exception(Error::Parse, "the 'backgroundMode' value '%1' is not valid").arg(enumValue); + } else if (field == "opengl") { + app->m_openGLConfiguration = v.toMap(); + + // sanity check + static QStringList validKeys = { + qSL("desktopProfile"), + qSL("esMajorVersion"), + qSL("esMinorVersion") + }; + for (auto it = app->m_openGLConfiguration.cbegin(); it != app->m_openGLConfiguration.cend(); ++it) { + if (!validKeys.contains(it.key())) + throw Exception(Error::Parse, "the 'opengl' object contains the unsupported key '%1'").arg(it.key()); + } } else { unknownField = true; } diff --git a/src/common-lib/logging.cpp b/src/common-lib/logging.cpp index f4c7ecca..42af5482 100644 --- a/src/common-lib/logging.cpp +++ b/src/common-lib/logging.cpp @@ -105,6 +105,7 @@ bool Logging::s_dltEnabled = false; #endif bool Logging::s_useDefaultQtHandler = false; +QStringList Logging::s_rules; QtMessageHandler Logging::s_defaultQtHandler = nullptr; QByteArray Logging::s_applicationId = QByteArray(); @@ -303,6 +304,17 @@ void Logging::initialize() s_defaultQtHandler = qInstallMessageHandler(messageHandler); } +QStringList Logging::filterRules() +{ + return s_rules; +} + +void Logging::setFilterRules(const QStringList &rules) +{ + s_rules = rules; + QLoggingCategory::setFilterRules(rules.join(qL1C('\n'))); +} + QByteArray Logging::applicationId() { return s_applicationId; diff --git a/src/common-lib/logging.h b/src/common-lib/logging.h index 0f3b81a2..478cd234 100644 --- a/src/common-lib/logging.h +++ b/src/common-lib/logging.h @@ -58,6 +58,8 @@ class Logging { public: static void initialize(); + static QStringList filterRules(); + static void setFilterRules(const QStringList &rules); static QByteArray applicationId(); static void setApplicationId(const QByteArray &appId); @@ -72,6 +74,7 @@ public: private: static bool s_dltEnabled; static bool s_useDefaultQtHandler; + static QStringList s_rules; static QtMessageHandler s_defaultQtHandler; static QByteArray s_applicationId; }; diff --git a/src/common-lib/qml-utilities.cpp b/src/common-lib/qml-utilities.cpp index 5b5af432..53f3a069 100644 --- a/src/common-lib/qml-utilities.cpp +++ b/src/common-lib/qml-utilities.cpp @@ -40,9 +40,13 @@ ****************************************************************************/ #include <QTimer> +#include <QDir> +#include <QQmlComponent> +#include <QQmlContext> #include <private/qqmlmetatype_p.h> #include <private/qvariant_p.h> +#include "logging.h" #include "qml-utilities.h" QT_BEGIN_NAMESPACE_AM @@ -120,4 +124,28 @@ void retakeSingletonOwnershipFromQmlEngine(QQmlEngine *qmlEngine, QObject *singl QTimer::singleShot(0, qmlEngine, retake); } +// copied straight from Qt 5.1.0 qmlscene/main.cpp for now - needs to be revised +void loadQmlDummyDataFiles(QQmlEngine *engine, const QString &directory) +{ + QDir dir(directory + qSL("/dummydata"), qSL("*.qml")); + QStringList list = dir.entryList(); + for (int i = 0; i < list.size(); ++i) { + QString qml = list.at(i); + QQmlComponent comp(engine, dir.filePath(qml)); + QObject *dummyData = comp.create(); + + if (comp.isError()) { + const QList<QQmlError> errors = comp.errors(); + for (const QQmlError &error : errors) + qCWarning(LogQml) << "Loading dummy data:" << error; + } + + if (dummyData) { + qml.truncate(qml.length() - 4); + engine->rootContext()->setContextProperty(qml, dummyData); + dummyData->setParent(engine); + } + } +} + QT_END_NAMESPACE_AM diff --git a/src/common-lib/qml-utilities.h b/src/common-lib/qml-utilities.h index 458a2da1..c657405e 100644 --- a/src/common-lib/qml-utilities.h +++ b/src/common-lib/qml-utilities.h @@ -53,4 +53,6 @@ void fixNullValuesForQml(QVariantMap &map); void retakeSingletonOwnershipFromQmlEngine(QQmlEngine *qmlEngine, QObject *singleton, bool immediately = false); +void loadQmlDummyDataFiles(QQmlEngine *engine, const QString &directory); + QT_END_NAMESPACE_AM diff --git a/src/launcher-lib/launcher-lib.pro b/src/launcher-lib/launcher-lib.pro index f5275cd6..fba6deac 100644 --- a/src/launcher-lib/launcher-lib.pro +++ b/src/launcher-lib/launcher-lib.pro @@ -8,6 +8,7 @@ QT = qml dbus core-private !headless:QT += quick gui gui-private quick-private QT_FOR_PRIVATE *= \ appman_common-private \ + appman_shared_main-private \ appman_application-private \ appman_notification-private \ @@ -17,7 +18,8 @@ SOURCES += \ qmlapplicationinterface.cpp \ ipcwrapperobject.cpp \ qmlapplicationinterfaceextension.cpp \ - qmlnotification.cpp + qmlnotification.cpp \ + launchermain.cpp !headless:SOURCES += \ applicationmanagerwindow.cpp \ @@ -27,7 +29,8 @@ HEADERS += \ ipcwrapperobject.h \ ipcwrapperobject_p.h \ qmlapplicationinterfaceextension.h \ - qmlnotification.h + qmlnotification.h \ + launchermain.h !headless:HEADERS += \ applicationmanagerwindow_p.h diff --git a/src/launcher-lib/launchermain.cpp b/src/launcher-lib/launchermain.cpp new file mode 100644 index 00000000..8eb54002 --- /dev/null +++ b/src/launcher-lib/launchermain.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#include <QDBusConnection> + +#include <QtAppManCommon/exception.h> +#include <QtAppManCommon/logging.h> +#include <QtAppManCommon/utilities.h> +#include <QtAppManCommon/crashhandler.h> +#include <QtAppManCommon/exception.h> + +#include <QtAppManCommon/qtyaml.h> + +#include "launchermain.h" + +QT_BEGIN_NAMESPACE_AM + +LauncherMain::LauncherMain(int &argc, char **argv, const QByteArray &configYaml) Q_DECL_NOEXCEPT_EXPR(false) + : LauncherMainBase(SharedMain::preConstructor(argc), argv) + , SharedMain() +{ + auto docs = QtYaml::variantDocumentsFromYaml(configYaml.isEmpty() ? qgetenv("AM_CONFIG") + : configYaml); + if (docs.size() == 1) + m_configuration = docs.first().toMap(); + + m_baseDir = m_configuration.value(qSL("baseDir")).toString() + qL1C('/'); + m_runtimeConfiguration = m_configuration.value(qSL("runtimeConfiguration")).toMap(); + m_securityToken = QByteArray::fromHex(m_configuration.value(qSL("")).toString().toLatin1()); + m_systemProperties = m_configuration.value(qSL("systemProperties")).toMap(); + + QVariantMap loggingConfig = m_configuration.value(qSL("logging")).toMap(); + m_loggingRules = variantToStringList(loggingConfig.value(qSL("rules"))); + + QVariantMap dbusConfig = m_configuration.value(qSL("dbus")).toMap(); + m_dbusAddressP2P = dbusConfig.value(qSL("p2p")).toString(); + m_dbusAddressNotifications = dbusConfig.value(qSL("org.freedesktop.Notifications")).toString(); + + QVariantMap uiConfig = m_configuration.value(qSL("ui")).toMap(); + m_slowAnimations = uiConfig.value(qSL("slowAnimations")).toBool(); + m_openGLConfiguration = uiConfig.value(qSL("opengl")).toMap(); + + // un-comment this if things go south: + //qWarning() << "### LOG " << m_loggingRules; + //qWarning() << "### DBUS" << dbusConfig; + //qWarning() << "### UI " << uiConfig; + //qWarning() << "### RT " << m_runtimeConfiguration; + //qWarning() << "### SYSP" << m_systemProperties; + //qWarning() << "### GL " << m_openGLConfiguration; + + // sanity checks + if (m_baseDir == qL1C('/')) + throw Exception("Runtime launcher received an empty baseDir"); + if (loggingConfig.isEmpty()) + throw Exception("Runtime launcher received no logging configuration"); + if (dbusConfig.isEmpty()) + throw Exception("Runtime launcher received no D-Bus configuration"); +} + +LauncherMain::~LauncherMain() +{ } + +QString LauncherMain::baseDir() const +{ + return m_baseDir; +} + +QVariantMap LauncherMain::runtimeConfiguration() const +{ + return m_runtimeConfiguration; +} + +QByteArray LauncherMain::securityToken() const +{ + return m_securityToken; +} + +bool LauncherMain::slowAnimations() const +{ + return m_slowAnimations; +} + +QVariantMap LauncherMain::systemProperties() const +{ + return m_systemProperties; +} + +QStringList LauncherMain::loggingRules() const +{ + return m_loggingRules; +} + +QString LauncherMain::p2pDBusName() const +{ + return qSL("am"); +} + +QString LauncherMain::notificationDBusName() const +{ + return qSL("am_notification_bus"); +} + +QVariantMap LauncherMain::openGLConfiguration() const +{ + return m_openGLConfiguration; +} + +void LauncherMain::setupDBusConnections() Q_DECL_NOEXCEPT_EXPR(false) +{ + if (m_dbusAddressP2P.isEmpty()) + throw Exception("No address available to connect to the P2P D-Bus"); + + auto dbusConnection = QDBusConnection::connectToPeer(m_dbusAddressP2P, p2pDBusName()); + + if (!dbusConnection.isConnected()) + throw Exception("could not connect to the P2P D-Bus via: %1").arg(m_dbusAddressP2P); + + qCDebug(LogQmlRuntime) << "Connected to the P2P D-Bus via:" << m_dbusAddressP2P; + + if (m_dbusAddressNotifications.isEmpty()) + m_dbusAddressNotifications = qSL("session"); + + if (m_dbusAddressNotifications == qL1S("system")) + dbusConnection = QDBusConnection::connectToBus(QDBusConnection::SystemBus, notificationDBusName()); + else if (m_dbusAddressNotifications == qL1S("session")) + dbusConnection = QDBusConnection::connectToBus(QDBusConnection::SessionBus, notificationDBusName()); + else + dbusConnection = QDBusConnection::connectToBus(m_dbusAddressNotifications, notificationDBusName()); + + if (!dbusConnection.isConnected()) + throw Exception("could not connect to the Notification D-Bus via: %1").arg(m_dbusAddressNotifications); + + qCDebug(LogQmlRuntime) << "Connected to the Notification D-Bus via:" << m_dbusAddressNotifications; +} + +QT_END_NAMESPACE_AM diff --git a/src/launcher-lib/launchermain.h b/src/launcher-lib/launchermain.h new file mode 100644 index 00000000..69684dab --- /dev/null +++ b/src/launcher-lib/launchermain.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#pragma once + +#include <QtAppManCommon/global.h> +#include <QtAppManSharedMain/sharedmain.h> + +#if defined(AM_HEADLESS) +# include <QCoreApplication> +typedef QCoreApplication LauncherMainBase; +#else +# include <QGuiApplication> +typedef QGuiApplication LauncherMainBase; +#endif + + +QT_BEGIN_NAMESPACE_AM + +class LauncherMain : public LauncherMainBase, public SharedMain +{ + Q_OBJECT +public: + LauncherMain(int &argc, char **argv, const QByteArray &configYaml = QByteArray()) Q_DECL_NOEXCEPT_EXPR(false); + ~LauncherMain(); + +public: + void setupDBusConnections() Q_DECL_NOEXCEPT_EXPR(false); + + QString baseDir() const; + QVariantMap runtimeConfiguration() const; + QByteArray securityToken() const; + bool slowAnimations() const; + QVariantMap systemProperties() const; + QStringList loggingRules() const; + + QString p2pDBusName() const; + QString notificationDBusName() const; + + QVariantMap openGLConfiguration() const; + +private: + QVariantMap m_configuration; + QString m_baseDir; + QVariantMap m_runtimeConfiguration; + QByteArray m_securityToken; + bool m_slowAnimations = false; + QVariantMap m_systemProperties; + QStringList m_loggingRules; + QString m_dbusAddressP2P; + QString m_dbusAddressNotifications; + QVariantMap m_openGLConfiguration; +}; + +QT_END_NAMESPACE_AM diff --git a/src/launchers/qml/main.cpp b/src/launchers/qml/main.cpp index c996436d..d168db8c 100644 --- a/src/launchers/qml/main.cpp +++ b/src/launchers/qml/main.cpp @@ -64,6 +64,8 @@ #include <private/qabstractanimation_p.h> // For QUnifiedTimer #include <qplatformdefs.h> +#include <QtAppManLauncher/launchermain.h> + #if !defined(AM_HEADLESS) # include <QGuiApplication> # include <QQuickItem> @@ -91,6 +93,7 @@ #include "dbus-utilities.h" #include "startuptimer.h" #include "processtitle.h" +#include "qml-utilities.h" QT_BEGIN_NAMESPACE_AM @@ -113,36 +116,12 @@ protected: -// copied straight from Qt 5.1.0 qmlscene/main.cpp for now - needs to be revised -static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory) -{ - QDir dir(directory + qSL("/dummydata"), qSL("*.qml")); - QStringList list = dir.entryList(); - for (int i = 0; i < list.size(); ++i) { - QString qml = list.at(i); - QQmlComponent comp(&engine, dir.filePath(qml)); - QObject *dummyData = comp.create(); - - if (comp.isError()) { - const QList<QQmlError> errors = comp.errors(); - for (const QQmlError &error : errors) - qWarning() << error; - } - - if (dummyData) { - qml.truncate(qml.length()-4); - engine.rootContext()->setContextProperty(qml, dummyData); - dummyData->setParent(&engine); - } - } -} - class Controller : public QObject { Q_OBJECT public: - Controller(QCoreApplication *a, bool quickLaunched, const QString &directLoad = QString()); + Controller(LauncherMain *a, bool quickLaunched, const QString &directLoad = QString()); public slots: void startApplication(const QString &baseDir, const QString &qmlFile, const QString &document, @@ -161,13 +140,6 @@ private slots: #endif }; -static QString p2pBusName = qSL("am"); -static QString notificationBusName = qSL("am_notification_bus"); -static QCommandLineParser cp; -static QCommandLineOption qmlDebugOption(qSL("qml-debug"), qSL("Enables QML debugging and profiling.")); -static QCommandLineOption quickLaunchOption(qSL("quicklaunch"), qSL("Starts the launcher in the quicklaunching mode.")); -static QCommandLineOption directLoadOption(qSL("directload") , qSL("The info.yaml to start."), qSL("info.yaml")); - QT_END_NAMESPACE_AM @@ -178,6 +150,11 @@ int main(int argc, char *argv[]) { StartupTimer::instance()->checkpoint("entered main"); + QCoreApplication::setApplicationName(qSL("ApplicationManager QML Launcher")); + QCoreApplication::setOrganizationName(qSL("Pelagicore AG")); + QCoreApplication::setOrganizationDomain(qSL("pelagicore.com")); + QCoreApplication::setApplicationVersion(qSL(AM_VERSION)); + if (qEnvironmentVariableIsSet("AM_NO_DLT_LOGGING")) Logging::setDltEnabled(false); @@ -191,114 +168,68 @@ int main(int argc, char *argv[]) Logging::setApplicationId("qml-launcher"); Logging::initialize(); - QLoggingCategory::setFilterRules(QString::fromUtf8(qgetenv("AM_LOGGING_RULES"))); + try { + LauncherMain a(argc, argv); -#if defined(AM_HEADLESS) - QCoreApplication a(argc, argv); -#else - // this is needed for WebEngine - QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); - - QGuiApplication a(argc, argv); - a.setApplicationDisplayName(qL1S("appman-launcher-qml")); - cp.addHelpOption(); - cp.addOption(directLoadOption); - cp.addOption(qmlDebugOption); - cp.addOption(quickLaunchOption); - - cp.process(a); - - if (!static_cast<QCoreApplicationPrivate *>(QObjectPrivate::get(&a))->qmljsDebugArgumentsString().isEmpty() - || cp.isSet(qmlDebugOption)) { -#if !defined(QT_NO_QML_DEBUGGER) - (void) new QQmlDebuggingEnabler(true); - if (!QLoggingCategory::defaultCategory()->isDebugEnabled()) { - qCCritical(LogQmlRuntime) << "The default 'debug' logging category was disabled. " - "Re-enabling it for the QML Debugger interface to work correctly."; - QLoggingCategory::defaultCategory()->setEnabled(QtDebugMsg, true); - } -#else - qCWarning(LogQmlRuntime) << "The --qml-debug/-qmljsdebugger options are ignored, because Qt was built without support for QML Debugging!"; -#endif - } - - qmlRegisterType<ApplicationManagerWindow>("QtApplicationManager", 1, 0, "ApplicationManagerWindow"); -#endif - - qmlRegisterType<QmlNotification>("QtApplicationManager", 1, 0, "Notification"); - qmlRegisterType<QmlApplicationInterfaceExtension>("QtApplicationManager", 1, 0, "ApplicationInterfaceExtension"); + QCommandLineParser clp; + clp.addHelpOption(); + clp.addOption({ qSL("qml-debug"), qSL("Enables QML debugging and profiling.") }); + clp.addOption({ qSL("quicklaunch"), qSL("Starts the launcher in the quicklaunching mode.") }); + clp.addOption({ qSL("directload") , qSL("The info.yaml to start."), qSL("info.yaml") }); + clp.process(a); - StartupTimer::instance()->checkpoint("after logging and qml register initialization"); + CrashHandler::setCrashActionConfiguration(a.runtimeConfiguration().value(qSL("crashAction")).toMap()); + a.setupLoggingRules(false, a.loggingRules()); // the verbose flag has already been factored into the rules + bool hasJSDebugArg = !static_cast<QCoreApplicationPrivate *>(QObjectPrivate::get(&a))->qmljsDebugArgumentsString().isEmpty(); + a.setupQmlDebugging(hasJSDebugArg || clp.isSet(qSL("qml-debug"))); + a.setupOpenGL(a.openGLConfiguration()); - if (cp.isSet(directLoadOption)) { - QFileInfo fi = cp.value(directLoadOption); + StartupTimer::instance()->checkpoint("after basic initialization"); - if (!fi.exists() || fi.fileName() != qSL("info.yaml")) { - qCCritical(LogQmlRuntime) << "ERROR: --directload needs a valid info.yaml file as parameter"; - return 2; - } - - new Controller(&a, cp.isSet(quickLaunchOption), fi.absoluteFilePath()); - } else { - QByteArray dbusAddress = qgetenv("AM_DBUS_PEER_ADDRESS"); - if (dbusAddress.isEmpty()) { - qCCritical(LogQmlRuntime) << "ERROR: $AM_DBUS_PEER_ADDRESS is empty"; - return 2; - } - QDBusConnection dbusConnection = QDBusConnection::connectToPeer(QString::fromUtf8(dbusAddress), p2pBusName); - - if (!dbusConnection.isConnected()) { - qCCritical(LogQmlRuntime) << "ERROR: could not connect to the P2P D-Bus via:" << dbusAddress; - return 3; - } - qCDebug(LogQmlRuntime) << "Connected to the P2P D-Bus via:" << dbusAddress; - - dbusAddress = qgetenv("AM_DBUS_NOTIFICATION_BUS_ADDRESS"); - if (dbusAddress.isEmpty()) - dbusAddress = "session"; - - if (dbusAddress == "system") - dbusConnection = QDBusConnection::connectToBus(QDBusConnection::SystemBus, notificationBusName); - else if (dbusAddress == "session") - dbusConnection = QDBusConnection::connectToBus(QDBusConnection::SessionBus, notificationBusName); - else - dbusConnection = QDBusConnection::connectToBus(QString::fromUtf8(dbusAddress), notificationBusName); - - if (!dbusConnection.isConnected()) { - qCCritical(LogQmlRuntime) << "ERROR: could not connect to the Notification D-Bus via:" << dbusAddress; - return 3; + bool quicklaunched = clp.isSet(qSL("quicklaunch")); + QString directLoad = clp.value(qSL("directload")); + if (!directLoad.isEmpty()) { + QFileInfo fi(directLoad); + if (!fi.exists() || fi.fileName() != qSL("info.yaml")) { + throw Exception("--directload needs a valid info.yaml file as parameter"); + return 2; + } + directLoad = fi.absoluteFilePath(); + } else { + a.setupDBusConnections(); + StartupTimer::instance()->checkpoint("after dbus initialization"); } - qCDebug(LogQmlRuntime) << "Connected to the Notification D-Bus via:" << dbusAddress; - StartupTimer::instance()->checkpoint("after dbus initialization"); + new Controller(&a, quicklaunched, directLoad); + return a.exec(); - new Controller(&a, cp.isSet(quickLaunchOption)); + } catch (const std::exception &e) { + qCCritical(LogQmlRuntime) << "ERROR:" << e.what(); + return 2; } - - return a.exec(); } -Controller::Controller(QCoreApplication *a, bool quickLaunched, const QString &directLoad) +Controller::Controller(LauncherMain *a, bool quickLaunched, const QString &directLoad) : QObject(a) , m_quickLaunched(quickLaunched) { - connect(&m_engine, &QObject::destroyed, &QCoreApplication::quit); - connect(&m_engine, &QQmlEngine::quit, &QCoreApplication::quit); + connect(&m_engine, &QObject::destroyed, a, &QCoreApplication::quit); + connect(&m_engine, &QQmlEngine::quit, a, &QCoreApplication::quit); - auto docs = QtYaml::variantDocumentsFromYaml(qgetenv("AM_RUNTIME_CONFIGURATION")); - if (docs.size() == 1) - m_configuration = docs.first().toMap(); - - CrashHandler::setCrashActionConfiguration(m_configuration.value(qSL("crashAction")).toMap()); +#if !defined(AM_HEADLESS) + qmlRegisterType<ApplicationManagerWindow>("QtApplicationManager", 1, 0, "ApplicationManagerWindow"); +#endif + qmlRegisterType<QmlNotification>("QtApplicationManager", 1, 0, "Notification"); + qmlRegisterType<QmlApplicationInterfaceExtension>("QtApplicationManager", 1, 0, "ApplicationInterfaceExtension"); - const QString baseDir = QString::fromLocal8Bit(qgetenv("AM_BASE_DIR") + "/"); + m_configuration = a->runtimeConfiguration(); QStringList importPaths = variantToStringList(m_configuration.value(qSL("importPaths"))); for (QString &path : importPaths) { if (QFileInfo(path).isRelative()) { - path.prepend(baseDir); + path.prepend(a->baseDir()); } else { - qCWarning(LogQmlRuntime) << "Absolute import path in config file can lead to problems inside containers:" + qCWarning(LogQmlRuntime) << "Absolute import paths in the runtime configuration can lead to problems inside containers:" << path; } m_engine.addImportPath(path); @@ -321,9 +252,9 @@ Controller::Controller(QCoreApplication *a, bool quickLaunched, const QString &d StartupTimer::instance()->checkpoint("after window registration"); QString quicklaunchQml = m_configuration.value((qSL("quicklaunchQml"))).toString(); - if (!quicklaunchQml.isEmpty() && cp.isSet(quickLaunchOption)) { + if (!quicklaunchQml.isEmpty() && quickLaunched) { if (QFileInfo(quicklaunchQml).isRelative()) - quicklaunchQml.prepend(baseDir); + quicklaunchQml.prepend(a->baseDir()); QQmlComponent quicklaunchComp(&m_engine, quicklaunchQml); if (!quicklaunchComp.isError()) { @@ -337,13 +268,11 @@ Controller::Controller(QCoreApplication *a, bool quickLaunched, const QString &d } if (directLoad.isEmpty()) { - m_applicationInterface = new QmlApplicationInterface(p2pBusName, notificationBusName, this); + m_applicationInterface = new QmlApplicationInterface(a->p2pDBusName(), a->notificationDBusName(), this); connect(m_applicationInterface, &QmlApplicationInterface::startApplication, this, &Controller::startApplication); - if (!m_applicationInterface->initialize()) { - qCritical("ERROR: could not connect to the application manager's interface on the peer D-Bus"); - qApp->exit(4); - } + if (!m_applicationInterface->initialize()) + throw Exception("Could not connect to the application manager's interface on the peer D-Bus"); } else { QTimer::singleShot(0, [this, directLoad]() { QFileInfo fi(directLoad); @@ -352,8 +281,7 @@ Controller::Controller(QCoreApplication *a, bool quickLaunched, const QString &d const Application *a = yas.scan(directLoad); startApplication(fi.absolutePath(), a->codeFilePath(), QString(), QString(), a->toVariantMap(), QVariantMap()); } catch (const Exception &e) { - qCritical("ERROR: could not parse info.yaml file: %s", e.what()); - qApp->exit(5); + throw Exception("Could not parse info.yaml file: %1").arg(e.what()); } }); } @@ -456,7 +384,7 @@ void Controller::startApplication(const QString &baseDir, const QString &qmlFile if (loadDummyData) { qCDebug(LogQmlRuntime) << "loading dummy-data"; - loadDummyDataFiles(m_engine, QFileInfo(qmlFile).path()); + loadQmlDummyDataFiles(&m_engine, QFileInfo(qmlFile).path()); } QVariant imports = runtimeParameters.value(qSL("importPaths")); diff --git a/src/main-lib/defaultconfiguration.cpp b/src/main-lib/defaultconfiguration.cpp index 8384c095..91a5278d 100644 --- a/src/main-lib/defaultconfiguration.cpp +++ b/src/main-lib/defaultconfiguration.cpp @@ -258,21 +258,9 @@ bool DefaultConfiguration::enableTouchEmulation() const return value<bool>("enable-touch-emulation", { "ui", "enableTouchEmulation" }); } -QString DefaultConfiguration::openGLESProfile() const +QVariantMap DefaultConfiguration::openGLConfiguration() const { - return value<QString>(nullptr, { "ui", "opengl", "desktopProfile" }); -} - -int DefaultConfiguration::openGLESVersionMajor() const -{ - auto v = value<QVariant>(nullptr, { "ui", "opengl", "esMajorVersion" }); - return v.isValid() ? v.toInt() : -1; -} - -int DefaultConfiguration::openGLESVersionMinor() const -{ - auto v = value<QVariant>(nullptr, { "ui", "opengl", "esMinorVersion" }); - return v.isValid() ? v.toInt() : -1; + return value<QVariant>(nullptr, { "ui", "opengl" }).toMap(); } QVariantList DefaultConfiguration::installationLocations() const diff --git a/src/main-lib/defaultconfiguration.h b/src/main-lib/defaultconfiguration.h index 405204c0..d7c4b1e7 100644 --- a/src/main-lib/defaultconfiguration.h +++ b/src/main-lib/defaultconfiguration.h @@ -85,9 +85,7 @@ public: QString style() const; bool enableTouchEmulation() const; - QString openGLESProfile() const; - int openGLESVersionMajor() const; - int openGLESVersionMinor() const; + QVariantMap openGLConfiguration() const; QVariantList installationLocations() const; diff --git a/src/main-lib/main-lib.pro b/src/main-lib/main-lib.pro index 9006e6ed..0211e242 100644 --- a/src/main-lib/main-lib.pro +++ b/src/main-lib/main-lib.pro @@ -6,7 +6,7 @@ load(am-config) QT = core network qml core-private enable-widgets:QT *= widgets -!headless:QT *= gui gui-private quick +!headless:QT *= gui quick qtHaveModule(pssdp):QT *= pssdp qtHaveModule(pshellserver):QT *= pshellserver QT *= \ @@ -17,6 +17,7 @@ QT *= \ appman_installer-private \ appman_notification-private \ appman_monitor-private \ + appman_shared_main-private \ !headless:QT *= appman_window-private !disable-external-dbus-interfaces:qtHaveModule(dbus):QT *= dbus appman_dbus-private @@ -28,14 +29,12 @@ win32:LIBS += -luser32 DEFINES += AM_BUILD_DIR=\\\"$$BUILD_DIR\\\" HEADERS += \ - $$PWD/qmllogger.h \ $$PWD/configuration.h \ $$PWD/main.h \ $$PWD/defaultconfiguration.h SOURCES += \ $$PWD/main.cpp \ - $$PWD/qmllogger.cpp \ $$PWD/configuration.cpp \ $$PWD/defaultconfiguration.cpp diff --git a/src/main-lib/main.cpp b/src/main-lib/main.cpp index 998ec8ad..22cbf951 100644 --- a/src/main-lib/main.cpp +++ b/src/main-lib/main.cpp @@ -138,44 +138,11 @@ QT_BEGIN_NAMESPACE_AM -#if !defined(AM_HEADLESS) -static QMap<int, QString> openGLProfileNames = { - { QSurfaceFormat::NoProfile, qSL("default") }, - { QSurfaceFormat::CoreProfile, qSL("core") }, - { QSurfaceFormat::CompatibilityProfile, qSL("compatibility") } -}; -#endif - -// We need to do some things BEFORE the Q*Application constructor runs, so we're using this -// old trick to do this hooking transparently for the user of the class. -int &Main::preConstructor(int &argc) -{ -#if !defined(AM_HEADLESS) -# if !defined(QT_NO_SESSIONMANAGER) - QGuiApplication::setFallbackSessionManagementEnabled(false); -# endif - - // this is needed for both WebEngine and Wayland Multi-screen rendering - QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); - - // Calling this semi-private function before the QGuiApplication constructor is the only way to - // set a custom global shared GL context. We are NOT creating it right now, since we still need - // to parse the requested format from the config files - the creation is completed later - // in setupOpenGL(). - qt_gl_set_global_share_context(new QOpenGLContext()); - -# if defined(Q_OS_UNIX) && defined(AM_MULTI_PROCESS) - // set a reasonable default for OSes/distros that do not set this by default - setenv("XDG_RUNTIME_DIR", "/tmp", 0); -# endif -#endif - return argc; -} - // The QGuiApplication constructor Main::Main(int &argc, char **argv) - : MainBase(preConstructor(argc), argv) + : MainBase(SharedMain::preConstructor(argc), argv) + , SharedMain() { // this might be needed later on by the native runtime to find a suitable qml runtime launcher setProperty("_am_build_dir", qSL(AM_BUILD_DIR)); @@ -221,7 +188,6 @@ Main::~Main() delete m_quickLauncher; delete m_systemMonitor; delete m_applicationIPCManager; - delete m_debuggingEnabler; } /*! \internal @@ -239,7 +205,7 @@ void Main::setup(const DefaultConfiguration *cfg) Q_DECL_NOEXCEPT_EXPR(false) setupLoggingRules(cfg->verbose(), cfg->loggingRules()); setupQmlDebugging(cfg->qmlDebugging()); Logging::registerUnregisteredDltContexts(); - setupOpenGL(cfg->openGLESProfile(), cfg->openGLESVersionMajor(), cfg->openGLESVersionMinor()); + setupOpenGL(cfg->openGLConfiguration()); loadStartupPlugins(cfg->pluginFilePaths("startup")); parseSystemProperties(cfg->rawSystemProperties()); @@ -252,8 +218,8 @@ void Main::setup(const DefaultConfiguration *cfg) Q_DECL_NOEXCEPT_EXPR(false) setMainQmlFile(cfg->mainQmlFile()); setupSingleOrMultiProcess(cfg->forceSingleProcess(), cfg->forceMultiProcess()); - setupRuntimesAndContainers(cfg->runtimeConfigurations(), cfg->containerConfigurations(), - cfg->pluginFilePaths("container")); + setupRuntimesAndContainers(cfg->runtimeConfigurations(), cfg->openGLConfiguration(), + cfg->containerConfigurations(), cfg->pluginFilePaths("container")); setupInstallationLocations(cfg->installationLocations()); loadApplicationDatabase(cfg->database(), cfg->recreateDatabase(), cfg->singleApp()); setupSingletons(cfg->containerSelectionConfiguration(), cfg->quickLaunchRuntimesPerContainer(), @@ -318,133 +284,6 @@ QQmlApplicationEngine *Main::qmlEngine() const return m_engine; } -void Main::setupQmlDebugging(bool qmlDebugging) -{ - if (qmlDebugging) { -#if !defined(QT_NO_QML_DEBUGGER) - m_debuggingEnabler = new QQmlDebuggingEnabler(true); - if (!QLoggingCategory::defaultCategory()->isDebugEnabled()) { - qCCritical(LogQmlRuntime) << "The default 'debug' logging category was disabled. " - "Re-enabling it for the QML Debugger interface to work correctly."; - QLoggingCategory::defaultCategory()->setEnabled(QtDebugMsg, true); - } -#else - qCWarning(LogSystem) << "The --qml-debug option is ignored, because Qt was built without support for QML Debugging!"; -#endif - } -} - -void Main::setupLoggingRules(bool verbose, const QStringList &loggingRules) -{ - const QString rules = verbose ? qSL("*=true\nqt.*.debug=false") - : loggingRules.isEmpty() ? qSL("*.debug=false") - : loggingRules.join(qL1C('\n')); - - QLoggingCategory::setFilterRules(rules); - - // setting this for child processes //TODO: use a more generic IPC approach - qputenv("AM_LOGGING_RULES", rules.toUtf8()); - StartupTimer::instance()->checkpoint("after logging setup"); -} - -void Main::setupOpenGL(const QString &profileName, int majorVersion, int minorVersion) -{ -#if !defined(AM_HEADLESS) - QOpenGLContext *globalContext = qt_gl_global_share_context(); - QSurfaceFormat format = QSurfaceFormat::defaultFormat(); - bool isES = (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES); - - // either both are set or none is set - if ((majorVersion > -1) != (minorVersion > -1)) { - qCWarning(LogGraphics) << "Requesting only the major or minor OpenGL version number is not " - "supported - always specify both or none."; - } else if (majorVersion > -1) { - bool valid = isES; - - if (!isES) { - // we need to map the ES version to the corresponding desktop versions: - static int mapping[] = { - 2, 0, 2, 1, - 3, 0, 4, 3, - 3, 1, 4, 5, - 3, 2, 4, 6, - -1 - }; - for (int i = 0; mapping[i] != -1; i += 4) { - if ((majorVersion == mapping[i]) && (minorVersion == mapping[i + 1])) { - majorVersion = mapping[i + 2]; - minorVersion = mapping[i + 3]; - valid = true; - break; - } - } - if (!valid) { - qCWarning(LogGraphics).nospace() << "Requested OpenGLES version " << majorVersion - << "." << minorVersion - << ", but there is no mapping available to a corresponding desktop GL version."; - } - } - - if (valid) { - format.setMajorVersion(majorVersion); - format.setMinorVersion(minorVersion); - m_requestedOpenGLMajorVersion = majorVersion; - m_requestedOpenGLMinorVersion = minorVersion; - qCDebug(LogGraphics).nospace() << "Requested OpenGL" << (isES ? "ES" : "") << " version " - << majorVersion << "." << minorVersion; - } - } - if (!profileName.isEmpty()) { - int profile = openGLProfileNames.key(profileName, -1); - - if (profile == -1) { - qCWarning(LogGraphics) << "Requested an invalid OpenGL profile:" << profileName; - } else if (profile != QSurfaceFormat::NoProfile) { - m_requestedOpenGLProfile = (QSurfaceFormat::OpenGLContextProfile) profile; - format.setProfile(m_requestedOpenGLProfile); - qCDebug(LogGraphics) << "Requested OpenGL profile" << profileName; - } - } - if ((m_requestedOpenGLProfile != QSurfaceFormat::NoProfile) || (m_requestedOpenGLMajorVersion > -1)) - QSurfaceFormat::setDefaultFormat(format); - - // Setting the screen is normally done by the QOpenGLContext constructor, but our constructor - // ran before the QGuiApplication constructor, so the screen was not initialized yet. So we have - // to tell the context about the screen again now: - globalContext->setScreen(QGuiApplication::primaryScreen()); - - if (!globalContext->create()) - throw Exception("Failed to create the global shared OpenGL context."); - - // check if we got what we requested on the OpenGL side - checkOpenGLFormat("global shared context", globalContext->format()); -#endif -} - -#if !defined(AM_HEADLESS) -void Main::checkOpenGLFormat(const char *what, const QSurfaceFormat &format) const -{ - if ((m_requestedOpenGLProfile != QSurfaceFormat::NoProfile) - && (format.profile() != m_requestedOpenGLProfile)) { - qCWarning(LogGraphics) << "Failed to get the requested OpenGL profile" - << openGLProfileNames.value(m_requestedOpenGLProfile) << "for the" - << what << "- got" - << openGLProfileNames.value(format.profile()) << "instead."; - } - if (m_requestedOpenGLMajorVersion > -1) { - if ((format.majorVersion() != m_requestedOpenGLMajorVersion ) - || (format.minorVersion() != m_requestedOpenGLMinorVersion)) { - qCWarning(LogGraphics).nospace() << "Failed to get the requested OpenGL version " - << m_requestedOpenGLMajorVersion << "." - << m_requestedOpenGLMinorVersion << " for " - << what << " - got " - << format.majorVersion() << "." - << format.minorVersion() << " instead"; - } - } -} -#endif - void Main::loadStartupPlugins(const QStringList &startupPluginPaths) Q_DECL_NOEXCEPT_EXPR(false) { m_startupPlugins = loadPlugins<StartupInterface>("startup", startupPluginPaths); @@ -515,7 +354,8 @@ void Main::setupSingleOrMultiProcess(bool forceSingleProcess, bool forceMultiPro #endif } -void Main::setupRuntimesAndContainers(const QVariantMap &runtimeConfigurations, const QVariantMap &containerConfigurations, const QStringList &containerPluginPaths) +void Main::setupRuntimesAndContainers(const QVariantMap &runtimeConfigurations, const QVariantMap &openGLConfiguration, + const QVariantMap &containerConfigurations, const QStringList &containerPluginPaths) { if (m_isSingleProcessMode) { RuntimeFactory::instance()->registerRuntime(new QmlInProcessRuntimeManager()); @@ -539,7 +379,7 @@ void Main::setupRuntimesAndContainers(const QVariantMap &runtimeConfigurations, ContainerFactory::instance()->setConfiguration(containerConfigurations); RuntimeFactory::instance()->setConfiguration(runtimeConfigurations); - + RuntimeFactory::instance()->setSystemOpenGLConfiguration(openGLConfiguration); RuntimeFactory::instance()->setSystemProperties(m_systemProperties.at(SP_ThirdParty), m_systemProperties.at(SP_BuiltIn)); @@ -782,7 +622,7 @@ void Main::loadQml(bool loadDummyData) Q_DECL_NOEXCEPT_EXPR(false) if (m_mainQmlLocalFile.isEmpty()) { qCDebug(LogQml) << "Not loading QML dummy data on non-local URL" << m_mainQml; } else { - loadDummyDataFiles(QFileInfo(m_mainQmlLocalFile).path()); + loadQmlDummyDataFiles(m_engine, QFileInfo(m_mainQmlLocalFile).path()); StartupTimer::instance()->checkpoint("after loading dummy-data"); } } @@ -1057,30 +897,6 @@ void Main::registerDBusInterfaces(const std::function<QString(const char *)> &bu #endif // defined(QT_DBUS_LIB) && !defined(AM_DISABLE_EXTERNAL_DBUS_INTERFACES) } -// copied straight from Qt 5.1.0 qmlscene/main.cpp for now - needs to be revised -void Main::loadDummyDataFiles(const QString &directory) -{ - QDir dir(directory + qSL("/dummydata"), qSL("*.qml")); - QStringList list = dir.entryList(); - for (int i = 0; i < list.size(); ++i) { - QString qml = list.at(i); - QQmlComponent comp(m_engine, dir.filePath(qml)); - QObject *dummyData = comp.create(); - - if (comp.isError()) { - const QList<QQmlError> errors = comp.errors(); - for (const QQmlError &error : errors) - qWarning() << error; - } - - if (dummyData) { - qWarning() << "Loaded dummy data:" << dir.filePath(qml); - qml.truncate(qml.length()-4); - m_engine->rootContext()->setContextProperty(qml, dummyData); - dummyData->setParent(m_engine); - } - } -} QVector<const Application *> Main::scanForApplication(const QString &singleAppInfoYaml, const QStringList &builtinAppsDirs) Q_DECL_NOEXCEPT_EXPR(false) { diff --git a/src/main-lib/main.h b/src/main-lib/main.h index a2c13269..8e433370 100644 --- a/src/main-lib/main.h +++ b/src/main-lib/main.h @@ -58,10 +58,10 @@ typedef QGuiApplication MainBase; #endif #include <QtAppManInstaller/installationlocation.h> +#include <QtAppManSharedMain/sharedmain.h> #include <QVector> QT_FORWARD_DECLARE_CLASS(QQmlApplicationEngine) -QT_FORWARD_DECLARE_STRUCT(QQmlDebuggingEnabler) QT_FORWARD_DECLARE_CLASS(QQuickView) QT_FORWARD_DECLARE_CLASS(QDBusAbstractAdaptor) @@ -81,7 +81,8 @@ class QuickLauncher; class SystemMonitor; class DefaultConfiguration; -class Main : public MainBase + +class Main : public MainBase, protected SharedMain { Q_OBJECT Q_PROPERTY(bool singleProcessMode READ isSingleProcessMode) @@ -101,8 +102,6 @@ public: QQmlApplicationEngine *qmlEngine() const; protected: - void setupQmlDebugging(bool qmlDebugging); - void setupLoggingRules(bool verbose, const QStringList &loggingRules); void loadStartupPlugins(const QStringList &startupPluginPaths) Q_DECL_NOEXCEPT_EXPR(false); void parseSystemProperties(const QVariantMap &rawSystemProperties); void setupDBus(bool startSessionBus) Q_DECL_NOEXCEPT_EXPR(false); @@ -110,8 +109,8 @@ protected: const std::function<QVariantMap(const char *)> &policyForInterface); void setMainQmlFile(const QString &mainQml) Q_DECL_NOEXCEPT_EXPR(false); void setupSingleOrMultiProcess(bool forceSingleProcess, bool forceMultiProcess) Q_DECL_NOEXCEPT_EXPR(false); - void setupRuntimesAndContainers(const QVariantMap &runtimeConfigurations, const QVariantMap &containerConfigurations, - const QStringList &containerPluginPaths); + void setupRuntimesAndContainers(const QVariantMap &runtimeConfigurations, const QVariantMap &openGLConfiguration, + const QVariantMap &containerConfigurations, const QStringList &containerPluginPaths); void setupInstallationLocations(const QVariantList &installationLocations); void loadApplicationDatabase(const QString &databasePath, bool recreateDatabase, const QString &singleApp) Q_DECL_NOEXCEPT_EXPR(false); @@ -124,7 +123,6 @@ protected: void setupWindowTitle(const QString &title, const QString &iconPath); void setupWindowManager(const QString &waylandSocketName, bool slowAnimations, bool uiWatchdog); void setupTouchEmulation(bool enableTouchEmulation); - void setupOpenGL(const QString &profileName, int majorVersion, int minorVersion); void setupShellServer(const QString &telnetAddress, quint16 telnetPort) Q_DECL_NOEXCEPT_EXPR(false); void setupSSDPService() Q_DECL_NOEXCEPT_EXPR(false); @@ -138,9 +136,6 @@ protected: QString hardwareId() const; private: - static int &preConstructor(int &argc); - void loadDummyDataFiles(const QString &directory); - #if defined(QT_DBUS_LIB) && !defined(AM_DISABLE_EXTERNAL_DBUS_INTERFACES) const char *dbusInterfaceName(QObject *o) const Q_DECL_NOEXCEPT_EXPR(false); void registerDBusObject(QDBusAbstractAdaptor *adaptor, const QString &dbusName, const char *serviceName, @@ -158,7 +153,6 @@ private: QUrl m_mainQml; QString m_mainQmlLocalFile; - QQmlDebuggingEnabler *m_debuggingEnabler = nullptr; QQmlApplicationEngine *m_engine = nullptr; QQuickView *m_view = nullptr; // only set if we allocate the window ourselves @@ -176,14 +170,6 @@ private: bool m_noSecurity = false; QStringList m_builtinAppsManifestDirs; QString m_installedAppsManifestDir; - -#if !defined(AM_HEADLESS) - QSurfaceFormat::OpenGLContextProfile m_requestedOpenGLProfile = QSurfaceFormat::NoProfile; - int m_requestedOpenGLMajorVersion = -1; - int m_requestedOpenGLMinorVersion = -1; - - void checkOpenGLFormat(const char *what, const QSurfaceFormat &format) const; -#endif }; QT_END_NAMESPACE_AM diff --git a/src/manager-lib/abstractruntime.cpp b/src/manager-lib/abstractruntime.cpp index c9f63987..0eff5209 100644 --- a/src/manager-lib/abstractruntime.cpp +++ b/src/manager-lib/abstractruntime.cpp @@ -105,12 +105,25 @@ QByteArray AbstractRuntime::securityToken() const return m_securityToken; } + +void setOpenGLConfiguration(const QVariantMap &openGLConfiguration) +{ + // not every runtime needs this information + Q_UNUSED(openGLConfiguration) +} + void AbstractRuntime::openDocument(const QString &document, const QString &mimeType) { Q_UNUSED(document) Q_UNUSED(mimeType) } +void AbstractRuntime::setSlowAnimations(bool slow) +{ + // not every runtime needs this information + Q_UNUSED(slow) +} + const Application *AbstractRuntime::application() const { return m_app.data(); @@ -223,4 +236,14 @@ void AbstractRuntimeManager::setSystemProperties(const QVariantMap &thirdParty, m_systemPropertiesBuiltIn = builtIn; } +QVariantMap AbstractRuntimeManager::systemOpenGLConfiguration() const +{ + return m_systemOpenGLConfiguration; +} + +void AbstractRuntimeManager::setSystemOpenGLConfiguration(const QVariantMap &openGLConfiguration) +{ + m_systemOpenGLConfiguration = openGLConfiguration; +} + QT_END_NAMESPACE_AM diff --git a/src/manager-lib/abstractruntime.h b/src/manager-lib/abstractruntime.h index a6cad10b..77212a2f 100644 --- a/src/manager-lib/abstractruntime.h +++ b/src/manager-lib/abstractruntime.h @@ -81,11 +81,15 @@ public: QVariantMap systemProperties3rdParty() const; void setSystemProperties(const QVariantMap &thirdParty, const QVariantMap &builtIn); + QVariantMap systemOpenGLConfiguration() const; + void setSystemOpenGLConfiguration(const QVariantMap &openGLConfiguration); + private: QString m_id; QVariantMap m_configuration; QVariantMap m_systemPropertiesBuiltIn; QVariantMap m_systemProperties3rdParty; + QVariantMap m_systemOpenGLConfiguration; }; @@ -121,7 +125,7 @@ public: virtual void openDocument(const QString &document, const QString &mimeType); - virtual void setSlowAnimations(bool value) = 0; + virtual void setSlowAnimations(bool slow); void setInProcessQmlEngine(QQmlEngine *view); QQmlEngine* inProcessQmlEngine() const; diff --git a/src/manager-lib/applicationmanager.cpp b/src/manager-lib/applicationmanager.cpp index c15e2862..e431f1b4 100644 --- a/src/manager-lib/applicationmanager.cpp +++ b/src/manager-lib/applicationmanager.cpp @@ -720,12 +720,23 @@ bool ApplicationManager::startApplication(const Application *app, const QString // we cannot use the quicklaunch pool, if // (a) a debug-wrapper is being used, // (b) stdio is redirected or - // (c) the app requests special environment variables - bool cannotUseQuickLaunch = !debugWrapperCommand.isEmpty() - || hasStdioRedirections - || !app->environmentVariables().isEmpty(); - - if (!cannotUseQuickLaunch) { + // (c) the app requests special environment variables or + // (d) the app requests a different OpenGL config from the AM + const char *cannotUseQuickLaunch = nullptr; + + if (!debugWrapperCommand.isEmpty()) + cannotUseQuickLaunch = "the app is started using a debug-wrapper"; + else if (hasStdioRedirections) + cannotUseQuickLaunch = "standard I/O is redirected"; + else if (!app->environmentVariables().isEmpty()) + cannotUseQuickLaunch = "the app requests customs environment variables"; + else if (app->openGLConfiguration() != runtimeManager->systemOpenGLConfiguration()) + cannotUseQuickLaunch = "the app requests a custom OpenGL configuration"; + + if (cannotUseQuickLaunch) { + qCDebug(LogSystem) << "Cannot use quick-launch for application" << app->id() + << "because" << cannotUseQuickLaunch; + } else { // check quicklaunch pool QPair<AbstractContainer *, AbstractRuntime *> quickLaunch = QuickLauncher::instance()->take(containerId, app->m_runtimeName); diff --git a/src/manager-lib/manager-lib.pro b/src/manager-lib/manager-lib.pro index 93f728f8..8f57ee85 100644 --- a/src/manager-lib/manager-lib.pro +++ b/src/manager-lib/manager-lib.pro @@ -5,7 +5,7 @@ MODULE = appman_manager load(am-config) QT = core network qml -!headless:QT *= gui quick qml-private +!headless:QT *= gui gui-private quick qml-private qtHaveModule(dbus):QT *= dbus QT_FOR_PRIVATE *= \ appman_common-private \ @@ -36,7 +36,7 @@ HEADERS += \ applicationipcinterface_p.h \ applicationmanager_p.h \ systemreader.h \ - debugwrapper.h + debugwrapper.h \ linux:HEADERS += \ sysfsreader.h \ @@ -68,7 +68,7 @@ SOURCES += \ applicationipcmanager.cpp \ applicationipcinterface.cpp \ systemreader.cpp \ - debugwrapper.cpp + debugwrapper.cpp \ linux:SOURCES += \ sysfsreader.cpp \ diff --git a/src/manager-lib/nativeruntime.cpp b/src/manager-lib/nativeruntime.cpp index f10a20fa..c0fcf45e 100644 --- a/src/manager-lib/nativeruntime.cpp +++ b/src/manager-lib/nativeruntime.cpp @@ -264,23 +264,49 @@ bool NativeRuntime::start() break; } - QMap<QString, QString> env = { - { qSL("QT_QPA_PLATFORM"), qSL("wayland") }, - { qSL("QT_IM_MODULE"), QString() }, // Applications should use wayland text input - { qSL("AM_SECURITY_TOKEN"), qL1S(securityToken().toHex()) }, - { qSL("AM_DBUS_PEER_ADDRESS"), applicationInterfaceServer()->address() }, - { qSL("AM_DBUS_NOTIFICATION_BUS_ADDRESS"), NotificationManager::instance()->property("_am_dbus_name").toString() }, - { qSL("AM_RUNTIME_CONFIGURATION"), QString::fromUtf8(QtYaml::yamlFromVariantDocuments({ configuration() })) }, - { qSL("AM_BASE_DIR"), QDir::currentPath() } + QVariantMap dbusConfig = { + { qSL("p2p"), applicationInterfaceServer()->address() }, + { qSL("org.freedesktop.Notifications"), NotificationManager::instance()->property("_am_dbus_name").toString()} }; + QVariantMap loggingConfig = { + { qSL("dlt"), Logging::isDltEnabled() }, + { qSL("rules"), Logging::filterRules() } + }; + + QVariantMap uiConfig; if (m_slowAnimations) - env.insert(qSL("AM_SLOW_ANIMATIONS"), qSL("1")); + uiConfig.insert(qSL("slowAnimations"), true); + QVariantMap openGLConfig = m_app->openGLConfiguration(); + if (openGLConfig.isEmpty()) + openGLConfig = manager()->systemOpenGLConfiguration(); + if (!openGLConfig.isEmpty()) + uiConfig.insert(qSL("opengl"), openGLConfig); + + QVariantMap config = { + { qSL("logging"), loggingConfig }, + { qSL("baseDir"), QDir::currentPath() }, + { qSL("runtimeConfiguration"), configuration() }, + { qSL("securityToken"), qL1S(securityToken().toHex()) }, + { qSL("dbus"), dbusConfig } + }; if (!m_needsLauncher && !m_isQuickLauncher) - env.insert(qSL("AM_RUNTIME_SYSTEM_PROPERTIES"), QString::fromUtf8(QtYaml::yamlFromVariantDocuments({ systemProperties() }))); - if (!Logging::isDltEnabled()) + config.insert(qSL("systemProperties"), systemProperties()); + if (!uiConfig.isEmpty()) + config.insert(qSL("ui"), uiConfig); + + QMap<QString, QString> env = { + { qSL("QT_QPA_PLATFORM"), qSL("wayland") }, + { qSL("QT_IM_MODULE"), QString() }, // Applications should use wayland text input + { qSL("QT_SCALE_FACTOR"), QString() }, // do not scale wayland clients + { qSL("AM_CONFIG"), QString::fromUtf8(QtYaml::yamlFromVariantDocuments({ config })) }, + }; + + if (Logging::isDltEnabled()) { + // sadly we still need this, since we need to disable DLT as soon as possible env.insert(qSL("AM_NO_DLT_LOGGING"), qSL("1")); + } for (QMapIterator<QString, QVariant> it(configuration().value(qSL("environmentVariables")).toMap()); it.hasNext(); ) { it.next(); @@ -472,12 +498,12 @@ void NativeRuntime::openDocument(const QString &document, const QString &mimeTyp emit m_applicationInterface->openDocument(document, mimeType); } -void NativeRuntime::setSlowAnimations(bool value) +void NativeRuntime::setSlowAnimations(bool slow) { - if (m_slowAnimations != value) { - m_slowAnimations = value; + if (m_slowAnimations != slow) { + m_slowAnimations = slow; if (m_applicationInterface) - emit m_applicationInterface->slowAnimationsChanged(value); + emit m_applicationInterface->slowAnimationsChanged(slow); } } diff --git a/src/manager-lib/nativeruntime.h b/src/manager-lib/nativeruntime.h index b840ec60..8521fb5d 100644 --- a/src/manager-lib/nativeruntime.h +++ b/src/manager-lib/nativeruntime.h @@ -89,8 +89,8 @@ public: bool attachApplicationToQuickLauncher(const Application *app) override; qint64 applicationProcessId() const override; - virtual void openDocument(const QString &document, const QString &mimeType) override; - virtual void setSlowAnimations(bool value) override; + void openDocument(const QString &document, const QString &mimeType) override; + void setSlowAnimations(bool slow) override; bool sendNotificationUpdate(Notification *n); @@ -134,6 +134,7 @@ private: AbstractContainerProcess *m_process = nullptr; QDBusServer *m_applicationInterfaceServer; bool m_slowAnimations = false; + QVariantMap m_openGLConfiguration; friend class NativeRuntimeManager; }; diff --git a/src/manager-lib/qmlinprocessruntime.cpp b/src/manager-lib/qmlinprocessruntime.cpp index 51e7d0da..821f900a 100644 --- a/src/manager-lib/qmlinprocessruntime.cpp +++ b/src/manager-lib/qmlinprocessruntime.cpp @@ -60,6 +60,7 @@ #include "global.h" #include "utilities.h" #include "runtimefactory.h" +#include "qml-utilities.h" #if defined(Q_OS_UNIX) # include <signal.h> @@ -67,35 +68,6 @@ QT_BEGIN_NAMESPACE_AM -// copied straight from Qt 5.1.0 qmlscene/main.cpp for now - needs to be revised -static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory) -{ - QDir dir(directory + qSL("/dummydata"), qSL("*.qml")); - QStringList list = dir.entryList(); - for (int i = 0; i < list.size(); ++i) { - QString qml = list.at(i); - QFile f(dir.filePath(qml)); - f.open(QIODevice::ReadOnly); - QByteArray data = f.readAll(); - QQmlComponent comp(&engine); - comp.setData(data, QUrl()); - QObject *dummyData = comp.create(); - - if (comp.isError()) { - const QList<QQmlError> errors = comp.errors(); - for (const QQmlError &error : errors) - qWarning() << error; - } - - if (dummyData) { - qWarning() << "Loaded dummy data:" << dir.filePath(qml); - qml.truncate(qml.length()-4); - engine.rootContext()->setContextProperty(qml, dummyData); - dummyData->setParent(&engine); - } - } -} - const char *QmlInProcessRuntime::s_runtimeKey = "_am_runtime"; @@ -131,7 +103,7 @@ bool QmlInProcessRuntime::start() if (m_app->runtimeParameters().value(qSL("loadDummyData")).toBool()) { qCDebug(LogSystem) << "Loading dummy-data"; - loadDummyDataFiles(*m_inProcessQmlEngine, QFileInfo(m_app->absoluteCodeFilePath()).path()); + loadQmlDummyDataFiles(m_inProcessQmlEngine, QFileInfo(m_app->absoluteCodeFilePath()).path()); } const QStringList importPaths = variantToStringList(configuration().value(qSL("importPaths"))) diff --git a/src/manager-lib/qmlinprocessruntime.h b/src/manager-lib/qmlinprocessruntime.h index 6e8208c2..9685243f 100644 --- a/src/manager-lib/qmlinprocessruntime.h +++ b/src/manager-lib/qmlinprocessruntime.h @@ -73,9 +73,6 @@ public: void openDocument(const QString &document, const QString &mimeType) override; qint64 applicationProcessId() const override; - // No need to do anything as, being inprocess, it will use QUnified timers from appman itself - void setSlowAnimations(bool) override {} - public slots: bool start() override; void stop(bool forceKill = false) override; diff --git a/src/manager-lib/runtimefactory.cpp b/src/manager-lib/runtimefactory.cpp index 16176f71..bceb60ba 100644 --- a/src/manager-lib/runtimefactory.cpp +++ b/src/manager-lib/runtimefactory.cpp @@ -117,16 +117,14 @@ AbstractRuntime *RuntimeFactory::createQuickLauncher(AbstractContainer *containe void RuntimeFactory::setConfiguration(const QVariantMap &configuration) { - for (auto it = m_runtimes.cbegin(); it != m_runtimes.cend(); ++it) { + for (auto it = m_runtimes.cbegin(); it != m_runtimes.cend(); ++it) it.value()->setConfiguration(configuration.value(it.key()).toMap()); - } } void RuntimeFactory::setSystemProperties(const QVariantMap &thirdParty, const QVariantMap &builtIn) { - for (auto it = m_runtimes.cbegin(); it != m_runtimes.cend(); ++it) { + for (auto it = m_runtimes.cbegin(); it != m_runtimes.cend(); ++it) it.value()->setSystemProperties(thirdParty, builtIn); - } } void RuntimeFactory::setSlowAnimations(bool value) @@ -134,6 +132,12 @@ void RuntimeFactory::setSlowAnimations(bool value) m_slowAnimations = value; } +void RuntimeFactory::setSystemOpenGLConfiguration(const QVariantMap &openGLConfiguration) +{ + for (auto it = m_runtimes.cbegin(); it != m_runtimes.cend(); ++it) + it.value()->setSystemOpenGLConfiguration(openGLConfiguration); +} + bool RuntimeFactory::registerRuntime(AbstractRuntimeManager *manager) { return registerRuntime(manager, manager->identifier()); diff --git a/src/manager-lib/runtimefactory.h b/src/manager-lib/runtimefactory.h index 18ce504f..4ba41aca 100644 --- a/src/manager-lib/runtimefactory.h +++ b/src/manager-lib/runtimefactory.h @@ -70,6 +70,7 @@ public: void setConfiguration(const QVariantMap &configuration); void setSystemProperties(const QVariantMap &thirdParty, const QVariantMap &builtIn); void setSlowAnimations(bool isSlow); + void setSystemOpenGLConfiguration(const QVariantMap &openGLConfiguration); bool registerRuntime(AbstractRuntimeManager *manager); bool registerRuntime(AbstractRuntimeManager *manager, const QString &identifier); diff --git a/src/main-lib/qmllogger.cpp b/src/shared-main-lib/qmllogger.cpp index 21889251..21889251 100644 --- a/src/main-lib/qmllogger.cpp +++ b/src/shared-main-lib/qmllogger.cpp diff --git a/src/main-lib/qmllogger.h b/src/shared-main-lib/qmllogger.h index 7ea18cb5..7ea18cb5 100644 --- a/src/main-lib/qmllogger.h +++ b/src/shared-main-lib/qmllogger.h diff --git a/src/shared-main-lib/shared-main-lib.pro b/src/shared-main-lib/shared-main-lib.pro new file mode 100644 index 00000000..bbbb52c6 --- /dev/null +++ b/src/shared-main-lib/shared-main-lib.pro @@ -0,0 +1,22 @@ +TEMPLATE = lib +TARGET = QtAppManSharedMain +MODULE = appman_shared_main + +load(am-config) + +QT = core network qml +!headless:QT *= gui gui-private +QT *= \ + appman_common-private \ + +CONFIG *= static internal_module + +HEADERS += \ + $$PWD/sharedmain.h \ + $$PWD/qmllogger.h \ + +SOURCES += \ + $$PWD/sharedmain.cpp \ + $$PWD/qmllogger.cpp \ + +load(qt_module) diff --git a/src/shared-main-lib/sharedmain.cpp b/src/shared-main-lib/sharedmain.cpp new file mode 100644 index 00000000..862025de --- /dev/null +++ b/src/shared-main-lib/sharedmain.cpp @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#include <memory> +#include <qglobal.h> + +#include <QFile> +#include <QDir> +#include <QStringList> +#include <QVariant> +#include <QFileInfo> +#include <QLibrary> +#include <QQmlDebuggingEnabler> +#include <QQmlComponent> +#include <QQmlContext> +#include <QQmlEngine> + +#if !defined(AM_HEADLESS) +# include <QGuiApplication> +# include <private/qopenglcontext_p.h> +#endif + +#include "global.h" +#include "logging.h" +#include "sharedmain.h" + +#include "utilities.h" +#include "exception.h" +#include "crashhandler.h" +#include "qmllogger.h" +#include "startuptimer.h" +#include "unixsignalhandler.h" + +#include "../plugin-interfaces/startupinterface.h" + + +QT_BEGIN_NAMESPACE_AM + +#if !defined(AM_HEADLESS) +static QMap<int, QString> openGLProfileNames = { + { QSurfaceFormat::NoProfile, qSL("default") }, + { QSurfaceFormat::CoreProfile, qSL("core") }, + { QSurfaceFormat::CompatibilityProfile, qSL("compatibility") } +}; +#endif + +SharedMain::SharedMain() +{ } + +SharedMain::~SharedMain() +{ + delete m_debuggingEnabler; +} + +// We need to do some things BEFORE the Q*Application constructor runs, so we're using this +// old trick to do this hooking transparently for the user of the class. +int &SharedMain::preConstructor(int &argc) +{ +#if !defined(AM_HEADLESS) +# if !defined(QT_NO_SESSIONMANAGER) + QGuiApplication::setFallbackSessionManagementEnabled(false); +# endif + + // this is needed for both WebEngine and Wayland Multi-screen rendering + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + + // Calling this semi-private function before the QGuiApplication constructor is the only way to + // set a custom global shared GL context. We are NOT creating it right now, since we still need + // to parse the requested format from the config files - the creation is completed later + // in setupOpenGL(). + qt_gl_set_global_share_context(new QOpenGLContext()); + +# if defined(Q_OS_UNIX) && defined(AM_MULTI_PROCESS) + // set a reasonable default for OSes/distros that do not set this by default + setenv("XDG_RUNTIME_DIR", QDir::tempPath().toLocal8Bit(), 0); +# endif +#endif + return argc; +} + +void SharedMain::setupQmlDebugging(bool qmlDebugging) +{ + if (qmlDebugging) { +#if !defined(QT_NO_QML_DEBUGGER) + m_debuggingEnabler = new QQmlDebuggingEnabler(true); + if (!QLoggingCategory::defaultCategory()->isDebugEnabled()) { + qCCritical(LogQmlRuntime) << "The default 'debug' logging category was disabled. " + "Re-enabling it for the QML Debugger interface to work correctly."; + QLoggingCategory::defaultCategory()->setEnabled(QtDebugMsg, true); + } +#else + qCWarning(LogSystem) << "The --qml-debug option is ignored, because Qt was built without support for QML Debugging!"; +#endif + } +} + +void SharedMain::setupLoggingRules(bool verbose, const QStringList &loggingRules) +{ + const QStringList rules = verbose ? QStringList() << qSL("*=true") << qSL("qt.*.debug=false") + : loggingRules.isEmpty() ? QStringList(qSL("*.debug=false")) + : loggingRules; + Logging::setFilterRules(rules); + StartupTimer::instance()->checkpoint("after logging setup"); +} + +void SharedMain::setupOpenGL(const QVariantMap &openGLConfiguration) +{ +#if !defined(AM_HEADLESS) + QString profileName = openGLConfiguration.value(qSL("desktopProfile")).toString(); + int majorVersion = openGLConfiguration.value(qSL("esMajorVersion"), -1).toInt(); + int minorVersion = openGLConfiguration.value(qSL("esMinorVersion"), -1).toInt(); + + QOpenGLContext *globalContext = qt_gl_global_share_context(); + QSurfaceFormat format = QSurfaceFormat::defaultFormat(); + bool isES = (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES); + + // either both are set or none is set + if ((majorVersion > -1) != (minorVersion > -1)) { + qCWarning(LogGraphics) << "Requesting only the major or minor OpenGL version number is not " + "supported - always specify both or none."; + } else if (majorVersion > -1) { + bool valid = isES; + + if (!isES) { + // we need to map the ES version to the corresponding desktop versions: + static int mapping[] = { + 2, 0, 2, 1, + 3, 0, 4, 3, + 3, 1, 4, 5, + 3, 2, 4, 6, + -1 + }; + for (int i = 0; mapping[i] != -1; i += 4) { + if ((majorVersion == mapping[i]) && (minorVersion == mapping[i + 1])) { + majorVersion = mapping[i + 2]; + minorVersion = mapping[i + 3]; + valid = true; + break; + } + } + if (!valid) { + qCWarning(LogGraphics).nospace() << "Requested OpenGLES version " << majorVersion + << "." << minorVersion + << ", but there is no mapping available to a corresponding desktop GL version."; + } + } + + if (valid) { + format.setMajorVersion(majorVersion); + format.setMinorVersion(minorVersion); + m_requestedOpenGLMajorVersion = majorVersion; + m_requestedOpenGLMinorVersion = minorVersion; + qCDebug(LogGraphics).nospace() << "Requested OpenGL" << (isES ? "ES" : "") << " version " + << majorVersion << "." << minorVersion; + } + } + if (!profileName.isEmpty()) { + int profile = openGLProfileNames.key(profileName, -1); + + if (profile == -1) { + qCWarning(LogGraphics) << "Requested an invalid OpenGL profile:" << profileName; + } else if (profile != QSurfaceFormat::NoProfile) { + m_requestedOpenGLProfile = (QSurfaceFormat::OpenGLContextProfile) profile; + format.setProfile(m_requestedOpenGLProfile); + qCDebug(LogGraphics) << "Requested OpenGL profile" << profileName; + } + } + if ((m_requestedOpenGLProfile != QSurfaceFormat::NoProfile) || (m_requestedOpenGLMajorVersion > -1)) + QSurfaceFormat::setDefaultFormat(format); + + // Setting the screen is normally done by the QOpenGLContext constructor, but our constructor + // ran before the QGuiApplication constructor, so the screen was not initialized yet. So we have + // to tell the context about the screen again now: + globalContext->setScreen(QGuiApplication::primaryScreen()); + + if (!globalContext->create()) + throw Exception("Failed to create the global shared OpenGL context."); + + // check if we got what we requested on the OpenGL side + checkOpenGLFormat("global shared context", globalContext->format()); +#endif +} + +#if !defined(AM_HEADLESS) +void SharedMain::checkOpenGLFormat(const char *what, const QSurfaceFormat &format) const +{ + if ((m_requestedOpenGLProfile != QSurfaceFormat::NoProfile) + && (format.profile() != m_requestedOpenGLProfile)) { + qCWarning(LogGraphics) << "Failed to get the requested OpenGL profile" + << openGLProfileNames.value(m_requestedOpenGLProfile) << "for the" + << what << "- got" + << openGLProfileNames.value(format.profile()) << "instead."; + } + if (m_requestedOpenGLMajorVersion > -1) { + if ((format.majorVersion() != m_requestedOpenGLMajorVersion ) + || (format.minorVersion() != m_requestedOpenGLMinorVersion)) { + qCWarning(LogGraphics).nospace() << "Failed to get the requested OpenGL version " + << m_requestedOpenGLMajorVersion << "." + << m_requestedOpenGLMinorVersion << " for " + << what << " - got " + << format.majorVersion() << "." + << format.minorVersion() << " instead"; + } + } +} +#endif + +QT_END_NAMESPACE_AM diff --git a/src/shared-main-lib/sharedmain.h b/src/shared-main-lib/sharedmain.h new file mode 100644 index 00000000..8c9da1ba --- /dev/null +++ b/src/shared-main-lib/sharedmain.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#pragma once + +#include <QtAppManCommon/global.h> +#include <QVariantMap> +#include <QStringList> +#if !defined(AM_HEADLESS) +# include <QSurfaceFormat> +#endif + +QT_FORWARD_DECLARE_STRUCT(QQmlDebuggingEnabler) +QT_FORWARD_DECLARE_STRUCT(QQmlEngine) + +QT_BEGIN_NAMESPACE_AM + +class SharedMain +{ +public: + SharedMain(); + ~SharedMain(); + + static int &preConstructor(int &argc); + void setupQmlDebugging(bool qmlDebugging); + void setupLoggingRules(bool verbose, const QStringList &loggingRules); + void setupOpenGL(const QVariantMap &openGLConfiguration); + +#if !defined(AM_HEADLESS) + void checkOpenGLFormat(const char *what, const QSurfaceFormat &format) const; +#endif + +private: + QQmlDebuggingEnabler *m_debuggingEnabler = nullptr; + +#if !defined(AM_HEADLESS) + QSurfaceFormat::OpenGLContextProfile m_requestedOpenGLProfile = QSurfaceFormat::NoProfile; + int m_requestedOpenGLMajorVersion = -1; + int m_requestedOpenGLMinorVersion = -1; +#endif +}; + +QT_END_NAMESPACE_AM diff --git a/src/src.pro b/src/src.pro index 8a6ab09f..00543693 100644 --- a/src/src.pro +++ b/src/src.pro @@ -34,8 +34,11 @@ monitor_lib.depends = manager_lib window_lib launcher_lib.subdir = launcher-lib launcher_lib.depends = application_lib notification_lib +shared_main_lib.subdir = shared-main-lib +shared_main_lib.depends = common_lib + main_lib.subdir = main-lib -main_lib.depends = manager_lib installer_lib window_lib monitor_lib +main_lib.depends = shared_main_lib manager_lib installer_lib window_lib monitor_lib !disable-external-dbus-interfaces:qtHaveModule(dbus) { dbus_lib.subdir = dbus-lib @@ -45,7 +48,7 @@ main_lib.depends = manager_lib installer_lib window_lib monitor_lib } launchers_qml.subdir = launchers/qml -launchers_qml.depends = launcher_lib plugin_interfaces +launchers_qml.depends = shared_main_lib launcher_lib plugin_interfaces tools_appman.subdir = tools/appman tools_appman.depends = main_lib @@ -81,6 +84,7 @@ SUBDIRS = \ installer_lib \ window_lib \ monitor_lib \ + shared_main_lib \ main_lib \ tools_appman \ # Although the testrunner is in tools we don't want to build it with tools-only diff --git a/src/tools/appman/appman.pro b/src/tools/appman/appman.pro index 0b9e3e46..385860b0 100644 --- a/src/tools/appman/appman.pro +++ b/src/tools/appman/appman.pro @@ -1,5 +1,5 @@ TEMPLATE = app -TARGET = appman +TARGET = appman load(am-config) @@ -7,8 +7,6 @@ QT = appman_main-private CONFIG *= console -#win32:LIBS += -luser32 - SOURCES += \ $$PWD/appman.cpp |