1
0
Fork 0

Launcher: ensure extra args override launcher ones

Categorise arguments to ensure user-specified ones take precedence
over values set by the launcher.

When any positional arg is set, exclude all positional ones from being
set at all, to prevent strange interactions.
This commit is contained in:
James Turner 2018-06-25 23:06:20 +01:00
parent 9098e47664
commit 268f9c9c33
7 changed files with 144 additions and 37 deletions

View file

@ -1,5 +1,7 @@
#include "LaunchConfig.hxx" #include "LaunchConfig.hxx"
#include <set>
#include <Main/options.hxx> #include <Main/options.hxx>
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
@ -20,31 +22,50 @@ void LaunchConfig::reset()
void LaunchConfig::applyToOptions() const void LaunchConfig::applyToOptions() const
{ {
// build a set of all the extra args we have defined
std::set<std::string> extraArgNames;
for (auto arg : m_values) {
// don't override prop: arguments
if (arg.arg == "prop") continue;
if (arg.origin == ExtraArgs)
extraArgNames.insert(arg.arg.toStdString());
}
flightgear::Options* options = flightgear::Options::sharedInstance(); flightgear::Options* options = flightgear::Options::sharedInstance();
std::for_each(m_values.begin(), m_values.end(), [options](const Arg& arg) std::for_each(m_values.begin(), m_values.end(),
{ [options, &extraArgNames](const Arg& arg)
options->addOption(arg.arg.toStdString(), arg.value.toStdString()); {
}); const auto name = arg.arg.toStdString();
if (arg.origin == Launcher) {
auto it = extraArgNames.find(name);
if (it != extraArgNames.end()) {
qInfo() << "skipping arg:" << arg.arg << "=" << arg.value << "because the user has over-ridden it";
return;
}
}
options->addOption(name, arg.value.toStdString());
});
} }
void LaunchConfig::setArg(QString name, QString value) void LaunchConfig::setArg(QString name, QString value, Origin origin)
{ {
m_values.push_back(Arg(name, value)); m_values.push_back(Arg(name, value, origin));
} }
void LaunchConfig::setArg(const std::string &name, const std::string &value) void LaunchConfig::setArg(const std::string &name, const std::string &value)
{ {
setArg(QString::fromStdString(name), QString::fromStdString(value)); setArg(QString::fromStdString(name), QString::fromStdString(value), Launcher);
} }
void LaunchConfig::setProperty(QString path, QVariant value) void LaunchConfig::setProperty(QString path, QVariant value, Origin origin)
{ {
m_values.push_back(Arg("prop", path + "=" + value.toString())); m_values.push_back(Arg("prop", path + "=" + value.toString(), origin));
} }
void LaunchConfig::setEnableDisableOption(QString name, bool value) void LaunchConfig::setEnableDisableOption(QString name, bool value)
{ {
m_values.push_back(Arg((value ? "enable-" : "disable-") + name)); m_values.push_back(Arg((value ? "enable-" : "disable-") + name, "", Launcher));
} }
QString LaunchConfig::htmlForCommandLine() QString LaunchConfig::htmlForCommandLine()
@ -59,20 +80,26 @@ QString LaunchConfig::htmlForCommandLine()
} }
html += "</ul>\n"; html += "</ul>\n";
} }
#if 0
if (m_extraSettings) {
LauncherArgumentTokenizer tk;
Q_FOREACH(auto arg, tk.tokenize(m_extraSettings->argsText())) {
// m_config->setArg(arg.arg, arg.value);
}
}
#endif
reset(); reset();
collect(); collect();
html += tr("<p>Options set in the launcher:</p>\n"); html += tr("<p>Options set in the launcher:</p>\n");
html += "<ul>\n"; html += "<ul>\n";
for (auto arg : values()) { for (auto arg : valuesFromLauncher()) {
if (arg.value.isEmpty()) {
html += QString("<li>--") + arg.arg + "</li>\n";
} else if (arg.arg == "prop") {
html += QString("<li>--") + arg.arg + ":" + arg.value + "</li>\n";
} else {
html += QString("<li>--") + arg.arg + "=" + arg.value + "</li>\n";
}
}
html += "</ul>\n";
html += tr("<p>Options set as additional arguments:</p>\n");
html += "<ul>\n";
for (auto arg : valuesFromExtraArgs()) {
if (arg.value.isEmpty()) { if (arg.value.isEmpty()) {
html += QString("<li>--") + arg.arg + "</li>\n"; html += QString("<li>--") + arg.arg + "</li>\n";
} else if (arg.arg == "prop") { } else if (arg.arg == "prop") {
@ -127,3 +154,19 @@ auto LaunchConfig::values() const -> std::vector<Arg>
{ {
return m_values; return m_values;
} }
auto LaunchConfig::valuesFromLauncher() const -> std::vector<Arg>
{
std::vector<Arg> result;
std::copy_if(m_values.begin(), m_values.end(), std::back_inserter(result), [](const Arg& a)
{ return a.origin == Launcher; });
return result;
}
auto LaunchConfig::valuesFromExtraArgs() const -> std::vector<Arg>
{
std::vector<Arg> result;
std::copy_if(m_values.begin(), m_values.end(), std::back_inserter(result), [](const Arg& a)
{ return a.origin == ExtraArgs; });
return result;
}

View file

@ -13,13 +13,22 @@ class LaunchConfig : public QObject
Q_PROPERTY(QString defaultDownloadDir READ defaultDownloadDir CONSTANT) Q_PROPERTY(QString defaultDownloadDir READ defaultDownloadDir CONSTANT)
Q_PROPERTY(bool enableDownloadDirUI READ enableDownloadDirUI CONSTANT) Q_PROPERTY(bool enableDownloadDirUI READ enableDownloadDirUI CONSTANT)
public: public:
enum Origin
{
Launcher = 0,
ExtraArgs
};
Q_ENUMS(Origin);
class Arg class Arg
{ {
public: public:
explicit Arg(QString k, QString v = QString()) : arg(k), value(v) {} explicit Arg(QString k, QString v, Origin o) : arg(k), value(v), origin(o) {}
QString arg; QString arg;
QString value; QString value;
Origin origin;
}; };
@ -30,11 +39,13 @@ public:
std::vector<Arg> values() const; std::vector<Arg> values() const;
Q_INVOKABLE void setArg(QString name, QString value = QString());
Q_INVOKABLE void setArg(QString name, QString value = QString(), Origin origin = Launcher);
Q_INVOKABLE void setArg(const std::string& name, const std::string& value = std::string()); Q_INVOKABLE void setArg(const std::string& name, const std::string& value = std::string());
Q_INVOKABLE void setProperty(QString path, QVariant value); Q_INVOKABLE void setProperty(QString path, QVariant value, Origin origin = Launcher);
Q_INVOKABLE void setEnableDisableOption(QString name, bool value); Q_INVOKABLE void setEnableDisableOption(QString name, bool value);
@ -54,6 +65,9 @@ public:
static void setEnableDownloadDirUI(bool enableDownloadDirUI); static void setEnableDownloadDirUI(bool enableDownloadDirUI);
std::vector<Arg> valuesFromLauncher() const;
std::vector<Arg> valuesFromExtraArgs() const;
signals: signals:
void collect(); void collect();

View file

@ -128,6 +128,14 @@ bool LauncherArgumentTokenizer::isValid() const
return m_valid; return m_valid;
} }
const std::set<std::string> unsupportedArgs({
"fg-aircraft", "fg-root", "fg-home", "aircraft-dir", "fg-scenery"});
bool LauncherArgumentTokenizer::haveUnsupportedArgs() const
{
return haveArgsIn(unsupportedArgs);
}
void LauncherArgumentTokenizer::setArgString(QString argString) void LauncherArgumentTokenizer::setArgString(QString argString)
{ {
if (m_argString == argString) if (m_argString == argString)
@ -138,20 +146,29 @@ void LauncherArgumentTokenizer::setArgString(QString argString)
emit argStringChanged(m_argString); emit argStringChanged(m_argString);
} }
const std::set<std::string> argBlacklist({ const std::set<std::string> positionalArgs({
"lat", "lon", "aircraft", "airport", "parkpos", "season", "lat", "lon", "vc", "vor", "ndb", "fix"
"runway", "vor", "time-offset", "timeofday"}); "airport", "parkpos", "runway",
"heading", "altitude", "offset-azimuth",
"offset-distance", "glideslope",
"on-ground"});
bool LauncherArgumentTokenizer::haveProtectedArgs() const bool LauncherArgumentTokenizer::haveArgsIn(const std::set<std::string>& args) const
{ {
if (!m_valid) if (!m_valid)
return false; return false;
auto n = std::count_if(m_tokens.begin(), m_tokens.end(), auto n = std::count_if(m_tokens.begin(), m_tokens.end(),
[](const ArgumentToken& tk) [&args](const ArgumentToken& tk)
{ {
return (argBlacklist.find(tk.arg.toStdString()) != argBlacklist.end()); return (args.find(tk.arg.toStdString()) != args.end());
}); });
return (n > 0); return (n > 0);
} }
bool LauncherArgumentTokenizer::havePositionalArgs() const
{
return haveArgsIn(positionalArgs);
}

View file

@ -1,6 +1,8 @@
#ifndef LAUNCHERARGUMENTTOKENIZER_HXX #ifndef LAUNCHERARGUMENTTOKENIZER_HXX
#define LAUNCHERARGUMENTTOKENIZER_HXX #define LAUNCHERARGUMENTTOKENIZER_HXX
#include <set>
#include <QString> #include <QString>
#include <QList> #include <QList>
#include <QObject> #include <QObject>
@ -23,7 +25,8 @@ class LauncherArgumentTokenizer : public QObject
Q_PROPERTY(QVariantList tokens READ tokens NOTIFY argStringChanged) Q_PROPERTY(QVariantList tokens READ tokens NOTIFY argStringChanged)
Q_PROPERTY(bool valid READ isValid NOTIFY argStringChanged) Q_PROPERTY(bool valid READ isValid NOTIFY argStringChanged)
Q_PROPERTY(bool warnProtectedArgs READ haveProtectedArgs NOTIFY argStringChanged) Q_PROPERTY(bool havePositionalArgs READ havePositionalArgs NOTIFY argStringChanged)
Q_PROPERTY(bool haveUnsupportedArgs READ haveUnsupportedArgs NOTIFY argStringChanged)
public: public:
LauncherArgumentTokenizer(); LauncherArgumentTokenizer();
@ -38,7 +41,8 @@ public:
bool isValid() const; bool isValid() const;
bool haveProtectedArgs() const; bool haveUnsupportedArgs() const;
bool havePositionalArgs() const;
public slots: public slots:
void setArgString(QString argString); void setArgString(QString argString);
@ -46,6 +50,8 @@ signals:
void argStringChanged(QString argString); void argStringChanged(QString argString);
private: private:
bool haveArgsIn(const std::set<std::string> &args) const;
void tokenize(QString in); void tokenize(QString in);
enum State { enum State {

View file

@ -943,6 +943,11 @@ void LocationController::applyPositionOffset()
void LocationController::onCollectConfig() void LocationController::onCollectConfig()
{ {
if (m_skipFromArgs) {
qInfo() << Q_FUNC_INFO << "skipping argument collection";
return;
}
if (m_locationIsLatLon) { if (m_locationIsLatLon) {
m_config->setArg("lat", QString::number(m_geodLocation.getLatitudeDeg())); m_config->setArg("lat", QString::number(m_geodLocation.getLatitudeDeg()));
m_config->setArg("lon", QString::number(m_geodLocation.getLongitudeDeg())); m_config->setArg("lon", QString::number(m_geodLocation.getLongitudeDeg()));

View file

@ -70,6 +70,10 @@ class LocationController : public QObject
Q_PROPERTY(QmlPositioned* base READ baseLocation CONSTANT) Q_PROPERTY(QmlPositioned* base READ baseLocation CONSTANT)
Q_PROPERTY(QmlPositioned* detail READ detail CONSTANT) Q_PROPERTY(QmlPositioned* detail READ detail CONSTANT)
Q_PROPERTY(bool isBaseLatLon READ isBaseLatLon NOTIFY baseLocationChanged) Q_PROPERTY(bool isBaseLatLon READ isBaseLatLon NOTIFY baseLocationChanged)
// allow collecting the location properties to be disabled, if the
// user is setting conflicting ones
Q_PROPERTY(bool skipFromArgs MEMBER m_skipFromArgs NOTIFY skipFromArgsChanged)
public: public:
explicit LocationController(QObject *parent = nullptr); explicit LocationController(QObject *parent = nullptr);
~LocationController(); ~LocationController();
@ -184,6 +188,7 @@ Q_SIGNALS:
void offsetChanged(); void offsetChanged();
void baseLocationChanged(); void baseLocationChanged();
void configChanged(); void configChanged();
void skipFromArgsChanged();
private Q_SLOTS: private Q_SLOTS:
void onCollectConfig(); void onCollectConfig();
@ -229,6 +234,7 @@ private:
bool m_speedEnabled = false; bool m_speedEnabled = false;
AltitudeType m_altitudeType = Off; AltitudeType m_altitudeType = Off;
int m_flightLevel = 0; int m_flightLevel = 0;
bool m_skipFromArgs = false;
}; };
#endif // LOCATION_CONTROLLER_HXX #endif // LOCATION_CONTROLLER_HXX

View file

@ -29,12 +29,22 @@ SettingControl {
SettingDescription { SettingDescription {
id: warningText id: warningText
enabled: root.enabled enabled: root.enabled
visible: tokenizer.warnProtectedArgs visible: tokenizer.haveUnsupportedArgs
width: parent.width width: parent.width
text: qsTr("<b>Warning:</b> certain arguments such as the aircraft, location and time are set directly " + text: qsTr("<b>Warning:</b> specifying <tt>fg-root</tt>, <tt>fg-aircraft</tt>, <tt>fg-scenery</tt> or <tt>fg-home</tt> " +
"by this launcher. Attempting to set them here will <b>not</b> work as expected, " + "using this section is not recommended, and may cause problem or prevent the simulator from running. " +
"since the same or conflicting arguments may be set. (For exmmple, this may cause " + "Please use the add-ons page to setup scenery and aircrft directories, and the 'Select data files location' " +
"you to start at the wrong position)"); "menu item to change the root data directory.");
}
SettingDescription {
id: positionalArgsText
enabled: root.enabled
visible: tokenizer.havePositionalArgs
width: parent.width
text: qsTr("<b>Note:</b> you have entered arguments relating to the startup location below. " +
"To prevent problems caused by conflicting settings, the values entered on the location " +
"page (for example, airport or altitude) will be ignored.");
} }
Rectangle { Rectangle {
@ -73,13 +83,19 @@ SettingControl {
for (var i = 0; i < tokens.length; i++) { for (var i = 0; i < tokens.length; i++) {
var tk = tokens[i]; var tk = tokens[i];
if (tk.arg.substring(0, 5) === "prop:") { if (tk.arg.substring(0, 5) === "prop:") {
_config.setProperty(tk.arg.substring(5), tk.value); _config.setProperty(tk.arg.substring(5), tk.value, LaunchConfig.ExtraArgs);
} else { } else {
_config.setArg(tk.arg, tk.value); _config.setArg(tk.arg, tk.value, LaunchConfig.ExtraArgs);
} }
} }
} }
Binding {
target: _location
property: "skipFromArgs"
value: tokenizer.havePositionalArgs
}
ArgumentTokenizer { ArgumentTokenizer {
id: tokenizer id: tokenizer
argString: edit.text argString: edit.text