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

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