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