Merge branch 'master' of git://github.com/siana/SingularityViewer.git into V2MultiWear

Conflicts:
	indra/newview/llfloateravatarlist.cpp
	indra/newview/llfloateropenobject.cpp
	indra/newview/llinventorybridge.cpp
	indra/newview/llviewerinventory.cpp
This commit is contained in:
Shyotl
2012-02-24 13:21:59 -06:00
177 changed files with 9551 additions and 2012 deletions

3
.gitignore vendored
View File

@@ -1,4 +1,5 @@
/installed.xml
/indra/llcommon/llversionviewer.h
/indra/build-*
/indra/tools/vstool/obj/
*.aps
@@ -11,11 +12,13 @@
/indra/viewer-*
/indra/newview/vivox-runtime/
/libraries/
/lib/
*.pyc
*.orig
*.rej
*.bak
*~
*.DS_Store
/LICENSES/
/edited-files.txt
qtcreator-build/

View File

@@ -26,7 +26,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
include(Variables)
# Load versions now. Install locations need them.
include(Versions)
include(BuildVersion)
include(UnixInstall)

View File

@@ -28,6 +28,9 @@ set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release;ReleaseSSE2;Debug" CACHE S
# Platform-specific compilation flags.
if (WINDOWS)
# Remove default /Zm1000 flag that cmake inserts
string (REPLACE "/Zm1000" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
# Don't build DLLs.
set(BUILD_SHARED_LIBS OFF)
@@ -67,6 +70,10 @@ if (WINDOWS)
/Oy-
)
# configure win32 API for windows XP+ compatibility
set(WINVER "0x0501" CACHE STRING "Win32 API Target version (see http://msdn.microsoft.com/en-us/library/aa383745%28v=VS.85%29.aspx)")
add_definitions("/DWINVER=${WINVER}" "/D_WIN32_WINNT=${WINVER}")
if(MSVC80 OR MSVC90 OR MSVC10)
set(CMAKE_CXX_FLAGS_RELEASE
"${CMAKE_CXX_FLAGS_RELEASE} -D_SECURE_STL=0 -D_HAS_ITERATOR_DEBUGGING=0"
@@ -113,7 +120,7 @@ if (WINDOWS)
endif (WINDOWS)
set (GCC_EXTRA_OPTIMIZATIONS "-ffast-math -frounding-math")
set (GCC_EXTRA_OPTIMIZATIONS "-ffast-math")
if (LINUX)
set(CMAKE_SKIP_RPATH TRUE)
@@ -155,10 +162,10 @@ if (LINUX)
add_definitions(-D_FORTIFY_SOURCE=2)
endif (NOT ${GXX_VERSION} MATCHES " 4.1.*Red Hat")
endif (${GXX_VERSION} STREQUAL ${CXX_VERSION})
#Lets actualy get a numerical version of gxx's version
#Lets actually get a numerical version of gxx's version
STRING(REGEX REPLACE ".* ([0-9])\\.([0-9])\\.([0-9]).*" "\\1\\2\\3" CXX_VERSION ${CXX_VERSION})
#gcc 4.3 and above don't like the LL boost
if(${CXX_VERSION} GREATER 429)
add_definitions(-Wno-parentheses)
@@ -182,6 +189,8 @@ if (LINUX)
-pthread
)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
add_definitions(-DAPPID=secondlife)
add_definitions(-fvisibility=hidden)
# don't catch SIGCHLD in our base application class for the viewer - some of our 3rd party libs may need their *own* SIGCHLD handler to work. Sigh! The viewer doesn't need to catch SIGCHLD anyway.
@@ -200,10 +209,10 @@ if (LINUX)
if (NOT STANDALONE)
set(MARCH_FLAG " -march=pentium4")
endif (NOT STANDALONE)
set(CMAKE_CXX_FLAGS_RELEASESSE2 "${CMAKE_CXX_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
set(CMAKE_C_FLAGS_RELEASESSE2 "${CMAKE_C_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
set(CMAKE_CXX_FLAGS_RELEASESSE2 "${CMAKE_CXX_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
set(CMAKE_C_FLAGS_RELEASESSE2 "${CMAKE_C_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
endif (${ARCH} STREQUAL "x86_64")
set(CMAKE_CXX_FLAGS_DEBUG "-fno-inline ${CMAKE_CXX_FLAGS_DEBUG} -msse2")

View File

@@ -29,12 +29,12 @@ else (STANDALONE)
)
elseif (DARWIN)
set(APR_LIBRARIES
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.0.3.7.dylib
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.3.7.dylib
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.0.dylib
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.dylib
)
set(APRUTIL_LIBRARIES
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.0.3.8.dylib
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.0.3.8.dylib
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.0.dylib
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.0.dylib
)
set(APRICONV_LIBRARIES iconv)
else (WINDOWS)

View File

@@ -10,7 +10,7 @@ if (STANDALONE)
set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt)
set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-mt)
set(BOOST_REGEX_LIBRARY boost_regex-mt)
set(BOOST_SYSTEM_LIBRARY boost_system-mt)
set(BOOST_SYSTEM_LIBRARY boost_system-mt)
else (STANDALONE)
use_prebuilt_binary(boost)
set(Boost_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include)

View File

@@ -1,23 +1,45 @@
# -*- cmake -*-
function (build_version _target)
# Read version components from the header file.
file(STRINGS ${LIBS_OPEN_DIR}/llcommon/llversion${_target}.h lines
REGEX " LL_VERSION_")
foreach(line ${lines})
string(REGEX REPLACE ".*LL_VERSION_([A-Z]+).*" "\\1" comp "${line}")
string(REGEX REPLACE ".* = ([0-9]+);.*" "\\1" value "${line}")
set(v${comp} "${value}")
endforeach(line)
# Read version components from the header file.
file(STRINGS ${LIBS_OPEN_DIR}/llcommon/llversionviewer.h.in lines
REGEX " LL_VERSION_")
foreach(line ${lines})
string(REGEX REPLACE ".*LL_VERSION_([A-Z]+).*" "\\1" comp "${line}")
string(REGEX REPLACE ".* = ([0-9]+);.*" "\\1" value "${line}")
set(v${comp} "${value}")
endforeach(line)
# Compose the version.
set(${_target}_VERSION "${vMAJOR}.${vMINOR}.${vPATCH}.${vBUILD}")
if (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
message(STATUS "Version of ${_target} is ${${_target}_VERSION}")
else (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
message(FATAL_ERROR "Could not determine ${_target} version (${${_target}_VERSION})")
endif (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
execute_process(
COMMAND git rev-list HEAD
OUTPUT_VARIABLE GIT_REV_LIST_STR
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Report version to caller.
set(${_target}_VERSION "${${_target}_VERSION}" PARENT_SCOPE)
endfunction (build_version)
if(GIT_REV_LIST_STR)
string(REPLACE "\n" ";" GIT_REV_LIST ${GIT_REV_LIST_STR})
else()
string(REPLACE "\n" ";" GIT_REV_LIST "")
endif()
if(GIT_REV_LIST)
list(LENGTH GIT_REV_LIST vBUILD)
else()
set(vBUILD 99)
endif()
configure_file(
${CMAKE_SOURCE_DIR}/llcommon/llversionviewer.h.in
${CMAKE_SOURCE_DIR}/llcommon/llversionviewer.h
)
# Compose the version.
set(viewer_VERSION "${vMAJOR}.${vMINOR}.${vPATCH}.${vBUILD}")
if (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
message(STATUS "Version is ${viewer_VERSION}")
else (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
message(FATAL_ERROR "Could not determine version (${viewer_VERSION})")
endif (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
# Report version to caller.
#set(viewer_VERSION "${viewer_VERSION}" PARENT_SCOPE)

View File

@@ -79,7 +79,6 @@ set(cmake_SOURCE_FILES
UI.cmake
UnixInstall.cmake
Variables.cmake
Versions.cmake
XmlRpcEpi.cmake
ZLIB.cmake
)

View File

@@ -3,7 +3,7 @@
if (VIEWER AND WINDOWS)
find_path(DIRECTX_INCLUDE_DIR dxdiag.h
"$ENV{DXSDK_DIR}/Include"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2010)/Include"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2010)/Include"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2009)/Include"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Include"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2008)/Include"
@@ -25,7 +25,7 @@ if (VIEWER AND WINDOWS)
find_path(DIRECTX_LIBRARY_DIR dxguid.lib
"$ENV{DXSDK_DIR}/Lib/x86"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2010)/Lib/x86"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2010)/Lib/x86"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2009)/Lib/x86"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Lib/x86"
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2008)/Lib/x86"

View File

@@ -7,26 +7,13 @@ if(INSTALL_PROPRIETARY)
use_prebuilt_binary(fmod)
endif(INSTALL_PROPRIETARY)
find_library(FMOD_LIBRARY_RELEASE
find_library(FMOD_LIBRARY
NAMES fmod fmodvc fmod-3.75
PATHS
${ARCH_PREBUILT_DIRS_RELEASE}
optimized ${ARCH_PREBUILT_DIRS_RELEASE}
debug ${ARCH_PREBUILT_DIRS_DEBUG}
)
find_library(FMOD_LIBRARY_DEBUG
NAMES fmod fmodvc fmod-3.75
PATHS
${ARCH_PREBUILT_DIRS_DEBUG}
)
if (FMOD_LIBRARY_RELEASE AND FMOD_LIBRARY_DEBUG)
set(FMOD_LIBRARY
debug ${FMOD_LIBRARY_DEBUG}
optimized ${FMOD_LIBRARY_RELEASE})
elseif (FMOD_LIBRARY_RELEASE)
set(FMOD_LIBRARY ${FMOD_LIBRARY_RELEASE})
endif (FMOD_LIBRARY_RELEASE AND FMOD_LIBRARY_DEBUG)
if (NOT FMOD_LIBRARY)
set(FMOD_SDK_DIR CACHE PATH "Path to the FMOD SDK.")
if (FMOD_SDK_DIR)

View File

@@ -7,26 +7,13 @@ if(INSTALL_PROPRIETARY)
use_prebuilt_binary(fmodex)
endif(INSTALL_PROPRIETARY)
find_library(FMODEX_LIBRARY_RELEASE
find_library(FMODEX_LIBRARY
NAMES fmodex fmodex_vc fmodexL_vc
PATHS
${ARCH_PREBUILT_DIRS_RELEASE}
optimized ${ARCH_PREBUILT_DIRS_RELEASE}
debug ${ARCH_PREBUILT_DIRS_DEBUG}
)
find_library(FMODEX_LIBRARY_DEBUG
NAMES fmodex fmodex_vc fmodexL_vc
PATHS
${ARCH_PREBUILT_DIRS_DEBUG}
)
if (FMODEX_LIBRARY_RELEASE AND FMODEX_LIBRARY_DEBUG)
set(FMODEX_LIBRARY
debug ${FMODEX_LIBRARY_DEBUG}
optimized ${FMODEX_LIBRARY_RELEASE})
elseif (FMODEX_LIBRARY_RELEASE)
set(FMODEX_LIBRARY ${FMODEX_LIBRARY_RELEASE})
endif (FMODEX_LIBRARY_RELEASE AND FMODEX_LIBRARY_DEBUG)
if (NOT FMODEX_LIBRARY)
set(FMODEX_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.")
if (FMODEX_SDK_DIR)

View File

@@ -9,7 +9,7 @@
# also defined, but not for general use are
# JSONCPP_LIBRARY, where to find the jsoncpp library.
FIND_PATH(JSONCPP_INCLUDE_DIR jsoncpp/json.h
FIND_PATH(JSONCPP_INCLUDE_DIR json/json.h
/usr/local/include
/usr/include
)

View File

@@ -14,6 +14,7 @@ endif (DARWIN)
set(LLCOMMON_INCLUDE_DIRS
${LIBS_OPEN_DIR}/cwdebug
${LIBS_OPEN_DIR}/llcommon
${APRUTIL_INCLUDE_DIR}
${APR_INCLUDE_DIR}
${Boost_INCLUDE_DIRS}
)

View File

@@ -8,7 +8,7 @@ set(LLPLUGIN_INCLUDE_DIRS
if (LINUX)
# In order to support using ld.gold on linux, we need to explicitely
# specify all libraries that llplugin uses.
set(LLPLUGIN_LIBRARIES llplugin pthread)
set(LLPLUGIN_LIBRARIES llplugin pthread)
else (LINUX)
set(LLPLUGIN_LIBRARIES llplugin)
set(LLPLUGIN_LIBRARIES llplugin)
endif (LINUX)

View File

@@ -19,7 +19,6 @@ if (WINDOWS)
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.3\\InstallPath]
)
elseif (EXISTS /etc/debian_version)
# On Debian and Ubuntu, avoid Python 2.4 if possible.

View File

@@ -38,6 +38,7 @@ else (STANDALONE)
if (LINUX)
set(UI_LIBRARIES
atk-1.0
X11
gdk-x11-2.0
gdk_pixbuf-2.0
Xinerama

View File

@@ -1,9 +0,0 @@
include(BuildVersion)
if(VIEWER)
build_version(viewer)
endif(VIEWER)
if(SERVER)
build_version(server)
endif(SERVER)

View File

@@ -67,11 +67,16 @@ elseif (LINUX)
QtNetwork
QtGui
QtCore
crypto
ssl
# qgif
# qjpeg
jscore
jpeg
fontconfig
X11
Xrender
Xext
GL
)
endif (STANDALONE)

View File

@@ -171,7 +171,8 @@ void stop_recording_backtraces(void)
channel_ct gtk DDCN("GTK"); //!< This debug channel is used for output related to gtk.
channel_ct sdl DDCN("SDL"); //!< This debug channel is used for output related to sdl locking.
channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces.
channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used class AIStateMachine.
channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used for output related to class AIStateMachine.
channel_ct caps DDCN("CAPS"); //!< This debug channel is used for output related to Capabilities.
} // namespace dc
} // namespace DEBUGCHANNELS

View File

@@ -117,6 +117,7 @@ extern CWD_API channel_ct gtk;
extern CWD_API channel_ct sdl;
extern CWD_API channel_ct backtrace;
extern CWD_API channel_ct statemachine;
extern CWD_API channel_ct caps;
#endif

View File

@@ -239,12 +239,15 @@ protected:
// A list of all audio sources that are known to the viewer at this time.
// This is most likely a superset of the ones that we actually have audio
// data for, or are playing back.
public://Jay: IDGAF
typedef std::map<LLUUID, LLAudioSource *> source_map;
protected:
typedef std::map<LLUUID, LLAudioData *> data_map;
public://Jay: IDGAF
source_map mAllSources;
protected:
data_map mAllData;
LLAudioChannel *mChannels[MAX_CHANNELS];
// Buffers needs to change into a different data structure, as the number of buffers
@@ -351,6 +354,9 @@ public:
protected:
LLUUID mID; // The ID of the source is that of the object if it's attached to an object.
LLUUID mOwnerID; // owner of the object playing the sound
public:
const LLUUID &getOwnerID() { return mOwnerID; }
protected:
F32 mPriority;
F32 mGain;
bool mSourceMuted;

View File

@@ -29,14 +29,21 @@ set(llcommon_SOURCE_FILES
llbase64.cpp
llcommon.cpp
llcommonutils.cpp
llcoros.cpp
llcrc.cpp
llcriticaldamp.cpp
llcursortypes.cpp
lldate.cpp
lldependencies.cpp
lldictionary.cpp
llerror.cpp
llerrorthread.cpp
llevent.cpp
lleventapi.cpp
lleventcoro.cpp
lleventdispatcher.cpp
lleventfilter.cpp
llevents.cpp
lleventtimer.cpp
llfasttimer_class.cpp
llfile.cpp
@@ -84,6 +91,7 @@ set(llcommon_SOURCE_FILES
lluri.cpp
lluuid.cpp
llworkerthread.cpp
ll_template_cast.h
metaclass.cpp
metaproperty.cpp
reflective.cpp
@@ -121,6 +129,7 @@ set(llcommon_HEADER_FILES
llclickaction.h
llcommon.h
llcommonutils.h
llcoros.h
llcrc.h
llcriticaldamp.h
llcursortypes.h
@@ -128,6 +137,7 @@ set(llcommon_HEADER_FILES
lldarrayptr.h
lldate.h
lldefs.h
lldependencies.h
lldeleteutils.h
lldepthstack.h
lldictionary.h
@@ -140,6 +150,11 @@ set(llcommon_HEADER_FILES
llerrorlegacy.h
llerrorthread.h
llevent.h
lleventapi.h
lleventcoro.h
lleventdispatcher.h
lleventfilter.h
llevents.h
lleventemitter.h
llextendedstatus.h
lleventtimer.h
@@ -213,8 +228,7 @@ set(llcommon_HEADER_FILES
lluri.h
lluuid.h
lluuidhashmap.h
llversionserver.h
llversionviewer.h
llversionviewer.h.in
llworkerthread.h
metaclass.h
metaclasst.h

View File

@@ -27,8 +27,38 @@
* - Initial version, written by Aleric Inglewood @ SL
*/
#include "linden_common.h"
// An AIFrameTimer object provides a callback API for timer events.
//
// Typical usage:
//
// // Any thread.
// AIFrameTimer timer;
//
// ...
// // Any thread (after successful construction is guaranteed).
// timer.create(5.5, boost::bind(&the_callback, <optional params>)); // Call the_callback(<optional params>) in 5.5 seconds.
//
// The callback function is always called by the main thread and should therefore
// be light weight.
//
// If timer.cancel() is called before the timer expires, then the callback
// function isn't called. If cancel() is called by another thread than the
// main thread, then it is possible that the callback function is called
// even while still inside cancel(), but as soon as cancel() returned it
// is guarenteed that the callback function won't be called anymore.
// Hence, if the callback function is a member of some object then
// cancel() must be called before the destruction of that object (ie from
// it's destructor). Calling cancel() multiple times is not a problem.
// Note: if cancel() is called while the callback function is being
// called then cancel() will block until the callback function returned.
//
// The timer object can be reused (by calling create() again), but
// only after either the callback function was called, or after cancel()
// returned. Most notably, you can call create() again from inside the
// callback function to "restart" the timer.
//
#include "linden_common.h"
#include "aiframetimer.h"
static F64 const NEVER = 1e16; // 317 million years.
@@ -115,7 +145,7 @@ void AIFrameTimer::handleExpiration(F64 current_frame_time)
// function here because the trylock fails.
//
// Assuming the main thread arrived here, there are two possible states
// for the other thread that tries to delete the call back function,
// for the other thread that tries to delete the callback function,
// right after calling the cancel() function too:
//
// 1. It hasn't obtained the first lock yet, we obtain the handle.mMutex

View File

@@ -61,20 +61,37 @@ class LL_COMMON_API AIFrameTimer
// See aiframetimer.cpp for more notes.
class AIRunningFrameTimer {
private:
F64 mExpire; // Time at which the timer expires, in seconds since application start (compared to LLFrameTimer::sFrameTime).
Signal* mCallback;
AIFrameTimer* mTimer;
F64 mExpire; // Time at which the timer expires, in seconds since application start (compared to LLFrameTimer::sFrameTime).
AIFrameTimer* mTimer; // The actual timer.
// Can be mutable, since only the mExpire is used for ordering this object in the multiset AIFrameTimer::sTimerList.
mutable Signal* mCallback; // Pointer to callback struct, or NULL when the object wasn't added to sTimerList yet.
public:
AIRunningFrameTimer(F64 expiration, AIFrameTimer* timer) : mExpire(LLFrameTimer::getElapsedSeconds() + expiration), mCallback(new Signal), mTimer(timer) { }
AIRunningFrameTimer(F64 expiration, AIFrameTimer* timer) : mExpire(LLFrameTimer::getElapsedSeconds() + expiration), mCallback(NULL), mTimer(timer) { }
~AIRunningFrameTimer() { delete mCallback; }
void init(signal_type::slot_type const& slot) const { mCallback->mSignal.connect(slot); }
// This function is called after the final object was added to sTimerList (where it is initialized in-place).
void init(signal_type::slot_type const& slot) const
{
// We may only call init() once.
llassert(!mCallback);
mCallback = new Signal;
mCallback->mSignal.connect(slot);
}
// Order AIFrameTimer::sTimerList so that the timer that expires first is up front.
friend bool operator<(AIRunningFrameTimer const& ft1, AIRunningFrameTimer const& ft2) { return ft1.mExpire < ft2.mExpire; }
void do_callback(void) const { mCallback->mSignal(); }
F64 expiration(void) const { return mExpire; }
AIFrameTimer* getTimer(void) const { return mTimer; }
#if LL_DEBUG
// May not copy this object after it was initialized.
AIRunningFrameTimer(AIRunningFrameTimer const& running_frame_timer) :
mExpire(running_frame_timer.mExpire), mCallback(running_frame_timer.mCallback), mTimer(running_frame_timer.mTimer)
{ llassert(!mCallback); }
#endif
};
typedef std::multiset<AIRunningFrameTimer> timer_list_type;
@@ -98,12 +115,12 @@ class LL_COMMON_API AIFrameTimer
// Actual initialization used by AIFrameTimer::create.
void init(timer_list_type::iterator const& running_timer, signal_type::slot_type const& slot)
{
// Locking AIFrameTimer::sMutex is not neccessary here, because we're creating
// the object and no other thread knows of mRunningTimer at this point.
mRunningTimer = running_timer;
mRunningTimer->init(slot);
}
{
// Locking AIFrameTimer::sMutex is not neccessary here, because we're creating
// the object and no other thread knows of mRunningTimer at this point.
mRunningTimer = running_timer;
mRunningTimer->init(slot);
}
private:
// LLMutex has no assignment operator.
@@ -129,6 +146,8 @@ class LL_COMMON_API AIFrameTimer
void create(F64 expiration, signal_type::slot_type const& slot);
void cancel(void);
bool isRunning(void) const { bool running; sMutex.lock(); running = mHandle.mRunningTimer != sTimerList.end(); sMutex.unlock(); return running; }
protected:
static void handleExpiration(F64 current_frame_time);
};

View File

@@ -0,0 +1,177 @@
/**
* @file ll_template_cast.h
* @author Nat Goodspeed
* @date 2009-11-21
* @brief Define ll_template_cast function
*
* $LicenseInfo:firstyear=2009&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$
*/
#if ! defined(LL_LL_TEMPLATE_CAST_H)
#define LL_LL_TEMPLATE_CAST_H
/**
* Implementation for ll_template_cast() (q.v.).
*
* Default implementation: trying to cast two completely unrelated types
* returns 0. Typically you'd specify T and U as pointer types, but in fact T
* can be any type that can be initialized with 0.
*/
template <typename T, typename U>
struct ll_template_cast_impl
{
T operator()(U)
{
return 0;
}
};
/**
* ll_template_cast<T>(some_value) is for use in a template function when
* some_value might be of arbitrary type, but you want to recognize type T
* specially.
*
* It's designed for use with pointer types. Example:
* @code
* struct SpecialClass
* {
* void someMethod(const std::string&) const;
* };
*
* template <class REALCLASS>
* void somefunc(const REALCLASS& instance)
* {
* const SpecialClass* ptr = ll_template_cast<const SpecialClass*>(&instance);
* if (ptr)
* {
* ptr->someMethod("Call method only available on SpecialClass");
* }
* }
* @endcode
*
* Why is this better than dynamic_cast<>? Because unless OtherClass is
* polymorphic, the following won't even compile (gcc 4.0.1):
* @code
* OtherClass other;
* SpecialClass* ptr = dynamic_cast<SpecialClass*>(&other);
* @endcode
* to say nothing of this:
* @code
* void function(int);
* SpecialClass* ptr = dynamic_cast<SpecialClass*>(&function);
* @endcode
* ll_template_cast handles these kinds of cases by returning 0.
*/
template <typename T, typename U>
T ll_template_cast(U value)
{
return ll_template_cast_impl<T, U>()(value);
}
/**
* Implementation for ll_template_cast() (q.v.).
*
* Implementation for identical types: return same value.
*/
template <typename T>
struct ll_template_cast_impl<T, T>
{
T operator()(T value)
{
return value;
}
};
/**
* LL_TEMPLATE_CONVERTIBLE(dest, source) asserts that, for a value @c s of
* type @c source, <tt>ll_template_cast<dest>(s)</tt> will return @c s --
* presuming that @c source can be converted to @c dest by the normal rules of
* C++.
*
* By default, <tt>ll_template_cast<dest>(s)</tt> will return 0 unless @c s's
* type is literally identical to @c dest. (This is because of the
* straightforward application of template specialization rules.) That can
* lead to surprising results, e.g.:
*
* @code
* Foo myFoo;
* const Foo* fooptr = ll_template_cast<const Foo*>(&myFoo);
* @endcode
*
* Here @c fooptr will be 0 because <tt>&myFoo</tt> is of type <tt>Foo*</tt>
* -- @em not <tt>const Foo*</tt>. (Declaring <tt>const Foo myFoo;</tt> would
* force the compiler to do the right thing.)
*
* More disappointingly:
* @code
* struct Base {};
* struct Subclass: public Base {};
* Subclass object;
* Base* ptr = ll_template_cast<Base*>(&object);
* @endcode
*
* Here @c ptr will be 0 because <tt>&object</tt> is of type
* <tt>Subclass*</tt> rather than <tt>Base*</tt>. We @em want this cast to
* succeed, but without our help ll_template_cast can't recognize it.
*
* The following would suffice:
* @code
* LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*);
* ...
* Base* ptr = ll_template_cast<Base*>(&object);
* @endcode
*
* However, as noted earlier, this is easily fooled:
* @code
* const Base* ptr = ll_template_cast<const Base*>(&object);
* @endcode
* would still produce 0 because we haven't yet seen:
* @code
* LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*);
* @endcode
*
* @TODO
* This macro should use Boost type_traits facilities for stripping and
* re-adding @c const and @c volatile qualifiers so that invoking
* LL_TEMPLATE_CONVERTIBLE(dest, source) will automatically generate all
* permitted permutations. It's really not fair to the coder to require
* separate:
* @code
* LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*);
* LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*);
* LL_TEMPLATE_CONVERTIBLE(const Base*, const Subclass*);
* @endcode
*
* (Naturally we omit <tt>LL_TEMPLATE_CONVERTIBLE(Base*, const Subclass*)</tt>
* because that's not permitted by normal C++ assignment anyway.)
*/
#define LL_TEMPLATE_CONVERTIBLE(DEST, SOURCE) \
template <> \
struct ll_template_cast_impl<DEST, SOURCE> \
{ \
DEST operator()(SOURCE wrapper) \
{ \
return wrapper; \
} \
}
#endif /* ! defined(LL_LL_TEMPLATE_CAST_H) */

154
indra/llcommon/llcoros.cpp Normal file
View File

@@ -0,0 +1,154 @@
/**
* @file llcoros.cpp
* @author Nat Goodspeed
* @date 2009-06-03
* @brief Implementation for llcoros.
*
* $LicenseInfo:firstyear=2009&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$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llcoros.h"
// STL headers
// std headers
// external library headers
#include <boost/bind.hpp>
// other Linden headers
#include "llevents.h"
#include "llerror.h"
#include "stringize.h"
LLCoros::LLCoros()
{
// Register our cleanup() method for "mainloop" ticks
LLEventPumps::instance().obtain("mainloop").listen(
"LLCoros", boost::bind(&LLCoros::cleanup, this, _1));
}
bool LLCoros::cleanup(const LLSD&)
{
// Walk the mCoros map, checking and removing completed coroutines.
for (CoroMap::iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; )
{
// Has this coroutine exited (normal return, exception, exit() call)
// since last tick?
if (mi->second->exited())
{
LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
// The erase() call will invalidate its passed iterator value --
// so increment mi FIRST -- but pass its original value to
// erase(). This is what postincrement is all about.
mCoros.erase(mi++);
}
else
{
// Still live, just skip this entry as if incrementing at the top
// of the loop as usual.
++mi;
}
}
return false;
}
std::string LLCoros::generateDistinctName(const std::string& prefix) const
{
// Allowing empty name would make getName()'s not-found return ambiguous.
if (prefix.empty())
{
LL_ERRS("LLCoros") << "LLCoros::launch(): pass non-empty name string" << LL_ENDL;
}
// If the specified name isn't already in the map, just use that.
std::string name(prefix);
// Find the lowest numeric suffix that doesn't collide with an existing
// entry. Start with 2 just to make it more intuitive for any interested
// parties: e.g. "joe", "joe2", "joe3"...
for (int i = 2; ; name = STRINGIZE(prefix << i++))
{
if (mCoros.find(name) == mCoros.end())
{
LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
return name;
}
}
}
bool LLCoros::kill(const std::string& name)
{
CoroMap::iterator found = mCoros.find(name);
if (found == mCoros.end())
{
return false;
}
// Because this is a boost::ptr_map, erasing the map entry also destroys
// the referenced heap object, in this case the boost::coroutine object,
// which will terminate the coroutine.
mCoros.erase(found);
return true;
}
std::string LLCoros::getNameByID(const void* self_id) const
{
// Walk the existing coroutines, looking for one from which the 'self_id'
// passed to us comes.
for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi)
{
namespace coro_private = boost::coroutines::detail;
if (static_cast<void*>(coro_private::coroutine_accessor::get_impl(const_cast<coro&>(*mi->second)).get())
== self_id)
{
return mi->first;
}
}
return "";
}
/*****************************************************************************
* MUST BE LAST
*****************************************************************************/
// Turn off MSVC optimizations for just LLCoros::launchImpl() -- see
// DEV-32777. But MSVC doesn't support push/pop for optimization flags as it
// does for warning suppression, and we really don't want to force
// optimization ON for other code even in Debug or RelWithDebInfo builds.
#if LL_MSVC
// work around broken optimizations
#pragma warning(disable: 4748)
#pragma optimize("", off)
#endif // LL_MSVC
std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro)
{
std::string name(generateDistinctName(prefix));
mCoros.insert(name, newCoro);
/* Run the coroutine until its first wait, then return here */
(*newCoro)(std::nothrow);
return name;
}
#if LL_MSVC
// reenable optimizations
#pragma optimize("", on)
#endif // LL_MSVC

166
indra/llcommon/llcoros.h Normal file
View File

@@ -0,0 +1,166 @@
/**
* @file llcoros.h
* @author Nat Goodspeed
* @date 2009-06-02
* @brief Manage running boost::coroutine instances
*
* $LicenseInfo:firstyear=2009&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$
*/
#if ! defined(LL_LLCOROS_H)
#define LL_LLCOROS_H
#include <boost/coroutine/coroutine.hpp>
#include "llsingleton.h"
#include <boost/ptr_container/ptr_map.hpp>
#include <string>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/iteration/local.hpp>
#include <stdexcept>
/**
* Registry of named Boost.Coroutine instances
*
* The Boost.Coroutine library supports the general case of a coroutine
* accepting arbitrary parameters and yielding multiple (sets of) results. For
* such use cases, it's natural for the invoking code to retain the coroutine
* instance: the consumer repeatedly calls into the coroutine, perhaps passing
* new parameter values, prompting it to yield its next result.
*
* Our typical coroutine usage is different, though. For us, coroutines
* provide an alternative to the @c Responder pattern. Our typical coroutine
* has @c void return, invoked in fire-and-forget mode: the handler for some
* user gesture launches the coroutine and promptly returns to the main loop.
* The coroutine initiates some action that will take multiple frames (e.g. a
* capability request), waits for its result, processes it and silently steals
* away.
*
* This usage poses two (related) problems:
*
* # Who should own the coroutine instance? If it's simply local to the
* handler code that launches it, return from the handler will destroy the
* coroutine object, terminating the coroutine.
* # Once the coroutine terminates, in whatever way, who's responsible for
* cleaning up the coroutine object?
*
* LLCoros is a Singleton collection of currently-active coroutine instances.
* Each has a name. You ask LLCoros to launch a new coroutine with a suggested
* name prefix; from your prefix it generates a distinct name, registers the
* new coroutine and returns the actual name.
*
* The name can be used to kill off the coroutine prematurely, if needed. It
* can also provide diagnostic info: we can look up the name of the
* currently-running coroutine.
*
* Finally, the next frame ("mainloop" event) after the coroutine terminates,
* LLCoros will notice its demise and destroy it.
*/
class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
{
public:
/// Canonical boost::coroutines::coroutine signature we use
typedef boost::coroutines::coroutine<void()> coro;
/// Canonical 'self' type
typedef coro::self self;
/**
* Create and start running a new coroutine with specified name. The name
* string you pass is a suggestion; it will be tweaked for uniqueness. The
* actual name is returned to you.
*
* Usage looks like this, for (e.g.) two coroutine parameters:
* @code
* class MyClass
* {
* public:
* ...
* // Do NOT NOT NOT accept reference params other than 'self'!
* // Pass by value only!
* void myCoroutineMethod(LLCoros::self& self, std::string, LLSD);
* ...
* };
* ...
* std::string name = LLCoros::instance().launch(
* "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1,
* "somestring", LLSD(17));
* @endcode
*
* Your function/method must accept LLCoros::self& as its first parameter.
* It can accept any other parameters you want -- but ONLY BY VALUE!
* Other reference parameters are a BAD IDEA! You Have Been Warned. See
* DEV-32777 comments for an explanation.
*
* Pass a callable that accepts the single LLCoros::self& parameter. It
* may work to pass a free function whose only parameter is 'self'; for
* all other cases use boost::bind(). Of course, for a non-static class
* method, the first parameter must be the class instance. Use the
* placeholder _1 for the 'self' parameter. Any other parameters should be
* passed via the bind() expression.
*
* launch() tweaks the suggested name so it won't collide with any
* existing coroutine instance, creates the coroutine instance, registers
* it with the tweaked name and runs it until its first wait. At that
* point it returns the tweaked name.
*/
template <typename CALLABLE>
std::string launch(const std::string& prefix, const CALLABLE& callable)
{
return launchImpl(prefix, new coro(callable));
}
/**
* Abort a running coroutine by name. Normally, when a coroutine either
* runs to completion or terminates with an exception, LLCoros quietly
* cleans it up. This is for use only when you must explicitly interrupt
* one prematurely. Returns @c true if the specified name was found and
* still running at the time.
*/
bool kill(const std::string& name);
/**
* From within a coroutine, pass its @c self object to look up the
* (tweaked) name string by which this coroutine is registered. Returns
* the empty string if not found (e.g. if the coroutine was launched by
* hand rather than using LLCoros::launch()).
*/
template <typename COROUTINE_SELF>
std::string getName(const COROUTINE_SELF& self) const
{
return getNameByID(self.get_id());
}
/// getName() by self.get_id()
std::string getNameByID(const void* self_id) const;
private:
friend class LLSingleton<LLCoros>;
LLCoros();
std::string launchImpl(const std::string& prefix, coro* newCoro);
std::string generateDistinctName(const std::string& prefix) const;
bool cleanup(const LLSD&);
typedef boost::ptr_map<std::string, coro> CoroMap;
CoroMap mCoros;
};
#endif /* ! defined(LL_LLCOROS_H) */

View File

@@ -0,0 +1,103 @@
/**
* @file lldependencies.cpp
* @author Nat Goodspeed
* @date 2008-09-17
* @brief Implementation for lldependencies.
*
* $LicenseInfo:firstyear=2008&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$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "lldependencies.h"
// STL headers
#include <map>
#include <sstream>
// std headers
// external library headers
#include <boost/graph/graph_traits.hpp> // for boost::graph_traits
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/graph/exception.hpp>
// other Linden headers
LLDependenciesBase::VertexList LLDependenciesBase::topo_sort(int vertices, const EdgeList& edges) const
{
// Construct a Boost Graph Library graph according to the constraints
// we've collected. It seems as though we ought to be able to capture
// the uniqueness of vertex keys using a setS of vertices with a
// string property -- but I don't yet understand adjacency_list well
// enough to get there. All the examples I've seen so far use integers
// for vertices.
// Define the Graph type. Use a vector for vertices so we can use the
// default topological_sort vertex lookup by int index. Use a set for
// edges because the same dependency may be stated twice: Node "a" may
// specify that it must precede "b", while "b" may also state that it
// must follow "a".
typedef boost::adjacency_list<boost::setS, boost::vecS, boost::directedS,
boost::no_property> Graph;
// Instantiate the graph. Without vertex properties, we need say no
// more about vertices than the total number.
Graph g(edges.begin(), edges.end(), vertices);
// topo sort
typedef boost::graph_traits<Graph>::vertex_descriptor VertexDesc;
typedef std::vector<VertexDesc> SortedList;
SortedList sorted;
// note that it throws not_a_dag if it finds a cycle
try
{
boost::topological_sort(g, std::back_inserter(sorted));
}
catch (const boost::not_a_dag& e)
{
// translate to the exception we define
std::ostringstream out;
out << "LLDependencies cycle: " << e.what() << '\n';
// Omit independent nodes: display only those that might contribute to
// the cycle.
describe(out, false);
throw Cycle(out.str());
}
// A peculiarity of boost::topological_sort() is that it emits results in
// REVERSE topological order: to get the result you want, you must
// traverse the SortedList using reverse iterators.
return VertexList(sorted.rbegin(), sorted.rend());
}
std::ostream& LLDependenciesBase::describe(std::ostream& out, bool full) const
{
// Should never encounter this base-class implementation; may mean that
// the KEY type doesn't have a suitable ostream operator<<().
out << "<no description available>";
return out;
}
std::string LLDependenciesBase::describe(bool full) const
{
// Just use the ostream-based describe() on a std::ostringstream. The
// implementation is here mostly so that we can avoid #include <sstream>
// in the header file.
std::ostringstream out;
describe(out, full);
return out.str();
}

View File

@@ -0,0 +1,799 @@
/**
* @file lldependencies.h
* @author Nat Goodspeed
* @date 2008-09-17
* @brief LLDependencies: a generic mechanism for expressing "b must follow a,
* but precede c"
*
* $LicenseInfo:firstyear=2008&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$
*/
#if ! defined(LL_LLDEPENDENCIES_H)
#define LL_LLDEPENDENCIES_H
#include <string>
#include <vector>
#include <set>
#include <map>
#include <stdexcept>
#include <iosfwd>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/indirect_iterator.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
/*****************************************************************************
* Utilities
*****************************************************************************/
/**
* generic range transformer: given a range acceptable to Boost.Range (e.g. a
* standard container, an iterator pair, ...) and a unary function to apply to
* each element of the range, make a corresponding range that lazily applies
* that function to each element on dereferencing.
*/
template<typename FUNCTION, typename RANGE>
inline
boost::iterator_range<boost::transform_iterator<FUNCTION,
typename boost::range_const_iterator<RANGE>::type> >
make_transform_range(const RANGE& range, FUNCTION function)
{
// shorthand for the iterator type embedded in our return type
typedef boost::transform_iterator<FUNCTION, typename boost::range_const_iterator<RANGE>::type>
transform_iterator;
return boost::make_iterator_range(transform_iterator(boost::begin(range), function),
transform_iterator(boost::end(range), function));
}
/// non-const version of make_transform_range()
template<typename FUNCTION, typename RANGE>
inline
boost::iterator_range<boost::transform_iterator<FUNCTION,
typename boost::range_iterator<RANGE>::type> >
make_transform_range(RANGE& range, FUNCTION function)
{
// shorthand for the iterator type embedded in our return type
typedef boost::transform_iterator<FUNCTION, typename boost::range_iterator<RANGE>::type>
transform_iterator;
return boost::make_iterator_range(transform_iterator(boost::begin(range), function),
transform_iterator(boost::end(range), function));
}
/**
* From any range compatible with Boost.Range, instantiate any class capable
* of accepting an iterator pair.
*/
template<class TYPE>
struct instance_from_range: public TYPE
{
template<typename RANGE>
instance_from_range(RANGE range):
TYPE(boost::begin(range), boost::end(range))
{}
};
/*****************************************************************************
* LLDependencies
*****************************************************************************/
/**
* LLDependencies components that should not be reinstantiated for each KEY,
* NODE specialization
*/
class LL_COMMON_API LLDependenciesBase
{
public:
virtual ~LLDependenciesBase() {}
/**
* Exception thrown by sort() if there's a cycle
*/
struct Cycle: public std::runtime_error
{
Cycle(const std::string& what): std::runtime_error(what) {}
};
/**
* Provide a short description of this LLDependencies instance on the
* specified output stream, assuming that its KEY type has an operator<<()
* that works with std::ostream.
*
* Pass @a full as @c false to omit any keys without dependency constraints.
*/
virtual std::ostream& describe(std::ostream& out, bool full=true) const;
/// describe() to a string
virtual std::string describe(bool full=true) const;
protected:
typedef std::vector< std::pair<int, int> > EdgeList;
typedef std::vector<int> VertexList;
VertexList topo_sort(int vertices, const EdgeList& edges) const;
/**
* refpair is specifically intended to capture a pair of references. This
* is better than std::pair<T1&, T2&> because some implementations of
* std::pair's ctor accept const references to the two types. If the
* types are themselves references, this results in an illegal reference-
* to-reference.
*/
template<typename T1, typename T2>
struct refpair
{
refpair(T1 value1, T2 value2):
first(value1),
second(value2)
{}
T1 first;
T2 second;
};
};
/// describe() helper: for most types, report the type as usual
template<typename T>
inline
std::ostream& LLDependencies_describe(std::ostream& out, const T& key)
{
out << key;
return out;
}
/// specialize LLDependencies_describe() for std::string
template<>
inline
std::ostream& LLDependencies_describe(std::ostream& out, const std::string& key)
{
out << '"' << key << '"';
return out;
}
/**
* It's reasonable to use LLDependencies in a keys-only way, more or less like
* std::set. For that case, the default NODE type is an empty struct.
*/
struct LLDependenciesEmpty
{
LLDependenciesEmpty() {}
/**
* Give it a constructor accepting void* so caller can pass placeholder
* values such as NULL or 0 rather than having to write
* LLDependenciesEmpty().
*/
LLDependenciesEmpty(void*) {}
};
/**
* This class manages abstract dependencies between node types of your
* choosing. As with a std::map, nodes are copied when add()ed, so the node
* type should be relatively lightweight; to manipulate dependencies between
* expensive objects, use a pointer type.
*
* For a given node, you may state the keys of nodes that must precede it
* and/or nodes that must follow it. The sort() method will produce an order
* that should work, or throw an exception if the constraints are impossible.
* We cache results to minimize the cost of repeated sort() calls.
*/
template<typename KEY = std::string,
typename NODE = LLDependenciesEmpty>
class LLDependencies: public LLDependenciesBase
{
typedef LLDependencies<KEY, NODE> self_type;
/**
* Internally, we bundle the client's NODE with its before/after keys.
*/
struct DepNode
{
typedef std::set<KEY> dep_set;
DepNode(const NODE& node_, const dep_set& after_, const dep_set& before_):
node(node_),
after(after_),
before(before_)
{}
NODE node;
dep_set after, before;
};
typedef std::map<KEY, DepNode> DepNodeMap;
typedef typename DepNodeMap::value_type DepNodeMapEntry;
/// We have various ways to get the dependencies for a given DepNode.
/// Rather than having to restate each one for 'after' and 'before'
/// separately, pass a dep_selector so we can apply each to either.
typedef boost::function<const typename DepNode::dep_set&(const DepNode&)> dep_selector;
public:
LLDependencies() {}
typedef KEY key_type;
typedef NODE node_type;
/// param type used to express lists of other node keys -- note that such
/// lists can be initialized with boost::assign::list_of()
typedef std::vector<KEY> KeyList;
/**
* Add a new node. State its dependencies on other nodes (which may not
* yet have been added) by listing the keys of nodes this new one must
* follow, and separately the keys of nodes this new one must precede.
*
* The node you pass is @em copied into an internal data structure. If you
* want to modify the node value after add()ing it, capture the returned
* NODE& reference.
*
* @note
* Actual dependency analysis is deferred to the sort() method, so
* you can add an arbitrary number of nodes without incurring analysis
* overhead for each. The flip side of this is that add()ing nodes that
* define a cycle leaves this object in a state in which sort() will
* always throw the Cycle exception.
*
* Two distinct use cases are anticipated:
* * The data used to load this object are completely known at compile
* time (e.g. LLEventPump listener names). A Cycle exception represents a
* bug which can be corrected by the coder. The program need neither catch
* Cycle nor attempt to salvage the state of this object.
* * The data are loaded at runtime, therefore the universe of
* dependencies cannot be known at compile time. The client code should
* catch Cycle.
* ** If a Cycle exception indicates fatally-flawed input data, this
* object can simply be discarded, possibly with the entire program run.
* ** If it is essential to restore this object to a working state, the
* simplest workaround is to remove() nodes in LIFO order.
* *** It may be useful to add functionality to this class to track the
* add() chronology, providing a pop() method to remove the most recently
* added node.
* *** It may further be useful to add a restore() method which would
* pop() until sort() no longer throws Cycle. This method would be
* expensive -- but it's not clear that client code could resolve the
* problem more cheaply.
*/
NODE& add(const KEY& key, const NODE& node = NODE(),
const KeyList& after = KeyList(),
const KeyList& before = KeyList())
{
// Get the passed-in lists as sets for equality comparison
typename DepNode::dep_set
after_set(after.begin(), after.end()),
before_set(before.begin(), before.end());
// Try to insert the new node; if it already exists, find the old
// node instead.
std::pair<typename DepNodeMap::iterator, bool> inserted =
mNodes.insert(typename DepNodeMap::value_type(key,
DepNode(node, after_set, before_set)));
if (! inserted.second) // bool indicating success of insert()
{
// We already have a node by this name. Have its dependencies
// changed? If the existing node's dependencies are identical, the
// result will be unchanged, so we can leave the cache intact.
// Regardless of inserted.second, inserted.first is the iterator
// to the newly-inserted (or existing) map entry. Of course, that
// entry's second is the DepNode of interest.
if (inserted.first->second.after != after_set ||
inserted.first->second.before != before_set)
{
// Dependencies have changed: clear the cached result.
mCache.clear();
// save the new dependencies
inserted.first->second.after = after_set;
inserted.first->second.before = before_set;
}
}
else // this node is new
{
// This will change results.
mCache.clear();
}
return inserted.first->second.node;
}
/// the value of an iterator, showing both KEY and its NODE
typedef refpair<const KEY&, NODE&> value_type;
/// the value of a const_iterator
typedef refpair<const KEY&, const NODE&> const_value_type;
private:
// Extract functors
static value_type value_extract(DepNodeMapEntry& entry)
{
return value_type(entry.first, entry.second.node);
}
static const_value_type const_value_extract(const DepNodeMapEntry& entry)
{
return const_value_type(entry.first, entry.second.node);
}
// All the iterator access methods return iterator ranges just to cut down
// on the friggin' boilerplate!!
/// generic mNodes range method
template<typename ITERATOR, typename FUNCTION>
boost::iterator_range<ITERATOR> generic_range(FUNCTION function)
{
return make_transform_range(mNodes, function);
}
/// generic mNodes const range method
template<typename ITERATOR, typename FUNCTION>
boost::iterator_range<ITERATOR> generic_range(FUNCTION function) const
{
return make_transform_range(mNodes, function);
}
public:
/// iterator over value_type entries
typedef boost::transform_iterator<boost::function<value_type(DepNodeMapEntry&)>,
typename DepNodeMap::iterator> iterator;
/// range over value_type entries
typedef boost::iterator_range<iterator> range;
/// iterate over value_type <i>in @c KEY order</i> rather than dependency order
range get_range()
{
return generic_range<iterator>(value_extract);
}
/// iterator over const_value_type entries
typedef boost::transform_iterator<boost::function<const_value_type(const DepNodeMapEntry&)>,
typename DepNodeMap::const_iterator> const_iterator;
/// range over const_value_type entries
typedef boost::iterator_range<const_iterator> const_range;
/// iterate over const_value_type <i>in @c KEY order</i> rather than dependency order
const_range get_range() const
{
return generic_range<const_iterator>(const_value_extract);
}
/// iterator over stored NODEs
typedef boost::transform_iterator<boost::function<NODE&(DepNodeMapEntry&)>,
typename DepNodeMap::iterator> node_iterator;
/// range over stored NODEs
typedef boost::iterator_range<node_iterator> node_range;
/// iterate over NODE <i>in @c KEY order</i> rather than dependency order
node_range get_node_range()
{
// First take a DepNodeMapEntry and extract a reference to its
// DepNode, then from that extract a reference to its NODE.
return generic_range<node_iterator>(
boost::bind<NODE&>(&DepNode::node,
boost::bind<DepNode&>(&DepNodeMapEntry::second, _1)));
}
/// const iterator over stored NODEs
typedef boost::transform_iterator<boost::function<const NODE&(const DepNodeMapEntry&)>,
typename DepNodeMap::const_iterator> const_node_iterator;
/// const range over stored NODEs
typedef boost::iterator_range<const_node_iterator> const_node_range;
/// iterate over const NODE <i>in @c KEY order</i> rather than dependency order
const_node_range get_node_range() const
{
// First take a DepNodeMapEntry and extract a reference to its
// DepNode, then from that extract a reference to its NODE.
return generic_range<const_node_iterator>(
boost::bind<const NODE&>(&DepNode::node,
boost::bind<const DepNode&>(&DepNodeMapEntry::second, _1)));
}
/// const iterator over stored KEYs
typedef boost::transform_iterator<boost::function<const KEY&(const DepNodeMapEntry&)>,
typename DepNodeMap::const_iterator> const_key_iterator;
/// const range over stored KEYs
typedef boost::iterator_range<const_key_iterator> const_key_range;
// We don't provide a non-const iterator over KEYs because they should be
// immutable, and in fact our underlying std::map won't give us non-const
// references.
/// iterate over const KEY <i>in @c KEY order</i> rather than dependency order
const_key_range get_key_range() const
{
// From a DepNodeMapEntry, extract a reference to its KEY.
return generic_range<const_key_iterator>(
boost::bind<const KEY&>(&DepNodeMapEntry::first, _1));
}
/**
* Find an existing NODE, or return NULL. We decided to avoid providing a
* method analogous to std::map::find(), for a couple of reasons:
*
* * For a find-by-key, getting back an iterator to the (key, value) pair
* is less than useful, since you already have the key in hand.
* * For a failed request, comparing to end() is problematic. First, we
* provide range accessors, so it's more code to get end(). Second, we
* provide a number of different ranges -- quick, to which one's end()
* should we compare the iterator returned by find()?
*
* The returned pointer is solely to allow expressing the not-found
* condition. LLDependencies still owns the found NODE.
*/
const NODE* get(const KEY& key) const
{
typename DepNodeMap::const_iterator found = mNodes.find(key);
if (found != mNodes.end())
{
return &found->second.node;
}
return NULL;
}
/**
* non-const get()
*/
NODE* get(const KEY& key)
{
// Use const implementation, then cast away const-ness of return
return const_cast<NODE*>(const_cast<const self_type*>(this)->get(key));
}
/**
* Remove a node with specified key. This operation is the major reason
* we rebuild the graph on the fly instead of storing it.
*/
bool remove(const KEY& key)
{
typename DepNodeMap::iterator found = mNodes.find(key);
if (found != mNodes.end())
{
mNodes.erase(found);
return true;
}
return false;
}
private:
/// cached list of iterators
typedef std::vector<iterator> iterator_list;
typedef typename iterator_list::iterator iterator_list_iterator;
public:
/**
* The return type of the sort() method needs some explanation. Provide a
* public typedef to facilitate storing the result.
*
* * We will prepare mCache by looking up DepNodeMap iterators.
* * We want to return a range containing iterators that will walk mCache.
* * If we simply stored DepNodeMap iterators and returned
* (mCache.begin(), mCache.end()), dereferencing each iterator would
* obtain a DepNodeMap iterator.
* * We want the caller to loop over @c value_type: pair<KEY, NODE>.
* * This requires two transformations:
* ** mCache must contain @c LLDependencies::iterator so that
* dereferencing each entry will obtain an @c LLDependencies::value_type
* rather than a DepNodeMapEntry.
* ** We must wrap mCache's iterators in boost::indirect_iterator so that
* dereferencing one of our returned iterators will also dereference the
* iterator contained in mCache.
*/
typedef boost::iterator_range<boost::indirect_iterator<iterator_list_iterator> > sorted_range;
/// for convenience in looping over a sorted_range
typedef typename sorted_range::iterator sorted_iterator;
/**
* Once we've loaded in the dependencies of interest, arrange them into an
* order that works -- or throw Cycle exception.
*
* Return an iterator range over (key, node) pairs that traverses them in
* the desired order.
*/
sorted_range sort() const
{
// Changes to mNodes cause us to clear our cache, so empty mCache
// means it's invalid and should be recomputed. However, if mNodes is
// also empty, then an empty mCache represents a valid order, so don't
// bother sorting.
if (mCache.empty() && ! mNodes.empty())
{
// Construct a map of node keys to distinct vertex numbers -- even for
// nodes mentioned only in before/after constraints, that haven't yet
// been explicitly added. Rely on std::map rejecting a second attempt
// to insert the same key. Use the map's size() as the vertex number
// to get a distinct value for each successful insertion.
typedef std::map<KEY, int> VertexMap;
VertexMap vmap;
// Nest each of these loops because !@#$%? MSVC warns us that its
// former broken behavior has finally been fixed -- and our builds
// treat warnings as errors.
{
for (typename DepNodeMap::const_iterator nmi = mNodes.begin(), nmend = mNodes.end();
nmi != nmend; ++nmi)
{
vmap.insert(typename VertexMap::value_type(nmi->first, vmap.size()));
for (typename DepNode::dep_set::const_iterator ai = nmi->second.after.begin(),
aend = nmi->second.after.end();
ai != aend; ++ai)
{
vmap.insert(typename VertexMap::value_type(*ai, vmap.size()));
}
for (typename DepNode::dep_set::const_iterator bi = nmi->second.before.begin(),
bend = nmi->second.before.end();
bi != bend; ++bi)
{
vmap.insert(typename VertexMap::value_type(*bi, vmap.size()));
}
}
}
// Define the edges. For this we must traverse mNodes again, mapping
// all the known key dependencies to integer pairs.
EdgeList edges;
{
for (typename DepNodeMap::const_iterator nmi = mNodes.begin(), nmend = mNodes.end();
nmi != nmend; ++nmi)
{
int thisnode = vmap[nmi->first];
// after dependencies: build edges from the named node to this one
for (typename DepNode::dep_set::const_iterator ai = nmi->second.after.begin(),
aend = nmi->second.after.end();
ai != aend; ++ai)
{
edges.push_back(EdgeList::value_type(vmap[*ai], thisnode));
}
// before dependencies: build edges from this node to the
// named one
for (typename DepNode::dep_set::const_iterator bi = nmi->second.before.begin(),
bend = nmi->second.before.end();
bi != bend; ++bi)
{
edges.push_back(EdgeList::value_type(thisnode, vmap[*bi]));
}
}
}
// Hide the gory details of our topological sort, since they shouldn't
// get reinstantiated for each distinct NODE type.
VertexList sorted(topo_sort(vmap.size(), edges));
// Build the reverse of vmap to look up the key for each vertex
// descriptor. vmap contains exactly one entry for each distinct key,
// and we're certain that the associated int values are distinct
// indexes. The fact that they're not in order is irrelevant.
KeyList vkeys(vmap.size());
for (typename VertexMap::const_iterator vmi = vmap.begin(), vmend = vmap.end();
vmi != vmend; ++vmi)
{
vkeys[vmi->second] = vmi->first;
}
// Walk the sorted output list, building the result into mCache so
// we'll have it next time someone asks.
mCache.clear();
for (VertexList::const_iterator svi = sorted.begin(), svend = sorted.end();
svi != svend; ++svi)
{
// We're certain that vkeys[*svi] exists. However, there might not
// yet be a corresponding entry in mNodes.
self_type* non_const_this(const_cast<self_type*>(this));
typename DepNodeMap::iterator found = non_const_this->mNodes.find(vkeys[*svi]);
if (found != non_const_this->mNodes.end())
{
// Make an iterator of appropriate type.
mCache.push_back(iterator(found, value_extract));
}
}
}
// Whether or not we've just recomputed mCache, it should now contain
// the results we want. Return a range of indirect_iterators over it
// so that dereferencing a returned iterator will dereference the
// iterator stored in mCache and directly reference the (key, node)
// pair.
boost::indirect_iterator<iterator_list_iterator>
begin(mCache.begin()),
end(mCache.end());
return sorted_range(begin, end);
}
using LLDependenciesBase::describe; // unhide virtual std::string describe(bool full=true) const;
/// Override base-class describe() with actual implementation
virtual std::ostream& describe(std::ostream& out, bool full=true) const
{
typename DepNodeMap::const_iterator dmi(mNodes.begin()), dmend(mNodes.end());
if (dmi != dmend)
{
std::string sep;
describe(out, sep, *dmi, full);
while (++dmi != dmend)
{
describe(out, sep, *dmi, full);
}
}
return out;
}
/// describe() helper: report a DepNodeEntry
static std::ostream& describe(std::ostream& out, std::string& sep,
const DepNodeMapEntry& entry, bool full)
{
// If we were asked for a full report, describe every node regardless
// of whether it has dependencies. If we were asked to suppress
// independent nodes, describe this one if either after or before is
// non-empty.
if (full || (! entry.second.after.empty()) || (! entry.second.before.empty()))
{
out << sep;
sep = "\n";
if (! entry.second.after.empty())
{
out << "after ";
describe(out, entry.second.after);
out << " -> ";
}
LLDependencies_describe(out, entry.first);
if (! entry.second.before.empty())
{
out << " -> before ";
describe(out, entry.second.before);
}
}
return out;
}
/// describe() helper: report a dep_set
static std::ostream& describe(std::ostream& out, const typename DepNode::dep_set& keys)
{
out << '(';
typename DepNode::dep_set::const_iterator ki(keys.begin()), kend(keys.end());
if (ki != kend)
{
LLDependencies_describe(out, *ki);
while (++ki != kend)
{
out << ", ";
LLDependencies_describe(out, *ki);
}
}
out << ')';
return out;
}
/// Iterator over the before/after KEYs on which a given NODE depends
typedef typename DepNode::dep_set::const_iterator dep_iterator;
/// range over the before/after KEYs on which a given NODE depends
typedef boost::iterator_range<dep_iterator> dep_range;
/// dependencies access from key
dep_range get_dep_range_from_key(const KEY& key, const dep_selector& selector) const
{
typename DepNodeMap::const_iterator found = mNodes.find(key);
if (found != mNodes.end())
{
return dep_range(selector(found->second));
}
// We want to return an empty range. On some platforms a default-
// constructed range (e.g. dep_range()) does NOT suffice! The client
// is likely to try to iterate from boost::begin(range) to
// boost::end(range); yet these iterators might not be valid. Instead
// return a range over a valid, empty container.
static const typename DepNode::dep_set empty_deps;
return dep_range(empty_deps.begin(), empty_deps.end());
}
/// dependencies access from any one of our key-order iterators
template<typename ITERATOR>
dep_range get_dep_range_from_xform(const ITERATOR& iterator, const dep_selector& selector) const
{
return dep_range(selector(iterator.base()->second));
}
/// dependencies access from sorted_iterator
dep_range get_dep_range_from_sorted(const sorted_iterator& sortiter,
const dep_selector& selector) const
{
// sorted_iterator is a boost::indirect_iterator wrapping an mCache
// iterator, which we can obtain by sortiter.base(). Deferencing that
// gets us an mCache entry, an 'iterator' -- one of our traversal
// iterators -- on which we can use get_dep_range_from_xform().
return get_dep_range_from_xform(*sortiter.base(), selector);
}
/**
* Get a range over the after KEYs stored for the passed KEY or iterator,
* in <i>arbitrary order.</i> If you pass a nonexistent KEY, returns empty
* range -- same as a KEY with no after KEYs. Detect existence of a KEY
* using get() instead.
*/
template<typename KEY_OR_ITER>
dep_range get_after_range(const KEY_OR_ITER& key) const;
/**
* Get a range over the before KEYs stored for the passed KEY or iterator,
* in <i>arbitrary order.</i> If you pass a nonexistent KEY, returns empty
* range -- same as a KEY with no before KEYs. Detect existence of a KEY
* using get() instead.
*/
template<typename KEY_OR_ITER>
dep_range get_before_range(const KEY_OR_ITER& key) const;
private:
DepNodeMap mNodes;
mutable iterator_list mCache;
};
/**
* Functor to get a dep_range from a KEY or iterator -- generic case. If the
* passed value isn't one of our iterator specializations, assume it's
* convertible to the KEY type.
*/
template<typename KEY_ITER>
struct LLDependencies_dep_range_from
{
template<typename KEY, typename NODE, typename SELECTOR>
typename LLDependencies<KEY, NODE>::dep_range
operator()(const LLDependencies<KEY, NODE>& deps,
const KEY_ITER& key,
const SELECTOR& selector)
{
return deps.get_dep_range_from_key(key, selector);
}
};
/// Specialize LLDependencies_dep_range_from for our key-order iterators
template<typename FUNCTION, typename ITERATOR>
struct LLDependencies_dep_range_from< boost::transform_iterator<FUNCTION, ITERATOR> >
{
template<typename KEY, typename NODE, typename SELECTOR>
typename LLDependencies<KEY, NODE>::dep_range
operator()(const LLDependencies<KEY, NODE>& deps,
const boost::transform_iterator<FUNCTION, ITERATOR>& iter,
const SELECTOR& selector)
{
return deps.get_dep_range_from_xform(iter, selector);
}
};
/// Specialize LLDependencies_dep_range_from for sorted_iterator
template<typename BASEITER>
struct LLDependencies_dep_range_from< boost::indirect_iterator<BASEITER> >
{
template<typename KEY, typename NODE, typename SELECTOR>
typename LLDependencies<KEY, NODE>::dep_range
operator()(const LLDependencies<KEY, NODE>& deps,
const boost::indirect_iterator<BASEITER>& iter,
const SELECTOR& selector)
{
return deps.get_dep_range_from_sorted(iter, selector);
}
};
/// generic get_after_range() implementation
template<typename KEY, typename NODE>
template<typename KEY_OR_ITER>
typename LLDependencies<KEY, NODE>::dep_range
LLDependencies<KEY, NODE>::get_after_range(const KEY_OR_ITER& key_iter) const
{
return LLDependencies_dep_range_from<KEY_OR_ITER>()(
*this,
key_iter,
boost::bind<const typename DepNode::dep_set&>(&DepNode::after, _1));
}
/// generic get_before_range() implementation
template<typename KEY, typename NODE>
template<typename KEY_OR_ITER>
typename LLDependencies<KEY, NODE>::dep_range
LLDependencies<KEY, NODE>::get_before_range(const KEY_OR_ITER& key_iter) const
{
return LLDependencies_dep_range_from<KEY_OR_ITER>()(
*this,
key_iter,
boost::bind<const typename DepNode::dep_set&>(&DepNode::before, _1));
}
#endif /* ! defined(LL_LLDEPENDENCIES_H) */

View File

@@ -34,6 +34,8 @@
#include "llevent.h"
using namespace LLOldEvents;
/************************************************
Events
************************************************/

View File

@@ -35,9 +35,12 @@
#define LL_EVENT_H
#include "llsd.h"
#include "llmemory.h"
#include "llpointer.h"
#include "llthread.h"
namespace LLOldEvents
{
class LLEventListener;
class LLEvent;
class LLEventDispatcher;
@@ -194,4 +197,6 @@ public:
LLSD mValue;
};
} // LLOldEvents
#endif // LL_EVENT_H

View File

@@ -0,0 +1,77 @@
/**
* @file lleventapi.cpp
* @author Nat Goodspeed
* @date 2009-11-10
* @brief Implementation for lleventapi.
*
* $LicenseInfo:firstyear=2009&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$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "lleventapi.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "llerror.h"
LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const std::string& field):
lbase(name, field),
ibase(name),
mDesc(desc)
{
}
LLEventAPI::~LLEventAPI()
{
}
LLEventAPI::Response::Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey):
mResp(seed),
mReq(request),
mKey(replyKey)
{}
LLEventAPI::Response::~Response()
{
// When you instantiate a stack Response object, if the original
// request requested a reply, send it when we leave this block, no
// matter how.
sendReply(mResp, mReq, mKey);
}
void LLEventAPI::Response::warn(const std::string& warning)
{
LL_WARNS("LLEventAPI::Response") << warning << LL_ENDL;
mResp["warnings"].append(warning);
}
void LLEventAPI::Response::error(const std::string& error)
{
// Use LL_WARNS rather than LL_ERROR: we don't want the viewer to shut
// down altogether.
LL_WARNS("LLEventAPI::Response") << error << LL_ENDL;
mResp["error"] = error;
}

166
indra/llcommon/lleventapi.h Normal file
View File

@@ -0,0 +1,166 @@
/**
* @file lleventapi.h
* @author Nat Goodspeed
* @date 2009-10-28
* @brief LLEventAPI is the base class for every class that wraps a C++ API
* in an event API
* (see https://wiki.lindenlab.com/wiki/Incremental_Viewer_Automation/Event_API).
*
* $LicenseInfo:firstyear=2009&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$
*/
#if ! defined(LL_LLEVENTAPI_H)
#define LL_LLEVENTAPI_H
#include "lleventdispatcher.h"
#include "llinstancetracker.h"
#include <string>
/**
* LLEventAPI not only provides operation dispatch functionality, inherited
* from LLDispatchListener -- it also gives us event API introspection.
* Deriving from LLInstanceTracker lets us enumerate instances.
*/
class LL_COMMON_API LLEventAPI: public LLDispatchListener,
public LLInstanceTracker<LLEventAPI, std::string>
{
typedef LLDispatchListener lbase;
typedef LLInstanceTracker<LLEventAPI, std::string> ibase;
public:
/**
* @param name LLEventPump name on which this LLEventAPI will listen. This
* also serves as the LLInstanceTracker instance key.
* @param desc Documentation string shown to a client trying to discover
* available event APIs.
* @param field LLSD::Map key used by LLDispatchListener to look up the
* subclass method to invoke [default "op"].
*/
LLEventAPI(const std::string& name, const std::string& desc, const std::string& field="op");
virtual ~LLEventAPI();
/// Get the string name of this LLEventAPI
std::string getName() const { return ibase::getKey(); }
/// Get the documentation string
std::string getDesc() const { return mDesc; }
/**
* Publish only selected add() methods from LLEventDispatcher.
* Every LLEventAPI add() @em must have a description string.
*/
template <typename CALLABLE>
void add(const std::string& name,
const std::string& desc,
CALLABLE callable,
const LLSD& required=LLSD())
{
LLEventDispatcher::add(name, desc, callable, required);
}
/**
* Instantiate a Response object in any LLEventAPI subclass method that
* wants to guarantee a reply (if requested) will be sent on exit from the
* method. The reply will be sent if request.has(@a replyKey), default
* "reply". If specified, the value of request[replyKey] is the name of
* the LLEventPump on which to send the reply. Conventionally you might
* code something like:
*
* @code
* void MyEventAPI::someMethod(const LLSD& request)
* {
* // Send a reply event as long as request.has("reply")
* Response response(LLSD(), request);
* // ...
* // will be sent in reply event
* response["somekey"] = some_data;
* }
* @endcode
*/
class LL_COMMON_API Response
{
public:
/**
* Instantiating a Response object in an LLEventAPI subclass method
* ensures that, if desired, a reply event will be sent.
*
* @a seed is the initial reply LLSD that will be further decorated before
* being sent as the reply
*
* @a request is the incoming request LLSD; we particularly care about
* [replyKey] and ["reqid"]
*
* @a replyKey [default "reply"] is the string name of the LLEventPump
* on which the caller wants a reply. If <tt>(!
* request.has(replyKey))</tt>, no reply will be sent.
*/
Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey="reply");
~Response();
/**
* @code
* if (some condition)
* {
* response.warn("warnings are logged and collected in [\"warnings\"]");
* }
* @endcode
*/
void warn(const std::string& warning);
/**
* @code
* if (some condition isn't met)
* {
* // In a function returning void, you can validly 'return
* // expression' if the expression is itself of type void. But
* // returning is up to you; response.error() has no effect on
* // flow of control.
* return response.error("error message, logged and also sent as [\"error\"]");
* }
* @endcode
*/
void error(const std::string& error);
/**
* set other keys...
*
* @code
* // set any attributes you want to be sent in the reply
* response["info"] = some_value;
* // ...
* response["ok"] = went_well;
* @endcode
*/
LLSD& operator[](const LLSD::String& key) { return mResp[key]; }
/**
* set the response to the given data
*/
void setResponse(LLSD const & response){ mResp = response; }
LLSD mResp, mReq;
LLSD::String mKey;
};
private:
std::string mDesc;
};
#endif /* ! defined(LL_LLEVENTAPI_H) */

View File

@@ -0,0 +1,146 @@
/**
* @file lleventcoro.cpp
* @author Nat Goodspeed
* @date 2009-04-29
* @brief Implementation for lleventcoro.
*
* $LicenseInfo:firstyear=2009&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$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "lleventcoro.h"
// STL headers
#include <map>
// std headers
// external library headers
// other Linden headers
#include "llsdserialize.h"
#include "llerror.h"
#include "llcoros.h"
std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id)
{
// First, if this coroutine was launched by LLCoros::launch(), find that name.
std::string name(LLCoros::instance().getNameByID(self_id));
if (! name.empty())
{
return name;
}
// Apparently this coroutine wasn't launched by LLCoros::launch(). Check
// whether we have a memo for this self_id.
typedef std::map<const void*, std::string> MapType;
static MapType memo;
MapType::const_iterator found = memo.find(self_id);
if (found != memo.end())
{
// this coroutine instance has called us before, reuse same name
return found->second;
}
// this is the first time we've been called for this coroutine instance
name = LLEventPump::inventName("coro");
memo[self_id] = name;
LL_INFOS("LLEventCoro") << "listenerNameForCoroImpl(" << self_id << "): inventing coro name '"
<< name << "'" << LL_ENDL;
return name;
}
void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value)
{
if (rawPath.isUndefined())
{
// no-op case
return;
}
// Arrange to treat rawPath uniformly as an array. If it's not already an
// array, store it as the only entry in one.
LLSD path;
if (rawPath.isArray())
{
path = rawPath;
}
else
{
path.append(rawPath);
}
// Need to indicate a current destination -- but that current destination
// needs to change as we step through the path array. Where normally we'd
// use an LLSD& to capture a subscripted LLSD lvalue, this time we must
// instead use a pointer -- since it must be reassigned.
LLSD* pdest = &dest;
// Now loop through that array
for (LLSD::Integer i = 0; i < path.size(); ++i)
{
if (path[i].isString())
{
// *pdest is an LLSD map
pdest = &((*pdest)[path[i].asString()]);
}
else if (path[i].isInteger())
{
// *pdest is an LLSD array
pdest = &((*pdest)[path[i].asInteger()]);
}
else
{
// What do we do with Real or Array or Map or ...?
// As it's a coder error -- not a user error -- rub the coder's
// face in it so it gets fixed.
LL_ERRS("lleventcoro") << "storeToLLSDPath(" << dest << ", " << rawPath << ", " << value
<< "): path[" << i << "] bad type " << path[i].type() << LL_ENDL;
}
}
// Here *pdest is where we should store value.
*pdest = value;
}
LLSD errorException(const LLEventWithID& result, const std::string& desc)
{
// If the result arrived on the error pump (pump 1), instead of
// returning it, deliver it via exception.
if (result.second)
{
throw LLErrorEvent(desc, result.first);
}
// That way, our caller knows a simple return must be from the reply
// pump (pump 0).
return result.first;
}
LLSD errorLog(const LLEventWithID& result, const std::string& desc)
{
// If the result arrived on the error pump (pump 1), log it as a fatal
// error.
if (result.second)
{
LL_ERRS("errorLog") << desc << ":" << std::endl;
LLSDSerialize::toPrettyXML(result.first, LL_CONT);
LL_CONT << LL_ENDL;
}
// A simple return must therefore be from the reply pump (pump 0).
return result.first;
}

View File

@@ -0,0 +1,569 @@
/**
* @file lleventcoro.h
* @author Nat Goodspeed
* @date 2009-04-29
* @brief Utilities to interface between coroutines and events.
*
* $LicenseInfo:firstyear=2009&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$
*/
#if ! defined(LL_LLEVENTCORO_H)
#define LL_LLEVENTCORO_H
#include <boost/coroutine/coroutine.hpp>
#include <boost/coroutine/future.hpp>
#include <boost/optional.hpp>
#include <string>
#include <stdexcept>
#include "llevents.h"
#include "llerror.h"
/**
* Like LLListenerOrPumpName, this is a class intended for parameter lists:
* accept a <tt>const LLEventPumpOrPumpName&</tt> and you can accept either an
* <tt>LLEventPump&</tt> or its string name. For a single parameter that could
* be either, it's not hard to overload the function -- but as soon as you
* want to accept two such parameters, this is cheaper than four overloads.
*/
class LLEventPumpOrPumpName
{
public:
/// Pass an actual LLEventPump&
LLEventPumpOrPumpName(LLEventPump& pump):
mPump(pump)
{}
/// Pass the string name of an LLEventPump
LLEventPumpOrPumpName(const std::string& pumpname):
mPump(LLEventPumps::instance().obtain(pumpname))
{}
/// Pass string constant name of an LLEventPump. This override must be
/// explicit, since otherwise passing <tt>const char*</tt> to a function
/// accepting <tt>const LLEventPumpOrPumpName&</tt> would require two
/// different implicit conversions: <tt>const char*</tt> -> <tt>const
/// std::string&</tt> -> <tt>const LLEventPumpOrPumpName&</tt>.
LLEventPumpOrPumpName(const char* pumpname):
mPump(LLEventPumps::instance().obtain(pumpname))
{}
/// Unspecified: "I choose not to identify an LLEventPump."
LLEventPumpOrPumpName() {}
operator LLEventPump& () const { return *mPump; }
LLEventPump& getPump() const { return *mPump; }
operator bool() const { return mPump; }
bool operator!() const { return ! mPump; }
private:
boost::optional<LLEventPump&> mPump;
};
/// This is an adapter for a signature like void LISTENER(const LLSD&), which
/// isn't a valid LLEventPump listener: such listeners should return bool.
template <typename LISTENER>
class LLVoidListener
{
public:
LLVoidListener(const LISTENER& listener):
mListener(listener)
{}
bool operator()(const LLSD& event)
{
mListener(event);
// don't swallow the event, let other listeners see it
return false;
}
private:
LISTENER mListener;
};
/// LLVoidListener helper function to infer the type of the LISTENER
template <typename LISTENER>
LLVoidListener<LISTENER> voidlistener(const LISTENER& listener)
{
return LLVoidListener<LISTENER>(listener);
}
namespace LLEventDetail
{
/**
* waitForEventOn() permits a coroutine to temporarily listen on an
* LLEventPump any number of times. We don't really want to have to ask
* the caller to label each such call with a distinct string; the whole
* point of waitForEventOn() is to present a nice sequential interface to
* the underlying LLEventPump-with-named-listeners machinery. So we'll use
* LLEventPump::inventName() to generate a distinct name for each
* temporary listener. On the other hand, because a given coroutine might
* call waitForEventOn() any number of times, we don't really want to
* consume an arbitrary number of generated inventName()s: that namespace,
* though large, is nonetheless finite. So we memoize an invented name for
* each distinct coroutine instance (each different 'self' object). We
* can't know the type of 'self', because it depends on the coroutine
* body's signature. So we cast its address to void*, looking for distinct
* pointer values. Yes, that means that an early coroutine could cache a
* value here, then be destroyed, only to be supplanted by a later
* coroutine (of the same or different type), and we'll end up
* "recognizing" the second one and reusing the listener name -- but
* that's okay, since it won't collide with any listener name used by the
* earlier coroutine since that earlier coroutine no longer exists.
*/
template <typename COROUTINE_SELF>
std::string listenerNameForCoro(COROUTINE_SELF& self)
{
return listenerNameForCoroImpl(self.get_id());
}
/// Implementation for listenerNameForCoro()
LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id);
/**
* Implement behavior described for postAndWait()'s @a replyPumpNamePath
* parameter:
*
* * If <tt>path.isUndefined()</tt>, do nothing.
* * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value
* into <tt>dest[path.asString()]</tt>.
* * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a
* value into <tt>dest[path.asInteger()]</tt>.
* * If <tt>path.isArray()</tt>, iteratively apply the rules above to step
* down through the structure of @a dest. The last array entry in @a
* path specifies the entry in the lowest-level structure in @a dest
* into which to store @a value.
*
* @note
* In the degenerate case in which @a path is an empty array, @a dest will
* @em become @a value rather than @em containing it.
*/
LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value);
} // namespace LLEventDetail
/**
* Post specified LLSD event on the specified LLEventPump, then wait for a
* response on specified other LLEventPump. This is more than mere
* convenience: the difference between this function and the sequence
* @code
* requestPump.post(myEvent);
* LLSD reply = waitForEventOn(self, replyPump);
* @endcode
* is that the sequence above fails if the reply is posted immediately on
* @a replyPump, that is, before <tt>requestPump.post()</tt> returns. In the
* sequence above, the running coroutine isn't even listening on @a replyPump
* until <tt>requestPump.post()</tt> returns and @c waitForEventOn() is
* entered. Therefore, the coroutine completely misses an immediate reply
* event, making it wait indefinitely.
*
* By contrast, postAndWait() listens on the @a replyPump @em before posting
* the specified LLSD event on the specified @a requestPump.
*
* @param self The @c self object passed into a coroutine
* @param event LLSD data to be posted on @a requestPump
* @param requestPump an LLEventPump on which to post @a event. Pass either
* the LLEventPump& or its string name. However, if you pass a
* default-constructed @c LLEventPumpOrPumpName, we skip the post() call.
* @param replyPump an LLEventPump on which postAndWait() will listen for a
* reply. Pass either the LLEventPump& or its string name. The calling
* coroutine will wait until that reply arrives. (If you're concerned about a
* reply that might not arrive, please see also LLEventTimeout.)
* @param replyPumpNamePath specifies the location within @a event in which to
* store <tt>replyPump.getName()</tt>. This is a strictly optional convenience
* feature; obviously you can store the name in @a event "by hand" if desired.
* @a replyPumpNamePath can be specified in any of four forms:
* * @c isUndefined() (default-constructed LLSD object): do nothing. This is
* the default behavior if you omit @a replyPumpNamePath.
* * @c isInteger(): @a event is an array. Store <tt>replyPump.getName()</tt>
* in <tt>event[replyPumpNamePath.asInteger()]</tt>.
* * @c isString(): @a event is a map. Store <tt>replyPump.getName()</tt> in
* <tt>event[replyPumpNamePath.asString()]</tt>.
* * @c isArray(): @a event has several levels of structure, e.g. map of
* maps, array of arrays, array of maps, map of arrays, ... Store
* <tt>replyPump.getName()</tt> in
* <tt>event[replyPumpNamePath[0]][replyPumpNamePath[1]]...</tt> In other
* words, examine each array entry in @a replyPumpNamePath in turn. If it's an
* <tt>LLSD::String</tt>, the current level of @a event is a map; step down to
* that map entry. If it's an <tt>LLSD::Integer</tt>, the current level of @a
* event is an array; step down to that array entry. The last array entry in
* @a replyPumpNamePath specifies the entry in the lowest-level structure in
* @a event into which to store <tt>replyPump.getName()</tt>.
*/
template <typename SELF>
LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD())
{
// declare the future
boost::coroutines::future<LLSD> future(self);
// make a callback that will assign a value to the future, and listen on
// the specified LLEventPump with that callback
std::string listenerName(LLEventDetail::listenerNameForCoro(self));
LLTempBoundListener connection(
replyPump.getPump().listen(listenerName,
voidlistener(boost::coroutines::make_callback(future))));
// skip the "post" part if requestPump is default-constructed
if (requestPump)
{
// If replyPumpNamePath is non-empty, store the replyPump name in the
// request event.
LLSD modevent(event);
LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName());
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
<< " posting to " << requestPump.getPump().getName()
<< LL_ENDL;
// *NOTE:Mani - Removed because modevent could contain user's hashed passwd.
// << ": " << modevent << LL_ENDL;
requestPump.getPump().post(modevent);
}
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
<< " about to wait on LLEventPump " << replyPump.getPump().getName()
<< LL_ENDL;
// trying to dereference ("resolve") the future makes us wait for it
LLSD value(*future);
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
<< " resuming with " << value << LL_ENDL;
// returning should disconnect the connection
return value;
}
/// Wait for the next event on the specified LLEventPump. Pass either the
/// LLEventPump& or its string name.
template <typename SELF>
LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump)
{
// This is now a convenience wrapper for postAndWait().
return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump);
}
/// return type for two-pump variant of waitForEventOn()
typedef std::pair<LLSD, int> LLEventWithID;
namespace LLEventDetail
{
/**
* This helper is specifically for the two-pump version of waitForEventOn().
* We use a single future object, but we want to listen on two pumps with it.
* Since we must still adapt from (the callable constructed by)
* boost::coroutines::make_callback() (void return) to provide an event
* listener (bool return), we've adapted LLVoidListener for the purpose. The
* basic idea is that we construct a distinct instance of WaitForEventOnHelper
* -- binding different instance data -- for each of the pumps. Then, when a
* pump delivers an LLSD value to either WaitForEventOnHelper, it can combine
* that LLSD with its discriminator to feed the future object.
*/
template <typename LISTENER>
class WaitForEventOnHelper
{
public:
WaitForEventOnHelper(const LISTENER& listener, int discriminator):
mListener(listener),
mDiscrim(discriminator)
{}
// this signature is required for an LLEventPump listener
bool operator()(const LLSD& event)
{
// our future object is defined to accept LLEventWithID
mListener(LLEventWithID(event, mDiscrim));
// don't swallow the event, let other listeners see it
return false;
}
private:
LISTENER mListener;
const int mDiscrim;
};
/// WaitForEventOnHelper type-inference helper
template <typename LISTENER>
WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator)
{
return WaitForEventOnHelper<LISTENER>(listener, discriminator);
}
} // namespace LLEventDetail
/**
* This function waits for a reply on either of two specified LLEventPumps.
* Otherwise, it closely resembles postAndWait(); please see the documentation
* for that function for detailed parameter info.
*
* While we could have implemented the single-pump variant in terms of this
* one, there's enough added complexity here to make it worthwhile to give the
* single-pump variant its own straightforward implementation. Conversely,
* though we could use preprocessor logic to generate n-pump overloads up to
* BOOST_COROUTINE_WAIT_MAX, we don't foresee a use case. This two-pump
* overload exists because certain event APIs are defined in terms of a reply
* LLEventPump and an error LLEventPump.
*
* The LLEventWithID return value provides not only the received event, but
* the index of the pump on which it arrived (0 or 1).
*
* @note
* I'd have preferred to overload the name postAndWait() for both signatures.
* But consider the following ambiguous call:
* @code
* postAndWait(self, LLSD(), requestPump, replyPump, "someString");
* @endcode
* "someString" could be converted to either LLSD (@a replyPumpNamePath for
* the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump
* function).
*
* It seems less burdensome to write postAndWait2() than to write either
* LLSD("someString") or LLEventOrPumpName("someString").
*/
template <typename SELF>
LLEventWithID postAndWait2(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLEventPumpOrPumpName& replyPump0,
const LLEventPumpOrPumpName& replyPump1,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
// declare the future
boost::coroutines::future<LLEventWithID> future(self);
// either callback will assign a value to this future; listen on
// each specified LLEventPump with a callback
std::string name(LLEventDetail::listenerNameForCoro(self));
LLTempBoundListener connection0(
replyPump0.getPump().listen(name + "a",
LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 0)));
LLTempBoundListener connection1(
replyPump1.getPump().listen(name + "b",
LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 1)));
// skip the "post" part if requestPump is default-constructed
if (requestPump)
{
// If either replyPumpNamePath is non-empty, store the corresponding
// replyPump name in the request event.
LLSD modevent(event);
LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath,
replyPump0.getPump().getName());
LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath,
replyPump1.getPump().getName());
LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
<< " posting to " << requestPump.getPump().getName()
<< ": " << modevent << LL_ENDL;
requestPump.getPump().post(modevent);
}
LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
<< " about to wait on LLEventPumps " << replyPump0.getPump().getName()
<< ", " << replyPump1.getPump().getName() << LL_ENDL;
// trying to dereference ("resolve") the future makes us wait for it
LLEventWithID value(*future);
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name
<< " resuming with (" << value.first << ", " << value.second << ")"
<< LL_ENDL;
// returning should disconnect both connections
return value;
}
/**
* Wait for the next event on either of two specified LLEventPumps.
*/
template <typename SELF>
LLEventWithID
waitForEventOn(SELF& self,
const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1)
{
// This is now a convenience wrapper for postAndWait2().
return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1);
}
/**
* Helper for the two-pump variant of waitForEventOn(), e.g.:
*
* @code
* LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump),
* "error response from login.cgi");
* @endcode
*
* Examines an LLEventWithID, assuming that the second pump (pump 1) is
* listening for an error indication. If the incoming data arrived on pump 1,
* throw an LLErrorEvent exception. If the incoming data arrived on pump 0,
* just return it. Since a normal return can only be from pump 0, we no longer
* need the LLEventWithID's discriminator int; we can just return the LLSD.
*
* @note I'm not worried about introducing the (fairly generic) name
* errorException() into global namespace, because how many other overloads of
* the same name are going to accept an LLEventWithID parameter?
*/
LLSD errorException(const LLEventWithID& result, const std::string& desc);
/**
* Exception thrown by errorException(). We don't call this LLEventError
* because it's not an error in event processing: rather, this exception
* announces an event that bears error information (for some other API).
*/
class LL_COMMON_API LLErrorEvent: public std::runtime_error
{
public:
LLErrorEvent(const std::string& what, const LLSD& data):
std::runtime_error(what),
mData(data)
{}
virtual ~LLErrorEvent() throw() {}
LLSD getData() const { return mData; }
private:
LLSD mData;
};
/**
* Like errorException(), save that this trips a fatal error using LL_ERRS
* rather than throwing an exception.
*/
LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc);
/**
* Certain event APIs require the name of an LLEventPump on which they should
* post results. While it works to invent a distinct name and let
* LLEventPumps::obtain() instantiate the LLEventPump as a "named singleton,"
* in a certain sense it's more robust to instantiate a local LLEventPump and
* provide its name instead. This class packages the following idiom:
*
* 1. Instantiate a local LLCoroEventPump, with an optional name prefix.
* 2. Provide its actual name to the event API in question as the name of the
* reply LLEventPump.
* 3. Initiate the request to the event API.
* 4. Call your LLEventTempStream's wait() method to wait for the reply.
* 5. Let the LLCoroEventPump go out of scope.
*/
class LL_COMMON_API LLCoroEventPump
{
public:
LLCoroEventPump(const std::string& name="coro"):
mPump(name, true) // allow tweaking the pump instance name
{}
/// It's typical to request the LLEventPump name to direct an event API to
/// send its response to this pump.
std::string getName() const { return mPump.getName(); }
/// Less typically, we'd request the pump itself for some reason.
LLEventPump& getPump() { return mPump; }
/**
* Wait for an event on this LLEventPump.
*
* @note
* The other major usage pattern we considered was to bind @c self at
* LLCoroEventPump construction time, which would avoid passing the
* parameter to each wait() call. But if we were going to bind @c self as
* a class member, we'd need to specify a class template parameter
* indicating its type. The big advantage of passing it to the wait() call
* is that the type can be implicit.
*/
template <typename SELF>
LLSD wait(SELF& self)
{
return waitForEventOn(self, mPump);
}
template <typename SELF>
LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPumpNamePath=LLSD())
{
return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath);
}
private:
LLEventStream mPump;
};
/**
* Other event APIs require the names of two different LLEventPumps: one for
* success response, the other for error response. Extend LLCoroEventPump
* for the two-pump use case.
*/
class LL_COMMON_API LLCoroEventPumps
{
public:
LLCoroEventPumps(const std::string& name="coro",
const std::string& suff0="Reply",
const std::string& suff1="Error"):
mPump0(name + suff0, true), // allow tweaking the pump instance name
mPump1(name + suff1, true)
{}
/// request pump 0's name
std::string getName0() const { return mPump0.getName(); }
/// request pump 1's name
std::string getName1() const { return mPump1.getName(); }
/// request both names
std::pair<std::string, std::string> getNames() const
{
return std::pair<std::string, std::string>(mPump0.getName(), mPump1.getName());
}
/// request pump 0
LLEventPump& getPump0() { return mPump0; }
/// request pump 1
LLEventPump& getPump1() { return mPump1; }
/// waitForEventOn(self, either of our two LLEventPumps)
template <typename SELF>
LLEventWithID wait(SELF& self)
{
return waitForEventOn(self, mPump0, mPump1);
}
/// errorException(wait(self))
template <typename SELF>
LLSD waitWithException(SELF& self)
{
return errorException(wait(self), std::string("Error event on ") + getName1());
}
/// errorLog(wait(self))
template <typename SELF>
LLSD waitWithLog(SELF& self)
{
return errorLog(wait(self), std::string("Error event on ") + getName1());
}
template <typename SELF>
LLEventWithID postAndWait(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
return postAndWait2(self, event, requestPump, mPump0, mPump1,
replyPump0NamePath, replyPump1NamePath);
}
template <typename SELF>
LLSD postAndWaitWithException(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
return errorException(postAndWait(self, event, requestPump,
replyPump0NamePath, replyPump1NamePath),
std::string("Error event on ") + getName1());
}
template <typename SELF>
LLSD postAndWaitWithLog(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
return errorLog(postAndWait(self, event, requestPump,
replyPump0NamePath, replyPump1NamePath),
std::string("Error event on ") + getName1());
}
private:
LLEventStream mPump0, mPump1;
};
#endif /* ! defined(LL_LLEVENTCORO_H) */

