1// Copyright (C) 2016 Research In Motion.
2// Copyright (C) 2019 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
4
5#include "conf.h"
6
7#include <QCoreApplication>
8
9#ifdef QT_GUI_LIB
10#include <QGuiApplication>
11#include <QWindow>
12#include <QFileOpenEvent>
13#include <QSurfaceFormat>
14#ifdef QT_WIDGETS_LIB
15#include <QApplication>
16#endif // QT_WIDGETS_LIB
17#endif // QT_GUI_LIB
18
19#include <QQmlApplicationEngine>
20#include <QQmlComponent>
21#include <QCommandLineOption>
22#include <QCommandLineParser>
23#include <QDir>
24#include <QFile>
25#include <QFileInfo>
26#include <QLoggingCategory>
27#include <QStringList>
28#include <QScopedPointer>
29#include <QDebug>
30#include <QStandardPaths>
31#include <QTranslator>
32#include <QtGlobal>
33#include <QLibraryInfo>
34#include <qqml.h>
35#include <qqmldebug.h>
36#include <qqmlfileselector.h>
37
38#include <private/qtqmlglobal_p.h>
39#include <private/qqmlimport_p.h>
40#if QT_CONFIG(qml_animation)
41#include <private/qabstractanimation_p.h>
42#endif
43
44#include <cstdio>
45#include <cstring>
46#include <cstdlib>
47#include <memory>
48
49#define FILE_OPEN_EVENT_WAIT_TIME 3000 // ms
50
51Q_LOGGING_CATEGORY(lcDeprecated, "qt.tools.qml.deprecated")
52
53enum QmlApplicationType {
54 QmlApplicationTypeUnknown
55 , QmlApplicationTypeCore
56#ifdef QT_GUI_LIB
57 , QmlApplicationTypeGui
58#ifdef QT_WIDGETS_LIB
59 , QmlApplicationTypeWidget
60#endif // QT_WIDGETS_LIB
61#endif // QT_GUI_LIB
62};
63
64static QmlApplicationType applicationType =
65#ifndef QT_GUI_LIB
66 QmlApplicationTypeCore;
67#else
68 QmlApplicationTypeGui;
69#endif // QT_GUI_LIB
70
71static Config *conf = nullptr;
72static QQmlApplicationEngine *qae = nullptr;
73#if defined(Q_OS_DARWIN) || defined(QT_GUI_LIB)
74static int exitTimerId = -1;
75#endif
76static const QString iconResourcePath(QStringLiteral(":/qt-project.org/imports/QmlRuntime/Config/resources/qml-64.png"));
77static const QString confResourcePath(QStringLiteral(":/qt-project.org/imports/QmlRuntime/Config/"));
78static const QString customConfFileName(QStringLiteral("configuration.qml"));
79static bool verboseMode = false;
80static bool quietMode = false;
81static bool glShareContexts = true;
82static bool disableShaderCache = true;
83#if defined(QT_GUI_LIB)
84static bool requestAlphaChannel = false;
85static bool requestMSAA = false;
86static bool requestCoreProfile = false;
87#endif
88
89static void loadConf(const QString &override, bool quiet) // Terminates app on failure
90{
91 const QString defaultFileName = QLatin1String("default.qml");
92 QUrl settingsUrl;
93 bool builtIn = false; //just for keeping track of the warning
94 if (override.isEmpty()) {
95 QFileInfo fi;
96 fi.setFile(QStandardPaths::locate(type: QStandardPaths::AppDataLocation, fileName: defaultFileName));
97 if (fi.exists()) {
98 settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
99 } else {
100 // ### If different built-in configs are needed per-platform, just apply QFileSelector to the qrc conf.qml path
101 fi.setFile(confResourcePath + defaultFileName);
102 settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
103 builtIn = true;
104 }
105 } else {
106 QFileInfo fi;
107 fi.setFile(confResourcePath + override + QLatin1String(".qml"));
108 if (fi.exists()) {
109 settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
110 builtIn = true;
111 } else {
112 fi.setFile(dir: QDir(QStandardPaths::locate(type: QStandardPaths::AppConfigLocation, fileName: override, options: QStandardPaths::LocateDirectory)), file: customConfFileName);
113 if (fi.exists())
114 settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
115 else
116 fi.setFile(override);
117 if (!fi.exists()) {
118 printf(format: "qml: Couldn't find required configuration file: %s\n",
119 qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())));
120 exit(status: 1);
121 }
122 settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
123 }
124 }
125
126 if (!quiet) {
127 printf(format: "qml: %s\n", QLibraryInfo::build());
128 if (builtIn) {
129 printf(format: "qml: Using built-in configuration: %s\n",
130 qPrintable(override.isEmpty() ? defaultFileName : override));
131 } else {
132 printf(format: "qml: Using configuration: %s\n",
133 qPrintable(settingsUrl.isLocalFile()
134 ? QDir::toNativeSeparators(settingsUrl.toLocalFile())
135 : settingsUrl.toString()));
136 }
137 }
138
139 // TODO: When we have better engine control, ban QtQuick* imports on this engine
140 QQmlEngine e2;
141 QQmlComponent c2(&e2, settingsUrl);
142 conf = qobject_cast<Config*>(object: c2.create());
143
144 if (!conf){
145 printf(format: "qml: Error loading configuration file: %s\n", qPrintable(c2.errorString()));
146 exit(status: 1);
147 }
148}
149
150void noFilesGiven()
151{
152 if (!quietMode)
153 printf(format: "qml: No files specified. Terminating.\n");
154 exit(status: 1);
155}
156
157static void listConfFiles()
158{
159 const QDir confResourceDir(confResourcePath);
160 printf(format: "%s\n", qPrintable(QCoreApplication::translate("main", "Built-in configurations:")));
161 for (const QFileInfo &fi : confResourceDir.entryInfoList(filters: QDir::Files)) {
162 if (fi.completeSuffix() != QLatin1String("qml"))
163 continue;
164
165 const QString baseName = fi.baseName();
166 if (baseName.isEmpty() || baseName[0].isUpper())
167 continue;
168
169 printf(format: " %s\n", qPrintable(baseName));
170 }
171 printf(format: "%s\n", qPrintable(QCoreApplication::translate("main", "Other configurations:")));
172 bool foundOther = false;
173 const QStringList otherLocations = QStandardPaths::standardLocations(type: QStandardPaths::AppConfigLocation);
174 for (const auto &confDirPath : otherLocations) {
175 const QDir confDir(confDirPath);
176 for (const QFileInfo &fi : confDir.entryInfoList(filters: QDir::Dirs | QDir::NoDotAndDotDot)) {
177 foundOther = true;
178 if (verboseMode)
179 printf(format: " %s\n", qPrintable(fi.absoluteFilePath()));
180 else
181 printf(format: " %s\n", qPrintable(fi.baseName()));
182 }
183 }
184 if (!foundOther)
185 printf(format: " %s\n", qPrintable(QCoreApplication::translate("main", "none")));
186 if (verboseMode) {
187 printf(format: "%s\n", qPrintable(QCoreApplication::translate("main", "Checked in:")));
188 for (const auto &confDirPath : otherLocations)
189 printf(format: " %s\n", qPrintable(confDirPath));
190 }
191 exit(status: 0);
192}
193
194#ifdef QT_GUI_LIB
195
196// Loads qml after receiving a QFileOpenEvent
197class LoaderApplication : public QGuiApplication
198{
199public:
200 LoaderApplication(int& argc, char **argv) : QGuiApplication(argc, argv)
201 {
202 setWindowIcon(QIcon(iconResourcePath));
203 }
204
205 bool event(QEvent *ev) override
206 {
207 if (ev->type() == QEvent::FileOpen) {
208 if (exitTimerId >= 0) {
209 killTimer(id: exitTimerId);
210 exitTimerId = -1;
211 }
212 qae->load(url: static_cast<QFileOpenEvent *>(ev)->url());
213 }
214 else
215 return QGuiApplication::event(ev);
216 return true;
217 }
218
219 void timerEvent(QTimerEvent *) override {
220 noFilesGiven();
221 }
222};
223
224#endif // QT_GUI_LIB
225
226// Listens to the appEngine signals to determine if all files failed to load
227class LoadWatcher : public QObject
228{
229 Q_OBJECT
230public:
231 LoadWatcher(QQmlApplicationEngine *e, int expected)
232 : QObject(e)
233 , expectedFileCount(expected)
234 {
235 connect(sender: e, signal: &QQmlApplicationEngine::objectCreated, context: this, slot: &LoadWatcher::checkFinished);
236 // QQmlApplicationEngine also connects quit() to QCoreApplication::quit
237 // and exit() to QCoreApplication::exit but if called before exec()
238 // then QCoreApplication::quit or QCoreApplication::exit does nothing
239 connect(sender: e, signal: &QQmlEngine::quit, context: this, slot: &LoadWatcher::quit);
240 connect(sender: e, signal: &QQmlEngine::exit, context: this, slot: &LoadWatcher::exit);
241 }
242
243 int returnCode = 0;
244 bool earlyExit = false;
245
246public Q_SLOTS:
247 void checkFinished(QObject *o, const QUrl &url)
248 {
249 Q_UNUSED(url);
250 if (o) {
251 ++createdObjects;
252 if (conf && qae)
253 for (PartialScene *ps : std::as_const(t&: conf->completers))
254 if (o->inherits(classname: ps->itemType().toUtf8().constData()))
255 contain(o, containPath: ps->container());
256 }
257
258 if (!--expectedFileCount && !createdObjects) {
259 printf(format: "qml: Did not load any objects, exiting.\n");
260 exit(retCode: 2);
261 QCoreApplication::exit(retcode: 2);
262 }
263 }
264
265 void quit() {
266 // Will be checked before calling exec()
267 earlyExit = true;
268 returnCode = 0;
269 }
270 void exit(int retCode) {
271 earlyExit = true;
272 returnCode = retCode;
273 }
274
275private:
276 void contain(QObject *o, const QUrl &containPath);
277
278private:
279 int expectedFileCount;
280 int createdObjects = 0;
281};
282
283void LoadWatcher::contain(QObject *o, const QUrl &containPath)
284{
285 QQmlComponent c(qae, containPath);
286 QObject *o2 = c.create();
287 if (!o2)
288 return;
289 o2->setParent(this);
290 bool success = false;
291 int idx;
292 if ((idx = o2->metaObject()->indexOfProperty(name: "containedObject")) != -1)
293 success = o2->metaObject()->property(index: idx).write(obj: o2, value: QVariant::fromValue<QObject*>(value: o));
294 if (!success)
295 o->setParent(o2); // Set QObject parent, and assume container will react as needed
296}
297
298void quietMessageHandler(QtMsgType type, const QMessageLogContext &ctxt, const QString &msg)
299{
300 Q_UNUSED(ctxt);
301 Q_UNUSED(msg);
302 // Doesn't print anything
303 switch (type) {
304 case QtFatalMsg:
305 exit(status: -1);
306 case QtCriticalMsg:
307 case QtDebugMsg:
308 case QtInfoMsg:
309 case QtWarningMsg:
310 ;
311 }
312}
313
314// Called before application initialization
315static void getAppFlags(int argc, char **argv)
316{
317#ifdef QT_GUI_LIB
318 for (int i=0; i<argc; i++) {
319 if (!strcmp(s1: argv[i], s2: "--apptype") || !strcmp(s1: argv[i], s2: "-a") || !strcmp(s1: argv[i], s2: "-apptype")) {
320 applicationType = QmlApplicationTypeUnknown;
321 if (i+1 < argc) {
322 ++i;
323 if (!strcmp(s1: argv[i], s2: "core"))
324 applicationType = QmlApplicationTypeCore;
325 else if (!strcmp(s1: argv[i], s2: "gui"))
326 applicationType = QmlApplicationTypeGui;
327# ifdef QT_WIDGETS_LIB
328 else if (!strcmp(s1: argv[i], s2: "widget"))
329 applicationType = QmlApplicationTypeWidget;
330# endif // QT_WIDGETS_LIB
331
332 }
333 } else if (!strcmp(s1: argv[i], s2: "-desktop") || !strcmp(s1: argv[i], s2: "--desktop")) {
334 QCoreApplication::setAttribute(attribute: Qt::AA_UseDesktopOpenGL);
335 } else if (!strcmp(s1: argv[i], s2: "-gles") || !strcmp(s1: argv[i], s2: "--gles")) {
336 QCoreApplication::setAttribute(attribute: Qt::AA_UseOpenGLES);
337 } else if (!strcmp(s1: argv[i], s2: "-software") || !strcmp(s1: argv[i], s2: "--software")) {
338 QCoreApplication::setAttribute(attribute: Qt::AA_UseSoftwareOpenGL);
339 } else if (!strcmp(s1: argv[i], s2: "-disable-context-sharing") || !strcmp(s1: argv[i], s2: "--disable-context-sharing")) {
340 glShareContexts = false;
341 } else if (!strcmp(s1: argv[i], s2: "-enable-shader-cache") || !strcmp(s1: argv[i], s2: "--enable-shader-cache")) {
342 disableShaderCache = false;
343 } else if (!strcmp(s1: argv[i], s2: "-transparent") || !strcmp(s1: argv[i], s2: "--transparent")) {
344 requestAlphaChannel = true;
345 } else if (!strcmp(s1: argv[i], s2: "-multisample") || !strcmp(s1: argv[i], s2: "--multisample")) {
346 requestMSAA = true;
347 } else if (!strcmp(s1: argv[i], s2: "-core-profile") || !strcmp(s1: argv[i], s2: "--core-profile")) {
348 requestCoreProfile = true;
349 }
350 }
351#else
352 Q_UNUSED(argc);
353 Q_UNUSED(argv);
354#endif // QT_GUI_LIB
355}
356
357#if QT_DEPRECATED_SINCE(6, 3)
358static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory)
359{
360 QDir dir(directory+"/dummydata", "*.qml");
361 QStringList list = dir.entryList();
362 for (int i = 0; i < list.size(); ++i) {
363 QString qml = list.at(i);
364 QQmlComponent comp(&engine, dir.filePath(fileName: qml));
365 QObject *dummyData = comp.create();
366
367 if (comp.isError()) {
368 const QList<QQmlError> errors = comp.errors();
369 for (const QQmlError &error : errors)
370 qWarning() << error;
371 }
372
373 if (dummyData && !quietMode) {
374 printf(format: "qml: Loaded dummy data: %s\n", qPrintable(dir.filePath(qml)));
375 qml.truncate(pos: qml.size()-4);
376 engine.rootContext()->setContextProperty(qml, dummyData);
377 dummyData->setParent(&engine);
378 }
379 }
380}
381#endif
382
383int main(int argc, char *argv[])
384{
385 getAppFlags(argc, argv);
386
387 // Must set the default QSurfaceFormat before creating the app object if
388 // AA_ShareOpenGLContexts is going to be set.
389#if defined(QT_GUI_LIB)
390 QSurfaceFormat surfaceFormat;
391 surfaceFormat.setDepthBufferSize(24);
392 surfaceFormat.setStencilBufferSize(8);
393 if (requestMSAA)
394 surfaceFormat.setSamples(4);
395 if (requestAlphaChannel)
396 surfaceFormat.setAlphaBufferSize(8);
397 if (qEnvironmentVariableIsSet(varName: "QSG_CORE_PROFILE")
398 || qEnvironmentVariableIsSet(varName: "QML_CORE_PROFILE")
399 || requestCoreProfile)
400 {
401 // intentionally requesting 4.1 core to play nice with macOS
402 surfaceFormat.setVersion(major: 4, minor: 1);
403 surfaceFormat.setProfile(QSurfaceFormat::CoreProfile);
404 }
405 QSurfaceFormat::setDefaultFormat(surfaceFormat);
406#endif
407
408 if (glShareContexts)
409 QCoreApplication::setAttribute(attribute: Qt::AA_ShareOpenGLContexts);
410 if (disableShaderCache)
411 QCoreApplication::setAttribute(attribute: Qt::AA_DisableShaderDiskCache);
412
413 std::unique_ptr<QCoreApplication> app;
414 switch (applicationType) {
415#ifdef QT_GUI_LIB
416 case QmlApplicationTypeGui:
417 app = std::make_unique<LoaderApplication>(args&: argc, args&: argv);
418 break;
419#ifdef QT_WIDGETS_LIB
420 case QmlApplicationTypeWidget:
421 app = std::make_unique<QApplication>(args&: argc, args&: argv);
422 static_cast<QApplication *>(app.get())->setWindowIcon(QIcon(iconResourcePath));
423 break;
424#endif // QT_WIDGETS_LIB
425#endif // QT_GUI_LIB
426 case QmlApplicationTypeCore:
427 Q_FALLTHROUGH();
428 default: // QmlApplicationTypeUnknown: not allowed, but we'll exit after checking apptypeOption below
429 app = std::make_unique<QCoreApplication>(args&: argc, args&: argv);
430 break;
431 }
432
433 app->setApplicationName("Qml Runtime");
434 app->setOrganizationName("QtProject");
435 app->setOrganizationDomain("qt-project.org");
436 QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
437
438 QStringList files;
439 QString confFile;
440 QString translationFile;
441
442 // Handle main arguments
443 QCommandLineParser parser;
444 parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
445 parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsPositionalArguments);
446 parser.addHelpOption();
447 parser.addVersionOption();
448#ifdef QT_GUI_LIB
449 QCommandLineOption apptypeOption(QStringList() << QStringLiteral("a") << QStringLiteral("apptype"),
450 QCoreApplication::translate(context: "main", key: "Select which application class to use. Default is gui."),
451#ifdef QT_WIDGETS_LIB
452 QStringLiteral("core|gui|widget"));
453#else
454 QStringLiteral("core|gui"));
455#endif // QT_WIDGETS_LIB
456 parser.addOption(commandLineOption: apptypeOption); // Just for the help text... we've already handled this argument above
457#endif // QT_GUI_LIB
458 QCommandLineOption importOption(QStringLiteral("I"),
459 QCoreApplication::translate(context: "main", key: "Prepend the given path to the import paths."), QStringLiteral("path"));
460 parser.addOption(commandLineOption: importOption);
461 QCommandLineOption qmlFileOption(QStringLiteral("f"),
462 QCoreApplication::translate(context: "main", key: "Load the given file as a QML file."), QStringLiteral("file"));
463 parser.addOption(commandLineOption: qmlFileOption);
464 QCommandLineOption configOption(QStringList() << QStringLiteral("c") << QStringLiteral("config"),
465 QCoreApplication::translate(context: "main", key: "Load the given built-in configuration or configuration file."), QStringLiteral("file"));
466 parser.addOption(commandLineOption: configOption);
467 QCommandLineOption listConfOption(QStringList() << QStringLiteral("list-conf"),
468 QCoreApplication::translate(context: "main", key: "List the built-in configurations."));
469 parser.addOption(commandLineOption: listConfOption);
470 QCommandLineOption translationOption(QStringLiteral("translation"),
471 QCoreApplication::translate(context: "main", key: "Load the given file as the translations file."), QStringLiteral("file"));
472 parser.addOption(commandLineOption: translationOption);
473#if QT_DEPRECATED_SINCE(6, 3)
474 QCommandLineOption dummyDataOption(QStringLiteral("dummy-data"),
475 QCoreApplication::translate(context: "main", key: "Load QML files from the given directory as context properties. (deprecated)"), QStringLiteral("file"));
476 parser.addOption(commandLineOption: dummyDataOption);
477#endif
478#ifdef QT_GUI_LIB
479 // OpenGL options
480 QCommandLineOption glDesktopOption(QStringLiteral("desktop"),
481 QCoreApplication::translate(context: "main", key: "Force use of desktop OpenGL (AA_UseDesktopOpenGL)."));
482 parser.addOption(commandLineOption: glDesktopOption); // Just for the help text... we've already handled this argument above
483 QCommandLineOption glEsOption(QStringLiteral("gles"),
484 QCoreApplication::translate(context: "main", key: "Force use of GLES (AA_UseOpenGLES)."));
485 parser.addOption(commandLineOption: glEsOption); // Just for the help text... we've already handled this argument above
486 QCommandLineOption glSoftwareOption(QStringLiteral("software"),
487 QCoreApplication::translate(context: "main", key: "Force use of software rendering (AA_UseSoftwareOpenGL)."));
488 parser.addOption(commandLineOption: glSoftwareOption); // Just for the help text... we've already handled this argument above
489 QCommandLineOption glCoreProfile(QStringLiteral("core-profile"),
490 QCoreApplication::translate(context: "main", key: "Force use of OpenGL Core Profile."));
491 parser.addOption(commandLineOption: glCoreProfile); // Just for the help text... we've already handled this argument above
492 QCommandLineOption glContextSharing(QStringLiteral("disable-context-sharing"),
493 QCoreApplication::translate(context: "main", key: "Disable the use of a shared GL context for QtQuick Windows"));
494 parser.addOption(commandLineOption: glContextSharing); // Just for the help text... we've already handled this argument above
495 // Options relevant for other 3D APIs as well
496 QCommandLineOption shaderCaching(QStringLiteral("enable-shader-cache"),
497 QCoreApplication::translate(context: "main", key: "Enable persistent caching of generated shaders"));
498 parser.addOption(commandLineOption: shaderCaching); // Just for the help text... we've already handled this argument above
499 QCommandLineOption transparentOption(QStringLiteral("transparent"),
500 QCoreApplication::translate(context: "main", key: "Requests an alpha channel in order to enable semi-transparent windows."));
501 parser.addOption(commandLineOption: transparentOption); // Just for the help text... we've already handled this argument above
502 QCommandLineOption multisampleOption(QStringLiteral("multisample"),
503 QCoreApplication::translate(context: "main", key: "Requests 4x multisample antialiasing."));
504 parser.addOption(commandLineOption: multisampleOption); // Just for the help text... we've already handled this argument above
505#endif // QT_GUI_LIB
506
507 // Debugging and verbosity options
508 QCommandLineOption quietOption(QStringLiteral("quiet"),
509 QCoreApplication::translate(context: "main", key: "Suppress all output."));
510 parser.addOption(commandLineOption: quietOption);
511 QCommandLineOption verboseOption(QStringLiteral("verbose"),
512 QCoreApplication::translate(context: "main", key: "Print information about what qml is doing, like specific file URLs being loaded."));
513 parser.addOption(commandLineOption: verboseOption);
514 QCommandLineOption slowAnimationsOption(QStringLiteral("slow-animations"),
515 QCoreApplication::translate(context: "main", key: "Run all animations in slow motion."));
516 parser.addOption(commandLineOption: slowAnimationsOption);
517 QCommandLineOption fixedAnimationsOption(QStringLiteral("fixed-animations"),
518 QCoreApplication::translate(context: "main", key: "Run animations off animation tick rather than wall time."));
519 parser.addOption(commandLineOption: fixedAnimationsOption);
520 QCommandLineOption rhiOption(QStringList() << QStringLiteral("r") << QStringLiteral("rhi"),
521 QCoreApplication::translate(context: "main", key: "Set the backend for the Qt graphics abstraction (RHI). "
522 "Backend is one of: default, vulkan, metal, d3d11, d3d12, opengl"),
523 QStringLiteral("backend"));
524 parser.addOption(commandLineOption: rhiOption);
525 QCommandLineOption selectorOption(QStringLiteral("S"), QCoreApplication::translate(context: "main",
526 key: "Add selector to the list of QQmlFileSelectors."), QStringLiteral("selector"));
527 parser.addOption(commandLineOption: selectorOption);
528
529 // Positional arguments
530 parser.addPositionalArgument(name: "files",
531 description: QCoreApplication::translate(context: "main", key: "Any number of QML files can be loaded. They will share the same engine."), syntax: "[files...]");
532 parser.addPositionalArgument(name: "args",
533 description: QCoreApplication::translate(context: "main", key: "Arguments after '--' are ignored, but passed through to the application.arguments variable in QML."), syntax: "[-- args...]");
534
535 parser.process(app: *app);
536 if (parser.isSet(option: verboseOption))
537 verboseMode = true;
538 if (parser.isSet(option: quietOption)) {
539 quietMode = true;
540 verboseMode = false;
541 }
542 if (parser.isSet(option: listConfOption))
543 listConfFiles();
544 if (applicationType == QmlApplicationTypeUnknown) {
545#ifdef QT_WIDGETS_LIB
546 qWarning() << QCoreApplication::translate(context: "main", key: "--apptype must be followed by one of the following: core gui widget\n");
547#else
548 qWarning() << QCoreApplication::translate("main", "--apptype must be followed by one of the following: core gui\n");
549#endif // QT_WIDGETS_LIB
550 parser.showHelp();
551 }
552#if QT_CONFIG(qml_animation)
553 if (parser.isSet(option: slowAnimationsOption))
554 QUnifiedTimer::instance()->setSlowModeEnabled(true);
555 if (parser.isSet(option: fixedAnimationsOption))
556 QUnifiedTimer::instance()->setConsistentTiming(true);
557#endif
558
559 QQmlApplicationEngine e;
560
561 for (const QString &importPath : parser.values(option: importOption))
562 e.addImportPath(dir: importPath);
563
564 QStringList customSelectors;
565 for (const QString &selector : parser.values(option: selectorOption))
566 customSelectors.append(t: selector);
567
568 if (!customSelectors.isEmpty())
569 e.setExtraFileSelectors(customSelectors);
570
571 files << parser.values(option: qmlFileOption);
572 if (parser.isSet(option: configOption))
573 confFile = parser.value(option: configOption);
574 if (parser.isSet(option: translationOption))
575 translationFile = parser.value(option: translationOption);
576 if (parser.isSet(option: rhiOption)) {
577 const QString rhiBackend = parser.value(option: rhiOption);
578 if (rhiBackend == QLatin1String("default"))
579 qunsetenv(varName: "QSG_RHI_BACKEND");
580 else
581 qputenv(varName: "QSG_RHI_BACKEND", value: rhiBackend.toLatin1());
582 }
583 for (QString posArg : parser.positionalArguments()) {
584 if (posArg == QLatin1String("--"))
585 break;
586 else
587 files << posArg;
588 }
589
590#if QT_CONFIG(translation)
591 // Need to be installed before QQmlApplicationEngine's automatic translation loading
592 // (qt_ translations are loaded there)
593 QTranslator translator;
594 if (!translationFile.isEmpty()) {
595 if (translator.load(filename: translationFile)) {
596 app->installTranslator(messageFile: &translator);
597 if (verboseMode)
598 printf(format: "qml: Loaded translation file %s\n", qPrintable(QDir::toNativeSeparators(translationFile)));
599 } else {
600 if (!quietMode)
601 printf(format: "qml: Could not load the translation file %s\n", qPrintable(QDir::toNativeSeparators(translationFile)));
602 }
603 }
604#else
605 if (!translationFile.isEmpty() && !quietMode)
606 printf("qml: Translation file specified, but Qt built without translation support.\n");
607#endif
608
609 if (quietMode) {
610 qInstallMessageHandler(quietMessageHandler);
611 QLoggingCategory::setFilterRules(QStringLiteral("*=false"));
612 }
613
614 if (files.size() <= 0) {
615#if defined(Q_OS_DARWIN) && defined(QT_GUI_LIB)
616 if (applicationType == QmlApplicationTypeGui)
617 exitTimerId = static_cast<LoaderApplication *>(app.get())->startTimer(FILE_OPEN_EVENT_WAIT_TIME);
618 else
619#endif
620 noFilesGiven();
621 }
622
623 qae = &e;
624 loadConf(override: confFile, quiet: !verboseMode);
625
626 // Load files
627 QScopedPointer<LoadWatcher> lw(new LoadWatcher(&e, files.size()));
628
629#if QT_DEPRECATED_SINCE(6, 3)
630 QString dummyDir;
631 if (parser.isSet(option: dummyDataOption))
632 dummyDir = parser.value(option: dummyDataOption);
633 // Load dummy data before loading QML-files
634 if (!dummyDir.isEmpty() && QFileInfo (dummyDir).isDir()) {
635 qCWarning(lcDeprecated()) << "Warning: the qml --dummy-data option is deprecated and will be removed in a future version of Qt.";
636 loadDummyDataFiles(engine&: e, directory: dummyDir);
637 }
638#endif
639
640 for (const QString &path : std::as_const(t&: files)) {
641 QUrl url = QUrl::fromUserInput(userInput: path, workingDirectory: QDir::currentPath(), options: QUrl::AssumeLocalFile);
642 if (verboseMode)
643 printf(format: "qml: loading %s\n", qPrintable(url.toString()));
644 e.load(url);
645 }
646
647 if (lw->earlyExit)
648 return lw->returnCode;
649
650 return app->exec();
651}
652
653#include "main.moc"
654

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtdeclarative/tools/qml/main.cpp