/** * @file llcontrol.cpp * @brief Holds global state for viewer. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "linden_common.h" #include <iostream> #include <fstream> #include <algorithm> #include "llcontrol.h" #include "llstl.h" #include "llstring.h" #include "v3math.h" #include "v3dmath.h" #include "v4coloru.h" #include "v4color.h" #include "v3color.h" #include "llquaternion.h" #include "llrect.h" #include "llxmltree.h" #include "llsdserialize.h" #include "llfile.h" #include "lltimer.h" #include "lldir.h" #if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG #define CONTROL_ERRS LL_ERRS("ControlErrors") #else #define CONTROL_ERRS LL_WARNS("ControlErrors") #endif template <> eControlType get_control_type<U32>(); template <> eControlType get_control_type<S32>(); template <> eControlType get_control_type<F32>(); template <> eControlType get_control_type<bool>(); template <> eControlType get_control_type<std::string>(); template <> eControlType get_control_type<LLVector3>(); template <> eControlType get_control_type<LLVector3d>(); template <> eControlType get_control_type<LLRect>(); template <> eControlType get_control_type<LLColor4>(); template <> eControlType get_control_type<LLColor3>(); template <> eControlType get_control_type<LLColor4U>(); template <> eControlType get_control_type<LLSD>(); template <> LLSD convert_to_llsd<U32>(const U32& in); template <> LLSD convert_to_llsd<LLVector3>(const LLVector3& in); template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in); template <> LLSD convert_to_llsd<LLRect>(const LLRect& in); template <> LLSD convert_to_llsd<LLColor4>(const LLColor4& in); template <> LLSD convert_to_llsd<LLColor3>(const LLColor3& in); template <> LLSD convert_to_llsd<LLColor4U>(const LLColor4U& in); template <> bool convert_from_llsd<bool>(const LLSD& sd, eControlType type, std::string_view control_name); template <> S32 convert_from_llsd<S32>(const LLSD& sd, eControlType type, std::string_view control_name); template <> U32 convert_from_llsd<U32>(const LLSD& sd, eControlType type, std::string_view control_name); template <> F32 convert_from_llsd<F32>(const LLSD& sd, eControlType type, std::string_view control_name); template <> std::string convert_from_llsd<std::string>(const LLSD& sd, eControlType type, std::string_view control_name); template <> LLWString convert_from_llsd<LLWString>(const LLSD& sd, eControlType type, std::string_view control_name); template <> LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, std::string_view control_name); template <> LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, std::string_view control_name); template <> LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, std::string_view control_name); template <> LLColor4 convert_from_llsd<LLColor4>(const LLSD& sd, eControlType type, std::string_view control_name); template <> LLColor4U convert_from_llsd<LLColor4U>(const LLSD& sd, eControlType type, std::string_view control_name); template <> LLColor3 convert_from_llsd<LLColor3>(const LLSD& sd, eControlType type, std::string_view control_name); template <> LLSD convert_from_llsd<LLSD>(const LLSD& sd, eControlType type, std::string_view control_name); //this defines the current version of the settings file const S32 CURRENT_VERSION = 101; // If you define the environment variable LL_SETTINGS_PROFILE to any value this will activate // the gSavedSettings profiling code. This code tracks the calls to get a saved (debug) setting. // When the viewer exits the results are written to the log directory to the file specified // by SETTINGS_PROFILE below. Only settings with an average access rate >= 2/second are output. typedef std::pair<std::string, U32> settings_pair_t; typedef std::vector<settings_pair_t> settings_vec_t; LLSD getCount; settings_vec_t getCount_v; F64 start_time = 0; std::string SETTINGS_PROFILE = "settings_profile.log"; bool LLControlVariable::llsd_compare(const LLSD& a, const LLSD & b) { bool result = false; switch (mType) { case TYPE_U32: case TYPE_S32: result = a.asInteger() == b.asInteger(); break; case TYPE_BOOLEAN: result = a.asBoolean() == b.asBoolean(); break; case TYPE_F32: result = a.asReal() == b.asReal(); break; case TYPE_VEC3: case TYPE_VEC3D: result = LLVector3d(a) == LLVector3d(b); break; case TYPE_QUAT: result = LLQuaternion(a) == LLQuaternion(b); break; case TYPE_RECT: result = LLRect(a) == LLRect(b); break; case TYPE_COL4: result = LLColor4(a) == LLColor4(b); break; case TYPE_COL3: result = LLColor3(a) == LLColor3(b); break; case TYPE_STRING: result = a.asString() == b.asString(); break; default: break; } return result; } LLControlVariable::LLControlVariable(const std::string& name, eControlType type, LLSD initial, const std::string& comment, ePersist persist, bool hidefromsettingseditor) : mName(name), mComment(comment), mType(type), mPersist(persist), mHideFromSettingsEditor(hidefromsettingseditor) { if ((persist != PERSIST_NO) && mComment.empty()) { LL_ERRS() << "Must supply a comment for control " << mName << LL_ENDL; } //Push back versus setValue'ing here, since we don't want to call a signal yet mValues.push_back(initial); } LLControlVariable::~LLControlVariable() { } LLSD LLControlVariable::getComparableValue(const LLSD& value) { // *FIX:MEP - The following is needed to make the LLSD::ImplString // work with boolean controls... LLSD storable_value; if(TYPE_BOOLEAN == type() && value.isString()) { bool temp; if(LLStringUtil::convertToBOOL(value.asString(), temp)) { storable_value = temp; } else { storable_value = false; } } else if (TYPE_LLSD == type() && value.isString()) { LLPointer<LLSDNotationParser> parser = new LLSDNotationParser; LLSD result; std::stringstream value_stream(value.asString()); if (parser->parse(value_stream, result, LLSDSerialize::SIZE_UNLIMITED) != LLSDParser::PARSE_FAILURE) { storable_value = result; } else { storable_value = value; } } else { storable_value = value; } return storable_value; } void LLControlVariable::setValue(const LLSD& new_value, bool saved_value) { if (!mValidateSignal(this, new_value)) { // can not set new value, exit return; } LLSD storable_value = getComparableValue(new_value); LLSD original_value = getValue(); bool value_changed = !llsd_compare(original_value, storable_value); if(saved_value) { // If we're going to save this value, return to default but don't fire resetToDefault(false); if (!llsd_compare(mValues.back(), storable_value)) { mValues.push_back(storable_value); } } else { // This is an unsaved value. Its needs to reside at // mValues[2] (or greater). It must not affect // the result of getSaveValue() if (!llsd_compare(mValues.back(), storable_value)) { while(mValues.size() > 2) { // Remove any unsaved values. mValues.pop_back(); } if(mValues.size() < 2) { // Add the default to the 'save' value. mValues.push_back(mValues[0]); } // Add the 'un-save' value. mValues.push_back(storable_value); } } if(value_changed) { firePropertyChanged(original_value); } } void LLControlVariable::setDefaultValue(const LLSD& value) { // Set the control variables value and make it // the default value. If the active value is changed, // send the signal. // *NOTE: Default values are not saved, only read. LLSD comparable_value = getComparableValue(value); LLSD original_value = getValue(); bool value_changed = !llsd_compare(original_value, comparable_value); resetToDefault(false); mValues[0] = comparable_value; if (value_changed) { firePropertyChanged(original_value); } } void LLControlVariable::setPersist(ePersist state) { mPersist = state; } void LLControlVariable::setHiddenFromSettingsEditor(bool hide) { mHideFromSettingsEditor = hide; } void LLControlVariable::setComment(const std::string& comment) { mComment = comment; } void LLControlVariable::resetToDefault(bool fire_signal) { //The first setting is always the default //Pop to it and fire off the listener LLSD originalValue = mValues.back(); while(mValues.size() > 1) { mValues.pop_back(); } if(fire_signal) { firePropertyChanged(originalValue); } } bool LLControlVariable::shouldSave(bool nondefault_only) { // This method is used to decide whether we should save a given // variable. Two of the three values of mPersist are easy. if (mPersist == PERSIST_NO) return false; if (mPersist == PERSIST_ALWAYS) return true; // PERSIST_NONDFT // If caller doesn't need us to filter, just save. if (! nondefault_only) return true; // PERSIST_NONDFT: caller only wants us to save this variable if its value // differs from default. if (isDefault()) // never been altered return false; // We've set at least one other value: compare it to default. Save only if // they differ. return ! llsd_compare(getSaveValue(), getDefault()); } LLSD LLControlVariable::getSaveValue() const { //The first level of the stack is default //We assume that the second level is user preferences that should be saved if(mValues.size() > 1) return mValues[1]; return mValues[0]; } LLPointer<LLControlVariable> LLControlGroup::getControl(std::string_view name) { if (mSettingsProfile) { incrCount(name); } ctrl_name_table_t::iterator iter = mNameTable.find(name); return iter == mNameTable.end() ? LLPointer<LLControlVariable>() : iter->second; } //////////////////////////////////////////////////////////////////////////// // Must match the type definition in llcontrol.h const std::string LLControlGroup::mTypeString[TYPE_COUNT] = { "U32" ,"S32" ,"F32" ,"Boolean" ,"String" ,"Vector3" ,"Vector3D" ,"Quaternion" ,"Rect" ,"Color4" ,"Color3" ,"LLSD" }; LLControlGroup::LLControlGroup(const std::string& name) : LLInstanceTracker<LLControlGroup, std::string>(name), mSettingsProfile(false) { if (NULL != getenv("LL_SETTINGS_PROFILE")) { mSettingsProfile = true; } } LLControlGroup::~LLControlGroup() { cleanup(); } static bool compareRoutine(settings_pair_t lhs, settings_pair_t rhs) { return lhs.second > rhs.second; }; void LLControlGroup::cleanup() { if(mSettingsProfile && getCount.size() != 0) { std::string file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, SETTINGS_PROFILE); LLFILE* out = LLFile::fopen(file, "w"); /* Flawfinder: ignore */ if(!out) { LL_WARNS("SettingsProfile") << "Error opening " << SETTINGS_PROFILE << LL_ENDL; } else { F64 end_time = LLTimer::getTotalSeconds(); U32 total_seconds = (U32)(end_time - start_time); std::string msg = llformat("Runtime (seconds): %d\n\n No. accesses Avg. accesses/sec Name\n", total_seconds); std::ostringstream data_msg; data_msg << msg; size_t data_size = data_msg.str().size(); if (fwrite(data_msg.str().c_str(), 1, data_size, out) != data_size) { LL_WARNS("SettingsProfile") << "Failed to write settings profile header" << LL_ENDL; } for (LLSD::map_const_iterator iter = getCount.beginMap(); iter != getCount.endMap(); ++iter) { getCount_v.push_back(settings_pair_t(iter->first, iter->second.asInteger())); } sort(getCount_v.begin(), getCount_v.end(), compareRoutine); for (settings_vec_t::iterator iter = getCount_v.begin(); iter != getCount_v.end(); ++iter) { U32 access_rate = 0; if (total_seconds != 0) { access_rate = iter->second / total_seconds; } if (access_rate >= 2) { std::ostringstream data_msg; msg = llformat("%13d %7d %s", iter->second, access_rate, iter->first.c_str()); data_msg << msg << "\n"; size_t data_size = data_msg.str().size(); if (fwrite(data_msg.str().c_str(), 1, data_size, out) != data_size) { LL_WARNS("SettingsProfile") << "Failed to write settings profile" << LL_ENDL; } } } getCount = LLSD::emptyMap(); fclose(out); } } mNameTable.clear(); } eControlType LLControlGroup::typeStringToEnum(const std::string& typestr) { for(int i = 0; i < (int)TYPE_COUNT; ++i) { if(mTypeString[i] == typestr) return (eControlType)i; } return (eControlType)-1; } std::string LLControlGroup::typeEnumToString(eControlType typeenum) { return mTypeString[typeenum]; } LLControlVariable* LLControlGroup::declareControl(const std::string& name, eControlType type, const LLSD initial_val, const std::string& comment, LLControlVariable::ePersist persist, bool hidefromsettingseditor) { LLControlVariable* existing_control = getControl(name); if (existing_control) { if ((persist != LLControlVariable::PERSIST_NO) && existing_control->isType(type)) { if (!existing_control->llsd_compare(existing_control->getDefault(), initial_val)) { // Sometimes we need to declare a control *after* it has been loaded from a settings file. LLSD cur_value = existing_control->getValue(); // get the current value existing_control->setDefaultValue(initial_val); // set the default to the declared value existing_control->setValue(cur_value); // now set to the loaded value } } else { LL_WARNS("Settings") << "Control named " << name << " already exists, ignoring new declaration." << LL_ENDL; } return existing_control; } // if not, create the control and add it to the name table LLControlVariable* control = new LLControlVariable(name, type, initial_val, comment, persist, hidefromsettingseditor); mNameTable[name] = control; return control; } LLControlVariable* LLControlGroup::declareU32(const std::string& name, const U32 initial_val, const std::string& comment, LLControlVariable::ePersist persist) { return declareControl(name, TYPE_U32, (LLSD::Integer) initial_val, comment, persist); } LLControlVariable* LLControlGroup::declareS32(const std::string& name, const S32 initial_val, const std::string& comment, LLControlVariable::ePersist persist) { return declareControl(name, TYPE_S32, initial_val, comment, persist); } LLControlVariable* LLControlGroup::declareF32(const std::string& name, const F32 initial_val, const std::string& comment, LLControlVariable::ePersist persist) { return declareControl(name, TYPE_F32, initial_val, comment, persist); } LLControlVariable* LLControlGroup::declareBOOL(const std::string& name, const bool initial_val, const std::string& comment, LLControlVariable::ePersist persist) { return declareControl(name, TYPE_BOOLEAN, initial_val, comment, persist); } LLControlVariable* LLControlGroup::declareString(const std::string& name, const std::string& initial_val, const std::string& comment, LLControlVariable::ePersist persist) { return declareControl(name, TYPE_STRING, initial_val, comment, persist); } LLControlVariable* LLControlGroup::declareVec3(const std::string& name, const LLVector3 &initial_val, const std::string& comment, LLControlVariable::ePersist persist) { return declareControl(name, TYPE_VEC3, initial_val.getValue(), comment, persist); } LLControlVariable* LLControlGroup::declareVec3d(const std::string& name, const LLVector3d &initial_val, const std::string& comment, LLControlVariable::ePersist persist) { return declareControl(name, TYPE_VEC3D, initial_val.getValue(), comment, persist); } LLControlVariable* LLControlGroup::declareQuat(const std::string& name, const LLQuaternion &initial_val, const std::string& comment, LLControlVariable::ePersist persist) { return declareControl(name, TYPE_QUAT, initial_val.getValue(), comment, persist); } LLControlVariable* LLControlGroup::declareRect(const std::string& name, const LLRect &initial_val, const std::string& comment, LLControlVariable::ePersist persist) { return declareControl(name, TYPE_RECT, initial_val.getValue(), comment, persist); } LLControlVariable* LLControlGroup::declareColor4(const std::string& name, const LLColor4 &initial_val, const std::string& comment, LLControlVariable::ePersist persist ) { return declareControl(name, TYPE_COL4, initial_val.getValue(), comment, persist); } LLControlVariable* LLControlGroup::declareColor3(const std::string& name, const LLColor3 &initial_val, const std::string& comment, LLControlVariable::ePersist persist ) { return declareControl(name, TYPE_COL3, initial_val.getValue(), comment, persist); } LLControlVariable* LLControlGroup::declareLLSD(const std::string& name, const LLSD &initial_val, const std::string& comment, LLControlVariable::ePersist persist ) { return declareControl(name, TYPE_LLSD, initial_val, comment, persist); } void LLControlGroup::incrCount(std::string_view name) { if (0.0 == start_time) { start_time = LLTimer::getTotalSeconds(); } getCount[name.data()] = getCount[name.data()].asInteger() + 1; } bool LLControlGroup::getBOOL(std::string_view name) { return get<bool>(name); } S32 LLControlGroup::getS32(std::string_view name) { return get<S32>(name); } U32 LLControlGroup::getU32(std::string_view name) { return get<U32>(name); } F32 LLControlGroup::getF32(std::string_view name) { return get<F32>(name); } std::string LLControlGroup::getString(std::string_view name) { return get<std::string>(name); } LLWString LLControlGroup::getWString(std::string_view name) { return get<LLWString>(name); } std::string LLControlGroup::getText(std::string_view name) { std::string utf8_string = getString(name); LLStringUtil::replaceChar(utf8_string, '^', '\n'); LLStringUtil::replaceChar(utf8_string, '%', ' '); return (utf8_string); } LLVector3 LLControlGroup::getVector3(std::string_view name) { return get<LLVector3>(name); } LLVector3d LLControlGroup::getVector3d(std::string_view name) { return get<LLVector3d>(name); } LLQuaternion LLControlGroup::getQuaternion(std::string_view name) { return get<LLQuaternion>(name); } LLRect LLControlGroup::getRect(std::string_view name) { return get<LLRect>(name); } LLColor4 LLControlGroup::getColor(std::string_view name) { return get<LLColor4>(name); } LLColor4 LLControlGroup::getColor4(std::string_view name) { return get<LLColor4>(name); } LLColor3 LLControlGroup::getColor3(std::string_view name) { return get<LLColor3>(name); } LLSD LLControlGroup::getLLSD(std::string_view name) { return get<LLSD>(name); } LLSD LLControlGroup::asLLSD(bool diffs_only) { // Dump all stored values as LLSD LLSD result = LLSD::emptyArray(); for (ctrl_name_table_t::iterator iter = mNameTable.begin(); iter != mNameTable.end(); iter++) { LLControlVariable *control = iter->second; if (!control || control->isType(TYPE_STRING) || (diffs_only && control->isDefault())) { continue; } const std::string& name = iter->first; result[name] = getLLSD(name); } return result; } bool LLControlGroup::controlExists(std::string_view name) { ctrl_name_table_t::iterator iter = mNameTable.find(name); return iter != mNameTable.end(); } //------------------------------------------------------------------- // Set functions //------------------------------------------------------------------- void LLControlGroup::setBOOL(std::string_view name, bool val) { set<bool>(name, val); } void LLControlGroup::setS32(std::string_view name, S32 val) { set(name, val); } void LLControlGroup::setF32(std::string_view name, F32 val) { set(name, val); } void LLControlGroup::setU32(std::string_view name, U32 val) { set(name, val); } void LLControlGroup::setString(std::string_view name, const std::string &val) { set(name, val); } void LLControlGroup::setVector3(std::string_view name, const LLVector3 &val) { set(name, val); } void LLControlGroup::setVector3d(std::string_view name, const LLVector3d &val) { set(name, val); } void LLControlGroup::setQuaternion(std::string_view name, const LLQuaternion &val) { set(name, val); } void LLControlGroup::setRect(std::string_view name, const LLRect &val) { set(name, val); } void LLControlGroup::setColor4(std::string_view name, const LLColor4 &val) { set(name, val); } void LLControlGroup::setLLSD(std::string_view name, const LLSD& val) { set(name, val); } void LLControlGroup::setUntypedValue(std::string_view name, const LLSD& val, bool saved_value) { if (name.empty()) { return; } LLControlVariable* control = getControl(name); if (control) { control->setValue(val, saved_value); } else { CONTROL_ERRS << "Invalid control " << name << LL_ENDL; } } //--------------------------------------------------------------- // Load and save //--------------------------------------------------------------- // Returns number of controls loaded, so 0 if failure U32 LLControlGroup::loadFromFileLegacy(const std::string& filename, bool require_declaration, eControlType declare_as) { std::string name; LLXmlTree xml_controls; if (!xml_controls.parseFile(filename)) { LL_WARNS("Settings") << "Unable to open control file " << filename << LL_ENDL; return 0; } LLXmlTreeNode* rootp = xml_controls.getRoot(); if (!rootp || !rootp->hasAttribute("version")) { LL_WARNS("Settings") << "No valid settings header found in control file " << filename << LL_ENDL; return 0; } U32 validitems = 0; S32 version; rootp->getAttributeS32("version", version); // Check file version if (version != CURRENT_VERSION) { LL_INFOS("Settings") << filename << " does not appear to be a version " << CURRENT_VERSION << " controls file" << LL_ENDL; return 0; } LLXmlTreeNode* child_nodep = rootp->getFirstChild(); while(child_nodep) { name = child_nodep->getName(); bool declared = controlExists(name); if (require_declaration && !declared) { // Declaration required, but this name not declared. // Complain about non-empty names. if (!name.empty()) { //read in to end of line LL_WARNS("Settings") << "LLControlGroup::loadFromFile() : Trying to set \"" << name << "\", setting doesn't exist." << LL_ENDL; } child_nodep = rootp->getNextChild(); continue; } // Got an item. Load it up. // If not declared, assume it's a string if (!declared) { switch(declare_as) { case TYPE_COL4: declareColor4(name, LLColor4::white, LLStringUtil::null, LLControlVariable::PERSIST_NO); break; case TYPE_STRING: default: declareString(name, LLStringUtil::null, LLStringUtil::null, LLControlVariable::PERSIST_NO); break; } } // Control name has been declared in code. LLControlVariable *control = getControl(name); llassert(control); switch(control->mType) { case TYPE_F32: { F32 initial = 0.f; child_nodep->getAttributeF32("value", initial); control->set(initial); validitems++; } break; case TYPE_S32: { S32 initial = 0; child_nodep->getAttributeS32("value", initial); control->set(initial); validitems++; } break; case TYPE_U32: { U32 initial = 0; child_nodep->getAttributeU32("value", initial); control->set((LLSD::Integer) initial); validitems++; } break; case TYPE_BOOLEAN: { bool initial = false; child_nodep->getAttributeBOOL("value", initial); control->set(initial); validitems++; } break; case TYPE_STRING: { std::string string; child_nodep->getAttributeString("value", string); control->set(string); validitems++; } break; case TYPE_VEC3: { LLVector3 vector; child_nodep->getAttributeVector3("value", vector); control->set(vector.getValue()); validitems++; } break; case TYPE_VEC3D: { LLVector3d vector; child_nodep->getAttributeVector3d("value", vector); control->set(vector.getValue()); validitems++; } break; case TYPE_QUAT: { LLQuaternion quat; child_nodep->getAttributeQuat("value", quat); control->set(quat.getValue()); validitems++; } break; case TYPE_RECT: { //RN: hack to support reading rectangles from a string std::string rect_string; child_nodep->getAttributeString("value", rect_string); std::istringstream istream(rect_string); S32 left, bottom, width, height; istream >> left >> bottom >> width >> height; LLRect rect; rect.setOriginAndSize(left, bottom, width, height); control->set(rect.getValue()); validitems++; } break; case TYPE_COL4: { LLColor4 color; child_nodep->getAttributeColor4("value", color); control->set(color.getValue()); validitems++; } break; case TYPE_COL3: { LLVector3 color; child_nodep->getAttributeVector3("value", color); control->set(LLColor3(color.mV).getValue()); validitems++; } break; default: break; } child_nodep = rootp->getNextChild(); } return validitems; } U32 LLControlGroup::saveToFile(const std::string& filename, bool nondefault_only) { LLSD settings; int num_saved = 0; for (ctrl_name_table_t::iterator iter = mNameTable.begin(); iter != mNameTable.end(); iter++) { LLControlVariable* control = iter->second; if (!control) { LL_WARNS("Settings") << "Tried to save invalid control: " << iter->first << LL_ENDL; } else if( control->shouldSave(nondefault_only) ) { settings[iter->first]["Type"] = typeEnumToString(control->type()); settings[iter->first]["Comment"] = control->getComment(); settings[iter->first]["Value"] = control->getSaveValue(); ++num_saved; } } llofstream file; file.open(filename.c_str()); if (file.is_open()) { LLSDSerialize::toPrettyXML(settings, file); file.close(); LL_INFOS("Settings") << "Saved to " << filename << LL_ENDL; } else { // This is a warning because sometime we want to use settings files which can't be written... LL_WARNS("Settings") << "Unable to open settings file: " << filename << LL_ENDL; return 0; } return num_saved; } U32 LLControlGroup::loadFromFile(const std::string& filename, bool set_default_values, bool save_values) { LLSD settings; llifstream infile; infile.open(filename.c_str()); if(!infile.is_open()) { LL_WARNS("Settings") << "Cannot find file " << filename << " to load." << LL_ENDL; return 0; } if (LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXML(settings, infile)) { infile.close(); LL_WARNS("Settings") << "Unable to parse LLSD control file " << filename << ". Trying Legacy Method." << LL_ENDL; return loadFromFileLegacy(filename, true, TYPE_STRING); } U32 validitems = 0; bool hidefromsettingseditor = false; for(LLSD::map_const_iterator itr = settings.beginMap(); itr != settings.endMap(); ++itr) { LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT; std::string const & name = itr->first; LLSD const & control_map = itr->second; if(control_map.has("Persist")) { persist = control_map["Persist"].asInteger()? LLControlVariable::PERSIST_NONDFT : LLControlVariable::PERSIST_NO; } // Sometimes we want to use the settings system to provide cheap persistence, but we // don't want the settings themselves to be easily manipulated in the UI because // doing so can cause support problems. So we have this option: if(control_map.has("HideFromEditor")) { hidefromsettingseditor = control_map["HideFromEditor"].asInteger(); } else { hidefromsettingseditor = false; } // If the control exists just set the value from the input file. LLControlVariable* existing_control = getControl(name); if(existing_control) { // set_default_values is true when we're loading the initial, // immutable files from app_settings, e.g. settings.xml. if(set_default_values) { // Override all previously set properties of this control. // ... except for type. The types must match. eControlType new_type = typeStringToEnum(control_map["Type"].asString()); if(existing_control->isType(new_type)) { existing_control->setDefaultValue(control_map["Value"]); existing_control->setPersist(persist); existing_control->setHiddenFromSettingsEditor(hidefromsettingseditor); existing_control->setComment(control_map["Comment"].asString()); } else { LL_ERRS() << "Mismatched type of control variable '" << name << "' found while loading '" << filename << "'." << LL_ENDL; } } else if(existing_control->isPersisted()) { // save_values is specifically false for (e.g.) // SessionSettingsFile and UserSessionSettingsFile -- in other // words, for a file that's supposed to be transient. existing_control->setValue(control_map["Value"], save_values); } // *NOTE: If not persisted and not setting defaults, // the value should not get loaded. } else { // We've never seen this control before. Either we're loading up // the initial set of default settings files (set_default_values) // -- or we're loading user settings last saved by a viewer that // supports a superset of the variables we know. // CHOP-962: if we're loading an unrecognized user setting, make // sure we save it later. If you try an experimental viewer, tweak // a new setting, briefly revert to an old viewer, then return to // the new one, we don't want the old viewer to discard the // setting you changed. if (! set_default_values) { // Using PERSIST_ALWAYS insists that saveToFile() (which calls // LLControlVariable::shouldSave()) must save this control // variable regardless of its value. We can safely set this // LLControlVariable persistent because the 'persistent' flag // is not itself persisted! persist = LLControlVariable::PERSIST_ALWAYS; // We want to mention unrecognized user settings variables // (e.g. from a newer version of the viewer) in the log. But // we also arrive here for Boolean variables generated by // the notifications subsystem when the user checks "Don't // show me this again." These aren't declared in settings.xml; // they're actually named for the notification they suppress. // We don't want to mention those. Apologies, this is a bit of // a hack: we happen to know that user settings go into an // LLControlGroup whose name is "Global". if (getKey() == "Global") { LL_INFOS("LLControlGroup") << "preserving unrecognized " << getKey() << " settings variable " << name << LL_ENDL; } } declareControl(name, typeStringToEnum(control_map["Type"].asString()), control_map["Value"], control_map["Comment"].asString(), persist, hidefromsettingseditor ); } ++validitems; } LL_DEBUGS("Settings") << "Loaded " << validitems << " settings from " << filename << LL_ENDL; return validitems; } void LLControlGroup::resetToDefaults() { ctrl_name_table_t::iterator control_iter; for (control_iter = mNameTable.begin(); control_iter != mNameTable.end(); ++control_iter) { LLControlVariable* control = (*control_iter).second; control->resetToDefault(); } } void LLControlGroup::applyToAll(ApplyFunctor* func) { for (ctrl_name_table_t::iterator iter = mNameTable.begin(); iter != mNameTable.end(); iter++) { func->apply(iter->first, iter->second); } } //============================================================================ #ifdef TEST_HARNESS void main() { F32_CONTROL foo, getfoo; S32_CONTROL bar, getbar; BOOL_CONTROL baz; U32 count = gGlobals.loadFromFile("controls.ini"); LL_INFOS("Settings") << "Loaded " << count << " controls" << LL_ENDL; // test insertion foo = new LLControlVariable<F32>("gFoo", 5.f, 1.f, 20.f); gGlobals.addEntry("gFoo", foo); bar = new LLControlVariable<S32>("gBar", 10, 2, 22); gGlobals.addEntry("gBar", bar); baz = new LLControlVariable<bool>("gBaz", false); gGlobals.addEntry("gBaz", baz); // test retrieval getfoo = (LLControlVariable<F32>*) gGlobals.resolveName("gFoo"); getfoo->dump(); getbar = (S32_CONTROL) gGlobals.resolveName("gBar"); getbar->dump(); // change data getfoo->set(10.f); getfoo->dump(); // Failure modes // ...min > max // badfoo = new LLControlVariable<F32>("gFoo2", 100.f, 20.f, 5.f); // ...initial > max // badbar = new LLControlVariable<S32>("gBar2", 10, 20, 100000); // ...misspelled name // getfoo = (F32_CONTROL) gGlobals.resolveName("fooMisspelled"); // getfoo->dump(); // ...invalid data type getfoo = (F32_CONTROL) gGlobals.resolveName("gFoo"); getfoo->set(true); getfoo->dump(); // ...out of range data // getfoo->set(100000000.f); // getfoo->dump(); // Clean Up delete foo; delete bar; delete baz; } #endif template <> eControlType get_control_type<U32>() { return TYPE_U32; } template <> eControlType get_control_type<S32>() { return TYPE_S32; } template <> eControlType get_control_type<F32>() { return TYPE_F32; } template <> eControlType get_control_type<bool> () { return TYPE_BOOLEAN; } template <> eControlType get_control_type<std::string>() { return TYPE_STRING; } template <> eControlType get_control_type<LLVector3>() { return TYPE_VEC3; } template <> eControlType get_control_type<LLVector3d>() { return TYPE_VEC3D; } template <> eControlType get_control_type<LLQuaternion>() { return TYPE_QUAT; } template <> eControlType get_control_type<LLRect>() { return TYPE_RECT; } template <> eControlType get_control_type<LLColor4>() { return TYPE_COL4; } template <> eControlType get_control_type<LLColor3>() { return TYPE_COL3; } template <> eControlType get_control_type<LLSD>() { return TYPE_LLSD; } template <> LLSD convert_to_llsd<U32>(const U32& in) { return (LLSD::Integer)in; } template <> LLSD convert_to_llsd<LLVector3>(const LLVector3& in) { return in.getValue(); } template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in) { return in.getValue(); } template <> LLSD convert_to_llsd<LLQuaternion>(const LLQuaternion& in) { return in.getValue(); } template <> LLSD convert_to_llsd<LLRect>(const LLRect& in) { return in.getValue(); } template <> LLSD convert_to_llsd<LLColor4>(const LLColor4& in) { return in.getValue(); } template <> LLSD convert_to_llsd<LLColor3>(const LLColor3& in) { return in.getValue(); } template <> LLSD convert_to_llsd<LLColor4U>(const LLColor4U& in) { return in.getValue(); } template<> bool convert_from_llsd<bool>(const LLSD& sd, eControlType type, std::string_view control_name) { if (type == TYPE_BOOLEAN) return sd.asBoolean(); else { CONTROL_ERRS << "Invalid bool value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; return false; } } template<> S32 convert_from_llsd<S32>(const LLSD& sd, eControlType type, std::string_view control_name) { if (type == TYPE_S32) return sd.asInteger(); else { CONTROL_ERRS << "Invalid S32 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; return 0; } } template<> U32 convert_from_llsd<U32>(const LLSD& sd, eControlType type, std::string_view control_name) { if (type == TYPE_U32) return sd.asInteger(); else { CONTROL_ERRS << "Invalid U32 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; return 0; } } template<> F32 convert_from_llsd<F32>(const LLSD& sd, eControlType type, std::string_view control_name) { if (type == TYPE_F32) return (F32) sd.asReal(); else { CONTROL_ERRS << "Invalid F32 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; return 0.0f; } } template<> std::string convert_from_llsd<std::string>(const LLSD& sd, eControlType type, std::string_view control_name) { if (type == TYPE_STRING) return sd.asString(); else { CONTROL_ERRS << "Invalid string value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; return LLStringUtil::null; } } template<> LLWString convert_from_llsd<LLWString>(const LLSD& sd, eControlType type, std::string_view control_name) { return utf8str_to_wstring(convert_from_llsd<std::string>(sd, type, control_name)); } template<> LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, std::string_view control_name) { if (type == TYPE_VEC3) return (LLVector3)sd; else { CONTROL_ERRS << "Invalid LLVector3 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; return LLVector3::zero; } } template<> LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, std::string_view control_name) { if (type == TYPE_VEC3D) return (LLVector3d)sd; else { CONTROL_ERRS << "Invalid LLVector3d value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; return LLVector3d::zero; } } template<> LLQuaternion convert_from_llsd<LLQuaternion>(const LLSD& sd, eControlType type, std::string_view control_name) { if (type == TYPE_QUAT) return (LLQuaternion)sd; else { CONTROL_ERRS << "Invalid LLQuaternion value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; return LLQuaternion(); } } template<> LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, std::string_view control_name) { if (type == TYPE_RECT) return LLRect(sd); else { CONTROL_ERRS << "Invalid rect value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; return LLRect::null; } } template<> LLColor4 convert_from_llsd<LLColor4>(const LLSD& sd, eControlType type, std::string_view control_name) { if (type == TYPE_COL4) { LLColor4 color(sd); if (color.mV[VRED] < 0.f || color.mV[VRED] > 1.f) { LL_WARNS("Settings") << "Color " << control_name << " red value out of range: " << color << LL_ENDL; } else if (color.mV[VGREEN] < 0.f || color.mV[VGREEN] > 1.f) { LL_WARNS("Settings") << "Color " << control_name << " green value out of range: " << color << LL_ENDL; } else if (color.mV[VBLUE] < 0.f || color.mV[VBLUE] > 1.f) { LL_WARNS("Settings") << "Color " << control_name << " blue value out of range: " << color << LL_ENDL; } else if (color.mV[VALPHA] < 0.f || color.mV[VALPHA] > 1.f) { LL_WARNS("Settings") << "Color " << control_name << " alpha value out of range: " << color << LL_ENDL; } return LLColor4(sd); } else { CONTROL_ERRS << "Control " << control_name << " not a color" << LL_ENDL; return LLColor4::white; } } template<> LLColor3 convert_from_llsd<LLColor3>(const LLSD& sd, eControlType type, std::string_view control_name) { if (type == TYPE_COL3) return sd; else { CONTROL_ERRS << "Invalid LLColor3 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; return LLColor3::white; } } template<> LLSD convert_from_llsd<LLSD>(const LLSD& sd, eControlType type, std::string_view control_name) { return sd; } #if TEST_CACHED_CONTROL #define DECL_LLCC(T, V) static LLCachedControl<T> mySetting_##T("TestCachedControl"#T, V) DECL_LLCC(U32, (U32)666); DECL_LLCC(S32, (S32)-666); DECL_LLCC(F32, (F32)-666.666); DECL_LLCC(bool, true); static LLCachedControl<std::string> mySetting_string("TestCachedControlstring", "Default String Value"); DECL_LLCC(LLVector3, LLVector3(1.0f, 2.0f, 3.0f)); DECL_LLCC(LLVector3d, LLVector3d(6.0f, 5.0f, 4.0f)); DECL_LLCC(LLRect, LLRect(0, 0, 100, 500)); DECL_LLCC(LLColor4, LLColor4(0.0f, 0.5f, 1.0f)); DECL_LLCC(LLColor3, LLColor3(1.0f, 0.f, 0.5f)); DECL_LLCC(LLColor4U, LLColor4U(255, 200, 100, 255)); LLSD test_llsd = LLSD()["testing1"] = LLSD()["testing2"]; DECL_LLCC(LLSD, test_llsd); void test_cached_control() { #define TEST_LLCC(T, V) if((T)mySetting_##T != V) LL_ERRS() << "Fail "#T << LL_ENDL TEST_LLCC(U32, 666); TEST_LLCC(S32, (S32)-666); TEST_LLCC(F32, (F32)-666.666); TEST_LLCC(bool, true); if((std::string)mySetting_string != "Default String Value") LL_ERRS() << "Fail string" << LL_ENDL; TEST_LLCC(LLVector3, LLVector3(1.0f, 2.0f, 3.0f)); TEST_LLCC(LLVector3d, LLVector3d(6.0f, 5.0f, 4.0f)); TEST_LLCC(LLRect, LLRect(0, 0, 100, 500)); TEST_LLCC(LLColor4, LLColor4(0.0f, 0.5f, 1.0f)); TEST_LLCC(LLColor3, LLColor3(1.0f, 0.f, 0.5f)); TEST_LLCC(LLColor4U, LLColor4U(255, 200, 100, 255)); //There's no LLSD comparsion for LLCC yet. TEST_LLCC(LLSD, test_llsd); } #endif // TEST_CACHED_CONTROL