View File

@@ -0,0 +1,672 @@
/**
* @file lleventdispatcher.cpp
* @author Nat Goodspeed
* @date 2009-06-18
* @brief Implementation for lleventdispatcher.
*
* $LicenseInfo:firstyear=2009&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$
*/
#if LL_WINDOWS
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
#endif
// Precompiled header
#include "linden_common.h"
// associated header
#include "lleventdispatcher.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#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),
mKey(key)
{
}
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(
new LLSDDispatchEntry(desc, callable, required))));
}
void LLEventDispatcher::addFail(const std::string& name, const std::string& classname) const
{
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ")::add(" << name
<< "): " << classname << " is not a subclass "
<< "of LLEventDispatcher" << LL_ENDL;
}
/// Unregister a callable
bool LLEventDispatcher::remove(const std::string& name)
{
DispatchMap::iterator found = mDispatch.find(name);
if (found == mDispatch.end())
{
return false;
}
mDispatch.erase(found);
return true;
}
/// Call a registered callable with an explicitly-specified name. If no
/// such callable exists, die with LL_ERRS.
void LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const
{
if (! try_call(name, event))
{
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): '" << name
<< "' not found" << LL_ENDL;
}
}
/// 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.
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 (! try_call(name, event))
{
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey
<< " value '" << name << "'" << LL_ENDL;
}
}
bool LLEventDispatcher::try_call(const LLSD& event) const
{
return try_call(event[mKey], event);
}
bool LLEventDispatcher::try_call(const std::string& name, const LLSD& event) const
{
DispatchMap::const_iterator found = mDispatch.find(name);
if (found == mDispatch.end())
{
return false;
}
// 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
{
DispatchMap::const_iterator found = mDispatch.find(name);
if (found == mDispatch.end())
{
return LLSD();
}
LLSD meta;
meta["name"] = name;
meta["desc"] = found->second->mDesc;
return found->second->addMetadata(meta);
}
LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key):
LLEventDispatcher(pumpname, key),
mPump(pumpname, true), // allow tweaking for uniqueness
mBoundListener(mPump.listen("self", boost::bind(&LLDispatchListener::process, this, _1)))
{
}
bool LLDispatchListener::process(const LLSD& event)
{
(*this)(event);
return false;
}
LLEventDispatcher::DispatchEntry::DispatchEntry(const std::string& desc):
mDesc(desc)
{}

