From 45bb199a0d380e62aac6af461b7b1d6358ef0db7 Mon Sep 17 00:00:00 2001 From: Vasily Date: Sat, 2 Mar 2019 19:40:13 +0100 Subject: [PATCH] Replace arg parsing function with Qt built ins This automatically provides version and help functions, and offloads the arg parsing to a third party lib. --- src/main.cpp | 66 ++++++------- src/utils/setup.h | 234 +++++++++++++++++++++++++++++----------------- 2 files changed, 176 insertions(+), 124 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 1966f18..849a601 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,52 +6,39 @@ int main( int argc, char* argv[] ) { setUpLogging( argc, argv ); - const bool desktopMode = argument::CheckCommandLineArgument( - argc, argv, argument::kDesktopMode ); - const bool noSound - = argument::CheckCommandLineArgument( argc, argv, argument::kNoSound ); - const bool noManifest = argument::CheckCommandLineArgument( - argc, argv, argument::kNoManifest ); - const bool installManifest = argument::CheckCommandLineArgument( - argc, argv, argument::kInstallManifest ); - const bool removeManifest = argument::CheckCommandLineArgument( - argc, argv, argument::kRemoveManifest ); + // QCoreApplication (QApplication inherits it.) can parse the args from + // argc and argv. We don't use this since it's relatively slow and we + // need the args before the main Qt stuff happens. If the arg parsing + // needs of the program grow it could be useful to utilize the Qt arg + // parsing. + MyQApplication mainEventLoop( argc, argv ); + mainEventLoop.setOrganizationName( + advsettings::OverlayController::applicationOrganizationName ); + mainEventLoop.setApplicationName( + advsettings::OverlayController::applicationName ); + mainEventLoop.setApplicationDisplayName( + advsettings::OverlayController::applicationDisplayName ); + mainEventLoop.setApplicationVersion( + advsettings::OverlayController::applicationVersionString ); - // If a command line arg is set, make sure the logs reflect that. - LOG_IF( desktopMode, INFO ) << "Desktop mode enabled."; - LOG_IF( noSound, INFO ) << "Sound effects disabled."; - LOG_IF( noManifest, INFO ) << "vrmanifest disabled."; - LOG_IF( installManifest, INFO ) << "Install manifest enabled."; - LOG_IF( removeManifest, INFO ) << "Remove manifest enabled."; + qInstallMessageHandler( mainQtMessageHandler ); + + const auto commandLineArgs + = argument::returnCommandLineParser( mainEventLoop ); // It is important that either install_manifest or remove_manifest are true, // otherwise the handleManifests function will not behave properly. - if ( installManifest || removeManifest ) + if ( commandLineArgs.forceInstallManifest + || commandLineArgs.forceRemoveManifest ) { // The function does not return, it exits inside the function // with an appropriate exit code. - manifest::handleManifests( installManifest, removeManifest ); + manifest::handleManifests( commandLineArgs.forceInstallManifest, + commandLineArgs.forceRemoveManifest ); } try { - // QCoreApplication (QApplication inherits it.) can parse the args from - // argc and argv. We don't use this since it's relatively slow and we - // need the args before the main Qt stuff happens. If the arg parsing - // needs of the program grow it could be useful to utilize the Qt arg - // parsing. - MyQApplication mainEventLoop( argc, argv ); - mainEventLoop.setOrganizationName( - advsettings::OverlayController::applicationOrganizationName ); - mainEventLoop.setApplicationName( - advsettings::OverlayController::applicationName ); - mainEventLoop.setApplicationDisplayName( - advsettings::OverlayController::applicationDisplayName ); - mainEventLoop.setApplicationVersion( - advsettings::OverlayController::applicationVersionString ); - - qInstallMessageHandler( mainQtMessageHandler ); - // QSettings contains a platform independant way of storing settings in // an .ini file. QSettings appSettings( QSettings::IniFormat, @@ -67,8 +54,9 @@ int main( int argc, char* argv[] ) // The OverlayController handles the majority of the application specfic // things. It contains all the other tabs. - advsettings::OverlayController controller( - desktopMode, noSound, qmlEngine ); + advsettings::OverlayController controller( commandLineArgs.desktopMode, + commandLineArgs.forceNoSound, + qmlEngine ); QString path = QStandardPaths::locate( QStandardPaths::AppDataLocation, @@ -88,7 +76,7 @@ int main( int argc, char* argv[] ) advsettings::OverlayController::applicationKey ); // Attempts to install the application manifest on all "regular" starts. - if ( !desktopMode && !noManifest ) + if ( !commandLineArgs.desktopMode && !commandLineArgs.forceNoManifest ) { try { @@ -104,7 +92,7 @@ int main( int argc, char* argv[] ) } // Creates an identical settings menu on the desktop as well as in VR. - if ( desktopMode ) + if ( commandLineArgs.desktopMode ) { auto m_pWindow = new QQuickWindow(); qobject_cast( quickObj ) diff --git a/src/utils/setup.h b/src/utils/setup.h index 713ba65..515aa54 100644 --- a/src/utils/setup.h +++ b/src/utils/setup.h @@ -18,33 +18,163 @@ enum ReturnErrorCode OPENVR_INIT_ERROR = -2, }; -namespace argument +struct CommandLineOptions { -// Checks whether a specific string was passed as a launch argument. -bool CheckCommandLineArgument( int argc, - char* argv[], - const std::string parameter ) + const bool desktopMode = false; + const bool forceNoSound = false; + const bool forceNoManifest = false; + const bool forceInstallManifest = false; + const bool forceRemoveManifest = false; +}; + +// Manages the programs control flow and main settings. +class MyQApplication : public QApplication { - // The old way was one giant loop that tested every single parameter. - // Having an individual loop for all args most likely has a very small - // performance penalty compared to the original solution, but the gains in - // readability and ease of extension of not having a hundred line+ for loop - // are worth it. - for ( int i = 0; i < argc; i++ ) +public: + using QApplication::QApplication; + + // Intercept event calls and log them on exceptions. + // From the official docs + // https://doc.qt.io/qt-5/qcoreapplication.html#notify: + // "Future direction: + // This function will not be called for objects that live outside the main + // thread in Qt 6. Applications that need that functionality should find + // other solutions for their event inspection needs in the meantime. The + // change may be extended to the main thread, causing this function to be + // deprecated." + // Should look into replacements for this function if Qt 6 ever rolls + // around. There are multiple suggestions for other solutions in the + // provided link. + virtual bool notify( QObject* receiver, QEvent* event ) override { - if ( std::string( argv[i] ) == parameter ) + try { - return true; + return QApplication::notify( receiver, event ); } + catch ( std::exception& e ) + { + LOG( ERROR ) << "Exception thrown from an event handler: " + << e.what(); + } + return false; + } +}; + +// The default Qt message handler prints to stdout on X11 and to the debugger on +// Windows. That is borderline useless for us, therefore we create our own +// message handler. +void mainQtMessageHandler( QtMsgType type, + const QMessageLogContext& context, + const QString& msg ) +{ + QByteArray localMsg = msg.toLocal8Bit(); + switch ( type ) + { + case QtDebugMsg: + LOG( DEBUG ) << localMsg.constData() << " (" << context.file << ":" + << context.line << ")"; + break; + case QtInfoMsg: + LOG( INFO ) << localMsg.constData() << " (" << context.file << ":" + << context.line << ")"; + break; + case QtWarningMsg: + LOG( WARNING ) << localMsg.constData() << " (" << context.file << ":" + << context.line << ")"; + break; + case QtCriticalMsg: + LOG( ERROR ) << localMsg.constData() << " (" << context.file << ":" + << context.line << ")"; + break; + case QtFatalMsg: + LOG( FATAL ) << localMsg.constData() << " (" << context.file << ":" + << context.line << ")"; + break; } - return false; } -constexpr auto kDesktopMode = "-desktop"; -constexpr auto kNoSound = "-nosound"; -constexpr auto kNoManifest = "-nomanifest"; -constexpr auto kInstallManifest = "-installmanifest"; -constexpr auto kRemoveManifest = "-removemanifest"; +namespace argument +{ +constexpr auto k_desktopMode = "desktop-mode"; +constexpr auto k_desktopModeDescription + = "Shows the options panel on the desktop instead of in VR."; + +constexpr auto k_forceNoSound = "force-no-sound"; +constexpr auto k_forceNoSoundDescription + = "Forces sounds off for the application."; + +constexpr auto k_forceNoManifest = "force-no-manifest"; +constexpr auto k_forceNoManifestDescription + = "Runs the application without installing the applications manifest."; + +constexpr auto k_forceInstallManifest = "force-install-manifest"; +constexpr auto k_forceInstallManifestDescription + = "Forces installing the applications manifest into SteamVR. Application " + "will exit early."; + +constexpr auto k_forceRemoveManifest = "force-remove-anifest"; +constexpr auto k_forceRemoveManifestDescription + = "Forces removing the applications manifest. Application will exit early."; + +CommandLineOptions returnCommandLineParser( const MyQApplication& application ) +{ + QCommandLineParser parser; + + parser.setApplicationDescription( + "Advanced Settings overlay for SteamVR." ); + parser.addHelpOption(); + parser.addVersionOption(); + + QCommandLineOption desktopMode( k_desktopMode, k_desktopModeDescription ); + parser.addOption( desktopMode ); + + QCommandLineOption forceNoSound( k_forceNoSound, + k_forceNoSoundDescription ); + parser.addOption( forceNoSound ); + + QCommandLineOption forceNoManifest( k_forceNoManifest, + k_forceNoManifestDescription ); + parser.addOption( forceNoManifest ); + + QCommandLineOption forceInstallManifest( + k_forceInstallManifest, k_forceInstallManifestDescription ); + parser.addOption( forceInstallManifest ); + + QCommandLineOption forceRemoveManifest( k_forceRemoveManifest, + k_forceRemoveManifestDescription ); + parser.addOption( forceRemoveManifest ); + + parser.process( application ); + + const bool desktopModeEnabled = parser.isSet( desktopMode ); + LOG_IF( desktopModeEnabled, INFO ) << "Desktop mode enabled."; + + const bool forceNoSoundEnabled = parser.isSet( forceNoSound ); + LOG_IF( forceNoSoundEnabled, INFO ) << "Sound effects disabled."; + + const bool forceNoManifestEnabled = parser.isSet( forceNoManifest ); + LOG_IF( forceNoManifestEnabled, INFO ) + << "Running without applications manifest."; + + const bool forceInstallManifestEnabled + = parser.isSet( forceInstallManifest ); + LOG_IF( forceInstallManifestEnabled, INFO ) + << "Forcing install of applications manifest."; + + const bool forceRemoveManifestEnabled = parser.isSet( forceRemoveManifest ); + LOG_IF( forceRemoveManifestEnabled, INFO ) + << "Forcing removal of applications manifest."; + + const CommandLineOptions commandLineArgs{ desktopModeEnabled, + forceNoSoundEnabled, + forceNoManifestEnabled, + forceInstallManifestEnabled, + forceRemoveManifestEnabled }; + + LOG( INFO ) << "Command line arguments processed."; + + return commandLineArgs; +} } // namespace argument namespace manifest @@ -275,69 +405,3 @@ void setUpLogging( int argc, char* argv[] ) << QDir::toNativeSeparators( logconfigfile ).toStdString(); LOG( INFO ) << "Log File: " << logFilePath; } - -// Manages the programs control flow and main settings. -class MyQApplication : public QApplication -{ -public: - using QApplication::QApplication; - - // Intercept event calls and log them on exceptions. - // From the official docs - // https://doc.qt.io/qt-5/qcoreapplication.html#notify: - // "Future direction: - // This function will not be called for objects that live outside the main - // thread in Qt 6. Applications that need that functionality should find - // other solutions for their event inspection needs in the meantime. The - // change may be extended to the main thread, causing this function to be - // deprecated." - // Should look into replacements for this function if Qt 6 ever rolls - // around. There are multiple suggestions for other solutions in the - // provided link. - virtual bool notify( QObject* receiver, QEvent* event ) override - { - try - { - return QApplication::notify( receiver, event ); - } - catch ( std::exception& e ) - { - LOG( ERROR ) << "Exception thrown from an event handler: " - << e.what(); - } - return false; - } -}; - -// The default Qt message handler prints to stdout on X11 and to the debugger on -// Windows. That is borderline useless for us, therefore we create our own -// message handler. -void mainQtMessageHandler( QtMsgType type, - const QMessageLogContext& context, - const QString& msg ) -{ - QByteArray localMsg = msg.toLocal8Bit(); - switch ( type ) - { - case QtDebugMsg: - LOG( DEBUG ) << localMsg.constData() << " (" << context.file << ":" - << context.line << ")"; - break; - case QtInfoMsg: - LOG( INFO ) << localMsg.constData() << " (" << context.file << ":" - << context.line << ")"; - break; - case QtWarningMsg: - LOG( WARNING ) << localMsg.constData() << " (" << context.file << ":" - << context.line << ")"; - break; - case QtCriticalMsg: - LOG( ERROR ) << localMsg.constData() << " (" << context.file << ":" - << context.line << ")"; - break; - case QtFatalMsg: - LOG( FATAL ) << localMsg.constData() << " (" << context.file << ":" - << context.line << ")"; - break; - } -}