Branch data Line data Source code
1 : : // Copyright (C) 2021 The Qt Company Ltd.
2 : : // Copyright (C) 2019 Luxoft Sweden AB
3 : : // Copyright (C) 2018 Pelagicore AG
4 : : // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
5 : :
6 : : #include <memory>
7 : :
8 : : #include <QQmlComponent>
9 : : #include <QQmlContext>
10 : : #include <QQmlDebuggingEnabler>
11 : :
12 : : #include <QSocketNotifier>
13 : : #include <QFile>
14 : : #include <QFileInfo>
15 : : #include <QDir>
16 : : #include <QtEndian>
17 : : #include <QMetaObject>
18 : : #include <QRegularExpression>
19 : : #include <QCommandLineParser>
20 : : #include <QLibraryInfo>
21 : :
22 : : #include <QDBusConnection>
23 : : #include <QDBusInterface>
24 : : #include <QDBusArgument>
25 : : #include <QLoggingCategory>
26 : : #include <private/qabstractanimation_p.h> // For QUnifiedTimer
27 : : #include <qplatformdefs.h>
28 : :
29 : : #include <QGuiApplication>
30 : : #include <QQuickItem>
31 : : #include <QQuickView>
32 : : #include <QQuickWindow>
33 : :
34 : : #include "applicationmain.h"
35 : : #include "qtyaml.h"
36 : : #include "global.h"
37 : : #include "logging.h"
38 : : #include "utilities.h"
39 : : #include "exception.h"
40 : : #include "crashhandler.h"
41 : : #include "yamlpackagescanner.h"
42 : : #include "packageinfo.h"
43 : : #include "applicationinfo.h"
44 : : #include "startupinterface.h"
45 : : #include "dbus-utilities.h"
46 : : #include "startuptimer.h"
47 : : #include "processtitle.h"
48 : : #include "qml-utilities.h"
49 : : #include "launcher-qml_p.h"
50 : : #include "applicationmanagerwindow.h"
51 : :
52 : : using namespace Qt::StringLiterals;
53 : :
54 : :
55 : : QT_USE_NAMESPACE_AM
56 : :
57 : :
58 : 72 : int main(int argc, char *argv[])
59 : : {
60 : 72 : StartupTimer::instance()->checkpoint("entered main");
61 : :
62 : 72 : ProcessTitle::adjustArgumentCount(argc);
63 : :
64 [ + - ]: 72 : QCoreApplication::setApplicationName(u"Qt Application Manager QML Launcher"_s);
65 [ + - ]: 72 : QCoreApplication::setOrganizationName(u"QtProject"_s);
66 [ + - ]: 72 : QCoreApplication::setOrganizationDomain(u"qt-project.org"_s);
67 [ + - ]: 72 : QCoreApplication::setApplicationVersion(QString::fromLatin1(QT_AM_VERSION_STR));
68 : :
69 [ + - ]: 72 : if (Logging::isDltAvailable()) {
70 [ + - ]: 72 : if (qEnvironmentVariableIntValue("AM_NO_DLT_LOGGING") == 1)
71 : 72 : Logging::setDltEnabled(false);
72 : :
73 : : // The common-lib is already registering the DLT Application for the application manager.
74 : : // As the appID needs to be unique within the system, we cannot use the same appID and
75 : : // need to change it as early as possible.
76 : :
77 : : // As we don't know the app-id yet, we are registering a place holder so we are able to see
78 : : // something in the dlt logs if general errors occur.
79 [ + - + - ]: 144 : Logging::setDltApplicationId("QTLQ", "Qt Application Manager Launcher QML");
80 : : }
81 [ + - ]: 72 : Logging::setApplicationId("qml-launcher");
82 : 72 : Logging::initialize();
83 : :
84 : 72 : std::unique_ptr<ApplicationMain> am;
85 : 72 : std::unique_ptr<Controller> controller; // this needs to die BEFORE qApp does
86 : :
87 : 72 : try {
88 [ + - + - ]: 144 : const QString socket = QDir(qEnvironmentVariable("XDG_RUNTIME_DIR"))
89 [ + - + - ]: 144 : .filePath(qEnvironmentVariable("WAYLAND_DISPLAY"));
90 [ + - - + ]: 72 : if (!QFileInfo::exists(socket))
91 : 0 : throw Exception("Cannot start application: no wayland display - expected socket at: %1").arg(socket);
92 : :
93 [ + - ]: 144 : am = std::make_unique<ApplicationMain>(argc, argv);
94 : :
95 [ + - ]: 72 : QCommandLineParser clp;
96 [ + - ]: 72 : clp.addHelpOption();
97 [ + - + - ]: 72 : clp.addOption({ u"qml-debug"_s, u"Enables QML debugging and profiling."_s });
98 [ + - + - ]: 72 : clp.addOption({ u"quicklaunch"_s, u"Starts the launcher in the quicklaunching mode."_s });
99 [ + - + - ]: 72 : clp.addOption({ u"directload"_s , u"The info.yaml to start (you can add '@<appid>' to start a specific app within the package, instead of the first one)."_s, u"info.yaml"_s });
100 [ + - + - ]: 72 : clp.process(*am);
101 : :
102 [ + - ]: 72 : bool quicklaunched = clp.isSet(u"quicklaunch"_s);
103 [ + - ]: 72 : QString directLoadManifest = clp.value(u"directload"_s);
104 : :
105 [ + - ]: 72 : if (directLoadManifest.isEmpty())
106 [ + - ]: 144 : am->loadConfiguration();
107 : :
108 : : //CrashHandler::setCrashActionConfiguration(am->runtimeConfiguration().value(u"crashAction"_s).toMap());
109 : : // the verbose flag has already been factored into the rules:
110 [ + - + - : 144 : am->setupLogging(false, am->loggingRules(), QString(), am->useAMConsoleLogger());
+ - ]
111 [ + - + - ]: 72 : am->setupQmlDebugging(clp.isSet(u"qml-debug"_s));
112 [ + - + - ]: 72 : am->setupWatchdog(am->watchdogConfiguration());
113 [ + - + - ]: 72 : am->setupOpenGL(am->openGLConfiguration());
114 [ + - + - : 144 : am->setupIconTheme(am->iconThemeSearchPaths(), am->iconThemeName());
+ - ]
115 : 72 : am->registerWaylandExtensions();
116 : :
117 [ + - + - ]: 72 : if (Logging::isDltAvailable())
118 [ + - + - ]: 144 : Logging::setDltLongMessageBehavior(am->dltLongMessageBehavior());
119 : :
120 [ + - + - ]: 72 : StartupTimer::instance()->checkpoint("after basic initialization");
121 : :
122 [ - + ]: 72 : if (!directLoadManifest.isEmpty()) {
123 : 0 : QString directLoadAppId;
124 [ # # ]: 0 : qsizetype appPos = directLoadManifest.indexOf(u"@"_s);
125 [ # # ]: 0 : if (appPos > 0) {
126 [ # # ]: 0 : directLoadAppId = directLoadManifest.mid(appPos + 1);
127 [ # # ]: 0 : directLoadManifest.truncate(appPos);
128 : : }
129 : :
130 [ # # ]: 0 : QFileInfo fi(directLoadManifest);
131 [ # # # # : 0 : if (!fi.exists() || fi.fileName() != u"info.yaml"_s)
# # # # #
# # # # #
# # ]
132 : 0 : throw Exception("--directload needs a valid info.yaml file as parameter");
133 [ # # ]: 0 : directLoadManifest = fi.absoluteFilePath();
134 [ # # # # ]: 0 : controller.reset(new Controller(am.get(), quicklaunched, qMakePair(directLoadManifest, directLoadAppId)));
135 : 0 : } else {
136 [ + - ]: 72 : am->setupDBusConnections();
137 [ + - + - ]: 72 : StartupTimer::instance()->checkpoint("after dbus initialization");
138 [ + - + - ]: 144 : controller.reset(new Controller(am.get(), quicklaunched));
139 : : }
140 [ - - ]: 72 : } catch (const std::exception &e) {
141 [ - - - - : 0 : qCCritical(LogQmlRuntime) << "ERROR:" << e.what();
- - - - -
- ]
142 : 0 : return 2;
143 : 0 : }
144 : :
145 : : // we want the exec() outside of the try/catch block, so stray user exceptions trigger the
146 : : // CrashHandler's set_terminate callback.
147 [ + - ]: 72 : return ApplicationMainBase::exec();
148 : 67 : }
149 : :
150 [ + - ]: 72 : Controller::Controller(ApplicationMain *am, bool quickLaunched)
151 [ + - ]: 144 : : Controller(am, quickLaunched, qMakePair(QString{}, QString{}))
152 : 72 : { }
153 : :
154 : 72 : Controller::Controller(ApplicationMain *am, bool quickLaunched, const QPair<QString, QString> &directLoad)
155 : : : QObject(am)
156 [ + - + - ]: 72 : , m_quickLaunched(quickLaunched)
157 : : {
158 [ + - ]: 72 : connect(&m_engine, &QObject::destroyed, QCoreApplication::instance(), &QCoreApplication::quit);
159 [ + - ]: 72 : CrashHandler::setQmlEngine(&m_engine);
160 : :
161 [ + - ]: 72 : m_configuration = am->runtimeConfiguration();
162 : :
163 [ + - + - ]: 144 : const QStringList resources = variantToStringList(m_configuration.value(u"resources"_s));
164 [ + + ]: 74 : for (const QString &resource: resources) {
165 [ + - + - : 4 : const QString path = QFileInfo(resource).isRelative() ? am->baseDir() + resource : resource;
+ - + - +
- - - ]
166 : :
167 : 2 : try {
168 [ + - ]: 2 : loadResource(path);
169 [ - - ]: 0 : } catch (const Exception &e) {
170 [ - - - - : 0 : qCWarning(LogSystem).noquote() << e.errorString();
- - - - ]
171 : 0 : }
172 : 2 : }
173 : :
174 : 72 : QString absolutePluginPath;
175 [ + - + - ]: 144 : QStringList pluginPaths = variantToStringList(m_configuration.value(u"pluginPaths"_s));
176 [ + - + - : 72 : for (QString &path : pluginPaths) {
- + ]
177 [ # # # # : 0 : if (QFileInfo(path).isRelative())
# # ]
178 [ # # ]: 0 : path.prepend(am->baseDir());
179 [ # # ]: 0 : else if (absolutePluginPath.isEmpty())
180 : 0 : absolutePluginPath = path;
181 : :
182 [ # # ]: 0 : qApp->addLibraryPath(path);
183 : : }
184 : :
185 [ - + ]: 72 : if (!absolutePluginPath.isEmpty()) {
186 [ # # # # : 0 : qCWarning(LogDeployment).nospace() << "Absolute plugin path in the runtime configuration "
# # ]
187 [ # # # # : 0 : "can lead to problems inside containers (e.g. " << absolutePluginPath << ")";
# # ]
188 : : }
189 : :
190 : 72 : QString absoluteImportPath;
191 [ + - + - ]: 144 : QStringList importPaths = variantToStringList(m_configuration.value(u"importPaths"_s));
192 [ + - + - : 76 : for (QString &path : importPaths) {
+ + ]
193 [ + - ]: 4 : const QFileInfo fi(path);
194 [ + - + - : 8 : if (fi.isNativePath() && fi.isAbsolute() && absoluteImportPath.isEmpty())
- + - - ]
195 : 0 : absoluteImportPath = path;
196 [ + - + - : 8 : m_engine.addImportPath(toAbsoluteFilePath(path, am->baseDir()));
+ - ]
197 : 4 : }
198 : :
199 [ - + ]: 72 : if (!absoluteImportPath.isEmpty()) {
200 [ # # # # : 0 : qCWarning(LogDeployment).nospace() << "Absolute import path in the runtime configuration "
# # ]
201 [ # # # # : 0 : "can lead to problems inside containers (e.g. " << absoluteImportPath << ")";
# # ]
202 : : }
203 : :
204 [ + - + - ]: 72 : StartupTimer::instance()->checkpoint("after application config initialization");
205 : :
206 [ + - ]: 72 : if (directLoad.first.isEmpty()) {
207 [ + - ]: 72 : am->connectDBusInterfaces(true);
208 : :
209 : 72 : connect(am, &ApplicationMain::startApplication,
210 [ + - ]: 144 : this, &Controller::startApplication);
211 : : } else {
212 [ # # ]: 0 : QMetaObject::invokeMethod(this, [this, directLoad]() {
213 : 0 : PackageInfo *pi;
214 : 0 : try {
215 [ # # ]: 0 : pi = YamlPackageScanner().scan(directLoad.first);
216 [ - - ]: 0 : } catch (const Exception &e) {
217 [ - - - - : 0 : qCCritical(LogQmlRuntime) << "Could not parse info.yaml file:" << e.what();
- - - - -
- ]
218 [ - - ]: 0 : QCoreApplication::exit(20);
219 : 0 : return;
220 : 0 : }
221 : 0 : const auto apps = pi->applications();
222 [ # # ]: 0 : const ApplicationInfo *a = apps.constFirst();
223 [ # # ]: 0 : if (!directLoad.second.isEmpty()) {
224 [ # # ]: 0 : auto it = std::find_if(apps.cbegin(), apps.cend(),
225 [ # # ]: 0 : [appId = directLoad.second](ApplicationInfo *appInfo) -> bool {
226 : 0 : return (appInfo->id() == appId);
227 : : });
228 [ # # ]: 0 : if (it == apps.end()) {
229 [ # # # # : 0 : qCCritical(LogQmlRuntime) << "Could not find the requested application id"
# # # # ]
230 [ # # # # ]: 0 : << directLoad.second << "within the info.yaml file";
231 [ # # ]: 0 : QCoreApplication::exit(21);
232 : 0 : return;
233 : : }
234 : 0 : a = *it;
235 : : }
236 : :
237 [ # # # # : 0 : startApplication(QFileInfo(directLoad.first).absolutePath(), a->codeFilePath(),
# # ]
238 [ # # # # ]: 0 : QString(), QString(), a->toVariantMap(), QVariantMap());
239 : 0 : }, Qt::QueuedConnection);
240 : : }
241 : :
242 [ + - + - ]: 144 : const QString quicklaunchQml = m_configuration.value((u"quicklaunchQml"_s)).toString();
243 [ + + + - ]: 72 : if (!quicklaunchQml.isEmpty() && quickLaunched) {
244 [ + - + - : 4 : QQmlComponent quicklaunchComp(&m_engine, filePathToUrl(quicklaunchQml, am->baseDir()));
+ - ]
245 [ + - + - ]: 2 : if (!quicklaunchComp.isError()) {
246 [ + - ]: 2 : std::unique_ptr<QObject> quicklaunchInstance(quicklaunchComp.create());
247 : 2 : } else {
248 [ # # ]: 0 : const QList<QQmlError> errors = quicklaunchComp.errors();
249 [ # # ]: 0 : for (const QQmlError &error : errors)
250 [ # # # # : 0 : qCCritical(LogQmlRuntime) << error;
# # # # ]
251 : 0 : }
252 : 2 : }
253 : :
254 [ + - + - ]: 72 : StartupTimer::instance()->checkpoint("after application interface initialization");
255 : 72 : }
256 : :
257 : 69 : void Controller::startApplication(const QString &baseDir, const QString &qmlFile, const QString &document,
258 : : const QString &mimeType, const QVariantMap &application,
259 : : const QVariantMap &systemProperties)
260 : : {
261 [ + - ]: 69 : if (m_launched)
262 : 0 : return;
263 : 69 : m_launched = true;
264 : :
265 [ + - + - : 69 : static const QString applicationId = application.value(u"id"_s).toString();
+ - + - ]
266 : :
267 [ - + ]: 69 : if (applicationId.isEmpty()) {
268 [ # # # # ]: 0 : qCCritical(LogQmlRuntime) << "did not receive an application id";
269 : 0 : QCoreApplication::exit(2);
270 : 0 : return;
271 : : }
272 : :
273 : 69 : const QStringList applicationIdParts = applicationId.split(u'.');
274 [ + + ]: 69 : if (applicationIdParts.size() < 2) {
275 [ + - + - ]: 11 : Logging::setApplicationId(applicationId.toLocal8Bit());
276 [ + - ]: 11 : QCoreApplication::setApplicationName(applicationId);
277 : : } else {
278 : 58 : const QString appName = applicationIdParts.last();
279 : 58 : QString domainName;
280 : 58 : QString loggingId;
281 : :
282 [ + + ]: 172 : for (qsizetype i = 0; i < applicationIdParts.size() - 1; ++i) {
283 : : // reverse the reverse-DNS domain name
284 [ + + ]: 114 : if (!domainName.isEmpty())
285 [ + - ]: 56 : domainName.prepend(u'.');
286 [ + - ]: 114 : domainName.prepend(applicationIdParts.at(i));
287 : :
288 : : // shorten application id to make the debug output more readable
289 [ + - ]: 114 : loggingId.append(applicationIdParts.at(i).at(0));
290 [ + - ]: 114 : loggingId.append(u'.');
291 : : }
292 [ + - ]: 58 : loggingId.append(applicationIdParts.last());
293 [ + - + - ]: 58 : Logging::setApplicationId(loggingId.toLocal8Bit());
294 : :
295 : : // we need these to be unique, because other libraries (e.g. QtInsights) expect them to be
296 [ + - ]: 58 : QCoreApplication::setApplicationName(appName);
297 [ + - ]: 58 : QCoreApplication::setOrganizationName(domainName);
298 [ + - ]: 58 : QCoreApplication::setOrganizationDomain(domainName);
299 : 58 : }
300 [ + - + - : 138 : if (const auto version = application.value(u"version"_s).toString(); !version.isEmpty())
+ + ]
301 [ + - ]: 16 : QCoreApplication::setApplicationVersion(version);
302 : :
303 [ + - + - : 69 : if (Logging::isDltAvailable() && Logging::isDltEnabled()) {
+ - - + ]
304 : : // Change the DLT Application description, to easily identify the application on the DLT logs.
305 [ # # # # ]: 0 : const QVariantMap dlt = qdbus_cast<QVariantMap>(application.value(u"dlt"_s));
306 [ # # # # : 0 : QByteArray dltId = dlt.value(u"id"_s).toString().toLocal8Bit();
# # ]
307 [ # # # # : 0 : QByteArray dltDescription = dlt.value(u"description"_s).toString().toLocal8Bit();
# # ]
308 : :
309 [ # # ]: 0 : if (dltId.isEmpty()) {
310 [ # # # # : 0 : qCCritical(LogQmlRuntime) << "did not receive a DLT id, but DLT is enabled";
# # # # ]
311 [ # # ]: 0 : QCoreApplication::exit(2);
312 : 0 : return;
313 : : }
314 [ # # ]: 0 : if (dltDescription.isEmpty())
315 [ # # # # : 0 : dltDescription = QByteArray("Qt Application Manager App: ") + applicationId.toLocal8Bit();
# # ]
316 : :
317 [ # # ]: 0 : Logging::setDltApplicationId(dltId, dltDescription);
318 [ # # ]: 0 : Logging::registerUnregisteredDltContexts();
319 : 0 : }
320 : : // Dress up the ps output to make it easier to correlate all the launcher processes
321 [ + - + - ]: 138 : ProcessTitle::augmentCommand(applicationId.toLocal8Bit().constData());
322 : :
323 [ + - ]: 69 : auto *am = ApplicationMain::instance();
324 [ + - + - : 138 : am->setApplication(convertFromDBusVariant(application).toMap());
+ - ]
325 [ + - + - : 138 : am->setSystemProperties(convertFromDBusVariant(systemProperties).toMap());
+ - ]
326 : :
327 [ + + ]: 69 : if (m_quickLaunched) {
328 : : //StartupTimer::instance()->createReport(applicationId + u" [process launch]"_s);
329 [ + - + - ]: 6 : StartupTimer::instance()->reset();
330 : : } else {
331 [ + - + - ]: 63 : StartupTimer::instance()->checkpoint("starting application");
332 : : }
333 : :
334 [ + - + - ]: 138 : QVariantMap runtimeParameters = qdbus_cast<QVariantMap>(application.value(u"runtimeParameters"_s));
335 : :
336 [ + - - - : 138 : qCDebug(LogQmlRuntime) << "loading" << applicationId << "- main:" << qmlFile << "- document:" << document
- - - - -
- - - - -
- - - + ]
337 [ # # # # : 0 : << "- mimeType:" << mimeType << "- parameters:" << runtimeParameters
# # # # ]
338 [ # # # # ]: 0 : << "- baseDir:" << baseDir;
339 : :
340 [ + - - + ]: 69 : if (!QDir::setCurrent(baseDir)) {
341 [ # # # # : 0 : qCCritical(LogQmlRuntime) << "could not set the current directory to" << baseDir;
# # # # #
# ]
342 [ # # ]: 0 : QCoreApplication::exit(2);
343 : : return;
344 : : }
345 : :
346 [ + - ]: 138 : QVariant resVar = runtimeParameters.value(u"resources"_s);
347 [ + - ]: 69 : const QVariantList resources = (resVar.metaType() == QMetaType::fromType<QString>())
348 : 1 : ? QVariantList{resVar}
349 [ + + + - : 69 : : qdbus_cast<QVariantList>(resVar);
- + + - ]
350 : :
351 [ + + ]: 72 : for (const QVariant &resource : resources) {
352 : 3 : try {
353 [ + - + - ]: 6 : loadResource(resource.toString());
354 [ - - ]: 0 : } catch (const Exception &e) {
355 [ - - - - : 0 : qCWarning(LogSystem).noquote() << e.errorString();
- - - - ]
356 : 0 : }
357 : : }
358 : :
359 [ + - ]: 69 : const QUrl qmlFileUrl = filePathToUrl(qmlFile, baseDir);
360 [ + - ]: 69 : const QString qmlFileStr = urlToLocalFilePath(qmlFileUrl);
361 : :
362 [ + - - + ]: 69 : if (!QFile::exists(qmlFileStr)) {
363 [ # # # # : 0 : qCCritical(LogQmlRuntime) << "could not load" << qmlFile << ": file does not exist";
# # # # #
# # # ]
364 [ # # ]: 0 : QCoreApplication::exit(2);
365 : : return;
366 : : }
367 : :
368 [ + - ]: 69 : connect(ApplicationMain::instance(), &ApplicationMain::slowAnimationsChanged,
369 [ + - ]: 69 : this, &Controller::updateSlowAnimations);
370 [ + - + - : 69 : updateSlowAnimations(ApplicationMain::instance()->slowAnimations());
+ - ]
371 : :
372 : : // we need to catch all show events to apply the slow-animations
373 [ + - ]: 69 : QCoreApplication::instance()->installEventFilter(this);
374 : :
375 : 69 : QStringList systemStartupPluginPaths;
376 [ + - + - : 138 : const QDir systemStartupPluginDir(QLibraryInfo::path(QLibraryInfo::PluginsPath) + QDir::separator() + u"appman_startup"_s);
+ - + - ]
377 [ + - + - : 69 : for (const auto &pluginName : systemStartupPluginDir.entryList(QDir::Files | QDir::NoDotAndDotDot)) {
+ - - + ]
378 [ # # ]: 0 : const QString filePath = systemStartupPluginDir.absoluteFilePath(pluginName);
379 [ # # # # ]: 0 : if (!QLibrary::isLibrary(filePath))
380 : 0 : continue;
381 [ # # ]: 0 : systemStartupPluginPaths += filePath;
382 : 0 : }
383 : :
384 [ + - + - : 138 : QStringList startupPluginFiles = variantToStringList(m_configuration.value(u"plugins"_s).toMap().value(u"startup"_s));
+ - + - ]
385 : 69 : QVector<StartupInterface *> startupPlugins;
386 : 69 : try {
387 [ + - + - ]: 138 : startupPlugins = loadPlugins<StartupInterface>("startup", systemStartupPluginPaths + startupPluginFiles);
388 [ - - ]: 0 : } catch (const Exception &e) {
389 [ - - - - : 0 : qCCritical(LogQmlRuntime) << qPrintable(e.errorString());
- - - - -
- ]
390 [ - - ]: 0 : QCoreApplication::exit(2);
391 : 0 : return;
392 : 0 : }
393 [ - + ]: 69 : for (StartupInterface *iface : std::as_const(startupPlugins))
394 [ # # # # ]: 0 : iface->initialize(am->systemProperties());
395 : :
396 [ + - + - ]: 138 : bool loadDummyData = runtimeParameters.value(u"loadDummyData"_s).toBool()
397 [ + - + - : 207 : || m_configuration.value(u"loadDummydata"_s).toBool();
+ - - + +
- + - + -
- - - - -
- - - ]
398 : :
399 [ - + ]: 69 : if (loadDummyData) {
400 [ # # # # : 0 : qCWarning(LogDeployment) << "Loading dummy data is deprecated and will be removed soon";
# # # # ]
401 [ # # # # : 0 : qCDebug(LogQmlRuntime) << "loading dummy-data";
# # # # ]
402 [ # # # # : 0 : loadQmlDummyDataFiles(&m_engine, QFileInfo(qmlFileStr).path());
# # ]
403 : : }
404 : :
405 [ + - ]: 138 : QVariant pluginPaths = runtimeParameters.value(u"pluginPaths"_s);
406 [ + - ]: 69 : const QVariantList ppvl = (pluginPaths.metaType() == QMetaType::fromType<QString>())
407 : 0 : ? QVariantList{pluginPaths}
408 [ - + - - : 69 : : qdbus_cast<QVariantList>(pluginPaths);
- - + - ]
409 : :
410 [ - + ]: 69 : for (const QVariant &v : ppvl) {
411 [ # # ]: 0 : const QString path = v.toString();
412 [ # # # # : 0 : if (QFileInfo(path).isRelative())
# # ]
413 [ # # # # : 0 : qApp->addLibraryPath(QDir().absoluteFilePath(path));
# # ]
414 : : else
415 [ # # # # : 0 : qCWarning(LogQmlRuntime) << "Omitting absolute plugin path in info file for safety reasons:" << path;
# # # # #
# ]
416 : 0 : }
417 [ + - - - : 138 : qCDebug(LogQmlRuntime) << "Plugin paths:" << qApp->libraryPaths();
- - - - -
- - + ]
418 : :
419 [ + - ]: 138 : QVariant imports = runtimeParameters.value(u"importPaths"_s);
420 [ + - ]: 69 : const QVariantList ipvl = (imports.metaType() == QMetaType::fromType<QString>())
421 : 2 : ? QVariantList{imports}
422 [ + + + - : 69 : : qdbus_cast<QVariantList>(imports);
- + + - ]
423 : :
424 [ + + ]: 76 : for (const QVariant &v : ipvl) {
425 [ + - ]: 7 : const QString path = v.toString();
426 [ + - ]: 7 : const QFileInfo fi(path);
427 [ + - + - : 14 : if (!(fi.isNativePath() && fi.isAbsolute()))
- + ]
428 [ + - + - : 14 : m_engine.addImportPath(toAbsoluteFilePath(path));
+ - ]
429 : : else
430 [ # # # # : 0 : qCWarning(LogQmlRuntime) << "Omitting absolute import path in info file for safety reasons:" << path;
# # # # #
# ]
431 : 7 : }
432 [ + - - - : 138 : qCDebug(LogQmlRuntime) << "Qml import paths:" << m_engine.importPathList();
- - - - -
- - + ]
433 : :
434 [ - + ]: 69 : for (StartupInterface *iface : std::as_const(startupPlugins))
435 [ # # ]: 0 : iface->beforeQmlEngineLoad(&m_engine);
436 : :
437 [ + - + - ]: 69 : StartupTimer::instance()->checkpoint("after loading plugins and import paths");
438 : :
439 : : // protect our namespace from this point onward
440 [ + - ]: 69 : qmlProtectModule("QtApplicationManager", 1);
441 [ + - ]: 69 : qmlProtectModule("QtApplicationManager.Application", 1);
442 [ + - ]: 69 : qmlProtectModule("QtApplicationManager", 2);
443 [ + - ]: 69 : qmlProtectModule("QtApplicationManager.Application", 2);
444 : :
445 [ + - ]: 69 : m_engine.load(qmlFileUrl);
446 : :
447 [ + - + - ]: 69 : StartupTimer::instance()->checkpoint("after engine loading main qml file");
448 : :
449 [ + - ]: 69 : auto topLevels = m_engine.rootObjects();
450 : :
451 [ + - ]: 69 : if (Q_UNLIKELY(topLevels.isEmpty() || !topLevels.at(0))) {
452 [ # # # # : 0 : qCCritical(LogSystem) << "Failed to load component" << qmlFile << ": no root object";
# # # # #
# # # ]
453 [ # # ]: 0 : QCoreApplication::exit(3);
454 : 0 : return;
455 : : }
456 : :
457 [ - + ]: 69 : for (StartupInterface *iface : std::as_const(startupPlugins))
458 [ # # ]: 0 : iface->afterQmlEngineLoad(&m_engine);
459 : :
460 [ + - ]: 69 : QObject *topLevel = topLevels.at(0);
461 [ + - + + ]: 69 : if (auto amw = qobject_cast<ApplicationManagerWindow*>(topLevel)) {
462 [ + - + - ]: 55 : m_window = qobject_cast<QQuickWindow*>(amw->backingObject());
463 : : } else {
464 [ + - ]: 14 : m_window = qobject_cast<QQuickWindow *>(topLevel);
465 [ + + ]: 14 : if (!m_window) {
466 [ + - ]: 12 : QQuickItem *contentItem = qobject_cast<QQuickItem *>(topLevel);
467 : 2 : if (contentItem) {
468 [ + - + - ]: 2 : QQuickView* view = new QQuickView(&m_engine, nullptr);
469 : 2 : m_window = view;
470 [ + - ]: 2 : view->setContent(qmlFileUrl, nullptr, topLevel);
471 [ + - + - ]: 2 : view->setVisible(contentItem->isVisible());
472 [ + - ]: 2 : connect(contentItem, &QQuickItem::visibleChanged, this, [view, contentItem]() {
473 : 0 : view->setVisible(contentItem->isVisible());
474 : 0 : });
475 : : }
476 : : }
477 : : }
478 : :
479 [ + - + - ]: 69 : StartupTimer::instance()->checkpoint("after creating and setting application window");
480 : :
481 : : // needed, even though we do not explicitly show() the window any more
482 [ - + ]: 69 : for (StartupInterface *iface : std::as_const(startupPlugins))
483 [ # # ]: 0 : iface->beforeWindowShow(m_window);
484 : :
485 [ + - - - : 138 : qCDebug(LogQmlRuntime) << "component loading and creating complete.";
- - - + ]
486 : :
487 [ + - + - ]: 69 : StartupTimer::instance()->checkpoint("component loading and creating complete.");
488 : :
489 [ + + ]: 69 : if (m_window) {
490 : 59 : Q_ASSERT(m_engine.incubationController());
491 : :
492 : : // not sure if this is needed .. or even the best thing to do ... see connects above, they seem to work better
493 [ + - ]: 59 : QObject::connect(&m_engine, &QQmlEngine::quit, m_window, &QObject::deleteLater);
494 : :
495 : : // create the startup report on first frame drawn
496 : 118 : static QMetaObject::Connection conn = QObject::connect(m_window, &QQuickWindow::frameSwapped,
497 : 58 : this, [this, startupPlugins]() {
498 : : // this is a queued signal, so there may be still one in the queue after calling disconnect()
499 [ + - ]: 57 : if (conn) {
500 : 57 : QObject::disconnect(conn);
501 : :
502 : 57 : auto st = StartupTimer::instance();
503 : 57 : st->checkFirstFrame();
504 : 57 : st->createAutomaticReport(applicationId);
505 : :
506 [ - + ]: 57 : for (StartupInterface *iface : std::as_const(startupPlugins))
507 : 0 : iface->afterWindowShow(m_window);
508 : : }
509 [ + - + - : 59 : });
+ - ]
510 : : } else {
511 [ + - + - ]: 10 : StartupTimer::instance()->createAutomaticReport(applicationId);
512 : : }
513 : :
514 [ + + ]: 69 : if (!document.isEmpty())
515 [ + - ]: 17 : emit am->openDocument(document, mimeType);
516 : 69 : }
517 : :
518 : 3789 : bool Controller::eventFilter(QObject *o, QEvent *e)
519 : : {
520 [ + - + + : 3865 : if (e && (e->type() == QEvent::Show) && qobject_cast<QQuickWindow *>(o)) {
+ - ]
521 : 76 : auto window = static_cast<QQuickWindow *>(o);
522 [ + - + - ]: 228 : m_allWindows.append(window);
523 : 76 : updateSlowAnimationsForWindow(window);
524 : : }
525 : 3789 : return QObject::eventFilter(o, e);
526 : : }
527 : :
528 : 76 : void Controller::updateSlowAnimationsForWindow(QQuickWindow *window)
529 : : {
530 : : // QUnifiedTimer are thread-local. To also slow down animations running in the SG thread
531 : : // we need to enable the slow mode in this timer as well.
532 [ + - ]: 76 : QMetaObject::Connection *connection = new QMetaObject::Connection;
533 : 76 : *connection = connect(window, &QQuickWindow::beforeRendering,
534 : 222 : this, [connection] {
535 [ + - + - ]: 74 : if (connection && *connection) {
536 : : #if defined(Q_CC_MSVC)
537 : : qApp->disconnect(*connection); // MSVC cannot distinguish between static and non-static overloads in lambdas
538 : : #else
539 : 74 : QObject::disconnect(*connection);
540 : : #endif
541 [ + - ]: 74 : QUnifiedTimer::instance()->setSlowModeEnabled(ApplicationMain::instance()->slowAnimations());
542 [ + - ]: 74 : delete connection;
543 : : }
544 : 76 : }, Qt::DirectConnection);
545 : 76 : }
546 : :
547 : 69 : void Controller::updateSlowAnimations(bool isSlow)
548 : : {
549 : 69 : QUnifiedTimer::instance()->setSlowModeEnabled(isSlow);
550 : :
551 [ - + ]: 69 : for (auto it = m_allWindows.cbegin(); it != m_allWindows.cend(); ) {
552 [ # # ]: 0 : QPointer<QQuickWindow> window = *it;
553 [ # # ]: 0 : if (!window) {
554 [ # # ]: 0 : m_allWindows.erase(it);
555 : : } else {
556 [ # # ]: 0 : updateSlowAnimationsForWindow(window.data());
557 : 0 : ++it;
558 : : }
559 : 0 : }
560 : 69 : }
561 : :
562 : :
563 : : #include "moc_launcher-qml_p.cpp"
|