View File

@@ -0,0 +1,541 @@
/**
* @file lleventdispatcher.h
* @author Nat Goodspeed
* @date 2009-06-18
* @brief Central mechanism for dispatching events by string name. This is
* useful when you have a single LLEventPump listener on which you can
* request different operations, vs. instantiating a different
* LLEventPump for each such operation.
*
* $LicenseInfo:firstyear=2009&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$
*
* 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 <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;
/**
* Given an LLSD map, examine a string-valued key and call a corresponding
* callable. This class is designed to be contained by an LLEventPump
* listener class that will register some of its own methods, though any
* callable can be used.
*/
class LL_COMMON_API LLEventDispatcher
{
public:
LLEventDispatcher(const std::string& desc, const std::string& key);
virtual ~LLEventDispatcher();
/// @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 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,
const std::string& desc,
const Callable& callable,
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 (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,
const std::string& desc,
void (CLASS::*method)(const LLSD&),
const LLSD& required=LLSD())
{
addMethod<CLASS>(name, desc, method, required);
}
/// 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,
void (CLASS::*method)(const LLSD&) const,
const LLSD& required=LLSD())
{
addMethod<CLASS>(name, desc, method, 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);
/// Call a registered callable with an explicitly-specified name. 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 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;
private:
struct DispatchEntry
{
DispatchEntry(const std::string& desc);
virtual ~DispatchEntry() {} // suppress MSVC warning, sigh
std::string mDesc;
virtual void call(const std::string& desc, const LLSD& event) const = 0;
virtual LLSD addMetadata(LLSD) const = 0;
};
// 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,
/// therefore our public interface doesn't expose DispatchMap iterators,
/// or DispatchMap itself, or DispatchEntry. Instead we explicitly
/// transform each DispatchMap item to NameDesc on dereferencing.
typedef boost::transform_iterator<NameDesc(*)(const DispatchMap::value_type&), DispatchMap::const_iterator> const_iterator;
const_iterator begin() const
{
return boost::make_transform_iterator(mDispatch.begin(), makeNameDesc);
}
const_iterator end() const
{
return boost::make_transform_iterator(mDispatch.end(), makeNameDesc);
}
//@}
/// Get information about a specific Callable
LLSD getMetadata(const std::string& name) const;
/// Retrieve the LLSD key we use for one-arg <tt>operator()</tt> method
std::string getDispatchKey() const { return mKey; }
private:
template <class CLASS, typename METHOD>
void addMethod(const std::string& name, const std::string& desc,
const METHOD& method, const LLSD& required)
{
CLASS* downcast = dynamic_cast<CLASS*>(this);
if (! downcast)
{
addFail(name, typeid(CLASS).name());
}
else
{
add(name, desc, boost::bind(method, downcast, _1), required);
}
}
void addFail(const std::string& name, const std::string& classname) const;
std::string mDesc, mKey;
DispatchMap mDispatch;
static NameDesc makeNameDesc(const DispatchMap::value_type& item)
{
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
* LLEventPump name and dispatch key, and add() its methods. Incoming events
* will automatically be dispatched.
*/
class LL_COMMON_API LLDispatchListener: public LLEventDispatcher
{
public:
LLDispatchListener(const std::string& pumpname, const std::string& key);
std::string getPumpName() const { return mPump.getName(); }
private:
bool process(const LLSD& event);
LLEventStream mPump;
LLTempBoundListener mBoundListener;
};
#endif /* ! defined(LL_LLEVENTDISPATCHER_H) */

View File

@@ -0,0 +1,166 @@
/**
* @file lleventfilter.cpp
* @author Nat Goodspeed
* @date 2009-03-05
* @brief Implementation for lleventfilter.
*
* $LicenseInfo:firstyear=2009&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$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "lleventfilter.h"
// STL headers
// std headers
// external library headers
#include <boost/bind.hpp>
// other Linden headers
#include "llerror.h" // LL_ERRS
#include "llsdutil.h" // llsd_matches()
LLEventFilter::LLEventFilter(LLEventPump& source, const std::string& name, bool tweak):
LLEventStream(name, tweak)
{
source.listen(getName(), boost::bind(&LLEventFilter::post, this, _1));
}
LLEventMatching::LLEventMatching(const LLSD& pattern):
LLEventFilter("matching"),
mPattern(pattern)
{
}
LLEventMatching::LLEventMatching(LLEventPump& source, const LLSD& pattern):
LLEventFilter(source, "matching"),
mPattern(pattern)
{
}
bool LLEventMatching::post(const LLSD& event)
{
if (! llsd_matches(mPattern, event).empty())
return false;
return LLEventStream::post(event);
}
LLEventTimeoutBase::LLEventTimeoutBase():
LLEventFilter("timeout")
{
}
LLEventTimeoutBase::LLEventTimeoutBase(LLEventPump& source):
LLEventFilter(source, "timeout")
{
}
void LLEventTimeoutBase::actionAfter(F32 seconds, const Action& action)
{
setCountdown(seconds);
mAction = action;
if (! mMainloop.connected())
{
LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
mMainloop = mainloop.listen(getName(), boost::bind(&LLEventTimeoutBase::tick, this, _1));
}
}
class ErrorAfter
{
public:
ErrorAfter(const std::string& message): mMessage(message) {}
void operator()()
{
LL_ERRS("LLEventTimeout") << mMessage << LL_ENDL;
}
private:
std::string mMessage;
};
void LLEventTimeoutBase::errorAfter(F32 seconds, const std::string& message)
{
actionAfter(seconds, ErrorAfter(message));
}
class EventAfter
{
public:
EventAfter(LLEventPump& pump, const LLSD& event):
mPump(pump),
mEvent(event)
{}
void operator()()
{
mPump.post(mEvent);
}
private:
LLEventPump& mPump;
LLSD mEvent;
};
void LLEventTimeoutBase::eventAfter(F32 seconds, const LLSD& event)
{
actionAfter(seconds, EventAfter(*this, event));
}
bool LLEventTimeoutBase::post(const LLSD& event)
{
cancel();
return LLEventStream::post(event);
}
void LLEventTimeoutBase::cancel()
{
mMainloop.disconnect();
}
bool LLEventTimeoutBase::tick(const LLSD&)
{
if (countdownElapsed())
{
cancel();
mAction();
}
return false; // show event to other listeners
}
LLEventTimeout::LLEventTimeout() {}
LLEventTimeout::LLEventTimeout(LLEventPump& source):
LLEventTimeoutBase(source)
{
}
void LLEventTimeout::setCountdown(F32 seconds)
{
mTimer.setTimerExpirySec(seconds);
}
bool LLEventTimeout::countdownElapsed() const
{
return mTimer.hasExpired();
}

View File

@@ -0,0 +1,203 @@
/**
* @file lleventfilter.h
* @author Nat Goodspeed
* @date 2009-03-05
* @brief Define LLEventFilter: LLEventStream subclass with conditions
*
* $LicenseInfo:firstyear=2009&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$
*/
#if ! defined(LL_LLEVENTFILTER_H)
#define LL_LLEVENTFILTER_H
#include "llevents.h"
#include "stdtypes.h"
#include "lltimer.h"
#include <boost/function.hpp>
/**
* Generic base class
*/
class LL_COMMON_API LLEventFilter: public LLEventStream
{
public:
/// construct a standalone LLEventFilter
LLEventFilter(const std::string& name="filter", bool tweak=true):
LLEventStream(name, tweak)
{}
/// construct LLEventFilter and connect it to the specified LLEventPump
LLEventFilter(LLEventPump& source, const std::string& name="filter", bool tweak=true);
/// Post an event to all listeners
virtual bool post(const LLSD& event) = 0;
};
/**
* Pass through only events matching a specified pattern
*/
class LLEventMatching: public LLEventFilter
{
public:
/// Pass an LLSD map with keys and values the incoming event must match
LLEventMatching(const LLSD& pattern);
/// instantiate and connect
LLEventMatching(LLEventPump& source, const LLSD& pattern);
/// Only pass through events matching the pattern
virtual bool post(const LLSD& event);
private:
LLSD mPattern;
};
/**
* Wait for an event to be posted. If no such event arrives within a specified
* time, take a specified action. See LLEventTimeout for production
* implementation.
*
* @NOTE This is an abstract base class so that, for testing, we can use an
* alternate "timer" that doesn't actually consume real time.
*/
class LL_COMMON_API LLEventTimeoutBase: public LLEventFilter
{
public:
/// construct standalone
LLEventTimeoutBase();
/// construct and connect
LLEventTimeoutBase(LLEventPump& source);
/// Callable, can be constructed with boost::bind()
typedef boost::function<void()> Action;
/**
* Start countdown timer for the specified number of @a seconds. Forward
* all events. If any event arrives before timer expires, cancel timer. If
* no event arrives before timer expires, take specified @a action.
*
* This is a one-shot timer. Once it has either expired or been canceled,
* it is inert until another call to actionAfter().
*
* Calling actionAfter() while an existing timer is running cheaply
* replaces that original timer. Thus, a valid use case is to detect
* idleness of some event source by calling actionAfter() on each new
* event. A rapid sequence of events will keep the timer from expiring;
* the first gap in events longer than the specified timer will fire the
* specified Action.
*
* Any post() call cancels the timer. To be satisfied with only a
* particular event, chain on an LLEventMatching that only passes such
* events:
*
* @code
* event ultimate
* source ---> LLEventMatching ---> LLEventTimeout ---> listener
* @endcode
*
* @NOTE
* The implementation relies on frequent events on the LLEventPump named
* "mainloop".
*/
void actionAfter(F32 seconds, const Action& action);
/**
* Like actionAfter(), but where the desired Action is LL_ERRS
* termination. Pass the timeout time and the desired LL_ERRS @a message.
*
* This method is useful when, for instance, some async API guarantees an
* event, whether success or failure, within a stated time window.
* Instantiate an LLEventTimeout listening to that API and call
* errorAfter() on each async request with a timeout comfortably longer
* than the API's time guarantee (much longer than the anticipated
* "mainloop" granularity).
*
* Then if the async API breaks its promise, the program terminates with
* the specified LL_ERRS @a message. The client of the async API can
* therefore assume the guarantee is upheld.
*
* @NOTE
* errorAfter() is implemented in terms of actionAfter(), so all remarks
* about calling actionAfter() also apply to errorAfter().
*/
void errorAfter(F32 seconds, const std::string& message);
/**
* Like actionAfter(), but where the desired Action is a particular event
* for all listeners. Pass the timeout time and the desired @a event data.
*
* Suppose the timeout should only be satisfied by a particular event, but
* the ultimate listener must see all other incoming events as well, plus
* the timeout @a event if any:
*
* @code
* some LLEventMatching LLEventMatching
* event ---> for particular ---> LLEventTimeout ---> for timeout
* source event event \
* \ \ ultimate
* `-----------------------------------------------------> listener
* @endcode
*
* Since a given listener can listen on more than one LLEventPump, we can
* set things up so it sees the set union of events from LLEventTimeout
* and the original event source. However, as LLEventTimeout passes
* through all incoming events, the "particular event" that satisfies the
* left LLEventMatching would reach the ultimate listener twice. So we add
* an LLEventMatching that only passes timeout events.
*
* @NOTE
* eventAfter() is implemented in terms of actionAfter(), so all remarks
* about calling actionAfter() also apply to eventAfter().
*/
void eventAfter(F32 seconds, const LLSD& event);
/// Pass event through, canceling the countdown timer
virtual bool post(const LLSD& event);
/// Cancel timer without event
void cancel();
protected:
virtual void setCountdown(F32 seconds) = 0;
virtual bool countdownElapsed() const = 0;
private:
bool tick(const LLSD&);
LLBoundListener mMainloop;
Action mAction;
};
/// Production implementation of LLEventTimoutBase
class LL_COMMON_API LLEventTimeout: public LLEventTimeoutBase
{
public:
LLEventTimeout();
LLEventTimeout(LLEventPump& source);
protected:
virtual void setCountdown(F32 seconds);
virtual bool countdownElapsed() const;
private:
LLTimer mTimer;
};
#endif /* ! defined(LL_LLEVENTFILTER_H) */

628
indra/llcommon/llevents.cpp Normal file
View File

@@ -0,0 +1,628 @@
/**
* @file llevents.cpp
* @author Nat Goodspeed
* @date 2008-09-12
* @brief Implementation for llevents.
*
* $LicenseInfo:firstyear=2008&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$
*/
// Precompiled header
#include "linden_common.h"
#if LL_WINDOWS
#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
#endif
// associated header
#include "llevents.h"
// STL headers
#include <set>
#include <sstream>
#include <algorithm>
// std headers
#include <typeinfo>
#include <cassert>
#include <cmath>
#include <cctype>
// external library headers
#include <boost/range/iterator_range.hpp>
#if LL_WINDOWS
#pragma warning (push)
#pragma warning (disable : 4701) // compiler thinks might use uninitialized var, but no
#endif
#include <boost/lexical_cast.hpp>
#if LL_WINDOWS
#pragma warning (pop)
#endif
// other Linden headers
#include "stringize.h"
#include "llerror.h"
#include "llsdutil.h"
#if LL_MSVC
#pragma warning (disable : 4702)
#endif
/*****************************************************************************
* queue_names: specify LLEventPump names that should be instantiated as
* LLEventQueue
*****************************************************************************/
/**
* At present, we recognize particular requested LLEventPump names as needing
* LLEventQueues. Later on we'll migrate this information to an external
* configuration file.
*/
const char* queue_names[] =
{
"placeholder - replace with first real name string"
};
/*****************************************************************************
* If there's a "mainloop" pump, listen on that to flush all LLEventQueues
*****************************************************************************/
struct RegisterFlush : public LLEventTrackable
{
RegisterFlush():
pumps(LLEventPumps::instance())
{
pumps.obtain("mainloop").listen("flushLLEventQueues", boost::bind(&RegisterFlush::flush, this, _1));
}
bool flush(const LLSD&)
{
pumps.flush();
return false;
}
~RegisterFlush()
{
// LLEventTrackable handles stopListening for us.
}
LLEventPumps& pumps;
};
static RegisterFlush registerFlush;
/*****************************************************************************
* LLEventPumps
*****************************************************************************/
LLEventPumps::LLEventPumps():
// Until we migrate this information to an external config file,
// initialize mQueueNames from the static queue_names array.
mQueueNames(boost::begin(queue_names), boost::end(queue_names))
{
}
LLEventPump& LLEventPumps::obtain(const std::string& name)
{
PumpMap::iterator found = mPumpMap.find(name);
if (found != mPumpMap.end())
{
// Here we already have an LLEventPump instance with the requested
// name.
return *found->second;
}
// Here we must instantiate an LLEventPump subclass.
LLEventPump* newInstance;
// Should this name be an LLEventQueue?
PumpNames::const_iterator nfound = mQueueNames.find(name);
if (nfound != mQueueNames.end())
newInstance = new LLEventQueue(name);
else
newInstance = new LLEventStream(name);
// LLEventPump's constructor implicitly registers each new instance in
// mPumpMap. But remember that we instantiated it (in mOurPumps) so we'll
// delete it later.
mOurPumps.insert(newInstance);
return *newInstance;
}
void LLEventPumps::flush()
{
// Flush every known LLEventPump instance. Leave it up to each instance to
// decide what to do with the flush() call.
for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi)
{
pmi->second->flush();
}
}
void LLEventPumps::reset()
{
// Reset every known LLEventPump instance. Leave it up to each instance to
// decide what to do with the reset() call.
for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi)
{
pmi->second->reset();
}
}
std::string LLEventPumps::registerNew(const LLEventPump& pump, const std::string& name, bool tweak)
{
std::pair<PumpMap::iterator, bool> inserted =
mPumpMap.insert(PumpMap::value_type(name, const_cast<LLEventPump*>(&pump)));
// If the insert worked, then the name is unique; return that.
if (inserted.second)
return name;
// Here the new entry was NOT inserted, and therefore name isn't unique.
// Unless we're permitted to tweak it, that's Bad.
if (! tweak)
{
throw LLEventPump::DupPumpName(std::string("Duplicate LLEventPump name '") + name + "'");
}
// The passed name isn't unique, but we're permitted to tweak it. Find the
// first decimal-integer suffix not already taken. The insert() attempt
// above will have set inserted.first to the iterator of the existing
// entry by that name. Starting there, walk forward until we reach an
// entry that doesn't start with 'name'. For each entry consisting of name
// + integer suffix, capture the integer suffix in a set. Use a set
// because we're going to encounter string suffixes in the order: name1,
// name10, name11, name2, ... Walking those possibilities in that order
// isn't convenient to detect the first available "hole."
std::set<int> suffixes;
PumpMap::iterator pmi(inserted.first), pmend(mPumpMap.end());
// We already know inserted.first references the existing entry with
// 'name' as the key; skip that one and start with the next.
while (++pmi != pmend)
{
if (pmi->first.substr(0, name.length()) != name)
{
// Found the first entry beyond the entries starting with 'name':
// stop looping.
break;
}
// Here we're looking at an entry that starts with 'name'. Is the rest
// of it an integer?
// Dubious (?) assumption: in the local character set, decimal digits
// are in increasing order such that '9' is the last of them. This
// test deals with 'name' values such as 'a', where there might be a
// very large number of entries starting with 'a' whose suffixes
// aren't integers. A secondary assumption is that digit characters
// precede most common name characters (true in ASCII, false in
// EBCDIC). The test below is correct either way, but it's worth more
// if the assumption holds.
if (pmi->first[name.length()] > '9')
break;
// It should be cheaper to detect that we're not looking at a digit
// character -- and therefore the suffix can't possibly be an integer
// -- than to attempt the lexical_cast and catch the exception.
if (! std::isdigit(pmi->first[name.length()]))
continue;
// Okay, the first character of the suffix is a digit, it's worth at
// least attempting to convert to int.
try
{
suffixes.insert(boost::lexical_cast<int>(pmi->first.substr(name.length())));
}
catch (const boost::bad_lexical_cast&)
{
// If the rest of pmi->first isn't an int, just ignore it.
}
}
// Here we've accumulated in 'suffixes' all existing int suffixes of the
// entries starting with 'name'. Find the first unused one.
int suffix = 1;
for ( ; suffixes.find(suffix) != suffixes.end(); ++suffix)
;
// Here 'suffix' is not in 'suffixes'. Construct a new name based on that
// suffix, insert it and return it.
std::ostringstream out;
out << name << suffix;
return registerNew(pump, out.str(), tweak);
}
void LLEventPumps::unregister(const LLEventPump& pump)
{
// Remove this instance from mPumpMap
PumpMap::iterator found = mPumpMap.find(pump.getName());
if (found != mPumpMap.end())
{
mPumpMap.erase(found);
}
// If this instance is one we created, also remove it from mOurPumps so we
// won't try again to delete it later!
PumpSet::iterator psfound = mOurPumps.find(const_cast<LLEventPump*>(&pump));
if (psfound != mOurPumps.end())
{
mOurPumps.erase(psfound);
}
}
//static
bool LLEventPumps::sDeleted;
//static
void LLEventPumps::maybe_unregister(const LLEventPump& pump)
{
if (!sDeleted)
{
LLEventPumps::instance().unregister(pump);
}
}
LLEventPumps::~LLEventPumps()
{
// Deleting an LLEventPump calls its destructor, which calls maybe_unregister(),
// which would try to remove that LLEventPump instance from a NEWLY created LLEventPumps
// singleton (as we're already being destructed). Therefore, mark that we're not
// home anymore... --Aleric
sDeleted = true;
// Subsequently we can delete every LLEventPump we instantiated (via obtain()).
// We're not clearing mPumpMap or mOurPumps here... their destructors will.
for (LLEventPumps::PumpSet::iterator pump = mOurPumps.begin(); pump != mOurPumps.end(); ++pump)
{
delete *pump;
}
}
/*****************************************************************************
* LLEventPump
*****************************************************************************/
#if LL_WINDOWS
#pragma warning (push)
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
#endif
LLEventPump::LLEventPump(const std::string& name, bool tweak):
// Register every new instance with LLEventPumps
mName(LLEventPumps::instance().registerNew(*this, name, tweak)),
mSignal(new LLStandardSignal()),
mEnabled(true)
{}
#if LL_WINDOWS
#pragma warning (pop)
#endif
LLEventPump::~LLEventPump()
{
// Unregister this doomed instance from LLEventPumps
LLEventPumps::maybe_unregister(*this);
}
// static data member
const LLEventPump::NameList LLEventPump::empty;
std::string LLEventPump::inventName(const std::string& pfx)
{
static long suffix = 0;
return STRINGIZE(pfx << suffix++);
}
void LLEventPump::reset()
{
mSignal.reset();
mConnections.clear();
//mDeps.clear();
}
LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventListener& listener,
const NameList& after,
const NameList& before)
{
// Check for duplicate name before connecting listener to mSignal
ConnectionMap::const_iterator found = mConnections.find(name);
// In some cases the user might disconnect a connection explicitly -- or
// might use LLEventTrackable to disconnect implicitly. Either way, we can
// end up retaining in mConnections a zombie connection object that's
// already been disconnected. Such a connection object can't be
// reconnected -- nor, in the case of LLEventTrackable, would we want to
// try, since disconnection happens with the destruction of the listener
// object. That means it's safe to overwrite a disconnected connection
// object with the new one we're attempting. The case we want to prevent
// is only when the existing connection object is still connected.
if (found != mConnections.end() && found->second.connected())
{
throw DupListenerName(std::string("Attempt to register duplicate listener name '") + name +
"' on " + typeid(*this).name() + " '" + getName() + "'");
}
// Okay, name is unique, try to reconcile its dependencies. Specify a new
// "node" value that we never use for an mSignal placement; we'll fix it
// later.
DependencyMap::node_type& newNode = mDeps.add(name, -1.0, after, before);
// What if this listener has been added, removed and re-added? In that
// case newNode already has a non-negative value because we never remove a
// listener from mDeps. But keep processing uniformly anyway in case the
// listener was added back with different dependencies. Then mDeps.sort()
// would put it in a different position, and the old newNode placement
// value would be wrong, so we'd have to reassign it anyway. Trust that
// re-adding a listener with the same dependencies is the trivial case for
// mDeps.sort(): it can just replay its cache.
DependencyMap::sorted_range sorted_range;
try
{
// Can we pick an order that works including this new entry?
sorted_range = mDeps.sort();
}
catch (const DependencyMap::Cycle& e)
{
// No: the new node's after/before dependencies have made mDeps
// unsortable. If we leave the new node in mDeps, it will continue
// to screw up all future attempts to sort()! Pull it out.
mDeps.remove(name);
throw Cycle(std::string("New listener '") + name + "' on " + typeid(*this).name() +
" '" + getName() + "' would cause cycle: " + e.what());
}
// Walk the list to verify that we haven't changed the order.
float previous = 0.0, myprev = 0.0;
DependencyMap::sorted_iterator mydmi = sorted_range.end(); // need this visible after loop
for (DependencyMap::sorted_iterator dmi = sorted_range.begin();
dmi != sorted_range.end(); ++dmi)
{
// Since we've added the new entry with an invalid placement,
// recognize it and skip it.
if (dmi->first == name)
{
// Remember the iterator belonging to our new node, and which
// placement value was 'previous' at that point.
mydmi = dmi;
myprev = previous;
continue;
}
// If the new node has rearranged the existing nodes, we'll find
// that their placement values are no longer in increasing order.
if (dmi->second < previous)
{
// This is another scenario in which we'd better back out the
// newly-added node from mDeps -- but don't do it yet, we want to
// traverse the existing mDeps to report on it!
// Describe the change to the order of our listeners. Copy
// everything but the newest listener to a vector we can sort to
// obtain the old order.
typedef std::vector< std::pair<float, std::string> > SortNameList;
SortNameList sortnames;
for (DependencyMap::sorted_iterator cdmi(sorted_range.begin()), cdmend(sorted_range.end());
cdmi != cdmend; ++cdmi)
{
if (cdmi->first != name)
{
sortnames.push_back(SortNameList::value_type(cdmi->second, cdmi->first));
}
}
std::sort(sortnames.begin(), sortnames.end());
std::ostringstream out;
out << "New listener '" << name << "' on " << typeid(*this).name() << " '" << getName()
<< "' would move previous listener '" << dmi->first << "'\nwas: ";
SortNameList::const_iterator sni(sortnames.begin()), snend(sortnames.end());
if (sni != snend)
{
out << sni->second;
while (++sni != snend)
{
out << ", " << sni->second;
}
}
out << "\nnow: ";
DependencyMap::sorted_iterator ddmi(sorted_range.begin()), ddmend(sorted_range.end());
if (ddmi != ddmend)
{
out << ddmi->first;
while (++ddmi != ddmend)
{
out << ", " << ddmi->first;
}
}
// NOW remove the offending listener node.
mDeps.remove(name);
// Having constructed a description of the order change, inform caller.
throw OrderChange(out.str());
}
// This node becomes the previous one.
previous = dmi->second;
}
// We just got done with a successful mDeps.add(name, ...) call. We'd
// better have found 'name' somewhere in that sorted list!
assert(mydmi != sorted_range.end());
// Four cases:
// 0. name is the only entry: placement 1.0
// 1. name is the first of several entries: placement (next placement)/2
// 2. name is between two other entries: placement (myprev + (next placement))/2
// 3. name is the last entry: placement ceil(myprev) + 1.0
// Since we've cleverly arranged for myprev to be 0.0 if name is the
// first entry, this folds down to two cases. Case 1 is subsumed by
// case 2, and case 0 is subsumed by case 3. So we need only handle
// cases 2 and 3, which means we need only detect whether name is the
// last entry. Increment mydmi to see if there's anything beyond.
if (++mydmi != sorted_range.end())
{
// The new node isn't last. Place it between the previous node and
// the successor.
newNode = (myprev + mydmi->second)/2.0;
}
else
{
// The new node is last. Bump myprev up to the next integer, add
// 1.0 and use that.
newNode = std::ceil(myprev) + 1.0;
}
// Now that newNode has a value that places it appropriately in mSignal,
// connect it.
LLBoundListener bound = mSignal->connect(newNode, listener);
mConnections[name] = bound;
return bound;
}
LLBoundListener LLEventPump::getListener(const std::string& name) const
{
ConnectionMap::const_iterator found = mConnections.find(name);
if (found != mConnections.end())
{
return found->second;
}
// not found, return dummy LLBoundListener
return LLBoundListener();
}
void LLEventPump::stopListening(const std::string& name)
{
ConnectionMap::iterator found = mConnections.find(name);
if (found != mConnections.end())
{
found->second.disconnect();
mConnections.erase(found);
}
// We intentionally do NOT remove this name from mDeps. It may happen that
// the same listener with the same name and dependencies will jump on and
// off this LLEventPump repeatedly. Keeping a cache of dependencies will
// avoid a new dependency sort in such cases.
}
/*****************************************************************************
* LLEventStream
*****************************************************************************/
bool LLEventStream::post(const LLSD& event)
{
if (! mEnabled || !mSignal)
{
return false;
}
// NOTE NOTE NOTE: Any new access to member data beyond this point should
// cause us to move our LLStandardSignal object to a pimpl class along
// with said member data. Then the local shared_ptr will preserve both.
// DEV-43463: capture a local copy of mSignal. We've turned up a
// cross-coroutine scenario (described in the Jira) in which this post()
// call could end up destroying 'this', the LLEventPump subclass instance
// containing mSignal, during the call through *mSignal. So -- capture a
// *stack* instance of the shared_ptr, ensuring that our heap
// LLStandardSignal object will live at least until post() returns, even
// if 'this' gets destroyed during the call.
boost::shared_ptr<LLStandardSignal> signal(mSignal);
// Let caller know if any one listener handled the event. This is mostly
// useful when using LLEventStream as a listener for an upstream
// LLEventPump.
return (*signal)(event);
}
/*****************************************************************************
* LLEventQueue
*****************************************************************************/
bool LLEventQueue::post(const LLSD& event)
{
if (mEnabled)
{
// Defer sending this event by queueing it until flush()
mEventQueue.push_back(event);
}
// Unconditionally return false. We won't know until flush() whether a
// listener claims to have handled the event -- meanwhile, don't block
// other listeners.
return false;
}
void LLEventQueue::flush()
{
if(!mSignal) return;
// Consider the case when a given listener on this LLEventQueue posts yet
// another event on the same queue. If we loop over mEventQueue directly,
// we'll end up processing all those events during the same flush() call
// -- rather like an EventStream. Instead, copy mEventQueue and clear it,
// so that any new events posted to this LLEventQueue during flush() will
// be processed in the *next* flush() call.
EventQueue queue(mEventQueue);
mEventQueue.clear();
// NOTE NOTE NOTE: Any new access to member data beyond this point should
// cause us to move our LLStandardSignal object to a pimpl class along
// with said member data. Then the local shared_ptr will preserve both.
// DEV-43463: capture a local copy of mSignal. See LLEventStream::post()
// for detailed comments.
boost::shared_ptr<LLStandardSignal> signal(mSignal);
for ( ; ! queue.empty(); queue.pop_front())
{
(*signal)(queue.front());
}
}
/*****************************************************************************
* LLListenerOrPumpName
*****************************************************************************/
LLListenerOrPumpName::LLListenerOrPumpName(const std::string& pumpname):
// Look up the specified pumpname, and bind its post() method as our listener
mListener(boost::bind(&LLEventPump::post,
boost::ref(LLEventPumps::instance().obtain(pumpname)),
_1))
{
}
LLListenerOrPumpName::LLListenerOrPumpName(const char* pumpname):
// Look up the specified pumpname, and bind its post() method as our listener
mListener(boost::bind(&LLEventPump::post,
boost::ref(LLEventPumps::instance().obtain(pumpname)),
_1))
{
}
bool LLListenerOrPumpName::operator()(const LLSD& event) const
{
if (! mListener)
{
throw Empty("attempting to call uninitialized");
}
return (*mListener)(event);
}
void LLReqID::stamp(LLSD& response) const
{
if (! (response.isUndefined() || response.isMap()))
{
// If 'response' was previously completely empty, it's okay to
// turn it into a map. If it was already a map, then it should be
// okay to add a key. But if it was anything else (e.g. a scalar),
// assigning a ["reqid"] key will DISCARD the previous value,
// replacing it with a map. That would be Bad.
LL_INFOS("LLReqID") << "stamp(" << mReqid << ") leaving non-map response unmodified: "
<< response << LL_ENDL;
return;
}
LLSD oldReqid(response["reqid"]);
if (! (oldReqid.isUndefined() || llsd_equals(oldReqid, mReqid)))
{
LL_INFOS("LLReqID") << "stamp(" << mReqid << ") preserving existing [\"reqid\"] value "
<< oldReqid << " in response: " << response << LL_ENDL;
return;
}
response["reqid"] = mReqid;
}
bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyKey)
{
// If the original request has no value for replyKey, it's pointless to
// construct or send a reply event: on which LLEventPump should we send
// it? Allow that to be optional: if the caller wants to require replyKey,
// it can so specify when registering the operation method.
if (! request.has(replyKey))
{
return false;
}
// Here the request definitely contains replyKey; reasonable to proceed.
// 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);
}

