From 9c7022f802b7704d61a998aab36160054e81cb05 Mon Sep 17 00:00:00 2001 From: username223 Date: Tue, 8 Jan 2019 02:24:20 +0100 Subject: [PATCH] Fix QML error spam from MyStackViewPage and PlayspacePage (#24) * Move logging set up into own function This does not affect the ability to log, the logging macros are available globally. * Clean up logging logic and add comments The logic to find the logging location was broken, in that it always placed the logs in the directory of the executable. It was supposed to place the logs in AppData/Roaming/matzman666/OpenVRAdvancedSettings/AdvancedSettings.log, but it hasn't done this since at least git tag "v2.7". Since the change hasn't been documented anywhere it is assumed to be an unintentional regression. The settings file is also currently placed in AppData/Roaming/matzman666/OpenVRAdvancedSettings.ini, so this commit undoes the probable regression until a strategy can be developed for changing all paths at once. * Move command line arg logging calls and add to manifest calls The LOG_IF macros are cleaner than if blocks, and they do not need to be in the middle of the Qt logic inside a giant try block. * Remove unused QCoreApplication constructor in manifest logic The object is not used in any of the manifest logic functions and running without the remove/install manifest calls does not use it either. * Unindent logic in installManifest The error checking was unnecessarily done in an else block. Moving it into an if block and negating the result does the same but leaves the rest of the logic at a much smaller indentation level. * Change array in installManifest to be 0 intialized Not zero initializing could lead to strange bugs if the terminating null isn't there. * Move manifest install/removal to separate function The manifest handling is tangential to the actual main operation of the program, hence moving it to a separate function improves readability of the actual program. The enum ReturnErrorCode has also been moved to the top in order to be available to all functions. On introspection it is revealed that it should always have been placed at the top. * Unindent logic in removeManifest The logic is unnecessarily indented, leading to harder to follow logic. While the current logic isn't that hard to understand, it's better if it follows the same rhythm as installManifest, which errors checks the same way with a !QFile::exists( manifestQPath ). * Move manifestPath out of install/removeManifest Moving the variable outside of the functions reduces code duplication and increases the versatility of the functions. Variable was renamed from manifestQPath to manifestPath, because the Q does not add anything to the name except (maybe wrongfully in the future) typing a variable that has been typed with 'auto'. The default variable has been removed, as it did not improve readability at all. * Rename installManifest to handleManifestInstallLogic The new name better reflects the overall purpose. installManifest should be a function that only installs the manifest. * Move manifest related things into manifest namespace * Move manifestInstallation logic into functions The logic becomes clearer when replaced by function names instead of just blocks of error checking. * Refactor installManifest into separate functions The uses for the removeManifest and installManifest functions could be boiled down to three separate use cases: * If the application was passed the "-installmanifest" arg: Reinstall the manifest and enable application autostart. * If the application was passed thed "-removemanifest" arg: Remove the manifest. * If the application was not passed "-desktop" and also not passed "-nomanifest" (basically every start): Install the manifest. Where reinstalling means to remove the manifest if installed and then install it. The relatively complex logic in installManifest hid this logic behind several if statements and changing what the "alreadyInstalled" bool signified. The refactored functions should make everything easier to understand. * Add variable for manifest name * Rename variables in main to camelCase This appears to be the style chosen for the majority of the project. * Change cerr to be LOG(ERROR) and add logging for manifest completion The cerr calls are not likely to be seen by anybody. Logging them as ERROR will make sure they are available for diagnostics. * Add comments for new manifest functions * Reorder namespaces and functions in main Namespaces and functions are now ordered as they appear in main. * Add static_cast to height and width in func call in main width and height are "qreal" (typedef for double). Having them implicitly cast to int leads to warnings on some compilers, and can lead to doubt whether they're supposed to be converted. * Move organizationName and applicationName into overlaycontroller.h The string have been moved to static constexpr autos inside the overlaycontroller. Them remaining static means that they are included in the final binary, even if they don't need to be included as static. They should probably also not be directly gotten, instead being offered up as static getters, but it's better to do them all at the same time in a specific commit for that. * Add comment to installApplicationManifest Describing that OpenVR must be initialized before the function is called. * Change QApplication var name and add comments The previous QApplication was named "a". This is obviously not sufficient for such a large part of the application, even if the class is rather boilerplate heavy. * Add comments to Qt portions of main Some things have been left uncommented. That's because I don't know what it does. * Change documentation for command line arguments, logging config The information under the Desktop Mode header could be rolled under the "-desktop" command line arg. Support for the command line args of Easylogging++ and custom logging files have not been supported for the original developer. Both should probably be dropped in favor of having the same logging behavior for all users. * Remove error causing property from QML MyStackViewPage and PlayspacePage The properties were causing an error message to be spammed in the log QML QQuickText: Detected anchors on an item that is managed by a layout. This is undefined behavior; use Layout.alignment instead. and QML RowLayout: Detected anchors on an item that is managed by a layout. This is undefined behavior; use Layout.alignment instead. Removing them fixed the spam and didn't noticeably change the appearance of the pages. --- Readme.md | 18 +- src/main.cpp | 662 ++++++++++++++++++-------------- src/overlaycontroller.h | 4 +- src/res/qml/MyStackViewPage.qml | 1 - src/res/qml/PlayspacePage.qml | 1 - 5 files changed, 385 insertions(+), 301 deletions(-) diff --git a/Readme.md b/Readme.md index 49e6c7b..b922dd5 100644 --- a/Readme.md +++ b/Readme.md @@ -8,7 +8,7 @@ > * [Installer](#installer) > * [Standalone](#standalone) > * [Command Line Arguments](#command_line_arguments) - > * [Desktop mode](#desktop_mode) + > * [Logging Config](#logging_config) > * [Preview Builds](#preview_builds) > * [Documentation](#documentation) > * [Top Page](#top_page) @@ -84,20 +84,22 @@ To upgrade an existing installation first stop SteamVR and delete the old applic The application (`AdvancedSettings.exe`) can be run with the following five optional arguments: -`"-desktop"`: Creates a settings window on the desktop. +`"-desktop"`: Creates a settings window on the desktop as well as in VR. Running `startdesktopmode.bat` in the install directory has the same effect. `"-nosound"`: Forces sound effects off. -`"-nomanifest"`: Forces not creating a `.vrmanifest`. +`"-nomanifest"`: Forces not using a `.vrmanifest`. -`"-installmanifest"`: Force installs the `.vrmanifest`. +`"-installmanifest"`: Force installs the `.vrmanifest` and adds the application to autostart. If you're having issues with autostart not working try running the program once with this set. The program will exit early when this flag is set. -`"-removemanifest"`: Force uninstalls the `.vrmanifest`. +`"-removemanifest"`: Force uninstalls the `.vrmanifest`. This should be done every time the application is uninstalled. On Windows it is automatically done by the uninstaller. The program will exit early when this flag is set. - -## Desktop mode +In addition, the application can receive the command line arguments from the [Easylogging++ library](https://github.com/zuhd-org/easyloggingpp#application-arguments). -Executing startdesktopmode.bat from the application folder shows a window on the desktop instead of a vr overlay. Desktop mode can be used alongside an already running vr overlay instance. + +## Logging Config + +The logging output of the [Easylogging++ library](https://github.com/zuhd-org/easyloggingpp#application-arguments) can be customized by placing a file called `logging.conf` in the directory of the binary. This should be considered for advanced users only and it is not recommended. ## Preview builds diff --git a/src/main.cpp b/src/main.cpp index 5bb4bdb..fe968c0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,9 +12,310 @@ INITIALIZE_EASYLOGGINGPP -void myQtMessageHandler( QtMsgType type, - const QMessageLogContext& context, - const QString& msg ) +enum ReturnErrorCode +{ + SUCCESS = 0, + GENERAL_FAILURE = -1, + OPENVR_INIT_ERROR = -2, +}; + +namespace argument +{ +// Checks whether a specific string was passed as a launch argument. +bool CheckCommandLineArgument( int argc, + char* argv[], + const std::string parameter ) +{ + // 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++ ) + { + if ( std::string( argv[i] ) == parameter ) + { + return true; + } + } + return false; +} + +constexpr auto kDesktopMode = "-desktop"; +constexpr auto kNoSound = "-nosound"; +constexpr auto kNoManifest = "-nomanifest"; +constexpr auto kInstallManifest = "-installmanifest"; +constexpr auto kRemoveManifest = "-removemanifest"; +} // namespace argument + +namespace manifest +{ +// File name of the manifest. Is placed in the same directory as the binary. +constexpr auto kVRManifestName = "manifest.vrmanifest"; + +// Enables autostart of OpenVR Advanced Settings. +void enableApplicationAutostart() +{ + const auto app_error = vr::VRApplications()->SetApplicationAutoLaunch( + advsettings::OverlayController::applicationKey, true ); + if ( app_error != vr::VRApplicationError_None ) + { + throw std::runtime_error( + std::string( "Could not set auto start: " ) + + std::string( + vr::VRApplications()->GetApplicationsErrorNameFromEnum( + app_error ) ) ); + } +} + +// Installs the application manifest. If a manifest with the same application +// key is already installed, nothing will happen. +// OpenVR must be initialized before calling this function. +void installApplicationManifest( const QString manifestPath ) +{ + if ( vr::VRApplications()->IsApplicationInstalled( + advsettings::OverlayController::applicationKey ) ) + { + // We don't want to disrupt applications that are already installed. + return; + } + + const auto app_error = vr::VRApplications()->AddApplicationManifest( + QDir::toNativeSeparators( manifestPath ).toStdString().c_str() ); + if ( app_error != vr::VRApplicationError_None ) + { + throw std::runtime_error( + std::string( "Could not add application manifest: " ) + + std::string( + vr::VRApplications()->GetApplicationsErrorNameFromEnum( + app_error ) ) ); + } +} + +// Removes the application manifest. +void removeApplicationManifest( const QString manifestPath ) +{ + if ( !QFile::exists( manifestPath ) ) + { + throw std::runtime_error( + std::string( "Could not find application manifest: " ) + + manifestPath.toStdString() ); + } + + if ( vr::VRApplications()->IsApplicationInstalled( + advsettings::OverlayController::applicationKey ) ) + { + vr::VRApplications()->RemoveApplicationManifest( + QDir::toNativeSeparators( manifestPath ).toStdString().c_str() ); + } +} + +// Removes and then installs the application manifest. +void reinstallApplicationManifest( const QString manifestPath ) +{ + if ( !QFile::exists( manifestPath ) ) + { + throw std::runtime_error( + std::string( "Could not find application manifest: " ) + + manifestPath.toStdString() ); + } + + if ( vr::VRApplications()->IsApplicationInstalled( + advsettings::OverlayController::applicationKey ) ) + { + // String size was arbitrarily chosen by original author. + constexpr auto kStringSize = 1024; + char oldApplicationWorkingDir[kStringSize] = { 0 }; + auto app_error = vr::VRApplicationError_None; + vr::VRApplications()->GetApplicationPropertyString( + advsettings::OverlayController::applicationKey, + vr::VRApplicationProperty_WorkingDirectory_String, + oldApplicationWorkingDir, + kStringSize, + &app_error ); + + if ( app_error != vr::VRApplicationError_None ) + { + throw std::runtime_error( + "Could not find working directory of already " + "installed application: " + + std::string( + vr::VRApplications()->GetApplicationsErrorNameFromEnum( + app_error ) ) ); + } + + const auto oldManifestPath + = QDir::cleanPath( QDir( oldApplicationWorkingDir ) + .absoluteFilePath( kVRManifestName ) ); + + removeApplicationManifest( manifestPath ); + } + + installApplicationManifest( manifestPath ); +} + +// Initializes OpenVR and calls the relevant manifest functions. +// The .vrmanifest is used by SteamVR however the exact functiontionality is not +// documented in the official documentation. +// The manifest is installed upon using the NSIS installer for windows (this +// program is called with "-installmanifest" by the installer) and every time +// the program is launched without both "-desktop" and "-nomanifest". +// The OpenVR initialization is necessary for both removing and installing +// manifests. +[[noreturn]] void handleManifests( const bool installManifest, + const bool removeManifest ) +{ + int exit_code = ReturnErrorCode::SUCCESS; + auto openvr_init_status = vr::VRInitError_None; + vr::VR_Init( &openvr_init_status, vr::VRApplication_Utility ); + if ( openvr_init_status == vr::VRInitError_None ) + { + try + { + const auto manifestPath = QDir::cleanPath( + QDir( QCoreApplication::applicationDirPath() ) + .absoluteFilePath( kVRManifestName ) ); + + if ( installManifest ) + { + reinstallApplicationManifest( manifestPath ); + enableApplicationAutostart(); + LOG( INFO ) << "Manifest reinstalled."; + } + else if ( removeManifest ) + { + removeApplicationManifest( manifestPath ); + LOG( INFO ) << "Manifest removed."; + } + } + catch ( std::exception& e ) + { + exit_code = ReturnErrorCode::GENERAL_FAILURE; + LOG( ERROR ) << e.what(); + } + } + else + { + exit_code = ReturnErrorCode::OPENVR_INIT_ERROR; + LOG( ERROR ) << std::string( + "Failed to initialize OpenVR: " + + std::string( vr::VR_GetVRInitErrorAsEnglishDescription( + openvr_init_status ) ) ); + } + + vr::VR_Shutdown(); + exit( exit_code ); +} +} // namespace manifest + +// Sets up the logging library and outputs startup logging data. +// argc and argv are necessary for the START_EASYLOGGINGPP() call. +void setUpLogging( int argc, char* argv[] ) +{ + constexpr auto logConfigDefault + = "* GLOBAL:\n" + " FORMAT = \"[%level] %datetime{%Y-%M-%d %H:%m:%s}: %msg\"\n" + " FILENAME = \"AdvancedSettings.log\"\n" + " ENABLED = true\n" + " TO_FILE = true\n" + " TO_STANDARD_OUTPUT = true\n" + " MAX_LOG_FILE_SIZE = 2097152 ## 2MB\n" + "* TRACE:\n" + " ENABLED = false\n" + "* DEBUG:\n" + " ENABLED = false\n"; + + // This allows easylogging++ to parse command line arguments. + // For a full list go here + // https://github.com/zuhd-org/easyloggingpp#application-arguments + // The parsed args are mostly setting the verbosity level or setting the + // conf file. + START_EASYLOGGINGPP( argc, argv ); + + el::Loggers::addFlag( el::LoggingFlag::DisableApplicationAbortOnFatalLog ); + + // If there is a default logging config file present, use that. If not, use + // the default settings. + constexpr auto logConfigFileName = "logging.conf"; + const auto logconfigfile + = QFileInfo( logConfigFileName ).absoluteFilePath(); + el::Configurations conf; + if ( QFile::exists( logconfigfile ) ) + { + conf.parseFromFile( logconfigfile.toStdString() ); + } + else + { + conf.parseFromText( logConfigDefault ); + } + + // This places the log file in + // Roaming/AppData/matzman666/OpenVRAdvancedSettings/AdvancedSettings.log. + // The log file placement has been broken since at least git tag "v2.7". + // It was being placed in the working dir of the executable. + // The change hasn't been documented anywhere, so it is likely that it was + // unintentional. This fixes the probable regression until a new path is + // decided on. + constexpr auto appDataFolders = "/matzman666/OpenVRAdvancedSettings"; + const QString logFilePath = QDir( QStandardPaths::writableLocation( + QStandardPaths::AppDataLocation ) + + appDataFolders ) + .absoluteFilePath( "AdvancedSettings.log" ); + conf.set( el::Level::Global, + el::ConfigurationType::Filename, + QDir::toNativeSeparators( logFilePath ).toStdString() ); + + conf.setRemainingToDefault(); + + el::Loggers::reconfigureAllLoggers( conf ); + + LOG( INFO ) << "Application started (Version " + << advsettings::OverlayController::applicationVersionString + << ")"; + LOG( INFO ) << "Log Config: " + << 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 ) @@ -42,307 +343,80 @@ void myQtMessageHandler( QtMsgType type, } } -void installManifest( bool cleaninstall = false ) -{ - auto manifestQPath - = QDir::cleanPath( QDir( QCoreApplication::applicationDirPath() ) - .absoluteFilePath( "manifest.vrmanifest" ) ); - if ( QFile::exists( manifestQPath ) ) - { - bool alreadyInstalled = false; - if ( vr::VRApplications()->IsApplicationInstalled( - advsettings::OverlayController::applicationKey ) ) - { - if ( cleaninstall ) - { - char buffer[1024]; - auto appError = vr::VRApplicationError_None; - vr::VRApplications()->GetApplicationPropertyString( - advsettings::OverlayController::applicationKey, - vr::VRApplicationProperty_WorkingDirectory_String, - buffer, - 1024, - &appError ); - if ( appError == vr::VRApplicationError_None ) - { - auto oldManifestQPath - = QDir::cleanPath( QDir( buffer ).absoluteFilePath( - "manifest.vrmanifest" ) ); - if ( oldManifestQPath.compare( manifestQPath, - Qt::CaseInsensitive ) - != 0 ) - { - vr::VRApplications()->RemoveApplicationManifest( - QDir::toNativeSeparators( oldManifestQPath ) - .toStdString() - .c_str() ); - } - else - { - alreadyInstalled = true; - } - } - } - else - { - alreadyInstalled = true; - } - } - auto apperror = vr::VRApplications()->AddApplicationManifest( - QDir::toNativeSeparators( manifestQPath ).toStdString().c_str() ); - if ( apperror != vr::VRApplicationError_None ) - { - throw std::runtime_error( - std::string( "Could not add application manifest: " ) - + std::string( - vr::VRApplications()->GetApplicationsErrorNameFromEnum( - apperror ) ) ); - } - else if ( !alreadyInstalled || cleaninstall ) - { - auto app_error = vr::VRApplications()->SetApplicationAutoLaunch( - advsettings::OverlayController::applicationKey, true ); - if ( app_error != vr::VRApplicationError_None ) - { - throw std::runtime_error( - std::string( "Could not set auto start: " ) - + std::string( vr::VRApplications() - ->GetApplicationsErrorNameFromEnum( - apperror ) ) ); - } - } - } - else - { - throw std::runtime_error( - std::string( "Could not find application manifest: " ) - + manifestQPath.toStdString() ); - } -} - -void removeManifest() -{ - auto manifestQPath - = QDir::cleanPath( QDir( QCoreApplication::applicationDirPath() ) - .absoluteFilePath( "manifest.vrmanifest" ) ); - if ( QFile::exists( manifestQPath ) ) - { - if ( vr::VRApplications()->IsApplicationInstalled( - advsettings::OverlayController::applicationKey ) ) - { - vr::VRApplications()->RemoveApplicationManifest( - QDir::toNativeSeparators( manifestQPath ) - .toStdString() - .c_str() ); - } - } - else - { - throw std::runtime_error( - std::string( "Could not find application manifest: " ) - + manifestQPath.toStdString() ); - } -} - -class MyQApplication : public QApplication -{ -public: - using QApplication::QApplication; - - 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; - } -}; - -namespace argument -{ -// Checks whether a specific string was passed as a launch argument. -bool CheckCommandLineArgument( int argc, - char* argv[], - const std::string parameter ) -{ - // 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++ ) - { - if ( std::string( argv[i] ) == parameter ) - { - return true; - } - } - return false; -} - -constexpr auto DESKTOP_MODE = "-desktop"; -constexpr auto NO_SOUND = "-nosound"; -constexpr auto NO_MANIFEST = "-nomanifest"; -constexpr auto INSTALL_MANIFEST = "-installmanifest"; -constexpr auto REMOVE_MANIFEST = "-removemanifest"; -} // namespace argument - -enum ReturnErrorCode -{ - SUCCESS = 0, - GENERAL_FAILURE = -1, - OPENVR_INIT_ERROR = -2, -}; - int main( int argc, char* argv[] ) { - const char* logConfigFileName = "logging.conf"; + setUpLogging( argc, argv ); - const char* logConfigDefault - = "* GLOBAL:\n" - " FORMAT = \"[%level] %datetime{%Y-%M-%d %H:%m:%s}: %msg\"\n" - " FILENAME = \"AdvancedSettings.log\"\n" - " ENABLED = true\n" - " TO_FILE = true\n" - " TO_STANDARD_OUTPUT = true\n" - " MAX_LOG_FILE_SIZE = 2097152 ## 2MB\n" - "* TRACE:\n" - " ENABLED = false\n" - "* DEBUG:\n" - " ENABLED = false\n"; + 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 ); - const bool desktop_mode = argument::CheckCommandLineArgument( - argc, argv, argument::DESKTOP_MODE ); - const bool no_sound - = argument::CheckCommandLineArgument( argc, argv, argument::NO_SOUND ); - const bool no_manifest = argument::CheckCommandLineArgument( - argc, argv, argument::NO_MANIFEST ); - const bool install_manifest = argument::CheckCommandLineArgument( - argc, argv, argument::INSTALL_MANIFEST ); - const bool remove_manifest = argument::CheckCommandLineArgument( - argc, argv, argument::REMOVE_MANIFEST ); + // 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."; - if ( install_manifest || remove_manifest ) + // It is important that either install_manifest or remove_manifest are true, + // otherwise the handleManifests function will not behave properly. + if ( installManifest || removeManifest ) { - int exit_code = ReturnErrorCode::SUCCESS; - QCoreApplication coreApp( argc, argv ); - auto openvr_init_status = vr::VRInitError_None; - vr::VR_Init( &openvr_init_status, vr::VRApplication_Utility ); - - if ( openvr_init_status == vr::VRInitError_None ) - { - try - { - if ( install_manifest ) - { - installManifest( true ); - } - else if ( remove_manifest ) - { - removeManifest(); - } - } - catch ( std::exception& e ) - { - exit_code = ReturnErrorCode::GENERAL_FAILURE; - std::cerr << e.what() << std::endl; - } - } - else - { - exit_code = ReturnErrorCode::OPENVR_INIT_ERROR; - std::cerr << std::string( - "Failed to initialize OpenVR: " - + std::string( - vr::VR_GetVRInitErrorAsEnglishDescription( - openvr_init_status ) ) ) - << std::endl; - } - - vr::VR_Shutdown(); - exit( exit_code ); + // The function does not return, it exits inside the function + // with an appropriate exit code. + manifest::handleManifests( installManifest, removeManifest ); } try { - MyQApplication a( argc, argv ); - a.setOrganizationName( "matzman666" ); - a.setApplicationName( "OpenVRAdvancedSettings" ); - a.setApplicationDisplayName( + // 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 ); - a.setApplicationVersion( + mainEventLoop.setApplicationDisplayName( + advsettings::OverlayController::applicationDisplayName ); + mainEventLoop.setApplicationVersion( advsettings::OverlayController::applicationVersionString ); - qInstallMessageHandler( myQtMessageHandler ); - - // Configure logger - QString logFilePath; - START_EASYLOGGINGPP( argc, argv ); - el::Loggers::addFlag( - el::LoggingFlag::DisableApplicationAbortOnFatalLog ); - auto logconfigfile = QFileInfo( logConfigFileName ).absoluteFilePath(); - el::Configurations conf; - if ( QFile::exists( logconfigfile ) ) - { - conf.parseFromFile( logconfigfile.toStdString() ); - } - else - { - conf.parseFromText( logConfigDefault ); - } - if ( !conf.get( el::Level::Global, el::ConfigurationType::Filename ) ) - { - logFilePath = QDir( QStandardPaths::writableLocation( - QStandardPaths::AppDataLocation ) ) - .absoluteFilePath( "AdvancedSettings.log" ); - conf.set( el::Level::Global, - el::ConfigurationType::Filename, - QDir::toNativeSeparators( logFilePath ).toStdString() ); - } - conf.setRemainingToDefault(); - el::Loggers::reconfigureAllLoggers( conf ); - LOG( INFO ) << "Application started (Version " - << advsettings::OverlayController::applicationVersionString - << ")"; - LOG( INFO ) << "Log Config: " - << QDir::toNativeSeparators( logconfigfile ).toStdString(); - if ( !logFilePath.isEmpty() ) - { - LOG( INFO ) << "Log File: " << logFilePath; - } - - if ( desktop_mode ) - { - LOG( INFO ) << "Desktop mode enabled."; - } - if ( no_sound ) - { - LOG( INFO ) << "Sound effects disabled."; - } - if ( no_manifest ) - { - LOG( INFO ) << "vrmanifest disabled."; - } + qInstallMessageHandler( mainQtMessageHandler ); + // QSettings contains a platform independant way of storing settings in + // an .ini file. QSettings appSettings( QSettings::IniFormat, QSettings::UserScope, - a.organizationName(), - a.applicationName() ); + mainEventLoop.organizationName(), + mainEventLoop.applicationName() ); advsettings::OverlayController::setAppSettings( &appSettings ); LOG( INFO ) << "Settings File: " << appSettings.fileName().toStdString(); + // The QML Engine is necessary for instantiating QML files. QQmlEngine qmlEngine; + // The OverlayController handles the majority of the application specfic + // things. It contains all the other tabs. + // It is created using a singleton pattern that destructs the old + // instance whenever createInstance is called. This is obviously not + // ideal since it can lead to dangling points and use after free. + // The constructor only sets two member vars called desktopMode and + // noSound, the rest of the initialization is done in Init. It is + // unknown why this is done. advsettings::OverlayController* controller - = advsettings::OverlayController::createInstance( desktop_mode, - no_sound ); + = advsettings::OverlayController::createInstance( desktopMode, + noSound ); controller->Init( &qmlEngine ); QQmlComponent component( @@ -354,15 +428,20 @@ int main( int argc, char* argv[] ) << std::endl; } auto quickObj = component.create(); - controller->SetWidget( qobject_cast( quickObj ), - advsettings::OverlayController::applicationName, - advsettings::OverlayController::applicationKey ); + controller->SetWidget( + qobject_cast( quickObj ), + advsettings::OverlayController::applicationDisplayName, + advsettings::OverlayController::applicationKey ); - if ( !desktop_mode && !no_manifest ) + // Attempts to install the application manifest on all "regular" starts. + if ( !desktopMode && !noManifest ) { try { - installManifest(); + const auto manifestPath = QDir::cleanPath( + QDir( QCoreApplication::applicationDirPath() ) + .absoluteFilePath( manifest::kVRManifestName ) ); + manifest::installApplicationManifest( manifestPath ); } catch ( std::exception& e ) { @@ -370,7 +449,8 @@ int main( int argc, char* argv[] ) } } - if ( desktop_mode ) + // Creates an identical settings menu on the desktop as well as in VR. + if ( desktopMode ) { auto m_pWindow = new QQuickWindow(); qobject_cast( quickObj ) @@ -378,12 +458,14 @@ int main( int argc, char* argv[] ) m_pWindow->setGeometry( 0, 0, - qobject_cast( quickObj )->width(), - qobject_cast( quickObj )->height() ); + static_cast( + qobject_cast( quickObj )->width() ), + static_cast( + qobject_cast( quickObj )->height() ) ); m_pWindow->show(); } - return a.exec(); + return mainEventLoop.exec(); } catch ( const std::exception& e ) { diff --git a/src/overlaycontroller.h b/src/overlaycontroller.h index 5b5e07d..0f57b0c 100644 --- a/src/overlaycontroller.h +++ b/src/overlaycontroller.h @@ -45,8 +45,10 @@ class OverlayController : public QObject Q_PROPERTY( bool desktopMode READ isDesktopMode ) public: + static constexpr auto applicationOrganizationName = "matzman666"; + static constexpr auto applicationName = "OpenVRAdvancedSettings"; static constexpr const char* applicationKey = "matzman666.AdvancedSettings"; - static constexpr const char* applicationName = "Advanced Settings"; + static constexpr const char* applicationDisplayName = "Advanced Settings"; static constexpr const char* applicationVersionString = "v2.8.0"; private: diff --git a/src/res/qml/MyStackViewPage.qml b/src/res/qml/MyStackViewPage.qml index 71f39be..b30140a 100644 --- a/src/res/qml/MyStackViewPage.qml +++ b/src/res/qml/MyStackViewPage.qml @@ -51,7 +51,6 @@ Rectangle { id: headerTitle text: headerText font.pointSize: 30 - anchors.verticalCenter: headerBackButton.verticalCenter Layout.leftMargin: 32 } } diff --git a/src/res/qml/PlayspacePage.qml b/src/res/qml/PlayspacePage.qml index 49cb301..a109652 100644 --- a/src/res/qml/PlayspacePage.qml +++ b/src/res/qml/PlayspacePage.qml @@ -318,7 +318,6 @@ MyStackViewPage { } ColumnLayout { RowLayout { - anchors.fill: parent MyToggleButton { id: moveShortcutLeft