1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
3 | |
4 | #include <QtCore/qabstractanimation.h> |
5 | #include <QtCore/qdir.h> |
6 | #include <QtCore/qmath.h> |
7 | #include <QtCore/qelapsedtimer.h> |
8 | #include <QtCore/qpointer.h> |
9 | #include <QtCore/qscopedpointer.h> |
10 | #include <QtCore/qtextstream.h> |
11 | #include <QtCore/qregularexpression.h> |
12 | #include <QtCore/qloggingcategory.h> |
13 | |
14 | #include <QtGui/QGuiApplication> |
15 | |
16 | #include <QtQml/qqml.h> |
17 | #include <QtQml/qqmlengine.h> |
18 | #include <QtQml/qqmlcomponent.h> |
19 | #include <QtQml/qqmlcontext.h> |
20 | #include <QtQml/qqmlfileselector.h> |
21 | |
22 | #include <QtQuick/qquickitem.h> |
23 | #include <QtQuick/qquickview.h> |
24 | |
25 | #include <private/qabstractanimation_p.h> |
26 | |
27 | #ifdef QT_WIDGETS_LIB |
28 | #include <QtWidgets/QApplication> |
29 | #if QT_CONFIG(filedialog) |
30 | #include <QtWidgets/QFileDialog> |
31 | #endif // QT_CONFIG(filedialog) |
32 | #endif // QT_WIDGETS_LIB |
33 | |
34 | #include <QtCore/QTranslator> |
35 | #include <QtCore/QLibraryInfo> |
36 | |
37 | Q_LOGGING_CATEGORY(lcQmlsceneDeprecated, "qt.tools.qmlscene.deprecated" ) |
38 | |
39 | #ifdef QML_RUNTIME_TESTING |
40 | class RenderStatistics |
41 | { |
42 | public: |
43 | static void updateStats(); |
44 | static void printTotalStats(); |
45 | private: |
46 | static QVector<qreal> timePerFrame; |
47 | static QVector<int> timesPerFrames; |
48 | }; |
49 | |
50 | QVector<qreal> RenderStatistics::timePerFrame; |
51 | QVector<int> RenderStatistics::timesPerFrames; |
52 | |
53 | void RenderStatistics::updateStats() |
54 | { |
55 | static QElapsedTimer time; |
56 | static int frames; |
57 | static int lastTime; |
58 | |
59 | if (frames == 0) { |
60 | time.start(); |
61 | } else { |
62 | int elapsed = time.elapsed(); |
63 | timesPerFrames.append(t: elapsed - lastTime); |
64 | lastTime = elapsed; |
65 | |
66 | if (elapsed > 5000) { |
67 | qreal avgtime = elapsed / (qreal) frames; |
68 | qreal var = 0; |
69 | for (int i = 0; i < timesPerFrames.size(); ++i) { |
70 | qreal diff = timesPerFrames.at(i) - avgtime; |
71 | var += diff * diff; |
72 | } |
73 | var /= timesPerFrames.size(); |
74 | |
75 | printf(format: "Average time per frame: %f ms (%i fps), std.dev: %f ms\n" , avgtime, qRound(d: 1000. / avgtime), qSqrt(v: var)); |
76 | |
77 | timePerFrame.append(t: avgtime); |
78 | timesPerFrames.clear(); |
79 | time.start(); |
80 | lastTime = 0; |
81 | frames = 0; |
82 | } |
83 | } |
84 | ++frames; |
85 | } |
86 | |
87 | void RenderStatistics::printTotalStats() |
88 | { |
89 | int count = timePerFrame.size(); |
90 | if (count == 0) |
91 | return; |
92 | |
93 | qreal minTime = 0; |
94 | qreal maxTime = 0; |
95 | qreal avg = 0; |
96 | for (int i = 0; i < count; ++i) { |
97 | minTime = minTime == 0 ? timePerFrame.at(i) : qMin(a: minTime, b: timePerFrame.at(i)); |
98 | maxTime = qMax(a: maxTime, b: timePerFrame.at(i)); |
99 | avg += timePerFrame.at(i); |
100 | } |
101 | avg /= count; |
102 | |
103 | puts(s: " " ); |
104 | puts(s: "----- Statistics -----" ); |
105 | printf(format: "Average time per frame: %f ms (%i fps)\n" , avg, qRound(d: 1000. / avg)); |
106 | printf(format: "Best time per frame: %f ms (%i fps)\n" , minTime, int(1000 / minTime)); |
107 | printf(format: "Worst time per frame: %f ms (%i fps)\n" , maxTime, int(1000 / maxTime)); |
108 | puts(s: "----------------------" ); |
109 | puts(s: " " ); |
110 | } |
111 | #endif |
112 | |
113 | struct Options |
114 | { |
115 | enum QmlApplicationType |
116 | { |
117 | QmlApplicationTypeGui, |
118 | QmlApplicationTypeWidget, |
119 | #ifdef QT_WIDGETS_LIB |
120 | DefaultQmlApplicationType = QmlApplicationTypeWidget |
121 | #else |
122 | DefaultQmlApplicationType = QmlApplicationTypeGui |
123 | #endif |
124 | }; |
125 | |
126 | Options() |
127 | : textRenderType(QQuickWindow::textRenderType()) |
128 | { |
129 | // QtWebEngine needs a shared context in order for the GPU thread to |
130 | // upload textures. |
131 | applicationAttributes.append(t: Qt::AA_ShareOpenGLContexts); |
132 | } |
133 | |
134 | QUrl url; |
135 | bool originalQml = false; |
136 | bool originalQmlRaster = false; |
137 | bool maximized = false; |
138 | bool fullscreen = false; |
139 | bool transparent = false; |
140 | bool clip = false; |
141 | bool versionDetection = true; |
142 | bool slowAnimations = false; |
143 | bool quitImmediately = false; |
144 | bool resizeViewToRootItem = false; |
145 | bool multisample = false; |
146 | bool coreProfile = false; |
147 | bool verbose = false; |
148 | bool rhi = false; |
149 | bool rhiBackendSet = false; |
150 | QVector<Qt::ApplicationAttribute> applicationAttributes; |
151 | QString translationFile; |
152 | QmlApplicationType applicationType = DefaultQmlApplicationType; |
153 | QQuickWindow::TextRenderType textRenderType; |
154 | QString rhiBackend; |
155 | }; |
156 | |
157 | #if defined(QMLSCENE_BUNDLE) |
158 | QFileInfoList findQmlFiles(const QString &dirName) |
159 | { |
160 | QDir dir(dirName); |
161 | |
162 | QFileInfoList ret; |
163 | if (dir.exists()) { |
164 | const QFileInfoList fileInfos = dir.entryInfoList(QStringList() << "*.qml" , |
165 | QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot); |
166 | |
167 | for (const QFileInfo &fileInfo : fileInfos) { |
168 | if (fileInfo.isDir()) |
169 | ret += findQmlFiles(fileInfo.filePath()); |
170 | else if (fileInfo.fileName().length() > 0 && fileInfo.fileName().at(0).isLower()) |
171 | ret.append(fileInfo); |
172 | } |
173 | } |
174 | |
175 | return ret; |
176 | } |
177 | |
178 | static int displayOptionsDialog(Options *options) |
179 | { |
180 | QDialog dialog; |
181 | |
182 | QFormLayout *layout = new QFormLayout(&dialog); |
183 | |
184 | QComboBox *qmlFileComboBox = new QComboBox(&dialog); |
185 | const QFileInfoList fileInfos = findQmlFiles(":/bundle" ) + findQmlFiles("./qmlscene-resources" ); |
186 | |
187 | for (const QFileInfo &fileInfo : fileInfos) |
188 | qmlFileComboBox->addItem(fileInfo.dir().dirName() + QLatin1Char('/') + fileInfo.fileName(), QVariant::fromValue(fileInfo)); |
189 | |
190 | QCheckBox *originalCheckBox = new QCheckBox(&dialog); |
191 | originalCheckBox->setText("Use original QML viewer" ); |
192 | originalCheckBox->setChecked(options->originalQml); |
193 | |
194 | QCheckBox *fullscreenCheckBox = new QCheckBox(&dialog); |
195 | fullscreenCheckBox->setText("Start fullscreen" ); |
196 | fullscreenCheckBox->setChecked(options->fullscreen); |
197 | |
198 | QCheckBox *maximizedCheckBox = new QCheckBox(&dialog); |
199 | maximizedCheckBox->setText("Start maximized" ); |
200 | maximizedCheckBox->setChecked(options->maximized); |
201 | |
202 | QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, |
203 | Qt::Horizontal, |
204 | &dialog); |
205 | QObject::connect(buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept())); |
206 | QObject::connect(buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject())); |
207 | |
208 | layout->addRow("Qml file:" , qmlFileComboBox); |
209 | layout->addWidget(originalCheckBox); |
210 | layout->addWidget(maximizedCheckBox); |
211 | layout->addWidget(fullscreenCheckBox); |
212 | layout->addWidget(buttonBox); |
213 | |
214 | int result = dialog.exec(); |
215 | if (result == QDialog::Accepted) { |
216 | QVariant variant = qmlFileComboBox->itemData(qmlFileComboBox->currentIndex()); |
217 | QFileInfo fileInfo = variant.value<QFileInfo>(); |
218 | |
219 | if (fileInfo.canonicalFilePath().startsWith(QLatin1Char(':'))) |
220 | options->file = QUrl("qrc" + fileInfo.canonicalFilePath()); |
221 | else |
222 | options->file = QUrl::fromLocalFile(fileInfo.canonicalFilePath()); |
223 | options->originalQml = originalCheckBox->isChecked(); |
224 | options->maximized = maximizedCheckBox->isChecked(); |
225 | options->fullscreen = fullscreenCheckBox->isChecked(); |
226 | } |
227 | return result; |
228 | } |
229 | #endif |
230 | |
231 | static bool checkVersion(const QUrl &url) |
232 | { |
233 | if (!qgetenv(varName: "QMLSCENE_IMPORT_NAME" ).isEmpty()) |
234 | fprintf(stderr, format: "QMLSCENE_IMPORT_NAME is no longer supported.\n" ); |
235 | |
236 | if (!url.isLocalFile()) |
237 | return true; |
238 | |
239 | const QString fileName = url.toLocalFile(); |
240 | QFile f(fileName); |
241 | if (!f.open(flags: QFile::ReadOnly | QFile::Text)) { |
242 | fprintf(stderr, format: "qmlscene: failed to check version of file '%s', could not open...\n" , |
243 | qPrintable(fileName)); |
244 | return false; |
245 | } |
246 | |
247 | QRegularExpression quick1("^\\s*import +QtQuick +1\\.\\w*" ); |
248 | QRegularExpression qt47("^\\s*import +Qt +4\\.7" ); |
249 | |
250 | QTextStream stream(&f); |
251 | bool codeFound= false; |
252 | while (!codeFound) { |
253 | if (stream.atEnd()) { |
254 | fprintf(stderr, format: "qmlscene: no code found in file '%s'.\n" , qPrintable(fileName)); |
255 | return false; |
256 | } |
257 | QString line = stream.readLine(); |
258 | if (line.contains(c: QLatin1Char('{'))) { |
259 | codeFound = true; |
260 | } else { |
261 | QString import; |
262 | QRegularExpressionMatch match = quick1.match(subject: line); |
263 | if (match.hasMatch()) |
264 | import = match.captured(nth: 0).trimmed(); |
265 | else if ((match = qt47.match(subject: line)).hasMatch()) |
266 | import = match.captured(nth: 0).trimmed(); |
267 | |
268 | if (!import.isNull()) { |
269 | fprintf(stderr, format: "qmlscene: '%s' is no longer supported.\n" |
270 | "Use qmlviewer to load file '%s'.\n" , |
271 | qPrintable(import), |
272 | qPrintable(fileName)); |
273 | return false; |
274 | } |
275 | } |
276 | } |
277 | |
278 | return true; |
279 | } |
280 | |
281 | static void displayFileDialog(Options *options) |
282 | { |
283 | #if defined(QT_WIDGETS_LIB) && QT_CONFIG(filedialog) |
284 | if (options->applicationType == Options::QmlApplicationTypeWidget) { |
285 | QString fileName = QFileDialog::getOpenFileName(parent: nullptr, caption: "Open QML file" , dir: QString(), filter: "QML Files (*.qml)" ); |
286 | if (!fileName.isEmpty()) { |
287 | QFileInfo fi(fileName); |
288 | options->url = QUrl::fromLocalFile(localfile: fi.canonicalFilePath()); |
289 | } |
290 | return; |
291 | } |
292 | #endif // QT_WIDGETS_LIB && QT_CONFIG(filedialog) |
293 | Q_UNUSED(options); |
294 | puts(s: "No filename specified..." ); |
295 | } |
296 | |
297 | static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory) |
298 | { |
299 | QDir dir(directory+"/dummydata" , "*.qml" ); |
300 | QStringList list = dir.entryList(); |
301 | for (int i = 0; i < list.size(); ++i) { |
302 | QString qml = list.at(i); |
303 | QQmlComponent comp(&engine, dir.filePath(fileName: qml)); |
304 | QObject *dummyData = comp.create(); |
305 | |
306 | if(comp.isError()) { |
307 | const QList<QQmlError> errors = comp.errors(); |
308 | for (const QQmlError &error : errors) |
309 | fprintf(stderr, format: "%s\n" , qPrintable(error.toString())); |
310 | } |
311 | |
312 | if (dummyData) { |
313 | fprintf(stderr, format: "Loaded dummy data: %s\n" , qPrintable(dir.filePath(qml))); |
314 | qml.truncate(pos: qml.size()-4); |
315 | engine.rootContext()->setContextProperty(qml, dummyData); |
316 | dummyData->setParent(&engine); |
317 | } |
318 | } |
319 | } |
320 | |
321 | static void usage() |
322 | { |
323 | puts(s: "Usage: qmlscene [options] <filename>" ); |
324 | puts(s: " " ); |
325 | puts(s: " Options:" ); |
326 | puts(s: " --maximized ...................... Run maximized" ); |
327 | puts(s: " --fullscreen ..................... Run fullscreen" ); |
328 | puts(s: " --transparent .................... Make the window transparent" ); |
329 | puts(s: " --multisample .................... Enable multisampling (OpenGL anti-aliasing)" ); |
330 | puts(s: " --core-profile ................... Request a core profile OpenGL context" ); |
331 | puts(s: " --rhi [vulkan|metal|d3d11|gl] .... Specify backend for the Qt graphics abstraction (RHI).\n" ); |
332 | puts(s: " --no-version-detection ........... Do not try to detect the version of the .qml file" ); |
333 | puts(s: " --slow-animations ................ Run all animations in slow motion" ); |
334 | puts(s: " --resize-to-root ................. Resize the window to the size of the root item" ); |
335 | puts(s: " --quit ........................... Quit immediately after starting" ); |
336 | puts(s: " --disable-context-sharing ........ Disable the use of a shared GL context for QtQuick Windows\n" |
337 | " ........ (remove AA_ShareOpenGLContexts)" ); |
338 | puts(s: " --desktop......................... Force use of desktop GL (AA_UseDesktopOpenGL)" ); |
339 | puts(s: " --gles............................ Force use of GLES (AA_UseOpenGLES)" ); |
340 | puts(s: " --software........................ Force use of software rendering (AA_UseSoftwareOpenGL)" ); |
341 | puts(s: " --verbose......................... Print version and graphical diagnostics for the run-time" ); |
342 | #ifdef QT_WIDGETS_LIB |
343 | puts(s: " --apptype [gui|widgets] .......... Select which application class to use. Default is widgets." ); |
344 | #endif |
345 | puts(s: " --textrendertype [qt|native]...... Select the default render type for text-like elements." ); |
346 | puts(s: " -I <path> ........................ Add <path> to the list of import paths" ); |
347 | puts(s: " -S <selector> .................... Add <selector> to the list of QQmlFileSelector selectors" ); |
348 | puts(s: " -P <path> ........................ Add <path> to the list of plugin paths" ); |
349 | puts(s: " -translation <translationfile> ... Set the language to run in" ); |
350 | |
351 | puts(s: " " ); |
352 | exit(status: 1); |
353 | } |
354 | |
355 | static void setWindowTitle(bool verbose, const QObject *topLevel, QWindow *window) |
356 | { |
357 | const QString oldTitle = window->title(); |
358 | QString newTitle = oldTitle; |
359 | if (newTitle.isEmpty()) { |
360 | newTitle = QLatin1String("qmlscene" ); |
361 | if (!qobject_cast<const QWindow *>(o: topLevel) && !topLevel->objectName().isEmpty()) |
362 | newTitle += QLatin1String(": " ) + topLevel->objectName(); |
363 | } |
364 | if (verbose) { |
365 | newTitle += QLatin1String(" [Qt " ) + QLatin1String(QT_VERSION_STR) + QLatin1Char(' ') |
366 | + QGuiApplication::platformName() + QLatin1Char(' '); |
367 | newTitle += QLatin1Char(']'); |
368 | } |
369 | if (oldTitle != newTitle) |
370 | window->setTitle(newTitle); |
371 | } |
372 | |
373 | static QUrl parseUrlArgument(const QString &arg) |
374 | { |
375 | const QUrl url = QUrl::fromUserInput(userInput: arg, workingDirectory: QDir::currentPath(), options: QUrl::AssumeLocalFile); |
376 | if (!url.isValid()) { |
377 | fprintf(stderr, format: "Invalid URL: \"%s\"\n" , qPrintable(arg)); |
378 | return QUrl(); |
379 | } |
380 | if (url.isLocalFile()) { |
381 | const QFileInfo fi(url.toLocalFile()); |
382 | if (!fi.exists()) { |
383 | fprintf(stderr, format: "\"%s\" does not exist.\n" , |
384 | qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath()))); |
385 | return QUrl(); |
386 | } |
387 | } |
388 | return url; |
389 | } |
390 | |
391 | static QQuickWindow::TextRenderType parseTextRenderType(const QString &renderType) |
392 | { |
393 | if (renderType == QLatin1String("qt" )) |
394 | return QQuickWindow::QtTextRendering; |
395 | else if (renderType == QLatin1String("native" )) |
396 | return QQuickWindow::NativeTextRendering; |
397 | |
398 | usage(); |
399 | |
400 | Q_UNREACHABLE_RETURN(QQuickWindow::QtTextRendering); |
401 | } |
402 | |
403 | int main(int argc, char ** argv) |
404 | { |
405 | Options options; |
406 | |
407 | QStringList imports; |
408 | QStringList customSelectors; |
409 | QStringList pluginPaths; |
410 | |
411 | qCWarning(lcQmlsceneDeprecated()) << "Warning: qmlscene is deprecated and will be removed in a future version of Qt. Please use qml instead." ; |
412 | |
413 | // Parse arguments for application attributes to be applied before Q[Gui]Application creation. |
414 | for (int i = 1; i < argc; ++i) { |
415 | const char *arg = argv[i]; |
416 | if (!qstrcmp(str1: arg, str2: "--disable-context-sharing" )) { |
417 | options.applicationAttributes.removeAll(t: Qt::AA_ShareOpenGLContexts); |
418 | } else if (!qstrcmp(str1: arg, str2: "--gles" )) { |
419 | options.applicationAttributes.append(t: Qt::AA_UseOpenGLES); |
420 | } else if (!qstrcmp(str1: arg, str2: "--software" )) { |
421 | options.applicationAttributes.append(t: Qt::AA_UseSoftwareOpenGL); |
422 | } else if (!qstrcmp(str1: arg, str2: "--desktop" )) { |
423 | options.applicationAttributes.append(t: Qt::AA_UseDesktopOpenGL); |
424 | } else if (!qstrcmp(str1: arg, str2: "--transparent" )) { |
425 | options.transparent = true; |
426 | } else if (!qstrcmp(str1: arg, str2: "--multisample" )) { |
427 | options.multisample = true; |
428 | } else if (!qstrcmp(str1: arg, str2: "--core-profile" )) { |
429 | options.coreProfile = true; |
430 | } else if (!qstrcmp(str1: arg, str2: "--apptype" )) { |
431 | if (++i >= argc) |
432 | usage(); |
433 | if (!qstrcmp(str1: argv[i], str2: "gui" )) |
434 | options.applicationType = Options::QmlApplicationTypeGui; |
435 | } |
436 | } |
437 | |
438 | if (qEnvironmentVariableIsSet(varName: "QMLSCENE_CORE_PROFILE" ) |
439 | || qEnvironmentVariableIsSet(varName: "QSG_CORE_PROFILE" )) |
440 | options.coreProfile = true; |
441 | |
442 | // Set default surface format before creating the window |
443 | QSurfaceFormat surfaceFormat; |
444 | surfaceFormat.setStencilBufferSize(8); |
445 | surfaceFormat.setDepthBufferSize(24); |
446 | if (options.multisample) |
447 | surfaceFormat.setSamples(16); |
448 | if (options.transparent) |
449 | surfaceFormat.setAlphaBufferSize(8); |
450 | if (options.coreProfile) { |
451 | surfaceFormat.setVersion(major: 4, minor: 1); |
452 | surfaceFormat.setProfile(QSurfaceFormat::CoreProfile); |
453 | } |
454 | QSurfaceFormat::setDefaultFormat(surfaceFormat); |
455 | |
456 | for (Qt::ApplicationAttribute a : std::as_const(t&: options.applicationAttributes)) |
457 | QCoreApplication::setAttribute(attribute: a); |
458 | QScopedPointer<QGuiApplication> app; |
459 | #ifdef QT_WIDGETS_LIB |
460 | if (options.applicationType == Options::QmlApplicationTypeWidget) |
461 | app.reset(other: new QApplication(argc, argv)); |
462 | #endif |
463 | if (app.isNull()) |
464 | app.reset(other: new QGuiApplication(argc, argv)); |
465 | QCoreApplication::setApplicationName(QStringLiteral("QtQmlViewer" )); |
466 | QCoreApplication::setOrganizationName(QStringLiteral("QtProject" )); |
467 | QCoreApplication::setOrganizationDomain(QStringLiteral("qt-project.org" )); |
468 | QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR)); |
469 | |
470 | const QStringList arguments = QCoreApplication::arguments(); |
471 | for (int i = 1, size = arguments.size(); i < size; ++i) { |
472 | if (!arguments.at(i).startsWith(c: QLatin1Char('-'))) { |
473 | options.url = parseUrlArgument(arg: arguments.at(i)); |
474 | } else { |
475 | const QString lowerArgument = arguments.at(i).toLower(); |
476 | if (lowerArgument == QLatin1String("--maximized" )) |
477 | options.maximized = true; |
478 | else if (lowerArgument == QLatin1String("--fullscreen" )) |
479 | options.fullscreen = true; |
480 | else if (lowerArgument == QLatin1String("--clip" )) |
481 | options.clip = true; |
482 | else if (lowerArgument == QLatin1String("--no-version-detection" )) |
483 | options.versionDetection = false; |
484 | else if (lowerArgument == QLatin1String("--slow-animations" )) |
485 | options.slowAnimations = true; |
486 | else if (lowerArgument == QLatin1String("--quit" )) |
487 | options.quitImmediately = true; |
488 | else if (lowerArgument == QLatin1String("-translation" )) |
489 | options.translationFile = QLatin1String(argv[++i]); |
490 | else if (lowerArgument == QLatin1String("--resize-to-root" )) |
491 | options.resizeViewToRootItem = true; |
492 | else if (lowerArgument == QLatin1String("--verbose" )) |
493 | options.verbose = true; |
494 | else if (lowerArgument == QLatin1String("--rhi" )) { |
495 | options.rhi = true; |
496 | if (i + 1 < size && !arguments.at(i: i + 1).startsWith(c: QLatin1Char('-'))) { |
497 | options.rhiBackendSet = true; |
498 | options.rhiBackend = arguments.at(i: ++i); |
499 | } |
500 | } else if (lowerArgument == QLatin1String("-i" ) && i + 1 < size) |
501 | imports.append(t: arguments.at(i: ++i)); |
502 | else if (lowerArgument == QLatin1String("-s" ) && i + 1 < size) |
503 | customSelectors.append(t: arguments.at(i: ++i)); |
504 | else if (lowerArgument == QLatin1String("-p" ) && i + 1 < size) |
505 | pluginPaths.append(t: arguments.at(i: ++i)); |
506 | else if (lowerArgument == QLatin1String("--apptype" )) |
507 | ++i; // Consume previously parsed argument |
508 | else if (lowerArgument == QLatin1String("--textrendertype" ) && i + 1 < size) |
509 | options.textRenderType = parseTextRenderType(renderType: arguments.at(i: ++i)); |
510 | else if (lowerArgument == QLatin1String("--help" ) |
511 | || lowerArgument == QLatin1String("-help" ) |
512 | || lowerArgument == QLatin1String("--h" ) |
513 | || lowerArgument == QLatin1String("-h" )) |
514 | usage(); |
515 | } |
516 | } |
517 | |
518 | #if QT_CONFIG(translation) |
519 | QLocale locale; |
520 | QTranslator qtTranslator; |
521 | if (qtTranslator.load(locale, filename: QLatin1String("qt" ), prefix: QLatin1String("_" ), directory: QLibraryInfo::path(p: QLibraryInfo::TranslationsPath))) |
522 | QCoreApplication::installTranslator(messageFile: &qtTranslator); |
523 | QTranslator translator; |
524 | if (translator.load(locale, filename: QLatin1String("qmlscene" ), prefix: QLatin1String("_" ), directory: QLibraryInfo::path(p: QLibraryInfo::TranslationsPath))) |
525 | QCoreApplication::installTranslator(messageFile: &translator); |
526 | |
527 | QTranslator qmlTranslator; |
528 | if (!options.translationFile.isEmpty()) { |
529 | if (qmlTranslator.load(filename: options.translationFile)) { |
530 | QCoreApplication::installTranslator(messageFile: &qmlTranslator); |
531 | } else { |
532 | fprintf(stderr, format: "Could not load the translation file \"%s\"\n" , |
533 | qPrintable(options.translationFile)); |
534 | } |
535 | } |
536 | #endif |
537 | |
538 | QQuickWindow::setTextRenderType(options.textRenderType); |
539 | |
540 | QUnifiedTimer::instance()->setSlowModeEnabled(options.slowAnimations); |
541 | |
542 | if (options.rhi) { |
543 | if (options.rhiBackendSet) |
544 | qputenv(varName: "QSG_RHI_BACKEND" , value: options.rhiBackend.toLatin1()); |
545 | else |
546 | qunsetenv(varName: "QSG_RHI_BACKEND" ); |
547 | } |
548 | |
549 | if (options.url.isEmpty()) |
550 | #if defined(QMLSCENE_BUNDLE) |
551 | displayOptionsDialog(&options); |
552 | #else |
553 | displayFileDialog(options: &options); |
554 | #endif |
555 | |
556 | int exitCode = 0; |
557 | |
558 | if (options.verbose) |
559 | puts(s: QLibraryInfo::build()); |
560 | |
561 | if (!options.url.isEmpty()) { |
562 | if (!options.versionDetection || checkVersion(url: options.url)) { |
563 | // TODO: as soon as the engine construction completes, the debug service is |
564 | // listening for connections. But actually we aren't ready to debug anything. |
565 | QQmlEngine engine; |
566 | QQmlFileSelector* selector = new QQmlFileSelector(&engine, &engine); |
567 | selector->setExtraSelectors(customSelectors); |
568 | QPointer<QQmlComponent> component = new QQmlComponent(&engine); |
569 | for (int i = 0; i < imports.size(); ++i) |
570 | engine.addImportPath(dir: imports.at(i)); |
571 | for (int i = 0; i < pluginPaths.size(); ++i) |
572 | engine.addPluginPath(dir: pluginPaths.at(i)); |
573 | if (options.url.isLocalFile()) { |
574 | QFileInfo fi(options.url.toLocalFile()); |
575 | #if QT_CONFIG(translation) |
576 | QTranslator *translator = new QTranslator(app.get()); |
577 | if (translator->load(locale: QLocale(), filename: QLatin1String("qml" ), prefix: QLatin1String("_" ), directory: fi.path() + QLatin1String("/i18n" ))) |
578 | QCoreApplication::installTranslator(messageFile: translator); |
579 | #endif |
580 | loadDummyDataFiles(engine, directory: fi.path()); |
581 | } |
582 | QObject::connect(sender: &engine, SIGNAL(quit()), receiver: QCoreApplication::instance(), SLOT(quit())); |
583 | QObject::connect(sender: &engine, signal: &QQmlEngine::exit, context: QCoreApplication::instance(), slot: &QCoreApplication::exit); |
584 | component->loadUrl(url: options.url); |
585 | while (component->isLoading()) |
586 | QCoreApplication::processEvents(); |
587 | if ( !component->isReady() ) { |
588 | fprintf(stderr, format: "%s\n" , qPrintable(component->errorString())); |
589 | return -1; |
590 | } |
591 | |
592 | QObject *topLevel = component->create(); |
593 | if (!topLevel && component->isError()) { |
594 | fprintf(stderr, format: "%s\n" , qPrintable(component->errorString())); |
595 | return -1; |
596 | } |
597 | |
598 | QScopedPointer<QQuickWindow> window(qobject_cast<QQuickWindow *>(object: topLevel)); |
599 | if (window) { |
600 | engine.setIncubationController(window->incubationController()); |
601 | } else { |
602 | QQuickItem *contentItem = qobject_cast<QQuickItem *>(o: topLevel); |
603 | if (contentItem) { |
604 | QQuickView* qxView = new QQuickView(&engine, nullptr); |
605 | window.reset(other: qxView); |
606 | // Set window default properties; the qml can still override them |
607 | if (options.resizeViewToRootItem) |
608 | qxView->setResizeMode(QQuickView::SizeViewToRootObject); |
609 | else |
610 | qxView->setResizeMode(QQuickView::SizeRootObjectToView); |
611 | qxView->setContent(url: options.url, component, item: contentItem); |
612 | } |
613 | } |
614 | |
615 | if (window) { |
616 | setWindowTitle(verbose: options.verbose, topLevel, window: window.data()); |
617 | if (options.transparent) { |
618 | window->setColor(QColor(Qt::transparent)); |
619 | window->setFlags(Qt::FramelessWindowHint); |
620 | } |
621 | window->setFormat(surfaceFormat); |
622 | |
623 | if (window->flags() == Qt::Window) // Fix window flags unless set by QML. |
624 | window->setFlags(Qt::Window | Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint | Qt::WindowFullscreenButtonHint); |
625 | |
626 | if (options.fullscreen) |
627 | window->showFullScreen(); |
628 | else if (options.maximized) |
629 | window->showMaximized(); |
630 | else if (!window->isVisible()) |
631 | window->show(); |
632 | } |
633 | |
634 | if (options.quitImmediately) |
635 | QMetaObject::invokeMethod(obj: QCoreApplication::instance(), member: "quit" , c: Qt::QueuedConnection); |
636 | |
637 | // Now would be a good time to inform the debug service to start listening. |
638 | |
639 | exitCode = app->exec(); |
640 | |
641 | #ifdef QML_RUNTIME_TESTING |
642 | RenderStatistics::printTotalStats(); |
643 | #endif |
644 | // Ready to exit. Notice that the component might be owned by |
645 | // QQuickView if one was created. That case is tracked by |
646 | // QPointer, so it is safe to delete the component here. |
647 | delete component; |
648 | } else { |
649 | exitCode = 1; |
650 | } |
651 | } |
652 | |
653 | return exitCode; |
654 | } |
655 | |