1051
indra/llcommon/llevents.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,44 +0,0 @@
/**
* @file llversionserver.h
* @brief
*
* $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLVERSIONSERVER_H
#define LL_LLVERSIONSERVER_H
const S32 LL_VERSION_MAJOR = 1;
const S32 LL_VERSION_MINOR = 27;
const S32 LL_VERSION_PATCH = 0;
const S32 LL_VERSION_BUILD = 132845;
const char * const LL_CHANNEL = "Second Life Server";
#endif

View File

@@ -36,12 +36,13 @@
const S32 LL_VERSION_MAJOR = 1;
const S32 LL_VERSION_MINOR = 6;
const S32 LL_VERSION_PATCH = 0;
const S32 LL_VERSION_BUILD = 3;
const S32 LL_VERSION_BUILD = ${vBUILD};
const char * const LL_CHANNEL = "Singularity";
const char * const LL_CHANNEL = "${VIEWER_CHANNEL}";
#if LL_DARWIN
const char * const LL_VERSION_BUNDLE_ID = "com.secondlife.singularity.viewer";
const char * const LL_VERSION_BUNDLE_ID = "org.singularityviewer.singularity";
#endif
#endif

View File

@@ -168,7 +168,7 @@ BOOL LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod
// dereference the array.
if(!image || !image->numcomps)
{
llwarns << "ERROR -> decodeImpl: failed to decode image!" << llendl;
llwarns << "failed to decode image!" << llendl;
if (image)
{
opj_image_destroy(image);
@@ -183,6 +183,7 @@ BOOL LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod
{
if (image->comps[i].factor != base.getRawDiscardLevel())
{
llwarns << "Expected discard level not reached!" << llendl;
// if we didn't get the discard level we're expecting, fail
opj_image_destroy(image);
base.decodeFailed();

View File

@@ -30,8 +30,8 @@
* $/LicenseInfo$
*/
#include <iostream>
#include "linden_common.h"
#include <iostream>
#include "llsaleinfo.h"

