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 | |
51 | Q_LOGGING_CATEGORY(lcDeprecated, "qt.tools.qml.deprecated" ) |
52 | |
53 | enum 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 | |
64 | static QmlApplicationType applicationType = |
65 | #ifndef QT_GUI_LIB |
66 | QmlApplicationTypeCore; |
67 | #else |
68 | QmlApplicationTypeGui; |
69 | #endif // QT_GUI_LIB |
70 | |
71 | static Config *conf = nullptr; |
72 | static QQmlApplicationEngine *qae = nullptr; |
73 | #if defined(Q_OS_DARWIN) || defined(QT_GUI_LIB) |
74 | static int exitTimerId = -1; |
75 | #endif |
76 | static const QString iconResourcePath(QStringLiteral(":/qt-project.org/imports/QmlRuntime/Config/resources/qml-64.png" )); |
77 | static const QString confResourcePath(QStringLiteral(":/qt-project.org/imports/QmlRuntime/Config/" )); |
78 | static const QString customConfFileName(QStringLiteral("configuration.qml" )); |
79 | static bool verboseMode = false; |
80 | static bool quietMode = false; |
81 | static bool glShareContexts = true; |
82 | static bool disableShaderCache = true; |
83 | static bool requestAlphaChannel = false; |
84 | static bool requestMSAA = false; |
85 | static bool requestCoreProfile = false; |
86 | |
87 | static 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 | |
148 | void noFilesGiven() |
149 | { |
150 | if (!quietMode) |
151 | printf(format: "qml: No files specified. Terminating.\n" ); |
152 | exit(status: 1); |
153 | } |
154 | |
155 | static 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 |
195 | class LoaderApplication : public QGuiApplication |
196 | { |
197 | public: |
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 |
225 | class LoadWatcher : public QObject |
226 | { |
227 | Q_OBJECT |
228 | public: |
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 | |
244 | public 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 | |
273 | private: |
274 | void contain(QObject *o, const QUrl &containPath); |
275 | |
276 | private: |
277 | int expectedFileCount; |
278 | int createdObjects = 0; |
279 | }; |
280 | |
281 | void 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 | |
296 | void 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 |
313 | static 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) |
356 | static 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 | |
381 | int 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 | |