diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0bf9d126da5..fd45ef77460 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -724,67 +723,24 @@ extern DisplayPluginList getDisplayPlugins(); extern InputPluginList getInputPlugins(); extern void saveInputPluginSettings(const InputPluginList& plugins); -// Parameters used for running tests from teh command line -const QString TEST_SCRIPT_COMMAND{ "--testScript" }; -const QString TEST_QUIT_WHEN_FINISHED_OPTION{ "quitWhenFinished" }; -const QString TEST_RESULTS_LOCATION_COMMAND{ "--testResultsLocation" }; - -bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { +bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, bool runningMarkerExisted) { const char** constArgv = const_cast(argv); qInstallMessageHandler(messageHandler); - // HRS: I could not figure out how to move these any earlier in startup, so when using this option, be sure to also supply - // --allowMultipleInstances - auto reportAndQuit = [&](const char* commandSwitch, std::function report) { - const char* reportfile = getCmdOption(argc, constArgv, commandSwitch); - // Reports to the specified file, because stdout is set up to be captured for logging. - if (reportfile) { - FILE* fp = fopen(reportfile, "w"); - if (fp) { - report(fp); - fclose(fp); - if (!runningMarkerExisted) { // don't leave ours around - RunningMarker runingMarker(RUNNING_MARKER_FILENAME); - runingMarker.deleteRunningMarkerFile(); // happens in deleter, but making the side-effect explicit. - } - _exit(0); - } - } - }; - reportAndQuit("--protocolVersion", [&](FILE* fp) { - auto version = protocolVersionsSignatureBase64(); - fputs(version.toLatin1().data(), fp); - }); - reportAndQuit("--version", [&](FILE* fp) { - fputs(BuildInfo::VERSION.toLatin1().data(), fp); - }); - const char* portStr = getCmdOption(argc, constArgv, "--listenPort"); - const int listenPort = portStr ? atoi(portStr) : INVALID_PORT; + const int listenPort = parser.isSet("listenPort") ? parser.value("listenPort").toInt() : INVALID_PORT; - static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset"; - bool suppressPrompt = cmdOptionExists(argc, const_cast(argv), SUPPRESS_SETTINGS_RESET); + bool suppressPrompt = parser.isSet("suppress-settings-reset"); // set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store - static const auto OCULUS_STORE_ARG = "--oculus-store"; - bool isStore = cmdOptionExists(argc, const_cast(argv), OCULUS_STORE_ARG); - qApp->setProperty(hifi::properties::OCULUS_STORE, isStore); + qApp->setProperty(hifi::properties::OCULUS_STORE, parser.isSet("oculus-store")); // emulate standalone device - static const auto STANDALONE_ARG = "--standalone"; - bool isStandalone = cmdOptionExists(argc, const_cast(argv), STANDALONE_ARG); - qApp->setProperty(hifi::properties::STANDALONE, isStandalone); + qApp->setProperty(hifi::properties::STANDALONE, parser.isSet("standalone")); // Ignore any previous crashes if running from command line with a test script. - bool inTestMode { false }; - for (int i = 0; i < argc; ++i) { - QString parameter(argv[i]); - if (parameter == TEST_SCRIPT_COMMAND) { - inTestMode = true; - break; - } - } + bool inTestMode = parser.isSet("testScript"); bool previousSessionCrashed { false }; if (!inTestMode) { @@ -792,10 +748,8 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { } // get dir to use for cache - static const auto CACHE_SWITCH = "--cache"; - QString cacheDir = getCmdOption(argc, const_cast(argv), CACHE_SWITCH); - if (!cacheDir.isEmpty()) { - qApp->setProperty(hifi::properties::APP_LOCAL_DATA_PATH, cacheDir); + if (parser.isSet("cache")) { + qApp->setProperty(hifi::properties::APP_LOCAL_DATA_PATH, parser.value("cache")); } { @@ -837,7 +791,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { QCoreApplication::addLibraryPath(audioDLLPath); #endif - QString defaultScriptsOverrideOption = getCmdOption(argc, constArgv, "--defaultScriptsOverride"); + QString defaultScriptsOverrideOption = parser.value("defaultScriptsOverride"); DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); @@ -963,7 +917,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { }); - QString setBookmarkValue = getCmdOption(argc, constArgv, "--setBookmark"); + QString setBookmarkValue = parser.value("setBookmark"); if (!setBookmarkValue.isEmpty()) { // Bookmarks are expected to be in a name=url form. // An `=` character in the name or url is unsupported. @@ -1020,14 +974,19 @@ QSharedPointer getOffscreenUI() { #endif } -Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bool runningMarkerExisted) : +Application::Application( + int& argc, char** argv, + const QCommandLineParser& parser, + QElapsedTimer& startupTimer, + bool runningMarkerExisted +) : QApplication(argc, argv), _window(new MainWindow(desktop())), _sessionRunTimer(startupTimer), #ifndef Q_OS_ANDROID _logger(new FileLogger(this)), #endif - _previousSessionCrashed(setupEssentials(argc, argv, runningMarkerExisted)), + _previousSessionCrashed(setupEssentials(argc, argv, parser, runningMarkerExisted)), _entitySimulation(std::make_shared()), _physicsEngine(std::make_shared(Vectors::ZERO)), _entityClipboard(std::make_shared()), @@ -1064,12 +1023,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo LogHandler::getInstance().setupRepeatedMessageFlusher(); { - const QStringList args = arguments(); - - for (int i = 0; i < args.size() - 1; ++i) { - if (args.at(i) == TEST_SCRIPT_COMMAND && (i + 1) < args.size()) { - QString testScriptPath = args.at(i + 1); - + if (parser.isSet("testScript")) { + QString testScriptPath = parser.value("testScript"); // If the URL scheme is http(s) or ftp, then use as is, else - treat it as a local file // This is done so as not break previous command line scripts if (testScriptPath.left(HIFI_URL_SCHEME_HTTP.length()) == HIFI_URL_SCHEME_HTTP || @@ -1080,20 +1035,20 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath)); } - // quite when finished parameter must directly follow the test script - if ((i + 2) < args.size() && args.at(i + 2) == TEST_QUIT_WHEN_FINISHED_OPTION) { + if (parser.isSet("quitWhenFinished")) { quitWhenFinished = true; } - } else if (args.at(i) == TEST_RESULTS_LOCATION_COMMAND) { - // Set test snapshot location only if it is a writeable directory - QString path(args.at(i + 1)); + } + if (parser.isSet("testResultsLocation")) { + // Set test snapshot location only if it is a writeable directory + QString path = parser.value("testResultsLocation"); - QFileInfo fileInfo(path); - if (fileInfo.isDir() && fileInfo.isWritable()) { - TestScriptingInterface::getInstance()->setTestResultsLocation(path); - } + QFileInfo fileInfo(path); + if (fileInfo.isDir() && fileInfo.isWritable()) { + TestScriptingInterface::getInstance()->setTestResultsLocation(path); } } + _urlParam = parser.value("url"); } { @@ -1159,8 +1114,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto addressManager = DependencyManager::get(); addressManager->moveToThread(nodeList->thread()); - const char** constArgv = const_cast(argv); - if (cmdOptionExists(argc, constArgv, "--disableWatchdog")) { + if (parser.isSet("disableWatchdog")) { DISABLE_WATCHDOG = true; } // Set up a watchdog thread to intentionally crash the application on deadlocks @@ -1383,25 +1337,27 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress); connect(this, &Application::activeDisplayPluginChanged, this, &Application::updateThreadPoolCount); - connect(this, &Application::activeDisplayPluginChanged, this, [=](){ - qApp->setProperty(hifi::properties::HMD, qApp->isHMDMode()); - auto displayPlugin = qApp->getActiveDisplayPlugin(); + if (parser.isSet("system-cursor")) { + _preferredCursor.set(Cursor::Manager::getIconName(Cursor::Icon::SYSTEM)); - if (displayPlugin->isHmd()) { - if (_preferredCursor.get() == Cursor::Manager::getIconName(Cursor::Icon::RETICLE)) { - setPreferredCursor(Cursor::Manager::getIconName(Cursor::Icon::RETICLE)); - } - else { - setPreferredCursor(Cursor::Manager::getIconName(Cursor::Icon::ARROW)); + connect(this, &Application::activeDisplayPluginChanged, this, [=](){ + qApp->setProperty(hifi::properties::HMD, qApp->isHMDMode()); + auto displayPlugin = qApp->getActiveDisplayPlugin(); + + if (displayPlugin->isHmd()) { + if (_preferredCursor.get() == Cursor::Manager::getIconName(Cursor::Icon::RETICLE)) { + setPreferredCursor(Cursor::Manager::getIconName(Cursor::Icon::RETICLE)); + } else { + setPreferredCursor(Cursor::Manager::getIconName(Cursor::Icon::ARROW)); + } + } else { + setPreferredCursor(Cursor::Manager::getIconName(Cursor::Icon::SYSTEM)); } - } - else { - setPreferredCursor(Cursor::Manager::getIconName(Cursor::Icon::SYSTEM)); - } - setCrashAnnotation("display_plugin", displayPlugin->getName().toStdString()); - setCrashAnnotation("hmd", displayPlugin->isHmd() ? "1" : "0"); - }); + setCrashAnnotation("display_plugin", displayPlugin->getName().toStdString()); + setCrashAnnotation("hmd", displayPlugin->isHmd() ? "1" : "0"); + }); + } connect(this, &Application::activeDisplayPluginChanged, this, &Application::updateSystemTabletMode); connect(this, &Application::activeDisplayPluginChanged, this, [&](){ if (getLoginDialogPoppedUp()) { @@ -1481,24 +1437,26 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); connect(&_entityEditSender, &EntityEditPacketSender::addingEntityWithCertificate, this, &Application::addingEntityWithCertificate); - QString concurrentDownloadsStr = getCmdOption(argc, constArgv, "--concurrent-downloads"); - bool success; - uint32_t concurrentDownloads = concurrentDownloadsStr.toUInt(&success); - if (!success) { - concurrentDownloads = MAX_CONCURRENT_RESOURCE_DOWNLOADS; + if (parser.isSet("concurrent-downloads")) { + bool success; + uint32_t concurrentDownloads = parser.value("concurrent-downloads").toUInt(&success); + if (!success) { + concurrentDownloads = MAX_CONCURRENT_RESOURCE_DOWNLOADS; + } + ResourceCache::setRequestLimit(concurrentDownloads); } - ResourceCache::setRequestLimit(concurrentDownloads); // perhaps override the avatar url. Since we will test later for validity // we don't need to do so here. - QString avatarURL = getCmdOption(argc, constArgv, "--avatarURL"); - _avatarOverrideUrl = QUrl::fromUserInput(avatarURL); + if (parser.isSet("avatarURL")) { + _avatarOverrideUrl = QUrl::fromUserInput(parser.value("avatarURL")); + } // If someone specifies both --avatarURL and --replaceAvatarURL, // the replaceAvatarURL wins. So only set the _overrideUrl if this // does have a non-empty string. - QString replaceURL = getCmdOption(argc, constArgv, "--replaceAvatarURL"); - if (!replaceURL.isEmpty()) { + if (parser.isSet("replaceAvatarURL")) { + QString replaceURL = parser.value("replaceAvatarURL"); _avatarOverrideUrl = QUrl::fromUserInput(replaceURL); _saveAvatarOverrideUrl = true; } @@ -1516,9 +1474,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _glWidget->setFocusPolicy(Qt::StrongFocus); _glWidget->setFocus(); - if (cmdOptionExists(argc, constArgv, "--system-cursor")) { - _preferredCursor.set(Cursor::Manager::getIconName(Cursor::Icon::SYSTEM)); - } showCursor(Cursor::Manager::lookupIcon(_preferredCursor.get())); // enable mouse tracking; otherwise, we only get drag events @@ -1585,21 +1540,23 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo } }); +#if defined(Q_OS_ANDROID) || defined(DISABLE_QML) connect(offscreenUi.data(), &OffscreenUi::keyboardFocusActive, [this]() { -#if !defined(Q_OS_ANDROID) && !defined(DISABLE_QML) - // Do not show login dialog if requested not to on the command line - QString hifiNoLoginCommandLineKey = QString("--").append(HIFI_NO_LOGIN_COMMAND_LINE_KEY); - int index = arguments().indexOf(hifiNoLoginCommandLineKey); - if (index != -1 || _disableLoginScreen) { - resumeAfterLoginDialogActionTaken(); - return; - } - - showLoginScreen(); -#else resumeAfterLoginDialogActionTaken(); -#endif }); +#else + // Do not show login dialog if requested not to on the command line + if (_disableLoginScreen || parser.isSet("no-login-suggestion")) { + connect(offscreenUi.data(), &OffscreenUi::keyboardFocusActive, [this]() { + resumeAfterLoginDialogActionTaken(); + }); + } else { + connect(offscreenUi.data(), &OffscreenUi::keyboardFocusActive, [this]() { + showLoginScreen(); + resumeAfterLoginDialogActionTaken(); + }); + } +#endif // Initialize the user interface and menu system // Needs to happen AFTER the render engine initialization to access its configuration @@ -1961,13 +1918,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo userInputMapper->registerDevice(_touchscreenVirtualPadDevice->getInputDevice()); } - QString scriptsSwitch = QString("--").append(SCRIPTS_SWITCH); - _defaultScriptsLocation.setPath(getCmdOption(argc, constArgv, scriptsSwitch.toStdString().c_str())); + if (parser.isSet("scripts")) { + _defaultScriptsLocation.setPath(parser.value("scripts")); // Might need to be done in "main.cpp". + _overrideDefaultScriptsLocation = true; + } else { + _overrideDefaultScriptsLocation = false; + } // Make sure we don't time out during slow operations at startup updateHeartbeat(); - loadSettings(); + loadSettings(parser); updateVerboseLogging(); @@ -2018,11 +1979,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // If launched from Steam, let it handle updates - const QString HIFI_NO_UPDATER_COMMAND_LINE_KEY = "--no-updater"; - bool noUpdater = arguments().indexOf(HIFI_NO_UPDATER_COMMAND_LINE_KEY) != -1; bool buildCanUpdate = BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable || BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Master; - if (!noUpdater && buildCanUpdate) { + if (!parser.isSet("no-updater") && buildCanUpdate) { constexpr auto INSTALLER_TYPE_CLIENT_ONLY = "client_only"; auto applicationUpdater = DependencyManager::set(); @@ -2191,8 +2150,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo static int NEARBY_AVATAR_RADIUS_METERS = 10; // setup the stats interval depending on if the 1s faster hearbeat was requested - static const QString FAST_STATS_ARG = "--fast-heartbeat"; - static int SEND_STATS_INTERVAL_MS = arguments().indexOf(FAST_STATS_ARG) != -1 ? 1000 : 10000; + static int SEND_STATS_INTERVAL_MS; + if (parser.isSet("fast-heartbeat")) { + SEND_STATS_INTERVAL_MS = 1000; + } else { + SEND_STATS_INTERVAL_MS = 10000; + } static glm::vec3 lastAvatarPosition = myAvatar->getWorldPosition(); static glm::mat4 lastHMDHeadPose = getHMDSensorPose(); @@ -4022,16 +3985,11 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { QString addressLookupString; // when --url in command line, teleport to location - QCommandLineParser parser; - QCommandLineOption urlOption("url", "", "value"); - parser.addOption(urlOption); - parser.parse(arguments()); - if (parser.isSet(urlOption)) { - QUrl url = QUrl(parser.value(urlOption)); - if (url.scheme() == URL_SCHEME_VIRCADIAAPP) { - Setting::Handle("startUpApp").set(url.path()); + if (!_urlParam.isEmpty()) { // Not sure if format supported by isValid(). + if (_urlParam.scheme() == URL_SCHEME_VIRCADIAAPP) { + Setting::Handle("startUpApp").set(_urlParam.path()); } else { - addressLookupString = url.toString(); + addressLookupString = _urlParam.toString(); } } @@ -5514,7 +5472,7 @@ bool Application::exportEntities(const QString& filename, float x, float y, floa return exportEntities(filename, entities, ¢er); } -void Application::loadSettings() { +void Application::loadSettings(const QCommandLineParser& parser) { sessionRunTime.set(0); // Just clean living. We're about to saveSettings, which will update value. DependencyManager::get()->loadSettings(); @@ -5544,7 +5502,7 @@ void Application::loadSettings() { } bool isFirstPerson = false; - if (arguments().contains("--no-launcher")) { + if (parser.isSet("no-launcher")) { const auto& displayPlugins = pluginManager->getDisplayPlugins(); for (const auto& plugin : displayPlugins) { if (!plugin->isHmd()) { @@ -5849,7 +5807,7 @@ void Application::resumeAfterLoginDialogActionTaken() { scriptEngines->reloadLocalFiles(); // if the --scripts command-line argument was used. - if (_defaultScriptsLocation.exists() && (arguments().indexOf(QString("--").append(SCRIPTS_SWITCH))) != -1) { + if (_overrideDefaultScriptsLocation && _defaultScriptsLocation.exists()) { scriptEngines->loadDefaultScripts(); scriptEngines->defaultScriptsLocationOverridden(true); } else { @@ -5869,7 +5827,7 @@ void Application::resumeAfterLoginDialogActionTaken() { // Set last parameter to exit interface when the test script finishes, if so requested DependencyManager::get()->loadScript(testScript, false, false, false, false, quitWhenFinished); // This is done so we don't get a "connection time-out" message when we haven't passed in a URL. - if (arguments().contains("--url")) { + if (!_urlParam.isEmpty()) { auto reply = SandboxUtils::getStatus(); connect(reply, &QNetworkReply::finished, this, [this, reply] { handleSandboxStatus(reply); }); } @@ -8831,31 +8789,21 @@ void Application::sendLambdaEvent(const std::function& f) { } } -void Application::initPlugins(const QStringList& arguments) { - QCommandLineOption display("display", "Preferred displays", "displays"); - QCommandLineOption disableDisplays("disable-displays", "Displays to disable", "displays"); - QCommandLineOption disableInputs("disable-inputs", "Inputs to disable", "inputs"); - - QCommandLineParser parser; - parser.addOption(display); - parser.addOption(disableDisplays); - parser.addOption(disableInputs); - parser.parse(arguments); - - if (parser.isSet(display)) { - auto preferredDisplays = parser.value(display).split(',', Qt::SkipEmptyParts); +void Application::initPlugins(const QCommandLineParser& parser) { + if (parser.isSet("display")) { + auto preferredDisplays = parser.value("display").split(',', Qt::SkipEmptyParts); qInfo() << "Setting prefered display plugins:" << preferredDisplays; PluginManager::getInstance()->setPreferredDisplayPlugins(preferredDisplays); } - if (parser.isSet(disableDisplays)) { - auto disabledDisplays = parser.value(disableDisplays).split(',', Qt::SkipEmptyParts); + if (parser.isSet("disable-displays")) { + auto disabledDisplays = parser.value("disableDisplays").split(',', Qt::SkipEmptyParts); qInfo() << "Disabling following display plugins:" << disabledDisplays; PluginManager::getInstance()->disableDisplays(disabledDisplays); } - if (parser.isSet(disableInputs)) { - auto disabledInputs = parser.value(disableInputs).split(',', Qt::SkipEmptyParts); + if (parser.isSet("disable-inputs")) { + auto disabledInputs = parser.value("disableInputs").split(',', Qt::SkipEmptyParts); qInfo() << "Disabling following input plugins:" << disabledInputs; PluginManager::getInstance()->disableInputs(disabledInputs); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 215473ddfbc..cd323eef08f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -15,6 +15,7 @@ #include +#include #include #include #include @@ -96,8 +97,6 @@ namespace controller { static const QString RUNNING_MARKER_FILENAME = "Interface.running"; -static const QString SCRIPTS_SWITCH = "scripts"; -static const QString HIFI_NO_LOGIN_COMMAND_LINE_KEY = "no-login-suggestion"; class Application; #if defined(qApp) @@ -130,10 +129,15 @@ class Application : public QApplication, virtual DisplayPluginPointer getActiveDisplayPlugin() const override; // FIXME? Empty methods, do we still need them? - static void initPlugins(const QStringList& arguments); + static void initPlugins(const QCommandLineParser& parser); static void shutdownPlugins(); - Application(int& argc, char** argv, QElapsedTimer& startup_time, bool runningMarkerExisted); + Application( + int& argc, char** argv, + const QCommandLineParser& parser, + QElapsedTimer& startup_time, + bool runningMarkerExisted + ); ~Application(); void postLambdaEvent(const std::function& f) override; @@ -505,7 +509,7 @@ private slots: void notifyPacketVersionMismatch(); - void loadSettings(); + void loadSettings(const QCommandLineParser& parser); void saveSettings() const; void setFailedToConnectToEntityServer(); @@ -705,6 +709,8 @@ private slots: QPointer _logDialog; QPointer _entityScriptServerLogDialog; QDir _defaultScriptsLocation; + // If above is only set by parameter, below is unnecessary. + bool _overrideDefaultScriptsLocation; TouchEvent _lastTouchEvent; @@ -830,6 +836,8 @@ private slots: bool quitWhenFinished { false }; + QUrl _urlParam; + bool _showTrackedObjects { false }; bool _prevShowTrackedObjects { false }; diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 8c8a4c3ce99..e18312f8b72 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -64,59 +64,219 @@ int main(int argc, const char* argv[]) { setupHifiApplication(BuildInfo::INTERFACE_NAME); - QStringList arguments; - for (int i = 0; i < argc; ++i) { - arguments << argv[i]; - } - QCommandLineParser parser; - parser.setApplicationDescription("Overte"); - QCommandLineOption versionOption = parser.addVersionOption(); + parser.setApplicationDescription("Overte -- A free/libre and open-source metaverse client"); QCommandLineOption helpOption = parser.addHelpOption(); + QCommandLineOption versionOption = parser.addVersionOption(); - QCommandLineOption urlOption("url", "", "value"); - QCommandLineOption noLauncherOption("no-launcher", "Do not execute the launcher"); - QCommandLineOption noUpdaterOption("no-updater", "Do not show auto-updater"); - QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications"); - QCommandLineOption runServerOption("runServer", "Whether to run the server"); - QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content ", "serverContentPath"); - QCommandLineOption allowMultipleInstancesOption("allowMultipleInstances", "Allow multiple instances to run"); - QCommandLineOption overrideAppLocalDataPathOption("cache", "set test cache ", "dir"); - QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts ", "path"); - QCommandLineOption responseTokensOption("tokens", "set response tokens ", "json"); - QCommandLineOption displayNameOption("displayName", "set user display name ", "string"); - QCommandLineOption setBookmarkOption("setBookmark", "set bookmark key=value pair", "string"); - QCommandLineOption defaultScriptOverrideOption("defaultScriptsOverride", "override defaultsScripts.js", "string"); - QCommandLineOption forceCrashReportingOption("forceCrashReporting", "Force crash reporting to initialize"); + QCommandLineOption urlOption( + "url", + "Start at specified URL location.", + "string" + ); + QCommandLineOption protocolVersionOption( + "protocolVersion", + "Writes the protocol version base64 signature to a file?", + "path" + ); + QCommandLineOption noUpdaterOption( + "no-updater", + "Do not show auto-updater." + ); + QCommandLineOption checkMinSpecOption( + "checkMinSpec", + "Check if machine meets minimum specifications. The program will run if check passes." + ); + QCommandLineOption runServerOption( + "runServer", + "Run the server." + ); + QCommandLineOption listenPortOption( + "listenPort", + "Port to listen on.", + "port_number" + ); + QCommandLineOption serverContentPathOption( + "serverContentPath", + "Path to find server content.", // What content?? + "path" + ); + QCommandLineOption overrideAppLocalDataPathOption( + "cache", + "Set test cache.", + "dir" + ); + QCommandLineOption scriptsOption( + "scripts", + "Set path for defaultScripts. These are probably scripts that run automatically. This parameter does not seem to work.", + "dir" + ); + QCommandLineOption allowMultipleInstancesOption( + "allowMultipleInstances", + "Allow multiple instances to run." + ); + QCommandLineOption displaysOption( + "display", + "Preferred display.", + "displays" + ); + QCommandLineOption disableDisplaysOption( + "disable-displays", + "Displays to disable.", + "string" + ); + QCommandLineOption disableInputsOption( + "disable-inputs", + "Inputs to disable.", + "string" + ); + QCommandLineOption suppressSettingsResetOption( + "suppress-settings-reset", + "Suppress the prompt to reset interface settings." + ); + QCommandLineOption oculusStoreOption( + "oculus-store", + "Let the Oculus plugin know if interface was run from the Oculus Store." + ); + QCommandLineOption standaloneOption( + "standalone", + "Emulate a standalone device." + ); + QCommandLineOption disableWatchdogOption( + "disableWatchdog", + "Disable the watchdog thread. The interface will crash on deadlocks." + ); + QCommandLineOption systemCursorOption( + "system-cursor", + "Use the default system cursor." + ); + QCommandLineOption concurrentDownloadsOption( + "concurrent-downloads", + "Maximum concurrent resource downloads. Default is 16, except for Android where it is 4.", + "integer" + ); + QCommandLineOption avatarURLOption( + "avatarURL", + "Override the avatar U.R.L.", + "url" + ); + QCommandLineOption replaceAvatarURLOption( + "replace-avatar-url", + "Replaces the avatar U.R.L. When used with --avatarURL, this takes precedence.", + "url" + ); + QCommandLineOption setBookmarkOption( + "setBookmark", + "Set bookmark as key=value pair. Including the '=' symbol in either string is unsupported.", + "string" + ); + QCommandLineOption forceCrashReportingOption( + "forceCrashReporting", + "Force crash reporting to initialize." + ); + // The documented "--disable-lod" does not seem to exist. + // Below are undocumented. + QCommandLineOption noLauncherOption( + "no-launcher", + "Supposedly does something for the server, unrelated to the application launcher. The feature may never have been implemented." + ); + QCommandLineOption overrideScriptsPathOption( + "overrideScriptsPath", + "Specifies path to default directory where the application will look for scripts to load.", + "string" + ); + QCommandLineOption defaultScriptsOverrideOption( + "defaultScriptsOverride", + "Override default script to run automatically on start. Default is \"defaultsScripts.js\".", + "string" + ); + QCommandLineOption responseTokensOption( + "tokens", + "Set response tokens .", + "json" + ); + QCommandLineOption displayNameOption( + "displayName", + "Set user display name .", + "string" + ); + QCommandLineOption noLoginOption( + "no-login-suggestion", + "Do not show log-in dialogue." + ); + QCommandLineOption traceFileOption( + "traceFile", + "Writes a trace to a file in the documents folder. Only works if \"--traceDuration\" is specified.", + "path" + ); + QCommandLineOption traceDurationOption( + "traceDuration", + "Automatically quit interface after duration. Only works if \"--traceFile\" is specified.", + "seconds" + ); + QCommandLineOption clockSkewOption( + "clockSkew", + "Forces client instance's clock to skew for demonstration purposes.", + "integer" + ); // This should probably be removed. + QCommandLineOption testScriptOption( + "testScript", + "Undocumented. Accepts parameter as U.R.L.", + "string" + ); + QCommandLineOption testResultsLocationOption( + "testResultsLocation", + "Undocumented", + "path" + ); + QCommandLineOption quitWhenFinishedOption( + "quitWhenFinished", + "Only works if \"--testScript\" is provided." + ); // Should probably also be made to work on testResultsLocationOption. + QCommandLineOption fastHeartbeatOption( + "fast-heartbeat", + "Change stats polling interval from 10000ms to 1000ms." + ); + // "--qmljsdebugger", which appears in output from "--help-all". + // Those below don't seem to be optional. + // --ignore-gpu-blacklist + // --suppress-settings-reset parser.addOption(urlOption); - parser.addOption(noLauncherOption); + parser.addOption(protocolVersionOption); parser.addOption(noUpdaterOption); parser.addOption(checkMinSpecOption); parser.addOption(runServerOption); + parser.addOption(listenPortOption); parser.addOption(serverContentPathOption); parser.addOption(overrideAppLocalDataPathOption); - parser.addOption(overrideScriptsPathOption); + parser.addOption(scriptsOption); parser.addOption(allowMultipleInstancesOption); - parser.addOption(responseTokensOption); - parser.addOption(displayNameOption); + parser.addOption(displaysOption); + parser.addOption(disableDisplaysOption); + parser.addOption(disableInputsOption); + parser.addOption(suppressSettingsResetOption); + parser.addOption(oculusStoreOption); + parser.addOption(standaloneOption); + parser.addOption(disableWatchdogOption); + parser.addOption(systemCursorOption); + parser.addOption(concurrentDownloadsOption); + parser.addOption(avatarURLOption); + parser.addOption(replaceAvatarURLOption); parser.addOption(setBookmarkOption); - parser.addOption(defaultScriptOverrideOption); parser.addOption(forceCrashReportingOption); - - if (!parser.parse(arguments)) { - std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam - } - - if (parser.isSet(versionOption)) { - parser.showVersion(); - Q_UNREACHABLE(); - } - if (parser.isSet(helpOption)) { - QCoreApplication mockApp(argc, const_cast(argv)); // required for call to showHelp() - parser.showHelp(); - Q_UNREACHABLE(); - } + parser.addOption(noLauncherOption); + parser.addOption(responseTokensOption); + parser.addOption(displayNameOption); + parser.addOption(overrideScriptsPathOption); + parser.addOption(defaultScriptsOverrideOption); + parser.addOption(traceFileOption); + parser.addOption(traceDurationOption); + parser.addOption(clockSkewOption); + parser.addOption(testScriptOption); + parser.addOption(testResultsLocationOption); + parser.addOption(quitWhenFinishedOption); + parser.addOption(fastHeartbeatOption); QString applicationPath; // A temporary application instance is needed to get the location of the running executable @@ -125,6 +285,9 @@ int main(int argc, const char* argv[]) { // cross-platform implementation. { QCoreApplication tempApp(argc, const_cast(argv)); + + parser.process(QCoreApplication::arguments()); // Must be run after QCoreApplication is initalised. + #ifdef Q_OS_OSX if (QFileInfo::exists(QCoreApplication::applicationDirPath() + "/../../../config.json")) { applicationPath = QCoreApplication::applicationDirPath() + "/../../../"; @@ -135,6 +298,29 @@ int main(int argc, const char* argv[]) { applicationPath = QCoreApplication::applicationDirPath(); #endif } + + // Act on arguments for early termination. + if (parser.isSet(versionOption)) { + parser.showVersion(); + Q_UNREACHABLE(); + } + if (parser.isSet(helpOption)) { + QCoreApplication mockApp(argc, const_cast(argv)); // required for call to showHelp() + parser.showHelp(); + Q_UNREACHABLE(); + } + if (parser.isSet(protocolVersionOption)) { + FILE* fp = fopen(parser.value(protocolVersionOption).toStdString().c_str(), "w"); + if (fp) { + fputs(protocolVersionsSignatureBase64().toStdString().c_str(), fp); + fclose(fp); + return 0; + } else { + qWarning() << "Failed to open file specified for --protocolVersion."; + return 1; + } + } + static const QString APPLICATION_CONFIG_FILENAME = "config.json"; QDir applicationDir(applicationPath); QString configFileName = applicationDir.filePath(APPLICATION_CONFIG_FILENAME); @@ -173,20 +359,17 @@ int main(int argc, const char* argv[]) { // Early check for --traceFile argument auto tracer = DependencyManager::set(); const char * traceFile = nullptr; - const QString traceFileFlag("--traceFile"); float traceDuration = 0.0f; - for (int a = 1; a < argc; ++a) { - if (traceFileFlag == argv[a] && argc > a + 1) { - traceFile = argv[a + 1]; - if (argc > a + 2) { - traceDuration = atof(argv[a + 2]); - } - break; + if (parser.isSet(traceFileOption)) { + traceFile = parser.value(traceFileOption).toStdString().c_str(); + if (parser.isSet(traceDurationOption)) { + traceDuration = parser.value(traceDurationOption).toFloat(); + tracer->startTracing(); + } else { + qWarning() << "\"--traceDuration\" must be specified along with \"--traceFile\"..."; + return 1; } } - if (traceFile != nullptr) { - tracer->startTracing(); - } PROFILE_SYNC_BEGIN(startup, "main startup", ""); @@ -242,7 +425,7 @@ int main(int argc, const char* argv[]) { instanceMightBeRunning = false; } // this needs to be done here in main, as the mechanism for setting the - // scripts directory appears not to work. See the bug report + // scripts directory appears not to work. See the bug report (dead link) // https://highfidelity.fogbugz.com/f/cases/5759/Issues-changing-scripts-directory-in-ScriptsEngine if (parser.isSet(overrideScriptsPathOption)) { QDir scriptsPath(parser.value(overrideScriptsPathOption)); @@ -317,18 +500,17 @@ int main(int argc, const char* argv[]) { // Debug option to demonstrate that the client's local time does not // need to be in sync with any other network node. This forces clock // skew for the individual client - const char* CLOCK_SKEW = "--clockSkew"; - const char* clockSkewOption = getCmdOption(argc, argv, CLOCK_SKEW); - if (clockSkewOption) { - qint64 clockSkew = atoll(clockSkewOption); + if (parser.isSet(clockSkewOption)) { + const char* clockSkewValue = parser.value(clockSkewOption).toStdString().c_str(); + qint64 clockSkew = atoll(clockSkewValue); usecTimestampNowForceClockSkew(clockSkew); - qCDebug(interfaceapp) << "clockSkewOption=" << clockSkewOption << "clockSkew=" << clockSkew; + qCDebug(interfaceapp) << "clockSkewOption=" << clockSkewValue << "clockSkew=" << clockSkew; } // Oculus initialization MUST PRECEDE OpenGL context creation. // The nature of the Application constructor means this has to be either here, // or in the main window ctor, before GL startup. - Application::initPlugins(arguments); + Application::initPlugins(parser); #ifdef Q_OS_WIN // If we're running in steam mode, we need to do an explicit check to ensure we're up to the required min spec @@ -376,7 +558,7 @@ int main(int argc, const char* argv[]) { PROFILE_SYNC_END(startup, "main startup", ""); PROFILE_SYNC_BEGIN(startup, "app full ctor", ""); - Application app(argcExtended, const_cast(argvExtended.data()), startupTime, runningMarkerExisted); + Application app(argcExtended, const_cast(argvExtended.data()), parser, startupTime, runningMarkerExisted); PROFILE_SYNC_END(startup, "app full ctor", ""); #if defined(Q_OS_LINUX)