View File

@@ -65,6 +65,7 @@ set(llmessage_SOURCE_FILES
llregionpresenceverifier.cpp
llsdappservices.cpp
llsdhttpserver.cpp
llsdmessage.cpp
llsdmessagebuilder.cpp
llsdmessagereader.cpp
llsdrpcclient.cpp
@@ -163,6 +164,7 @@ set(llmessage_HEADER_FILES
llregionpresenceverifier.h
llsdappservices.h
llsdhttpserver.h
llsdmessage.h
llsdmessagebuilder.h
llsdmessagereader.h
llsdrpcclient.h

View File

@@ -2,31 +2,25 @@
* @file llregionflags.h
* @brief Flags that are sent in the statistics message region_flags field.
*
* $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
* Copyright (C) 2010, Linden Research, Inc.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
* This library is 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.
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
* 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.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* 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$
*/
@@ -48,9 +42,6 @@ const U32 REGION_FLAGS_RESET_HOME_ON_TELEPORT = (1 << 3);
// Does the sun move?
const U32 REGION_FLAGS_SUN_FIXED = (1 << 4);
// Tax free zone (no taxes on objects, land, etc.)
const U32 REGION_FLAGS_TAX_FREE = (1 << 5);
// Can't change the terrain heightfield, even on owned parcels,
// but can plant trees and grass.
const U32 REGION_FLAGS_BLOCK_TERRAFORM = (1 << 6);
@@ -60,17 +51,12 @@ const U32 REGION_FLAGS_BLOCK_LAND_RESELL = (1 << 7);
// All content wiped once per night
const U32 REGION_FLAGS_SANDBOX = (1 << 8);
const U32 REGION_FLAGS_NULL_LAYER = (1 << 9);
// const U32 REGION_FLAGS_SKIP_AGENT_ACTION = (1 << 10);
const U32 REGION_FLAGS_HARD_ALLOW_LAND_TRANSFER = (1 << 10); // Region allows land reselling
// const U32 REGION_FLAGS_SKIP_UPDATE_INTEREST_LIST= (1 << 11);
const U32 REGION_FLAGS_HARD_ALLOW_POST_CLASSIFIED = (1 << 11); // Region allows posting of classified ads
const U32 REGION_FLAGS_SKIP_COLLISIONS = (1 << 12); // Pin all non agent rigid bodies
const U32 REGION_FLAGS_SKIP_SCRIPTS = (1 << 13);
const U32 REGION_FLAGS_SKIP_PHYSICS = (1 << 14); // Skip all physics
const U32 REGION_FLAGS_EXTERNALLY_VISIBLE = (1 << 15);
//const U32 REGION_FLAGS_MAINLAND_VISIBLE = (1 << 16);
const U32 REGION_FLAGS_PUBLIC_ALLOWED = (1 << 17);
const U32 REGION_FLAGS_ALLOW_RETURN_ENCROACHING_OBJECT = (1 << 16);
const U32 REGION_FLAGS_ALLOW_RETURN_ENCROACHING_ESTATE_OBJECT = (1 << 17);
const U32 REGION_FLAGS_BLOCK_DWELL = (1 << 18);
// Is flight allowed?
@@ -87,18 +73,13 @@ const U32 REGION_FLAGS_ESTATE_SKIP_SCRIPTS = (1 << 21);
const U32 REGION_FLAGS_RESTRICT_PUSHOBJECT = (1 << 22);
const U32 REGION_FLAGS_DENY_ANONYMOUS = (1 << 23);
// const U32 REGION_FLAGS_DENY_IDENTIFIED = (1 << 24);
// const U32 REGION_FLAGS_DENY_TRANSACTED = (1 << 25);
const U32 REGION_FLAGS_ALLOW_PARCEL_CHANGES = (1 << 26);
const U32 REGION_FLAGS_ABUSE_EMAIL_TO_ESTATE_OWNER = (1 << 27);
const U32 REGION_FLAGS_ALLOW_VOICE = (1 << 28);
const U32 REGION_FLAGS_BLOCK_PARCEL_SEARCH = (1 << 29);
const U32 REGION_FLAGS_DENY_AGEUNVERIFIED = (1 << 30);
const U32 REGION_FLAGS_SKIP_MONO_SCRIPTS = (1 << 31);
const U32 REGION_FLAGS_DEFAULT = REGION_FLAGS_ALLOW_LANDMARK |
REGION_FLAGS_ALLOW_SET_HOME |
@@ -111,7 +92,6 @@ const U32 REGION_FLAGS_PRELUDE_UNSET = REGION_FLAGS_ALLOW_LANDMARK
| REGION_FLAGS_ALLOW_SET_HOME;
const U32 REGION_FLAGS_ESTATE_MASK = REGION_FLAGS_EXTERNALLY_VISIBLE
| REGION_FLAGS_PUBLIC_ALLOWED
| REGION_FLAGS_SUN_FIXED
| REGION_FLAGS_DENY_ANONYMOUS
| REGION_FLAGS_DENY_AGEUNVERIFIED;

View File

@@ -0,0 +1,170 @@
/**
* @file llsdmessage.cpp
* @author Nat Goodspeed
* @date 2008-10-31
* @brief Implementation for llsdmessage.
*
* $LicenseInfo:firstyear=2008&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$
*/
#if LL_WINDOWS
#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
#endif
// Precompiled header
#include "linden_common.h"
// associated header
#include "llsdmessage.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "llevents.h"
#include "llsdserialize.h"
#include "llhttpclient.h"
#include "llmessageconfig.h"
#include "llhost.h"
#include "message.h"
#include "llsdutil.h"
// Declare a static LLSDMessage instance to ensure that we have a listener as
// soon as someone tries to post on our canonical LLEventPump name.
static LLSDMessage httpListener;
LLSDMessage::LLSDMessage():
// Instantiating our own local LLEventPump with a string name the
// constructor is NOT allowed to tweak is a way of ensuring Singleton
// semantics: attempting to instantiate a second LLSDMessage object would
// throw LLEventPump::DupPumpName.
mEventPump("LLHTTPClient")
{
mEventPump.listen("self", boost::bind(&LLSDMessage::httpListener, this, _1));
}
bool LLSDMessage::httpListener(const LLSD& request)
{
// Extract what we want from the request object. We do it all up front
// partly to document what we expect.
LLSD::String url(request["url"]);
LLSD payload(request["payload"]);
LLSD::String reply(request["reply"]);
LLSD::String error(request["error"]);
LLSD::Real timeout(request["timeout"]);
// If the LLSD doesn't even have a "url" key, we doubt it was intended for
// this listener.
if (url.empty())
{
std::ostringstream out;
out << "request event without 'url' key to '" << mEventPump.getName() << "'";
throw ArgError(out.str());
}
// Establish default timeout. This test relies on LLSD::asReal() returning
// exactly 0.0 for an undef value.
if (! timeout)
{
timeout = HTTP_REQUEST_EXPIRY_SECS;
}
LLHTTPClient::post(url, payload,
new LLSDMessage::EventResponder(LLEventPumps::instance(),
request,
url, "POST", reply, error),
LLSD(), // headers
timeout);
return false;
}
void LLSDMessage::EventResponder::result(const LLSD& data)
{
// If our caller passed an empty replyPump name, they're not
// listening: this is a fire-and-forget message. Don't bother posting
// to the pump whose name is "".
if (! mReplyPump.empty())
{
LLSD response(data);
mReqID.stamp(response);
mPumps.obtain(mReplyPump).post(response);
}
else // default success handling
{
LL_INFOS("LLSDMessage::EventResponder")
<< "'" << mMessage << "' to '" << mTarget << "' succeeded"
<< LL_ENDL;
}
}
void LLSDMessage::EventResponder::errorWithContent(U32 status, const std::string& reason, const LLSD& content)
{
// If our caller passed an empty errorPump name, they're not
// listening: "default error handling is acceptable." Only post to an
// explicit pump name.
if (! mErrorPump.empty())
{
LLSD info(mReqID.makeResponse());
info["target"] = mTarget;
info["message"] = mMessage;
info["status"] = LLSD::Integer(status);
info["reason"] = reason;
info["content"] = content;
mPumps.obtain(mErrorPump).post(info);
}
else // default error handling
{
// convention seems to be to use llinfos, but that seems a bit casual?
LL_WARNS("LLSDMessage::EventResponder")
<< "'" << mMessage << "' to '" << mTarget
<< "' failed with code " << status << ": " << reason << '\n'
<< ll_pretty_print_sd(content)
<< LL_ENDL;
}
}
LLSDMessage::ResponderAdapter::ResponderAdapter(LLHTTPClient::ResponderPtr responder,
const std::string& name):
mResponder(responder),
mReplyPump(name + ".reply", true), // tweak name for uniqueness
mErrorPump(name + ".error", true)
{
mReplyPump.listen("self", boost::bind(&ResponderAdapter::listener, this, _1, true));
mErrorPump.listen("self", boost::bind(&ResponderAdapter::listener, this, _1, false));
}
bool LLSDMessage::ResponderAdapter::listener(const LLSD& payload, bool success)
{
if (success)
{
mResponder->result(payload);
}
else
{
mResponder->errorWithContent(payload["status"].asInteger(), payload["reason"], payload["content"]);
}
/*---------------- MUST BE LAST STATEMENT BEFORE RETURN ----------------*/
delete this;
// Destruction of mResponder will usually implicitly free its referent as well
/*------------------------- NOTHING AFTER THIS -------------------------*/
return false;
}
void LLSDMessage::link()
{
}

View File

@@ -0,0 +1,166 @@
/**
* @file llsdmessage.h
* @author Nat Goodspeed
* @date 2008-10-30
* @brief API intended to unify sending capability, UDP and TCP messages:
* https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes
*
* $LicenseInfo:firstyear=2008&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$
*/
#if ! defined(LL_LLSDMESSAGE_H)
#define LL_LLSDMESSAGE_H
#include "llerror.h" // LOG_CLASS()
#include "llevents.h" // LLEventPumps
#include "llhttpclient.h"
#include <string>
#include <stdexcept>
class LLSD;
/**
* Class managing the messaging API described in
* https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes
*/
class LLSDMessage
{
LOG_CLASS(LLSDMessage);
public:
LLSDMessage();
/// Exception if you specify arguments badly
struct ArgError: public std::runtime_error
{
ArgError(const std::string& what):
std::runtime_error(std::string("ArgError: ") + what) {}
};
/**
* The response idiom used by LLSDMessage -- LLEventPump names on which to
* post reply or error -- is designed for the case in which your
* reply/error handlers are methods on the same class as the method
* sending the message. Any state available to the sending method that
* must be visible to the reply/error methods can conveniently be stored
* on that class itself, if it's not already.
*
* The LLHTTPClient::Responder idiom requires a separate instance of a
* separate class so that it can dispatch to the code of interest by
* calling canonical virtual methods. Interesting state must be copied
* into that new object.
*
* With some trepidation, because existing response code is packaged in
* LLHTTPClient::Responder subclasses, we provide this adapter class
* <i>for transitional purposes only.</i> Instantiate a new heap
* ResponderAdapter with your new LLHTTPClient::ResponderPtr. Pass
* ResponderAdapter::getReplyName() and/or getErrorName() in your
* LLSDMessage (or LLViewerRegion::getCapAPI()) request event. The
* ResponderAdapter will call the appropriate Responder method, then
* @c delete itself.
*/
class ResponderAdapter
{
public:
/**
* Bind the new LLHTTPClient::Responder subclass instance.
*
* Passing the constructor a name other than the default is only
* interesting if you suspect some usage will lead to an exception or
* log message.
*/
ResponderAdapter(LLHTTPClient::ResponderPtr responder,
const std::string& name="ResponderAdapter");
/// EventPump name on which LLSDMessage should post reply event
std::string getReplyName() const { return mReplyPump.getName(); }
/// EventPump name on which LLSDMessage should post error event
std::string getErrorName() const { return mErrorPump.getName(); }
private:
// We have two different LLEventStreams, though we route them both to
// the same listener, so that we can bind an extra flag identifying
// which case (reply or error) reached that listener.
bool listener(const LLSD&, bool success);
LLHTTPClient::ResponderPtr mResponder;
LLEventStream mReplyPump, mErrorPump;
};
/**
* Force our implementation file to be linked with caller. The .cpp file
* contains a static instance of this class, which must be linked into the
* executable to support the canonical listener. But since the primary
* interface to that static instance is via a named LLEventPump rather
* than by direct reference, the linker doesn't necessarily perceive the
* necessity to bring in the translation unit. Referencing this dummy
* method forces the issue.
*/
static void link();
private:
friend class LLCapabilityListener;
/// Responder used for internal purposes by LLSDMessage and
/// LLCapabilityListener. Others should use higher-level APIs.
class EventResponder: public LLHTTPClient::Responder
{
public:
/**
* LLHTTPClient::Responder that dispatches via named LLEventPump instances.
* We bind LLEventPumps, even though it's an LLSingleton, for testability.
* We bind the string names of the desired LLEventPump instances rather
* than actually obtain()ing them so we only obtain() the one we're going
* to use. If the caller doesn't bother to listen() on it, the other pump
* may never materialize at all.
* @a target and @a message are only to clarify error processing.
* For a capability message, @a target should be the region description,
* @a message should be the capability name.
* For a service with a visible URL, pass the URL as @a target and the HTTP verb
* (e.g. "POST") as @a message.
*/
EventResponder(LLEventPumps& pumps,
const LLSD& request,
const std::string& target, const std::string& message,
const std::string& replyPump, const std::string& errorPump):
mPumps(pumps),
mReqID(request),
mTarget(target),
mMessage(message),
mReplyPump(replyPump),
mErrorPump(errorPump)
{}
virtual void result(const LLSD& data);
virtual void errorWithContent(U32 status, const std::string& reason, const LLSD& content);
private:
LLEventPumps& mPumps;
LLReqID mReqID;
const std::string mTarget, mMessage, mReplyPump, mErrorPump;
};
private:
bool httpListener(const LLSD&);
LLEventStream mEventPump;
};
#endif /* ! defined(LL_LLSDMESSAGE_H) */

View File

@@ -1141,7 +1141,7 @@ LLFontGL* LLFontGL::getFontSansSerifBig()
//static
LLFontGL* LLFontGL::getFontSansSerifHuge()
{
static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Large",0));
static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Huge",0));
return fontp;
}
@@ -1320,4 +1320,4 @@ void LLFontGL::drawGlyph(const LLRectf& screen_rect, const LLRectf& uv_rect, con
}
gGL.end();
}
}

View File

@@ -699,7 +699,7 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade
text[count++] = strdup("return texture2D(tex0, texcoord);\n");
text[count++] = strdup("}\n");
}
else if (gGLManager.mGLVersion >= 3.f)
else if (gGLManager.mGLVersion >= 3.f && !(gGLManager.mIsATI && gGLManager.mGLVersion < 3.3f) )
{
text[count++] = strdup("\tswitch (int(vary_texture_index+0.25))\n");
text[count++] = strdup("\t{\n");

View File

@@ -62,7 +62,7 @@
#include "llevent.h"
template <class T>
class LLMemberListener : public LLSimpleListener
class LLMemberListener : public LLOldEvents::LLSimpleListener
{
public:
LLMemberListener() : mPtr(NULL), mRegisteredName() { }
@@ -75,7 +75,7 @@ public:
}
// This is what you have to override to handle this event
virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) = 0;
virtual bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) = 0;
protected:
T *mPtr; // The object that this listener manipulates

View File

@@ -67,6 +67,8 @@
#include <set>
#include <boost/tokenizer.hpp>
using namespace LLOldEvents;
// static
LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL;

View File

