Merge pull request #66 from Penguin-Guru/FixingParameters

Overhaul command-line parameter functionality.
This commit is contained in:
Dale Glass 2022-06-04 21:31:18 +02:00 committed by GitHub
commit 1283c5230d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 357 additions and 219 deletions

View file

@ -26,7 +26,6 @@
#include <QtCore/QResource>
#include <QtCore/QAbstractNativeEventFilter>
#include <QtCore/QCommandLineParser>
#include <QtCore/QMimeData>
#include <QtCore/QThreadPool>
#include <QtCore/QFileSelector>
@ -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<const char**>(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<void(FILE* fp)> 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<const char**>(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<const char**>(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<const char**>(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<const char**>(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<LimitedNodeList, NodeList>();
DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
@ -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<OffscreenUi> 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<PhysicalEntitySimulation>()),
_physicsEngine(std::make_shared<PhysicsEngine>(Vectors::ZERO)),
_entityClipboard(std::make_shared<EntityTree>()),
@ -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>();
addressManager->moveToThread(nodeList->thread());
const char** constArgv = const_cast<const char**>(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));
}
}
else {
setPreferredCursor(Cursor::Manager::getIconName(Cursor::Icon::SYSTEM));
}
connect(this, &Application::activeDisplayPluginChanged, this, [=](){
qApp->setProperty(hifi::properties::HMD, qApp->isHMDMode());
auto displayPlugin = qApp->getActiveDisplayPlugin();
setCrashAnnotation("display_plugin", displayPlugin->getName().toStdString());
setCrashAnnotation("hmd", displayPlugin->isHmd() ? "1" : "0");
});
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));
}
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<AutoUpdater>();
@ -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<QVariant>("startUpApp").set(url.path());
if (!_urlParam.isEmpty()) { // Not sure if format supported by isValid().
if (_urlParam.scheme() == URL_SCHEME_VIRCADIAAPP) {
Setting::Handle<QVariant>("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, &center);
}
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<AudioClient>()->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<ScriptEngines>()->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<void()>& 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);
}

View file

@ -15,6 +15,7 @@
#include <functional>
#include <QtCore/QCommandLineParser>
#include <QtCore/QHash>
#include <QtCore/QPointer>
#include <QtCore/QSet>
@ -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 @@ public:
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<void()>& 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:
QPointer<LogDialog> _logDialog;
QPointer<EntityScriptServerLogDialog> _entityScriptServerLogDialog;
QDir _defaultScriptsLocation;
// If above is only set by parameter, below is unnecessary.
bool _overrideDefaultScriptsLocation;
TouchEvent _lastTouchEvent;
@ -830,6 +836,8 @@ private:
bool quitWhenFinished { false };
QUrl _urlParam;
bool _showTrackedObjects { false };
bool _prevShowTrackedObjects { false };

View file

@ -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 <path>", "serverContentPath");
QCommandLineOption allowMultipleInstancesOption("allowMultipleInstances", "Allow multiple instances to run");
QCommandLineOption overrideAppLocalDataPathOption("cache", "set test cache <dir>", "dir");
QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts <path>", "path");
QCommandLineOption responseTokensOption("tokens", "set response tokens <json>", "json");
QCommandLineOption displayNameOption("displayName", "set user display name <string>", "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>.",
"json"
);
QCommandLineOption displayNameOption(
"displayName",
"Set user display name <string>.",
"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(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(forceCrashReportingOption);
parser.addOption(noLauncherOption);
parser.addOption(responseTokensOption);
parser.addOption(displayNameOption);
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<char**>(argv)); // required for call to showHelp()
parser.showHelp();
Q_UNREACHABLE();
}
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<char**>(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<char**>(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<tracing::Tracer>();
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<char**>(argvExtended.data()), startupTime, runningMarkerExisted);
Application app(argcExtended, const_cast<char**>(argvExtended.data()), parser, startupTime, runningMarkerExisted);
PROFILE_SYNC_END(startup, "app full ctor", "");
#if defined(Q_OS_LINUX)