diff options
author | Debi King (Dessie) <dessie@lindenlab.com> | 2011-04-18 10:07:40 -0400 |
---|---|---|
committer | Debi King (Dessie) <dessie@lindenlab.com> | 2011-04-18 10:07:40 -0400 |
commit | da7cd9f8117757cd6acb0a5e93bc3c60443374f9 (patch) | |
tree | 3916874ed180ee9567b4fb76d88d8ce72689c257 | |
parent | d534c8009f2573400a21c19b00691536fc3cc23f (diff) | |
parent | b8069d1c250c03e9fffda0a9264bfd04a12f8292 (diff) |
merged from 2.6.4-start tag in viewer-development
184 files changed, 7536 insertions, 2076 deletions
@@ -52,6 +52,7 @@ a82e5b1e22c7f90e3c7977d146b80588f004ed0d 2.5.0-start 345b17e7cf630db77e840b4fe3451bd476d750a3 76f586a8e22b 0000000000000000000000000000000000000000 76f586a8e22b 54d772d8687c69b1d773f6ce14bbc7bdc9d6c05f 2.5.0-beta2 +b542f8134a2bb5dd054ff4e509a44b2ee463b1bf nat-eventapi2-base 7076e22f9f43f479a4ea75eac447a36364bead5a DRTVWR-5_2.2.0-beta1 9822eb3e25f7fe0c28ffd8aba45c507caa383cbc DRTVWR-3_2.2.0-beta2 b0cd7e150009809a0b5b0a9d5785cd4bb230413a DRTVWR-7_2.2.0-beta3 @@ -70,6 +71,7 @@ b723921b5c711bd24dbe77dc76ef488b544dac78 DRTVWR-34_2.5.0-beta3 b723921b5c711bd24dbe77dc76ef488b544dac78 2.5.0-release b723921b5c711bd24dbe77dc76ef488b544dac78 DRTVWR-31_2.5.0-release 92e58e51776a4f8c29069b1a62ff21454d2085f0 2.6.0-start +3178e311da3a8739a85363665006ea3c4610cad4 dons-headless-hackathon-work 63a6aedfce785a6c760377bf685b2dae616797d2 2.5.1-start 4dede9ae1ec74d41f6887719f6f1de7340d8578d 2.5.1-release 4dede9ae1ec74d41f6887719f6f1de7340d8578d DRTVWR-37_2.5.1-release @@ -86,16 +88,21 @@ f1827b441e05bf37c68e2c15ebc6d09e9b03f527 2.6.0-start c5bdef3aaa2744626aef3c217ce29e1900d357b3 2.6.1-start c5bdef3aaa2744626aef3c217ce29e1900d357b3 DRTVWR-43_2.6.1-beta1 c5bdef3aaa2744626aef3c217ce29e1900d357b3 2.6.1-beta1 -9e4641f4a7870c0f565a25a2971368d5a29516a1 DRTVWR-41_2.6.0-beta2 -9e4641f4a7870c0f565a25a2971368d5a29516a1 2.6.0-beta2 +c9182ed77d427c759cfacf49a7b71a2e20d522aa DRTVWR-42_2.6.1-release +c9182ed77d427c759cfacf49a7b71a2e20d522aa 2.6.1-release 56b2778c743c2a964d82e1caf11084d76a87de2c 2.6.2-start 42f32494bac475d0737799346f6831558ae8bf5d DRTVWR-39_2.6.0-release 42f32494bac475d0737799346f6831558ae8bf5d 2.6.0-release d1203046bb653b763f835b04d184646949d8dd5c DRTVWR-45_2.6.2-beta1 d1203046bb653b763f835b04d184646949d8dd5c 2.6.2-beta1 +214180ad5714ce8392b82bbebcc92f4babd98300 DRTVWR-44_2.6.2-release +214180ad5714ce8392b82bbebcc92f4babd98300 2.6.2-release 52b2263ab28f0976c689fd0b76c55a9eb027cdbf end-of-develop.py ec32f1045e7c2644015245df3a9933620aa194b8 2.6.3-start d7fcefabdf32bb61a9ea6d6037c1bb26190a85bc DRTVWR-47_2.6.3-beta1 d7fcefabdf32bb61a9ea6d6037c1bb26190a85bc 2.6.3-beta1 0630e977504af5ea320c58d33cae4e1ddee793e9 DRTVWR-48_2.6.3-beta2 0630e977504af5ea320c58d33cae4e1ddee793e9 2.6.3-beta2 +74cd32a06837b0c2cb793b2e8d4d82f5d49462b2 2.6.4-start +74cd32a06837b0c2cb793b2e8d4d82f5d49462b2 2.6.4-start +f632f87bb71b0f13d21f2f64b0c42cedb008c749 2.6.4-start diff --git a/BuildParams b/BuildParams index e8bf99db40..f8d078250b 100644 --- a/BuildParams +++ b/BuildParams @@ -68,10 +68,10 @@ viewer-pre-release.build_debug_release_separately = true viewer-pre-release.build_viewer_update_version_manager = true viewer-pre-release.release-viewer.jira = DRTVWR-42 + # ======================================= # brad # ======================================== - debug-halting.email = cg@lindenlab.com debug-halting.build_server = false debug-halting.build_server_tests = false @@ -156,11 +156,11 @@ media.build_viewer_update_version_manager = false # ================ oz_viewer-devreview.build_debug_release_separately = true -oz_viewer-poreview.build_debug_release_separately = true oz-project-1.build_debug_release_separately = true oz-project-2.build_debug_release_separately = true oz-project-3.build_debug_release_separately = true + # ======================================== # enus # ======================================== diff --git a/doc/contributions.txt b/doc/contributions.txt index 71c54e8624..e7db8c0ded 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -155,6 +155,8 @@ Angus Boyd VWR-592 Ann Congrejo CT-193 +Ansariel Hiller + STORM-1101 Ardy Lay VWR-19499 VWR-24917 @@ -417,6 +419,9 @@ Jonathan Yap STORM-1020 STORM-1064 STORM-960 + STORM-1101 + STORM-1108 + STORM-1094 STORM-1077 STORM-953 Kage Pixel @@ -440,6 +445,8 @@ Kitty Barnett Kunnis Basiat VWR-82 VWR-102 +Lance Corrimal + VWR-25269 Latif Khalifa VWR-5370 Lisa Lowe @@ -789,6 +796,7 @@ Thickbrick Sleaford VWR-13483 VWR-13947 VWR-24420 + STORM-1147 Thraxis Epsilon SVC-371 VWR-383 @@ -864,6 +872,7 @@ WolfPup Lowenhar STORM-674 STORM-776 STORM-825 + STORM-1098 VWR-20741 VWR-20933 Zai Lynch diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 6439ac3349..22e0705036 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -63,6 +63,7 @@ set(llcommon_SOURCE_FILES llformat.cpp llframetimer.cpp llheartbeat.cpp + llinstancetracker.cpp llliveappconfig.cpp lllivefile.cpp lllog.cpp @@ -314,6 +315,7 @@ if (LL_TESTS) LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}") LL_ADD_INTEGRATION_TEST(reflection "" "${test_libs}") LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}") + LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}") # *TODO - reenable these once tcmalloc libs no longer break the build. #ADD_BUILD_TEST(llallocator llcommon) diff --git a/indra/llcommon/lleventdispatcher.cpp b/indra/llcommon/lleventdispatcher.cpp index d6e820d793..5b6d4efbe9 100644 --- a/indra/llcommon/lleventdispatcher.cpp +++ b/indra/llcommon/lleventdispatcher.cpp @@ -41,6 +41,354 @@ #include "llevents.h" #include "llerror.h" #include "llsdutil.h" +#include "stringize.h" +#include <memory> // std::auto_ptr + +/***************************************************************************** +* LLSDArgsSource +*****************************************************************************/ +/** + * Store an LLSD array, producing its elements one at a time. Die with LL_ERRS + * if the consumer requests more elements than the array contains. + */ +class LL_COMMON_API LLSDArgsSource +{ +public: + LLSDArgsSource(const std::string function, const LLSD& args); + ~LLSDArgsSource(); + + LLSD next(); + + void done() const; + +private: + std::string _function; + LLSD _args; + LLSD::Integer _index; +}; + +LLSDArgsSource::LLSDArgsSource(const std::string function, const LLSD& args): + _function(function), + _args(args), + _index(0) +{ + if (! (_args.isUndefined() || _args.isArray())) + { + LL_ERRS("LLSDArgsSource") << _function << " needs an args array instead of " + << _args << LL_ENDL; + } +} + +LLSDArgsSource::~LLSDArgsSource() +{ + done(); +} + +LLSD LLSDArgsSource::next() +{ + if (_index >= _args.size()) + { + LL_ERRS("LLSDArgsSource") << _function << " requires more arguments than the " + << _args.size() << " provided: " << _args << LL_ENDL; + } + return _args[_index++]; +} + +void LLSDArgsSource::done() const +{ + if (_index < _args.size()) + { + LL_WARNS("LLSDArgsSource") << _function << " only consumed " << _index + << " of the " << _args.size() << " arguments provided: " + << _args << LL_ENDL; + } +} + +/***************************************************************************** +* LLSDArgsMapper +*****************************************************************************/ +/** + * From a formal parameters description and a map of arguments, construct an + * arguments array. + * + * That is, given: + * - an LLSD array of length n containing parameter-name strings, + * corresponding to the arguments of a function of interest + * - an LLSD collection specifying default parameter values, either: + * - an LLSD array of length m <= n, matching the rightmost m params, or + * - an LLSD map explicitly stating default name=value pairs + * - an LLSD map of parameter names and actual values for a particular + * function call + * construct an LLSD array of actual argument values for this function call. + * + * The parameter-names array and the defaults collection describe the function + * being called. The map might vary with every call, providing argument values + * for the described parameters. + * + * The array of parameter names must match the number of parameters expected + * by the function of interest. + * + * If you pass a map of default parameter values, it provides default values + * as you might expect. It is an error to specify a default value for a name + * not listed in the parameters array. + * + * If you pass an array of default parameter values, it is mapped to the + * rightmost m of the n parameter names. It is an error if the default-values + * array is longer than the parameter-names array. Consider the following + * parameter names: ["a", "b", "c", "d"]. + * + * - An empty array of default values (or an isUndefined() value) asserts that + * every one of the above parameter names is required. + * - An array of four default values [1, 2, 3, 4] asserts that every one of + * the above parameters is optional. If the current parameter map is empty, + * they will be passed to the function as [1, 2, 3, 4]. + * - An array of two default values [11, 12] asserts that parameters "a" and + * "b" are required, while "c" and "d" are optional, having default values + * "c"=11 and "d"=12. + * + * The arguments array is constructed as follows: + * + * - Arguments-map keys not found in the parameter-names array are ignored. + * - Entries from the map provide values for an improper subset of the + * parameters named in the parameter-names array. This results in a + * tentative values array with "holes." (size of map) + (number of holes) = + * (size of names array) + * - Holes are filled with the default values. + * - Any remaining holes constitute an error. + */ +class LL_COMMON_API LLSDArgsMapper +{ +public: + /// Accept description of function: function name, param names, param + /// default values + LLSDArgsMapper(const std::string& function, const LLSD& names, const LLSD& defaults); + + /// Given arguments map, return LLSD::Array of parameter values, or LL_ERRS. + LLSD map(const LLSD& argsmap) const; + +private: + static std::string formatlist(const LLSD&); + + // The function-name string is purely descriptive. We want error messages + // to be able to indicate which function's LLSDArgsMapper has the problem. + std::string _function; + // Store the names array pretty much as given. + LLSD _names; + // Though we're handed an array of name strings, it's more useful to us to + // store it as a map from name string to position index. Of course that's + // easy to generate from the incoming names array, but why do it more than + // once? + typedef std::map<LLSD::String, LLSD::Integer> IndexMap; + IndexMap _indexes; + // Generated array of default values, aligned with the array of param names. + LLSD _defaults; + // Indicate whether we have a default value for each param. + typedef std::vector<char> FilledVector; + FilledVector _has_dft; +}; + +LLSDArgsMapper::LLSDArgsMapper(const std::string& function, + const LLSD& names, const LLSD& defaults): + _function(function), + _names(names), + _has_dft(names.size()) +{ + if (! (_names.isUndefined() || _names.isArray())) + { + LL_ERRS("LLSDArgsMapper") << function << " names must be an array, not " << names << LL_ENDL; + } + LLSD::Integer nparams(_names.size()); + // From _names generate _indexes. + for (LLSD::Integer ni = 0, nend = _names.size(); ni < nend; ++ni) + { + _indexes[_names[ni]] = ni; + } + + // Presize _defaults() array so we don't have to resize it more than once. + // All entries are initialized to LLSD(); but since _has_dft is still all + // 0, they're all "holes" for now. + if (nparams) + { + _defaults[nparams - 1] = LLSD(); + } + + if (defaults.isUndefined() || defaults.isArray()) + { + LLSD::Integer ndefaults = defaults.size(); + // defaults is a (possibly empty) array. Right-align it with names. + if (ndefaults > nparams) + { + LL_ERRS("LLSDArgsMapper") << function << " names array " << names + << " shorter than defaults array " << defaults << LL_ENDL; + } + + // Offset by which we slide defaults array right to right-align with + // _names array + LLSD::Integer offset = nparams - ndefaults; + // Fill rightmost _defaults entries from defaults, and mark them as + // filled + for (LLSD::Integer i = 0, iend = ndefaults; i < iend; ++i) + { + _defaults[i + offset] = defaults[i]; + _has_dft[i + offset] = 1; + } + } + else if (defaults.isMap()) + { + // defaults is a map. Use it to populate the _defaults array. + LLSD bogus; + for (LLSD::map_const_iterator mi(defaults.beginMap()), mend(defaults.endMap()); + mi != mend; ++mi) + { + IndexMap::const_iterator ixit(_indexes.find(mi->first)); + if (ixit == _indexes.end()) + { + bogus.append(mi->first); + continue; + } + + LLSD::Integer pos = ixit->second; + // Store default value at that position in the _defaults array. + _defaults[pos] = mi->second; + // Don't forget to record the fact that we've filled this + // position. + _has_dft[pos] = 1; + } + if (bogus.size()) + { + LL_ERRS("LLSDArgsMapper") << function << " defaults specified for nonexistent params " + << formatlist(bogus) << LL_ENDL; + } + } + else + { + LL_ERRS("LLSDArgsMapper") << function << " defaults must be a map or an array, not " + << defaults << LL_ENDL; + } +} + +LLSD LLSDArgsMapper::map(const LLSD& argsmap) const +{ + if (! (argsmap.isUndefined() || argsmap.isMap() || argsmap.isArray())) + { + LL_ERRS("LLSDArgsMapper") << _function << " map() needs a map or array, not " + << argsmap << LL_ENDL; + } + // Initialize the args array. Indexing a non-const LLSD array grows it + // to appropriate size, but we don't want to resize this one on each + // new operation. Just make it as big as we need before we start + // stuffing values into it. + LLSD args(LLSD::emptyArray()); + if (_defaults.size() == 0) + { + // If this function requires no arguments, fast exit. (Don't try to + // assign to args[-1].) + return args; + } + args[_defaults.size() - 1] = LLSD(); + + // Get a vector of chars to indicate holes. It's tempting to just scan + // for LLSD::isUndefined() values after filling the args array from + // the map, but it's plausible for caller to explicitly pass + // isUndefined() as the value of some parameter name. That's legal + // since isUndefined() has well-defined conversions (default value) + // for LLSD data types. So use a whole separate array for detecting + // holes. (Avoid std::vector<bool> which is known to be odd -- can we + // iterate?) + FilledVector filled(args.size()); + + if (argsmap.isArray()) + { + // Fill args from array. If there are too many args in passed array, + // ignore the rest. + LLSD::Integer size(argsmap.size()); + if (size > args.size()) + { + // We don't just use std::min() because we want to sneak in this + // warning if caller passes too many args. + LL_WARNS("LLSDArgsMapper") << _function << " needs " << args.size() + << " params, ignoring last " << (size - args.size()) + << " of passed " << size << ": " << argsmap << LL_ENDL; + size = args.size(); + } + for (LLSD::Integer i(0); i < size; ++i) + { + // Copy the actual argument from argsmap + args[i] = argsmap[i]; + // Note that it's been filled + filled[i] = 1; + } + } + else + { + // argsmap is in fact a map. Walk the map. + for (LLSD::map_const_iterator mi(argsmap.beginMap()), mend(argsmap.endMap()); + mi != mend; ++mi) + { + // mi->first is a parameter-name string, with mi->second its + // value. Look up the name's position index in _indexes. + IndexMap::const_iterator ixit(_indexes.find(mi->first)); + if (ixit == _indexes.end()) + { + // Allow for a map containing more params than were passed in + // our names array. Caller typically receives a map containing + // the function name, cruft such as reqid, etc. Ignore keys + // not defined in _indexes. + LL_DEBUGS("LLSDArgsMapper") << _function << " ignoring " + << mi->first << "=" << mi->second << LL_ENDL; + continue; + } + LLSD::Integer pos = ixit->second; + // Store the value at that position in the args array. + args[pos] = mi->second; + // Don't forget to record the fact that we've filled this + // position. + filled[pos] = 1; + } + } + + // Fill any remaining holes from _defaults. + LLSD unfilled(LLSD::emptyArray()); + for (LLSD::Integer i = 0, iend = args.size(); i < iend; ++i) + { + if (! filled[i]) + { + // If there's no default value for this parameter, that's an + // error. + if (! _has_dft[i]) + { + unfilled.append(_names[i]); + } + else + { + args[i] = _defaults[i]; + } + } + } + // If any required args -- args without defaults -- were left unfilled + // by argsmap, that's a problem. + if (unfilled.size()) + { + LL_ERRS("LLSDArgsMapper") << _function << " missing required arguments " + << formatlist(unfilled) << " from " << argsmap << LL_ENDL; + } + + // done + return args; +} + +std::string LLSDArgsMapper::formatlist(const LLSD& list) +{ + std::ostringstream out; + const char* delim = ""; + for (LLSD::array_const_iterator li(list.beginArray()), lend(list.endArray()); + li != lend; ++li) + { + out << delim << li->asString(); + delim = ", "; + } + return out.str(); +} LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key): mDesc(desc), @@ -52,12 +400,181 @@ LLEventDispatcher::~LLEventDispatcher() { } +/** + * DispatchEntry subclass used for callables accepting(const LLSD&) + */ +struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchEntry +{ + LLSDDispatchEntry(const std::string& desc, const Callable& func, const LLSD& required): + DispatchEntry(desc), + mFunc(func), + mRequired(required) + {} + + Callable mFunc; + LLSD mRequired; + + virtual void call(const std::string& desc, const LLSD& event) const + { + // Validate the syntax of the event itself. + std::string mismatch(llsd_matches(mRequired, event)); + if (! mismatch.empty()) + { + LL_ERRS("LLEventDispatcher") << desc << ": bad request: " << mismatch << LL_ENDL; + } + // Event syntax looks good, go for it! + mFunc(event); + } + + virtual LLSD addMetadata(LLSD meta) const + { + meta["required"] = mRequired; + return meta; + } +}; + +/** + * DispatchEntry subclass for passing LLSD to functions accepting + * arbitrary argument types (convertible via LLSDParam) + */ +struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::DispatchEntry +{ + ParamsDispatchEntry(const std::string& desc, const invoker_function& func): + DispatchEntry(desc), + mInvoker(func) + {} + + invoker_function mInvoker; + + virtual void call(const std::string& desc, const LLSD& event) const + { + LLSDArgsSource src(desc, event); + mInvoker(boost::bind(&LLSDArgsSource::next, boost::ref(src))); + } +}; + +/** + * DispatchEntry subclass for dispatching LLSD::Array to functions accepting + * arbitrary argument types (convertible via LLSDParam) + */ +struct LLEventDispatcher::ArrayParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry +{ + ArrayParamsDispatchEntry(const std::string& desc, const invoker_function& func, + LLSD::Integer arity): + ParamsDispatchEntry(desc, func), + mArity(arity) + {} + + LLSD::Integer mArity; + + virtual LLSD addMetadata(LLSD meta) const + { + LLSD array(LLSD::emptyArray()); + // Resize to number of arguments required + if (mArity) + array[mArity - 1] = LLSD(); + llassert_always(array.size() == mArity); + meta["required"] = array; + return meta; + } +}; + +/** + * DispatchEntry subclass for dispatching LLSD::Map to functions accepting + * arbitrary argument types (convertible via LLSDParam) + */ +struct LLEventDispatcher::MapParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry +{ + MapParamsDispatchEntry(const std::string& name, const std::string& desc, + const invoker_function& func, + const LLSD& params, const LLSD& defaults): + ParamsDispatchEntry(desc, func), + mMapper(name, params, defaults), + mRequired(LLSD::emptyMap()) + { + // Build the set of all param keys, then delete the ones that are + // optional. What's left are the ones that are required. + for (LLSD::array_const_iterator pi(params.beginArray()), pend(params.endArray()); + pi != pend; ++pi) + { + mRequired[pi->asString()] = LLSD(); + } + + if (defaults.isArray() || defaults.isUndefined()) + { + // Right-align the params and defaults arrays. + LLSD::Integer offset = params.size() - defaults.size(); + // Now the name of every defaults[i] is at params[i + offset]. + for (LLSD::Integer i(0), iend(defaults.size()); i < iend; ++i) + { + // Erase this optional param from mRequired. + mRequired.erase(params[i + offset].asString()); + // Instead, make an entry in mOptional with the default + // param's name and value. + mOptional[params[i + offset].asString()] = defaults[i]; + } + } + else if (defaults.isMap()) + { + // if defaults is already a map, then it's already in the form we + // intend to deliver in metadata + mOptional = defaults; + // Just delete from mRequired every key appearing in mOptional. + for (LLSD::map_const_iterator mi(mOptional.beginMap()), mend(mOptional.endMap()); + mi != mend; ++mi) + { + mRequired.erase(mi->first); + } + } + } + + LLSDArgsMapper mMapper; + LLSD mRequired; + LLSD mOptional; + + virtual void call(const std::string& desc, const LLSD& event) const + { + // Just convert from LLSD::Map to LLSD::Array using mMapper, then pass + // to base-class call() method. + ParamsDispatchEntry::call(desc, mMapper.map(event)); + } + + virtual LLSD addMetadata(LLSD meta) const + { + meta["required"] = mRequired; + meta["optional"] = mOptional; + return meta; + } +}; + +void LLEventDispatcher::addArrayParamsDispatchEntry(const std::string& name, + const std::string& desc, + const invoker_function& invoker, + LLSD::Integer arity) +{ + mDispatch.insert( + DispatchMap::value_type(name, DispatchMap::mapped_type( + new ArrayParamsDispatchEntry(desc, invoker, arity)))); +} + +void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name, + const std::string& desc, + const invoker_function& invoker, + const LLSD& params, + const LLSD& defaults) +{ + mDispatch.insert( + DispatchMap::value_type(name, DispatchMap::mapped_type( + new MapParamsDispatchEntry(name, desc, invoker, params, defaults)))); +} + /// Register a callable by name void LLEventDispatcher::add(const std::string& name, const std::string& desc, const Callable& callable, const LLSD& required) { - mDispatch.insert(DispatchMap::value_type(name, - DispatchMap::mapped_type(callable, desc, required))); + mDispatch.insert( + DispatchMap::value_type(name, DispatchMap::mapped_type( + new LLSDDispatchEntry(desc, callable, required)))); } void LLEventDispatcher::addFail(const std::string& name, const std::string& classname) const @@ -83,7 +600,7 @@ bool LLEventDispatcher::remove(const std::string& name) /// such callable exists, die with LL_ERRS. void LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const { - if (! attemptCall(name, event)) + if (! try_call(name, event)) { LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): '" << name << "' not found" << LL_ENDL; @@ -98,44 +615,29 @@ void LLEventDispatcher::operator()(const LLSD& event) const // This could/should be implemented in terms of the two-arg overload. // However -- we can produce a more informative error message. std::string name(event[mKey]); - if (! attemptCall(name, event)) + if (! try_call(name, event)) { LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey << " value '" << name << "'" << LL_ENDL; } } -bool LLEventDispatcher::attemptCall(const std::string& name, const LLSD& event) const +bool LLEventDispatcher::try_call(const LLSD& event) const { - DispatchMap::const_iterator found = mDispatch.find(name); - if (found == mDispatch.end()) - { - // The reason we only return false, leaving it up to our caller to die - // with LL_ERRS, is that different callers have different amounts of - // available information. - return false; - } - // Found the name, so it's plausible to even attempt the call. But first, - // validate the syntax of the event itself. - std::string mismatch(llsd_matches(found->second.mRequired, event)); - if (! mismatch.empty()) - { - LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ") calling '" << name - << "': bad request: " << mismatch << LL_ENDL; - } - // Event syntax looks good, go for it! - (found->second.mFunc)(event); - return true; // tell caller we were able to call + return try_call(event[mKey], event); } -LLEventDispatcher::Callable LLEventDispatcher::get(const std::string& name) const +bool LLEventDispatcher::try_call(const std::string& name, const LLSD& event) const { DispatchMap::const_iterator found = mDispatch.find(name); if (found == mDispatch.end()) { - return Callable(); + return false; } - return found->second.mFunc; + // Found the name, so it's plausible to even attempt the call. + found->second->call(STRINGIZE("LLEventDispatcher(" << mDesc << ") calling '" << name << "'"), + event); + return true; // tell caller we were able to call } LLSD LLEventDispatcher::getMetadata(const std::string& name) const @@ -147,9 +649,8 @@ LLSD LLEventDispatcher::getMetadata(const std::string& name) const } LLSD meta; meta["name"] = name; - meta["desc"] = found->second.mDesc; - meta["required"] = found->second.mRequired; - return meta; + meta["desc"] = found->second->mDesc; + return found->second->addMetadata(meta); } LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key): @@ -164,3 +665,8 @@ bool LLDispatchListener::process(const LLSD& event) (*this)(event); return false; } + +LLEventDispatcher::DispatchEntry::DispatchEntry(const std::string& desc): + mDesc(desc) +{} + diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h index dfffd59eb6..7acc61de4e 100644 --- a/indra/llcommon/lleventdispatcher.h +++ b/indra/llcommon/lleventdispatcher.h @@ -27,18 +27,56 @@ * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ + * + * The invoker machinery that constructs a boost::fusion argument list for use + * with boost::fusion::invoke() is derived from + * http://www.boost.org/doc/libs/1_45_0/libs/function_types/example/interpreter.hpp + * whose license information is copied below: + * + * "(C) Copyright Tobias Schwinger + * + * Use modification and distribution are subject to the boost Software License, + * Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)." */ #if ! defined(LL_LLEVENTDISPATCHER_H) #define LL_LLEVENTDISPATCHER_H +// nil is too generic a term to be allowed to be a global macro. In +// particular, boost::fusion defines a 'class nil' (properly encapsulated in a +// namespace) that a global 'nil' macro breaks badly. +#if defined(nil) +// Capture the value of the macro 'nil', hoping int is an appropriate type. +static const int nil_(nil); +// Now forget the macro. +#undef nil +// Finally, reintroduce 'nil' as a properly-scoped alias for the previously- +// defined const 'nil_'. Make it static since otherwise it produces duplicate- +// symbol link errors later. +static const int& nil(nil_); +#endif + #include <string> -#include <map> +#include <boost/shared_ptr.hpp> #include <boost/function.hpp> #include <boost/bind.hpp> #include <boost/iterator/transform_iterator.hpp> +#include <boost/utility/enable_if.hpp> +#include <boost/function_types/is_nonmember_callable_builtin.hpp> +#include <boost/function_types/parameter_types.hpp> +#include <boost/function_types/function_arity.hpp> +#include <boost/type_traits/remove_cv.hpp> +#include <boost/type_traits/remove_reference.hpp> +#include <boost/fusion/include/push_back.hpp> +#include <boost/fusion/include/cons.hpp> +#include <boost/fusion/include/invoke.hpp> +#include <boost/mpl/begin.hpp> +#include <boost/mpl/end.hpp> +#include <boost/mpl/next.hpp> +#include <boost/mpl/deref.hpp> #include <typeinfo> #include "llevents.h" +#include "llsdutil.h" class LLSD; @@ -54,12 +92,18 @@ public: LLEventDispatcher(const std::string& desc, const std::string& key); virtual ~LLEventDispatcher(); - /// Accept any C++ callable, typically a boost::bind() expression + /// @name Register functions accepting(const LLSD&) + //@{ + + /// Accept any C++ callable with the right signature, typically a + /// boost::bind() expression typedef boost::function<void(const LLSD&)> Callable; /** - * Register a @a callable by @a name. The optional @a required parameter - * is used to validate the structure of each incoming event (see + * Register a @a callable by @a name. The passed @a callable accepts a + * single LLSD value and uses it in any way desired, e.g. extract + * parameters and call some other function. The optional @a required + * parameter is used to validate the structure of each incoming event (see * llsd_matches()). */ void add(const std::string& name, @@ -68,9 +112,23 @@ public: const LLSD& required=LLSD()); /** + * The case of a free function (or static method) accepting(const LLSD&) + * could also be intercepted by the arbitrary-args overload below. Ensure + * that it's directed to the Callable overload above instead. + */ + void add(const std::string& name, + const std::string& desc, + void (*f)(const LLSD&), + const LLSD& required=LLSD()) + { + add(name, desc, Callable(f), required); + } + + /** * Special case: a subclass of this class can pass an unbound member - * function pointer without explicitly specifying the - * <tt>boost::bind()</tt> expression. + * function pointer (of an LLEventDispatcher subclass) without explicitly + * specifying the <tt>boost::bind()</tt> expression. The passed @a method + * accepts a single LLSD value, presumably containing other parameters. */ template <class CLASS> void add(const std::string& name, @@ -81,7 +139,8 @@ public: addMethod<CLASS>(name, desc, method, required); } - /// Overload for both const and non-const methods + /// Overload for both const and non-const methods. The passed @a method + /// accepts a single LLSD value, presumably containing other parameters. template <class CLASS> void add(const std::string& name, const std::string& desc, @@ -91,15 +150,106 @@ public: addMethod<CLASS>(name, desc, method, required); } - /// Convenience: for LLEventDispatcher, not every callable needs a - /// documentation string. - template <typename CALLABLE> - void add(const std::string& name, - CALLABLE callable, - const LLSD& required=LLSD()) - { - add(name, "", callable, required); - } + //@} + + /// @name Register functions with arbitrary param lists + //@{ + + /** + * Register a free function with arbitrary parameters. (This also works + * for static class methods.) + * + * @note This supports functions with up to about 6 parameters -- after + * that you start getting dismaying compile errors in which + * boost::fusion::joint_view is mentioned a surprising number of times. + * + * When calling this name, pass an LLSD::Array. Each entry in turn will be + * converted to the corresponding parameter type using LLSDParam. + */ + template<typename Function> + typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> + >::type add(const std::string& name, + const std::string& desc, + Function f); + + /** + * Register a nonstatic class method with arbitrary parameters. + * + * @note This supports functions with up to about 6 parameters -- after + * that you start getting dismaying compile errors in which + * boost::fusion::joint_view is mentioned a surprising number of times. + * + * To cover cases such as a method on an LLSingleton we don't yet want to + * instantiate, instead of directly storing an instance pointer, accept a + * nullary callable returning a pointer/reference to the desired class + * instance. If you already have an instance in hand, + * boost::lambda::var(instance) or boost::lambda::constant(instance_ptr) + * produce suitable callables. + * + * When calling this name, pass an LLSD::Array. Each entry in turn will be + * converted to the corresponding parameter type using LLSDParam. + */ + template<typename Method, typename InstanceGetter> + typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> + >::type add(const std::string& name, + const std::string& desc, + Method f, + const InstanceGetter& getter); + + /** + * Register a free function with arbitrary parameters. (This also works + * for static class methods.) + * + * @note This supports functions with up to about 6 parameters -- after + * that you start getting dismaying compile errors in which + * boost::fusion::joint_view is mentioned a surprising number of times. + * + * Pass an LLSD::Array of parameter names, and optionally another + * LLSD::Array of default parameter values, a la LLSDArgsMapper. + * + * When calling this name, pass an LLSD::Map. We will internally generate + * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn + * to the corresponding parameter type using LLSDParam. + */ + template<typename Function> + typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> + >::type add(const std::string& name, + const std::string& desc, + Function f, + const LLSD& params, + const LLSD& defaults=LLSD()); + + /** + * Register a nonstatic class method with arbitrary parameters. + * + * @note This supports functions with up to about 6 parameters -- after + * that you start getting dismaying compile errors in which + * boost::fusion::joint_view is mentioned a surprising number of times. + * + * To cover cases such as a method on an LLSingleton we don't yet want to + * instantiate, instead of directly storing an instance pointer, accept a + * nullary callable returning a pointer/reference to the desired class + * instance. If you already have an instance in hand, + * boost::lambda::var(instance) or boost::lambda::constant(instance_ptr) + * produce suitable callables. + * + * Pass an LLSD::Array of parameter names, and optionally another + * LLSD::Array of default parameter values, a la LLSDArgsMapper. + * + * When calling this name, pass an LLSD::Map. We will internally generate + * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn + * to the corresponding parameter type using LLSDParam. + */ + template<typename Method, typename InstanceGetter> + typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> + >::type add(const std::string& name, + const std::string& desc, + Method f, + const InstanceGetter& getter, + const LLSD& params, + const LLSD& defaults=LLSD()); + + //@} /// Unregister a callable bool remove(const std::string& name); @@ -109,12 +259,25 @@ public: /// the @a required prototype specified at add() time, die with LL_ERRS. void operator()(const std::string& name, const LLSD& event) const; + /// Call a registered callable with an explicitly-specified name and + /// return <tt>true</tt>. If no such callable exists, return + /// <tt>false</tt>. If the @a event fails to match the @a required + /// prototype specified at add() time, die with LL_ERRS. + bool try_call(const std::string& name, const LLSD& event) const; + /// Extract the @a key value from the incoming @a event, and call the /// callable whose name is specified by that map @a key. If no such /// callable exists, die with LL_ERRS. If the @a event fails to match the /// @a required prototype specified at add() time, die with LL_ERRS. void operator()(const LLSD& event) const; + /// Extract the @a key value from the incoming @a event, call the callable + /// whose name is specified by that map @a key and return <tt>true</tt>. + /// If no such callable exists, return <tt>false</tt>. If the @a event + /// fails to match the @a required prototype specified at add() time, die + /// with LL_ERRS. + bool try_call(const LLSD& event) const; + /// @name Iterate over defined names //@{ typedef std::pair<std::string, std::string> NameDesc; @@ -122,16 +285,22 @@ public: private: struct DispatchEntry { - DispatchEntry(const Callable& func, const std::string& desc, const LLSD& required): - mFunc(func), - mDesc(desc), - mRequired(required) - {} - Callable mFunc; + DispatchEntry(const std::string& desc); + virtual ~DispatchEntry() {} // suppress MSVC warning, sigh + std::string mDesc; - LLSD mRequired; + + virtual void call(const std::string& desc, const LLSD& event) const = 0; + virtual LLSD addMetadata(LLSD) const = 0; }; - typedef std::map<std::string, DispatchEntry> DispatchMap; + // Tried using boost::ptr_map<std::string, DispatchEntry>, but ptr_map<> + // wants its value type to be "clonable," even just to dereference an + // iterator. I don't want to clone entries -- if I have to copy an entry + // around, I want it to continue pointing to the same DispatchEntry + // subclass object. However, I definitely want DispatchMap to destroy + // DispatchEntry if no references are outstanding at the time an entry is + // removed. This looks like a job for boost::shared_ptr. + typedef std::map<std::string, boost::shared_ptr<DispatchEntry> > DispatchMap; public: /// We want the flexibility to redefine what data we store per name, @@ -149,10 +318,6 @@ public: } //@} - /// Fetch the Callable for the specified name. If no such name was - /// registered, return an empty() Callable. - Callable get(const std::string& name) const; - /// Get information about a specific Callable LLSD getMetadata(const std::string& name) const; @@ -175,18 +340,184 @@ private: } } void addFail(const std::string& name, const std::string& classname) const; - /// try to dispatch, return @c true if success - bool attemptCall(const std::string& name, const LLSD& event) const; std::string mDesc, mKey; DispatchMap mDispatch; static NameDesc makeNameDesc(const DispatchMap::value_type& item) { - return NameDesc(item.first, item.second.mDesc); + return NameDesc(item.first, item.second->mDesc); + } + + struct LLSDDispatchEntry; + struct ParamsDispatchEntry; + struct ArrayParamsDispatchEntry; + struct MapParamsDispatchEntry; + + // Step 2 of parameter analysis. Instantiating invoker<some_function_type> + // implicitly sets its From and To parameters to the (compile time) begin + // and end iterators over that function's parameter types. + template< typename Function + , class From = typename boost::mpl::begin< boost::function_types::parameter_types<Function> >::type + , class To = typename boost::mpl::end< boost::function_types::parameter_types<Function> >::type + > + struct invoker; + + // deliver LLSD arguments one at a time + typedef boost::function<LLSD()> args_source; + // obtain args from an args_source to build param list and call target + // function + typedef boost::function<void(const args_source&)> invoker_function; + + template <typename Function> + invoker_function make_invoker(Function f); + template <typename Method, typename InstanceGetter> + invoker_function make_invoker(Method f, const InstanceGetter& getter); + void addArrayParamsDispatchEntry(const std::string& name, + const std::string& desc, + const invoker_function& invoker, + LLSD::Integer arity); + void addMapParamsDispatchEntry(const std::string& name, + const std::string& desc, + const invoker_function& invoker, + const LLSD& params, + const LLSD& defaults); +}; + +/***************************************************************************** +* LLEventDispatcher template implementation details +*****************************************************************************/ +// Step 3 of parameter analysis, the recursive case. +template<typename Function, class From, class To> +struct LLEventDispatcher::invoker +{ + template<typename T> + struct remove_cv_ref + : boost::remove_cv< typename boost::remove_reference<T>::type > + { }; + + // apply() accepts an arbitrary boost::fusion sequence as args. It + // examines the next parameter type in the parameter-types sequence + // bounded by From and To, obtains the next LLSD object from the passed + // args_source and constructs an LLSDParam of appropriate type to try + // to convert the value. It then recurs with the next parameter-types + // iterator, passing the args sequence thus far. + template<typename Args> + static inline + void apply(Function func, const args_source& argsrc, Args const & args) + { + typedef typename boost::mpl::deref<From>::type arg_type; + typedef typename boost::mpl::next<From>::type next_iter_type; + typedef typename remove_cv_ref<arg_type>::type plain_arg_type; + + invoker<Function, next_iter_type, To>::apply + ( func, argsrc, boost::fusion::push_back(args, LLSDParam<plain_arg_type>(argsrc()))); + } + + // Special treatment for instance (first) parameter of a non-static member + // function. Accept the instance-getter callable, calling that to produce + // the first args value. Since we know we're at the top of the recursion + // chain, we need not also require a partial args sequence from our caller. + template <typename InstanceGetter> + static inline + void method_apply(Function func, const args_source& argsrc, const InstanceGetter& getter) + { + typedef typename boost::mpl::next<From>::type next_iter_type; + + // Instead of grabbing the first item from argsrc and making an + // LLSDParam of it, call getter() and pass that as the instance param. + invoker<Function, next_iter_type, To>::apply + ( func, argsrc, boost::fusion::push_back(boost::fusion::nil(), boost::ref(getter()))); + } +}; + +// Step 4 of parameter analysis, the leaf case. When the general +// invoker<Function, From, To> logic has advanced From until it matches To, +// the compiler will pick this template specialization. +template<typename Function, class To> +struct LLEventDispatcher::invoker<Function,To,To> +{ + // the argument list is complete, now call the function + template<typename Args> + static inline + void apply(Function func, const args_source&, Args const & args) + { + boost::fusion::invoke(func, args); } }; +template<typename Function> +typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type +LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f) +{ + // Construct an invoker_function, a callable accepting const args_source&. + // Add to DispatchMap an ArrayParamsDispatchEntry that will handle the + // caller's LLSD::Array. + addArrayParamsDispatchEntry(name, desc, make_invoker(f), + boost::function_types::function_arity<Function>::value); +} + +template<typename Method, typename InstanceGetter> +typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type +LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f, + const InstanceGetter& getter) +{ + // Subtract 1 from the compile-time arity because the getter takes care of + // the first parameter. We only need (arity - 1) additional arguments. + addArrayParamsDispatchEntry(name, desc, make_invoker(f, getter), + boost::function_types::function_arity<Method>::value - 1); +} + +template<typename Function> +typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type +LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f, + const LLSD& params, const LLSD& defaults) +{ + // See comments for previous is_nonmember_callable_builtin add(). + addMapParamsDispatchEntry(name, desc, make_invoker(f), params, defaults); +} + +template<typename Method, typename InstanceGetter> +typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type +LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f, + const InstanceGetter& getter, + const LLSD& params, const LLSD& defaults) +{ + addMapParamsDispatchEntry(name, desc, make_invoker(f, getter), params, defaults); +} + +template <typename Function> +LLEventDispatcher::invoker_function +LLEventDispatcher::make_invoker(Function f) +{ + // Step 1 of parameter analysis, the top of the recursion. Passing a + // suitable f (see add()'s enable_if condition) to this method causes it + // to infer the function type; specifying that function type to invoker<> + // causes it to fill in the begin/end MPL iterators over the function's + // list of parameter types. + // While normally invoker::apply() could infer its template type from the + // boost::fusion::nil parameter value, here we must be explicit since + // we're boost::bind()ing it rather than calling it directly. + return boost::bind(&invoker<Function>::template apply<boost::fusion::nil>, + f, + _1, + boost::fusion::nil()); +} + +template <typename Method, typename InstanceGetter> +LLEventDispatcher::invoker_function +LLEventDispatcher::make_invoker(Method f, const InstanceGetter& getter) +{ + // Use invoker::method_apply() to treat the instance (first) arg specially. + return boost::bind(&invoker<Method>::template method_apply<InstanceGetter>, + f, + _1, + getter); +} + +/***************************************************************************** +* LLDispatchListener +*****************************************************************************/ /** * Bundle an LLEventPump and a listener with an LLEventDispatcher. A class * that contains (or derives from) LLDispatchListener need only specify the diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 97e2bdeb57..ff03506e84 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -588,3 +588,16 @@ void LLReqID::stamp(LLSD& response) const } response["reqid"] = mReqid; } + +bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyKey) +{ + // Copy 'reply' to modify it. + LLSD newreply(reply); + // Get the ["reqid"] element from request + LLReqID reqID(request); + // and copy it to 'newreply'. + reqID.stamp(newreply); + // Send reply on LLEventPump named in request[replyKey]. Don't forget to + // send the modified 'newreply' instead of the original 'reply'. + return LLEventPumps::instance().obtain(request[replyKey]).post(newreply); +} diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 2491cf1371..65b0fef354 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -692,6 +692,20 @@ private: }; /** + * Conventionally send a reply to a request event. + * + * @a reply is the LLSD reply event to send + * @a request is the corresponding LLSD request event + * @a replyKey is the key in the @a request event, conventionally ["reply"], + * whose value is the name of the LLEventPump on which to send the reply. + * + * Before sending the reply event, sendReply() copies the ["reqid"] item from + * the request to the reply. + */ +LL_COMMON_API bool sendReply(const LLSD& reply, const LLSD& request, + const std::string& replyKey="reply"); + +/** * Base class for LLListenerWrapper. See visit_and_connect() and llwrap(). We * provide virtual @c accept_xxx() methods, customization points allowing a * subclass access to certain data visible at LLEventPump::listen() time. diff --git a/indra/llcommon/llinstancetracker.cpp b/indra/llcommon/llinstancetracker.cpp index 89bc6cca39..f576204511 100644 --- a/indra/llcommon/llinstancetracker.cpp +++ b/indra/llcommon/llinstancetracker.cpp @@ -32,6 +32,17 @@ // external library headers // other Linden headers -// llinstancetracker.h is presently header-only. This file exists only because our CMake -// test macro ADD_BUILD_TEST requires it. -int dummy = 0; +//static +void * & LLInstanceTrackerBase::getInstances(std::type_info const & info) +{ + static std::map<std::string, void *> instances; + + std::string k = info.name(); + if(instances.find(k) == instances.end()) + { + instances[k] = NULL; + } + + return instances[k]; +} + diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 4945461d62..b971b2f914 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -37,14 +37,21 @@ #include <boost/iterator/transform_iterator.hpp> #include <boost/iterator/indirect_iterator.hpp> +class LL_COMMON_API LLInstanceTrackerBase : public boost::noncopyable +{ + protected: + static void * & getInstances(std::type_info const & info); +}; + /// This mix-in class adds support for tracking all instances of the specified class parameter T /// The (optional) key associates a value of type KEY with a given instance of T, for quick lookup /// If KEY is not provided, then instances are stored in a simple set /// @NOTE: see explicit specialization below for default KEY==T* case template<typename T, typename KEY = T*> -class LLInstanceTracker : boost::noncopyable +class LLInstanceTracker : public LLInstanceTrackerBase { typedef typename std::map<KEY, T*> InstanceMap; + typedef LLInstanceTracker<T, KEY> MyT; typedef boost::function<const KEY&(typename InstanceMap::value_type&)> KeyGetter; typedef boost::function<T*(typename InstanceMap::value_type&)> InstancePtrGetter; public: @@ -99,25 +106,26 @@ private: static InstanceMap& getMap_() { - if (! sInstances) + void * & instances = getInstances(typeid(MyT)); + if (! instances) { - sInstances = new InstanceMap; + instances = new InstanceMap; } - return *sInstances; + return * static_cast<InstanceMap*>(instances); } private: KEY mKey; - static InstanceMap* sInstances; }; /// explicit specialization for default case where KEY is T* /// use a simple std::set<T*> template<typename T> -class LLInstanceTracker<T, T*> +class LLInstanceTracker<T, T*> : public LLInstanceTrackerBase { typedef typename std::set<T*> InstanceSet; + typedef LLInstanceTracker<T, T*> MyT; public: /// Dereferencing key_iter gives you a T* (since T* is the key) typedef typename InstanceSet::iterator key_iter; @@ -172,19 +180,17 @@ protected: static InstanceSet& getSet_() { - if (! sInstances) + void * & instances = getInstances(typeid(MyT)); + if (! instances) { - sInstances = new InstanceSet; + instances = new InstanceSet; } - return *sInstances; + return * static_cast<InstanceSet *>(instances); } - static InstanceSet* sInstances; static S32 sIterationNestDepth; }; -template <typename T, typename KEY> typename LLInstanceTracker<T, KEY>::InstanceMap* LLInstanceTracker<T, KEY>::sInstances = NULL; -template <typename T> typename LLInstanceTracker<T, T*>::InstanceSet* LLInstanceTracker<T, T*>::sInstances = NULL; template <typename T> S32 LLInstanceTracker<T, T*>::sIterationNestDepth = 0; #endif diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp index f8f9ece058..803417d368 100644 --- a/indra/llcommon/llsdutil.cpp +++ b/indra/llcommon/llsdutil.cpp @@ -41,6 +41,7 @@ #include "llsdserialize.h" #include "stringize.h" +#include "is_approx_equal_fraction.h" #include <map> #include <set> @@ -571,7 +572,7 @@ std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::str return match_types(prototype.type(), TypeVector(), data.type(), pfx); } -bool llsd_equals(const LLSD& lhs, const LLSD& rhs) +bool llsd_equals(const LLSD& lhs, const LLSD& rhs, unsigned bits) { // We're comparing strict equality of LLSD representation rather than // performing any conversions. So if the types aren't equal, the LLSD @@ -588,6 +589,20 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs) // Both are TypeUndefined. There's nothing more to know. return true; + case LLSD::TypeReal: + // This is where the 'bits' argument comes in handy. If passed + // explicitly, it means to use is_approx_equal_fraction() to compare. + if (bits >= 0) + { + return is_approx_equal_fraction(lhs.asReal(), rhs.asReal(), bits); + } + // Otherwise we compare bit representations, and the usual caveats + // about comparing floating-point numbers apply. Omitting 'bits' when + // comparing Real values is only useful when we expect identical bit + // representation for a given Real value, e.g. for integer-valued + // Reals. + return (lhs.asReal() == rhs.asReal()); + #define COMPARE_SCALAR(type) \ case LLSD::Type##type: \ /* LLSD::URI has operator!=() but not operator==() */ \ @@ -596,10 +611,6 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs) COMPARE_SCALAR(Boolean); COMPARE_SCALAR(Integer); - // The usual caveats about comparing floating-point numbers apply. This is - // only useful when we expect identical bit representation for a given - // Real value, e.g. for integer-valued Reals. - COMPARE_SCALAR(Real); COMPARE_SCALAR(String); COMPARE_SCALAR(UUID); COMPARE_SCALAR(Date); @@ -617,7 +628,7 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs) for ( ; lai != laend && rai != raend; ++lai, ++rai) { // If any one array element is unequal, the arrays are unequal. - if (! llsd_equals(*lai, *rai)) + if (! llsd_equals(*lai, *rai, bits)) return false; } // Here we've reached the end of one or the other array. They're equal @@ -644,7 +655,7 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs) if (rhskeys.erase(lmi->first) != 1) return false; // Both maps have the current key. Compare values. - if (! llsd_equals(lmi->second, rhs[lmi->first])) + if (! llsd_equals(lmi->second, rhs[lmi->first], bits)) return false; } // We've now established that all the lhs keys have equal values in @@ -657,7 +668,7 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs) // We expect that every possible type() value is specifically handled // above. Failing to extend this switch to support a new LLSD type is // an error that must be brought to the coder's attention. - LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << "): " + LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << ", " << bits << "): " "unknown type " << lhs.type() << LL_ENDL; return false; // pacify the compiler } diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index bb8c0690b1..65c7297cbf 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -123,8 +123,10 @@ LL_COMMON_API BOOL compare_llsd_with_template( */ LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx=""); -/// Deep equality -LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs); +/// Deep equality. If you want to compare LLSD::Real values for approximate +/// equality rather than bitwise equality, pass @a bits as for +/// is_approx_equal_fraction(). +LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, unsigned bits=-1); // Simple function to copy data out of input & output iterators if // there is no need for casting. @@ -138,4 +140,283 @@ template<typename Input> LLSD llsd_copy_array(Input iter, Input end) return dest; } +/***************************************************************************** +* LLSDArray +*****************************************************************************/ +/** + * Construct an LLSD::Array inline, with implicit conversion to LLSD. Usage: + * + * @code + * void somefunc(const LLSD&); + * ... + * somefunc(LLSDArray("text")(17)(3.14)); + * @endcode + * + * For completeness, LLSDArray() with no args constructs an empty array, so + * <tt>LLSDArray()("text")(17)(3.14)</tt> produces an array equivalent to the + * above. But for most purposes, LLSD() is already equivalent to an empty + * array, and if you explicitly want an empty isArray(), there's + * LLSD::emptyArray(). However, supporting a no-args LLSDArray() constructor + * follows the principle of least astonishment. + */ +class LLSDArray +{ +public: + LLSDArray(): + _data(LLSD::emptyArray()) + {} + + /** + * Need an explicit copy constructor. Consider the following: + * + * @code + * LLSD array_of_arrays(LLSDArray(LLSDArray(17)(34)) + * (LLSDArray("x")("y"))); + * @endcode + * + * The coder intends to construct [[17, 34], ["x", "y"]]. + * + * With the compiler's implicit copy constructor, s/he gets instead + * [17, 34, ["x", "y"]]. + * + * The expression LLSDArray(17)(34) constructs an LLSDArray with those two + * values. The reader assumes it should be converted to LLSD, as we always + * want with LLSDArray, before passing it to the @em outer LLSDArray + * constructor! This copy constructor makes that happen. + */ + LLSDArray(const LLSDArray& inner): + _data(LLSD::emptyArray()) + { + _data.append(inner); + } + + LLSDArray(const LLSD& value): + _data(LLSD::emptyArray()) + { + _data.append(value); + } + + LLSDArray& operator()(const LLSD& value) + { + _data.append(value); + return *this; + } + + operator LLSD() const { return _data; } + LLSD get() const { return _data; } + +private: + LLSD _data; +}; + +/***************************************************************************** +* LLSDMap +*****************************************************************************/ +/** + * Construct an LLSD::Map inline, with implicit conversion to LLSD. Usage: + * + * @code + * void somefunc(const LLSD&); + * ... + * somefunc(LLSDMap("alpha", "abc")("number", 17)("pi", 3.14)); + * @endcode + * + * For completeness, LLSDMap() with no args constructs an empty map, so + * <tt>LLSDMap()("alpha", "abc")("number", 17)("pi", 3.14)</tt> produces a map + * equivalent to the above. But for most purposes, LLSD() is already + * equivalent to an empty map, and if you explicitly want an empty isMap(), + * there's LLSD::emptyMap(). However, supporting a no-args LLSDMap() + * constructor follows the principle of least astonishment. + */ +class LLSDMap +{ +public: + LLSDMap(): + _data(LLSD::emptyMap()) + {} + LLSDMap(const LLSD::String& key, const LLSD& value): + _data(LLSD::emptyMap()) + { + _data[key] = value; + } + + LLSDMap& operator()(const LLSD::String& key, const LLSD& value) + { + _data[key] = value; + return *this; + } + + operator LLSD() const { return _data; } + LLSD get() const { return _data; } + +private: + LLSD _data; +}; + +/***************************************************************************** +* LLSDParam +*****************************************************************************/ +/** + * LLSDParam is a customization point for passing LLSD values to function + * parameters of more or less arbitrary type. LLSD provides a small set of + * native conversions; but if a generic algorithm explicitly constructs an + * LLSDParam object in the function's argument list, a consumer can provide + * LLSDParam specializations to support more different parameter types than + * LLSD's native conversions. + * + * Usage: + * + * @code + * void somefunc(const paramtype&); + * ... + * somefunc(..., LLSDParam<paramtype>(someLLSD), ...); + * @endcode + */ +template <typename T> +class LLSDParam +{ +public: + /** + * Default implementation converts to T on construction, saves converted + * value for later retrieval + */ + LLSDParam(const LLSD& value): + _value(value) + {} + + operator T() const { return _value; } + +private: + T _value; +}; + +/** + * Turns out that several target types could accept an LLSD param using any of + * a few different conversions, e.g. LLUUID's constructor can accept LLUUID or + * std::string. Therefore, the compiler can't decide which LLSD conversion + * operator to choose, even though to us it seems obvious. But that's okay, we + * can specialize LLSDParam for such target types, explicitly specifying the + * desired conversion -- that's part of what LLSDParam is all about. Turns out + * we have to do that enough to make it worthwhile generalizing. Use a macro + * because I need to specify one of the asReal, etc., explicit conversion + * methods as well as a type. If I'm overlooking a clever way to implement + * that using a template instead, feel free to reimplement. + */ +#define LLSDParam_for(T, AS) \ +template <> \ +class LLSDParam<T> \ +{ \ +public: \ + LLSDParam(const LLSD& value): \ + _value(value.AS()) \ + {} \ + \ + operator T() const { return _value; } \ + \ +private: \ + T _value; \ +} + +LLSDParam_for(float, asReal); +LLSDParam_for(LLUUID, asUUID); +LLSDParam_for(LLDate, asDate); +LLSDParam_for(LLURI, asURI); +LLSDParam_for(LLSD::Binary, asBinary); + +/** + * LLSDParam<const char*> is an example of the kind of conversion you can + * support with LLSDParam beyond native LLSD conversions. Normally you can't + * pass an LLSD object to a function accepting const char* -- but you can + * safely pass an LLSDParam<const char*>(yourLLSD). + */ +template <> +class LLSDParam<const char*> +{ +private: + // The difference here is that we store a std::string rather than a const + // char*. It's important that the LLSDParam object own the std::string. + std::string _value; + // We don't bother storing the incoming LLSD object, but we do have to + // distinguish whether _value is an empty string because the LLSD object + // contains an empty string or because it's isUndefined(). + bool _undefined; + +public: + LLSDParam(const LLSD& value): + _value(value), + _undefined(value.isUndefined()) + {} + + // The const char* we retrieve is for storage owned by our _value member. + // That's how we guarantee that the const char* is valid for the lifetime + // of this LLSDParam object. Constructing your LLSDParam in the argument + // list should ensure that the LLSDParam object will persist for the + // duration of the function call. + operator const char*() const + { + if (_undefined) + { + // By default, an isUndefined() LLSD object's asString() method + // will produce an empty string. But for a function accepting + // const char*, it's often important to be able to pass NULL, and + // isUndefined() seems like the best way. If you want to pass an + // empty string, you can still pass LLSD(""). Without this special + // case, though, no LLSD value could pass NULL. + return NULL; + } + return _value.c_str(); + } +}; + +namespace llsd +{ + +/***************************************************************************** +* BOOST_FOREACH() helpers for LLSD +*****************************************************************************/ +/// Usage: BOOST_FOREACH(LLSD item, inArray(someLLSDarray)) { ... } +class inArray +{ +public: + inArray(const LLSD& array): + _array(array) + {} + + typedef LLSD::array_const_iterator const_iterator; + typedef LLSD::array_iterator iterator; + + iterator begin() { return _array.beginArray(); } + iterator end() { return _array.endArray(); } + const_iterator begin() const { return _array.beginArray(); } + const_iterator end() const { return _array.endArray(); } + +private: + LLSD _array; +}; + +/// MapEntry is what you get from dereferencing an LLSD::map_[const_]iterator. +typedef std::map<LLSD::String, LLSD>::value_type MapEntry; + +/// Usage: BOOST_FOREACH([const] MapEntry& e, inMap(someLLSDmap)) { ... } +class inMap +{ +public: + inMap(const LLSD& map): + _map(map) + {} + + typedef LLSD::map_const_iterator const_iterator; + typedef LLSD::map_iterator iterator; + + iterator begin() { return _map.beginMap(); } + iterator end() { return _map.endMap(); } + const_iterator begin() const { return _map.beginMap(); } + const_iterator end() const { return _map.endMap(); } + +private: + LLSD _map; +}; + +} // namespace llsd + #endif // LL_LLSDUTIL_H diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h index 488ec5b239..bd6df962a1 100644 --- a/indra/llcommon/llversionviewer.h +++ b/indra/llcommon/llversionviewer.h @@ -29,7 +29,7 @@ const S32 LL_VERSION_MAJOR = 2; const S32 LL_VERSION_MINOR = 6; -const S32 LL_VERSION_PATCH = 3; +const S32 LL_VERSION_PATCH = 5; const S32 LL_VERSION_BUILD = 0; const char * const LL_CHANNEL = "Second Life Developer"; diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp new file mode 100644 index 0000000000..263c9b171f --- /dev/null +++ b/indra/llcommon/tests/lleventdispatcher_test.cpp @@ -0,0 +1,1324 @@ +/** + * @file lleventdispatcher_test.cpp + * @author Nat Goodspeed + * @date 2011-01-20 + * @brief Test for lleventdispatcher. + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Copyright (c) 2011, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "lleventdispatcher.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "../test/lltut.h" +#include "llsd.h" +#include "llsdutil.h" +#include "stringize.h" +#include "tests/wrapllerrs.h" + +#include <map> +#include <string> +#include <stdexcept> + +#include <boost/bind.hpp> +#include <boost/function.hpp> +#include <boost/range.hpp> +#include <boost/foreach.hpp> +#define foreach BOOST_FOREACH + +#include <boost/lambda/lambda.hpp> + +#include <iostream> +#include <iomanip> + +using boost::lambda::constant; +using boost::lambda::constant_ref; +using boost::lambda::var; + +using namespace llsd; + +/***************************************************************************** +* Output control +*****************************************************************************/ +#ifdef DEBUG_ON +using std::cout; +#else +static std::ostringstream cout; +#endif + +/***************************************************************************** +* Example data, functions, classes +*****************************************************************************/ +// We don't need a whole lot of different arbitrary-params methods, just (no | +// (const LLSD&) | arbitrary) args (function | static method | non-static +// method), where 'arbitrary' is (every LLSD datatype + (const char*)). +// But we need to register each one under different names for the different +// registration styles. Don't forget LLEventDispatcher subclass methods(const +// LLSD&). + +// However, the number of target parameter conversions we want to try exceeds +// boost::fusion::invoke()'s supported parameter-list size. Break out two +// different lists. +#define NPARAMSa bool b, int i, float f, double d, const char* cp +#define NPARAMSb const std::string& s, const LLUUID& uuid, const LLDate& date, \ + const LLURI& uri, const std::vector<U8>& bin +#define NARGSa b, i, f, d, cp +#define NARGSb s, uuid, date, uri, bin + +// For some registration methods we need methods on a subclass of +// LLEventDispatcher. To simplify things, we'll use this Dispatcher subclass +// for all our testing, including testing its own methods. +class Dispatcher: public LLEventDispatcher +{ +public: + Dispatcher(const std::string& name, const std::string& key): + LLEventDispatcher(name, key) + {} + + // sensing member, mutable because we want to know when we've reached our + // const method too + mutable LLSD llsd; + + void method1(const LLSD& obj) { llsd = obj; } + void cmethod1(const LLSD& obj) const { llsd = obj; } +}; + +// sensing vars, captured in a struct to make it convenient to clear them +struct Vars +{ + LLSD llsd; + bool b; + int i; + float f; + double d; + // Capture param passed as char*. But merely storing a char* received from + // our caller, possibly the .c_str() from a concatenation expression, + // would be Bad: the pointer will be invalidated long before we can query + // it. We could allocate a new chunk of memory, copy the string data and + // point to that instead -- but hey, guess what, we already have a class + // that does that! + std::string cp; + std::string s; + LLUUID uuid; + LLDate date; + LLURI uri; + std::vector<U8> bin; + + Vars(): + // Only need to initialize the POD types, the rest should take care of + // default-constructing themselves. + b(false), + i(0), + f(0), + d(0) + {} + + // Detect any non-default values for convenient testing + LLSD inspect() const + { + LLSD result; + + if (llsd.isDefined()) + result["llsd"] = llsd; + if (b) + result["b"] = b; + if (i) + result["i"] = i; + if (f) + result["f"] = f; + if (d) + result["d"] = d; + if (! cp.empty()) + result["cp"] = cp; + if (! s.empty()) + result["s"] = s; + if (uuid != LLUUID()) + result["uuid"] = uuid; + if (date != LLDate()) + result["date"] = date; + if (uri != LLURI()) + result["uri"] = uri; + if (! bin.empty()) + result["bin"] = bin; + + return result; + } + + /*------------- no-args (non-const, const, static) methods -------------*/ + void method0() + { + cout << "method0()\n"; + i = 17; + } + + void cmethod0() const + { + cout << 'c'; + const_cast<Vars*>(this)->method0(); + } + + static void smethod0(); + + /*------------ Callable (non-const, const, static) methods -------------*/ + void method1(const LLSD& obj) + { + cout << "method1(" << obj << ")\n"; + llsd = obj; + } + + void cmethod1(const LLSD& obj) const + { + cout << 'c'; + const_cast<Vars*>(this)->method1(obj); + } + + static void smethod1(const LLSD& obj); + + /*-------- Arbitrary-params (non-const, const, static) methods ---------*/ + void methodna(NPARAMSa) + { + // Because our const char* param cp might be NULL, and because we + // intend to capture the value in a std::string, have to distinguish + // between the NULL value and any non-NULL value. Use a convention + // easy for a human reader: enclose any non-NULL value in single + // quotes, reserving the unquoted string "NULL" to represent a NULL ptr. + std::string vcp; + if (cp == NULL) + vcp = "NULL"; + else + vcp = std::string("'") + cp + "'"; + + cout << "methodna(" << b + << ", " << i + << ", " << f + << ", " << d + << ", " << vcp + << ")\n"; + + this->b = b; + this->i = i; + this->f = f; + this->d = d; + this->cp = vcp; + } + + void methodnb(NPARAMSb) + { + std::ostringstream vbin; + foreach(U8 byte, bin) + { + vbin << std::hex << std::setfill('0') << std::setw(2) << unsigned(byte); + } + + cout << "methodnb(" << "'" << s << "'" + << ", " << uuid + << ", " << date + << ", '" << uri << "'" + << ", " << vbin.str() + << ")\n"; + + this->s = s; + this->uuid = uuid; + this->date = date; + this->uri = uri; + this->bin = bin; + } + + void cmethodna(NPARAMSa) const + { + cout << 'c'; + const_cast<Vars*>(this)->methodna(NARGSa); + } + + void cmethodnb(NPARAMSb) const + { + cout << 'c'; + const_cast<Vars*>(this)->methodnb(NARGSb); + } + + static void smethodna(NPARAMSa); + static void smethodnb(NPARAMSb); +}; +/*------- Global Vars instance for free functions and static methods -------*/ +static Vars g; + +/*------------ Static Vars method implementations reference 'g' ------------*/ +void Vars::smethod0() +{ + cout << "smethod0() -> "; + g.method0(); +} + +void Vars::smethod1(const LLSD& obj) +{ + cout << "smethod1(" << obj << ") -> "; + g.method1(obj); +} + +void Vars::smethodna(NPARAMSa) +{ + cout << "smethodna(...) -> "; + g.methodna(NARGSa); +} + +void Vars::smethodnb(NPARAMSb) +{ + cout << "smethodnb(...) -> "; + g.methodnb(NARGSb); +} + +/*--------------------------- Reset global Vars ----------------------------*/ +void clear() +{ + g = Vars(); +} + +/*------------------- Free functions also reference 'g' --------------------*/ +void free0() +{ + cout << "free0() -> "; + g.method0(); +} + +void free1(const LLSD& obj) +{ + cout << "free1(" << obj << ") -> "; + g.method1(obj); +} + +void freena(NPARAMSa) +{ + cout << "freena(...) -> "; + g.methodna(NARGSa); +} + +void freenb(NPARAMSb) +{ + cout << "freenb(...) -> "; + g.methodnb(NARGSb); +} + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct lleventdispatcher_data + { + WrapLL_ERRS redirect; + Dispatcher work; + Vars v; + std::string name, desc; + // Capture our own copy of all registered functions' descriptions + typedef std::map<std::string, std::string> DescMap; + DescMap descs; + // Capture the Vars instance on which we expect each function to operate + typedef std::map<std::string, Vars*> VarsMap; + VarsMap funcvars; + // Required structure for Callables with requirements + LLSD required; + // Parameter names for freena(), freenb() + LLSD params; + // Full, partial defaults arrays for params for freena(), freenb() + LLSD dft_array_full, dft_array_partial; + // Start index of partial defaults arrays + const LLSD::Integer partial_offset; + // Full, partial defaults maps for params for freena(), freenb() + LLSD dft_map_full, dft_map_partial; + // Most of the above are indexed by "a" or "b". Useful to have an + // array containing those strings for iterating. + std::vector<LLSD::String> ab; + + lleventdispatcher_data(): + work("test dispatcher", "op"), + // map {d=double, array=[3 elements]} + required(LLSDMap("d", LLSD::Real(0))("array", LLSDArray(LLSD())(LLSD())(LLSD()))), + // first several params are required, last couple optional + partial_offset(3) + { + // This object is reconstructed for every test<n> method. But + // clear global variables every time too. + ::clear(); + + const char* abs[] = { "a", "b" }; + ab.assign(boost::begin(abs), boost::end(abs)); + + // Registration cases: + // - (Callable | subclass const method | subclass non-const method | + // non-subclass method) (with | without) required + // - (Free function | static method | non-static method), (no | arbitrary) params, + // array style + // - (Free function | static method | non-static method), (no | arbitrary) params, + // map style, (empty | partial | full) (array | map) defaults + // - Map-style errors: + // - (scalar | map) param names + // - defaults scalar + // - defaults array longer than params array + // - defaults map with plural unknown param names + + // I hate to have to write things twice, because of having to keep + // them consistent. If we had variadic functions, addf() would be + // a variadic method, capturing the name and desc and passing them + // plus "everything else" to work.add(). If I could return a pair + // and use that pair as the first two args to work.add(), I'd do + // that. But the best I can do with present C++ is to set two + // instance variables as a side effect of addf(), and pass those + // variables to each work.add() call. :-P + + /*------------------------- Callables --------------------------*/ + + // Arbitrary Callable with/out required params + addf("free1", "free1", &g); + work.add(name, desc, free1); + addf("free1_req", "free1", &g); + work.add(name, desc, free1, required); + // Subclass non-const method with/out required params + addf("Dmethod1", "method1", NULL); + work.add(name, desc, &Dispatcher::method1); + addf("Dmethod1_req", "method1", NULL); + work.add(name, desc, &Dispatcher::method1, required); + // Subclass const method with/out required params + addf("Dcmethod1", "cmethod1", NULL); + work.add(name, desc, &Dispatcher::cmethod1); + addf("Dcmethod1_req", "cmethod1", NULL); + work.add(name, desc, &Dispatcher::cmethod1, required); + // Non-subclass method with/out required params + addf("method1", "method1", &v); + work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1)); + addf("method1_req", "method1", &v); + work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1), required); + + /*--------------- Arbitrary params, array style ----------------*/ + + // (Free function | static method) with (no | arbitrary) params, array style + addf("free0_array", "free0", &g); + work.add(name, desc, free0); + addf("freena_array", "freena", &g); + work.add(name, desc, freena); + addf("freenb_array", "freenb", &g); + work.add(name, desc, freenb); + addf("smethod0_array", "smethod0", &g); + work.add(name, desc, &Vars::smethod0); + addf("smethodna_array", "smethodna", &g); + work.add(name, desc, &Vars::smethodna); + addf("smethodnb_array", "smethodnb", &g); + work.add(name, desc, &Vars::smethodnb); + // Non-static method with (no | arbitrary) params, array style + addf("method0_array", "method0", &v); + work.add(name, desc, &Vars::method0, boost::lambda::var(v)); + addf("methodna_array", "methodna", &v); + work.add(name, desc, &Vars::methodna, boost::lambda::var(v)); + addf("methodnb_array", "methodnb", &v); + work.add(name, desc, &Vars::methodnb, boost::lambda::var(v)); + + /*---------------- Arbitrary params, map style -----------------*/ + + // We lay out each params list as an array, also each array of + // default values we'll register. We'll zip these into + // (param=value) maps. Why not define them as maps and just + // extract the keys and values to arrays? Because that wouldn't + // give us the right params-list order. + + // freena(), methodna(), cmethodna(), smethodna() all take same param list. + // Same for freenb() et al. + params = LLSDMap("a", LLSDArray("b")("i")("f")("d")("cp")) + ("b", LLSDArray("s")("uuid")("date")("uri")("bin")); + cout << "params:\n" << params << "\nparams[\"a\"]:\n" << params["a"] << "\nparams[\"b\"]:\n" << params["b"] << std::endl; + // default LLSD::Binary value + std::vector<U8> binary; + for (size_t ix = 0, h = 0xaa; ix < 6; ++ix, h += 0x11) + { + binary.push_back(h); + } + // Full defaults arrays. We actually don't care what the LLUUID or + // LLDate values are, as long as they're different from the + // LLUUID() and LLDate() default values so inspect() will report + // them. + dft_array_full = LLSDMap("a", LLSDArray(true)(17)(3.14)(123456.78)("classic")) + ("b", LLSDArray("string") + (LLUUID::generateNewID()) + (LLDate::now()) + (LLURI("http://www.ietf.org/rfc/rfc3986.txt")) + (binary)); + cout << "dft_array_full:\n" << dft_array_full << std::endl; + // Partial defaults arrays. + foreach(LLSD::String a, ab) + { + LLSD::Integer partition(std::min(partial_offset, dft_array_full[a].size())); + dft_array_partial[a] = + llsd_copy_array(dft_array_full[a].beginArray() + partition, + dft_array_full[a].endArray()); + } + cout << "dft_array_partial:\n" << dft_array_partial << std::endl; + + foreach(LLSD::String a, ab) + { + // Generate full defaults maps by zipping (params, dft_array_full). + dft_map_full[a] = zipmap(params[a], dft_array_full[a]); + + // Generate partial defaults map by zipping alternate entries from + // (params, dft_array_full). Part of the point of using map-style + // defaults is to allow any subset of the target function's + // parameters to be optional, not just the rightmost. + for (LLSD::Integer ix = 0, ixend = params[a].size(); ix < ixend; ix += 2) + { + dft_map_partial[a][params[a][ix].asString()] = dft_array_full[a][ix]; + } + } + cout << "dft_map_full:\n" << dft_map_full << "\ndft_map_partial:\n" << dft_map_partial << '\n'; + + // (Free function | static method) with (no | arbitrary) params, + // map style, no (empty array) defaults + addf("free0_map", "free0", &g); + work.add(name, desc, free0, LLSD::emptyArray()); + addf("smethod0_map", "smethod0", &g); + work.add(name, desc, &Vars::smethod0, LLSD::emptyArray()); + addf("freena_map_allreq", "freena", &g); + work.add(name, desc, freena, params["a"]); + addf("freenb_map_allreq", "freenb", &g); + work.add(name, desc, freenb, params["b"]); + addf("smethodna_map_allreq", "smethodna", &g); + work.add(name, desc, &Vars::smethodna, params["a"]); + addf("smethodnb_map_allreq", "smethodnb", &g); + work.add(name, desc, &Vars::smethodnb, params["b"]); + // Non-static method with (no | arbitrary) params, map style, no + // (empty array) defaults + addf("method0_map", "method0", &v); + work.add(name, desc, &Vars::method0, var(v), LLSD::emptyArray()); + addf("methodna_map_allreq", "methodna", &v); + work.add(name, desc, &Vars::methodna, var(v), params["a"]); + addf("methodnb_map_allreq", "methodnb", &v); + work.add(name, desc, &Vars::methodnb, var(v), params["b"]); + + // Except for the "more (array | map) defaults than params" error + // cases, tested separately below, the (partial | full)(array | + // map) defaults cases don't apply to no-params functions/methods. + // So eliminate free0, smethod0, method0 from the cases below. + + // (Free function | static method) with arbitrary params, map + // style, partial (array | map) defaults + addf("freena_map_leftreq", "freena", &g); + work.add(name, desc, freena, params["a"], dft_array_partial["a"]); + addf("freenb_map_leftreq", "freenb", &g); + work.add(name, desc, freenb, params["b"], dft_array_partial["b"]); + addf("smethodna_map_leftreq", "smethodna", &g); + work.add(name, desc, &Vars::smethodna, params["a"], dft_array_partial["a"]); + addf("smethodnb_map_leftreq", "smethodnb", &g); + work.add(name, desc, &Vars::smethodnb, params["b"], dft_array_partial["b"]); + addf("freena_map_skipreq", "freena", &g); + work.add(name, desc, freena, params["a"], dft_map_partial["a"]); + addf("freenb_map_skipreq", "freenb", &g); + work.add(name, desc, freenb, params["b"], dft_map_partial["b"]); + addf("smethodna_map_skipreq", "smethodna", &g); + work.add(name, desc, &Vars::smethodna, params["a"], dft_map_partial["a"]); + addf("smethodnb_map_skipreq", "smethodnb", &g); + work.add(name, desc, &Vars::smethodnb, params["b"], dft_map_partial["b"]); + // Non-static method with arbitrary params, map style, partial + // (array | map) defaults + addf("methodna_map_leftreq", "methodna", &v); + work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_array_partial["a"]); + addf("methodnb_map_leftreq", "methodnb", &v); + work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_array_partial["b"]); + addf("methodna_map_skipreq", "methodna", &v); + work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_map_partial["a"]); + addf("methodnb_map_skipreq", "methodnb", &v); + work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_map_partial["b"]); + + // (Free function | static method) with arbitrary params, map + // style, full (array | map) defaults + addf("freena_map_adft", "freena", &g); + work.add(name, desc, freena, params["a"], dft_array_full["a"]); + addf("freenb_map_adft", "freenb", &g); + work.add(name, desc, freenb, params["b"], dft_array_full["b"]); + addf("smethodna_map_adft", "smethodna", &g); + work.add(name, desc, &Vars::smethodna, params["a"], dft_array_full["a"]); + addf("smethodnb_map_adft", "smethodnb", &g); + work.add(name, desc, &Vars::smethodnb, params["b"], dft_array_full["b"]); + addf("freena_map_mdft", "freena", &g); + work.add(name, desc, freena, params["a"], dft_map_full["a"]); + addf("freenb_map_mdft", "freenb", &g); + work.add(name, desc, freenb, params["b"], dft_map_full["b"]); + addf("smethodna_map_mdft", "smethodna", &g); + work.add(name, desc, &Vars::smethodna, params["a"], dft_map_full["a"]); + addf("smethodnb_map_mdft", "smethodnb", &g); + work.add(name, desc, &Vars::smethodnb, params["b"], dft_map_full["b"]); + // Non-static method with arbitrary params, map style, full + // (array | map) defaults + addf("methodna_map_adft", "methodna", &v); + work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_array_full["a"]); + addf("methodnb_map_adft", "methodnb", &v); + work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_array_full["b"]); + addf("methodna_map_mdft", "methodna", &v); + work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_map_full["a"]); + addf("methodnb_map_mdft", "methodnb", &v); + work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_map_full["b"]); + + // All the above are expected to succeed, and are setup for the + // tests to follow. Registration error cases are exercised as + // tests rather than as test setup. + } + + void addf(const std::string& n, const std::string& d, Vars* v) + { + // This method is to capture in our own DescMap the name and + // description of every registered function, for metadata query + // testing. + descs[n] = d; + // Also capture the Vars instance on which each function should operate. + funcvars[n] = v; + // See constructor for rationale for setting these instance vars. + this->name = n; + this->desc = d; + } + + void verify_descs() + { + // Copy descs to a temp map of same type. + DescMap forgotten(descs.begin(), descs.end()); + // LLEventDispatcher intentionally provides only const_iterator: + // since dereferencing that iterator generates values on the fly, + // it's meaningless to have a modifiable iterator. But since our + // 'work' object isn't const, by default BOOST_FOREACH() wants to + // use non-const iterators. Persuade it to use the const_iterator. + foreach(LLEventDispatcher::NameDesc nd, const_cast<const Dispatcher&>(work)) + { + DescMap::iterator found = forgotten.find(nd.first); + ensure(STRINGIZE("LLEventDispatcher records function '" << nd.first + << "' we didn't enter"), + found != forgotten.end()); + ensure_equals(STRINGIZE("LLEventDispatcher desc '" << nd.second << + "' doesn't match what we entered: '" << found->second << "'"), + nd.second, found->second); + // found in our map the name from LLEventDispatcher, good, erase + // our map entry + forgotten.erase(found); + } + if (! forgotten.empty()) + { + std::ostringstream out; + out << "LLEventDispatcher failed to report"; + const char* delim = ": "; + foreach(const DescMap::value_type& fme, forgotten) + { + out << delim << fme.first; + delim = ", "; + } + ensure(out.str(), false); + } + } + + Vars* varsfor(const std::string& name) + { + VarsMap::const_iterator found = funcvars.find(name); + ensure(STRINGIZE("No Vars* for " << name), found != funcvars.end()); + ensure(STRINGIZE("NULL Vars* for " << name), found->second); + return found->second; + } + + void ensure_has(const std::string& outer, const std::string& inner) + { + ensure(STRINGIZE("'" << outer << "' does not contain '" << inner << "'").c_str(), + outer.find(inner) != std::string::npos); + } + + void call_exc(const std::string& func, const LLSD& args, const std::string& exc_frag) + { + std::string threw; + try + { + work(func, args); + } + catch (const std::runtime_error& e) + { + cout << "*** " << e.what() << '\n'; + threw = e.what(); + } + ensure_has(threw, exc_frag); + } + + LLSD getMetadata(const std::string& name) + { + LLSD meta(work.getMetadata(name)); + ensure(STRINGIZE("No metadata for " << name), meta.isDefined()); + return meta; + } + + // From two related LLSD arrays, e.g. a param-names array and a values + // array, zip them together into an LLSD map. + LLSD zipmap(const LLSD& keys, const LLSD& values) + { + LLSD map; + for (LLSD::Integer i = 0, iend = keys.size(); i < iend; ++i) + { + // Have to select asString() since you can index an LLSD + // object with either String or Integer. + map[keys[i].asString()] = values[i]; + } + return map; + } + + // If I call this ensure_equals(), it blocks visibility of all other + // ensure_equals() overloads. Normally I could say 'using + // baseclass::ensure_equals;' and fix that, but I don't know what the + // base class is! + void ensure_llsd(const std::string& msg, const LLSD& actual, const LLSD& expected, U32 bits) + { + std::ostringstream out; + if (! msg.empty()) + { + out << msg << ": "; + } + out << "expected " << expected << ", actual " << actual; + ensure(out.str(), llsd_equals(actual, expected, bits)); + } + + void ensure_llsd(const LLSD& actual, const LLSD& expected, U32 bits) + { + ensure_llsd("", actual, expected, bits); + } + }; + typedef test_group<lleventdispatcher_data> lleventdispatcher_group; + typedef lleventdispatcher_group::object object; + lleventdispatcher_group lleventdispatchergrp("lleventdispatcher"); + + // Call cases: + // - (try_call | call) (explicit name | event key) (real | bogus) name + // - Callable with args that (do | do not) match required + // - (Free function | non-static method), no args, (array | map) style + // - (Free function | non-static method), arbitrary args, + // (array style with (scalar | map) | map style with scalar) + // - (Free function | non-static method), arbitrary args, array style with + // array (too short | too long | just right) + // [trap LL_WARNS for too-long case?] + // - (Free function | non-static method), arbitrary args, map style with + // (array | map) (all | too many | holes (with | without) defaults) + // - const char* param gets ("" | NULL) + + // Query cases: + // - Iterate over all (with | without) remove() + // - getDispatchKey() + // - Callable style (with | without) required + // - (Free function | non-static method), array style, (no | arbitrary) params + // - (Free function | non-static method), map style, (no | arbitrary) params, + // (empty | full | partial (array | map)) defaults + + template<> template<> + void object::test<1>() + { + set_test_name("map-style registration with non-array params"); + // Pass "param names" as scalar or as map + LLSD attempts(LLSDArray(17)(LLSDMap("pi", 3.14)("two", 2))); + foreach(LLSD ae, inArray(attempts)) + { + std::string threw; + try + { + work.add("freena_err", "freena", freena, ae); + } + catch (const std::exception& e) + { + threw = e.what(); + } + ensure_has(threw, "must be an array"); + } + } + + template<> template<> + void object::test<2>() + { + set_test_name("map-style registration with badly-formed defaults"); + std::string threw; + try + { + work.add("freena_err", "freena", freena, LLSDArray("a")("b"), 17); + } + catch (const std::exception& e) + { + threw = e.what(); + } + ensure_has(threw, "must be a map or an array"); + } + + template<> template<> + void object::test<3>() + { + set_test_name("map-style registration with too many array defaults"); + std::string threw; + try + { + work.add("freena_err", "freena", freena, + LLSDArray("a")("b"), + LLSDArray(17)(0.9)("gack")); + } + catch (const std::exception& e) + { + threw = e.what(); + } + ensure_has(threw, "shorter than"); + } + + template<> template<> + void object::test<4>() + { + set_test_name("map-style registration with too many map defaults"); + std::string threw; + try + { + work.add("freena_err", "freena", freena, + LLSDArray("a")("b"), + LLSDMap("b", 17)("foo", 3.14)("bar", "sinister")); + } + catch (const std::exception& e) + { + threw = e.what(); + } + ensure_has(threw, "nonexistent params"); + ensure_has(threw, "foo"); + ensure_has(threw, "bar"); + } + + template<> template<> + void object::test<5>() + { + set_test_name("query all"); + verify_descs(); + } + + template<> template<> + void object::test<6>() + { + set_test_name("query all with remove()"); + ensure("remove('bogus') returned true", ! work.remove("bogus")); + ensure("remove('real') returned false", work.remove("free1")); + // Of course, remove that from 'descs' too... + descs.erase("free1"); + verify_descs(); + } + + template<> template<> + void object::test<7>() + { + set_test_name("getDispatchKey()"); + ensure_equals(work.getDispatchKey(), "op"); + } + + template<> template<> + void object::test<8>() + { + set_test_name("query Callables with/out required params"); + LLSD names(LLSDArray("free1")("Dmethod1")("Dcmethod1")("method1")); + foreach(LLSD nm, inArray(names)) + { + LLSD metadata(getMetadata(nm)); + ensure_equals("name mismatch", metadata["name"], nm); + ensure_equals(metadata["desc"].asString(), descs[nm]); + ensure("should not have required structure", metadata["required"].isUndefined()); + ensure("should not have optional", metadata["optional"].isUndefined()); + + std::string name_req(nm.asString() + "_req"); + metadata = getMetadata(name_req); + ensure_equals(metadata["name"].asString(), name_req); + ensure_equals(metadata["desc"].asString(), descs[name_req]); + ensure_equals("required mismatch", required, metadata["required"]); + ensure("should not have optional", metadata["optional"].isUndefined()); + } + } + + template<> template<> + void object::test<9>() + { + set_test_name("query array-style functions/methods"); + // Associate each registered name with expected arity. + LLSD expected(LLSDArray + (LLSDArray + (0)(LLSDArray("free0_array")("smethod0_array")("method0_array"))) + (LLSDArray + (5)(LLSDArray("freena_array")("smethodna_array")("methodna_array"))) + (LLSDArray + (5)(LLSDArray("freenb_array")("smethodnb_array")("methodnb_array")))); + foreach(LLSD ae, inArray(expected)) + { + LLSD::Integer arity(ae[0].asInteger()); + LLSD names(ae[1]); + LLSD req(LLSD::emptyArray()); + if (arity) + req[arity - 1] = LLSD(); + foreach(LLSD nm, inArray(names)) + { + LLSD metadata(getMetadata(nm)); + ensure_equals("name mismatch", metadata["name"], nm); + ensure_equals(metadata["desc"].asString(), descs[nm]); + ensure_equals(STRINGIZE("mismatched required for " << nm.asString()), + metadata["required"], req); + ensure("should not have optional", metadata["optional"].isUndefined()); + } + } + } + + template<> template<> + void object::test<10>() + { + set_test_name("query map-style no-params functions/methods"); + // - (Free function | non-static method), map style, no params (ergo + // no defaults) + LLSD names(LLSDArray("free0_map")("smethod0_map")("method0_map")); + foreach(LLSD nm, inArray(names)) + { + LLSD metadata(getMetadata(nm)); + ensure_equals("name mismatch", metadata["name"], nm); + ensure_equals(metadata["desc"].asString(), descs[nm]); + ensure("should not have required", + (metadata["required"].isUndefined() || metadata["required"].size() == 0)); + ensure("should not have optional", metadata["optional"].isUndefined()); + } + } + + template<> template<> + void object::test<11>() + { + set_test_name("query map-style arbitrary-params functions/methods: " + "full array defaults vs. full map defaults"); + // With functions registered with no defaults ("_allreq" suffixes), + // there is of course no difference between array defaults and map + // defaults. (We don't even bother registering with LLSD::emptyArray() + // vs. LLSD::emptyMap().) With functions registered with all defaults, + // there should (!) be no difference beween array defaults and map + // defaults. Verify, so we can ignore the distinction for all other + // tests. + LLSD equivalences(LLSDArray + (LLSDArray("freena_map_adft")("freena_map_mdft")) + (LLSDArray("freenb_map_adft")("freenb_map_mdft")) + (LLSDArray("smethodna_map_adft")("smethodna_map_mdft")) + (LLSDArray("smethodnb_map_adft")("smethodnb_map_mdft")) + (LLSDArray("methodna_map_adft")("methodna_map_mdft")) + (LLSDArray("methodnb_map_adft")("methodnb_map_mdft"))); + foreach(LLSD eq, inArray(equivalences)) + { + LLSD adft(eq[0]); + LLSD mdft(eq[1]); + // We can't just compare the results of the two getMetadata() + // calls, because they contain ["name"], which are different. So + // capture them, verify that each ["name"] is as expected, then + // remove for comparing the rest. + LLSD ameta(getMetadata(adft)); + LLSD mmeta(getMetadata(mdft)); + ensure_equals("adft name", adft, ameta["name"]); + ensure_equals("mdft name", mdft, mmeta["name"]); + ameta.erase("name"); + mmeta.erase("name"); + ensure_equals(STRINGIZE("metadata for " << adft.asString() + << " vs. " << mdft.asString()), + ameta, mmeta); + } + } + + template<> template<> + void object::test<12>() + { + set_test_name("query map-style arbitrary-params functions/methods"); + // - (Free function | non-static method), map style, arbitrary params, + // (empty | full | partial (array | map)) defaults + + // Generate maps containing all parameter names for cases in which all + // params are required. Also maps containing left requirements for + // partial defaults arrays. Also defaults maps from defaults arrays. + LLSD allreq, leftreq, rightdft; + foreach(LLSD::String a, ab) + { + // The map in which all params are required uses params[a] as + // keys, with all isUndefined() as values. We can accomplish that + // by passing zipmap() an empty values array. + allreq[a] = zipmap(params[a], LLSD::emptyArray()); + // Same for leftreq, save that we use the subset of the params not + // supplied by dft_array_partial[a]. + LLSD::Integer partition(params[a].size() - dft_array_partial[a].size()); + leftreq[a] = zipmap(llsd_copy_array(params[a].beginArray(), + params[a].beginArray() + partition), + LLSD::emptyArray()); + // Generate map pairing dft_array_partial[a] values with their + // param names. + rightdft[a] = zipmap(llsd_copy_array(params[a].beginArray() + partition, + params[a].endArray()), + dft_array_partial[a]); + } + cout << "allreq:\n" << allreq << "\nleftreq:\n" << leftreq << "\nrightdft:\n" << rightdft << std::endl; + + // Generate maps containing parameter names not provided by the + // dft_map_partial maps. + LLSD skipreq(allreq); + foreach(LLSD::String a, ab) + { + foreach(const MapEntry& me, inMap(dft_map_partial[a])) + { + skipreq[a].erase(me.first); + } + } + cout << "skipreq:\n" << skipreq << std::endl; + + LLSD groups(LLSDArray // array of groups + + (LLSDArray // group + (LLSDArray("freena_map_allreq")("smethodna_map_allreq")("methodna_map_allreq")) + (LLSDArray(allreq["a"])(LLSD()))) // required, optional + + (LLSDArray // group + (LLSDArray("freenb_map_allreq")("smethodnb_map_allreq")("methodnb_map_allreq")) + (LLSDArray(allreq["b"])(LLSD()))) // required, optional + + (LLSDArray // group + (LLSDArray("freena_map_leftreq")("smethodna_map_leftreq")("methodna_map_leftreq")) + (LLSDArray(leftreq["a"])(rightdft["a"]))) // required, optional + + (LLSDArray // group + (LLSDArray("freenb_map_leftreq")("smethodnb_map_leftreq")("methodnb_map_leftreq")) + (LLSDArray(leftreq["b"])(rightdft["b"]))) // required, optional + + (LLSDArray // group + (LLSDArray("freena_map_skipreq")("smethodna_map_skipreq")("methodna_map_skipreq")) + (LLSDArray(skipreq["a"])(dft_map_partial["a"]))) // required, optional + + (LLSDArray // group + (LLSDArray("freenb_map_skipreq")("smethodnb_map_skipreq")("methodnb_map_skipreq")) + (LLSDArray(skipreq["b"])(dft_map_partial["b"]))) // required, optional + + // We only need mention the full-map-defaults ("_mdft" suffix) + // registrations, having established their equivalence with the + // full-array-defaults ("_adft" suffix) registrations in another test. + (LLSDArray // group + (LLSDArray("freena_map_mdft")("smethodna_map_mdft")("methodna_map_mdft")) + (LLSDArray(LLSD::emptyMap())(dft_map_full["a"]))) // required, optional + + (LLSDArray // group + (LLSDArray("freenb_map_mdft")("smethodnb_map_mdft")("methodnb_map_mdft")) + (LLSDArray(LLSD::emptyMap())(dft_map_full["b"])))); // required, optional + + foreach(LLSD grp, inArray(groups)) + { + // Internal structure of each group in 'groups': + LLSD names(grp[0]); + LLSD required(grp[1][0]); + LLSD optional(grp[1][1]); + cout << "For " << names << ",\n" << "required:\n" << required << "\noptional:\n" << optional << std::endl; + + // Loop through 'names' + foreach(LLSD nm, inArray(names)) + { + LLSD metadata(getMetadata(nm)); + ensure_equals("name mismatch", metadata["name"], nm); + ensure_equals(nm.asString(), metadata["desc"].asString(), descs[nm]); + ensure_equals(STRINGIZE(nm << " required mismatch"), + metadata["required"], required); + ensure_equals(STRINGIZE(nm << " optional mismatch"), + metadata["optional"], optional); + } + } + } + + template<> template<> + void object::test<13>() + { + set_test_name("try_call()"); + ensure("try_call(bogus name, LLSD()) returned true", ! work.try_call("freek", LLSD())); + ensure("try_call(bogus name) returned true", ! work.try_call(LLSDMap("op", "freek"))); + ensure("try_call(real name, LLSD()) returned false", work.try_call("free0_array", LLSD())); + ensure("try_call(real name) returned false", work.try_call(LLSDMap("op", "free0_map"))); + } + + template<> template<> + void object::test<14>() + { + set_test_name("call with bad name"); + call_exc("freek", LLSD(), "not found"); + // We don't have a comparable helper function for the one-arg + // operator() method, and it's not worth building one just for this + // case. Write it out. + std::string threw; + try + { + work(LLSDMap("op", "freek")); + } + catch (const std::runtime_error& e) + { + cout << "*** " << e.what() << "\n"; + threw = e.what(); + } + ensure_has(threw, "bad"); + ensure_has(threw, "op"); + ensure_has(threw, "freek"); + } + + template<> template<> + void object::test<15>() + { + set_test_name("call with event key"); + // We don't need a separate test for operator()(string, LLSD) with + // valid name, because all the rest of the tests exercise that case. + // The one we don't exercise elsewhere is operator()(LLSD) with valid + // name, so here it is. + work(LLSDMap("op", "free0_map")); + ensure_equals(g.i, 17); + } + + // Cannot be defined inside function body... remind me again why we use C++... :-P + struct CallablesTriple + { + std::string name, name_req; + LLSD& llsd; + }; + + template<> template<> + void object::test<16>() + { + set_test_name("call Callables"); + CallablesTriple tests[] = + { + { "free1", "free1_req", g.llsd }, + { "Dmethod1", "Dmethod1_req", work.llsd }, + { "Dcmethod1", "Dcmethod1_req", work.llsd }, + { "method1", "method1_req", v.llsd } + }; + // Arbitrary LLSD value that we should be able to pass to Callables + // without 'required', but should not be able to pass to Callables + // with 'required'. + LLSD answer(42); + // LLSD value matching 'required' according to llsd_matches() rules. + LLSD matching(LLSDMap("d", 3.14)("array", LLSDArray("answer")(true)(answer))); + // Okay, walk through 'tests'. + foreach(const CallablesTriple& tr, tests) + { + // Should be able to pass 'answer' to Callables registered + // without 'required'. + work(tr.name, answer); + ensure_equals("answer mismatch", tr.llsd, answer); + // Should NOT be able to pass 'answer' to Callables registered + // with 'required'. + call_exc(tr.name_req, answer, "bad request"); + // But SHOULD be able to pass 'matching' to Callables registered + // with 'required'. + work(tr.name_req, matching); + ensure_equals("matching mismatch", tr.llsd, matching); + } + } + + template<> template<> + void object::test<17>() + { + set_test_name("passing wrong args to (map | array)-style registrations"); + + // Pass scalar/map to array-style functions, scalar/array to map-style + // functions. As that validation happens well before we engage the + // argument magic, it seems pointless to repeat this with every + // variation: (free function | non-static method), (no | arbitrary) + // args. We should only need to engage it for one map-style + // registration and one array-style registration. + std::string array_exc("needs an args array"); + call_exc("free0_array", 17, array_exc); + call_exc("free0_array", LLSDMap("pi", 3.14), array_exc); + + std::string map_exc("needs a map"); + call_exc("free0_map", 17, map_exc); + // Passing an array to a map-style function works now! No longer an + // error case! +// call_exc("free0_map", LLSDArray("a")("b"), map_exc); + } + + template<> template<> + void object::test<18>() + { + set_test_name("call no-args functions"); + LLSD names(LLSDArray + ("free0_array")("free0_map") + ("smethod0_array")("smethod0_map") + ("method0_array")("method0_map")); + foreach(LLSD name, inArray(names)) + { + // Look up the Vars instance for this function. + Vars* vars(varsfor(name)); + // Both the global and stack Vars instances are automatically + // cleared at the start of each test<n> method. But since we're + // calling these things several different times in the same + // test<n> method, manually reset the Vars between each. + *vars = Vars(); + ensure_equals(vars->i, 0); + // call function with empty array (or LLSD(), should be equivalent) + work(name, LLSD()); + ensure_equals(vars->i, 17); + } + } + + // Break out this data because we use it in a couple different tests. + LLSD array_funcs(LLSDArray + (LLSDMap("a", "freena_array") ("b", "freenb_array")) + (LLSDMap("a", "smethodna_array")("b", "smethodnb_array")) + (LLSDMap("a", "methodna_array") ("b", "methodnb_array"))); + + template<> template<> + void object::test<19>() + { + set_test_name("call array-style functions with too-short arrays"); + // Could have two different too-short arrays, one for *na and one for + // *nb, but since they both take 5 params... + LLSD tooshort(LLSDArray("this")("array")("too")("short")); + foreach(const LLSD& funcsab, inArray(array_funcs)) + { + foreach(const llsd::MapEntry& e, inMap(funcsab)) + { + call_exc(e.second, tooshort, "requires more arguments"); + } + } + } + + template<> template<> + void object::test<20>() + { + set_test_name("call array-style functions with (just right | too long) arrays"); + std::vector<U8> binary; + for (size_t h(0x01), i(0); i < 5; h+= 0x22, ++i) + { + binary.push_back(h); + } + LLSD args(LLSDMap("a", LLSDArray(true)(17)(3.14)(123.456)("char*")) + ("b", LLSDArray("string") + (LLUUID("01234567-89ab-cdef-0123-456789abcdef")) + (LLDate("2011-02-03T15:07:00Z")) + (LLURI("http://secondlife.com")) + (binary))); + LLSD argsplus(args); + argsplus["a"].append("bogus"); + argsplus["b"].append("bogus"); + LLSD expect; + foreach(LLSD::String a, ab) + { + expect[a] = zipmap(params[a], args[a]); + } + // Adjust expect["a"]["cp"] for special Vars::cp treatment. + expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'"; + cout << "expect: " << expect << '\n'; + + // Use substantially the same logic for args and argsplus + LLSD argsarrays(LLSDArray(args)(argsplus)); + // So i==0 selects 'args', i==1 selects argsplus + for (LLSD::Integer i(0), iend(argsarrays.size()); i < iend; ++i) + { + foreach(const LLSD& funcsab, inArray(array_funcs)) + { + foreach(LLSD::String a, ab) + { + // Reset the Vars instance before each call + Vars* vars(varsfor(funcsab[a])); + *vars = Vars(); + work(funcsab[a], argsarrays[i][a]); + ensure_llsd(STRINGIZE(funcsab[a].asString() << + ": expect[\"" << a << "\"] mismatch"), + vars->inspect(), expect[a], 7); // 7 bits ~= 2 decimal digits + + // TODO: in the i==1 or argsplus case, intercept LL_WARNS + // output? Even without that, using argsplus verifies that + // passing too many args isn't fatal; it works -- but + // would be nice to notice the warning too. + } + } + } + } + + template<> template<> + void object::test<21>() + { + set_test_name("verify that passing LLSD() to const char* sends NULL"); + + ensure_equals("Vars::cp init", v.cp, ""); + work("methodna_map_mdft", LLSDMap("cp", LLSD())); + ensure_equals("passing LLSD()", v.cp, "NULL"); + work("methodna_map_mdft", LLSDMap("cp", "")); + ensure_equals("passing \"\"", v.cp, "''"); + work("methodna_map_mdft", LLSDMap("cp", "non-NULL")); + ensure_equals("passing \"non-NULL\"", v.cp, "'non-NULL'"); + } + + template<> template<> + void object::test<22>() + { + set_test_name("call map-style functions with (full | oversized) (arrays | maps)"); + const char binary[] = "\x99\x88\x77\x66\x55"; + LLSD array_full(LLSDMap + ("a", LLSDArray(false)(255)(98.6)(1024.5)("pointer")) + ("b", LLSDArray("object")(LLUUID::generateNewID())(LLDate::now())(LLURI("http://wiki.lindenlab.com/wiki"))(LLSD::Binary(boost::begin(binary), boost::end(binary))))); + LLSD array_overfull(array_full); + foreach(LLSD::String a, ab) + { + array_overfull[a].append("bogus"); + } + cout << "array_full: " << array_full << "\narray_overfull: " << array_overfull << std::endl; + // We rather hope that LLDate::now() will generate a timestamp + // distinct from the one it generated in the constructor, moments ago. + ensure_not_equals("Timestamps too close", + array_full["b"][2].asDate(), dft_array_full["b"][2].asDate()); + // We /insist/ that LLUUID::generateNewID() do so. + ensure_not_equals("UUID collision", + array_full["b"][1].asUUID(), dft_array_full["b"][1].asUUID()); + LLSD map_full, map_overfull; + foreach(LLSD::String a, ab) + { + map_full[a] = zipmap(params[a], array_full[a]); + map_overfull[a] = map_full[a]; + map_overfull[a]["extra"] = "ignore"; + } + cout << "map_full: " << map_full << "\nmap_overfull: " << map_overfull << std::endl; + LLSD expect(map_full); + // Twiddle the const char* param. + expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'"; + // Another adjustment. For each data type, we're trying to distinguish + // three values: the Vars member's initial value (member wasn't + // stored; control never reached the set function), the registered + // default param value from dft_array_full, and the array_full value + // in this test. But bool can only distinguish two values. In this + // case, we want to differentiate the local array_full value from the + // dft_array_full value, so we use 'false'. However, that means + // Vars::inspect() doesn't differentiate it from the initial value, + // so won't bother returning it. Predict that behavior to match the + // LLSD values. + expect["a"].erase("b"); + cout << "expect: " << expect << std::endl; + // For this test, calling functions registered with different sets of + // parameter defaults should make NO DIFFERENCE WHATSOEVER. Every call + // should pass all params. + LLSD names(LLSDMap + ("a", LLSDArray + ("freena_map_allreq") ("smethodna_map_allreq") ("methodna_map_allreq") + ("freena_map_leftreq")("smethodna_map_leftreq")("methodna_map_leftreq") + ("freena_map_skipreq")("smethodna_map_skipreq")("methodna_map_skipreq") + ("freena_map_adft") ("smethodna_map_adft") ("methodna_map_adft") + ("freena_map_mdft") ("smethodna_map_mdft") ("methodna_map_mdft")) + ("b", LLSDArray + ("freenb_map_allreq") ("smethodnb_map_allreq") ("methodnb_map_allreq") + ("freenb_map_leftreq")("smethodnb_map_leftreq")("methodnb_map_leftreq") + ("freenb_map_skipreq")("smethodnb_map_skipreq")("methodnb_map_skipreq") + ("freenb_map_adft") ("smethodnb_map_adft") ("methodnb_map_adft") + ("freenb_map_mdft") ("smethodnb_map_mdft") ("methodnb_map_mdft"))); + // Treat (full | overfull) (array | map) the same. + LLSD argssets(LLSDArray(array_full)(array_overfull)(map_full)(map_overfull)); + foreach(const LLSD& args, inArray(argssets)) + { + foreach(LLSD::String a, ab) + { + foreach(LLSD::String name, inArray(names[a])) + { + // Reset the Vars instance + Vars* vars(varsfor(name)); + *vars = Vars(); + work(name, args[a]); + ensure_llsd(STRINGIZE(name << ": expect[\"" << a << "\"] mismatch"), + vars->inspect(), expect[a], 7); // 7 bits, 2 decimal digits + // intercept LL_WARNS for the two overfull cases? + } + } + } + } +} // namespace tut diff --git a/indra/llimage/llimagedimensionsinfo.cpp b/indra/llimage/llimagedimensionsinfo.cpp index 835664c60f..c6bfa50b40 100644 --- a/indra/llimage/llimagedimensionsinfo.cpp +++ b/indra/llimage/llimagedimensionsinfo.cpp @@ -73,9 +73,28 @@ bool LLImageDimensionsInfo::load(const std::string& src_filename,U32 codec) bool LLImageDimensionsInfo::getImageDimensionsBmp() { - const S32 BMP_FILE_HEADER_SIZE = 14; + // Make sure the file is long enough. + const S32 DATA_LEN = 26; // BMP header (14) + DIB header size (4) + width (4) + height (4) + if (!checkFileLength(DATA_LEN)) + { + llwarns << "Premature end of file" << llendl; + return false; + } + + // Read BMP signature. + U8 signature[2]; + mInfile.read((void*)signature, sizeof(signature)/sizeof(signature[0])); + + // Make sure this is actually a BMP file. + // We only support Windows bitmaps (BM), according to LLImageBMP::updateData(). + if (signature[0] != 'B' || signature[1] != 'M') + { + llwarns << "Not a BMP" << llendl; + return false; + } - mInfile.seek(APR_CUR,BMP_FILE_HEADER_SIZE+4); + // Read image dimensions. + mInfile.seek(APR_CUR, 16); mWidth = read_reverse_s32(); mHeight = read_reverse_s32(); @@ -86,6 +105,14 @@ bool LLImageDimensionsInfo::getImageDimensionsTga() { const S32 TGA_FILE_HEADER_SIZE = 12; + // Make sure the file is long enough. + if (!checkFileLength(TGA_FILE_HEADER_SIZE + 1 /* width */ + 1 /* height */)) + { + llwarns << "Premature end of file" << llendl; + return false; + } + + // *TODO: Detect non-TGA files somehow. mInfile.seek(APR_CUR,TGA_FILE_HEADER_SIZE); mWidth = read_byte() | read_byte() << 8; mHeight = read_byte() | read_byte() << 8; @@ -95,9 +122,29 @@ bool LLImageDimensionsInfo::getImageDimensionsTga() bool LLImageDimensionsInfo::getImageDimensionsPng() { - const S32 PNG_FILE_MARKER_SIZE = 8; + const S32 PNG_MAGIC_SIZE = 8; + + // Make sure the file is long enough. + if (!checkFileLength(PNG_MAGIC_SIZE + 8 + sizeof(S32) * 2 /* width, height */)) + { + llwarns << "Premature end of file" << llendl; + return false; + } - mInfile.seek(APR_CUR,PNG_FILE_MARKER_SIZE + 8/*header offset+chunk length+chunk type*/); + // Read PNG signature. + const U8 png_magic[PNG_MAGIC_SIZE] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; + U8 signature[PNG_MAGIC_SIZE]; + mInfile.read((void*)signature, PNG_MAGIC_SIZE); + + // Make sure it's a PNG file. + if (memcmp(signature, png_magic, PNG_MAGIC_SIZE) != 0) + { + llwarns << "Not a PNG" << llendl; + return false; + } + + // Read image dimensions. + mInfile.seek(APR_CUR, 8 /* chunk length + chunk type */); mWidth = read_s32(); mHeight = read_s32(); @@ -122,6 +169,24 @@ bool LLImageDimensionsInfo::getImageDimensionsJpeg() setLastError("Unable to open file for reading", mSrcFilename); return false; } + + /* Make sure this is a JPEG file. */ + const size_t JPEG_MAGIC_SIZE = 2; + const uint8_t jpeg_magic[JPEG_MAGIC_SIZE] = {0xFF, 0xD8}; + uint8_t signature[JPEG_MAGIC_SIZE]; + + if (fread(signature, sizeof(signature), 1, fp) != 1) + { + llwarns << "Premature end of file" << llendl; + return false; + } + if (memcmp(signature, jpeg_magic, JPEG_MAGIC_SIZE) != 0) + { + llwarns << "Not a JPEG" << llendl; + return false; + } + fseek(fp, 0, SEEK_SET); // go back to start of the file + /* Init jpeg */ jpeg_error_mgr jerr; jpeg_decompress_struct cinfo; @@ -145,3 +210,13 @@ bool LLImageDimensionsInfo::getImageDimensionsJpeg() return !sJpegErrorEncountered; } +bool LLImageDimensionsInfo::checkFileLength(S32 min_len) +{ + // Make sure the file is not shorter than min_len bytes. + // so that we don't have to check value returned by each read() or seek(). + char* buf = new char[min_len]; + int nread = mInfile.read(buf, min_len); + delete[] buf; + mInfile.seek(APR_SET, 0); + return nread == min_len; +} diff --git a/indra/llimage/llimagedimensionsinfo.h b/indra/llimage/llimagedimensionsinfo.h index 5384faf3f4..382fdb2a0e 100644 --- a/indra/llimage/llimagedimensionsinfo.h +++ b/indra/llimage/llimagedimensionsinfo.h @@ -119,6 +119,9 @@ protected: return read_byte() << 8 | read_byte(); } + /// Check if the file is not shorter than min_len bytes. + bool checkFileLength(S32 min_len); + protected: LLAPRFile mInfile ; std::string mSrcFilename; diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index 26a20cede8..2103216536 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -144,8 +144,10 @@ void LLPluginClassMedia::reset() mStatusText.clear(); mProgressPercent = 0; mClickURL.clear(); + mClickNavType.clear(); mClickTarget.clear(); mClickUUID.clear(); + mStatusCode = 0; // media_time class mCurrentTime = 0.0f; @@ -1024,9 +1026,15 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) else if(message_name == "click_nofollow") { mClickURL = message.getValue("uri"); + mClickNavType = message.getValue("nav_type"); mClickTarget.clear(); mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CLICK_LINK_NOFOLLOW); } + else if(message_name == "navigate_error_page") + { + mStatusCode = message.getValueS32("status_code"); + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_NAVIGATE_ERROR_PAGE); + } else if(message_name == "cookie_set") { if(mOwner) @@ -1192,16 +1200,6 @@ void LLPluginClassMedia::browse_back() sendMessage(message); } -void LLPluginClassMedia::set_status_redirect(int code, const std::string &url) -{ - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "set_status_redirect"); - - message.setValueS32("code", code); - message.setValue("url", url); - - sendMessage(message); -} - void LLPluginClassMedia::setBrowserUserAgent(const std::string& user_agent) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "set_user_agent"); diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index 618e928a08..cf8d8b26b9 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -199,7 +199,6 @@ public: void browse_reload(bool ignore_cache = false); void browse_forward(); void browse_back(); - void set_status_redirect(int code, const std::string &url); void setBrowserUserAgent(const std::string& user_agent); void proxyWindowOpened(const std::string &target, const std::string &uuid); void proxyWindowClosed(const std::string &uuid); @@ -227,11 +226,17 @@ public: // This is valid after MEDIA_EVENT_CLICK_LINK_HREF or MEDIA_EVENT_CLICK_LINK_NOFOLLOW std::string getClickURL() const { return mClickURL; }; + // This is valid after MEDIA_EVENT_CLICK_LINK_NOFOLLOW + std::string getClickNavType() const { return mClickNavType; }; + // This is valid after MEDIA_EVENT_CLICK_LINK_HREF std::string getClickTarget() const { return mClickTarget; }; // This is valid during MEDIA_EVENT_CLICK_LINK_HREF and MEDIA_EVENT_GEOMETRY_CHANGE std::string getClickUUID() const { return mClickUUID; }; + + // This is valid after MEDIA_EVENT_NAVIGATE_ERROR_PAGE + S32 getStatusCode() const { return mStatusCode; }; // These are valid during MEDIA_EVENT_GEOMETRY_CHANGE S32 getGeometryX() const { return mGeometryX; }; @@ -378,12 +383,14 @@ protected: int mProgressPercent; std::string mLocation; std::string mClickURL; + std::string mClickNavType; std::string mClickTarget; std::string mClickUUID; S32 mGeometryX; S32 mGeometryY; S32 mGeometryWidth; S32 mGeometryHeight; + S32 mStatusCode; std::string mAuthURL; std::string mAuthRealm; std::string mHoverText; diff --git a/indra/llplugin/llpluginclassmediaowner.h b/indra/llplugin/llpluginclassmediaowner.h index 42e93cc6d7..5a4fb1ce90 100644 --- a/indra/llplugin/llpluginclassmediaowner.h +++ b/indra/llplugin/llpluginclassmediaowner.h @@ -52,12 +52,13 @@ public: MEDIA_EVENT_STATUS_TEXT_CHANGED, // browser has updated the status text MEDIA_EVENT_NAME_CHANGED, // browser has updated the name of the media (typically <title> tag) MEDIA_EVENT_LOCATION_CHANGED, // browser location (URL) has changed (maybe due to internal navagation/frames/etc) + MEDIA_EVENT_NAVIGATE_ERROR_PAGE, // browser navigated to a page that resulted in an HTTP error MEDIA_EVENT_CLICK_LINK_HREF, // I'm not entirely sure what the semantics of these two are MEDIA_EVENT_CLICK_LINK_NOFOLLOW, MEDIA_EVENT_CLOSE_REQUEST, // The plugin requested its window be closed (currently hooked up to javascript window.close in webkit) MEDIA_EVENT_PICK_FILE_REQUEST, // The plugin wants the user to pick a file MEDIA_EVENT_GEOMETRY_CHANGE, // The plugin requested its window geometry be changed (per the javascript window interface) - + MEDIA_EVENT_PLUGIN_FAILED_LAUNCH, // The plugin failed to launch MEDIA_EVENT_PLUGIN_FAILED, // The plugin died unexpectedly diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index 6ea63809f8..c86c89fa9b 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -57,9 +57,12 @@ BOOL gDebugSession = FALSE; BOOL gDebugGL = FALSE; BOOL gClothRipple = FALSE; -BOOL gNoRender = FALSE; +BOOL gHeadlessClient = FALSE; BOOL gGLActive = FALSE; +static const std::string HEADLESS_VENDOR_STRING("Linden Lab"); +static const std::string HEADLESS_RENDERER_STRING("Headless"); +static const std::string HEADLESS_VERSION_STRING("1.0"); std::ofstream gFailLog; @@ -538,9 +541,19 @@ void LLGLManager::setToDebugGPU() void LLGLManager::getGLInfo(LLSD& info) { - info["GLInfo"]["GLVendor"] = std::string((const char *)glGetString(GL_VENDOR)); - info["GLInfo"]["GLRenderer"] = std::string((const char *)glGetString(GL_RENDERER)); - info["GLInfo"]["GLVersion"] = std::string((const char *)glGetString(GL_VERSION)); + if (gHeadlessClient) + { + info["GLInfo"]["GLVendor"] = HEADLESS_VENDOR_STRING; + info["GLInfo"]["GLRenderer"] = HEADLESS_RENDERER_STRING; + info["GLInfo"]["GLVersion"] = HEADLESS_VERSION_STRING; + return; + } + else + { + info["GLInfo"]["GLVendor"] = std::string((const char *)glGetString(GL_VENDOR)); + info["GLInfo"]["GLRenderer"] = std::string((const char *)glGetString(GL_RENDERER)); + info["GLInfo"]["GLVersion"] = std::string((const char *)glGetString(GL_VERSION)); + } #if !LL_MESA_HEADLESS std::string all_exts = ll_safe_string((const char *)gGLHExts.mSysExts); @@ -556,14 +569,22 @@ void LLGLManager::getGLInfo(LLSD& info) std::string LLGLManager::getGLInfoString() { std::string info_str; - std::string all_exts, line; - info_str += std::string("GL_VENDOR ") + ll_safe_string((const char *)glGetString(GL_VENDOR)) + std::string("\n"); - info_str += std::string("GL_RENDERER ") + ll_safe_string((const char *)glGetString(GL_RENDERER)) + std::string("\n"); - info_str += std::string("GL_VERSION ") + ll_safe_string((const char *)glGetString(GL_VERSION)) + std::string("\n"); + if (gHeadlessClient) + { + info_str += std::string("GL_VENDOR ") + HEADLESS_VENDOR_STRING + std::string("\n"); + info_str += std::string("GL_RENDERER ") + HEADLESS_RENDERER_STRING + std::string("\n"); + info_str += std::string("GL_VERSION ") + HEADLESS_VERSION_STRING + std::string("\n"); + } + else + { + info_str += std::string("GL_VENDOR ") + ll_safe_string((const char *)glGetString(GL_VENDOR)) + std::string("\n"); + info_str += std::string("GL_RENDERER ") + ll_safe_string((const char *)glGetString(GL_RENDERER)) + std::string("\n"); + info_str += std::string("GL_VERSION ") + ll_safe_string((const char *)glGetString(GL_VERSION)) + std::string("\n"); + } #if !LL_MESA_HEADLESS - all_exts = (const char *)gGLHExts.mSysExts; + std::string all_exts= ll_safe_string(((const char *)gGLHExts.mSysExts)); LLStringUtil::replaceChar(all_exts, ' ', '\n'); info_str += std::string("GL_EXTENSIONS:\n") + all_exts + std::string("\n"); #endif @@ -573,15 +594,21 @@ std::string LLGLManager::getGLInfoString() void LLGLManager::printGLInfoString() { - std::string info_str; - std::string all_exts, line; - - LL_INFOS("RenderInit") << "GL_VENDOR: " << ((const char *)glGetString(GL_VENDOR)) << LL_ENDL; - LL_INFOS("RenderInit") << "GL_RENDERER: " << ((const char *)glGetString(GL_RENDERER)) << LL_ENDL; - LL_INFOS("RenderInit") << "GL_VERSION: " << ((const char *)glGetString(GL_VERSION)) << LL_ENDL; + if (gHeadlessClient) + { + LL_INFOS("RenderInit") << "GL_VENDOR: " << HEADLESS_VENDOR_STRING << LL_ENDL; + LL_INFOS("RenderInit") << "GL_RENDERER: " << HEADLESS_RENDERER_STRING << LL_ENDL; + LL_INFOS("RenderInit") << "GL_VERSION: " << HEADLESS_VERSION_STRING << LL_ENDL; + } + else + { + LL_INFOS("RenderInit") << "GL_VENDOR: " << ((const char *)glGetString(GL_VENDOR)) << LL_ENDL; + LL_INFOS("RenderInit") << "GL_RENDERER: " << ((const char *)glGetString(GL_RENDERER)) << LL_ENDL; + LL_INFOS("RenderInit") << "GL_VERSION: " << ((const char *)glGetString(GL_VERSION)) << LL_ENDL; + } #if !LL_MESA_HEADLESS - all_exts = std::string(gGLHExts.mSysExts); + std::string all_exts= ll_safe_string(((const char *)gGLHExts.mSysExts)); LLStringUtil::replaceChar(all_exts, ' ', '\n'); LL_DEBUGS("RenderInit") << "GL_EXTENSIONS:\n" << all_exts << LL_ENDL; #endif @@ -590,7 +617,14 @@ void LLGLManager::printGLInfoString() std::string LLGLManager::getRawGLString() { std::string gl_string; - gl_string = ll_safe_string((char*)glGetString(GL_VENDOR)) + " " + ll_safe_string((char*)glGetString(GL_RENDERER)); + if (gHeadlessClient) + { + gl_string = HEADLESS_VENDOR_STRING + " " + HEADLESS_RENDERER_STRING; + } + else + { + gl_string = ll_safe_string((char*)glGetString(GL_VENDOR)) + " " + ll_safe_string((char*)glGetString(GL_RENDERER)); + } return gl_string; } @@ -614,47 +648,47 @@ void LLGLManager::initExtensions() mHasMultitexture = TRUE; # else mHasMultitexture = FALSE; -# endif +# endif // GL_ARB_multitexture # ifdef GL_ARB_texture_env_combine mHasARBEnvCombine = TRUE; # else mHasARBEnvCombine = FALSE; -# endif +# endif // GL_ARB_texture_env_combine # ifdef GL_ARB_texture_compression mHasCompressedTextures = TRUE; # else mHasCompressedTextures = FALSE; -# endif +# endif // GL_ARB_texture_compression # ifdef GL_ARB_vertex_buffer_object mHasVertexBufferObject = TRUE; # else mHasVertexBufferObject = FALSE; -# endif +# endif // GL_ARB_vertex_buffer_object # ifdef GL_EXT_framebuffer_object mHasFramebufferObject = TRUE; # else mHasFramebufferObject = FALSE; -# endif +# endif // GL_EXT_framebuffer_object # ifdef GL_EXT_framebuffer_multisample mHasFramebufferMultisample = TRUE; # else mHasFramebufferMultisample = FALSE; -# endif +# endif // GL_EXT_framebuffer_multisample # ifdef GL_ARB_draw_buffers mHasDrawBuffers = TRUE; #else mHasDrawBuffers = FALSE; -# endif +# endif // GL_ARB_draw_buffers # if defined(GL_NV_depth_clamp) || defined(GL_ARB_depth_clamp) mHasDepthClamp = TRUE; #else mHasDepthClamp = FALSE; -#endif +#endif // defined(GL_NV_depth_clamp) || defined(GL_ARB_depth_clamp) # if GL_EXT_blend_func_separate mHasBlendFuncSeparate = TRUE; #else mHasBlendFuncSeparate = FALSE; -# endif +# endif // GL_EXT_blend_func_separate mHasMipMapGeneration = FALSE; mHasSeparateSpecularColor = FALSE; mHasAnisotropic = FALSE; @@ -1145,7 +1179,7 @@ void assert_glerror() } } - if (!gNoRender && gDebugGL) + if (gDebugGL) { do_assert_glerror(); } diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index 85fab7a0f8..684fd50883 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -415,7 +415,7 @@ void set_binormals(const S32 index, const U32 stride, const LLVector3 *binormals void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific ); extern BOOL gClothRipple; -extern BOOL gNoRender; +extern BOOL gHeadlessClient; extern BOOL gGLActive; #endif // LL_LLGL_H diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index e8e98211f1..d4ffd6f88e 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -967,12 +967,14 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 } if (mTexName == 0) { - llwarns << "Setting subimage on image without GL texture" << llendl; + // *TODO: Re-enable warning? Ran into thread locking issues? DK 2011-02-18 + //llwarns << "Setting subimage on image without GL texture" << llendl; return FALSE; } if (datap == NULL) { - llwarns << "Setting subimage on image with NULL datap" << llendl; + // *TODO: Re-enable warning? Ran into thread locking issues? DK 2011-02-18 + //llwarns << "Setting subimage on image with NULL datap" << llendl; return FALSE; } @@ -1100,6 +1102,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt //the texture is assiciate with some image by calling glTexImage outside LLImageGL BOOL LLImageGL::createGLTexture() { + if (gHeadlessClient) return FALSE; if (gGLManager.mIsDisabled) { llwarns << "Trying to create a texture while GL is disabled!" << llendl; @@ -1128,6 +1131,7 @@ BOOL LLImageGL::createGLTexture() BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/, BOOL to_create, S32 category) { + if (gHeadlessClient) return FALSE; if (gGLManager.mIsDisabled) { llwarns << "Trying to create a texture while GL is disabled!" << llendl; diff --git a/indra/llui/llfloaterreglistener.cpp b/indra/llui/llfloaterreglistener.cpp index 821d4543ae..7525b8cab3 100644 --- a/indra/llui/llfloaterreglistener.cpp +++ b/indra/llui/llfloaterreglistener.cpp @@ -60,6 +60,11 @@ LLFloaterRegListener::LLFloaterRegListener(): "Ask to toggle the state of the floater specified in [\"name\"]", &LLFloaterRegListener::toggleInstance, requiredName); + add("instanceVisible", + "Return on [\"reply\"] an event whose [\"visible\"] indicates the visibility " + "of the floater specified in [\"name\"]", + &LLFloaterRegListener::instanceVisible, + requiredName); LLSD requiredNameButton; requiredNameButton["name"] = LLSD(); requiredNameButton["button"] = LLSD(); @@ -71,9 +76,7 @@ LLFloaterRegListener::LLFloaterRegListener(): void LLFloaterRegListener::getBuildMap(const LLSD& event) const { - // Honor the "reqid" convention by echoing event["reqid"] in our reply packet. - LLReqID reqID(event); - LLSD reply(reqID.makeResponse()); + LLSD reply; // Build an LLSD map that mirrors sBuildMap. Since we have no good way to // represent a C++ callable in LLSD, the only part of BuildData we can // store is the filename. For each LLSD map entry, it would be more @@ -86,7 +89,7 @@ void LLFloaterRegListener::getBuildMap(const LLSD& event) const reply[mi->first] = mi->second.mFile; } // Send the reply to the LLEventPump named in event["reply"]. - LLEventPumps::instance().obtain(event["reply"]).post(reply); + sendReply(reply, event); } void LLFloaterRegListener::showInstance(const LLSD& event) const @@ -104,6 +107,12 @@ void LLFloaterRegListener::toggleInstance(const LLSD& event) const LLFloaterReg::toggleInstance(event["name"], event["key"]); } +void LLFloaterRegListener::instanceVisible(const LLSD& event) const +{ + sendReply(LLSDMap("visible", LLFloaterReg::instanceVisible(event["name"], event["key"])), + event); +} + void LLFloaterRegListener::clickButton(const LLSD& event) const { // If the caller requests a reply, build the reply. diff --git a/indra/llui/llfloaterreglistener.h b/indra/llui/llfloaterreglistener.h index 586656667c..24311a2dfa 100644 --- a/indra/llui/llfloaterreglistener.h +++ b/indra/llui/llfloaterreglistener.h @@ -47,6 +47,7 @@ private: void showInstance(const LLSD& event) const; void hideInstance(const LLSD& event) const; void toggleInstance(const LLSD& event) const; + void instanceVisible(const LLSD& event) const; void clickButton(const LLSD& event) const; }; diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h index e9bdab2d47..669e126266 100644 --- a/indra/llui/lliconctrl.h +++ b/indra/llui/lliconctrl.h @@ -69,6 +69,7 @@ public: void setColor(const LLColor4& color) { mColor = color; } void setImage(LLPointer<LLUIImage> image) { mImagep = image; } + const LLPointer<LLUIImage> getImage() { return mImagep; } private: void setIconImageDrawSize() ; diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 7e348656a9..d99ee5a545 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -2290,3 +2290,8 @@ void LLLineEditor::setContextMenu(LLContextMenu* new_context_menu) else mContextMenuHandle.markDead(); } + +void LLLineEditor::setFont(const LLFontGL* font) +{ + mGLFont = font; +} diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index 723423a5b9..7b5fa218f2 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -201,6 +201,7 @@ public: const LLColor4& getTentativeFgColor() const { return mTentativeFgColor.get(); } const LLFontGL* getFont() const { return mGLFont; } + void setFont(const LLFontGL* font); void setIgnoreArrowKeys(BOOL b) { mIgnoreArrowKeys = b; } void setIgnoreTab(BOOL b) { mIgnoreTab = b; } diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index f0374de98f..8de9c769e2 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -3105,9 +3105,6 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2); menu->translateIntoRectWithExclusion( menu_region_rect, mouse_rect, FALSE ); menu->getParent()->sendChildToFront(menu); - - - } ///============================================================================ diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 49537ef78f..82269282ef 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -157,6 +157,7 @@ LLTextBase::Params::Params() read_only("read_only", false), v_pad("v_pad", 0), h_pad("h_pad", 0), + clip("clip", true), clip_partial("clip_partial", true), line_spacing("line_spacing"), max_text_length("max_length", 255), @@ -199,6 +200,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) mVAlign(p.font_valign), mLineSpacingMult(p.line_spacing.multiple), mLineSpacingPixels(p.line_spacing.pixels), + mClip(p.clip), mClipPartial(p.clip_partial && !p.allow_scroll), mTrackEnd( p.track_end ), mScrollIndex(-1), @@ -334,7 +336,7 @@ void LLTextBase::drawSelectionBackground() // binary search for line that starts before top of visible buffer line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mTop, compare_bottom()); - line_list_t::const_iterator end_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top()); + line_list_t::const_iterator end_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top()); bool done = false; @@ -512,7 +514,6 @@ void LLTextBase::drawText() selection_right = llmax( mSelectionStart, mSelectionEnd ); } - LLRect scrolled_view_rect = getVisibleDocumentRect(); std::pair<S32, S32> line_range = getVisibleLines(mClipPartial); S32 first_line = line_range.first; S32 last_line = line_range.second; @@ -545,10 +546,10 @@ void LLTextBase::drawText() line_end = next_start; } - LLRect text_rect(line.mRect.mLeft + mVisibleTextRect.mLeft - scrolled_view_rect.mLeft, - line.mRect.mTop - scrolled_view_rect.mBottom + mVisibleTextRect.mBottom, - llmin(mDocumentView->getRect().getWidth(), line.mRect.mRight) - scrolled_view_rect.mLeft, - line.mRect.mBottom - scrolled_view_rect.mBottom + mVisibleTextRect.mBottom); + LLRect text_rect(line.mRect); + text_rect.mRight = llmin(mDocumentView->getRect().getWidth(), text_rect.mRight); // clamp right edge to document extents + text_rect.translate(mVisibleTextRect.mLeft, mVisibleTextRect.mBottom); // translate into display region of text widget + text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom); // adjust by scroll position // draw a single line of text S32 seg_start = line_start; @@ -993,14 +994,28 @@ void LLTextBase::draw() updateScrollFromCursor(); } - LLRect doc_rect; + LLRect text_rect; if (mScroller) { - mScroller->localRectToOtherView(mScroller->getContentWindowRect(), &doc_rect, this); + mScroller->localRectToOtherView(mScroller->getContentWindowRect(), &text_rect, this); } else { - doc_rect = getLocalRect(); + LLRect visible_lines_rect; + std::pair<S32, S32> line_range = getVisibleLines(mClipPartial); + for (S32 i = line_range.first; i < line_range.second; i++) + { + if (visible_lines_rect.isEmpty()) + { + visible_lines_rect = mLineInfoList[i].mRect; + } + else + { + visible_lines_rect.unionWith(mLineInfoList[i].mRect); + } + } + text_rect = visible_lines_rect; + text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom); } if (mBGVisible) @@ -1010,28 +1025,37 @@ void LLTextBase::draw() LLRect bg_rect = mVisibleTextRect; if (mScroller) { - bg_rect.intersectWith(doc_rect); + bg_rect.intersectWith(text_rect); } LLColor4 bg_color = mReadOnly ? mReadOnlyBgColor.get() : hasFocus() ? mFocusBgColor.get() : mWriteableBgColor.get(); - gl_rect_2d(doc_rect, bg_color % alpha, TRUE); + gl_rect_2d(text_rect, bg_color % alpha, TRUE); } - // draw document view - LLUICtrl::draw(); - - { - // only clip if we support scrolling... - // since convention is that text boxes never vertically truncate their contents - // regardless of rect bounds - LLLocalClipRect clip(doc_rect, mScroller != NULL); + bool should_clip = mClip || mScroller != NULL; + { LLLocalClipRect clip(text_rect, should_clip); + + // draw document view + if (mScroller) + { + drawChild(mScroller); + } + else + { + drawChild(mDocumentView); + } + drawSelectionBackground(); drawText(); drawCursor(); } + + mDocumentView->setVisible(FALSE); + LLUICtrl::draw(); + mDocumentView->setVisible(TRUE); } @@ -1415,7 +1439,7 @@ S32 LLTextBase::getFirstVisibleLine() const return iter - mLineInfoList.begin(); } -std::pair<S32, S32> LLTextBase::getVisibleLines(bool fully_visible) +std::pair<S32, S32> LLTextBase::getVisibleLines(bool require_fully_visible) { LLRect visible_region = getVisibleDocumentRect(); line_list_t::const_iterator first_iter; @@ -1424,14 +1448,14 @@ std::pair<S32, S32> LLTextBase::getVisibleLines(bool fully_visible) // make sure we have an up-to-date mLineInfoList reflow(); - if (fully_visible) + if (require_fully_visible) { first_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_top()); - last_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_bottom()); + last_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_bottom()); } else { - first_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom()); + first_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom()); last_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_top()); } return std::pair<S32, S32>(first_iter - mLineInfoList.begin(), last_iter - mLineInfoList.begin()); @@ -2405,14 +2429,41 @@ LLRect LLTextBase::getVisibleDocumentRect() const { return mScroller->getVisibleContentRect(); } - else + else if (mClip) { - // entire document rect is visible when not scrolling + LLRect visible_text_rect = getVisibleTextRect(); + LLRect doc_rect = mDocumentView->getRect(); + visible_text_rect.translate(-doc_rect.mLeft, -doc_rect.mBottom); + + // reject partially visible lines + LLRect visible_lines_rect; + for (line_list_t::const_iterator it = mLineInfoList.begin(), end_it = mLineInfoList.end(); + it != end_it; + ++it) + { + bool line_visible = mClipPartial ? visible_text_rect.contains(it->mRect) : visible_text_rect.overlaps(it->mRect); + if (line_visible) + { + if (visible_lines_rect.isEmpty()) + { + visible_lines_rect = it->mRect; + } + else + { + visible_lines_rect.unionWith(it->mRect); + } + } + } + return visible_lines_rect; + } + else + { // entire document rect is visible // but offset according to height of widget + LLRect doc_rect = mDocumentView->getLocalRect(); doc_rect.mLeft -= mDocumentView->getRect().mLeft; // adjust for height of text above widget baseline - doc_rect.mBottom = doc_rect.getHeight() - mVisibleTextRect.getHeight(); + doc_rect.mBottom = llmin(0, doc_rect.getHeight() - mVisibleTextRect.getHeight()); return doc_rect; } } diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index aafcf8ceb0..7d545a1ba6 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -265,6 +265,7 @@ public: use_ellipses, parse_urls, parse_highlights, + clip, clip_partial; Optional<S32> v_pad, @@ -338,7 +339,7 @@ public: void addDocumentChild(LLView* view); void removeDocumentChild(LLView* view); const LLView* getDocumentView() const { return mDocumentView; } - LLRect getVisibleTextRect() { return mVisibleTextRect; } + LLRect getVisibleTextRect() const { return mVisibleTextRect; } LLRect getTextBoundingRect(); LLRect getVisibleDocumentRect() const; @@ -552,6 +553,7 @@ protected: bool mTrackEnd; // if true, keeps scroll position at end of document during resize bool mReadOnly; bool mBGVisible; // render background? + bool mClip; // clip text to widget rect bool mClipPartial; // false if we show lines that are partially inside bounding rect bool mPlainText; // didn't use Image or Icon segments S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index c300fe55d9..87669574c2 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -61,6 +61,8 @@ // for XUIParse #include "llquaternion.h" #include <boost/tokenizer.hpp> +#include <boost/algorithm/string/find_iterator.hpp> +#include <boost/algorithm/string/finder.hpp> // // Globals @@ -2020,6 +2022,53 @@ void LLUI::positionViewNearMouse(LLView* view, S32 spawn_x, S32 spawn_y) view->translateIntoRectWithExclusion( virtual_window_rect, mouse_rect, FALSE ); } +LLView* LLUI::resolvePath(LLView* context, const std::string& path) +{ + // Nothing about resolvePath() should require non-const LLView*. If caller + // wants non-const, call the const flavor and then cast away const-ness. + return const_cast<LLView*>(resolvePath(const_cast<const LLView*>(context), path)); +} + +const LLView* LLUI::resolvePath(const LLView* context, const std::string& path) +{ + // Create an iterator over slash-separated parts of 'path'. Dereferencing + // this iterator returns an iterator_range over the substring. Unlike + // LLStringUtil::getTokens(), this split_iterator doesn't combine adjacent + // delimiters: leading/trailing slash produces an empty substring, double + // slash produces an empty substring. That's what we need. + boost::split_iterator<std::string::const_iterator> ti(path, boost::first_finder("/")), tend; + + if (ti == tend) + { + // 'path' is completely empty, no navigation + return context; + } + + // leading / means "start at root" + if (ti->empty()) + { + context = getRootView(); + ++ti; + } + + bool recurse = false; + for (; ti != tend && context; ++ti) + { + if (ti->empty()) + { + recurse = true; + } + else + { + std::string part(ti->begin(), ti->end()); + context = context->findChildView(part, recurse); + recurse = false; + } + } + + return context; +} + // LLLocalClipRect and LLScreenClipRect moved to lllocalcliprect.h/cpp diff --git a/indra/llui/llui.h b/indra/llui/llui.h index 62d10df8b2..50cb9e6632 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -185,6 +185,33 @@ public: //helper functions (should probably move free standing rendering helper functions here) static LLView* getRootView() { return sRootView; } static void setRootView(LLView* view) { sRootView = view; } + /** + * Walk the LLView tree to resolve a path + * Paths can be discovered using Develop > XUI > Show XUI Paths + * + * A leading "/" indicates the root of the tree is the starting + * position of the search, (otherwise the context node is used) + * + * Adjacent "//" mean that the next level of the search is done + * recursively ("descendant" rather than "child"). + * + * Return values: If no match is found, NULL is returned, + * otherwise the matching LLView* is returned. + * + * Examples: + * + * "/" -> return the root view + * "/foo" -> find "foo" as a direct child of the root + * "foo" -> find "foo" as a direct child of the context node + * "//foo" -> find the first "foo" child anywhere in the tree + * "/foo/bar" -> find "foo" as direct child of the root, and + * "bar" as a direct child of "foo" + * "//foo//bar/baz" -> find the first "foo" anywhere in the + * tree, the first "bar" anywhere under it, and "baz" + * as a direct child of that + */ + static const LLView* resolvePath(const LLView* context, const std::string& path); + static LLView* resolvePath(LLView* context, const std::string& path); static std::string locateSkin(const std::string& filename); static void setMousePositionScreen(S32 x, S32 y); static void getMousePositionScreen(S32 *x, S32 *y); diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index 9d174ef1cd..3d89867bc1 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -35,16 +35,20 @@ include_directories( set(llwindow_SOURCE_FILES llkeyboard.cpp + llkeyboardheadless.cpp llwindowheadless.cpp llwindowcallbacks.cpp + llwindowlistener.cpp ) set(llwindow_HEADER_FILES CMakeLists.txt llkeyboard.h + llkeyboardheadless.h llwindowheadless.h llwindowcallbacks.h + llwindowlistener.h ) set(viewer_SOURCE_FILES diff --git a/indra/llwindow/llkeyboardheadless.cpp b/indra/llwindow/llkeyboardheadless.cpp new file mode 100644 index 0000000000..4dfaaed4e1 --- /dev/null +++ b/indra/llwindow/llkeyboardheadless.cpp @@ -0,0 +1,50 @@ +/** + * @file llkeyboardheadless.cpp + * @brief Handler for assignable key bindings + * + * $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 "llkeyboardheadless.h" +#include "llwindowcallbacks.h" + +LLKeyboardHeadless::LLKeyboardHeadless() +{ } + +void LLKeyboardHeadless::resetMaskKeys() +{ } + + +BOOL LLKeyboardHeadless::handleKeyDown(const U16 key, const U32 mask) +{ return FALSE; } + + +BOOL LLKeyboardHeadless::handleKeyUp(const U16 key, const U32 mask) +{ return FALSE; } + +MASK LLKeyboardHeadless::currentMask(BOOL for_mouse_event) +{ return MASK_NONE; } + +void LLKeyboardHeadless::scanKeyboard() +{ } + diff --git a/indra/llwindow/llkeyboardheadless.h b/indra/llwindow/llkeyboardheadless.h new file mode 100644 index 0000000000..4e666f8ce8 --- /dev/null +++ b/indra/llwindow/llkeyboardheadless.h @@ -0,0 +1,45 @@ +/** + * @file llkeyboardheadless.h + * @brief Handler for assignable key bindings + * + * $LicenseInfo:firstyear=2004&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$ + */ + +#ifndef LL_LLKEYBOARDHEADLESS_H +#define LL_LLKEYBOARDHEADLESS_H + +#include "llkeyboard.h" + +class LLKeyboardHeadless : public LLKeyboard +{ +public: + LLKeyboardHeadless(); + /*virtual*/ ~LLKeyboardHeadless() {}; + + /*virtual*/ BOOL handleKeyUp(const U16 key, MASK mask); + /*virtual*/ BOOL handleKeyDown(const U16 key, MASK mask); + /*virtual*/ void resetMaskKeys(); + /*virtual*/ MASK currentMask(BOOL for_mouse_event); + /*virtual*/ void scanKeyboard(); +}; + +#endif diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index 072f694c24..71a5df910d 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -41,6 +41,8 @@ #include "llkeyboard.h" #include "linked_lists.h" #include "llwindowcallbacks.h" +#include "llwindowlistener.h" +#include <boost/lambda/core.hpp> // @@ -115,10 +117,19 @@ LLWindow::LLWindow(LLWindowCallbacks* callbacks, BOOL fullscreen, U32 flags) mHideCursorPermanent(FALSE), mFlags(flags), mHighSurrogate(0) -{ } +{ + // gKeyboard is still NULL, so it doesn't do LLWindowListener any good to + // pass its value right now. Instead, pass it a nullary function that + // will, when we later need it, return the value of gKeyboard. + // boost::lambda::var() constructs such a functor on the fly. + mListener = new LLWindowListener(callbacks, boost::lambda::var(gKeyboard)); +} LLWindow::~LLWindow() -{ } +{ + delete mListener; + mListener = NULL; +} //virtual BOOL LLWindow::isValid() diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index e8a86a1880..6bdc01ae88 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -36,6 +36,7 @@ class LLSplashScreen; class LLPreeditor; class LLWindowCallbacks; +class LLWindowListener; // Refer to llwindow_test in test/common/llwindow for usage example @@ -188,6 +189,7 @@ protected: BOOL mHideCursorPermanent; U32 mFlags; U16 mHighSurrogate; + LLWindowListener* mListener; // Handle a UTF-16 encoding unit received from keyboard. // Converting the series of UTF-16 encoding units to UTF-32 data, diff --git a/indra/llwindow/llwindowheadless.cpp b/indra/llwindow/llwindowheadless.cpp index 35398f1c09..e6e6bc67ff 100644 --- a/indra/llwindow/llwindowheadless.cpp +++ b/indra/llwindow/llwindowheadless.cpp @@ -28,6 +28,7 @@ #include "indra_constants.h" #include "llwindowheadless.h" +#include "llkeyboardheadless.h" // // LLWindowHeadless @@ -37,6 +38,9 @@ LLWindowHeadless::LLWindowHeadless(LLWindowCallbacks* callbacks, const std::stri BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth) : LLWindow(callbacks, fullscreen, flags) { + // Initialize a headless keyboard. + gKeyboard = new LLKeyboardHeadless(); + gKeyboard->setCallbacks(callbacks); } diff --git a/indra/llwindow/llwindowlistener.cpp b/indra/llwindow/llwindowlistener.cpp new file mode 100644 index 0000000000..91b99d83c6 --- /dev/null +++ b/indra/llwindow/llwindowlistener.cpp @@ -0,0 +1,307 @@ +/** + * @file llwindowlistener.cpp + * @brief EventAPI interface for injecting input into LLWindow + * + * $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 "llwindowlistener.h" + +#include "llcoord.h" +#include "llkeyboard.h" +#include "llwindowcallbacks.h" +#include <map> + +LLWindowListener::LLWindowListener(LLWindowCallbacks *window, const KeyboardGetter& kbgetter) + : LLEventAPI("LLWindow", "Inject input events into the LLWindow instance"), + mWindow(window), + mKbGetter(kbgetter) +{ + std::string keySomething = + "Given [\"keysym\"], [\"keycode\"] or [\"char\"], inject the specified "; + std::string keyExplain = + "(integer keycode values, or keysym \"XXXX\" from any KEY_XXXX, in\n" + "http://hg.secondlife.com/viewer-development/src/tip/indra/llcommon/indra_constants.h )"; + std::string mask = + "Specify optional [\"mask\"] as an array containing any of \"CONTROL\", \"ALT\",\n" + "\"SHIFT\" or \"MAC_CONTROL\"; the corresponding modifier bits will be combined\n" + "to form the mask used with the event."; + + std::string mouseSomething = + "Given [\"button\"], [\"x\"] and [\"y\"], inject the given mouse "; + std::string mouseExplain = + "(button values \"LEFT\", \"MIDDLE\", \"RIGHT\")"; + + add("keyDown", + keySomething + "keypress event.\n" + keyExplain + '\n' + mask, + &LLWindowListener::keyDown); + add("keyUp", + keySomething + "key release event.\n" + keyExplain + '\n' + mask, + &LLWindowListener::keyUp); + add("mouseDown", + mouseSomething + "click event.\n" + mouseExplain + '\n' + mask, + &LLWindowListener::mouseDown); + add("mouseUp", + mouseSomething + "release event.\n" + mouseExplain + '\n' + mask, + &LLWindowListener::mouseUp); + add("mouseMove", + std::string("Given [\"x\"] and [\"y\"], inject the given mouse movement event.\n") + + mask, + &LLWindowListener::mouseMove); + add("mouseScroll", + "Given an integer number of [\"clicks\"], inject the given mouse scroll event.\n" + "(positive clicks moves downward through typical content)", + &LLWindowListener::mouseScroll); +} + +template <typename MAPPED> +class StringLookup +{ +private: + std::string mDesc; + typedef std::map<std::string, MAPPED> Map; + Map mMap; + +public: + StringLookup(const std::string& desc): mDesc(desc) {} + + MAPPED lookup(const typename Map::key_type& key) const + { + typename Map::const_iterator found = mMap.find(key); + if (found == mMap.end()) + { + LL_WARNS("LLWindowListener") << "Unknown " << mDesc << " '" << key << "'" << LL_ENDL; + return MAPPED(); + } + return found->second; + } + +protected: + void add(const typename Map::key_type& key, const typename Map::mapped_type& value) + { + mMap.insert(typename Map::value_type(key, value)); + } +}; + +// for WhichKeysym. KeyProxy is like the typedef KEY, except that KeyProxy() +// (default-constructed) is guaranteed to have the value KEY_NONE. +class KeyProxy +{ +public: + KeyProxy(KEY k): mKey(k) {} + KeyProxy(): mKey(KEY_NONE) {} + operator KEY() const { return mKey; } + +private: + KEY mKey; +}; + +struct WhichKeysym: public StringLookup<KeyProxy> +{ + WhichKeysym(): StringLookup<KeyProxy>("keysym") + { + add("RETURN", KEY_RETURN); + add("LEFT", KEY_LEFT); + add("RIGHT", KEY_RIGHT); + add("UP", KEY_UP); + add("DOWN", KEY_DOWN); + add("ESCAPE", KEY_ESCAPE); + add("BACKSPACE", KEY_BACKSPACE); + add("DELETE", KEY_DELETE); + add("SHIFT", KEY_SHIFT); + add("CONTROL", KEY_CONTROL); + add("ALT", KEY_ALT); + add("HOME", KEY_HOME); + add("END", KEY_END); + add("PAGE_UP", KEY_PAGE_UP); + add("PAGE_DOWN", KEY_PAGE_DOWN); + add("HYPHEN", KEY_HYPHEN); + add("EQUALS", KEY_EQUALS); + add("INSERT", KEY_INSERT); + add("CAPSLOCK", KEY_CAPSLOCK); + add("TAB", KEY_TAB); + add("ADD", KEY_ADD); + add("SUBTRACT", KEY_SUBTRACT); + add("MULTIPLY", KEY_MULTIPLY); + add("DIVIDE", KEY_DIVIDE); + add("F1", KEY_F1); + add("F2", KEY_F2); + add("F3", KEY_F3); + add("F4", KEY_F4); + add("F5", KEY_F5); + add("F6", KEY_F6); + add("F7", KEY_F7); + add("F8", KEY_F8); + add("F9", KEY_F9); + add("F10", KEY_F10); + add("F11", KEY_F11); + add("F12", KEY_F12); + + add("PAD_UP", KEY_PAD_UP); + add("PAD_DOWN", KEY_PAD_DOWN); + add("PAD_LEFT", KEY_PAD_LEFT); + add("PAD_RIGHT", KEY_PAD_RIGHT); + add("PAD_HOME", KEY_PAD_HOME); + add("PAD_END", KEY_PAD_END); + add("PAD_PGUP", KEY_PAD_PGUP); + add("PAD_PGDN", KEY_PAD_PGDN); + add("PAD_CENTER", KEY_PAD_CENTER); // the 5 in the middle + add("PAD_INS", KEY_PAD_INS); + add("PAD_DEL", KEY_PAD_DEL); + add("PAD_RETURN", KEY_PAD_RETURN); + add("PAD_ADD", KEY_PAD_ADD); // not used + add("PAD_SUBTRACT", KEY_PAD_SUBTRACT); // not used + add("PAD_MULTIPLY", KEY_PAD_MULTIPLY); // not used + add("PAD_DIVIDE", KEY_PAD_DIVIDE); // not used + + add("BUTTON0", KEY_BUTTON0); + add("BUTTON1", KEY_BUTTON1); + add("BUTTON2", KEY_BUTTON2); + add("BUTTON3", KEY_BUTTON3); + add("BUTTON4", KEY_BUTTON4); + add("BUTTON5", KEY_BUTTON5); + add("BUTTON6", KEY_BUTTON6); + add("BUTTON7", KEY_BUTTON7); + add("BUTTON8", KEY_BUTTON8); + add("BUTTON9", KEY_BUTTON9); + add("BUTTON10", KEY_BUTTON10); + add("BUTTON11", KEY_BUTTON11); + add("BUTTON12", KEY_BUTTON12); + add("BUTTON13", KEY_BUTTON13); + add("BUTTON14", KEY_BUTTON14); + add("BUTTON15", KEY_BUTTON15); + } +}; +static WhichKeysym keysyms; + +struct WhichMask: public StringLookup<MASK> +{ + WhichMask(): StringLookup<MASK>("shift mask") + { + add("NONE", MASK_NONE); + add("CONTROL", MASK_CONTROL); // Mapped to cmd on Macs + add("ALT", MASK_ALT); + add("SHIFT", MASK_SHIFT); + add("MAC_CONTROL", MASK_MAC_CONTROL); // Un-mapped Ctrl key on Macs, not used on Windows + } +}; +static WhichMask masks; + +static MASK getMask(const LLSD& event) +{ + MASK mask(MASK_NONE); + LLSD masknames(event["mask"]); + for (LLSD::array_const_iterator ai(masknames.beginArray()), aend(masknames.endArray()); + ai != aend; ++ai) + { + mask |= masks.lookup(*ai); + } + return mask; +} + +static KEY getKEY(const LLSD& event) +{ + if (event.has("keysym")) + { + return keysyms.lookup(event["keysym"]); + } + else if (event.has("keycode")) + { + return KEY(event["keycode"].asInteger()); + } + else + { + return KEY(event["char"].asString()[0]); + } +} + +void LLWindowListener::keyDown(LLSD const & evt) +{ + mKbGetter()->handleTranslatedKeyDown(getKEY(evt), getMask(evt)); +} + +void LLWindowListener::keyUp(LLSD const & evt) +{ + mKbGetter()->handleTranslatedKeyUp(getKEY(evt), getMask(evt)); +} + +// for WhichButton +typedef BOOL (LLWindowCallbacks::*MouseFunc)(LLWindow *, LLCoordGL, MASK); +struct Actions +{ + Actions(const MouseFunc& d, const MouseFunc& u): down(d), up(u), valid(true) {} + Actions(): valid(false) {} + MouseFunc down, up; + bool valid; +}; + +struct WhichButton: public StringLookup<Actions> +{ + WhichButton(): StringLookup<Actions>("mouse button") + { + add("LEFT", Actions(&LLWindowCallbacks::handleMouseDown, + &LLWindowCallbacks::handleMouseUp)); + add("RIGHT", Actions(&LLWindowCallbacks::handleRightMouseDown, + &LLWindowCallbacks::handleRightMouseUp)); + add("MIDDLE", Actions(&LLWindowCallbacks::handleMiddleMouseDown, + &LLWindowCallbacks::handleMiddleMouseUp)); + } +}; +static WhichButton buttons; + +static LLCoordGL getPos(const LLSD& event) +{ + return LLCoordGL(event["x"].asInteger(), event["y"].asInteger()); +} + +void LLWindowListener::mouseDown(LLSD const & evt) +{ + Actions actions(buttons.lookup(evt["button"])); + if (actions.valid) + { + (mWindow->*(actions.down))(NULL, getPos(evt), getMask(evt)); + } +} + +void LLWindowListener::mouseUp(LLSD const & evt) +{ + Actions actions(buttons.lookup(evt["button"])); + if (actions.valid) + { + (mWindow->*(actions.up))(NULL, getPos(evt), getMask(evt)); + } +} + +void LLWindowListener::mouseMove(LLSD const & evt) +{ + mWindow->handleMouseMove(NULL, getPos(evt), getMask(evt)); +} + +void LLWindowListener::mouseScroll(LLSD const & evt) +{ + S32 clicks = evt["clicks"].asInteger(); + + mWindow->handleScrollWheel(NULL, clicks); +} + diff --git a/indra/llwindow/llwindowlistener.h b/indra/llwindow/llwindowlistener.h new file mode 100644 index 0000000000..74e577ff93 --- /dev/null +++ b/indra/llwindow/llwindowlistener.h @@ -0,0 +1,55 @@ +/** + * @file llwindowlistener.h + * @brief EventAPI interface for injecting input into LLWindow + * + * $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$ + */ + +#ifndef LL_LLWINDOWLISTENER_H +#define LL_LLWINDOWLISTENER_H + +#include "lleventapi.h" +#include <boost/function.hpp> + +class LLKeyboard; +class LLWindowCallbacks; + +class LLWindowListener : public LLEventAPI +{ +public: + typedef boost::function<LLKeyboard*()> KeyboardGetter; + LLWindowListener(LLWindowCallbacks * window, const KeyboardGetter& kbgetter); + + void keyDown(LLSD const & evt); + void keyUp(LLSD const & evt); + void mouseDown(LLSD const & evt); + void mouseUp(LLSD const & evt); + void mouseMove(LLSD const & evt); + void mouseScroll(LLSD const & evt); + +private: + LLWindowCallbacks * mWindow; + KeyboardGetter mKbGetter; +}; + + +#endif // LL_LLWINDOWLISTENER_H diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 6722248c23..551d487cc8 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -500,7 +500,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, //----------------------------------------------------------------------- DEVMODE dev_mode; - ::ZeroMemory(&dev_mode, sizeof(DEVMODE));
+ ::ZeroMemory(&dev_mode, sizeof(DEVMODE)); dev_mode.dmSize = sizeof(DEVMODE); DWORD current_refresh; if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode)) @@ -880,7 +880,7 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO { GLuint pixel_format; DEVMODE dev_mode; - ::ZeroMemory(&dev_mode, sizeof(DEVMODE));
+ ::ZeroMemory(&dev_mode, sizeof(DEVMODE)); dev_mode.dmSize = sizeof(DEVMODE); DWORD current_refresh; DWORD dw_ex_style; @@ -2715,7 +2715,7 @@ LLWindow::LLWindowResolution* LLWindowWin32::getSupportedResolutions(S32 &num_re { mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS]; DEVMODE dev_mode; - ::ZeroMemory(&dev_mode, sizeof(DEVMODE));
+ ::ZeroMemory(&dev_mode, sizeof(DEVMODE)); dev_mode.dmSize = sizeof(DEVMODE); mNumSupportedResolutions = 0; @@ -2792,7 +2792,7 @@ F32 LLWindowWin32::getPixelAspectRatio() BOOL LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh) { DEVMODE dev_mode; - ::ZeroMemory(&dev_mode, sizeof(DEVMODE));
+ ::ZeroMemory(&dev_mode, sizeof(DEVMODE)); dev_mode.dmSize = sizeof(DEVMODE); BOOL success = FALSE; diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp index e24ee6629e..9ba8edbb59 100644 --- a/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp @@ -492,6 +492,15 @@ private: //////////////////////////////////////////////////////////////////////////////// // virtual + void onNavigateErrorPage(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_error_page"); + message.setValueS32("status_code", event.getIntValue()); + sendMessage(message); + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual void onLocationChange(const EventType& event) { if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE) @@ -519,6 +528,11 @@ private: { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_nofollow"); message.setValue("uri", event.getEventUri()); +#if LLQTWEBKIT_API_VERSION >= 7 + message.setValue("nav_type", event.getNavigationType()); +#else + message.setValue("nav_type", "clicked"); +#endif sendMessage(message); } diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 32d3a31786..b1cb10665b 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -416,6 +416,7 @@ set(viewer_SOURCE_FILES llsidepaneliteminfo.cpp llsidepaneltaskinfo.cpp llsidetray.cpp + llsidetraylistener.cpp llsidetraypanelcontainer.cpp llsky.cpp llslurl.cpp @@ -954,6 +955,7 @@ set(viewer_HEADER_FILES llsidepaneliteminfo.h llsidepaneltaskinfo.h llsidetray.h + llsidetraylistener.h llsidetraypanelcontainer.h llsky.h llslurl.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index dd85c5cb86..d98f0da1c2 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2623,10 +2623,10 @@ <key>Value</key> <integer>0</integer> </map> - <key>DisableRendering</key> + <key>HeadlessClient</key> <map> <key>Comment</key> - <string>Disable GL rendering and GUI (load testing)</string> + <string>Run in headless mode by disabling GL rendering, keyboard, etc</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -3232,14 +3232,14 @@ <map> <key>Comment</key> <string>Specifies that you have not logged in with the viewer since you performed a clean install</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>Boolean</string> - <key>Value</key> - <integer>1</integer> - </map> - <key>FirstSelectedDisabledPopups</key> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>FirstSelectedDisabledPopups</key> <map> <key>Comment</key> <string>Return false if there is not disabled popup selected in the list of floater preferences popups</string> @@ -9557,7 +9557,7 @@ <key>ShowNetStats</key> <map> <key>Comment</key> - <string>Show the Search Bar in the Status Overlay</string> + <string>Show the Status Indicators for the Viewer and Network Usage in the Status Overlay</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -11926,6 +11926,33 @@ <key>Value</key> <integer>0</integer> </map> + <key>WaterFogColor</key> + <map> + <key>Comment</key> + <string>Water fog color</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Color4</string> + <key>Value</key> + <array> + <real>0.0863</real> + <real>0.168</real> + <real>0.212</real> + <real>0</real> + </array> + </map> + <key>WaterFogDensity</key> + <map> + <key>Comment</key> + <string>Water fog density</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>16.0</real> + </map> <key>WaterGLFogDensityScale</key> <map> <key>Comment</key> @@ -12641,5 +12668,27 @@ <key>Value</key> <integer>1</integer> </map> + <key>GenericErrorPageURL</key> + <map> + <key>Comment</key> + <string>URL to set as a property on LLMediaControl to navigate to if the a page completes with a 400-499 HTTP status code</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>http://common-flash-secondlife-com.s3.amazonaws.com/viewer/v2.6/agni/404.html</string> + </map> + <key>DestinationsAndAvatarsVisibility</key> + <map> + <key>Comment</key> + <string>Whether destination panel or avatar picker are open (0=destination guide, 1=avatar picker, default=nothing)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>0</integer> + </map> </map> </llsd> diff --git a/indra/newview/app_settings/settings_minimal.xml b/indra/newview/app_settings/settings_minimal.xml index 03656f2a53..490da2c9d4 100644 --- a/indra/newview/app_settings/settings_minimal.xml +++ b/indra/newview/app_settings/settings_minimal.xml @@ -61,7 +61,7 @@ <key>Type</key> <string>String</string> <key>Value</key> - <string>http://common-flash-secondlife-com.s3.amazonaws.com/viewer/v2.6/damballah/howto/index.html?topic=[TOPIC]</string> + <string>http://common-flash-secondlife-com.s3.amazonaws.com/viewer/v2.6/agni/howto/index.html?topic=[TOPIC]</string> </map> <key>PreferredMaturity</key> <map> @@ -345,7 +345,7 @@ <key>Type</key> <string>String</string> <key>Value</key> - <string>http://common-flash-secondlife-com.s3.amazonaws.com/viewer/v2.6/damballah/guide.html</string> + <string>http://common-flash-secondlife-com.s3.amazonaws.com/viewer/v2.6/agni/guide.html</string> </map> <key>AvatarPickerURL</key> <map> @@ -356,7 +356,7 @@ <key>Type</key> <string>String</string> <key>Value</key> - <string>http://common-flash-secondlife-com.s3.amazonaws.com/viewer/v2.6/damballah/avatars.html</string> + <string>http://common-flash-secondlife-com.s3.amazonaws.com/viewer/v2.6/agni/avatars.html</string> </map> <key>LogInventoryDecline</key> <map> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 7d908df5ce..7d491a7774 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -1119,12 +1119,6 @@ void LLAgent::resetControlFlags() //----------------------------------------------------------------------------- void LLAgent::setAFK() { - // Drones can't go AFK - if (gNoRender) - { - return; - } - if (!gAgent.getRegion()) { // Don't set AFK if we're not talking to a region yet. @@ -1684,11 +1678,6 @@ void LLAgent::clearRenderState(U8 clearstate) //----------------------------------------------------------------------------- U8 LLAgent::getRenderState() { - if (gNoRender || gKeyboard == NULL) - { - return 0; - } - // *FIX: don't do stuff in a getter! This is infinite loop city! if ((mTypingTimer.getElapsedTimeF32() > TYPING_TIMEOUT_SECS) && (mRenderState & AGENT_STATE_TYPING)) diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index f01d5ff1f5..6c5c3bcdab 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -282,25 +282,22 @@ void LLAgentCamera::resetView(BOOL reset_camera, BOOL change_camera) gAgent.stopAutoPilot(TRUE); } - if (!gNoRender) - { - LLSelectMgr::getInstance()->unhighlightAll(); + LLSelectMgr::getInstance()->unhighlightAll(); - // By popular request, keep land selection while walking around. JC - // LLViewerParcelMgr::getInstance()->deselectLand(); + // By popular request, keep land selection while walking around. JC + // LLViewerParcelMgr::getInstance()->deselectLand(); - // force deselect when walking and attachment is selected - // this is so people don't wig out when their avatar moves without animating - if (LLSelectMgr::getInstance()->getSelection()->isAttachment()) - { - LLSelectMgr::getInstance()->deselectAll(); - } + // force deselect when walking and attachment is selected + // this is so people don't wig out when their avatar moves without animating + if (LLSelectMgr::getInstance()->getSelection()->isAttachment()) + { + LLSelectMgr::getInstance()->deselectAll(); + } - if (gMenuHolder != NULL) - { - // Hide all popup menus - gMenuHolder->hideMenus(); - } + if (gMenuHolder != NULL) + { + // Hide all popup menus + gMenuHolder->hideMenus(); } if (change_camera && !gSavedSettings.getBOOL("FreezeTime")) diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index d520debc31..ed24febf41 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -37,6 +37,8 @@ #include "llviewerobject.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" +#include "llsdutil.h" +#include "llsdutil_math.h" LLAgentListener::LLAgentListener(LLAgent &agent) : LLEventAPI("LLAgent", @@ -53,6 +55,15 @@ LLAgentListener::LLAgentListener(LLAgent &agent) add("requestStand", "Ask to stand up", &LLAgentListener::requestStand); + add("resetAxes", + "Set the agent to a fixed orientation (optionally specify [\"lookat\"] = array of [x, y, z])", + &LLAgentListener::resetAxes); + add("getAxes", + "Send information about the agent's orientation on [\"reply\"]:\n" + "[\"euler\"]: map of {roll, pitch, yaw}\n" + "[\"quat\"]: array of [x, y, z, w] quaternion values", + &LLAgentListener::getAxes, + LLSDMap("reply", LLSD())); } void LLAgentListener::requestTeleport(LLSD const & event_data) const @@ -64,7 +75,7 @@ void LLAgentListener::requestTeleport(LLSD const & event_data) const params.append(event_data["x"]); params.append(event_data["y"]); params.append(event_data["z"]); - LLCommandDispatcher::dispatch("teleport", params, LLSD(), NULL, true); + LLCommandDispatcher::dispatch("teleport", params, LLSD(), NULL, "clicked", true); // *TODO - lookup other LLCommandHandlers for "agent", "classified", "event", "group", "floater", "parcel", "login", login_refresh", "balance", "chat" // should we just compose LLCommandHandler and LLDispatchListener? } @@ -74,7 +85,7 @@ void LLAgentListener::requestTeleport(LLSD const & event_data) const LLVector3(event_data["x"].asReal(), event_data["y"].asReal(), event_data["z"].asReal())).getSLURLString(); - LLURLDispatcher::dispatch(url, NULL, false); + LLURLDispatcher::dispatch(url, "clicked", NULL, false); } } @@ -104,3 +115,28 @@ void LLAgentListener::requestStand(LLSD const & event_data) const mAgent.setControlFlags(AGENT_CONTROL_STAND_UP); } +void LLAgentListener::resetAxes(const LLSD& event) const +{ + if (event.has("lookat")) + { + mAgent.resetAxes(ll_vector3_from_sd(event["lookat"])); + } + else + { + // no "lookat", default call + mAgent.resetAxes(); + } +} + +void LLAgentListener::getAxes(const LLSD& event) const +{ + LLQuaternion quat(mAgent.getQuat()); + F32 roll, pitch, yaw; + quat.getEulerAngles(&roll, &pitch, &yaw); + // The official query API for LLQuaternion's [x, y, z, w] values is its + // public member mQ... + sendReply(LLSDMap + ("quat", llsd_copy_array(boost::begin(quat.mQ), boost::end(quat.mQ))) + ("euler", LLSDMap("roll", roll)("pitch", pitch)("yaw", yaw)), + event); +} diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h index 9b585152f4..0aa58d0b16 100644 --- a/indra/newview/llagentlistener.h +++ b/indra/newview/llagentlistener.h @@ -44,6 +44,8 @@ private: void requestTeleport(LLSD const & event_data) const; void requestSit(LLSD const & event_data) const; void requestStand(LLSD const & event_data) const; + void resetAxes(const LLSD& event) const; + void getAxes(const LLSD& event) const; private: LLAgent & mAgent; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index cfb5853cfd..061ef7268e 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -78,7 +78,6 @@ #include "llvoicechannel.h" #include "llvoavatarself.h" #include "llsidetray.h" -#include "llfeaturemanager.h" #include "llurlmatch.h" #include "lltextutil.h" #include "lllogininstance.h" @@ -756,7 +755,7 @@ bool LLAppViewer::init() // // Various introspection concerning the libs we're using - particularly - // the libs involved in getting to a full login screen. + // the libs involved in getting to a full login screen. // LL_INFOS("InitInfo") << "J2C Engine is: " << LLImageJ2C::getEngineInfo() << LL_ENDL; LL_INFOS("InitInfo") << "libcurl version is: " << LLCurl::getVersionString() << LL_ENDL; @@ -925,7 +924,7 @@ bool LLAppViewer::init() } // If we don't have the right GL requirements, exit. - if (!gGLManager.mHasRequirements && !gNoRender) + if (!gGLManager.mHasRequirements) { // can't use an alert here since we're exiting and // all hell breaks lose. @@ -1215,7 +1214,8 @@ bool LLAppViewer::mainLoop() } // Render scene. - if (!LLApp::isExiting()) + // *TODO: Should we run display() even during gHeadlessClient? DK 2011-02-18 + if (!LLApp::isExiting() && !gHeadlessClient) { pingMainloopTimeout("Main:Display"); gGLActive = TRUE; @@ -1243,8 +1243,7 @@ bool LLAppViewer::mainLoop() } // yield cooperatively when not running as foreground window - if ( gNoRender - || (gViewerWindow && !gViewerWindow->mWindow->getVisible()) + if ( (gViewerWindow && !gViewerWindow->mWindow->getVisible()) || !gFocusMgr.getAppHasFocus()) { // Sleep if we're not rendering, or the window is minimized. @@ -2468,6 +2467,14 @@ bool LLAppViewer::initConfiguration() } } + // If automatic login from command line with --login switch + // init StartSLURL location. In interactive login, LLPanelLogin + // will take care of it. + if ((clp.hasOption("login") || clp.hasOption("autologin")) && !clp.hasOption("url") && !clp.hasOption("slurl")) + { + LLStartUp::setStartSLURL(LLSLURL(gSavedSettings.getString("LoginLocation"))); + } + if (!gSavedSettings.getBOOL("AllowMultipleViewers")) { // @@ -2732,12 +2739,26 @@ void LLAppViewer::checkForCrash(void) } +// +// This function decides whether the client machine meets the minimum requirements to +// run in a maximized window, per the consensus of davep, boa and nyx on 3/30/2011. +// +bool LLAppViewer::meetsRequirementsForMaximizedStart() +{ + bool maximizedOk = (LLFeatureManager::getInstance()->getGPUClass() >= GPU_CLASS_2); + + const U32 one_gigabyte_kb = 1024 * 1024; + maximizedOk &= (gSysMemory.getPhysicalMemoryKB() >= one_gigabyte_kb); + + return maximizedOk; +} + bool LLAppViewer::initWindow() { LL_INFOS("AppInit") << "Initializing window..." << LL_ENDL; // store setting in a global for easy access and modification - gNoRender = gSavedSettings.getBOOL("DisableRendering"); + gHeadlessClient = gSavedSettings.getBOOL("HeadlessClient"); // always start windowed BOOL ignorePixelDepth = gSavedSettings.getBOOL("IgnorePixelDepth"); @@ -2751,7 +2772,8 @@ bool LLAppViewer::initWindow() const S32 NEVER_SUBMIT_REPORT = 2; bool use_watchdog = false; int watchdog_enabled_setting = gSavedSettings.getS32("WatchdogEnabled"); - if(watchdog_enabled_setting == -1){ + if(watchdog_enabled_setting == -1) + { use_watchdog = !LLFeatureManager::getInstance()->isFeatureAvailable("WatchdogDisabled"); } else @@ -2773,28 +2795,25 @@ bool LLAppViewer::initWindow() gViewerWindow->mWindow->maximize(); } - if (!gNoRender) + // + // Initialize GL stuff + // + + if (mForceGraphicsDetail) { - // - // Initialize GL stuff - // + LLFeatureManager::getInstance()->setGraphicsLevel(gSavedSettings.getU32("RenderQualityPerformance"), false); + } + + // Set this flag in case we crash while initializing GL + gSavedSettings.setBOOL("RenderInitError", TRUE); + gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); - if (mForceGraphicsDetail) - { - LLFeatureManager::getInstance()->setGraphicsLevel(gSavedSettings.getU32("RenderQualityPerformance"), false); - } - - // Set this flag in case we crash while initializing GL - gSavedSettings.setBOOL("RenderInitError", TRUE); - gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); - - gPipeline.init(); - stop_glerror(); - gViewerWindow->initGLDefaults(); + gPipeline.init(); + stop_glerror(); + gViewerWindow->initGLDefaults(); - gSavedSettings.setBOOL("RenderInitError", FALSE); - gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); - } + gSavedSettings.setBOOL("RenderInitError", FALSE); + gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); //If we have a startup crash, it's usually near GL initialization, so simulate that. if(gCrashOnStartup) @@ -2802,6 +2821,21 @@ bool LLAppViewer::initWindow() LLAppViewer::instance()->forceErrorLLError(); } + // + // Determine if the window should start maximized on initial run based + // on graphics capability + // + if (gSavedSettings.getBOOL("FirstLoginThisInstall") && meetsRequirementsForMaximizedStart()) + { + LL_INFOS("AppInit") << "This client met the requirements for a maximized initial screen." << LL_ENDL; + gSavedSettings.setBOOL("WindowMaximized", TRUE); + } + + if (gSavedSettings.getBOOL("WindowMaximized")) + { + gViewerWindow->mWindow->maximize(); + } + LLUI::sWindow = gViewerWindow->getWindow(); // Show watch cursor @@ -2836,12 +2870,9 @@ void LLAppViewer::cleanupSavedSettings() gSavedSettings.setBOOL("ShowObjectUpdates", gShowObjectUpdates); - if (!gNoRender) + if (gDebugView) { - if (gDebugView) - { - gSavedSettings.setBOOL("ShowDebugConsole", gDebugView->mDebugConsolep->getVisible()); - } + gSavedSettings.setBOOL("ShowDebugConsole", gDebugView->mDebugConsolep->getVisible()); } // save window position if not maximized @@ -3822,7 +3853,7 @@ void LLAppViewer::badNetworkHandler() // is destroyed. void LLAppViewer::saveFinalSnapshot() { - if (!mSavedFinalSnapshot && !gNoRender) + if (!mSavedFinalSnapshot) { gSavedSettings.setVector3d("FocusPosOnLogout", gAgentCamera.calcFocusPositionTargetGlobal()); gSavedSettings.setVector3d("CameraPosOnLogout", gAgentCamera.calcCameraPositionTargetGlobal()); @@ -4226,34 +4257,31 @@ void LLAppViewer::idle() // // Update weather effects // - if (!gNoRender) - { - LLWorld::getInstance()->updateClouds(gFrameDTClamped); - gSky.propagateHeavenlyBodies(gFrameDTClamped); // moves sun, moon, and planets + LLWorld::getInstance()->updateClouds(gFrameDTClamped); + gSky.propagateHeavenlyBodies(gFrameDTClamped); // moves sun, moon, and planets - // Update wind vector - LLVector3 wind_position_region; - static LLVector3 average_wind; + // Update wind vector + LLVector3 wind_position_region; + static LLVector3 average_wind; - LLViewerRegion *regionp; - regionp = LLWorld::getInstance()->resolveRegionGlobal(wind_position_region, gAgent.getPositionGlobal()); // puts agent's local coords into wind_position - if (regionp) - { - gWindVec = regionp->mWind.getVelocity(wind_position_region); + LLViewerRegion *regionp; + regionp = LLWorld::getInstance()->resolveRegionGlobal(wind_position_region, gAgent.getPositionGlobal()); // puts agent's local coords into wind_position + if (regionp) + { + gWindVec = regionp->mWind.getVelocity(wind_position_region); - // Compute average wind and use to drive motion of water - - average_wind = regionp->mWind.getAverage(); - F32 cloud_density = regionp->mCloudLayer.getDensityRegion(wind_position_region); - - gSky.setCloudDensityAtAgent(cloud_density); - gSky.setWind(average_wind); - //LLVOWater::setWind(average_wind); - } - else - { - gWindVec.setVec(0.0f, 0.0f, 0.0f); - } + // Compute average wind and use to drive motion of water + + average_wind = regionp->mWind.getAverage(); + F32 cloud_density = regionp->mCloudLayer.getDensityRegion(wind_position_region); + + gSky.setCloudDensityAtAgent(cloud_density); + gSky.setWind(average_wind); + //LLVOWater::setWind(average_wind); + } + else + { + gWindVec.setVec(0.0f, 0.0f, 0.0f); } ////////////////////////////////////// @@ -4262,13 +4290,10 @@ void LLAppViewer::idle() // Here, particles are updated and drawables are moved. // - if (!gNoRender) - { - LLFastTimer t(FTM_WORLD_UPDATE); - gPipeline.updateMove(); + LLFastTimer t(FTM_WORLD_UPDATE); + gPipeline.updateMove(); - LLWorld::getInstance()->updateParticles(); - } + LLWorld::getInstance()->updateParticles(); if (LLViewerJoystick::getInstance()->getOverrideCamera()) { @@ -4634,12 +4659,9 @@ void LLAppViewer::disconnectViewer() gSavedSettings.setBOOL("FlyingAtExit", gAgent.getFlying() ); // Un-minimize all windows so they don't get saved minimized - if (!gNoRender) + if (gFloaterView) { - if (gFloaterView) - { - gFloaterView->restoreAll(); - } + gFloaterView->restoreAll(); } if (LLSelectMgr::getInstance()) diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 0226211735..61ee6a7cf1 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -187,6 +187,7 @@ protected: virtual std::string generateSerialNumber() = 0; // Platforms specific classes generate this. + virtual bool meetsRequirementsForMaximizedStart(); // Used on first login to decide to launch maximized private: diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index 898cc1c0ba..523c2e3adf 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -233,7 +233,7 @@ gboolean viewer_app_api_GoSLURL(ViewerAppAPI *obj, gchar *slurl, gboolean **succ std::string url = slurl; LLMediaCtrl* web = NULL; const bool trusted_browser = false; - if (LLURLDispatcher::dispatch(url, web, trusted_browser)) + if (LLURLDispatcher::dispatch(url, "", web, trusted_browser)) { // bring window to foreground, as it has just been "launched" from a URL // todo: hmm, how to get there from here? diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index 1cd80986d8..c2916717bd 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -500,7 +500,7 @@ OSErr AEGURLHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn) LLMediaCtrl* web = NULL; const bool trusted_browser = false; - LLURLDispatcher::dispatch(url, web, trusted_browser); + LLURLDispatcher::dispatch(url, "", web, trusted_browser); } return(result); diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index afa8b62c74..ca7ec7cc30 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -279,7 +279,7 @@ bool LLAvatarActions::isCalling(const LLUUID &id) //static bool LLAvatarActions::canCall() { - return LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); + return LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); } // static @@ -300,40 +300,21 @@ void LLAvatarActions::startConference(const uuid_vec_t& ids) make_ui_sound("UISndStartIM"); } +static void on_avatar_name_show_profile(const LLUUID& agent_id, const LLAvatarName& av_name) +{ + llinfos << "opening web profile for " << av_name.mUsername << llendl; + std::string url = getProfileURL(av_name.mUsername); + + // PROFILES: open in webkit window + LLWeb::loadWebURLInternal(url, "", agent_id.asString()); +} + // static void LLAvatarActions::showProfile(const LLUUID& id) { if (id.notNull()) { - LLSD params; - params["id"] = id; - params["open_tab_name"] = "panel_profile"; - - // PROFILES: open in webkit window - std::string full_name; - if (gCacheName->getFullName(id,full_name)) - { - std::string agent_name = LLCacheName::buildUsername(full_name); - llinfos << "opening web profile for " << agent_name << llendl; - std::string url = getProfileURL(agent_name); - LLWeb::loadWebURLInternal(url, "", id.asString()); - } - else - { - llwarns << "no name info for agent id " << id << llendl; - } -#if 0 - //Show own profile - if(gAgent.getID() == id) - { - LLSideTray::getInstance()->showPanel("panel_me", params); - } - //Show other user profile - else - { - LLSideTray::getInstance()->showPanel("panel_profile_view", params); - } -#endif + LLAvatarNameCache::get(id, boost::bind(&on_avatar_name_show_profile, _1, _2)); } } diff --git a/indra/newview/llavatarpropertiesprocessor.cpp b/indra/newview/llavatarpropertiesprocessor.cpp index 99aeb4cbad..b1cd83a1fb 100644 --- a/indra/newview/llavatarpropertiesprocessor.cpp +++ b/indra/newview/llavatarpropertiesprocessor.cpp @@ -160,6 +160,12 @@ void LLAvatarPropertiesProcessor::sendAvatarClassifiedsRequest(const LLUUID& ava void LLAvatarPropertiesProcessor::sendAvatarPropertiesUpdate(const LLAvatarData* avatar_props) { + if (!gAgent.isInitialized() || (gAgent.getID() == LLUUID::null)) + { + llwarns << "Sending avatarinfo update DENIED - invalid agent" << llendl; + return; + } + llinfos << "Sending avatarinfo update" << llendl; // This value is required by sendAvatarPropertiesUpdate method. @@ -168,20 +174,21 @@ void LLAvatarPropertiesProcessor::sendAvatarPropertiesUpdate(const LLAvatarData* LLMessageSystem *msg = gMessageSystem; - msg->newMessageFast(_PREHASH_AvatarPropertiesUpdate); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast( _PREHASH_AgentID, gAgent.getID() ); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); - msg->nextBlockFast(_PREHASH_PropertiesData); + msg->newMessageFast (_PREHASH_AvatarPropertiesUpdate); + msg->nextBlockFast (_PREHASH_AgentData); + msg->addUUIDFast (_PREHASH_AgentID, gAgent.getID() ); + msg->addUUIDFast (_PREHASH_SessionID, gAgent.getSessionID() ); + msg->nextBlockFast (_PREHASH_PropertiesData); - msg->addUUIDFast( _PREHASH_ImageID, avatar_props->image_id); - msg->addUUIDFast( _PREHASH_FLImageID, avatar_props->fl_image_id); - msg->addStringFast( _PREHASH_AboutText, avatar_props->about_text); - msg->addStringFast( _PREHASH_FLAboutText, avatar_props->fl_about_text); + msg->addUUIDFast (_PREHASH_ImageID, avatar_props->image_id); + msg->addUUIDFast (_PREHASH_FLImageID, avatar_props->fl_image_id); + msg->addStringFast (_PREHASH_AboutText, avatar_props->about_text); + msg->addStringFast (_PREHASH_FLAboutText, avatar_props->fl_about_text); msg->addBOOL(_PREHASH_AllowPublish, avatar_props->allow_publish); msg->addBOOL(_PREHASH_MaturePublish, mature); msg->addString(_PREHASH_ProfileURL, avatar_props->profile_url); + gAgent.sendReliableMessage(); } diff --git a/indra/newview/llbreastmotion.cpp b/indra/newview/llbreastmotion.cpp index 7c205a8b9f..9a8cd5ceae 100644 --- a/indra/newview/llbreastmotion.cpp +++ b/indra/newview/llbreastmotion.cpp @@ -2,31 +2,25 @@ * @file llbreastmotion.cpp * @brief Implementation of LLBreastMotion class. * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2011, 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/newview/llbreastmotion.h b/indra/newview/llbreastmotion.h index 8578d4ad1a..aa0fdf9f8b 100644 --- a/indra/newview/llbreastmotion.h +++ b/indra/newview/llbreastmotion.h @@ -2,31 +2,25 @@ * @file llbreastmotion.h * @brief Implementation of LLBreastMotion class. * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2011, 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/newview/llcommanddispatcherlistener.cpp b/indra/newview/llcommanddispatcherlistener.cpp index 5744d055a9..586f45fd8f 100644 --- a/indra/newview/llcommanddispatcherlistener.cpp +++ b/indra/newview/llcommanddispatcherlistener.cpp @@ -65,7 +65,7 @@ void LLCommandDispatcherListener::dispatch(const LLSD& params) const trusted_browser = params["trusted"].asBoolean(); } LLCommandDispatcher::dispatch(params["cmd"], params["params"], params["query"], NULL, - trusted_browser); + "clicked", trusted_browser); } void LLCommandDispatcherListener::enumerate(const LLSD& params) const diff --git a/indra/newview/llcommandhandler.cpp b/indra/newview/llcommandhandler.cpp index 1b6ba02aac..19dba3f917 100644 --- a/indra/newview/llcommandhandler.cpp +++ b/indra/newview/llcommandhandler.cpp @@ -35,7 +35,7 @@ // system includes #include <boost/tokenizer.hpp> -#define THROTTLE_PERIOD 5 // required secs between throttled commands +#define THROTTLE_PERIOD 5 // required seconds between throttled commands static LLCommandDispatcherListener sCommandDispatcherListener; @@ -59,6 +59,7 @@ public: const LLSD& params, const LLSD& query_map, LLMediaCtrl* web, + const std::string& nav_type, bool trusted_browser); private: @@ -91,6 +92,7 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, LLMediaCtrl* web, + const std::string& nav_type, bool trusted_browser) { static bool slurl_blocked = false; @@ -120,11 +122,19 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, return true; case LLCommandHandler::UNTRUSTED_THROTTLE: + // if users actually click on a link, we don't need to throttle it + // (throttling mechanism is used to prevent an avalanche of clicks via + // javascript + if ( nav_type == "clicked" ) + { + break; + } + cur_time = LLTimer::getElapsedSeconds(); if (cur_time < last_throttle_time + THROTTLE_PERIOD) { // block request from external browser if it happened - // within THROTTLE_PERIOD secs of the last command + // within THROTTLE_PERIOD seconds of the last command LL_WARNS_ONCE("SLURL") << "Throttled SLURL command from untrusted browser" << LL_ENDL; if (! slurl_throttled) { @@ -166,10 +176,11 @@ bool LLCommandDispatcher::dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, LLMediaCtrl* web, + const std::string& nav_type, bool trusted_browser) { return LLCommandHandlerRegistry::instance().dispatch( - cmd, params, query_map, web, trusted_browser); + cmd, params, query_map, web, nav_type, trusted_browser); } static std::string lookup(LLCommandHandler::EUntrustedAccess value); diff --git a/indra/newview/llcommandhandler.h b/indra/newview/llcommandhandler.h index 1e65b6de23..1e0895565a 100644 --- a/indra/newview/llcommandhandler.h +++ b/indra/newview/llcommandhandler.h @@ -95,6 +95,7 @@ public: const LLSD& params, const LLSD& query_map, LLMediaCtrl* web, + const std::string& nav_type, bool trusted_browser); // Execute a command registered via the above mechanism, // passing string parameters. diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp index 5e10f60aba..5501b8c2ac 100644 --- a/indra/newview/llexpandabletextbox.cpp +++ b/indra/newview/llexpandabletextbox.cpp @@ -123,10 +123,7 @@ void LLExpandableTextBox::LLTextBoxEx::reshape(S32 width, S32 height, BOOL calle { LLTextEditor::reshape(width, height, called_from_parent); - if (getTextPixelHeight() > getRect().getHeight()) - { - showExpandText(); - } + hideOrShowExpandTextAsNeeded(); } void LLExpandableTextBox::LLTextBoxEx::setText(const LLStringExplicit& text,const LLStyle::Params& input_params) @@ -136,17 +133,7 @@ void LLExpandableTextBox::LLTextBoxEx::setText(const LLStringExplicit& text,cons mExpanderVisible = false; LLTextEditor::setText(text, input_params); - // text contents have changed, segments are cleared out - // so hide the expander and determine if we need it - //mExpanderVisible = false; - if (getTextPixelHeight() > getRect().getHeight()) - { - showExpandText(); - } - else - { - hideExpandText(); - } + hideOrShowExpandTextAsNeeded(); } @@ -200,6 +187,22 @@ S32 LLExpandableTextBox::LLTextBoxEx::getTextPixelHeight() return getTextBoundingRect().getHeight(); } +void LLExpandableTextBox::LLTextBoxEx::hideOrShowExpandTextAsNeeded() +{ + // Restore the text box contents to calculate the text height properly, + // otherwise if a part of the text is hidden under "More" link + // getTextPixelHeight() returns only the height of currently visible text + // including the "More" link. See STORM-250. + hideExpandText(); + + // Show the expander a.k.a. "More" link if we need it, depending on text + // contents height. If not, keep it hidden. + if (getTextPixelHeight() > getRect().getHeight()) + { + showExpandText(); + } +} + ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llexpandabletextbox.h b/indra/newview/llexpandabletextbox.h index bce77225c4..f75ef954ff 100644 --- a/indra/newview/llexpandabletextbox.h +++ b/indra/newview/llexpandabletextbox.h @@ -77,6 +77,12 @@ protected: */ void hideExpandText(); + /** + * Shows the "More" link if the text is too high to be completely + * visible without expanding the text box. Hides that link otherwise. + */ + void hideOrShowExpandTextAsNeeded(); + protected: LLTextBoxEx(const Params& p); diff --git a/indra/newview/llfirstuse.cpp b/indra/newview/llfirstuse.cpp index d63685e1af..2c4153688a 100644 --- a/indra/newview/llfirstuse.cpp +++ b/indra/newview/llfirstuse.cpp @@ -121,7 +121,11 @@ void LLFirstUse::notMoving(bool enable) { // fire off 2 notifications and rely on filtering to select the relevant one firstUseNotification("FirstNotMoving", enable, "HintMove", LLSD(), LLSD().with("target", "move_btn").with("direction", "top")); - firstUseNotification("FirstNotMoving", enable, "HintMoveArrows", LLSD(), LLSD().with("target", "bottom_tray").with("direction", "top").with("hint_image", "arrow_keys.png").with("down_arrow", "")); + firstUseNotification("FirstNotMoving", enable, "HintMoveClick", LLSD(), LLSD() + .with("target", "nav_bar") + .with("direction", "bottom") + .with("hint_image", "click_to_move.png") + .with("up_arrow", "")); } // static diff --git a/indra/newview/llfloaterbump.cpp b/indra/newview/llfloaterbump.cpp index 61cf4dad93..eeb81085bb 100644 --- a/indra/newview/llfloaterbump.cpp +++ b/indra/newview/llfloaterbump.cpp @@ -38,13 +38,11 @@ ///---------------------------------------------------------------------------- /// Class LLFloaterBump ///---------------------------------------------------------------------------- -extern BOOL gNoRender; // Default constructor LLFloaterBump::LLFloaterBump(const LLSD& key) : LLFloater(key) { - if(gNoRender) return; } diff --git a/indra/newview/llfloaterhelpbrowser.cpp b/indra/newview/llfloaterhelpbrowser.cpp index a650886d89..627defd006 100644 --- a/indra/newview/llfloaterhelpbrowser.cpp +++ b/indra/newview/llfloaterhelpbrowser.cpp @@ -50,6 +50,7 @@ BOOL LLFloaterHelpBrowser::postBuild() { mBrowser = getChild<LLMediaCtrl>("browser"); mBrowser->addObserver(this); + mBrowser->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); childSetAction("open_browser", onClickOpenWebBrowser, this); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index ffbb0efad3..1a9d0af9af 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -285,6 +285,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mGotPersonalInfo(false), mOriginalIMViaEmail(false), mLanguageChanged(false), + mAvatarDataInitialized(false), mDoubleClickActionDirty(false), mFavoritesRecordMayExist(false) { @@ -343,7 +344,7 @@ void LLFloaterPreference::processProperties( void* pData, EAvatarProcessorType t if ( APT_PROPERTIES == type ) { const LLAvatarData* pAvatarData = static_cast<const LLAvatarData*>( pData ); - if( pAvatarData && gAgent.getID() == pAvatarData->avatar_id ) + if (pAvatarData && (gAgent.getID() == pAvatarData->avatar_id) && (pAvatarData->avatar_id != LLUUID::null)) { storeAvatarProperties( pAvatarData ); processProfileProperties( pAvatarData ); @@ -353,14 +354,19 @@ void LLFloaterPreference::processProperties( void* pData, EAvatarProcessorType t void LLFloaterPreference::storeAvatarProperties( const LLAvatarData* pAvatarData ) { - mAvatarProperties.avatar_id = gAgent.getID(); - mAvatarProperties.image_id = pAvatarData->image_id; - mAvatarProperties.fl_image_id = pAvatarData->fl_image_id; - mAvatarProperties.about_text = pAvatarData->about_text; - mAvatarProperties.fl_about_text = pAvatarData->fl_about_text; - mAvatarProperties.profile_url = pAvatarData->profile_url; - mAvatarProperties.flags = pAvatarData->flags; - mAvatarProperties.allow_publish = pAvatarData->flags & AVATAR_ALLOW_PUBLISH; + if (LLStartUp::getStartupState() == STATE_STARTED) + { + mAvatarProperties.avatar_id = pAvatarData->avatar_id; + mAvatarProperties.image_id = pAvatarData->image_id; + mAvatarProperties.fl_image_id = pAvatarData->fl_image_id; + mAvatarProperties.about_text = pAvatarData->about_text; + mAvatarProperties.fl_about_text = pAvatarData->fl_about_text; + mAvatarProperties.profile_url = pAvatarData->profile_url; + mAvatarProperties.flags = pAvatarData->flags; + mAvatarProperties.allow_publish = pAvatarData->flags & AVATAR_ALLOW_PUBLISH; + + mAvatarDataInitialized = true; + } } void LLFloaterPreference::processProfileProperties(const LLAvatarData* pAvatarData ) @@ -370,15 +376,31 @@ void LLFloaterPreference::processProfileProperties(const LLAvatarData* pAvatarDa void LLFloaterPreference::saveAvatarProperties( void ) { - mAvatarProperties.allow_publish = getChild<LLUICtrl>("online_searchresults")->getValue(); - if ( mAvatarProperties.allow_publish ) + const BOOL allowPublish = getChild<LLUICtrl>("online_searchresults")->getValue(); + + if (allowPublish) { mAvatarProperties.flags |= AVATAR_ALLOW_PUBLISH; } - - LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesUpdate( &mAvatarProperties ); -} + // + // NOTE: We really don't want to send the avatar properties unless we absolutely + // need to so we can avoid the accidental profile reset bug, so, if we're + // logged in, the avatar data has been initialized and we have a state change + // for the "allow publish" flag, then set the flag to its new value and send + // the properties update. + // + // NOTE: The only reason we can not remove this update altogether is because of the + // "allow publish" flag, the last remaining profile setting in the viewer + // that doesn't exist in the web profile. + // + if ((LLStartUp::getStartupState() == STATE_STARTED) && mAvatarDataInitialized && (allowPublish != mAvatarProperties.allow_publish)) + { + mAvatarProperties.allow_publish = allowPublish; + + LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesUpdate( &mAvatarProperties ); + } +} BOOL LLFloaterPreference::postBuild() { @@ -1355,6 +1377,8 @@ void LLFloaterPreference::setPersonalInfo(const std::string& visibility, bool im mOriginalHideOnlineStatus = true; } + getChild<LLUICtrl>("online_searchresults")->setEnabled(TRUE); + getChildView("include_im_in_chat_history")->setEnabled(TRUE); getChildView("show_timestamps_check_im")->setEnabled(TRUE); getChildView("friends_online_notify_checkbox")->setEnabled(TRUE); @@ -1734,7 +1758,6 @@ void LLPanelPreferenceGraphics::draw() bool enable = hasDirtyChilds(); button_apply->setEnabled(enable); - } } bool LLPanelPreferenceGraphics::hasDirtyChilds() diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 46014804ec..5d5e066ec5 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -169,6 +169,7 @@ private: bool mGotPersonalInfo; bool mOriginalIMViaEmail; bool mLanguageChanged; + bool mAvatarDataInitialized; bool mOriginalHideOnlineStatus; // Record of current user's favorites may be stored in file on disk. diff --git a/indra/newview/llhints.cpp b/indra/newview/llhints.cpp index c4dcaf11f9..e15862e2a4 100644 --- a/indra/newview/llhints.cpp +++ b/indra/newview/llhints.cpp @@ -191,6 +191,8 @@ BOOL LLHintPopup::postBuild() LLRect text_bounds = hint_text.getTextBoundingRect(); S32 delta_height = text_bounds.getHeight() - hint_text.getRect().getHeight(); reshape(getRect().getWidth(), getRect().getHeight() + delta_height); + hint_text.reshape(hint_text.getRect().getWidth(), hint_text.getRect().getHeight() + delta_height); +// hint_text.translate(0, -delta_height); return TRUE; } @@ -211,6 +213,24 @@ void LLHintPopup::draw() alpha = clamp_rescale(mFadeTimer.getElapsedTimeF32(), 0.f, mFadeInTime, 0.f, 1.f); } + LLIconCtrl* hint_icon = findChild<LLIconCtrl>("hint_image"); + + if (hint_icon) + { + LLUIImagePtr hint_image = hint_icon->getImage(); + S32 image_height = hint_image.isNull() ? 0 : hint_image->getHeight(); + S32 image_width = hint_image.isNull() ? 0 : hint_image->getWidth(); + + LLView* layout_stack = hint_icon->getParent()->getParent(); + S32 delta_height = image_height - layout_stack->getRect().getHeight(); + hint_icon->getParent()->reshape(image_width, hint_icon->getParent()->getRect().getHeight()); + layout_stack->reshape(layout_stack->getRect().getWidth(), image_height); + layout_stack->translate(0, -delta_height); + + LLRect hint_rect = getLocalRect(); + reshape(hint_rect.getWidth(), hint_rect.getHeight() + delta_height); + } + { LLViewDrawContext context(alpha); if (mTarget.empty()) diff --git a/indra/newview/llhudeffectlookat.cpp b/indra/newview/llhudeffectlookat.cpp index 8cf7d23f88..72f64752d6 100644 --- a/indra/newview/llhudeffectlookat.cpp +++ b/indra/newview/llhudeffectlookat.cpp @@ -587,11 +587,6 @@ void LLHUDEffectLookAt::update() */ bool LLHUDEffectLookAt::calcTargetPosition() { - if (gNoRender) - { - return false; - } - LLViewerObject *target_obj = (LLViewerObject *)mTargetObject; LLVector3 local_offset; diff --git a/indra/newview/llhudmanager.cpp b/indra/newview/llhudmanager.cpp index 5f3178b955..8f14b53db0 100644 --- a/indra/newview/llhudmanager.cpp +++ b/indra/newview/llhudmanager.cpp @@ -38,8 +38,6 @@ #include "llviewercontrol.h" #include "llviewerobjectlist.h" -extern BOOL gNoRender; - // These are loaded from saved settings. LLColor4 LLHUDManager::sParentColor; LLColor4 LLHUDManager::sChildColor; @@ -150,11 +148,6 @@ LLHUDEffect *LLHUDManager::createViewerEffect(const U8 type, BOOL send_to_sim, B //static void LLHUDManager::processViewerEffect(LLMessageSystem *mesgsys, void **user_data) { - if (gNoRender) - { - return; - } - LLHUDEffect *effectp = NULL; LLUUID effect_id; U8 effect_type = 0; diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index f74ae92a7b..50a9c56518 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -56,6 +56,7 @@ #include "llrootview.h" #include "llspeakers.h" #include "llsidetray.h" +#include "llviewerchat.h" static const S32 RECT_PADDING_NOT_INIT = -1; @@ -266,7 +267,9 @@ BOOL LLIMFloater::postBuild() mInputEditor->setMaxTextLength(1023); // enable line history support for instant message bar mInputEditor->setEnableLineHistory(TRUE); - + + LLFontGL* font = LLViewerChat::getChatFont(); + mInputEditor->setFont(font); mInputEditor->setFocusReceivedCallback( boost::bind(onInputEditorFocusReceived, _1, this) ); mInputEditor->setFocusLostCallback( boost::bind(onInputEditorFocusLost, _1, this) ); @@ -891,6 +894,7 @@ void LLIMFloater::updateChatHistoryStyle() void LLIMFloater::processChatHistoryStyleUpdate(const LLSD& newvalue) { + LLFontGL* font = LLViewerChat::getChatFont(); LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) @@ -899,6 +903,7 @@ void LLIMFloater::processChatHistoryStyleUpdate(const LLSD& newvalue) if (floater) { floater->updateChatHistoryStyle(); + floater->mInputEditor->setFont(font); } } diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 0ef502b81b..ec3fe48151 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -3183,10 +3183,6 @@ public: //just like a normal IM //this is just replicated code from process_improved_im //and should really go in it's own function -jwolk - if (gNoRender) - { - return; - } LLChat chat; std::string message = message_params["message"].asString(); @@ -3263,11 +3259,6 @@ public: } //end if invitation has instant message else if ( input["body"].has("voice") ) { - if (gNoRender) - { - return; - } - if(!LLVoiceClient::getInstance()->voiceEnabled() || !LLVoiceClient::getInstance()->isVoiceWorking()) { // Don't display voice invites unless the user has voice enabled. diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 53835f0166..b1975c7261 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1059,12 +1059,11 @@ void LLInventoryModel::idleNotifyObservers() { return; } - notifyObservers(""); + notifyObservers(); } // Call this method when it's time to update everyone on a new state. -// The optional argument 'service_name' is used by Agent Inventory Service [DEV-20328] -void LLInventoryModel::notifyObservers(const std::string service_name) +void LLInventoryModel::notifyObservers() { if (mIsNotifyObservers) { @@ -1081,15 +1080,7 @@ void LLInventoryModel::notifyObservers(const std::string service_name) { LLInventoryObserver* observer = *iter; - if (service_name.empty()) - { - observer->changed(mModifyMask); - } - else - { - observer->mMessageName = service_name; - observer->changed(mModifyMask); - } + observer->changed(mModifyMask); // safe way to increment since changed may delete entries! (@!##%@!@&*!) iter = mObservers.upper_bound(observer); @@ -1187,7 +1178,7 @@ void LLInventoryModel::fetchInventoryResponder::result(const LLSD& content) { changes |= gInventory.updateItem(*it); } - gInventory.notifyObservers("fetchinventory"); + gInventory.notifyObservers(); gViewerWindow->getWindow()->decBusyCount(); } @@ -1196,7 +1187,7 @@ void LLInventoryModel::fetchInventoryResponder::error(U32 status, const std::str { llinfos << "fetchInventory::error " << status << ": " << reason << llendl; - gInventory.notifyObservers("fetchinventory"); + gInventory.notifyObservers(); } bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index f6728fd575..15da09990f 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -425,9 +425,8 @@ public: // has been indicated. void idleNotifyObservers(); - // Call to explicitly update everyone on a new state. The optional argument - // 'service_name' is used by Agent Inventory Service [DEV-20328] - void notifyObservers(const std::string service_name=""); + // Call to explicitly update everyone on a new state. + void notifyObservers(); // Allows outsiders to tell the inventory if something has // been changed 'under the hood', but outside the control of the diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index e31360fcbc..7b1ff102e7 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -388,7 +388,7 @@ void LLInventoryModelFetchDescendentsResponder::result(const LLSD& content) titem->setParent(lost_uuid); titem->updateParentOnServer(FALSE); gInventory.updateItem(titem); - gInventory.notifyObservers("fetchDescendents"); + gInventory.notifyObservers(); } } @@ -464,7 +464,7 @@ void LLInventoryModelFetchDescendentsResponder::result(const LLSD& content) fetcher->setAllFoldersFetched(); } - gInventory.notifyObservers("fetchDescendents"); + gInventory.notifyObservers(); } // If we get back an error (not found, etc...), handle it here. @@ -496,7 +496,7 @@ void LLInventoryModelFetchDescendentsResponder::error(U32 status, const std::str fetcher->setAllFoldersFetched(); } } - gInventory.notifyObservers("fetchDescendents"); + gInventory.notifyObservers(); } BOOL LLInventoryModelFetchDescendentsResponder::getIsRecursive(const LLUUID& cat_id) const diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp index 0fd4b2bee5..6bf19e346d 100644 --- a/indra/newview/llinventoryobserver.cpp +++ b/indra/newview/llinventoryobserver.cpp @@ -572,16 +572,7 @@ void LLInventoryAddedObserver::changed(U32 mask) // the network, figure out which item was updated. LLMessageSystem* msg = gMessageSystem; - std::string msg_name; - if (mMessageName.empty()) - { - msg_name = msg->getMessageName(); - } - else - { - msg_name = mMessageName; - } - + std::string msg_name = msg->getMessageName(); if (msg_name.empty()) { return; diff --git a/indra/newview/llinventoryobserver.h b/indra/newview/llinventoryobserver.h index f2a2049a51..2d9021961e 100644 --- a/indra/newview/llinventoryobserver.h +++ b/indra/newview/llinventoryobserver.h @@ -63,7 +63,6 @@ public: LLInventoryObserver(); virtual ~LLInventoryObserver(); virtual void changed(U32 mask) = 0; - std::string mMessageName; // used by Agent Inventory Service only. [DEV-20328] }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index 33e051bfab..abcd8588dc 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -480,10 +480,12 @@ LLLoginInstance::LLLoginInstance() : { mLoginModule->getEventPump().listen("lllogininstance", boost::bind(&LLLoginInstance::handleLoginEvent, this, _1)); - mDispatcher.add("fail.login", boost::bind(&LLLoginInstance::handleLoginFailure, this, _1)); - mDispatcher.add("connect", boost::bind(&LLLoginInstance::handleLoginSuccess, this, _1)); - mDispatcher.add("disconnect", boost::bind(&LLLoginInstance::handleDisconnect, this, _1)); - mDispatcher.add("indeterminate", boost::bind(&LLLoginInstance::handleIndeterminate, this, _1)); + // This internal use of LLEventDispatcher doesn't really need + // per-function descriptions. + mDispatcher.add("fail.login", "", boost::bind(&LLLoginInstance::handleLoginFailure, this, _1)); + mDispatcher.add("connect", "", boost::bind(&LLLoginInstance::handleLoginSuccess, this, _1)); + mDispatcher.add("disconnect", "", boost::bind(&LLLoginInstance::handleDisconnect, this, _1)); + mDispatcher.add("indeterminate", "", boost::bind(&LLLoginInstance::handleIndeterminate, this, _1)); } LLLoginInstance::~LLLoginInstance() @@ -625,11 +627,7 @@ bool LLLoginInstance::handleLoginEvent(const LLSD& event) // Call the method registered in constructor, if any, for more specific // handling - LLEventDispatcher::Callable method(mDispatcher.get(event["change"])); - if (! method.empty()) - { - method(event); - } + mDispatcher.try_call(event); return false; } diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index 9493fddf50..5007f1c17a 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -44,6 +44,7 @@ #include "llslurl.h" #include "lluictrlfactory.h" // LLDefaultChildRegistry #include "llkeyboard.h" +#include "llviewermenu.h" // linden library includes #include "llfocusmgr.h" @@ -73,6 +74,7 @@ LLMediaCtrl::Params::Params() texture_height("texture_height", 1024), caret_color("caret_color"), initial_mime_type("initial_mime_type"), + error_page_url("error_page_url"), media_id("media_id"), trusted_content("trusted_content", false), focus_on_click("focus_on_click", true) @@ -102,9 +104,11 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) : mTextureHeight ( 1024 ), mClearCache(false), mHomePageMimeType(p.initial_mime_type), + mErrorPageURL(p.error_page_url), mTrusted(p.trusted_content), mWindowShade(NULL), - mHoverTextChanged(false) + mHoverTextChanged(false), + mContextMenu(NULL) { { LLColor4 color = p.caret_color().get(); @@ -149,7 +153,6 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) : LLMediaCtrl::~LLMediaCtrl() { - if (mMediaSource) { mMediaSource->remObserver( this ); @@ -304,10 +307,12 @@ BOOL LLMediaCtrl::handleRightMouseUp( S32 x, S32 y, MASK mask ) BOOL LLMediaCtrl::handleRightMouseDown( S32 x, S32 y, MASK mask ) { if (LLPanel::handleRightMouseDown(x, y, mask)) return TRUE; - convertInputCoords(x, y); + + S32 media_x = x, media_y = y; + convertInputCoords(media_x, media_y); if (mMediaSource) - mMediaSource->mouseDown(x, y, mask, 1); + mMediaSource->mouseDown(media_x, media_y, mask, 1); gFocusMgr.setMouseCapture( this ); @@ -316,6 +321,12 @@ BOOL LLMediaCtrl::handleRightMouseDown( S32 x, S32 y, MASK mask ) setFocus( TRUE ); } + if (mContextMenu) + { + mContextMenu->show(x, y); + LLMenuGL::showPopup(this, mContextMenu, x, y); + } + return TRUE; } @@ -378,6 +389,8 @@ void LLMediaCtrl::onFocusLost() // BOOL LLMediaCtrl::postBuild () { + mContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>( + "menu_media_ctrl.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance()); setVisibleCallback(boost::bind(&LLMediaCtrl::onVisibilityChange, this, _2)); return TRUE; } @@ -503,22 +516,6 @@ bool LLMediaCtrl::canNavigateForward() //////////////////////////////////////////////////////////////////////////////// // -void LLMediaCtrl::set404RedirectUrl( std::string redirect_url ) -{ - if(mMediaSource && mMediaSource->hasMedia()) - mMediaSource->getMediaPlugin()->set_status_redirect( 404, redirect_url ); -} - -//////////////////////////////////////////////////////////////////////////////// -// -void LLMediaCtrl::clr404RedirectUrl() -{ - if(mMediaSource && mMediaSource->hasMedia()) - mMediaSource->getMediaPlugin()->set_status_redirect(404, ""); -} - -//////////////////////////////////////////////////////////////////////////////// -// void LLMediaCtrl::clearCache() { if(mMediaSource) @@ -626,6 +623,16 @@ void LLMediaCtrl::setTarget(const std::string& target) } } +void LLMediaCtrl::setErrorPageURL(const std::string& url) +{ + mErrorPageURL = url; +} + +const std::string& LLMediaCtrl::getErrorPageURL() +{ + return mErrorPageURL; +} + //////////////////////////////////////////////////////////////////////////////// // bool LLMediaCtrl::setCaretColor(unsigned int red, unsigned int green, unsigned int blue) @@ -976,6 +983,16 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) }; break; + case MEDIA_EVENT_NAVIGATE_ERROR_PAGE: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_ERROR_PAGE" << LL_ENDL; + if ( mErrorPageURL.length() > 0 ) + { + navigateTo(mErrorPageURL, "text/html"); + }; + }; + break; + case MEDIA_EVENT_CLICK_LINK_HREF: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << self->getClickTarget() << "\", uri is " << self->getClickURL() << LL_ENDL; diff --git a/indra/newview/llmediactrl.h b/indra/newview/llmediactrl.h index 38a74f90d3..28666e620f 100644 --- a/indra/newview/llmediactrl.h +++ b/indra/newview/llmediactrl.h @@ -34,6 +34,7 @@ class LLViewBorder; class LLUICtrlFactory; +class LLContextMenu; //////////////////////////////////////////////////////////////////////////////// // @@ -63,6 +64,7 @@ public: Optional<std::string> initial_mime_type; Optional<std::string> media_id; + Optional<std::string> error_page_url; Params(); }; @@ -113,10 +115,9 @@ public: void setTarget(const std::string& target); - // set/clear URL to visit when a 404 page is reached - void set404RedirectUrl( std::string redirect_url ); - void clr404RedirectUrl(); - + void setErrorPageURL(const std::string& url); + const std::string& getErrorPageURL(); + // Clear the browser cache when the instance gets loaded void clearCache(); @@ -179,6 +180,7 @@ public: std::string mHomePageUrl; std::string mHomePageMimeType; std::string mCurrentNavUrl; + std::string mErrorPageURL; std::string mTarget; bool mIgnoreUIScale; bool mAlwaysRefresh; @@ -194,6 +196,7 @@ public: bool mClearCache; class LLWindowShade* mWindowShade; bool mHoverTextChanged; + LLContextMenu* mContextMenu; }; #endif // LL_LLMediaCtrl_H diff --git a/indra/newview/llnavigationbar.cpp b/indra/newview/llnavigationbar.cpp index 3b160ddc8e..b8832dfd8e 100644 --- a/indra/newview/llnavigationbar.cpp +++ b/indra/newview/llnavigationbar.cpp @@ -57,6 +57,7 @@ #include "llviewercontrol.h" #include "llfloatermediabrowser.h" #include "llweb.h" +#include "llhints.h" #include "llinventorymodel.h" #include "lllandmarkactions.h" @@ -324,6 +325,8 @@ BOOL LLNavigationBar::postBuild() LLTeleportHistory::getInstance()->setHistoryChangedCallback( boost::bind(&LLNavigationBar::onTeleportHistoryChanged, this)); + LLHints::registerHintTarget("nav_bar", LLView::getHandle()); + return TRUE; } diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 903cf4780d..7820ac3ecd 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -214,7 +214,9 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, } updateLocationCombo(false); - gSavedSettings.getControl("SessionSettingsFile")->getSignal()->connect(boost::bind(&onModeChange)); + LLUICtrl& mode_combo = getChildRef<LLUICtrl>("mode_combo"); + mode_combo.setValue(gSavedSettings.getString("SessionSettingsFile")); + mode_combo.setCommitCallback(boost::bind(&LLPanelLogin::onModeChange, this, getChild<LLUICtrl>("mode_combo")->getValue(), _2)); LLComboBox* server_choice_combo = sInstance->getChild<LLComboBox>("server_combo"); server_choice_combo->setCommitCallback(onSelectServer, NULL); @@ -1159,23 +1161,26 @@ void LLPanelLogin::updateLoginPanelLinks() sInstance->getChildView("forgot_password_text")->setVisible( system_grid); } -//static -void LLPanelLogin::onModeChange() +void LLPanelLogin::onModeChange(const LLSD& original_value, const LLSD& new_value) { - LLNotificationsUtil::add("ModeChange", LLSD(), LLSD(), boost::bind(&onModeChangeConfirm, _1, _2)); + if (original_value.asString() != new_value.asString()) + { + LLNotificationsUtil::add("ModeChange", LLSD(), LLSD(), boost::bind(&LLPanelLogin::onModeChangeConfirm, this, original_value, new_value, _1, _2)); + } } -//static -void LLPanelLogin::onModeChangeConfirm(const LLSD& notification, const LLSD& response) +void LLPanelLogin::onModeChangeConfirm(const LLSD& original_value, const LLSD& new_value, const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); switch (option) { case 0: - LLAppViewer::instance()->requestQuit(); + gSavedSettings.getControl("SessionSettingsFile")->set(new_value); + LLAppViewer::instance()->forceQuit(); break; case 1: - // do nothing + // revert to original value + getChild<LLUICtrl>("mode_combo")->setValue(original_value); break; default: break; diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index 1430bec832..9cc5e3456a 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -98,8 +98,8 @@ private: static void onServerComboLostFocus(LLFocusableElement*); static void updateServerCombo(); static void updateStartSLURL(); - static void onModeChange(); - static void onModeChangeConfirm(const LLSD& notification, const LLSD& response); + void onModeChange(const LLSD& original_value, const LLSD& new_value); + void onModeChangeConfirm(const LLSD& original_value, const LLSD& new_value, const LLSD& notification, const LLSD& response); static void updateLoginPanelLinks(); diff --git a/indra/newview/llpanelme.cpp b/indra/newview/llpanelme.cpp index d3c9c3e131..1347a02a52 100644 --- a/indra/newview/llpanelme.cpp +++ b/indra/newview/llpanelme.cpp @@ -67,8 +67,6 @@ BOOL LLPanelMe::postBuild() { LLPanelProfile::postBuild(); - getTabContainer()[PANEL_PROFILE]->childSetAction("edit_profile_btn", boost::bind(&LLPanelMe::onEditProfileClicked, this), this); - return TRUE; } @@ -135,7 +133,11 @@ void LLPanelMe::buildEditPanel() if (NULL == mEditPanel) { mEditPanel = new LLPanelMyProfileEdit(); - mEditPanel->childSetAction("save_btn", boost::bind(&LLPanelMe::onSaveChangesClicked, this), this); + + // Note: Remove support for editing profile through this method. + // All profile editing should go through the web. + //mEditPanel->childSetAction("save_btn", boost::bind(&LLPanelMe::onSaveChangesClicked, this), this); + mEditPanel->childSetAction("cancel_btn", boost::bind(&LLPanelMe::onCancelClicked, this), this); } } @@ -147,22 +149,6 @@ void LLPanelMe::onEditProfileClicked() togglePanel(mEditPanel, getAvatarId()); // open } -void LLPanelMe::onSaveChangesClicked() -{ - LLAvatarData data = LLAvatarData(); - data.avatar_id = gAgent.getID(); - data.image_id = mEditPanel->getChild<LLTextureCtrl>(PICKER_SECOND_LIFE)->getImageAssetID(); - data.fl_image_id = mEditPanel->getChild<LLTextureCtrl>(PICKER_FIRST_LIFE)->getImageAssetID(); - data.about_text = mEditPanel->getChild<LLUICtrl>("sl_description_edit")->getValue().asString(); - data.fl_about_text = mEditPanel->getChild<LLUICtrl>("fl_description_edit")->getValue().asString(); - data.profile_url = mEditPanel->getChild<LLUICtrl>("homepage_edit")->getValue().asString(); - data.allow_publish = mEditPanel->getChild<LLUICtrl>("show_in_search_checkbox")->getValue(); - - LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesUpdate(&data); - togglePanel(mEditPanel); // close - onOpen(getAvatarId()); -} - void LLPanelMe::onCancelClicked() { togglePanel(mEditPanel); // close diff --git a/indra/newview/llpanelme.h b/indra/newview/llpanelme.h index d5b2fee869..f27f5a268e 100644 --- a/indra/newview/llpanelme.h +++ b/indra/newview/llpanelme.h @@ -58,7 +58,6 @@ private: void buildEditPanel(); void onEditProfileClicked(); - void onSaveChangesClicked(); void onCancelClicked(); LLPanelMyProfileEdit * mEditPanel; diff --git a/indra/newview/llphysicsmotion.cpp b/indra/newview/llphysicsmotion.cpp index 1d3c2b72f2..23fa0cbd9c 100644 --- a/indra/newview/llphysicsmotion.cpp +++ b/indra/newview/llphysicsmotion.cpp @@ -1,723 +1,717 @@ -/**
- * @file llphysicsmotion.cpp
- * @brief Implementation of LLPhysicsMotion class.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-//-----------------------------------------------------------------------------
-// Header Files
-//-----------------------------------------------------------------------------
-#include "llviewerprecompiledheaders.h"
-#include "linden_common.h"
-
-#include "m3math.h"
-#include "v3dmath.h"
-
-#include "llphysicsmotion.h"
-#include "llagent.h"
-#include "llcharacter.h"
-#include "llviewercontrol.h"
-#include "llviewervisualparam.h"
-#include "llvoavatarself.h"
-
-typedef std::map<std::string, std::string> controller_map_t;
-typedef std::map<std::string, F32> default_controller_map_t;
-
-#define MIN_REQUIRED_PIXEL_AREA_BREAST_MOTION 0.f;
-
-inline F64 llsgn(const F64 a)
-{
- if (a >= 0)
- return 1;
- return -1;
-}
-
-/*
- At a high level, this works by setting temporary parameters that are not stored
- in the avatar's list of params, and are not conveyed to other users. We accomplish
- this by creating some new temporary driven params inside avatar_lad that are then driven
- by the actual params that the user sees and sets. For example, in the old system,
- the user sets a param called breast bouyancy, which controls the Z value of the breasts.
- In our new system, the user still sets the breast bouyancy, but that param is redefined
- as a driver param so that affects a new temporary driven param that the bounce is applied
- to.
-*/
-
-class LLPhysicsMotion
-{
-public:
- /*
- param_driver_name: The param that controls the params that are being affected by the physics.
- joint_name: The joint that the body part is attached to. The joint is
- used to determine the orientation (rotation) of the body part.
-
- character: The avatar that this physics affects.
-
- motion_direction_vec: The direction (in world coordinates) that determines the
- motion. For example, (0,0,1) is up-down, and means that up-down motion is what
- determines how this joint moves.
-
- controllers: The various settings (e.g. spring force, mass) that determine how
- the body part behaves.
- */
- LLPhysicsMotion(const std::string ¶m_driver_name,
- const std::string &joint_name,
- LLCharacter *character,
- const LLVector3 &motion_direction_vec,
- const controller_map_t &controllers) :
- mParamDriverName(param_driver_name),
- mJointName(joint_name),
- mMotionDirectionVec(motion_direction_vec),
- mParamDriver(NULL),
- mParamControllers(controllers),
- mCharacter(character),
- mLastTime(0),
- mPosition_local(0),
- mVelocityJoint_local(0),
- mPositionLastUpdate_local(0)
- {
- mJointState = new LLJointState;
- }
-
- BOOL initialize();
-
- ~LLPhysicsMotion() {}
-
- BOOL onUpdate(F32 time);
-
- LLPointer<LLJointState> getJointState()
- {
- return mJointState;
- }
-protected:
- F32 getParamValue(const std::string& controller_key)
- {
- const controller_map_t::const_iterator& entry = mParamControllers.find(controller_key);
- if (entry == mParamControllers.end())
- {
- return sDefaultController[controller_key];
- }
- const std::string& param_name = (*entry).second.c_str();
- return mCharacter->getVisualParamWeight(param_name.c_str());
- }
- void setParamValue(LLViewerVisualParam *param,
- const F32 new_value_local,
- F32 behavior_maxeffect);
-
- F32 toLocal(const LLVector3 &world);
- F32 calculateVelocity_local(const F32 time_delta);
- F32 calculateAcceleration_local(F32 velocity_local,
- const F32 time_delta);
-private:
- const std::string mParamDriverName;
- const std::string mParamControllerName;
- const LLVector3 mMotionDirectionVec;
- const std::string mJointName;
-
- F32 mPosition_local;
- F32 mVelocityJoint_local; // How fast the joint is moving
- F32 mAccelerationJoint_local; // Acceleration on the joint
-
- F32 mVelocity_local; // How fast the param is moving
- F32 mPositionLastUpdate_local;
- LLVector3 mPosition_world;
-
- LLViewerVisualParam *mParamDriver;
- const controller_map_t mParamControllers;
-
- LLPointer<LLJointState> mJointState;
- LLCharacter *mCharacter;
-
- F32 mLastTime;
-
- static default_controller_map_t sDefaultController;
-};
-
-default_controller_map_t initDefaultController()
-{
- default_controller_map_t controller;
- controller["Mass"] = 0.2f;
- controller["Gravity"] = 0.0f;
- controller["Damping"] = .05f;
- controller["Drag"] = 0.15f;
- controller["MaxEffect"] = 0.1f;
- controller["Spring"] = 0.1f;
- controller["Gain"] = 10.0f;
- return controller;
-}
-
-default_controller_map_t LLPhysicsMotion::sDefaultController = initDefaultController();
-
-BOOL LLPhysicsMotion::initialize()
-{
- if (!mJointState->setJoint(mCharacter->getJoint(mJointName.c_str())))
- return FALSE;
- mJointState->setUsage(LLJointState::ROT);
-
- mParamDriver = (LLViewerVisualParam*)mCharacter->getVisualParam(mParamDriverName.c_str());
- if (mParamDriver == NULL)
- {
- llinfos << "Failure reading in [ " << mParamDriverName << " ]" << llendl;
- return FALSE;
- }
-
- return TRUE;
-}
-
-LLPhysicsMotionController::LLPhysicsMotionController(const LLUUID &id) :
- LLMotion(id),
- mCharacter(NULL)
-{
- mName = "breast_motion";
-}
-
-LLPhysicsMotionController::~LLPhysicsMotionController()
-{
- for (motion_vec_t::iterator iter = mMotions.begin();
- iter != mMotions.end();
- ++iter)
- {
- delete (*iter);
- }
-}
-
-BOOL LLPhysicsMotionController::onActivate()
-{
- return TRUE;
-}
-
-void LLPhysicsMotionController::onDeactivate()
-{
-}
-
-LLMotion::LLMotionInitStatus LLPhysicsMotionController::onInitialize(LLCharacter *character)
-{
- mCharacter = character;
-
- mMotions.clear();
-
- // Breast Cleavage
- {
- controller_map_t controller;
- controller["Mass"] = "Breast_Physics_Mass";
- controller["Gravity"] = "Breast_Physics_Gravity";
- controller["Drag"] = "Breast_Physics_Drag";
- controller["Damping"] = "Breast_Physics_InOut_Damping";
- controller["MaxEffect"] = "Breast_Physics_InOut_Max_Effect";
- controller["Spring"] = "Breast_Physics_InOut_Spring";
- controller["Gain"] = "Breast_Physics_InOut_Gain";
- LLPhysicsMotion *motion = new LLPhysicsMotion("Breast_Physics_InOut_Controller",
- "mChest",
- character,
- LLVector3(-1,0,0),
- controller);
- if (!motion->initialize())
- {
- llassert_always(FALSE);
- return STATUS_FAILURE;
- }
- addMotion(motion);
- }
-
- // Breast Bounce
- {
- controller_map_t controller;
- controller["Mass"] = "Breast_Physics_Mass";
- controller["Gravity"] = "Breast_Physics_Gravity";
- controller["Drag"] = "Breast_Physics_Drag";
- controller["Damping"] = "Breast_Physics_UpDown_Damping";
- controller["MaxEffect"] = "Breast_Physics_UpDown_Max_Effect";
- controller["Spring"] = "Breast_Physics_UpDown_Spring";
- controller["Gain"] = "Breast_Physics_UpDown_Gain";
- LLPhysicsMotion *motion = new LLPhysicsMotion("Breast_Physics_UpDown_Controller",
- "mChest",
- character,
- LLVector3(0,0,1),
- controller);
- if (!motion->initialize())
- {
- llassert_always(FALSE);
- return STATUS_FAILURE;
- }
- addMotion(motion);
- }
-
- // Breast Sway
- {
- controller_map_t controller;
- controller["Mass"] = "Breast_Physics_Mass";
- controller["Gravity"] = "Breast_Physics_Gravity";
- controller["Drag"] = "Breast_Physics_Drag";
- controller["Damping"] = "Breast_Physics_LeftRight_Damping";
- controller["MaxEffect"] = "Breast_Physics_LeftRight_Max_Effect";
- controller["Spring"] = "Breast_Physics_LeftRight_Spring";
- controller["Gain"] = "Breast_Physics_LeftRight_Gain";
- LLPhysicsMotion *motion = new LLPhysicsMotion("Breast_Physics_LeftRight_Controller",
- "mChest",
- character,
- LLVector3(0,-1,0),
- controller);
- if (!motion->initialize())
- {
- llassert_always(FALSE);
- return STATUS_FAILURE;
- }
- addMotion(motion);
- }
- // Butt Bounce
- {
- controller_map_t controller;
- controller["Mass"] = "Butt_Physics_Mass";
- controller["Gravity"] = "Butt_Physics_Gravity";
- controller["Drag"] = "Butt_Physics_Drag";
- controller["Damping"] = "Butt_Physics_UpDown_Damping";
- controller["MaxEffect"] = "Butt_Physics_UpDown_Max_Effect";
- controller["Spring"] = "Butt_Physics_UpDown_Spring";
- controller["Gain"] = "Butt_Physics_UpDown_Gain";
- LLPhysicsMotion *motion = new LLPhysicsMotion("Butt_Physics_UpDown_Controller",
- "mPelvis",
- character,
- LLVector3(0,0,-1),
- controller);
- if (!motion->initialize())
- {
- llassert_always(FALSE);
- return STATUS_FAILURE;
- }
- addMotion(motion);
- }
-
- // Butt LeftRight
- {
- controller_map_t controller;
- controller["Mass"] = "Butt_Physics_Mass";
- controller["Gravity"] = "Butt_Physics_Gravity";
- controller["Drag"] = "Butt_Physics_Drag";
- controller["Damping"] = "Butt_Physics_LeftRight_Damping";
- controller["MaxEffect"] = "Butt_Physics_LeftRight_Max_Effect";
- controller["Spring"] = "Butt_Physics_LeftRight_Spring";
- controller["Gain"] = "Butt_Physics_LeftRight_Gain";
- LLPhysicsMotion *motion = new LLPhysicsMotion("Butt_Physics_LeftRight_Controller",
- "mPelvis",
- character,
- LLVector3(0,-1,0),
- controller);
- if (!motion->initialize())
- {
- llassert_always(FALSE);
- return STATUS_FAILURE;
- }
- addMotion(motion);
- }
-
- // Belly Bounce
- {
- controller_map_t controller;
- controller["Mass"] = "Belly_Physics_Mass";
- controller["Gravity"] = "Belly_Physics_Gravity";
- controller["Drag"] = "Belly_Physics_Drag";
- controller["Damping"] = "Belly_Physics_UpDown_Damping";
- controller["MaxEffect"] = "Belly_Physics_UpDown_Max_Effect";
- controller["Spring"] = "Belly_Physics_UpDown_Spring";
- controller["Gain"] = "Belly_Physics_UpDown_Gain";
- LLPhysicsMotion *motion = new LLPhysicsMotion("Belly_Physics_UpDown_Controller",
- "mPelvis",
- character,
- LLVector3(0,0,-1),
- controller);
- if (!motion->initialize())
- {
- llassert_always(FALSE);
- return STATUS_FAILURE;
- }
- addMotion(motion);
- }
-
- return STATUS_SUCCESS;
-}
-
-void LLPhysicsMotionController::addMotion(LLPhysicsMotion *motion)
-{
- addJointState(motion->getJointState());
- mMotions.push_back(motion);
-}
-
-F32 LLPhysicsMotionController::getMinPixelArea()
-{
- return MIN_REQUIRED_PIXEL_AREA_BREAST_MOTION;
-}
-
-// Local space means "parameter space".
-F32 LLPhysicsMotion::toLocal(const LLVector3 &world)
-{
- LLJoint *joint = mJointState->getJoint();
- const LLQuaternion rotation_world = joint->getWorldRotation();
-
- LLVector3 dir_world = mMotionDirectionVec * rotation_world;
- dir_world.normalize();
- return world * dir_world;
-}
-
-F32 LLPhysicsMotion::calculateVelocity_local(const F32 time_delta)
-{
- LLJoint *joint = mJointState->getJoint();
- const LLVector3 position_world = joint->getWorldPosition();
- const LLQuaternion rotation_world = joint->getWorldRotation();
- const LLVector3 last_position_world = mPosition_world;
- const LLVector3 velocity_world = (position_world-last_position_world) / time_delta;
- const F32 velocity_local = toLocal(velocity_world);
- return velocity_local;
-}
-
-F32 LLPhysicsMotion::calculateAcceleration_local(const F32 velocity_local,
- const F32 time_delta)
-{
-// const F32 smoothing = getParamValue("Smoothing");
- static const F32 smoothing = 3.0f; // Removed smoothing param since it's probably not necessary
- const F32 acceleration_local = velocity_local - mVelocityJoint_local;
-
- const F32 smoothed_acceleration_local =
- acceleration_local * 1.0/smoothing +
- mAccelerationJoint_local * (smoothing-1.0)/smoothing;
-
- return smoothed_acceleration_local;
-}
-
-BOOL LLPhysicsMotionController::onUpdate(F32 time, U8* joint_mask)
-{
- // Skip if disabled globally.
- if (!gSavedSettings.getBOOL("AvatarPhysics"))
- {
- return TRUE;
- }
-
- BOOL update_visuals = FALSE;
- for (motion_vec_t::iterator iter = mMotions.begin();
- iter != mMotions.end();
- ++iter)
- {
- LLPhysicsMotion *motion = (*iter);
- update_visuals |= motion->onUpdate(time);
- }
-
- if (update_visuals)
- mCharacter->updateVisualParams();
-
- return TRUE;
-}
-
-
-// Return TRUE if character has to update visual params.
-BOOL LLPhysicsMotion::onUpdate(F32 time)
-{
- // static FILE *mFileWrite = fopen("c:\\temp\\avatar_data.txt","w");
-
- if (!mParamDriver)
- return FALSE;
-
- if (!mLastTime)
- {
- mLastTime = time;
- return FALSE;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Get all parameters and settings
- //
-
- const F32 time_delta = time - mLastTime;
- if (time_delta > 3.0 || time_delta <= 0.01)
- {
- mLastTime = time;
- return FALSE;
- }
-
- // Higher LOD is better. This controls the granularity
- // and frequency of updates for the motions.
- const F32 lod_factor = LLVOAvatar::sPhysicsLODFactor;
- if (lod_factor == 0)
- {
- return TRUE;
- }
-
- LLJoint *joint = mJointState->getJoint();
-
- const F32 behavior_mass = getParamValue("Mass");
- const F32 behavior_gravity = getParamValue("Gravity");
- const F32 behavior_spring = getParamValue("Spring");
- const F32 behavior_gain = getParamValue("Gain");
- const F32 behavior_damping = getParamValue("Damping");
- const F32 behavior_drag = getParamValue("Drag");
- const BOOL physics_test = gSavedSettings.getBOOL("AvatarPhysicsTest") && gAgent.isGodlike();
-
- F32 behavior_maxeffect = getParamValue("MaxEffect");
- if (physics_test)
- behavior_maxeffect = 1.0f;
-
- // mPositon_local should be in normalized 0,1 range already. Just making sure...
- F32 position_current_local = llclamp(mPosition_local,
- 0.0f,
- 1.0f);
-
- // Normalize the param position to be from [0,1].
- // We have to use normalized values because there may be more than one driven param,
- // and each of these driven params may have its own range.
- // This means we'll do all our calculations in normalized [0,1] local coordinates.
- F32 position_user_local = mParamDriver->getWeight();
- position_user_local = (position_user_local - mParamDriver->getMinWeight()) / (mParamDriver->getMaxWeight() - mParamDriver->getMinWeight());
-
- // If the effect is turned off then don't process unless we need one more update
- // to set the position to the default (i.e. user) position.
- if ((behavior_maxeffect == 0) && (position_current_local == position_user_local))
- {
- return FALSE;
- }
-
- //
- // End parameters and settings
- ////////////////////////////////////////////////////////////////////////////////
-
-
- ////////////////////////////////////////////////////////////////////////////////
- // Calculate velocity and acceleration in parameter space.
- //
-
- const F32 velocity_joint_local = calculateVelocity_local(time_delta);
- const F32 acceleration_joint_local = calculateAcceleration_local(velocity_joint_local, time_delta);
-
- //
- // End velocity and acceleration
- ////////////////////////////////////////////////////////////////////////////////
-
-
- ////////////////////////////////////////////////////////////////////////////////
- // Calculate the total force
- //
-
- // Spring force is a restoring force towards the original user-set breast position.
- // F = kx
- const F32 spring_length = position_current_local - position_user_local;
- const F32 force_spring = -spring_length * behavior_spring;
-
- // Acceleration is the force that comes from the change in velocity of the torso.
- // F = ma
- const F32 force_accel = behavior_gain * (acceleration_joint_local * behavior_mass);
-
- // Gravity always points downward in world space.
- // F = mg
- const LLVector3 gravity_world(0,0,1);
- const F32 force_gravity = behavior_gain * (toLocal(gravity_world) * behavior_gravity * behavior_mass);
-
- // Damping is a restoring force that opposes the current velocity.
- // F = -kv
- const F32 force_damping = -behavior_damping * mVelocity_local;
-
- // Drag is a force imparted by velocity (intuitively it is similar to wind resistance)
- // F = .5kv^2
- const F32 force_drag = .5*behavior_drag*velocity_joint_local*velocity_joint_local*llsgn(velocity_joint_local);
-
- const F32 force_net = (force_accel +
- force_gravity +
- force_spring +
- force_damping +
- force_drag);
-
- //
- // End total force
- ////////////////////////////////////////////////////////////////////////////////
-
-
- ////////////////////////////////////////////////////////////////////////////////
- // Calculate new params
- //
-
- // Calculate the new acceleration based on the net force.
- // a = F/m
- const F32 acceleration_new_local = force_net / behavior_mass;
- static const F32 max_acceleration = 10.0f; // magic number, used to be customizable.
- F32 velocity_new_local = mVelocity_local + acceleration_new_local;
- velocity_new_local = llclamp(velocity_new_local,
- -max_acceleration, max_acceleration);
-
- // Temporary debugging setting to cause all avatars to move, for profiling purposes.
- if (physics_test)
- {
- velocity_new_local = sin(time*4.0);
- }
- // Calculate the new parameters, or remain unchanged if max speed is 0.
- F32 position_new_local = position_current_local + velocity_new_local*time_delta;
- if (behavior_maxeffect == 0)
- position_new_local = position_user_local;
-
- // Zero out the velocity if the param is being pushed beyond its limits.
- if ((position_new_local < 0 && velocity_new_local < 0) ||
- (position_new_local > 1 && velocity_new_local > 0))
- {
- velocity_new_local = 0;
- }
-
- // Check for NaN values. A NaN value is detected if the variables doesn't equal itself.
- // If NaN, then reset everything.
- if ((mPosition_local != mPosition_local) ||
- (mVelocity_local != mVelocity_local) ||
- (position_new_local != position_new_local))
- {
- position_new_local = 0;
- position_current_local = 0;
- position_user_local = 0;
- mVelocity_local = 0;
- mVelocityJoint_local = 0;
- mAccelerationJoint_local = 0;
- mPosition_local = 0;
- mPosition_world = LLVector3(0,0,0);
- }
-
- const F32 position_new_local_clamped = llclamp(position_new_local,
- 0.0f,
- 1.0f);
-
- LLDriverParam *driver_param = dynamic_cast<LLDriverParam *>(mParamDriver);
- llassert_always(driver_param);
- if (driver_param)
- {
- // If this is one of our "hidden" driver params, then make sure it's
- // the default value.
- if ((driver_param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) &&
- (driver_param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT))
- {
- mCharacter->setVisualParamWeight(driver_param,
- 0,
- FALSE);
- }
- for (LLDriverParam::entry_list_t::iterator iter = driver_param->mDriven.begin();
- iter != driver_param->mDriven.end();
- ++iter)
- {
- LLDrivenEntry &entry = (*iter);
- LLViewerVisualParam *driven_param = entry.mParam;
- setParamValue(driven_param,position_new_local_clamped, behavior_maxeffect);
- }
- }
-
- //
- // End calculate new params
- ////////////////////////////////////////////////////////////////////////////////
-
- ////////////////////////////////////////////////////////////////////////////////
- // Conditionally update the visual params
- //
-
- // Updating the visual params (i.e. what the user sees) is fairly expensive.
- // So only update if the params have changed enough, and also take into account
- // the graphics LOD settings.
-
- BOOL update_visuals = FALSE;
-
- // For non-self, if the avatar is small enough visually, then don't update.
- const F32 area_for_max_settings = 0.0;
- const F32 area_for_min_settings = 1400.0;
- const F32 area_for_this_setting = area_for_max_settings + (area_for_min_settings-area_for_max_settings)*(1.0-lod_factor);
- const F32 pixel_area = fsqrtf(mCharacter->getPixelArea());
-
- const BOOL is_self = (dynamic_cast<LLVOAvatarSelf *>(mCharacter) != NULL);
- if ((pixel_area > area_for_this_setting) || is_self)
- {
- const F32 position_diff_local = llabs(mPositionLastUpdate_local-position_new_local_clamped);
- const F32 min_delta = (1.01f-lod_factor)*0.4f;
- if (llabs(position_diff_local) > min_delta)
- {
- update_visuals = TRUE;
- mPositionLastUpdate_local = position_new_local;
- }
- }
-
- //
- // End update visual params
- ////////////////////////////////////////////////////////////////////////////////
-
- mVelocityJoint_local = velocity_joint_local;
-
- mVelocity_local = velocity_new_local;
- mAccelerationJoint_local = acceleration_joint_local;
- mPosition_local = position_new_local;
-
- mPosition_world = joint->getWorldPosition();
- mLastTime = time;
-
- /*
- // Write out debugging info into a spreadsheet.
- if (mFileWrite != NULL && is_self)
- {
- fprintf(mFileWrite,"%f\t%f\t%f \t\t%f \t\t%f\t%f\t%f\t \t\t%f\t%f\t%f\t%f\t%f \t\t%f\t%f\t%f\n",
- position_new_local,
- velocity_new_local,
- acceleration_new_local,
-
- time_delta,
-
- mPosition_world[0],
- mPosition_world[1],
- mPosition_world[2],
-
- force_net,
- force_spring,
- force_accel,
- force_damping,
- force_drag,
-
- spring_length,
- velocity_joint_local,
- acceleration_joint_local
- );
- }
- */
-
- return update_visuals;
-}
-
-// Range of new_value_local is assumed to be [0 , 1] normalized.
-void LLPhysicsMotion::setParamValue(LLViewerVisualParam *param,
- F32 new_value_normalized,
- F32 behavior_maxeffect)
-{
- const F32 value_min_local = param->getMinWeight();
- const F32 value_max_local = param->getMaxWeight();
- const F32 min_val = 0.5f-behavior_maxeffect/2.0;
- const F32 max_val = 0.5f+behavior_maxeffect/2.0;
-
- // Scale from [0,1] to [min_val,max_val]
- const F32 new_value_rescaled = min_val + (max_val-min_val) * new_value_normalized;
-
- // Scale from [0,1] to [value_min_local,value_max_local]
- const F32 new_value_local = value_min_local + (value_max_local-value_min_local) * new_value_rescaled;
-
- mCharacter->setVisualParamWeight(param,
- new_value_local,
- FALSE);
-}
+/** + * @file llphysicsmotion.cpp + * @brief Implementation of LLPhysicsMotion class. + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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$ + */ + +//----------------------------------------------------------------------------- +// Header Files +//----------------------------------------------------------------------------- +#include "llviewerprecompiledheaders.h" +#include "linden_common.h" + +#include "m3math.h" +#include "v3dmath.h" + +#include "llphysicsmotion.h" +#include "llagent.h" +#include "llcharacter.h" +#include "llviewercontrol.h" +#include "llviewervisualparam.h" +#include "llvoavatarself.h" + +typedef std::map<std::string, std::string> controller_map_t; +typedef std::map<std::string, F32> default_controller_map_t; + +#define MIN_REQUIRED_PIXEL_AREA_BREAST_MOTION 0.f; + +inline F64 llsgn(const F64 a) +{ + if (a >= 0) + return 1; + return -1; +} + +/* + At a high level, this works by setting temporary parameters that are not stored + in the avatar's list of params, and are not conveyed to other users. We accomplish + this by creating some new temporary driven params inside avatar_lad that are then driven + by the actual params that the user sees and sets. For example, in the old system, + the user sets a param called breast bouyancy, which controls the Z value of the breasts. + In our new system, the user still sets the breast bouyancy, but that param is redefined + as a driver param so that affects a new temporary driven param that the bounce is applied + to. +*/ + +class LLPhysicsMotion +{ +public: + /* + param_driver_name: The param that controls the params that are being affected by the physics. + joint_name: The joint that the body part is attached to. The joint is + used to determine the orientation (rotation) of the body part. + + character: The avatar that this physics affects. + + motion_direction_vec: The direction (in world coordinates) that determines the + motion. For example, (0,0,1) is up-down, and means that up-down motion is what + determines how this joint moves. + + controllers: The various settings (e.g. spring force, mass) that determine how + the body part behaves. + */ + LLPhysicsMotion(const std::string ¶m_driver_name, + const std::string &joint_name, + LLCharacter *character, + const LLVector3 &motion_direction_vec, + const controller_map_t &controllers) : + mParamDriverName(param_driver_name), + mJointName(joint_name), + mMotionDirectionVec(motion_direction_vec), + mParamDriver(NULL), + mParamControllers(controllers), + mCharacter(character), + mLastTime(0), + mPosition_local(0), + mVelocityJoint_local(0), + mPositionLastUpdate_local(0) + { + mJointState = new LLJointState; + } + + BOOL initialize(); + + ~LLPhysicsMotion() {} + + BOOL onUpdate(F32 time); + + LLPointer<LLJointState> getJointState() + { + return mJointState; + } +protected: + F32 getParamValue(const std::string& controller_key) + { + const controller_map_t::const_iterator& entry = mParamControllers.find(controller_key); + if (entry == mParamControllers.end()) + { + return sDefaultController[controller_key]; + } + const std::string& param_name = (*entry).second.c_str(); + return mCharacter->getVisualParamWeight(param_name.c_str()); + } + void setParamValue(LLViewerVisualParam *param, + const F32 new_value_local, + F32 behavior_maxeffect); + + F32 toLocal(const LLVector3 &world); + F32 calculateVelocity_local(const F32 time_delta); + F32 calculateAcceleration_local(F32 velocity_local, + const F32 time_delta); +private: + const std::string mParamDriverName; + const std::string mParamControllerName; + const LLVector3 mMotionDirectionVec; + const std::string mJointName; + + F32 mPosition_local; + F32 mVelocityJoint_local; // How fast the joint is moving + F32 mAccelerationJoint_local; // Acceleration on the joint + + F32 mVelocity_local; // How fast the param is moving + F32 mPositionLastUpdate_local; + LLVector3 mPosition_world; + + LLViewerVisualParam *mParamDriver; + const controller_map_t mParamControllers; + + LLPointer<LLJointState> mJointState; + LLCharacter *mCharacter; + + F32 mLastTime; + + static default_controller_map_t sDefaultController; +}; + +default_controller_map_t initDefaultController() +{ + default_controller_map_t controller; + controller["Mass"] = 0.2f; + controller["Gravity"] = 0.0f; + controller["Damping"] = .05f; + controller["Drag"] = 0.15f; + controller["MaxEffect"] = 0.1f; + controller["Spring"] = 0.1f; + controller["Gain"] = 10.0f; + return controller; +} + +default_controller_map_t LLPhysicsMotion::sDefaultController = initDefaultController(); + +BOOL LLPhysicsMotion::initialize() +{ + if (!mJointState->setJoint(mCharacter->getJoint(mJointName.c_str()))) + return FALSE; + mJointState->setUsage(LLJointState::ROT); + + mParamDriver = (LLViewerVisualParam*)mCharacter->getVisualParam(mParamDriverName.c_str()); + if (mParamDriver == NULL) + { + llinfos << "Failure reading in [ " << mParamDriverName << " ]" << llendl; + return FALSE; + } + + return TRUE; +} + +LLPhysicsMotionController::LLPhysicsMotionController(const LLUUID &id) : + LLMotion(id), + mCharacter(NULL) +{ + mName = "breast_motion"; +} + +LLPhysicsMotionController::~LLPhysicsMotionController() +{ + for (motion_vec_t::iterator iter = mMotions.begin(); + iter != mMotions.end(); + ++iter) + { + delete (*iter); + } +} + +BOOL LLPhysicsMotionController::onActivate() +{ + return TRUE; +} + +void LLPhysicsMotionController::onDeactivate() +{ +} + +LLMotion::LLMotionInitStatus LLPhysicsMotionController::onInitialize(LLCharacter *character) +{ + mCharacter = character; + + mMotions.clear(); + + // Breast Cleavage + { + controller_map_t controller; + controller["Mass"] = "Breast_Physics_Mass"; + controller["Gravity"] = "Breast_Physics_Gravity"; + controller["Drag"] = "Breast_Physics_Drag"; + controller["Damping"] = "Breast_Physics_InOut_Damping"; + controller["MaxEffect"] = "Breast_Physics_InOut_Max_Effect"; + controller["Spring"] = "Breast_Physics_InOut_Spring"; + controller["Gain"] = "Breast_Physics_InOut_Gain"; + LLPhysicsMotion *motion = new LLPhysicsMotion("Breast_Physics_InOut_Controller", + "mChest", + character, + LLVector3(-1,0,0), + controller); + if (!motion->initialize()) + { + llassert_always(FALSE); + return STATUS_FAILURE; + } + addMotion(motion); + } + + // Breast Bounce + { + controller_map_t controller; + controller["Mass"] = "Breast_Physics_Mass"; + controller["Gravity"] = "Breast_Physics_Gravity"; + controller["Drag"] = "Breast_Physics_Drag"; + controller["Damping"] = "Breast_Physics_UpDown_Damping"; + controller["MaxEffect"] = "Breast_Physics_UpDown_Max_Effect"; + controller["Spring"] = "Breast_Physics_UpDown_Spring"; + controller["Gain"] = "Breast_Physics_UpDown_Gain"; + LLPhysicsMotion *motion = new LLPhysicsMotion("Breast_Physics_UpDown_Controller", + "mChest", + character, + LLVector3(0,0,1), + controller); + if (!motion->initialize()) + { + llassert_always(FALSE); + return STATUS_FAILURE; + } + addMotion(motion); + } + + // Breast Sway + { + controller_map_t controller; + controller["Mass"] = "Breast_Physics_Mass"; + controller["Gravity"] = "Breast_Physics_Gravity"; + controller["Drag"] = "Breast_Physics_Drag"; + controller["Damping"] = "Breast_Physics_LeftRight_Damping"; + controller["MaxEffect"] = "Breast_Physics_LeftRight_Max_Effect"; + controller["Spring"] = "Breast_Physics_LeftRight_Spring"; + controller["Gain"] = "Breast_Physics_LeftRight_Gain"; + LLPhysicsMotion *motion = new LLPhysicsMotion("Breast_Physics_LeftRight_Controller", + "mChest", + character, + LLVector3(0,-1,0), + controller); + if (!motion->initialize()) + { + llassert_always(FALSE); + return STATUS_FAILURE; + } + addMotion(motion); + } + // Butt Bounce + { + controller_map_t controller; + controller["Mass"] = "Butt_Physics_Mass"; + controller["Gravity"] = "Butt_Physics_Gravity"; + controller["Drag"] = "Butt_Physics_Drag"; + controller["Damping"] = "Butt_Physics_UpDown_Damping"; + controller["MaxEffect"] = "Butt_Physics_UpDown_Max_Effect"; + controller["Spring"] = "Butt_Physics_UpDown_Spring"; + controller["Gain"] = "Butt_Physics_UpDown_Gain"; + LLPhysicsMotion *motion = new LLPhysicsMotion("Butt_Physics_UpDown_Controller", + "mPelvis", + character, + LLVector3(0,0,-1), + controller); + if (!motion->initialize()) + { + llassert_always(FALSE); + return STATUS_FAILURE; + } + addMotion(motion); + } + + // Butt LeftRight + { + controller_map_t controller; + controller["Mass"] = "Butt_Physics_Mass"; + controller["Gravity"] = "Butt_Physics_Gravity"; + controller["Drag"] = "Butt_Physics_Drag"; + controller["Damping"] = "Butt_Physics_LeftRight_Damping"; + controller["MaxEffect"] = "Butt_Physics_LeftRight_Max_Effect"; + controller["Spring"] = "Butt_Physics_LeftRight_Spring"; + controller["Gain"] = "Butt_Physics_LeftRight_Gain"; + LLPhysicsMotion *motion = new LLPhysicsMotion("Butt_Physics_LeftRight_Controller", + "mPelvis", + character, + LLVector3(0,-1,0), + controller); + if (!motion->initialize()) + { + llassert_always(FALSE); + return STATUS_FAILURE; + } + addMotion(motion); + } + + // Belly Bounce + { + controller_map_t controller; + controller["Mass"] = "Belly_Physics_Mass"; + controller["Gravity"] = "Belly_Physics_Gravity"; + controller["Drag"] = "Belly_Physics_Drag"; + controller["Damping"] = "Belly_Physics_UpDown_Damping"; + controller["MaxEffect"] = "Belly_Physics_UpDown_Max_Effect"; + controller["Spring"] = "Belly_Physics_UpDown_Spring"; + controller["Gain"] = "Belly_Physics_UpDown_Gain"; + LLPhysicsMotion *motion = new LLPhysicsMotion("Belly_Physics_UpDown_Controller", + "mPelvis", + character, + LLVector3(0,0,-1), + controller); + if (!motion->initialize()) + { + llassert_always(FALSE); + return STATUS_FAILURE; + } + addMotion(motion); + } + + return STATUS_SUCCESS; +} + +void LLPhysicsMotionController::addMotion(LLPhysicsMotion *motion) +{ + addJointState(motion->getJointState()); + mMotions.push_back(motion); +} + +F32 LLPhysicsMotionController::getMinPixelArea() +{ + return MIN_REQUIRED_PIXEL_AREA_BREAST_MOTION; +} + +// Local space means "parameter space". +F32 LLPhysicsMotion::toLocal(const LLVector3 &world) +{ + LLJoint *joint = mJointState->getJoint(); + const LLQuaternion rotation_world = joint->getWorldRotation(); + + LLVector3 dir_world = mMotionDirectionVec * rotation_world; + dir_world.normalize(); + return world * dir_world; +} + +F32 LLPhysicsMotion::calculateVelocity_local(const F32 time_delta) +{ + LLJoint *joint = mJointState->getJoint(); + const LLVector3 position_world = joint->getWorldPosition(); + const LLQuaternion rotation_world = joint->getWorldRotation(); + const LLVector3 last_position_world = mPosition_world; + const LLVector3 velocity_world = (position_world-last_position_world) / time_delta; + const F32 velocity_local = toLocal(velocity_world); + return velocity_local; +} + +F32 LLPhysicsMotion::calculateAcceleration_local(const F32 velocity_local, + const F32 time_delta) +{ +// const F32 smoothing = getParamValue("Smoothing"); + static const F32 smoothing = 3.0f; // Removed smoothing param since it's probably not necessary + const F32 acceleration_local = velocity_local - mVelocityJoint_local; + + const F32 smoothed_acceleration_local = + acceleration_local * 1.0/smoothing + + mAccelerationJoint_local * (smoothing-1.0)/smoothing; + + return smoothed_acceleration_local; +} + +BOOL LLPhysicsMotionController::onUpdate(F32 time, U8* joint_mask) +{ + // Skip if disabled globally. + if (!gSavedSettings.getBOOL("AvatarPhysics")) + { + return TRUE; + } + + BOOL update_visuals = FALSE; + for (motion_vec_t::iterator iter = mMotions.begin(); + iter != mMotions.end(); + ++iter) + { + LLPhysicsMotion *motion = (*iter); + update_visuals |= motion->onUpdate(time); + } + + if (update_visuals) + mCharacter->updateVisualParams(); + + return TRUE; +} + + +// Return TRUE if character has to update visual params. +BOOL LLPhysicsMotion::onUpdate(F32 time) +{ + // static FILE *mFileWrite = fopen("c:\\temp\\avatar_data.txt","w"); + + if (!mParamDriver) + return FALSE; + + if (!mLastTime) + { + mLastTime = time; + return FALSE; + } + + //////////////////////////////////////////////////////////////////////////////// + // Get all parameters and settings + // + + const F32 time_delta = time - mLastTime; + if (time_delta > 3.0 || time_delta <= 0.01) + { + mLastTime = time; + return FALSE; + } + + // Higher LOD is better. This controls the granularity + // and frequency of updates for the motions. + const F32 lod_factor = LLVOAvatar::sPhysicsLODFactor; + if (lod_factor == 0) + { + return TRUE; + } + + LLJoint *joint = mJointState->getJoint(); + + const F32 behavior_mass = getParamValue("Mass"); + const F32 behavior_gravity = getParamValue("Gravity"); + const F32 behavior_spring = getParamValue("Spring"); + const F32 behavior_gain = getParamValue("Gain"); + const F32 behavior_damping = getParamValue("Damping"); + const F32 behavior_drag = getParamValue("Drag"); + const BOOL physics_test = gSavedSettings.getBOOL("AvatarPhysicsTest") && gAgent.isGodlike(); + + F32 behavior_maxeffect = getParamValue("MaxEffect"); + if (physics_test) + behavior_maxeffect = 1.0f; + + // mPositon_local should be in normalized 0,1 range already. Just making sure... + F32 position_current_local = llclamp(mPosition_local, + 0.0f, + 1.0f); + + // Normalize the param position to be from [0,1]. + // We have to use normalized values because there may be more than one driven param, + // and each of these driven params may have its own range. + // This means we'll do all our calculations in normalized [0,1] local coordinates. + F32 position_user_local = mParamDriver->getWeight(); + position_user_local = (position_user_local - mParamDriver->getMinWeight()) / (mParamDriver->getMaxWeight() - mParamDriver->getMinWeight()); + + // If the effect is turned off then don't process unless we need one more update + // to set the position to the default (i.e. user) position. + if ((behavior_maxeffect == 0) && (position_current_local == position_user_local)) + { + return FALSE; + } + + // + // End parameters and settings + //////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////////////////////////////////// + // Calculate velocity and acceleration in parameter space. + // + + const F32 velocity_joint_local = calculateVelocity_local(time_delta); + const F32 acceleration_joint_local = calculateAcceleration_local(velocity_joint_local, time_delta); + + // + // End velocity and acceleration + //////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////////////////////////////////// + // Calculate the total force + // + + // Spring force is a restoring force towards the original user-set breast position. + // F = kx + const F32 spring_length = position_current_local - position_user_local; + const F32 force_spring = -spring_length * behavior_spring; + + // Acceleration is the force that comes from the change in velocity of the torso. + // F = ma + const F32 force_accel = behavior_gain * (acceleration_joint_local * behavior_mass); + + // Gravity always points downward in world space. + // F = mg + const LLVector3 gravity_world(0,0,1); + const F32 force_gravity = behavior_gain * (toLocal(gravity_world) * behavior_gravity * behavior_mass); + + // Damping is a restoring force that opposes the current velocity. + // F = -kv + const F32 force_damping = -behavior_damping * mVelocity_local; + + // Drag is a force imparted by velocity (intuitively it is similar to wind resistance) + // F = .5kv^2 + const F32 force_drag = .5*behavior_drag*velocity_joint_local*velocity_joint_local*llsgn(velocity_joint_local); + + const F32 force_net = (force_accel + + force_gravity + + force_spring + + force_damping + + force_drag); + + // + // End total force + //////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////////////////////////////////// + // Calculate new params + // + + // Calculate the new acceleration based on the net force. + // a = F/m + const F32 acceleration_new_local = force_net / behavior_mass; + static const F32 max_acceleration = 10.0f; // magic number, used to be customizable. + F32 velocity_new_local = mVelocity_local + acceleration_new_local; + velocity_new_local = llclamp(velocity_new_local, + -max_acceleration, max_acceleration); + + // Temporary debugging setting to cause all avatars to move, for profiling purposes. + if (physics_test) + { + velocity_new_local = sin(time*4.0); + } + // Calculate the new parameters, or remain unchanged if max speed is 0. + F32 position_new_local = position_current_local + velocity_new_local*time_delta; + if (behavior_maxeffect == 0) + position_new_local = position_user_local; + + // Zero out the velocity if the param is being pushed beyond its limits. + if ((position_new_local < 0 && velocity_new_local < 0) || + (position_new_local > 1 && velocity_new_local > 0)) + { + velocity_new_local = 0; + } + + // Check for NaN values. A NaN value is detected if the variables doesn't equal itself. + // If NaN, then reset everything. + if ((mPosition_local != mPosition_local) || + (mVelocity_local != mVelocity_local) || + (position_new_local != position_new_local)) + { + position_new_local = 0; + position_current_local = 0; + position_user_local = 0; + mVelocity_local = 0; + mVelocityJoint_local = 0; + mAccelerationJoint_local = 0; + mPosition_local = 0; + mPosition_world = LLVector3(0,0,0); + } + + const F32 position_new_local_clamped = llclamp(position_new_local, + 0.0f, + 1.0f); + + LLDriverParam *driver_param = dynamic_cast<LLDriverParam *>(mParamDriver); + llassert_always(driver_param); + if (driver_param) + { + // If this is one of our "hidden" driver params, then make sure it's + // the default value. + if ((driver_param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) && + (driver_param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT)) + { + mCharacter->setVisualParamWeight(driver_param, + 0, + FALSE); + } + for (LLDriverParam::entry_list_t::iterator iter = driver_param->mDriven.begin(); + iter != driver_param->mDriven.end(); + ++iter) + { + LLDrivenEntry &entry = (*iter); + LLViewerVisualParam *driven_param = entry.mParam; + setParamValue(driven_param,position_new_local_clamped, behavior_maxeffect); + } + } + + // + // End calculate new params + //////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////// + // Conditionally update the visual params + // + + // Updating the visual params (i.e. what the user sees) is fairly expensive. + // So only update if the params have changed enough, and also take into account + // the graphics LOD settings. + + BOOL update_visuals = FALSE; + + // For non-self, if the avatar is small enough visually, then don't update. + const F32 area_for_max_settings = 0.0; + const F32 area_for_min_settings = 1400.0; + const F32 area_for_this_setting = area_for_max_settings + (area_for_min_settings-area_for_max_settings)*(1.0-lod_factor); + const F32 pixel_area = fsqrtf(mCharacter->getPixelArea()); + + const BOOL is_self = (dynamic_cast<LLVOAvatarSelf *>(mCharacter) != NULL); + if ((pixel_area > area_for_this_setting) || is_self) + { + const F32 position_diff_local = llabs(mPositionLastUpdate_local-position_new_local_clamped); + const F32 min_delta = (1.01f-lod_factor)*0.4f; + if (llabs(position_diff_local) > min_delta) + { + update_visuals = TRUE; + mPositionLastUpdate_local = position_new_local; + } + } + + // + // End update visual params + //////////////////////////////////////////////////////////////////////////////// + + mVelocityJoint_local = velocity_joint_local; + + mVelocity_local = velocity_new_local; + mAccelerationJoint_local = acceleration_joint_local; + mPosition_local = position_new_local; + + mPosition_world = joint->getWorldPosition(); + mLastTime = time; + + /* + // Write out debugging info into a spreadsheet. + if (mFileWrite != NULL && is_self) + { + fprintf(mFileWrite,"%f\t%f\t%f \t\t%f \t\t%f\t%f\t%f\t \t\t%f\t%f\t%f\t%f\t%f \t\t%f\t%f\t%f\n", + position_new_local, + velocity_new_local, + acceleration_new_local, + + time_delta, + + mPosition_world[0], + mPosition_world[1], + mPosition_world[2], + + force_net, + force_spring, + force_accel, + force_damping, + force_drag, + + spring_length, + velocity_joint_local, + acceleration_joint_local + ); + } + */ + + return update_visuals; +} + +// Range of new_value_local is assumed to be [0 , 1] normalized. +void LLPhysicsMotion::setParamValue(LLViewerVisualParam *param, + F32 new_value_normalized, + F32 behavior_maxeffect) +{ + const F32 value_min_local = param->getMinWeight(); + const F32 value_max_local = param->getMaxWeight(); + const F32 min_val = 0.5f-behavior_maxeffect/2.0; + const F32 max_val = 0.5f+behavior_maxeffect/2.0; + + // Scale from [0,1] to [min_val,max_val] + const F32 new_value_rescaled = min_val + (max_val-min_val) * new_value_normalized; + + // Scale from [0,1] to [value_min_local,value_max_local] + const F32 new_value_local = value_min_local + (value_max_local-value_min_local) * new_value_rescaled; + + mCharacter->setVisualParamWeight(param, + new_value_local, + FALSE); +} diff --git a/indra/newview/llphysicsmotion.h b/indra/newview/llphysicsmotion.h index 0c0087d269..b246fa99bb 100644 --- a/indra/newview/llphysicsmotion.h +++ b/indra/newview/llphysicsmotion.h @@ -1,124 +1,118 @@ -/**
- * @file llphysicsmotion.h
- * @brief Implementation of LLPhysicsMotion class.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLPHYSICSMOTIONCONTROLLER_H
-#define LL_LLPHYSICSMOTIONCONTROLLER_H
-
-//-----------------------------------------------------------------------------
-// Header files
-//-----------------------------------------------------------------------------
-#include "llmotion.h"
-#include "llframetimer.h"
-
-#define PHYSICS_MOTION_FADEIN_TIME 1.0f
-#define PHYSICS_MOTION_FADEOUT_TIME 1.0f
-
-class LLPhysicsMotion;
-
-//-----------------------------------------------------------------------------
-// class LLPhysicsMotion
-//-----------------------------------------------------------------------------
-class LLPhysicsMotionController :
- public LLMotion
-{
-public:
- // Constructor
- LLPhysicsMotionController(const LLUUID &id);
-
- // Destructor
- virtual ~LLPhysicsMotionController();
-
-public:
- //-------------------------------------------------------------------------
- // functions to support MotionController and MotionRegistry
- //-------------------------------------------------------------------------
-
- // static constructor
- // all subclasses must implement such a function and register it
- static LLMotion *create(const LLUUID &id) { return new LLPhysicsMotionController(id); }
-
-public:
- //-------------------------------------------------------------------------
- // animation callbacks to be implemented by subclasses
- //-------------------------------------------------------------------------
-
- // motions must specify whether or not they loop
- virtual BOOL getLoop() { return TRUE; }
-
- // motions must report their total duration
- virtual F32 getDuration() { return 0.0; }
-
- // motions must report their "ease in" duration
- virtual F32 getEaseInDuration() { return PHYSICS_MOTION_FADEIN_TIME; }
-
- // motions must report their "ease out" duration.
- virtual F32 getEaseOutDuration() { return PHYSICS_MOTION_FADEOUT_TIME; }
-
- // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
- virtual F32 getMinPixelArea();
-
- // motions must report their priority
- virtual LLJoint::JointPriority getPriority() { return LLJoint::MEDIUM_PRIORITY; }
-
- virtual LLMotionBlendType getBlendType() { return ADDITIVE_BLEND; }
-
- // run-time (post constructor) initialization,
- // called after parameters have been set
- // must return true to indicate success and be available for activation
- virtual LLMotionInitStatus onInitialize(LLCharacter *character);
-
- // called when a motion is activated
- // must return TRUE to indicate success, or else
- // it will be deactivated
- virtual BOOL onActivate();
-
- // called per time step
- // must return TRUE while it is active, and
- // must return FALSE when the motion is completed.
- virtual BOOL onUpdate(F32 time, U8* joint_mask);
-
- // called when a motion is deactivated
- virtual void onDeactivate();
-
- LLCharacter* getCharacter() { return mCharacter; }
-
-protected:
- void addMotion(LLPhysicsMotion *motion);
-private:
- LLCharacter* mCharacter;
-
- typedef std::vector<LLPhysicsMotion *> motion_vec_t;
- motion_vec_t mMotions;
-};
-
-#endif // LL_LLPHYSICSMOTION_H
-
+/** + * @file llphysicsmotion.h + * @brief Implementation of LLPhysicsMotion class. + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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$ + */ + +#ifndef LL_LLPHYSICSMOTIONCONTROLLER_H +#define LL_LLPHYSICSMOTIONCONTROLLER_H + +//----------------------------------------------------------------------------- +// Header files +//----------------------------------------------------------------------------- +#include "llmotion.h" +#include "llframetimer.h" + +#define PHYSICS_MOTION_FADEIN_TIME 1.0f +#define PHYSICS_MOTION_FADEOUT_TIME 1.0f + +class LLPhysicsMotion; + +//----------------------------------------------------------------------------- +// class LLPhysicsMotion +//----------------------------------------------------------------------------- +class LLPhysicsMotionController : + public LLMotion +{ +public: + // Constructor + LLPhysicsMotionController(const LLUUID &id); + + // Destructor + virtual ~LLPhysicsMotionController(); + +public: + //------------------------------------------------------------------------- + // functions to support MotionController and MotionRegistry + //------------------------------------------------------------------------- + + // static constructor + // all subclasses must implement such a function and register it + static LLMotion *create(const LLUUID &id) { return new LLPhysicsMotionController(id); } + +public: + //------------------------------------------------------------------------- + // animation callbacks to be implemented by subclasses + //------------------------------------------------------------------------- + + // motions must specify whether or not they loop + virtual BOOL getLoop() { return TRUE; } + + // motions must report their total duration + virtual F32 getDuration() { return 0.0; } + + // motions must report their "ease in" duration + virtual F32 getEaseInDuration() { return PHYSICS_MOTION_FADEIN_TIME; } + + // motions must report their "ease out" duration. + virtual F32 getEaseOutDuration() { return PHYSICS_MOTION_FADEOUT_TIME; } + + // called to determine when a motion should be activated/deactivated based on avatar pixel coverage + virtual F32 getMinPixelArea(); + + // motions must report their priority + virtual LLJoint::JointPriority getPriority() { return LLJoint::MEDIUM_PRIORITY; } + + virtual LLMotionBlendType getBlendType() { return ADDITIVE_BLEND; } + + // run-time (post constructor) initialization, + // called after parameters have been set + // must return true to indicate success and be available for activation + virtual LLMotionInitStatus onInitialize(LLCharacter *character); + + // called when a motion is activated + // must return TRUE to indicate success, or else + // it will be deactivated + virtual BOOL onActivate(); + + // called per time step + // must return TRUE while it is active, and + // must return FALSE when the motion is completed. + virtual BOOL onUpdate(F32 time, U8* joint_mask); + + // called when a motion is deactivated + virtual void onDeactivate(); + + LLCharacter* getCharacter() { return mCharacter; } + +protected: + void addMotion(LLPhysicsMotion *motion); +private: + LLCharacter* mCharacter; + + typedef std::vector<LLPhysicsMotion *> motion_vec_t; + motion_vec_t mMotions; +}; + +#endif // LL_LLPHYSICSMOTION_H + diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 50bc0b4a98..87a2008e2b 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -517,17 +517,15 @@ BOOL LLSelectMgr::removeObjectFromSelections(const LLUUID &id) { BOOL object_found = FALSE; LLTool *tool = NULL; - if (!gNoRender) - { - tool = LLToolMgr::getInstance()->getCurrentTool(); - // It's possible that the tool is editing an object that is not selected - LLViewerObject* tool_editing_object = tool->getEditingObject(); - if( tool_editing_object && tool_editing_object->mID == id) - { - tool->stopEditing(); - object_found = TRUE; - } + tool = LLToolMgr::getInstance()->getCurrentTool(); + + // It's possible that the tool is editing an object that is not selected + LLViewerObject* tool_editing_object = tool->getEditingObject(); + if( tool_editing_object && tool_editing_object->mID == id) + { + tool->stopEditing(); + object_found = TRUE; } // Iterate through selected objects list and kill the object diff --git a/indra/newview/llsidetray.cpp b/indra/newview/llsidetray.cpp index fcd200d24a..4f18ee1da2 100644 --- a/indra/newview/llsidetray.cpp +++ b/indra/newview/llsidetray.cpp @@ -53,6 +53,8 @@ #include "llsidepanelappearance.h" +#include "llsidetraylistener.h" + //#include "llscrollcontainer.h" using namespace std; @@ -71,6 +73,8 @@ static const std::string TAB_PANEL_CAPTION_TITLE_BOX = "sidetray_tab_title"; LLSideTray* LLSideTray::sInstance = 0; +static LLSideTrayListener sSideTrayListener(LLSideTray::getInstance); + // static LLSideTray* LLSideTray::getInstance() { @@ -454,6 +458,11 @@ LLSideTrayTab* LLSideTrayTab::createInstance () return tab; } +// Now that we know the definition of LLSideTrayTab, we can implement +// tab_cast. +template <> +LLPanel* tab_cast<LLPanel*>(LLSideTrayTab* tab) { return tab; } + ////////////////////////////////////////////////////////////////////////////// // LLSideTrayButton // Side Tray tab button with "tear off" handling. @@ -567,6 +576,8 @@ LLSideTray::LLSideTray(const Params& params) // register handler function to process data from the xml. // panel_name should be specified via "parameter" attribute. commit.add("SideTray.ShowPanel", boost::bind(&LLSideTray::showPanel, this, _2, LLUUID::null)); + commit.add("SideTray.Toggle", boost::bind(&LLSideTray::onToggleCollapse, this)); + commit.add("SideTray.Collapse", boost::bind(&LLSideTray::collapseSideBar, this)); LLTransientFloaterMgr::getInstance()->addControlView(this); LLView* side_bar_tabs = gViewerWindow->getRootView()->getChildView("side_bar_tabs"); if (side_bar_tabs != NULL) diff --git a/indra/newview/llsidetray.h b/indra/newview/llsidetray.h index 2516b5689f..1dddd9e9bc 100644 --- a/indra/newview/llsidetray.h +++ b/indra/newview/llsidetray.h @@ -33,6 +33,13 @@ class LLAccordionCtrl; class LLSideTrayTab; +// Deal with LLSideTrayTab being opaque. Generic do-nothing cast... +template <class T> +T tab_cast(LLSideTrayTab* tab) { return tab; } +// specialized for implementation in presence of LLSideTrayTab definition +template <> +LLPanel* tab_cast<LLPanel*>(LLSideTrayTab* tab); + // added inheritance from LLDestroyClass<LLSideTray> to enable Side Tray perform necessary actions // while disconnecting viewer in LLAppViewer::disconnectViewer(). // LLDestroyClassList::instance().fireCallbacks() calls destroyClass method. See EXT-245. @@ -221,6 +228,9 @@ private: } private: + // Since we provide no public way to query mTabs and mDetachedTabs, give + // LLSideTrayListener friend access. + friend class LLSideTrayListener; LLPanel* mButtonsPanel; typedef std::map<std::string,LLButton*> button_map_t; button_map_t mTabButtons; diff --git a/indra/newview/llsidetraylistener.cpp b/indra/newview/llsidetraylistener.cpp new file mode 100644 index 0000000000..6db13e517d --- /dev/null +++ b/indra/newview/llsidetraylistener.cpp @@ -0,0 +1,162 @@ +/** + * @file llsidetraylistener.cpp + * @author Nat Goodspeed + * @date 2011-02-15 + * @brief Implementation for llsidetraylistener. + * + * $LicenseInfo:firstyear=2011&license=lgpl$ + * Copyright (c) 2011, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "llviewerprecompiledheaders.h" +// associated header +#include "llsidetraylistener.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llsidetray.h" +#include "llsdutil.h" + +LLSideTrayListener::LLSideTrayListener(const Getter& getter): + LLEventAPI("LLSideTray", + "Operations on side tray (e.g. query state, query tabs)"), + mGetter(getter) +{ + add("getCollapsed", "Send on [\"reply\"] an [\"open\"] Boolean", + &LLSideTrayListener::getCollapsed, LLSDMap("reply", LLSD())); + add("getTabs", + "Send on [\"reply\"] a map of tab names and info about them", + &LLSideTrayListener::getTabs, LLSDMap("reply", LLSD())); + add("getPanels", + "Send on [\"reply\"] data about panels available with SideTray.ShowPanel", + &LLSideTrayListener::getPanels, LLSDMap("reply", LLSD())); +} + +void LLSideTrayListener::getCollapsed(const LLSD& event) const +{ + sendReply(LLSDMap("open", ! mGetter()->getCollapsed()), event); +} + +void LLSideTrayListener::getTabs(const LLSD& event) const +{ + LLSD reply; + + LLSideTray* tray = mGetter(); + LLSD::Integer ord(0); + for (LLSideTray::child_list_const_iter_t chi(tray->beginChild()), chend(tray->endChild()); + chi != chend; ++chi, ++ord) + { + LLView* child = *chi; + // How much info is important? Toss in as much as seems reasonable for + // each tab. But to me, at least for the moment, the most important + // item is the tab name. + LLSD info; + // I like the idea of returning a map keyed by tab name. But as + // compared to an array of maps, that loses sequence information. + // Address that by indicating the original order in each map entry. + info["ord"] = ord; + info["visible"] = bool(child->getVisible()); + info["enabled"] = bool(child->getEnabled()); + info["available"] = child->isAvailable(); + reply[child->getName()] = info; + } + + sendReply(reply, event); +} + +static LLSD getTabInfo(LLPanel* tab) +{ + LLSD panels; + for (LLPanel::tree_iterator_t ti(tab->beginTreeDFS()), tend(tab->endTreeDFS()); + ti != tend; ++ti) + { + // *ti is actually an LLView*, which had better not be NULL + LLView* view(*ti); + if (! view) + { + LL_ERRS("LLSideTrayListener") << "LLSideTrayTab '" << tab->getName() + << "' has a NULL child LLView*" << LL_ENDL; + } + + // The logic we use to decide what "panel" names to return is heavily + // based on LLSideTray::showPanel(): the function that actually + // implements the "SideTray.ShowPanel" operation. showPanel(), in + // turn, depends on LLSideTray::openChildPanel(): when + // openChildPanel() returns non-NULL, showPanel() stops searching + // attached and detached LLSideTrayTab tabs. + + // For each LLSideTrayTab, openChildPanel() first calls + // findChildView(panel_name, true). In other words, panel_name need + // not be a direct LLSideTrayTab child, it's sought recursively. + // That's why we use (begin|end)TreeDFS() in this loop. + + // But this tree_iterator_t loop will actually traverse every widget + // in every panel. Returning all those names will not help our caller: + // passing most such names to openChildPanel() would not do what we + // want. Even though the code suggests that passing ANY valid + // side-panel widget name to openChildPanel() will open the tab + // containing that widget, results could get confusing since followup + // (onOpen()) logic wouldn't be invoked, and showPanel() wouldn't stop + // searching because openChildPanel() would return NULL. + + // We must filter these LLView items, using logic that (sigh!) mirrors + // openChildPanel()'s own. + + // openChildPanel() returns a non-NULL LLPanel* when either: + // - the LLView is a direct child of an LLSideTrayPanelContainer + // - the LLView is itself an LLPanel. + // But as LLSideTrayPanelContainer can directly contain LLView items + // that are NOT themselves LLPanels (e.g. "sidebar_me" contains an + // LLButton called "Jump Right Arrow"), we'd better focus only on + // LLSideTrayPanelContainer children that are themselves LLPanel + // items. Which means that the second test completely subsumes the + // first. + LLPanel* panel(dynamic_cast<LLPanel*>(view)); + if (panel) + { + // Maybe it's overkill to construct an LLSD::Map for each panel, but + // the possibility remains that we might want to deliver more info + // about each panel than just its name. + panels.append(LLSDMap("name", panel->getName())); + } + } + + return LLSDMap("panels", panels); +} + +void LLSideTrayListener::getPanels(const LLSD& event) const +{ + LLSD reply; + + LLSideTray* tray = mGetter(); + // Iterate through the attached tabs. + LLSD::Integer ord(0); + for (LLSideTray::child_vector_t::const_iterator + ati(tray->mTabs.begin()), atend(tray->mTabs.end()); + ati != atend; ++ati) + { + // We don't have access to LLSideTrayTab: the class definition is + // hidden in llsidetray.cpp. But as LLSideTrayTab isa LLPanel, use the + // LLPanel API. Unfortunately, without the LLSideTrayTab definition, + // the compiler doesn't even know this LLSideTrayTab* is an LLPanel*. + // Persuade it. + LLPanel* tab(tab_cast<LLPanel*>(*ati)); + reply[tab->getName()] = getTabInfo(tab).with("attached", true).with("ord", ord); + } + + // Now iterate over the detached tabs. These can also be opened via + // SideTray.ShowPanel. + ord = 0; + for (LLSideTray::child_vector_t::const_iterator + dti(tray->mDetachedTabs.begin()), dtend(tray->mDetachedTabs.end()); + dti != dtend; ++dti) + { + LLPanel* tab(tab_cast<LLPanel*>(*dti)); + reply[tab->getName()] = getTabInfo(tab).with("attached", false).with("ord", ord); + } + + sendReply(reply, event); +} diff --git a/indra/newview/llsidetraylistener.h b/indra/newview/llsidetraylistener.h new file mode 100644 index 0000000000..0dd2067433 --- /dev/null +++ b/indra/newview/llsidetraylistener.h @@ -0,0 +1,36 @@ +/** + * @file llsidetraylistener.h + * @author Nat Goodspeed + * @date 2011-02-15 + * @brief + * + * $LicenseInfo:firstyear=2011&license=lgpl$ + * Copyright (c) 2011, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLSIDETRAYLISTENER_H) +#define LL_LLSIDETRAYLISTENER_H + +#include "lleventapi.h" +#include <boost/function.hpp> + +class LLSideTray; +class LLSD; + +class LLSideTrayListener: public LLEventAPI +{ + typedef boost::function<LLSideTray*()> Getter; + +public: + LLSideTrayListener(const Getter& getter); + +private: + void getCollapsed(const LLSD& event) const; + void getTabs(const LLSD& event) const; + void getPanels(const LLSD& event) const; + + Getter mGetter; +}; + +#endif /* ! defined(LL_LLSIDETRAYLISTENER_H) */ diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 8fccb35886..ca908ef822 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -354,11 +354,8 @@ bool idle_startup() LLStringUtil::setLocale (LLTrans::getString(system)); - if (!gNoRender) - { - //note: Removing this line will cause incorrect button size in the login screen. -- bao. - gTextureList.updateImages(0.01f) ; - } + //note: Removing this line will cause incorrect button size in the login screen. -- bao. + gTextureList.updateImages(0.01f) ; if ( STATE_FIRST == LLStartUp::getStartupState() ) { @@ -673,6 +670,7 @@ bool idle_startup() { gUserCredential = gLoginHandler.initializeLoginInfo(); } + // Previous initializeLoginInfo may have generated user credentials. Re-check them. if (gUserCredential.isNull()) { show_connect_box = TRUE; @@ -722,6 +720,8 @@ bool idle_startup() timeout_count = 0; + initialize_edit_menu(); + if (show_connect_box) { // Load all the name information out of the login view @@ -731,15 +731,13 @@ bool idle_startup() { gUserCredential = gLoginHandler.initializeLoginInfo(); } - if (gNoRender) + if (gHeadlessClient) { - LL_ERRS("AppInit") << "Need to autologin or use command line with norender!" << LL_ENDL; + LL_WARNS("AppInit") << "Waiting at connection box in headless client. Did you mean to add autologin params?" << LL_ENDL; } // Make sure the process dialog doesn't hide things gViewerWindow->setShowProgress(FALSE); - initialize_edit_menu(); - // Show the login dialog login_show(); // connect dialog is already shown, so fill in the names @@ -940,10 +938,7 @@ bool idle_startup() gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT); - if (!gNoRender) - { - init_start_screen(agent_location_id); - } + init_start_screen(agent_location_id); // Display the startup progress bar. gViewerWindow->setShowProgress(TRUE); @@ -974,11 +969,6 @@ bool idle_startup() // Setting initial values... LLLoginInstance* login = LLLoginInstance::getInstance(); login->setNotificationsInterface(LLNotifications::getInstance()); - if(gNoRender) - { - // HACK, skip optional updates if you're running drones - login->setSkipOptionalUpdate(true); - } login->setSerialNumber(LLAppViewer::instance()->getSerialNumber()); login->setLastExecEvent(gLastExecEvent); @@ -1264,14 +1254,11 @@ bool idle_startup() gLoginMenuBarView->setVisible( FALSE ); gLoginMenuBarView->setEnabled( FALSE ); - if (!gNoRender) - { - // direct logging to the debug console's line buffer - LLError::logToFixedBuffer(gDebugView->mDebugConsolep); - - // set initial visibility of debug console - gDebugView->mDebugConsolep->setVisible(gSavedSettings.getBOOL("ShowDebugConsole")); - } + // direct logging to the debug console's line buffer + LLError::logToFixedBuffer(gDebugView->mDebugConsolep); + + // set initial visibility of debug console + gDebugView->mDebugConsolep->setVisible(gSavedSettings.getBOOL("ShowDebugConsole")); // // Set message handlers @@ -1299,7 +1286,7 @@ bool idle_startup() //gCacheName is required for nearby chat history loading //so I just moved nearby history loading a few states further - if (!gNoRender && gSavedPerAccountSettings.getBOOL("LogShowHistory")) + if (gSavedPerAccountSettings.getBOOL("LogShowHistory")) { LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); if (nearby_chat) nearby_chat->loadHistory(); @@ -1351,18 +1338,15 @@ bool idle_startup() gAgentCamera.resetCamera(); // Initialize global class data needed for surfaces (i.e. textures) - if (!gNoRender) - { - LL_DEBUGS("AppInit") << "Initializing sky..." << LL_ENDL; - // Initialize all of the viewer object classes for the first time (doing things like texture fetches. - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); + LL_DEBUGS("AppInit") << "Initializing sky..." << LL_ENDL; + // Initialize all of the viewer object classes for the first time (doing things like texture fetches. + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); - gSky.init(initial_sun_direction); + gSky.init(initial_sun_direction); - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - } + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); LL_DEBUGS("AppInit") << "Decoding images..." << LL_ENDL; // For all images pre-loaded into viewer cache, decode them. @@ -1722,46 +1706,43 @@ bool idle_startup() LLUIColorTable::instance().saveUserSettings(); }; - if (!gNoRender) - { - // JC: Initializing audio requests many sounds for download. - init_audio(); - - // JC: Initialize "active" gestures. This may also trigger - // many gesture downloads, if this is the user's first - // time on this machine or -purge has been run. - LLSD gesture_options - = LLLoginInstance::getInstance()->getResponse("gestures"); - if (gesture_options.isDefined()) + // JC: Initializing audio requests many sounds for download. + init_audio(); + + // JC: Initialize "active" gestures. This may also trigger + // many gesture downloads, if this is the user's first + // time on this machine or -purge has been run. + LLSD gesture_options + = LLLoginInstance::getInstance()->getResponse("gestures"); + if (gesture_options.isDefined()) + { + LL_DEBUGS("AppInit") << "Gesture Manager loading " << gesture_options.size() + << LL_ENDL; + uuid_vec_t item_ids; + for(LLSD::array_const_iterator resp_it = gesture_options.beginArray(), + end = gesture_options.endArray(); resp_it != end; ++resp_it) { - LL_DEBUGS("AppInit") << "Gesture Manager loading " << gesture_options.size() - << LL_ENDL; - uuid_vec_t item_ids; - for(LLSD::array_const_iterator resp_it = gesture_options.beginArray(), - end = gesture_options.endArray(); resp_it != end; ++resp_it) - { - // If the id is not specifed in the LLSD, - // the LLSD operator[]() will return a null LLUUID. - LLUUID item_id = (*resp_it)["item_id"]; - LLUUID asset_id = (*resp_it)["asset_id"]; + // If the id is not specifed in the LLSD, + // the LLSD operator[]() will return a null LLUUID. + LLUUID item_id = (*resp_it)["item_id"]; + LLUUID asset_id = (*resp_it)["asset_id"]; - if (item_id.notNull() && asset_id.notNull()) - { - // Could schedule and delay these for later. - const BOOL no_inform_server = FALSE; - const BOOL no_deactivate_similar = FALSE; - LLGestureMgr::instance().activateGestureWithAsset(item_id, asset_id, - no_inform_server, - no_deactivate_similar); - // We need to fetch the inventory items for these gestures - // so we have the names to populate the UI. - item_ids.push_back(item_id); - } + if (item_id.notNull() && asset_id.notNull()) + { + // Could schedule and delay these for later. + const BOOL no_inform_server = FALSE; + const BOOL no_deactivate_similar = FALSE; + LLGestureMgr::instance().activateGestureWithAsset(item_id, asset_id, + no_inform_server, + no_deactivate_similar); + // We need to fetch the inventory items for these gestures + // so we have the names to populate the UI. + item_ids.push_back(item_id); } - // no need to add gesture to inventory observer, it's already made in constructor - LLGestureMgr::instance().setFetchIDs(item_ids); - LLGestureMgr::instance().startFetch(); } + // no need to add gesture to inventory observer, it's already made in constructor + LLGestureMgr::instance().setFetchIDs(item_ids); + LLGestureMgr::instance().startFetch(); } gDisplaySwapBuffers = TRUE; @@ -1782,13 +1763,6 @@ bool idle_startup() // JC - 7/20/2002 gViewerWindow->sendShapeToSim(); - - // Ignore stipend information for now. Money history is on the web site. - // if needed, show the L$ history window - //if (stipend_since_login && !gNoRender) - //{ - //} - // The reason we show the alert is because we want to // reduce confusion for when you log in and your provided // location is not your expected location. So, if this is @@ -2717,7 +2691,7 @@ bool LLStartUp::dispatchURL() || (dx*dx > SLOP*SLOP) || (dy*dy > SLOP*SLOP) ) { - LLURLDispatcher::dispatch(getStartSLURL().getSLURLString(), + LLURLDispatcher::dispatch(getStartSLURL().getSLURLString(), "clicked", NULL, false); } return true; @@ -3214,7 +3188,7 @@ bool process_login_success_response() void transition_back_to_login_panel(const std::string& emsg) { - if (gNoRender) + if (gHeadlessClient && gSavedSettings.getBOOL("AutoLogin")) { LL_WARNS("AppInit") << "Failed to login!" << LL_ENDL; LL_WARNS("AppInit") << emsg << LL_ENDL; diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp index 6fc8153b77..bccabe21a8 100644 --- a/indra/newview/llsurface.cpp +++ b/indra/newview/llsurface.cpp @@ -340,11 +340,6 @@ void LLSurface::connectNeighbor(LLSurface *neighborp, U32 direction) S32 i; LLSurfacePatch *patchp, *neighbor_patchp; - if (gNoRender) - { - return; - } - mNeighbors[direction] = neighborp; neighborp->mNeighbors[gDirOpposite[direction]] = this; diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index e7b5c13860..cb49976e5f 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -310,7 +310,7 @@ void LLIMWellWindow::RowPanel::onChicletSizeChanged(LLChiclet* ctrl, const LLSD& S32 new_text_left = mChiclet->getRect().mRight + CHICLET_HPAD; LLRect text_rect = text->getRect(); text_rect.mLeft = new_text_left; - text->setRect(text_rect); + text->setShape(text_rect); } //--------------------------------------------------------------------------------- diff --git a/indra/newview/lltexturestats.cpp b/indra/newview/lltexturestats.cpp index dd35d5cf83..f820ae65df 100644 --- a/indra/newview/lltexturestats.cpp +++ b/indra/newview/lltexturestats.cpp @@ -37,7 +37,7 @@ void send_texture_stats_to_sim(const LLSD &texture_stats) { LLSD texture_stats_report; // Only send stats if the agent is connected to a region. - if (!gAgent.getRegion() || gNoRender) + if (!gAgent.getRegion()) { return; } diff --git a/indra/newview/lltoastalertpanel.cpp b/indra/newview/lltoastalertpanel.cpp index 8b2f066d41..0f337825e9 100644 --- a/indra/newview/lltoastalertpanel.cpp +++ b/indra/newview/lltoastalertpanel.cpp @@ -220,7 +220,6 @@ LLToastAlertPanel::LLToastAlertPanel( LLNotificationPtr notification, bool modal } static LLUIColor alert_caution_text_color = LLUIColorTable::instance().getColor("AlertCautionTextColor"); - static LLUIColor alert_text_color = LLUIColorTable::instance().getColor("AlertTextColor"); if (mCaution) { LLIconCtrl* icon = LLUICtrlFactory::getInstance()->createFromFile<LLIconCtrl>("alert_icon.xml", this, LLPanel::child_registry_t::instance()); @@ -233,10 +232,6 @@ LLToastAlertPanel::LLToastAlertPanel( LLNotificationPtr notification, bool modal msg_x += 32 + HPAD; msg_box->setColor( alert_caution_text_color ); } - else - { - msg_box->setColor( alert_text_color ); - } LLRect rect; rect.setLeftTopAndSize( msg_x, msg_y, text_rect.getWidth(), text_rect.getHeight() ); diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 1c745906aa..ba243f258a 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -1386,6 +1386,10 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL worn = TRUE; } break; + case LLAssetType::AT_CALLINGCARD: + // Calling Cards in object are disabled for now + // because of incomplete LSL support. See STORM-1117. + return ACCEPT_NO; default: break; } diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index 9549f180df..95bd210ae3 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -37,7 +37,7 @@ #include "llavatarnamecache.h" #include "llviewercontrol.h" #include "llfocusmgr.h" -//#include "llfirstuse.h" +#include "llfirstuse.h" #include "llfloaterland.h" #include "llfloaterreg.h" #include "llfloaterscriptdebug.h" @@ -528,42 +528,17 @@ void LLToolPie::selectionPropertiesReceived() BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) { - if (!mMouseOutsideSlop - && mMouseButtonDown - && gSavedSettings.getBOOL("ClickToWalk")) - { - S32 delta_x = x - mMouseDownX; - S32 delta_y = y - mMouseDownY; - S32 threshold = gSavedSettings.getS32("DragAndDropDistanceThreshold"); - if (delta_x * delta_x + delta_y * delta_y > threshold * threshold) - { - startCameraSteering(); - } - } - mHoverPick = gViewerWindow->pickImmediate(x, y, FALSE); - - if (inCameraSteerMode()) - { - steerCameraWithMouse(x, y); - gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB); - return TRUE; - } - - // perform a separate pick that detects transparent objects since they respond to 1-click actions - LLPickInfo click_action_pick = gViewerWindow->pickImmediate(x, y, TRUE); - - // Show screen-space highlight glow effect - bool show_highlight = false; LLViewerObject *parent = NULL; LLViewerObject *object = mHoverPick.getObject(); - if (object) { parent = object->getRootEdit(); } - LLViewerObject* click_action_object = click_action_pick.getObject(); + // Show screen-space highlight glow effect + bool show_highlight = false; + if (handleMediaHover(mHoverPick)) { // *NOTE: If you think the hover glow conflicts with the media outline, you @@ -572,39 +547,70 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) // cursor set by media object lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl; } - else if (click_action_object && useClickAction(mask, click_action_object, click_action_object->getRootEdit())) + else if (!mMouseOutsideSlop + && mMouseButtonDown + && gSavedSettings.getBOOL("ClickToWalk")) { - show_highlight = true; - ECursorType cursor = cursorFromObject(click_action_object); - gViewerWindow->setCursor(cursor); - lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl; + S32 delta_x = x - mMouseDownX; + S32 delta_y = y - mMouseDownY; + S32 threshold = gSavedSettings.getS32("DragAndDropDistanceThreshold"); + if (delta_x * delta_x + delta_y * delta_y > threshold * threshold) + { + startCameraSteering(); + steerCameraWithMouse(x, y); + gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB); + } + else + { + gViewerWindow->setCursor(UI_CURSOR_ARROW); + } } - - else if ((object && !object->isAvatar() && object->usePhysics()) - || (parent && !parent->isAvatar() && parent->usePhysics())) + else if (inCameraSteerMode()) { - show_highlight = true; + steerCameraWithMouse(x, y); gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB); - lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl; - } - else if ( (object && object->flagHandleTouch()) - || (parent && parent->flagHandleTouch())) - { - show_highlight = true; - gViewerWindow->setCursor(UI_CURSOR_HAND); - lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl; } else { - gViewerWindow->setCursor(UI_CURSOR_ARROW); - lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl; + // perform a separate pick that detects transparent objects since they respond to 1-click actions + LLPickInfo click_action_pick = gViewerWindow->pickImmediate(x, y, TRUE); + + LLViewerObject* click_action_object = click_action_pick.getObject(); - if(!object) + if (click_action_object && useClickAction(mask, click_action_object, click_action_object->getRootEdit())) + { + show_highlight = true; + ECursorType cursor = cursorFromObject(click_action_object); + gViewerWindow->setCursor(cursor); + lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl; + } + + else if ((object && !object->isAvatar() && object->usePhysics()) + || (parent && !parent->isAvatar() && parent->usePhysics())) + { + show_highlight = true; + gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB); + lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl; + } + else if ( (object && object->flagHandleTouch()) + || (parent && parent->flagHandleTouch())) + { + show_highlight = true; + gViewerWindow->setCursor(UI_CURSOR_HAND); + lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl; + } + else { - LLViewerMediaFocus::getInstance()->clearHover(); + gViewerWindow->setCursor(UI_CURSOR_ARROW); + lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl; } } + if(!object) + { + LLViewerMediaFocus::getInstance()->clearHover(); + } + static LLCachedControl<bool> enable_highlight( gSavedSettings, "RenderHoverGlowEnable", false); LLDrawable* drawable = NULL; @@ -662,6 +668,8 @@ BOOL LLToolPie::handleMouseUp(S32 x, S32 y, MASK mask) mAutoPilotDestination->setDuration(3.f); handle_go_to(); + LLFirstUse::notMoving(false); + mBlockClickToWalk = false; return TRUE; @@ -1430,6 +1438,7 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick) { // Make sure keyboard focus is set to the media focus object. gFocusMgr.setKeyboardFocus(LLViewerMediaFocus::getInstance()); + LLEditMenuHandler::gEditMenuHandler = LLViewerMediaFocus::instance().getFocusedMediaImpl(); media_impl->mouseDown(pick.mUVCoords, gKeyboard->currentMask(TRUE)); mMediaMouseCaptureID = mep->getMediaID(); @@ -1709,8 +1718,13 @@ void LLToolPie::showVisualContextMenuEffect() effectp->setDuration(0.25f); } +typedef enum e_near_far +{ + NEAR_INTERSECTION, + FAR_INTERSECTION +} ENearFar; -bool intersect_ray_with_sphere( const LLVector3& ray_pt, const LLVector3& ray_dir, const LLVector3& sphere_center, F32 sphere_radius, LLVector3& intersection_pt) +bool intersect_ray_with_sphere( const LLVector3& ray_pt, const LLVector3& ray_dir, const LLVector3& sphere_center, F32 sphere_radius, e_near_far near_far, LLVector3& intersection_pt) { // do ray/sphere intersection by solving quadratic equation LLVector3 sphere_to_ray_start_vec = ray_pt - sphere_center; @@ -1718,10 +1732,11 @@ bool intersect_ray_with_sphere( const LLVector3& ray_pt, const LLVector3& ray_di F32 C = sphere_to_ray_start_vec.lengthSquared() - (sphere_radius * sphere_radius); F32 discriminant = B*B - 4.f*C; - if (discriminant > 0.f) + if (discriminant >= 0.f) { // intersection detected, now find closest one F32 t0 = (-B - sqrtf(discriminant)) / 2.f; - if (t0 > 0.f) + + if (t0 > 0.f && near_far == NEAR_INTERSECTION) { intersection_pt = ray_pt + ray_dir * t0; } @@ -1732,12 +1747,15 @@ bool intersect_ray_with_sphere( const LLVector3& ray_pt, const LLVector3& ray_di } return true; } - - return false; + else + { // no intersection + return false; + } } void LLToolPie::startCameraSteering() { + LLFirstUse::notMoving(false); mMouseOutsideSlop = true; mBlockClickToWalk = true; @@ -1786,64 +1804,80 @@ void LLToolPie::startCameraSteering() void LLToolPie::steerCameraWithMouse(S32 x, S32 y) { - const F32 MIN_ROTATION_RADIUS_FRACTION = 0.2f; - + const LLViewerCamera& camera = LLViewerCamera::instance(); + const LLCoordFrame& rotation_frame = gAgent.getFrameAgent(); const LLVector3 pick_pos = gAgent.getPosAgentFromGlobal(mSteerPick.mPosGlobal); - const LLVector3 rotation_center = gAgent.getFrameAgent().getOrigin(); - // FIXME: get this to work with camera tilt (i.e. sitting on a rotating object) - const LLVector3 rotation_up_axis(LLVector3::z_axis); - - LLVector3 object_rotation_center = rotation_center + parallel_component(pick_pos - rotation_center, rotation_up_axis); - F32 min_rotation_radius = MIN_ROTATION_RADIUS_FRACTION * dist_vec(rotation_center, LLViewerCamera::instance().getOrigin());; - F32 pick_distance_from_rotation_center = llclamp(dist_vec(pick_pos, object_rotation_center), min_rotation_radius, F32_MAX); + const LLVector3 pick_rotation_center = rotation_frame.getOrigin() + parallel_component(pick_pos - rotation_frame.getOrigin(), rotation_frame.getUpAxis()); + const F32 MIN_ROTATION_RADIUS_FRACTION = 0.2f; + const F32 min_rotation_radius = MIN_ROTATION_RADIUS_FRACTION * dist_vec(pick_rotation_center, camera.getOrigin());; + const F32 pick_distance_from_rotation_center = llclamp(dist_vec(pick_pos, pick_rotation_center), min_rotation_radius, F32_MAX); + const LLVector3 camera_to_rotation_center = pick_rotation_center - camera.getOrigin(); + const LLVector3 adjusted_camera_pos = LLViewerCamera::instance().getOrigin() + projected_vec(camera_to_rotation_center, rotation_frame.getUpAxis()); + const F32 camera_distance_from_rotation_center = dist_vec(adjusted_camera_pos, pick_rotation_center); - LLVector3 mouse_ray = orthogonal_component(gViewerWindow->mouseDirectionGlobal(x, y), rotation_up_axis); + LLVector3 mouse_ray = orthogonal_component(gViewerWindow->mouseDirectionGlobal(x, y), rotation_frame.getUpAxis()); mouse_ray.normalize(); - LLVector3 old_mouse_ray = orthogonal_component(gViewerWindow->mouseDirectionGlobal(mMouseSteerX, mMouseSteerY), rotation_up_axis); + LLVector3 old_mouse_ray = orthogonal_component(gViewerWindow->mouseDirectionGlobal(mMouseSteerX, mMouseSteerY), rotation_frame.getUpAxis()); old_mouse_ray.normalize(); - LLVector3 camera_pos = gAgentCamera.getCameraPositionAgent(); - LLVector3 camera_to_rotation_center = object_rotation_center - camera_pos; - LLVector3 adjusted_camera_pos = camera_pos + projected_vec(camera_to_rotation_center, rotation_up_axis); - LLVector3 rotation_fwd_axis = LLViewerCamera::instance().getAtAxis() - projected_vec(LLViewerCamera::instance().getAtAxis(), rotation_up_axis); - rotation_fwd_axis.normalize(); - F32 pick_dist = dist_vec(pick_pos, adjusted_camera_pos); - + F32 yaw_angle; + F32 old_yaw_angle; LLVector3 mouse_on_sphere; - bool mouse_hit_sphere = intersect_ray_with_sphere(adjusted_camera_pos + (mouse_ray * pick_dist * 1.1f), - -1.f * mouse_ray, - object_rotation_center, - pick_distance_from_rotation_center, - mouse_on_sphere); - LLVector3 old_mouse_on_sphere; - intersect_ray_with_sphere(adjusted_camera_pos + (old_mouse_ray * pick_dist * 1.1f), - -1.f * old_mouse_ray, - object_rotation_center, - pick_distance_from_rotation_center, - old_mouse_on_sphere); - if (mouse_hit_sphere) + if (intersect_ray_with_sphere( + adjusted_camera_pos, + mouse_ray, + pick_rotation_center, + pick_distance_from_rotation_center, + FAR_INTERSECTION, + mouse_on_sphere)) { - // calculate rotation frame in screen space - LLVector3 screen_rotation_up_axis = orthogonal_component(rotation_up_axis, LLViewerCamera::instance().getAtAxis()); - screen_rotation_up_axis.normalize(); - - LLVector3 screen_rotation_left_axis = screen_rotation_up_axis % LLViewerCamera::instance().getAtAxis(); + LLVector3 mouse_sphere_offset = mouse_on_sphere - pick_rotation_center; + yaw_angle = atan2f(mouse_sphere_offset * rotation_frame.getLeftAxis(), mouse_sphere_offset * rotation_frame.getAtAxis()); + } + else + { + yaw_angle = F_PI_BY_TWO + asinf(pick_distance_from_rotation_center / camera_distance_from_rotation_center); + if (mouse_ray * rotation_frame.getLeftAxis() < 0.f) + { + yaw_angle *= -1.f; + } + } - LLVector3 rotation_furthest_pt = object_rotation_center + pick_distance_from_rotation_center * rotation_fwd_axis; - F32 mouse_lateral_distance = llclamp(((mouse_on_sphere - rotation_furthest_pt) * screen_rotation_left_axis) / pick_distance_from_rotation_center, -1.f, 1.f); - F32 old_mouse_lateral_distance = llclamp(((old_mouse_on_sphere - rotation_furthest_pt) * screen_rotation_left_axis) / pick_distance_from_rotation_center, -1.f, 1.f); + if (intersect_ray_with_sphere( + adjusted_camera_pos, + old_mouse_ray, + pick_rotation_center, + pick_distance_from_rotation_center, + FAR_INTERSECTION, + old_mouse_on_sphere)) + { + LLVector3 mouse_sphere_offset = old_mouse_on_sphere - pick_rotation_center; + old_yaw_angle = atan2f(mouse_sphere_offset * rotation_frame.getLeftAxis(), mouse_sphere_offset * rotation_frame.getAtAxis()); + } + else + { + old_yaw_angle = F_PI_BY_TWO + asinf(pick_distance_from_rotation_center / camera_distance_from_rotation_center); - F32 yaw_angle = asinf(mouse_lateral_distance); - F32 old_yaw_angle = asinf(old_mouse_lateral_distance); + if (mouse_ray * rotation_frame.getLeftAxis() < 0.f) + { + old_yaw_angle *= -1.f; + } + } - F32 delta_angle = yaw_angle - old_yaw_angle; - if (!mClockwise) delta_angle *= -1.f; + const F32 delta_angle = yaw_angle - old_yaw_angle; + if (mClockwise) + { gAgent.yaw(delta_angle); - mMouseSteerX = x; - mMouseSteerY = y; } + else + { + gAgent.yaw(-delta_angle); + } + + mMouseSteerX = x; + mMouseSteerY = y; } diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp index 4d6eac4958..6b2cd71d40 100644 --- a/indra/newview/lluilistener.cpp +++ b/indra/newview/lluilistener.cpp @@ -34,9 +34,11 @@ // std headers // external library headers // other Linden headers +#include "llui.h" // getRootView(), resolvePath() #include "lluictrl.h" #include "llerror.h" + LLUIListener::LLUIListener(): LLEventAPI("UI", "LLUICtrl::CommitCallbackRegistry listener.\n" @@ -47,6 +49,12 @@ LLUIListener::LLUIListener(): "as if from a user gesture on a menu -- or a button click.", &LLUIListener::call, LLSD().with("function", LLSD())); + + add("getValue", + "For the UI control identified by the path in [\"path\"], return the control's\n" + "current value as [\"value\"] reply.", + &LLUIListener::getValue, + LLSDMap("path", LLSD())("reply", LLSD())); } void LLUIListener::call(const LLSD& event) const @@ -71,3 +79,23 @@ void LLUIListener::call(const LLSD& event) const (*func)(NULL, event["parameter"]); } } + +void LLUIListener::getValue(const LLSD&event) const +{ + LLSD reply = LLSD::emptyMap(); + + const LLView* root = LLUI::getRootView(); + const LLView* view = LLUI::resolvePath(root, event["path"].asString()); + const LLUICtrl* ctrl(dynamic_cast<const LLUICtrl*>(view)); + + if (ctrl) + { + reply["value"] = ctrl->getValue(); + } + else + { + // *TODO: ??? return something indicating failure to resolve + } + + sendReply(reply, event); +} diff --git a/indra/newview/lluilistener.h b/indra/newview/lluilistener.h index e7847f01e8..08724024dc 100644 --- a/indra/newview/lluilistener.h +++ b/indra/newview/lluilistener.h @@ -41,6 +41,7 @@ public: private: void call(const LLSD& event) const; + void getValue(const LLSD&event) const; }; #endif /* ! defined(LL_LLUILISTENER_H) */ diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index ebbb045f0a..ed4d278e90 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -53,6 +53,7 @@ class LLURLDispatcherImpl { public: static bool dispatch(const LLSLURL& slurl, + const std::string& nav_type, LLMediaCtrl* web, bool trusted_browser); // returns true if handled or explicitly blocked. @@ -61,6 +62,7 @@ public: private: static bool dispatchCore(const LLSLURL& slurl, + const std::string& nav_type, bool right_mouse, LLMediaCtrl* web, bool trusted_browser); @@ -71,6 +73,7 @@ private: // Returns true if handled. static bool dispatchApp(const LLSLURL& slurl, + const std::string& nav_type, bool right_mouse, LLMediaCtrl* web, bool trusted_browser); @@ -78,7 +81,7 @@ private: // by showing panel in Search floater. // Returns true if handled or explicitly blocked. - static bool dispatchRegion(const LLSLURL& slurl, bool right_mouse); + static bool dispatchRegion(const LLSLURL& slurl, const std::string& nav_type, bool right_mouse); // handles secondlife://Ahern/123/45/67/ // Returns true if handled. @@ -97,6 +100,7 @@ private: // static bool LLURLDispatcherImpl::dispatchCore(const LLSLURL& slurl, + const std::string& nav_type, bool right_mouse, LLMediaCtrl* web, bool trusted_browser) @@ -105,9 +109,9 @@ bool LLURLDispatcherImpl::dispatchCore(const LLSLURL& slurl, switch(slurl.getType()) { case LLSLURL::APP: - return dispatchApp(slurl, right_mouse, web, trusted_browser); + return dispatchApp(slurl, nav_type, right_mouse, web, trusted_browser); case LLSLURL::LOCATION: - return dispatchRegion(slurl, right_mouse); + return dispatchRegion(slurl, nav_type, right_mouse); default: return false; } @@ -122,11 +126,12 @@ bool LLURLDispatcherImpl::dispatchCore(const LLSLURL& slurl, // static bool LLURLDispatcherImpl::dispatch(const LLSLURL& slurl, + const std::string& nav_type, LLMediaCtrl* web, bool trusted_browser) { const bool right_click = false; - return dispatchCore(slurl, right_click, web, trusted_browser); + return dispatchCore(slurl, nav_type, right_click, web, trusted_browser); } // static @@ -135,11 +140,12 @@ bool LLURLDispatcherImpl::dispatchRightClick(const LLSLURL& slurl) const bool right_click = true; LLMediaCtrl* web = NULL; const bool trusted_browser = false; - return dispatchCore(slurl, right_click, web, trusted_browser); + return dispatchCore(slurl, "clicked", right_click, web, trusted_browser); } // static bool LLURLDispatcherImpl::dispatchApp(const LLSLURL& slurl, + const std::string& nav_type, bool right_mouse, LLMediaCtrl* web, bool trusted_browser) @@ -147,7 +153,7 @@ bool LLURLDispatcherImpl::dispatchApp(const LLSLURL& slurl, llinfos << "cmd: " << slurl.getAppCmd() << " path: " << slurl.getAppPath() << " query: " << slurl.getAppQuery() << llendl; const LLSD& query_map = LLURI::queryMap(slurl.getAppQuery()); bool handled = LLCommandDispatcher::dispatch( - slurl.getAppCmd(), slurl.getAppPath(), query_map, web, trusted_browser); + slurl.getAppCmd(), slurl.getAppPath(), query_map, web, nav_type, trusted_browser); // alert if we didn't handle this secondlife:///app/ SLURL // (but still return true because it is a valid app SLURL) @@ -159,7 +165,7 @@ bool LLURLDispatcherImpl::dispatchApp(const LLSLURL& slurl, } // static -bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, bool right_mouse) +bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, const std::string& nav_type, bool right_mouse) { if(slurl.getType() != LLSLURL::LOCATION) { @@ -287,10 +293,11 @@ LLTeleportHandler gTeleportHandler; // static bool LLURLDispatcher::dispatch(const std::string& slurl, + const std::string& nav_type, LLMediaCtrl* web, bool trusted_browser) { - return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), web, trusted_browser); + return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), nav_type, web, trusted_browser); } // static @@ -310,7 +317,7 @@ bool LLURLDispatcher::dispatchFromTextEditor(const std::string& slurl) // *TODO: Make this trust model more refined. JC const bool trusted_browser = true; LLMediaCtrl* web = NULL; - return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), web, trusted_browser); + return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), "clicked", web, trusted_browser); } diff --git a/indra/newview/llurldispatcher.h b/indra/newview/llurldispatcher.h index b07db4da3f..6309a97af5 100644 --- a/indra/newview/llurldispatcher.h +++ b/indra/newview/llurldispatcher.h @@ -33,6 +33,7 @@ class LLURLDispatcher public: static bool dispatch(const std::string& slurl, + const std::string& nav_type, LLMediaCtrl* web, bool trusted_browser); // At startup time and on clicks in internal web browsers, @@ -41,6 +42,8 @@ public: // secondlife://RegionName/123/45/67/ // secondlife:///app/agent/3d6181b0-6a4b-97ef-18d8-722652995cf1/show // sl://app/foo/bar + // @param nav_type + // type of navigation type (see LLQtWebKit::LLWebPage::acceptNavigationRequest) // @param web // Pointer to LLMediaCtrl sending URL, can be NULL // @param trusted_browser diff --git a/indra/newview/llurldispatcherlistener.cpp b/indra/newview/llurldispatcherlistener.cpp index d0441d7bfa..c7b9afafef 100644 --- a/indra/newview/llurldispatcherlistener.cpp +++ b/indra/newview/llurldispatcherlistener.cpp @@ -61,7 +61,7 @@ void LLURLDispatcherListener::dispatch(const LLSD& params) const // But for testing, allow a caller to specify untrusted. trusted_browser = params["trusted"].asBoolean(); } - LLURLDispatcher::dispatch(params["url"], NULL, trusted_browser); + LLURLDispatcher::dispatch(params["url"], "clicked", NULL, trusted_browser); } void LLURLDispatcherListener::dispatchRightClick(const LLSD& params) const diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 41b7c13826..8593c4cf79 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -115,8 +115,7 @@ void display_startup() { if ( !gViewerWindow->getActive() || !gViewerWindow->mWindow->getVisible() - || gViewerWindow->mWindow->getMinimized() - || gNoRender ) + || gViewerWindow->mWindow->getMinimized() ) { return; } @@ -294,7 +293,8 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) // Logic for forcing window updates if we're in drone mode. // - if (gNoRender) + // *TODO: Investigate running display() during gHeadlessClient. See if this early exit is needed DK 2011-02-18 + if (gHeadlessClient) { #if LL_WINDOWS static F32 last_update_time = 0.f; @@ -1066,8 +1066,8 @@ bool get_hud_matrices(const LLRect& screen_region, glh::matrix4f &proj, glh::mat F32 scale_y = (F32)gViewerWindow->getWorldViewHeightScaled() / (F32)screen_region.getHeight(); mat.set_scale(glh::vec3f(scale_x, scale_y, 1.f)); mat.set_translate( - glh::vec3f(clamp_rescale((F32)screen_region.getCenterX(), 0.f, (F32)gViewerWindow->getWorldViewWidthScaled(), 0.5f * scale_x * aspect_ratio, -0.5f * scale_x * aspect_ratio), - clamp_rescale((F32)screen_region.getCenterY(), 0.f, (F32)gViewerWindow->getWorldViewHeightScaled(), 0.5f * scale_y, -0.5f * scale_y), + glh::vec3f(clamp_rescale((F32)(screen_region.getCenterX() - screen_region.mLeft), 0.f, (F32)gViewerWindow->getWorldViewWidthScaled(), 0.5f * scale_x * aspect_ratio, -0.5f * scale_x * aspect_ratio), + clamp_rescale((F32)(screen_region.getCenterY() - screen_region.mBottom), 0.f, (F32)gViewerWindow->getWorldViewHeightScaled(), 0.5f * scale_y, -0.5f * scale_y), 0.f)); proj *= mat; diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 499165135a..037e22584f 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -2543,23 +2543,7 @@ bool LLViewerMediaImpl::handleKeyHere(KEY key, MASK mask) // Menu keys should be handled by the menu system and not passed to UI elements, but this is how LLTextEditor and LLLineEditor do it... if( MASK_CONTROL & mask ) { - if( 'C' == key ) - { - mMediaSource->copy(); - result = true; - } - else - if( 'V' == key ) - { - mMediaSource->paste(); - result = true; - } - else - if( 'X' == key ) - { - mMediaSource->cut(); - result = true; - } + result = true; } if(!result) @@ -3000,7 +2984,8 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla { LL_DEBUGS("Media") << "MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is: " << plugin->getClickURL() << LL_ENDL; std::string url = plugin->getClickURL(); - LLURLDispatcher::dispatch(url, NULL, mTrustedBrowser); + std::string nav_type = plugin->getClickNavType(); + LLURLDispatcher::dispatch(url, nav_type, NULL, mTrustedBrowser); } break; case MEDIA_EVENT_CLICK_LINK_HREF: diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp index de52aa17d1..4543a1ba9a 100644 --- a/indra/newview/llviewermediafocus.cpp +++ b/indra/newview/llviewermediafocus.cpp @@ -113,6 +113,11 @@ void LLViewerMediaFocus::setFocusFace(LLPointer<LLViewerObject> objectp, S32 fac media_impl->focus(true); gFocusMgr.setKeyboardFocus(this); + LLViewerMediaImpl* impl = getFocusedMediaImpl(); + if (impl) + { + LLEditMenuHandler::gEditMenuHandler = impl; + } // We must do this before processing the media HUD zoom, or it may zoom to the wrong face. update(); @@ -139,6 +144,13 @@ void LLViewerMediaFocus::setFocusFace(LLPointer<LLViewerObject> objectp, S32 fac { gFocusMgr.setKeyboardFocus(NULL); } + + LLViewerMediaImpl* impl = getFocusedMediaImpl(); + if (LLEditMenuHandler::gEditMenuHandler == impl) + { + LLEditMenuHandler::gEditMenuHandler = NULL; + } + mFocusedImplID = LLUUID::null; if (objectp.notNull()) diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index bd46ee1b67..5a3baf2650 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -856,40 +856,36 @@ void toggle_destination_and_avatar_picker(const LLSD& show) LLButton* avatar_btn = gViewerWindow->getRootView()->getChildView("bottom_tray")->getChild<LLButton>("avatar_btn"); LLButton* destination_btn = gViewerWindow->getRootView()->getChildView("bottom_tray")->getChild<LLButton>("destination_btn"); - switch(panel_idx) - { - case 0: - if (!destinations->getVisible()) - { - container->setVisible(true); - destinations->setVisible(true); - avatar_picker->setVisible(false); - LLFirstUse::notUsingDestinationGuide(false); - avatar_btn->setToggleState(false); - destination_btn->setToggleState(true); - return; - } - break; - case 1: - if (!avatar_picker->getVisible()) - { - container->setVisible(true); - destinations->setVisible(false); - avatar_picker->setVisible(true); - avatar_btn->setToggleState(true); - destination_btn->setToggleState(false); - return; - } - break; - default: - break; + if (panel_idx == 0 + && !destinations->getVisible()) + { // opening destinations guide + container->setVisible(true); + destinations->setVisible(true); + avatar_picker->setVisible(false); + LLFirstUse::notUsingDestinationGuide(false); + avatar_btn->setToggleState(false); + destination_btn->setToggleState(true); + gSavedSettings.setS32("DestinationsAndAvatarsVisibility", 0); + } + else if (panel_idx == 1 + && !avatar_picker->getVisible()) + { // opening avatar picker + container->setVisible(true); + destinations->setVisible(false); + avatar_picker->setVisible(true); + avatar_btn->setToggleState(true); + destination_btn->setToggleState(false); + gSavedSettings.setS32("DestinationsAndAvatarsVisibility", 1); + } + else + { // toggling off dest guide or avatar picker + container->setVisible(false); + destinations->setVisible(false); + avatar_picker->setVisible(false); + avatar_btn->setToggleState(false); + destination_btn->setToggleState(false); + gSavedSettings.setS32("DestinationsAndAvatarsVisibility", -1); } - - container->setVisible(false); - destinations->setVisible(false); - avatar_picker->setVisible(false); - avatar_btn->setToggleState(false); - destination_btn->setToggleState(false); }; @@ -6416,12 +6412,12 @@ class LLToolsSelectedScriptAction : public view_listener_t else if (action == "start") { name = "start_queue"; - msg = "Running"; + msg = "SetRunning"; } else if (action == "stop") { name = "stop_queue"; - msg = "RunningNot"; + msg = "SetRunningNot"; } LLUUID id; id.generate(); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 8b52d478e6..9641a0901c 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -343,12 +343,6 @@ void process_layer_data(LLMessageSystem *mesgsys, void **user_data) { LLViewerRegion *regionp = LLWorld::getInstance()->getRegion(mesgsys->getSender()); - if (!regionp || gNoRender) - { - return; - } - - S32 size; S8 type; @@ -2168,10 +2162,6 @@ void god_message_name_cb(const LLAvatarName& av_name, LLChat chat, std::string m void process_improved_im(LLMessageSystem *msg, void **user_data) { - if (gNoRender) - { - return; - } LLUUID from_id; BOOL from_group; LLUUID to_id; @@ -3960,7 +3950,9 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) // LBUTTON and ML_LBUTTON so that using the camera (alt-key) doesn't // trigger a control event. U32 control_flags = gAgent.getControlFlags(); + MASK key_mask = gKeyboard->currentMask(TRUE); + if (key_mask & MASK_ALT || key_mask & MASK_CONTROL) { control_flags &= ~( AGENT_CONTROL_LBUTTON_DOWN | @@ -4279,7 +4271,7 @@ void process_time_synch(LLMessageSystem *mesgsys, void **user_data) gSky.setSunPhase(phase); gSky.setSunTargetDirection(sun_direction, sun_ang_velocity); - if (!gNoRender && !(gSavedSettings.getBOOL("SkyOverrideSimSunPosition") || gSky.getOverrideSun())) + if ( !(gSavedSettings.getBOOL("SkyOverrideSimSunPosition") || gSky.getOverrideSun()) ) { gSky.setSunDirection(sun_direction, sun_ang_velocity); } @@ -5541,21 +5533,12 @@ time_t gLastDisplayedTime = 0; void handle_show_mean_events(void *) { - if (gNoRender) - { - return; - } LLFloaterReg::showInstance("bumps"); //LLFloaterBump::showInstance(); } void mean_name_callback(const LLUUID &id, const std::string& full_name, bool is_group) { - if (gNoRender) - { - return; - } - static const U32 max_collision_list_size = 20; if (gMeanCollisionList.size() > max_collision_list_size) { diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 32cd8dbb39..e8828e63a9 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -469,11 +469,6 @@ void LLViewerObject::initVOClasses() // Initialized shared class stuff first. LLVOAvatar::initClass(); LLVOTree::initClass(); - if (gNoRender) - { - // Don't init anything else in drone mode - return; - } llinfos << "Viewer Object size: " << sizeof(LLViewerObject) << llendl; LLVOGrass::initClass(); LLVOWater::initClass(); @@ -2150,12 +2145,6 @@ BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) } } - if (gNoRender) - { - // Skip drawable stuff if not rendering. - return TRUE; - } - updateDrawable(FALSE); return TRUE; diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 979d91cfcb..da95bacc41 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -636,19 +636,16 @@ void LLViewerObjectList::updateApparentAngles(LLAgent &agent) } - if (!gNoRender) + // Slam priorities for textures that we care about (hovered, selected, and focused) + // Hovered + // Assumes only one level deep of parenting + LLSelectNode* nodep = LLSelectMgr::instance().getHoverNode(); + if (nodep) { - // Slam priorities for textures that we care about (hovered, selected, and focused) - // Hovered - // Assumes only one level deep of parenting - LLSelectNode* nodep = LLSelectMgr::instance().getHoverNode(); - if (nodep) + objectp = nodep->getObject(); + if (objectp) { - objectp = nodep->getObject(); - if (objectp) - { - objectp->boostTexturePriority(); - } + objectp->boostTexturePriority(); } } @@ -1100,7 +1097,7 @@ void LLViewerObjectList::shiftObjects(const LLVector3 &offset) // We need to update many object caches, I'll document this more as I dig through the code // cleaning things out... - if (gNoRender || 0 == offset.magVecSquared()) + if (0 == offset.magVecSquared()) { return; } @@ -1568,11 +1565,6 @@ void LLViewerObjectList::orphanize(LLViewerObject *childp, U32 parent_id, U32 ip void LLViewerObjectList::findOrphans(LLViewerObject* objectp, U32 ip, U32 port) { - if (gNoRender) - { - return; - } - if (objectp->isDead()) { llwarns << "Trying to find orphans for dead obj " << objectp->mID diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp index 40f0b43313..dfa35edef4 100644 --- a/indra/newview/llviewerparcelmedia.cpp +++ b/indra/newview/llviewerparcelmedia.cpp @@ -539,6 +539,12 @@ void LLViewerParcelMedia::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent }; break; + case MEDIA_EVENT_NAVIGATE_ERROR_PAGE: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_ERROR_PAGE" << LL_ENDL; + }; + break; + case MEDIA_EVENT_CLICK_LINK_HREF: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << self->getClickTarget() << "\", uri is " << self->getClickURL() << LL_ENDL; diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index fccd1156d3..e84e4a859a 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -1383,11 +1383,6 @@ void LLViewerParcelMgr::setHoverParcel(const LLVector3d& pos) // static void LLViewerParcelMgr::processParcelOverlay(LLMessageSystem *msg, void **user) { - if (gNoRender) - { - return; - } - // Extract the packed overlay information S32 packed_overlay_size = msg->getSizeFast(_PREHASH_ParcelData, _PREHASH_Data); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index e1d3e8a0b3..c53fdc3393 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -73,8 +73,6 @@ #pragma warning(disable:4355) #endif -extern BOOL gNoRender; - const F32 WATER_TEXTURE_SCALE = 8.f; // Number of times to repeat the water texture across a region const S16 MAX_MAP_DIST = 10; @@ -111,7 +109,7 @@ public: } // Process the SLapp as if it was a secondlife://{PLACE} SLurl - LLURLDispatcher::dispatch(url, web, true); + LLURLDispatcher::dispatch(url, "clicked", web, true); return true; } }; @@ -235,28 +233,19 @@ LLViewerRegion::LLViewerRegion(const U64 &handle, updateRenderMatrix(); mLandp = new LLSurface('l', NULL); - if (!gNoRender) - { - // Create the composition layer for the surface - mCompositionp = new LLVLComposition(mLandp, grids_per_region_edge, region_width_meters/grids_per_region_edge); - mCompositionp->setSurface(mLandp); - - // Create the surfaces - mLandp->setRegion(this); - mLandp->create(grids_per_region_edge, - grids_per_patch_edge, - mOriginGlobal, - mWidth); - } - if (!gNoRender) - { - mParcelOverlay = new LLViewerParcelOverlay(this, region_width_meters); - } - else - { - mParcelOverlay = NULL; - } + // Create the composition layer for the surface + mCompositionp = new LLVLComposition(mLandp, grids_per_region_edge, region_width_meters/grids_per_region_edge); + mCompositionp->setSurface(mLandp); + + // Create the surfaces + mLandp->setRegion(this); + mLandp->create(grids_per_region_edge, + grids_per_patch_edge, + mOriginGlobal, + mWidth); + + mParcelOverlay = new LLViewerParcelOverlay(this, region_width_meters); setOriginGlobal(from_region_handle(handle)); calculateCenterGlobal(); diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 874519a59f..fa60e572ac 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -711,7 +711,7 @@ void send_stats() // but that day is not today. // Only send stats if the agent is connected to a region. - if (!gAgent.getRegion() || gNoRender) + if (!gAgent.getRegion()) { return; } diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index cf7f3f80ad..f5fb074992 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1415,63 +1415,61 @@ BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/) // mRawImage->getWidth(), mRawImage->getHeight(),mRawImage->getDataSize()) // << mID.getString() << llendl; BOOL res = TRUE; - if (!gNoRender) - { - // store original size only for locally-sourced images - if (mUrl.compare(0, 7, "file://") == 0) - { - mOrigWidth = mRawImage->getWidth(); - mOrigHeight = mRawImage->getHeight(); - // leave black border, do not scale image content - mRawImage->expandToPowerOfTwo(MAX_IMAGE_SIZE, FALSE); - - mFullWidth = mRawImage->getWidth(); - mFullHeight = mRawImage->getHeight(); - setTexelsPerImage(); - } - else - { - mOrigWidth = mFullWidth; - mOrigHeight = mFullHeight; - } + // store original size only for locally-sourced images + if (mUrl.compare(0, 7, "file://") == 0) + { + mOrigWidth = mRawImage->getWidth(); + mOrigHeight = mRawImage->getHeight(); - bool size_okay = true; + // leave black border, do not scale image content + mRawImage->expandToPowerOfTwo(MAX_IMAGE_SIZE, FALSE); - U32 raw_width = mRawImage->getWidth() << mRawDiscardLevel; - U32 raw_height = mRawImage->getHeight() << mRawDiscardLevel; - if( raw_width > MAX_IMAGE_SIZE || raw_height > MAX_IMAGE_SIZE ) - { - llinfos << "Width or height is greater than " << MAX_IMAGE_SIZE << ": (" << raw_width << "," << raw_height << ")" << llendl; - size_okay = false; - } - - if (!LLImageGL::checkSize(mRawImage->getWidth(), mRawImage->getHeight())) - { - // A non power-of-two image was uploaded (through a non standard client) - llinfos << "Non power of two width or height: (" << mRawImage->getWidth() << "," << mRawImage->getHeight() << ")" << llendl; - size_okay = false; - } - - if( !size_okay ) - { - // An inappropriately-sized image was uploaded (through a non standard client) - // We treat these images as missing assets which causes them to - // be renderd as 'missing image' and to stop requesting data - setIsMissingAsset(); - destroyRawImage(); - return FALSE; - } - - if(!(res = insertToAtlas())) - { - res = mGLTexturep->createGLTexture(mRawDiscardLevel, mRawImage, usename, TRUE, mBoostLevel); - resetFaceAtlas() ; - } - setActive() ; + mFullWidth = mRawImage->getWidth(); + mFullHeight = mRawImage->getHeight(); + setTexelsPerImage(); + } + else + { + mOrigWidth = mFullWidth; + mOrigHeight = mFullHeight; + } + + bool size_okay = true; + + U32 raw_width = mRawImage->getWidth() << mRawDiscardLevel; + U32 raw_height = mRawImage->getHeight() << mRawDiscardLevel; + if( raw_width > MAX_IMAGE_SIZE || raw_height > MAX_IMAGE_SIZE ) + { + llinfos << "Width or height is greater than " << MAX_IMAGE_SIZE << ": (" << raw_width << "," << raw_height << ")" << llendl; + size_okay = false; + } + + if (!LLImageGL::checkSize(mRawImage->getWidth(), mRawImage->getHeight())) + { + // A non power-of-two image was uploaded (through a non standard client) + llinfos << "Non power of two width or height: (" << mRawImage->getWidth() << "," << mRawImage->getHeight() << ")" << llendl; + size_okay = false; + } + + if( !size_okay ) + { + // An inappropriately-sized image was uploaded (through a non standard client) + // We treat these images as missing assets which causes them to + // be renderd as 'missing image' and to stop requesting data + setIsMissingAsset(); + destroyRawImage(); + return FALSE; + } + + if(!(res = insertToAtlas())) + { + res = mGLTexturep->createGLTexture(mRawDiscardLevel, mRawImage, usename, TRUE, mBoostLevel); + resetFaceAtlas() ; } + setActive() ; - if (!mForceToSaveRawImage) + if (!needsToSaveRawImage()) { mNeedsAux = FALSE; destroyRawImage(); diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 10126219f8..06f6ff23c2 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -91,11 +91,6 @@ void LLViewerTextureList::init() sNumImages = 0; mMaxResidentTexMemInMegaBytes = 0; mMaxTotalTextureMemInMegaBytes = 0 ; - if (gNoRender) - { - // Don't initialize GL stuff if we're not rendering. - return; - } mUpdateStats = TRUE; @@ -345,13 +340,6 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& LLGLenum primary_format, const LLUUID& force_id) { - if (gNoRender) - { - // Never mind that this ignores image_set_id; - // getImage() will handle that later. - return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLViewerTexture::BOOST_UI); - } - // generate UUID based on hash of filename LLUUID new_id; if (force_id.notNull()) @@ -741,7 +729,7 @@ static LLFastTimer::DeclareTimer FTM_IMAGE_CREATE("Create Images"); F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time) { - if (gNoRender || gGLManager.mIsDisabled) return 0.0f; + if (gGLManager.mIsDisabled) return 0.0f; // // Create GL textures for all textures that need them (images which have been @@ -876,7 +864,6 @@ void LLViewerTextureList::updateImagesUpdateStats() void LLViewerTextureList::decodeAllImages(F32 max_time) { LLTimer timer; - if(gNoRender) return; // Update texture stats and priorities std::vector<LLPointer<LLViewerFetchedTexture> > image_list; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 8e049e76df..4305349ea2 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -925,7 +925,7 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi { if (drop) { - LLURLDispatcher::dispatch( dropped_slurl.getSLURLString(), NULL, true ); + LLURLDispatcher::dispatch( dropped_slurl.getSLURLString(), "clicked", NULL, true ); return LLWindowCallbacks::DND_MOVE; } return LLWindowCallbacks::DND_COPY; @@ -1251,8 +1251,9 @@ void LLViewerWindow::handleMenuSelect(LLWindow *window, S32 menu_item) BOOL LLViewerWindow::handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S32 height) { + // *TODO: Enable similar information output for other platforms? DK 2011-02-18 #if LL_WINDOWS - if (gNoRender) + if (gHeadlessClient) { HWND window_handle = (HWND)window->getPlatformWindow(); PAINTSTRUCT ps; @@ -1282,7 +1283,7 @@ BOOL LLViewerWindow::handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S len = temp_str.length(); TextOutA(hdc, 0, 25, temp_str.c_str(), len); - TextOutA(hdc, 0, 50, "Set \"DisableRendering FALSE\" in settings.ini file to reenable", 61); + TextOutA(hdc, 0, 50, "Set \"HeadlessClient FALSE\" in settings.ini file to reenable", 61); EndPaint(window_handle, &ps); return TRUE; } @@ -1316,7 +1317,8 @@ void LLViewerWindow::handleDataCopy(LLWindow *window, S32 data_type, void *data) std::string url = (const char*)data; LLMediaCtrl* web = NULL; const bool trusted_browser = false; - if (LLURLDispatcher::dispatch(url, web, trusted_browser)) + // don't treat slapps coming from external browsers as "clicks" as this would bypass throttling + if (LLURLDispatcher::dispatch(url, "", web, trusted_browser)) { // bring window to foreground, as it has just been "launched" from a URL mWindow->bringToFront(); @@ -1430,9 +1432,9 @@ LLViewerWindow::LLViewerWindow( mWindow = LLWindowManager::createWindow(this, title, name, x, y, width, height, 0, fullscreen, - gNoRender, + gHeadlessClient, gSavedSettings.getBOOL("DisableVerticalSync"), - !gNoRender, + !gHeadlessClient, ignore_pixel_depth, gSavedSettings.getBOOL("RenderUseFBO") ? 0 : gSavedSettings.getU32("RenderFSAASamples")); //don't use window level anti-aliasing if FBOs are enabled @@ -1801,19 +1803,18 @@ void LLViewerWindow::initWorldUI() LLMediaCtrl* avatar_picker = avatar_picker_destination_guide_container->findChild<LLMediaCtrl>("avatar_picker_contents"); if (destinations) { + destinations->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); destinations->navigateTo(gSavedSettings.getString("DestinationGuideURL"), "text/html"); } if (avatar_picker) { + avatar_picker->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); avatar_picker->navigateTo(gSavedSettings.getString("AvatarPickerURL"), "text/html"); } - if (gSavedSettings.getBOOL("FirstLoginThisInstall")) - { - toggle_destination_and_avatar_picker(0); - gSavedSettings.setBOOL("FirstLoginThisInstall", FALSE); - } + // show destinations by default + toggle_destination_and_avatar_picker(gSavedSettings.getS32("DestinationsAndAvatarsVisibility")); } // Destroy the UI @@ -1895,11 +1896,8 @@ void LLViewerWindow::shutdownGL() LLVertexBuffer::cleanupClass(); llinfos << "Stopping GL during shutdown" << llendl; - if (!gNoRender) - { - stopGL(FALSE); - stop_glerror(); - } + stopGL(FALSE); + stop_glerror(); gGL.shutdown(); } @@ -1963,11 +1961,6 @@ void LLViewerWindow::reshape(S32 width, S32 height) // may have been destructed. if (!LLApp::isExiting()) { - if (gNoRender) - { - return; - } - gWindowResized = TRUE; // update our window rectangle @@ -2604,12 +2597,8 @@ void LLViewerWindow::updateUI() S32 x = mCurrentMousePoint.mX; S32 y = mCurrentMousePoint.mY; - MASK mask = gKeyboard->currentMask(TRUE); - if (gNoRender) - { - return; - } + MASK mask = gKeyboard->currentMask(TRUE); if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST)) { @@ -3000,7 +2989,8 @@ void LLViewerWindow::updateLayout() } // Update the location of the blue box tool popup LLCoordGL select_center_screen; - gFloaterTools->updatePopup( select_center_screen, gKeyboard->currentMask(TRUE) ); + MASK mask = gKeyboard->currentMask(TRUE); + gFloaterTools->updatePopup( select_center_screen, mask ); } else { @@ -3126,7 +3116,8 @@ void LLViewerWindow::updateKeyboardFocus() // sync all floaters with their focus state gFloaterView->highlightFocusedFloater(); gSnapshotFloaterView->highlightFocusedFloater(); - if ((gKeyboard->currentMask(TRUE) & MASK_CONTROL) == 0) + MASK mask = gKeyboard->currentMask(TRUE); + if ((mask & MASK_CONTROL) == 0) { // control key no longer held down, finish cycle mode gFloaterView->setCycleMode(FALSE); @@ -3439,11 +3430,6 @@ BOOL LLViewerWindow::clickPointOnSurfaceGlobal(const S32 x, const S32 y, LLViewe void LLViewerWindow::pickAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(const LLPickInfo& info), BOOL pick_transparent) { - if (gNoRender) - { - return; - } - BOOL in_build_mode = LLFloaterReg::instanceVisible("build"); if (in_build_mode || LLDrawPoolAlpha::sShowDebugAlpha) { @@ -3479,11 +3465,6 @@ void LLViewerWindow::schedulePick(LLPickInfo& pick_info) void LLViewerWindow::performPick() { - if (gNoRender) - { - return; - } - if (!mPicks.empty()) { std::vector<LLPickInfo>::iterator pick_it; @@ -3515,11 +3496,6 @@ void LLViewerWindow::returnEmptyPicks() // Performs the GL object/land pick. LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot, BOOL pick_transparent) { - if (gNoRender) - { - return LLPickInfo(); - } - BOOL in_build_mode = LLFloaterReg::instanceVisible("build"); if (in_build_mode || LLDrawPoolAlpha::sShowDebugAlpha) { @@ -3529,7 +3505,8 @@ LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot, BOOL pick_trans } // shortcut queueing in mPicks and just update mLastPick in place - mLastPick = LLPickInfo(LLCoordGL(x, y_from_bot), gKeyboard->currentMask(TRUE), pick_transparent, TRUE, NULL); + MASK key_mask = gKeyboard->currentMask(TRUE); + mLastPick = LLPickInfo(LLCoordGL(x, y_from_bot), key_mask, pick_transparent, TRUE, NULL); mLastPick.fetchResults(); return mLastPick; @@ -4785,12 +4762,9 @@ bool LLViewerWindow::onAlert(const LLSD& notify) { LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - if (gNoRender) + if (gHeadlessClient) { llinfos << "Alert: " << notification->getName() << llendl; - notification->respond(LLSD::emptyMap()); - LLNotifications::instance().cancel(notification); - return false; } // If we're in mouselook, the mouse is hidden and so the user can't click diff --git a/indra/newview/llviewerwindowlistener.cpp b/indra/newview/llviewerwindowlistener.cpp index 0b52948680..1fe5fc9800 100644 --- a/indra/newview/llviewerwindowlistener.cpp +++ b/indra/newview/llviewerwindowlistener.cpp @@ -65,7 +65,6 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow): void LLViewerWindowListener::saveSnapshot(const LLSD& event) const { - LLReqID reqid(event); typedef std::map<LLSD::String, LLViewerWindow::ESnapshotType> TypeMap; TypeMap types; #define tp(name) types[#name] = LLViewerWindow::SNAPSHOT_TYPE_##name @@ -98,9 +97,7 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const type = found->second; } bool ok = mViewerWindow->saveSnapshot(event["filename"], width, height, showui, rebuild, type); - LLSD response(reqid.makeResponse()); - response["ok"] = ok; - LLEventPumps::instance().obtain(event["reply"]).post(response); + sendReply(LLSDMap("ok", ok), event); } void LLViewerWindowListener::requestReshape(LLSD const & event_data) const diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 2c5e728c87..f1934933b5 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1302,18 +1302,8 @@ void LLVOAvatar::initInstance(void) } - if (gNoRender) - { - return; - } - buildCharacter(); - if (gNoRender) - { - return; - } - // preload specific motions here createMotion( ANIM_AGENT_CUSTOMIZE); createMotion( ANIM_AGENT_CUSTOMIZE_DONE); @@ -1755,12 +1745,6 @@ void LLVOAvatar::buildCharacter() BOOL status = loadAvatar(); stop_glerror(); - if (gNoRender) - { - // Still want to load the avatar skeleton so visual parameters work. - return; - } - // gPrintMessagesThisFrame = TRUE; lldebugs << "Avatar load took " << timer.getElapsedTimeF32() << " seconds." << llendl; @@ -2231,7 +2215,7 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) setPixelAreaAndAngle(gAgent); // force asynchronous drawable update - if(mDrawable.notNull() && !gNoRender) + if(mDrawable.notNull()) { LLFastTimer t(FTM_JOINT_UPDATE); @@ -2288,11 +2272,6 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) LLVector3 root_pos_last = mRoot.getWorldPosition(); BOOL detailed_update = updateCharacter(agent); - if (gNoRender) - { - return TRUE; - } - static LLUICachedControl<bool> visualizers_in_calls("ShowVoiceVisualizersInCalls", false); bool voice_enabled = (visualizers_in_calls || LLVoiceClient::getInstance()->inProximalChannel()) && LLVoiceClient::getInstance()->getVoiceEnabled(mID); @@ -3265,17 +3244,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) } } - if (gNoRender) - { - // Hack if we're running drones... - if (isSelf()) - { - gAgent.setPositionAgent(getPositionAgent()); - } - return FALSE; - } - - LLVector3d root_pos_global; if (!mIsBuilt) @@ -4202,7 +4170,7 @@ void LLVOAvatar::updateTextures() { BOOL render_avatar = TRUE; - if (mIsDummy || gNoRender) + if (mIsDummy) { return; } @@ -4476,11 +4444,6 @@ void LLVOAvatar::processAnimationStateChanges() { LLMemType mt(LLMemType::MTYPE_AVATAR); - if (gNoRender) - { - return; - } - if ( isAnyAnimationSignaled(AGENT_WALK_ANIMS, NUM_AGENT_WALK_ANIMS) ) { startMotion(ANIM_AGENT_WALK_ADJUST); @@ -4875,7 +4838,7 @@ void LLVOAvatar::getGround(const LLVector3 &in_pos_agent, LLVector3 &out_pos_age LLVector3d z_vec(0.0f, 0.0f, 1.0f); LLVector3d p0_global, p1_global; - if (gNoRender || mIsDummy) + if (mIsDummy) { outNorm.setVec(z_vec); out_pos_agent = in_pos_agent; @@ -5447,11 +5410,6 @@ BOOL LLVOAvatar::loadLayersets() //----------------------------------------------------------------------------- void LLVOAvatar::updateVisualParams() { - if (gNoRender) - { - return; - } - setSex( (getVisualParamWeight( "male" ) > 0.5f) ? SEX_MALE : SEX_FEMALE ); LLCharacter::updateVisualParams(); @@ -6182,8 +6140,6 @@ LLMotion* LLVOAvatar::findMotion(const LLUUID& id) const void LLVOAvatar::updateMeshTextures() { // llinfos << "updateMeshTextures" << llendl; - if (gNoRender) return; - // if user has never specified a texture, assign the default for (U32 i=0; i < getNumTEs(); i++) { @@ -6837,11 +6793,6 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) } } - if (gNoRender) - { - return; - } - ESex old_sex = getSex(); // llinfos << "LLVOAvatar::processAvatarAppearance()" << llendl; diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 799ed433d7..3b4066e3ca 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -2513,10 +2513,6 @@ bool LLVOAvatarSelf::sendAppearanceMessage(LLMessageSystem *mesgsys) const //------------------------------------------------------------------------ BOOL LLVOAvatarSelf::needsRenderBeam() { - if (gNoRender) - { - return FALSE; - } LLTool *tool = LLToolMgr::getInstance()->getCurrentTool(); BOOL is_touching_or_grabbing = (tool == LLToolGrab::getInstance() && LLToolGrab::getInstance()->isEditing()); diff --git a/indra/newview/llwaterparammanager.cpp b/indra/newview/llwaterparammanager.cpp index d239347810..4b3a9a4dc3 100644 --- a/indra/newview/llwaterparammanager.cpp +++ b/indra/newview/llwaterparammanager.cpp @@ -72,6 +72,8 @@ LLWaterParamManager::LLWaterParamManager() : mWave1Dir(.5f, .5f, "wave1Dir"), mWave2Dir(.5f, .5f, "wave2Dir"), mDensitySliderValue(1.0f), + mPrevFogDensity(16.0f), // 2^4 + mPrevFogColor(22.f/255.f, 43.f/255.f, 54.f/255.f, 0.0f), mWaterFogKS(1.0f) { } @@ -265,6 +267,20 @@ void LLWaterParamManager::update(LLViewerCamera * cam) // update the shaders and the menu propagateParameters(); + // If water fog color has been changed, save it. + if (mPrevFogColor != mFogColor) + { + gSavedSettings.setColor4("WaterFogColor", mFogColor); + mPrevFogColor = mFogColor; + } + + // If water fog density has been changed, save it. + if (mPrevFogDensity != mFogDensity) + { + gSavedSettings.setF32("WaterFogDensity", mFogDensity); + mPrevFogDensity = mFogDensity; + } + // sync menus if they exist LLFloaterWater* waterfloater = LLFloaterReg::findTypedInstance<LLFloaterWater>("env_water"); if(waterfloater) @@ -449,7 +465,24 @@ LLWaterParamManager * LLWaterParamManager::instance() sInstance->loadAllPresets(LLStringUtil::null); sInstance->getParamSet("Default", sInstance->mCurParams); + sInstance->initOverrides(); } return sInstance; } + +void LLWaterParamManager::initOverrides() +{ + // Override fog color from the current preset with the saved setting. + LLColor4 fog_color_override = gSavedSettings.getColor4("WaterFogColor"); + mFogColor = fog_color_override; + mPrevFogColor = fog_color_override; + mCurParams.set("waterFogColor", fog_color_override); + + // Do the same with fog density. + F32 fog_density = gSavedSettings.getF32("WaterFogDensity"); + mPrevFogDensity = fog_density; + mFogDensity = fog_density; + mCurParams.set("waterFogDensity", fog_density); + setDensitySliderValue(mFogDensity.mExp); +} diff --git a/indra/newview/llwaterparammanager.h b/indra/newview/llwaterparammanager.h index c479f1861c..f465034c39 100644 --- a/indra/newview/llwaterparammanager.h +++ b/indra/newview/llwaterparammanager.h @@ -284,6 +284,9 @@ public: // singleton pattern implementation static LLWaterParamManager * instance(); +private: + void initOverrides(); + public: LLWaterParamSet mCurParams; @@ -314,6 +317,9 @@ private: LLVector4 mWaterPlane; F32 mWaterFogKS; + LLColor4 mPrevFogColor; + F32 mPrevFogDensity; + // our parameter manager singleton instance static LLWaterParamManager * sInstance; }; diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 8f7197c607..6b2af1f8b7 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -98,11 +98,6 @@ LLWorld::LLWorld() : mEdgeWaterObjects[i] = NULL; } - if (gNoRender) - { - return; - } - LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,4); U8 *default_texture = raw->getData(); *(default_texture++) = MAX_WATER_COLOR.mV[0]; @@ -626,10 +621,7 @@ void LLWorld::updateVisibilities() if (LLViewerCamera::getInstance()->sphereInFrustum(regionp->getCenterAgent(), radius)) { regionp->calculateCameraDistance(); - if (!gNoRender) - { - regionp->getLand().updatePatchVisibilities(gAgent); - } + regionp->getLand().updatePatchVisibilities(gAgent); } else { @@ -1454,59 +1446,52 @@ void LLWorld::getAvatars(uuid_vec_t* avatar_ids, std::vector<LLVector3d>* positi { positions->clear(); } - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + // get the list of avatars from the character list first, so distances are correct + // when agent is above 1020m and other avatars are nearby + for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin(); + iter != LLCharacter::sInstances.end(); ++iter) { - LLViewerRegion* regionp = *iter; - const LLVector3d& origin_global = regionp->getOriginGlobal(); - S32 count = regionp->mMapAvatars.count(); - for (S32 i = 0; i < count; i++) + LLVOAvatar* pVOAvatar = (LLVOAvatar*) *iter; + if(!pVOAvatar->isDead() && !pVOAvatar->isSelf()) { - LLVector3d pos_global = unpackLocalToGlobalPosition(regionp->mMapAvatars.get(i), origin_global); - if(dist_vec(pos_global, relative_to) <= radius) + LLUUID uuid = pVOAvatar->getID(); + if(!uuid.isNull()) { - if(positions != NULL) + LLVector3d pos_global = pVOAvatar->getPositionGlobal(); + if(dist_vec(pos_global, relative_to) <= radius) { - positions->push_back(pos_global); - } - if(avatar_ids != NULL) - { - avatar_ids->push_back(regionp->mMapAvatarIDs.get(i)); + if(positions != NULL) + { + positions->push_back(pos_global); + } + if(avatar_ids !=NULL) + { + avatar_ids->push_back(uuid); + } } } } } - // retrieve the list of close avatars from viewer objects as well - // for when we are above 1000m, only do this when we are retrieving - // uuid's too as there could be duplicates - if(avatar_ids != NULL) + // region avatars added for situations where radius is greater than RenderFarClip + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) { - for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin(); - iter != LLCharacter::sInstances.end(); ++iter) + LLViewerRegion* regionp = *iter; + const LLVector3d& origin_global = regionp->getOriginGlobal(); + S32 count = regionp->mMapAvatars.count(); + for (S32 i = 0; i < count; i++) { - LLVOAvatar* pVOAvatar = (LLVOAvatar*) *iter; - if(pVOAvatar->isDead() || pVOAvatar->isSelf()) - continue; - LLUUID uuid = pVOAvatar->getID(); - if(uuid.isNull()) - continue; - LLVector3d pos_global = pVOAvatar->getPositionGlobal(); + LLVector3d pos_global = unpackLocalToGlobalPosition(regionp->mMapAvatars.get(i), origin_global); if(dist_vec(pos_global, relative_to) <= radius) { - bool found = false; - uuid_vec_t::iterator sel_iter = avatar_ids->begin(); - for (; sel_iter != avatar_ids->end(); sel_iter++) - { - if(*sel_iter == uuid) - { - found = true; - break; - } - } - if(!found) + LLUUID uuid = regionp->mMapAvatarIDs.get(i); + // if this avatar doesn't already exist in the list, add it + if(uuid.notNull() && avatar_ids!=NULL && std::find(avatar_ids->begin(), avatar_ids->end(), uuid) == avatar_ids->end()) { if(positions != NULL) + { positions->push_back(pos_global); + } avatar_ids->push_back(uuid); } } diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index 8ef3a3b839..8cdb615686 100644 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -1745,7 +1745,7 @@ BOOL LLWorldMapView::handleDoubleClick( S32 x, S32 y, MASK mask ) // Invoke the event details floater if someone is clicking on an event. LLSD params(LLSD::emptyArray()); params.append(event_id); - LLCommandDispatcher::dispatch("event", params, LLSD(), NULL, true); + LLCommandDispatcher::dispatch("event", params, LLSD(), NULL, "clicked", true); break; } case MAP_ITEM_LAND_FOR_SALE: diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 58dc90ccd9..6dc8f28265 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -1228,10 +1228,6 @@ void LLPipeline::unlinkDrawable(LLDrawable *drawable) U32 LLPipeline::addObject(LLViewerObject *vobj) { LLMemType mt_ao(LLMemType::MTYPE_PIPELINE_ADD_OBJECT); - if (gNoRender) - { - return 0; - } if (gSavedSettings.getBOOL("RenderDelayCreation")) { diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 75aec21f93..72a4dd7f63 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -115,9 +115,6 @@ name="AlertCautionTextColor" reference="LtYellow" /> <color - name="AlertTextColor" - value="0.58 0.66 0.84 1" /> - <color name="AvatarListItemIconDefaultColor" reference="White" /> <color diff --git a/indra/newview/skins/default/xui/da/panel_preferences_chat.xml b/indra/newview/skins/default/xui/da/panel_preferences_chat.xml index 3705a5902a..ed499619f6 100644 --- a/indra/newview/skins/default/xui/da/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/da/panel_preferences_chat.xml @@ -29,7 +29,8 @@ <check_box label="IM chats" name="EnableIMChatPopups" tool_tip="Vælg for at se popup vindue når personlige beskeder (IM) modtages"/> <spinner label="Tid før chatvisning forsvinder:" name="nearby_toasts_lifetime"/> <spinner label="Tid før chatvisning forsvinder:" name="nearby_toasts_fadingtime"/> - <check_box label="Benyt maskin-oversættelse ved chat (håndteret af Google)" name="translate_chat_checkbox"/> + <check_box name="translate_chat_checkbox"/> + <text name="translate_chb_label" >Benyt maskin-oversættelse ved chat (håndteret af Google)</text> <text name="translate_language_text" width="110"> Oversæt chat til : </text> diff --git a/indra/newview/skins/default/xui/da/panel_preferences_sound.xml b/indra/newview/skins/default/xui/da/panel_preferences_sound.xml index 5810cc21e7..067463be02 100644 --- a/indra/newview/skins/default/xui/da/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/da/panel_preferences_sound.xml @@ -4,7 +4,8 @@ Midterste museknap </panel.string> <slider label="Generel" name="System Volume"/> - <check_box initial_value="true" label="Sluk lyd når minimeret" name="mute_when_minimized"/> + <check_box initial_value="true" name="mute_when_minimized"/> + <text name="mute_chb_label">Sluk lyd når minimeret</text> <slider label="Knapper" name="UI Volume"/> <slider label="Omgivelser" name="Wind Volume"/> <slider label="Lyd effekter" name="SFX Volume"/> diff --git a/indra/newview/skins/default/xui/de/panel_preferences_chat.xml b/indra/newview/skins/default/xui/de/panel_preferences_chat.xml index 8086128dd7..8c8cdd31fe 100644 --- a/indra/newview/skins/default/xui/de/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/de/panel_preferences_chat.xml @@ -29,7 +29,8 @@ <check_box label="IM-Chats" name="EnableIMChatPopups" tool_tip="Markieren, um Popups zu sehen, wenn Instant Message eintrifft"/> <spinner label="Lebenszeit von Toasts für Chat in der Nähe:" name="nearby_toasts_lifetime"/> <spinner label="Ein-/Ausblenddauer von Toasts für Chat in der Nähe:" name="nearby_toasts_fadingtime"/> - <check_box label="Bei Chat Maschinenübersetzung verwenden (Service von Google)" name="translate_chat_checkbox"/> + <check_box name="translate_chat_checkbox"/> + <text name="translate_chb_label" >Bei Chat Maschinenübersetzung verwenden (Service von Google)</text> <text name="translate_language_text"> Chat übersetzen in: </text> diff --git a/indra/newview/skins/default/xui/de/panel_preferences_sound.xml b/indra/newview/skins/default/xui/de/panel_preferences_sound.xml index 0f029d8664..07631b6a91 100644 --- a/indra/newview/skins/default/xui/de/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/de/panel_preferences_sound.xml @@ -4,7 +4,8 @@ Mittlere Maustaste </panel.string> <slider label="Master-Lautstärke" name="System Volume"/> - <check_box initial_value="true" label="Stummschalten, wenn minimiert" name="mute_when_minimized"/> + <check_box initial_value="true" name="mute_when_minimized"/> + <text name="mute_chb_label">Stummschalten, wenn minimiert</text> <slider label="Schaltflächen" name="UI Volume"/> <slider label="Umgebung" name="Wind Volume"/> <slider label="Soundeffekte" name="SFX Volume"/> diff --git a/indra/newview/skins/default/xui/en/floater_preferences.xml b/indra/newview/skins/default/xui/en/floater_preferences.xml index 8eee8f44b5..dcfa8bc060 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences.xml @@ -45,7 +45,7 @@ name="pref core" tab_group="1" tab_position="left" - tab_width="115" + tab_width="140" tab_padding_right="0" top="21" width="658"> @@ -96,7 +96,7 @@ filename="panel_preferences_colors.xml" label="Colors" layout="topleft" - help_topic="preferences_im_tab" + help_topic="preferences_colors_tab" name="colors" /> <panel class="panel_preference" diff --git a/indra/newview/skins/default/xui/en/menu_media_ctrl.xml b/indra/newview/skins/default/xui/en/menu_media_ctrl.xml new file mode 100644 index 0000000000..c39c26f25f --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_media_ctrl.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<context_menu + name="media ctrl context menu"> + <menu_item_call + label="Cut" + layout="topleft" + name="Cut"> + <menu_item_call.on_click + function="Edit.Cut" /> + <menu_item_call.on_enable + function="Edit.EnableCut" /> + </menu_item_call> + <menu_item_call + label="Copy" + layout="topleft" + name="Copy"> + <menu_item_call.on_click + function="Edit.Copy" /> + <menu_item_call.on_enable + function="Edit.EnableCopy" /> + </menu_item_call> + <menu_item_call + label="Paste" + layout="topleft" + name="Paste"> + <menu_item_call.on_click + function="Edit.Paste" /> + <menu_item_call.on_enable + function="Edit.EnablePaste" /> + </menu_item_call> +</context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_text_editor.xml b/indra/newview/skins/default/xui/en/menu_text_editor.xml index ecd96088e7..fe8489166b 100644 --- a/indra/newview/skins/default/xui/en/menu_text_editor.xml +++ b/indra/newview/skins/default/xui/en/menu_text_editor.xml @@ -4,8 +4,7 @@ <menu_item_call label="Cut" layout="topleft" - name="Cut" - shortcut="control|X"> + name="Cut"> <menu_item_call.on_click function="Edit.Cut" /> <menu_item_call.on_enable @@ -14,8 +13,7 @@ <menu_item_call label="Copy" layout="topleft" - name="Copy" - shortcut="control|C"> + name="Copy"> <menu_item_call.on_click function="Edit.Copy" /> <menu_item_call.on_enable @@ -24,8 +22,7 @@ <menu_item_call label="Paste" layout="topleft" - name="Paste" - shortcut="control|V"> + name="Paste"> <menu_item_call.on_click function="Edit.Paste" /> <menu_item_call.on_enable @@ -34,8 +31,7 @@ <menu_item_call label="Delete" layout="topleft" - name="Delete" - shortcut="Del"> + name="Delete"> <menu_item_call.on_click function="Edit.Delete" /> <menu_item_call.on_enable @@ -44,8 +40,7 @@ <menu_item_call label="Select All" layout="topleft" - name="Select All" - shortcut="control|A"> + name="Select All"> <menu_item_call.on_click function="Edit.SelectAll" /> <menu_item_call.on_enable diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 433f623273..318bc9251f 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -7022,22 +7022,27 @@ Hiding the Speak button will disable the voice feature. </notification> <notification - name="HintDisplayName" - label="Display Name" + name="HintMoveClick" + label="" type="hint" unique="true"> - Set your customizable display name here. This is in addition to your unique username, which can't be changed. You can change how you see other people's names in your preferences. +1. Click to Walk +Click anywhere on the ground to walk to that spot. + +2. Click and Drag to Rotate View +Click and drag anywhere on the world to rotate your view + <tag>custom_skin</tag> </notification> <notification - name="HintMoveArrows" - label="Move" + name="HintDisplayName" + label="Display Name" type="hint" unique="true"> - To walk, use the directional keys on your keyboard. You can run by pressing the Up arrow twice. - <tag>custom_skin</tag> + Set your customizable display name here. This is in addition to your unique username, which can't be changed. You can change how you see other people's names in your preferences. </notification> + <notification name="HintView" label="View" diff --git a/indra/newview/skins/default/xui/en/panel_hint.xml b/indra/newview/skins/default/xui/en/panel_hint.xml index e2e9d0aef0..f7434f0330 100644 --- a/indra/newview/skins/default/xui/en/panel_hint.xml +++ b/indra/newview/skins/default/xui/en/panel_hint.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel width="205" - height="100"> + height="34"> <text name="hint_title" font="SansSerifMedium" left="8" @@ -15,8 +15,8 @@ left="8" right="197" top="26" - bottom="92" - follows="all" + bottom="26" + follows="left|right|bottom" text_color="Black" wrap="true"/> <button right="197" diff --git a/indra/newview/skins/default/xui/en/panel_hint_image.xml b/indra/newview/skins/default/xui/en/panel_hint_image.xml index 00b6e42497..df05d50dc5 100644 --- a/indra/newview/skins/default/xui/en/panel_hint_image.xml +++ b/indra/newview/skins/default/xui/en/panel_hint_image.xml @@ -1,33 +1,43 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel - width="205" - height="140" + width="305" + height="40" layout="topleft"> <text name="hint_title" font="SansSerifMedium" left="8" - right="180" + right="290" top="8" bottom="20" follows="left|right|top" text_color="Black" wrap="false"/> - <icon name="hint_image" - left="42" - top="25" - width="115" - height="86" - image_name="arrow_keys.png" - /> + <layout_stack left="0" + top="25" + width="305" + height="0" + follows="left|top|right" + orientation="horizontal"> + <layout_panel auto_resize="true" width="100"/> + <layout_panel auto_resize="true" width="0"> + <icon name="hint_image" + top="0" + left="0" + height="0" + width="0" + follows="all"/> + </layout_panel> + <layout_panel auto_resize="true" width="100"/> + </layout_stack> <text name="hint_text" left="8" - right="197" - top_pad="5" - bottom="120" - follows="all" + right="297" + top="30" + bottom="30" + follows="left|right|bottom" text_color="Black" wrap="true"/> - <button right="197" + <button right="297" top="8" width="16" height="16" diff --git a/indra/newview/skins/default/xui/en/panel_login.xml b/indra/newview/skins/default/xui/en/panel_login.xml index 4c2faddfe4..8d0f1437e6 100644 --- a/indra/newview/skins/default/xui/en/panel_login.xml +++ b/indra/newview/skins/default/xui/en/panel_login.xml @@ -133,7 +133,6 @@ label="Remember password" max_chars="128" tool_tip="Select your mode. Choose Basic for fast, easy exploration and chat. Choose Advanced to access more features." top_pad="0" - control_name="SessionSettingsFile" name="mode_combo" width="110"> <combo_box.item diff --git a/indra/newview/skins/default/xui/en/panel_preferences_alerts.xml b/indra/newview/skins/default/xui/en/panel_preferences_alerts.xml index 559df5bec9..714dca7fac 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_alerts.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_alerts.xml @@ -67,7 +67,7 @@ image_overlay="Arrow_Up" hover_glow_amount="0.15" layout="topleft" - left="180" + left="200" name="enable_this_popup" top_pad="5" width="40"> @@ -81,7 +81,7 @@ image_overlay="Arrow_Down" hover_glow_amount="0.15" layout="topleft" - left_pad="40" + left_pad="20" name="disable_this_popup" top_delta="0" width="40"> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml index a1082d9c32..404537e1f2 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml @@ -208,15 +208,27 @@ <check_box control_name="TranslateChat" enabled="true" - height="16" - label="Use machine translation while chatting (powered by Google)" + height="16" layout="topleft" left="30" name="translate_chat_checkbox" - bottom_delta="30" + top_pad="5" width="400" /> + <!-- *HACK + After storm-1109 will be fixed: instead of using this text_box, word_wrap should be applied for "translate_chat_checkbox" check_box's label.--> + <text + follows="top|left" + height="15" + layout="topleft" + left="50" + name="translate_chb_label" + top_delta="1" + width="450" + wrap="true"> + Use machine translation while chatting (powered by Google) + </text> <text - bottom_delta="30" + top_pad="20" name="translate_language_text" follows="left|top" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_general.xml b/indra/newview/skins/default/xui/en/panel_preferences_general.xml index 36f8f99178..9c718fdb87 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_general.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_general.xml @@ -307,17 +307,6 @@ tool_tip="Check to use display names in chat, IM, name tags, etc." top_pad="3"/> - <check_box - control_name="EnableUIHints" - follows="top|left" - height="16" - label="Enable Viewer UI Hints" - layout="topleft" - left="27" - name="viewer_hints_check" - top_pad="5" - width="237"/> - <text type="string" length="1" @@ -326,7 +315,7 @@ layout="topleft" left="30" name="inworld_typing_rg_label" - top_pad="6" + top_pad="1" width="400"> Pressing letter keys: </text> @@ -348,9 +337,9 @@ width="150" /> <radio_item label="Affects movement (i.e. WASD)" - left_pad="0" + left="0" layout="topleft" - top_delta="0" + top="18" height="16" name="radio_move" value="0" @@ -365,7 +354,7 @@ layout="topleft" left="30" name="title_afk_text" - top_pad="4" + top_pad="15" width="190"> Away timeout: </text> @@ -408,7 +397,7 @@ left="30" mouse_opaque="false" name="text_box3" - top_pad="5" + top_pad="3" width="240"> Busy mode response: </text> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_move.xml b/indra/newview/skins/default/xui/en/panel_preferences_move.xml index d2fc6ea09a..04412bdb9c 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_move.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_move.xml @@ -120,12 +120,13 @@ type="string" length="1" follows="left|top" - height="10" + height="15" layout="topleft" left_delta="3" name=" Mouse Sensitivity" top_pad="10" - width="160"> + width="160" + wrap="true"> Mouselook mouse sensitivity: </text> <slider @@ -139,7 +140,7 @@ max_val="15" name="mouse_sensitivity" top_delta="-1" - width="145" /> + width="115" /> <check_box control_name="InvertMouse" height="16" @@ -158,7 +159,7 @@ left="78" name="arrow_keys_move_avatar_check" width="237" - top_pad="1"/> + top_pad="10"/> <check_box control_name="AllowTapTapHoldRun" follows="left|top" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml b/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml index ef25588ca3..30be5bc853 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml @@ -42,7 +42,7 @@ </text> <check_box height="16" - enabled="true" + enabled="false" label="Show me in Search results" layout="topleft" left="30" @@ -82,8 +82,8 @@ control_name="ShowFavoritesOnLogin" enabled="false" height="16" - label="Show my Favorite Landmarks at Login (via 'Start At' drop-down menu)" layout="topleft" + label="Show my Favorite Landmarks at Login (via 'Start At' drop-down menu)" left="30" name="favorites_on_login_check" top_pad="10" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml index 26af8dc29d..f89494da72 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml @@ -50,12 +50,24 @@ control_name="MuteWhenMinimized" height="15" initial_value="true" - label="Mute when minimized" layout="topleft" name="mute_when_minimized" top_delta="3" left_pad="5" - width="235" /> + width="20" /> + <!-- *HACK + After storm-1109 will be fixed: instead of using this text_box, word_wrap should be applied for "mute_when_minimized" check_box's label.--> + <text + follows="top|left" + height="15" + layout="topleft" + left_pad="0" + name="mute_chb_label" + top_delta="0" + width="150" + wrap="true"> + Mute when minimized + </text> <slider control_name="AudioLevelUI" disabled_control="MuteAudio" diff --git a/indra/newview/skins/default/xui/en/panel_profile.xml b/indra/newview/skins/default/xui/en/panel_profile.xml index d36220385d..cb701e03da 100644 --- a/indra/newview/skins/default/xui/en/panel_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_profile.xml @@ -453,23 +453,6 @@ </layout_panel> </layout_stack> </layout_panel> - <layout_panel - follows="bottom|left" - height="30" - layout="topleft" - name="profile_me_buttons_panel" - visible="false" - width="313"> - <button - follows="bottom|right" - height="23" - left="20" - top="0" - label="Edit Profile" - name="edit_profile_btn" - tool_tip="Edit your personal information" - width="130" /> - </layout_panel> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/es/panel_preferences_chat.xml b/indra/newview/skins/default/xui/es/panel_preferences_chat.xml index 0b304fe287..aba85f9ff1 100644 --- a/indra/newview/skins/default/xui/es/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/es/panel_preferences_chat.xml @@ -29,7 +29,8 @@ <check_box label="Chats de MI" name="EnableIMChatPopups" tool_tip="Activa esta casilla para ver una ventana emergente cada vez que recibas un mensaje instantáneo"/> <spinner label="Duración de los interlocutores favoritos:" name="nearby_toasts_lifetime"/> <spinner label="Tiempo de los otros interlocutores:" name="nearby_toasts_fadingtime"/> - <check_box label="Usar la traducción automática (con Google) en el chat" name="translate_chat_checkbox"/> + <check_box name="translate_chat_checkbox"/> + <text name="translate_chb_label" >Usar la traducción automática (con Google) en el chat</text> <text name="translate_language_text"> Traducir el chat al: </text> diff --git a/indra/newview/skins/default/xui/es/panel_preferences_sound.xml b/indra/newview/skins/default/xui/es/panel_preferences_sound.xml index db3659abcd..2bc82307a8 100644 --- a/indra/newview/skins/default/xui/es/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/es/panel_preferences_sound.xml @@ -4,7 +4,8 @@ Botón medio del ratón </panel.string> <slider label="Volumen general" name="System Volume"/> - <check_box initial_value="true" label="Silenciar cuando minimice" name="mute_when_minimized"/> + <check_box initial_value="true" name="mute_when_minimized"/> + <text name="mute_chb_label">Silenciar cuando minimice</text> <slider label="Botones" name="UI Volume"/> <slider label="Ambiental" name="Wind Volume"/> <slider label="Efectos de sonido" name="SFX Volume"/> diff --git a/indra/newview/skins/default/xui/fr/panel_preferences_chat.xml b/indra/newview/skins/default/xui/fr/panel_preferences_chat.xml index 4b3fc35150..d5cecfc698 100644 --- a/indra/newview/skins/default/xui/fr/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/fr/panel_preferences_chat.xml @@ -29,7 +29,8 @@ <check_box label="Chats IM" name="EnableIMChatPopups" tool_tip="Cocher cette case pour qu'un popup s'affiche à réception d'un message instantané."/> <spinner label="Durée de vie du popup Chat près de moi :" name="nearby_toasts_lifetime"/> <spinner label="Disparition progressive du popup Chat près de moi :" name="nearby_toasts_fadingtime"/> - <check_box label="Utiliser la traduction automatique lors des chats (fournie par Google)" name="translate_chat_checkbox"/> + <check_box name="translate_chat_checkbox"/> + <text name="translate_chb_label" >Utiliser la traduction automatique lors des chats (fournie par Google)</text> <text name="translate_language_text"> Traduire le chat en : </text> diff --git a/indra/newview/skins/default/xui/fr/panel_preferences_sound.xml b/indra/newview/skins/default/xui/fr/panel_preferences_sound.xml index 48630918d7..ac7f72d367 100644 --- a/indra/newview/skins/default/xui/fr/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/fr/panel_preferences_sound.xml @@ -4,7 +4,8 @@ Bouton central de la souris </panel.string> <slider label="Volume principal" name="System Volume"/> - <check_box initial_value="true" label="Couper quand minimisé" name="mute_when_minimized"/> + <check_box initial_value="true" name="mute_when_minimized"/> + <text name="mute_chb_label">Couper quand minimisé</text> <slider label="Boutons" name="UI Volume"/> <slider label="Ambiant" name="Wind Volume"/> <slider label="Effets sonores" name="SFX Volume"/> diff --git a/indra/newview/skins/default/xui/it/panel_preferences_chat.xml b/indra/newview/skins/default/xui/it/panel_preferences_chat.xml index 4a1bbdf64a..208dd5ed28 100644 --- a/indra/newview/skins/default/xui/it/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/it/panel_preferences_chat.xml @@ -56,7 +56,8 @@ <radio_item label="Finestre separate" name="radio" value="0"/> <radio_item label="Schede" name="radio2" value="1"/> </radio_group> - <check_box label="Usa la traduzione meccanica durante le chat (tecnologia Google)" name="translate_chat_checkbox"/> + <check_box name="translate_chat_checkbox"/> + <text name="translate_chb_label" >Usa la traduzione meccanica durante le chat (tecnologia Google)</text> <text name="translate_language_text" width="110"> Traduci chat in: </text> diff --git a/indra/newview/skins/default/xui/it/panel_preferences_sound.xml b/indra/newview/skins/default/xui/it/panel_preferences_sound.xml index 6e70a314c5..e2332b63d0 100644 --- a/indra/newview/skins/default/xui/it/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/it/panel_preferences_sound.xml @@ -1,7 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel label="Suoni" name="Preference Media panel"> <slider label="Vol. principale" name="System Volume"/> - <check_box initial_value="true" label="Disatt. se a icona" name="mute_when_minimized"/> + <check_box initial_value="true" label="" name="mute_when_minimized"/> + <text name="mute_chb_label">Disatt. se a icona</text> <slider label="Pulsanti" name="UI Volume"/> <slider label="Ambiente" name="Wind Volume"/> <slider label="Effetti sonori" name="SFX Volume"/> diff --git a/indra/newview/skins/default/xui/ja/panel_preferences_chat.xml b/indra/newview/skins/default/xui/ja/panel_preferences_chat.xml index c260cebef8..ce2a0f35e4 100644 --- a/indra/newview/skins/default/xui/ja/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/ja/panel_preferences_chat.xml @@ -56,7 +56,8 @@ <radio_item label="別々のウィンドウ" name="radio" value="0"/> <radio_item label="タブ" name="radio2" value="1"/> </radio_group> - <check_box label="チャット中に内容を機械翻訳する(Google翻訳)" name="translate_chat_checkbox"/> + <check_box name="translate_chat_checkbox"/> + <text name="translate_chb_label" >チャット中に内容を機械翻訳する(Google翻訳)</text> <text name="translate_language_text"> 翻訳する言語: </text> diff --git a/indra/newview/skins/default/xui/ja/panel_preferences_sound.xml b/indra/newview/skins/default/xui/ja/panel_preferences_sound.xml index 9fbbd46220..74696a3b35 100644 --- a/indra/newview/skins/default/xui/ja/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/ja/panel_preferences_sound.xml @@ -1,7 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel label="サウンド" name="Preference Media panel"> <slider label="全体の音量" name="System Volume"/> - <check_box initial_value="true" label="最小化でミュート" name="mute_when_minimized"/> + <check_box initial_value="true" name="mute_when_minimized"/> + <text name="mute_chb_label">最小化でミュート</text> <slider label="ボタン" name="UI Volume"/> <slider label="風" name="Wind Volume"/> <slider label="効果音" name="SFX Volume"/> diff --git a/indra/newview/skins/default/xui/nl/panel_preferences_sound.xml b/indra/newview/skins/default/xui/nl/panel_preferences_sound.xml index 2b709bde40..5ded015868 100644 --- a/indra/newview/skins/default/xui/nl/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/nl/panel_preferences_sound.xml @@ -1,7 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel label="Geluid" name="Preference Media panel"> <slider label="Hoofd volume" name="System Volume"/> - <check_box initial_value="true" label="Dempen indien geminimaliseerd" name="mute_when_minimized"/> + <check_box initial_value="true" name="mute_when_minimized"/> + <text name="mute_chb_label">Dempen indien geminimaliseerd</text> <slider label="Omliggend" name="Wind Volume"/> <slider label="Knoppen" name="UI Volume"/> <slider label="Media" name="Media Volume"/> diff --git a/indra/newview/skins/default/xui/pl/panel_preferences_chat.xml b/indra/newview/skins/default/xui/pl/panel_preferences_chat.xml index c7142c8419..4a4e6509ab 100644 --- a/indra/newview/skins/default/xui/pl/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/pl/panel_preferences_chat.xml @@ -29,7 +29,8 @@ <check_box label="Czat IM" name="EnableIMChatPopups" tool_tip="Zaznacz aby widzieć wyskakujące okienka kiedy IM się pojawia"/> <spinner label="Czas widoczności czatu w pobliżu:" name="nearby_toasts_lifetime"/> <spinner label="Czas znikania czatu w pobliżu:" name="nearby_toasts_fadingtime"/> - <check_box label="Używaj translatora podczas rozmowy (wspierany przez Google)" name="translate_chat_checkbox"/> + <check_box name="translate_chat_checkbox"/> + <text name="translate_chb_label" >Używaj translatora podczas rozmowy (wspierany przez Google)</text> <text name="translate_language_text"> Przetłumacz czat na: </text> diff --git a/indra/newview/skins/default/xui/pl/panel_preferences_sound.xml b/indra/newview/skins/default/xui/pl/panel_preferences_sound.xml index ac93949a1b..692f24715b 100644 --- a/indra/newview/skins/default/xui/pl/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/pl/panel_preferences_sound.xml @@ -4,7 +4,8 @@ Środkowy przycisk myszy </panel.string> <slider label="Główny" name="System Volume"/> - <check_box initial_value="true" label="Wycisz podczas minimalizacji" name="mute_when_minimized"/> + <check_box initial_value="true" name="mute_when_minimized"/> + <text name="mute_chb_label">Wycisz podczas minimalizacji</text> <slider label="Interfejs" name="UI Volume"/> <slider label="Otoczenie" name="Wind Volume"/> <slider label="Efekty dźwiękowe" name="SFX Volume"/> diff --git a/indra/newview/skins/default/xui/pt/panel_preferences_chat.xml b/indra/newview/skins/default/xui/pt/panel_preferences_chat.xml index 368c474ee9..412bdbb13e 100644 --- a/indra/newview/skins/default/xui/pt/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/pt/panel_preferences_chat.xml @@ -29,7 +29,8 @@ <check_box label="Bate-papos de MI" name="EnableIMChatPopups" tool_tip="Exibir pop-up de mensagens instantâneas novas"/> <spinner label="Transição de avisos de bate-papos por perto:" name="nearby_toasts_lifetime"/> <spinner label="Transição de avisos de bate-papos por perto:" name="nearby_toasts_fadingtime"/> - <check_box label="Traduzir bate-papo automaticamente (via Google)" name="translate_chat_checkbox"/> + <check_box name="translate_chat_checkbox"/> + <text name="translate_chb_label" >Traduzir bate-papo automaticamente (via Google)</text> <text name="translate_language_text"> Traduzir bate-papo para: </text> diff --git a/indra/newview/skins/default/xui/pt/panel_preferences_sound.xml b/indra/newview/skins/default/xui/pt/panel_preferences_sound.xml index 3846bfb377..6053deb5b1 100644 --- a/indra/newview/skins/default/xui/pt/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/pt/panel_preferences_sound.xml @@ -4,7 +4,8 @@ Botão do meio do mouse </panel.string> <slider label="Volume principal" name="System Volume"/> - <check_box initial_value="true" label="Silenciar ao minimizar" name="mute_when_minimized"/> + <check_box initial_value="true" name="mute_when_minimized"/> + <text name="mute_chb_label">Silenciar ao minimizar</text> <slider label="Botões" name="UI Volume"/> <slider label="Ambiente" name="Wind Volume"/> <slider label="Efeitos sonoros" name="SFX Volume"/> diff --git a/indra/newview/skins/minimal/textures/click_to_move.png b/indra/newview/skins/minimal/textures/click_to_move.png Binary files differnew file mode 100644 index 0000000000..74e3faa8ff --- /dev/null +++ b/indra/newview/skins/minimal/textures/click_to_move.png diff --git a/indra/newview/skins/minimal/textures/textures.xml b/indra/newview/skins/minimal/textures/textures.xml index 3e2f5cd397..b4848a0619 100644 --- a/indra/newview/skins/minimal/textures/textures.xml +++ b/indra/newview/skins/minimal/textures/textures.xml @@ -2,6 +2,7 @@ <textures version="101"> <texture name="Button_Separator" file_name="bottomtray/button_separator.png" preload="true" /> <texture name="arrow_keys.png"/> + <texture name="click_to_move" file_name="click_to_move.png"/> <texture name="bottomtray_close_off" file_name="bottomtray/close_off.png" preload="true" /> <texture name="bottomtray_close_over" file_name="bottomtray/close_over.png" preload="true" /> <texture name="bottomtray_close_press" file_name="bottomtray/close_press.png" preload="true" /> diff --git a/indra/newview/skins/minimal/xui/en/menu_avatar_icon.xml b/indra/newview/skins/minimal/xui/en/menu_avatar_icon.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_avatar_icon.xml +++ b/indra/newview/skins/minimal/xui/en/menu_avatar_icon.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_bottomtray.xml b/indra/newview/skins/minimal/xui/en/menu_bottomtray.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_bottomtray.xml +++ b/indra/newview/skins/minimal/xui/en/menu_bottomtray.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_favorites.xml b/indra/newview/skins/minimal/xui/en/menu_favorites.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_favorites.xml +++ b/indra/newview/skins/minimal/xui/en/menu_favorites.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_group_plus.xml b/indra/newview/skins/minimal/xui/en/menu_group_plus.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_group_plus.xml +++ b/indra/newview/skins/minimal/xui/en/menu_group_plus.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_hide_navbar.xml b/indra/newview/skins/minimal/xui/en/menu_hide_navbar.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_hide_navbar.xml +++ b/indra/newview/skins/minimal/xui/en/menu_hide_navbar.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_imchiclet_adhoc.xml b/indra/newview/skins/minimal/xui/en/menu_imchiclet_adhoc.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_imchiclet_adhoc.xml +++ b/indra/newview/skins/minimal/xui/en/menu_imchiclet_adhoc.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_imchiclet_group.xml b/indra/newview/skins/minimal/xui/en/menu_imchiclet_group.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_imchiclet_group.xml +++ b/indra/newview/skins/minimal/xui/en/menu_imchiclet_group.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_imchiclet_p2p.xml b/indra/newview/skins/minimal/xui/en/menu_imchiclet_p2p.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_imchiclet_p2p.xml +++ b/indra/newview/skins/minimal/xui/en/menu_imchiclet_p2p.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_inv_offer_chiclet.xml b/indra/newview/skins/minimal/xui/en/menu_inv_offer_chiclet.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_inv_offer_chiclet.xml +++ b/indra/newview/skins/minimal/xui/en/menu_inv_offer_chiclet.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_inventory.xml b/indra/newview/skins/minimal/xui/en/menu_inventory.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_inventory.xml +++ b/indra/newview/skins/minimal/xui/en/menu_inventory.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_inventory_add.xml b/indra/newview/skins/minimal/xui/en/menu_inventory_add.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_inventory_add.xml +++ b/indra/newview/skins/minimal/xui/en/menu_inventory_add.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_mini_map.xml b/indra/newview/skins/minimal/xui/en/menu_mini_map.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_mini_map.xml +++ b/indra/newview/skins/minimal/xui/en/menu_mini_map.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_navbar.xml b/indra/newview/skins/minimal/xui/en/menu_navbar.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_navbar.xml +++ b/indra/newview/skins/minimal/xui/en/menu_navbar.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_nearby_chat.xml b/indra/newview/skins/minimal/xui/en/menu_nearby_chat.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_nearby_chat.xml +++ b/indra/newview/skins/minimal/xui/en/menu_nearby_chat.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_object_icon.xml b/indra/newview/skins/minimal/xui/en/menu_object_icon.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_object_icon.xml +++ b/indra/newview/skins/minimal/xui/en/menu_object_icon.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_people_groups.xml b/indra/newview/skins/minimal/xui/en/menu_people_groups.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_people_groups.xml +++ b/indra/newview/skins/minimal/xui/en/menu_people_groups.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_place_add_button.xml b/indra/newview/skins/minimal/xui/en/menu_place_add_button.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_place_add_button.xml +++ b/indra/newview/skins/minimal/xui/en/menu_place_add_button.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_script_chiclet.xml b/indra/newview/skins/minimal/xui/en/menu_script_chiclet.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_script_chiclet.xml +++ b/indra/newview/skins/minimal/xui/en/menu_script_chiclet.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_slurl.xml b/indra/newview/skins/minimal/xui/en/menu_slurl.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_slurl.xml +++ b/indra/newview/skins/minimal/xui/en/menu_slurl.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/menu_topinfobar.xml b/indra/newview/skins/minimal/xui/en/menu_topinfobar.xml index 9ebab9ef98..d3d9e2ef8a 100644 --- a/indra/newview/skins/minimal/xui/en/menu_topinfobar.xml +++ b/indra/newview/skins/minimal/xui/en/menu_topinfobar.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu/> +<menu visible="false"/> diff --git a/indra/newview/skins/minimal/xui/en/panel_bottomtray.xml b/indra/newview/skins/minimal/xui/en/panel_bottomtray.xml index 0145de8be9..d0a77e8c2a 100644 --- a/indra/newview/skins/minimal/xui/en/panel_bottomtray.xml +++ b/indra/newview/skins/minimal/xui/en/panel_bottomtray.xml @@ -163,7 +163,7 @@ layout="topleft" left="0" name="destination_btn" - tool_tip="Shows people window" + tool_tip="Shows destinations" top="5" is_toggle="true" use_ellipses="true" diff --git a/indra/newview/skins/minimal/xui/en/panel_login.xml b/indra/newview/skins/minimal/xui/en/panel_login.xml index ef058e5567..3903658e71 100644 --- a/indra/newview/skins/minimal/xui/en/panel_login.xml +++ b/indra/newview/skins/minimal/xui/en/panel_login.xml @@ -134,7 +134,6 @@ top="20" max_chars="128" top_pad="0" tool_tip="Select your mode. Choose Basic for fast, easy exploration and chat. Choose Advanced to access more features." - control_name="SessionSettingsFile" name="mode_combo" width="120"> <combo_box.item diff --git a/indra/test_apps/llplugintest/llmediaplugintest.cpp b/indra/test_apps/llplugintest/llmediaplugintest.cpp index e9d4d99753..7164934b26 100644 --- a/indra/test_apps/llplugintest/llmediaplugintest.cpp +++ b/indra/test_apps/llplugintest/llmediaplugintest.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2011, 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 @@ -2233,7 +2233,13 @@ void LLMediaPluginTest::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent e case MEDIA_EVENT_LINK_HOVERED: { std::cerr << "Media event: MEDIA_EVENT_LINK_HOVERED, hover text is: " << self->getHoverText() << std::endl; - }; + } + break; + + default: + { + std::cerr << "Media event: <unknown>, code is: " << int(event) << std::endl; + } break; } } diff --git a/install.xml b/install.xml new file mode 100644 index 0000000000..9658a4195a --- /dev/null +++ b/install.xml @@ -0,0 +1,1898 @@ +<?xml version="1.0" ?> +<llsd> +<map> + <key>installables</key> + <map> + <key>GL</key> + <map> + <key>copyright</key> + <string>Copyright (c) 1991-2000 Silicon Graphics, Inc.</string> + <key>description</key> + <string>A standard for 3D Graphics rendering engine.</string> + <key>license</key> + <string>GL</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>0bd2795a2afe09f6c563f2f888f24cc9</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/GL-darwin-20080613.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>2ab29212a7f3acdaebf10059af816be0</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/GL-linux-20080812.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>9c3dff3817f1105f9054401fdef1fe50</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/GL-linux64-20080909.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>91155239b02f576384603795d41eb971</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/GL-windows-20090505.tar.bz2</uri> + </map> + </map> + </map> + <key>SDL</key> + <map> + <key>copyright</key> + <string>Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga</string> + <key>description</key> + <string>The Simple DirectMedia Layer libraries are used for handling input and basic window/GL setup on the Linux client. Packages also include cursors.</string> + <key>license</key> + <string>lgpl</string> + <key>packages</key> + <map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>fce0ff7d2cdf0f36c1647e6a3916e29e</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/SDL-1.2.12-linux-20090218.tar.bz2</uri> + </map> + </map> + </map> + <key>apr_suite</key> + <map> + <key>license</key> + <string>apache 2.0</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>115d8ac44a91efdb173e9b3e478c46b6</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/apr_suite-1.3.7-darwin-20090805.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>7b84cd6a3c601a104d9c09e58ef2f50c</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/apr_suite-1.2.8-linux-20080812.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>1a7e1186855d48d8316ce86803095f70</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/apr_suite-1.2.8-linux64-20080909a.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>a02619c1e30a3db02d3883bf1ad7a1e6</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/apr_suite-1.3.8-windows-20090911.tar.bz2</uri> + </map> + </map> + </map> + <key>ares</key> + <map> + <key>copyright</key> + <string>Copyright 1998 by the Massachusetts Institute of Technology.</string> + <key>description</key> + <string>Performs DNS requests and name resolves asynchronously. Used with libcurl to keep all HTTP operations async.</string> + <key>license</key> + <string>c-ares</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>cdb2f5c4a5a1f9ecd75bc1dbdd4db8e9</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ares-1.7.1-darwin-20100606.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>91694429e391efeea1de974df26032a2</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ares-1.7.1-linux-20100527.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>c4242416e0b2e642c0bf062a19a250e4</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ares-1.3.0-linux64-20080909.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>4b84738eec2e21b0c096d53b79ee2681</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ares-1.7.1-windows-20100611a.tar.bz2</uri> + </map> + </map> + </map> + <key>artwork-common</key> + <map> + <key>copyright</key> + <string>(C) 2008 Linden Research, Inc.</string> + <key>description</key> + <string>Second Life(TM) Viewer Artwork</string> + <key>license</key> + <string>artwork</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>5e047437d73d1017bf270a6c6e936f23</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/artwork-common-20090415.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>5e047437d73d1017bf270a6c6e936f23</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/artwork-common-20090415.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>5e047437d73d1017bf270a6c6e936f23</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/artwork-common-20090415.tar.bz2</uri> + </map> + </map> + </map> + <key>berkeley</key> + <map> + <key>copyright</key> + <string>Copyright (c) 1990-1999 Sleepycat Software. All rights reserved.</string> + <key>description</key> + <string>a high-performance, embedded database library</string> + <key>license</key> + <string>sleepycat</string> + </map> + <key>boost</key> + <map> + <key>copyright</key> + <string>various</string> + <key>description</key> + <string>A set of portable C++ libraries which provide a wide set of functionality. </string> + <key>license</key> + <string>boost</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>71defd179827bf172b76d6020023e0e8</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.39.0-darwin-20100222a.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>4db3d74e40d149eeec06f4d97a609bb1</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.39.0-linux-20100624.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>af4badd6b2c10bc4db82ff1256695892</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.39.0-linux64-20100119.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>72e6e2eff5d146a107f3059b6c31fb95</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.39.0-windows-20100630.tar.bz2</uri> + </map> + </map> + </map> + <key>curl</key> + <map> + <key>copyright</key> + <string>Copyright (c) 1996 - 2008, Daniel Stenberg, <daniel@haxx.se>.</string> + <key>description</key> + <string>Client-side URL transfer library. Handles moving data across the net in many different protocols. Used to GET/POST/PUT/DELETE web resources. </string> + <key>license</key> + <string>curl</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>ca8f0134fa5ab6f34a6eeb8d0896c9b0</string> + <key>url</key> + <uri>https://s3.amazonaws.com/automated-builds-secondlife-com/hg/repo/brad_curl-autobuild/rev/216961/arch/Darwin/installer/curl-7.21.1-darwin-20101214.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>9c9b629b62bf874d550c430ad678dc04</string> + <key>url</key> + <uri>https://s3.amazonaws.com/automated-builds-secondlife-com/hg/repo/brad_curl-autobuild/rev/216961/arch/Linux/installer/curl-7.21.1-linux-20101215.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>6994192cea7ab2d885a158a3de474273</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/curl-7.16.4a-linux64-20090303.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>48691883065a82d53691d73aae81d4c1</string> + <key>url</key> + <uri>https://s3.amazonaws.com/automated-builds-secondlife-com/hg/repo/brad_curl-autobuild/rev/216961/arch/CYGWIN/installer/curl-7.21.1-windows-20101214.tar.bz2</uri> + </map> + </map> + </map> + <key>dbghelp</key> + <map> + <key>copyright</key> + <string>Copyright Microsoft Corporation</string> + <key>description</key> + <string>dbghelp: Debug helper from Microsoft Debugging Tools For Windows</string> + <key>license</key> + <string>MSDTW</string> + <key>packages</key> + <map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>b7563064037e032143ca2d610aae5153</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/dbghelp-6.11.1.404-windows-20090520.tar.bz2</uri> + </map> + </map> + </map> + <key>dbusglib</key> + <map> + <key>copyright</key> + <string>Copyright (C) 2002, 2003 CodeFactory AB / Copyright (C) 2003, 2004 Red Hat, Inc.</string> + <key>description</key> + <string>dbus/dbus-glib: headers only</string> + <key>license</key> + <string>AFL2.1</string> + <key>packages</key> + <map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>eb25444142d4102b0ce1b7ffaadb071e</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/dbusglib-linux-20080707.tar.bz2</uri> + </map> + </map> + </map> + <key>elfio</key> + <map> + <key>license</key> + <string>lgpl</string> + <key>packages</key> + <map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>82ea408af2f968cfe5f013ab241323ef</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/elfio-1.0.3-linux-20080812.tar.bz2</uri> + </map> + </map> + </map> + <key>expat</key> + <map> + <key>copyright</key> + <string>Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd</string> + <key>description</key> + <string>An XML parser library written in C. It is a stream-oriented parser in which an application registers handlers for things the parser might find in the XML document (like start tags).</string> + <key>license</key> + <string>mit</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>c457a0a041ac4946265889a503d26c3d</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/expat-1.95.8-darwin-20090805.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>67b470fd446b08c9831d1039674eae4e</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/expat-1.95.8-linux-20080812.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>278c61871419b9a4d50a4f88b7922403</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/expat-1.95.8-linux64-20080909.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>5dbbdb4a9b5bec86d180ef20a5f8ccfb</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/expat-1.95.8-windows-20090917.tar.bz2</uri> + </map> + </map> + </map> + <key>fmod</key> + <map> + <key>copyright</key> + <string>FMOD Sound System, copyright (C) Firelight Technologies Pty, Ltd., 1994-2006.</string> + <key>description</key> + <string>Audio engine and mp3 stream decoder .</string> + <key>license</key> + <string>fmod</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>261bcd3387066cf0a1d46549400052b5</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/fmod-3.75-darwin-20101007.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>8490d97430c12c2e1ac19ff80a8d4db4</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/fmod-3.75-linux-20101007.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>bab1babcb01ff9849b7f072d352e1ecd</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/fmod-3.75-windows-20101007.tar.bz2</uri> + </map> + </map> + </map> + <key>fontconfig</key> + <map> + <key>license</key> + <string>mit</string> + <key>packages</key> + <map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>9af6a1ed39fa540bfcaa402b0ea22f78</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/fontconfig-2.2.3-linux-20080613.tar.bz2</uri> + </map> + </map> + </map> + <key>freeglut</key> + <map> + <key>license</key> + <string>mit</string> + <key>packages</key> + <map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>fcbb695ff203775fad96d184bf5f34fc</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/freeglut-2.4.0-windows-20090608.tar.bz2</uri> + </map> + </map> + </map> + <key>freetype</key> + <map> + <key>copyright</key> + <string>Copyright</string> + <key>description</key> + <string>Font</string> + <key>license</key> + <string>freetype</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>f00144dfb597140f328774c3244f0c3e</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/freetype-2.3.9-darwin-20090922.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>9de3f44be65645c7f6af236139596942</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/freetype-2.3.9-linux-2010-02-19a-nommap.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>35f6fa557ba90f9cda0a18d1af2055a4</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/freetype-2.1.5-linux64-20080909.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>88980fd6d91ac541b62dea877ebe6ba6</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/freetype-2.3.9-windows-20090917.tar.bz2</uri> + </map> + </map> + </map> + <key>glh_linear</key> + <map> + <key>copyright</key> + <string>Copyright (c) 2000 Cass Everitt; Copyright (c) 2000 NVIDIA Corporation; All rights reserved.</string> + <key>description</key> + <string>nVidia NVParse SDK: platform-indepenedent C++ Apple OpenGL helper library</string> + <key>license</key> + <string>glh_linear</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>23bd9a75e5a2365a827461e6c324f52b</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/glh_linear-darwin-20080613.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>23bd9a75e5a2365a827461e6c324f52b</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/glh_linear-linux-20080812.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>2965646aea1d2a6aec1fbc431c02733f</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/glh_linear-linux-20080613.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>38b9ddfe8dceff55ee4351016a937d1b</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/glh_linear-windows-20080613.tar.bz2</uri> + </map> + </map> + </map> + <key>glib</key> + <map> + <key>description</key> + <string>GLib is a library containing many useful C routines for things such as trees, hashes, and lists.</string> + <key>license</key> + <string>gpl</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>6cc5ce1fafd10299fdb890b3d4c3cf53</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/glib-2.0-darwin-20080817.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>2f1a9e14f9213c2c9564c1c1cfdd6d47</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/glib-2.0-linux-20080817.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>3d5e29d444dde4815b36082eedfc775a</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/glib-2.0-windows-20080817.tar.bz2</uri> + </map> + </map> + </map> + <key>glui</key> + <map> + <key>license</key> + <string>lgpl</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>84f792a860691d0fad6d1de6eeb31baa</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/glui-2.36-darwin-20090623a.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>5b8631fe510d4ebaeb965c673937e1e7</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/glui-2.3.6-windows-freeglut-20090608.tar.bz2</uri> + </map> + </map> + </map> + <key>google</key> + <map> + <key>license</key> + <string>mit</string> + <key>packages</key> + <map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>40db900872612615e849f17cbdfd2c27</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/google-linux-20080812.tar.bz2</uri> + </map> + </map> + </map> + <key>google-perftools</key> + <map> + <key>copyright</key> + <string>Copyright (c) 2005, Google Inc.</string> + <key>description</key> + <string>Heap performance and validity checking tools from google. Includes TCMalloc, heap-checker, heap-profiler and cpu-profiler.</string> + <key>license</key> + <string>bsd</string> + <key>packages</key> + <map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>32dba32ddd460a08e082898ebba6315c</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/google-perftools-1.0-windows-20090406.tar.bz2</uri> + </map> + </map> + </map> + <key>google_breakpad</key> + <map> + <key>copyright</key> + <string>Copyright (c) 2006, Google Inc.</string> + <key>description</key> + <string>An open-source multi-platform crash reporting system </string> + <key>license</key> + <string>bsd</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>ced4010b59f1a579caa7fe3c18512499</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/google_breakpad-0.0.0-rev599-darwin-20100528a.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>29c3e7dad60bbf02c811786436d99523</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/google_breakpad-0.0.0-rev599-linux-20100521b.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>0859d47242990125f17eaab30bece2ff</string> + <key>url</key> + <uri>http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/google_breakpad-0.0.0-rev599-windows-20100524.tar.bz2</uri> + </map> + </map> + </map> + <key>googlemock</key> + <map> + <key>copyright</key> + <string>Copyright 2008, Google Inc.</string> + <key>description</key> + <string>Google C++ Mocking Framework (or Google Mock for short) is a library for writing and using C++ mock classes.</string> + <key>license</key> + <string>bsd</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>4863e9fea433d0a4be761ea5d3e8346a</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/googlemock-1.1.0-darwin-20090626.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>877dabecf84339690191c6115c76366e</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/googlemock-1.1.0-linux32-20090527.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>f601a82ea91030974072da8924cae41e</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/googlemock-1.1.0-windows-20090921.tar.bz2</uri> + </map> + </map> + </map> + <key>gstreamer</key> + <map> + <key>license</key> + <string>lgpl</string> + <key>packages</key> + <map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>c829b638b6eef71ca63418cb9aea46a2</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/gstreamer-linux-20080613.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>befc7520fe01250f39458f65c29bc584</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/gstreamer-linux64-20080909.tar.bz2</uri> + </map> + </map> + </map> + <key>gtk-atk-pango-glib</key> + <map> + <key>copyright</key> + <string>Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald</string> + <key>description</key> + <string>Libraries associated with GTK for gui features. atk: interfaces for accessibility; glib: low-level core functionality for using GTK+ and GNOME; pango: layout/rendering of text w/ emphasis on internationalization.</string> + <key>license</key> + <string>lgpl</string> + <key>packages</key> + <map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>21c16a74f8fc9a62e3ab944a6eb7403d</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/gtk-atk-pango-glib-linux-20080616.tar.bz</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>d963750bcd333a108b3697d220c87d09</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/gtk-atk-pango-glib-windows-20080613.tar.bz2</uri> + </map> + </map> + </map> + <key>havok</key> + <map> + <key>copyright</key> + <string>on file</string> + <key>description</key> + <string>Physics engine for the simulator</string> + <key>license</key> + <string>havok</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>f64c08771a4fc456db2a55b47302078b</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/havok-4.6-darwin-20080812.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>af7b1fc9072443009f19e43fb3c8342f</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/havok-4.6.1-linux-20081029.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>cd4076d6caf5fabff36bf48bd01e4ba8</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/havok-4.6.1-linux64-20081030.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>f25fbb29c2275267233c79f0c68ca37f</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/havok-4.6.1-windows-20081030.tar.bz2</uri> + </map> + </map> + </map> + <key>jpeglib</key> + <map> + <key>copyright</key> + <string>Copyright (c) 2003, Yves Piguet.</string> + <key>description</key> + <string>An open-source JPEG (JFIF) library</string> + <key>license</key> + <string>jpeglib</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>8d38d74c481e9aab4518c8f2a7d52800</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/jpeglib-6b-darwin-20080812.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>8aa8e01e0c21f60f0ede0ffb04e9214f</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/jpeglib-6b-linux-20081218.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>0e7facf7d48531d20c0cd6a3c3f04021</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/jpeglib-6b-linux64-20080909.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>6a6bb0143a2561e3276dab4bcfa425ef</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/jpeglib-6b-windows-20090917a.tar.bz2</uri> + </map> + </map> + </map> + <key>jsoncpp</key> + <map> + <key>copyright</key> + <string>json-cpp library released to Public Domain by Baptiste Lepilleur <blep@users.sourceforge.net></string> + <key>description</key> + <string>jsoncpp is an implementation of a JSON (http://json.org) reader and writer in C++.</string> + <key>license</key> + <string>jsoncpp</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>4c6b949778099a63550898f00f3e6a5e</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/jsoncpp-svn-r69-darwin-20090923.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>a2a94b8ca1d32f23e3e668d64023514e</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/jsoncpp-svn-r69-linux-20090922.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>a06ab38628ab7b53b8f3326cd942a6a8</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/jsoncpp-svn-r69-linux64-20090922.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>caf152cfc730737c124f7612cf68fbd3</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/jsoncpp-svn-r69-windows-20090922.tar.bz2</uri> + </map> + </map> + </map> + <key>kdu</key> + <map> + <key>copyright</key> + <string>on file</string> + <key>description</key> + <string>Kakadu (KDU) JPEG-2000 decoder library. </string> + <key>license</key> + <string>kdu</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>8261994de5af6581e08c26fefe1b2810</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/kdu-6.4.1-darwin-20101123.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>ed3e58899a424684dad49c94ba3813e7</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/kdu-6.4.1-linux-20101124.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>066e089a5d9faeaf131e1f4e4860a163</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/kdu-6.4.1-windows-20101123.tar.bz2</uri> + </map> + </map> + </map> + <key>libmono</key> + <map> + <key>copyright</key> + <string>(C) 2005 Novell, Inc. http://www.novell.com</string> + <key>description</key> + <string>An open source implementation of the ECMA/ISO ECMA-334 Common L\ +anguage Infrstructure (CLI) international standard</string> + <key>license</key> + <string>lgpl</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>39a803fcbe6f11b72358fc78b7777b6c</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/libmono-darwin-20080724.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>9bc0f8b7d5e0ff194b6d5635daf9ae3a</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/libmono-1.2.6-linux-20080816a.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>451521b4cb57c35caf3efb8dcf99b99e</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/libmono-1.2.6-linux64-20080926.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>6712a09311a914752f47d5d62562a239</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/libmono-1.2.6-windows-20080903.tar.bz2</uri> + </map> + </map> + </map> + <key>libpng</key> + <map> + <key>copyright</key> + <string>Copyright (c) 2004, 2006-2008 Glenn Randers-Pehrson</string> + <key>description</key> + <string>An open, extensible image format with lossless compression. PNG Reference Library </string> + <key>license</key> + <string>libpng</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>82659b48831cbf58bf04b86602939e0b</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/libpng-1.2.35-darwin-20090304.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>f5e84c991f6e3caacb26db259593cbea</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/libpng-1.2.35-linux-20090304.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>5ee1e62bde38520c7f134c4afb9ac9b1</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/libpng-1.2.35-linux64-20090304.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>c781cd9846cf20afb90ac40ad1a0ce9d</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/libpng-1.2.35-windows-20090917.tar.bz2</uri> + </map> + </map> + </map> + <key>libuuid</key> + <map> + <key>copyright</key> + <string>Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/></string> + <key>description</key> + <string>Generates UUIDs under Linux. Originally a part of the ext2fs filesystem. Also see lluuid.cpp for all platforms. Part of the e2fsprogs package.</string> + <key>license</key> + <string>lgpl</string> + <key>packages</key> + <map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>91b194aed4b38bc23493b198009a8c6a</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/libuuid-linux-20090417.tar.bz2</uri> + </map> + </map> + </map> + <key>libxml</key> + <map> + <key>license</key> + <string>mit</string> + <key>packages</key> + <map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>4b5d2dcfe8a49b73fb69f10aab441092</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/libxml-2.6.24-linux-20080613.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>921d7f980519101afb74623e29e9d175</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/libxml-2.6.24-linux64-20080909.tar.bz2</uri> + </map> + </map> + </map> + <key>llqtwebkit</key> + <map> + <key>license</key> + <string>lgpl</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>f07b063cdc207479b111576dc74127f0</string> + <key>url</key> + <uri>http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/llqtwebkit-darwin-qt4.7.1-20110322.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>5d743c93b970abe685b185de83001a6e</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/llqtwebkit-linux-qt4.6-20100923.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>1e66b1ecab911a60ba50b59361ef62e1</string> + <key>url</key> + <uri>http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/llqtwebkit-windows-qt4.7.1-20110317.tar.bz2</uri> + </map> + </map> + </map> + <key>mesa</key> + <map> + <key>copyright</key> + <string>Copyright (C) 1999-2007 Brian Paul All Rights Reserved.</string> + <key>description</key> + <string>Mesa 3-D graphics library. Provides the required Apple OpenGL headers under Linux.</string> + <key>license</key> + <string>mesa</string> + <key>packages</key> + <map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>70d0bbe1145fff29a0131349c898260e</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/mesa-7.0-linux-20080812.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>56630977f9261bd82039b0da08a0685c</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/mesa-6.2.1-linux64-20081016.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>82cdcdcb2d0615389a7480485ea35f4c</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/mesa-7.0-windows-20080613.tar.bz2</uri> + </map> + </map> + </map> + <key>mysql</key> + <map> + <key>license</key> + <string>gpl</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>df27f2db244ea2762759a06cd75ada4e</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/mysql-darwin-20080812.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>cc86b4cc858655e23704d1168325d7b9</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/mysqlclient-linux-20090320.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>98bac06680dca907e783d8dd4aa9edde</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/mysql-windows-20080804.tar.bz2</uri> + </map> + </map> + </map> + <key>ndofdev</key> + <map> + <key>copyright</key> + <string>Copyright (c) 2007, 3Dconnexion, Inc. - All rights reserved.</string> + <key>description</key> + <string>in use on windows and darwin for joystick support.</string> + <key>license</key> + <string>linden</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>17999c47e17f2dd9e12a22372ce8ff14</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ndofdev-darwin-20080812.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>9469c3732a33a154fa0a2807b9f36ccc</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ndofdev-linux-0.2-20080828.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>f0df8a1e60991095e3adca1450b8c9c0</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ndofdev-windows-20090917.tar.bz2</uri> + </map> + </map> + </map> + <key>ogg-vorbis</key> + <map> + <key>copyright</key> + <string>Copyright (C) 2008 Xiph.org Foundation</string> + <key>description</key> + <string>Ogg: container format Vorbis: audio compression scheme</string> + <key>license</key> + <string>ogg-vorbis</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>a6843398b780645c4897c9776c688926</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ogg-vorbis-1.03-1.1.2-darwin-20080812.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>6dc0536329a0aadf76e3054ffd4da61c</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ogg-vorbis-1.2.0-linux-20081201.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>964c71e6ee22be1bcaf6d480e74cdd14</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ogg-vorbis-1.2.0-linux64-20080909.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>9bf1fea65e66b2cd3075e6ffd7eb57ad</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ogg-vorbis-1.1.3-1.2.0-windows-20080723.tar.bz2</uri> + </map> + </map> + </map> + <key>openSSL</key> + <map> + <key>license</key> + <string>openSSL</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>11d2be4f2b172430747b7d4a6739e3d8</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openSSL-0.9.8l-darwin-20100428.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>f219ef07b02e2abb9282345c3a8f2b39</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openSSL-0.9.7c-linux-20080812.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>00b23f28a2457d9dabbaff0b29ee7323</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openSSL-0.9.8g-linux64-20080909.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>dd85209081b832e836de6e1538541d89</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openSSL-0.9.8j-windows-20090129.tar.bz2</uri> + </map> + </map> + </map> + <key>openal-soft</key> + <map> + <key>copyright</key> + <string>Copyright (C) 2008 by authors.</string> + <key>description</key> + <string>3D Audio library</string> + <key>license</key> + <string>lgpl</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>a0757244e3e6688fde2ffeea35cc1f96</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openal-darwin-20080924.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>75a7004ab14bea46594b1c652f1a6040</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openal-linux-20100120-3ad86a1c.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>5ad0a3ab623356c1ad61394ba238f99f</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openal-soft-1.6.372-linux64-20081219.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>a0757244e3e6688fde2ffeea35cc1f96</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openal-windows-20080924.tar.bz2</uri> + </map> + </map> + </map> + <key>openjpeg</key> + <map> + <key>copyright</key> + <string>Copyright (c) 2002-2007, Communications and Remote Sensing Laboratory, Universite catholique de Louvain (UCL), Belgium</string> + <key>description</key> + <string>An open-source JPEG-2000 library; a slower alternative to Kadaku. Used in the open source release </string> + <key>license</key> + <string>openjpeg</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>23313fda213a2496945435db2a0ee78b</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openjpeg-1.3.0-darwin-20090501.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>02af0dad64803e0d821bc09e6038682c</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openjpeg-1.3-linux-20081124b.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>44f1bc9d47e4a54fc274c213f2cb565f</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openjpeg-1.2.0-linux64-20080909.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>41541a98106894e28a6bf585010fea65</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openjpeg-1.3-windows-20090407.tar.bz2</uri> + </map> + </map> + </map> + <key>pth</key> + <map> + <key>copyright</key> + <string>Copyright (c) 1999-2006 Ralf S. Engelschall <rse@gnu.org></string> + <key>description</key> + <string>Portable cooperative threads package, used to support Boost.Coroutine on Mac OS X 10.4</string> + <key>license</key> + <string>lgpl</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>216cb6217a06c64dfae30a55ab8b975c</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-darwin-20090923.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>c5c2f73847c126e679d925beab48c7d4</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-linux-20090427.tar.bz2</uri> + </map> + <key>linux32</key> + <map> + <key>md5sum</key> + <string>c5c2f73847c126e679d925beab48c7d4</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-linux32-20090427.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>c5c2f73847c126e679d925beab48c7d4</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-linux64-20090427.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>c5c2f73847c126e679d925beab48c7d4</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-windows-20090427.tar.bz2</uri> + </map> + </map> + </map> + <key>pulseaudio</key> + <map> + <key>copyright</key> + <string>Copyright 2004-2006 Lennart Poettering, Copyright 2006 Pierre Ossman (ossman@cendio.se) for Cendio AB</string> + <key>description</key> + <string>pulseaudio: headers only</string> + <key>license</key> + <string>lgpl</string> + <key>packages</key> + <map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>30cb00069fe2a545fbf7be1070386236</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/linux-pulse-headers-0.9.14.tar.bz2</uri> + </map> + </map> + </map> + <key>quicktime</key> + <map> + <key>copyright</key> + <string>Copyright (C) 1990-2007 by Apple Computer, Inc., all rights reserved.</string> + <key>description</key> + <string>Separate download. Used to play in-world video clips on a prim. </string> + <key>license</key> + <string>quicktime</string> + <key>packages</key> + <map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>be45825cc14ede53790ac93c58307dcb</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/quicktime-sdk-windows-7.3-20091110.tar.bz2</uri> + </map> + </map> + </map> + <key>smartheap</key> + <map> + <key>copyright</key> + <string>Copyright (C) 1991-2000 Compuware Corporation. All Rights Reserved.</string> + <key>description</key> + <string>Memory Management Library</string> + <key>license</key> + <string>smartheap</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>f54131b5f228e805c64c2e4e6c96579a</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/smartheap-6.0.2-darwin-20080610.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>499208522bf7d7843e1d014d64214e06</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/smartheap-6.0.2-linux-20080610.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>78fd47017f21d11eae43bca3e38a3e1e</string> + <key>url</key> + <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/smartheap-6.0.2-windows-20080611.tar.bz2</uri> + </map> + </map> + </map> + <key>tut</key> + <map> + <key>copyright</key> + <string>Copyright 2002-2006 Vladimir Dyuzhev, Copyright 2007 Denis Kononenko, Copyright 2008 Michal Rzechonek</string> + <key>description</key> + <string>C++ Template Unit Test</string> + <key>license</key> + <string>bsd</string> + <key>packages</key> + <map> + <key>common</key> + <map> + <key>md5sum</key> + <string>a1b8a118ba9df1f2a73f6aafa7980e83</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/tut-2008-11-30-common-20081208.tar.bz2</uri> + </map> + </map> + </map> + <key>unistd</key> + <map> + <key>copyright</key> + <string>(c) 2008 Linden Lab.</string> + <key>description</key> + <string>Placeholder file to make flex happy on windows.</string> + <key>license</key> + <string>linden</string> + <key>packages</key> + <map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>6353aff33d7d03b22055aec76f53a866</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/unistd-windows-20080611.tar.bz2</uri> + </map> + </map> + </map> + <key>slvoice</key> + <map> + <key>copyright</key> + <string> </string> + <key>license</key> + <string>vivox</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>2f9b3528d4b5f858fb8dcee4b6dd5188</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/slvoice-3.2.0002.9361-darwin-20101117a.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>cde4728b8a75a76c72a8785815cb769f</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/slvoice-3.2.0002.9361-linux-20101117a.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>940ac55a6d0141c958bf2b14939d8474</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/slvoice-3.2.0002.9361-windows-20101117a.tar.bz2</uri> + </map> + </map> + </map> + <key>xmlrpc-epi</key> + <map> + <key>copyright</key> + <string>Copyright 2000 Epinions, Inc.</string> + <key>description</key> + <string>Implementation of the xmlrpc protocol in C that provides an API for developers to serialize RPC requests to and from XML. </string> + <key>license</key> + <string>xmlrpc-epi</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>2d3a918c88d756422c1a8139ebe15f56</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/xmlrpc-epi-0.51-darwin-20080812.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>84a219199240ea70f54439c02acef0cd</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/xmlrpc-epi-0.51-linux-20080812.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>dc67b896c56116df8e18f2d1bbd07031</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/xmlrpc-epi-0.51-linux64-20080909.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>262629bcaa39dcf7266caa50da01a599</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/xmlrpc-epi-0.51-windows-20091016.tar.bz2</uri> + </map> + </map> + </map> + <key>zlib</key> + <map> + <key>copyright</key> + <string>Copyright (C) 1995-2003 Jean-loup Gailly and Mark Adler</string> + <key>description</key> + <string>A Massively Spiffy Yet Delicately Unobtrusive Compression Library (Also Free, Not to Mention Unencumbered by Patents)</string> + <key>license</key> + <string>zlib</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>c844e1b05723ce078dbbd5aea9cdd3ad</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/zlib-1.1.4-darwin-20080818.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>26fe88213c213dc6153690ab142c25ca</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/zlib-1.2.3dfsg-linux-20091208.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>4bddfb2c6dd7b1470a3ed675ac14bd9a</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/zlib-1.2.3-linux64-20080909.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>73baf52a740d151fddbc2a008369c462</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/zlib-1.2.3-windows-20090921.tar.bz2</uri> + </map> + </map> + </map> + </map> + <key>licenses</key> + <map> + <key>AFL2.1</key> + <map> + <key>url</key> + <string>http://opensource-definition.org/licenses/afl-2.1.html</string> + </map> + <key>GL</key> + <map> + <key>url</key> + <string>http://www.xfree86.org/4.4.0/LICENSE9.html#sgi</string> + </map> + <key>MSDTW</key> + <map> + <key>text</key> + <string>MICROSOFT SOFTWARE LICENSE TERMS +MICROSOFT DEBUGGING TOOLS FOR WINDOWS +These license terms are an agreement between Microsoft Corporation (or based on where you live, one of +its affiliates) and you. Please read them. They apply to the software named above, which includes the +media on which you received it, if any. The terms also apply to any Microsoft +* updates, +* supplements, +* Internet-based services +* support services, and +* Debugging symbol files that you may access over the internet +for this software, unless other terms accompany those items. If so, those terms apply. +By using the software, you accept these terms. If you do not accept them, do not use the +software. +If you comply with these license terms, you have the rights below. +1. INSTALLATION AND USE RIGHTS. One user may install and use any number of copies of the +software on your devices to design, develop, debug and test your programs. +2. ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS. +a. Distributable Code. The software contains code that you are permitted to distribute in programs +you develop if you comply with the terms below. +i. Right to Use and Distribute. The code and text files listed below are Distributable Code. +* REDIST.TXT Files. You may copy and distribute the object code form of code listed in +REDIST.TXT files. +* Sample Code. You may modify, copy, and distribute the source and object code form of +code marked as sample. +* Third Party Distribution. You may permit distributors of your programs to copy and +distribute the Distributable Code as part of those programs. +ii. Distribution Requirements. For any Distributable Code you distribute, you must +* add significant primary functionality to it in your programs; +* require distributors and external end users to agree to terms that protect it at least as much +as this agreement; +* display your valid copyright notice on your programs; and +* indemnify, defend, and hold harmless Microsoft from any claims, including attorneys fees, +related to the distribution or use of your programs. +iii. Distribution Restrictions. You may not +* alter any copyright, trademark or patent notice in the Distributable Code; +* distribute any symbol files which you may access or use under these license terms for the +software; +* use Microsofts trademarks in your programs names or in a way that suggests your +programs come from or are endorsed by Microsoft; +* distribute Distributable Code to run on a platform other than the Windows platform; +* include Distributable Code in malicious, deceptive or unlawful programs; or +* modify or distribute the source code of any Distributable Code so that any part of it +becomes subject to an Excluded License. An Excluded License is one that requires, as a +condition of use, modification or distribution, that +* the code be disclosed or distributed in source code form; or +* others have the right to modify it. +3. SCOPE OF LICENSE. The software is licensed, not sold. This agreement only gives you some rights +to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights +despite this limitation, you may use the software only as expressly permitted in this agreement. In +doing so, you must comply with any technical limitations in the software that only allow you to use it in +certain ways. You may not +* work around any technical limitations in the software; +* reverse engineer, decompile or disassemble the software, except and only to the extent that +applicable law expressly permits, despite this limitation; +* make more copies of the software than specified in this agreement or allowed by applicable law, +despite this limitation; +* publish the software for others to copy; +* rent, lease or lend the software; +* transfer the software or this agreement to any third party; or +* use the software for commercial software hosting services. +4. INTERNET-BASED SERVICES. Microsoft provides Internet-based services with the software. It may +change or cancel them at any time. +a. Consent for Internet-Based Services. The software contains features which may connect to +Microsoft or service provider computer systems over the Internet. In some cases, you will not +receive a separate notice when they connect. You may switch these features on or you may +choose not to use them. For more information about these features, see +http://www.microsoft.com/info/privacy/default.mspx. By using these features, you consent to the transmission of +this information. Microsoft does not use the information to identify or contact you. +b. Misuse of Internet-based Services. You may not use these services in any way that could +harm them or impair anyone elses use of them. You may not use the services to try to gain +unauthorized access to any service, data, account or network by any means. + +5. BACKUP COPY. You may make one backup copy of the software. You may use it only to reinstall the +software. +6. DOCUMENTATION. Any person that has valid access to your computer or internal network may copy +and use the documentation for your internal, reference purposes. +7. EXPORT RESTRICTIONS. The software is subject to United States export laws and regulations. You +must comply with all domestic and international export laws and regulations that apply to the software. +These laws include restrictions on destinations, end users and end use. For additional information, see +www.microsoft.com/exporting. +8. SUPPORT SERVICES. Because this software is as is, we may not provide support services for it. +9. ENTIRE AGREEMENT. This agreement, and the terms for supplements, updates, Internet-based +services and support services that you use, are the entire agreement for the software and support +services. +10. APPLICABLE LAW. +a. United States. If you acquired the software in the United States, Washington state law governs +the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of +laws principles. The laws of the state where you live govern all other claims, including claims under +state consumer protection laws, unfair competition laws, and in tort. +b. Outside the United States. If you acquired the software in any other country, the laws of that +country apply. +11. LEGAL EFFECT. This agreement describes certain legal rights. You may have other rights under the +laws of your country. You may also have rights with respect to the party from whom you acquired the +software. This agreement does not change your rights under the laws of your country if the laws of +your country do not permit it to do so. +12. DISCLAIMER OF WARRANTY. The software is licensed as-is. You bear the risk of using +it. Microsoft gives no express warranties, guarantees or conditions. You may have +additional consumer rights under your local laws which this agreement cannot change. To +the extent permitted under your local laws, Microsoft excludes the implied warranties of +merchantability, fitness for a particular purpose and non-infringement. +13. LIMITATION ON AND EXCLUSION OF REMEDIES AND DAMAGES. You can recover from +Microsoft and its suppliers only direct damages up to U.S. $5.00. You cannot recover any +other damages, including consequential, lost profits, special, indirect or incidental +damages. +This limitation applies to +* anything related to the software, services, content (including code) on third party Internet sites, or +third party programs; and +* claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, +or other tort to the extent permitted by applicable law. +It also applies even if Microsoft knew or should have known about the possibility of the damages. The +above limitation or exclusion may not apply to you because your country may not allow the exclusion or +limitation of incidental, consequential or other damages. + </string> + </map> + <key>apache 2.0</key> + <map> + <key>url</key> + <string>http://www.apache.org/licenses/LICENSE-2.0</string> + </map> + <key>artwork</key> + <map> + <key>text</key> + <string>COPYRIGHT AND PERMISSION NOTICE + +Second Life(TM) Viewer Artwork. Copyright (C) 2008 Linden Research, Inc. + +Linden Research, Inc. ("Linden Lab") licenses the Second Life viewer +artwork and other works in the files distributed with this Notice under +the Creative Commons Attribution-Share Alike 3.0 License, available at +http://creativecommons.org/licenses/by-sa/3.0/legalcode. For the license +summary, see http://creativecommons.org/licenses/by-sa/3.0/. + +Notwithstanding the foregoing, all of Linden Lab's trademarks, including +but not limited to the Second Life brand name and Second Life Eye-in-Hand +logo, are subject to our trademark policy at +http://secondlife.com/corporate/trademark/. + +If you distribute any copies or adaptations of the Second Life viewer +artwork or any other works in these files, you must include this Notice +and clearly identify any changes made to the original works. Include +this Notice and information where copyright notices are usually included, +for example, after your own copyright notice acknowledging your use of +the Second Life viewer artwork, in a text file distributed with your +program, in your application's About window, or on a credits page for +your work. +</string> + <key>url</key> + <string>http://svn.secondlife.com/svn/linden/trunk/doc/LICENSE-logos.txt</string> + </map> + <key>boost</key> + <map> + <key>url</key> + <string>http://www.boost.org/LICENSE_1_0.txt</string> + </map> + <key>bsd</key> + <map> + <key>url</key> + <string>http://www.opensource.org/licenses/bsd-license.php</string> + </map> + <key>c-ares</key> + <map> + <key>text</key> + <string>http://daniel.haxx.se/projects/c-ares/license.html</string> + </map> + <key>curl</key> + <map> + <key>url</key> + <string>http://curl.haxx.se/docs/copyright.html</string> + </map> + <key>fmod</key> + <map> + <key>url</key> + <string>http://www.fmod.org/ifmodlicense.html</string> + </map> + <key>freetype</key> + <map> + <key>url</key> + <string>http://freetype.sourceforge.net/FTL.TXT</string> + </map> + <key>glh_linear</key> + <map> + <key>text</key> + <string>glh - is a platform-indepenedent C++ OpenGL helper library + +Copyright (c) 2000 Cass Everitt +Copyright (c) 2000 NVIDIA Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the following +conditions are met: + +Redistributions of source code must retain the above +copyright notice, this list of conditions and the following +disclaimer. +Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following +disclaimer in the documentation and/or other materials +provided with the distribution. +The names of contributors to this software may not be used +to endorse or promote products derived from this software +without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +Cass Everitt - cass@r3.nu + +</string> + </map> + <key>glut</key> + <map> + <key>url</key> + <string>http://www.xmission.com/~nate/glut/README-win32.txt</string> + </map> + <key>gpl</key> + <map> + <key>url</key> + <string>http://www.gnu.org/licenses/gpl.html</string> + </map> + <key>havok</key> + <map> + <key>text</key> + <string>on file</string> + </map> + <key>intel</key> + <map> + <key>text</key> + <string>Haven't yet found. +</string> + </map> + <key>jpeglib</key> + <map> + <key>text</key> + <string>http://nyctergatis.com/jpeglib/</string> + </map> + <key>jsoncpp</key> + <map> + <key>text</key> + <string>The json-cpp library and this documentation are in Public Domain. Retrieved from http://jsoncpp.sourceforge.net/ on 2009-09-04.</string> + <key>url</key> + <string>http://jsoncpp.sourceforge.net</string> + </map> + <key>kdu</key> + <map> + <key>text</key> + <string>jpeg2000 license #00024 (on file)</string> + </map> + <key>lgpl</key> + <map> + <key>url</key> + <string>http://www.gnu.org/copyleft/lgpl.html</string> + </map> + <key>libpng</key> + <map> + <key>text</key> + <string>http://www.libpng.org/pub/png/src/libpng-LICENSE.txt</string> + </map> + <key>linden</key> + <map> + <key>text</key> + <string>Using this license for Linden Lab owned library files</string> + </map> + <key>mesa</key> + <map> + <key>url</key> + <string>http://www.mesa3d.org/license.html</string> + </map> + <key>mit</key> + <map> + <key>text</key> + <string>http://www.jclark.com/xml/copying.txt</string> + </map> + <key>ogg-vorbis</key> + <map> + <key>url</key> + <string>http://www.xiph.org/licenses/bsd/</string> + </map> + <key>openSSL</key> + <map> + <key>url</key> + <string>http://www.openssl.org/source/license.html</string> + </map> + <key>openjpeg</key> + <map> + <key>url</key> + <string>http://www.openjpeg.org/BSDlicense.txt</string> + </map> + <key>quicktime</key> + <map> + <key>text</key> + <string>ENGLISH + +Apple Computer, Inc. +QuickTime 7 Software Developer Kit (SDK) +Software License Agreement + +PLEASE READ THIS SOFTWARE LICENSE AGREEMENT ("LICENSE") BEFORE USING THE SOFTWARE. BY USING THE SOFTWARE, YOU ARE AGREEING TO BE BOUND BY THE TERMS OF THIS LICENSE. IF YOU ARE ACCESSING THE SOFTWARE ELECTRONICALLY, SIGNIFY YOUR AGREEMENT TO BE BOUND BY THE TERMS OF THIS LICENSE BY CLICKING THE "AGREE/ACCEPT" BUTTON. IF YOU DO NOT AGREE TO THE TERMS OF THIS LICENSE, RETURN THE APPLE SOFTWARE TO THE PLACE WHERE YOU OBTAINED IT FOR A REFUND OR, IF THE SOFTWARE WAS ACCESSED ELECTRONICALLY, CLICK "DISAGREE/DECLINE". + +IMPORTANT NOTE: To the extent this software may be used to reproduce materials, it is licensed to you only for reproduction of materials you are authorized or legally permitted to reproduce. + +1. License. Any software, tools, utilities, sample code, documentation, fonts, API?s, header files and other materials accompanying this License, whether on disk, print or electronic documentation, in read only memory, or any other media, (collectively, the "Apple Software") are licensed, not sold, to you by Apple Computer, Inc. ("Apple") for use only under the terms of this License, and Apple reserves all rights not expressly granted to you. The rights granted herein are limited to Apple's and its licensors' intellectual property rights in the Apple Software and do not include any other patents or intellectual property rights. You own the media on which the Apple Software is recorded but Apple and/or Apple's licensor(s) retain ownership of the Apple Software itself. The Apple Software in this package and any copies, modifications and derivative works that this License authorizes you to make are subject to this License. + +2. Permitted Uses and Restrictions. You may use the Apple Software to develop application software that is compatible with, and runs only on Mac OS X and/or Windows platforms with QuickTime installed. Except for compiling header files and linking libraries as necessary to build your application software, you have no right to modify, incorporate into or include in combination with your own programs, license or otherwise redistribute any portion of the Apple Software. Your software application may not interfere with the functionality of QuickTime Player or the QuickTime Plug-in, including but not limited to file type or MIME type associations that are registered to QuickTime. You may make only as many internal use copies of the Apple Software as reasonably necessary to use the Apple Software as permitted in this paragraph and distribute such copies only to your employees whose job duties require them to so use the Apple Software. You must reproduce on each copy of the Apple Software or portion thereof, the Apple copyright notice and any other proprietary legends that were on the original copy of the Apple Software. Except as expressly permitted in this License, you may not decompile, reverse engineer, disassemble, modify, rent, lease, loan, sublicense, distribute or create derivative works based upon the Apple Software in whole or part. Your rights under this License will terminate automatically without notice from Apple if you fail to comply with any term(s) of this License. In addition, Apple reserves the right to terminate this License if a new version of Apple's operating system software or the Apple Software is released which is incompatible with the Apple Software. + +3. Disclaimer Of Warranty. The Apple Software may be "alpha", "beta", "development", pre-release, untested, and/or not fully tested and may contain errors that could cause failures or loss of data, be incomplete or contain inaccuracies. YOU EXPRESSLY ACKNOWLEDGE AND AGREE THAT USE OF THE APPLE SOFTWARE IS AT YOUR SOLE RISK AND THAT THE ENTIRE RISK AS TO SATISFACTORY QUALITY, PERFORMANCE, ACCURACY AND EFFORT IS WITH YOU. EXCEPT FOR THE LIMITED WARRANTY ON MEDIA SET FORTH ABOVE AND TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE APPLE SOFTWARE IS PROVIDED "AS IS", WITH ALL FAULTS AND WITHOUT WARRANTY OF ANY KIND, AND APPLE AND APPLE'S LICENSORS (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE PURPOSES OF SECTIONS 4 AND 5) HEREBY DISCLAIM ALL WARRANTIES AND CONDITIONS WITH RESPECT TO THE APPLE SOFTWARE, EITHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NON-INFRINGEMENT OF THIRD PARTY RIGHTS. APPLE DOES NOT WARRANT AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE APPLE SOFTWARE, THAT THE FUNCTIONS CONTAINED IN THE APPLE SOFTWARE WILL MEET YOUR REQUIREMENTS, THAT THE OPERATION OF THE APPLE SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT DEFECTS IN THE APPLE SOFTWARE WILL BE CORRECTED. NO ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE OR AN APPLE AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY. SHOULD THE APPLE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE ENTIRE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES OR LIMITATIONS ON APPLICABLE STATUTORY RIGHTS OF A CONSUMER, SO THE ABOVE EXCLUSION AND LIMITATIONS MAY NOT APPLY TO YOU. + +4. Limitation Of Liability. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT SHALL APPLE BE LIABLE FOR PERSONAL INJURY, OR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER, INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, LOSS OF DATA, BUSINESS INTERRUPTION OR ANY OTHER COMMERCIAL DAMAGES OR LOSSES, ARISING OUT OF OR RELATED TO YOUR USE OR INABILITY TO USE THE APPLE SOFTWARE, HOWEVER CAUSED, REGARDLESS OF THE THEORY OF LIABILITY (CONTRACT, TORT OR OTHERWISE) AND EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY FOR PERSONAL INJURY, OR OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU. In no event shall Apple's total liability to you for all damages (other than as may be required by applicable law in cases involving personal injury) exceed the amount of fifty dollars ($50.00). The foregoing limitations will apply even if the above stated remedy fails of its essential purpose. + +5. Export Control. You may not use or otherwise export or reexport the Apple Product except as authorized by United States law and the laws of the jurisdiction in which the Apple Product was obtained. In particular, but without limitation, the Apple Product may not be exported or re-exported (a) into any U.S. embargoed countries or (b) to anyone on the U.S. Treasury Department's list of Specially Designated Nationals or the U.S. Department of Commerce Denied Person?s List or Entity List. By using the Apple Product, you represent and warrant that you are not located in any such country or on any such list. + +6. Government End Users. The Apple Software and related documentation are "Commercial Items", as that term is defined at 48 C.F.R. ?2.101, consisting of "Commercial Computer Software" and "Commercial Computer Software Documentation", as such terms are used in 48 C.F.R. ?12.212 or 48 C.F.R. ?227.7202, as applicable. Consistent with 48 C.F.R. ?12.212 or 48 C.F.R. ?227.7202-1through 227.7202-4, as applicable, the Commercial Computer Software and Commercial Computer Software Documentation are being licensed to U.S. Government end users (a) only as Commercial Items and (b) with only those rights as are granted to all other end users pursuant to the terms and conditions herein. Unpublished-rights reserved under the copyright laws of the United States. + +7. Controlling Law and Severability. This License will be governed by and construed in accordance with the laws of the State of California, as applied to agreements entered into and to be performed entirely within California between California residents. This License shall not be governed by the United Nations Convention on Contracts for the International Sale of Goods, the application of which is expressly excluded. If for any reason a court of competent jurisdiction finds any provision, or portion thereof, to be unenforceable, the remainder of this License shall continue in full force and effect. + +8. Complete Agreement. This License constitutes the entire agreement between the parties with respect to the use of the Apple Software licensed hereunder and supersedes all prior or contemporaneous understandings regarding such subject matter. No amendment to or modification of this License will be binding unless in writing and signed by Apple. Any translation of this License is done for local requirements and in the event of a dispute between the English and any non-English versions, the English version of this License shall govern. + +EA0300 +</string> + </map> + <key>sleepycat</key> + <map> + <key>url</key> + <string>http://opensource.org/licenses/sleepycat.php</string> + </map> + <key>smartheap</key> + <map> + <key>text</key> + <string>on file +</string> + </map> + <key>things</key> + <map> + </map> + <key>vivox</key> + <map> + <key>text</key> + <string>on file</string> + </map> + <key>xmlrpc-epi</key> + <map> + <key>url</key> + <string>http://xmlrpc-epi.sourceforge.net/main.php?t=license</string> + </map> + <key>zlib</key> + <map> + <key>url</key> + <string>http://www.gzip.org/zlib/zlib_license.html</string> + </map> + </map> + </map> +</llsd> |