@@ -263,7 +263,7 @@ public:
// calls a user defined callback.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class LLMenuItemCallGL : public LLMenuItemGL, public LLObservable
class LLMenuItemCallGL : public LLMenuItemGL, public LLOldEvents::LLObservable
{
public:
// normal constructor

View File

@@ -100,43 +100,12 @@
#include "llinstancetracker.h"
// and we need this to manage the notification callbacks
#include "llevents.h"
#include "llfunctorregistry.h"
#include "llui.h"
#include "llxmlnode.h"
#include "llnotificationptr.h"
/*****************************************************************************
* Signal and handler declarations
* Using a single handler signature means that we can have a common handler
* type, rather than needing a distinct one for each different handler.
*****************************************************************************/
/**
* A boost::signals2 Combiner that stops the first time a handler returns true
* We need this because we want to have our handlers return bool, so that
* we have the option to cause a handler to stop further processing. The
* default handler fails when the signal returns a value but has no slots.
*/
struct LLStopWhenHandled
{
typedef bool result_type;
template<typename InputIterator>
result_type operator()(InputIterator first, InputIterator last) const
{
for (InputIterator si = first; si != last; ++si)
{
if (*si)
{
return true;
}
}
return false;
}
};
typedef enum e_notification_priority
{
NOTIFICATION_PRIORITY_UNSPECIFIED,
@@ -146,27 +115,11 @@ typedef enum e_notification_priority
NOTIFICATION_PRIORITY_CRITICAL
} ENotificationPriority;
/**
* We want to have a standard signature for all signals; this way,
* we can easily document a protocol for communicating across
* dlls and into scripting languages someday.
* we want to return a bool to indicate whether the signal has been
* handled and should NOT be passed on to other listeners.
* Return true to stop further handling of the signal, and false
* to continue.
* We take an LLSD because this way the contents of the signal
* are independent of the API used to communicate it.
* It is const ref because then there's low cost to pass it;
* if you only need to inspect it, it's very cheap.
*/
typedef boost::function<void (const LLSD&, const LLSD&)> LLNotificationResponder;
typedef LLFunctorRegistry<LLNotificationResponder> LLNotificationFunctorRegistry;
typedef LLFunctorRegistration<LLNotificationResponder> LLNotificationFunctorRegistration;
typedef boost::signals2::signal<bool(const LLSD&), LLStopWhenHandled> LLStandardSignal;
// context data that can be looked up via a notification's payload by the display logic
// derive from this class to implement specific contexts
class LLNotificationContext : public LLInstanceTracker<LLNotificationContext, LLUUID>

View File

@@ -117,6 +117,69 @@ std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::
}
}
//static
std::string LLTrans::getString(const std::string &xml_desc, const LLSD& msg_args)
{
// Don't care about time as much as call count. Make sure we're not
// calling LLTrans::getString() in an inner loop. JC
//V3: LLFastTimer timer(FTM_GET_TRANS);
template_map_t::iterator iter = sStringTemplates.find(xml_desc);
if (iter != sStringTemplates.end())
{
std::string text = iter->second.mText;
LLStringUtil::format(text, msg_args);
return text;
}
else
{
LL_WARNS_ONCE("configuration") << "Missing String in strings.xml: [" << xml_desc << "]" << LL_ENDL;
return "MissingString("+xml_desc+")";
}
}
//static
bool LLTrans::findString(std::string &result, const std::string &xml_desc, const LLStringUtil::format_map_t& msg_args)
{
//V3: LLFastTimer timer(FTM_GET_TRANS);
template_map_t::iterator iter = sStringTemplates.find(xml_desc);
if (iter != sStringTemplates.end())
{
std::string text = iter->second.mText;
LLStringUtil::format_map_t args = sDefaultArgs;
args.insert(msg_args.begin(), msg_args.end());
LLStringUtil::format(text, args);
result = text;
return true;
}
else
{
LL_WARNS_ONCE("configuration") << "Missing String in strings.xml: [" << xml_desc << "]" << LL_ENDL;
return false;
}
}
//static
bool LLTrans::findString(std::string &result, const std::string &xml_desc, const LLSD& msg_args)
{
//V3: LLFastTimer timer(FTM_GET_TRANS);
template_map_t::iterator iter = sStringTemplates.find(xml_desc);
if (iter != sStringTemplates.end())
{
std::string text = iter->second.mText;
LLStringUtil::format(text, msg_args);
result = text;
return true;
}
else
{
LL_WARNS_ONCE("configuration") << "Missing String in strings.xml: [" << xml_desc << "]" << LL_ENDL;
return false;
}
}
void LLTrans::setDefaultArg(const std::string& name, const std::string& value)
{
sDefaultArgs[name] = value;

View File

@@ -60,12 +60,14 @@ public:
/**
* @brief Parses the xml file that holds the strings. Used once on startup
* @param xml_filename Filename to parse
// *FIXME * @param xml_filename Filename to parse
* @param default_args Set of strings (expected to be in the file) to use as default replacement args, e.g. "SECOND_LIFE"
* @returns true if the file was parsed successfully, true if something went wrong
*/
static bool parseStrings(const std::string& xml_filename, const std::set<std::string>& default_args);
//V3: static bool parseLanguageStrings(LLPointer<LLXMLNode> & root);
/**
* @brief Returns a translated string
* @param xml_desc String's description
@@ -73,6 +75,9 @@ public:
* @returns Translated string
*/
static std::string getString(const std::string &xml_desc, const LLStringUtil::format_map_t& args);
static std::string getString(const std::string &xml_desc, const LLSD& args);
static bool findString(std::string &result, const std::string &xml_desc, const LLStringUtil::format_map_t& args);
static bool findString(std::string &result, const std::string &xml_desc, const LLSD& args);
/**
* @brief Returns a translated string
@@ -84,7 +89,20 @@ public:
LLStringUtil::format_map_t empty;
return getString(xml_desc, empty);
}
static bool findString(std::string &result, const std::string &xml_desc)
{
LLStringUtil::format_map_t empty;
return findString(result, xml_desc, empty);
}
static std::string getKeyboardString(const char* keystring)
{
std::string key_str(keystring);
std::string trans_str;
return findString(trans_str, key_str) ? trans_str : key_str;
}
// get the default args
static const LLStringUtil::format_map_t& getDefaultArgs()
{

View File

@@ -63,6 +63,8 @@
#include "lldelayeduidelete.h"
// </edit>
using namespace LLOldEvents;
//HACK: this allows you to instantiate LLView from xml with "<view/>" which we don't want
static LLRegisterWidget<LLView> r("view");

View File

@@ -138,7 +138,7 @@ virtual void setControlName(const std::string& control, LLView *context);
LLSliderCtrl, LLCheckBoxCtrl
virtual std::string getControlName() const { return mControlName; }
LLSliderCtrl, LLCheckBoxCtrl
virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
virtual bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
LLMenuItem
virtual void setValue(const LLSD& value);
*
@@ -449,15 +449,15 @@ public:
void localRectToScreen( const LLRect& local, LLRect* screen ) const;
// Listener dispatching functions (Dispatcher deletes pointers to listeners on deregistration or destruction)
LLSimpleListener* getListenerByName(const std::string& callback_name);
void registerEventListener(std::string name, LLSimpleListener* function);
LLOldEvents::LLSimpleListener* getListenerByName(const std::string& callback_name);
void registerEventListener(std::string name, LLOldEvents::LLSimpleListener* function);
void deregisterEventListener(std::string name);
typedef boost::signals2::signal<void (LLPointer<LLEvent> event, const LLSD& userdata)> event_signal_t;
typedef boost::signals2::signal<void (LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)> event_signal_t;
void registerEventListener(std::string name, event_signal_t::slot_type &cb);
std::string findEventListener(LLSimpleListener *listener) const;
void addListenerToControl(LLEventDispatcher *observer, const std::string& name, LLSD filter, LLSD userdata);
std::string findEventListener(LLOldEvents::LLSimpleListener *listener) const;
void addListenerToControl(LLOldEvents::LLEventDispatcher *observer, const std::string& name, LLSD filter, LLSD userdata);
void addBoolControl(const std::string& name, bool initial_value);
LLControlVariable *getControl(const std::string& name);
@@ -466,7 +466,7 @@ public:
bool setControlValue(const LLSD& value);
virtual void setControlName(const std::string& control, LLView *context);
virtual std::string getControlName() const { return mControlName; }
// virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
// virtual bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
virtual void setValue(const LLSD& value);
virtual LLSD getValue() const;
@@ -676,7 +676,7 @@ private:
static LLWindow* sWindow; // All root views must know about their window.
typedef std::map<std::string, LLPointer<LLSimpleListener> > dispatch_list_t;
typedef std::map<std::string, LLPointer<LLOldEvents::LLSimpleListener> > dispatch_list_t;
dispatch_list_t mDispatchList;
std::string mControlName;

View File

@@ -1723,7 +1723,7 @@ void LLWindowSDL::processMiscNativeEvents()
void LLWindowSDL::gatherInput()
{
const Uint32 CLICK_THRESHOLD = 300; // milliseconds
const Uint32 CLICK_THRESHOLD = 500; // milliseconds
static int leftClick = 0;
static int rightClick = 0;
static Uint32 lastLeftDown = 0;

View File

@@ -19,7 +19,7 @@ endif(NOT FMODEX)
include(OPENAL)
include(HUNSPELL)
include(FindOpenGL)
include(JsonCpp)
#include(JsonCpp)
include(LLAddBuildTest)
include(LLAudio)
include(LLCharacter)
@@ -76,6 +76,8 @@ include_directories(
)
set(viewer_SOURCE_FILES
sgmemstat.cpp
sgversion.cpp
llviewerobjectbackup.cpp
slfloatermediafilter.cpp
floaterlocalassetbrowse.cpp
@@ -134,6 +136,7 @@ set(viewer_SOURCE_FILES
llbuildnewviewsscheduler.cpp
llcallbacklist.cpp
llcallingcard.cpp
llcapabilitylistener.cpp
llcaphttpsender.cpp
llchatbar.cpp
llclassifiedinfo.cpp
@@ -445,7 +448,6 @@ set(viewer_SOURCE_FILES
lltoolselectrect.cpp
lltoolview.cpp
lltracker.cpp
lltranslate.cpp
lluploaddialog.cpp
lluploadfloaterobservers.cpp
llurl.cpp
@@ -496,6 +498,7 @@ set(viewer_SOURCE_FILES
llviewerregion.cpp
llviewershadermgr.cpp
llviewerstats.cpp
llviewerstatsrecorder.cpp
llviewertexteditor.cpp
llviewertexture.cpp
llviewertextureanim.cpp
@@ -567,6 +570,8 @@ set(viewer_HEADER_FILES
CMakeLists.txt
ViewerInstall.cmake
sgmemstat.h
sgversion.h
llviewerobjectbackup.h
slfloatermediafilter.h
floaterlocalassetbrowse.h
@@ -624,6 +629,7 @@ set(viewer_HEADER_FILES
llbuildnewviewsscheduler.h
llcallbacklist.h
llcallingcard.h
llcapabilitylistener.h
llcaphttpsender.h
llchatbar.h
llclassifiedinfo.h
@@ -648,7 +654,6 @@ set(viewer_HEADER_FILES
lldrawpoolalpha.h
lldrawpoolavatar.h
lldrawpoolbump.h
lldrawpoolclouds.h
lldrawpoolground.h
lldrawpoolsimple.h
lldrawpoolsky.h
@@ -955,7 +960,6 @@ set(viewer_HEADER_FILES
llviewerassetstorage.h
llviewerassettype.h
llvieweraudio.h
llviewerbuild.h
llviewercamera.h
llviewercontrol.h
llviewerdisplay.h
@@ -992,6 +996,7 @@ set(viewer_HEADER_FILES
llviewerregion.h
llviewershadermgr.h
llviewerstats.h
llviewerstatsrecorder.h
llviewertexteditor.h
llviewertexture.h
llviewertextureanim.h
@@ -1358,12 +1363,16 @@ if (FMOD OR FMODEX)
if (NOT WINDOWS)
set(fmodwrapper_SOURCE_FILES fmodwrapper.cpp)
add_library(fmodwrapper SHARED ${fmodwrapper_SOURCE_FILES})
if(FMODEX)
set(fmodwrapper_needed_LIBRARIES ${FMODEX_LIBRARY})
endif(FMODEX)
if(FMOD)
set(fmodwrapper_needed_LIBRARIES "${fmodwrapper_needed_LIBRARIES} ${FMOD_LIBRARY}")
endif(FMOD)
if(FMOD AND FMODEX)
set(fmodwrapper_needed_LIBRARIES "${FMODEX_LIBRARY} ${FMOD_LIBRARY}")
else(FMOD AND FMODEX)
if(FMODEX)
set(fmodwrapper_needed_LIBRARIES ${FMODEX_LIBRARY})
endif(FMODEX)
if(FMOD)
set(fmodwrapper_needed_LIBRARIES "${FMOD_LIBRARY}")
endif(FMOD)
endif(FMOD AND FMODEX)
if (DARWIN)
list(APPEND fmodwrapper_needed_LIBRARIES ${CARBON_LIBRARY})
set_target_properties(

View File

@@ -2,15 +2,16 @@
<array>
<map>
<key>default_grids_version</key><string>2</string>
<key>default_grids_version</key><string>20</string>
</map>
<!-- Second Life -->
<map>
<key>gridnick</key><string>secondlife</string>
<key>gridname</key><string>Second Life</string>
<key>platform</key><string>SecondLife</string>
<key>loginuri</key><string>https://login.agni.lindenlab.com/cgi-bin/login.cgi</string>
<key>loginpage</key><string>http://viewer-login.agni.lindenlab.com/</string>
<key>loginpage</key><string>http://viewer-login.agni.lindenlab.com/</string>
<key>helperuri</key><string>https://secondlife.com/helpers/</string>
<key>website</key><string>http://secondlife.com/</string>
<key>support</key><string>http://secondlife.com/support/</string>
@@ -18,30 +19,34 @@
<key>password</key><string>http://secondlife.com/account/request.php</string>
<key>render_compat</key><boolean>1</boolean>
<key>inventory_links</key><boolean>1</boolean>
<key>auto_update</key><boolean>0</boolean>
</map>
<map>
<key>gridnick</key><string>secondlife_beta</string>
<key>gridname</key><string>Second Life BETA</string>
<key>helperuri</key><string>http://aditi-secondlife.webdev.lindenlab.com/helpers/</string>
<key>loginpage</key><string>http://viewer-login.agni.lindenlab.com/</string>
<key>loginpage</key><string>http://viewer-login.agni.lindenlab.com/</string>
<key>loginuri</key><string>https://login.aditi.lindenlab.com/cgi-bin/login.cgi</string>
<key>password</key><string>http://secondlife.com/account/request.php</string>
<key>platform</key><string>SecondLife</string>
<key>website</key><string>http://secondlife.com/</string>
<key>support</key><string>http://secondlife.com/support/</string>
<key>register</key><string>http://secondlife.com/registration/</string>
<key>render_compat</key><boolean>0</boolean>
<key>inventory_links</key><boolean>1</boolean>
<key>support</key><string>http://secondlife.com/support/</string>
<key>version</key><integer>0</integer>
<key>website</key><string>http://secondlife.com/</string>
<key>auto_update</key><boolean>0</boolean>
</map>
<!-- Local Host -->
<map>
<key>gridnick</key><string>local</string>
<key>gridname</key><string>Local Host</string>
<key>platform</key><string>OpenSim</string>
<key>loginuri</key><string>http://127.0.0.1:9000/</string>
<key>helperuri</key><string>http://127.0.0.1:9000/</string>
<key>auto_update</key><boolean>0</boolean>
</map>
</array>

View File

@@ -8,6 +8,30 @@
<string>settings_sh.xml</string>
<string>settings_rlv.xml</string>
</array>
<key>SianaRenderDeferredInvisiprim</key>
<map>
<key>Comment</key>
<string>Support invisiprims in deferred mode</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>SGAbsolutePointer</key>
<map>
<key>Comment</key>
<string>Support pen tablets and absolute pointer devices by disabling mouse wrapping</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>WaterPresetName</key>
<map>
@@ -4225,6 +4249,17 @@
<key>Value</key>
<integer>-1</integer>
</map>
<key>DebugStatModeMalloc</key>
<map>
<key>Comment</key>
<string>Mode of stat in Statistics floater</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>-1</integer>
</map>
<key>DebugStatModeFormattedMem</key>
<map>
<key>Comment</key>
@@ -8198,7 +8233,7 @@
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
<integer>0</integer>
</map>
<key>MemoryPrivatePoolSize</key>
<map>

View File

@@ -84,7 +84,7 @@
<key>AscentAutoCloseOOC</key>
<map>
<key>Comment</key>
<string>Auto-close OOC chat (i.e. add \"))\" if not found and \"((\" was used)</string>
<string>Auto-close OOC chat (i.e. add "))" if not found and "((" was used)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -150,7 +150,7 @@
<key>AscentShowSelfTagColor</key>
<map>
<key>Comment</key>
<string>Show your own tag</string>
<string>Show your own tagcolor to yourself(instead of default linden viewer color).</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -293,7 +293,7 @@
<key>AscentShowIdleTime</key>
<map>
<key>Comment</key>
<string>Show client tags for others.</string>
<string>Show idle time of others in tags.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -403,7 +403,7 @@
<key>AscentBuildPrefs_PivotIsPercent</key>
<map>
<key>Comment</key>
<string>Would you like the chatbar to be able to be used for command line functions?</string>
<string>Pivot points are based on percentages</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -414,7 +414,7 @@
<key>AscentBuildPrefs_PivotX</key>
<map>
<key>Comment</key>
<string>idfk</string>
<string>Pivot point on X-axis for new objects</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -425,7 +425,7 @@
<key>AscentBuildPrefs_PivotY</key>
<map>
<key>Comment</key>
<string>idfk</string>
<string>Pivot point on Y-axis for new objects</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -436,7 +436,7 @@
<key>AscentBuildPrefs_PivotZ</key>
<map>
<key>Comment</key>
<string>idfk</string>
<string>Pivot point on Z-axis for new objects</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -620,5 +620,16 @@
<key>Value</key>
<string>tp2</string>
</map>
<key>SinguCmdLineAway</key>
<map>
<key>Comment</key>
<string>Toggle Fake Away Status.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string>/away</string>
</map>
</map>
</llsd>

View File

@@ -71,6 +71,7 @@ LLPrefsAscentSys::LLPrefsAscentSys()
childSetCommitCallback("AscentCmdLineOfferTp", onCommitCmdLine, this);
childSetCommitCallback("AscentCmdLineMapTo", onCommitCmdLine, this);
childSetCommitCallback("AscentCmdLineTP2", onCommitCmdLine, this);
childSetCommitCallback("SinguCmdLineAway", onCommitCmdLine, this);
refreshValues();
refresh();
@@ -169,6 +170,7 @@ void LLPrefsAscentSys::onCommitCmdLine(LLUICtrl* ctrl, void* user_data)
self->childSetEnabled("AscentCmdLineMapTo", enabled);
self->childSetEnabled("map_to_keep_pos", enabled);
self->childSetEnabled("AscentCmdLineTP2", enabled);
self->childSetEnabled("SinguCmdLineAway", enabled);
}
gSavedSettings.setString("AscentCmdLinePos", self->childGetValue("AscentCmdLinePos"));
@@ -184,6 +186,7 @@ void LLPrefsAscentSys::onCommitCmdLine(LLUICtrl* ctrl, void* user_data)
gSavedSettings.setString("AscentCmdLineOfferTp", self->childGetValue("AscentCmdLineOfferTp"));
gSavedSettings.setString("AscentCmdLineMapTo", self->childGetValue("AscentCmdLineMapTo"));
gSavedSettings.setString("AscentCmdLineTP2", self->childGetValue("AscentCmdLineTP2"));
gSavedSettings.setString("SinguCmdLineAway", self->childGetValue("SinguCmdLineAway"));
}
void LLPrefsAscentSys::refreshValues()
@@ -225,12 +228,14 @@ void LLPrefsAscentSys::refreshValues()
mCmdLineMapTo = gSavedSettings.getString("AscentCmdLineMapTo");
mCmdMapToKeepPos = gSavedSettings.getBOOL("AscentMapToKeepPos");
mCmdLineTP2 = gSavedSettings.getString("AscentCmdLineTP2");
mCmdLineAway = gSavedSettings.getString("SinguCmdLineAway");
//Privacy -----------------------------------------------------------------------------
//Security ----------------------------------------------------------------------------
mBroadcastViewerEffects = gSavedSettings.getBOOL("BroadcastViewerEffects");
mDisablePointAtAndBeam = gSavedSettings.getBOOL("DisablePointAtAndBeam");
mPrivateLookAt = gSavedSettings.getBOOL("PrivateLookAt");
mShowLookAt = gSavedSettings.getBOOL("AscentShowLookAt");
mQuietSnapshotsToDisk = gSavedSettings.getBOOL("QuietSnapshotsToDisk");
mRevokePermsOnStandUp = gSavedSettings.getBOOL("RevokePermsOnStandUp");
mDisableClickSit = gSavedSettings.getBOOL("DisableClickSit");
mDisplayScriptJumps = gSavedSettings.getBOOL("AscentDisplayTotalScriptJumps");
@@ -275,6 +280,7 @@ void LLPrefsAscentSys::refresh()
childSetEnabled("AscentCmdLineMapTo", mCmdLine);
childSetEnabled("map_to_keep_pos", mCmdLine);
childSetEnabled("AscentCmdLineTP2", mCmdLine);
childSetEnabled("SinguCmdLineAway", mCmdLine);
childSetValue("AscentCmdLinePos", mCmdLinePos);
childSetValue("AscentCmdLineGround", mCmdLineGround);
@@ -289,6 +295,7 @@ void LLPrefsAscentSys::refresh()
childSetValue("AscentCmdLineOfferTp", mCmdLineOfferTp);
childSetValue("AscentCmdLineMapTo", mCmdLineMapTo);
childSetValue("AscentCmdLineTP2", mCmdLineTP2);
childSetValue("SinguCmdLineAway", mCmdLineAway);
}
void LLPrefsAscentSys::cancel()
@@ -329,12 +336,14 @@ void LLPrefsAscentSys::cancel()
gSavedSettings.setString("AscentCmdLineMapTo", mCmdLineMapTo);
gSavedSettings.setBOOL("AscentMapToKeepPos", mCmdMapToKeepPos);
gSavedSettings.setString("AscentCmdLineTP2", mCmdLineTP2);
gSavedSettings.setString("SinguCmdLineAway", mCmdLineAway);
//Privacy -----------------------------------------------------------------------------
//Security ----------------------------------------------------------------------------
gSavedSettings.setBOOL("BroadcastViewerEffects", mBroadcastViewerEffects);
gSavedSettings.setBOOL("DisablePointAtAndBeam", mDisablePointAtAndBeam);
gSavedSettings.setBOOL("PrivateLookAt", mPrivateLookAt);
gSavedSettings.setBOOL("AscentShowLookAt", mShowLookAt);
gSavedSettings.setBOOL("QuietSnapshotsToDisk", mQuietSnapshotsToDisk);
gSavedSettings.setBOOL("RevokePermsOnStandUp", mRevokePermsOnStandUp);
gSavedSettings.setBOOL("DisableClickSit", mDisableClickSit);
gSavedSettings.setBOOL("AscentDisplayTotalScriptJumps", mDisplayScriptJumps);

View File

@@ -88,13 +88,15 @@ protected:
std::string mCmdLineMapTo;
BOOL mCmdMapToKeepPos;
std::string mCmdLineTP2;
std::string mCmdLineAway;
//Privacy -----------------------------------------------------------------------------
//Security ----------------------------------------------------------------------------
BOOL mBroadcastViewerEffects;
BOOL mDisablePointAtAndBeam;
BOOL mPrivateLookAt;
BOOL mShowLookAt;
BOOL mRevokePermsOnStandUp;
BOOL mQuietSnapshotsToDisk;
BOOL mRevokePermsOnStandUp;
BOOL mDisableClickSit;
BOOL mDisplayScriptJumps;
F32 mNumScriptDiff;

View File

@@ -55,6 +55,7 @@
#include "llfloateravatarlist.h"
#include "llviewerobjectlist.h"
#include "llviewertexteditor.h"
#include "llviewermenu.h"
#include "llvoavatar.h"
#include "lltooldraganddrop.h"
#include "llinventorymodel.h"
@@ -398,6 +399,10 @@ bool cmd_line_chat(std::string revised_text, EChatType type)
cmdline_tp2name(name);
}
return false;
}else if(command == utf8str_tolower(gSavedSettings.getString("SinguCmdLineAway")))
{
handle_fake_away_status(NULL);
return false;
}else if(command == "typingstop")
{
std::string text;

View File

@@ -30,26 +30,30 @@
* $/LicenseInfo$
*/
//Hack to build Darwin.
//Need to find a better way to do this later.
#define LL_FMOD
extern "C"
{
#if LL_FMODEX
#ifdef LL_FMODEX
void FSOUND_Sound_Init(void);
#endif
#if LL_FMOD
#ifdef LL_FMOD
void FSOUND_Init(void);
#endif
}
void* fmodwrapper(void)
{
{
// When building the fmodwrapper library, the linker doesn't seem to want to bring in libfmod.a unless I explicitly
// reference at least one symbol in the library. This seemed like the simplest way.
void *ret = NULL;
#if LL_FMODEX
ret = (void*)&FSOUND_Sound_Init;
#ifdef LL_FMODEX
return (void*)&FSOUND_Sound_Init;
#endif
#if LL_FMOD
ret = (void*)&FSOUND_Init;
#ifdef LL_FMOD
return (void*)&FSOUND_Init;
#endif
return ret;
}

View File

@@ -40,8 +40,9 @@ HippoGridInfo HippoGridInfo::FALLBACK_GRIDINFO("");
// Initialize
HippoGridInfo::HippoGridInfo(const std::string& gridName) :
mPlatform(PLATFORM_AURORA),
mPlatform(PLATFORM_OPENSIM),
mGridName(gridName),
mGridNick(LLStringUtil::null),
mLoginUri(LLStringUtil::null),
mLoginPage(LLStringUtil::null),
mHelperUri(LLStringUtil::null),
@@ -50,14 +51,12 @@ HippoGridInfo::HippoGridInfo(const std::string& gridName) :
mRegisterUrl(LLStringUtil::null),
mPasswordUrl(LLStringUtil::null),
mSearchUrl(LLStringUtil::null),
mFirstName(LLStringUtil::null),
mLastName(LLStringUtil::null),
mAvatarPassword(LLStringUtil::null),
mGridMessage(""),
mXmlState(XML_VOID),
mVoiceConnector("SLVoice"),
mRenderCompat(true),
mInvLinks(false),
mAutoUpdate(false),
mMaxAgentGroups(-1),
mCurrencySymbol("OS$"),
mRealCurrencySymbol("US$"),
@@ -143,21 +142,6 @@ const std::string& HippoGridInfo::getGridMessage() const
return mGridMessage;
}
const std::string& HippoGridInfo::getFirstName() const
{
return mFirstName;
}
const std::string& HippoGridInfo::getLastName() const
{
return mLastName;
}
const std::string& HippoGridInfo::getAvatarPassword() const
{
return mAvatarPassword;
}
bool HippoGridInfo::isRenderCompat() const
{
return mRenderCompat;
@@ -215,7 +199,30 @@ void HippoGridInfo::setPlatform(const std::string& platform)
void HippoGridInfo::setGridName(const std::string& gridName)
{
HippoGridManager::GridIterator it;
for(it = gHippoGridManager->beginGrid(); it != gHippoGridManager->endGrid(); ++it)
{
if (it->second == this)
{
gHippoGridManager->mGridInfo.erase(it);
gHippoGridManager->mGridInfo[gridName] = this;
break;
}
}
mGridName = gridName;
/*if(mGridNick.empty() && !gridName.empty())
{
setGridNick(gridName);
}*/
}
void HippoGridInfo::setGridNick(std::string gridNick)
{
mGridNick = sanitizeGridNick(gridNick);
if(mGridName.empty() && !gridNick.empty())
{
setGridName(gridNick);
}
}
void HippoGridInfo::setLoginUri(const std::string& loginUri)
@@ -265,21 +272,6 @@ void HippoGridInfo::setGridMessage(const std::string& message)
mGridMessage = message;
}
void HippoGridInfo::setFirstName(const std::string& firstName)
{
mFirstName = firstName;
}
void HippoGridInfo::setLastName(const std::string& lastName)
{
mLastName = lastName;
}
void HippoGridInfo::setAvatarPassword(const std::string& avatarPassword)
{
mAvatarPassword = avatarPassword;
}
void HippoGridInfo::setRenderCompat(bool compat)
{
mRenderCompat = compat;
@@ -376,25 +368,25 @@ std::string HippoGridInfo::getSearchUrl(SearchType ty, bool is_web) const
}
else
{
// Use the old search all
if (ty == SEARCH_ALL_EMPTY)
// Use the old search all
if (ty == SEARCH_ALL_EMPTY)
{
return (mSearchUrl + "panel=All&");
}
return (mSearchUrl + "panel=All&");
}
else if (ty == SEARCH_ALL_QUERY)
{
return (mSearchUrl + "q=[QUERY]&s=[COLLECTION]&");
}
return (mSearchUrl + "q=[QUERY]&s=[COLLECTION]&");
}
else if (ty == SEARCH_ALL_TEMPLATE)
{
return "lang=[LANG]&m=[MATURITY]&t=[TEEN]&region=[REGION]&x=[X]&y=[Y]&z=[Z]&session=[SESSION]";
}
return "lang=[LANG]&m=[MATURITY]&t=[TEEN]&region=[REGION]&x=[X]&y=[Y]&z=[Z]&session=[SESSION]";
}
else
{
llinfos << "Illegal search URL type " << ty << llendl;
return "";
}
}
llinfos << "Illegal search URL type " << ty << llendl;
return "";
}
}
}
@@ -402,7 +394,9 @@ std::string HippoGridInfo::getSearchUrl(SearchType ty, bool is_web) const
void HippoGridInfo::onXmlElementStart(void* userData, const XML_Char* name, const XML_Char** atts)
{
HippoGridInfo* self = (HippoGridInfo*)userData;
if (strcasecmp(name, "gridname") == 0)
if (strcasecmp(name, "gridnick") == 0)
self->mXmlState = XML_GRIDNICK;
else if (strcasecmp(name, "gridname") == 0)
self->mXmlState = XML_GRIDNAME;
else if (strcasecmp(name, "platform") == 0)
self->mXmlState = XML_PLATFORM;
@@ -439,28 +433,35 @@ void HippoGridInfo::onXmlCharacterData(void* userData, const XML_Char* s, int le
HippoGridInfo* self = (HippoGridInfo*)userData;
switch (self->mXmlState)
{
case XML_PLATFORM:
case XML_GRIDNICK:
{
if (self->mGridNick == "") self->mGridNick.assign(s, len);
self->mGridNick = sanitizeGridNick(self->mGridNick);
break;
}
case XML_PLATFORM:
{
std::string platform(s, len);
self->setPlatform(platform);
break;
}
case XML_LOGINURI:
case XML_LOGINURI:
{
std::string loginuri(s, len);
self->mLoginUri = sanitizeUri( loginuri );
break;
}
case XML_HELPERURI:
case XML_HELPERURI:
{
std::string helperuri(s, len);
self->mHelperUri = sanitizeUri( helperuri );
break;
}
case XML_SEARCH:
case XML_SEARCH:
{
self->mSearchUrl.assign(s, len);
//sanitizeQueryUrl(mSearchUrl);
@@ -547,6 +548,41 @@ void HippoGridInfo::formatFee(std::string &fee, int cost, bool showFree) const
}
}
//static
std::string HippoGridInfo::sanitizeGridNick(std::string &gridnick)
{
std::string tmp;
int size = gridnick.size();
for (int i=0; i<size; i++)
{
char c = gridnick[i];
if ((c == '_') || isalnum(c))
{
tmp += tolower(c);
}
else if (isspace(c))
{
tmp += "_";
}
}
if(tmp.length() > 16) {
tmp.resize(16);
}
return tmp;
}
std::string HippoGridInfo::getGridNick()
{
if(!mGridNick.empty())
{
return mGridNick;
}
else
{
return sanitizeGridNick(mGridName);
}
}
// ********************************************************************
// Static Helpers
@@ -589,14 +625,16 @@ void HippoGridInfo::initFallback()
FALLBACK_GRIDINFO.setHelperUri("http://127.0.0.1:9000/");
}
bool HippoGridInfo::supportsInvLinks(){
bool HippoGridInfo::supportsInvLinks()
{
if(isSecondLife())
return true;
else
return mInvLinks;
}
void HippoGridInfo::setSupportsInvLinks(bool b) {
void HippoGridInfo::setSupportsInvLinks(bool b)
{
if (b == true && mInvLinks == false)
{
llinfos << "Inventory Link support detected" << llendl;
@@ -604,6 +642,19 @@ void HippoGridInfo::setSupportsInvLinks(bool b) {
mInvLinks = b;
}
bool HippoGridInfo::getAutoUpdate()
{
if(isSecondLife())
return false;
else
return mAutoUpdate;
}
void HippoGridInfo::setAutoUpdate(bool b)
{
mAutoUpdate = b;
}
// ********************************************************************
// ********************************************************************
// HippoGridManager
@@ -615,8 +666,8 @@ void HippoGridInfo::setSupportsInvLinks(bool b) {
// Initialize
HippoGridManager::HippoGridManager() :
mConnectedGrid(0),
mDefaultGridsVersion(0),
mConnectedGrid(0),
mDefaultGridsVersion(0),
mCurrentGrid("Local Host"),
mDefaultGrid("Local Host")
{
@@ -759,13 +810,13 @@ void HippoGridManager::setDefaultGrid(const std::string& grid)
{
mDefaultGrid = grid;
}
else if (mGridInfo.find("secondlife") != mGridInfo.end())
else if (mGridInfo.find("Second life") != mGridInfo.end())
{
mDefaultGrid = "secondlife";
mDefaultGrid = "Second Life";
}
else if (!mGridInfo.empty())
{
mDefaultGrid = mGridInfo.begin()->first;
mDefaultGrid = mGridInfo.begin()->first;
}
else
{
@@ -784,7 +835,7 @@ void HippoGridManager::setCurrentGrid(const std::string& grid)
else if (!mGridInfo.empty())
{
llwarns << "Unknown grid '" << grid << "'. Setting to default grid." << llendl;
mCurrentGrid = mDefaultGrid;
mCurrentGrid = mDefaultGrid;
}
}
@@ -796,7 +847,7 @@ void HippoGridManager::loadFromFile()
{
mDefaultGridsVersion = 0;
// load user grid info
parseFile(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "grids_sg2.xml"), false);
parseFile(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "grids_sg1.xml"), false);
// merge default grid info, if newer. Force load, if list of grids is empty.
parseFile(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "default_grids.xml"), !mGridInfo.empty());
// merge grid info from web site, if newer. Force load, if list of grids is empty.
@@ -886,14 +937,32 @@ void HippoGridManager::parseData(LLSD &gridInfo, bool mergeIfNewer)
{
mDefaultGridsVersion = gridMap["default_grids_version"];
}
else if (gridMap.has("gridname") && gridMap.has("loginuri"))
else if ((gridMap.has("gridnick") || gridMap.has("gridname")) && gridMap.has("loginuri"))
{
std::string gridnick = gridMap["gridnick"];
std::string gridname = gridMap["gridname"];
HippoGridInfo* grid;
GridIterator it = mGridInfo.find(gridname);
bool newGrid = (it == mGridInfo.end());
if (newGrid)
GridIterator it = mGridInfo.end();
for (it = mGridInfo.begin(); it != mGridInfo.end(); ++it)
{
if(!gridnick.empty() && (it->second->getGridNick() == gridnick))
{
break;
}
if(gridnick.empty() && !gridname.empty() && (it->first == gridname))
{
break;
}
}
bool newGrid = (it == mGridInfo.end());
if (newGrid || !it->second)
{
if(gridname.empty())
{
gridname = gridnick;
}
// create new grid info
grid = new HippoGridInfo(gridname);
}
@@ -905,6 +974,7 @@ void HippoGridManager::parseData(LLSD &gridInfo, bool mergeIfNewer)
grid->setLoginUri(gridMap["loginuri"]);
if (gridMap.has("platform")) grid->setPlatform(gridMap["platform"]);
if (gridMap.has("gridname")) grid->setGridName(gridMap["gridname"]);
if (gridMap.has("gridnick")) grid->setGridNick(gridMap["gridnick"]);
if (gridMap.has("loginpage")) grid->setLoginPage(gridMap["loginpage"]);
if (gridMap.has("helperuri")) grid->setHelperUri(gridMap["helperuri"]);
if (gridMap.has("website")) grid->setWebSite(gridMap["website"]);
@@ -913,10 +983,8 @@ void HippoGridManager::parseData(LLSD &gridInfo, bool mergeIfNewer)
if (gridMap.has("password")) grid->setPasswordUrl(gridMap["password"]);
if (gridMap.has("search")) grid->setSearchUrl(gridMap["search"]);
if (gridMap.has("render_compat")) grid->setRenderCompat(gridMap["render_compat"]);
if (gridMap.has("inventory_links")) grid->setSupportsInvLinks(gridMap["inventory_links"]);
// if (gridMap.has("firstname")) grid->setFirstName(gridMap["firstname"]);
// if (gridMap.has("lastname")) grid->setLastName(gridMap["lastname"]);
// if (gridMap.has("avatarpassword")) grid->setAvatarPassword(gridMap["avatarpassword"]);
if (gridMap.has("inventory_links")) grid->setSupportsInvLinks(gridMap["inventory_links"]);
if (gridMap.has("auto_update")) grid->setAutoUpdate(gridMap["auto_update"]);
if (newGrid) addGrid(grid);
}
}
@@ -939,6 +1007,7 @@ void HippoGridManager::saveFile()
{
HippoGridInfo* grid = it->second;
gridInfo[i]["platform"] = HippoGridInfo::getPlatformString(grid->getPlatform());
gridInfo[i]["gridnick"] = grid->getGridNick();
gridInfo[i]["gridname"] = grid->getGridName();
gridInfo[i]["loginuri"] = grid->getLoginUri();
gridInfo[i]["loginpage"] = grid->getLoginPage();
@@ -947,17 +1016,15 @@ void HippoGridManager::saveFile()
gridInfo[i]["support"] = grid->getSupportUrl();
gridInfo[i]["register"] = grid->getRegisterUrl();
gridInfo[i]["password"] = grid->getPasswordUrl();
// gridInfo[i]["firstname"] = grid->getFirstName();
// gridInfo[i]["lastname"] = grid->getLastName();
// gridInfo[i]["avatarpassword"] = grid->getAvatarPassword();
gridInfo[i]["search"] = grid->getSearchUrl();
gridInfo[i]["render_compat"] = grid->isRenderCompat();
gridInfo[i]["inventory_links"] = grid->supportsInvLinks();
gridInfo[i]["auto_update"] = grid->getAutoUpdate();
}
// write client grid info file
std::string fileName = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "grids_sg2.xml");
std::string fileName = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "grids_sg1.xml");
llofstream file;
file.open(fileName.c_str());
if (file.is_open())

View File

@@ -51,12 +51,10 @@ public:
// Returns the url base used for the Web Search tab
const std::string& getSearchUrl() const;
const std::string& getGridMessage() const;
const std::string& getFirstName() const;
const std::string& getLastName() const;
const std::string& getAvatarPassword() const;
const std::string& getVoiceConnector() const { return mVoiceConnector; }
std::string getSearchUrl(SearchType ty, bool is_web) const;
bool isRenderCompat() const;
std::string getGridNick();
int getMaxAgentGroups() const { return mMaxAgentGroups; }
const std::string& getCurrencySymbol() const;
@@ -67,6 +65,7 @@ public:
void setPlatform (const std::string& platform);
void setPlatform (Platform platform);
void setGridNick (std::string gridNick);
void setGridName (const std::string& gridName);
void setLoginUri (const std::string& loginUri);
void setLoginPage(const std::string& loginPage);
@@ -80,9 +79,6 @@ public:
void setGridMessage(const std::string& message);
void setRenderCompat(bool compat);
void setMaxAgentGroups(int max) { mMaxAgentGroups = max; }
void setFirstName(const std::string& firstName);
void setLastName(const std::string& lastName);
void setAvatarPassword(const std::string& avatarPassword);
void setVoiceConnector(const std::string& vc) { mVoiceConnector = vc; }
void setCurrencySymbol(const std::string& sym);
@@ -90,10 +86,13 @@ public:
void setDirectoryFee(int fee);
bool supportsInvLinks();
void setSupportsInvLinks(bool b);
bool getAutoUpdate();
void setAutoUpdate(bool b);
bool retrieveGridInfo();
static const char* getPlatformString(Platform platform);
static std::string sanitizeGridNick(std::string &gridnick);
static HippoGridInfo FALLBACK_GRIDINFO;
static void initFallback();
@@ -101,6 +100,7 @@ public:
private:
Platform mPlatform;
std::string mGridName;
std::string mGridNick;
std::string mLoginUri;
std::string mLoginPage;
std::string mHelperUri;
@@ -110,11 +110,9 @@ private:
std::string mPasswordUrl;
std::string mSearchUrl;
std::string mVoiceConnector;
std::string mFirstName;
std::string mLastName;
std::string mAvatarPassword;
bool mRenderCompat;
bool mInvLinks;
bool mAutoUpdate;
int mMaxAgentGroups;
std::string mCurrencySymbol;
@@ -125,7 +123,7 @@ private:
// for parsing grid info XML
enum XmlState
{
XML_VOID, XML_PLATFORM, XML_GRIDNAME,
XML_VOID, XML_PLATFORM, XML_GRIDNAME, XML_GRIDNICK,
XML_LOGINURI, XML_LOGINPAGE, XML_HELPERURI,
XML_WEBSITE, XML_SUPPORT, XML_REGISTER, XML_PASSWORD, XML_SEARCH, XML_MESSAGE
};
@@ -168,6 +166,7 @@ public:
GridIterator endGrid() { return mGridInfo.end(); }
private:
friend class HippoGridInfo;
std::map<std::string, HippoGridInfo*> mGridInfo;
std::string mDefaultGrid;
std::string mCurrentGrid;

View File

@@ -295,14 +295,21 @@ bool HippoPanelGridsImpl::saveCurGrid()
HippoGridInfo *gridInfo = 0;
gridInfo = gHippoGridManager->getGrid(mCurGrid);
gridInfo->retrieveGridInfo();
//gridInfo->retrieveGridInfo();
refresh();
std::string gridname = childGetValue("gridname");
if (gridname == "<required>") gridname = "";
std::string loginuri = childGetValue("loginuri");
if (loginuri == "<required>") loginuri = "";
if (gridname.empty() && !loginuri.empty())
this->retrieveGridInfo();
if ((mState == ADD_NEW) || (mState == ADD_COPY)) {
// check nickname
std::string gridname = childGetValue("gridname");
if (gridname == "<required>") gridname = "";
childSetValue("gridname", (gridname != "")? gridname: "<required>");
if (gridname == "") {
LLNotificationsUtil::add("GridsNoNick");
@@ -316,8 +323,7 @@ bool HippoPanelGridsImpl::saveCurGrid()
}
// check login URI
std::string loginuri = childGetValue("loginuri");
if ((loginuri == "") || (loginuri == "<required>")) {
if (loginuri == "") {
LLSD args;
args["NAME"] = gridname;
LLNotificationsUtil::add("GridsNoLoginUri", args);
@@ -329,11 +335,6 @@ bool HippoPanelGridsImpl::saveCurGrid()
gridInfo = new HippoGridInfo(gridname);
gHippoGridManager->addGrid(gridInfo);
gridInfo->retrieveGridInfo();
} else {
llwarns << "Illegal state " << mState << '.' << llendl;
return true;
}
if (!gridInfo) {

View File

@@ -57,6 +57,11 @@
#export LL_WRAPPER='gdb --args'
#export LL_WRAPPER='valgrind --smc-check=all --error-limit=no --log-file=secondlife.vg --leak-check=full --suppressions=/usr/lib/valgrind/glibc-2.5.supp --suppressions=secondlife-i686.supp'
## - This allows one to set an arbitrary value for LD_PRELOAD.
## It won't work if LL_TCMALLOC is set because that uses it's
## own value of LD_PRELOAD.
#export AI_PRELOAD='/path/to/libmemleak.so'
## - Avoids an often-buggy X feature that doesn't really benefit us anyway.
export SDL_VIDEO_X11_DGAMOUSE=0
@@ -96,6 +101,12 @@ cd "${RUN_PATH}"
## subprocesses that care.
export SAVED_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}"
SL_ENV=
if [ -n "$AI_PRELOAD" ]; then
SL_ENV='LD_PRELOAD="$AI_PRELOAD" '
fi
if [ -n "$LL_TCMALLOC" ]; then
tcmalloc_libs='/usr/lib/libtcmalloc.so.0 /usr/lib/libstacktrace.so.0 /lib/libpthread.so.0'
all=1
@@ -107,7 +118,7 @@ if [ -n "$LL_TCMALLOC" ]; then
if [ $all != 1 ]; then
echo 'Cannot use tcmalloc libraries: components missing' 1>&2
else
export LD_PRELOAD=$(echo $tcmalloc_libs | tr ' ' :)
SL_ENV='LD_PRELOAD="$(echo $tcmalloc_libs | tr '"' '"' :)" '
if [ -z "$HEAPCHECK" -a -z "$HEAPPROFILE" ]; then
export HEAPCHECK=${HEAPCHECK:-normal}
fi
@@ -119,10 +130,10 @@ BINARY_TYPE=$(expr match "$(file -b bin/$VIEWER_BINARY)" '\(.*executable\)')
QPP=qt4/plugins/imageformats/
if [ "${BINARY_TYPE}" == "ELF 64-bit LSB executable" ]; then
QTPLUGINS=/usr/lib64/$QPP:/lib64/$QPP:/usr/local/lib64/$QPP
export SL_ENV='LD_LIBRARY_PATH="`pwd`"/lib64:"`pwd`"/lib32:$QTPLUGINS:"${LD_LIBRARY_PATH}"'
SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib64:`pwd`/lib32:$QTPLUGINS:$LD_LIBRARY_PATH"'
else
QTPLUGINS=/usr/lib/$QPP:/lib/$QPP:/usr/local/lib/$QPP
export SL_ENV='LD_LIBRARY_PATH="`pwd`"/lib:$QTPLUGINS:"${LD_LIBRARY_PATH}"'
SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib:$QTPLUGINS:$LD_LIBRARY_PATH"'
fi
export SL_CMD='$LL_WRAPPER bin/$VIEWER_BINARY'

View File

@@ -57,6 +57,7 @@
#include "llnotificationsutil.h"
#include "llparcel.h"
#include "llrendersphere.h"
#include "llsdmessage.h"
#include "llsdutil.h"
#include "llsky.h"
#include "llsmoothstep.h"
@@ -2105,11 +2106,15 @@ LLVector3 ll_vector3_from_sdmap(const LLSD& sd)
void LLAgent::setStartPosition( U32 location_id )
{
LLViewerObject *object;
if (gAgentID == LLUUID::null)
{
return;
}
if (gObjectList.findAvatar(gAgentID) == NULL)
// we've got an ID for an agent viewerobject
object = gObjectList.findObject(gAgentID);
if (! object)
{
llinfos << "setStartPosition - Can't find agent viewerobject id " << gAgentID << llendl;
return;
@@ -2126,7 +2131,7 @@ void LLAgent::setStartPosition( U32 location_id )
if (isAgentAvatarValid())
{
// the z height is at the agent's feet
agent_pos.mV[VZ] -= 0.5f * gAgentAvatarp->mBodySize.mV[VZ];
agent_pos.mV[VZ] -= 0.5f * gAgentAvatarp->mBodySize.mV[VZ];
}
agent_pos.mV[VX] = llclamp( agent_pos.mV[VX], INSET, REGION_WIDTH - INSET );
@@ -2136,46 +2141,44 @@ void LLAgent::setStartPosition( U32 location_id )
agent_pos.mV[VZ] = llclamp( agent_pos.mV[VZ],
mRegionp->getLandHeightRegion( agent_pos ),
LLWorld::getInstance()->getRegionMaxHeight() );
std::string url = gAgent.getRegion()->getCapability("HomeLocation");
if( !url.empty() )
{
// Send the CapReq
LLSD request;
LLSD body;
LLSD homeLocation;
// Send the CapReq
LLSD request;
LLSD body;
LLSD homeLocation;
homeLocation["LocationId"] = LLSD::Integer(location_id);
homeLocation["LocationPos"] = ll_sdmap_from_vector3(agent_pos);
homeLocation["LocationLookAt"] = ll_sdmap_from_vector3(mFrameAgent.getAtAxis());
homeLocation["LocationId"] = LLSD::Integer(location_id);
homeLocation["LocationPos"] = ll_sdmap_from_vector3(agent_pos);
homeLocation["LocationLookAt"] = ll_sdmap_from_vector3(mFrameAgent.getAtAxis());
body["HomeLocation"] = homeLocation;
body["HomeLocation"] = homeLocation;
LLHTTPClient::post( url, body, new LLHomeLocationResponder() );
}
else
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_SetStartLocationRequest);
msg->nextBlockFast( _PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, getID());
msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
msg->nextBlockFast( _PREHASH_StartLocationData);
// corrected by sim
msg->addStringFast(_PREHASH_SimName, "");
msg->addU32Fast(_PREHASH_LocationID, location_id);
msg->addVector3Fast(_PREHASH_LocationPos, agent_pos);
msg->addVector3Fast(_PREHASH_LocationLookAt,mFrameAgent.getAtAxis());
// Reliable only helps when setting home location. Last
// location is sent on quit, and we don't have time to ack
// the packets.
msg->sendReliable(mRegionp->getHost());
}
const U32 HOME_INDEX = 1;
if( HOME_INDEX == location_id )
{
setHomePosRegion( mRegionp->getHandle(), getPositionAgent() );
}
// This awkward idiom warrants explanation.
// For starters, LLSDMessage::ResponderAdapter is ONLY for testing the new
// LLSDMessage functionality with a pre-existing LLHTTPClient::Responder.
// In new code, define your reply/error methods on the same class as the
// sending method, bind them to local LLEventPump objects and pass those
// LLEventPump names in the request LLSD object.
// When testing old code, the new LLHomeLocationResponder object
// is referenced by an LLHTTPClient::ResponderPtr, so when the
// ResponderAdapter is deleted, the LLHomeLocationResponder will be too.
// We must trust that the underlying LLHTTPClient code will eventually
// fire either the reply callback or the error callback; either will cause
// the ResponderAdapter to delete itself.
LLSDMessage::ResponderAdapter*
adapter(new LLSDMessage::ResponderAdapter(new LLHomeLocationResponder()));
request["message"] = "HomeLocation";
request["payload"] = body;
request["reply"] = adapter->getReplyName();
request["error"] = adapter->getErrorName();
gAgent.getRegion()->getCapAPI().post(request);
const U32 HOME_INDEX = 1;
if( HOME_INDEX == location_id )
{
setHomePosRegion( mRegionp->getHandle(), getPositionAgent() );
}
}
void LLAgent::requestStopMotion( LLMotion* motion )
@@ -2818,7 +2821,7 @@ void update_group_floaters(const LLUUID& group_id)
gIMMgr->refresh();
}
gAgent.fireEvent(new LLEvent(&gAgent, "new group"), "");
gAgent.fireEvent(new LLOldEvents::LLEvent(&gAgent, "new group"), "");
}
// static
@@ -3982,7 +3985,10 @@ void LLAgent::sendAgentSetAppearance()
}
}
// llinfos << "Avatar XML num VisualParams transmitted = " << transmitted_params << llendl;
llinfos << "Avatar XML num VisualParams transmitted = " << transmitted_params << llendl;
if(transmitted_params < 218) {
LLNotificationsUtil::add("SGIncompleteAppearence");
}
sendReliableMessage();
}

