diff options
76 files changed, 4227 insertions, 703 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 diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 9342a22d46..4f7e2f4c0d 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -62,6 +62,7 @@ set(llcommon_SOURCE_FILES      llformat.cpp      llframetimer.cpp      llheartbeat.cpp +    llinstancetracker.cpp      llliveappconfig.cpp      lllivefile.cpp      lllog.cpp @@ -313,6 +314,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/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/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/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/llui/tests/llurlentry_stub.cpp b/indra/llui/tests/llurlentry_stub.cpp index 96ebe83826..c11ad11de9 100644 --- a/indra/llui/tests/llurlentry_stub.cpp +++ b/indra/llui/tests/llurlentry_stub.cpp @@ -193,8 +193,8 @@ LLFontGL* LLFontGL::getFontDefault()  	return NULL;   } -char* _PREHASH_AgentData = "AgentData"; -char* _PREHASH_AgentID = "AgentID"; +char* _PREHASH_AgentData = const_cast<char*>("AgentData"); +char* _PREHASH_AgentID   = const_cast<char*>("AgentID");  LLHost LLHost::invalid(INVALID_PORT,INVALID_HOST_IP_ADDRESS); diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index 4d2677fd91..08ebaf7577 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -34,16 +34,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..c87617c9ff --- /dev/null +++ b/indra/llwindow/llkeyboardheadless.cpp @@ -0,0 +1,73 @@ +/**  + * @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() +{ +	for (S32 key = 0; key < KEY_COUNT; key++) +	{ +		// Generate callback if any event has occurred on this key this frame. +		// Can't just test mKeyLevel, because this could be a slow frame and +		// key might have gone down then up. JC +		if (mKeyLevel[key] || mKeyDown[key] || mKeyUp[key]) +		{ +			mCurScanKey = key; +			mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]); +		} +	} + +	// Reset edges for next frame +	for (S32 key = 0; key < KEY_COUNT; key++) +	{ +		mKeyUp[key] = FALSE; +		mKeyDown[key] = FALSE; +		if (mKeyLevel[key]) +		{ +			mKeyLevelFrameCount[key]++; +		} +	} +} +  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..2d00c37719 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -41,6 +41,7 @@  #include "llkeyboard.h"  #include "linked_lists.h"  #include "llwindowcallbacks.h" +#include "llwindowlistener.h"  // @@ -115,10 +116,15 @@ LLWindow::LLWindow(LLWindowCallbacks* callbacks, BOOL fullscreen, U32 flags)  	  mHideCursorPermanent(FALSE),  	  mFlags(flags),  	  mHighSurrogate(0) -{ } +{ +	mListener = new LLWindowListener(callbacks, 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..59f4c89e34 --- /dev/null +++ b/indra/llwindow/llwindowlistener.cpp @@ -0,0 +1,174 @@ +/**  + * @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" + +LLWindowListener::LLWindowListener(LLWindowCallbacks *window, LLKeyboard * keyboard) +	: LLEventAPI("LLWindow", "Inject input events into the LLWindow instance"), +	  mWindow(window), +	  mKeyboard(keyboard) +{ +	add("keyDown", +		"Given [\"keycode\"] or [\"char\"], will inject the given keypress event.", +		&LLWindowListener::keyDown); +	add("keyUp", +		"Given [\"keycode\"] or [\"char\"], will inject the given key release event.", +		&LLWindowListener::keyUp); +	add("mouseDown", +		"Given [\"button\"], [\"x\"] and [\"y\"], will inject the given mouse click event.", +		&LLWindowListener::mouseDown); +	add("mouseUp", +		"Given [\"button\"], [\"x\"] and [\"y\"], will inject the given mouse release event.", +		&LLWindowListener::mouseUp); +	add("mouseMove", +		"Given [\"x\"] and [\"y\"], will inject the given mouse movement event.", +		&LLWindowListener::mouseMove); +	add("mouseScroll", +		"Given a number of [\"clicks\"], will inject the given mouse scroll event.", +		&LLWindowListener::mouseScroll); +} + +void LLWindowListener::keyDown(LLSD const & evt) +{ +	if(NULL == mKeyboard) +	{ +		// *HACK to handle the fact that LLWindow subclasses have to initialize +		// things in an inconvenient order +		mKeyboard = gKeyboard; +	} + +	KEY keycode = 0; +	if(evt.has("keycode")) +	{ +		keycode = KEY(evt["keycode"].asInteger()); +	} +	else +	{ +		keycode = KEY(evt["char"].asString()[0]); +	} + +	// *TODO - figure out how to handle the mask +	mKeyboard->handleTranslatedKeyDown(keycode, 0); +} + +void LLWindowListener::keyUp(LLSD const & evt) +{ +	if(NULL == mKeyboard) +	{ +		// *HACK to handle the fact that LLWindow subclasses have to initialize +		// things in an inconvenient order +		mKeyboard = gKeyboard; +	} + +	KEY keycode = 0; +	if(evt.has("keycode")) +	{ +		keycode = KEY(evt["keycode"].asInteger()); +	} +	else +	{ +		keycode = KEY(evt["char"].asString()[0]); +	} + +	// *TODO - figure out how to handle the mask +	mKeyboard->handleTranslatedKeyUp(keycode, 0); +} + +void LLWindowListener::mouseDown(LLSD const & evt) +{ +	LLCoordGL pos(evt["x"].asInteger(), evt["y"].asInteger()); + +	std::string const & button = evt["button"].asString(); + +	if(button == "LEFT") +	{ +		// *TODO - figure out how to handle the mask +		mWindow->handleMouseDown(NULL, pos, 0); +	} +	else if (button == "RIGHT") +	{ +		// *TODO - figure out how to handle the mask +		mWindow->handleRightMouseDown(NULL, pos, 0); +	} +	else if (button == "MIDDLE") +	{ +		// *TODO - figure out how to handle the mask +		mWindow->handleMiddleMouseDown(NULL, pos, 0); +	} +	else +	{ +		llwarns << "ignoring unknown mous button \"" << button << '\"' << llendl; +	} +} + +void LLWindowListener::mouseUp(LLSD const & evt) +{ +	LLCoordGL pos(evt["x"].asInteger(), evt["y"].asInteger()); + +	std::string const & button = evt["button"].asString(); + +	if(button == "LEFT") +	{ +		// *TODO - figure out how to handle the mask +		mWindow->handleMouseUp(NULL, pos, 0); +	} +	else if (button == "RIGHT") +	{ +		// *TODO - figure out how to handle the mask +		mWindow->handleRightMouseUp(NULL, pos, 0); +	} +	else if (button == "MIDDLE") +	{ +		// *TODO - figure out how to handle the mask +		mWindow->handleMiddleMouseUp(NULL, pos, 0); +	} +	else +	{ +		llwarns << "ignoring unknown mous button \"" << button << '\"' << llendl; +	} +} + +void LLWindowListener::mouseMove(LLSD const & evt) +{ +	LLCoordGL pos(evt["x"].asInteger(), evt["y"].asInteger()); + +	// *TODO - figure out how to handle the mask +	mWindow->handleMouseMove(NULL, pos, 0); +} + +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..5b234c5d1d --- /dev/null +++ b/indra/llwindow/llwindowlistener.h @@ -0,0 +1,53 @@ +/**  + * @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" + +class LLKeyboard; +class LLWindowCallbacks; + +class LLWindowListener : public LLEventAPI +{ +public: +	LLWindowListener(LLWindowCallbacks * window, LLKeyboard * keyboard); + +	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; +	LLKeyboard * mKeyboard; +}; + + +#endif // LL_LLWINDOWLISTENER_H diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 66869288b6..d6a144f1dc 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -308,6 +308,7 @@ set(viewer_SOURCE_FILES      llnearbychat.cpp      llnearbychatbar.cpp      llnearbychathandler.cpp +    llnearbychatbarlistener.cpp      llnetmap.cpp      llnotificationalerthandler.cpp      llnotificationgrouphandler.cpp @@ -413,6 +414,7 @@ set(viewer_SOURCE_FILES      llsidepaneliteminfo.cpp      llsidepaneltaskinfo.cpp      llsidetray.cpp +    llsidetraylistener.cpp      llsidetraypanelcontainer.cpp      llsky.cpp      llslurl.cpp @@ -849,6 +851,7 @@ set(viewer_HEADER_FILES      llnearbychat.h      llnearbychatbar.h      llnearbychathandler.h +    llnearbychatbarlistener.h      llnetmap.h      llnotificationhandler.h      llnotificationmanager.h @@ -949,6 +952,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 f0e28d4ae3..cc316a2ca7 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2601,10 +2601,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> @@ -12190,6 +12190,17 @@  		<key>Value</key>  		<integer>1</integer>  	</map> +    <key>SLURLPassToOtherInstance</key> +    <map> +    <key>Comment</key> +    <string>Pass execution to prevoius viewer instances if there is a given slurl</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Boolean</string> +    <key>Value</key> +    <integer>1</integer> +    </map>      <key>soundsbeacon</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 7d908df5ce..fd5cee2772 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -198,6 +198,7 @@ LLAgent::LLAgent() :  	mAutoPilot(FALSE),  	mAutoPilotFlyOnStop(FALSE), +	mAutoPilotAllowFlying(TRUE),  	mAutoPilotTargetGlobal(),  	mAutoPilotStopDistance(1.f),  	mAutoPilotUseRotation(FALSE), @@ -1119,12 +1120,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. @@ -1213,17 +1208,24 @@ BOOL LLAgent::getBusy() const  //-----------------------------------------------------------------------------  // startAutoPilotGlobal()  //----------------------------------------------------------------------------- -void LLAgent::startAutoPilotGlobal(const LLVector3d &target_global, const std::string& behavior_name, const LLQuaternion *target_rotation, void (*finish_callback)(BOOL, void *),  void *callback_data, F32 stop_distance, F32 rot_threshold) +void LLAgent::startAutoPilotGlobal(const LLVector3d &target_global, const std::string& behavior_name, const LLQuaternion *target_rotation, void (*finish_callback)(BOOL, void *),  void *callback_data, F32 stop_distance, F32 rot_threshold, BOOL allow_flying)  {  	if (!isAgentAvatarValid())  	{  		return;  	} +	// Are there any pending callbacks from previous auto pilot requests? +	if (mAutoPilotFinishedCallback) +	{ +		mAutoPilotFinishedCallback(dist_vec(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal) < mAutoPilotStopDistance, mAutoPilotCallbackData); +	} +  	mAutoPilotFinishedCallback = finish_callback;  	mAutoPilotCallbackData = callback_data;  	mAutoPilotRotationThreshold = rot_threshold;  	mAutoPilotBehaviorName = behavior_name; +	mAutoPilotAllowFlying = allow_flying;  	LLVector3d delta_pos( target_global );  	delta_pos -= getPositionGlobal(); @@ -1251,14 +1253,23 @@ void LLAgent::startAutoPilotGlobal(const LLVector3d &target_global, const std::s  		}  	} -	mAutoPilotFlyOnStop = getFlying(); +	if (mAutoPilotAllowFlying) +	{ +		mAutoPilotFlyOnStop = getFlying(); +	} +	else +	{ +		mAutoPilotFlyOnStop = FALSE; +	} -	if (distance > 30.0) +	if (distance > 30.0 && mAutoPilotAllowFlying)  	{  		setFlying(TRUE);  	} -	if ( distance > 1.f && heightDelta > (sqrtf(mAutoPilotStopDistance) + 1.f)) +	if ( distance > 1.f &&  +		mAutoPilotAllowFlying && +		heightDelta > (sqrtf(mAutoPilotStopDistance) + 1.f))  	{  		setFlying(TRUE);  		// Do not force flying for "Sit" behavior to prevent flying after pressing "Stand" @@ -1268,22 +1279,8 @@ void LLAgent::startAutoPilotGlobal(const LLVector3d &target_global, const std::s  	}  	mAutoPilot = TRUE; -	mAutoPilotTargetGlobal = target_global; +	setAutoPilotTargetGlobal(target_global); -	// trace ray down to find height of destination from ground -	LLVector3d traceEndPt = target_global; -	traceEndPt.mdV[VZ] -= 20.f; - -	LLVector3d targetOnGround; -	LLVector3 groundNorm; -	LLViewerObject *obj; - -	LLWorld::getInstance()->resolveStepHeightGlobal(NULL, target_global, traceEndPt, targetOnGround, groundNorm, &obj); -	F64 target_height = llmax((F64)gAgentAvatarp->getPelvisToFoot(), target_global.mdV[VZ] - targetOnGround.mdV[VZ]); - -	// clamp z value of target to minimum height above ground -	mAutoPilotTargetGlobal.mdV[VZ] = targetOnGround.mdV[VZ] + target_height; -	mAutoPilotTargetDist = (F32)dist_vec(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal);  	if (target_rotation)  	{  		mAutoPilotUseRotation = TRUE; @@ -1301,12 +1298,36 @@ void LLAgent::startAutoPilotGlobal(const LLVector3d &target_global, const std::s  //----------------------------------------------------------------------------- -// startFollowPilot() +// setAutoPilotTargetGlobal  //----------------------------------------------------------------------------- -void LLAgent::startFollowPilot(const LLUUID &leader_id) +void LLAgent::setAutoPilotTargetGlobal(const LLVector3d &target_global)  { -	if (!mAutoPilot) return; +	if (mAutoPilot) +	{ +		mAutoPilotTargetGlobal = target_global; + +		// trace ray down to find height of destination from ground +		LLVector3d traceEndPt = target_global; +		traceEndPt.mdV[VZ] -= 20.f; + +		LLVector3d targetOnGround; +		LLVector3 groundNorm; +		LLViewerObject *obj; + +		LLWorld::getInstance()->resolveStepHeightGlobal(NULL, target_global, traceEndPt, targetOnGround, groundNorm, &obj); +		F64 target_height = llmax((F64)gAgentAvatarp->getPelvisToFoot(), target_global.mdV[VZ] - targetOnGround.mdV[VZ]); + +		// clamp z value of target to minimum height above ground +		mAutoPilotTargetGlobal.mdV[VZ] = targetOnGround.mdV[VZ] + target_height; +		mAutoPilotTargetDist = (F32)dist_vec(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal); +	} +} +//----------------------------------------------------------------------------- +// startFollowPilot() +//----------------------------------------------------------------------------- +void LLAgent::startFollowPilot(const LLUUID &leader_id, BOOL allow_flying, F32 stop_distance) +{  	mLeaderID = leader_id;  	if ( mLeaderID.isNull() ) return; @@ -1317,7 +1338,14 @@ void LLAgent::startFollowPilot(const LLUUID &leader_id)  		return;  	} -	startAutoPilotGlobal(object->getPositionGlobal()); +	startAutoPilotGlobal(object->getPositionGlobal(),  +						 std::string(),	// behavior_name +						 NULL,			// target_rotation +						 NULL,			// finish_callback +						 NULL,			// callback_data +						 stop_distance, +						 0.03f,			// rotation_threshold +						 allow_flying);  } @@ -1344,6 +1372,7 @@ void LLAgent::stopAutoPilot(BOOL user_cancel)  		if (mAutoPilotFinishedCallback)  		{  			mAutoPilotFinishedCallback(!user_cancel && dist_vec(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal) < mAutoPilotStopDistance, mAutoPilotCallbackData); +			mAutoPilotFinishedCallback = NULL;  		}  		mLeaderID = LLUUID::null; @@ -1383,7 +1412,7 @@ void LLAgent::autoPilot(F32 *delta_yaw)  		if (!isAgentAvatarValid()) return; -		if (gAgentAvatarp->mInAir) +		if (gAgentAvatarp->mInAir && mAutoPilotAllowFlying)  		{  			setFlying(TRUE);  		} @@ -1684,11 +1713,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/llagent.h b/indra/newview/llagent.h index 896408c0dd..33c05816e2 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -467,19 +467,29 @@ public:  public:  	BOOL			getAutoPilot() const				{ return mAutoPilot; }  	LLVector3d		getAutoPilotTargetGlobal() const 	{ return mAutoPilotTargetGlobal; } +	LLUUID			getAutoPilotLeaderID() const		{ return mLeaderID; } +	F32				getAutoPilotStopDistance() const	{ return mAutoPilotStopDistance; } +	F32				getAutoPilotTargetDist() const		{ return mAutoPilotTargetDist; } +	BOOL			getAutoPilotUseRotation() const		{ return mAutoPilotUseRotation; } +	LLVector3		getAutoPilotTargetFacing() const	{ return mAutoPilotTargetFacing; } +	F32				getAutoPilotRotationThreshold() const	{ return mAutoPilotRotationThreshold; } +	std::string		getAutoPilotBehaviorName() const	{ return mAutoPilotBehaviorName; } +  	void			startAutoPilotGlobal(const LLVector3d &pos_global,   										 const std::string& behavior_name = std::string(),   										 const LLQuaternion *target_rotation = NULL,   										 void (*finish_callback)(BOOL, void *) = NULL, void *callback_data = NULL,  -										 F32 stop_distance = 0.f, F32 rotation_threshold = 0.03f); -	void 			startFollowPilot(const LLUUID &leader_id); +										 F32 stop_distance = 0.f, F32 rotation_threshold = 0.03f, +										 BOOL allow_flying = TRUE); +	void 			startFollowPilot(const LLUUID &leader_id, BOOL allow_flying = TRUE, F32 stop_distance = 0.5f);  	void			stopAutoPilot(BOOL user_cancel = FALSE); -	void 			setAutoPilotGlobal(const LLVector3d &pos_global); +	void 			setAutoPilotTargetGlobal(const LLVector3d &target_global);  	void			autoPilot(F32 *delta_yaw); 			// Autopilot walking action, angles in radians  	void			renderAutoPilotTarget();  private:  	BOOL			mAutoPilot;  	BOOL			mAutoPilotFlyOnStop; +	BOOL			mAutoPilotAllowFlying;  	LLVector3d		mAutoPilotTargetGlobal;  	F32				mAutoPilotStopDistance;  	BOOL			mAutoPilotUseRotation; 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..0d30b95074 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -31,76 +31,342 @@  #include "llagentlistener.h"  #include "llagent.h" +#include "llvoavatar.h"  #include "llcommandhandler.h"  #include "llslurl.h"  #include "llurldispatcher.h"  #include "llviewerobject.h"  #include "llviewerobjectlist.h"  #include "llviewerregion.h" +#include "llsdutil.h" +#include "llsdutil_math.h"  LLAgentListener::LLAgentListener(LLAgent &agent)    : LLEventAPI("LLAgent",                 "LLAgent listener to (e.g.) teleport, sit, stand, etc."),      mAgent(agent)  { -	add("requestTeleport", +    add("requestTeleport",          "Teleport: [\"regionname\"], [\"x\"], [\"y\"], [\"z\"]\n"          "If [\"skip_confirmation\"] is true, use LLURLDispatcher rather than LLCommandDispatcher.",          &LLAgentListener::requestTeleport); -	add("requestSit", +    add("requestSit",          "Ask to sit on the object specified in [\"obj_uuid\"]",          &LLAgentListener::requestSit); -	add("requestStand", +    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", +        "Obsolete - use getPosition instead\n" +        "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())); +    add("getPosition", +        "Send information about the agent's position and orientation on [\"reply\"]:\n" +        "[\"region\"]: array of region {x, y, z} position\n" +        "[\"global\"]: array of global {x, y, z} position\n" +        "[\"euler\"]: map of {roll, pitch, yaw}\n" +        "[\"quat\"]:  array of [x, y, z, w] quaternion values", +        &LLAgentListener::getPosition, +        LLSDMap("reply", LLSD())); +    add("startAutoPilot", +        "Start the autopilot system using the following parameters:\n" +        "[\"target_global\"]: array of target global {x, y, z} position\n" +        "[\"stop_distance\"]: target maxiumum distance from target [default: autopilot guess]\n" +        "[\"target_rotation\"]: array of [x, y, z, w] quaternion values [default: no target]\n" +        "[\"rotation_threshold\"]: target maximum angle from target facing rotation [default: 0.03 radians]\n" +        "[\"behavior_name\"]: name of the autopilot behavior [default: \"\"]" +        "[\"allow_flying\"]: allow flying during autopilot [default: True]", +        //"[\"callback_pump\"]: pump to send success/failure and callback data to [default: none]\n" +        //"[\"callback_data\"]: data to send back during a callback [default: none]", +        &LLAgentListener::startAutoPilot); +    add("getAutoPilot", +        "Send information about current state of the autopilot system to [\"reply\"]:\n" +        "[\"enabled\"]: boolean indicating whether or not autopilot is enabled\n" +        "[\"target_global\"]: array of target global {x, y, z} position\n" +        "[\"leader_id\"]: uuid of target autopilot is following\n" +        "[\"stop_distance\"]: target maximum distance from target\n" +        "[\"target_distance\"]: last known distance from target\n" +        "[\"use_rotation\"]: boolean indicating if autopilot has a target facing rotation\n" +        "[\"target_facing\"]: array of {x, y} target direction to face\n" +        "[\"rotation_threshold\"]: target maximum angle from target facing rotation\n" +        "[\"behavior_name\"]: name of the autopilot behavior", +        &LLAgentListener::getAutoPilot, +        LLSDMap("reply", LLSD())); +    add("startFollowPilot", +		"[\"leader_id\"]: uuid of target to follow using the autopilot system (optional with avatar_name)\n" +		"[\"avatar_name\"]: avatar name to follow using the autopilot system (optional with leader_id)\n" +        "[\"allow_flying\"]: allow flying during autopilot [default: True]\n" +        "[\"stop_distance\"]: target maxiumum distance from target [default: autopilot guess]", +        &LLAgentListener::startFollowPilot); +    add("setAutoPilotTarget", +        "Update target for currently running autopilot:\n" +        "[\"target_global\"]: array of target global {x, y, z} position", +        &LLAgentListener::setAutoPilotTarget); +    add("stopAutoPilot", +        "Stop the autopilot system:\n" +        "[\"user_cancel\"] indicates whether or not to act as though user canceled autopilot [default: false]", +        &LLAgentListener::stopAutoPilot); +  }  void LLAgentListener::requestTeleport(LLSD const & event_data) const  { -	if(event_data["skip_confirmation"].asBoolean()) +    if(event_data["skip_confirmation"].asBoolean()) +    { +        LLSD params(LLSD::emptyArray()); +        params.append(event_data["regionname"]); +        params.append(event_data["x"]); +        params.append(event_data["y"]); +        params.append(event_data["z"]); +        LLCommandDispatcher::dispatch("teleport", params, LLSD(), NULL, true); +        // *TODO - lookup other LLCommandHandlers for "agent", "classified", "event", "group", "floater", "parcel", "login", login_refresh", "balance", "chat" +        // should we just compose LLCommandHandler and LLDispatchListener? +    } +    else +    { +        std::string url = LLSLURL(event_data["regionname"],  +                                  LLVector3(event_data["x"].asReal(),  +                                            event_data["y"].asReal(),  +                                            event_data["z"].asReal())).getSLURLString(); +        LLURLDispatcher::dispatch(url, NULL, false); +    } +} + +void LLAgentListener::requestSit(LLSD const & event_data) const +{ +    //mAgent.getAvatarObject()->sitOnObject(); +    // shamelessly ripped from llviewermenu.cpp:handle_sit_or_stand() +    // *TODO - find a permanent place to share this code properly. +    LLViewerObject *object = gObjectList.findObject(event_data["obj_uuid"]); + +    if (object && object->getPCode() == LL_PCODE_VOLUME) +    { +        gMessageSystem->newMessageFast(_PREHASH_AgentRequestSit); +        gMessageSystem->nextBlockFast(_PREHASH_AgentData); +        gMessageSystem->addUUIDFast(_PREHASH_AgentID, mAgent.getID()); +        gMessageSystem->addUUIDFast(_PREHASH_SessionID, mAgent.getSessionID()); +        gMessageSystem->nextBlockFast(_PREHASH_TargetObject); +        gMessageSystem->addUUIDFast(_PREHASH_TargetID, object->mID); +        gMessageSystem->addVector3Fast(_PREHASH_Offset, LLVector3(0,0,0)); + +        object->getRegion()->sendReliableMessage(); +    } +	else  	{ -		LLSD params(LLSD::emptyArray()); -		params.append(event_data["regionname"]); -		params.append(event_data["x"]); -		params.append(event_data["y"]); -		params.append(event_data["z"]); -		LLCommandDispatcher::dispatch("teleport", params, LLSD(), NULL, true); -		// *TODO - lookup other LLCommandHandlers for "agent", "classified", "event", "group", "floater", "parcel", "login", login_refresh", "balance", "chat" -		// should we just compose LLCommandHandler and LLDispatchListener? +		llwarns << "LLAgent requestSit could not find the sit target "  +			<< event_data["obj_uuid"].asUUID() << llendl;  	} -	else +} + +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... +	LLSD reply = LLSD::emptyMap(); +	reply["quat"] = llsd_copy_array(boost::begin(quat.mQ), boost::end(quat.mQ)); +	reply["euler"] = LLSD::emptyMap(); +	reply["euler"]["roll"] = roll; +	reply["euler"]["pitch"] = pitch; +	reply["euler"]["yaw"] = yaw; +    sendReply(reply, event); +} + + +void LLAgentListener::getPosition(const LLSD& event) const +{ +    F32 roll, pitch, yaw; +    LLQuaternion quat(mAgent.getQuat()); +    quat.getEulerAngles(&roll, &pitch, &yaw); + +	LLSD reply = LLSD::emptyMap(); +	reply["quat"] = llsd_copy_array(boost::begin(quat.mQ), boost::end(quat.mQ)); +	reply["euler"] = LLSD::emptyMap(); +	reply["euler"]["roll"] = roll; +	reply["euler"]["pitch"] = pitch; +	reply["euler"]["yaw"] = yaw; +    reply["region"] = ll_sd_from_vector3(mAgent.getPositionAgent()); +    reply["global"] = ll_sd_from_vector3d(mAgent.getPositionGlobal()); + +	sendReply(reply, event); +} + + +void LLAgentListener::startAutoPilot(LLSD const & event) +{ +    LLQuaternion target_rotation_value; +    LLQuaternion* target_rotation = NULL; +    if (event.has("target_rotation")) +    { +        target_rotation_value = ll_quaternion_from_sd(event["target_rotation"]); +        target_rotation = &target_rotation_value; +    } +    // *TODO: Use callback_pump and callback_data +    F32 rotation_threshold = 0.03f; +    if (event.has("rotation_threshold")) +    { +        rotation_threshold = event["rotation_threshold"].asReal(); +    } +	 +	BOOL allow_flying = TRUE; +	if (event.has("allow_flying")) +	{ +		allow_flying = (BOOL) event["allow_flying"].asBoolean(); +		if (!allow_flying) +		{ +			mAgent.setFlying(FALSE); +		} +	} + +	F32 stop_distance = 0.f; +	if (event.has("stop_distance"))  	{ -		std::string url = LLSLURL(event_data["regionname"],  -								  LLVector3(event_data["x"].asReal(),  -											event_data["y"].asReal(),  -											event_data["z"].asReal())).getSLURLString(); -		LLURLDispatcher::dispatch(url, NULL, false); +		stop_distance = event["stop_distance"].asReal();  	} + +	// Clear follow target, this is doing a path +	mFollowTarget.setNull(); + +    mAgent.startAutoPilotGlobal(ll_vector3d_from_sd(event["target_global"]), +                                event["behavior_name"], +                                target_rotation, +                                NULL, NULL, +                                stop_distance, +                                rotation_threshold, +								allow_flying);  } -void LLAgentListener::requestSit(LLSD const & event_data) const +void LLAgentListener::getAutoPilot(const LLSD& event) const +{ +	LLSD reply = LLSD::emptyMap(); +	 +	LLSD::Boolean enabled = mAgent.getAutoPilot(); +	reply["enabled"] = enabled; +	 +	reply["target_global"] = ll_sd_from_vector3d(mAgent.getAutoPilotTargetGlobal()); +	 +	reply["leader_id"] = mAgent.getAutoPilotLeaderID(); +	 +	reply["stop_distance"] = mAgent.getAutoPilotStopDistance(); + +	reply["target_distance"] = mAgent.getAutoPilotTargetDist(); +	if (!enabled && +		mFollowTarget.notNull()) +	{	// Get an actual distance from the target object we were following +		LLViewerObject * target = gObjectList.findObject(mFollowTarget); +		if (target) +		{	// Found the target AV, return the actual distance to them as well as their ID +			LLVector3 difference = target->getPositionRegion() - mAgent.getPositionAgent(); +			reply["target_distance"] = difference.length(); +			reply["leader_id"] = mFollowTarget; +		} +	} + +	reply["use_rotation"] = (LLSD::Boolean) mAgent.getAutoPilotUseRotation(); +	reply["target_facing"] = ll_sd_from_vector3(mAgent.getAutoPilotTargetFacing()); +	reply["rotation_threshold"] = mAgent.getAutoPilotRotationThreshold(); +	reply["behavior_name"] = mAgent.getAutoPilotBehaviorName(); +	reply["fly"] = (LLSD::Boolean) mAgent.getFlying(); + +	sendReply(reply, event); +} + +void LLAgentListener::startFollowPilot(LLSD const & event)  { -	//mAgent.getAvatarObject()->sitOnObject(); -	// shamelessly ripped from llviewermenu.cpp:handle_sit_or_stand() -	// *TODO - find a permanent place to share this code properly. -	LLViewerObject *object = gObjectList.findObject(event_data["obj_uuid"]); +	LLUUID target_id; + +	BOOL allow_flying = TRUE; +	if (event.has("allow_flying")) +	{ +		allow_flying = (BOOL) event["allow_flying"].asBoolean(); +	} -	if (object && object->getPCode() == LL_PCODE_VOLUME) +	if (event.has("leader_id"))  	{ -		gMessageSystem->newMessageFast(_PREHASH_AgentRequestSit); -		gMessageSystem->nextBlockFast(_PREHASH_AgentData); -		gMessageSystem->addUUIDFast(_PREHASH_AgentID, mAgent.getID()); -		gMessageSystem->addUUIDFast(_PREHASH_SessionID, mAgent.getSessionID()); -		gMessageSystem->nextBlockFast(_PREHASH_TargetObject); -		gMessageSystem->addUUIDFast(_PREHASH_TargetID, object->mID); -		gMessageSystem->addVector3Fast(_PREHASH_Offset, LLVector3(0,0,0)); - -		object->getRegion()->sendReliableMessage(); +		target_id = event["leader_id"]; +	} +	else if (event.has("avatar_name")) +	{	// Find the avatar with matching name +		std::string target_name = event["avatar_name"].asString(); + +		if (target_name.length() > 0) +		{ +			S32 num_objects = gObjectList.getNumObjects(); +			S32 cur_index = 0; +			while (cur_index < num_objects) +			{ +				LLViewerObject * cur_object = gObjectList.getObject(cur_index++); +				if (cur_object && +					cur_object->asAvatar() && +					cur_object->asAvatar()->getFullname() == target_name) +				{	// Found avatar with matching name, extract id and break out of loop +					target_id = cur_object->getID(); +					break; +				} +			} +		} +	} + +	F32 stop_distance = 0.f; +	if (event.has("stop_distance")) +	{ +		stop_distance = event["stop_distance"].asReal(); +	} + +	if (target_id.notNull()) +	{ +		if (!allow_flying) +		{ +			mAgent.setFlying(FALSE); +		} +		mFollowTarget = target_id;	// Save follow target so we can report distance later + +	    mAgent.startFollowPilot(target_id, allow_flying, stop_distance);  	}  } -void LLAgentListener::requestStand(LLSD const & event_data) const +void LLAgentListener::setAutoPilotTarget(LLSD const & event) const  { -	mAgent.setControlFlags(AGENT_CONTROL_STAND_UP); +	if (event.has("target_global")) +	{ +		LLVector3d target_global(ll_vector3d_from_sd(event["target_global"])); +		mAgent.setAutoPilotTargetGlobal(target_global); +	} +} + +void LLAgentListener::stopAutoPilot(LLSD const & event) const +{ +	BOOL user_cancel = FALSE; +	if (event.has("user_cancel")) +	{ +		user_cancel = event["user_cancel"].asBoolean(); +	} +    mAgent.stopAutoPilot(user_cancel);  } diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h index 9b585152f4..aadb87db12 100644 --- a/indra/newview/llagentlistener.h +++ b/indra/newview/llagentlistener.h @@ -38,15 +38,24 @@ class LLSD;  class LLAgentListener : public LLEventAPI  {  public: -	LLAgentListener(LLAgent &agent); +    LLAgentListener(LLAgent &agent);  private: -	void requestTeleport(LLSD const & event_data) const; -	void requestSit(LLSD const & event_data) const; -	void requestStand(LLSD const & event_data) const; +    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; +    void getPosition(const LLSD& event) const; +    void startAutoPilot(const LLSD& event); +    void getAutoPilot(const LLSD& event) const; +    void startFollowPilot(const LLSD& event); +    void setAutoPilotTarget(const LLSD& event) const; +    void stopAutoPilot(const LLSD& event) const;  private: -	LLAgent & mAgent; +    LLAgent &	mAgent; +	LLUUID		mFollowTarget;  };  #endif // LL_LLAGENTLISTENER_H diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f6fe7ecd01..b670a40cd0 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -924,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. @@ -1162,11 +1162,11 @@ bool LLAppViewer::mainLoop()  				// Scan keyboard for movement keys.  Command keys and typing  				// are handled by windows callbacks.  Don't do this until we're  				// done initializing.  JC -				if (gViewerWindow->mWindow->getVisible()  +				if ((gHeadlessClient || gViewerWindow->mWindow->getVisible())  					&& gViewerWindow->getActive()  					&& !gViewerWindow->mWindow->getMinimized()  					&& LLStartUp::getStartupState() == STATE_STARTED -					&& !gViewerWindow->getShowProgress() +					&& (gHeadlessClient || !gViewerWindow->getShowProgress())  					&& !gFocusMgr.focusLocked())  				{  					LLMemType mjk(LLMemType::MTYPE_JOY_KEY); @@ -1214,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; @@ -1242,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. @@ -2458,7 +2458,8 @@ bool LLAppViewer::initConfiguration()  	// it relies on checking a marker file which will not work when running  	// out of different directories -	if (LLStartUp::getStartSLURL().isValid()) +	if (LLStartUp::getStartSLURL().isValid() && +		!(gSavedSettings.getBOOL("SLURLPassToOtherInstance")))  	{  		if (sendURLToOtherInstance(LLStartUp::getStartSLURL().getSLURLString()))  		{ @@ -2736,7 +2737,7 @@ 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"); @@ -2772,28 +2773,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) @@ -2835,12 +2833,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 @@ -3821,7 +3816,7 @@ void LLAppViewer::badNetworkHandler()  // is destroyed.  void LLAppViewer::saveFinalSnapshot()  { -	if (!mSavedFinalSnapshot && !gNoRender) +	if (!mSavedFinalSnapshot)  	{  		gSavedSettings.setVector3d("FocusPosOnLogout", gAgentCamera.calcFocusPositionTargetGlobal());  		gSavedSettings.setVector3d("CameraPosOnLogout", gAgentCamera.calcCameraPositionTargetGlobal()); @@ -4225,34 +4220,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);  	}  	////////////////////////////////////// @@ -4261,13 +4253,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())  	{ @@ -4633,12 +4622,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/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index d328567a0e..b9d546de59 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -301,23 +301,44 @@ void create_console()  	// redirect unbuffered STDOUT to the console  	l_std_handle = (long)GetStdHandle(STD_OUTPUT_HANDLE);  	h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT); -	fp = _fdopen( h_con_handle, "w" ); -	*stdout = *fp; -	setvbuf( stdout, NULL, _IONBF, 0 ); +	if (h_con_handle == -1) +	{ +		llwarns << "create_console() failed to open stdout handle" << llendl; +	} +	else +	{ +		fp = _fdopen( h_con_handle, "w" ); +		*stdout = *fp; +		setvbuf( stdout, NULL, _IONBF, 0 ); +	}  	// redirect unbuffered STDIN to the console  	l_std_handle = (long)GetStdHandle(STD_INPUT_HANDLE);  	h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT); -	fp = _fdopen( h_con_handle, "r" ); -	*stdin = *fp; -	setvbuf( stdin, NULL, _IONBF, 0 ); +	if (h_con_handle == -1) +	{ +		llwarns << "create_console() failed to open stdin handle" << llendl; +	} +	else +	{ +		fp = _fdopen( h_con_handle, "r" ); +		*stdin = *fp; +		setvbuf( stdin, NULL, _IONBF, 0 ); +	}  	// redirect unbuffered STDERR to the console  	l_std_handle = (long)GetStdHandle(STD_ERROR_HANDLE);  	h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT); -	fp = _fdopen( h_con_handle, "w" ); -	*stderr = *fp; -	setvbuf( stderr, NULL, _IONBF, 0 ); +	if (h_con_handle == -1) +	{ +		llwarns << "create_console() failed to open stderr handle" << llendl; +	} +	else +	{ +		fp = _fdopen( h_con_handle, "w" ); +		*stderr = *fp; +		setvbuf( stderr, NULL, _IONBF, 0 ); +	}  }  LLAppViewerWin32::LLAppViewerWin32(const char* cmd_line) : diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index 1fb83fe567..97d7757339 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -46,6 +46,7 @@  #include "llhints.h"  #include "llimfloater.h" // for LLIMFloater  #include "llnearbychatbar.h" +#include "llnearbychatbarlistener.h"  #include "llsidetray.h"  #include "llspeakbutton.h"  #include "llsplitbutton.h" @@ -550,6 +551,8 @@ BOOL LLBottomTray::postBuild()  	mNearbyChatBar = findChild<LLNearbyChatBar>("chat_bar");  	LLHints::registerHintTarget("chat_bar", mNearbyChatBar->LLView::getHandle()); +	mListener.reset(new LLNearbyChatBarListener(*mNearbyChatBar)); +  	mChatBarContainer = getChild<LLLayoutPanel>("chat_bar_layout_panel");  	mNearbyCharResizeHandlePanel = getChild<LLPanel>("chat_bar_resize_handle_panel"); diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index 04e5f5e9e0..83a33845bf 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -39,6 +39,7 @@ class LLIMChiclet;  class LLBottomTrayLite;  class LLLayoutPanel;  class LLMenuGL; +class LLNearbyChatBarListener;  // Build time optimization, generate once in .cpp file  #ifndef LLBOTTOMTRAY_CPP @@ -508,6 +509,9 @@ protected:  	 * Image used to show position where dragged button will be dropped.  	 */  	LLUIImage* mImageDragIndication; + +	// We want only one LLNearbyChatBarListener object, so it's tied to this singleton +	boost::shared_ptr<LLNearbyChatBarListener> mListener;  };  #endif // LL_LLBOTTOMPANEL_H diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index 6e58be8174..4a58dc558a 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -671,6 +671,9 @@ void LLChatBar::onCommitGesture(LLUICtrl* ctrl)  	}  } + +/* Cruft - global gChatHandler declared below has been commented out, +   so this class is never used.  See similar code in llnearbychatbar.cpp  class LLChatHandler : public LLCommandHandler  {  public: @@ -710,3 +713,4 @@ public:  // Creating the object registers with the dispatcher.  //LLChatHandler gChatHandler; +cruft */ 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/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/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/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/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp index 162e465fef..947efdbb19 100644 --- a/indra/newview/llnearbychatbar.cpp +++ b/indra/newview/llnearbychatbar.cpp @@ -875,11 +875,11 @@ void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32  	LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT);  } -class LLChatHandler : public LLCommandHandler +class LLChatCommandHandler : public LLCommandHandler  {  public:  	// not allowed from outside the app -	LLChatHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { } +	LLChatCommandHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { }      // Your code here  	bool handle(const LLSD& tokens, const LLSD& query_map, @@ -913,6 +913,6 @@ public:  };  // Creating the object registers with the dispatcher. -LLChatHandler gChatHandler; +LLChatCommandHandler gChatHandler; diff --git a/indra/newview/llnearbychatbarlistener.cpp b/indra/newview/llnearbychatbarlistener.cpp new file mode 100644 index 0000000000..99286d972b --- /dev/null +++ b/indra/newview/llnearbychatbarlistener.cpp @@ -0,0 +1,100 @@ +/**
 + * @file   llnearbychatbarlistener.cpp
 + * @author Dave Simmons
 + * @date   2011-03-15
 + * @brief  Implementation for LLNearbyChatBarListener.
 + *
 + * $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$
 + */
 +
 +#include "llviewerprecompiledheaders.h"
 +
 +#include "llnearbychatbarlistener.h"
 +#include "llnearbychatbar.h"
 +
 +#include "llagent.h"
 +#include "llchat.h"
 +
 +
 +
 +LLNearbyChatBarListener::LLNearbyChatBarListener(LLNearbyChatBar & chatbar)
 +  : LLEventAPI("LLChatBar",
 +               "LLChatBar listener to (e.g.) sendChat, etc."),
 +	mChatbar(chatbar)
 +{
 +    add("sendChat",
 +        "Send chat to the simulator:\n"
 +        "[\"message\"] chat message text [required]\n"
 +        "[\"channel\"] chat channel number [default = 0]\n"
 +		"[\"type\"] chat type \"whisper\", \"normal\", \"shout\" [default = \"normal\"]",
 +        &LLNearbyChatBarListener::sendChat);
 +}
 +
 +
 +// "sendChat" command
 +void LLNearbyChatBarListener::sendChat(LLSD const & chat_data) const
 +{
 +	// Extract the data
 +	std::string chat_text = chat_data["message"].asString();
 +
 +	S32 channel = 0;
 +	if (chat_data.has("channel"))
 +	{
 +		channel = chat_data["channel"].asInteger();
 +		if (channel < 0 || channel >= 2147483647)
 +		{	// Use 0 up to (but not including) DEBUG_CHANNEL (wtf isn't that defined??)
 +			channel = 0;
 +		}
 +	}
 +
 +	EChatType type_o_chat = CHAT_TYPE_NORMAL;
 +	if (chat_data.has("type"))
 +	{
 +		std::string type_string = chat_data["type"].asString();
 +		if (type_string == "whisper")
 +		{
 +			type_o_chat = CHAT_TYPE_WHISPER;
 +		}
 +		else if (type_string == "shout")
 +		{
 +			type_o_chat = CHAT_TYPE_SHOUT;
 +		}
 +	}
 +
 +	// Have to prepend /42 style channel numbers
 +	std::string chat_to_send;
 +	if (channel == 0)
 +	{
 +		chat_to_send = chat_text;
 +	}
 +	else
 +	{
 +		chat_to_send += "/";
 +		chat_to_send += chat_data["channel"].asString();
 +		chat_to_send += " ";
 +		chat_to_send += chat_text;
 +	}
 +
 +	// Send it as if it was typed in
 +	mChatbar.sendChatFromViewer(chat_to_send, type_o_chat, (BOOL)(channel == 0));
 +}
 +
 diff --git a/indra/newview/llnearbychatbarlistener.h b/indra/newview/llnearbychatbarlistener.h new file mode 100644 index 0000000000..13e4e5990c --- /dev/null +++ b/indra/newview/llnearbychatbarlistener.h @@ -0,0 +1,50 @@ +/**
 + * @file   llnearbychatbarlistener.h
 + * @author Dave Simmons
 + * @date   2011-03-15
 + * @brief  Class definition for LLNearbyChatBarListener.
 + *
 + * $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_LLNEARBYCHATBARLISTENER_H
 +#define LL_LLNEARBYCHATBARLISTENER_H
 +
 +#include "lleventapi.h"
 +
 +class LLSD;
 +class LLNearbyChatBar;
 +
 +class LLNearbyChatBarListener : public LLEventAPI
 +{
 +public:
 +	LLNearbyChatBarListener(LLNearbyChatBar & chatbar);
 +
 +private:
 +    void sendChat(LLSD const & chat_data) const;
 +
 +	LLNearbyChatBar & mChatbar;
 +};
 +
 +#endif // LL_LLNEARBYCHATBARLISTENER_H
 +
 diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp index de5439e4e0..7e0f066b1d 100644 --- a/indra/newview/llnearbychathandler.cpp +++ b/indra/newview/llnearbychathandler.cpp @@ -441,6 +441,8 @@ void LLNearbyChatScreenChannel::reshape			(S32 width, S32 height, BOOL called_fr  //-----------------------------------------------------------------------------------------------  //LLNearbyChatHandler  //----------------------------------------------------------------------------------------------- +boost::scoped_ptr<LLEventPump> LLNearbyChatHandler::sChatWatcher(new LLEventStream("LLChat")); +  LLNearbyChatHandler::LLNearbyChatHandler(e_notification_type type, const LLSD& id)  {  	mType = type; @@ -525,6 +527,19 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, const LLSD &args)   		LLFirstUse::otherAvatarChatFirst();  	} +	// Build data and send event on to LLEventStream +	LLSD notification; +	notification["message"] = chat_msg.mText; +	notification["from"] = chat_msg.mFromName; +	notification["from_id"] = chat_msg.mFromID; +	notification["time"] = chat_msg.mTime; +	notification["source"] = (S32)chat_msg.mSourceType; +	notification["chat_type"] = (S32)chat_msg.mChatType; +	notification["chat_style"] = (S32)chat_msg.mChatStyle; +	 +	sChatWatcher->post(notification); + +  	if( nearby_chat->getVisible()  		|| ( chat_msg.mSourceType == CHAT_SOURCE_AGENT  			&& gSavedSettings.getBOOL("UseChatBubbles") ) @@ -558,24 +573,13 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, const LLSD &args)  	}  	*/ -	LLUUID id; -	id.generate(); -  	LLNearbyChatScreenChannel* channel = dynamic_cast<LLNearbyChatScreenChannel*>(mChannel); -	  	if(channel)  	{ -		LLSD notification; +		LLUUID id; +		id.generate();  		notification["id"] = id; -		notification["message"] = chat_msg.mText; -		notification["from"] = chat_msg.mFromName; -		notification["from_id"] = chat_msg.mFromID; -		notification["time"] = chat_msg.mTime; -		notification["source"] = (S32)chat_msg.mSourceType; -		notification["chat_type"] = (S32)chat_msg.mChatType; -		notification["chat_style"] = (S32)chat_msg.mChatStyle; -		  		std::string r_color_name = "White";  		F32 r_color_alpha = 1.0f;   		LLViewerChat::getChatColor( chat_msg, r_color_name, r_color_alpha); @@ -585,7 +589,6 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, const LLSD &args)  		notification["font_size"] = (S32)LLViewerChat::getChatFontSize() ;  		channel->addNotification(notification);	  	} -  }  void LLNearbyChatHandler::onDeleteToast(LLToast* toast) diff --git a/indra/newview/llnearbychathandler.h b/indra/newview/llnearbychathandler.h index ec1f29cdfc..b0e4f62d51 100644 --- a/indra/newview/llnearbychathandler.h +++ b/indra/newview/llnearbychathandler.h @@ -29,6 +29,8 @@  #include "llnotificationhandler.h" +class LLEventPump; +  //add LLNearbyChatHandler to LLNotificationsUI namespace  namespace LLNotificationsUI{ @@ -44,6 +46,8 @@ public:  protected:  	virtual void onDeleteToast(LLToast* toast);  	virtual void initChannel(); + +	static boost::scoped_ptr<LLEventPump> sChatWatcher;  };  } diff --git a/indra/newview/llnotificationmanager.h b/indra/newview/llnotificationmanager.h index 72fa394621..16e82e4cce 100644 --- a/indra/newview/llnotificationmanager.h +++ b/indra/newview/llnotificationmanager.h @@ -69,7 +69,7 @@ public:  private:  	//TODO (*)  	std::map<std::string, boost::shared_ptr<LLEventHandler> > mNotifyHandlers; -	std::map<std::string, LLChatHandler*> mChatHandlers; +	// cruft std::map<std::string, LLChatHandler*> mChatHandlers;  };  } 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 a9bb01ac70..b819b9b18d 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..0e38d4844a 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; @@ -731,9 +729,9 @@ 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); @@ -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 @@ -1987,7 +1961,6 @@ bool idle_startup()  		// Start automatic replay if the flag is set.  		if (gSavedSettings.getBOOL("StatsAutoRun") || LLAgentPilot::sReplaySession)  		{ -			LLUUID id;  			LL_DEBUGS("AppInit") << "Starting automatic playback" << LL_ENDL;  			gAgentPilot.startPlayback();  		} @@ -3214,7 +3187,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/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/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/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 41b7c13826..de8e37b572 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; 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/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..8c21e1a409 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; @@ -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..cc635f71f9 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1415,61 +1415,59 @@ 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)  	{ 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 aa83bcb68b..e70f88ccb8 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1241,8 +1241,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;  @@ -1272,7 +1273,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;  	} @@ -1420,9 +1421,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 @@ -1885,11 +1886,8 @@ void LLViewerWindow::shutdownGL()  	LLVertexBuffer::cleanupClass();  	llinfos << "Stopping GL during shutdown" << llendl; -	if (!gNoRender) -	{ -		stopGL(FALSE); -		stop_glerror(); -	} +	stopGL(FALSE); +	stop_glerror();  	gGL.shutdown();  } @@ -1953,11 +1951,6 @@ void LLViewerWindow::reshape(S32 width, S32 height)  	// may have been destructed.  	if (!LLApp::isExiting())  	{ -		if (gNoRender) -		{ -			return; -		} -  		gWindowResized = TRUE;  		// update our window rectangle @@ -2594,12 +2587,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))  	{ @@ -2990,7 +2979,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  		{ @@ -3116,7 +3106,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); @@ -3429,11 +3420,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)  	{ @@ -3469,11 +3455,6 @@ void LLViewerWindow::schedulePick(LLPickInfo& pick_info)  void LLViewerWindow::performPick()  { -	if (gNoRender) -	{ -		return; -	} -  	if (!mPicks.empty())  	{  		std::vector<LLPickInfo>::iterator pick_it; @@ -3505,11 +3486,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)  	{ @@ -3519,7 +3495,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; @@ -4775,12 +4752,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 2e376e8568..38d2437405 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1295,18 +1295,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); @@ -1747,12 +1737,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; @@ -2223,7 +2207,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); @@ -2280,11 +2264,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); @@ -3257,17 +3236,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) @@ -4194,7 +4162,7 @@ void LLVOAvatar::updateTextures()  {  	BOOL render_avatar = TRUE; -	if (mIsDummy || gNoRender) +	if (mIsDummy)  	{  		return;  	} @@ -4468,11 +4436,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); @@ -4867,7 +4830,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; @@ -5439,11 +5402,6 @@ BOOL LLVOAvatar::loadLayersets()  //-----------------------------------------------------------------------------  void LLVOAvatar::updateVisualParams()  { -	if (gNoRender) -	{ -		return; -	} -  	setSex( (getVisualParamWeight( "male" ) > 0.5f) ? SEX_MALE : SEX_FEMALE );  	LLCharacter::updateVisualParams(); @@ -6174,8 +6132,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++)  	{ @@ -6831,11 +6787,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 5f9e343907..c74b60f7e7 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -2506,10 +2506,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/llworld.cpp b/indra/newview/llworld.cpp index 8f7197c607..c6f9b6f6e4 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  		{ diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 65fdc12f0a..6ab3f0fb6e 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -1227,10 +1227,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/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 6ecaef1bf3..a5352540e3 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -5243,6 +5243,20 @@ Insufficient permissions to rez object.    <notification     icon="notifytip.tga" +   name="IMAcrossParentEstates" +   type="notifytip"> +Unable to send IM across parent estates. +  </notification> + +  <notification +   icon="notifytip.tga" +   name="TransferInventoryAcrossParentEstates" +   type="notifytip"> +Unable to transfer inventory across parent estates. +  </notification> + +  <notification +   icon="notifytip.tga"     name="UnableToLoadNotecard"     type="notifytip">  Unable to load notecard.  | 
