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