View File

@@ -92,7 +92,7 @@ struct LLGroupData
//
class LLAgent : public LLObservable
class LLAgent : public LLOldEvents::LLObservable
{
LOG_CLASS(LLAgent);

View File

@@ -36,7 +36,7 @@
#include "llmemory.h" // LLSingleton<>
#include "llevent.h"
class LLAgentLanguage: public LLSingleton<LLAgentLanguage>, public LLSimpleListener
class LLAgentLanguage: public LLSingleton<LLAgentLanguage>, public LLOldEvents::LLSimpleListener
{
public:
LLAgentLanguage();

View File

@@ -37,7 +37,7 @@
#include "hippogridmanager.h"
#include "hippolimits.h"
#include "llversionviewer.h"
#include "sgversion.h"
#include "llfeaturemanager.h"
#include "lluictrlfactory.h"
#include "lltexteditor.h"
@@ -643,11 +643,11 @@ bool LLAppViewer::init()
// Build a string representing the current version number.
gCurrentVersion = llformat("%s %d.%d.%d.%d",
LL_CHANNEL,
LL_VERSION_MAJOR,
LL_VERSION_MINOR,
LL_VERSION_PATCH,
LL_VERSION_BUILD );
gVersionChannel,
gVersionMajor,
gVersionMinor,
gVersionPatch,
gVersionBuild );
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
@@ -2045,7 +2045,7 @@ bool LLAppViewer::initConfiguration()
gSavedSettings.setString("ClientSettingsFile",
gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Global")));
gSavedSettings.setString("VersionChannelName", LL_CHANNEL);
gSavedSettings.setString("VersionChannelName", gVersionChannel);
#ifndef LL_RELEASE_FOR_DOWNLOAD
// provide developer build only overrides for these control variables that are not
@@ -2654,12 +2654,12 @@ void LLAppViewer::writeSystemInfo()
{
gDebugInfo["SLLog"] = LLError::logFileName();
gDebugInfo["ClientInfo"]["Name"] = LL_CHANNEL;
gDebugInfo["ClientInfo"]["Name"] = gVersionChannel;
gDebugInfo["ClientInfo"]["MajorVersion"] = LL_VERSION_MAJOR;
gDebugInfo["ClientInfo"]["MinorVersion"] = LL_VERSION_MINOR;
gDebugInfo["ClientInfo"]["PatchVersion"] = LL_VERSION_PATCH;
gDebugInfo["ClientInfo"]["BuildVersion"] = LL_VERSION_BUILD;
gDebugInfo["ClientInfo"]["MajorVersion"] = gVersionMajor;
gDebugInfo["ClientInfo"]["MinorVersion"] = gVersionMinor;
gDebugInfo["ClientInfo"]["PatchVersion"] = gVersionPatch;
gDebugInfo["ClientInfo"]["BuildVersion"] = gVersionBuild;
gDebugInfo["CAFilename"] = gDirUtilp->getCAFile();
@@ -2694,7 +2694,7 @@ void LLAppViewer::writeSystemInfo()
// Dump some debugging info
LL_INFOS("SystemInfo") << LLTrans::getString("APP_NAME")
<< " version " << LL_VERSION_MAJOR << "." << LL_VERSION_MINOR << "." << LL_VERSION_PATCH
<< " version " << gVersionMajor << "." << gVersionMinor << "." << gVersionPatch
<< LL_ENDL;
// Dump the local time and time zone
@@ -2758,12 +2758,12 @@ void LLAppViewer::handleViewerCrash()
//We already do this in writeSystemInfo(), but we do it again here to make /sure/ we have a version
//to check against no matter what
gDebugInfo["ClientInfo"]["Name"] = LL_CHANNEL;
gDebugInfo["ClientInfo"]["Name"] = gVersionChannel;
gDebugInfo["ClientInfo"]["MajorVersion"] = LL_VERSION_MAJOR;
gDebugInfo["ClientInfo"]["MinorVersion"] = LL_VERSION_MINOR;
gDebugInfo["ClientInfo"]["PatchVersion"] = LL_VERSION_PATCH;
gDebugInfo["ClientInfo"]["BuildVersion"] = LL_VERSION_BUILD;
gDebugInfo["ClientInfo"]["MajorVersion"] = gVersionMajor;
gDebugInfo["ClientInfo"]["MinorVersion"] = gVersionMinor;
gDebugInfo["ClientInfo"]["PatchVersion"] = gVersionPatch;
gDebugInfo["ClientInfo"]["BuildVersion"] = gVersionBuild;
LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
if ( parcel && parcel->getMusicURL()[0])
@@ -4607,12 +4607,12 @@ void LLAppViewer::handleLoginComplete()
initMainloopTimeout("Mainloop Init");
// Store some data to DebugInfo in case of a freeze.
gDebugInfo["ClientInfo"]["Name"] = LL_CHANNEL;
gDebugInfo["ClientInfo"]["Name"] = gVersionChannel;
gDebugInfo["ClientInfo"]["MajorVersion"] = LL_VERSION_MAJOR;
gDebugInfo["ClientInfo"]["MinorVersion"] = LL_VERSION_MINOR;
gDebugInfo["ClientInfo"]["PatchVersion"] = LL_VERSION_PATCH;
gDebugInfo["ClientInfo"]["BuildVersion"] = LL_VERSION_BUILD;
gDebugInfo["ClientInfo"]["MajorVersion"] = gVersionMajor;
gDebugInfo["ClientInfo"]["MinorVersion"] = gVersionMinor;
gDebugInfo["ClientInfo"]["PatchVersion"] = gVersionPatch;
gDebugInfo["ClientInfo"]["BuildVersion"] = gVersionBuild;
LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
if ( parcel && parcel->getMusicURL()[0])

View File

@@ -0,0 +1,202 @@
/**
* @file llcapabilitylistener.cpp
* @author Nat Goodspeed
* @date 2009-01-07
* @brief Implementation for llcapabilitylistener.
*
* $LicenseInfo:firstyear=2009&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$
*/
// Precompiled header
#include "llviewerprecompiledheaders.h"
// associated header
#include "llcapabilitylistener.h"
// STL headers
#include <map>
// std headers
// external library headers
#include <boost/bind.hpp>
// other Linden headers
#include "stringize.h"
#include "llcapabilityprovider.h"
#include "message.h"
class LLCapabilityListener::CapabilityMappers: public LLSingleton<LLCapabilityListener::CapabilityMappers>
{
public:
void registerMapper(const LLCapabilityListener::CapabilityMapper*);
void unregisterMapper(const LLCapabilityListener::CapabilityMapper*);
const LLCapabilityListener::CapabilityMapper* find(const std::string& cap) const;
struct DupCapMapper: public std::runtime_error
{
DupCapMapper(const std::string& what):
std::runtime_error(std::string("DupCapMapper: ") + what)
{}
};
private:
friend class LLSingleton<LLCapabilityListener::CapabilityMappers>;
CapabilityMappers();
typedef std::map<std::string, const LLCapabilityListener::CapabilityMapper*> CapabilityMap;
CapabilityMap mMap;
};
LLCapabilityListener::LLCapabilityListener(const std::string& name,
LLMessageSystem* messageSystem,
const LLCapabilityProvider& provider,
const LLUUID& agentID,
const LLUUID& sessionID):
mEventPump(name),
mMessageSystem(messageSystem),
mProvider(provider),
mAgentID(agentID),
mSessionID(sessionID)
{
mEventPump.listen("self", boost::bind(&LLCapabilityListener::capListener, this, _1));
}
bool LLCapabilityListener::capListener(const LLSD& request)
{
// Extract what we want from the request object. We do it all up front
// partly to document what we expect.
LLSD::String cap(request["message"]);
LLSD payload(request["payload"]);
LLSD::String reply(request["reply"]);
LLSD::String error(request["error"]);
LLSD::Real timeout(request["timeout"]);
// If the LLSD doesn't even have a "message" key, we doubt it was intended
// for this listener.
if (cap.empty())
{
LL_ERRS("capListener") << "capability request event without 'message' key to '"
<< getCapAPI().getName()
<< "' on region\n" << mProvider.getDescription()
<< LL_ENDL;
return false; // in case fatal-error function isn't
}
// Establish default timeout. This test relies on LLSD::asReal() returning
// exactly 0.0 for an undef value.
if (! timeout)
{
timeout = HTTP_REQUEST_EXPIRY_SECS;
}
// Look up the url for the requested capability name.
std::string url = mProvider.getCapability(cap);
if (! url.empty())
{
// This capability is supported by the region to which we're talking.
LLHTTPClient::post(url, payload,
new LLSDMessage::EventResponder(LLEventPumps::instance(),
request,
mProvider.getDescription(),
cap, reply, error),
LLSD(), // headers
timeout);
}
else
{
// Capability not supported -- do we have a registered mapper?
const CapabilityMapper* mapper = CapabilityMappers::instance().find(cap);
if (! mapper) // capability neither supported nor mapped
{
LL_ERRS("capListener") << "unsupported capability '" << cap << "' request to '"
<< getCapAPI().getName() << "' on region\n"
<< mProvider.getDescription()
<< LL_ENDL;
}
else if (! mapper->getReplyName().empty()) // mapper expects reply support
{
LL_ERRS("capListener") << "Mapper for capability '" << cap
<< "' requires unimplemented support for reply message '"
<< mapper->getReplyName()
<< "' on '" << getCapAPI().getName() << "' on region\n"
<< mProvider.getDescription()
<< LL_ENDL;
}
else
{
LL_INFOS("capListener") << "fallback invoked for capability '" << cap
<< "' request to '" << getCapAPI().getName()
<< "' on region\n" << mProvider.getDescription()
<< LL_ENDL;
mapper->buildMessage(mMessageSystem, mAgentID, mSessionID, cap, payload);
mMessageSystem->sendReliable(mProvider.getHost());
}
}
return false;
}
LLCapabilityListener::CapabilityMapper::CapabilityMapper(const std::string& cap, const std::string& reply):
mCapName(cap),
mReplyName(reply)
{
LLCapabilityListener::CapabilityMappers::instance().registerMapper(this);
}
LLCapabilityListener::CapabilityMapper::~CapabilityMapper()
{
LLCapabilityListener::CapabilityMappers::instance().unregisterMapper(this);
}
LLSD LLCapabilityListener::CapabilityMapper::readResponse(LLMessageSystem* messageSystem) const
{
return LLSD();
}
LLCapabilityListener::CapabilityMappers::CapabilityMappers() {}
void LLCapabilityListener::CapabilityMappers::registerMapper(const LLCapabilityListener::CapabilityMapper* mapper)
{
// Try to insert a new map entry by which we can look up the passed mapper
// instance.
std::pair<CapabilityMap::iterator, bool> inserted =
mMap.insert(CapabilityMap::value_type(mapper->getCapName(), mapper));
// If we already have a mapper for that name, insert() merely located the
// existing iterator and returned false. It is a coding error to try to
// register more than one mapper for the same capability name.
if (! inserted.second)
{
throw DupCapMapper(std::string("Duplicate capability name ") + mapper->getCapName());
}
}
void LLCapabilityListener::CapabilityMappers::unregisterMapper(const LLCapabilityListener::CapabilityMapper* mapper)
{
CapabilityMap::iterator found = mMap.find(mapper->getCapName());
if (found != mMap.end())
{
mMap.erase(found);
}
}
const LLCapabilityListener::CapabilityMapper*
LLCapabilityListener::CapabilityMappers::find(const std::string& cap) const
{
CapabilityMap::const_iterator found = mMap.find(cap);
if (found != mMap.end())
{
return found->second;
}
return NULL;
}

View File

@@ -0,0 +1,131 @@
/**
* @file llcapabilitylistener.h
* @author Nat Goodspeed
* @date 2009-01-07
* @brief Provide an event-based API for capability requests
*
* $LicenseInfo:firstyear=2009&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$
*/
#if ! defined(LL_LLCAPABILITYLISTENER_H)
#define LL_LLCAPABILITYLISTENER_H
#include "llevents.h" // LLEventPump
#include "llsdmessage.h" // LLSDMessage::ArgError
#include "llerror.h" // LOG_CLASS()
class LLCapabilityProvider;
class LLMessageSystem;
class LLSD;
class LLCapabilityListener
{
LOG_CLASS(LLCapabilityListener);
public:
LLCapabilityListener(const std::string& name, LLMessageSystem* messageSystem,
const LLCapabilityProvider& provider,
const LLUUID& agentID, const LLUUID& sessionID);
/// Capability-request exception
typedef LLSDMessage::ArgError ArgError;
/// Get LLEventPump on which we listen for capability requests
/// (https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes#Capabilities)
LLEventPump& getCapAPI() { return mEventPump; }
/**
* Base class for mapping an as-yet-undeployed capability name to a (pair
* of) LLMessageSystem message(s). To map a capability name to such
* messages, derive a subclass of CapabilityMapper and declare a static
* instance in a translation unit known to be loaded. The mapping is not
* region-specific. If an LLViewerRegion's capListener() receives a
* request for a supported capability, it will use the capability's URL.
* If not, it will look for an applicable CapabilityMapper subclass
* instance.
*/
class CapabilityMapper
{
public:
/**
* Base-class constructor. Typically your subclass constructor will
* pass these parameters as literals.
* @param cap the capability name handled by this (subclass) instance
* @param reply the name of the response LLMessageSystem message. Omit
* if the LLMessageSystem message you intend to send doesn't prompt a
* reply message, or if you already handle that message in some other
* way.
*/
CapabilityMapper(const std::string& cap, const std::string& reply = "");
virtual ~CapabilityMapper();
/// query the capability name
std::string getCapName() const { return mCapName; }
/// query the reply message name
std::string getReplyName() const { return mReplyName; }
/**
* Override this method to build the LLMessageSystem message we should
* send instead of the requested capability message. DO NOT send that
* message: that will be handled by the caller.
*/
virtual void buildMessage(LLMessageSystem* messageSystem,
const LLUUID& agentID,
const LLUUID& sessionID,
const std::string& capabilityName,
const LLSD& payload) const = 0;
/**
* Override this method if you pass a non-empty @a reply
* LLMessageSystem message name to the constructor: that is, if you
* expect to receive an LLMessageSystem message in response to the
* message you constructed in buildMessage(). If you don't pass a @a
* reply message name, you need not override this method as it won't
* be called.
*
* Using LLMessageSystem message-reading operations, your
* readResponse() override should construct and return an LLSD object
* of the form you expect to receive from the real implementation of
* the capability you intend to invoke, when it finally goes live.
*/
virtual LLSD readResponse(LLMessageSystem* messageSystem) const;
private:
const std::string mCapName;
const std::string mReplyName;
};
private:
/// Bind the LLCapabilityProvider passed to our ctor
const LLCapabilityProvider& mProvider;
/// Post an event to this LLEventPump to invoke a capability message on
/// the bound LLCapabilityProvider's server
/// (https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes#Capabilities)
LLEventStream mEventPump;
LLMessageSystem* mMessageSystem;
LLUUID mAgentID, mSessionID;
/// listener to process capability requests
bool capListener(const LLSD&);
/// helper class for capListener()
class CapabilityMappers;
};
#endif /* ! defined(LL_LLCAPABILITYLISTENER_H) */

View File

@@ -41,7 +41,6 @@
#include "lldrawpoolalpha.h"
#include "lldrawpoolavatar.h"
#include "lldrawpoolbump.h"
#include "lldrawpoolclouds.h"
#include "lldrawpoolground.h"
#include "lldrawpoolsimple.h"
#include "lldrawpoolsky.h"

View File

@@ -139,6 +139,7 @@ public:
PASS_ALPHA,
PASS_ALPHA_MASK,
PASS_FULLBRIGHT_ALPHA_MASK,
PASS_ALPHA_INVISIBLE,
NUM_RENDER_TYPES,
};

View File

@@ -344,6 +344,7 @@ void LLDrawPoolAlpha::render(S32 pass)
pushBatches(LLRenderPass::PASS_ALPHA_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE);
pushBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE);
pushBatches(LLRenderPass::PASS_ALPHA_INVISIBLE, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE);
if(shaders)
{

View File

@@ -1589,7 +1589,11 @@ void LLDrawPoolInvisible::endDeferredPass( S32 pass )
void LLDrawPoolInvisible::renderDeferred( S32 pass )
{ //render invisiprims; this doesn't work becaue it also blocks all the post-deferred stuff
#if 0
static const LLCachedControl<bool> enable("SianaRenderDeferredInvisiprim");
if (!enable) {
return;
}
LLFastTimer t(FTM_RENDER_INVISIBLE);
U32 invisi_mask = LLVertexBuffer::MAP_VERTEX;
@@ -1607,5 +1611,4 @@ void LLDrawPoolInvisible::renderDeferred( S32 pass )
renderShiny(true);
endShiny(true);
}
#endif
}

View File

@@ -1,103 +0,0 @@
/**
* @file lldrawpoolclouds.cpp
* @brief LLDrawPoolClouds class implementation
*
* $LicenseInfo:firstyear=2006&license=viewergpl$
*
* Copyright (c) 2006-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "lldrawpoolclouds.h"
#include "llface.h"
#include "llsky.h"
#include "llviewercamera.h"
#include "llvoclouds.h"
#include "pipeline.h"
LLDrawPoolClouds::LLDrawPoolClouds() :
LLDrawPool(POOL_CLOUDS)
{
}
LLDrawPool *LLDrawPoolClouds::instancePool()
{
return new LLDrawPoolClouds();
}
BOOL LLDrawPoolClouds::addFace(LLFace* face)
{
llerrs << "WTF?" << llendl;
return FALSE;
}
void LLDrawPoolClouds::enqueue(LLFace *facep)
{
mDrawFace.push_back(facep);
facep->mDistance = (facep->mCenterAgent - gCamera->getOrigin()) * gCamera->getAtAxis();
}
void LLDrawPoolClouds::beginRenderPass(S32 pass)
{
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
}
void LLDrawPoolClouds::prerender()
{
mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT);
}
void LLDrawPoolClouds::render(S32 pass)
{
LLFastTimer ftm(FTM_RENDER_CLOUDS);
if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS)))
{
return;
}
if (mDrawFace.empty())
{
return;
}
LLGLSPipelineAlpha gls_pipeline_alpha;
LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
glAlphaFunc(GL_GREATER,0.01f);
gPipeline.enableLightsFullbright(LLColor4(1.f,1.f,1.f));
mDrawFace[0]->bindTexture();
std::sort(mDrawFace.begin(), mDrawFace.end(), LLFace::CompareDistanceGreater());
drawLoop();
}

View File

@@ -1,60 +0,0 @@
/**
* @file lldrawpoolclouds.h
* @brief LLDrawPoolClouds class definition
*
* $LicenseInfo:firstyear=2006&license=viewergpl$
*
* Copyright (c) 2006-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLDRAWPOOLCLOUDS_H
#define LL_LLDRAWPOOLCLOUDS_H
#include "lldrawpool.h"
class LLDrawPoolClouds : public LLDrawPool
{
public:
enum
{
VERTEX_DATA_MASK = LLVertexBuffer::MAP_VERTEX |
LLVertexBuffer::MAP_NORMAL |
LLVertexBuffer::MAP_TEXCOORD0
};
BOOL addFace(LLFace* face);
virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; }
LLDrawPoolClouds();
/*virtual*/ void prerender();
/*virtual*/ LLDrawPool *instancePool();
/*virtual*/ void enqueue(LLFace *face);
/*virtual*/ void beginRenderPass(S32 pass);
/*virtual*/ void render(S32 pass = 0);
};
#endif // LL_LLDRAWPOOLSKY_H

