6#include <unordered_set>
9#include <simgear/io/iostreams/sgstream.hxx>
10#include <simgear/misc/sg_dir.hxx>
11#include <simgear/props/props_io.hxx>
12#include <simgear/structure/commands.hxx>
13#include <simgear/structure/exception.hxx>
26const char* kPresetPropPath =
"/sim/rendering/preset";
27const char* kPresetNameProp =
"/sim/rendering/preset-name";
28const char* kPresetDescriptionProp =
"/sim/rendering/preset-description";
29const char* kPresetActiveProp =
"/sim/rendering/preset-active";
32const char* kRestartRequiredProp =
"/sim/rendering/restart-required";
33const char* kSceneryReloadRequiredProp =
"/sim/rendering/scenery-reload-required";
34const char* kCompositorReloadRequiredProp =
"/sim/rendering/compositor-reload-required";
51 std::string previousValue;
52 SGPropertyNode_ptr node;
55 using PropertyNodeList = std::vector<WatchedProp>;
56 PropertyNodeList _watchedProps;
61 _presetProp =
fgGetNode(kPresetPropPath,
true);
66 auto it = findProp(n);
67 if (it != _watchedProps.end()) {
69 SG_LOG(SG_GUI, SG_ALERT,
"GraphicsPresets: Duplicate registration for: " << n->getPath());
73 WatchedProp
p = {n->getStringValue(), n};
74 _watchedProps.push_back(
p);
75 n->addChangeListener(
this);
80 for (
const auto& w : _watchedProps) {
81 w.node->removeChangeListener(
this);
83 _watchedProps.clear();
88 if (!_presetProp->hasValue()) {
92 auto it = findProp(prop);
93 assert(it != _watchedProps.end());
94 const std::string newValue = prop->getStringValue();
95 if (newValue == it->previousValue) {
99 SG_LOG(SG_GUI, SG_INFO,
"GraphicsPreset clearing; setting: " << prop->getPath() <<
" was modified");
101 _presetProp->clearValue();
108 SGPropertyNode_ptr _presetProp;
110 PropertyNodeList::iterator findProp(SGPropertyNode* node)
112 return std::find_if(_watchedProps.begin(), _watchedProps.end(), [node](
const WatchedProp& w) {
113 return w.node == node;
128 _requiredProp =
fgGetNode(requiredProp,
true);
129 _requiredProp->setBoolValue(
false);
135 for (
const auto& c : props->getChildren(
"property")) {
137 string path = simgear::strutils::strip(c->getStringValue());
141 SGPropertyNode_ptr n =
fgGetNode(path,
true);
143 n->addChangeListener(
this);
150 if (_requiredProp->getBoolValue()) {
151 _requiredProp->setBoolValue(
false);
157 if (!_requiredProp->getBoolValue()) {
158 SG_LOG(SG_GUI, SG_INFO,
"GraphicsPreset: saw modification of: " << prop->getPath() <<
", setting:" << _requiredProp->getPath() <<
" to true");
159 _requiredProp->setBoolValue(
true);
164 SGPropertyNode_ptr _requiredProp;
172 if (arg->hasChild(
"path")) {
173 SGPath
p = SGPath::fromUtf8(arg->getStringValue(
"path"));
175 SG_LOG(SG_IO, SG_ALERT,
"apply-graphics-preset: no file at: " <<
p);
179 result = gp->applyCustomPreset(
p);
180 }
else if (arg->hasChild(
"preset-name")) {
183 gp->applyPresetByName(arg->getStringValue(
"preset-name"));
184 }
else if (arg->hasChild(
"preset")) {
185 fgSetString(kPresetPropPath, arg->getStringValue(
"preset"));
186 result = gp->applyCurrentPreset();
189 result = gp->applyCurrentPreset();
192 if (arg->getBoolValue(
"reload-scenery")) {
193 if (
fgGetBool(kSceneryReloadRequiredProp)) {
194 SG_LOG(SG_GUI, SG_MANDATORY_INFO,
"apply-graphics-preset: triggering scenery reload");
195 globals->get_scenery()->reinit();
204 if (!arg->hasChild(
"path")) {
205 SG_LOG(SG_GUI, SG_ALERT,
"do_save_preset: no out path argument provided");
209 const string spath = arg->getStringValue(
"path");
210 if (spath ==
"!ask") {
213 const SGPath path = SGPath::fromUtf8(spath);
214 const string name = arg->getStringValue(
"name");
215 const string description = arg->getStringValue(
"description");
224 if (!arg->hasValue(
"destination-path")) {
225 SG_LOG(SG_GUI, SG_ALERT,
"list-graphics-preset: no destination path supplied");
229 SGPropertyNode_ptr destRoot =
fgGetNode(arg->getStringValue(
"destination-path"),
true );
231 if (arg->hasValue(
"clear-destination")) {
232 destRoot->removeAllChildren();
236 if (arg->getBoolValue(
"as-combobox-values")) {
237 for (
const auto& preset : gp->listPresets()) {
238 SGPropertyNode_ptr v = destRoot->addChild(
"value");
239 v->setStringValue(preset.name);
242 for (
const auto& preset : gp->listPresets()) {
243 SGPropertyNode_ptr pn = destRoot->addChild(
"preset");
244 pn->setStringValue(
"name", preset.name);
245 pn->setStringValue(
"id", preset.id);
246 pn->setStringValue(
"description", preset.description);
267 const string currentPreset =
fgGetString(kPresetPropPath);
269 if (!currentPreset.empty()) {
270 SG_LOG(SG_GUI, SG_INFO,
"Applying graphics preset: " << currentPreset);
281 SGPropertyNode_ptr graphicsPropsXML(
new SGPropertyNode);
283 readProperties(
globals->findDataPath(
"Video/graphics-properties.xml"), graphicsPropsXML.get());
285 _restartListener.reset(
new RequiredPropertyListener{kRestartRequiredProp, graphicsPropsXML->getChild(
"restart-required")});
286 _sceneryReloadListener.reset(
new RequiredPropertyListener{kSceneryReloadRequiredProp, graphicsPropsXML->getChild(
"scenery-reload-required")});
287 _compositorReloadListener.reset(
new RequiredPropertyListener{kCompositorReloadRequiredProp, graphicsPropsXML->getChild(
"compositor-reload-required")});
289 SGPropertyNode_ptr toSave = graphicsPropsXML->getChild(
"save-to-file");
291 for (
const auto&
p : toSave->getChildren(
"property")) {
292 string t = simgear::strutils::strip(
p->getStringValue());
293 if (t.at(0) ==
'/') {
296 _propertiesToSave.push_back(t);
299 }
catch (sg_exception& e) {
300 SG_LOG(SG_GUI, SG_ALERT,
"Failed to read graphics-properties.xml");
310 SG_UNUSED(delta_time_sec);
315 globals->get_commands()->removeCommand(
"apply-graphics-preset");
316 globals->get_commands()->removeCommand(
"save-graphics-preset");
317 globals->get_commands()->removeCommand(
"list-graphics-presets");
319 _listener->unregisterFromProperties();
325 _listener->unregisterFromProperties();
327 const string presetId =
fgGetString(kPresetPropPath);
329 const auto ok = loadStandardPreset(presetId, info);
336 return innerApplyPreset(info,
true);
339bool GraphicsPresets::loadStandardPreset(
const std::string&
id, GraphicsPresetInfo& info)
341 const auto path =
globals->findDataPath(
"Video/" +
id +
"-preset.xml");
342 if (!path.exists()) {
343 SG_LOG(SG_GUI, SG_ALERT,
"No such graphics preset '" <<
id <<
"' found");
347 return loadPresetXML(path, info);
355 auto videoPaths =
globals->get_data_paths(
"Video");
356 for (
const auto& vp : videoPaths) {
357 simgear::Dir videoDir(vp);
358 for (
const auto& presetFile : videoDir.children(simgear::Dir::TYPE_FILE,
"-preset.xml")) {
360 loadPresetXML(presetFile, info);
361 result.push_back(info);
367 sort(result.begin(), result.end(), [](
auto &a,
auto &b) {
368 if (a.orderNum != b.orderNum)
369 return a.orderNum < b.orderNum;
370 return a.name < b.name;
379 const auto ok = loadPresetXML(path, info);
385 return innerApplyPreset(info,
true);
391 auto it = std::find_if(presets.begin(), presets.end(), [
name](
const GraphicsPresetInfo&
pi) { return simgear::strutils::iequals(name, pi.name); });
392 if (it == presets.end()) {
393 SG_LOG(SG_GUI, SG_ALERT,
"Couldn't find graphics preset with name: " <<
name);
401bool GraphicsPresets::innerApplyPreset(
const GraphicsPresetInfo& info,
bool overwriteAutosaved)
404 fgSetString(kPresetDescriptionProp, info.description);
406 if (info.id ==
"custom") {
410 std::unordered_set<string> leafProps;
412 copyPropertiesIf(info.properties,
globals->get_props(), [overwriteAutosaved, &leafProps](
const SGPropertyNode* src) {
413 if (src->getParent() == nullptr)
419 const auto path = src->getPath(true);
421 auto it = std::find_if(kWitelistedPrefixes.begin(), kWitelistedPrefixes.end(), [&path](const string& p) {
424 if (path.length() < p.length()) {
425 return simgear::strutils::starts_with(p, path);
430 return simgear::strutils::starts_with(path, p);
433 if (it == kWitelistedPrefixes.end()) {
442 if (!overwriteAutosaved && dstNode && (dstNode->getAttribute(SGPropertyNode::ARCHIVE) ==
false)) {
447 const bool isLeaf = src->nChildren() == 0;
449 leafProps.insert(path);
454 _listener->unregisterFromProperties();
455 for (
const auto&
p : leafProps) {
456 _listener->registerWithProperty(
fgGetNode(
p));
463void GraphicsPresets::clearPreset()
468 _listener->unregisterFromProperties();
471bool GraphicsPresets::loadPresetXML(
const SGPath&
p, GraphicsPresetInfo& info)
473 SGPropertyNode_ptr props(
new SGPropertyNode);
476 readProperties(
p, props.get());
477 }
catch (sg_exception& e) {
478 SG_LOG(SG_IO, SG_ALERT,
"XML errors loading " <<
p.str() <<
"\n\t" << e.getFormattedMessage());
482 const string id = props->getStringValue(
"id");
483 const string rawName = props->getStringValue(
"name");
484 const string rawDesc = props->getStringValue(
"description");
485 int orderNum = props->getIntValue(
"order-num", 99);
487 if (
id.empty() || rawName.empty() || rawDesc.empty()) {
488 SG_LOG(SG_IO, SG_ALERT,
"Missing preset info loading: " <<
p.str());
493 info.name =
globals->get_locale()->getLocalizedString(rawName,
"graphics-presets");
494 info.description =
globals->get_locale()->getLocalizedString(rawDesc,
"graphics-presets");
495 info.orderNum = orderNum;
497 if (info.name.empty())
499 if (info.description.empty())
500 info.description = rawDesc;
502 info.properties = props->getChild(
"settings");
503 if (!info.properties) {
504 SG_LOG(SG_IO, SG_ALERT,
"Missing settings loading: " <<
p.str());
509 info.devices.clear();
510 SGPropertyNode_ptr devices = props->getChild(
"devices");
512 for (
auto d : devices->getChildren(
"device")) {
513 const auto t = simgear::strutils::strip(d->getStringValue());
514 info.devices.push_back(t);
523 SGPropertyNode_ptr presetXML(
new SGPropertyNode);
524 presetXML->setStringValue(
"id", path.file_base());
525 presetXML->setStringValue(
"name",
name);
526 presetXML->setStringValue(
"description", desc);
528 auto settingsNode = presetXML->getChild(
"settings", 0,
true);
530 for (
const auto& path : _propertiesToSave) {
532 if (!srcNode || !srcNode->hasValue())
535 auto dstNode = settingsNode->getNode(path,
true);
536 copyProperties(srcNode, dstNode);
540 sg_ofstream os(path, std::ios::out | std::ios::trunc);
541 writeProperties(os, presetXML,
true );
542 }
catch (sg_exception& e) {
543 SG_LOG(SG_GENERAL, SG_ALERT,
"Failed to save presets file to: " << path <<
"\nt\tFailed: " << e.getFormattedMessage());
552 SGSubsystemMgr::DISPLAY);
SGPropertyNode * get_props()
void registerWithProperty(SGPropertyNode_ptr n)
void valueChanged(SGPropertyNode *prop) override
GraphicsConfigChangeListener()
void unregisterFromProperties()
monitor a collection of properties, and set a flag property to true when any of them are modified.
RequiredPropertyListener(const std::string &requiredProp, SGPropertyNode_ptr props)
void valueChanged(SGPropertyNode *prop) override
bool applyCustomPreset(const SGPath &path)
void applyInitialPreset()
init() is called too late (after fgOSInit), so we call this method early, to load the initial preset ...
bool applyPresetByName(const std::string &name)
apply a preset identified by its (localized) name.
bool saveToXML(const SGPath &path, const std::string &name, const std::string &desc)
GraphicsPresetVec listPresets()
retrieve all standard presets which are defined
bool applyCurrentPreset()
Apply the settings defined in the current graphics preset, to the property tree.
void update(double delta_time_sec) override
std::vector< GraphicsPresetInfo > GraphicsPresetVec
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
std::vector< std::string > string_list
FlightGear Localization Support.
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
SGSubsystemMgr::Registrant< GraphicsPresets > registrantGraphicsPresets(SGSubsystemMgr::DISPLAY)
static bool do_list_standard_presets(const SGPropertyNode *arg, SGPropertyNode *root)
static bool do_save_preset(const SGPropertyNode *arg, SGPropertyNode *root)
void addSentryBreadcrumb(const std::string &, const std::string &)
static bool do_apply_preset(const SGPropertyNode *arg, SGPropertyNode *root)
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
bool fgSetBool(char const *name, bool val)
Set a bool value for a property.
bool fgSetString(char const *name, char const *str)
Set a string value for a property.
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.