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:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -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/
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -79,7 +79,6 @@ set(cmake_SOURCE_FILES
|
||||
UI.cmake
|
||||
UnixInstall.cmake
|
||||
Variables.cmake
|
||||
Versions.cmake
|
||||
XmlRpcEpi.cmake
|
||||
ZLIB.cmake
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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}
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -38,6 +38,7 @@ else (STANDALONE)
|
||||
if (LINUX)
|
||||
set(UI_LIBRARIES
|
||||
atk-1.0
|
||||
X11
|
||||
gdk-x11-2.0
|
||||
gdk_pixbuf-2.0
|
||||
Xinerama
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
include(BuildVersion)
|
||||
|
||||
if(VIEWER)
|
||||
build_version(viewer)
|
||||
endif(VIEWER)
|
||||
|
||||
if(SERVER)
|
||||
build_version(server)
|
||||
endif(SERVER)
|
||||
@@ -67,11 +67,16 @@ elseif (LINUX)
|
||||
QtNetwork
|
||||
QtGui
|
||||
QtCore
|
||||
crypto
|
||||
ssl
|
||||
# qgif
|
||||
# qjpeg
|
||||
jscore
|
||||
jpeg
|
||||
fontconfig
|
||||
X11
|
||||
Xrender
|
||||
Xext
|
||||
GL
|
||||
)
|
||||
endif (STANDALONE)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
177
indra/llcommon/ll_template_cast.h
Normal file
177
indra/llcommon/ll_template_cast.h
Normal 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
154
indra/llcommon/llcoros.cpp
Normal 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
166
indra/llcommon/llcoros.h
Normal 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) */
|
||||
103
indra/llcommon/lldependencies.cpp
Normal file
103
indra/llcommon/lldependencies.cpp
Normal 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();
|
||||
}
|
||||
799
indra/llcommon/lldependencies.h
Normal file
799
indra/llcommon/lldependencies.h
Normal 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) */
|
||||
@@ -34,6 +34,8 @@
|
||||
|
||||
#include "llevent.h"
|
||||
|
||||
using namespace LLOldEvents;
|
||||
|
||||
/************************************************
|
||||
Events
|
||||
************************************************/
|
||||
|
||||
@@ -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
|
||||
|
||||
77
indra/llcommon/lleventapi.cpp
Normal file
77
indra/llcommon/lleventapi.cpp
Normal 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
166
indra/llcommon/lleventapi.h
Normal 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) */
|
||||
146
indra/llcommon/lleventcoro.cpp
Normal file
146
indra/llcommon/lleventcoro.cpp
Normal 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;
|
||||
}
|
||||
569
indra/llcommon/lleventcoro.h
Normal file
569
indra/llcommon/lleventcoro.h
Normal 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) */
|
||||
672
indra/llcommon/lleventdispatcher.cpp
Normal file
672
indra/llcommon/lleventdispatcher.cpp
Normal 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)
|
||||
{}
|
||||
|
||||
541
indra/llcommon/lleventdispatcher.h
Normal file
541
indra/llcommon/lleventdispatcher.h
Normal 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) */
|
||||
166
indra/llcommon/lleventfilter.cpp
Normal file
166
indra/llcommon/lleventfilter.cpp
Normal 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();
|
||||
}
|
||||
203
indra/llcommon/lleventfilter.h
Normal file
203
indra/llcommon/lleventfilter.h
Normal 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
628
indra/llcommon/llevents.cpp
Normal 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
1051
indra/llcommon/llevents.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include "linden_common.h"
|
||||
#include <iostream>
|
||||
|
||||
#include "llsaleinfo.h"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
170
indra/llmessage/llsdmessage.cpp
Normal file
170
indra/llmessage/llsdmessage.cpp
Normal 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()
|
||||
{
|
||||
}
|
||||
166
indra/llmessage/llsdmessage.h
Normal file
166
indra/llmessage/llsdmessage.h
Normal 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) */
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -67,6 +67,8 @@
|
||||
#include <set>
|
||||
#include <boost/tokenizer.hpp>
|
||||
|
||||
using namespace LLOldEvents;
|
||||
|
||||
// static
|
||||
LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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]®ion=[REGION]&x=[X]&y=[Y]&z=[Z]&session=[SESSION]";
|
||||
}
|
||||
return "lang=[LANG]&m=[MATURITY]&t=[TEEN]®ion=[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())
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ struct LLGroupData
|
||||
|
||||
//
|
||||
|
||||
class LLAgent : public LLObservable
|
||||
class LLAgent : public LLOldEvents::LLObservable
|
||||
{
|
||||
LOG_CLASS(LLAgent);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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])
|
||||
|
||||
202
indra/newview/llcapabilitylistener.cpp
Normal file
202
indra/newview/llcapabilitylistener.cpp
Normal 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;
|
||||
}
|
||||
131
indra/newview/llcapabilitylistener.h
Normal file
131
indra/newview/llcapabilitylistener.h
Normal 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) */
|
||||
@@ -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"
|
||||
|
||||
@@ -139,6 +139,7 @@ public:
|
||||
PASS_ALPHA,
|
||||
PASS_ALPHA_MASK,
|
||||
PASS_FULLBRIGHT_ALPHA_MASK,
|
||||
PASS_ALPHA_INVISIBLE,
|
||||
NUM_RENDER_TYPES,
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -62,6 +62,8 @@
|
||||
|
||||
#include "hippolimits.h"
|
||||
|
||||
using namespace LLOldEvents;
|
||||
|
||||
// static
|
||||
std::map<const LLUUID, LLFloaterGroupPicker*> LLFloaterGroupPicker::sInstances;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user