View File

@@ -49,7 +49,7 @@
#include "llagent.h"
#include "llviewerstats.h"
#include "llviewerregion.h"
#include "llversionviewer.h"
#include "sgversion.h"
#include "llviewerbuild.h"
#include "lluictrlfactory.h"
#include "lluri.h"
@@ -137,9 +137,9 @@ LLFloaterAbout::LLFloaterAbout()
// Version string
std::string version = std::string(LLAppViewer::instance()->getSecondLifeTitle()
+ llformat(" %d.%d.%d (%d) %s %s (%s)\n",
LL_VERSION_MAJOR, LL_VERSION_MINOR, LL_VERSION_PATCH, LL_VIEWER_BUILD,
gVersionMajor, gVersionMinor, gVersionPatch, LL_VIEWER_BUILD,
__DATE__, __TIME__,
LL_CHANNEL));
gVersionChannel));
support_widget->appendColoredText(version, FALSE, FALSE, gColors.getColor("TextFgReadOnlyColor"));
support_widget->appendStyledText(LLTrans::getString("ReleaseNotes"), false, false, viewer_link_style);
@@ -331,13 +331,13 @@ static std::string get_viewer_release_notes_url()
{
return "http://www.singularityviewer.org";
/*std::ostringstream version;
version << LL_VERSION_MAJOR
<< "." << LL_VERSION_MINOR
<< "." << LL_VERSION_PATCH
<< "." << LL_VERSION_BUILD;
version << gVersionMajor
<< "." << gVersionMinor
<< "." << gVersionPatch
<< "." << gVersionBuild;
LLSD query;
query["channel"] = LL_CHANNEL;
query["channel"] = gVersionChannel;
query["version"] = version.str();

View File

@@ -57,6 +57,8 @@
#include "llavatarname.h"
using namespace LLOldEvents;
const F32 SPEAKER_TIMEOUT = 10.f; // seconds of not being on voice channel before removed from list of active speakers
const F32 RESORT_TIMEOUT = 5.f; // seconds of mouse inactivity before it's ok to sort regardless of mouse-in-view.
const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f);

View File

@@ -49,7 +49,7 @@ class LLVoiceChannel;
// data for a given participant in a voice channel
class LLSpeaker : public LLRefCount, public LLObservable, public LLHandleProvider<LLSpeaker>
class LLSpeaker : public LLRefCount, public LLOldEvents::LLObservable, public LLHandleProvider<LLSpeaker>
{
public:
typedef enum e_speaker_type
@@ -96,21 +96,21 @@ public:
std::string mLegacyName;
};
class LLSpeakerTextModerationEvent : public LLEvent
class LLSpeakerTextModerationEvent : public LLOldEvents::LLEvent
{
public:
LLSpeakerTextModerationEvent(LLSpeaker* source);
/*virtual*/ LLSD getValue();
};
class LLSpeakerVoiceModerationEvent : public LLEvent
class LLSpeakerVoiceModerationEvent : public LLOldEvents::LLEvent
{
public:
LLSpeakerVoiceModerationEvent(LLSpeaker* source);
/*virtual*/ LLSD getValue();
};
class LLSpeakerListChangeEvent : public LLEvent
class LLSpeakerListChangeEvent : public LLOldEvents::LLEvent
{
public:
LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id);
@@ -120,7 +120,7 @@ private:
const LLUUID& mSpeakerID;
};
class LLSpeakerMgr : public LLObservable
class LLSpeakerMgr : public LLOldEvents::LLObservable
{
public:
LLSpeakerMgr(LLVoiceChannel* channelp);
@@ -237,46 +237,46 @@ public:
static void onChangeModerationMode(LLUICtrl* ctrl, void* user_data);
protected:
class SpeakerMuteListener : public LLSimpleListener
class SpeakerMuteListener : public LLOldEvents::LLSimpleListener
{
public:
SpeakerMuteListener(LLPanelActiveSpeakers* panel) : mPanel(panel) {}
/*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
/*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
LLPanelActiveSpeakers* mPanel;
};
friend class SpeakerAddListener;
class SpeakerAddListener : public LLSimpleListener
class SpeakerAddListener : public LLOldEvents::LLSimpleListener
{
public:
SpeakerAddListener(LLPanelActiveSpeakers* panel) : mPanel(panel) {}
/*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
/*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
LLPanelActiveSpeakers* mPanel;
};
friend class SpeakerRemoveListener;
class SpeakerRemoveListener : public LLSimpleListener
class SpeakerRemoveListener : public LLOldEvents::LLSimpleListener
{
public:
SpeakerRemoveListener(LLPanelActiveSpeakers* panel) : mPanel(panel) {}
/*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
/*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
LLPanelActiveSpeakers* mPanel;
};
friend class SpeakerClearListener;
class SpeakerClearListener : public LLSimpleListener
class SpeakerClearListener : public LLOldEvents::LLSimpleListener
{
public:
SpeakerClearListener(LLPanelActiveSpeakers* panel) : mPanel(panel) {}
/*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
/*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
LLPanelActiveSpeakers* mPanel;
};

View File

@@ -57,6 +57,10 @@
#include "llsdutil.h"
#include "llaudioengine.h"
#include "llstartup.h"
//<edit>
#include "llviewermenu.h"
//</edit>
@@ -125,7 +129,8 @@ void chat_avatar_status(std::string name, LLUUID key, ERadarAlertType type, bool
LLAvatarListEntry::LLAvatarListEntry(const LLUUID& id, const std::string &name, const LLVector3d &position) :
mID(id), mName(name), mPosition(position), mDrawPosition(), mMarked(FALSE), mFocused(FALSE),
mUpdateTimer(), mFrame(gFrameCount), mInSimFrame(U32_MAX), mInDrawFrame(U32_MAX),
mInChatFrame(U32_MAX), mInShoutFrame(U32_MAX)
mInChatFrame(U32_MAX), mInShoutFrame(U32_MAX),
mActivityType(ACTIVITY_NEW), mActivityTimer()
{
}
@@ -214,6 +219,26 @@ BOOL LLAvatarListEntry::isDead()
{
return getEntryAgeSeconds() > DEAD_KEEP_TIME;
}
const F32 ACTIVITY_TIMEOUT = 1.0f;
void LLAvatarListEntry::setActivity(ACTIVITY_TYPE activity)
{
if ( activity >= mActivityType || mActivityTimer.getElapsedTimeF32() > ACTIVITY_TIMEOUT )
{
mActivityType = activity;
mActivityTimer.start();
}
}
LLAvatarListEntry::ACTIVITY_TYPE LLAvatarListEntry::getActivity()
{
if ( mActivityTimer.getElapsedTimeF32() > ACTIVITY_TIMEOUT )
{
mActivityType = ACTIVITY_NONE;
}
if(isDead())return ACTIVITY_DEAD;
return mActivityType;
}
LLFloaterAvatarList::LLFloaterAvatarList() : LLFloater(std::string("radar"))
{
@@ -229,7 +254,7 @@ LLFloaterAvatarList::~LLFloaterAvatarList()
void LLFloaterAvatarList::toggle(void*)
{
// [RLVa:KB]
if(!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES))
if(gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES))
{
if(instanceExists())
getInstance()->close();
@@ -237,9 +262,13 @@ void LLFloaterAvatarList::toggle(void*)
else
// [/RLVa:KB]
if(!instanceExists() || !getInstance()->getVisible())
{
showInstance();
}
else
{
getInstance()->close();
}
}
//static
@@ -325,6 +354,23 @@ BOOL LLFloaterAvatarList::postBuild()
return TRUE;
}
void updateParticleActivity(LLDrawable *drawablep)
{
if (LLFloaterAvatarList::instanceExists())
{
LLViewerObject *vobj = drawablep->getVObj();
if (vobj && vobj->isParticleSource())
{
LLUUID id = vobj->mPartSourcep->getOwnerUUID();
LLAvatarListEntry *ent = LLFloaterAvatarList::getInstance()->getAvatarEntry(id);
if ( NULL != ent )
{
ent->setActivity(LLAvatarListEntry::ACTIVITY_PARTICLES);
}
}
}
}
void LLFloaterAvatarList::updateAvatarList()
{
//llinfos << "radar refresh: updating map" << llendl;
@@ -341,6 +387,25 @@ void LLFloaterAvatarList::updateAvatarList()
{
mUpdate = TRUE;
}
//moved to pipeline to prevent a crash
//gPipeline.forAllVisibleDrawables(updateParticleActivity);
//todo: make this less of a hacked up copypasta from dales 1.18.
if(gAudiop != NULL)
{
LLAudioEngine::source_map::iterator iter;
for (iter = gAudiop->mAllSources.begin(); iter != gAudiop->mAllSources.end(); ++iter)
{
LLAudioSource *sourcep = iter->second;
LLUUID uuid = sourcep->getOwnerID();
LLAvatarListEntry *ent = getAvatarEntry(uuid);
if ( ent )
{
ent->setActivity(LLAvatarListEntry::ACTIVITY_SOUND);
}
}
}
LLVector3d mypos = gAgent.getPositionGlobal();
@@ -433,6 +498,7 @@ void LLFloaterAvatarList::updateAvatarList()
// Avatar already in list, update position
F32 dist = (F32)(position - mypos).magVec();
mAvatars[avid].setPosition(position, (avatarp->getRegion() == gAgent.getRegion()), true, dist < 20.0, dist < 100.0);
if(avatarp->isTyping())mAvatars[avid].setActivity(LLAvatarListEntry::ACTIVITY_TYPING);
}
else
{
@@ -777,6 +843,40 @@ void LLFloaterAvatarList::refreshAvatarList()
snprintf(temp, sizeof(temp), "%d", (S32)position.mdV[VZ]);
}
element["columns"][LIST_ALTITUDE]["value"] = temp;
element["columns"][LIST_ACTIVITY]["column"] = "activity";
element["columns"][LIST_ACTIVITY]["type"] = "icon";
std::string activity_icon = "";
switch(entry->getActivity())
{
case LLAvatarListEntry::ACTIVITY_MOVING:
activity_icon = "inv_item_animation.tga";
break;
case LLAvatarListEntry::ACTIVITY_GESTURING:
activity_icon = "inv_item_gesture.tga";
break;
case LLAvatarListEntry::ACTIVITY_SOUND:
activity_icon = "inv_item_sound.tga";
break;
case LLAvatarListEntry::ACTIVITY_REZZING:
activity_icon = "ff_edit_theirs.tga";
break;
case LLAvatarListEntry::ACTIVITY_PARTICLES:
activity_icon = "particles_scan.tga";
break;
case LLAvatarListEntry::ACTIVITY_NEW:
activity_icon = "avatar_new.tga";
break;
case LLAvatarListEntry::ACTIVITY_TYPING:
activity_icon = "avatar_typing.tga";
break;
default:
break;
}
element["columns"][LIST_ACTIVITY]["value"] = activity_icon;//icon_image_id; //"icn_active-speakers-dot-lvl0.tga";
//element["columns"][LIST_AVATAR_ACTIVITY]["color"] = icon_color.getValue();
element["columns"][LIST_CLIENT]["column"] = "client";
element["columns"][LIST_CLIENT]["type"] = "text";

View File

@@ -33,6 +33,18 @@ class LLAvatarListEntry {
public:
enum ACTIVITY_TYPE
{
ACTIVITY_NONE, /** Avatar not doing anything */
ACTIVITY_MOVING, /** Changing position */
ACTIVITY_GESTURING, /** Playing a gesture */
ACTIVITY_REZZING, /** Rezzing objects */
ACTIVITY_PARTICLES, /** Creating particles */
ACTIVITY_TYPING, /** Typing */
ACTIVITY_NEW, /** Avatar just appeared */
ACTIVITY_SOUND, /** Playing a sound */
ACTIVITY_DEAD /** Avatar isn't around anymore, and will be removed soon from the list */
};
/**
* @brief Initializes a list entry
* @param id Avatar's key
@@ -72,6 +84,13 @@ public:
*/
LLUUID getID() { return mID; }
void setActivity(ACTIVITY_TYPE activity);
/**
* @brief Returns the activity type
*/
ACTIVITY_TYPE getActivity();
/**
* @brief Sets the 'focus' status on this entry (camera focused on this avatar)
*/
@@ -106,8 +125,13 @@ private:
/**
* @brief Timer to keep track of whether avatars are still there
*/
LLTimer mUpdateTimer;
ACTIVITY_TYPE mActivityType;
LLTimer mActivityTimer;
/**
* @brief Last frame when this avatar was updated
*/
@@ -200,9 +224,11 @@ private:
LIST_DISTANCE,
LIST_POSITION,
LIST_ALTITUDE,
LIST_ACTIVITY,
LIST_CLIENT,
};
typedef void (*avlist_command_t)(const LLUUID &avatar, const std::string &name);
/**

View File

@@ -11,12 +11,14 @@
#include "llviewerwindow.h"
#include "llwindow.h"
#include "llviewercontrol.h"
#include "llviewerobjectlist.h"
#include "lldate.h"
#include "llagent.h"
LLFloaterBlacklist* LLFloaterBlacklist::sInstance;
std::vector<LLUUID> LLFloaterBlacklist::blacklist_textures;
std::vector<LLUUID> LLFloaterBlacklist::blacklist_objects;
std::map<LLUUID,LLSD> LLFloaterBlacklist::blacklist_entries;
@@ -49,11 +51,13 @@ BOOL LLFloaterBlacklist::postBuild()
childSetAction("remove_btn", onClickRemove, this);
childSetAction("save_btn", onClickSave, this);
childSetAction("load_btn", onClickLoad, this);
childSetAction("rerender_btn", onClickRerender, this);
childSetVisible("copy_uuid_btn",false);
LLComboBox* box = getChild<LLComboBox>("asset_combo");
box->add("Texture",LLSD(0));
box->add("Sound",LLSD(1));
box->add("Animation",LLSD(20));
box->add("Object",LLSD(6));
refresh();
return TRUE;
}
@@ -64,6 +68,7 @@ void LLFloaterBlacklist::refresh()
list->clearRows();
for(std::map<LLUUID,LLSD>::iterator iter = blacklist_entries.begin(); iter != blacklist_entries.end(); ++iter)
{
if(iter->first.isNull()) continue;
LLSD element;
std::string agent;
gCacheName->getFullName(LLUUID(iter->second["entry_agent"].asString()), agent);
@@ -121,10 +126,14 @@ void LLFloaterBlacklist::addEntry(LLUUID key, LLSD data)
{
if(key.notNull())
{
if(!data.has("entry_type"))
if(!data.has("entry_type"))
{
LL_WARNS("FloaterBlacklistAdd") << "addEntry called with no entry type, specify LLAssetType::Etype" << LL_ENDL;
}
else if(!data.has("entry_name"))
{
LL_WARNS("FloaterBlacklistAdd") << "addEntry called with no entry name, specify the name that should appear in the listing for this entry." << LL_ENDL;
}
else
{
if(!data.has("entry_date"))
@@ -137,20 +146,22 @@ void LLFloaterBlacklist::addEntry(LLUUID key, LLSD data)
}
if(data["entry_type"].asString() == "1")
{
//remove sounds
LLUUID sound_id=LLUUID(key);
gVFS->removeFile(sound_id,LLAssetType::AT_SOUND);
std::string wav_path= gDirUtilp->getExpandedFilename(LL_PATH_CACHE,sound_id.asString()) + ".dsf";
if(LLAPRFile::isExist(wav_path, LL_APR_RPB))
LLAPRFile::remove(wav_path);
gAudiop->removeAudioData(sound_id);
//remove sounds
LLUUID sound_id=LLUUID(key);
gVFS->removeFile(sound_id,LLAssetType::AT_SOUND);
std::string wav_path= gDirUtilp->getExpandedFilename(LL_PATH_CACHE,sound_id.asString()) + ".dsf";
if(LLAPRFile::isExist(wav_path, LL_APR_RPB))
LLAPRFile::remove(wav_path);
gAudiop->removeAudioData(sound_id);
}
blacklist_entries.insert(std::pair<LLUUID,LLSD>(key,data));
updateBlacklists();
}
}
else
{
LL_WARNS("FloaterBlacklistAdd") << "addEntry called with a null entry key, please specify LLUUID of asset." << LL_ENDL;
}
}
// static
@@ -173,19 +184,19 @@ void LLFloaterBlacklist::onClickRemove(void* user_data)
LLScrollListCtrl* list = floaterp->getChild<LLScrollListCtrl>("file_list");
if(list->getFirstSelected())
{
LLScrollListItem* item = list->getFirstSelected();
LLUUID selected_id(item->getColumn(0)->getValue().asUUID());
if(selected_id.isNull()) return;
list->deleteSingleItem(list->getFirstSelectedIndex());
blacklist_entries.erase(selected_id);
updateBlacklists();
LLScrollListItem* item = list->getFirstSelected();
LLUUID selected_id = item->getColumn(0)->getValue().asUUID();
if(selected_id.isNull()) return;
list->deleteSingleItem(list->getFirstSelectedIndex());
blacklist_entries.erase(selected_id);
updateBlacklists();
}
}
// static
void LLFloaterBlacklist::loadFromSave()
{
std::string file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "floater_blist_settings.xml");
std::string file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "blacklist_sg1.xml");
llifstream xml_file(file_name);
if(!xml_file.is_open()) return;
LLSD data;
@@ -206,6 +217,7 @@ void LLFloaterBlacklist::updateBlacklists()
if(gAssetStorage)
{
blacklist_textures.clear();
blacklist_objects.clear();
gAssetStorage->mBlackListedAsset.clear();
for(std::map<LLUUID,LLSD>::iterator iter = blacklist_entries.begin(); iter != blacklist_entries.end(); ++iter)
{
@@ -217,6 +229,12 @@ void LLFloaterBlacklist::updateBlacklists()
{
gAssetStorage->mBlackListedAsset.push_back(LLUUID(iter->first));
}
if(blacklist_entries[iter->first]["entry_type"].asString() == "6")
{
blacklist_objects.push_back(LLUUID(iter->first));
}
}
saveToDisk();
LLFloaterBlacklist* instance = LLFloaterBlacklist::getInstance();
@@ -228,7 +246,7 @@ void LLFloaterBlacklist::updateBlacklists()
//static
void LLFloaterBlacklist::saveToDisk()
{
std::string file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "floater_blist_settings.xml");
std::string file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "blacklist_sg1.xml");
llofstream export_file(file_name);
LLSD data;
for(std::map<LLUUID,LLSD>::iterator iter = blacklist_entries.begin(); iter != blacklist_entries.end(); ++iter)
@@ -259,7 +277,7 @@ void LLFloaterBlacklist::onClickSave_continued(AIFilePicker* filepicker)
{
data[iter->first.asString()] = iter->second;
}
LLSDSerialize::toPrettyXML(data, export_file);
LLSDSerialize::toPrettyXML(data, export_file);
export_file.close();
}
}
@@ -291,4 +309,25 @@ void LLFloaterBlacklist::onClickLoad_continued(AIFilePicker* filepicker)
xml_file.close();
}
}
void LLFloaterBlacklist::onClickRerender(void* user_data)
{
std::map<LLUUID,LLSD> blacklist_new;
for(std::map<LLUUID,LLSD>::iterator itr = blacklist_entries.begin(); itr != blacklist_entries.end(); ++itr)
{
if(blacklist_entries[itr->first]["entry_type"].asString() == "6") continue;
blacklist_new[itr->first] = blacklist_entries[itr->first];
blacklist_new[itr->second] = blacklist_entries[itr->second];
}
blacklist_entries = blacklist_new;
saveToDisk();
LLFloaterBlacklist* instance = LLFloaterBlacklist::getInstance();
if(instance)
{
instance->refresh();
}
}
// </edit>

View File

@@ -32,6 +32,7 @@ public:
static std::map<LLUUID,LLSD> blacklist_entries;
static std::vector<LLUUID> blacklist_textures;
static std::vector<LLUUID> blacklist_objects;
static void loadFromSave();
@@ -48,6 +49,7 @@ private:
static void onClickSave_continued(AIFilePicker* filepicker);
static void onClickLoad(void* user_data);
static void onClickLoad_continued(AIFilePicker* filepicker);
static void onClickRerender(void* user_data);
static void onClickCopyUUID(void* user_data);
static void onClickRemove(void* user_data);

View File

@@ -540,7 +540,7 @@ void LLFloaterBuyLandUI::updateCovenantInfo()
LLTextBox* region_type = getChild<LLTextBox>("region_type_text");
if (region_type)
{
region_type->setText(region->getSimProductName());
region_type->setText(region->getLocalizedSimProductName());
}
LLTextBox* resellable_clause = getChild<LLTextBox>("resellable_clause");

View File

@@ -62,6 +62,8 @@
#include "hippolimits.h"
using namespace LLOldEvents;
// static
std::map<const LLUUID, LLFloaterGroupPicker*> LLFloaterGroupPicker::sInstances;

View File

@@ -84,14 +84,14 @@ protected:
static instance_map_t sInstances;
};
class LLPanelGroups : public LLPanel, public LLSimpleListener
class LLPanelGroups : public LLPanel, public LLOldEvents::LLSimpleListener
{
public:
LLPanelGroups();
virtual ~LLPanelGroups();
//LLEventListener
/*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
/*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
// clear the group list, and get a fresh set of info.
void reset();

View File

@@ -525,7 +525,7 @@ void LLPanelLandGeneral::refresh()
if (regionp)
{
mContentRating->setText(regionp->getSimAccessString());
mLandType->setText(regionp->getSimProductName());
mLandType->setText(regionp->getLocalizedSimProductName());
}
// estate owner/manager cannot edit other parts of the parcel
@@ -2907,7 +2907,7 @@ void LLPanelLandCovenant::refresh()
LLTextBox* region_landtype = getChild<LLTextBox>("region_landtype_text");
if (region_landtype)
{
region_landtype->setText(region->getSimProductName());
region_landtype->setText(region->getLocalizedSimProductName());
}
LLTextBox* region_maturity = getChild<LLTextBox>("region_maturity_text");

View File

@@ -2720,7 +2720,7 @@ bool LLPanelEstateCovenant::refreshFromRegion(LLViewerRegion* region)
LLTextBox* region_landtype = getChild<LLTextBox>("region_landtype_text");
if (region_landtype)
{
region_landtype->setText(region->getSimProductName());
region_landtype->setText(region->getLocalizedSimProductName());
}

View File

@@ -47,7 +47,7 @@
#include "llimagej2c.h"
#include "llstring.h"
#include "llsys.h"
#include "llversionviewer.h"
#include "sgversion.h"
#include "message.h"
#include "v3math.h"
@@ -107,7 +107,6 @@ LLMap< EReportType, LLFloaterReporter* > gReporterInstances;
// keeps track of where email is going to - global to avoid a pile
// of static/non-static access outside my control
namespace {
static BOOL gEmailToEstateOwner = FALSE;
static BOOL gDialogVisible = FALSE;
}
@@ -227,16 +226,10 @@ void LLFloaterReporter::processRegionInfo(LLMessageSystem* msg)
{
U32 region_flags;
msg->getU32("RegionInfo", "RegionFlags", region_flags);
gEmailToEstateOwner = ( region_flags & REGION_FLAGS_ABUSE_EMAIL_TO_ESTATE_OWNER );
if ( gDialogVisible )
{
if ( gEmailToEstateOwner )
{
LLNotificationsUtil::add("HelpReportAbuseEmailEO");
}
else
LLNotificationsUtil::add("HelpReportAbuseEmailLL");
LLNotificationsUtil::add("HelpReportAbuseEmailLL");
};
}
@@ -264,18 +257,7 @@ LLFloaterReporter::~LLFloaterReporter()
// virtual
void LLFloaterReporter::draw()
{
// this is set by a static callback sometime after the dialog is created.
// Only disable screenshot for abuse reports to estate owners - bug reports always
// allow screenshots to be taken.
if ( gEmailToEstateOwner && ( mReportType != BUG_REPORT ) )
{
childSetValue("screen_check", FALSE );
childSetEnabled("screen_check", FALSE );
}
else
{
childSetEnabled("screen_check", TRUE );
}
childSetEnabled("screen_check", TRUE );
LLFloater::draw();
}
@@ -747,10 +729,10 @@ LLSD LLFloaterReporter::gatherReport()
if ( mReportType == BUG_REPORT)
{
summary << short_platform << " V" << LL_VERSION_MAJOR << "."
<< LL_VERSION_MINOR << "."
<< LL_VERSION_PATCH << "."
<< LL_VERSION_BUILD
summary << short_platform << " V" << gVersionMajor << "."
<< gVersionMinor << "."
<< gVersionPatch << "."
<< gVersionBuild
<< " (" << regionp->getName() << ")"
<< "[" << category_name << "] "
<< "\"" << childGetValue("summary_edit").asString() << "\"";
@@ -768,10 +750,10 @@ LLSD LLFloaterReporter::gatherReport()
std::ostringstream details;
if (mReportType != BUG_REPORT)
{
details << "V" << LL_VERSION_MAJOR << "." // client version moved to body of email for abuse reports
<< LL_VERSION_MINOR << "."
<< LL_VERSION_PATCH << "."
<< LL_VERSION_BUILD << std::endl << std::endl;
details << "V" << gVersionMajor << "." // client version moved to body of email for abuse reports
<< gVersionMinor << "."
<< gVersionPatch << "."
<< gVersionBuild << std::endl << std::endl;
}
std::string object_name = childGetText("object_name");
std::string owner_name = childGetText("owner_name");
@@ -792,9 +774,9 @@ LLSD LLFloaterReporter::gatherReport()
std::string version_string;
version_string = llformat(
"%d.%d.%d %s %s %s %s",
LL_VERSION_MAJOR,
LL_VERSION_MINOR,
LL_VERSION_PATCH,
gVersionMajor,
gVersionMinor,
gVersionPatch,
platform,
gSysCPU.getFamily().c_str(),
gGLManager.mGLRenderer.c_str(),
@@ -805,17 +787,7 @@ LLSD LLFloaterReporter::gatherReport()
LLUUID screenshot_id = LLUUID::null;
if (childGetValue("screen_check"))
{
if ( mReportType != BUG_REPORT )
{
if ( gEmailToEstateOwner == FALSE )
{
screenshot_id = childGetValue("screenshot");
}
}
else
{
screenshot_id = childGetValue("screenshot");
};
screenshot_id = childGetValue("screenshot");
};
LLSD report = LLSD::emptyMap();

View File

@@ -45,6 +45,7 @@
#include "pipeline.h"
#include "llviewerobjectlist.h"
#include "llviewertexturelist.h"
#include "sgmemstat.h"
const S32 LL_SCROLL_BORDER = 1;
@@ -99,7 +100,17 @@ void LLFloaterStats::buildStats()
stat_barp->mLabelSpacing = 200.f;
stat_barp->mPerSec = FALSE;
stat_barp->mDisplayMean = FALSE;
if(SGMemStat::haveStat()) {
stat_barp = stat_viewp->addStat("Allocated memory", &(LLViewerStats::getInstance()->mMallocStat), "DebugStatModeMalloc");
stat_barp->setUnitLabel(" MB");
stat_barp->mMinBar = 0.f;
stat_barp->mMaxBar = 4000.f;
stat_barp->mTickSpacing = 100.f;
stat_barp->mLabelSpacing = 200.f;
stat_barp->mPerSec = FALSE;
stat_barp->mDisplayMean = FALSE;
}
stat_viewp = new LLStatView("advanced stat view", "Advanced", "OpenDebugStatAdvanced", rect);
addStatView(stat_viewp);

View File

@@ -640,7 +640,7 @@ void LLFloaterWindLight::onColorControlIMoved(LLUICtrl* ctrl, void* userData)
if(color_ctrl->isSunOrAmbientColor)
scale = WL_SUN_AMBIENT_SLIDER_SCALE;
else if(color_ctrl->isBlueHorizonOrDensity)
WL_BLUE_HORIZON_DENSITY_SCALE;
scale = WL_BLUE_HORIZON_DENSITY_SCALE;
F32 iVal = color_ctrl->i * scale;
@@ -1070,5 +1070,13 @@ void LLFloaterWindLight::populateSkyPresetsList()
mSkyPresetCombo->add(*it);
}
LLWLParamManager::preset_name_list_t user_presets;
LLWLParamManager::getInstance()->getUserPresetNames(user_presets);
for (LLWLParamManager::preset_name_list_t::const_iterator it = user_presets.begin(); it != user_presets.end(); ++it)
{
mSkyPresetCombo->add(*it);
}
mSkyPresetCombo->selectByValue(LLEnvManagerNew::instance().getSkyPresetName());
}

View File

@@ -100,6 +100,8 @@
#include "statemachine/aifilepicker.h"
// </edit>
using namespace LLOldEvents;
const std::string NEW_LSL_NAME = "New Script"; // *TODO:Translate? (probably not)
const std::string NEW_NOTECARD_NAME = "New Note"; // *TODO:Translate? (probably not)
const std::string NEW_GESTURE_NAME = "New Gesture"; // *TODO:Translate? (probably not)

View File

@@ -147,6 +147,8 @@ struct LLMoveInv
void* mUserData;
};
using namespace LLOldEvents;
// Helpers
// bug in busy count inc/dec right now, logic is complex... do we really need it?
void inc_busy_count()
@@ -2098,7 +2100,7 @@ void warn_move_inventory(LLViewerObject* object, LLMoveInv* move_inv)
// Move/copy all inventory items from the Contents folder of an in-world
// object to the agent's inventory, inside a given category.
BOOL move_inv_category_world_to_agent(const LLUUID& object_id,
BOOL move_inv_category_world_to_agent(const LLUUID& object_id,
const LLUUID& category_id,
BOOL drop,
void (*callback)(S32, void*),
@@ -2125,7 +2127,7 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id,
llinfos << "Object contents not found for drop." << llendl;
return FALSE;
}
BOOL accept = TRUE;
BOOL is_move = FALSE;

Some files were not shown because too many files have changed in this diff Show More