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

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