diff --git a/src/Android/AndroidInterface.cc b/src/Android/AndroidInterface.cc index e63b95f2d2a7..f677e29680b0 100644 --- a/src/Android/AndroidInterface.cc +++ b/src/Android/AndroidInterface.cc @@ -118,11 +118,13 @@ bool checkStoragePermissions() futurePermissionResult = QtAndroidPrivate::requestPermission(permission); permissionResult = futurePermissionResult.result(); if (permissionResult == QtAndroidPrivate::PermissionResult::Denied) { + qCWarning(AndroidInterfaceLog) << "Denied:" << permission; return false; } } } + qCDebug(AndroidInterfaceLog) << "checkStoragePermissions Accepted"; return true; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 835c7f1a5505..0b83c8dcbc9e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,8 +46,6 @@ add_subdirectory(QtLocationPlugin) target_sources(${CMAKE_PROJECT_NAME} PRIVATE main.cc - CmdLineOptParser.cc - CmdLineOptParser.h pch.h QGCApplication.cc QGCApplication.h diff --git a/src/CmdLineOptParser.cc b/src/CmdLineOptParser.cc deleted file mode 100644 index 5e4b26a411c7..000000000000 --- a/src/CmdLineOptParser.cc +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2024 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - - -/// @file -/// @brief Command line option parser implementation -/// @author Don Gagne - -#include "CmdLineOptParser.h" - -/// @brief Implements a simple command line parser which sets booleans to true if the option is found. -void ParseCmdLineOptions(int& argc, ///< count of arguments in argv - char* argv[], ///< command line arguments - CmdLineOpt_t* prgOpts, ///< command line options - size_t cOpts, ///< count of command line options - bool removeParsedOptions) ///< true: remove parsed option from argc/argv -{ - // Start with all options off - for (size_t iOption=0; iOption - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - - -/// @file -/// @brief Command line option parser declaration -/// @author Don Gagne - -#pragma once - -#include - -/// @brief Structure used to pass command line options to the ParseCmdLineOptions function. -typedef struct { - const char* optionStr; ///< Command line option, for example "--foo" - bool* optionFound; ///< If option is found this variable will be set to true - QString* optionArg; ///< Option has additional argument, form is option:arg -} CmdLineOpt_t; - -void ParseCmdLineOptions(int& argc, - char* argv[], - CmdLineOpt_t* prgOpts, - size_t cOpts, - bool removeParsedOptions); - diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 07fd6f5d4ac8..d12c67bb56e4 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -27,7 +27,6 @@ #include "QGCLogging.h" #include "AudioOutput.h" -#include "CmdLineOptParser.h" #include "FollowMe.h" #include "JoystickManager.h" #include "JsonHelper.h" @@ -36,11 +35,13 @@ #include "MultiVehicleManager.h" #include "ParameterManager.h" #include "PositionManager.h" +#include "QGCCommandLineParser.h" #include "QGCCorePlugin.h" #include "QGCFileDownload.h" #include "QGCImageProvider.h" #include "QGCLoggingCategory.h" #include "SettingsManager.h" +#include "MavlinkSettings.h" #include "AppSettings.h" #include "UDPLink.h" #include "Vehicle.h" @@ -53,32 +54,22 @@ QGC_LOGGING_CATEGORY(QGCApplicationLog, "qgc.qgcapplication") -QGCApplication::QGCApplication(int &argc, char *argv[], bool unitTesting, bool simpleBootTest) +QGCApplication::QGCApplication(int &argc, char *argv[], const QGCCommandLineParser::CommandLineParseResult &cli) : QApplication(argc, argv) - , _runningUnitTests(unitTesting) - , _simpleBootTest(simpleBootTest) + , _runningUnitTests(cli.runningUnitTests) + , _simpleBootTest(cli.simpleBootTest) + , _fakeMobile(cli.fakeMobile) + , _logOutput(cli.logOutput) + , _systemId(cli.systemId.value_or(0)) { _msecsElapsedTime.start(); // Setup for network proxy support QNetworkProxyFactory::setUseSystemConfiguration(true); - // Parse command line options - bool fClearSettingsOptions = false; // Clear stored settings - bool fClearCache = false; // Clear parameter/airframe caches - bool logging = false; // Turn on logging - QString loggingOptions; - - CmdLineOpt_t rgCmdLineOptions[] = { - { "--clear-settings", &fClearSettingsOptions, nullptr }, - { "--clear-cache", &fClearCache, nullptr }, - { "--logging", &logging, &loggingOptions }, - { "--fake-mobile", &_fakeMobile, nullptr }, - { "--log-output", &_logOutput, nullptr }, - // Add additional command line option flags here - }; - - ParseCmdLineOptions(argc, argv, rgCmdLineOptions, std::size(rgCmdLineOptions), false); + bool fClearSettingsOptions = cli.clearSettingsOptions; // Clear stored settings + const bool fClearCache = cli.clearCache; // Clear parameter/airframe caches + const QString loggingOptions = cli.loggingOptions.value_or(QString("")); // Set up timer for delayed missing fact display _missingParamsDelayedDisplayTimer.setSingleShot(true); @@ -87,7 +78,7 @@ QGCApplication::QGCApplication(int &argc, char *argv[], bool unitTesting, bool s // Set application information QString applicationName; - if (_runningUnitTests || simpleBootTest) { + if (_runningUnitTests || _simpleBootTest) { // We don't want unit tests to use the same QSettings space as the normal app. So we tweak the app // name. Also we want to run unit tests with clean settings every time. applicationName = QStringLiteral("%1_unittest").arg(QGC_APP_NAME); @@ -117,7 +108,7 @@ QGCApplication::QGCApplication(int &argc, char *argv[], bool unitTesting, bool s // The setting will delete all settings on this boot fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey); - if (_runningUnitTests || simpleBootTest) { + if (_runningUnitTests || _simpleBootTest) { // Unit tests run with clean settings fClearSettingsOptions = true; } @@ -219,13 +210,17 @@ QGCApplication::~QGCApplication() void QGCApplication::init() { SettingsManager::instance()->init(); + if (_systemId > 0) { + qCDebug(QGCApplicationLog) << "Setting MAVLink System ID to:" << _systemId; + SettingsManager::instance()->mavlinkSettings()->gcsMavlinkSystemID()->setRawValue(_systemId); + } // Although this should really be in _initForNormalAppBoot putting it here allowws us to create unit tests which pop up more easily - if(QFontDatabase::addApplicationFont(":/fonts/opensans") < 0) { + if (QFontDatabase::addApplicationFont(":/fonts/opensans") < 0) { qCWarning(QGCApplicationLog) << "Could not load /fonts/opensans font"; } - if(QFontDatabase::addApplicationFont(":/fonts/opensans-demibold") < 0) { + if (QFontDatabase::addApplicationFont(":/fonts/opensans-demibold") < 0) { qCWarning(QGCApplicationLog) << "Could not load /fonts/opensans-demibold font"; } diff --git a/src/QGCApplication.h b/src/QGCApplication.h index 3eb8a68afac4..b22f7de38a3f 100644 --- a/src/QGCApplication.h +++ b/src/QGCApplication.h @@ -17,6 +17,10 @@ #include #include +namespace QGCCommandLineParser { + struct CommandLineParseResult; +} + class QQmlApplicationEngine; class QQuickWindow; class QGCImageProvider; @@ -50,7 +54,7 @@ class QGCApplication : public QApplication /// Unit Test have access to creating and destroying singletons friend class UnitTest; public: - QGCApplication(int &argc, char *argv[], bool unitTesting, bool simpleBootTest); + QGCApplication(int &argc, char *argv[], const QGCCommandLineParser::CommandLineParseResult &args); ~QGCApplication(); /// Sets the persistent flag to delete all settings the next time QGroundControl is started. @@ -140,14 +144,15 @@ private slots: bool _runningUnitTests = false; bool _simpleBootTest = false; + bool _fakeMobile = false; ///< true: Fake ui into displaying mobile interface + bool _logOutput = false; ///< true: Log Qt debug output to file + quint8 _systemId = 0; ///< MAVLink system ID, 0 means not set static constexpr int _missingParamsDelayedDisplayTimerTimeout = 1000; ///< Timeout to wait for next missing fact to come in before display QTimer _missingParamsDelayedDisplayTimer; ///< Timer use to delay missing fact display QList> _missingParams; ///< List of missing parameter component id:name QQmlApplicationEngine *_qmlAppEngine = nullptr; - bool _logOutput = false; ///< true: Log Qt debug output to file - bool _fakeMobile = false; ///< true: Fake ui into displaying mobile interface bool _settingsUpgraded = false; ///< true: Settings format has been upgrade to new version int _majorVersion = 0; int _minorVersion = 0; diff --git a/src/Utilities/CMakeLists.txt b/src/Utilities/CMakeLists.txt index 0af6035965c6..7291e9896a4c 100644 --- a/src/Utilities/CMakeLists.txt +++ b/src/Utilities/CMakeLists.txt @@ -14,6 +14,8 @@ target_sources(${CMAKE_PROJECT_NAME} Platform.h QGC.cc QGC.h + QGCCommandLineParser.cc + QGCCommandLineParser.h QGCLogging.cc QGCLogging.h QGCLoggingCategory.cc diff --git a/src/Utilities/Platform.cc b/src/Utilities/Platform.cc index f16a04690072..ebf110bfd91d 100644 --- a/src/Utilities/Platform.cc +++ b/src/Utilities/Platform.cc @@ -12,8 +12,14 @@ #include #include +#include "QGCCommandLineParser.h" + +#ifdef Q_OS_ANDROID + #include "AndroidInterface.h" +#endif + #if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID) -#include "SignalHandler.h" + #include "SignalHandler.h" #endif #if defined(Q_OS_MACOS) @@ -126,7 +132,7 @@ void setWindowsErrorModes(bool quietWindowsAsserts) } // namespace -void Platform::setupPreApp(bool quietWindowsAsserts) +void Platform::setupPreApp(const QGCCommandLineParser::CommandLineParseResult &cli) { #ifdef Q_OS_UNIX if (!qEnvironmentVariableIsSet("QT_ASSUME_STDERR_HAS_CONSOLE")) { @@ -138,15 +144,27 @@ void Platform::setupPreApp(bool quietWindowsAsserts) #endif #ifdef Q_OS_WIN + // (void) qputenv("QT_OPENGL_BUGLIST", ":/opengl/resources/opengl/buglist.json"); if (!qEnvironmentVariableIsSet("QT_WIN_DEBUG_CONSOLE")) { (void) qputenv("QT_WIN_DEBUG_CONSOLE", "attach"); } - setWindowsErrorModes(quietWindowsAsserts); + setWindowsErrorModes(cli.quietWindowsAsserts); #endif #ifdef Q_OS_MACOS disableAppNapViaInfoDict(); #endif + + if (cli.useDesktopGL) { + QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL); + } + + if (cli.useSwRast) { + QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL); + } + + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + QCoreApplication::setAttribute(Qt::AA_CompressTabletEvents); } void Platform::setupPostApp() @@ -155,4 +173,8 @@ void Platform::setupPostApp() SignalHandler* signalHandler = new SignalHandler(QCoreApplication::instance()); (void) signalHandler->setupSignalHandlers(); #endif + +#ifdef Q_OS_ANDROID + AndroidInterface::checkStoragePermissions(); +#endif } diff --git a/src/Utilities/Platform.h b/src/Utilities/Platform.h index fe9e1ec37367..516931f894b6 100644 --- a/src/Utilities/Platform.h +++ b/src/Utilities/Platform.h @@ -9,10 +9,14 @@ #pragma once +namespace QGCCommandLineParser { + struct CommandLineParseResult; +} + namespace Platform { // Call before constructing Q(Core)Application. -void setupPreApp(bool quietWindowsAsserts); +void setupPreApp(const QGCCommandLineParser::CommandLineParseResult &cli); // Call after Q(Core)Application exists and logging is installed. void setupPostApp(); diff --git a/src/Utilities/QGCCommandLineParser.cc b/src/Utilities/QGCCommandLineParser.cc new file mode 100644 index 000000000000..34d17fed5466 --- /dev/null +++ b/src/Utilities/QGCCommandLineParser.cc @@ -0,0 +1,269 @@ +/**************************************************************************** + * + * (c) 2009-2024 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "QGCCommandLineParser.h" + +#include +#include + +namespace QGCCommandLineParser { + +static const QString kOptSystemId = QStringLiteral("system-id"); +static const QString kOptClearSettings = QStringLiteral("clear-settings"); +static const QString kOptClearCache = QStringLiteral("clear-cache"); +static const QString kOptLogging = QStringLiteral("logging"); +static const QString kOptLogOutput = QStringLiteral("log-output"); +static const QString kOptSimpleBoot = QStringLiteral("simple-boot-test"); +static const QString kOptFakeMobile = QStringLiteral("fake-mobile"); +static const QString kOptAllowMultiple = QStringLiteral("allow-multiple"); +static const QString kOptUnittest = QStringLiteral("unittest"); +static const QString kOptUnittestStress = QStringLiteral("unittest-stress"); +static const QString kOptDesktop = QStringLiteral("desktop"); +static const QString kOptSwrast = QStringLiteral("swrast"); +static const QString kOptNoWinAssertUI = QStringLiteral("no-windows-assert-ui"); + +static QStringList normalizeArgs(const QStringList &args) +{ + QStringList out; + out.reserve(args.size() + 4); + for (const QString &arg : args) { + if (arg == QStringLiteral("--unittest")) { + out << arg << QString(); // empty value token prevents "Missing value" + continue; + } + + if (arg.startsWith("--") && arg.contains(':')) { + const int idx = arg.indexOf(':'); + const QString opt = arg.left(idx); + const QString val = arg.mid(idx + 1); + out << opt; + if (!val.isEmpty()) { + out << val; + } else if (opt == QStringLiteral("--unittest")) { + out << QString(); + } + } else { + out << arg; + } + } + return out; +} + +CommandLineParseResult parseCommandLine() +{ + CommandLineParseResult out{}; + out.parser = std::make_unique(); + + QCommandLineParser& parser = *out.parser; + parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); + parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsOptions); + parser.setApplicationDescription(QCoreApplication::applicationName()); + + const QCommandLineOption helpOption = parser.addHelpOption(); + const QCommandLineOption versionOption = parser.addVersionOption(); + + const QCommandLineOption systemIdOpt( + kOptSystemId, + QCoreApplication::translate("main", "MAVLink GCS system id."), + QCoreApplication::translate("main", "id")); + (void) parser.addOption(systemIdOpt); + + const QCommandLineOption clearSettingsOpt( + kOptClearSettings, + QCoreApplication::translate("main", "Clear stored application settings.")); + (void) parser.addOption(clearSettingsOpt); + + const QCommandLineOption clearCacheOpt( + kOptClearCache, + QCoreApplication::translate("main", "Clear parameter and airframe caches.")); + (void) parser.addOption(clearCacheOpt); + + const QCommandLineOption loggingOpt( + kOptLogging, + QCoreApplication::translate("main", "Enable logging with optional rules string."), + QCoreApplication::translate("main", "rules")); + (void) parser.addOption(loggingOpt); + + const QCommandLineOption logOutputOpt( + kOptLogOutput, + QCoreApplication::translate("main", "Log to console.")); + (void) parser.addOption(logOutputOpt); + + const QCommandLineOption simpleBootOpt( + kOptSimpleBoot, + QCoreApplication::translate("main", "Initialize subsystems and exit.")); + (void) parser.addOption(simpleBootOpt); + +#if defined(QGC_UNITTEST_BUILD) + const QCommandLineOption unittestOpt( + kOptUnittest, + QCoreApplication::translate("main", "Run unit tests (optional filter value)."), + QCoreApplication::translate("main", "filter")); + (void) parser.addOption(unittestOpt); + + const QCommandLineOption unittestStressOpt( + kOptUnittestStress, + QCoreApplication::translate("main", "Stress unit tests."), + QCoreApplication::translate("main", "count")); + (void) parser.addOption(unittestStressOpt); +#endif + +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + const QCommandLineOption fakeMobileOpt( + kOptFakeMobile, + QCoreApplication::translate("main", "Run with mobile-style UI.")); + (void) parser.addOption(fakeMobileOpt); + + const QCommandLineOption allowMultipleOpt( + kOptAllowMultiple, + QCoreApplication::translate("main", "Bypass single-instance guard.")); + (void) parser.addOption(allowMultipleOpt); +#endif + +#if defined(Q_OS_WIN) || defined(Q_OS_MACOS) + const QCommandLineOption swrastOpt( + kOptSwrast, + QCoreApplication::translate("main", "Force software OpenGL.")); + (void) parser.addOption(swrastOpt); +#endif + +#ifdef Q_OS_WIN + const QCommandLineOption desktopOpt( + kOptDesktop, + QCoreApplication::translate("main", "Force Desktop OpenGL.")); + (void) parser.addOption(desktopOpt); + + const QCommandLineOption quietWinAssertOpt( + kOptNoWinAssertUI, + QCoreApplication::translate("main", "Disable Windows assert dialog boxes.")); + (void) parser.addOption(quietWinAssertOpt); +#endif + + const QStringList normalizedArgs = normalizeArgs(QCoreApplication::arguments()); + parser.process(normalizedArgs); + + out.unknownOptions = parser.unknownOptionNames(); +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + if (out.unknownOptions.contains(kOptFakeMobile) || out.unknownOptions.contains(kOptAllowMultiple)) { + out.statusCode = CommandLineParseResult::Status::Error; + out.errorString = QCoreApplication::translate("main", "--%1/--%2 are not supported on this platform.").arg(kOptFakeMobile, kOptAllowMultiple); + return out; + } +#endif +#ifndef Q_OS_WIN + if (out.unknownOptions.contains(kOptDesktop) || out.unknownOptions.contains(kOptSwrast) || out.unknownOptions.contains(kOptNoWinAssertUI)) { + out.statusCode = CommandLineParseResult::Status::Error; + out.errorString = QCoreApplication::translate("main", "--%1/--%2/--%3 are only supported on Windows.").arg(kOptDesktop, kOptSwrast, kOptNoWinAssertUI); + return out; + } +#endif +#if !defined(QGC_UNITTEST_BUILD) + if (out.unknownOptions.contains(kOptUnittest) || out.unknownOptions.contains(kOptUnittestStress)) { + out.statusCode = CommandLineParseResult::Status::Error; + out.errorString = QCoreApplication::translate("main", "--%1/--%2 options are only available in unittest builds.").arg(kOptUnittest, kOptUnittestStress); + return out; + } +#endif + if (!out.unknownOptions.isEmpty()) { + out.statusCode = CommandLineParseResult::Status::Error; + out.errorString = QCoreApplication::translate("main", "Unknown options: %1").arg(out.unknownOptions.join(", ")); + return out; + } + + out.positional = parser.positionalArguments(); + if (!out.positional.isEmpty()) { + out.statusCode = CommandLineParseResult::Status::Error; + out.errorString = QCoreApplication::translate("main", "Unexpected positional arguments: %1").arg(out.positional.join(", ")); + return out; + } + + if (parser.isSet(helpOption)) { + out.statusCode = CommandLineParseResult::Status::HelpRequested; + out.helpText = parser.helpText(); + return out; + } + + if (parser.isSet(versionOption)) { + out.statusCode = CommandLineParseResult::Status::VersionRequested; + out.versionText = QCoreApplication::applicationVersion(); + return out; + } + + if (parser.isSet(systemIdOpt)) { + const QString systemIdStr = parser.value(systemIdOpt); + bool ok = false; + const uint systemId = systemIdStr.toUInt(&ok); + if (!ok || (systemId < 1) || (systemId > 255)) { + out.statusCode = CommandLineParseResult::Status::Error; + out.errorString = QCoreApplication::translate("main", "Invalid System ID (must be 1-255): %1").arg(systemIdStr); + return out; + } + out.systemId = static_cast(systemId); + } + + out.clearSettingsOptions = parser.isSet(clearSettingsOpt); + out.clearCache = parser.isSet(clearCacheOpt); + if (parser.isSet(loggingOpt)) { + out.loggingOptions = parser.value(loggingOpt); + } + out.logOutput = parser.isSet(logOutputOpt); + out.simpleBootTest = parser.isSet(simpleBootOpt); + +#if defined(QGC_UNITTEST_BUILD) + if (parser.isSet(unittestOpt)) { + out.runningUnitTests = true; + const QStringList vals = parser.values(unittestOpt); + if (vals.isEmpty()) { // || ((vals.size() == 1) && vals.first().isEmpty()) + // No ":name" given → run all tests + out.unitTests.clear(); + } else { + // One or more ":name" values provided + out.unitTests = vals; + } + } + + if (parser.isSet(unittestStressOpt)) { + out.runningUnitTests = true; + out.stressUnitTests = true; + const QString stress = parser.value(unittestStressOpt); + if (stress.isEmpty()) { + out.stressUnitTestsCount = 20; + } else { + bool ok = false; + const uint count = stress.toUInt(&ok); + if (!ok || (count == 0)) { + out.statusCode = CommandLineParseResult::Status::Error; + out.errorString = QCoreApplication::translate("main", "Invalid stress unit test count: %1").arg(count); + return out; + } + out.stressUnitTestsCount = count; + } + } +#endif + +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + out.fakeMobile = parser.isSet(fakeMobileOpt); + out.allowMultiple = parser.isSet(allowMultipleOpt); +#endif + +#ifdef Q_OS_WIN + out.useDesktopGL = parser.isSet(desktopOpt); + out.quietWindowsAsserts = parser.isSet(quietWinAssertOpt); +#endif + +#if defined(Q_OS_WIN) || defined(Q_OS_MACOS) + out.useSwRast = parser.isSet(swrastOpt); +#endif + + out.statusCode = CommandLineParseResult::Status::Ok; + + return out; +} + +} // namespace QGCCommandLineParser diff --git a/src/Utilities/QGCCommandLineParser.h b/src/Utilities/QGCCommandLineParser.h new file mode 100644 index 000000000000..749140b39184 --- /dev/null +++ b/src/Utilities/QGCCommandLineParser.h @@ -0,0 +1,60 @@ +/**************************************************************************** + * + * (c) 2009-2024 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include + +#include +#include + +namespace QGCCommandLineParser { + +struct CommandLineParseResult +{ + enum class Status { + Ok, + Error, + VersionRequested, + HelpRequested + } statusCode = Status::Ok; + + std::unique_ptr parser; + + std::optional errorString; + QString helpText; + QString versionText; + + QStringList positional; + QStringList unknownOptions; + + std::optional systemId; + bool clearSettingsOptions = false; + bool clearCache = false; + std::optional loggingOptions; + bool logOutput = false; + bool simpleBootTest = false; + + bool runningUnitTests = false; + QStringList unitTests; + bool stressUnitTests = false; + uint stressUnitTestsCount = 0; + + bool fakeMobile = false; + bool allowMultiple = false; + + bool useDesktopGL = false; + bool useSwRast = false; + bool quietWindowsAsserts = false; +}; + +/// Parse the application's command-line arguments into result. +CommandLineParseResult parseCommandLine(); + +} // namespace QGCCommandLineParser diff --git a/src/main.cc b/src/main.cc index 39f1b064d201..81ca2b54509b 100644 --- a/src/main.cc +++ b/src/main.cc @@ -7,15 +7,12 @@ * ****************************************************************************/ -#include // std::size #include #include #include "QGCApplication.h" +#include "QGCCommandLineParser.h" #include "QGCLogging.h" -#include "CmdLineOptParser.h" -#include "SettingsManager.h" -#include "MavlinkSettings.h" #include "Platform.h" #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) @@ -23,10 +20,6 @@ #include "RunGuard.h" #endif -#ifdef Q_OS_ANDROID - #include "AndroidInterface.h" -#endif - #ifdef Q_OS_LINUX #include #include @@ -38,83 +31,51 @@ int main(int argc, char *argv[]) { - bool runUnitTests = false; - bool simpleBootTest = false; - QString systemIdStr; - bool hasSystemId = false; - bool bypassRunGuard = false; - - bool stressUnitTests = false; // Stress test unit tests - bool quietWindowsAsserts = false; // Suppress Windows assert UI - QString unitTestOptions; - - CmdLineOpt_t rgCmdLineOptions[] = { -#ifdef QT_DEBUG - { "--unittest", &runUnitTests, &unitTestOptions }, - { "--unittest-stress", &stressUnitTests, &unitTestOptions }, - { "--no-windows-assert-ui", &quietWindowsAsserts, nullptr }, - { "--allow-multiple", &bypassRunGuard, nullptr }, -#endif - { "--system-id", &hasSystemId, &systemIdStr }, - { "--simple-boot-test", &simpleBootTest, nullptr }, - }; - - ParseCmdLineOptions(argc, argv, rgCmdLineOptions, std::size(rgCmdLineOptions), false); - -#ifdef QGC_UNITTEST_BUILD - if (stressUnitTests) { - runUnitTests = true; - } -#ifdef Q_OS_WIN - if (runUnitTests) { - quietWindowsAsserts = true; - } -#endif -#endif - -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) - // Single-instance guard for desktop - const QString runguardString = QStringLiteral("%1 RunGuardKey").arg(QGC_APP_NAME); - RunGuard guard(runguardString); - if (!bypassRunGuard && !guard.tryToRun()) { - const QApplication errorApp(argc, argv); - (void) QMessageBox::critical(nullptr, QObject::tr("Error"), - QObject::tr("A second instance of %1 is already running. Please close the other instance and try again.").arg(QGC_APP_NAME) - ); - return -1; - } -#endif - #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) if (::getuid() == 0) { const QApplication errorApp(argc, argv); - (void) QMessageBox::critical(nullptr, QObject::tr("Error"), - QObject::tr("You are running %1 as root. " - "You should not do this since it will cause other issues with %1. " - "%1 will now exit.

").arg(QGC_APP_NAME) - ); + // QErrorMessage + (void) QMessageBox::critical(nullptr, + QCoreApplication::translate("main", "Error"), + QCoreApplication::translate("main", "You are running %1 as root. " + "You should not do this since it will cause other issues with %1. " + "%1 will now exit.

").arg(QGC_APP_NAME)); return -1; } #endif - // Early platform setup before Qt app construction - Platform::setupPreApp(quietWindowsAsserts); - -#ifdef Q_OS_WIN - // Allow command-line override of renderer - for (int i = 0; i < argc; i++) { - const QString arg(argv[i]); - if (arg == QStringLiteral("-desktop")) { - QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL); - break; - } else if (arg == QStringLiteral("-swrast")) { - QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL); - break; + QGCCommandLineParser::CommandLineParseResult args; + { + const QCoreApplication pre(argc, argv); + QCoreApplication::setApplicationName(QStringLiteral(QGC_APP_NAME)); + args = QGCCommandLineParser::parseCommandLine(); + if (args.statusCode == QGCCommandLineParser::CommandLineParseResult::Status::Error) { + const QString errorMessage = args.errorString.value_or(QStringLiteral("Unknown error occurred")); + qCritical() << qPrintable(errorMessage); + // TODO: QCommandLineParser::showMessageAndExit(QCommandLineParser::MessageType::Error) - Qt6.9 + return 1; + } + } + +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + if (!args.allowMultiple) { + const QString runguardString = QStringLiteral("%1 RunGuardKey").arg(QStringLiteral(QGC_APP_NAME)); + RunGuard guard(runguardString); + if (!guard.tryToRun()) { + const QApplication errorApp(argc, argv); + (void) QMessageBox::critical(nullptr, + QCoreApplication::translate("main", "Error"), + QCoreApplication::translate("main", "A second instance of %1 is already running. " + "Please close the other instance and try again.").arg(QStringLiteral(QGC_APP_NAME))); + return -1; } } #endif - QGCApplication app(argc, argv, runUnitTests, simpleBootTest); + // Early platform setup before Qt app construction + Platform::setupPreApp(args); + + QGCApplication app(argc, argv, args); QGCLogging::installHandler(); @@ -123,35 +84,17 @@ int main(int argc, char *argv[]) app.init(); - // Optional: set MAVLink System ID from CLI, e.g. --system-id:255 - if (hasSystemId) { - bool ok = false; - const int systemId = systemIdStr.toInt(&ok); - if (ok && systemId >= 1 && systemId <= 255) { // MAVLink system IDs are 1..255 for GCS use - qDebug() << "Setting MAVLink System ID to:" << systemId; - SettingsManager::instance()->mavlinkSettings()->gcsMavlinkSystemID()->setRawValue(systemId); - } else { - qDebug() << "Not setting MAVLink System ID. It must be between 1 and 255. Invalid value:" << systemIdStr; - } - } - int exitCode = 0; - + if (args.runningUnitTests) { #ifdef QGC_UNITTEST_BUILD - if (runUnitTests) { - exitCode = runTests(stressUnitTests, unitTestOptions); - } else + exitCode = QGCUnitTest::runTests(args.stressUnitTests, args.unitTests); #endif - { -#ifdef Q_OS_ANDROID - AndroidInterface::checkStoragePermissions(); -#endif - if (!simpleBootTest) { - exitCode = app.exec(); - } + } else if (!args.simpleBootTest) { + exitCode = app.exec(); } app.shutdown(); + qDebug() << "Exiting main"; return exitCode; } diff --git a/test/UnitTestList.cc b/test/UnitTestList.cc index 26a80aac29c3..882c6feb149e 100644 --- a/test/UnitTestList.cc +++ b/test/UnitTestList.cc @@ -22,8 +22,6 @@ #include "PX4LogParserTest.h" // #include "ULogParserTest.h" - - // AutoPilotPlugins // #include "RadioConfigTest.h" @@ -116,7 +114,7 @@ QGC_LOGGING_CATEGORY(UnitTestsLog, "qgc.test.unittestlist") -int runTests(bool stress, QStringView unitTestOptions) +int QGCUnitTest::runTests(bool stress, const QStringList& unitTests) { // ADSB UT_REGISTER_TEST(ADSBTest) @@ -218,15 +216,18 @@ int runTests(bool stress, QStringView unitTestOptions) // UT_REGISTER_TEST(TCPLinkTest) int result = 0; - for (int i=0; i < (stress ? 20 : 1); i++) { - // Run the test - const int failures = UnitTest::run(unitTestOptions); + int failures = 0; + for (const QString& test: unitTests) { + // Run the test + failures += UnitTest::run(test); + } + if (failures == 0) { qDebug() << "ALL TESTS PASSED"; result = 0; } else { - qDebug() << failures << " TESTS FAILED!"; + qWarning() << failures << "TESTS FAILED!"; result = -failures; break; } diff --git a/test/UnitTestList.h b/test/UnitTestList.h index 118479171761..a66a066e56cb 100644 --- a/test/UnitTestList.h +++ b/test/UnitTestList.h @@ -1,7 +1,21 @@ +/**************************************************************************** + * + * (c) 2009-2020 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + #pragma once #include +#include Q_DECLARE_LOGGING_CATEGORY(UnitTestsLog) -int runTests(bool stress, QStringView unitTestOptions); +namespace QGCUnitTest { + +int runTests(bool stress, const QStringList& unitTests); + +}