QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
QGCApplication.cc
Go to the documentation of this file.
1#include "QGCApplication.h"
2
3#include <QtCore/QEvent>
4#include <QtCore/QFile>
5#include <QtCore/QMetaMethod>
6#include <QtCore/QMetaObject>
7#include <QtCore/QRegularExpression>
8#include <QtCore/private/qthread_p.h>
9#include <QtGui/QFontDatabase>
10#include <QtGui/QIcon>
11#include <QtQml/QQmlApplicationEngine>
12#include <QtQml/QQmlContext>
13#include <QtQuick/QQuickImageProvider>
14#include <QtQuick/QQuickWindow>
15#include <QtQuickControls2/QQuickStyle>
16#include <QtSvg/QSvgRenderer>
17
18#include "AppSettings.h"
19#include "AudioOutput.h"
21#include "FollowMe.h"
22#include "GraphicsSetup.h"
23#include "JoystickManager.h"
24#include "JsonParsing.h"
25#include "LinkManager.h"
26#include "LogManager.h"
27#include "MAVLinkProtocol.h"
28#include "MavlinkSettings.h"
29#include "MultiVehicleManager.h"
30#include "NTRIPManager.h"
31#include "ParameterManager.h"
32#include "PositionManager.h"
34#include "QGCCorePlugin.h"
35#include "QGCFileDownload.h"
36#include "QGCImageProvider.h"
37#include "QGCLoggingCategory.h"
39#include "QGCNetworkHelper.h"
40#include "SettingsManager.h"
41#include "Vehicle.h"
42#include "VideoManager.h"
43#include "qgc_version.h"
44
45#ifndef QGC_NO_SERIAL_LINK
46#include "SerialLink.h"
47#endif
48
49QGC_LOGGING_CATEGORY(QGCApplicationLog, "API.QGCApplication")
50QGC_LOGGING_CATEGORY(QGCAppMessageLog, "API.QGCApplication.AppMessage")
51
52QGCApplication::QGCApplication(int& argc, char* argv[], const QGCCommandLineParser::CommandLineParseResult& cli)
53 : QGuiApplication(argc, argv),
54 _runningUnitTests(cli.runningUnitTests),
55 _simpleBootTest(cli.simpleBootTest),
56 _fakeMobile(cli.fakeMobile),
57 _logOutput(cli.logOutput),
58 _systemId(cli.systemId.value_or(0))
59{
60 _msecsElapsedTime.start();
61
62 // Setup for network proxy support
64
65 bool fClearSettingsOptions = cli.clearSettingsOptions; // Clear stored settings
66 const bool fClearCache = cli.clearCache; // Clear parameter/airframe caches
67 const QString loggingOptions = cli.loggingOptions.value_or(QString(""));
68
69 // Set up timer for delayed missing fact display
70 _missingParamsDelayedDisplayTimer.setSingleShot(true);
71 _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
72 (void) connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
73
74 // Set application information
75 QString applicationName;
76 if (_runningUnitTests || _simpleBootTest) {
77 // We don't want unit tests to use the same QSettings space as the normal app. So we tweak the app
78 // name. Also we want to run unit tests with clean settings every time.
79 // Include test name or PID to prevent settings file conflicts when tests run in parallel
80 if (!cli.unitTests.isEmpty()) {
81 applicationName = QStringLiteral("%1_unittest_%2").arg(QGC_APP_NAME, cli.unitTests.first());
82 } else {
83 applicationName =
84 QStringLiteral("%1_unittest_%2").arg(QGC_APP_NAME).arg(QCoreApplication::applicationPid());
85 }
86 } else {
87#ifdef QGC_DAILY_BUILD
88 // This gives daily builds their own separate settings space. Allowing you to use daily and stable builds
89 // side by side without daily screwing up your stable settings.
90 applicationName = QStringLiteral("%1 Daily").arg(QGC_APP_NAME);
91#else
92 applicationName = QGC_APP_NAME;
93#endif
94 }
95 setApplicationName(applicationName);
96 setDesktopFileName(QGC_PACKAGE_NAME);
97 setOrganizationName(QGC_ORG_NAME);
98 setOrganizationDomain(QGC_ORG_DOMAIN);
99 setApplicationVersion(QString(QGC_APP_VERSION_STR));
100
101 // Set settings format
102 QSettings::setDefaultFormat(QSettings::IniFormat);
103 QSettings settings;
104 qCDebug(QGCApplicationLog) << "Settings location" << settings.fileName()
105 << "Is writable?:" << settings.isWritable();
106
107 if (!settings.isWritable()) {
108 qCWarning(QGCApplicationLog) << "Setings location is not writable";
109 }
110
111 // The setting will delete all settings on this boot
112 fClearSettingsOptions |= settings.value(AppSettings::clearSettingsNextBootKey, false).toBool();
113
114 if (_runningUnitTests || _simpleBootTest) {
115 // Unit tests run with clean settings
116 fClearSettingsOptions = true;
117 }
118
119 if (fClearSettingsOptions) {
120 // User requested settings to be cleared on command line
121 settings.clear();
122
123 // Clear parameter cache
125 paramDir.removeRecursively();
126 paramDir.mkpath(paramDir.absolutePath());
127 } else {
128 // Determine if upgrade message for settings version bump is required. Check and clear must happen before
129 // toolbox is started since that will write some settings.
130 if (settings.contains(_settingsVersionKey)) {
131 if (settings.value(_settingsVersionKey).toInt() != QGC_SETTINGS_VERSION) {
132 settings.clear();
133 _settingsUpgraded = true;
134 }
135 }
136 }
137 settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
138
139 if (fClearCache) {
141 dir.removeRecursively();
142 QFile parameter(cachedParameterMetaDataFile());
143 parameter.remove();
144 QFile airframe(cachedAirframeMetaDataFile());
145 airframe.remove();
146
147 // Clear versioned parameter metadata cache
148 const QString metaDataCachePath =
149 QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QStringLiteral("/ParameterMetaData");
150 QDir(metaDataCachePath).removeRecursively();
151 }
152
153 // Set up our logging filters
156
157 if (_runningUnitTests) {
158 // Enable the AppMessage category so showAppMessage() debug logs are visible during unit tests.
159 QGCLoggingCategoryManager::instance()->setCategoryEnabled(QStringLiteral("API.QGCApplication.AppMessage"),
160 true);
161 }
162
163 // We need to set language as early as possible prior to loading on JSON files.
164 setLanguage();
165
166 // Force old SVG Tiny 1.2 behavior for compatibility
167 QSvgRenderer::setDefaultOptions(QtSvg::Tiny12FeaturesOnly);
168
169#ifndef QGC_DAILY_BUILD
170 _checkForNewVersion();
171#endif
172}
173
175{
176 _locale = QLocale::system();
177 qCDebug(QGCApplicationLog) << "System reported locale:" << _locale << "; Name" << _locale.name()
178 << "; Preffered (used in maps): "
179 << (QLocale::system().uiLanguages().length() > 0 ? QLocale::system().uiLanguages()[0]
180 : "None");
181
182 QLocale::Language possibleLocale = AppSettings::_qLocaleLanguageEarlyAccess();
183 if (possibleLocale != QLocale::AnyLanguage) {
184 _locale = QLocale(possibleLocale);
185 }
186 //-- We have specific fonts for Korean
187 if (_locale == QLocale::Korean) {
188 qCDebug(QGCApplicationLog) << "Loading Korean fonts" << _locale.name();
189 if (QFontDatabase::addApplicationFont(":/fonts/NanumGothic-Regular") < 0) {
190 qCWarning(QGCApplicationLog) << "Could not load /fonts/NanumGothic-Regular font";
191 }
192 if (QFontDatabase::addApplicationFont(":/fonts/NanumGothic-Bold") < 0) {
193 qCWarning(QGCApplicationLog) << "Could not load /fonts/NanumGothic-Bold font";
194 }
195 }
196 qCDebug(QGCApplicationLog) << "Loading localizations for" << _locale.name();
197 removeTranslator(JsonParsing::translator());
198 removeTranslator(&_qgcTranslatorSourceCode);
199 removeTranslator(&_qgcTranslatorQtLibs);
200 if (_locale.name() != "en_US") {
201 QLocale::setDefault(_locale);
202 if (_qgcTranslatorQtLibs.load("qt_" + _locale.name(), QLibraryInfo::path(QLibraryInfo::TranslationsPath))) {
203 installTranslator(&_qgcTranslatorQtLibs);
204 } else {
205 qCWarning(QGCApplicationLog) << "Qt lib localization for" << _locale.name() << "is not present";
206 }
207 if (_qgcTranslatorSourceCode.load(_locale, QLatin1String("qgc_source_"), "", ":/i18n")) {
208 installTranslator(&_qgcTranslatorSourceCode);
209 } else {
210 qCWarning(QGCApplicationLog) << "Error loading source localization for" << _locale.name();
211 }
212 if (JsonParsing::translator()->load(_locale, QLatin1String("qgc_json_"), "", ":/i18n")) {
213 installTranslator(JsonParsing::translator());
214 } else {
215 qCWarning(QGCApplicationLog) << "Error loading json localization for" << _locale.name();
216 }
217 }
218
219 if (_qmlAppEngine) {
220 _qmlAppEngine->retranslate();
221 }
222
223 emit languageChanged(_locale);
224}
225
227
229{
231 if (_systemId > 0) {
232 qCDebug(QGCApplicationLog) << "Setting MAVLink System ID to:" << _systemId;
233 SettingsManager::instance()->mavlinkSettings()->gcsMavlinkSystemID()->setRawValue(_systemId);
234 }
235
237
238 // Although this should really be in _initForNormalAppBoot putting it here allowws us to create unit tests which pop
239 // up more easily
240 if (QFontDatabase::addApplicationFont(":/fonts/opensans") < 0) {
241 qCWarning(QGCApplicationLog) << "Could not load /fonts/opensans font";
242 }
243
244 if (QFontDatabase::addApplicationFont(":/fonts/opensans-demibold") < 0) {
245 qCWarning(QGCApplicationLog) << "Could not load /fonts/opensans-demibold font";
246 }
247
248 if (_simpleBootTest) {
249 // Since GStream builds are so problematic we initialize video during the simple boot test
250 // to make sure it works and verfies plugin availability.
251 const bool videoInitialized = _initVideo();
252 const bool qmlRootLoaded = _initQmlRootWindow();
253 _bootTestPassed = videoInitialized && qmlRootLoaded;
254 } else if (!_runningUnitTests) {
255 _initForNormalAppBoot();
256 }
257}
258
259bool QGCApplication::_initVideo()
260{
261#ifdef QGC_GST_STREAMING
262 qCDebug(QGCApplicationLog) << "Using default graphics API for appsink → VideoOutput video path";
263#endif
264
265 QGCCorePlugin::instance(); // CorePlugin must be initialized before VideoManager for Video Cleanup
266 VideoManager* videoManager = VideoManager::instance();
267 videoManager->startVideoBackendInit();
268 const bool initSucceeded = !_simpleBootTest || videoManager->waitForVideoBackendReady();
269 _videoManagerInitialized = true;
270 return initSucceeded;
271}
272
273bool QGCApplication::_initQmlRootWindow()
274{
275 QQuickStyle::setStyle("Basic");
280 QObject::connect(_qmlAppEngine, &QQmlApplicationEngine::objectCreationFailed, this, QCoreApplication::quit,
281 Qt::QueuedConnection);
282
283 // Must register before createRootWindow — root QML references QGCColoredImage which resolves image://coloredsvg/...
284 // at load time.
285 _qmlAppEngine->addImageProvider(_qgcImageProviderId, new QGCImageProvider());
286 _qmlAppEngine->addImageProvider(QLatin1String(ColoredSvgImageProvider::ProviderId), new ColoredSvgImageProvider());
287
289
290 // The root QQuickWindow exists now (load() is synchronous) but its scene graph has not been
291 // initialized yet -- the only safe point to apply RHI graphics config / forced device.
293
294 return mainRootWindow() != nullptr;
295}
296
297void QGCApplication::_initForNormalAppBoot()
298{
299 (void) _initVideo();
300
301 (void) _initQmlRootWindow();
302
303 AudioOutput::instance()->init(SettingsManager::instance()->appSettings()->audioVolume(),
304 SettingsManager::instance()->appSettings()->audioMuted());
310
311 // Set the window icon now that custom plugin has a chance to override it
312#ifdef Q_OS_LINUX
313 QUrl windowIcon = QUrl("qrc:/res/qgroundcontrol.ico");
314 windowIcon = _qmlAppEngine->interceptUrl(windowIcon, QQmlAbstractUrlInterceptor::UrlString);
315 // The interceptor needs "qrc:/path" but QIcon expects ":/path"
316 setWindowIcon(QIcon(":" + windowIcon.path()));
317#endif
318
319 // Safe to show popup error messages now that main window is created
320 _showErrorsInToolbar = true;
321
322#ifdef Q_OS_LINUX
323#ifndef Q_OS_ANDROID
324#ifndef QGC_NO_SERIAL_LINK
325 if (!_runningUnitTests) {
326 // Determine if we have the correct permissions to access USB serial devices
327 QFile permFile("/etc/group");
328 if (permFile.open(QIODevice::ReadOnly)) {
329 while (!permFile.atEnd()) {
330 const QString line = permFile.readLine();
331 if (line.contains("dialout") && !line.contains(getenv("USER"))) {
332 permFile.close();
334 tr("The current user does not have the correct permissions to access serial devices. "
335 "You should also remove modemmanager since it also interferes.<br/><br/>"
336 "If you are using Ubuntu, execute the following commands to fix these issues:<br/>"
337 "<pre>sudo usermod -a -G dialout $USER<br/>"
338 "sudo apt-get remove modemmanager</pre>"));
339 break;
340 }
341 }
342 permFile.close();
343 }
344 }
345#endif
346#endif
347#endif
348
349 // Now that main window is up check for lost log files
351
352 // Load known link configurations
354
355 // Probe for joysticks
357
358 if (_settingsUpgraded) {
359 showAppMessage(tr("The format for %1 saved settings has been modified. "
360 "Your saved settings have been reset to defaults.")
361 .arg(applicationName()));
362 }
363
364 // Connect links with flag AutoconnectLink
366}
367
368void QGCApplication::reportMissingParameter(int componentId, const QString& name)
369{
370 const QPair<int, QString> missingParam(componentId, name);
371
372 if (!_missingParams.contains(missingParam)) {
373 _missingParams.append(missingParam);
374 }
375 _missingParamsDelayedDisplayTimer.start();
376}
377
378void QGCApplication::_missingParamsDisplay()
379{
380 if (_missingParams.isEmpty()) {
381 return;
382 }
383
384 QString params;
385 for (QPair<int, QString>& missingParam : _missingParams) {
386 const QString param = QStringLiteral("%1:%2").arg(missingParam.first).arg(missingParam.second);
387 if (params.isEmpty()) {
388 params += param;
389 } else {
390 params += QStringLiteral(", %1").arg(param);
391 }
392 }
393 _missingParams.clear();
394
395 showAppMessage(tr("Parameters are missing from firmware. You may be running a version of firmware which is not "
396 "fully supported or your firmware has a bug in it. Missing params: %1")
397 .arg(params));
398}
399
400QObject* QGCApplication::_rootQmlObject()
401{
402 if (_qmlAppEngine && _qmlAppEngine->rootObjects().size()) {
403 return _qmlAppEngine->rootObjects()[0];
404 }
405
406 return nullptr;
407}
408
410{
411 // PreArm messages are handled by Vehicle and shown in Map
412 if (message.startsWith(QStringLiteral("PreArm")) ||
413 message.startsWith(QStringLiteral("preflight"), Qt::CaseInsensitive)) {
414 return;
415 }
416
417 QObject* const rootQmlObject = _rootQmlObject();
418 if (rootQmlObject && _showErrorsInToolbar) {
419 QVariant varReturn;
420 QVariant varMessage = QVariant::fromValue(message);
421 QMetaObject::invokeMethod(rootQmlObject, "showCriticalVehicleMessage", Q_RETURN_ARG(QVariant, varReturn),
422 Q_ARG(QVariant, varMessage));
423 } else if (runningUnitTests() || !_showErrorsInToolbar) {
424 // Unit tests can run without UI
425 qCDebug(QGCApplicationLog) << "QGCApplication::showCriticalVehicleMessage unittest" << message;
426 } else {
427 qCWarning(QGCApplicationLog) << "Internal error";
428 }
429}
430
431void QGCApplication::showAppMessage(const QString& message, const QString& title)
432{
433 const QString dialogTitle = title.isEmpty() ? applicationName() : title;
434
435 if (runningUnitTests()) {
436 // Never show a blocking dialog during unit tests — it would hang the test runner.
437 // Logged under QGCAppMessageLog so tests can use expectAppMessage() to white-list
438 // expected dialogs without matching against the general QGCApplication category.
439 qCDebug(QGCAppMessageLog) << "showAppMessage:" << dialogTitle << "-" << message;
440 return;
441 }
442
443 QObject* const rootQmlObject = _rootQmlObject();
444 if (rootQmlObject) {
445 QVariant varReturn;
446 QVariant varMessage = QVariant::fromValue(message);
447 QMetaObject::invokeMethod(rootQmlObject, "_showMessageDialog", Q_RETURN_ARG(QVariant, varReturn),
448 Q_ARG(QVariant, dialogTitle), Q_ARG(QVariant, varMessage));
449 } else {
450 // UI isn't ready yet
451 _delayedAppMessages.append(QPair<QString, QString>(dialogTitle, message));
452 QTimer::singleShot(200, this, &QGCApplication::_showDelayedAppMessages);
453 }
454}
455
456void QGCApplication::showRebootAppMessage(const QString& message, const QString& title)
457{
458 static QTime lastRebootMessage;
459
460 const QTime currentTime = QTime::currentTime();
461 const QTime previousTime = lastRebootMessage;
462 lastRebootMessage = currentTime;
463
464 if (previousTime.isValid() && (previousTime.msecsTo(currentTime) < (60 * 1000 * 2))) {
465 // Debounce reboot messages
466 return;
467 }
468
469 showAppMessage(message, title);
470}
471
472void QGCApplication::_showDelayedAppMessages()
473{
474 if (_rootQmlObject()) {
475 for (const QPair<QString, QString>& appMsg : _delayedAppMessages) {
476 showAppMessage(appMsg.second, appMsg.first);
477 }
478 _delayedAppMessages.clear();
479 } else {
480 QTimer::singleShot(200, this, &QGCApplication::_showDelayedAppMessages);
481 }
482}
483
485{
486 if (!_mainRootWindow) {
487 _mainRootWindow = qobject_cast<QQuickWindow*>(_rootQmlObject());
488 }
489
490 return _mainRootWindow;
491}
492
494{
495 if (_rootQmlObject()) {
496 QMetaObject::invokeMethod(_rootQmlObject(), "showVehicleConfig");
497 }
498}
499
501{
502 if (_rootQmlObject()) {
503 QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
504 }
505}
506
507void QGCApplication::_checkForNewVersion()
508{
509 if (_runningUnitTests) {
510 return;
511 }
512
513 if (!_parseVersionText(applicationVersion(), _majorVersion, _minorVersion, _buildVersion)) {
514 return;
515 }
516
517 const QString versionCheckFile = QGCCorePlugin::instance()->stableVersionCheckFileUrl();
518 if (!versionCheckFile.isEmpty()) {
519 QGCFileDownload* const download = new QGCFileDownload(this);
520 (void) connect(download, &QGCFileDownload::finished, this,
521 &QGCApplication::_qgcCurrentStableVersionDownloadComplete);
522 if (!download->start(versionCheckFile)) {
523 qCDebug(QGCApplicationLog) << "Download QGC stable version failed to start" << download->errorString();
524 download->deleteLater();
525 }
526 }
527}
528
529void QGCApplication::_qgcCurrentStableVersionDownloadComplete(bool success, const QString& localFile,
530 const QString& errorMsg)
531{
532 if (success) {
533 QFile versionFile(localFile);
534 if (versionFile.open(QIODevice::ReadOnly)) {
535 QTextStream textStream(&versionFile);
536 const QString version = textStream.readLine();
537
538 qCDebug(QGCApplicationLog) << version;
539
540 int majorVersion, minorVersion, buildVersion;
541 if (_parseVersionText(version, majorVersion, minorVersion, buildVersion)) {
542 if (_majorVersion < majorVersion ||
543 ((_majorVersion == majorVersion) && (_minorVersion < minorVersion)) ||
544 ((_majorVersion == majorVersion) && (_minorVersion == minorVersion) &&
545 (_buildVersion < buildVersion))) {
546 showAppMessage(tr("There is a newer version of %1 available. You can download it from %2.")
547 .arg(applicationName())
548 .arg(QGCCorePlugin::instance()->stableDownloadLocation()),
549 tr("New Version Available"));
550 }
551 }
552 }
553 } else if (!errorMsg.isEmpty()) {
554 qCDebug(QGCApplicationLog) << "Download QGC stable version failed" << errorMsg;
555 }
556
557 sender()->deleteLater();
558}
559
560bool QGCApplication::_parseVersionText(const QString& versionString, int& majorVersion, int& minorVersion,
561 int& buildVersion)
562{
563 static const QRegularExpression regExp("v(\\d+)\\.(\\d+)\\.(\\d+)");
564 const QRegularExpressionMatch match = regExp.match(versionString);
565 if (match.hasMatch() && match.lastCapturedIndex() == 3) {
566 majorVersion = match.captured(1).toInt();
567 minorVersion = match.captured(2).toInt();
568 buildVersion = match.captured(3).toInt();
569 return true;
570 }
571
572 return false;
573}
574
576{
577 QSettings settings;
578 const QDir parameterDir = QFileInfo(settings.fileName()).dir();
579 return parameterDir.filePath(QStringLiteral("ParameterFactMetaData.json"));
580}
581
583{
584 QSettings settings;
585 const QDir airframeDir = QFileInfo(settings.fileName()).dir();
586 return airframeDir.filePath(QStringLiteral("PX4AirframeFactMetaData.xml"));
587}
588
589int QGCApplication::CompressedSignalList::_signalIndex(const QMetaMethod& method)
590{
591 if (method.methodType() != QMetaMethod::Signal) {
592 qCWarning(QGCApplicationLog) << "Internal error:" << Q_FUNC_INFO << "not a signal" << method.methodType();
593 return -1;
594 }
595
596 int index = -1;
597 const QMetaObject* metaObject = method.enclosingMetaObject();
598 for (int i = 0; i <= method.methodIndex(); i++) {
599 if (metaObject->method(i).methodType() != QMetaMethod::Signal) {
600 continue;
601 }
602 index++;
603 }
604
605 return index;
606}
607
608void QGCApplication::CompressedSignalList::add(const QMetaMethod& method)
609{
610 const QMetaObject* metaObject = method.enclosingMetaObject();
611 const int signalIndex = _signalIndex(method);
612
613 if (signalIndex != -1 && !contains(metaObject, signalIndex)) {
614 _signalMap[method.enclosingMetaObject()].insert(signalIndex);
615 }
616}
617
618void QGCApplication::CompressedSignalList::remove(const QMetaMethod& method)
619{
620 const int signalIndex = _signalIndex(method);
621 const QMetaObject* const metaObject = method.enclosingMetaObject();
622
623 if (signalIndex != -1 && _signalMap.contains(metaObject) && _signalMap[metaObject].contains(signalIndex)) {
624 _signalMap[metaObject].remove(signalIndex);
625 if (_signalMap[metaObject].count() == 0) {
626 _signalMap.remove(metaObject);
627 }
628 }
629}
630
631bool QGCApplication::CompressedSignalList::contains(const QMetaObject* metaObject, int signalIndex)
632{
633 return _signalMap.contains(metaObject) && _signalMap[metaObject].contains(signalIndex);
634}
635
636void QGCApplication::addCompressedSignal(const QMetaMethod& method)
637{
638 _compressedSignals.add(method);
639}
640
641void QGCApplication::removeCompressedSignal(const QMetaMethod& method)
642{
643 _compressedSignals.remove(method);
644}
645
646QT_WARNING_PUSH
647
648QT_WARNING_DISABLE_DEPRECATED
649bool QGCApplication::compressEvent(QEvent* event, QObject* receiver, QPostEventList* postedEvents)
650{
651 if (event->type() != QEvent::MetaCall) {
652 return QGuiApplication::compressEvent(event, receiver, postedEvents);
653 }
654
655 const QMetaCallEvent* mce = static_cast<QMetaCallEvent*>(event);
656 if (!mce->sender() || !_compressedSignals.contains(mce->sender()->metaObject(), mce->signalId())) {
657 return QGuiApplication::compressEvent(event, receiver, postedEvents);
658 }
659
660 // QMetaCallEvent::id() was removed in 6.11; its protected Data is reachable from a derived helper.
661 struct MetaCallHelper : public QMetaCallEvent {
662 int id() const { return d.method_offset_ + d.method_relative_; }
663 };
664 const auto methodId = [](const QMetaCallEvent *e) { return static_cast<const MetaCallHelper*>(e)->id(); };
665
666 for (QPostEventList::iterator it = postedEvents->begin(); it != postedEvents->end(); ++it) {
667 QPostEvent& cur = *it;
668 if (cur.receiver != receiver || cur.event == 0 || cur.event->type() != event->type()) {
669 continue;
670 }
671 const QMetaCallEvent* cur_mce = static_cast<QMetaCallEvent*>(cur.event);
672 if (cur_mce->sender() != mce->sender() || cur_mce->signalId() != mce->signalId() ||
673 methodId(cur_mce) != methodId(mce)) {
674 continue;
675 }
676
677 /* Keep The Newest Call */
678 // We can't merely qSwap the existing posted event with the new one, since QEvent
679 // keeps track of whether it has been posted. Deletion of a formerly posted event
680 // takes the posted event list mutex and does a useless search of the posted event
681 // list upon deletion. We thus clear the QEvent::posted flag before deletion.
682 struct EventHelper : private QEvent
683 {
684 static void clearPostedFlag(QEvent* ev)
685 {
686 (&static_cast<EventHelper*>(ev)->t)[1] &= ~0x8001; // Hack to clear QEvent::posted
687 }
688 };
689
690 EventHelper::clearPostedFlag(cur.event);
691 delete cur.event;
692 cur.event = event;
693 return true;
694 }
695
696 return false;
697}
698
699QT_WARNING_POP
700
702{
703 if (e->type() == QEvent::Quit) {
704 if (!_mainRootWindow) {
705 return QGuiApplication::event(e);
706 }
707 // On OSX if the user selects Quit from the menu (or Command-Q) the ApplicationWindow does not signal closing.
708 // Instead you get a Quit event here only. This in turn causes the standard QGC shutdown sequence to not run. So
709 // in this case we close the window ourselves such that the signal is sent and the normal shutdown sequence
710 // runs.
711 const bool forceClose = _mainRootWindow->property("_forceClose").toBool();
712 qCDebug(QGCApplicationLog) << "Quit event" << forceClose;
713 // forceClose
714 // true: Standard QGC shutdown sequence is complete. Let the app quit normally by falling through to the base
715 // class processing. false: QGC shutdown sequence has not been run yet. Don't let this event close the app
716 // yet. Close the main window to kick off the normal shutdown.
717 if (!forceClose) {
718 //
719 _mainRootWindow->close();
720 e->ignore();
721 return true;
722 }
723 }
724
725 return QGuiApplication::event(e);
726}
727
729{
730 return dynamic_cast<QGCImageProvider*>(_qmlAppEngine->imageProvider(_qgcImageProviderId));
731}
732
734{
735 qCDebug(QGCApplicationLog) << "Exit";
736
737 if (_videoManagerInitialized) {
739 }
740
742
743 if (_runningUnitTests || _simpleBootTest) {
744 const QSettings settings;
745 const QString settingsFile = settings.fileName();
746 if (QFile::exists(settingsFile)) {
747 if (QFile::remove(settingsFile)) {
748 qCDebug(QGCApplicationLog) << "Removed test run settings file:" << settingsFile;
749 } else {
750 qCWarning(QGCApplicationLog) << "Failed to remove test run settings file:" << settingsFile;
751 }
752 }
753
754 // Remove the app-specific settings directory (parent of ParamCache)
755 QDir settingsAppDir(ParameterManager::parameterCacheDir());
756 settingsAppDir.cdUp();
757 if (settingsAppDir.exists()) {
758 if (settingsAppDir.removeRecursively()) {
759 qCDebug(QGCApplicationLog) << "Removed test run settings directory:" << settingsAppDir.absolutePath();
760 } else {
761 qCWarning(QGCApplicationLog)
762 << "Failed to remove test run settings directory:" << settingsAppDir.absolutePath();
763 }
764 }
765
766 QDir appDir(SettingsManager::instance()->appSettings()->savePath()->rawValue().toString());
767 if (appDir.exists()) {
768 if (appDir.removeRecursively()) {
769 qCDebug(QGCApplicationLog) << "Removed test run app data directory:" << appDir.absolutePath();
770 } else {
771 qCWarning(QGCApplicationLog)
772 << "Failed to remove test run app data directory:" << appDir.absolutePath();
773 }
774 }
775 }
776
777 // This is bad, but currently qobject inheritances are incorrect and cause crashes on exit without
778 delete _qmlAppEngine;
779}
Unified file download utility with decompression, verification, and QML support.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
static constexpr const char * clearSettingsNextBootKey
Definition AppSettings.h:60
static AudioOutput * instance()
void init(Fact *volumeFact, Fact *mutedFact)
Initialize the Singleton.
Image provider that rasterizes an SVG (or any QImage-loadable Qt resource)
static constexpr const char * ProviderId
static FollowMe * instance()
Definition FollowMe.cc:35
void init()
Definition FollowMe.cc:40
static JoystickManager * instance()
void loadLinkConfigurationList()
void startAutoConnectedLinks()
static LinkManager * instance()
void init()
static LogManager * instance()
Definition LogManager.cc:76
static MAVLinkProtocol * instance()
static MultiVehicleManager * instance()
static NTRIPManager * instance()
static QDir parameterCacheDir()
The main application and management class.
void qmlAttemptWindowClose()
void reportMissingParameter(int componentId, const QString &name)
void showRebootAppMessage(const QString &message, const QString &title=QString())
void init()
Perform initialize which is common to both normal application running and unit tests.
static QString cachedParameterMetaDataFile()
QQuickWindow * mainRootWindow()
void removeCompressedSignal(const QMetaMethod &method)
void languageChanged(const QLocale &locale)
static QString cachedAirframeMetaDataFile()
void addCompressedSignal(const QMetaMethod &method)
Registers the signal such that only the last duplicate signal added is left in the queue.
void showCriticalVehicleMessage(const QString &message)
Show non-modal vehicle message to the user.
bool runningUnitTests() const
QGCImageProvider * qgcImageProvider()
void showAppMessage(const QString &message, const QString &title=QString())
Show modal application message to the user.
bool event(QEvent *e) final
virtual QQmlApplicationEngine * createQmlApplicationEngine(QObject *parent)
virtual void createRootWindow(QQmlApplicationEngine *qmlEngine)
Allows the plugin to override the creation of the root (native) window.
virtual QString stableVersionCheckFileUrl() const
virtual void init()
virtual void cleanup()
static QGCCorePlugin * instance()
File download with progress, decompression, and hash verification.
void finished(bool success, const QString &localPath, const QString &errorMessage)
bool start(const QString &remoteUrl)
QString errorString() const
This is used to expose images from ImageProtocolHandler.
Q_INVOKABLE void setCategoryEnabled(const QString &fullCategoryName, bool enable)
static QGCLoggingCategoryManager * instance()
void installFilter(const QString &commandLineLoggingOptions=QString())
static QGCPositionManager * instance()
static SettingsManager * instance()
MavlinkSettings * mavlinkSettings() const
void startVideoBackendInit()
bool waitForVideoBackendReady(std::chrono::milliseconds timeout=std::chrono::minutes(1))
static VideoManager * instance()
void init(QQuickWindow *mainWindow)
void configureMainWindow(QQuickWindow *window)
QTranslator * translator()
Translator used by openInternalQGCJsonFile for localized strings.