diff options
324 files changed, 16297 insertions, 8993 deletions
diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000000..648ea3eabb --- /dev/null +++ b/.hgignore @@ -0,0 +1,36 @@ +syntax: glob + +*.pyc +*~ +.*.swp +LICENSES +indra/.distcc +indra/build-darwin-* +indra/build-vc[0-9]* +indra/lib/mono/1.0/*.dll +indra/lib/mono/indra/*.dll +indra/lib/mono/indra/*.exe +indra/lib/mono/indra/*.pdb +indra/lib/python/eventlet/ +indra/llwindow/glh/glh_linear.h +indra/newview/app_settings/mozilla +indra/newview/app_settings/mozilla-runtime-* +indra/newview/app_settings/mozilla_debug +indra/newview/app_settings/static_*.db2 +indra/newview/character +indra/newview/fmod.dll +indra/newview/mozilla-theme +indra/newview/mozilla-universal-darwin.tgz +indra/newview/res-sdl +indra/newview/skins +indra/newview/vivox-runtime +indra/server-linux-* +indra/test_apps/llmediatest/dependencies/i686-win32 +indra/test_apps/terrain_mule/*.dll +indra/viewer-linux-* +indra/web/doc/asset-upload/plugins/lsl_compiler/lslc +indra/web/doc/asset-upload/plugins/verify-notecard +indra/web/doc/asset-upload/plugins/verify-texture +installed.xml +libraries +tarfile_tmp diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 9418dbf271..261c0b17e2 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -66,6 +66,7 @@ if (VIEWER) add_subdirectory(${LIBS_OPEN_PREFIX}llplugin) add_subdirectory(${LIBS_OPEN_PREFIX}llui) add_subdirectory(${LIBS_OPEN_PREFIX}llxuixml) + add_subdirectory(${LIBS_OPEN_PREFIX}viewer_components) # viewer media plugins add_subdirectory(${LIBS_OPEN_PREFIX}media_plugins) diff --git a/indra/cmake/APR.cmake b/indra/cmake/APR.cmake index 0755aeee03..f4706dd4f2 100644 --- a/indra/cmake/APR.cmake +++ b/indra/cmake/APR.cmake @@ -13,26 +13,38 @@ if (STANDALONE) else (STANDALONE) use_prebuilt_binary(apr_suite) if (WINDOWS) + if (LLCOMMON_LINK_SHARED) + set(APR_selector "lib") + else (LLCOMMON_LINK_SHARED) + set(APR_selector "") + endif (LLCOMMON_LINK_SHARED) set(APR_LIBRARIES - debug ${ARCH_PREBUILT_DIRS_DEBUG}/apr-1.lib - optimized ${ARCH_PREBUILT_DIRS_RELEASE}/apr-1.lib + debug ${ARCH_PREBUILT_DIRS_DEBUG}/${APR_selector}apr-1.lib + optimized ${ARCH_PREBUILT_DIRS_RELEASE}/${APR_selector}apr-1.lib ) set(APRICONV_LIBRARIES - debug ${ARCH_PREBUILT_DIRS_DEBUG}/apriconv-1.lib - optimized ${ARCH_PREBUILT_DIRS_RELEASE}/apriconv-1.lib + debug ${ARCH_PREBUILT_DIRS_DEBUG}/${APR_selector}apriconv-1.lib + optimized ${ARCH_PREBUILT_DIRS_RELEASE}/${APR_selector}apriconv-1.lib ) set(APRUTIL_LIBRARIES - debug ${ARCH_PREBUILT_DIRS_DEBUG}/aprutil-1.lib ${APRICONV_LIBRARIES} - optimized ${ARCH_PREBUILT_DIRS_RELEASE}/aprutil-1.lib ${APRICONV_LIBRARIES} + debug ${ARCH_PREBUILT_DIRS_DEBUG}/${APR_selector}aprutil-1.lib ${APRICONV_LIBRARIES} + optimized ${ARCH_PREBUILT_DIRS_RELEASE}/${APR_selector}aprutil-1.lib ${APRICONV_LIBRARIES} ) elseif (DARWIN) + if (LLCOMMON_LINK_SHARED) + set(APR_selector "0.3.7.dylib") + set(APRUTIL_selector "0.3.8.dylib") + else (LLCOMMON_LINK_SHARED) + set(APR_selector "a") + set(APRUTIL_selector "a") + endif (LLCOMMON_LINK_SHARED) set(APR_LIBRARIES - debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.a - optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.a + debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.${APR_selector} + optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.${APR_selector} ) set(APRUTIL_LIBRARIES - debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.a - optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.a + debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.${APRUTIL_selector} + optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.${APRUTIL_selector} ) set(APRICONV_LIBRARIES iconv) else (WINDOWS) diff --git a/indra/cmake/Externals.cmake b/indra/cmake/Externals.cmake new file mode 100644 index 0000000000..26f3b56049 --- /dev/null +++ b/indra/cmake/Externals.cmake @@ -0,0 +1,34 @@ +# -*- cmake -*- + +include(Python) +include(FindSVN) + +macro (use_svn_external _binary _path _url _rev) + if (NOT STANDALONE) + if(${CMAKE_BINARY_DIR}/temp/sentinel_installed IS_NEWER_THAN ${CMAKE_BINARY_DIR}/temp/${_binary}_installed) + if(SVN_FOUND) + if(DEBUG_EXTERNALS) + message("cd ${_path} && ${SVN_EXECUTABLE} checkout -r ${_rev} ${_url} ${_binary}") + endif(DEBUG_EXTERNALS) + execute_process(COMMAND ${SVN_EXECUTABLE} + checkout + -r ${_rev} + ${_url} + ${_binary} + WORKING_DIRECTORY ${_path} + RESULT_VARIABLE ${_binary}_installed + ) + else(SVN_FOUND) + message(FATAL_ERROR "Failed to find SVN_EXECUTABLE") + endif(SVN_FOUND) + file(WRITE ${CMAKE_BINARY_DIR}/temp/${_binary}_installed "${${_binary}_installed}") + else(${CMAKE_BINARY_DIR}/temp/sentinel_installed IS_NEWER_THAN ${CMAKE_BINARY_DIR}/temp/${_binary}_installed) + set(${_binary}_installed 0) + endif(${CMAKE_BINARY_DIR}/temp/sentinel_installed IS_NEWER_THAN ${CMAKE_BINARY_DIR}/temp/${_binary}_installed) + if(NOT ${_binary}_installed EQUAL 0) + message(FATAL_ERROR + "Failed to download or unpack prebuilt '${_binary}'." + " Process returned ${${_binary}_installed}.") + endif (NOT ${_binary}_installed EQUAL 0) + endif (NOT STANDALONE) +endmacro (use_svn_external _binary _path _url _rev) diff --git a/indra/cmake/FindMono.cmake b/indra/cmake/FindMono.cmake index c36d7259e8..d956c48656 100644 --- a/indra/cmake/FindMono.cmake +++ b/indra/cmake/FindMono.cmake @@ -42,7 +42,7 @@ FIND_PROGRAM (GACUTIL_EXECUTABLE gacutil /usr/local/bin ) FIND_PROGRAM (ILASM_EXECUTABLE - ilasm + NAMES ilasm.bat ilasm NO_DEFAULT_PATH PATHS "$ENV{PROGRAMFILES}/Mono-1.9.1/bin" "$ENV{PROGRAMFILES}/Mono-1.2.6/bin" /bin /usr/bin /usr/local/bin ) diff --git a/indra/cmake/FindSVN.cmake b/indra/cmake/FindSVN.cmake new file mode 100644 index 0000000000..3322be4ca9 --- /dev/null +++ b/indra/cmake/FindSVN.cmake @@ -0,0 +1,34 @@ +# -*- cmake -*- +# +# Find the svn executable for exporting old svn:externals. +# +# Input variables: +# SVN_FIND_REQUIRED - set this if configuration should fail without scp +# +# Output variables: +# +# SVN_FOUND - set if svn was found +# SVN_EXECUTABLE - path to svn executable +# SVN_BATCH_FLAG - how to put svn into batch mode + + +SET(SVN_EXECUTABLE) +FIND_PROGRAM(SVN_EXECUTABLE NAMES svn svn.exe) + +IF (SVN_EXECUTABLE) + SET(SVN_FOUND ON) +ELSE (SVN_EXECUTABLE) + SET(SVN_FOUND OFF) +ENDIF (SVN_EXECUTABLE) + +IF (SVN_FOUND) + GET_FILENAME_COMPONENT(_svn_name ${SVN_EXECUTABLE} NAME_WE) + SET(SVN_BATCH_FLAG --non-interactive) +ELSE (SVN_FOUND) + IF (SVN_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find svn executable") + ENDIF (SVN_FIND_REQUIRED) +ENDIF (SVN_FOUND) + +MARK_AS_ADVANCED(SVN_EXECUTABLE SVN_FOUND SVN_BATCH_FLAG) + diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index 4a61725e09..013cc5109f 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -114,22 +114,29 @@ MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources) GET_TARGET_PROPERTY(TEST_EXE PROJECT_${project}_TEST_${name} LOCATION) SET(TEST_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/PROJECT_${project}_TEST_${name}_ok.txt) SET(TEST_CMD ${TEST_EXE} --touch=${TEST_OUTPUT} --sourcedir=${CMAKE_CURRENT_SOURCE_DIR}) - # daveh - what configuration does this use? Debug? it's cmake-time, not build time. + poppy 2009-04-19 + + # daveh - what configuration does this use? Debug? it's cmake-time, not build time. + poppy 2009-04-19 IF(LL_TEST_VERBOSE) MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_cmd = ${TEST_CMD}") ENDIF(LL_TEST_VERBOSE) - SET(TEST_SCRIPT_CMD - ${CMAKE_COMMAND} - -DLD_LIBRARY_PATH=${ARCH_PREBUILT_DIRS}:/usr/lib - -DTEST_CMD:STRING="${TEST_CMD}" - -P ${CMAKE_SOURCE_DIR}/cmake/RunBuildTest.cmake - ) + + IF(WINDOWS) + set(LD_LIBRARY_PATH ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}) + ELSE(WINDOWS) + set(LD_LIBRARY_PATH ${ARCH_PREBUILT_DIRS}:${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}:/usr/lib) + ENDIF(WINDOWS) + IF(LL_TEST_VERBOSE) MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_script = ${TEST_SCRIPT_CMD}") ENDIF(LL_TEST_VERBOSE) # Add test ADD_CUSTOM_COMMAND( OUTPUT ${TEST_OUTPUT} + COMMAND ${CMAKE_COMMAND} + ARGS + -DLD_LIBRARY_PATH=${LD_LIBRARY_PATH} + "-DTEST_CMD:STRING=\"${TEST_CMD}\"" + -P ${CMAKE_SOURCE_DIR}/cmake/RunBuildTest.cmake COMMAND ${TEST_SCRIPT_CMD} DEPENDS PROJECT_${project}_TEST_${name} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake index ef202dd879..d1ab264a41 100644 --- a/indra/cmake/LLCommon.cmake +++ b/indra/cmake/LLCommon.cmake @@ -17,3 +17,7 @@ set(LLCOMMON_LIBRARIES llcommon) add_definitions(${TCMALLOC_FLAG}) +set(LLCOMMON_LINK_SHARED ON CACHE BOOL "Build the llcommon target as a shared library.") +if(LLCOMMON_LINK_SHARED) + add_definitions(-DLL_COMMON_LINK_SHARED=1) +endif(LLCOMMON_LINK_SHARED) diff --git a/indra/cmake/LLLogin.cmake b/indra/cmake/LLLogin.cmake new file mode 100644 index 0000000000..47d171876a --- /dev/null +++ b/indra/cmake/LLLogin.cmake @@ -0,0 +1,7 @@ +# -*- cmake -*- + +set(LLLOGIN_INCLUDE_DIRS + ${LIBS_OPEN_DIR}/viewer_components/login + ) + +set(LLLOGIN_LIBRARIES lllogin) diff --git a/indra/cmake/Linking.cmake b/indra/cmake/Linking.cmake index eaa8a6dc29..1f3553539f 100644 --- a/indra/cmake/Linking.cmake +++ b/indra/cmake/Linking.cmake @@ -5,6 +5,7 @@ if (NOT STANDALONE) set(ARCH_PREBUILT_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/lib) set(ARCH_PREBUILT_DIRS_RELEASE ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/lib/release) set(ARCH_PREBUILT_DIRS_DEBUG ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/lib/debug) + set(SHARED_LIB_STAGING_DIR ${CMAKE_BINARY_DIR}/sharedlibs CACHE FILEPATH "Location of staged DLLs") elseif (LINUX) if (VIEWER) set(ARCH_PREBUILT_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/lib_release_client) @@ -13,10 +14,12 @@ if (NOT STANDALONE) endif (VIEWER) set(ARCH_PREBUILT_DIRS_RELEASE ${ARCH_PREBUILT_DIRS}) set(ARCH_PREBUILT_DIRS_DEBUG ${ARCH_PREBUILT_DIRS}) + set(SHARED_LIB_STAGING_DIR ${CMAKE_BINARY_DIR}/sharedlibs CACHE FILEPATH "Location of staged .sos") elseif (DARWIN) set(ARCH_PREBUILT_DIRS_RELEASE ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/lib_release) set(ARCH_PREBUILT_DIRS ${ARCH_PREBUILT_DIRS_RELEASE}) set(ARCH_PREBUILT_DIRS_DEBUG ${ARCH_PREBUILT_DIRS_RELEASE}) + set(SHARED_LIB_STAGING_DIR ${CMAKE_BINARY_DIR}/sharedlibs CACHE FILEPATH "Location of staged DLLs") endif (WINDOWS) endif (NOT STANDALONE) diff --git a/indra/cmake/Pth.cmake b/indra/cmake/Pth.cmake new file mode 100644 index 0000000000..a28f6ec696 --- /dev/null +++ b/indra/cmake/Pth.cmake @@ -0,0 +1,21 @@ +# -*- cmake -*- +include(Prebuilt) + +set(PTH_FIND_QUIETLY ON) +set(PTH_FIND_REQUIRED ON) + +if (STANDALONE) +# ?? How would I construct FindPTH.cmake? This file was cloned from +# CURL.cmake, which uses include(FindCURL), but there's no FindCURL.cmake? +# include(FindPTH) +else (STANDALONE) + # This library is only needed to support Boost.Coroutine, and only on Mac. + if (DARWIN) + use_prebuilt_binary(pth) + set(PTH_LIBRARIES pth) + set(PTH_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include) + else (DARWIN) + set(PTH_LIBRARIES) + set(PTH_INCLUDE_DIRS) + endif (DARWIN) +endif (STANDALONE) diff --git a/indra/develop.py b/indra/develop.py index 249b6519fc..bdc277202e 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -439,7 +439,6 @@ class DarwinSetup(UnixSetup): ) if self.universal == 'ON': args['universal'] = '-DCMAKE_OSX_ARCHITECTURES:STRING=\'i386;ppc\'' - pass #if simple: # return 'cmake %(opts)s %(dir)r' % args return ('cmake -G %(generator)r ' diff --git a/indra/lib/python/indra/base/lluuid.py b/indra/lib/python/indra/base/lluuid.py index aceea29cd2..1cdd8e915b 100644 --- a/indra/lib/python/indra/base/lluuid.py +++ b/indra/lib/python/indra/base/lluuid.py @@ -26,8 +26,14 @@ THE SOFTWARE. $/LicenseInfo$ """ -import md5, random, socket, string, time, re +import random, socket, string, time, re import uuid +try: + # Python 2.6 + from hashlib import md5 +except ImportError: + # Python 2.5 and earlier + from md5 import new as md5 def _int2binstr(i,l): s='' @@ -196,7 +202,7 @@ class UUID(object): from c++ implementation for portability reasons. Returns self. """ - m = md5.new() + m = md5() m.update(uuid.uuid1().bytes) self._bits = m.digest() return self diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 4b24a7ad2b..af573a7e6c 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -5,12 +5,19 @@ project(llcommon) include(00-Common) include(LLAddBuildTest) include(LLCommon) +include(Linking) include(Boost) +include (Pth) + +if (WINDOWS) + include(CopyWinLibs) +endif (WINDOWS) include_directories( ${EXPAT_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} + ${PTH_INCLUDE_DIRS} ) # add_executable(lltreeiterators lltreeiterators.cpp) @@ -27,6 +34,7 @@ set(llcommon_SOURCE_FILES llbase32.cpp llbase64.cpp llcommon.cpp + llcoros.cpp llcrc.cpp llcriticaldamp.cpp llcursortypes.cpp @@ -35,6 +43,9 @@ set(llcommon_SOURCE_FILES llerror.cpp llerrorthread.cpp llevent.cpp + lleventcoro.cpp + lleventdispatcher.cpp + lleventfilter.cpp llevents.cpp llfasttimer.cpp llfile.cpp @@ -64,6 +75,7 @@ set(llcommon_SOURCE_FILES llsdserialize_xml.cpp llsdutil.cpp llsecondlifeurls.cpp + llsingleton.cpp llstat.cpp llstacktrace.cpp llstreamtools.cpp @@ -106,6 +118,7 @@ set(llcommon_HEADER_FILES llchat.h llclickaction.h llcommon.h + llcoros.h llcrc.h llcriticaldamp.h llcursortypes.h @@ -127,6 +140,9 @@ set(llcommon_HEADER_FILES llerrorlegacy.h llerrorthread.h llevent.h + lleventcoro.h + lleventdispatcher.h + lleventfilter.h llevents.h lleventemitter.h llextendedstatus.h @@ -141,6 +157,7 @@ set(llcommon_HEADER_FILES llhttpstatuscodes.h llindexedqueue.h llinstancetracker.h + llinstancetracker.h llkeythrottle.h lllazy.h lllinkedqueue.h @@ -221,17 +238,57 @@ set_source_files_properties(${llcommon_HEADER_FILES} list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES}) -add_library (llcommon ${llcommon_SOURCE_FILES}) +if(LLCOMMON_LINK_SHARED) + add_library (llcommon SHARED ${llcommon_SOURCE_FILES}) + add_definitions(-DLL_COMMON_BUILD=1) + + if(SHARED_LIB_STAGING_DIR) + # *FIX:Mani --- + # llcommon.dll get written to the DLL staging directory. + # Also this directory is shared with RunBuildTest.cmake, y'know, for the tests. + set_target_properties(llcommon PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${SHARED_LIB_STAGING_DIR}) + if(NOT WINDOWS) + get_target_property(LLCOMMON_PATH llcommon LOCATION) + get_filename_component(LLCOMMON_FILE ${LLCOMMON_PATH} NAME) + add_custom_command( + TARGET llcommon POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS + -E + copy_if_different + ${LLCOMMON_PATH} + ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/${LLCOMMON_FILE} + COMMENT "Copying llcommon to the staging folder." + ) + endif(NOT WINDOWS) + endif(SHARED_LIB_STAGING_DIR) + + if (DARWIN) + set_target_properties(llcommon PROPERTIES + BUILD_WITH_INSTALL_RPATH 1 + INSTALL_NAME_DIR "@executable_path/../Resources" + ) + endif(DARWIN) + +else(LLCOMMON_LINK_SHARED) + add_library (llcommon ${llcommon_SOURCE_FILES}) +endif(LLCOMMON_LINK_SHARED) + target_link_libraries( llcommon ${APRUTIL_LIBRARIES} ${APR_LIBRARIES} ${EXPAT_LIBRARIES} ${ZLIB_LIBRARIES} + ${WINDOWS_LIBRARIES} ${BOOST_PROGRAM_OPTIONS_LIBRARY} ${BOOST_REGEX_LIBRARY} + ${PTH_LIBRARIES} ) +add_dependencies(llcommon stage_third_party_libs) + + include(LLAddBuildTest) SET(llcommon_TEST_SOURCE_FILES # WARNING: Please don't write tests against LLCommon or LLMath until this issue is resolved: https://jira.lindenlab.com/jira/browse/DEV-29456 diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h index 9adf24a492..d0ab5e969f 100644 --- a/indra/llcommon/linden_common.h +++ b/indra/llcommon/linden_common.h @@ -72,13 +72,7 @@ #ifdef LL_WINDOWS // Reenable warnings we disabled above #pragma warning (3 : 4702) // unreachable code, we like level 3, not 4 -// level 4 warnings that we need to disable: -#pragma warning (disable : 4100) // unreferenced formal parameter -#pragma warning (disable : 4127) // conditional expression is constant (e.g. while(1) ) -#pragma warning (disable : 4244) // possible loss of data on conversions -#pragma warning (disable : 4396) // the inline specifier cannot be used when a friend declaration refers to a specialization of a function template -#pragma warning (disable : 4512) // assignment operator could not be generated -#pragma warning (disable : 4706) // assignment within conditional (even if((x = y)) ) +// moved msvc warnings to llpreprocessor.h *TODO - delete this comment after merge conflicts are unlikely -brad #endif // LL_WINDOWS // Linden only libs in alpha-order other than stdtypes.h diff --git a/indra/llcommon/llallocator.h b/indra/llcommon/llallocator.h index 2b70fee0b8..0d6f18c5d4 100644 --- a/indra/llcommon/llallocator.h +++ b/indra/llcommon/llallocator.h @@ -1,63 +1,63 @@ -/** - * @file llallocator.h - * @brief Declaration of the LLAllocator class. - * - * $LicenseInfo:firstyear=2009&license=viewergpl$ - * - * Copyright (c) 2009-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_LLALLOCATOR_H -#define LL_LLALLOCATOR_H - -#include <string> - -#include "llmemtype.h" -#include "llallocator_heap_profile.h" - -class LLAllocator { - friend class LLMemoryView; - friend class LLMemType; - -private: - static void pushMemType(S32 type); - static S32 popMemType(); - -public: - void setProfilingEnabled(bool should_enable); - - static bool isProfiling(); - - LLAllocatorHeapProfile const & getProfile(); - -private: - std::string getRawProfile(); - -private: - LLAllocatorHeapProfile mProf; -}; - -#endif // LL_LLALLOCATOR_H +/**
+ * @file llallocator.h
+ * @brief Declaration of the LLAllocator class.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2009-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_LLALLOCATOR_H
+#define LL_LLALLOCATOR_H
+
+#include <string>
+
+#include "llmemtype.h"
+#include "llallocator_heap_profile.h"
+
+class LL_COMMON_API LLAllocator {
+ friend class LLMemoryView;
+ friend class LLMemType;
+
+private:
+ static void pushMemType(S32 type);
+ static S32 popMemType();
+
+public:
+ void setProfilingEnabled(bool should_enable);
+
+ static bool isProfiling();
+
+ LLAllocatorHeapProfile const & getProfile();
+
+private:
+ std::string getRawProfile();
+
+private:
+ LLAllocatorHeapProfile mProf;
+};
+
+#endif // LL_LLALLOCATOR_H
diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index cc60ba0b80..35f6f7028d 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -61,7 +61,7 @@ public: }; #endif -class LLApp +class LL_COMMON_API LLApp { friend class LLErrorThread; public: diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index 63130a89fc..0898aeec47 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -1,259 +1,259 @@ -/** - * @file llapr.h - * @author Phoenix - * @date 2004-11-28 - * @brief Helper functions for using the apache portable runtime library. - * - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * - * Copyright (c) 2004-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_LLAPR_H -#define LL_LLAPR_H - -#if LL_LINUX || LL_SOLARIS -#include <sys/param.h> // Need PATH_MAX in APR headers... -#endif - -#include <boost/noncopyable.hpp> - -#include "apr_thread_proc.h" -#include "apr_thread_mutex.h" -#include "apr_getopt.h" -#include "apr_signal.h" -#include "apr_atomic.h" -#include "llstring.h" - -extern apr_thread_mutex_t* gLogMutexp; -extern apr_thread_mutex_t* gCallStacksLogMutexp; - -/** - * @brief initialize the common apr constructs -- apr itself, the - * global pool, and a mutex. - */ -void ll_init_apr(); - -/** - * @brief Cleanup those common apr constructs. - */ -void ll_cleanup_apr(); - -// -//LL apr_pool -//manage apr_pool_t, destroy allocated apr_pool in the destruction function. -// -class LLAPRPool -{ -public: - LLAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE) ; - ~LLAPRPool() ; - - apr_pool_t* getAPRPool() ; - apr_status_t getStatus() {return mStatus ; } - -protected: - void releaseAPRPool() ; - void createAPRPool() ; - -protected: - apr_pool_t* mPool ; //pointing to an apr_pool - apr_pool_t* mParent ; //parent pool - apr_size_t mMaxSize ; //max size of mPool, mPool should return memory to system if allocated memory beyond this limit. However it seems not to work. - apr_status_t mStatus ; //status when creating the pool - BOOL mReleasePoolFlag ; //if set, mPool is destroyed when LLAPRPool is deleted. default value is true. -}; - -// -//volatile LL apr_pool -//which clears memory automatically. -//so it can not hold static data or data after memory is cleared -// -class LLVolatileAPRPool : public LLAPRPool -{ -public: - LLVolatileAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE); - ~LLVolatileAPRPool(){} - - apr_pool_t* getVolatileAPRPool() ; - - void clearVolatileAPRPool() ; - - BOOL isFull() ; - BOOL isEmpty() {return !mNumActiveRef ;} -private: - S32 mNumActiveRef ; //number of active pointers pointing to the apr_pool. - S32 mNumTotalRef ; //number of total pointers pointing to the apr_pool since last creating. -} ; - -/** - * @class LLScopedLock - * @brief Small class to help lock and unlock mutexes. - * - * This class is used to have a stack level lock once you already have - * an apr mutex handy. The constructor handles the lock, and the - * destructor handles the unlock. Instances of this class are - * <b>not</b> thread safe. - */ -class LLScopedLock : private boost::noncopyable -{ -public: - /** - * @brief Constructor which accepts a mutex, and locks it. - * - * @param mutex An allocated APR mutex. If you pass in NULL, - * this wrapper will not lock. - */ - LLScopedLock(apr_thread_mutex_t* mutex); - - /** - * @brief Destructor which unlocks the mutex if still locked. - */ - ~LLScopedLock(); - - /** - * @brief Check lock. - */ - bool isLocked() const { return mLocked; } - - /** - * @brief This method unlocks the mutex. - */ - void unlock(); - -protected: - bool mLocked; - apr_thread_mutex_t* mMutex; -}; - -template <typename Type> class LLAtomic32 -{ -public: - LLAtomic32<Type>() {}; - LLAtomic32<Type>(Type x) {apr_atomic_set32(&mData, apr_uint32_t(x)); }; - ~LLAtomic32<Type>() {}; - - operator const Type() { apr_uint32_t data = apr_atomic_read32(&mData); return Type(data); } - Type operator =(const Type& x) { apr_atomic_set32(&mData, apr_uint32_t(x)); return Type(mData); } - void operator -=(Type x) { apr_atomic_sub32(&mData, apr_uint32_t(x)); } - void operator +=(Type x) { apr_atomic_add32(&mData, apr_uint32_t(x)); } - Type operator ++(int) { return apr_atomic_inc32(&mData); } // Type++ - Type operator --(int) { return apr_atomic_dec32(&mData); } // Type-- - -private: - apr_uint32_t mData; -}; - -typedef LLAtomic32<U32> LLAtomicU32; -typedef LLAtomic32<S32> LLAtomicS32; - -// File IO convenience functions. -// Returns NULL if the file fails to openm sets *sizep to file size of not NULL -// abbreviated flags -#define LL_APR_R (APR_READ) // "r" -#define LL_APR_W (APR_CREATE|APR_TRUNCATE|APR_WRITE) // "w" -#define LL_APR_RB (APR_READ|APR_BINARY) // "rb" -#define LL_APR_WB (APR_CREATE|APR_TRUNCATE|APR_WRITE|APR_BINARY) // "wb" -#define LL_APR_RPB (APR_READ|APR_WRITE|APR_BINARY) // "r+b" -#define LL_APR_WPB (APR_CREATE|APR_TRUNCATE|APR_READ|APR_WRITE|APR_BINARY) // "w+b" - -// -//apr_file manager -//which: 1)only keeps one file open; -// 2)closes the open file in the destruction function -// 3)informs the apr_pool to clean the memory when the file is closed. -//Note: please close an open file at the earliest convenience. -// especially do not put some time-costly operations between open() and close(). -// otherwise it might lock the APRFilePool. -//there are two different apr_pools the APRFile can use: -// 1, a temperary pool passed to an APRFile function, which is used within this function and only once. -// 2, a global pool. -// -class LLAPRFile -{ -private: - apr_file_t* mFile ; - LLVolatileAPRPool *mCurrentFilePoolp ; //currently in use apr_pool, could be one of them: sAPRFilePoolp, or a temp pool. - -public: - LLAPRFile() ; - ~LLAPRFile() ; - - apr_status_t open(LLVolatileAPRPool* pool, const std::string& filename, apr_int32_t flags, S32* sizep = NULL); - apr_status_t open(const std::string& filename, apr_int32_t flags, apr_pool_t* pool = NULL, S32* sizep = NULL); - apr_status_t close() ; - - // Returns actual offset, -1 if seek fails - S32 seek(apr_seek_where_t where, S32 offset); - apr_status_t eof() { return apr_file_eof(mFile);} - - // Returns bytes read/written, 0 if read/write fails: - S32 read(void* buf, S32 nbytes); - S32 write(const void* buf, S32 nbytes); - - apr_file_t* getFileHandle() {return mFile;} - -private: - apr_pool_t* getAPRFilePool(apr_pool_t* pool) ; - -// -//******************************************************************************************************************************* -//static components -// -public: - static LLVolatileAPRPool *sAPRFilePoolp ; //a global apr_pool for APRFile, which is used only when local pool does not exist. - -private: - static apr_file_t* open(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags); - static apr_status_t close(apr_file_t* file, LLVolatileAPRPool* pool) ; - static S32 seek(apr_file_t* file, apr_seek_where_t where, S32 offset); -public: - // returns false if failure: - static bool remove(const std::string& filename, LLVolatileAPRPool* pool = NULL); - static bool rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool = NULL); - static bool isExist(const std::string& filename, LLVolatileAPRPool* pool = NULL, apr_int32_t flags = APR_READ); - static S32 size(const std::string& filename, LLVolatileAPRPool* pool = NULL); - static bool makeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL); - static bool removeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL); - - // Returns bytes read/written, 0 if read/write fails: - static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); - static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); -//******************************************************************************************************************************* -}; - -/** - * @brief Function which approprately logs error or remains quiet on - * APR_SUCCESS. - * @return Returns <code>true</code> if status is an error condition. - */ -bool ll_apr_warn_status(apr_status_t status); - -void ll_apr_assert_status(apr_status_t status); - -extern "C" apr_pool_t* gAPRPoolp; // Global APR memory pool - -#endif // LL_LLAPR_H +/**
+ * @file llapr.h
+ * @author Phoenix
+ * @date 2004-11-28
+ * @brief Helper functions for using the apache portable runtime library.
+ *
+ * $LicenseInfo:firstyear=2004&license=viewergpl$
+ *
+ * Copyright (c) 2004-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_LLAPR_H
+#define LL_LLAPR_H
+
+#if LL_LINUX || LL_SOLARIS
+#include <sys/param.h> // Need PATH_MAX in APR headers...
+#endif
+
+#include <boost/noncopyable.hpp>
+
+#include "apr_thread_proc.h"
+#include "apr_thread_mutex.h"
+#include "apr_getopt.h"
+#include "apr_signal.h"
+#include "apr_atomic.h"
+#include "llstring.h"
+
+extern LL_COMMON_API apr_thread_mutex_t* gLogMutexp;
+extern apr_thread_mutex_t* gCallStacksLogMutexp;
+
+/**
+ * @brief initialize the common apr constructs -- apr itself, the
+ * global pool, and a mutex.
+ */
+void LL_COMMON_API ll_init_apr();
+
+/**
+ * @brief Cleanup those common apr constructs.
+ */
+void LL_COMMON_API ll_cleanup_apr();
+
+//
+//LL apr_pool
+//manage apr_pool_t, destroy allocated apr_pool in the destruction function.
+//
+class LL_COMMON_API LLAPRPool
+{
+public:
+ LLAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE) ;
+ ~LLAPRPool() ;
+
+ apr_pool_t* getAPRPool() ;
+ apr_status_t getStatus() {return mStatus ; }
+
+protected:
+ void releaseAPRPool() ;
+ void createAPRPool() ;
+
+protected:
+ apr_pool_t* mPool ; //pointing to an apr_pool
+ apr_pool_t* mParent ; //parent pool
+ apr_size_t mMaxSize ; //max size of mPool, mPool should return memory to system if allocated memory beyond this limit. However it seems not to work.
+ apr_status_t mStatus ; //status when creating the pool
+ BOOL mReleasePoolFlag ; //if set, mPool is destroyed when LLAPRPool is deleted. default value is true.
+};
+
+//
+//volatile LL apr_pool
+//which clears memory automatically.
+//so it can not hold static data or data after memory is cleared
+//
+class LL_COMMON_API LLVolatileAPRPool : public LLAPRPool
+{
+public:
+ LLVolatileAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE);
+ ~LLVolatileAPRPool(){}
+
+ apr_pool_t* getVolatileAPRPool() ;
+
+ void clearVolatileAPRPool() ;
+
+ BOOL isFull() ;
+ BOOL isEmpty() {return !mNumActiveRef ;}
+private:
+ S32 mNumActiveRef ; //number of active pointers pointing to the apr_pool.
+ S32 mNumTotalRef ; //number of total pointers pointing to the apr_pool since last creating.
+} ;
+
+/**
+ * @class LLScopedLock
+ * @brief Small class to help lock and unlock mutexes.
+ *
+ * This class is used to have a stack level lock once you already have
+ * an apr mutex handy. The constructor handles the lock, and the
+ * destructor handles the unlock. Instances of this class are
+ * <b>not</b> thread safe.
+ */
+class LL_COMMON_API LLScopedLock : private boost::noncopyable
+{
+public:
+ /**
+ * @brief Constructor which accepts a mutex, and locks it.
+ *
+ * @param mutex An allocated APR mutex. If you pass in NULL,
+ * this wrapper will not lock.
+ */
+ LLScopedLock(apr_thread_mutex_t* mutex);
+
+ /**
+ * @brief Destructor which unlocks the mutex if still locked.
+ */
+ ~LLScopedLock();
+
+ /**
+ * @brief Check lock.
+ */
+ bool isLocked() const { return mLocked; }
+
+ /**
+ * @brief This method unlocks the mutex.
+ */
+ void unlock();
+
+protected:
+ bool mLocked;
+ apr_thread_mutex_t* mMutex;
+};
+
+template <typename Type> class LLAtomic32
+{
+public:
+ LLAtomic32<Type>() {};
+ LLAtomic32<Type>(Type x) {apr_atomic_set32(&mData, apr_uint32_t(x)); };
+ ~LLAtomic32<Type>() {};
+
+ operator const Type() { apr_uint32_t data = apr_atomic_read32(&mData); return Type(data); }
+ Type operator =(const Type& x) { apr_atomic_set32(&mData, apr_uint32_t(x)); return Type(mData); }
+ void operator -=(Type x) { apr_atomic_sub32(&mData, apr_uint32_t(x)); }
+ void operator +=(Type x) { apr_atomic_add32(&mData, apr_uint32_t(x)); }
+ Type operator ++(int) { return apr_atomic_inc32(&mData); } // Type++
+ Type operator --(int) { return apr_atomic_dec32(&mData); } // Type--
+
+private:
+ apr_uint32_t mData;
+};
+
+typedef LLAtomic32<U32> LLAtomicU32;
+typedef LLAtomic32<S32> LLAtomicS32;
+
+// File IO convenience functions.
+// Returns NULL if the file fails to openm sets *sizep to file size of not NULL
+// abbreviated flags
+#define LL_APR_R (APR_READ) // "r"
+#define LL_APR_W (APR_CREATE|APR_TRUNCATE|APR_WRITE) // "w"
+#define LL_APR_RB (APR_READ|APR_BINARY) // "rb"
+#define LL_APR_WB (APR_CREATE|APR_TRUNCATE|APR_WRITE|APR_BINARY) // "wb"
+#define LL_APR_RPB (APR_READ|APR_WRITE|APR_BINARY) // "r+b"
+#define LL_APR_WPB (APR_CREATE|APR_TRUNCATE|APR_READ|APR_WRITE|APR_BINARY) // "w+b"
+
+//
+//apr_file manager
+//which: 1)only keeps one file open;
+// 2)closes the open file in the destruction function
+// 3)informs the apr_pool to clean the memory when the file is closed.
+//Note: please close an open file at the earliest convenience.
+// especially do not put some time-costly operations between open() and close().
+// otherwise it might lock the APRFilePool.
+//there are two different apr_pools the APRFile can use:
+// 1, a temperary pool passed to an APRFile function, which is used within this function and only once.
+// 2, a global pool.
+//
+class LL_COMMON_API LLAPRFile
+{
+private:
+ apr_file_t* mFile ;
+ LLVolatileAPRPool *mCurrentFilePoolp ; //currently in use apr_pool, could be one of them: sAPRFilePoolp, or a temp pool.
+
+public:
+ LLAPRFile() ;
+ ~LLAPRFile() ;
+
+ apr_status_t open(LLVolatileAPRPool* pool, const std::string& filename, apr_int32_t flags, S32* sizep = NULL);
+ apr_status_t open(const std::string& filename, apr_int32_t flags, apr_pool_t* pool = NULL, S32* sizep = NULL);
+ apr_status_t close() ;
+
+ // Returns actual offset, -1 if seek fails
+ S32 seek(apr_seek_where_t where, S32 offset);
+ apr_status_t eof() { return apr_file_eof(mFile);}
+
+ // Returns bytes read/written, 0 if read/write fails:
+ S32 read(void* buf, S32 nbytes);
+ S32 write(const void* buf, S32 nbytes);
+
+ apr_file_t* getFileHandle() {return mFile;}
+
+private:
+ apr_pool_t* getAPRFilePool(apr_pool_t* pool) ;
+
+//
+//*******************************************************************************************************************************
+//static components
+//
+public:
+ static LLVolatileAPRPool *sAPRFilePoolp ; //a global apr_pool for APRFile, which is used only when local pool does not exist.
+
+private:
+ static apr_file_t* open(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags);
+ static apr_status_t close(apr_file_t* file, LLVolatileAPRPool* pool) ;
+ static S32 seek(apr_file_t* file, apr_seek_where_t where, S32 offset);
+public:
+ // returns false if failure:
+ static bool remove(const std::string& filename, LLVolatileAPRPool* pool = NULL);
+ static bool rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool = NULL);
+ static bool isExist(const std::string& filename, LLVolatileAPRPool* pool = NULL, apr_int32_t flags = APR_READ);
+ static S32 size(const std::string& filename, LLVolatileAPRPool* pool = NULL);
+ static bool makeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL);
+ static bool removeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL);
+
+ // Returns bytes read/written, 0 if read/write fails:
+ static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL);
+ static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL);
+//*******************************************************************************************************************************
+};
+
+/**
+ * @brief Function which approprately logs error or remains quiet on
+ * APR_SUCCESS.
+ * @return Returns <code>true</code> if status is an error condition.
+ */
+bool LL_COMMON_API ll_apr_warn_status(apr_status_t status);
+
+void LL_COMMON_API ll_apr_assert_status(apr_status_t status);
+
+extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool
+
+#endif // LL_LLAPR_H
diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index 5e51188541..8b29c8defa 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -1,204 +1,204 @@ -/** - * @file llassettype.h - * @brief Declaration of LLAssetType. - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#ifndef LL_LLASSETTYPE_H -#define LL_LLASSETTYPE_H - -#include <string> - -#include "stdenums.h" // for EDragAndDropType - -class LLAssetType -{ -public: - enum EType - { - AT_TEXTURE = 0, - // Used for painting the faces of geometry. - // Stored in typical j2c stream format. - - AT_SOUND = 1, - // Used to fill the aural spectrum. - - AT_CALLINGCARD = 2, - // Links instant message access to the user on the card. - // : E.G. A card for yourself, for linden support, for - // : the guy you were talking to in the coliseum. - - AT_LANDMARK = 3, - // Links to places in the world with location and a screen shot or image saved. - // : E.G. Home, linden headquarters, the coliseum, destinations where - // : we want to increase traffic. - - AT_SCRIPT = 4, - // Valid scripts that can be attached to an object. - // : E.G. Open a door, jump into the air. - - AT_CLOTHING = 5, - // A collection of textures and parameters that can be worn by an avatar. - - AT_OBJECT = 6, - // Any combination of textures, sounds, and scripts that are - // associated with a fixed piece of geometry. - // : E.G. A hot tub, a house with working door. - - AT_NOTECARD = 7, - // Just text. - - AT_CATEGORY = 8, - // Holds a collection of inventory items. - // It's treated as an item in the inventory and therefore needs a type. - - AT_ROOT_CATEGORY = 9, - // A user's root inventory category. - // We decided to expose it visually, so it seems logical to fold - // it into the asset types. - - AT_LSL_TEXT = 10, - AT_LSL_BYTECODE = 11, - // The LSL is the scripting language. - // We've split it into a text and bytecode representation. - - AT_TEXTURE_TGA = 12, - // Uncompressed TGA texture. - - AT_BODYPART = 13, - // A collection of textures and parameters that can be worn by an avatar. - - AT_TRASH = 14, - // Only to be used as a marker for a category preferred type. - // Using this, we can throw things in the trash before completely deleting. - - AT_SNAPSHOT_CATEGORY = 15, - // A marker for a folder meant for snapshots. - // No actual assets will be snapshots, though if there were, you - // could interpret them as textures. - - AT_LOST_AND_FOUND = 16, - // Used to stuff lost&found items into. - - AT_SOUND_WAV = 17, - // Uncompressed sound. - - AT_IMAGE_TGA = 18, - // Uncompressed image, non-square. - // Not appropriate for use as a texture. - - AT_IMAGE_JPEG = 19, - // Compressed image, non-square. - // Not appropriate for use as a texture. - - AT_ANIMATION = 20, - // Animation. - - AT_GESTURE = 21, - // Gesture, sequence of animations, sounds, chat, wait steps. - - AT_SIMSTATE = 22, - // Simstate file. - - AT_FAVORITE = 23, - // favorite items - - AT_LINK = 24, - // Inventory symbolic link - - AT_LINK_FOLDER = 25, - // Inventory folder link - - AT_FOLDER_ENSEMBLE_START = 26, - AT_FOLDER_ENSEMBLE_END = 45, - // This range is reserved for special clothing folder types. - - AT_CURRENT_OUTFIT = 46, - // Current outfit - - AT_OUTFIT = 47, - // Predefined outfit ("look") - - AT_MY_OUTFITS = 48, - // Folder that holds your outfits. - - - AT_COUNT = 49, - // +*********************************************************+ - // | TO ADD AN ELEMENT TO THIS ENUM: | - // +*********************************************************+ - // | 1. INSERT BEFORE AT_COUNT | - // | 2. INCREMENT AT_COUNT BY 1 | - // | 3. ADD TO LLAssetDictionary in LLAssetType.cpp | - // | 3. ADD TO DEFAULT_ASSET_FOR_INV in LLInventoryType.cpp | - // +*********************************************************+ - - AT_NONE = -1 - }; - - // machine transation between type and strings - static EType lookup(const char* name); // safe conversion to std::string, *TODO: deprecate - static EType lookup(const std::string& type_name); - static const char* lookup(EType asset_type); - - // translation from a type to a human readable form. - static EType lookupHumanReadable(const char* desc_name); // safe conversion to std::string, *TODO: deprecate - static EType lookupHumanReadable(const std::string& readable_name); - static const char* lookupHumanReadable(EType asset_type); - - // Generate a good default description. You may want to add a verb - // or agent name after this depending on your application. - static void generateDescriptionFor(LLAssetType::EType asset_type, - std::string& description); - - static EType getType(const std::string& desc_name); - static const std::string& getDesc(EType asset_type); - static EDragAndDropType lookupDragAndDropType(EType asset_type); - - static bool lookupCanLink(EType asset_type); - static bool lookupIsLinkType(EType asset_type); - - static const char* lookupCategoryName(EType asset_type); - static bool lookupIsProtectedCategoryType(EType asset_type); - static bool lookupIsEnsembleCategoryType(EType asset_type); - - /* TODO: Change return types from "const char *" to "const std::string &". - This is fairly straightforward, but requires changing some calls to use .c_str(). - e.g.: - - fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType)); - + fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType).c_str()); - */ - -private: - // don't instantiate or derive one of these objects - LLAssetType( void ) {} - ~LLAssetType( void ) {} -}; - -#endif // LL_LLASSETTYPE_H +/**
+ * @file llassettype.h
+ * @brief Declaration of LLAssetType.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLASSETTYPE_H
+#define LL_LLASSETTYPE_H
+
+#include <string>
+
+#include "stdenums.h" // for EDragAndDropType
+
+class LL_COMMON_API LLAssetType
+{
+public:
+ enum EType
+ {
+ AT_TEXTURE = 0,
+ // Used for painting the faces of geometry.
+ // Stored in typical j2c stream format.
+
+ AT_SOUND = 1,
+ // Used to fill the aural spectrum.
+
+ AT_CALLINGCARD = 2,
+ // Links instant message access to the user on the card.
+ // : E.G. A card for yourself, for linden support, for
+ // : the guy you were talking to in the coliseum.
+
+ AT_LANDMARK = 3,
+ // Links to places in the world with location and a screen shot or image saved.
+ // : E.G. Home, linden headquarters, the coliseum, destinations where
+ // : we want to increase traffic.
+
+ AT_SCRIPT = 4,
+ // Valid scripts that can be attached to an object.
+ // : E.G. Open a door, jump into the air.
+
+ AT_CLOTHING = 5,
+ // A collection of textures and parameters that can be worn by an avatar.
+
+ AT_OBJECT = 6,
+ // Any combination of textures, sounds, and scripts that are
+ // associated with a fixed piece of geometry.
+ // : E.G. A hot tub, a house with working door.
+
+ AT_NOTECARD = 7,
+ // Just text.
+
+ AT_CATEGORY = 8,
+ // Holds a collection of inventory items.
+ // It's treated as an item in the inventory and therefore needs a type.
+
+ AT_ROOT_CATEGORY = 9,
+ // A user's root inventory category.
+ // We decided to expose it visually, so it seems logical to fold
+ // it into the asset types.
+
+ AT_LSL_TEXT = 10,
+ AT_LSL_BYTECODE = 11,
+ // The LSL is the scripting language.
+ // We've split it into a text and bytecode representation.
+
+ AT_TEXTURE_TGA = 12,
+ // Uncompressed TGA texture.
+
+ AT_BODYPART = 13,
+ // A collection of textures and parameters that can be worn by an avatar.
+
+ AT_TRASH = 14,
+ // Only to be used as a marker for a category preferred type.
+ // Using this, we can throw things in the trash before completely deleting.
+
+ AT_SNAPSHOT_CATEGORY = 15,
+ // A marker for a folder meant for snapshots.
+ // No actual assets will be snapshots, though if there were, you
+ // could interpret them as textures.
+
+ AT_LOST_AND_FOUND = 16,
+ // Used to stuff lost&found items into.
+
+ AT_SOUND_WAV = 17,
+ // Uncompressed sound.
+
+ AT_IMAGE_TGA = 18,
+ // Uncompressed image, non-square.
+ // Not appropriate for use as a texture.
+
+ AT_IMAGE_JPEG = 19,
+ // Compressed image, non-square.
+ // Not appropriate for use as a texture.
+
+ AT_ANIMATION = 20,
+ // Animation.
+
+ AT_GESTURE = 21,
+ // Gesture, sequence of animations, sounds, chat, wait steps.
+
+ AT_SIMSTATE = 22,
+ // Simstate file.
+
+ AT_FAVORITE = 23,
+ // favorite items
+
+ AT_LINK = 24,
+ // Inventory symbolic link
+
+ AT_LINK_FOLDER = 25,
+ // Inventory folder link
+
+ AT_FOLDER_ENSEMBLE_START = 26,
+ AT_FOLDER_ENSEMBLE_END = 45,
+ // This range is reserved for special clothing folder types.
+
+ AT_CURRENT_OUTFIT = 46,
+ // Current outfit
+
+ AT_OUTFIT = 47,
+ // Predefined outfit ("look")
+
+ AT_MY_OUTFITS = 48,
+ // Folder that holds your outfits.
+
+
+ AT_COUNT = 49,
+ // +*********************************************************+
+ // | TO ADD AN ELEMENT TO THIS ENUM: |
+ // +*********************************************************+
+ // | 1. INSERT BEFORE AT_COUNT |
+ // | 2. INCREMENT AT_COUNT BY 1 |
+ // | 3. ADD TO LLAssetDictionary in LLAssetType.cpp |
+ // | 3. ADD TO DEFAULT_ASSET_FOR_INV in LLInventoryType.cpp |
+ // +*********************************************************+
+
+ AT_NONE = -1
+ };
+
+ // machine transation between type and strings
+ static EType lookup(const char* name); // safe conversion to std::string, *TODO: deprecate
+ static EType lookup(const std::string& type_name);
+ static const char* lookup(EType asset_type);
+
+ // translation from a type to a human readable form.
+ static EType lookupHumanReadable(const char* desc_name); // safe conversion to std::string, *TODO: deprecate
+ static EType lookupHumanReadable(const std::string& readable_name);
+ static const char* lookupHumanReadable(EType asset_type);
+
+ // Generate a good default description. You may want to add a verb
+ // or agent name after this depending on your application.
+ static void generateDescriptionFor(LLAssetType::EType asset_type,
+ std::string& description);
+
+ static EType getType(const std::string& desc_name);
+ static const std::string& getDesc(EType asset_type);
+ static EDragAndDropType lookupDragAndDropType(EType asset_type);
+
+ static bool lookupCanLink(EType asset_type);
+ static bool lookupIsLinkType(EType asset_type);
+
+ static const char* lookupCategoryName(EType asset_type);
+ static bool lookupIsProtectedCategoryType(EType asset_type);
+ static bool lookupIsEnsembleCategoryType(EType asset_type);
+
+ /* TODO: Change return types from "const char *" to "const std::string &".
+ This is fairly straightforward, but requires changing some calls to use .c_str().
+ e.g.:
+ - fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType));
+ + fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType).c_str());
+ */
+
+private:
+ // don't instantiate or derive one of these objects
+ LLAssetType( void ) {}
+ ~LLAssetType( void ) {}
+};
+
+#endif // LL_LLASSETTYPE_H
diff --git a/indra/llcommon/llbase32.h b/indra/llcommon/llbase32.h index 63a93e11ab..0697f7b8e2 100644 --- a/indra/llcommon/llbase32.h +++ b/indra/llcommon/llbase32.h @@ -32,9 +32,9 @@ */ #ifndef LLBASE32_H -#define LLBASE32_h +#define LLBASE32_H -class LLBase32 +class LL_COMMON_API LLBase32 { public: static std::string encode(const U8* input, size_t input_size); diff --git a/indra/llcommon/llbase64.h b/indra/llcommon/llbase64.h index 58414bba8b..c48fea2478 100644 --- a/indra/llcommon/llbase64.h +++ b/indra/llcommon/llbase64.h @@ -32,9 +32,9 @@ */ #ifndef LLBASE64_H -#define LLBASE64_h +#define LLBASE64_H -class LLBase64 +class LL_COMMON_API LLBase64 { public: static std::string encode(const U8* input, size_t input_size); diff --git a/indra/llcommon/llcommon.h b/indra/llcommon/llcommon.h index a1808e8a6c..b36471f9f8 100644 --- a/indra/llcommon/llcommon.h +++ b/indra/llcommon/llcommon.h @@ -37,7 +37,7 @@ #include "lltimer.h" #include "llfile.h" -class LLCommon +class LL_COMMON_API LLCommon { public: static void initClass(); diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp new file mode 100644 index 0000000000..5d23e1d284 --- /dev/null +++ b/indra/llcommon/llcoros.cpp @@ -0,0 +1,118 @@ +/** + * @file llcoros.cpp + * @author Nat Goodspeed + * @date 2009-06-03 + * @brief Implementation for llcoros. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/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::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; +} + +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 ""; +} diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h new file mode 100644 index 0000000000..6c5fa5af6d --- /dev/null +++ b/indra/llcommon/llcoros.h @@ -0,0 +1,149 @@ +/**
+ * @file llcoros.h
+ * @author Nat Goodspeed
+ * @date 2009-06-02
+ * @brief Manage running boost::coroutine instances
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/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) */
diff --git a/indra/llcommon/llcrc.h b/indra/llcommon/llcrc.h index 27fae7d269..74369062cc 100644 --- a/indra/llcommon/llcrc.h +++ b/indra/llcommon/llcrc.h @@ -50,7 +50,7 @@ // llinfos << "File crc: " << crc.getCRC() << llendl; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLCRC +class LL_COMMON_API LLCRC { protected: U32 mCurrent; diff --git a/indra/llcommon/llcriticaldamp.h b/indra/llcommon/llcriticaldamp.h index ad98284a6c..1ea5914b5b 100644 --- a/indra/llcommon/llcriticaldamp.h +++ b/indra/llcommon/llcriticaldamp.h @@ -38,7 +38,7 @@ #include "llframetimer.h" -class LLCriticalDamp +class LL_COMMON_API LLCriticalDamp { public: LLCriticalDamp(); diff --git a/indra/llcommon/llcursortypes.h b/indra/llcommon/llcursortypes.h index bea70351b7..836ecc3c04 100644 --- a/indra/llcommon/llcursortypes.h +++ b/indra/llcommon/llcursortypes.h @@ -77,6 +77,6 @@ enum ECursorType { UI_CURSOR_COUNT // Number of elements in this enum (NOT a cursor) }; -ECursorType getCursorFromString(const std::string& cursor_string); +LL_COMMON_API ECursorType getCursorFromString(const std::string& cursor_string); #endif // LL_LLCURSORTYPES_H diff --git a/indra/llcommon/lldate.h b/indra/llcommon/lldate.h index 23d3b900f8..4dffaafc7d 100644 --- a/indra/llcommon/lldate.h +++ b/indra/llcommon/lldate.h @@ -46,7 +46,7 @@ * * The date class represents a point in time after epoch - 1970-01-01. */ -class LLDate +class LL_COMMON_API LLDate { public: /** @@ -155,10 +155,10 @@ private: }; // Helper function to stream out a date -std::ostream& operator<<(std::ostream& s, const LLDate& date); +LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLDate& date); // Helper function to stream in a date -std::istream& operator>>(std::istream& s, LLDate& date); +LL_COMMON_API std::istream& operator>>(std::istream& s, LLDate& date); const static std::string weekdays[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; diff --git a/indra/llcommon/lldependencies.h b/indra/llcommon/lldependencies.h index 82f53c6e17..e6229db834 100644 --- a/indra/llcommon/lldependencies.h +++ b/indra/llcommon/lldependencies.h @@ -81,7 +81,7 @@ struct instance_from_range: public TYPE * LLDependencies components that should not be reinstantiated for each KEY, * NODE specialization */ -class LLDependenciesBase +class LL_COMMON_API LLDependenciesBase { public: virtual ~LLDependenciesBase() {} diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 6794be4904..4f68fb9f76 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -131,7 +131,7 @@ namespace LLError class CallSite; - class Log + class LL_COMMON_API Log { public: static bool shouldLog(CallSite&); @@ -140,7 +140,7 @@ namespace LLError static void flush(std::ostringstream*, const CallSite&); }; - class CallSite + class LL_COMMON_API CallSite { // Represents a specific place in the code where a message is logged // This is public because it is used by the macros below. It is not @@ -189,7 +189,7 @@ namespace LLError //LLCallStacks is designed not to be thread-safe. //so try not to use it in multiple parallel threads at same time. //Used in a single thread at a time is fine. - class LLCallStacks + class LL_COMMON_API LLCallStacks { private: static char** sBuffer ; diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index a14bcd2ae2..c84f5d0fd7 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -52,12 +52,12 @@ class LLSD; namespace LLError { - void initForServer(const std::string& identity); + LL_COMMON_API void initForServer(const std::string& identity); // resets all logging settings to defaults needed by server processes // logs to stderr, syslog, and windows debug log // the identity string is used for in the syslog - void initForApplication(const std::string& dir); + LL_COMMON_API void initForApplication(const std::string& dir); // resets all logging settings to defaults needed by applicaitons // logs to stderr and windows debug log // sets up log configuration from the file logcontrol.xml in dir @@ -68,14 +68,14 @@ namespace LLError Setting a level means log messages at that level or above. */ - void setPrintLocation(bool); - void setDefaultLevel(LLError::ELevel); - void setFunctionLevel(const std::string& function_name, LLError::ELevel); - void setClassLevel(const std::string& class_name, LLError::ELevel); - void setFileLevel(const std::string& file_name, LLError::ELevel); + LL_COMMON_API void setPrintLocation(bool); + LL_COMMON_API void setDefaultLevel(LLError::ELevel); + LL_COMMON_API void setFunctionLevel(const std::string& function_name, LLError::ELevel); + LL_COMMON_API void setClassLevel(const std::string& class_name, LLError::ELevel); + LL_COMMON_API void setFileLevel(const std::string& file_name, LLError::ELevel); void setTagLevel(const std::string& file_name, LLError::ELevel); - void configure(const LLSD&); + LL_COMMON_API void configure(const LLSD&); // the LLSD can configure all of the settings // usually read automatically from the live errorlog.xml file @@ -85,21 +85,21 @@ namespace LLError */ typedef boost::function<void(const std::string&)> FatalFunction; - void crashAndLoop(const std::string& message); + LL_COMMON_API void crashAndLoop(const std::string& message); // Default fatal function: access null pointer and loops forever - void setFatalFunction(const FatalFunction&); + LL_COMMON_API void setFatalFunction(const FatalFunction&); // The fatal function will be called when an message of LEVEL_ERROR // is logged. Note: supressing a LEVEL_ERROR message from being logged // (by, for example, setting a class level to LEVEL_NONE), will keep // the that message from causing the fatal funciton to be invoked. - FatalFunction getFatalFunction(); + LL_COMMON_API FatalFunction getFatalFunction(); // Retrieve the previously-set FatalFunction /// temporarily override the FatalFunction for the duration of a /// particular scope, e.g. for unit tests - class OverrideFatalFunction + class LL_COMMON_API OverrideFatalFunction { public: OverrideFatalFunction(const FatalFunction& func): @@ -117,15 +117,15 @@ namespace LLError }; typedef std::string (*TimeFunction)(); - std::string utcTime(); + LL_COMMON_API std::string utcTime(); - void setTimeFunction(TimeFunction); + LL_COMMON_API void setTimeFunction(TimeFunction); // The function is use to return the current time, formatted for // display by those error recorders that want the time included. - class Recorder + class LL_COMMON_API Recorder { // An object that handles the actual output or error messages. public: @@ -139,17 +139,17 @@ namespace LLError // included in the text of the message }; - void addRecorder(Recorder*); - void removeRecorder(Recorder*); + LL_COMMON_API void addRecorder(Recorder*); + LL_COMMON_API void removeRecorder(Recorder*); // each error message is passed to each recorder via recordMessage() - void logToFile(const std::string& filename); - void logToFixedBuffer(LLFixedBuffer*); + LL_COMMON_API void logToFile(const std::string& filename); + LL_COMMON_API void logToFixedBuffer(LLFixedBuffer*); // Utilities to add recorders for logging to a file or a fixed buffer // A second call to the same function will remove the logger added // with the first. // Passing the empty string or NULL to just removes any prior. - std::string logFileName(); + LL_COMMON_API std::string logFileName(); // returns name of current logging file, empty string if none @@ -158,11 +158,11 @@ namespace LLError */ class Settings; - Settings* saveAndResetSettings(); - void restoreSettings(Settings *); + LL_COMMON_API Settings* saveAndResetSettings(); + LL_COMMON_API void restoreSettings(Settings *); - std::string abbreviateFile(const std::string& filePath); - int shouldLogCallCount(); + LL_COMMON_API std::string abbreviateFile(const std::string& filePath); + LL_COMMON_API int shouldLogCallCount(); }; diff --git a/indra/llcommon/llerrorthread.h b/indra/llcommon/llerrorthread.h index f1d6ffc34f..3121d29675 100644 --- a/indra/llcommon/llerrorthread.h +++ b/indra/llcommon/llerrorthread.h @@ -35,7 +35,7 @@ #include "llthread.h" -class LLErrorThread : public LLThread +class LL_COMMON_API LLErrorThread : public LLThread { public: LLErrorThread(); diff --git a/indra/llcommon/llevent.h b/indra/llcommon/llevent.h index 2cc8577219..0ea7cf4ae8 100644 --- a/indra/llcommon/llevent.h +++ b/indra/llcommon/llevent.h @@ -47,7 +47,7 @@ class LLEventDispatcher; class LLObservable; // Abstract event. All events derive from LLEvent -class LLEvent : public LLThreadSafeRefCount +class LL_COMMON_API LLEvent : public LLThreadSafeRefCount { protected: virtual ~LLEvent(); @@ -75,7 +75,7 @@ private: }; // Abstract listener. All listeners derive from LLEventListener -class LLEventListener : public LLThreadSafeRefCount +class LL_COMMON_API LLEventListener : public LLThreadSafeRefCount { protected: virtual ~LLEventListener(); @@ -92,7 +92,7 @@ public: }; // A listener which tracks references to it and cleans up when it's deallocated -class LLSimpleListener : public LLEventListener +class LL_COMMON_API LLSimpleListener : public LLEventListener { public: void clearDispatchers(); @@ -117,7 +117,7 @@ struct LLListenerEntry // Base class for a dispatcher - an object which listens // to events being fired and relays them to their // appropriate destinations. -class LLEventDispatcher : public LLThreadSafeRefCount +class LL_COMMON_API LLEventDispatcher : public LLThreadSafeRefCount { protected: virtual ~LLEventDispatcher(); @@ -160,7 +160,7 @@ private: // In order for this class to work properly, it needs // an instance of an LLEventDispatcher to route events to their // listeners. -class LLObservable +class LL_COMMON_API LLObservable { public: // Initialize with the default Dispatcher diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp new file mode 100644 index 0000000000..d598f1cc4a --- /dev/null +++ b/indra/llcommon/lleventcoro.cpp @@ -0,0 +1,129 @@ +/** + * @file lleventcoro.cpp + * @author Nat Goodspeed + * @date 2009-04-29 + * @brief Implementation for lleventcoro. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/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; +} diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h new file mode 100644 index 0000000000..c6d9de171d --- /dev/null +++ b/indra/llcommon/lleventcoro.h @@ -0,0 +1,549 @@ +/** + * @file lleventcoro.h + * @author Nat Goodspeed + * @date 2009-04-29 + * @brief Utilities to interface between coroutines and events. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/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() + << ": " << 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) */ diff --git a/indra/llcommon/lleventdispatcher.cpp b/indra/llcommon/lleventdispatcher.cpp new file mode 100644 index 0000000000..2dbd59b156 --- /dev/null +++ b/indra/llcommon/lleventdispatcher.cpp @@ -0,0 +1,123 @@ +/** + * @file lleventdispatcher.cpp + * @author Nat Goodspeed + * @date 2009-06-18 + * @brief Implementation for lleventdispatcher. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/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" + +LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key): + mDesc(desc), + mKey(key) +{ +} + +LLEventDispatcher::~LLEventDispatcher() +{ +} + +/// Register a callable by name +void LLEventDispatcher::add(const std::string& name, const Callable& callable, const LLSD& required) +{ + mDispatch[name] = DispatchMap::mapped_type(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 (! attemptCall(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 (! attemptCall(name, event)) + { + LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey + << " value '" << name << "'" << LL_ENDL; + } +} + +bool LLEventDispatcher::attemptCall(const std::string& name, const LLSD& event) const +{ + DispatchMap::const_iterator found = mDispatch.find(name); + if (found == mDispatch.end()) + { + // The reason we only return false, leaving it up to our caller to die + // with LL_ERRS, is that different callers have different amounts of + // available information. + return false; + } + // Found the name, so it's plausible to even attempt the call. But first, + // validate the syntax of the event itself. + std::string mismatch(llsd_matches(found->second.second, event)); + if (! mismatch.empty()) + { + LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ") calling '" << name + << "': bad request: " << mismatch << LL_ENDL; + } + // Event syntax looks good, go for it! + (found->second.first)(event); + return true; // tell caller we were able to call +} + +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; +} diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h new file mode 100644 index 0000000000..ef83ebabc1 --- /dev/null +++ b/indra/llcommon/lleventdispatcher.h @@ -0,0 +1,126 @@ +/**
+ * @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=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLEVENTDISPATCHER_H)
+#define LL_LLEVENTDISPATCHER_H
+
+#include <string>
+#include <map>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <typeinfo>
+#include "llevents.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();
+
+ /// Accept any C++ callable, typically a boost::bind() expression
+ typedef boost::function<void(const LLSD&)> Callable;
+
+ /**
+ * Register a @a callable by @a name. The optional @a required parameter
+ * is used to validate the structure of each incoming event (see
+ * llsd_matches()).
+ */
+ void add(const std::string& name, const Callable& callable, const LLSD& required=LLSD());
+
+ /**
+ * Special case: a subclass of this class can pass an unbound member
+ * function pointer without explicitly specifying the
+ * <tt>boost::bind()</tt> expression.
+ */
+ template <class CLASS>
+ void add(const std::string& name, void (CLASS::*method)(const LLSD&),
+ const LLSD& required=LLSD())
+ {
+ addMethod<CLASS>(name, method, required);
+ }
+
+ /// Overload for both const and non-const methods
+ template <class CLASS>
+ void add(const std::string& name, void (CLASS::*method)(const LLSD&) const,
+ const LLSD& required=LLSD())
+ {
+ addMethod<CLASS>(name, method, required);
+ }
+
+ /// 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;
+
+ /// 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;
+
+private:
+ template <class CLASS, typename METHOD>
+ void addMethod(const std::string& name, const METHOD& method, const LLSD& required)
+ {
+ CLASS* downcast = dynamic_cast<CLASS*>(this);
+ if (! downcast)
+ {
+ addFail(name, typeid(CLASS).name());
+ }
+ else
+ {
+ add(name, boost::bind(method, downcast, _1), required);
+ }
+ }
+ void addFail(const std::string& name, const std::string& classname) const;
+ /// try to dispatch, return @c true if success
+ bool attemptCall(const std::string& name, const LLSD& event) const;
+
+ std::string mDesc, mKey;
+ typedef std::map<std::string, std::pair<Callable, LLSD> > DispatchMap;
+ DispatchMap mDispatch;
+};
+
+/**
+ * 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) */
diff --git a/indra/llcommon/lleventfilter.cpp b/indra/llcommon/lleventfilter.cpp new file mode 100644 index 0000000000..74133781be --- /dev/null +++ b/indra/llcommon/lleventfilter.cpp @@ -0,0 +1,149 @@ +/** + * @file lleventfilter.cpp + * @author Nat Goodspeed + * @date 2009-03-05 + * @brief Implementation for lleventfilter. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/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(); +} diff --git a/indra/llcommon/lleventfilter.h b/indra/llcommon/lleventfilter.h new file mode 100644 index 0000000000..89f0c7ea43 --- /dev/null +++ b/indra/llcommon/lleventfilter.h @@ -0,0 +1,186 @@ +/** + * @file lleventfilter.h + * @author Nat Goodspeed + * @date 2009-03-05 + * @brief Define LLEventFilter: LLEventStream subclass with conditions + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/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) */ diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index eb380ba7c8..aec9acc7ef 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -38,6 +38,9 @@ #pragma warning (pop) #endif // other Linden headers +#include "stringize.h" +#include "llerror.h" +#include "llsdutil.h" /***************************************************************************** * queue_names: specify LLEventPump names that should be instantiated as @@ -56,14 +59,12 @@ const char* queue_names[] = /***************************************************************************** * If there's a "mainloop" pump, listen on that to flush all LLEventQueues *****************************************************************************/ -struct RegisterFlush +struct RegisterFlush : public LLEventTrackable { RegisterFlush(): - pumps(LLEventPumps::instance()), - mainloop(pumps.obtain("mainloop")), - name("flushLLEventQueues") + pumps(LLEventPumps::instance()) { - mainloop.listen(name, boost::bind(&RegisterFlush::flush, this, _1)); + pumps.obtain("mainloop").listen("flushLLEventQueues", boost::bind(&RegisterFlush::flush, this, _1)); } bool flush(const LLSD&) { @@ -72,11 +73,9 @@ struct RegisterFlush } ~RegisterFlush() { - mainloop.stopListening(name); + // LLEventTrackable handles stopListening for us. } LLEventPumps& pumps; - LLEventPump& mainloop; - const std::string name; }; static RegisterFlush registerFlush; @@ -256,6 +255,12 @@ LLEventPump::~LLEventPump() // static data member const LLEventPump::NameList LLEventPump::empty; +std::string LLEventPump::inventName(const std::string& pfx) +{ + static long suffix = 0; + return STRINGIZE(pfx << suffix++); +} + LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventListener& listener, const NameList& after, const NameList& before) @@ -499,3 +504,26 @@ bool LLListenerOrPumpName::operator()(const LLSD& event) const } 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; +} diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 240adcdd41..b999bfafa7 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -1,831 +1,934 @@ -/** - * @file llevents.h - * @author Kent Quirk, Nat Goodspeed - * @date 2008-09-11 - * @brief This is an implementation of the event system described at - * https://wiki.lindenlab.com/wiki/Viewer:Messaging/Event_System, - * originally introduced in llnotifications.h. It has nothing - * whatsoever to do with the older system in llevent.h. - * - * $LicenseInfo:firstyear=2008&license=viewergpl$ - * Copyright (c) 2008, Linden Research, Inc. - * $/LicenseInfo$ - */ - -#if ! defined(LL_LLEVENTS_H) -#define LL_LLEVENTS_H - -#include <string> -#include <map> -#include <set> -#include <vector> -#include <list> -#include <deque> -#include <stdexcept> -#if LL_WINDOWS - #pragma warning (push) - #pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch - #pragma warning (disable : 4264) -#endif -#include <boost/signals2.hpp> -#if LL_WINDOWS - #pragma warning (pop) -#endif - -#include <boost/bind.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/enable_shared_from_this.hpp> -#include <boost/utility.hpp> // noncopyable -#include <boost/optional/optional.hpp> -#include <boost/ptr_container/ptr_vector.hpp> -#include <boost/visit_each.hpp> -#include <boost/ref.hpp> // reference_wrapper -#include <boost/type_traits/is_pointer.hpp> -#include <boost/utility/addressof.hpp> -#include <boost/preprocessor/repetition/enum_params.hpp> -#include <boost/preprocessor/iteration/local.hpp> -#include <boost/function.hpp> -#include <boost/static_assert.hpp> -#include "llsd.h" -#include "llsingleton.h" -#include "lldependencies.h" - -// override this to allow binding free functions with more parameters -#ifndef LLEVENTS_LISTENER_ARITY -#define LLEVENTS_LISTENER_ARITY 10 -#endif - -// hack for testing -#ifndef testable -#define testable private -#endif - -/***************************************************************************** -* 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::signals 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; - } -}; - -/** - * 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. - * - * @internal - * The @c float template parameter indicates that we will internally use @c - * float to indicate relative listener order on a given LLStandardSignal. - * Don't worry, the @c float values are strictly internal! They are not part - * of the interface, for the excellent reason that requiring the caller to - * specify a numeric key to establish order means that the caller must know - * the universe of possible values. We use LLDependencies for that instead. - */ -typedef boost::signals2::signal<bool(const LLSD&), LLStopWhenHandled, float> LLStandardSignal; -/// Methods that forward listeners (e.g. constructed with -/// <tt>boost::bind()</tt>) should accept (const LLEventListener&) -typedef LLStandardSignal::slot_type LLEventListener; -/// Result of registering a listener, supports <tt>connected()</tt>, -/// <tt>disconnect()</tt> and <tt>blocked()</tt> -typedef boost::signals2::connection LLBoundListener; - -/** - * A common idiom for event-based code is to accept either a callable -- - * directly called on completion -- or the string name of an LLEventPump on - * which to post the completion event. Specifying a parameter as <tt>const - * LLListenerOrPumpName&</tt> allows either. - * - * Calling a validly-constructed LLListenerOrPumpName, passing the LLSD - * 'event' object, either calls the callable or posts the event to the named - * LLEventPump. - * - * A default-constructed LLListenerOrPumpName is 'empty'. (This is useful as - * the default value of an optional method parameter.) Calling it throws - * LLListenerOrPumpName::Empty. Test for this condition beforehand using - * either <tt>if (param)</tt> or <tt>if (! param)</tt>. - */ -class LLListenerOrPumpName -{ -public: - /// passing string name of LLEventPump - LLListenerOrPumpName(const std::string& pumpname); - /// passing string literal (overload so compiler isn't forced to infer - /// double conversion) - LLListenerOrPumpName(const char* pumpname); - /// passing listener -- the "anything else" catch-all case. The type of an - /// object constructed by boost::bind() isn't intended to be written out. - /// Normally we'd just accept 'const LLEventListener&', but that would - /// require double implicit conversion: boost::bind() object to - /// LLEventListener, LLEventListener to LLListenerOrPumpName. So use a - /// template to forward anything. - template<typename T> - LLListenerOrPumpName(const T& listener): mListener(listener) {} - - /// for omitted method parameter: uninitialized mListener - LLListenerOrPumpName() {} - - /// test for validity - operator bool() const { return bool(mListener); } - bool operator! () const { return ! mListener; } - - /// explicit accessor - const LLEventListener& getListener() const { return *mListener; } - - /// implicit conversion to LLEventListener - operator LLEventListener() const { return *mListener; } - - /// allow calling directly - bool operator()(const LLSD& event) const; - - /// exception if you try to call when empty - struct Empty: public std::runtime_error - { - Empty(const std::string& what): - std::runtime_error(std::string("LLListenerOrPumpName::Empty: ") + what) {} - }; - -private: - boost::optional<LLEventListener> mListener; -}; - -/***************************************************************************** -* LLEventPumps -*****************************************************************************/ -class LLEventPump; - -/** - * LLEventPumps is a Singleton manager through which one typically accesses - * this subsystem. - */ -class LLEventPumps: public LLSingleton<LLEventPumps> -{ - friend class LLSingleton<LLEventPumps>; -public: - /** - * Find or create an LLEventPump instance with a specific name. We return - * a reference so there's no question about ownership. obtain() @em finds - * an instance without conferring @em ownership. - */ - LLEventPump& obtain(const std::string& name); - /** - * Flush all known LLEventPump instances - */ - void flush(); - -private: - friend class LLEventPump; - /** - * Register a new LLEventPump instance (internal) - */ - std::string registerNew(const LLEventPump&, const std::string& name, bool tweak); - /** - * Unregister a doomed LLEventPump instance (internal) - */ - void unregister(const LLEventPump&); - -private: - LLEventPumps(); - ~LLEventPumps(); - -testable: - // Map of all known LLEventPump instances, whether or not we instantiated - // them. We store a plain old LLEventPump* because this map doesn't claim - // ownership of the instances. Though the common usage pattern is to - // request an instance using obtain(), it's fair to instantiate an - // LLEventPump subclass statically, as a class member, on the stack or on - // the heap. In such cases, the instantiating party is responsible for its - // lifespan. - typedef std::map<std::string, LLEventPump*> PumpMap; - PumpMap mPumpMap; - // Set of all LLEventPumps we instantiated. Membership in this set means - // we claim ownership, and will delete them when this LLEventPumps is - // destroyed. - typedef std::set<LLEventPump*> PumpSet; - PumpSet mOurPumps; - // LLEventPump names that should be instantiated as LLEventQueue rather - // than as LLEventStream - typedef std::set<std::string> PumpNames; - PumpNames mQueueNames; -}; - -/***************************************************************************** -* details -*****************************************************************************/ -namespace LLEventDetail -{ - /// Any callable capable of connecting an LLEventListener to an - /// LLStandardSignal to produce an LLBoundListener can be mapped to this - /// signature. - typedef boost::function<LLBoundListener(const LLEventListener&)> ConnectFunc; - - /** - * Utility template function to use Visitor appropriately - * - * @param listener Callable to connect, typically a boost::bind() - * expression. This will be visited by Visitor using boost::visit_each(). - * @param connect_func Callable that will connect() @a listener to an - * LLStandardSignal, returning LLBoundListener. - */ - template <typename LISTENER> - LLBoundListener visit_and_connect(const LISTENER& listener, - const ConnectFunc& connect_func); -} // namespace LLEventDetail - -/***************************************************************************** -* LLEventPump -*****************************************************************************/ -/** - * LLEventPump is the base class interface through which we access the - * concrete subclasses LLEventStream and LLEventQueue. - */ -class LLEventPump: boost::noncopyable -{ -public: - /** - * Exception thrown by LLEventPump(). You are trying to instantiate an - * LLEventPump (subclass) using the same name as some other instance, and - * you didn't pass <tt>tweak=true</tt> to permit it to generate a unique - * variant. - */ - struct DupPumpName: public std::runtime_error - { - DupPumpName(const std::string& what): - std::runtime_error(std::string("DupPumpName: ") + what) {} - }; - - /** - * Instantiate an LLEventPump (subclass) with the string name by which it - * can be found using LLEventPumps::obtain(). - * - * If you pass (or default) @a tweak to @c false, then a duplicate name - * will throw DupPumpName. This won't happen if LLEventPumps::obtain() - * instantiates the LLEventPump, because obtain() uses find-or-create - * logic. It can only happen if you instantiate an LLEventPump in your own - * code -- and a collision with the name of some other LLEventPump is - * likely to cause much more subtle problems! - * - * When you hand-instantiate an LLEventPump, consider passing @a tweak as - * @c true. This directs LLEventPump() to append a suffix to the passed @a - * name to make it unique. You can retrieve the adjusted name by calling - * getName() on your new instance. - */ - LLEventPump(const std::string& name, bool tweak=false); - virtual ~LLEventPump(); - - /// group exceptions thrown by listen(). We use exceptions because these - /// particular errors are likely to be coding errors, found and fixed by - /// the developer even before preliminary checkin. - struct ListenError: public std::runtime_error - { - ListenError(const std::string& what): std::runtime_error(what) {} - }; - /** - * exception thrown by listen(). You are attempting to register a - * listener on this LLEventPump using the same listener name as an - * already-registered listener. - */ - struct DupListenerName: public ListenError - { - DupListenerName(const std::string& what): - ListenError(std::string("DupListenerName: ") + what) - {} - }; - /** - * exception thrown by listen(). The order dependencies specified for your - * listener are incompatible with existing listeners. - * - * Consider listener "a" which specifies before "b" and "b" which - * specifies before "c". You are now attempting to register "c" before - * "a". There is no order that can satisfy all constraints. - */ - struct Cycle: public ListenError - { - Cycle(const std::string& what): ListenError(std::string("Cycle: ") + what) {} - }; - /** - * exception thrown by listen(). This one means that your new listener - * would force a change to the order of previously-registered listeners, - * and we don't have a good way to implement that. - * - * Consider listeners "some", "other" and "third". "some" and "other" are - * registered earlier without specifying relative order, so "other" - * happens to be first. Now you attempt to register "third" after "some" - * and before "other". Whoops, that would require swapping "some" and - * "other", which we can't do. Instead we throw this exception. - * - * It may not be possible to change the registration order so we already - * know "third"s order requirement by the time we register the second of - * "some" and "other". A solution would be to specify that "some" must - * come before "other", or equivalently that "other" must come after - * "some". - */ - struct OrderChange: public ListenError - { - OrderChange(const std::string& what): ListenError(std::string("OrderChange: ") + what) {} - }; - - /// used by listen() - typedef std::vector<std::string> NameList; - /// convenience placeholder for when you explicitly want to pass an empty - /// NameList - const static NameList empty; - - /// Get this LLEventPump's name - std::string getName() const { return mName; } - - /** - * Register a new listener with a unique name. Specify an optional list - * of other listener names after which this one must be called, likewise - * an optional list of other listener names before which this one must be - * called. The other listeners mentioned need not yet be registered - * themselves. listen() can throw any ListenError; see ListenError - * subclasses. - * - * If (as is typical) you pass a <tt>boost::bind()</tt> expression, - * listen() will inspect the components of that expression. If a bound - * object matches any of several cases, the connection will automatically - * be disconnected when that object is destroyed. - * - * * You bind a <tt>boost::weak_ptr</tt>. - * * Binding a <tt>boost::shared_ptr</tt> that way would ensure that the - * referenced object would @em never be destroyed, since the @c - * shared_ptr stored in the LLEventPump would remain an outstanding - * reference. Use the weaken() function to convert your @c shared_ptr to - * @c weak_ptr. Because this is easy to forget, binding a @c shared_ptr - * will produce a compile error (@c BOOST_STATIC_ASSERT failure). - * * You bind a simple pointer or reference to an object derived from - * <tt>boost::enable_shared_from_this</tt>. (UNDER CONSTRUCTION) - * * You bind a simple pointer or reference to an object derived from - * LLEventTrackable. Unlike the cases described above, though, this is - * vulnerable to a couple of cross-thread race conditions, as described - * in the LLEventTrackable documentation. - */ - template <typename LISTENER> - LLBoundListener listen(const std::string& name, const LISTENER& listener, - const NameList& after=NameList(), - const NameList& before=NameList()) - { - // Examine listener, using our listen_impl() method to make the - // actual connection. - // This is why listen() is a template. Conversion from boost::bind() - // to LLEventListener performs type erasure, so it's important to look - // at the boost::bind object itself before that happens. - return LLEventDetail::visit_and_connect(listener, - boost::bind(&LLEventPump::listen_impl, - this, - name, - _1, - after, - before)); - } - - /// Get the LLBoundListener associated with the passed name (dummy - /// LLBoundListener if not found) - virtual LLBoundListener getListener(const std::string& name) const; - /** - * Instantiate one of these to block an existing connection: - * @code - * { // in some local scope - * LLEventPump::Blocker block(someLLBoundListener); - * // code that needs the connection blocked - * } // unblock the connection again - * @endcode - */ - typedef boost::signals2::shared_connection_block Blocker; - /// Unregister a listener by name. Prefer this to - /// <tt>getListener(name).disconnect()</tt> because stopListening() also - /// forgets this name. - virtual void stopListening(const std::string& name); - /// Post an event to all listeners. The @c bool return is only meaningful - /// if the underlying leaf class is LLEventStream -- beware of relying on - /// it too much! Truthfully, we return @c bool mostly to permit chaining - /// one LLEventPump as a listener on another. - virtual bool post(const LLSD&) = 0; - /// Enable/disable: while disabled, silently ignore all post() calls - virtual void enable(bool enabled=true) { mEnabled = enabled; } - /// query - virtual bool enabled() const { return mEnabled; } - -private: - friend class LLEventPumps; - /// flush queued events - virtual void flush() {} - -private: - virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&, - const NameList& after, - const NameList& before); - std::string mName; - -protected: - /// implement the dispatching - LLStandardSignal mSignal; - /// valve open? - bool mEnabled; - /// Map of named listeners. This tracks the listeners that actually exist - /// at this moment. When we stopListening(), we discard the entry from - /// this map. - typedef std::map<std::string, boost::signals2::connection> ConnectionMap; - ConnectionMap mConnections; - typedef LLDependencies<std::string, float> DependencyMap; - /// Dependencies between listeners. For each listener, track the float - /// used to establish its place in mSignal's order. This caches all the - /// listeners that have ever registered; stopListening() does not discard - /// the entry from this map. This is to avoid a new dependency sort if the - /// same listener with the same dependencies keeps hopping on and off this - /// LLEventPump. - DependencyMap mDeps; -}; - -/***************************************************************************** -* LLEventStream -*****************************************************************************/ -/** - * LLEventStream is a thin wrapper around LLStandardSignal. Posting an - * event immediately calls all registered listeners. - */ -class LLEventStream: public LLEventPump -{ -public: - LLEventStream(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {} - virtual ~LLEventStream() {} - - /// Post an event to all listeners - virtual bool post(const LLSD& event); -}; - -/***************************************************************************** -* LLEventQueue -*****************************************************************************/ -/** - * LLEventQueue isa LLEventPump whose post() method defers calling registered - * listeners until flush() is called. - */ -class LLEventQueue: public LLEventPump -{ -public: - LLEventQueue(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {} - virtual ~LLEventQueue() {} - - /// Post an event to all listeners - virtual bool post(const LLSD& event); - -private: - /// flush queued events - virtual void flush(); - -private: - typedef std::deque<LLSD> EventQueue; - EventQueue mEventQueue; -}; - -/***************************************************************************** -* LLEventTrackable and underpinnings -*****************************************************************************/ -/** - * LLEventTrackable wraps boost::signals2::trackable, which resembles - * boost::trackable. Derive your listener class from LLEventTrackable instead, - * and use something like - * <tt>LLEventPump::listen(boost::bind(&YourTrackableSubclass::method, - * instance, _1))</tt>. This will implicitly disconnect when the object - * referenced by @c instance is destroyed. - * - * @note - * LLEventTrackable doesn't address a couple of cases: - * * Object destroyed during call - * - You enter a slot call in thread A. - * - Thread B destroys the object, which of course disconnects it from any - * future slot calls. - * - Thread A's call uses 'this', which now refers to a defunct object. - * Undefined behavior results. - * * Call during destruction - * - @c MySubclass is derived from LLEventTrackable. - * - @c MySubclass registers one of its own methods using - * <tt>LLEventPump::listen()</tt>. - * - The @c MySubclass object begins destruction. <tt>~MySubclass()</tt> - * runs, destroying state specific to the subclass. (For instance, a - * <tt>Foo*</tt> data member is <tt>delete</tt>d but not zeroed.) - * - The listening method will not be disconnected until - * <tt>~LLEventTrackable()</tt> runs. - * - Before we get there, another thread posts data to the @c LLEventPump - * instance, calling the @c MySubclass method. - * - The method in question relies on valid @c MySubclass state. (For - * instance, it attempts to dereference the <tt>Foo*</tt> pointer that was - * <tt>delete</tt>d but not zeroed.) - * - Undefined behavior results. - * If you suspect you may encounter any such scenario, you're better off - * managing the lifespan of your object with <tt>boost::shared_ptr</tt>. - * Passing <tt>LLEventPump::listen()</tt> a <tt>boost::bind()</tt> expression - * involving a <tt>boost::weak_ptr<Foo></tt> is recognized specially, engaging - * thread-safe Boost.Signals2 machinery. - */ -typedef boost::signals2::trackable LLEventTrackable; - -/** - * We originally provided a suite of overloaded - * LLEventTrackable::listenTo(LLEventPump&, ...) methods that would call - * LLEventPump::listen(...) and then pass the returned LLBoundListener to - * LLEventTrackable::track(). This was workable but error-prone: the coder - * must remember to call listenTo() rather than the more straightforward - * listen() method. - * - * Now we publish only the single canonical listen() method, so there's a - * uniform mechanism. Having a single way to do this is good, in that there's - * no question in the coder's mind which of several alternatives to choose. - * - * To support automatic connection management, we use boost::visit_each - * (http://www.boost.org/doc/libs/1_37_0/doc/html/boost/visit_each.html) to - * inspect each argument of a boost::bind expression. (Although the visit_each - * mechanism was first introduced with the original Boost.Signals library, it - * was only later documented.) - * - * Cases: - * * At least one of the function's arguments is a boost::weak_ptr<T>. Pass - * the corresponding shared_ptr to slot_type::track(). Ideally that would be - * the object whose method we want to call, but in fact we do the same for - * any weak_ptr we might find among the bound arguments. If we're passing - * our bound method a weak_ptr to some object, wouldn't the destruction of - * that object invalidate the call? So we disconnect automatically when any - * such object is destroyed. This is the mechanism preferred by boost:: - * signals2. - * * One of the functions's arguments is a boost::shared_ptr<T>. This produces - * a compile error: the bound copy of the shared_ptr stored in the - * boost_bind object stored in the signal object would make the referenced - * T object immortal. We provide a weaken() function. Pass - * weaken(your_shared_ptr) instead. (We can inspect, but not modify, the - * boost::bind object. Otherwise we'd replace the shared_ptr with weak_ptr - * implicitly and just proceed.) - * * One of the function's arguments is a plain pointer/reference to an object - * derived from boost::enable_shared_from_this. We assume that this object - * is managed using boost::shared_ptr, so we implicitly extract a shared_ptr - * and track that. (UNDER CONSTRUCTION) - * * One of the function's arguments is derived from LLEventTrackable. Pass - * the LLBoundListener to its LLEventTrackable::track(). This is vulnerable - * to a couple different race conditions, as described in LLEventTrackable - * documentation. (NOTE: Now that LLEventTrackable is a typedef for - * boost::signals2::trackable, the Signals2 library handles this itself, so - * our visitor needs no special logic for this case.) - * * Any other argument type is irrelevant to automatic connection management. - */ - -namespace LLEventDetail -{ - template <typename F> - const F& unwrap(const F& f) { return f; } - - template <typename F> - const F& unwrap(const boost::reference_wrapper<F>& f) { return f.get(); } - - // Most of the following is lifted from the Boost.Signals use of - // visit_each. - template<bool Cond> struct truth {}; - - /** - * boost::visit_each() Visitor, used on a template argument <tt>const F& - * f</tt> as follows (see visit_and_connect()): - * @code - * LLEventListener listener(f); - * Visitor visitor(listener); // bind listener so it can track() shared_ptrs - * using boost::visit_each; // allow unqualified visit_each() call for ADL - * visit_each(visitor, unwrap(f)); - * @endcode - */ - class Visitor - { - public: - /** - * Visitor binds a reference to LLEventListener so we can track() any - * shared_ptrs we find in the argument list. - */ - Visitor(LLEventListener& listener): - mListener(listener) - { - } - - /** - * boost::visit_each() calls this method for each component of a - * boost::bind() expression. - */ - template <typename T> - void operator()(const T& t) const - { - decode(t, 0); - } - - private: - // decode() decides between a reference wrapper and anything else - // boost::ref() variant - template<typename T> - void decode(const boost::reference_wrapper<T>& t, int) const - { -// add_if_trackable(t.get_pointer()); - } - - // decode() anything else - template<typename T> - void decode(const T& t, long) const - { - typedef truth<(boost::is_pointer<T>::value)> is_a_pointer; - maybe_get_pointer(t, is_a_pointer()); - } - - // maybe_get_pointer() decides between a pointer and a non-pointer - // plain pointer variant - template<typename T> - void maybe_get_pointer(const T& t, truth<true>) const - { -// add_if_trackable(t); - } - - // shared_ptr variant - template<typename T> - void maybe_get_pointer(const boost::shared_ptr<T>& t, truth<false>) const - { - // If we have a shared_ptr to this object, it doesn't matter - // whether the object is derived from LLEventTrackable, so no - // further analysis of T is needed. -// mListener.track(t); - - // Make this case illegal. Passing a bound shared_ptr to - // slot_type::track() is useless, since the bound shared_ptr will - // keep the object alive anyway! Force the coder to cast to weak_ptr. - - // Trivial as it is, make the BOOST_STATIC_ASSERT() condition - // dependent on template param so the macro is only evaluated if - // this method is in fact instantiated, as described here: - // http://www.boost.org/doc/libs/1_34_1/doc/html/boost_staticassert.html - - // ATTENTION: Don't bind a shared_ptr<anything> using - // LLEventPump::listen(boost::bind()). Doing so captures a copy of - // the shared_ptr, making the referenced object effectively - // immortal. Use the weaken() function, e.g.: - // somepump.listen(boost::bind(...weaken(my_shared_ptr)...)); - // This lets us automatically disconnect when the referenced - // object is destroyed. - BOOST_STATIC_ASSERT(sizeof(T) == 0); - } - - // weak_ptr variant - template<typename T> - void maybe_get_pointer(const boost::weak_ptr<T>& t, truth<false>) const - { - // If we have a weak_ptr to this object, it doesn't matter - // whether the object is derived from LLEventTrackable, so no - // further analysis of T is needed. - mListener.track(t); -// std::cout << "Found weak_ptr<" << typeid(T).name() << ">!\n"; - } - -#if 0 - // reference to anything derived from boost::enable_shared_from_this - template <typename T> - inline void maybe_get_pointer(const boost::enable_shared_from_this<T>& ct, - truth<false>) const - { - // Use the slot_type::track(shared_ptr) mechanism. Cast away - // const-ness because (in our code base anyway) it's unusual - // to find shared_ptr<const T>. - boost::enable_shared_from_this<T>& - t(const_cast<boost::enable_shared_from_this<T>&>(ct)); - std::cout << "Capturing shared_from_this()" << std::endl; - boost::shared_ptr<T> sp(t.shared_from_this()); -/*==========================================================================*| - std::cout << "Capturing weak_ptr" << std::endl; - boost::weak_ptr<T> wp(sp); -|*==========================================================================*/ - std::cout << "Tracking shared__ptr" << std::endl; - mListener.track(sp); - } -#endif - - // non-pointer variant - template<typename T> - void maybe_get_pointer(const T& t, truth<false>) const - { - // Take the address of this object, because the object itself may be - // trackable -// add_if_trackable(boost::addressof(t)); - } - -/*==========================================================================*| - // add_if_trackable() adds LLEventTrackable objects to mTrackables - inline void add_if_trackable(const LLEventTrackable* t) const - { - if (t) - { - } - } - - // pointer to anything not an LLEventTrackable subclass - inline void add_if_trackable(const void*) const - { - } - - // pointer to free function - // The following construct uses the preprocessor to generate - // add_if_trackable() overloads accepting pointer-to-function taking - // 0, 1, ..., LLEVENTS_LISTENER_ARITY parameters of arbitrary type. -#define BOOST_PP_LOCAL_MACRO(n) \ - template <typename R \ - BOOST_PP_COMMA_IF(n) \ - BOOST_PP_ENUM_PARAMS(n, typename T)> \ - inline void \ - add_if_trackable(R (*)(BOOST_PP_ENUM_PARAMS(n, T))) const \ - { \ - } -#define BOOST_PP_LOCAL_LIMITS (0, LLEVENTS_LISTENER_ARITY) -#include BOOST_PP_LOCAL_ITERATE() -#undef BOOST_PP_LOCAL_MACRO -#undef BOOST_PP_LOCAL_LIMITS -|*==========================================================================*/ - - /// Bind a reference to the LLEventListener to call its track() method. - LLEventListener& mListener; - }; - - /** - * Utility template function to use Visitor appropriately - * - * @param raw_listener Callable to connect, typically a boost::bind() - * expression. This will be visited by Visitor using boost::visit_each(). - * @param connect_funct Callable that will connect() @a raw_listener to an - * LLStandardSignal, returning LLBoundListener. - */ - template <typename LISTENER> - LLBoundListener visit_and_connect(const LISTENER& raw_listener, - const ConnectFunc& connect_func) - { - // Capture the listener - LLEventListener listener(raw_listener); - // Define our Visitor, binding the listener so we can call - // listener.track() if we discover any shared_ptr<Foo>. - LLEventDetail::Visitor visitor(listener); - // Allow unqualified visit_each() call for ADL - using boost::visit_each; - // Visit each component of a boost::bind() expression. Pass - // 'raw_listener', our template argument, rather than 'listener' from - // which type details have been erased. unwrap() comes from - // Boost.Signals, in case we were passed a boost::ref(). - visit_each(visitor, LLEventDetail::unwrap(raw_listener)); - // Make the connection using passed function. At present, wrapping - // this functionality into this function is a bit silly: we don't - // really need a visit_and_connect() function any more, just a visit() - // function. The definition of this function dates from when, after - // visit_each(), after establishing the connection, we had to - // postprocess the new connection with the visitor object. That's no - // longer necessary. - return connect_func(listener); - } -} // namespace LLEventDetail - -// Somewhat to my surprise, passing boost::bind(...boost::weak_ptr<T>...) to -// listen() fails in Boost code trying to instantiate LLEventListener (i.e. -// LLStandardSignal::slot_type) because the boost::get_pointer() utility function isn't -// specialized for boost::weak_ptr. This remedies that omission. -namespace boost -{ - template <typename T> - T* get_pointer(const weak_ptr<T>& ptr) { return shared_ptr<T>(ptr).get(); } -} - -/// Since we forbid use of listen(boost::bind(...shared_ptr<T>...)), provide an -/// easy way to cast to the corresponding weak_ptr. -template <typename T> -boost::weak_ptr<T> weaken(const boost::shared_ptr<T>& ptr) -{ - return boost::weak_ptr<T>(ptr); -} - -#endif /* ! defined(LL_LLEVENTS_H) */ +/**
+ * @file llevents.h
+ * @author Kent Quirk, Nat Goodspeed
+ * @date 2008-09-11
+ * @brief This is an implementation of the event system described at
+ * https://wiki.lindenlab.com/wiki/Viewer:Messaging/Event_System,
+ * originally introduced in llnotifications.h. It has nothing
+ * whatsoever to do with the older system in llevent.h.
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ * Copyright (c) 2008, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLEVENTS_H)
+#define LL_LLEVENTS_H
+
+#include <string>
+#include <map>
+#include <set>
+#include <vector>
+#include <deque>
+#include <stdexcept>
+#if LL_WINDOWS
+ #pragma warning (push)
+ #pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch
+ #pragma warning (disable : 4264)
+#endif
+#include <boost/signals2.hpp>
+#if LL_WINDOWS
+ #pragma warning (pop)
+#endif
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/utility.hpp> // noncopyable
+#include <boost/optional/optional.hpp>
+#include <boost/visit_each.hpp>
+#include <boost/ref.hpp> // reference_wrapper
+#include <boost/type_traits/is_pointer.hpp>
+#include <boost/function.hpp>
+#include <boost/static_assert.hpp>
+#include "llsd.h"
+#include "llsingleton.h"
+#include "lldependencies.h"
+
+// override this to allow binding free functions with more parameters
+#ifndef LLEVENTS_LISTENER_ARITY
+#define LLEVENTS_LISTENER_ARITY 10
+#endif
+
+// hack for testing
+#ifndef testable
+#define testable private
+#endif
+
+/*****************************************************************************
+* 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::signals 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;
+ }
+};
+
+/**
+ * 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.
+ *
+ * @internal
+ * The @c float template parameter indicates that we will internally use @c
+ * float to indicate relative listener order on a given LLStandardSignal.
+ * Don't worry, the @c float values are strictly internal! They are not part
+ * of the interface, for the excellent reason that requiring the caller to
+ * specify a numeric key to establish order means that the caller must know
+ * the universe of possible values. We use LLDependencies for that instead.
+ */
+typedef boost::signals2::signal<bool(const LLSD&), LLStopWhenHandled, float> LLStandardSignal;
+/// Methods that forward listeners (e.g. constructed with
+/// <tt>boost::bind()</tt>) should accept (const LLEventListener&)
+typedef LLStandardSignal::slot_type LLEventListener;
+/// Result of registering a listener, supports <tt>connected()</tt>,
+/// <tt>disconnect()</tt> and <tt>blocked()</tt>
+typedef boost::signals2::connection LLBoundListener;
+/// Storing an LLBoundListener in LLTempBoundListener will disconnect the
+/// referenced listener when the LLTempBoundListener instance is destroyed.
+typedef boost::signals2::scoped_connection LLTempBoundListener;
+
+/**
+ * A common idiom for event-based code is to accept either a callable --
+ * directly called on completion -- or the string name of an LLEventPump on
+ * which to post the completion event. Specifying a parameter as <tt>const
+ * LLListenerOrPumpName&</tt> allows either.
+ *
+ * Calling a validly-constructed LLListenerOrPumpName, passing the LLSD
+ * 'event' object, either calls the callable or posts the event to the named
+ * LLEventPump.
+ *
+ * A default-constructed LLListenerOrPumpName is 'empty'. (This is useful as
+ * the default value of an optional method parameter.) Calling it throws
+ * LLListenerOrPumpName::Empty. Test for this condition beforehand using
+ * either <tt>if (param)</tt> or <tt>if (! param)</tt>.
+ */
+class LL_COMMON_API LLListenerOrPumpName
+{
+public:
+ /// passing string name of LLEventPump
+ LLListenerOrPumpName(const std::string& pumpname);
+ /// passing string literal (overload so compiler isn't forced to infer
+ /// double conversion)
+ LLListenerOrPumpName(const char* pumpname);
+ /// passing listener -- the "anything else" catch-all case. The type of an
+ /// object constructed by boost::bind() isn't intended to be written out.
+ /// Normally we'd just accept 'const LLEventListener&', but that would
+ /// require double implicit conversion: boost::bind() object to
+ /// LLEventListener, LLEventListener to LLListenerOrPumpName. So use a
+ /// template to forward anything.
+ template<typename T>
+ LLListenerOrPumpName(const T& listener): mListener(listener) {}
+
+ /// for omitted method parameter: uninitialized mListener
+ LLListenerOrPumpName() {}
+
+ /// test for validity
+ operator bool() const { return bool(mListener); }
+ bool operator! () const { return ! mListener; }
+
+ /// explicit accessor
+ const LLEventListener& getListener() const { return *mListener; }
+
+ /// implicit conversion to LLEventListener
+ operator LLEventListener() const { return *mListener; }
+
+ /// allow calling directly
+ bool operator()(const LLSD& event) const;
+
+ /// exception if you try to call when empty
+ struct Empty: public std::runtime_error
+ {
+ Empty(const std::string& what):
+ std::runtime_error(std::string("LLListenerOrPumpName::Empty: ") + what) {}
+ };
+
+private:
+ boost::optional<LLEventListener> mListener;
+};
+
+/*****************************************************************************
+* LLEventPumps
+*****************************************************************************/
+class LLEventPump;
+
+/**
+ * LLEventPumps is a Singleton manager through which one typically accesses
+ * this subsystem.
+ */
+class LL_COMMON_API LLEventPumps: public LLSingleton<LLEventPumps>
+{
+ friend class LLSingleton<LLEventPumps>;
+public:
+ /**
+ * Find or create an LLEventPump instance with a specific name. We return
+ * a reference so there's no question about ownership. obtain() @em finds
+ * an instance without conferring @em ownership.
+ */
+ LLEventPump& obtain(const std::string& name);
+ /**
+ * Flush all known LLEventPump instances
+ */
+ void flush();
+
+private:
+ friend class LLEventPump;
+ /**
+ * Register a new LLEventPump instance (internal)
+ */
+ std::string registerNew(const LLEventPump&, const std::string& name, bool tweak);
+ /**
+ * Unregister a doomed LLEventPump instance (internal)
+ */
+ void unregister(const LLEventPump&);
+
+private:
+ LLEventPumps();
+ ~LLEventPumps();
+
+testable:
+ // Map of all known LLEventPump instances, whether or not we instantiated
+ // them. We store a plain old LLEventPump* because this map doesn't claim
+ // ownership of the instances. Though the common usage pattern is to
+ // request an instance using obtain(), it's fair to instantiate an
+ // LLEventPump subclass statically, as a class member, on the stack or on
+ // the heap. In such cases, the instantiating party is responsible for its
+ // lifespan.
+ typedef std::map<std::string, LLEventPump*> PumpMap;
+ PumpMap mPumpMap;
+ // Set of all LLEventPumps we instantiated. Membership in this set means
+ // we claim ownership, and will delete them when this LLEventPumps is
+ // destroyed.
+ typedef std::set<LLEventPump*> PumpSet;
+ PumpSet mOurPumps;
+ // LLEventPump names that should be instantiated as LLEventQueue rather
+ // than as LLEventStream
+ typedef std::set<std::string> PumpNames;
+ PumpNames mQueueNames;
+};
+
+/*****************************************************************************
+* details
+*****************************************************************************/
+namespace LLEventDetail
+{
+ /// Any callable capable of connecting an LLEventListener to an
+ /// LLStandardSignal to produce an LLBoundListener can be mapped to this
+ /// signature.
+ typedef boost::function<LLBoundListener(const LLEventListener&)> ConnectFunc;
+
+ /**
+ * Utility template function to use Visitor appropriately
+ *
+ * @param listener Callable to connect, typically a boost::bind()
+ * expression. This will be visited by Visitor using boost::visit_each().
+ * @param connect_func Callable that will connect() @a listener to an
+ * LLStandardSignal, returning LLBoundListener.
+ */
+ template <typename LISTENER>
+ LLBoundListener visit_and_connect(const LISTENER& listener,
+ const ConnectFunc& connect_func);
+} // namespace LLEventDetail
+
+/*****************************************************************************
+* LLEventTrackable
+*****************************************************************************/
+/**
+ * LLEventTrackable wraps boost::signals2::trackable, which resembles
+ * boost::trackable. Derive your listener class from LLEventTrackable instead,
+ * and use something like
+ * <tt>LLEventPump::listen(boost::bind(&YourTrackableSubclass::method,
+ * instance, _1))</tt>. This will implicitly disconnect when the object
+ * referenced by @c instance is destroyed.
+ *
+ * @note
+ * LLEventTrackable doesn't address a couple of cases:
+ * * Object destroyed during call
+ * - You enter a slot call in thread A.
+ * - Thread B destroys the object, which of course disconnects it from any
+ * future slot calls.
+ * - Thread A's call uses 'this', which now refers to a defunct object.
+ * Undefined behavior results.
+ * * Call during destruction
+ * - @c MySubclass is derived from LLEventTrackable.
+ * - @c MySubclass registers one of its own methods using
+ * <tt>LLEventPump::listen()</tt>.
+ * - The @c MySubclass object begins destruction. <tt>~MySubclass()</tt>
+ * runs, destroying state specific to the subclass. (For instance, a
+ * <tt>Foo*</tt> data member is <tt>delete</tt>d but not zeroed.)
+ * - The listening method will not be disconnected until
+ * <tt>~LLEventTrackable()</tt> runs.
+ * - Before we get there, another thread posts data to the @c LLEventPump
+ * instance, calling the @c MySubclass method.
+ * - The method in question relies on valid @c MySubclass state. (For
+ * instance, it attempts to dereference the <tt>Foo*</tt> pointer that was
+ * <tt>delete</tt>d but not zeroed.)
+ * - Undefined behavior results.
+ * If you suspect you may encounter any such scenario, you're better off
+ * managing the lifespan of your object with <tt>boost::shared_ptr</tt>.
+ * Passing <tt>LLEventPump::listen()</tt> a <tt>boost::bind()</tt> expression
+ * involving a <tt>boost::weak_ptr<Foo></tt> is recognized specially, engaging
+ * thread-safe Boost.Signals2 machinery.
+ */
+typedef boost::signals2::trackable LLEventTrackable;
+
+/*****************************************************************************
+* LLEventPump
+*****************************************************************************/
+/**
+ * LLEventPump is the base class interface through which we access the
+ * concrete subclasses LLEventStream and LLEventQueue.
+ *
+ * @NOTE
+ * LLEventPump derives from LLEventTrackable so that when you "chain"
+ * LLEventPump instances together, they will automatically disconnect on
+ * destruction. Please see LLEventTrackable documentation for situations in
+ * which this may be perilous across threads.
+ */
+class LL_COMMON_API LLEventPump: public LLEventTrackable
+{
+public:
+ /**
+ * Exception thrown by LLEventPump(). You are trying to instantiate an
+ * LLEventPump (subclass) using the same name as some other instance, and
+ * you didn't pass <tt>tweak=true</tt> to permit it to generate a unique
+ * variant.
+ */
+ struct DupPumpName: public std::runtime_error
+ {
+ DupPumpName(const std::string& what):
+ std::runtime_error(std::string("DupPumpName: ") + what) {}
+ };
+
+ /**
+ * Instantiate an LLEventPump (subclass) with the string name by which it
+ * can be found using LLEventPumps::obtain().
+ *
+ * If you pass (or default) @a tweak to @c false, then a duplicate name
+ * will throw DupPumpName. This won't happen if LLEventPumps::obtain()
+ * instantiates the LLEventPump, because obtain() uses find-or-create
+ * logic. It can only happen if you instantiate an LLEventPump in your own
+ * code -- and a collision with the name of some other LLEventPump is
+ * likely to cause much more subtle problems!
+ *
+ * When you hand-instantiate an LLEventPump, consider passing @a tweak as
+ * @c true. This directs LLEventPump() to append a suffix to the passed @a
+ * name to make it unique. You can retrieve the adjusted name by calling
+ * getName() on your new instance.
+ */
+ LLEventPump(const std::string& name, bool tweak=false);
+ virtual ~LLEventPump();
+
+ /// group exceptions thrown by listen(). We use exceptions because these
+ /// particular errors are likely to be coding errors, found and fixed by
+ /// the developer even before preliminary checkin.
+ struct ListenError: public std::runtime_error
+ {
+ ListenError(const std::string& what): std::runtime_error(what) {}
+ };
+ /**
+ * exception thrown by listen(). You are attempting to register a
+ * listener on this LLEventPump using the same listener name as an
+ * already-registered listener.
+ */
+ struct DupListenerName: public ListenError
+ {
+ DupListenerName(const std::string& what):
+ ListenError(std::string("DupListenerName: ") + what)
+ {}
+ };
+ /**
+ * exception thrown by listen(). The order dependencies specified for your
+ * listener are incompatible with existing listeners.
+ *
+ * Consider listener "a" which specifies before "b" and "b" which
+ * specifies before "c". You are now attempting to register "c" before
+ * "a". There is no order that can satisfy all constraints.
+ */
+ struct Cycle: public ListenError
+ {
+ Cycle(const std::string& what): ListenError(std::string("Cycle: ") + what) {}
+ };
+ /**
+ * exception thrown by listen(). This one means that your new listener
+ * would force a change to the order of previously-registered listeners,
+ * and we don't have a good way to implement that.
+ *
+ * Consider listeners "some", "other" and "third". "some" and "other" are
+ * registered earlier without specifying relative order, so "other"
+ * happens to be first. Now you attempt to register "third" after "some"
+ * and before "other". Whoops, that would require swapping "some" and
+ * "other", which we can't do. Instead we throw this exception.
+ *
+ * It may not be possible to change the registration order so we already
+ * know "third"s order requirement by the time we register the second of
+ * "some" and "other". A solution would be to specify that "some" must
+ * come before "other", or equivalently that "other" must come after
+ * "some".
+ */
+ struct OrderChange: public ListenError
+ {
+ OrderChange(const std::string& what): ListenError(std::string("OrderChange: ") + what) {}
+ };
+
+ /// used by listen()
+ typedef std::vector<std::string> NameList;
+ /// convenience placeholder for when you explicitly want to pass an empty
+ /// NameList
+ const static NameList empty;
+
+ /// Get this LLEventPump's name
+ std::string getName() const { return mName; }
+
+ /**
+ * Register a new listener with a unique name. Specify an optional list
+ * of other listener names after which this one must be called, likewise
+ * an optional list of other listener names before which this one must be
+ * called. The other listeners mentioned need not yet be registered
+ * themselves. listen() can throw any ListenError; see ListenError
+ * subclasses.
+ *
+ * The listener name must be unique among active listeners for this
+ * LLEventPump, else you get DupListenerName. If you don't care to invent
+ * a name yourself, use inventName(). (I was tempted to recognize e.g. ""
+ * and internally generate a distinct name for that case. But that would
+ * handle badly the scenario in which you want to add, remove, re-add,
+ * etc. the same listener: each new listen() call would necessarily
+ * perform a new dependency sort. Assuming you specify the same
+ * after/before lists each time, using inventName() when you first
+ * instantiate your listener, then passing the same name on each listen()
+ * call, allows us to optimize away the second and subsequent dependency
+ * sorts.
+ *
+ * If (as is typical) you pass a <tt>boost::bind()</tt> expression as @a
+ * listener, listen() will inspect the components of that expression. If a
+ * bound object matches any of several cases, the connection will
+ * automatically be disconnected when that object is destroyed.
+ *
+ * * You bind a <tt>boost::weak_ptr</tt>.
+ * * Binding a <tt>boost::shared_ptr</tt> that way would ensure that the
+ * referenced object would @em never be destroyed, since the @c
+ * shared_ptr stored in the LLEventPump would remain an outstanding
+ * reference. Use the weaken() function to convert your @c shared_ptr to
+ * @c weak_ptr. Because this is easy to forget, binding a @c shared_ptr
+ * will produce a compile error (@c BOOST_STATIC_ASSERT failure).
+ * * You bind a simple pointer or reference to an object derived from
+ * <tt>boost::enable_shared_from_this</tt>. (UNDER CONSTRUCTION)
+ * * You bind a simple pointer or reference to an object derived from
+ * LLEventTrackable. Unlike the cases described above, though, this is
+ * vulnerable to a couple of cross-thread race conditions, as described
+ * in the LLEventTrackable documentation.
+ */
+ template <typename LISTENER>
+ LLBoundListener listen(const std::string& name, const LISTENER& listener,
+ const NameList& after=NameList(),
+ const NameList& before=NameList())
+ {
+ // Examine listener, using our listen_impl() method to make the
+ // actual connection.
+ // This is why listen() is a template. Conversion from boost::bind()
+ // to LLEventListener performs type erasure, so it's important to look
+ // at the boost::bind object itself before that happens.
+ return LLEventDetail::visit_and_connect(listener,
+ boost::bind(&LLEventPump::listen_impl,
+ this,
+ name,
+ _1,
+ after,
+ before));
+ }
+
+ /// Get the LLBoundListener associated with the passed name (dummy
+ /// LLBoundListener if not found)
+ virtual LLBoundListener getListener(const std::string& name) const;
+ /**
+ * Instantiate one of these to block an existing connection:
+ * @code
+ * { // in some local scope
+ * LLEventPump::Blocker block(someLLBoundListener);
+ * // code that needs the connection blocked
+ * } // unblock the connection again
+ * @endcode
+ */
+ typedef boost::signals2::shared_connection_block Blocker;
+ /// Unregister a listener by name. Prefer this to
+ /// <tt>getListener(name).disconnect()</tt> because stopListening() also
+ /// forgets this name.
+ virtual void stopListening(const std::string& name);
+ /// Post an event to all listeners. The @c bool return is only meaningful
+ /// if the underlying leaf class is LLEventStream -- beware of relying on
+ /// it too much! Truthfully, we return @c bool mostly to permit chaining
+ /// one LLEventPump as a listener on another.
+ virtual bool post(const LLSD&) = 0;
+ /// Enable/disable: while disabled, silently ignore all post() calls
+ virtual void enable(bool enabled=true) { mEnabled = enabled; }
+ /// query
+ virtual bool enabled() const { return mEnabled; }
+
+ /// Generate a distinct name for a listener -- see listen()
+ static std::string inventName(const std::string& pfx="listener");
+
+private:
+ friend class LLEventPumps;
+ /// flush queued events
+ virtual void flush() {}
+
+private:
+ virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
+ const NameList& after,
+ const NameList& before);
+ std::string mName;
+
+protected:
+ /// implement the dispatching
+ LLStandardSignal mSignal;
+ /// valve open?
+ bool mEnabled;
+ /// Map of named listeners. This tracks the listeners that actually exist
+ /// at this moment. When we stopListening(), we discard the entry from
+ /// this map.
+ typedef std::map<std::string, boost::signals2::connection> ConnectionMap;
+ ConnectionMap mConnections;
+ typedef LLDependencies<std::string, float> DependencyMap;
+ /// Dependencies between listeners. For each listener, track the float
+ /// used to establish its place in mSignal's order. This caches all the
+ /// listeners that have ever registered; stopListening() does not discard
+ /// the entry from this map. This is to avoid a new dependency sort if the
+ /// same listener with the same dependencies keeps hopping on and off this
+ /// LLEventPump.
+ DependencyMap mDeps;
+};
+
+/*****************************************************************************
+* LLEventStream
+*****************************************************************************/
+/**
+ * LLEventStream is a thin wrapper around LLStandardSignal. Posting an
+ * event immediately calls all registered listeners.
+ */
+class LL_COMMON_API LLEventStream: public LLEventPump
+{
+public:
+ LLEventStream(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {}
+ virtual ~LLEventStream() {}
+
+ /// Post an event to all listeners
+ virtual bool post(const LLSD& event);
+};
+
+/*****************************************************************************
+* LLEventQueue
+*****************************************************************************/
+/**
+ * LLEventQueue isa LLEventPump whose post() method defers calling registered
+ * listeners until flush() is called.
+ */
+class LL_COMMON_API LLEventQueue: public LLEventPump
+{
+public:
+ LLEventQueue(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {}
+ virtual ~LLEventQueue() {}
+
+ /// Post an event to all listeners
+ virtual bool post(const LLSD& event);
+
+private:
+ /// flush queued events
+ virtual void flush();
+
+private:
+ typedef std::deque<LLSD> EventQueue;
+ EventQueue mEventQueue;
+};
+
+/*****************************************************************************
+* LLReqID
+*****************************************************************************/
+/**
+ * This class helps the implementer of a given event API to honor the
+ * ["reqid"] convention. By this convention, each event API stamps into its
+ * response LLSD a ["reqid"] key whose value echoes the ["reqid"] value, if
+ * any, from the corresponding request.
+ *
+ * This supports an (atypical, but occasionally necessary) use case in which
+ * two or more asynchronous requests are multiplexed onto the same ["reply"]
+ * LLEventPump. Since the response events could arrive in arbitrary order, the
+ * caller must be able to demux them. It does so by matching the ["reqid"]
+ * value in each response with the ["reqid"] value in the corresponding
+ * request.
+ *
+ * It is the caller's responsibility to ensure distinct ["reqid"] values for
+ * that case. Though LLSD::UUID is guaranteed to work, it might be overkill:
+ * the "namespace" of unique ["reqid"] values is simply the set of requests
+ * specifying the same ["reply"] LLEventPump name.
+ *
+ * Making a given event API echo the request's ["reqid"] into the response is
+ * nearly trivial. This helper is mostly for mnemonic purposes, to serve as a
+ * place to put these comments. We hope that each time a coder implements a
+ * new event API based on some existing one, s/he will say, "Huh, what's an
+ * LLReqID?" and look up this material.
+ *
+ * The hardest part about the convention is deciding where to store the
+ * ["reqid"] value. Ironically, LLReqID can't help with that: you must store
+ * an LLReqID instance in whatever storage will persist until the reply is
+ * sent. For example, if the request ultimately ends up using a Responder
+ * subclass, storing an LLReqID instance in the Responder works.
+ *
+ * @note
+ * The @em implementer of an event API must honor the ["reqid"] convention.
+ * However, the @em caller of an event API need only use it if s/he is sharing
+ * the same ["reply"] LLEventPump for two or more asynchronous event API
+ * requests.
+ *
+ * In most cases, it's far easier for the caller to instantiate a local
+ * LLEventStream and pass its name to the event API in question. Then it's
+ * perfectly reasonable not to set a ["reqid"] key in the request, ignoring
+ * the @c isUndefined() ["reqid"] value in the response.
+ */
+class LL_COMMON_API LLReqID
+{
+public:
+ /**
+ * If you have the request in hand at the time you instantiate the
+ * LLReqID, pass that request to extract its ["reqid"].
+ */
+ LLReqID(const LLSD& request):
+ mReqid(request["reqid"])
+ {}
+ /// If you don't yet have the request, use setFrom() later.
+ LLReqID() {}
+
+ /// Extract and store the ["reqid"] value from an incoming request.
+ void setFrom(const LLSD& request)
+ {
+ mReqid = request["reqid"];
+ }
+
+ /// Set ["reqid"] key into a pending response LLSD object.
+ void stamp(LLSD& response) const;
+
+ /// Make a whole new response LLSD object with our ["reqid"].
+ LLSD makeResponse() const
+ {
+ LLSD response;
+ stamp(response);
+ return response;
+ }
+
+ /// Not really sure of a use case for this accessor...
+ LLSD getReqID() const { return mReqid; }
+
+private:
+ LLSD mReqid;
+};
+
+/*****************************************************************************
+* Underpinnings
+*****************************************************************************/
+/**
+ * We originally provided a suite of overloaded
+ * LLEventTrackable::listenTo(LLEventPump&, ...) methods that would call
+ * LLEventPump::listen(...) and then pass the returned LLBoundListener to
+ * LLEventTrackable::track(). This was workable but error-prone: the coder
+ * must remember to call listenTo() rather than the more straightforward
+ * listen() method.
+ *
+ * Now we publish only the single canonical listen() method, so there's a
+ * uniform mechanism. Having a single way to do this is good, in that there's
+ * no question in the coder's mind which of several alternatives to choose.
+ *
+ * To support automatic connection management, we use boost::visit_each
+ * (http://www.boost.org/doc/libs/1_37_0/doc/html/boost/visit_each.html) to
+ * inspect each argument of a boost::bind expression. (Although the visit_each
+ * mechanism was first introduced with the original Boost.Signals library, it
+ * was only later documented.)
+ *
+ * Cases:
+ * * At least one of the function's arguments is a boost::weak_ptr<T>. Pass
+ * the corresponding shared_ptr to slot_type::track(). Ideally that would be
+ * the object whose method we want to call, but in fact we do the same for
+ * any weak_ptr we might find among the bound arguments. If we're passing
+ * our bound method a weak_ptr to some object, wouldn't the destruction of
+ * that object invalidate the call? So we disconnect automatically when any
+ * such object is destroyed. This is the mechanism preferred by boost::
+ * signals2.
+ * * One of the functions's arguments is a boost::shared_ptr<T>. This produces
+ * a compile error: the bound copy of the shared_ptr stored in the
+ * boost_bind object stored in the signal object would make the referenced
+ * T object immortal. We provide a weaken() function. Pass
+ * weaken(your_shared_ptr) instead. (We can inspect, but not modify, the
+ * boost::bind object. Otherwise we'd replace the shared_ptr with weak_ptr
+ * implicitly and just proceed.)
+ * * One of the function's arguments is a plain pointer/reference to an object
+ * derived from boost::enable_shared_from_this. We assume that this object
+ * is managed using boost::shared_ptr, so we implicitly extract a shared_ptr
+ * and track that. (UNDER CONSTRUCTION)
+ * * One of the function's arguments is derived from LLEventTrackable. Pass
+ * the LLBoundListener to its LLEventTrackable::track(). This is vulnerable
+ * to a couple different race conditions, as described in LLEventTrackable
+ * documentation. (NOTE: Now that LLEventTrackable is a typedef for
+ * boost::signals2::trackable, the Signals2 library handles this itself, so
+ * our visitor needs no special logic for this case.)
+ * * Any other argument type is irrelevant to automatic connection management.
+ */
+
+namespace LLEventDetail
+{
+ template <typename F>
+ const F& unwrap(const F& f) { return f; }
+
+ template <typename F>
+ const F& unwrap(const boost::reference_wrapper<F>& f) { return f.get(); }
+
+ // Most of the following is lifted from the Boost.Signals use of
+ // visit_each.
+ template<bool Cond> struct truth {};
+
+ /**
+ * boost::visit_each() Visitor, used on a template argument <tt>const F&
+ * f</tt> as follows (see visit_and_connect()):
+ * @code
+ * LLEventListener listener(f);
+ * Visitor visitor(listener); // bind listener so it can track() shared_ptrs
+ * using boost::visit_each; // allow unqualified visit_each() call for ADL
+ * visit_each(visitor, unwrap(f));
+ * @endcode
+ */
+ class Visitor
+ {
+ public:
+ /**
+ * Visitor binds a reference to LLEventListener so we can track() any
+ * shared_ptrs we find in the argument list.
+ */
+ Visitor(LLEventListener& listener):
+ mListener(listener)
+ {
+ }
+
+ /**
+ * boost::visit_each() calls this method for each component of a
+ * boost::bind() expression.
+ */
+ template <typename T>
+ void operator()(const T& t) const
+ {
+ decode(t, 0);
+ }
+
+ private:
+ // decode() decides between a reference wrapper and anything else
+ // boost::ref() variant
+ template<typename T>
+ void decode(const boost::reference_wrapper<T>& t, int) const
+ {
+// add_if_trackable(t.get_pointer());
+ }
+
+ // decode() anything else
+ template<typename T>
+ void decode(const T& t, long) const
+ {
+ typedef truth<(boost::is_pointer<T>::value)> is_a_pointer;
+ maybe_get_pointer(t, is_a_pointer());
+ }
+
+ // maybe_get_pointer() decides between a pointer and a non-pointer
+ // plain pointer variant
+ template<typename T>
+ void maybe_get_pointer(const T& t, truth<true>) const
+ {
+// add_if_trackable(t);
+ }
+
+ // shared_ptr variant
+ template<typename T>
+ void maybe_get_pointer(const boost::shared_ptr<T>& t, truth<false>) const
+ {
+ // If we have a shared_ptr to this object, it doesn't matter
+ // whether the object is derived from LLEventTrackable, so no
+ // further analysis of T is needed.
+// mListener.track(t);
+
+ // Make this case illegal. Passing a bound shared_ptr to
+ // slot_type::track() is useless, since the bound shared_ptr will
+ // keep the object alive anyway! Force the coder to cast to weak_ptr.
+
+ // Trivial as it is, make the BOOST_STATIC_ASSERT() condition
+ // dependent on template param so the macro is only evaluated if
+ // this method is in fact instantiated, as described here:
+ // http://www.boost.org/doc/libs/1_34_1/doc/html/boost_staticassert.html
+
+ // ATTENTION: Don't bind a shared_ptr<anything> using
+ // LLEventPump::listen(boost::bind()). Doing so captures a copy of
+ // the shared_ptr, making the referenced object effectively
+ // immortal. Use the weaken() function, e.g.:
+ // somepump.listen(boost::bind(...weaken(my_shared_ptr)...));
+ // This lets us automatically disconnect when the referenced
+ // object is destroyed.
+ BOOST_STATIC_ASSERT(sizeof(T) == 0);
+ }
+
+ // weak_ptr variant
+ template<typename T>
+ void maybe_get_pointer(const boost::weak_ptr<T>& t, truth<false>) const
+ {
+ // If we have a weak_ptr to this object, it doesn't matter
+ // whether the object is derived from LLEventTrackable, so no
+ // further analysis of T is needed.
+ mListener.track(t);
+// std::cout << "Found weak_ptr<" << typeid(T).name() << ">!\n";
+ }
+
+#if 0
+ // reference to anything derived from boost::enable_shared_from_this
+ template <typename T>
+ inline void maybe_get_pointer(const boost::enable_shared_from_this<T>& ct,
+ truth<false>) const
+ {
+ // Use the slot_type::track(shared_ptr) mechanism. Cast away
+ // const-ness because (in our code base anyway) it's unusual
+ // to find shared_ptr<const T>.
+ boost::enable_shared_from_this<T>&
+ t(const_cast<boost::enable_shared_from_this<T>&>(ct));
+ std::cout << "Capturing shared_from_this()" << std::endl;
+ boost::shared_ptr<T> sp(t.shared_from_this());
+/*==========================================================================*|
+ std::cout << "Capturing weak_ptr" << std::endl;
+ boost::weak_ptr<T> wp(sp);
+|*==========================================================================*/
+ std::cout << "Tracking shared__ptr" << std::endl;
+ mListener.track(sp);
+ }
+#endif
+
+ // non-pointer variant
+ template<typename T>
+ void maybe_get_pointer(const T& t, truth<false>) const
+ {
+ // Take the address of this object, because the object itself may be
+ // trackable
+// add_if_trackable(boost::addressof(t));
+ }
+
+/*==========================================================================*|
+ // add_if_trackable() adds LLEventTrackable objects to mTrackables
+ inline void add_if_trackable(const LLEventTrackable* t) const
+ {
+ if (t)
+ {
+ }
+ }
+
+ // pointer to anything not an LLEventTrackable subclass
+ inline void add_if_trackable(const void*) const
+ {
+ }
+
+ // pointer to free function
+ // The following construct uses the preprocessor to generate
+ // add_if_trackable() overloads accepting pointer-to-function taking
+ // 0, 1, ..., LLEVENTS_LISTENER_ARITY parameters of arbitrary type.
+#define BOOST_PP_LOCAL_MACRO(n) \
+ template <typename R \
+ BOOST_PP_COMMA_IF(n) \
+ BOOST_PP_ENUM_PARAMS(n, typename T)> \
+ inline void \
+ add_if_trackable(R (*)(BOOST_PP_ENUM_PARAMS(n, T))) const \
+ { \
+ }
+#define BOOST_PP_LOCAL_LIMITS (0, LLEVENTS_LISTENER_ARITY)
+#include BOOST_PP_LOCAL_ITERATE()
+#undef BOOST_PP_LOCAL_MACRO
+#undef BOOST_PP_LOCAL_LIMITS
+|*==========================================================================*/
+
+ /// Bind a reference to the LLEventListener to call its track() method.
+ LLEventListener& mListener;
+ };
+
+ /**
+ * Utility template function to use Visitor appropriately
+ *
+ * @param raw_listener Callable to connect, typically a boost::bind()
+ * expression. This will be visited by Visitor using boost::visit_each().
+ * @param connect_funct Callable that will connect() @a raw_listener to an
+ * LLStandardSignal, returning LLBoundListener.
+ */
+ template <typename LISTENER>
+ LLBoundListener visit_and_connect(const LISTENER& raw_listener,
+ const ConnectFunc& connect_func)
+ {
+ // Capture the listener
+ LLEventListener listener(raw_listener);
+ // Define our Visitor, binding the listener so we can call
+ // listener.track() if we discover any shared_ptr<Foo>.
+ LLEventDetail::Visitor visitor(listener);
+ // Allow unqualified visit_each() call for ADL
+ using boost::visit_each;
+ // Visit each component of a boost::bind() expression. Pass
+ // 'raw_listener', our template argument, rather than 'listener' from
+ // which type details have been erased. unwrap() comes from
+ // Boost.Signals, in case we were passed a boost::ref().
+ visit_each(visitor, LLEventDetail::unwrap(raw_listener));
+ // Make the connection using passed function. At present, wrapping
+ // this functionality into this function is a bit silly: we don't
+ // really need a visit_and_connect() function any more, just a visit()
+ // function. The definition of this function dates from when, after
+ // visit_each(), after establishing the connection, we had to
+ // postprocess the new connection with the visitor object. That's no
+ // longer necessary.
+ return connect_func(listener);
+ }
+} // namespace LLEventDetail
+
+// Somewhat to my surprise, passing boost::bind(...boost::weak_ptr<T>...) to
+// listen() fails in Boost code trying to instantiate LLEventListener (i.e.
+// LLStandardSignal::slot_type) because the boost::get_pointer() utility function isn't
+// specialized for boost::weak_ptr. This remedies that omission.
+namespace boost
+{
+ template <typename T>
+ T* get_pointer(const weak_ptr<T>& ptr) { return shared_ptr<T>(ptr).get(); }
+}
+
+/// Since we forbid use of listen(boost::bind(...shared_ptr<T>...)), provide an
+/// easy way to cast to the corresponding weak_ptr.
+template <typename T>
+boost::weak_ptr<T> weaken(const boost::shared_ptr<T>& ptr)
+{
+ return boost::weak_ptr<T>(ptr);
+}
+
+#endif /* ! defined(LL_LLEVENTS_H) */
diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index 0d89353dee..6ea9762546 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -1,244 +1,244 @@ -/** - * @file llfasttimer.h - * @brief Declaration of a fast timer. - * - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * - * Copyright (c) 2004-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_FASTTIMER_H -#define LL_FASTTIMER_H - -#include "llinstancetracker.h" - -#define FAST_TIMER_ON 1 - -U64 get_cpu_clock_count(); - -class LLMutex; - -#include <queue> -#include "llsd.h" - - -class LLFastTimer -{ -public: - // stores a "named" timer instance to be reused via multiple LLFastTimer stack instances - class NamedTimer - : public LLInstanceTracker<NamedTimer> - { - public: - ~NamedTimer(); - - enum { HISTORY_NUM = 60 }; - - const std::string& getName() { return mName; } - NamedTimer* getParent() { return mParent; } - void setParent(NamedTimer* parent); - S32 getDepth(); - std::string getToolTip(S32 history_index = -1); - - typedef std::vector<NamedTimer*>::const_iterator child_const_iter; - child_const_iter beginChildren(); - child_const_iter endChildren(); - std::vector<NamedTimer*>& getChildren(); - - void setCollapsed(bool collapsed) { mCollapsed = collapsed; } - bool getCollapsed() const { return mCollapsed; } - - U64 getCountAverage() const { return mCountAverage; } - U64 getCallAverage() const { return mCallAverage; } - - U64 getHistoricalCount(S32 history_index = 0) const; - U64 getHistoricalCalls(S32 history_index = 0) const; - - static NamedTimer& getRootNamedTimer(); - - struct FrameState - { - FrameState(NamedTimer* timerp); - - U64 mSelfTimeCounter; - U64 mLastStartTime; // most recent time when this timer was started - U32 mCalls; - FrameState* mParent; // info for caller timer - FrameState* mLastCaller; // used to bootstrap tree construction - NamedTimer* mTimer; - U16 mActiveCount; // number of timers with this ID active on stack - bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame - }; - - FrameState& getFrameStateFast() const - { - return (*sTimerInfos)[mFrameStateIndex]; - } - - S32 getFrameStateIndex() const { return mFrameStateIndex; } - - FrameState& getFrameState() const; - - - private: - friend class LLFastTimer; - friend class NamedTimerFactory; - - // - // methods - // - NamedTimer(const std::string& name); - // recursive call to gather total time from children - static void accumulateTimings(); - - // called once per frame by LLFastTimer - static void processFrame(); - - static void buildHierarchy(); - static void resetFrame(); - static void reset(); - - typedef std::vector<FrameState> info_list_t; - static info_list_t& getFrameStateList(); - static void createFrameStateList(); // must call before any call to getFrameStateList() - - // - // members - // - S32 mFrameStateIndex; - - std::string mName; - - U64 mTotalTimeCounter; - - U64 mCountAverage; - U64 mCallAverage; - - U64* mCountHistory; - U64* mCallHistory; - - // tree structure - NamedTimer* mParent; // NamedTimer of caller(parent) - std::vector<NamedTimer*> mChildren; - bool mCollapsed; // don't show children - bool mNeedsSorting; // sort children whenever child added - - static info_list_t* sTimerInfos; - }; - - // used to statically declare a new named timer - class DeclareTimer - { - public: - DeclareTimer(const std::string& name, bool open); - DeclareTimer(const std::string& name); - - // convertable to NamedTimer::FrameState for convenient usage of LLFastTimer(declared_timer) - operator NamedTimer::FrameState&() { return mNamedTimer.getFrameStateFast(); } - private: - NamedTimer& mNamedTimer; - }; - - -public: - enum RootTimerMarker { ROOT }; - - static LLMutex* sLogLock; - static std::queue<LLSD> sLogQueue; - static BOOL sLog; - static BOOL sMetricLog; - - LLFastTimer(RootTimerMarker); - - LLFastTimer(NamedTimer::FrameState& timer) - : mFrameState(&timer) - { -#if FAST_TIMER_ON - NamedTimer::FrameState* frame_state = mFrameState; - frame_state->mLastStartTime = get_cpu_clock_count(); - mStartSelfTime = frame_state->mLastStartTime; - - frame_state->mActiveCount++; - frame_state->mCalls++; - // keep current parent as long as it is active when we are - frame_state->mMoveUpTree |= (frame_state->mParent->mActiveCount == 0); - - mLastTimer = sCurTimer; - sCurTimer = this; -#endif - } - - ~LLFastTimer() - { -#if FAST_TIMER_ON - NamedTimer::FrameState* frame_state = mFrameState; - U64 cur_time = get_cpu_clock_count(); - frame_state->mSelfTimeCounter += cur_time - mStartSelfTime; - - frame_state->mActiveCount--; - LLFastTimer* last_timer = mLastTimer; - sCurTimer = last_timer; - - // store last caller to bootstrap tree creation - frame_state->mLastCaller = last_timer->mFrameState; - - // we are only tracking self time, so subtract our total time delta from parents - U64 total_time = cur_time - frame_state->mLastStartTime; - last_timer->mStartSelfTime += total_time; -#endif - } - - - // call this once a frame to reset timers - static void nextFrame(); - - // call this to reset timer hierarchy, averages, etc. - static void reset(); - - static U64 countsPerSecond(); - static S32 getLastFrameIndex() { return sLastFrameIndex; } - static S32 getCurFrameIndex() { return sCurFrameIndex; } - - static void writeLog(std::ostream& os); - static const NamedTimer* getTimerByName(const std::string& name); - -public: - static bool sPauseHistory; - static bool sResetHistory; - -private: - typedef std::vector<LLFastTimer*> timer_stack_t; - static LLFastTimer* sCurTimer; - static S32 sCurFrameIndex; - static S32 sLastFrameIndex; - - static F64 sCPUClockFrequency; - U64 mStartSelfTime; // start time + time of all child timers - NamedTimer::FrameState* mFrameState; - LLFastTimer* mLastTimer; -}; - -#endif // LL_LLFASTTIMER_H +/**
+ * @file llfasttimer.h
+ * @brief Declaration of a fast timer.
+ *
+ * $LicenseInfo:firstyear=2004&license=viewergpl$
+ *
+ * Copyright (c) 2004-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_FASTTIMER_H
+#define LL_FASTTIMER_H
+
+#include "llinstancetracker.h"
+
+#define FAST_TIMER_ON 1
+
+LL_COMMON_API U64 get_cpu_clock_count();
+
+class LLMutex;
+
+#include <queue>
+#include "llsd.h"
+
+
+class LL_COMMON_API LLFastTimer
+{
+public:
+ // stores a "named" timer instance to be reused via multiple LLFastTimer stack instances
+ class LL_COMMON_API NamedTimer
+ : public LLInstanceTracker<NamedTimer>
+ {
+ public:
+ ~NamedTimer();
+
+ enum { HISTORY_NUM = 60 };
+
+ const std::string& getName() { return mName; }
+ NamedTimer* getParent() { return mParent; }
+ void setParent(NamedTimer* parent);
+ S32 getDepth();
+ std::string getToolTip(S32 history_index = -1);
+
+ typedef std::vector<NamedTimer*>::const_iterator child_const_iter;
+ child_const_iter beginChildren();
+ child_const_iter endChildren();
+ std::vector<NamedTimer*>& getChildren();
+
+ void setCollapsed(bool collapsed) { mCollapsed = collapsed; }
+ bool getCollapsed() const { return mCollapsed; }
+
+ U64 getCountAverage() const { return mCountAverage; }
+ U64 getCallAverage() const { return mCallAverage; }
+
+ U64 getHistoricalCount(S32 history_index = 0) const;
+ U64 getHistoricalCalls(S32 history_index = 0) const;
+
+ static NamedTimer& getRootNamedTimer();
+
+ struct FrameState
+ {
+ FrameState(NamedTimer* timerp);
+
+ U64 mSelfTimeCounter;
+ U64 mLastStartTime; // most recent time when this timer was started
+ U32 mCalls;
+ FrameState* mParent; // info for caller timer
+ FrameState* mLastCaller; // used to bootstrap tree construction
+ NamedTimer* mTimer;
+ U16 mActiveCount; // number of timers with this ID active on stack
+ bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame
+ };
+
+ FrameState& getFrameStateFast() const
+ {
+ return (*sTimerInfos)[mFrameStateIndex];
+ }
+
+ S32 getFrameStateIndex() const { return mFrameStateIndex; }
+
+ FrameState& getFrameState() const;
+
+
+ private:
+ friend class LLFastTimer;
+ friend class NamedTimerFactory;
+
+ //
+ // methods
+ //
+ NamedTimer(const std::string& name);
+ // recursive call to gather total time from children
+ static void accumulateTimings();
+
+ // called once per frame by LLFastTimer
+ static void processFrame();
+
+ static void buildHierarchy();
+ static void resetFrame();
+ static void reset();
+
+ typedef std::vector<FrameState> info_list_t;
+ static info_list_t& getFrameStateList();
+ static void createFrameStateList(); // must call before any call to getFrameStateList()
+
+ //
+ // members
+ //
+ S32 mFrameStateIndex;
+
+ std::string mName;
+
+ U64 mTotalTimeCounter;
+
+ U64 mCountAverage;
+ U64 mCallAverage;
+
+ U64* mCountHistory;
+ U64* mCallHistory;
+
+ // tree structure
+ NamedTimer* mParent; // NamedTimer of caller(parent)
+ std::vector<NamedTimer*> mChildren;
+ bool mCollapsed; // don't show children
+ bool mNeedsSorting; // sort children whenever child added
+
+ static info_list_t* sTimerInfos;
+ };
+
+ // used to statically declare a new named timer
+ class LL_COMMON_API DeclareTimer
+ {
+ public:
+ DeclareTimer(const std::string& name, bool open);
+ DeclareTimer(const std::string& name);
+
+ // convertable to NamedTimer::FrameState for convenient usage of LLFastTimer(declared_timer)
+ operator NamedTimer::FrameState&() { return mNamedTimer.getFrameStateFast(); }
+ private:
+ NamedTimer& mNamedTimer;
+ };
+
+
+public:
+ enum RootTimerMarker { ROOT };
+
+ static LLMutex* sLogLock;
+ static std::queue<LLSD> sLogQueue;
+ static BOOL sLog;
+ static BOOL sMetricLog;
+
+ LLFastTimer(RootTimerMarker);
+
+ LLFastTimer(NamedTimer::FrameState& timer)
+ : mFrameState(&timer)
+ {
+#if FAST_TIMER_ON
+ NamedTimer::FrameState* frame_state = mFrameState;
+ frame_state->mLastStartTime = get_cpu_clock_count();
+ mStartSelfTime = frame_state->mLastStartTime;
+
+ frame_state->mActiveCount++;
+ frame_state->mCalls++;
+ // keep current parent as long as it is active when we are
+ frame_state->mMoveUpTree |= (frame_state->mParent->mActiveCount == 0);
+
+ mLastTimer = sCurTimer;
+ sCurTimer = this;
+#endif
+ }
+
+ ~LLFastTimer()
+ {
+#if FAST_TIMER_ON
+ NamedTimer::FrameState* frame_state = mFrameState;
+ U64 cur_time = get_cpu_clock_count();
+ frame_state->mSelfTimeCounter += cur_time - mStartSelfTime;
+
+ frame_state->mActiveCount--;
+ LLFastTimer* last_timer = mLastTimer;
+ sCurTimer = last_timer;
+
+ // store last caller to bootstrap tree creation
+ frame_state->mLastCaller = last_timer->mFrameState;
+
+ // we are only tracking self time, so subtract our total time delta from parents
+ U64 total_time = cur_time - frame_state->mLastStartTime;
+ last_timer->mStartSelfTime += total_time;
+#endif
+ }
+
+
+ // call this once a frame to reset timers
+ static void nextFrame();
+
+ // call this to reset timer hierarchy, averages, etc.
+ static void reset();
+
+ static U64 countsPerSecond();
+ static S32 getLastFrameIndex() { return sLastFrameIndex; }
+ static S32 getCurFrameIndex() { return sCurFrameIndex; }
+
+ static void writeLog(std::ostream& os);
+ static const NamedTimer* getTimerByName(const std::string& name);
+
+public:
+ static bool sPauseHistory;
+ static bool sResetHistory;
+
+private:
+ typedef std::vector<LLFastTimer*> timer_stack_t;
+ static LLFastTimer* sCurTimer;
+ static S32 sCurFrameIndex;
+ static S32 sLastFrameIndex;
+
+ static F64 sCPUClockFrequency;
+ U64 mStartSelfTime; // start time + time of all child timers
+ NamedTimer::FrameState* mFrameState;
+ LLFastTimer* mLastTimer;
+};
+
+#endif // LL_LLFASTTIMER_H
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index c6092f7b9c..fea5d3ed2b 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -70,7 +70,7 @@ typedef struct stat llstat; #include "llstring.h" // safe char* -> std::string conversion -class LLFile +class LL_COMMON_API LLFile { public: // All these functions take UTF8 path/filenames. @@ -95,7 +95,7 @@ public: #if USE_LLFILESTREAMS -class llifstream : public std::basic_istream < char , std::char_traits < char > > +class LL_COMMON_API llifstream : public std::basic_istream < char , std::char_traits < char > > { // input stream associated with a C stream public: @@ -136,7 +136,7 @@ private: }; -class llofstream : public std::basic_ostream< char , std::char_traits < char > > +class LL_COMMON_API llofstream : public std::basic_ostream< char , std::char_traits < char > > { public: typedef std::basic_ostream< char , std::char_traits < char > > _Myt; @@ -185,7 +185,7 @@ private: //#define llifstream std::ifstream //#define llofstream std::ofstream -class llifstream : public std::ifstream +class LL_COMMON_API llifstream : public std::ifstream { public: llifstream() : std::ifstream() @@ -203,7 +203,7 @@ public: }; -class llofstream : public std::ofstream +class LL_COMMON_API llofstream : public std::ofstream { public: llofstream() : std::ofstream() @@ -231,7 +231,7 @@ public: * and should only be used for config files and the like -- not in a * loop. */ -std::streamsize llifstream_size(llifstream& fstr); -std::streamsize llofstream_size(llofstream& fstr); +std::streamsize LL_COMMON_API llifstream_size(llifstream& fstr); +std::streamsize LL_COMMON_API llofstream_size(llofstream& fstr); #endif // not LL_LLFILE_H diff --git a/indra/llcommon/llfindlocale.h b/indra/llcommon/llfindlocale.h index f17c7740f3..b812a065db 100644 --- a/indra/llcommon/llfindlocale.h +++ b/indra/llcommon/llfindlocale.h @@ -59,8 +59,8 @@ typedef enum { /* This allocates/fills in a FL_Locale structure with pointers to strings (which should be treated as static), or NULL for inappropriate / undetected fields. */ -FL_Success FL_FindLocale(FL_Locale **locale, FL_Domain domain); +LL_COMMON_API FL_Success FL_FindLocale(FL_Locale **locale, FL_Domain domain); /* This should be used to free the struct written by FL_FindLocale */ -void FL_FreeLocale(FL_Locale **locale); +LL_COMMON_API void FL_FreeLocale(FL_Locale **locale); #endif /*__findlocale_h_*/ diff --git a/indra/llcommon/llfixedbuffer.h b/indra/llcommon/llfixedbuffer.h index 992a024df1..51d0701736 100644 --- a/indra/llcommon/llfixedbuffer.h +++ b/indra/llcommon/llfixedbuffer.h @@ -41,7 +41,7 @@ // Fixed size buffer for console output and other things. -class LLFixedBuffer +class LL_COMMON_API LLFixedBuffer { public: LLFixedBuffer(const U32 max_lines = 20); diff --git a/indra/llcommon/llformat.h b/indra/llcommon/llformat.h index 44c62d9710..dc64edb26d 100644 --- a/indra/llcommon/llformat.h +++ b/indra/llcommon/llformat.h @@ -40,6 +40,6 @@ // *NOTE: buffer limited to 1024, (but vsnprintf prevents overrun) // should perhaps be replaced with boost::format. -std::string llformat(const char *fmt, ...); +std::string LL_COMMON_API llformat(const char *fmt, ...); #endif // LL_LLFORMAT_H diff --git a/indra/llcommon/llframetimer.h b/indra/llcommon/llframetimer.h index 8f51272af2..be2d9b0703 100644 --- a/indra/llcommon/llframetimer.h +++ b/indra/llcommon/llframetimer.h @@ -43,7 +43,7 @@ #include "lltimer.h" #include "timing.h" -class LLFrameTimer +class LL_COMMON_API LLFrameTimer { public: LLFrameTimer() : mStartTime( sFrameTime ), mExpiry(0), mStarted(TRUE) {} diff --git a/indra/llcommon/llheartbeat.h b/indra/llcommon/llheartbeat.h index fecb5b1e54..6f7026970f 100644 --- a/indra/llcommon/llheartbeat.h +++ b/indra/llcommon/llheartbeat.h @@ -40,7 +40,7 @@ // Note: Win32 does not support the heartbeat/smackdown system; // heartbeat-delivery turns into a no-op there. -class LLHeartbeat +class LL_COMMON_API LLHeartbeat { public: // secs_between_heartbeat: after a heartbeat is successfully delivered, diff --git a/indra/llcommon/llliveappconfig.h b/indra/llcommon/llliveappconfig.h index a6ece6e8b3..73b3a23352 100644 --- a/indra/llcommon/llliveappconfig.h +++ b/indra/llcommon/llliveappconfig.h @@ -45,7 +45,7 @@ * loop. The traditional name for it is live_config. Be sure to call * <code>live_config.checkAndReload()</code> periodically. */ -class LLLiveAppConfig : public LLLiveFile +class LL_COMMON_API LLLiveAppConfig : public LLLiveFile { public: diff --git a/indra/llcommon/lllivefile.h b/indra/llcommon/lllivefile.h index 89b5d95e44..2453d7a125 100644 --- a/indra/llcommon/lllivefile.h +++ b/indra/llcommon/lllivefile.h @@ -36,7 +36,7 @@ extern const F32 DEFAULT_CONFIG_FILE_REFRESH; -class LLLiveFile +class LL_COMMON_API LLLiveFile { public: LLLiveFile(const std::string& filename, const F32 refresh_period = 5.f); diff --git a/indra/llcommon/lllog.h b/indra/llcommon/lllog.h index 7ac6c8aa42..4b6777bb9c 100644 --- a/indra/llcommon/lllog.h +++ b/indra/llcommon/lllog.h @@ -41,7 +41,7 @@ class LLLogImpl; class LLApp; class LLSD; -class LLLog +class LL_COMMON_API LLLog { public: LLLog(LLApp* app); diff --git a/indra/llcommon/llmd5.h b/indra/llcommon/llmd5.h index d8bca03e4e..df9d7324ab 100644 --- a/indra/llcommon/llmd5.h +++ b/indra/llcommon/llmd5.h @@ -80,7 +80,7 @@ const int MD5RAW_BYTES = 16; const int MD5HEX_STR_SIZE = 33; // char hex[MD5HEX_STR_SIZE]; with null const int MD5HEX_STR_BYTES = 32; // message system fixed size -class LLMD5 { +class LL_COMMON_API LLMD5 { // first, some types: typedef unsigned int uint4; // assumes integer is 4 words long typedef unsigned short int uint2; // assumes short integer is 2 words long diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index f41da37ba6..09f19532b7 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -1,65 +1,65 @@ -/** - * @file llmemory.h - * @brief Memory allocation/deallocation header-stuff goes here. - * - * $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 LLMEMORY_H -#define LLMEMORY_H - - - -extern S32 gTotalDAlloc; -extern S32 gTotalDAUse; -extern S32 gDACount; - -extern void* ll_allocate (size_t size); -extern void ll_release (void *p); - -class LLMemory -{ -public: - static void initClass(); - static void cleanupClass(); - static void freeReserve(); - // Return the resident set size of the current process, in bytes. - // Return value is zero if not known. - static U64 getCurrentRSS(); -private: - static char* reserveMem; -}; - -// LLRefCount moved to llrefcount.h - -// LLPointer moved to llpointer.h - -// LLSafeHandle moved to llsafehandle.h - -// LLSingleton moved to llsingleton.h - -#endif +/**
+ * @file llmemory.h
+ * @brief Memory allocation/deallocation header-stuff goes here.
+ *
+ * $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 LLMEMORY_H
+#define LLMEMORY_H
+
+
+
+extern S32 gTotalDAlloc;
+extern S32 gTotalDAUse;
+extern S32 gDACount;
+
+extern void* ll_allocate (size_t size);
+extern void ll_release (void *p);
+
+class LL_COMMON_API LLMemory
+{
+public:
+ static void initClass();
+ static void cleanupClass();
+ static void freeReserve();
+ // Return the resident set size of the current process, in bytes.
+ // Return value is zero if not known.
+ static U64 getCurrentRSS();
+private:
+ static char* reserveMem;
+};
+
+// LLRefCount moved to llrefcount.h
+
+// LLPointer moved to llpointer.h
+
+// LLSafeHandle moved to llsafehandle.h
+
+// LLSingleton moved to llsingleton.h
+
+#endif
diff --git a/indra/llcommon/llmemorystream.h b/indra/llcommon/llmemorystream.h index f3486324c5..fa0f5d22f2 100644 --- a/indra/llcommon/llmemorystream.h +++ b/indra/llcommon/llmemorystream.h @@ -52,7 +52,7 @@ * be careful to always pass in a valid memory location that exists * for at least as long as this streambuf. */ -class LLMemoryStreamBuf : public std::streambuf +class LL_COMMON_API LLMemoryStreamBuf : public std::streambuf { public: LLMemoryStreamBuf(const U8* start, S32 length); @@ -74,7 +74,7 @@ protected: * be careful to always pass in a valid memory location that exists * for at least as long as this streambuf. */ -class LLMemoryStream : public std::istream +class LL_COMMON_API LLMemoryStream : public std::istream { public: LLMemoryStream(const U8* start, S32 length); diff --git a/indra/llcommon/llmemtype.h b/indra/llcommon/llmemtype.h index 12310fcdb4..5952a3a7c5 100644 --- a/indra/llcommon/llmemtype.h +++ b/indra/llcommon/llmemtype.h @@ -1,248 +1,248 @@ -/** - * @file llmemtype.h - * @brief Runtime memory usage debugging utilities. - * - * $LicenseInfo:firstyear=2005&license=viewergpl$ - * - * Copyright (c) 2005-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_MEMTYPE_H -#define LL_MEMTYPE_H - -//---------------------------------------------------------------------------- -//---------------------------------------------------------------------------- - -//---------------------------------------------------------------------------- - -#include "linden_common.h" -//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// WARNING: Never commit with MEM_TRACK_MEM == 1 -//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -#define MEM_TRACK_MEM (0 && LL_WINDOWS) - -#include <vector> - -#define MEM_TYPE_NEW(T) - -class LLMemType -{ -public: - - // class we'll initialize all instances of as - // static members of MemType. Then use - // to construct any new mem type. - class DeclareMemType - { - public: - DeclareMemType(char const * st); - ~DeclareMemType(); - - S32 mID; - char const * mName; - - // array so we can map an index ID to Name - static std::vector<char const *> mNameList; - }; - - LLMemType(DeclareMemType& dt); - ~LLMemType(); - - static char const * getNameFromID(S32 id); - - static DeclareMemType MTYPE_INIT; - static DeclareMemType MTYPE_STARTUP; - static DeclareMemType MTYPE_MAIN; - static DeclareMemType MTYPE_FRAME; - - static DeclareMemType MTYPE_GATHER_INPUT; - static DeclareMemType MTYPE_JOY_KEY; - - static DeclareMemType MTYPE_IDLE; - static DeclareMemType MTYPE_IDLE_PUMP; - static DeclareMemType MTYPE_IDLE_NETWORK; - static DeclareMemType MTYPE_IDLE_UPDATE_REGIONS; - static DeclareMemType MTYPE_IDLE_UPDATE_VIEWER_REGION; - static DeclareMemType MTYPE_IDLE_UPDATE_SURFACE; - static DeclareMemType MTYPE_IDLE_UPDATE_PARCEL_OVERLAY; - static DeclareMemType MTYPE_IDLE_AUDIO; - - static DeclareMemType MTYPE_CACHE_PROCESS_PENDING; - static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_ASKS; - static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_REPLIES; - - static DeclareMemType MTYPE_MESSAGE_CHECK_ALL; - static DeclareMemType MTYPE_MESSAGE_PROCESS_ACKS; - - static DeclareMemType MTYPE_RENDER; - static DeclareMemType MTYPE_SLEEP; - - static DeclareMemType MTYPE_NETWORK; - static DeclareMemType MTYPE_PHYSICS; - static DeclareMemType MTYPE_INTERESTLIST; - - static DeclareMemType MTYPE_IMAGEBASE; - static DeclareMemType MTYPE_IMAGERAW; - static DeclareMemType MTYPE_IMAGEFORMATTED; - - static DeclareMemType MTYPE_APPFMTIMAGE; - static DeclareMemType MTYPE_APPRAWIMAGE; - static DeclareMemType MTYPE_APPAUXRAWIMAGE; - - static DeclareMemType MTYPE_DRAWABLE; - - static DeclareMemType MTYPE_OBJECT; - static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE; - static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE_CORE; - - static DeclareMemType MTYPE_DISPLAY; - static DeclareMemType MTYPE_DISPLAY_UPDATE; - static DeclareMemType MTYPE_DISPLAY_UPDATE_CAMERA; - static DeclareMemType MTYPE_DISPLAY_UPDATE_GEOM; - static DeclareMemType MTYPE_DISPLAY_SWAP; - static DeclareMemType MTYPE_DISPLAY_UPDATE_HUD; - static DeclareMemType MTYPE_DISPLAY_GEN_REFLECTION; - static DeclareMemType MTYPE_DISPLAY_IMAGE_UPDATE; - static DeclareMemType MTYPE_DISPLAY_STATE_SORT; - static DeclareMemType MTYPE_DISPLAY_SKY; - static DeclareMemType MTYPE_DISPLAY_RENDER_GEOM; - static DeclareMemType MTYPE_DISPLAY_RENDER_FLUSH; - static DeclareMemType MTYPE_DISPLAY_RENDER_UI; - static DeclareMemType MTYPE_DISPLAY_RENDER_ATTACHMENTS; - - static DeclareMemType MTYPE_VERTEX_DATA; - static DeclareMemType MTYPE_VERTEX_CONSTRUCTOR; - static DeclareMemType MTYPE_VERTEX_DESTRUCTOR; - static DeclareMemType MTYPE_VERTEX_CREATE_VERTICES; - static DeclareMemType MTYPE_VERTEX_CREATE_INDICES; - static DeclareMemType MTYPE_VERTEX_DESTROY_BUFFER; - static DeclareMemType MTYPE_VERTEX_DESTROY_INDICES; - static DeclareMemType MTYPE_VERTEX_UPDATE_VERTS; - static DeclareMemType MTYPE_VERTEX_UPDATE_INDICES; - static DeclareMemType MTYPE_VERTEX_ALLOCATE_BUFFER; - static DeclareMemType MTYPE_VERTEX_RESIZE_BUFFER; - static DeclareMemType MTYPE_VERTEX_MAP_BUFFER; - static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_VERTICES; - static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_INDICES; - static DeclareMemType MTYPE_VERTEX_UNMAP_BUFFER; - static DeclareMemType MTYPE_VERTEX_SET_STRIDE; - static DeclareMemType MTYPE_VERTEX_SET_BUFFER; - static DeclareMemType MTYPE_VERTEX_SETUP_VERTEX_BUFFER; - static DeclareMemType MTYPE_VERTEX_CLEANUP_CLASS; - - static DeclareMemType MTYPE_SPACE_PARTITION; - - static DeclareMemType MTYPE_PIPELINE; - static DeclareMemType MTYPE_PIPELINE_INIT; - static DeclareMemType MTYPE_PIPELINE_CREATE_BUFFERS; - static DeclareMemType MTYPE_PIPELINE_RESTORE_GL; - static DeclareMemType MTYPE_PIPELINE_UNLOAD_SHADERS; - static DeclareMemType MTYPE_PIPELINE_LIGHTING_DETAIL; - static DeclareMemType MTYPE_PIPELINE_GET_POOL_TYPE; - static DeclareMemType MTYPE_PIPELINE_ADD_POOL; - static DeclareMemType MTYPE_PIPELINE_ALLOCATE_DRAWABLE; - static DeclareMemType MTYPE_PIPELINE_ADD_OBJECT; - static DeclareMemType MTYPE_PIPELINE_CREATE_OBJECTS; - static DeclareMemType MTYPE_PIPELINE_UPDATE_MOVE; - static DeclareMemType MTYPE_PIPELINE_UPDATE_GEOM; - static DeclareMemType MTYPE_PIPELINE_MARK_VISIBLE; - static DeclareMemType MTYPE_PIPELINE_MARK_MOVED; - static DeclareMemType MTYPE_PIPELINE_MARK_SHIFT; - static DeclareMemType MTYPE_PIPELINE_SHIFT_OBJECTS; - static DeclareMemType MTYPE_PIPELINE_MARK_TEXTURED; - static DeclareMemType MTYPE_PIPELINE_MARK_REBUILD; - static DeclareMemType MTYPE_PIPELINE_UPDATE_CULL; - static DeclareMemType MTYPE_PIPELINE_STATE_SORT; - static DeclareMemType MTYPE_PIPELINE_POST_SORT; - - static DeclareMemType MTYPE_PIPELINE_RENDER_HUD_ELS; - static DeclareMemType MTYPE_PIPELINE_RENDER_HL; - static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM; - static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED; - static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_POST_DEF; - static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_SHADOW; - static DeclareMemType MTYPE_PIPELINE_RENDER_SELECT; - static DeclareMemType MTYPE_PIPELINE_REBUILD_POOLS; - static DeclareMemType MTYPE_PIPELINE_QUICK_LOOKUP; - static DeclareMemType MTYPE_PIPELINE_RENDER_OBJECTS; - static DeclareMemType MTYPE_PIPELINE_GENERATE_IMPOSTOR; - static DeclareMemType MTYPE_PIPELINE_RENDER_BLOOM; - - static DeclareMemType MTYPE_UPKEEP_POOLS; - - static DeclareMemType MTYPE_AVATAR; - static DeclareMemType MTYPE_AVATAR_MESH; - static DeclareMemType MTYPE_PARTICLES; - static DeclareMemType MTYPE_REGIONS; - - static DeclareMemType MTYPE_INVENTORY; - static DeclareMemType MTYPE_INVENTORY_DRAW; - static DeclareMemType MTYPE_INVENTORY_BUILD_NEW_VIEWS; - static DeclareMemType MTYPE_INVENTORY_DO_FOLDER; - static DeclareMemType MTYPE_INVENTORY_POST_BUILD; - static DeclareMemType MTYPE_INVENTORY_FROM_XML; - static DeclareMemType MTYPE_INVENTORY_CREATE_NEW_ITEM; - static DeclareMemType MTYPE_INVENTORY_VIEW_INIT; - static DeclareMemType MTYPE_INVENTORY_VIEW_SHOW; - static DeclareMemType MTYPE_INVENTORY_VIEW_TOGGLE; - - static DeclareMemType MTYPE_ANIMATION; - static DeclareMemType MTYPE_VOLUME; - static DeclareMemType MTYPE_PRIMITIVE; - - static DeclareMemType MTYPE_SCRIPT; - static DeclareMemType MTYPE_SCRIPT_RUN; - static DeclareMemType MTYPE_SCRIPT_BYTECODE; - - static DeclareMemType MTYPE_IO_PUMP; - static DeclareMemType MTYPE_IO_TCP; - static DeclareMemType MTYPE_IO_BUFFER; - static DeclareMemType MTYPE_IO_HTTP_SERVER; - static DeclareMemType MTYPE_IO_SD_SERVER; - static DeclareMemType MTYPE_IO_SD_CLIENT; - static DeclareMemType MTYPE_IO_URL_REQUEST; - - static DeclareMemType MTYPE_DIRECTX_INIT; - - static DeclareMemType MTYPE_TEMP1; - static DeclareMemType MTYPE_TEMP2; - static DeclareMemType MTYPE_TEMP3; - static DeclareMemType MTYPE_TEMP4; - static DeclareMemType MTYPE_TEMP5; - static DeclareMemType MTYPE_TEMP6; - static DeclareMemType MTYPE_TEMP7; - static DeclareMemType MTYPE_TEMP8; - static DeclareMemType MTYPE_TEMP9; - - static DeclareMemType MTYPE_OTHER; // Special; used by display code - - S32 mTypeIndex; -}; - -//---------------------------------------------------------------------------- - -#endif - +/**
+ * @file llmemtype.h
+ * @brief Runtime memory usage debugging utilities.
+ *
+ * $LicenseInfo:firstyear=2005&license=viewergpl$
+ *
+ * Copyright (c) 2005-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_MEMTYPE_H
+#define LL_MEMTYPE_H
+
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------
+
+#include "linden_common.h"
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// WARNING: Never commit with MEM_TRACK_MEM == 1
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+#define MEM_TRACK_MEM (0 && LL_WINDOWS)
+
+#include <vector>
+
+#define MEM_TYPE_NEW(T)
+
+class LL_COMMON_API LLMemType
+{
+public:
+
+ // class we'll initialize all instances of as
+ // static members of MemType. Then use
+ // to construct any new mem type.
+ class LL_COMMON_API DeclareMemType
+ {
+ public:
+ DeclareMemType(char const * st);
+ ~DeclareMemType();
+
+ S32 mID;
+ char const * mName;
+
+ // array so we can map an index ID to Name
+ static std::vector<char const *> mNameList;
+ };
+
+ LLMemType(DeclareMemType& dt);
+ ~LLMemType();
+
+ static char const * getNameFromID(S32 id);
+
+ static DeclareMemType MTYPE_INIT;
+ static DeclareMemType MTYPE_STARTUP;
+ static DeclareMemType MTYPE_MAIN;
+ static DeclareMemType MTYPE_FRAME;
+
+ static DeclareMemType MTYPE_GATHER_INPUT;
+ static DeclareMemType MTYPE_JOY_KEY;
+
+ static DeclareMemType MTYPE_IDLE;
+ static DeclareMemType MTYPE_IDLE_PUMP;
+ static DeclareMemType MTYPE_IDLE_NETWORK;
+ static DeclareMemType MTYPE_IDLE_UPDATE_REGIONS;
+ static DeclareMemType MTYPE_IDLE_UPDATE_VIEWER_REGION;
+ static DeclareMemType MTYPE_IDLE_UPDATE_SURFACE;
+ static DeclareMemType MTYPE_IDLE_UPDATE_PARCEL_OVERLAY;
+ static DeclareMemType MTYPE_IDLE_AUDIO;
+
+ static DeclareMemType MTYPE_CACHE_PROCESS_PENDING;
+ static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_ASKS;
+ static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_REPLIES;
+
+ static DeclareMemType MTYPE_MESSAGE_CHECK_ALL;
+ static DeclareMemType MTYPE_MESSAGE_PROCESS_ACKS;
+
+ static DeclareMemType MTYPE_RENDER;
+ static DeclareMemType MTYPE_SLEEP;
+
+ static DeclareMemType MTYPE_NETWORK;
+ static DeclareMemType MTYPE_PHYSICS;
+ static DeclareMemType MTYPE_INTERESTLIST;
+
+ static DeclareMemType MTYPE_IMAGEBASE;
+ static DeclareMemType MTYPE_IMAGERAW;
+ static DeclareMemType MTYPE_IMAGEFORMATTED;
+
+ static DeclareMemType MTYPE_APPFMTIMAGE;
+ static DeclareMemType MTYPE_APPRAWIMAGE;
+ static DeclareMemType MTYPE_APPAUXRAWIMAGE;
+
+ static DeclareMemType MTYPE_DRAWABLE;
+
+ static DeclareMemType MTYPE_OBJECT;
+ static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE;
+ static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE_CORE;
+
+ static DeclareMemType MTYPE_DISPLAY;
+ static DeclareMemType MTYPE_DISPLAY_UPDATE;
+ static DeclareMemType MTYPE_DISPLAY_UPDATE_CAMERA;
+ static DeclareMemType MTYPE_DISPLAY_UPDATE_GEOM;
+ static DeclareMemType MTYPE_DISPLAY_SWAP;
+ static DeclareMemType MTYPE_DISPLAY_UPDATE_HUD;
+ static DeclareMemType MTYPE_DISPLAY_GEN_REFLECTION;
+ static DeclareMemType MTYPE_DISPLAY_IMAGE_UPDATE;
+ static DeclareMemType MTYPE_DISPLAY_STATE_SORT;
+ static DeclareMemType MTYPE_DISPLAY_SKY;
+ static DeclareMemType MTYPE_DISPLAY_RENDER_GEOM;
+ static DeclareMemType MTYPE_DISPLAY_RENDER_FLUSH;
+ static DeclareMemType MTYPE_DISPLAY_RENDER_UI;
+ static DeclareMemType MTYPE_DISPLAY_RENDER_ATTACHMENTS;
+
+ static DeclareMemType MTYPE_VERTEX_DATA;
+ static DeclareMemType MTYPE_VERTEX_CONSTRUCTOR;
+ static DeclareMemType MTYPE_VERTEX_DESTRUCTOR;
+ static DeclareMemType MTYPE_VERTEX_CREATE_VERTICES;
+ static DeclareMemType MTYPE_VERTEX_CREATE_INDICES;
+ static DeclareMemType MTYPE_VERTEX_DESTROY_BUFFER;
+ static DeclareMemType MTYPE_VERTEX_DESTROY_INDICES;
+ static DeclareMemType MTYPE_VERTEX_UPDATE_VERTS;
+ static DeclareMemType MTYPE_VERTEX_UPDATE_INDICES;
+ static DeclareMemType MTYPE_VERTEX_ALLOCATE_BUFFER;
+ static DeclareMemType MTYPE_VERTEX_RESIZE_BUFFER;
+ static DeclareMemType MTYPE_VERTEX_MAP_BUFFER;
+ static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_VERTICES;
+ static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_INDICES;
+ static DeclareMemType MTYPE_VERTEX_UNMAP_BUFFER;
+ static DeclareMemType MTYPE_VERTEX_SET_STRIDE;
+ static DeclareMemType MTYPE_VERTEX_SET_BUFFER;
+ static DeclareMemType MTYPE_VERTEX_SETUP_VERTEX_BUFFER;
+ static DeclareMemType MTYPE_VERTEX_CLEANUP_CLASS;
+
+ static DeclareMemType MTYPE_SPACE_PARTITION;
+
+ static DeclareMemType MTYPE_PIPELINE;
+ static DeclareMemType MTYPE_PIPELINE_INIT;
+ static DeclareMemType MTYPE_PIPELINE_CREATE_BUFFERS;
+ static DeclareMemType MTYPE_PIPELINE_RESTORE_GL;
+ static DeclareMemType MTYPE_PIPELINE_UNLOAD_SHADERS;
+ static DeclareMemType MTYPE_PIPELINE_LIGHTING_DETAIL;
+ static DeclareMemType MTYPE_PIPELINE_GET_POOL_TYPE;
+ static DeclareMemType MTYPE_PIPELINE_ADD_POOL;
+ static DeclareMemType MTYPE_PIPELINE_ALLOCATE_DRAWABLE;
+ static DeclareMemType MTYPE_PIPELINE_ADD_OBJECT;
+ static DeclareMemType MTYPE_PIPELINE_CREATE_OBJECTS;
+ static DeclareMemType MTYPE_PIPELINE_UPDATE_MOVE;
+ static DeclareMemType MTYPE_PIPELINE_UPDATE_GEOM;
+ static DeclareMemType MTYPE_PIPELINE_MARK_VISIBLE;
+ static DeclareMemType MTYPE_PIPELINE_MARK_MOVED;
+ static DeclareMemType MTYPE_PIPELINE_MARK_SHIFT;
+ static DeclareMemType MTYPE_PIPELINE_SHIFT_OBJECTS;
+ static DeclareMemType MTYPE_PIPELINE_MARK_TEXTURED;
+ static DeclareMemType MTYPE_PIPELINE_MARK_REBUILD;
+ static DeclareMemType MTYPE_PIPELINE_UPDATE_CULL;
+ static DeclareMemType MTYPE_PIPELINE_STATE_SORT;
+ static DeclareMemType MTYPE_PIPELINE_POST_SORT;
+
+ static DeclareMemType MTYPE_PIPELINE_RENDER_HUD_ELS;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_HL;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_POST_DEF;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_SHADOW;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_SELECT;
+ static DeclareMemType MTYPE_PIPELINE_REBUILD_POOLS;
+ static DeclareMemType MTYPE_PIPELINE_QUICK_LOOKUP;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_OBJECTS;
+ static DeclareMemType MTYPE_PIPELINE_GENERATE_IMPOSTOR;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_BLOOM;
+
+ static DeclareMemType MTYPE_UPKEEP_POOLS;
+
+ static DeclareMemType MTYPE_AVATAR;
+ static DeclareMemType MTYPE_AVATAR_MESH;
+ static DeclareMemType MTYPE_PARTICLES;
+ static DeclareMemType MTYPE_REGIONS;
+
+ static DeclareMemType MTYPE_INVENTORY;
+ static DeclareMemType MTYPE_INVENTORY_DRAW;
+ static DeclareMemType MTYPE_INVENTORY_BUILD_NEW_VIEWS;
+ static DeclareMemType MTYPE_INVENTORY_DO_FOLDER;
+ static DeclareMemType MTYPE_INVENTORY_POST_BUILD;
+ static DeclareMemType MTYPE_INVENTORY_FROM_XML;
+ static DeclareMemType MTYPE_INVENTORY_CREATE_NEW_ITEM;
+ static DeclareMemType MTYPE_INVENTORY_VIEW_INIT;
+ static DeclareMemType MTYPE_INVENTORY_VIEW_SHOW;
+ static DeclareMemType MTYPE_INVENTORY_VIEW_TOGGLE;
+
+ static DeclareMemType MTYPE_ANIMATION;
+ static DeclareMemType MTYPE_VOLUME;
+ static DeclareMemType MTYPE_PRIMITIVE;
+
+ static DeclareMemType MTYPE_SCRIPT;
+ static DeclareMemType MTYPE_SCRIPT_RUN;
+ static DeclareMemType MTYPE_SCRIPT_BYTECODE;
+
+ static DeclareMemType MTYPE_IO_PUMP;
+ static DeclareMemType MTYPE_IO_TCP;
+ static DeclareMemType MTYPE_IO_BUFFER;
+ static DeclareMemType MTYPE_IO_HTTP_SERVER;
+ static DeclareMemType MTYPE_IO_SD_SERVER;
+ static DeclareMemType MTYPE_IO_SD_CLIENT;
+ static DeclareMemType MTYPE_IO_URL_REQUEST;
+
+ static DeclareMemType MTYPE_DIRECTX_INIT;
+
+ static DeclareMemType MTYPE_TEMP1;
+ static DeclareMemType MTYPE_TEMP2;
+ static DeclareMemType MTYPE_TEMP3;
+ static DeclareMemType MTYPE_TEMP4;
+ static DeclareMemType MTYPE_TEMP5;
+ static DeclareMemType MTYPE_TEMP6;
+ static DeclareMemType MTYPE_TEMP7;
+ static DeclareMemType MTYPE_TEMP8;
+ static DeclareMemType MTYPE_TEMP9;
+
+ static DeclareMemType MTYPE_OTHER; // Special; used by display code
+
+ S32 mTypeIndex;
+};
+
+//----------------------------------------------------------------------------
+
+#endif
+
diff --git a/indra/llcommon/llmetrics.h b/indra/llcommon/llmetrics.h index 1d91e8c8a2..f6f49eb456 100644 --- a/indra/llcommon/llmetrics.h +++ b/indra/llcommon/llmetrics.h @@ -38,7 +38,7 @@ class LLMetricsImpl; class LLSD; -class LLMetrics +class LL_COMMON_API LLMetrics { public: LLMetrics(); diff --git a/indra/llcommon/llmortician.h b/indra/llcommon/llmortician.h index fcda3df58e..27bd8cd9b5 100644 --- a/indra/llcommon/llmortician.h +++ b/indra/llcommon/llmortician.h @@ -35,7 +35,7 @@ #include "stdtypes.h" -class LLMortician +class LL_COMMON_API LLMortician { public: LLMortician() { mIsDead = FALSE; } diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h index 4401be1679..f135beed6a 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -1,140 +1,161 @@ -/** - * @file llpreprocessor.h - * @brief This file should be included in all Linden Lab files and - * should only contain special preprocessor directives - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#ifndef LLPREPROCESSOR_H -#define LLPREPROCESSOR_H - -// Figure out endianness of platform -#ifdef LL_LINUX -#define __ENABLE_WSTRING -#include <endian.h> -#endif // LL_LINUX - -#if LL_SOLARIS -# ifdef __sparc // Since we're talking Solaris 10 and up, only 64 bit is supported. -# define LL_BIG_ENDIAN 1 -# define LL_SOLARIS_ALIGNED_CPU 1 // used to designate issues where SPARC alignment is addressed -# define LL_SOLARIS_NON_MESA_GL 1 // The SPARC GL does not provide a MESA-based GL API -# endif -# include <sys/isa_defs.h> // ensure we know which end is up -#endif // LL_SOLARIS - -#if (defined(LL_WINDOWS) || (defined(LL_LINUX) && (__BYTE_ORDER == __LITTLE_ENDIAN)) || (defined(LL_DARWIN) && defined(__LITTLE_ENDIAN__)) || (defined(LL_SOLARIS) && defined(__i386))) -#define LL_LITTLE_ENDIAN 1 -#else -#define LL_BIG_ENDIAN 1 -#endif - -// Per-compiler switches -#ifdef __GNUC__ -#define LL_FORCE_INLINE inline __attribute__((always_inline)) -#else -#define LL_FORCE_INLINE __forceinline -#endif - -// Figure out differences between compilers -#if defined(__GNUC__) - #define GCC_VERSION (__GNUC__ * 10000 \ - + __GNUC_MINOR__ * 100 \ - + __GNUC_PATCHLEVEL__) - #ifndef LL_GNUC - #define LL_GNUC 1 - #endif -#elif defined(__MSVC_VER__) || defined(_MSC_VER) - #ifndef LL_MSVC - #define LL_MSVC 1 - #endif - #if _MSC_VER < 1400 - #define LL_MSVC7 //Visual C++ 2003 or earlier - #endif -#endif - -// Deal with minor differences on Unixy OSes. -#if LL_DARWIN || LL_LINUX - // Different name, same functionality. - #define stricmp strcasecmp - #define strnicmp strncasecmp - - // Not sure why this is different, but... - #ifndef MAX_PATH - #define MAX_PATH PATH_MAX - #endif // not MAX_PATH - -#endif - - -// Deal with the differeneces on Windows -#if LL_MSVC -namespace snprintf_hack -{ - int snprintf(char *str, size_t size, const char *format, ...); -} - -// #define snprintf safe_snprintf /* Flawfinder: ignore */ -using snprintf_hack::snprintf; -#endif // LL_MSVC - -// Static linking with apr on windows needs to be declared. -#ifdef LL_WINDOWS -#ifndef APR_DECLARE_STATIC -#define APR_DECLARE_STATIC // For APR on Windows -#endif -#ifndef APU_DECLARE_STATIC -#define APU_DECLARE_STATIC // For APR util on Windows -#endif -#endif - -#if defined(LL_WINDOWS) -#define BOOST_REGEX_NO_LIB 1 -#define CURL_STATICLIB 1 -#define XML_STATIC -#endif // LL_WINDOWS - - -// Deal with VC6 problems -#if LL_MSVC -#pragma warning( 3 : 4701 ) // "local variable used without being initialized" Treat this as level 3, not level 4. -#pragma warning( 3 : 4702 ) // "unreachable code" Treat this as level 3, not level 4. -#pragma warning( 3 : 4189 ) // "local variable initialized but not referenced" Treat this as level 3, not level 4. -//#pragma warning( 3 : 4018 ) // "signed/unsigned mismatch" Treat this as level 3, not level 4. -#pragma warning( 3 : 4263 ) // 'function' : member function does not override any base class virtual member function -#pragma warning( 3 : 4264 ) // "'virtual_function' : no override available for virtual member function from base 'class'; function is hidden" -#pragma warning( 3 : 4265 ) // "class has virtual functions, but destructor is not virtual" -#pragma warning( 3 : 4266 ) // 'function' : no override available for virtual member function from base 'type'; function is hidden -#pragma warning( disable : 4284 ) // silly MS warning deep inside their <map> include file -#pragma warning( disable : 4503 ) // 'decorated name length exceeded, name was truncated'. Does not seem to affect compilation. -#pragma warning( disable : 4800 ) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning) -#pragma warning( disable : 4996 ) // warning: deprecated -#endif // LL_MSVC - -#endif // not LL_LINDEN_PREPROCESSOR_H +/**
+ * @file llpreprocessor.h
+ * @brief This file should be included in all Linden Lab files and
+ * should only contain special preprocessor directives
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LLPREPROCESSOR_H
+#define LLPREPROCESSOR_H
+
+// Figure out endianness of platform
+#ifdef LL_LINUX
+#define __ENABLE_WSTRING
+#include <endian.h>
+#endif // LL_LINUX
+
+#if LL_SOLARIS
+# ifdef __sparc // Since we're talking Solaris 10 and up, only 64 bit is supported.
+# define LL_BIG_ENDIAN 1
+# define LL_SOLARIS_ALIGNED_CPU 1 // used to designate issues where SPARC alignment is addressed
+# define LL_SOLARIS_NON_MESA_GL 1 // The SPARC GL does not provide a MESA-based GL API
+# endif
+# include <sys/isa_defs.h> // ensure we know which end is up
+#endif // LL_SOLARIS
+
+#if (defined(LL_WINDOWS) || (defined(LL_LINUX) && (__BYTE_ORDER == __LITTLE_ENDIAN)) || (defined(LL_DARWIN) && defined(__LITTLE_ENDIAN__)) || (defined(LL_SOLARIS) && defined(__i386)))
+#define LL_LITTLE_ENDIAN 1
+#else
+#define LL_BIG_ENDIAN 1
+#endif
+
+// Per-compiler switches
+#ifdef __GNUC__
+#define LL_FORCE_INLINE inline __attribute__((always_inline))
+#else
+#define LL_FORCE_INLINE __forceinline
+#endif
+
+// Figure out differences between compilers
+#if defined(__GNUC__)
+ #define GCC_VERSION (__GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__)
+ #ifndef LL_GNUC
+ #define LL_GNUC 1
+ #endif
+#elif defined(__MSVC_VER__) || defined(_MSC_VER)
+ #ifndef LL_MSVC
+ #define LL_MSVC 1
+ #endif
+ #if _MSC_VER < 1400
+ #define LL_MSVC7 //Visual C++ 2003 or earlier
+ #endif
+#endif
+
+// Deal with minor differences on Unixy OSes.
+#if LL_DARWIN || LL_LINUX
+ // Different name, same functionality.
+ #define stricmp strcasecmp
+ #define strnicmp strncasecmp
+
+ // Not sure why this is different, but...
+ #ifndef MAX_PATH
+ #define MAX_PATH PATH_MAX
+ #endif // not MAX_PATH
+
+#endif
+
+
+// Static linking with apr on windows needs to be declared.
+#if LL_WINDOWS && !LL_COMMON_LINK_SHARED
+#ifndef APR_DECLARE_STATIC
+#define APR_DECLARE_STATIC // For APR on Windows
+#endif
+#ifndef APU_DECLARE_STATIC
+#define APU_DECLARE_STATIC // For APR util on Windows
+#endif
+#endif
+
+#if defined(LL_WINDOWS)
+#define BOOST_REGEX_NO_LIB 1
+#define CURL_STATICLIB 1
+#define XML_STATIC
+#endif // LL_WINDOWS
+
+
+// Deal with VC6 problems
+#if LL_MSVC
+#pragma warning( 3 : 4701 ) // "local variable used without being initialized" Treat this as level 3, not level 4.
+#pragma warning( 3 : 4702 ) // "unreachable code" Treat this as level 3, not level 4.
+#pragma warning( 3 : 4189 ) // "local variable initialized but not referenced" Treat this as level 3, not level 4.
+//#pragma warning( 3 : 4018 ) // "signed/unsigned mismatch" Treat this as level 3, not level 4.
+#pragma warning( 3 : 4263 ) // 'function' : member function does not override any base class virtual member function
+#pragma warning( 3 : 4264 ) // "'virtual_function' : no override available for virtual member function from base 'class'; function is hidden"
+#pragma warning( 3 : 4265 ) // "class has virtual functions, but destructor is not virtual"
+#pragma warning( 3 : 4266 ) // 'function' : no override available for virtual member function from base 'type'; function is hidden
+#pragma warning( disable : 4284 ) // silly MS warning deep inside their <map> include file
+#pragma warning( disable : 4503 ) // 'decorated name length exceeded, name was truncated'. Does not seem to affect compilation.
+#pragma warning( disable : 4800 ) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning)
+#pragma warning( disable : 4996 ) // warning: deprecated
+
+// level 4 warnings that we need to disable:
+#pragma warning (disable : 4100) // unreferenced formal parameter
+#pragma warning (disable : 4127) // conditional expression is constant (e.g. while(1) )
+#pragma warning (disable : 4244) // possible loss of data on conversions
+#pragma warning (disable : 4396) // the inline specifier cannot be used when a friend declaration refers to a specialization of a function template
+#pragma warning (disable : 4512) // assignment operator could not be generated
+#pragma warning (disable : 4706) // assignment within conditional (even if((x = y)) )
+
+#pragma warning (disable : 4251) // member needs to have dll-interface to be used by clients of class
+#pragma warning (disable : 4275) // non dll-interface class used as base for dll-interface class
+#endif // LL_MSVC
+
+#if LL_WINDOWS
+#define LL_DLLEXPORT __declspec(dllexport)
+#define LL_DLLIMPORT __declspec(dllimport)
+#elif LL_LINUX
+#define LL_DLLEXPORT __attribute__ ((visibility("default")))
+#define LL_DLLIMPORT
+#else
+#define LL_DLLEXPORT
+#define LL_DLLIMPORT
+#endif // LL_WINDOWS
+
+#if LL_COMMON_LINK_SHARED
+# if LL_COMMON_BUILD
+# define LL_COMMON_API LL_DLLEXPORT
+# else //LL_COMMON_BUILD
+# define LL_COMMON_API LL_DLLIMPORT
+# endif //LL_COMMON_BUILD
+#else // LL_COMMON_LINK_SHARED
+# define LL_COMMON_API
+#endif // LL_COMMON_LINK_SHARED
+
+#endif // not LL_LINDEN_PREPROCESSOR_H
diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h index 3ba43e1e07..8bfa5632a1 100644 --- a/indra/llcommon/llqueuedthread.h +++ b/indra/llcommon/llqueuedthread.h @@ -47,7 +47,7 @@ // Note: ~LLQueuedThread is O(N) N=# of queued threads, assumed to be small // It is assumed that LLQueuedThreads are rarely created/destroyed. -class LLQueuedThread : public LLThread +class LL_COMMON_API LLQueuedThread : public LLThread { //------------------------------------------------------------------------ public: @@ -80,7 +80,7 @@ public: //------------------------------------------------------------------------ public: - class QueuedRequest : public LLSimpleHashEntry<handle_t> + class LL_COMMON_API QueuedRequest : public LLSimpleHashEntry<handle_t> { friend class LLQueuedThread; @@ -148,6 +148,7 @@ protected: } }; + //------------------------------------------------------------------------ public: diff --git a/indra/llcommon/llrand.h b/indra/llcommon/llrand.h index d12597bb53..30fec9b982 100644 --- a/indra/llcommon/llrand.h +++ b/indra/llcommon/llrand.h @@ -65,32 +65,32 @@ /** *@brief Generate a float from [0, RAND_MAX). */ -S32 ll_rand(); +S32 LL_COMMON_API ll_rand(); /** *@brief Generate a float from [0, val) or (val, 0]. */ -S32 ll_rand(S32 val); +S32 LL_COMMON_API ll_rand(S32 val); /** *@brief Generate a float from [0, 1.0). */ -F32 ll_frand(); +F32 LL_COMMON_API ll_frand(); /** *@brief Generate a float from [0, val) or (val, 0]. */ -F32 ll_frand(F32 val); +F32 LL_COMMON_API ll_frand(F32 val); /** *@brief Generate a double from [0, 1.0). */ -F64 ll_drand(); +F64 LL_COMMON_API ll_drand(); /** *@brief Generate a double from [0, val) or (val, 0]. */ -F64 ll_drand(F64 val); +F64 LL_COMMON_API ll_drand(F64 val); /** * @brief typedefs for good boost lagged fibonacci. diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h index d3597b454c..9ab844eb22 100644 --- a/indra/llcommon/llrefcount.h +++ b/indra/llcommon/llrefcount.h @@ -39,9 +39,9 @@ // see llthread.h for LLThreadSafeRefCount //---------------------------------------------------------------------------- -class LLRefCount +class LL_COMMON_API LLRefCount { -protected: +private: LLRefCount(const LLRefCount& other); // no implementation private: LLRefCount& operator=(const LLRefCount&); // no implementation diff --git a/indra/llcommon/llrun.h b/indra/llcommon/llrun.h index 77b23d9051..1fc9925df9 100644 --- a/indra/llcommon/llrun.h +++ b/indra/llcommon/llrun.h @@ -48,7 +48,7 @@ class LLRunnable; * which are scheduled to run on a repeating or one time basis. * @see LLRunnable */ -class LLRunner +class LL_COMMON_API LLRunner { public: /** @@ -149,7 +149,7 @@ protected: * something useful. * @see LLRunner */ -class LLRunnable +class LL_COMMON_API LLRunnable { public: LLRunnable(); diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h index d2845a3757..552bb57498 100644 --- a/indra/llcommon/llsd.h +++ b/indra/llcommon/llsd.h @@ -89,7 +89,7 @@ @nosubgrouping */ -class LLSD +class LL_COMMON_API LLSD { public: LLSD(); ///< initially Undefined @@ -387,7 +387,7 @@ struct llsd_select_string : public std::unary_function<LLSD, LLSD::String> } }; -std::ostream& operator<<(std::ostream& s, const LLSD& llsd); +LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLSD& llsd); /** QUESTIONS & TO DOS - Would Binary be more convenient as usigned char* buffer semantics? diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h index 7463d1e5dd..2f2b292189 100644 --- a/indra/llcommon/llsdserialize.h +++ b/indra/llcommon/llsdserialize.h @@ -44,7 +44,7 @@ * @class LLSDParser * @brief Abstract base class for LLSD parsers. */ -class LLSDParser : public LLRefCount +class LL_COMMON_API LLSDParser : public LLRefCount { protected: /** @@ -221,7 +221,7 @@ protected: * @class LLSDNotationParser * @brief Parser which handles the original notation format for LLSD. */ -class LLSDNotationParser : public LLSDParser +class LL_COMMON_API LLSDNotationParser : public LLSDParser { protected: /** @@ -294,7 +294,7 @@ private: * @class LLSDXMLParser * @brief Parser which handles XML format LLSD. */ -class LLSDXMLParser : public LLSDParser +class LL_COMMON_API LLSDXMLParser : public LLSDParser { protected: /** @@ -342,7 +342,7 @@ private: * @class LLSDBinaryParser * @brief Parser which handles binary formatted LLSD. */ -class LLSDBinaryParser : public LLSDParser +class LL_COMMON_API LLSDBinaryParser : public LLSDParser { protected: /** @@ -407,7 +407,7 @@ private: * @class LLSDFormatter * @brief Abstract base class for formatting LLSD. */ -class LLSDFormatter : public LLRefCount +class LL_COMMON_API LLSDFormatter : public LLRefCount { protected: /** @@ -479,7 +479,7 @@ protected: * @class LLSDNotationFormatter * @brief Formatter which outputs the original notation format for LLSD. */ -class LLSDNotationFormatter : public LLSDFormatter +class LL_COMMON_API LLSDNotationFormatter : public LLSDFormatter { protected: /** @@ -520,7 +520,7 @@ public: * @class LLSDXMLFormatter * @brief Formatter which outputs the LLSD as XML. */ -class LLSDXMLFormatter : public LLSDFormatter +class LL_COMMON_API LLSDXMLFormatter : public LLSDFormatter { protected: /** @@ -588,7 +588,7 @@ protected: * Map: '{' + 4 byte integer size every(key + value) + '}'<br> * map keys are serialized as 'k' + 4 byte integer size + string */ -class LLSDBinaryFormatter : public LLSDFormatter +class LL_COMMON_API LLSDBinaryFormatter : public LLSDFormatter { protected: /** @@ -638,9 +638,14 @@ protected: * params << "[{'version':i1}," << LLSDOStreamer<LLSDNotationFormatter>(sd) * << "]"; * </code> + * + * *NOTE - formerly this class inherited from its template parameter Formatter, + * but all insnatiations passed in LLRefCount subclasses. This conflicted with + * the auto allocation intended for this class template (demonstrated in the + * example above). -brad */ template <class Formatter> -class LLSDOStreamer : public Formatter +class LLSDOStreamer { public: /** @@ -661,7 +666,8 @@ public: std::ostream& str, const LLSDOStreamer<Formatter>& formatter) { - formatter.format(formatter.mSD, str, formatter.mOptions); + LLPointer<Formatter> f = new Formatter; + f->format(formatter.mSD, str, formatter.mOptions); return str; } @@ -677,7 +683,7 @@ typedef LLSDOStreamer<LLSDXMLFormatter> LLSDXMLStreamer; * @class LLSDSerialize * @brief Serializer / deserializer for the various LLSD formats */ -class LLSDSerialize +class LL_COMMON_API LLSDSerialize { public: enum ELLSD_Serialize diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp index 0202a033c3..c8d8030e87 100644 --- a/indra/llcommon/llsdutil.cpp +++ b/indra/llcommon/llsdutil.cpp @@ -46,6 +46,11 @@ #endif #include "llsdserialize.h" +#include "stringize.h" + +#include <map> +#include <set> +#include <boost/range.hpp> // U32 LLSD ll_sd_from_U32(const U32 val) @@ -313,3 +318,353 @@ BOOL compare_llsd_with_template( return TRUE; } + +/***************************************************************************** +* Helpers for llsd_matches() +*****************************************************************************/ +// raw data used for LLSD::Type lookup +struct Data +{ + LLSD::Type type; + const char* name; +} typedata[] = +{ +#define def(type) { LLSD::type, #type + 4 } + def(TypeUndefined), + def(TypeBoolean), + def(TypeInteger), + def(TypeReal), + def(TypeString), + def(TypeUUID), + def(TypeDate), + def(TypeURI), + def(TypeBinary), + def(TypeMap), + def(TypeArray) +#undef def +}; + +// LLSD::Type lookup class into which we load the above static data +class TypeLookup +{ + typedef std::map<LLSD::Type, std::string> MapType; + +public: + TypeLookup() + { + for (const Data *di(boost::begin(typedata)), *dend(boost::end(typedata)); di != dend; ++di) + { + mMap[di->type] = di->name; + } + } + + std::string lookup(LLSD::Type type) const + { + MapType::const_iterator found = mMap.find(type); + if (found != mMap.end()) + { + return found->second; + } + return STRINGIZE("<unknown LLSD type " << type << ">"); + } + +private: + MapType mMap; +}; + +// static instance of the lookup class +static const TypeLookup sTypes; + +// describe a mismatch; phrasing may want tweaking +const std::string op(" required instead of "); + +// llsd_matches() wants to identify specifically where in a complex prototype +// structure the mismatch occurred. This entails passing a prefix string, +// empty for the top-level call. If the prototype contains an array of maps, +// and the mismatch occurs in the second map in a key 'foo', we want to +// decorate the returned string with: "[1]['foo']: etc." On the other hand, we +// want to omit the entire prefix -- including colon -- if the mismatch is at +// top level. This helper accepts the (possibly empty) recursively-accumulated +// prefix string, returning either empty or the original string with colon +// appended. +static std::string colon(const std::string& pfx) +{ + if (pfx.empty()) + return pfx; + return pfx + ": "; +} + +// param type for match_types +typedef std::vector<LLSD::Type> TypeVector; + +// The scalar cases in llsd_matches() use this helper. In most cases, we can +// accept not only the exact type specified in the prototype, but also other +// types convertible to the expected type. That implies looping over an array +// of such types. If the actual type doesn't match any of them, we want to +// provide a list of acceptable conversions as well as the exact type, e.g.: +// "Integer (or Boolean, Real, String) required instead of UUID". Both the +// implementation and the calling logic are simplified by separating out the +// expected type from the convertible types. +static std::string match_types(LLSD::Type expect, // prototype.type() + const TypeVector& accept, // types convertible to that type + LLSD::Type actual, // type we're checking + const std::string& pfx) // as for llsd_matches +{ + // Trivial case: if the actual type is exactly what we expect, we're good. + if (actual == expect) + return ""; + + // For the rest of the logic, build up a suitable error string as we go so + // we only have to make a single pass over the list of acceptable types. + // If we detect success along the way, we'll simply discard the partial + // error string. + std::ostringstream out; + out << colon(pfx) << sTypes.lookup(expect); + + // If there are any convertible types, append that list. + if (! accept.empty()) + { + out << " ("; + const char* sep = "or "; + for (TypeVector::const_iterator ai(accept.begin()), aend(accept.end()); + ai != aend; ++ai, sep = ", ") + { + // Don't forget to return success if we match any of those types... + if (actual == *ai) + return ""; + out << sep << sTypes.lookup(*ai); + } + out << ')'; + } + // If we got this far, it's because 'actual' was not one of the acceptable + // types, so we must return an error. 'out' already contains colon(pfx) + // and the formatted list of acceptable types, so just append the mismatch + // phrase and the actual type. + out << op << sTypes.lookup(actual); + return out.str(); +} + +// see docstring in .h file +std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx) +{ + // An undefined prototype means that any data is valid. + // An undefined slot in an array or map prototype means that any data + // may fill that slot. + if (prototype.isUndefined()) + return ""; + // A prototype array must match a data array with at least as many + // entries. Moreover, every prototype entry must match the + // corresponding data entry. + if (prototype.isArray()) + { + if (! data.isArray()) + { + return STRINGIZE(colon(pfx) << "Array" << op << sTypes.lookup(data.type())); + } + if (data.size() < prototype.size()) + { + return STRINGIZE(colon(pfx) << "Array size " << prototype.size() << op + << "Array size " << data.size()); + } + for (LLSD::Integer i = 0; i < prototype.size(); ++i) + { + std::string match(llsd_matches(prototype[i], data[i], STRINGIZE('[' << i << ']'))); + if (! match.empty()) + { + return match; + } + } + return ""; + } + // A prototype map must match a data map. Every key in the prototype + // must have a corresponding key in the data map; every value in the + // prototype must match the corresponding key's value in the data. + if (prototype.isMap()) + { + if (! data.isMap()) + { + return STRINGIZE(colon(pfx) << "Map" << op << sTypes.lookup(data.type())); + } + // If there are a number of keys missing from the data, it would be + // frustrating to a coder to discover them one at a time, with a big + // build each time. Enumerate all missing keys. + std::ostringstream out; + out << colon(pfx); + const char* init = "Map missing keys: "; + const char* sep = init; + for (LLSD::map_const_iterator mi = prototype.beginMap(); mi != prototype.endMap(); ++mi) + { + if (! data.has(mi->first)) + { + out << sep << mi->first; + sep = ", "; + } + } + // So... are we missing any keys? + if (sep != init) + { + return out.str(); + } + // Good, the data block contains all the keys required by the + // prototype. Now match the prototype entries. + for (LLSD::map_const_iterator mi2 = prototype.beginMap(); mi2 != prototype.endMap(); ++mi2) + { + std::string match(llsd_matches(mi2->second, data[mi2->first], + STRINGIZE("['" << mi2->first << "']"))); + if (! match.empty()) + { + return match; + } + } + return ""; + } + // A String prototype can match String, Boolean, Integer, Real, UUID, + // Date and URI, because any of these can be converted to String. + if (prototype.isString()) + { + static LLSD::Type accept[] = + { + LLSD::TypeBoolean, + LLSD::TypeInteger, + LLSD::TypeReal, + LLSD::TypeUUID, + LLSD::TypeDate, + LLSD::TypeURI + }; + return match_types(prototype.type(), + TypeVector(boost::begin(accept), boost::end(accept)), + data.type(), + pfx); + } + // Boolean, Integer, Real match each other or String. TBD: ensure that + // a String value is numeric. + if (prototype.isBoolean() || prototype.isInteger() || prototype.isReal()) + { + static LLSD::Type all[] = + { + LLSD::TypeBoolean, + LLSD::TypeInteger, + LLSD::TypeReal, + LLSD::TypeString + }; + // Funny business: shuffle the set of acceptable types to include all + // but the prototype's type. Get the acceptable types in a set. + std::set<LLSD::Type> rest(boost::begin(all), boost::end(all)); + // Remove the prototype's type because we pass that separately. + rest.erase(prototype.type()); + return match_types(prototype.type(), + TypeVector(rest.begin(), rest.end()), + data.type(), + pfx); + } + // UUID, Date and URI match themselves or String. + if (prototype.isUUID() || prototype.isDate() || prototype.isURI()) + { + static LLSD::Type accept[] = + { + LLSD::TypeString + }; + return match_types(prototype.type(), + TypeVector(boost::begin(accept), boost::end(accept)), + data.type(), + pfx); + } + // We don't yet know the conversion semantics associated with any new LLSD + // data type that might be added, so until we've been extended to handle + // them, assume it's strict: the new type matches only itself. (This is + // true of Binary, which is why we don't handle that case separately.) Too + // bad LLSD doesn't define isConvertible(Type to, Type from). + return match_types(prototype.type(), TypeVector(), data.type(), pfx); +} + +bool llsd_equals(const LLSD& lhs, const LLSD& rhs) +{ + // We're comparing strict equality of LLSD representation rather than + // performing any conversions. So if the types aren't equal, the LLSD + // values aren't equal. + if (lhs.type() != rhs.type()) + { + return false; + } + + // Here we know both types are equal. Now compare values. + switch (lhs.type()) + { + case LLSD::TypeUndefined: + // Both are TypeUndefined. There's nothing more to know. + return true; + +#define COMPARE_SCALAR(type) \ + case LLSD::Type##type: \ + /* LLSD::URI has operator!=() but not operator==() */ \ + /* rely on the optimizer for all others */ \ + return (! (lhs.as##type() != rhs.as##type())) + + COMPARE_SCALAR(Boolean); + COMPARE_SCALAR(Integer); + // The usual caveats about comparing floating-point numbers apply. This is + // only useful when we expect identical bit representation for a given + // Real value, e.g. for integer-valued Reals. + COMPARE_SCALAR(Real); + COMPARE_SCALAR(String); + COMPARE_SCALAR(UUID); + COMPARE_SCALAR(Date); + COMPARE_SCALAR(URI); + COMPARE_SCALAR(Binary); + +#undef COMPARE_SCALAR + + case LLSD::TypeArray: + { + LLSD::array_const_iterator + lai(lhs.beginArray()), laend(lhs.endArray()), + rai(rhs.beginArray()), raend(rhs.endArray()); + // Compare array elements, walking the two arrays in parallel. + for ( ; lai != laend && rai != raend; ++lai, ++rai) + { + // If any one array element is unequal, the arrays are unequal. + if (! llsd_equals(*lai, *rai)) + return false; + } + // Here we've reached the end of one or the other array. They're equal + // only if they're BOTH at end: that is, if they have equal length too. + return (lai == laend && rai == raend); + } + + case LLSD::TypeMap: + { + // Build a set of all rhs keys. + std::set<LLSD::String> rhskeys; + for (LLSD::map_const_iterator rmi(rhs.beginMap()), rmend(rhs.endMap()); + rmi != rmend; ++rmi) + { + rhskeys.insert(rmi->first); + } + // Now walk all the lhs keys. + for (LLSD::map_const_iterator lmi(lhs.beginMap()), lmend(lhs.endMap()); + lmi != lmend; ++lmi) + { + // Try to erase this lhs key from the set of rhs keys. If rhs has + // no such key, the maps are unequal. erase(key) returns count of + // items erased. + if (rhskeys.erase(lmi->first) != 1) + return false; + // Both maps have the current key. Compare values. + if (! llsd_equals(lmi->second, rhs[lmi->first])) + return false; + } + // We've now established that all the lhs keys have equal values in + // both maps. The maps are equal unless rhs contains a superset of + // those keys. + return rhskeys.empty(); + } + + default: + // We expect that every possible type() value is specifically handled + // above. Failing to extend this switch to support a new LLSD type is + // an error that must be brought to the coder's attention. + LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << "): " + "unknown type " << lhs.type() << LL_ENDL; + return false; // pacify the compiler + } +} diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index 501600f1d9..6a6c396687 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -35,62 +35,32 @@ #ifndef LL_LLSDUTIL_H #define LL_LLSDUTIL_H -#include "llsd.h" - -// vector3 -class LLVector3; -LLSD ll_sd_from_vector3(const LLVector3& vec); -LLVector3 ll_vector3_from_sd(const LLSD& sd, S32 start_index = 0); - -// vector4 -class LLVector4; -LLSD ll_sd_from_vector4(const LLVector4& vec); -LLVector4 ll_vector4_from_sd(const LLSD& sd, S32 start_index = 0); - -// vector3d (double) -class LLVector3d; -LLSD ll_sd_from_vector3d(const LLVector3d& vec); -LLVector3d ll_vector3d_from_sd(const LLSD& sd, S32 start_index = 0); - -// vector2 -class LLVector2; -LLSD ll_sd_from_vector2(const LLVector2& vec); -LLVector2 ll_vector2_from_sd(const LLSD& sd); - -// Quaternion -class LLQuaternion; -LLSD ll_sd_from_quaternion(const LLQuaternion& quat); -LLQuaternion ll_quaternion_from_sd(const LLSD& sd); - -// color4 -class LLColor4; -LLSD ll_sd_from_color4(const LLColor4& c); -LLColor4 ll_color4_from_sd(const LLSD& sd); +class LLSD; // U32 -LLSD ll_sd_from_U32(const U32); -U32 ll_U32_from_sd(const LLSD& sd); +LL_COMMON_API LLSD ll_sd_from_U32(const U32); +LL_COMMON_API U32 ll_U32_from_sd(const LLSD& sd); // U64 -LLSD ll_sd_from_U64(const U64); -U64 ll_U64_from_sd(const LLSD& sd); +LL_COMMON_API LLSD ll_sd_from_U64(const U64); +LL_COMMON_API U64 ll_U64_from_sd(const LLSD& sd); // IP Address -LLSD ll_sd_from_ipaddr(const U32); -U32 ll_ipaddr_from_sd(const LLSD& sd); +LL_COMMON_API LLSD ll_sd_from_ipaddr(const U32); +LL_COMMON_API U32 ll_ipaddr_from_sd(const LLSD& sd); // Binary to string -LLSD ll_string_from_binary(const LLSD& sd); +LL_COMMON_API LLSD ll_string_from_binary(const LLSD& sd); //String to binary -LLSD ll_binary_from_string(const LLSD& sd); +LL_COMMON_API LLSD ll_binary_from_string(const LLSD& sd); // Serializes sd to static buffer and returns pointer, useful for gdb debugging. -char* ll_print_sd(const LLSD& sd); +LL_COMMON_API char* ll_print_sd(const LLSD& sd); // Serializes sd to static buffer and returns pointer, using "pretty printing" mode. -char* ll_pretty_print_sd_ptr(const LLSD* sd); -char* ll_pretty_print_sd(const LLSD& sd); +LL_COMMON_API char* ll_pretty_print_sd_ptr(const LLSD* sd); +LL_COMMON_API char* ll_pretty_print_sd(const LLSD& sd); //compares the structure of an LLSD to a template LLSD and stores the //"valid" values in a 3rd LLSD. Default values @@ -99,11 +69,69 @@ char* ll_pretty_print_sd(const LLSD& sd); //Returns false if the test is of same type but values differ in type //Otherwise, returns true -BOOL compare_llsd_with_template( +LL_COMMON_API BOOL compare_llsd_with_template( const LLSD& llsd_to_test, const LLSD& template_llsd, LLSD& resultant_llsd); +/** + * Recursively determine whether a given LLSD data block "matches" another + * LLSD prototype. The returned string is empty() on success, non-empty() on + * mismatch. + * + * This function tests structure (types) rather than data values. It is + * intended for when a consumer expects an LLSD block with a particular + * structure, and must succinctly detect whether the arriving block is + * well-formed. For instance, a test of the form: + * @code + * if (! (data.has("request") && data.has("target") && data.has("modifier") ...)) + * @endcode + * could instead be expressed by initializing a prototype LLSD map with the + * required keys and writing: + * @code + * if (! llsd_matches(prototype, data).empty()) + * @endcode + * + * A non-empty return value is an error-message fragment intended to indicate + * to (English-speaking) developers where in the prototype structure the + * mismatch occurred. + * + * * If a slot in the prototype isUndefined(), then anything is valid at that + * place in the real object. (Passing prototype == LLSD() matches anything + * at all.) + * * An array in the prototype must match a data array at least that large. + * (Additional entries in the data array are ignored.) Every isDefined() + * entry in the prototype array must match the corresponding entry in the + * data array. + * * A map in the prototype must match a map in the data. Every key in the + * prototype map must match a corresponding key in the data map. (Additional + * keys in the data map are ignored.) Every isDefined() value in the + * prototype map must match the corresponding key's value in the data map. + * * Scalar values in the prototype are tested for @em type rather than value. + * For instance, a String in the prototype matches any String at all. In + * effect, storing an Integer at a particular place in the prototype asserts + * that the caller intends to apply asInteger() to the corresponding slot in + * the data. + * * A String in the prototype matches String, Boolean, Integer, Real, UUID, + * Date and URI, because asString() applied to any of these produces a + * meaningful result. + * * Similarly, a Boolean, Integer or Real in the prototype can match any of + * Boolean, Integer or Real in the data -- or even String. + * * UUID matches UUID or String. + * * Date matches Date or String. + * * URI matches URI or String. + * * Binary in the prototype matches only Binary in the data. + * + * @TODO: when a Boolean, Integer or Real in the prototype matches a String in + * the data, we should examine the String @em value to ensure it can be + * meaningfully converted to the requested type. The same goes for UUID, Date + * and URI. + */ +LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx=""); + +/// Deep equality +LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs); + // Simple function to copy data out of input & output iterators if // there is no need for casting. template<typename Input> LLSD llsd_copy_array(Input iter, Input end) diff --git a/indra/llcommon/llsecondlifeurls.h b/indra/llcommon/llsecondlifeurls.h index a2e5f0b9c6..bd2f9f7604 100644 --- a/indra/llcommon/llsecondlifeurls.h +++ b/indra/llcommon/llsecondlifeurls.h @@ -34,49 +34,49 @@ #define LL_LLSECONDLIFEURLS_H /* // Account registration web page -extern const std::string CREATE_ACCOUNT_URL; +LL_COMMON_API extern const std::string CREATE_ACCOUNT_URL; // Manage Account -extern const std::string MANAGE_ACCOUNT; +LL_COMMON_API extern const std::string MANAGE_ACCOUNT; -extern const std::string AUCTION_URL; +LL_COMMON_API extern const std::string AUCTION_URL; -extern const std::string EVENTS_URL; +LL_COMMON_API extern const std::string EVENTS_URL; */ // Tier up to a new land level. -extern const std::string TIER_UP_URL; +LL_COMMON_API extern const std::string TIER_UP_URL; // Tier up to a new land level. -extern const std::string LAND_URL; +LL_COMMON_API extern const std::string LAND_URL; // How to get DirectX 9 -extern const std::string DIRECTX_9_URL; +LL_COMMON_API extern const std::string DIRECTX_9_URL; /* // Upgrade from basic membership to premium membership -extern const std::string UPGRADE_TO_PREMIUM_URL; +LL_COMMON_API extern const std::string UPGRADE_TO_PREMIUM_URL; // Out of date VIA chipset -extern const std::string VIA_URL; +LL_COMMON_API extern const std::string VIA_URL; // Support URL -extern const std::string SUPPORT_URL; +LL_COMMON_API extern const std::string SUPPORT_URL; // Linden Blogs page -extern const std::string BLOGS_URL; +LL_COMMON_API extern const std::string BLOGS_URL; // Currency page -extern const std::string BUY_CURRENCY_URL; +LL_COMMON_API extern const std::string BUY_CURRENCY_URL; // LSL script wiki -extern const std::string LSL_DOC_URL; +LL_COMMON_API extern const std::string LSL_DOC_URL; // SL KnowledgeBase page -extern const std::string SL_KB_URL; +LL_COMMON_API extern const std::string SL_KB_URL; // Release Notes Redirect URL for Server and Viewer -extern const std::string RELEASE_NOTES_BASE_URL; +LL_COMMON_API extern const std::string RELEASE_NOTES_BASE_URL; */ #endif diff --git a/indra/llcommon/llsimplehash.h b/indra/llcommon/llsimplehash.h index 0ba2a3014c..5df93b646e 100644 --- a/indra/llcommon/llsimplehash.h +++ b/indra/llcommon/llsimplehash.h @@ -64,7 +64,7 @@ public: }; template <typename HASH_KEY_TYPE, int TABLE_SIZE> -class LLSimpleHash +class LL_COMMON_API LLSimpleHash { public: LLSimpleHash() diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp new file mode 100644 index 0000000000..6b5feaf1c4 --- /dev/null +++ b/indra/llcommon/llsingleton.cpp @@ -0,0 +1,38 @@ +/** + * @file llsingleton.cpp + * @author Brad Kittenbrink + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009-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 "linden_common.h" + +#include "llsingleton.h" + +std::map<std::string, void *> * LLSingletonRegistry::sSingletonMap = NULL; + diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 2e7d845bf7..f55fafadd8 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -33,7 +33,41 @@ #include "llerror.h" // *TODO: eliminate this +#include <typeinfo> #include <boost/noncopyable.hpp> +#include <boost/any.hpp> + +/// @brief A global registry of all singletons to prevent duplicate allocations +/// across shared library boundaries +class LL_COMMON_API LLSingletonRegistry { + private: + typedef std::map<std::string, void *> TypeMap; + static TypeMap * sSingletonMap; + + static void checkInit() + { + if(sSingletonMap == NULL) + { + sSingletonMap = new TypeMap(); + } + } + + public: + template<typename T> static void * & get() + { + std::string name(typeid(T).name()); + + checkInit(); + + // the first entry of the pair returned by insert will be either the existing + // iterator matching our key, or the newly inserted NULL initialized entry + // see "Insert element" in http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html + TypeMap::iterator result = + sSingletonMap->insert(std::make_pair(name, (void*)NULL)).first; + + return result->second; + } +}; // LLSingleton implements the getInstance() method part of the Singleton // pattern. It can't make the derived class constructors protected, though, so @@ -107,8 +141,17 @@ public: static SingletonInstanceData& getData() { - static SingletonInstanceData data; - return data; + // this is static to cache the lookup results + static void * & registry = LLSingletonRegistry::get<DERIVED_TYPE>(); + + // *TODO - look into making this threadsafe + if(NULL == registry) + { + static SingletonInstanceData data; + registry = &data; + } + + return *static_cast<SingletonInstanceData *>(registry); } static DERIVED_TYPE* getInstance() diff --git a/indra/llcommon/llstacktrace.cpp b/indra/llcommon/llstacktrace.cpp index 4be91b5b11..3cb074257b 100644 --- a/indra/llcommon/llstacktrace.cpp +++ b/indra/llcommon/llstacktrace.cpp @@ -1,141 +1,142 @@ -/** - * @file llstacktrace.cpp - * @brief stack tracing functionality - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#include "llstacktrace.h" - -#ifdef LL_WINDOWS - -#include <iostream> -#include <sstream> - -#include "windows.h" -#include "Dbghelp.h" - -typedef USHORT NTAPI RtlCaptureStackBackTrace_Function( - IN ULONG frames_to_skip, - IN ULONG frames_to_capture, - OUT PVOID *backtrace, - OUT PULONG backtrace_hash); - -static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn = - (RtlCaptureStackBackTrace_Function*) - GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace"); - -bool ll_get_stack_trace(std::vector<std::string>& lines) -{ - const S32 MAX_STACK_DEPTH = 32; - const S32 STRING_NAME_LENGTH = 200; - const S32 FRAME_SKIP = 2; - static BOOL symbolsLoaded = false; - static BOOL firstCall = true; - - HANDLE hProc = GetCurrentProcess(); - - // load the symbols if they're not loaded - if(!symbolsLoaded && firstCall) - { - symbolsLoaded = SymInitialize(hProc, NULL, true); - firstCall = false; - } - - // if loaded, get the call stack - if(symbolsLoaded) - { - // create the frames to hold the addresses - void* frames[MAX_STACK_DEPTH]; - memset(frames, 0, sizeof(void*)*MAX_STACK_DEPTH); - S32 depth = 0; - - // get the addresses - depth = RtlCaptureStackBackTrace_fn(FRAME_SKIP, MAX_STACK_DEPTH, frames, NULL); - - IMAGEHLP_LINE64 line; - memset(&line, 0, sizeof(IMAGEHLP_LINE64)); - line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); - - // create something to hold address info - PIMAGEHLP_SYMBOL64 pSym; - pSym = (PIMAGEHLP_SYMBOL64)malloc(sizeof(IMAGEHLP_SYMBOL64) + STRING_NAME_LENGTH); - memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STRING_NAME_LENGTH); - pSym->MaxNameLength = STRING_NAME_LENGTH; - pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); - - // get address info for each address frame - // and store - for(S32 i=0; i < depth; i++) - { - std::stringstream stack_line; - BOOL ret; - - DWORD64 addr = (DWORD64)frames[i]; - ret = SymGetSymFromAddr64(hProc, addr, 0, pSym); - if(ret) - { - stack_line << pSym->Name << " "; - } - - DWORD dummy; - ret = SymGetLineFromAddr64(hProc, addr, &dummy, &line); - if(ret) - { - std::string file_name = line.FileName; - std::string::size_type index = file_name.rfind("\\"); - stack_line << file_name.substr(index + 1, file_name.size()) << ":" << line.LineNumber; - } - - lines.push_back(stack_line.str()); - } - - free(pSym); - - // TODO: figure out a way to cleanup symbol loading - // Not hugely necessary, however. - //SymCleanup(hProc); - return true; - } - else - { - lines.push_back("Stack Trace Failed. PDB symbol info not loaded"); - } - - return false; -} - -#else - -bool ll_get_stack_trace(std::vector<std::string>& lines) -{ - return false; -} - -#endif - +/**
+ * @file llstacktrace.cpp
+ * @brief stack tracing functionality
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llstacktrace.h"
+
+#ifdef LL_WINDOWS
+
+#include <iostream>
+#include <sstream>
+
+#include "windows.h"
+#include "Dbghelp.h"
+
+typedef USHORT NTAPI RtlCaptureStackBackTrace_Function(
+ IN ULONG frames_to_skip,
+ IN ULONG frames_to_capture,
+ OUT PVOID *backtrace,
+ OUT PULONG backtrace_hash);
+
+static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn =
+ (RtlCaptureStackBackTrace_Function*)
+ GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace");
+
+bool ll_get_stack_trace(std::vector<std::string>& lines)
+{
+ const S32 MAX_STACK_DEPTH = 32;
+ const S32 STRING_NAME_LENGTH = 200;
+ const S32 FRAME_SKIP = 2;
+ static BOOL symbolsLoaded = false;
+ static BOOL firstCall = true;
+
+ HANDLE hProc = GetCurrentProcess();
+
+ // load the symbols if they're not loaded
+ if(!symbolsLoaded && firstCall)
+ {
+ symbolsLoaded = SymInitialize(hProc, NULL, true);
+ firstCall = false;
+ }
+
+ // if loaded, get the call stack
+ if(symbolsLoaded)
+ {
+ // create the frames to hold the addresses
+ void* frames[MAX_STACK_DEPTH];
+ memset(frames, 0, sizeof(void*)*MAX_STACK_DEPTH);
+ S32 depth = 0;
+
+ // get the addresses
+ depth = RtlCaptureStackBackTrace_fn(FRAME_SKIP, MAX_STACK_DEPTH, frames, NULL);
+
+ IMAGEHLP_LINE64 line;
+ memset(&line, 0, sizeof(IMAGEHLP_LINE64));
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+
+ // create something to hold address info
+ PIMAGEHLP_SYMBOL64 pSym;
+ pSym = (PIMAGEHLP_SYMBOL64)malloc(sizeof(IMAGEHLP_SYMBOL64) + STRING_NAME_LENGTH);
+ memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STRING_NAME_LENGTH);
+ pSym->MaxNameLength = STRING_NAME_LENGTH;
+ pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+
+ // get address info for each address frame
+ // and store
+ for(S32 i=0; i < depth; i++)
+ {
+ std::stringstream stack_line;
+ BOOL ret;
+
+ DWORD64 addr = (DWORD64)frames[i];
+ ret = SymGetSymFromAddr64(hProc, addr, 0, pSym);
+ if(ret)
+ {
+ stack_line << pSym->Name << " ";
+ }
+
+ DWORD dummy;
+ ret = SymGetLineFromAddr64(hProc, addr, &dummy, &line);
+ if(ret)
+ {
+ std::string file_name = line.FileName;
+ std::string::size_type index = file_name.rfind("\\");
+ stack_line << file_name.substr(index + 1, file_name.size()) << ":" << line.LineNumber;
+ }
+
+ lines.push_back(stack_line.str());
+ }
+
+ free(pSym);
+
+ // TODO: figure out a way to cleanup symbol loading
+ // Not hugely necessary, however.
+ //SymCleanup(hProc);
+ return true;
+ }
+ else
+ {
+ lines.push_back("Stack Trace Failed. PDB symbol info not loaded");
+ }
+
+ return false;
+}
+
+#else
+
+bool ll_get_stack_trace(std::vector<std::string>& lines)
+{
+ return false;
+}
+
+#endif
+
diff --git a/indra/llcommon/llstacktrace.h b/indra/llcommon/llstacktrace.h index 609b934a97..b84b1aa6ad 100644 --- a/indra/llcommon/llstacktrace.h +++ b/indra/llcommon/llstacktrace.h @@ -1,44 +1,44 @@ -/** - * @file llstacktrace.h - * @brief stack trace functions - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - - -#ifndef LL_LLSTACKTRACE_H -#define LL_LLSTACKTRACE_H - -#include "stdtypes.h" -#include <vector> -#include <string> - -bool ll_get_stack_trace(std::vector<std::string>& lines); - -#endif - +/**
+ * @file llstacktrace.h
+ * @brief stack trace functions
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+
+#ifndef LL_LLSTACKTRACE_H
+#define LL_LLSTACKTRACE_H
+
+#include "stdtypes.h"
+#include <vector>
+#include <string>
+
+LL_COMMON_API bool ll_get_stack_trace(std::vector<std::string>& lines);
+
+#endif
+
diff --git a/indra/llcommon/llstat.h b/indra/llcommon/llstat.h index bad18f46a0..64ea8e5b40 100644 --- a/indra/llcommon/llstat.h +++ b/indra/llcommon/llstat.h @@ -52,7 +52,7 @@ class LLSD; // amounts of time with very low memory cost. // -class LLStatAccum +class LL_COMMON_API LLStatAccum { protected: LLStatAccum(bool use_frame_timer); @@ -116,7 +116,7 @@ public: F64 mLastSampleValue; }; -class LLStatMeasure : public LLStatAccum +class LL_COMMON_API LLStatMeasure : public LLStatAccum // gathers statistics about things that are measured // ex.: tempature, time dilation { @@ -131,7 +131,7 @@ public: }; -class LLStatRate : public LLStatAccum +class LL_COMMON_API LLStatRate : public LLStatAccum // gathers statistics about things that can be counted over time // ex.: LSL instructions executed, messages sent, simulator frames completed // renders it in terms of rate of thing per second @@ -147,7 +147,7 @@ public: }; -class LLStatTime : public LLStatAccum +class LL_COMMON_API LLStatTime : public LLStatAccum // gathers statistics about time spent in a block of code // measure average duration per second in the block { @@ -178,7 +178,7 @@ private: // Use this class on the stack to record statistics about an area of code -class LLPerfBlock +class LL_COMMON_API LLPerfBlock { public: struct StatEntry @@ -220,7 +220,7 @@ private: // ---------------------------------------------------------------------------- -class LLPerfStats +class LL_COMMON_API LLPerfStats { public: LLPerfStats(const std::string& process_name = "unknown", S32 process_pid = 0); @@ -256,7 +256,7 @@ private: }; // ---------------------------------------------------------------------------- -class LLStat +class LL_COMMON_API LLStat { private: typedef std::multimap<std::string, LLStat*> stat_map_t; diff --git a/indra/llcommon/llstreamtools.h b/indra/llcommon/llstreamtools.h index a6dc4d51e2..f64e761409 100644 --- a/indra/llcommon/llstreamtools.h +++ b/indra/llcommon/llstreamtools.h @@ -39,23 +39,23 @@ // unless specifed otherwise these all return input_stream.good() // skips spaces and tabs -bool skip_whitespace(std::istream& input_stream); +LL_COMMON_API bool skip_whitespace(std::istream& input_stream); // skips whitespace and newlines -bool skip_emptyspace(std::istream& input_stream); +LL_COMMON_API bool skip_emptyspace(std::istream& input_stream); // skips emptyspace and lines that start with a # -bool skip_comments_and_emptyspace(std::istream& input_stream); +LL_COMMON_API bool skip_comments_and_emptyspace(std::istream& input_stream); // skips to character after next newline -bool skip_line(std::istream& input_stream); +LL_COMMON_API bool skip_line(std::istream& input_stream); // skips to beginning of next non-emptyspace -bool skip_to_next_word(std::istream& input_stream); +LL_COMMON_API bool skip_to_next_word(std::istream& input_stream); // skips to character after the end of next keyword // a 'keyword' is defined as the first word on a line -bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream); +LL_COMMON_API bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream); // skip_to_start_of_next_keyword() is disabled -- might tickle corruption bug // in windows iostream @@ -65,14 +65,14 @@ bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream // characters are pulled out of input_stream and appended to output_string // returns result of input_stream.good() after characters are pulled -bool get_word(std::string& output_string, std::istream& input_stream); -bool get_line(std::string& output_string, std::istream& input_stream); +LL_COMMON_API bool get_word(std::string& output_string, std::istream& input_stream); +LL_COMMON_API bool get_line(std::string& output_string, std::istream& input_stream); // characters are pulled out of input_stream (up to a max of 'n') // and appended to output_string // returns result of input_stream.good() after characters are pulled -bool get_word(std::string& output_string, std::istream& input_stream, int n); -bool get_line(std::string& output_string, std::istream& input_stream, int n); +LL_COMMON_API bool get_word(std::string& output_string, std::istream& input_stream, int n); +LL_COMMON_API bool get_line(std::string& output_string, std::istream& input_stream, int n); // unget_line() is disabled -- might tickle corruption bug in windows iostream //// backs up the input_stream by line_size + 1 characters @@ -82,28 +82,28 @@ bool get_line(std::string& output_string, std::istream& input_stream, int n); // removes the last char in 'line' if it matches 'c' // returns true if removed last char -bool remove_last_char(char c, std::string& line); +LL_COMMON_API bool remove_last_char(char c, std::string& line); // replaces escaped characters with the correct characters from left to right // "\\" ---> '\\' // "\n" ---> '\n' -void unescape_string(std::string& line); +LL_COMMON_API void unescape_string(std::string& line); // replaces unescaped characters with expanded equivalents from left to right // '\\' ---> "\\" // '\n' ---> "\n" -void escape_string(std::string& line); +LL_COMMON_API void escape_string(std::string& line); // replaces each '\n' character with ' ' -void replace_newlines_with_whitespace(std::string& line); +LL_COMMON_API void replace_newlines_with_whitespace(std::string& line); // erases any double-quote characters in line -void remove_double_quotes(std::string& line); +LL_COMMON_API void remove_double_quotes(std::string& line); // the 'keyword' is defined as the first word on a line // the 'value' is everything after the keyword on the same line // starting at the first non-whitespace and ending right before the newline -void get_keyword_and_value(std::string& keyword, +LL_COMMON_API void get_keyword_and_value(std::string& keyword, std::string& value, const std::string& line); @@ -111,13 +111,13 @@ void get_keyword_and_value(std::string& keyword, // read anymore or until we hit the count. Some istream // implimentations have a max that they will read. // Returns the number of bytes read. -std::streamsize fullread( +LL_COMMON_API std::streamsize fullread( std::istream& istr, char* buf, std::streamsize requested); -std::istream& operator>>(std::istream& str, const char *tocheck); +LL_COMMON_API std::istream& operator>>(std::istream& str, const char *tocheck); #endif diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index a15d6a9a14..f5c6b297b9 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -1,1565 +1,1572 @@ -/** - * @file llstring.h - * @brief String utility functions and std::string class. - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#ifndef LL_LLSTRING_H -#define LL_LLSTRING_H - -#include <string> -#include <locale> -#include <iomanip> -#include <boost/regex.hpp> -#include "llsd.h" -#include "llfasttimer.h" - -#if LL_LINUX || LL_SOLARIS -#include <wctype.h> -#include <wchar.h> -#endif - -#include <string.h> - -#if LL_SOLARIS -// stricmp and strnicmp do not exist on Solaris: -#define stricmp strcasecmp -#define strnicmp strncasecmp -#endif - -const char LL_UNKNOWN_CHAR = '?'; - -#if LL_DARWIN || LL_LINUX || LL_SOLARIS -// Template specialization of char_traits for U16s. Only necessary on Mac and Linux (exists on Windows already) -#include <cstring> - -namespace std -{ -template<> -struct char_traits<U16> -{ - typedef U16 char_type; - typedef int int_type; - typedef streampos pos_type; - typedef streamoff off_type; - typedef mbstate_t state_type; - - static void - assign(char_type& __c1, const char_type& __c2) - { __c1 = __c2; } - - static bool - eq(const char_type& __c1, const char_type& __c2) - { return __c1 == __c2; } - - static bool - lt(const char_type& __c1, const char_type& __c2) - { return __c1 < __c2; } - - static int - compare(const char_type* __s1, const char_type* __s2, size_t __n) - { return memcmp(__s1, __s2, __n * sizeof(char_type)); } - - static size_t - length(const char_type* __s) - { - const char_type *cur_char = __s; - while (*cur_char != 0) - { - ++cur_char; - } - return cur_char - __s; - } - - static const char_type* - find(const char_type* __s, size_t __n, const char_type& __a) - { return static_cast<const char_type*>(memchr(__s, __a, __n * sizeof(char_type))); } - - static char_type* - move(char_type* __s1, const char_type* __s2, size_t __n) - { return static_cast<char_type*>(memmove(__s1, __s2, __n * sizeof(char_type))); } - - static char_type* - copy(char_type* __s1, const char_type* __s2, size_t __n) - { return static_cast<char_type*>(memcpy(__s1, __s2, __n * sizeof(char_type))); } /* Flawfinder: ignore */ - - static char_type* - assign(char_type* __s, size_t __n, char_type __a) - { - // This isn't right. - //return static_cast<char_type*>(memset(__s, __a, __n * sizeof(char_type))); - - // I don't think there's a standard 'memset' for 16-bit values. - // Do this the old-fashioned way. - - size_t __i; - for(__i = 0; __i < __n; __i++) - { - __s[__i] = __a; - } - return __s; - } - - static char_type - to_char_type(const int_type& __c) - { return static_cast<char_type>(__c); } - - static int_type - to_int_type(const char_type& __c) - { return static_cast<int_type>(__c); } - - static bool - eq_int_type(const int_type& __c1, const int_type& __c2) - { return __c1 == __c2; } - - static int_type - eof() { return static_cast<int_type>(EOF); } - - static int_type - not_eof(const int_type& __c) - { return (__c == eof()) ? 0 : __c; } - }; -}; -#endif - -class LLStringOps -{ -private: - static long sltOffset; - static long localTimeOffset; - static bool daylightSavings; - static std::map<std::string, std::string> datetimeToCodes; - -public: - static char toUpper(char elem) { return toupper((unsigned char)elem); } - static llwchar toUpper(llwchar elem) { return towupper(elem); } - - static char toLower(char elem) { return tolower((unsigned char)elem); } - static llwchar toLower(llwchar elem) { return towlower(elem); } - - static bool isSpace(char elem) { return isspace((unsigned char)elem) != 0; } - static bool isSpace(llwchar elem) { return iswspace(elem) != 0; } - - static bool isUpper(char elem) { return isupper((unsigned char)elem) != 0; } - static bool isUpper(llwchar elem) { return iswupper(elem) != 0; } - - static bool isLower(char elem) { return islower((unsigned char)elem) != 0; } - static bool isLower(llwchar elem) { return iswlower(elem) != 0; } - - static bool isDigit(char a) { return isdigit((unsigned char)a) != 0; } - static bool isDigit(llwchar a) { return iswdigit(a) != 0; } - - static bool isPunct(char a) { return ispunct((unsigned char)a) != 0; } - static bool isPunct(llwchar a) { return iswpunct(a) != 0; } - - static bool isAlnum(char a) { return isalnum((unsigned char)a) != 0; } - static bool isAlnum(llwchar a) { return iswalnum(a) != 0; } - - static S32 collate(const char* a, const char* b) { return strcoll(a, b); } - static S32 collate(const llwchar* a, const llwchar* b); - - static void setupDatetimeInfo (bool daylight); - static long getSltOffset (void) {return sltOffset;} - static long getLocalTimeOffset (void) {return localTimeOffset;} - static bool getDaylightSavings (void) {return daylightSavings;} - static std::string getDatetimeCode (std::string key); -}; - -/** - * @brief Return a string constructed from in without crashing if the - * pointer is NULL. - */ -std::string ll_safe_string(const char* in); -std::string ll_safe_string(const char* in, S32 maxlen); - - -// Allowing assignments from non-strings into format_map_t is apparently -// *really* error-prone, so subclass std::string with just basic c'tors. -class LLFormatMapString -{ -public: - LLFormatMapString() {}; - LLFormatMapString(const char* s) : mString(ll_safe_string(s)) {}; - LLFormatMapString(const std::string& s) : mString(s) {}; - operator std::string() const { return mString; } - bool operator<(const LLFormatMapString& rhs) const { return mString < rhs.mString; } - std::size_t length() const { return mString.length(); } - -private: - std::string mString; -}; - -template <class T> -class LLStringUtilBase -{ -private: - static std::string sLocale; - -public: - typedef typename std::basic_string<T>::size_type size_type; - -public: - ///////////////////////////////////////////////////////////////////////////////////////// - // Static Utility functions that operate on std::strings - - static std::basic_string<T> null; - - typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t; - static void getTokens (std::basic_string<T> input, std::vector<std::basic_string<T> >& tokens); - static void formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals); - static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, S32 secFromEpoch); - static S32 format(std::basic_string<T>& s, const format_map_t& substitutions); - static S32 format(std::basic_string<T>& s, const LLSD& substitutions); - static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const format_map_t& substitutions); - static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const LLSD& substitutions); - static void setLocale (std::string inLocale) {sLocale = inLocale;}; - static std::string getLocale (void) {return sLocale;}; - - static bool isValidIndex(const std::basic_string<T>& string, size_type i) - { - return !string.empty() && (0 <= i) && (i <= string.size()); - } - - static void trimHead(std::basic_string<T>& string); - static void trimTail(std::basic_string<T>& string); - static void trim(std::basic_string<T>& string) { trimHead(string); trimTail(string); } - static void truncate(std::basic_string<T>& string, size_type count); - - static void toUpper(std::basic_string<T>& string); - static void toLower(std::basic_string<T>& string); - - // True if this is the head of s. - static BOOL isHead( const std::basic_string<T>& string, const T* s ); - - /** - * @brief Returns true if string starts with substr - * - * If etither string or substr are empty, this method returns false. - */ - static bool startsWith( - const std::basic_string<T>& string, - const std::basic_string<T>& substr); - - /** - * @brief Returns true if string ends in substr - * - * If etither string or substr are empty, this method returns false. - */ - static bool endsWith( - const std::basic_string<T>& string, - const std::basic_string<T>& substr); - - static void addCRLF(std::basic_string<T>& string); - static void removeCRLF(std::basic_string<T>& string); - - static void replaceTabsWithSpaces( std::basic_string<T>& string, size_type spaces_per_tab ); - static void replaceNonstandardASCII( std::basic_string<T>& string, T replacement ); - static void replaceChar( std::basic_string<T>& string, T target, T replacement ); - - static BOOL containsNonprintable(const std::basic_string<T>& string); - static void stripNonprintable(std::basic_string<T>& string); - - /** - * @brief Unsafe way to make ascii characters. You should probably - * only call this when interacting with the host operating system. - * The 1 byte std::string does not work correctly. - * The 2 and 4 byte std::string probably work, so LLWStringUtil::_makeASCII - * should work. - */ - static void _makeASCII(std::basic_string<T>& string); - - // Conversion to other data types - static BOOL convertToBOOL(const std::basic_string<T>& string, BOOL& value); - static BOOL convertToU8(const std::basic_string<T>& string, U8& value); - static BOOL convertToS8(const std::basic_string<T>& string, S8& value); - static BOOL convertToS16(const std::basic_string<T>& string, S16& value); - static BOOL convertToU16(const std::basic_string<T>& string, U16& value); - static BOOL convertToU32(const std::basic_string<T>& string, U32& value); - static BOOL convertToS32(const std::basic_string<T>& string, S32& value); - static BOOL convertToF32(const std::basic_string<T>& string, F32& value); - static BOOL convertToF64(const std::basic_string<T>& string, F64& value); - - ///////////////////////////////////////////////////////////////////////////////////////// - // Utility functions for working with char*'s and strings - - // Like strcmp but also handles empty strings. Uses - // current locale. - static S32 compareStrings(const T* lhs, const T* rhs); - static S32 compareStrings(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs); - - // case insensitive version of above. Uses current locale on - // Win32, and falls back to a non-locale aware comparison on - // Linux. - static S32 compareInsensitive(const T* lhs, const T* rhs); - static S32 compareInsensitive(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs); - - // Case sensitive comparison with good handling of numbers. Does not use current locale. - // a.k.a. strdictcmp() - static S32 compareDict(const std::basic_string<T>& a, const std::basic_string<T>& b); - - // Case *in*sensitive comparison with good handling of numbers. Does not use current locale. - // a.k.a. strdictcmp() - static S32 compareDictInsensitive(const std::basic_string<T>& a, const std::basic_string<T>& b); - - // Puts compareDict() in a form appropriate for LL container classes to use for sorting. - static BOOL precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b ); - - // A replacement for strncpy. - // If the dst buffer is dst_size bytes long or more, ensures that dst is null terminated and holds - // up to dst_size-1 characters of src. - static void copy(T* dst, const T* src, size_type dst_size); - - // Copies src into dst at a given offset. - static void copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset); - - static bool isPartOfWord(T c) { return (c == (T)'_') || LLStringOps::isAlnum(c); } - - -#ifdef _DEBUG - static void testHarness(); -#endif - -}; - -template<class T> std::basic_string<T> LLStringUtilBase<T>::null; -template<class T> std::string LLStringUtilBase<T>::sLocale; - -typedef LLStringUtilBase<char> LLStringUtil; -typedef LLStringUtilBase<llwchar> LLWStringUtil; -typedef std::basic_string<llwchar> LLWString; - -//@ Use this where we want to disallow input in the form of "foo" -// This is used to catch places where english text is embedded in the code -// instead of in a translatable XUI file. -class LLStringExplicit : public std::string -{ -public: - explicit LLStringExplicit(const char* s) : std::string(s) {} - LLStringExplicit(const std::string& s) : std::string(s) {} - LLStringExplicit(const std::string& s, size_type pos, size_type n = std::string::npos) : std::string(s, pos, n) {} -}; - -struct LLDictionaryLess -{ -public: - bool operator()(const std::string& a, const std::string& b) - { - return (LLStringUtil::precedesDict(a, b) ? true : false); - } -}; - - -/** - * Simple support functions - */ - -/** - * @brief chop off the trailing characters in a string. - * - * This function works on bytes rather than glyphs, so this will - * incorrectly truncate non-single byte strings. - * Use utf8str_truncate() for utf8 strings - * @return a copy of in string minus the trailing count bytes. - */ -inline std::string chop_tail_copy( - const std::string& in, - std::string::size_type count) -{ - return std::string(in, 0, in.length() - count); -} - -/** - * @brief This translates a nybble stored as a hex value from 0-f back - * to a nybble in the low order bits of the return byte. - */ -U8 hex_as_nybble(char hex); - -/** - * @brief read the contents of a file into a string. - * - * Since this function has no concept of character encoding, most - * anything you do with this method ill-advised. Please avoid. - * @param str [out] The string which will have. - * @param filename The full name of the file to read. - * @return Returns true on success. If false, str is unmodified. - */ -bool _read_file_into_string(std::string& str, const std::string& filename); -bool iswindividual(llwchar elem); - -/** - * Unicode support - */ - -// Make the incoming string a utf8 string. Replaces any unknown glyph -// with the UNKOWN_CHARACTER. Once any unknown glph is found, the rest -// of the data may not be recovered. -std::string rawstr_to_utf8(const std::string& raw); - -// -// We should never use UTF16 except when communicating with Win32! -// -typedef std::basic_string<U16> llutf16string; - -LLWString utf16str_to_wstring(const llutf16string &utf16str, S32 len); -LLWString utf16str_to_wstring(const llutf16string &utf16str); - -llutf16string wstring_to_utf16str(const LLWString &utf32str, S32 len); -llutf16string wstring_to_utf16str(const LLWString &utf32str); - -llutf16string utf8str_to_utf16str ( const std::string& utf8str, S32 len); -llutf16string utf8str_to_utf16str ( const std::string& utf8str ); - -LLWString utf8str_to_wstring(const std::string &utf8str, S32 len); -LLWString utf8str_to_wstring(const std::string &utf8str); -// Same function, better name. JC -inline LLWString utf8string_to_wstring(const std::string& utf8_string) { return utf8str_to_wstring(utf8_string); } - -// -S32 wchar_to_utf8chars(llwchar inchar, char* outchars); - -std::string wstring_to_utf8str(const LLWString &utf32str, S32 len); -std::string wstring_to_utf8str(const LLWString &utf32str); - -std::string utf16str_to_utf8str(const llutf16string &utf16str, S32 len); -std::string utf16str_to_utf8str(const llutf16string &utf16str); - -// Length of this UTF32 string in bytes when transformed to UTF8 -S32 wstring_utf8_length(const LLWString& wstr); - -// Length in bytes of this wide char in a UTF8 string -S32 wchar_utf8_length(const llwchar wc); - -std::string utf8str_tolower(const std::string& utf8str); - -// Length in llwchar (UTF-32) of the first len units (16 bits) of the given UTF-16 string. -S32 utf16str_wstring_length(const llutf16string &utf16str, S32 len); - -// Length in utf16string (UTF-16) of wlen wchars beginning at woffset. -S32 wstring_utf16_length(const LLWString & wstr, S32 woffset, S32 wlen); - -// Length in wstring (i.e., llwchar count) of a part of a wstring specified by utf16 length (i.e., utf16 units.) -S32 wstring_wstring_length_from_utf16_length(const LLWString & wstr, S32 woffset, S32 utf16_length, BOOL *unaligned = NULL); - -/** - * @brief Properly truncate a utf8 string to a maximum byte count. - * - * The returned string may be less than max_len if the truncation - * happens in the middle of a glyph. If max_len is longer than the - * string passed in, the return value == utf8str. - * @param utf8str A valid utf8 string to truncate. - * @param max_len The maximum number of bytes in the return value. - * @return Returns a valid utf8 string with byte count <= max_len. - */ -std::string utf8str_truncate(const std::string& utf8str, const S32 max_len); - -std::string utf8str_trim(const std::string& utf8str); - -S32 utf8str_compare_insensitive( - const std::string& lhs, - const std::string& rhs); - -/** - * @brief Replace all occurences of target_char with replace_char - * - * @param utf8str A utf8 string to process. - * @param target_char The wchar to be replaced - * @param replace_char The wchar which is written on replace - */ -std::string utf8str_substChar( - const std::string& utf8str, - const llwchar target_char, - const llwchar replace_char); - -std::string utf8str_makeASCII(const std::string& utf8str); - -// Hack - used for evil notecards. -std::string mbcsstring_makeASCII(const std::string& str); - -std::string utf8str_removeCRLF(const std::string& utf8str); - - -#if LL_WINDOWS -/* @name Windows string helpers - */ -//@{ - -/** - * @brief Implementation the expected snprintf interface. - * - * If the size of the passed in buffer is not large enough to hold the string, - * two bad things happen: - * 1. resulting formatted string is NOT null terminated - * 2. Depending on the platform, the return value could be a) the required - * size of the buffer to copy the entire formatted string or b) -1. - * On Windows with VS.Net 2003, it returns -1 e.g. - * - * safe_snprintf always adds a NULL terminator so that the caller does not - * need to check for return value or need to add the NULL terminator. - * It does not, however change the return value - to let the caller know - * that the passed in buffer size was not large enough to hold the - * formatted string. - * - */ -int safe_snprintf(char* str, size_t size, const char* format, ...); - -/** - * @brief Convert a wide string to std::string - * - * This replaces the unsafe W2A macro from ATL. - */ -std::string ll_convert_wide_to_string(const wchar_t* in); - -//@} -#endif // LL_WINDOWS - -/** - * Many of the 'strip' and 'replace' methods of LLStringUtilBase need - * specialization to work with the signed char type. - * Sadly, it is not possible (AFAIK) to specialize a single method of - * a template class. - * That stuff should go here. - */ -namespace LLStringFn -{ - /** - * @brief Replace all non-printable characters with replacement in - * string. - * NOTE - this will zap non-ascii - * - * @param [in,out] string the to modify. out value is the string - * with zero non-printable characters. - * @param The replacement character. use LL_UNKNOWN_CHAR if unsure. - */ - void replace_nonprintable_in_ascii( - std::basic_string<char>& string, - char replacement); - - - /** - * @brief Replace all non-printable characters and pipe characters - * with replacement in a string. - * NOTE - this will zap non-ascii - * - * @param [in,out] the string to modify. out value is the string - * with zero non-printable characters and zero pipe characters. - * @param The replacement character. use LL_UNKNOWN_CHAR if unsure. - */ - void replace_nonprintable_and_pipe_in_ascii(std::basic_string<char>& str, - char replacement); - - - /** - * @brief Remove all characters that are not allowed in XML 1.0. - * Returns a copy of the string with those characters removed. - * Works with US ASCII and UTF-8 encoded strings. JC - */ - std::string strip_invalid_xml(const std::string& input); - - - /** - * @brief Replace all control characters (0 <= c < 0x20) with replacement in - * string. This is safe for utf-8 - * - * @param [in,out] string the to modify. out value is the string - * with zero non-printable characters. - * @param The replacement character. use LL_UNKNOWN_CHAR if unsure. - */ - void replace_ascii_controlchars( - std::basic_string<char>& string, - char replacement); -} - -//////////////////////////////////////////////////////////// - -//static -template<class T> -void LLStringUtilBase<T>::getTokens (std::basic_string<T> input, std::vector<std::basic_string<T> >& tokens) -{ - const std::basic_string<T> delims (","); - std::basic_string<T> currToken; - size_type begIdx, endIdx; - - begIdx = input.find_first_not_of (delims); - while (begIdx != std::basic_string<T>::npos) - { - endIdx = input.find_first_of (delims, begIdx); - if (endIdx == std::basic_string<T>::npos) - { - endIdx = input.length(); - } - - currToken = input.substr(begIdx, endIdx - begIdx); - trim (currToken); - tokens.push_back(currToken); - begIdx = input.find_first_not_of (delims, endIdx); - } -} - -extern LLFastTimer::DeclareTimer STRING_LOCALIZATION; - -// static -template<class T> -S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const format_map_t& substitutions) -{ - LLFastTimer ft(STRING_LOCALIZATION); - S32 res = 0; - - std::basic_ostringstream<T> output; - // match strings like [NAME,number,3] - const boost::regex key("\\[((\\s)*([0-9_A-Za-z]+)((\\s)*,(\\s)*[0-9_A-Za-z\\s]*){0,2}(\\s)*)]"); - - - typename std::basic_string<T>::const_iterator start = s.begin(); - typename std::basic_string<T>::const_iterator end = s.end(); - boost::smatch match; - - - while (boost::regex_search(start, end, match, key, boost::match_default)) - { - bool found_replacement = false; - std::vector<std::basic_string<T> > tokens; - std::basic_string<T> replacement; - - getTokens (std::basic_string<T>(match[1].first, match[1].second), tokens); - - if (tokens.size() == 1) - { - found_replacement = simpleReplacement (replacement, tokens[0], substitutions); - } - else if (tokens[1] == "number") - { - std::basic_string<T> param = "0"; - - if (tokens.size() > 2) param = tokens[2]; - found_replacement = simpleReplacement (replacement, tokens[0], substitutions); - if (found_replacement) formatNumber (replacement, param); - } - else if (tokens[1] == "datetime") - { - std::basic_string<T> param; - if (tokens.size() > 2) param = tokens[2]; - - format_map_t::const_iterator iter = substitutions.find("datetime"); - if (iter != substitutions.end()) - { - S32 secFromEpoch = 0; - BOOL r = LLStringUtil::convertToS32(iter->second, secFromEpoch); - if (r) - { - found_replacement = formatDatetime(replacement, tokens[0], param, secFromEpoch); - } - } - } - - if (found_replacement) - { - output << std::basic_string<T>(start, match[0].first) << replacement; - res++; - } - else - { - // we had no replacement, so leave the string we searched for so that it gets noticed by QA - // "hello [NAME_NOT_FOUND]" is output - output << std::basic_string<T>(start, match[0].second); - } - - // update search position - start = match[0].second; - } - // send the remainder of the string (with no further matches for bracketed names) - output << std::basic_string<T>(start, end); - s = output.str(); - return res; -} - -//static -template<class T> -S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const LLSD& substitutions) -{ - LLFastTimer ft(STRING_LOCALIZATION); - - S32 res = 0; - - if (!substitutions.isMap()) - { - return res; - } - - std::basic_ostringstream<T> output; - // match strings like [NAME,number,3] - const boost::regex key("\\[((\\s)*([0-9_A-Za-z]+)((\\s)*,(\\s)*[0-9_A-Za-z\\s]*){0,2}(\\s)*)]"); - - - typename std::basic_string<T>::const_iterator start = s.begin(); - typename std::basic_string<T>::const_iterator end = s.end(); - boost::smatch match; - - - while (boost::regex_search(start, end, match, key, boost::match_default)) - { - bool found_replacement = false; - std::vector<std::basic_string<T> > tokens; - std::basic_string<T> replacement; - - getTokens (std::basic_string<T>(match[1].first, match[1].second), tokens); - - if (tokens.size() == 1) - { - found_replacement = simpleReplacement (replacement, tokens[0], substitutions); - } - else if (tokens[1] == "number") - { - std::basic_string<T> param = "0"; - - if (tokens.size() > 2) param = tokens[2]; - found_replacement = simpleReplacement (replacement, tokens[0], substitutions); - if (found_replacement) formatNumber (replacement, param); - } - else if (tokens[1] == "datetime") - { - std::basic_string<T> param; - if (tokens.size() > 2) param = tokens[2]; - - S32 secFromEpoch = (S32) substitutions["datetime"].asInteger(); - found_replacement = formatDatetime (replacement, tokens[0], param, secFromEpoch); - } - - if (found_replacement) - { - output << std::basic_string<T>(start, match[0].first) << replacement; - res++; - } - else - { - // we had no replacement, so leave the string we searched for so that it gets noticed by QA - // "hello [NAME_NOT_FOUND]" is output - output << std::basic_string<T>(start, match[0].second); - } - - // update search position - start = match[0].second; - } - // send the remainder of the string (with no further matches for bracketed names) - output << std::basic_string<T>(start, end); - s = output.str(); - return res; -} - -// static -template<class T> -bool LLStringUtilBase<T>::simpleReplacement(std::basic_string<T> &replacement, std::basic_string<T> token, const format_map_t& substitutions) -{ - // see if we have a replacement for the bracketed string (without the brackets) - // test first using has() because if we just look up with operator[] we get back an - // empty string even if the value is missing. We want to distinguish between - // missing replacements and deliberately empty replacement strings. - format_map_t::const_iterator iter = substitutions.find(token); - if (iter != substitutions.end()) - { - replacement = iter->second; - return true; - } - // if not, see if there's one WITH brackets - iter = substitutions.find(std::basic_string<T>("[" + token + "]")); - if (iter != substitutions.end()) - { - replacement = iter->second; - return true; - } - - return false; -} - -// static -template<class T> -bool LLStringUtilBase<T>::simpleReplacement(std::basic_string<T> &replacement, std::basic_string<T> token, const LLSD& substitutions) -{ - // see if we have a replacement for the bracketed string (without the brackets) - // test first using has() because if we just look up with operator[] we get back an - // empty string even if the value is missing. We want to distinguish between - // missing replacements and deliberately empty replacement strings. - if (substitutions.has(token)) - { - replacement = substitutions[token].asString(); - return true; - } - // if not, see if there's one WITH brackets - else if (substitutions.has(std::basic_string<T>("[" + token + "]"))) - { - replacement = substitutions[std::basic_string<T>("[" + token + "]")].asString(); - return true; - } - - return false; -} - -// static -template<class T> -void LLStringUtilBase<T>::formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals) -{ - typedef typename std::basic_string<T>::size_type string_size_type_t; - std::basic_stringstream<T> strStream; - S32 intDecimals = 0; - - convertToS32 (decimals, intDecimals); - if (!sLocale.empty()) - { - strStream.imbue (std::locale(sLocale.c_str())); - } - - if (!intDecimals) - { - S32 intStr; - - if (convertToS32(numStr, intStr)) - { - strStream << intStr; - numStr = strStream.str(); - } - } - else - { - F32 floatStr; - - if (convertToF32(numStr, floatStr)) - { - strStream << std::fixed << std::showpoint << std::setprecision(intDecimals) << floatStr; - numStr = strStream.str(); - } - } -} - -// static -template<class T> -bool LLStringUtilBase<T>::formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, - std::basic_string<T> param, S32 secFromEpoch) -{ - if (param == "local") // local - { - secFromEpoch -= LLStringOps::getLocalTimeOffset(); - } - else if (param != "utc") // slt - { - secFromEpoch -= LLStringOps::getSltOffset(); - } - - // if never fell into those two ifs above, param must be utc - if (secFromEpoch < 0) secFromEpoch = 0; - - LLDate * datetime = new LLDate((F64)secFromEpoch); - std::string code = LLStringOps::getDatetimeCode (token); - - // special case to handle timezone - if (code == "%Z") { - if (param == "utc") replacement = "GMT"; - else if (param != "local") replacement = LLStringOps::getDaylightSavings()? "PDT" : "PST"; - return true; - } - - replacement = datetime->toHTTPDateString(code); - if (code.empty()) - { - return false; - } - else - { - return true; - } -} - -// static -template<class T> -S32 LLStringUtilBase<T>::compareStrings(const T* lhs, const T* rhs) -{ - S32 result; - if( lhs == rhs ) - { - result = 0; - } - else - if ( !lhs || !lhs[0] ) - { - result = ((!rhs || !rhs[0]) ? 0 : 1); - } - else - if ( !rhs || !rhs[0]) - { - result = -1; - } - else - { - result = LLStringOps::collate(lhs, rhs); - } - return result; -} - -//static -template<class T> -S32 LLStringUtilBase<T>::compareStrings(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs) -{ - return LLStringOps::collate(lhs.c_str(), rhs.c_str()); -} - -// static -template<class T> -S32 LLStringUtilBase<T>::compareInsensitive(const T* lhs, const T* rhs ) -{ - S32 result; - if( lhs == rhs ) - { - result = 0; - } - else - if ( !lhs || !lhs[0] ) - { - result = ((!rhs || !rhs[0]) ? 0 : 1); - } - else - if ( !rhs || !rhs[0] ) - { - result = -1; - } - else - { - std::basic_string<T> lhs_string(lhs); - std::basic_string<T> rhs_string(rhs); - LLStringUtilBase<T>::toUpper(lhs_string); - LLStringUtilBase<T>::toUpper(rhs_string); - result = LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str()); - } - return result; -} - -//static -template<class T> -S32 LLStringUtilBase<T>::compareInsensitive(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs) -{ - std::basic_string<T> lhs_string(lhs); - std::basic_string<T> rhs_string(rhs); - LLStringUtilBase<T>::toUpper(lhs_string); - LLStringUtilBase<T>::toUpper(rhs_string); - return LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str()); -} - -// Case sensitive comparison with good handling of numbers. Does not use current locale. -// a.k.a. strdictcmp() - -//static -template<class T> -S32 LLStringUtilBase<T>::compareDict(const std::basic_string<T>& astr, const std::basic_string<T>& bstr) -{ - const T* a = astr.c_str(); - const T* b = bstr.c_str(); - T ca, cb; - S32 ai, bi, cnt = 0; - S32 bias = 0; - - ca = *(a++); - cb = *(b++); - while( ca && cb ){ - if( bias==0 ){ - if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); bias--; } - if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); bias++; } - }else{ - if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); } - if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); } - } - if( LLStringOps::isDigit(ca) ){ - if( cnt-->0 ){ - if( cb!=ca ) break; - }else{ - if( !LLStringOps::isDigit(cb) ) break; - for(ai=0; LLStringOps::isDigit(a[ai]); ai++); - for(bi=0; LLStringOps::isDigit(b[bi]); bi++); - if( ai<bi ){ ca=0; break; } - if( bi<ai ){ cb=0; break; } - if( ca!=cb ) break; - cnt = ai; - } - }else if( ca!=cb ){ break; - } - ca = *(a++); - cb = *(b++); - } - if( ca==cb ) ca += bias; - return ca-cb; -} - -// static -template<class T> -S32 LLStringUtilBase<T>::compareDictInsensitive(const std::basic_string<T>& astr, const std::basic_string<T>& bstr) -{ - const T* a = astr.c_str(); - const T* b = bstr.c_str(); - T ca, cb; - S32 ai, bi, cnt = 0; - - ca = *(a++); - cb = *(b++); - while( ca && cb ){ - if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); } - if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); } - if( LLStringOps::isDigit(ca) ){ - if( cnt-->0 ){ - if( cb!=ca ) break; - }else{ - if( !LLStringOps::isDigit(cb) ) break; - for(ai=0; LLStringOps::isDigit(a[ai]); ai++); - for(bi=0; LLStringOps::isDigit(b[bi]); bi++); - if( ai<bi ){ ca=0; break; } - if( bi<ai ){ cb=0; break; } - if( ca!=cb ) break; - cnt = ai; - } - }else if( ca!=cb ){ break; - } - ca = *(a++); - cb = *(b++); - } - return ca-cb; -} - -// Puts compareDict() in a form appropriate for LL container classes to use for sorting. -// static -template<class T> -BOOL LLStringUtilBase<T>::precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b ) -{ - if( a.size() && b.size() ) - { - return (LLStringUtilBase<T>::compareDict(a.c_str(), b.c_str()) < 0); - } - else - { - return (!b.empty()); - } -} - -//static -template<class T> -void LLStringUtilBase<T>::toUpper(std::basic_string<T>& string) -{ - if( !string.empty() ) - { - std::transform( - string.begin(), - string.end(), - string.begin(), - (T(*)(T)) &LLStringOps::toUpper); - } -} - -//static -template<class T> -void LLStringUtilBase<T>::toLower(std::basic_string<T>& string) -{ - if( !string.empty() ) - { - std::transform( - string.begin(), - string.end(), - string.begin(), - (T(*)(T)) &LLStringOps::toLower); - } -} - -//static -template<class T> -void LLStringUtilBase<T>::trimHead(std::basic_string<T>& string) -{ - if( !string.empty() ) - { - size_type i = 0; - while( i < string.length() && LLStringOps::isSpace( string[i] ) ) - { - i++; - } - string.erase(0, i); - } -} - -//static -template<class T> -void LLStringUtilBase<T>::trimTail(std::basic_string<T>& string) -{ - if( string.size() ) - { - size_type len = string.length(); - size_type i = len; - while( i > 0 && LLStringOps::isSpace( string[i-1] ) ) - { - i--; - } - - string.erase( i, len - i ); - } -} - - -// Replace line feeds with carriage return-line feed pairs. -//static -template<class T> -void LLStringUtilBase<T>::addCRLF(std::basic_string<T>& string) -{ - const T LF = 10; - const T CR = 13; - - // Count the number of line feeds - size_type count = 0; - size_type len = string.size(); - size_type i; - for( i = 0; i < len; i++ ) - { - if( string[i] == LF ) - { - count++; - } - } - - // Insert a carriage return before each line feed - if( count ) - { - size_type size = len + count; - T *t = new T[size]; - size_type j = 0; - for( i = 0; i < len; ++i ) - { - if( string[i] == LF ) - { - t[j] = CR; - ++j; - } - t[j] = string[i]; - ++j; - } - - string.assign(t, size); - } -} - -// Remove all carriage returns -//static -template<class T> -void LLStringUtilBase<T>::removeCRLF(std::basic_string<T>& string) -{ - const T CR = 13; - - size_type cr_count = 0; - size_type len = string.size(); - size_type i; - for( i = 0; i < len - cr_count; i++ ) - { - if( string[i+cr_count] == CR ) - { - cr_count++; - } - - string[i] = string[i+cr_count]; - } - string.erase(i, cr_count); -} - -//static -template<class T> -void LLStringUtilBase<T>::replaceChar( std::basic_string<T>& string, T target, T replacement ) -{ - size_type found_pos = 0; - for (found_pos = string.find(target, found_pos); - found_pos != std::basic_string<T>::npos; - found_pos = string.find(target, found_pos)) - { - string[found_pos] = replacement; - } -} - -//static -template<class T> -void LLStringUtilBase<T>::replaceNonstandardASCII( std::basic_string<T>& string, T replacement ) -{ - const char LF = 10; - const S8 MIN = 32; -// const S8 MAX = 127; - - size_type len = string.size(); - for( size_type i = 0; i < len; i++ ) - { - // No need to test MAX < mText[i] because we treat mText[i] as a signed char, - // which has a max value of 127. - if( ( S8(string[i]) < MIN ) && (string[i] != LF) ) - { - string[i] = replacement; - } - } -} - -//static -template<class T> -void LLStringUtilBase<T>::replaceTabsWithSpaces( std::basic_string<T>& str, size_type spaces_per_tab ) -{ - const T TAB = '\t'; - const T SPACE = ' '; - - std::basic_string<T> out_str; - // Replace tabs with spaces - for (size_type i = 0; i < str.length(); i++) - { - if (str[i] == TAB) - { - for (size_type j = 0; j < spaces_per_tab; j++) - out_str += SPACE; - } - else - { - out_str += str[i]; - } - } - str = out_str; -} - -//static -template<class T> -BOOL LLStringUtilBase<T>::containsNonprintable(const std::basic_string<T>& string) -{ - const char MIN = 32; - BOOL rv = FALSE; - for (size_type i = 0; i < string.size(); i++) - { - if(string[i] < MIN) - { - rv = TRUE; - break; - } - } - return rv; -} - -//static -template<class T> -void LLStringUtilBase<T>::stripNonprintable(std::basic_string<T>& string) -{ - const char MIN = 32; - size_type j = 0; - if (string.empty()) - { - return; - } - char* c_string = new char[string.size() + 1]; - if(c_string == NULL) - { - return; - } - strcpy(c_string, string.c_str()); /*Flawfinder: ignore*/ - char* write_head = &c_string[0]; - for (size_type i = 0; i < string.size(); i++) - { - char* read_head = &string[i]; - write_head = &c_string[j]; - if(!(*read_head < MIN)) - { - *write_head = *read_head; - ++j; - } - } - c_string[j]= '\0'; - string = c_string; - delete []c_string; -} - -template<class T> -void LLStringUtilBase<T>::_makeASCII(std::basic_string<T>& string) -{ - // Replace non-ASCII chars with LL_UNKNOWN_CHAR - for (size_type i = 0; i < string.length(); i++) - { - if (string[i] > 0x7f) - { - string[i] = LL_UNKNOWN_CHAR; - } - } -} - -// static -template<class T> -void LLStringUtilBase<T>::copy( T* dst, const T* src, size_type dst_size ) -{ - if( dst_size > 0 ) - { - size_type min_len = 0; - if( src ) - { - min_len = llmin( dst_size - 1, strlen( src ) ); /* Flawfinder: ignore */ - memcpy(dst, src, min_len * sizeof(T)); /* Flawfinder: ignore */ - } - dst[min_len] = '\0'; - } -} - -// static -template<class T> -void LLStringUtilBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset) -{ - if ( offset == dst.length() ) - { - // special case - append to end of string and avoid expensive - // (when strings are large) string manipulations - dst += src; - } - else - { - std::basic_string<T> tail = dst.substr(offset); - - dst = dst.substr(0, offset); - dst += src; - dst += tail; - }; -} - -// True if this is the head of s. -//static -template<class T> -BOOL LLStringUtilBase<T>::isHead( const std::basic_string<T>& string, const T* s ) -{ - if( string.empty() ) - { - // Early exit - return FALSE; - } - else - { - return (strncmp( s, string.c_str(), string.size() ) == 0); - } -} - -// static -template<class T> -bool LLStringUtilBase<T>::startsWith( - const std::basic_string<T>& string, - const std::basic_string<T>& substr) -{ - if(string.empty() || (substr.empty())) return false; - if(0 == string.find(substr)) return true; - return false; -} - -// static -template<class T> -bool LLStringUtilBase<T>::endsWith( - const std::basic_string<T>& string, - const std::basic_string<T>& substr) -{ - if(string.empty() || (substr.empty())) return false; - std::string::size_type idx = string.rfind(substr); - if(std::string::npos == idx) return false; - return (idx == (string.size() - substr.size())); -} - - -template<class T> -BOOL LLStringUtilBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL& value) -{ - if( string.empty() ) - { - return FALSE; - } - - std::basic_string<T> temp( string ); - trim(temp); - if( - (temp == "1") || - (temp == "T") || - (temp == "t") || - (temp == "TRUE") || - (temp == "true") || - (temp == "True") ) - { - value = TRUE; - return TRUE; - } - else - if( - (temp == "0") || - (temp == "F") || - (temp == "f") || - (temp == "FALSE") || - (temp == "false") || - (temp == "False") ) - { - value = FALSE; - return TRUE; - } - - return FALSE; -} - -template<class T> -BOOL LLStringUtilBase<T>::convertToU8(const std::basic_string<T>& string, U8& value) -{ - S32 value32 = 0; - BOOL success = convertToS32(string, value32); - if( success && (U8_MIN <= value32) && (value32 <= U8_MAX) ) - { - value = (U8) value32; - return TRUE; - } - return FALSE; -} - -template<class T> -BOOL LLStringUtilBase<T>::convertToS8(const std::basic_string<T>& string, S8& value) -{ - S32 value32 = 0; - BOOL success = convertToS32(string, value32); - if( success && (S8_MIN <= value32) && (value32 <= S8_MAX) ) - { - value = (S8) value32; - return TRUE; - } - return FALSE; -} - -template<class T> -BOOL LLStringUtilBase<T>::convertToS16(const std::basic_string<T>& string, S16& value) -{ - S32 value32 = 0; - BOOL success = convertToS32(string, value32); - if( success && (S16_MIN <= value32) && (value32 <= S16_MAX) ) - { - value = (S16) value32; - return TRUE; - } - return FALSE; -} - -template<class T> -BOOL LLStringUtilBase<T>::convertToU16(const std::basic_string<T>& string, U16& value) -{ - S32 value32 = 0; - BOOL success = convertToS32(string, value32); - if( success && (U16_MIN <= value32) && (value32 <= U16_MAX) ) - { - value = (U16) value32; - return TRUE; - } - return FALSE; -} - -template<class T> -BOOL LLStringUtilBase<T>::convertToU32(const std::basic_string<T>& string, U32& value) -{ - if( string.empty() ) - { - return FALSE; - } - - std::basic_string<T> temp( string ); - trim(temp); - U32 v; - std::basic_istringstream<T> i_stream((std::basic_string<T>)temp); - if(i_stream >> v) - { - value = v; - return TRUE; - } - return FALSE; -} - -template<class T> -BOOL LLStringUtilBase<T>::convertToS32(const std::basic_string<T>& string, S32& value) -{ - if( string.empty() ) - { - return FALSE; - } - - std::basic_string<T> temp( string ); - trim(temp); - S32 v; - std::basic_istringstream<T> i_stream((std::basic_string<T>)temp); - if(i_stream >> v) - { - //TODO: figure out overflow and underflow reporting here - //if((LONG_MAX == v) || (LONG_MIN == v)) - //{ - // // Underflow or overflow - // return FALSE; - //} - - value = v; - return TRUE; - } - return FALSE; -} - -template<class T> -BOOL LLStringUtilBase<T>::convertToF32(const std::basic_string<T>& string, F32& value) -{ - F64 value64 = 0.0; - BOOL success = convertToF64(string, value64); - if( success && (-F32_MAX <= value64) && (value64 <= F32_MAX) ) - { - value = (F32) value64; - return TRUE; - } - return FALSE; -} - -template<class T> -BOOL LLStringUtilBase<T>::convertToF64(const std::basic_string<T>& string, F64& value) -{ - if( string.empty() ) - { - return FALSE; - } - - std::basic_string<T> temp( string ); - trim(temp); - F64 v; - std::basic_istringstream<T> i_stream((std::basic_string<T>)temp); - if(i_stream >> v) - { - //TODO: figure out overflow and underflow reporting here - //if( ((-HUGE_VAL == v) || (HUGE_VAL == v))) ) - //{ - // // Underflow or overflow - // return FALSE; - //} - - value = v; - return TRUE; - } - return FALSE; -} - -template<class T> -void LLStringUtilBase<T>::truncate(std::basic_string<T>& string, size_type count) -{ - size_type cur_size = string.size(); - string.resize(count < cur_size ? count : cur_size); -} - -#endif // LL_STRING_H +/**
+ * @file llstring.h
+ * @brief String utility functions and std::string class.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSTRING_H
+#define LL_LLSTRING_H
+
+#include <string>
+#include <locale>
+#include <iomanip>
+#include <boost/regex.hpp>
+#include "llsd.h"
+#include "llfasttimer.h"
+
+#if LL_LINUX || LL_SOLARIS
+#include <wctype.h>
+#include <wchar.h>
+#endif
+
+#include <string.h>
+
+#if LL_SOLARIS
+// stricmp and strnicmp do not exist on Solaris:
+#define stricmp strcasecmp
+#define strnicmp strncasecmp
+#endif
+
+const char LL_UNKNOWN_CHAR = '?';
+
+#if LL_DARWIN || LL_LINUX || LL_SOLARIS
+// Template specialization of char_traits for U16s. Only necessary on Mac and Linux (exists on Windows already)
+#include <cstring>
+
+namespace std
+{
+template<>
+struct char_traits<U16>
+{
+ typedef U16 char_type;
+ typedef int int_type;
+ typedef streampos pos_type;
+ typedef streamoff off_type;
+ typedef mbstate_t state_type;
+
+ static void
+ assign(char_type& __c1, const char_type& __c2)
+ { __c1 = __c2; }
+
+ static bool
+ eq(const char_type& __c1, const char_type& __c2)
+ { return __c1 == __c2; }
+
+ static bool
+ lt(const char_type& __c1, const char_type& __c2)
+ { return __c1 < __c2; }
+
+ static int
+ compare(const char_type* __s1, const char_type* __s2, size_t __n)
+ { return memcmp(__s1, __s2, __n * sizeof(char_type)); }
+
+ static size_t
+ length(const char_type* __s)
+ {
+ const char_type *cur_char = __s;
+ while (*cur_char != 0)
+ {
+ ++cur_char;
+ }
+ return cur_char - __s;
+ }
+
+ static const char_type*
+ find(const char_type* __s, size_t __n, const char_type& __a)
+ { return static_cast<const char_type*>(memchr(__s, __a, __n * sizeof(char_type))); }
+
+ static char_type*
+ move(char_type* __s1, const char_type* __s2, size_t __n)
+ { return static_cast<char_type*>(memmove(__s1, __s2, __n * sizeof(char_type))); }
+
+ static char_type*
+ copy(char_type* __s1, const char_type* __s2, size_t __n)
+ { return static_cast<char_type*>(memcpy(__s1, __s2, __n * sizeof(char_type))); } /* Flawfinder: ignore */
+
+ static char_type*
+ assign(char_type* __s, size_t __n, char_type __a)
+ {
+ // This isn't right.
+ //return static_cast<char_type*>(memset(__s, __a, __n * sizeof(char_type)));
+
+ // I don't think there's a standard 'memset' for 16-bit values.
+ // Do this the old-fashioned way.
+
+ size_t __i;
+ for(__i = 0; __i < __n; __i++)
+ {
+ __s[__i] = __a;
+ }
+ return __s;
+ }
+
+ static char_type
+ to_char_type(const int_type& __c)
+ { return static_cast<char_type>(__c); }
+
+ static int_type
+ to_int_type(const char_type& __c)
+ { return static_cast<int_type>(__c); }
+
+ static bool
+ eq_int_type(const int_type& __c1, const int_type& __c2)
+ { return __c1 == __c2; }
+
+ static int_type
+ eof() { return static_cast<int_type>(EOF); }
+
+ static int_type
+ not_eof(const int_type& __c)
+ { return (__c == eof()) ? 0 : __c; }
+ };
+};
+#endif
+
+class LL_COMMON_API LLStringOps
+{
+private:
+ static long sltOffset;
+ static long localTimeOffset;
+ static bool daylightSavings;
+ static std::map<std::string, std::string> datetimeToCodes;
+
+public:
+ static char toUpper(char elem) { return toupper((unsigned char)elem); }
+ static llwchar toUpper(llwchar elem) { return towupper(elem); }
+
+ static char toLower(char elem) { return tolower((unsigned char)elem); }
+ static llwchar toLower(llwchar elem) { return towlower(elem); }
+
+ static bool isSpace(char elem) { return isspace((unsigned char)elem) != 0; }
+ static bool isSpace(llwchar elem) { return iswspace(elem) != 0; }
+
+ static bool isUpper(char elem) { return isupper((unsigned char)elem) != 0; }
+ static bool isUpper(llwchar elem) { return iswupper(elem) != 0; }
+
+ static bool isLower(char elem) { return islower((unsigned char)elem) != 0; }
+ static bool isLower(llwchar elem) { return iswlower(elem) != 0; }
+
+ static bool isDigit(char a) { return isdigit((unsigned char)a) != 0; }
+ static bool isDigit(llwchar a) { return iswdigit(a) != 0; }
+
+ static bool isPunct(char a) { return ispunct((unsigned char)a) != 0; }
+ static bool isPunct(llwchar a) { return iswpunct(a) != 0; }
+
+ static bool isAlnum(char a) { return isalnum((unsigned char)a) != 0; }
+ static bool isAlnum(llwchar a) { return iswalnum(a) != 0; }
+
+ static S32 collate(const char* a, const char* b) { return strcoll(a, b); }
+ static S32 collate(const llwchar* a, const llwchar* b);
+
+ static void setupDatetimeInfo (bool daylight);
+ static long getSltOffset (void) {return sltOffset;}
+ static long getLocalTimeOffset (void) {return localTimeOffset;}
+ static bool getDaylightSavings (void) {return daylightSavings;}
+ static std::string getDatetimeCode (std::string key);
+};
+
+/**
+ * @brief Return a string constructed from in without crashing if the
+ * pointer is NULL.
+ */
+LL_COMMON_API std::string ll_safe_string(const char* in);
+LL_COMMON_API std::string ll_safe_string(const char* in, S32 maxlen);
+
+
+// Allowing assignments from non-strings into format_map_t is apparently
+// *really* error-prone, so subclass std::string with just basic c'tors.
+class LLFormatMapString
+{
+public:
+ LLFormatMapString() {};
+ LLFormatMapString(const char* s) : mString(ll_safe_string(s)) {};
+ LLFormatMapString(const std::string& s) : mString(s) {};
+ operator std::string() const { return mString; }
+ bool operator<(const LLFormatMapString& rhs) const { return mString < rhs.mString; }
+ std::size_t length() const { return mString.length(); }
+
+private:
+ std::string mString;
+};
+
+template <class T>
+class LLStringUtilBase
+{
+private:
+ static std::string sLocale;
+
+public:
+ typedef typename std::basic_string<T>::size_type size_type;
+
+public:
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // Static Utility functions that operate on std::strings
+
+ static std::basic_string<T> null;
+
+ typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t;
+ static void getTokens (std::basic_string<T> input, std::vector<std::basic_string<T> >& tokens);
+ static void formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals);
+ static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, S32 secFromEpoch);
+ static S32 format(std::basic_string<T>& s, const format_map_t& substitutions);
+ static S32 format(std::basic_string<T>& s, const LLSD& substitutions);
+ static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const format_map_t& substitutions);
+ static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const LLSD& substitutions);
+ static void setLocale (std::string inLocale) {sLocale = inLocale;};
+ static std::string getLocale (void) {return sLocale;};
+
+ static bool isValidIndex(const std::basic_string<T>& string, size_type i)
+ {
+ return !string.empty() && (0 <= i) && (i <= string.size());
+ }
+
+ static void trimHead(std::basic_string<T>& string);
+ static void trimTail(std::basic_string<T>& string);
+ static void trim(std::basic_string<T>& string) { trimHead(string); trimTail(string); }
+ static void truncate(std::basic_string<T>& string, size_type count);
+
+ static void toUpper(std::basic_string<T>& string);
+ static void toLower(std::basic_string<T>& string);
+
+ // True if this is the head of s.
+ static BOOL isHead( const std::basic_string<T>& string, const T* s );
+
+ /**
+ * @brief Returns true if string starts with substr
+ *
+ * If etither string or substr are empty, this method returns false.
+ */
+ static bool startsWith(
+ const std::basic_string<T>& string,
+ const std::basic_string<T>& substr);
+
+ /**
+ * @brief Returns true if string ends in substr
+ *
+ * If etither string or substr are empty, this method returns false.
+ */
+ static bool endsWith(
+ const std::basic_string<T>& string,
+ const std::basic_string<T>& substr);
+
+ static void addCRLF(std::basic_string<T>& string);
+ static void removeCRLF(std::basic_string<T>& string);
+
+ static void replaceTabsWithSpaces( std::basic_string<T>& string, size_type spaces_per_tab );
+ static void replaceNonstandardASCII( std::basic_string<T>& string, T replacement );
+ static void replaceChar( std::basic_string<T>& string, T target, T replacement );
+
+ static BOOL containsNonprintable(const std::basic_string<T>& string);
+ static void stripNonprintable(std::basic_string<T>& string);
+
+ /**
+ * @brief Unsafe way to make ascii characters. You should probably
+ * only call this when interacting with the host operating system.
+ * The 1 byte std::string does not work correctly.
+ * The 2 and 4 byte std::string probably work, so LLWStringUtil::_makeASCII
+ * should work.
+ */
+ static void _makeASCII(std::basic_string<T>& string);
+
+ // Conversion to other data types
+ static BOOL convertToBOOL(const std::basic_string<T>& string, BOOL& value);
+ static BOOL convertToU8(const std::basic_string<T>& string, U8& value);
+ static BOOL convertToS8(const std::basic_string<T>& string, S8& value);
+ static BOOL convertToS16(const std::basic_string<T>& string, S16& value);
+ static BOOL convertToU16(const std::basic_string<T>& string, U16& value);
+ static BOOL convertToU32(const std::basic_string<T>& string, U32& value);
+ static BOOL convertToS32(const std::basic_string<T>& string, S32& value);
+ static BOOL convertToF32(const std::basic_string<T>& string, F32& value);
+ static BOOL convertToF64(const std::basic_string<T>& string, F64& value);
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // Utility functions for working with char*'s and strings
+
+ // Like strcmp but also handles empty strings. Uses
+ // current locale.
+ static S32 compareStrings(const T* lhs, const T* rhs);
+ static S32 compareStrings(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs);
+
+ // case insensitive version of above. Uses current locale on
+ // Win32, and falls back to a non-locale aware comparison on
+ // Linux.
+ static S32 compareInsensitive(const T* lhs, const T* rhs);
+ static S32 compareInsensitive(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs);
+
+ // Case sensitive comparison with good handling of numbers. Does not use current locale.
+ // a.k.a. strdictcmp()
+ static S32 compareDict(const std::basic_string<T>& a, const std::basic_string<T>& b);
+
+ // Case *in*sensitive comparison with good handling of numbers. Does not use current locale.
+ // a.k.a. strdictcmp()
+ static S32 compareDictInsensitive(const std::basic_string<T>& a, const std::basic_string<T>& b);
+
+ // Puts compareDict() in a form appropriate for LL container classes to use for sorting.
+ static BOOL precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b );
+
+ // A replacement for strncpy.
+ // If the dst buffer is dst_size bytes long or more, ensures that dst is null terminated and holds
+ // up to dst_size-1 characters of src.
+ static void copy(T* dst, const T* src, size_type dst_size);
+
+ // Copies src into dst at a given offset.
+ static void copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset);
+
+ static bool isPartOfWord(T c) { return (c == (T)'_') || LLStringOps::isAlnum(c); }
+
+
+#ifdef _DEBUG
+ static void testHarness();
+#endif
+
+};
+
+template<class T> std::basic_string<T> LLStringUtilBase<T>::null;
+template<class T> std::string LLStringUtilBase<T>::sLocale;
+
+typedef LLStringUtilBase<char> LLStringUtil;
+typedef LLStringUtilBase<llwchar> LLWStringUtil;
+typedef std::basic_string<llwchar> LLWString;
+
+//@ Use this where we want to disallow input in the form of "foo"
+// This is used to catch places where english text is embedded in the code
+// instead of in a translatable XUI file.
+class LLStringExplicit : public std::string
+{
+public:
+ explicit LLStringExplicit(const char* s) : std::string(s) {}
+ LLStringExplicit(const std::string& s) : std::string(s) {}
+ LLStringExplicit(const std::string& s, size_type pos, size_type n = std::string::npos) : std::string(s, pos, n) {}
+};
+
+struct LLDictionaryLess
+{
+public:
+ bool operator()(const std::string& a, const std::string& b)
+ {
+ return (LLStringUtil::precedesDict(a, b) ? true : false);
+ }
+};
+
+
+/**
+ * Simple support functions
+ */
+
+/**
+ * @brief chop off the trailing characters in a string.
+ *
+ * This function works on bytes rather than glyphs, so this will
+ * incorrectly truncate non-single byte strings.
+ * Use utf8str_truncate() for utf8 strings
+ * @return a copy of in string minus the trailing count bytes.
+ */
+inline std::string chop_tail_copy(
+ const std::string& in,
+ std::string::size_type count)
+{
+ return std::string(in, 0, in.length() - count);
+}
+
+/**
+ * @brief This translates a nybble stored as a hex value from 0-f back
+ * to a nybble in the low order bits of the return byte.
+ */
+LL_COMMON_API U8 hex_as_nybble(char hex);
+
+/**
+ * @brief read the contents of a file into a string.
+ *
+ * Since this function has no concept of character encoding, most
+ * anything you do with this method ill-advised. Please avoid.
+ * @param str [out] The string which will have.
+ * @param filename The full name of the file to read.
+ * @return Returns true on success. If false, str is unmodified.
+ */
+LL_COMMON_API bool _read_file_into_string(std::string& str, const std::string& filename);
+LL_COMMON_API bool iswindividual(llwchar elem);
+
+/**
+ * Unicode support
+ */
+
+// Make the incoming string a utf8 string. Replaces any unknown glyph
+// with the UNKOWN_CHARACTER. Once any unknown glph is found, the rest
+// of the data may not be recovered.
+LL_COMMON_API std::string rawstr_to_utf8(const std::string& raw);
+
+//
+// We should never use UTF16 except when communicating with Win32!
+//
+typedef std::basic_string<U16> llutf16string;
+
+LL_COMMON_API LLWString utf16str_to_wstring(const llutf16string &utf16str, S32 len);
+LL_COMMON_API LLWString utf16str_to_wstring(const llutf16string &utf16str);
+
+LL_COMMON_API llutf16string wstring_to_utf16str(const LLWString &utf32str, S32 len);
+LL_COMMON_API llutf16string wstring_to_utf16str(const LLWString &utf32str);
+
+LL_COMMON_API llutf16string utf8str_to_utf16str ( const std::string& utf8str, S32 len);
+LL_COMMON_API llutf16string utf8str_to_utf16str ( const std::string& utf8str );
+
+LL_COMMON_API LLWString utf8str_to_wstring(const std::string &utf8str, S32 len);
+LL_COMMON_API LLWString utf8str_to_wstring(const std::string &utf8str);
+// Same function, better name. JC
+inline LLWString utf8string_to_wstring(const std::string& utf8_string) { return utf8str_to_wstring(utf8_string); }
+
+//
+LL_COMMON_API S32 wchar_to_utf8chars(llwchar inchar, char* outchars);
+
+LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str, S32 len);
+LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str);
+
+LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str, S32 len);
+LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str);
+
+// Length of this UTF32 string in bytes when transformed to UTF8
+LL_COMMON_API S32 wstring_utf8_length(const LLWString& wstr);
+
+// Length in bytes of this wide char in a UTF8 string
+LL_COMMON_API S32 wchar_utf8_length(const llwchar wc);
+
+LL_COMMON_API std::string utf8str_tolower(const std::string& utf8str);
+
+// Length in llwchar (UTF-32) of the first len units (16 bits) of the given UTF-16 string.
+LL_COMMON_API S32 utf16str_wstring_length(const llutf16string &utf16str, S32 len);
+
+// Length in utf16string (UTF-16) of wlen wchars beginning at woffset.
+LL_COMMON_API S32 wstring_utf16_length(const LLWString & wstr, S32 woffset, S32 wlen);
+
+// Length in wstring (i.e., llwchar count) of a part of a wstring specified by utf16 length (i.e., utf16 units.)
+LL_COMMON_API S32 wstring_wstring_length_from_utf16_length(const LLWString & wstr, S32 woffset, S32 utf16_length, BOOL *unaligned = NULL);
+
+/**
+ * @brief Properly truncate a utf8 string to a maximum byte count.
+ *
+ * The returned string may be less than max_len if the truncation
+ * happens in the middle of a glyph. If max_len is longer than the
+ * string passed in, the return value == utf8str.
+ * @param utf8str A valid utf8 string to truncate.
+ * @param max_len The maximum number of bytes in the return value.
+ * @return Returns a valid utf8 string with byte count <= max_len.
+ */
+LL_COMMON_API std::string utf8str_truncate(const std::string& utf8str, const S32 max_len);
+
+LL_COMMON_API std::string utf8str_trim(const std::string& utf8str);
+
+LL_COMMON_API S32 utf8str_compare_insensitive(
+ const std::string& lhs,
+ const std::string& rhs);
+
+/**
+ * @brief Replace all occurences of target_char with replace_char
+ *
+ * @param utf8str A utf8 string to process.
+ * @param target_char The wchar to be replaced
+ * @param replace_char The wchar which is written on replace
+ */
+LL_COMMON_API std::string utf8str_substChar(
+ const std::string& utf8str,
+ const llwchar target_char,
+ const llwchar replace_char);
+
+LL_COMMON_API std::string utf8str_makeASCII(const std::string& utf8str);
+
+// Hack - used for evil notecards.
+LL_COMMON_API std::string mbcsstring_makeASCII(const std::string& str);
+
+LL_COMMON_API std::string utf8str_removeCRLF(const std::string& utf8str);
+
+
+#if LL_WINDOWS
+/* @name Windows string helpers
+ */
+//@{
+
+/**
+ * @brief Implementation the expected snprintf interface.
+ *
+ * If the size of the passed in buffer is not large enough to hold the string,
+ * two bad things happen:
+ * 1. resulting formatted string is NOT null terminated
+ * 2. Depending on the platform, the return value could be a) the required
+ * size of the buffer to copy the entire formatted string or b) -1.
+ * On Windows with VS.Net 2003, it returns -1 e.g.
+ *
+ * safe_snprintf always adds a NULL terminator so that the caller does not
+ * need to check for return value or need to add the NULL terminator.
+ * It does not, however change the return value - to let the caller know
+ * that the passed in buffer size was not large enough to hold the
+ * formatted string.
+ *
+ */
+
+// Deal with the differeneces on Windows
+namespace snprintf_hack
+{
+ LL_COMMON_API int snprintf(char *str, size_t size, const char *format, ...);
+}
+
+using snprintf_hack::snprintf;
+
+/**
+ * @brief Convert a wide string to std::string
+ *
+ * This replaces the unsafe W2A macro from ATL.
+ */
+LL_COMMON_API std::string ll_convert_wide_to_string(const wchar_t* in);
+
+//@}
+#endif // LL_WINDOWS
+
+/**
+ * Many of the 'strip' and 'replace' methods of LLStringUtilBase need
+ * specialization to work with the signed char type.
+ * Sadly, it is not possible (AFAIK) to specialize a single method of
+ * a template class.
+ * That stuff should go here.
+ */
+namespace LLStringFn
+{
+ /**
+ * @brief Replace all non-printable characters with replacement in
+ * string.
+ * NOTE - this will zap non-ascii
+ *
+ * @param [in,out] string the to modify. out value is the string
+ * with zero non-printable characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ LL_COMMON_API void replace_nonprintable_in_ascii(
+ std::basic_string<char>& string,
+ char replacement);
+
+
+ /**
+ * @brief Replace all non-printable characters and pipe characters
+ * with replacement in a string.
+ * NOTE - this will zap non-ascii
+ *
+ * @param [in,out] the string to modify. out value is the string
+ * with zero non-printable characters and zero pipe characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ LL_COMMON_API void replace_nonprintable_and_pipe_in_ascii(std::basic_string<char>& str,
+ char replacement);
+
+
+ /**
+ * @brief Remove all characters that are not allowed in XML 1.0.
+ * Returns a copy of the string with those characters removed.
+ * Works with US ASCII and UTF-8 encoded strings. JC
+ */
+ LL_COMMON_API std::string strip_invalid_xml(const std::string& input);
+
+
+ /**
+ * @brief Replace all control characters (0 <= c < 0x20) with replacement in
+ * string. This is safe for utf-8
+ *
+ * @param [in,out] string the to modify. out value is the string
+ * with zero non-printable characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ LL_COMMON_API void replace_ascii_controlchars(
+ std::basic_string<char>& string,
+ char replacement);
+}
+
+////////////////////////////////////////////////////////////
+
+//static
+template<class T>
+void LLStringUtilBase<T>::getTokens (std::basic_string<T> input, std::vector<std::basic_string<T> >& tokens)
+{
+ const std::basic_string<T> delims (",");
+ std::basic_string<T> currToken;
+ size_type begIdx, endIdx;
+
+ begIdx = input.find_first_not_of (delims);
+ while (begIdx != std::basic_string<T>::npos)
+ {
+ endIdx = input.find_first_of (delims, begIdx);
+ if (endIdx == std::basic_string<T>::npos)
+ {
+ endIdx = input.length();
+ }
+
+ currToken = input.substr(begIdx, endIdx - begIdx);
+ trim (currToken);
+ tokens.push_back(currToken);
+ begIdx = input.find_first_not_of (delims, endIdx);
+ }
+}
+
+extern LLFastTimer::DeclareTimer STRING_LOCALIZATION;
+
+// static
+template<class T>
+S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const format_map_t& substitutions)
+{
+ LLFastTimer ft(STRING_LOCALIZATION);
+ S32 res = 0;
+
+ std::basic_ostringstream<T> output;
+ // match strings like [NAME,number,3]
+ const boost::regex key("\\[((\\s)*([0-9_A-Za-z]+)((\\s)*,(\\s)*[0-9_A-Za-z\\s]*){0,2}(\\s)*)]");
+
+
+ typename std::basic_string<T>::const_iterator start = s.begin();
+ typename std::basic_string<T>::const_iterator end = s.end();
+ boost::smatch match;
+
+
+ while (boost::regex_search(start, end, match, key, boost::match_default))
+ {
+ bool found_replacement = false;
+ std::vector<std::basic_string<T> > tokens;
+ std::basic_string<T> replacement;
+
+ getTokens (std::basic_string<T>(match[1].first, match[1].second), tokens);
+
+ if (tokens.size() == 1)
+ {
+ found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
+ }
+ else if (tokens[1] == "number")
+ {
+ std::basic_string<T> param = "0";
+
+ if (tokens.size() > 2) param = tokens[2];
+ found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
+ if (found_replacement) formatNumber (replacement, param);
+ }
+ else if (tokens[1] == "datetime")
+ {
+ std::basic_string<T> param;
+ if (tokens.size() > 2) param = tokens[2];
+
+ format_map_t::const_iterator iter = substitutions.find("datetime");
+ if (iter != substitutions.end())
+ {
+ S32 secFromEpoch = 0;
+ BOOL r = LLStringUtil::convertToS32(iter->second, secFromEpoch);
+ if (r)
+ {
+ found_replacement = formatDatetime(replacement, tokens[0], param, secFromEpoch);
+ }
+ }
+ }
+
+ if (found_replacement)
+ {
+ output << std::basic_string<T>(start, match[0].first) << replacement;
+ res++;
+ }
+ else
+ {
+ // we had no replacement, so leave the string we searched for so that it gets noticed by QA
+ // "hello [NAME_NOT_FOUND]" is output
+ output << std::basic_string<T>(start, match[0].second);
+ }
+
+ // update search position
+ start = match[0].second;
+ }
+ // send the remainder of the string (with no further matches for bracketed names)
+ output << std::basic_string<T>(start, end);
+ s = output.str();
+ return res;
+}
+
+//static
+template<class T>
+S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const LLSD& substitutions)
+{
+ LLFastTimer ft(STRING_LOCALIZATION);
+
+ S32 res = 0;
+
+ if (!substitutions.isMap())
+ {
+ return res;
+ }
+
+ std::basic_ostringstream<T> output;
+ // match strings like [NAME,number,3]
+ const boost::regex key("\\[((\\s)*([0-9_A-Za-z]+)((\\s)*,(\\s)*[0-9_A-Za-z\\s]*){0,2}(\\s)*)]");
+
+
+ typename std::basic_string<T>::const_iterator start = s.begin();
+ typename std::basic_string<T>::const_iterator end = s.end();
+ boost::smatch match;
+
+
+ while (boost::regex_search(start, end, match, key, boost::match_default))
+ {
+ bool found_replacement = false;
+ std::vector<std::basic_string<T> > tokens;
+ std::basic_string<T> replacement;
+
+ getTokens (std::basic_string<T>(match[1].first, match[1].second), tokens);
+
+ if (tokens.size() == 1)
+ {
+ found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
+ }
+ else if (tokens[1] == "number")
+ {
+ std::basic_string<T> param = "0";
+
+ if (tokens.size() > 2) param = tokens[2];
+ found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
+ if (found_replacement) formatNumber (replacement, param);
+ }
+ else if (tokens[1] == "datetime")
+ {
+ std::basic_string<T> param;
+ if (tokens.size() > 2) param = tokens[2];
+
+ S32 secFromEpoch = (S32) substitutions["datetime"].asInteger();
+ found_replacement = formatDatetime (replacement, tokens[0], param, secFromEpoch);
+ }
+
+ if (found_replacement)
+ {
+ output << std::basic_string<T>(start, match[0].first) << replacement;
+ res++;
+ }
+ else
+ {
+ // we had no replacement, so leave the string we searched for so that it gets noticed by QA
+ // "hello [NAME_NOT_FOUND]" is output
+ output << std::basic_string<T>(start, match[0].second);
+ }
+
+ // update search position
+ start = match[0].second;
+ }
+ // send the remainder of the string (with no further matches for bracketed names)
+ output << std::basic_string<T>(start, end);
+ s = output.str();
+ return res;
+}
+
+// static
+template<class T>
+bool LLStringUtilBase<T>::simpleReplacement(std::basic_string<T> &replacement, std::basic_string<T> token, const format_map_t& substitutions)
+{
+ // see if we have a replacement for the bracketed string (without the brackets)
+ // test first using has() because if we just look up with operator[] we get back an
+ // empty string even if the value is missing. We want to distinguish between
+ // missing replacements and deliberately empty replacement strings.
+ format_map_t::const_iterator iter = substitutions.find(token);
+ if (iter != substitutions.end())
+ {
+ replacement = iter->second;
+ return true;
+ }
+ // if not, see if there's one WITH brackets
+ iter = substitutions.find(std::basic_string<T>("[" + token + "]"));
+ if (iter != substitutions.end())
+ {
+ replacement = iter->second;
+ return true;
+ }
+
+ return false;
+}
+
+// static
+template<class T>
+bool LLStringUtilBase<T>::simpleReplacement(std::basic_string<T> &replacement, std::basic_string<T> token, const LLSD& substitutions)
+{
+ // see if we have a replacement for the bracketed string (without the brackets)
+ // test first using has() because if we just look up with operator[] we get back an
+ // empty string even if the value is missing. We want to distinguish between
+ // missing replacements and deliberately empty replacement strings.
+ if (substitutions.has(token))
+ {
+ replacement = substitutions[token].asString();
+ return true;
+ }
+ // if not, see if there's one WITH brackets
+ else if (substitutions.has(std::basic_string<T>("[" + token + "]")))
+ {
+ replacement = substitutions[std::basic_string<T>("[" + token + "]")].asString();
+ return true;
+ }
+
+ return false;
+}
+
+// static
+template<class T>
+void LLStringUtilBase<T>::formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals)
+{
+ typedef typename std::basic_string<T>::size_type string_size_type_t;
+ std::basic_stringstream<T> strStream;
+ S32 intDecimals = 0;
+
+ convertToS32 (decimals, intDecimals);
+ if (!sLocale.empty())
+ {
+ strStream.imbue (std::locale(sLocale.c_str()));
+ }
+
+ if (!intDecimals)
+ {
+ S32 intStr;
+
+ if (convertToS32(numStr, intStr))
+ {
+ strStream << intStr;
+ numStr = strStream.str();
+ }
+ }
+ else
+ {
+ F32 floatStr;
+
+ if (convertToF32(numStr, floatStr))
+ {
+ strStream << std::fixed << std::showpoint << std::setprecision(intDecimals) << floatStr;
+ numStr = strStream.str();
+ }
+ }
+}
+
+// static
+template<class T>
+bool LLStringUtilBase<T>::formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token,
+ std::basic_string<T> param, S32 secFromEpoch)
+{
+ if (param == "local") // local
+ {
+ secFromEpoch -= LLStringOps::getLocalTimeOffset();
+ }
+ else if (param != "utc") // slt
+ {
+ secFromEpoch -= LLStringOps::getSltOffset();
+ }
+
+ // if never fell into those two ifs above, param must be utc
+ if (secFromEpoch < 0) secFromEpoch = 0;
+
+ LLDate * datetime = new LLDate((F64)secFromEpoch);
+ std::string code = LLStringOps::getDatetimeCode (token);
+
+ // special case to handle timezone
+ if (code == "%Z") {
+ if (param == "utc") replacement = "GMT";
+ else if (param != "local") replacement = LLStringOps::getDaylightSavings()? "PDT" : "PST";
+ return true;
+ }
+
+ replacement = datetime->toHTTPDateString(code);
+ if (code.empty())
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+// static
+template<class T>
+S32 LLStringUtilBase<T>::compareStrings(const T* lhs, const T* rhs)
+{
+ S32 result;
+ if( lhs == rhs )
+ {
+ result = 0;
+ }
+ else
+ if ( !lhs || !lhs[0] )
+ {
+ result = ((!rhs || !rhs[0]) ? 0 : 1);
+ }
+ else
+ if ( !rhs || !rhs[0])
+ {
+ result = -1;
+ }
+ else
+ {
+ result = LLStringOps::collate(lhs, rhs);
+ }
+ return result;
+}
+
+//static
+template<class T>
+S32 LLStringUtilBase<T>::compareStrings(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs)
+{
+ return LLStringOps::collate(lhs.c_str(), rhs.c_str());
+}
+
+// static
+template<class T>
+S32 LLStringUtilBase<T>::compareInsensitive(const T* lhs, const T* rhs )
+{
+ S32 result;
+ if( lhs == rhs )
+ {
+ result = 0;
+ }
+ else
+ if ( !lhs || !lhs[0] )
+ {
+ result = ((!rhs || !rhs[0]) ? 0 : 1);
+ }
+ else
+ if ( !rhs || !rhs[0] )
+ {
+ result = -1;
+ }
+ else
+ {
+ std::basic_string<T> lhs_string(lhs);
+ std::basic_string<T> rhs_string(rhs);
+ LLStringUtilBase<T>::toUpper(lhs_string);
+ LLStringUtilBase<T>::toUpper(rhs_string);
+ result = LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
+ }
+ return result;
+}
+
+//static
+template<class T>
+S32 LLStringUtilBase<T>::compareInsensitive(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs)
+{
+ std::basic_string<T> lhs_string(lhs);
+ std::basic_string<T> rhs_string(rhs);
+ LLStringUtilBase<T>::toUpper(lhs_string);
+ LLStringUtilBase<T>::toUpper(rhs_string);
+ return LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
+}
+
+// Case sensitive comparison with good handling of numbers. Does not use current locale.
+// a.k.a. strdictcmp()
+
+//static
+template<class T>
+S32 LLStringUtilBase<T>::compareDict(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
+{
+ const T* a = astr.c_str();
+ const T* b = bstr.c_str();
+ T ca, cb;
+ S32 ai, bi, cnt = 0;
+ S32 bias = 0;
+
+ ca = *(a++);
+ cb = *(b++);
+ while( ca && cb ){
+ if( bias==0 ){
+ if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); bias--; }
+ if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); bias++; }
+ }else{
+ if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); }
+ if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); }
+ }
+ if( LLStringOps::isDigit(ca) ){
+ if( cnt-->0 ){
+ if( cb!=ca ) break;
+ }else{
+ if( !LLStringOps::isDigit(cb) ) break;
+ for(ai=0; LLStringOps::isDigit(a[ai]); ai++);
+ for(bi=0; LLStringOps::isDigit(b[bi]); bi++);
+ if( ai<bi ){ ca=0; break; }
+ if( bi<ai ){ cb=0; break; }
+ if( ca!=cb ) break;
+ cnt = ai;
+ }
+ }else if( ca!=cb ){ break;
+ }
+ ca = *(a++);
+ cb = *(b++);
+ }
+ if( ca==cb ) ca += bias;
+ return ca-cb;
+}
+
+// static
+template<class T>
+S32 LLStringUtilBase<T>::compareDictInsensitive(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
+{
+ const T* a = astr.c_str();
+ const T* b = bstr.c_str();
+ T ca, cb;
+ S32 ai, bi, cnt = 0;
+
+ ca = *(a++);
+ cb = *(b++);
+ while( ca && cb ){
+ if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); }
+ if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); }
+ if( LLStringOps::isDigit(ca) ){
+ if( cnt-->0 ){
+ if( cb!=ca ) break;
+ }else{
+ if( !LLStringOps::isDigit(cb) ) break;
+ for(ai=0; LLStringOps::isDigit(a[ai]); ai++);
+ for(bi=0; LLStringOps::isDigit(b[bi]); bi++);
+ if( ai<bi ){ ca=0; break; }
+ if( bi<ai ){ cb=0; break; }
+ if( ca!=cb ) break;
+ cnt = ai;
+ }
+ }else if( ca!=cb ){ break;
+ }
+ ca = *(a++);
+ cb = *(b++);
+ }
+ return ca-cb;
+}
+
+// Puts compareDict() in a form appropriate for LL container classes to use for sorting.
+// static
+template<class T>
+BOOL LLStringUtilBase<T>::precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b )
+{
+ if( a.size() && b.size() )
+ {
+ return (LLStringUtilBase<T>::compareDict(a.c_str(), b.c_str()) < 0);
+ }
+ else
+ {
+ return (!b.empty());
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::toUpper(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ std::transform(
+ string.begin(),
+ string.end(),
+ string.begin(),
+ (T(*)(T)) &LLStringOps::toUpper);
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::toLower(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ std::transform(
+ string.begin(),
+ string.end(),
+ string.begin(),
+ (T(*)(T)) &LLStringOps::toLower);
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::trimHead(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ size_type i = 0;
+ while( i < string.length() && LLStringOps::isSpace( string[i] ) )
+ {
+ i++;
+ }
+ string.erase(0, i);
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::trimTail(std::basic_string<T>& string)
+{
+ if( string.size() )
+ {
+ size_type len = string.length();
+ size_type i = len;
+ while( i > 0 && LLStringOps::isSpace( string[i-1] ) )
+ {
+ i--;
+ }
+
+ string.erase( i, len - i );
+ }
+}
+
+
+// Replace line feeds with carriage return-line feed pairs.
+//static
+template<class T>
+void LLStringUtilBase<T>::addCRLF(std::basic_string<T>& string)
+{
+ const T LF = 10;
+ const T CR = 13;
+
+ // Count the number of line feeds
+ size_type count = 0;
+ size_type len = string.size();
+ size_type i;
+ for( i = 0; i < len; i++ )
+ {
+ if( string[i] == LF )
+ {
+ count++;
+ }
+ }
+
+ // Insert a carriage return before each line feed
+ if( count )
+ {
+ size_type size = len + count;
+ T *t = new T[size];
+ size_type j = 0;
+ for( i = 0; i < len; ++i )
+ {
+ if( string[i] == LF )
+ {
+ t[j] = CR;
+ ++j;
+ }
+ t[j] = string[i];
+ ++j;
+ }
+
+ string.assign(t, size);
+ }
+}
+
+// Remove all carriage returns
+//static
+template<class T>
+void LLStringUtilBase<T>::removeCRLF(std::basic_string<T>& string)
+{
+ const T CR = 13;
+
+ size_type cr_count = 0;
+ size_type len = string.size();
+ size_type i;
+ for( i = 0; i < len - cr_count; i++ )
+ {
+ if( string[i+cr_count] == CR )
+ {
+ cr_count++;
+ }
+
+ string[i] = string[i+cr_count];
+ }
+ string.erase(i, cr_count);
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::replaceChar( std::basic_string<T>& string, T target, T replacement )
+{
+ size_type found_pos = 0;
+ for (found_pos = string.find(target, found_pos);
+ found_pos != std::basic_string<T>::npos;
+ found_pos = string.find(target, found_pos))
+ {
+ string[found_pos] = replacement;
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::replaceNonstandardASCII( std::basic_string<T>& string, T replacement )
+{
+ const char LF = 10;
+ const S8 MIN = 32;
+// const S8 MAX = 127;
+
+ size_type len = string.size();
+ for( size_type i = 0; i < len; i++ )
+ {
+ // No need to test MAX < mText[i] because we treat mText[i] as a signed char,
+ // which has a max value of 127.
+ if( ( S8(string[i]) < MIN ) && (string[i] != LF) )
+ {
+ string[i] = replacement;
+ }
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::replaceTabsWithSpaces( std::basic_string<T>& str, size_type spaces_per_tab )
+{
+ const T TAB = '\t';
+ const T SPACE = ' ';
+
+ std::basic_string<T> out_str;
+ // Replace tabs with spaces
+ for (size_type i = 0; i < str.length(); i++)
+ {
+ if (str[i] == TAB)
+ {
+ for (size_type j = 0; j < spaces_per_tab; j++)
+ out_str += SPACE;
+ }
+ else
+ {
+ out_str += str[i];
+ }
+ }
+ str = out_str;
+}
+
+//static
+template<class T>
+BOOL LLStringUtilBase<T>::containsNonprintable(const std::basic_string<T>& string)
+{
+ const char MIN = 32;
+ BOOL rv = FALSE;
+ for (size_type i = 0; i < string.size(); i++)
+ {
+ if(string[i] < MIN)
+ {
+ rv = TRUE;
+ break;
+ }
+ }
+ return rv;
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::stripNonprintable(std::basic_string<T>& string)
+{
+ const char MIN = 32;
+ size_type j = 0;
+ if (string.empty())
+ {
+ return;
+ }
+ char* c_string = new char[string.size() + 1];
+ if(c_string == NULL)
+ {
+ return;
+ }
+ strcpy(c_string, string.c_str()); /*Flawfinder: ignore*/
+ char* write_head = &c_string[0];
+ for (size_type i = 0; i < string.size(); i++)
+ {
+ char* read_head = &string[i];
+ write_head = &c_string[j];
+ if(!(*read_head < MIN))
+ {
+ *write_head = *read_head;
+ ++j;
+ }
+ }
+ c_string[j]= '\0';
+ string = c_string;
+ delete []c_string;
+}
+
+template<class T>
+void LLStringUtilBase<T>::_makeASCII(std::basic_string<T>& string)
+{
+ // Replace non-ASCII chars with LL_UNKNOWN_CHAR
+ for (size_type i = 0; i < string.length(); i++)
+ {
+ if (string[i] > 0x7f)
+ {
+ string[i] = LL_UNKNOWN_CHAR;
+ }
+ }
+}
+
+// static
+template<class T>
+void LLStringUtilBase<T>::copy( T* dst, const T* src, size_type dst_size )
+{
+ if( dst_size > 0 )
+ {
+ size_type min_len = 0;
+ if( src )
+ {
+ min_len = llmin( dst_size - 1, strlen( src ) ); /* Flawfinder: ignore */
+ memcpy(dst, src, min_len * sizeof(T)); /* Flawfinder: ignore */
+ }
+ dst[min_len] = '\0';
+ }
+}
+
+// static
+template<class T>
+void LLStringUtilBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset)
+{
+ if ( offset == dst.length() )
+ {
+ // special case - append to end of string and avoid expensive
+ // (when strings are large) string manipulations
+ dst += src;
+ }
+ else
+ {
+ std::basic_string<T> tail = dst.substr(offset);
+
+ dst = dst.substr(0, offset);
+ dst += src;
+ dst += tail;
+ };
+}
+
+// True if this is the head of s.
+//static
+template<class T>
+BOOL LLStringUtilBase<T>::isHead( const std::basic_string<T>& string, const T* s )
+{
+ if( string.empty() )
+ {
+ // Early exit
+ return FALSE;
+ }
+ else
+ {
+ return (strncmp( s, string.c_str(), string.size() ) == 0);
+ }
+}
+
+// static
+template<class T>
+bool LLStringUtilBase<T>::startsWith(
+ const std::basic_string<T>& string,
+ const std::basic_string<T>& substr)
+{
+ if(string.empty() || (substr.empty())) return false;
+ if(0 == string.find(substr)) return true;
+ return false;
+}
+
+// static
+template<class T>
+bool LLStringUtilBase<T>::endsWith(
+ const std::basic_string<T>& string,
+ const std::basic_string<T>& substr)
+{
+ if(string.empty() || (substr.empty())) return false;
+ std::string::size_type idx = string.rfind(substr);
+ if(std::string::npos == idx) return false;
+ return (idx == (string.size() - substr.size()));
+}
+
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ std::basic_string<T> temp( string );
+ trim(temp);
+ if(
+ (temp == "1") ||
+ (temp == "T") ||
+ (temp == "t") ||
+ (temp == "TRUE") ||
+ (temp == "true") ||
+ (temp == "True") )
+ {
+ value = TRUE;
+ return TRUE;
+ }
+ else
+ if(
+ (temp == "0") ||
+ (temp == "F") ||
+ (temp == "f") ||
+ (temp == "FALSE") ||
+ (temp == "false") ||
+ (temp == "False") )
+ {
+ value = FALSE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToU8(const std::basic_string<T>& string, U8& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (U8_MIN <= value32) && (value32 <= U8_MAX) )
+ {
+ value = (U8) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToS8(const std::basic_string<T>& string, S8& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (S8_MIN <= value32) && (value32 <= S8_MAX) )
+ {
+ value = (S8) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToS16(const std::basic_string<T>& string, S16& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (S16_MIN <= value32) && (value32 <= S16_MAX) )
+ {
+ value = (S16) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToU16(const std::basic_string<T>& string, U16& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (U16_MIN <= value32) && (value32 <= U16_MAX) )
+ {
+ value = (U16) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToU32(const std::basic_string<T>& string, U32& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ std::basic_string<T> temp( string );
+ trim(temp);
+ U32 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToS32(const std::basic_string<T>& string, S32& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ std::basic_string<T> temp( string );
+ trim(temp);
+ S32 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ //TODO: figure out overflow and underflow reporting here
+ //if((LONG_MAX == v) || (LONG_MIN == v))
+ //{
+ // // Underflow or overflow
+ // return FALSE;
+ //}
+
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToF32(const std::basic_string<T>& string, F32& value)
+{
+ F64 value64 = 0.0;
+ BOOL success = convertToF64(string, value64);
+ if( success && (-F32_MAX <= value64) && (value64 <= F32_MAX) )
+ {
+ value = (F32) value64;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToF64(const std::basic_string<T>& string, F64& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ std::basic_string<T> temp( string );
+ trim(temp);
+ F64 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ //TODO: figure out overflow and underflow reporting here
+ //if( ((-HUGE_VAL == v) || (HUGE_VAL == v))) )
+ //{
+ // // Underflow or overflow
+ // return FALSE;
+ //}
+
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+void LLStringUtilBase<T>::truncate(std::basic_string<T>& string, size_type count)
+{
+ size_type cur_size = string.size();
+ string.resize(count < cur_size ? count : cur_size);
+}
+
+#endif // LL_STRING_H
diff --git a/indra/llcommon/llstringtable.h b/indra/llcommon/llstringtable.h index 4492063275..b13b016396 100644 --- a/indra/llcommon/llstringtable.h +++ b/indra/llcommon/llstringtable.h @@ -56,7 +56,7 @@ const U32 MAX_STRINGS_LENGTH = 256; -class LLStringTableEntry +class LL_COMMON_API LLStringTableEntry { public: LLStringTableEntry(const char *str) @@ -81,7 +81,7 @@ public: S32 mCount; }; -class LLStringTable +class LL_COMMON_API LLStringTable { public: LLStringTable(int tablesize); @@ -115,7 +115,7 @@ public: #endif }; -extern LLStringTable gStringTable; +extern LL_COMMON_API LLStringTable gStringTable; //============================================================================ @@ -125,7 +125,7 @@ extern LLStringTable gStringTable; typedef const std::string* LLStdStringHandle; -class LLStdStringTable +class LL_COMMON_API LLStdStringTable { public: LLStdStringTable(S32 tablesize = 0) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 4d03c4d40d..127baa737d 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -128,8 +128,16 @@ LLOSInfo::LLOSInfo() : mOSStringSimple = "Microsoft Windows Vista "; else mOSStringSimple = "Microsoft Windows Vista Server "; } + else if(osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) + { + if(osvi.wProductType == VER_NT_WORKSTATION) + mOSStringSimple = "Microsoft Windows 7 "; + else mOSStringSimple = "Microsoft Windows 7 Server "; + } else // Use the registry on early versions of Windows NT. { + mOSStringSimple = "Microsoft Windows (unrecognized) "; + HKEY hKey; WCHAR szProductType[80]; DWORD dwBufLen; diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h index 03f48ca018..c2c45bec9a 100644 --- a/indra/llcommon/llsys.h +++ b/indra/llcommon/llsys.h @@ -45,7 +45,7 @@ #include <iosfwd> #include <string> -class LLOSInfo +class LL_COMMON_API LLOSInfo { public: LLOSInfo(); @@ -70,7 +70,7 @@ private: }; -class LLCPUInfo +class LL_COMMON_API LLCPUInfo { public: LLCPUInfo(); @@ -99,7 +99,7 @@ private: // // CLASS LLMemoryInfo -class LLMemoryInfo +class LL_COMMON_API LLMemoryInfo /*! @brief Class to query the memory subsystem @@ -123,15 +123,15 @@ public: }; -std::ostream& operator<<(std::ostream& s, const LLOSInfo& info); -std::ostream& operator<<(std::ostream& s, const LLCPUInfo& info); -std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info); +LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLOSInfo& info); +LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLCPUInfo& info); +LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info); // gunzip srcfile into dstfile. Returns FALSE on error. -BOOL gunzip_file(const std::string& srcfile, const std::string& dstfile); +BOOL LL_COMMON_API gunzip_file(const std::string& srcfile, const std::string& dstfile); // gzip srcfile into dstfile. Returns FALSE on error. -BOOL gzip_file(const std::string& srcfile, const std::string& dstfile); +BOOL LL_COMMON_API gzip_file(const std::string& srcfile, const std::string& dstfile); -extern LLCPUInfo gSysCPU; +extern LL_COMMON_API LLCPUInfo gSysCPU; #endif // LL_LLSYS_H diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index f25339f48d..c3d7650bd9 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -42,7 +42,7 @@ class LLThread; class LLMutex; class LLCondition; -class LLThread +class LL_COMMON_API LLThread { public: typedef enum e_thread_status @@ -130,7 +130,7 @@ protected: //============================================================================ -class LLMutex +class LL_COMMON_API LLMutex { public: LLMutex(apr_pool_t *apr_poolp); // NULL pool constructs a new pool for the mutex @@ -147,7 +147,7 @@ protected: }; // Actually a condition/mutex pair (since each condition needs to be associated with a mutex). -class LLCondition : public LLMutex +class LL_COMMON_API LLCondition : public LLMutex { public: LLCondition(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well. @@ -194,7 +194,7 @@ void LLThread::unlockData() // see llmemory.h for LLPointer<> definition -class LLThreadSafeRefCount +class LL_COMMON_API LLThreadSafeRefCount { public: static void initThreadSafeRefCount(); // creates sMutex @@ -246,7 +246,7 @@ private: // Simple responder for self destructing callbacks // Pure virtual class -class LLResponder : public LLThreadSafeRefCount +class LL_COMMON_API LLResponder : public LLThreadSafeRefCount { protected: virtual ~LLResponder(); diff --git a/indra/llcommon/lltimer.h b/indra/llcommon/lltimer.h index 0319bec45b..d009c0f5f7 100644 --- a/indra/llcommon/lltimer.h +++ b/indra/llcommon/lltimer.h @@ -55,7 +55,7 @@ const U32 USEC_PER_HOUR = USEC_PER_MIN * MIN_PER_HOUR; const U32 SEC_PER_HOUR = SEC_PER_MIN * MIN_PER_HOUR; const F64 SEC_PER_USEC = 1.0 / (F64) USEC_PER_SEC; -class LLTimer +class LL_COMMON_API LLTimer { public: static LLTimer *sTimer; // global timer @@ -114,17 +114,17 @@ public: // // Various functions for initializing/accessing clock and timing stuff. Don't use these without REALLY knowing how they work. // -U64 get_clock_count(); -F64 calc_clock_frequency(U32 msecs); -void update_clock_frequencies(); +LL_COMMON_API U64 get_clock_count(); +LL_COMMON_API F64 calc_clock_frequency(U32 msecs); +LL_COMMON_API void update_clock_frequencies(); // Sleep for milliseconds -void ms_sleep(U32 ms); -U32 micro_sleep(U64 us, U32 max_yields = 0xFFFFFFFF); +LL_COMMON_API void ms_sleep(U32 ms); +LL_COMMON_API U32 micro_sleep(U64 us, U32 max_yields = 0xFFFFFFFF); // Returns the correct UTC time in seconds, like time(NULL). // Useful on the viewer, which may have its local clock set wrong. -time_t time_corrected(); +LL_COMMON_API time_t time_corrected(); static inline time_t time_min() { @@ -155,24 +155,24 @@ static inline time_t time_max() } // Correction factor used by time_corrected() above. -extern S32 gUTCOffset; +extern LL_COMMON_API S32 gUTCOffset; // Is the current computer (in its current time zone) // observing daylight savings time? -BOOL is_daylight_savings(); +LL_COMMON_API BOOL is_daylight_savings(); // Converts internal "struct tm" time buffer to Pacific Standard/Daylight Time // Usage: // S32 utc_time; // utc_time = time_corrected(); // struct tm* internal_time = utc_to_pacific_time(utc_time, gDaylight); -struct tm* utc_to_pacific_time(time_t utc_time, BOOL pacific_daylight_time); +LL_COMMON_API struct tm* utc_to_pacific_time(time_t utc_time, BOOL pacific_daylight_time); -void microsecondsToTimecodeString(U64 current_time, std::string& tcstring); -void secondsToTimecodeString(F32 current_time, std::string& tcstring); +LL_COMMON_API void microsecondsToTimecodeString(U64 current_time, std::string& tcstring); +LL_COMMON_API void secondsToTimecodeString(F32 current_time, std::string& tcstring); // class for scheduling a function to be called at a given frequency (approximate, inprecise) -class LLEventTimer : protected LLInstanceTracker<LLEventTimer> +class LL_COMMON_API LLEventTimer : protected LLInstanceTracker<LLEventTimer> { public: LLEventTimer(F32 period); // period is the amount of time between each call to tick() in seconds @@ -190,4 +190,6 @@ protected: F32 mPeriod; }; +U64 LL_COMMON_API totalTime(); // Returns current system time in microseconds + #endif diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h index 8e46e2e89e..eb5c5203eb 100644 --- a/indra/llcommon/lluri.h +++ b/indra/llcommon/lluri.h @@ -47,7 +47,7 @@ class LLApp; * See: http://www.ietf.org/rfc/rfc3986.txt * */ -class LLURI +class LL_COMMON_API LLURI { public: LLURI(); diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h index 4b32138a06..c78fb12018 100644 --- a/indra/llcommon/lluuid.h +++ b/indra/llcommon/lluuid.h @@ -35,6 +35,7 @@ #include <iostream> #include <set> #include "stdtypes.h" +#include "llpreprocessor.h" const S32 UUID_BYTES = 16; const S32 UUID_WORDS = 4; @@ -47,7 +48,7 @@ struct uuid_time_t { U32 low; }; -class LLUUID +class LL_COMMON_API LLUUID { public: // @@ -106,8 +107,8 @@ public: LLUUID combine(const LLUUID& other) const; void combine(const LLUUID& other, LLUUID& result) const; - friend std::ostream& operator<<(std::ostream& s, const LLUUID &uuid); - friend std::istream& operator>>(std::istream& s, LLUUID &uuid); + friend LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLUUID &uuid); + friend LL_COMMON_API std::istream& operator>>(std::istream& s, LLUUID &uuid); void toString(char *out) const; // Does not allocate memory, needs 36 characters (including \0) void toString(std::string& out) const; @@ -323,7 +324,7 @@ typedef std::set<LLUUID, lluuid_less> uuid_list_t; */ typedef LLUUID LLAssetID; -class LLTransactionID : public LLUUID +class LL_COMMON_API LLTransactionID : public LLUUID { public: LLTransactionID() : LLUUID() { } diff --git a/indra/llcommon/llworkerthread.h b/indra/llcommon/llworkerthread.h index 19407f4463..a12bd52a64 100644 --- a/indra/llcommon/llworkerthread.h +++ b/indra/llcommon/llworkerthread.h @@ -50,7 +50,7 @@ class LLWorkerClass; // Note: ~LLWorkerThread is O(N) N=# of worker threads, assumed to be small // It is assumed that LLWorkerThreads are rarely created/destroyed. -class LLWorkerThread : public LLQueuedThread +class LL_COMMON_API LLWorkerThread : public LLQueuedThread { public: class WorkRequest : public LLQueuedThread::QueuedRequest @@ -113,7 +113,7 @@ public: // Only one background task can be active at a time (per instance). // i.e. don't call addWork() if haveWork() returns true -class LLWorkerClass +class LL_COMMON_API LLWorkerClass { friend class LLWorkerThread; friend class LLWorkerThread::WorkRequest; diff --git a/indra/llcommon/metaclass.h b/indra/llcommon/metaclass.h index cc10f1675f..f38bcd2d57 100644 --- a/indra/llcommon/metaclass.h +++ b/indra/llcommon/metaclass.h @@ -43,7 +43,7 @@ class LLReflective; class LLMetaProperty; class LLMetaMethod; -class LLMetaClass +class LL_COMMON_API LLMetaClass { public: diff --git a/indra/llcommon/metaproperty.h b/indra/llcommon/metaproperty.h index e5ac35907c..6c016c56dd 100644 --- a/indra/llcommon/metaproperty.h +++ b/indra/llcommon/metaproperty.h @@ -41,7 +41,7 @@ class LLMetaClass; class LLReflective; -class LLMetaProperty +class LL_COMMON_API LLMetaProperty { public: LLMetaProperty(const std::string& name, const LLMetaClass& object_class); diff --git a/indra/llcommon/reflective.h b/indra/llcommon/reflective.h index e2c18ebc6d..a13537681d 100644 --- a/indra/llcommon/reflective.h +++ b/indra/llcommon/reflective.h @@ -36,7 +36,7 @@ #define LL_REFLECTIVE_H class LLMetaClass; -class LLReflective +class LL_COMMON_API LLReflective { public: LLReflective(); diff --git a/indra/llcommon/stringize.h b/indra/llcommon/stringize.h index 1b2958020f..6399547f5e 100644 --- a/indra/llcommon/stringize.h +++ b/indra/llcommon/stringize.h @@ -13,6 +13,7 @@ #define LL_STRINGIZE_H #include <sstream> +#include <boost/lambda/lambda.hpp> /** * stringize(item) encapsulates an idiom we use constantly, using @@ -28,6 +29,17 @@ std::string stringize(const T& item) } /** + * stringize_f(functor) + */ +template <typename Functor> +std::string stringize_f(Functor const & f) +{ + std::ostringstream out; + f(out); + return out.str(); +} + +/** * STRINGIZE(item1 << item2 << item3 ...) effectively expands to the * following: * @code @@ -36,40 +48,43 @@ std::string stringize(const T& item) * return out.str(); * @endcode */ -#define STRINGIZE(EXPRESSION) (static_cast<std::ostringstream&>(Stringize() << EXPRESSION).str()) +#define STRINGIZE(EXPRESSION) (stringize_f(boost::lambda::_1 << EXPRESSION)) + /** - * Helper class for STRINGIZE() macro. Ideally the body of - * STRINGIZE(EXPRESSION) would look something like this: + * destringize(str) + * defined for symmetry with stringize + * *NOTE - this has distinct behavior from boost::lexical_cast<T> regarding + * leading/trailing whitespace and handling of bad_lexical_cast exceptions + */ +template <typename T> +T destringize(std::string const & str) +{ + T val; + std::istringstream in(str); + in >> val; + return val; +} + +/** + * destringize_f(str, functor) + */ +template <typename Functor> +void destringize_f(std::string const & str, Functor const & f) +{ + std::istringstream in(str); + f(in); +} + +/** + * DESTRINGIZE(str, item1 >> item2 >> item3 ...) effectively expands to the + * following: * @code - * (std::ostringstream() << EXPRESSION).str() + * std::istringstream in(str); + * in >> item1 >> item2 >> item3 ... ; * @endcode - * That doesn't work because each of the relevant operator<<() functions - * accepts a non-const std::ostream&, to which you can't pass a temp instance - * of std::ostringstream. Stringize plays the necessary const tricks to make - * the whole thing work. */ -class Stringize -{ -public: - /** - * This is the essence of Stringize. The leftmost << operator (the one - * coded in the STRINGIZE() macro) engages this operator<<() const method - * on the temp Stringize instance. Every other << operator (ones embedded - * in EXPRESSION) simply sees the std::ostream& returned by the first one. - * - * Finally, the STRINGIZE() macro downcasts that std::ostream& to - * std::ostringstream&. - */ - template <typename T> - std::ostream& operator<<(const T& item) const - { - mOut << item; - return mOut; - } +#define DESTRINGIZE(STR, EXPRESSION) (destringize_f((STR), (boost::lambda::_1 >> EXPRESSION))) -private: - mutable std::ostringstream mOut; -}; #endif /* ! defined(LL_STRINGIZE_H) */ diff --git a/indra/llcommon/tests/listener.h b/indra/llcommon/tests/listener.h new file mode 100644 index 0000000000..fa12f944ef --- /dev/null +++ b/indra/llcommon/tests/listener.h @@ -0,0 +1,139 @@ +/** + * @file listener.h + * @author Nat Goodspeed + * @date 2009-03-06 + * @brief Useful for tests of the LLEventPump family of classes + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LISTENER_H) +#define LL_LISTENER_H + +#include "llsd.h" +#include <iostream> + +/***************************************************************************** +* test listener class +*****************************************************************************/ +class Listener; +std::ostream& operator<<(std::ostream&, const Listener&); + +/// Bear in mind that this is strictly for testing +class Listener +{ +public: + /// Every Listener is instantiated with a name + Listener(const std::string& name): + mName(name) + { +// std::cout << *this << ": ctor\n"; + } +/*==========================================================================*| + // These methods are only useful when trying to track Listener instance + // lifespan + Listener(const Listener& that): + mName(that.mName), + mLastEvent(that.mLastEvent) + { + std::cout << *this << ": copy\n"; + } + virtual ~Listener() + { + std::cout << *this << ": dtor\n"; + } +|*==========================================================================*/ + /// You can request the name + std::string getName() const { return mName; } + /// This is a typical listener method that returns 'false' when done, + /// allowing subsequent listeners on the LLEventPump to process the + /// incoming event. + bool call(const LLSD& event) + { +// std::cout << *this << "::call(" << event << ")\n"; + mLastEvent = event; + return false; + } + /// This is an alternate listener that returns 'true' when done, which + /// stops processing of the incoming event. + bool callstop(const LLSD& event) + { +// std::cout << *this << "::callstop(" << event << ")\n"; + mLastEvent = event; + return true; + } + /// ListenMethod can represent either call() or callstop(). + typedef bool (Listener::*ListenMethod)(const LLSD&); + /** + * This helper method is only because our test code makes so many + * repetitive listen() calls to ListenerMethods. In real code, you should + * call LLEventPump::listen() directly so it can examine the specific + * object you pass to boost::bind(). + */ + LLBoundListener listenTo(LLEventPump& pump, + ListenMethod method=&Listener::call, + const LLEventPump::NameList& after=LLEventPump::empty, + const LLEventPump::NameList& before=LLEventPump::empty) + { + return pump.listen(getName(), boost::bind(method, this, _1), after, before); + } + /// Both call() and callstop() set mLastEvent. Retrieve it. + LLSD getLastEvent() const + { +// std::cout << *this << "::getLastEvent() -> " << mLastEvent << "\n"; + return mLastEvent; + } + /// Reset mLastEvent to a known state. + void reset(const LLSD& to = LLSD()) + { +// std::cout << *this << "::reset(" << to << ")\n"; + mLastEvent = to; + } + +private: + std::string mName; + LLSD mLastEvent; +}; + +std::ostream& operator<<(std::ostream& out, const Listener& listener) +{ + out << "Listener(" << listener.getName() /* << "@" << &listener */ << ')'; + return out; +} + +/** + * This class tests the relative order in which various listeners on a given + * LLEventPump are called. Each listen() call binds a particular string, which + * we collect for later examination. The actual event is ignored. + */ +struct Collect +{ + bool add(const std::string& bound, const LLSD& event) + { + result.push_back(bound); + return false; + } + void clear() { result.clear(); } + typedef std::vector<std::string> StringList; + StringList result; +}; + +std::ostream& operator<<(std::ostream& out, const Collect::StringList& strings) +{ + out << '('; + Collect::StringList::const_iterator begin(strings.begin()), end(strings.end()); + if (begin != end) + { + out << '"' << *begin << '"'; + while (++begin != end) + { + out << ", \"" << *begin << '"'; + } + } + out << ')'; + return out; +} + +#endif /* ! defined(LL_LISTENER_H) */ diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp new file mode 100644 index 0000000000..3a2cda7735 --- /dev/null +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -0,0 +1,782 @@ +/** + * @file coroutine_test.cpp + * @author Nat Goodspeed + * @date 2009-04-22 + * @brief Test for coroutine. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +/*****************************************************************************/ +// test<1>() is cloned from a Boost.Coroutine example program whose copyright +// info is reproduced here: +/*---------------------------------------------------------------------------*/ +// Copyright (c) 2006, Giovanni P. Deretta +// +// This code may be used under either of the following two licences: +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. OF SUCH DAMAGE. +// +// Or: +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +/*****************************************************************************/ + +// On some platforms, Boost.Coroutine must #define magic symbols before +// #including platform-API headers. Naturally, that's ineffective unless the +// Boost.Coroutine #include is the *first* #include of the platform header. +// That means that client code must generally #include Boost.Coroutine headers +// before anything else. +#include <boost/coroutine/coroutine.hpp> +// Normally, lleventcoro.h obviates future.hpp. We only include this because +// we implement a "by hand" test of future functionality. +#include <boost/coroutine/future.hpp> +#include <boost/bind.hpp> +#include <boost/range.hpp> + +#include "linden_common.h" + +#include <iostream> +#include <string> + +#include "../test/lltut.h" +#include "llsd.h" +#include "llevents.h" +#include "tests/wrapllerrs.h" +#include "stringize.h" +#include "lleventcoro.h" +#include "../test/debug.h" + +/***************************************************************************** +* from the banana.cpp example program borrowed for test<1>() +*****************************************************************************/ +namespace coroutines = boost::coroutines; +using coroutines::coroutine; + +template<typename Iter> +bool match(Iter first, Iter last, std::string match) { + std::string::iterator i = match.begin(); + i != match.end(); + for(; (first != last) && (i != match.end()); ++i) { + if (*first != *i) + return false; + ++first; + } + return i == match.end(); +} + +template<typename BidirectionalIterator> +BidirectionalIterator +match_substring(BidirectionalIterator begin, + BidirectionalIterator end, + std::string xmatch, + BOOST_DEDUCED_TYPENAME coroutine<BidirectionalIterator(void)>::self& self) { + BidirectionalIterator begin_ = begin; + for(; begin != end; ++begin) + if(match(begin, end, xmatch)) { + self.yield(begin); + } + return end; +} + +typedef coroutine<std::string::iterator(void)> match_coroutine_type; + +/***************************************************************************** +* Test helpers +*****************************************************************************/ +// I suspect this will be typical of coroutines used in Linden software +typedef boost::coroutines::coroutine<void()> coroutine_type; + +/// Simulate an event API whose response is immediate: sent on receipt of the +/// initial request, rather than after some delay. This is the case that +/// distinguishes postAndWait() from calling post(), then calling +/// waitForEventOn(). +class ImmediateAPI +{ +public: + ImmediateAPI(): + mPump("immediate", true) + { + mPump.listen("API", boost::bind(&ImmediateAPI::operator(), this, _1)); + } + + LLEventPump& getPump() { return mPump; } + + // Invoke this with an LLSD map containing: + // ["value"]: Integer value. We will reply with ["value"] + 1. + // ["reply"]: Name of LLEventPump on which to send success response. + // ["error"]: Name of LLEventPump on which to send error response. + // ["fail"]: Presence of this key selects ["error"], else ["success"] as + // the name of the pump on which to send the response. + bool operator()(const LLSD& event) const + { + LLSD::Integer value(event["value"]); + LLSD::String replyPumpName(event.has("fail")? "error" : "reply"); + LLEventPumps::instance().obtain(event[replyPumpName]).post(value + 1); + return false; + } + +private: + LLEventStream mPump; +}; + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct coroutine_data + { + // Define coroutine bodies as methods here so they can use ensure*() + + void explicit_wait(coroutine_type::self& self) + { + BEGIN + { + // ... do whatever preliminary stuff must happen ... + + // declare the future + boost::coroutines::future<LLSD> future(self); + // tell the future what to wait for + LLTempBoundListener connection( + LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::coroutines::make_callback(future)))); + ensure("Not yet", ! future); + // attempting to dereference ("resolve") the future causes the calling + // coroutine to wait for it + debug("about to wait"); + result = *future; + ensure("Got it", future); + } + END + } + + void waitForEventOn1(coroutine_type::self& self) + { + BEGIN + { + result = waitForEventOn(self, "source"); + } + END + } + + void waitForEventOn2(coroutine_type::self& self) + { + BEGIN + { + LLEventWithID pair = waitForEventOn(self, "reply", "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + + void postAndWait1(coroutine_type::self& self) + { + BEGIN + { + result = postAndWait(self, + LLSD().insert("value", 17), // request event + immediateAPI.getPump(), // requestPump + "reply1", // replyPump + "reply"); // request["reply"] = name + } + END + } + + void postAndWait2(coroutine_type::self& self) + { + BEGIN + { + LLEventWithID pair = ::postAndWait2(self, + LLSD().insert("value", 18), + immediateAPI.getPump(), + "reply2", + "error2", + "reply", + "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + + void postAndWait2_1(coroutine_type::self& self) + { + BEGIN + { + LLEventWithID pair = ::postAndWait2(self, + LLSD().insert("value", 18).insert("fail", LLSD()), + immediateAPI.getPump(), + "reply2", + "error2", + "reply", + "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + + void coroPump(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPump waiter; + replyName = waiter.getName(); + result = waiter.wait(self); + } + END + } + + void coroPumpPost(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPump waiter; + result = waiter.postAndWait(self, LLSD().insert("value", 17), + immediateAPI.getPump(), "reply"); + } + END + } + + void coroPumps(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + LLEventWithID pair(waiter.wait(self)); + result = pair.first; + which = pair.second; + } + END + } + + void coroPumpsNoEx(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + result = waiter.waitWithException(self); + } + END + } + + void coroPumpsEx(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + try + { + result = waiter.waitWithException(self); + debug("no exception"); + } + catch (const LLErrorEvent& e) + { + debug(STRINGIZE("exception " << e.what())); + errordata = e.getData(); + } + } + END + } + + void coroPumpsNoLog(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + result = waiter.waitWithLog(self); + } + END + } + + void coroPumpsLog(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + WrapLL_ERRS capture; + try + { + result = waiter.waitWithLog(self); + debug("no exception"); + } + catch (const WrapLL_ERRS::FatalException& e) + { + debug(STRINGIZE("exception " << e.what())); + threw = e.what(); + } + } + END + } + + void coroPumpsPost(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + LLEventWithID pair(waiter.postAndWait(self, LLSD().insert("value", 23), + immediateAPI.getPump(), "reply", "error")); + result = pair.first; + which = pair.second; + } + END + } + + void coroPumpsPost_1(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + LLEventWithID pair( + waiter.postAndWait(self, LLSD().insert("value", 23).insert("fail", LLSD()), + immediateAPI.getPump(), "reply", "error")); + result = pair.first; + which = pair.second; + } + END + } + + void coroPumpsPostNoEx(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + result = waiter.postAndWaitWithException(self, LLSD().insert("value", 8), + immediateAPI.getPump(), "reply", "error"); + } + END + } + + void coroPumpsPostEx(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + try + { + result = waiter.postAndWaitWithException(self, + LLSD().insert("value", 9).insert("fail", LLSD()), + immediateAPI.getPump(), "reply", "error"); + debug("no exception"); + } + catch (const LLErrorEvent& e) + { + debug(STRINGIZE("exception " << e.what())); + errordata = e.getData(); + } + } + END + } + + void coroPumpsPostNoLog(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + result = waiter.postAndWaitWithLog(self, LLSD().insert("value", 30), + immediateAPI.getPump(), "reply", "error"); + } + END + } + + void coroPumpsPostLog(coroutine_type::self& self) + { + BEGIN + { + LLCoroEventPumps waiter; + WrapLL_ERRS capture; + try + { + result = waiter.postAndWaitWithLog(self, + LLSD().insert("value", 31).insert("fail", LLSD()), + immediateAPI.getPump(), "reply", "error"); + debug("no exception"); + } + catch (const WrapLL_ERRS::FatalException& e) + { + debug(STRINGIZE("exception " << e.what())); + threw = e.what(); + } + } + END + } + + void ensure_done(coroutine_type& coro) + { + ensure("coroutine complete", ! coro); + } + + ImmediateAPI immediateAPI; + std::string replyName, errorName, threw; + LLSD result, errordata; + int which; + }; + typedef test_group<coroutine_data> coroutine_group; + typedef coroutine_group::object object; + coroutine_group coroutinegrp("coroutine"); + + template<> template<> + void object::test<1>() + { + set_test_name("From banana.cpp example program in Boost.Coroutine distro"); + std::string buffer = "banananana"; + std::string match = "nana"; + std::string::iterator begin = buffer.begin(); + std::string::iterator end = buffer.end(); + +#if defined(BOOST_CORO_POSIX_IMPL) +// std::cout << "Using Boost.Coroutine " << BOOST_CORO_POSIX_IMPL << '\n'; +#else +// std::cout << "Using non-Posix Boost.Coroutine implementation" << std::endl; +#endif + + typedef std::string::iterator signature(std::string::iterator, + std::string::iterator, + std::string, + match_coroutine_type::self&); + + coroutine<std::string::iterator(void)> matcher + (boost::bind(static_cast<signature*>(match_substring), + begin, + end, + match, + _1)); + + std::string::iterator i = matcher(); +/*==========================================================================*| + while(matcher && i != buffer.end()) { + std::cout <<"Match at: "<< std::distance(buffer.begin(), i)<<'\n'; + i = matcher(); + } +|*==========================================================================*/ + size_t matches[] = { 2, 4, 6 }; + for (size_t *mi(boost::begin(matches)), *mend(boost::end(matches)); + mi != mend; ++mi, i = matcher()) + { + ensure("more", matcher); + ensure("found", i != buffer.end()); + ensure_equals("value", std::distance(buffer.begin(), i), *mi); + } + ensure("done", ! matcher); + } + + template<> template<> + void object::test<2>() + { + set_test_name("explicit_wait"); + DEBUG; + + // Construct the coroutine instance that will run explicit_wait. + // Pass the ctor a callable that accepts the coroutine_type::self + // param passed by the library. + coroutine_type coro(boost::bind(&coroutine_data::explicit_wait, this, _1)); + // Start the coroutine + coro(std::nothrow); + // When the coroutine waits for the event pump, it returns here. + debug("about to send"); + // Satisfy the wait. + LLEventPumps::instance().obtain("source").post("received"); + // Now wait for the coroutine to complete. + ensure_done(coro); + // ensure the coroutine ran and woke up again with the intended result + ensure_equals(result.asString(), "received"); + } + + template<> template<> + void object::test<3>() + { + set_test_name("waitForEventOn1"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn1, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain("source").post("received"); + debug("back from send"); + ensure_done(coro); + ensure_equals(result.asString(), "received"); + } + + template<> template<> + void object::test<4>() + { + set_test_name("waitForEventOn2 reply"); + { + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain("reply").post("received"); + debug("back from send"); + ensure_done(coro); + } + ensure_equals(result.asString(), "received"); + ensure_equals("which pump", which, 0); + } + + template<> template<> + void object::test<5>() + { + set_test_name("waitForEventOn2 error"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain("error").post("badness"); + debug("back from send"); + ensure_done(coro); + ensure_equals(result.asString(), "badness"); + ensure_equals("which pump", which, 1); + } + + template<> template<> + void object::test<6>() + { + set_test_name("coroPump"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPump, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain(replyName).post("received"); + debug("back from send"); + ensure_done(coro); + ensure_equals(result.asString(), "received"); + } + + template<> template<> + void object::test<7>() + { + set_test_name("coroPumps reply"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain(replyName).post("received"); + debug("back from send"); + ensure_done(coro); + ensure_equals(result.asString(), "received"); + ensure_equals("which pump", which, 0); + } + + template<> template<> + void object::test<8>() + { + set_test_name("coroPumps error"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain(errorName).post("badness"); + debug("back from send"); + ensure_done(coro); + ensure_equals(result.asString(), "badness"); + ensure_equals("which pump", which, 1); + } + + template<> template<> + void object::test<9>() + { + set_test_name("coroPumpsNoEx"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoEx, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain(replyName).post("received"); + debug("back from send"); + ensure_done(coro); + ensure_equals(result.asString(), "received"); + } + + template<> template<> + void object::test<10>() + { + set_test_name("coroPumpsEx"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsEx, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain(errorName).post("badness"); + debug("back from send"); + ensure_done(coro); + ensure("no result", result.isUndefined()); + ensure_equals("got error", errordata.asString(), "badness"); + } + + template<> template<> + void object::test<11>() + { + set_test_name("coroPumpsNoLog"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoLog, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain(replyName).post("received"); + debug("back from send"); + ensure_done(coro); + ensure_equals(result.asString(), "received"); + } + + template<> template<> + void object::test<12>() + { + set_test_name("coroPumpsLog"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsLog, this, _1)); + coro(std::nothrow); + debug("about to send"); + LLEventPumps::instance().obtain(errorName).post("badness"); + debug("back from send"); + ensure_done(coro); + ensure("no result", result.isUndefined()); + ensure_contains("got error", threw, "badness"); + } + + template<> template<> + void object::test<13>() + { + set_test_name("postAndWait1"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::postAndWait1, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 18); + } + + template<> template<> + void object::test<14>() + { + set_test_name("postAndWait2"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::postAndWait2, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 19); + ensure_equals(which, 0); + } + + template<> template<> + void object::test<15>() + { + set_test_name("postAndWait2_1"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::postAndWait2_1, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 19); + ensure_equals(which, 1); + } + + template<> template<> + void object::test<16>() + { + set_test_name("coroPumpPost"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpPost, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 18); + } + + template<> template<> + void object::test<17>() + { + set_test_name("coroPumpsPost reply"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 24); + ensure_equals("which pump", which, 0); + } + + template<> template<> + void object::test<18>() + { + set_test_name("coroPumpsPost error"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost_1, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 24); + ensure_equals("which pump", which, 1); + } + + template<> template<> + void object::test<19>() + { + set_test_name("coroPumpsPostNoEx"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoEx, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 9); + } + + template<> template<> + void object::test<20>() + { + set_test_name("coroPumpsPostEx"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostEx, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure("no result", result.isUndefined()); + ensure_equals("got error", errordata.asInteger(), 10); + } + + template<> template<> + void object::test<21>() + { + set_test_name("coroPumpsPostNoLog"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoLog, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure_equals(result.asInteger(), 31); + } + + template<> template<> + void object::test<22>() + { + set_test_name("coroPumpsPostLog"); + DEBUG; + coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostLog, this, _1)); + coro(std::nothrow); + ensure_done(coro); + ensure("no result", result.isUndefined()); + ensure_contains("got error", threw, "32"); + } +} // namespace tut diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp new file mode 100644 index 0000000000..28b909298e --- /dev/null +++ b/indra/llcommon/tests/lleventfilter_test.cpp @@ -0,0 +1,276 @@ +/** + * @file lleventfilter_test.cpp + * @author Nat Goodspeed + * @date 2009-03-06 + * @brief Test for lleventfilter. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "lleventfilter.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "../test/lltut.h" +#include "stringize.h" +#include "listener.h" +#include "tests/wrapllerrs.h" + +/***************************************************************************** +* Test classes +*****************************************************************************/ +// Strictly speaking, we're testing LLEventTimeoutBase rather than the +// production LLEventTimeout (using LLTimer) because we don't want every test +// run to pause for some number of seconds until we reach a real timeout. But +// as we've carefully put all functionality except actual LLTimer calls into +// LLEventTimeoutBase, that should suffice. We're not not not trying to test +// LLTimer here. +class TestEventTimeout: public LLEventTimeoutBase +{ +public: + TestEventTimeout(): + mElapsed(true) + {} + TestEventTimeout(LLEventPump& source): + LLEventTimeoutBase(source), + mElapsed(true) + {} + + // test hook + void forceTimeout(bool timeout=true) { mElapsed = timeout; } + +protected: + virtual void setCountdown(F32 seconds) { mElapsed = false; } + virtual bool countdownElapsed() const { return mElapsed; } + +private: + bool mElapsed; +}; + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct filter_data + { + // The resemblance between this test data and that in llevents_tut.cpp + // is not coincidental. + filter_data(): + pumps(LLEventPumps::instance()), + mainloop(pumps.obtain("mainloop")), + listener0("first"), + listener1("second") + {} + LLEventPumps& pumps; + LLEventPump& mainloop; + Listener listener0; + Listener listener1; + + void check_listener(const std::string& desc, const Listener& listener, const LLSD& got) + { + ensure_equals(STRINGIZE(listener << ' ' << desc), + listener.getLastEvent(), got); + } + }; + typedef test_group<filter_data> filter_group; + typedef filter_group::object filter_object; + filter_group filtergrp("lleventfilter"); + + template<> template<> + void filter_object::test<1>() + { + set_test_name("LLEventMatching"); + LLEventPump& driver(pumps.obtain("driver")); + listener0.reset(0); + // Listener isn't derived from LLEventTrackable specifically to test + // various connection-management mechanisms. But that means we have a + // couple of transient Listener objects, one of which is listening to + // a persistent LLEventPump. Capture those connections in local + // LLTempBoundListener instances so they'll disconnect + // on destruction. + LLTempBoundListener temp1( + listener0.listenTo(driver)); + // Construct a pattern LLSD: desired Event must have a key "foo" + // containing string "bar" + LLEventMatching filter(driver, LLSD().insert("foo", "bar")); + listener1.reset(0); + LLTempBoundListener temp2( + listener1.listenTo(filter)); + driver.post(1); + check_listener("direct", listener0, LLSD(1)); + check_listener("filtered", listener1, LLSD(0)); + // Okay, construct an LLSD map matching the pattern + LLSD data; + data["foo"] = "bar"; + data["random"] = 17; + driver.post(data); + check_listener("direct", listener0, data); + check_listener("filtered", listener1, data); + } + + template<> template<> + void filter_object::test<2>() + { + set_test_name("LLEventTimeout::actionAfter()"); + LLEventPump& driver(pumps.obtain("driver")); + TestEventTimeout filter(driver); + listener0.reset(0); + LLTempBoundListener temp1( + listener0.listenTo(filter)); + // Use listener1.call() as the Action for actionAfter(), since it + // already provides a way to sense the call + listener1.reset(0); + // driver --> filter --> listener0 + filter.actionAfter(20, + boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout"))); + // Okay, (fake) timer is ticking. 'filter' can only sense the timer + // when we pump mainloop. Do that right now to take the logic path + // before either the anticipated event arrives or the timer expires. + mainloop.post(17); + check_listener("no timeout 1", listener1, LLSD(0)); + // Expected event arrives... + driver.post(1); + check_listener("event passed thru", listener0, LLSD(1)); + // Should have canceled the timer. Verify that by asserting that the + // time has expired, then pumping mainloop again. + filter.forceTimeout(); + mainloop.post(17); + check_listener("no timeout 2", listener1, LLSD(0)); + // Verify chained actionAfter() calls, that is, that a second + // actionAfter() resets the timer established by the first + // actionAfter(). + filter.actionAfter(20, + boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout"))); + // Since our TestEventTimeout class isn't actually manipulating time + // (quantities of seconds), only a bool "elapsed" flag, sense that by + // forcing the flag between actionAfter() calls. + filter.forceTimeout(); + // Pumping mainloop here would result in a timeout (as we'll verify + // below). This state simulates a ticking timer that has not yet timed + // out. But now, before a mainloop event lets 'filter' recognize + // timeout on the previous actionAfter() call, pretend we're pushing + // that timeout farther into the future. + filter.actionAfter(20, + boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout"))); + // Look ma, no timeout! + mainloop.post(17); + check_listener("no timeout 3", listener1, LLSD(0)); + // Now let the updated actionAfter() timer expire. + filter.forceTimeout(); + // Notice the timeout. + mainloop.post(17); + check_listener("timeout", listener1, LLSD("timeout")); + // Timing out cancels the timer. Verify that. + listener1.reset(0); + filter.forceTimeout(); + mainloop.post(17); + check_listener("no timeout 4", listener1, LLSD(0)); + // Reset the timer and then cancel() it. + filter.actionAfter(20, + boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout"))); + // neither expired nor satisified + mainloop.post(17); + check_listener("no timeout 5", listener1, LLSD(0)); + // cancel + filter.cancel(); + // timeout! + filter.forceTimeout(); + mainloop.post(17); + check_listener("no timeout 6", listener1, LLSD(0)); + } + + template<> template<> + void filter_object::test<3>() + { + set_test_name("LLEventTimeout::eventAfter()"); + LLEventPump& driver(pumps.obtain("driver")); + TestEventTimeout filter(driver); + listener0.reset(0); + LLTempBoundListener temp1( + listener0.listenTo(filter)); + filter.eventAfter(20, LLSD("timeout")); + // Okay, (fake) timer is ticking. 'filter' can only sense the timer + // when we pump mainloop. Do that right now to take the logic path + // before either the anticipated event arrives or the timer expires. + mainloop.post(17); + check_listener("no timeout 1", listener0, LLSD(0)); + // Expected event arrives... + driver.post(1); + check_listener("event passed thru", listener0, LLSD(1)); + // Should have canceled the timer. Verify that by asserting that the + // time has expired, then pumping mainloop again. + filter.forceTimeout(); + mainloop.post(17); + check_listener("no timeout 2", listener0, LLSD(1)); + // Set timer again. + filter.eventAfter(20, LLSD("timeout")); + // Now let the timer expire. + filter.forceTimeout(); + // Notice the timeout. + mainloop.post(17); + check_listener("timeout", listener0, LLSD("timeout")); + // Timing out cancels the timer. Verify that. + listener0.reset(0); + filter.forceTimeout(); + mainloop.post(17); + check_listener("no timeout 3", listener0, LLSD(0)); + } + + template<> template<> + void filter_object::test<4>() + { + set_test_name("LLEventTimeout::errorAfter()"); + WrapLL_ERRS capture; + LLEventPump& driver(pumps.obtain("driver")); + TestEventTimeout filter(driver); + listener0.reset(0); + LLTempBoundListener temp1( + listener0.listenTo(filter)); + filter.errorAfter(20, "timeout"); + // Okay, (fake) timer is ticking. 'filter' can only sense the timer + // when we pump mainloop. Do that right now to take the logic path + // before either the anticipated event arrives or the timer expires. + mainloop.post(17); + check_listener("no timeout 1", listener0, LLSD(0)); + // Expected event arrives... + driver.post(1); + check_listener("event passed thru", listener0, LLSD(1)); + // Should have canceled the timer. Verify that by asserting that the + // time has expired, then pumping mainloop again. + filter.forceTimeout(); + mainloop.post(17); + check_listener("no timeout 2", listener0, LLSD(1)); + // Set timer again. + filter.errorAfter(20, "timeout"); + // Now let the timer expire. + filter.forceTimeout(); + // Notice the timeout. + std::string threw; + try + { + mainloop.post(17); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("errorAfter() timeout exception", threw, "timeout"); + // Timing out cancels the timer. Verify that. + listener0.reset(0); + filter.forceTimeout(); + mainloop.post(17); + check_listener("no timeout 3", listener0, LLSD(0)); + } +} // namespace tut + +/***************************************************************************** +* Link dependencies +*****************************************************************************/ +#include "llsdutil.cpp" diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h new file mode 100644 index 0000000000..1001ebc466 --- /dev/null +++ b/indra/llcommon/tests/wrapllerrs.h @@ -0,0 +1,56 @@ +/** + * @file wrapllerrs.h + * @author Nat Goodspeed + * @date 2009-03-11 + * @brief Define a class useful for unit tests that engage llerrs (LL_ERRS) functionality + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_WRAPLLERRS_H) +#define LL_WRAPLLERRS_H + +#include "llerrorcontrol.h" + +struct WrapLL_ERRS +{ + WrapLL_ERRS(): + // Resetting Settings discards the default Recorder that writes to + // stderr. Otherwise, expected llerrs (LL_ERRS) messages clutter the + // console output of successful tests, potentially confusing things. + mPriorErrorSettings(LLError::saveAndResetSettings()), + // Save shutdown function called by LL_ERRS + mPriorFatal(LLError::getFatalFunction()) + { + // Make LL_ERRS call our own operator() method + LLError::setFatalFunction(boost::bind(&WrapLL_ERRS::operator(), this, _1)); + } + + ~WrapLL_ERRS() + { + LLError::setFatalFunction(mPriorFatal); + LLError::restoreSettings(mPriorErrorSettings); + } + + struct FatalException: public std::runtime_error + { + FatalException(const std::string& what): std::runtime_error(what) {} + }; + + void operator()(const std::string& message) + { + // Save message for later in case consumer wants to sense the result directly + error = message; + // Also throw an appropriate exception since calling code is likely to + // assume that control won't continue beyond LL_ERRS. + throw FatalException(message); + } + + std::string error; + LLError::Settings* mPriorErrorSettings; + LLError::FatalFunction mPriorFatal; +}; + +#endif /* ! defined(LL_WRAPLLERRS_H) */ diff --git a/indra/llcommon/timing.h b/indra/llcommon/timing.h index 2b9f60adad..140ce1fcaa 100644 --- a/indra/llcommon/timing.h +++ b/indra/llcommon/timing.h @@ -43,7 +43,6 @@ const F32 SEC_TO_MICROSEC = 1000000.f; const U64 SEC_TO_MICROSEC_U64 = 1000000; const U32 SEC_PER_DAY = 86400; -// This is just a stub, implementation in lltimer.cpp. This file will be deprecated in the future. -U64 totalTime(); // Returns current system time in microseconds +// functionality has been moved lltimer.{cpp,h}. This file will be deprecated in the future. #endif diff --git a/indra/llcommon/u64.h b/indra/llcommon/u64.h index 09a6b3e18d..eb51131e94 100644 --- a/indra/llcommon/u64.h +++ b/indra/llcommon/u64.h @@ -39,14 +39,14 @@ * @param str The string to parse. * @return Returns the first U64 value found in the string or 0 on failure. */ -U64 str_to_U64(const std::string& str); +LL_COMMON_API U64 str_to_U64(const std::string& str); /** * @brief Given a U64 value, return a printable representation. * @param value The U64 to turn into a printable character array. * @return Returns the result string. */ -std::string U64_to_str(U64 value); +LL_COMMON_API std::string U64_to_str(U64 value); /** * @brief Given a U64 value, return a printable representation. @@ -65,16 +65,16 @@ std::string U64_to_str(U64 value); * @param result_size The size of the buffer allocated. Use U64_BUF. * @return Returns the result pointer. */ -char* U64_to_str(U64 value, char* result, S32 result_size); +LL_COMMON_API char* U64_to_str(U64 value, char* result, S32 result_size); /** * @brief Convert a U64 to the closest F64 value. */ -F64 U64_to_F64(const U64 value); +LL_COMMON_API F64 U64_to_F64(const U64 value); /** * @brief Helper function to wrap strtoull() which is not available on windows. */ -U64 llstrtou64(const char* str, char** end, S32 base); +LL_COMMON_API U64 llstrtou64(const char* str, char** end, S32 base); #endif diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp index e48690908e..2808effdc6 100644 --- a/indra/llinventory/llparcel.cpp +++ b/indra/llinventory/llparcel.cpp @@ -43,7 +43,7 @@ #include "llsdutil.h" #include "lltransactiontypes.h" #include "lltransactionflags.h" -#include "llsdutil.h" +#include "llsdutil_math.h" #include "message.h" #include "u64.h" diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index d287a5063a..3970c7a92c 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -62,6 +62,7 @@ set(llmath_HEADER_FILES llv4vector3.h llvolume.h llvolumemgr.h + llsdutil_math.h m3math.h m4math.h raytrace.h diff --git a/indra/llmath/llbbox.cpp b/indra/llmath/llbbox.cpp index acf93a2a38..914cbfdc12 100644 --- a/indra/llmath/llbbox.cpp +++ b/indra/llmath/llbbox.cpp @@ -30,6 +30,8 @@ * $/LicenseInfo$ */ +#include "linden_common.h" + // self include #include "llbbox.h" diff --git a/indra/llmath/llsdutil_math.cpp b/indra/llmath/llsdutil_math.cpp index c5176681ce..1bd12ae513 100644 --- a/indra/llmath/llsdutil_math.cpp +++ b/indra/llmath/llsdutil_math.cpp @@ -34,7 +34,7 @@ #include "linden_common.h" -#include "llsdutil.h" +#include "llsdutil_math.h" #include "v3math.h" #include "v4math.h" diff --git a/indra/llmath/llsdutil_math.h b/indra/llmath/llsdutil_math.h new file mode 100644 index 0000000000..121f4b746a --- /dev/null +++ b/indra/llmath/llsdutil_math.h @@ -0,0 +1,70 @@ +/** + * @file llsdutil_math.h + * @author Brad + * @date 2009-05-19 + * @brief Utility classes, functions, etc, for using structured data with math classes. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009-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_LLSDUTIL_MATH_H +#define LL_LLSDUTIL_MATH_H + +class LL_COMMON_API LLSD; + +// vector3 +class LLVector3; +LLSD ll_sd_from_vector3(const LLVector3& vec); +LLVector3 ll_vector3_from_sd(const LLSD& sd, S32 start_index = 0); + +// vector4 +class LLVector4; +LLSD ll_sd_from_vector4(const LLVector4& vec); +LLVector4 ll_vector4_from_sd(const LLSD& sd, S32 start_index = 0); + +// vector3d (double) +class LLVector3d; +LLSD ll_sd_from_vector3d(const LLVector3d& vec); +LLVector3d ll_vector3d_from_sd(const LLSD& sd, S32 start_index = 0); + +// vector2 +class LLVector2; +LLSD ll_sd_from_vector2(const LLVector2& vec); +LLVector2 ll_vector2_from_sd(const LLSD& sd); + +// Quaternion +class LLQuaternion; +LLSD ll_sd_from_quaternion(const LLQuaternion& quat); +LLQuaternion ll_quaternion_from_sd(const LLSD& sd); + +// color4 +class LLColor4; +LLSD ll_sd_from_color4(const LLColor4& c); +LLColor4 ll_color4_from_sd(const LLSD& sd); + +#endif // LL_LLSDUTIL_MATH_H diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 88f83ba78e..2c50a9b1cd 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -22,6 +22,7 @@ include_directories( set(llmessage_SOURCE_FILES llares.cpp + llareslistener.cpp llassetstorage.cpp llblowfishcipher.cpp llbuffer.cpp @@ -104,6 +105,7 @@ set(llmessage_HEADER_FILES CMakeLists.txt llares.h + llareslistener.h llassetstorage.h llblowfishcipher.h llbuffer.h @@ -221,10 +223,11 @@ IF (NOT LINUX AND VIEWER) # llhttpclientadapter.cpp lltrustedmessageservice.cpp lltemplatemessagedispatcher.cpp + # Commented out - see rationale at bottom of newview's build file + poppy 2009-06-05 + # Don't make llmessage depend on llsdmessage_test because ADD_COMM_BUILD_TEST depends on llmessage! + # ADD_COMM_BUILD_TEST(llsdmessage "" "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py") + # llareslistener.cpp ) LL_ADD_PROJECT_UNIT_TESTS(llmessage "${llmessage_TEST_SOURCE_FILES}") - - # Commented out - see rationale at bottom of newview's build file + poppy 2009-06-05 - # Don't make llmessage depend on llsdmessage_test because ADD_COMM_BUILD_TEST depends on llmessage! - # ADD_COMM_BUILD_TEST(llsdmessage "" "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py") + ENDIF (NOT LINUX AND VIEWER) diff --git a/indra/llmessage/llares.cpp b/indra/llmessage/llares.cpp index fe37fe8142..acbf51d75c 100644 --- a/indra/llmessage/llares.cpp +++ b/indra/llmessage/llares.cpp @@ -33,6 +33,7 @@ */ #include "linden_common.h" +#include "llares.h" #include <ares_dns.h> #include <ares_version.h> @@ -42,9 +43,10 @@ #include "apr_poll.h" #include "llapr.h" -#include "llares.h" +#include "llareslistener.h" #if defined(LL_WINDOWS) +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally # define ns_c_in 1 # define NS_HFIXEDSZ 12 /* #/bytes of fixed data in header */ # define NS_QFIXEDSZ 4 /* #/bytes of fixed data in query */ @@ -102,7 +104,9 @@ void LLAres::QueryResponder::queryError(int code) } LLAres::LLAres() : -chan_(NULL), mInitSuccess(false) + chan_(NULL), + mInitSuccess(false), + mListener(new LLAresListener("LLAres", this)) { if (ares_init(&chan_) != ARES_SUCCESS) { diff --git a/indra/llmessage/llares.h b/indra/llmessage/llares.h index c709a08499..78febcd560 100644 --- a/indra/llmessage/llares.h +++ b/indra/llmessage/llares.h @@ -36,7 +36,13 @@ #define LL_LLARES_H #ifdef LL_WINDOWS +// ares.h is broken on windows in that it depends on types defined in ws2tcpip.h +// we need to include them first to work around it, but the headers issue warnings +# pragma warning(push) +# pragma warning(disable:4996) +# include <winsock2.h> # include <ws2tcpip.h> +# pragma warning(pop) #endif #ifdef LL_STANDALONE @@ -49,7 +55,10 @@ #include "llrefcount.h" #include "lluri.h" +#include <boost/shared_ptr.hpp> + class LLQueryResponder; +class LLAresListener; /** * @brief Supported DNS RR types. @@ -444,6 +453,9 @@ public: protected: ares_channel chan_; bool mInitSuccess; + // boost::scoped_ptr would actually fit the requirement better, but it + // can't handle incomplete types as boost::shared_ptr can. + boost::shared_ptr<LLAresListener> mListener; }; /** diff --git a/indra/llmessage/llareslistener.cpp b/indra/llmessage/llareslistener.cpp new file mode 100644 index 0000000000..a8beb8cbde --- /dev/null +++ b/indra/llmessage/llareslistener.cpp @@ -0,0 +1,75 @@ +/** + * @file llareslistener.cpp + * @author Nat Goodspeed + * @date 2009-03-18 + * @brief Implementation for llareslistener. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llareslistener.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llares.h" +#include "llerror.h" +#include "llevents.h" +#include "llsdutil.h" + +LLAresListener::LLAresListener(const std::string& pumpname, LLAres* llares): + LLDispatchListener(pumpname, "op"), + mAres(llares) +{ + // add() every method we want to be able to invoke via this event API. + // Optional third parameter validates expected LLSD request structure. + add("rewriteURI", &LLAresListener::rewriteURI, + LLSD().insert("uri", LLSD()).insert("reply", LLSD())); +} + +/// This UriRewriteResponder subclass packages returned URIs as an LLSD +/// array to send back to the requester. +class UriRewriteResponder: public LLAres::UriRewriteResponder +{ +public: + /** + * Specify the request, containing the event pump name on which to send + * the reply. + */ + UriRewriteResponder(const LLSD& request): + mReqID(request), + mPumpName(request["reply"]) + {} + + /// Called by base class with results. This is called in both the + /// success and error cases. On error, the calling logic passes the + /// original URI. + virtual void rewriteResult(const std::vector<std::string>& uris) + { + LLSD result; + for (std::vector<std::string>::const_iterator ui(uris.begin()), uend(uris.end()); + ui != uend; ++ui) + { + result.append(*ui); + } + // This call knows enough to avoid trying to insert a map key into an + // LLSD array. It's there so that if, for any reason, we ever decide + // to change the response from array to map, it will Just Start Working. + mReqID.stamp(result); + LLEventPumps::instance().obtain(mPumpName).post(result); + } + +private: + LLReqID mReqID; + const std::string mPumpName; +}; + +void LLAresListener::rewriteURI(const LLSD& data) +{ + mAres->rewriteURI(data["uri"], new UriRewriteResponder(data)); +} diff --git a/indra/llmessage/llareslistener.h b/indra/llmessage/llareslistener.h new file mode 100644 index 0000000000..bf093b3d3d --- /dev/null +++ b/indra/llmessage/llareslistener.h @@ -0,0 +1,37 @@ +/** + * @file llareslistener.h + * @author Nat Goodspeed + * @date 2009-03-18 + * @brief LLEventPump API for LLAres. This header doesn't actually define the + * API; the API is defined by the pump name on which this class + * listens, and by the expected content of LLSD it receives. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLARESLISTENER_H) +#define LL_LLARESLISTENER_H + +#include "lleventdispatcher.h" + +class LLAres; +class LLSD; + +/// Listen on an LLEventPump with specified name for LLAres request events. +class LLAresListener: public LLDispatchListener +{ +public: + /// Specify the pump name on which to listen, and bind the LLAres instance + /// to use (e.g. gAres) + LLAresListener(const std::string& pumpname, LLAres* llares); + +private: + /// command["op"] == "rewriteURI" + void rewriteURI(const LLSD& data); + + LLAres* mAres; +}; + +#endif /* ! defined(LL_LLARESLISTENER_H) */ diff --git a/indra/llmessage/llcachename.h b/indra/llmessage/llcachename.h index 47d49076f4..792f1aeb0a 100644 --- a/indra/llmessage/llcachename.h +++ b/indra/llmessage/llcachename.h @@ -42,9 +42,9 @@ class LLUUID; typedef boost::signals2::signal<void (const LLUUID& id, - const std::string& first_name, - const std::string& last_name, - BOOL is_group)> LLCacheNameSignal; + const std::string& first_name, + const std::string& last_name, + BOOL is_group)> LLCacheNameSignal; typedef LLCacheNameSignal::slot_type LLCacheNameCallback; // Old callback with user data for compatability diff --git a/indra/llmessage/llinstantmessage.cpp b/indra/llmessage/llinstantmessage.cpp index 7c63625004..3da41939fa 100644 --- a/indra/llmessage/llinstantmessage.cpp +++ b/indra/llmessage/llinstantmessage.cpp @@ -40,7 +40,7 @@ #include "lluuid.h" #include "llsd.h" #include "llsdserialize.h" -#include "llsdutil.h" +#include "llsdutil_math.h" #include "llpointer.h" #include "message.h" diff --git a/indra/llmessage/llpartdata.cpp b/indra/llmessage/llpartdata.cpp index 485bc6aa44..9376cde7b5 100644 --- a/indra/llmessage/llpartdata.cpp +++ b/indra/llmessage/llpartdata.cpp @@ -39,6 +39,8 @@ #include "v4coloru.h" #include "llsdutil.h" +#include "llsdutil_math.h" + const S32 PS_PART_DATA_BLOCK_SIZE = 4 + 2 + 4 + 4 + 2 + 2; // 18 diff --git a/indra/llmessage/llregionpresenceverifier.cpp b/indra/llmessage/llregionpresenceverifier.cpp index 0527d5cb8d..e6be4af07b 100644 --- a/indra/llmessage/llregionpresenceverifier.cpp +++ b/indra/llmessage/llregionpresenceverifier.cpp @@ -30,6 +30,8 @@ * $/LicenseInfo$ */ +#include "linden_common.h" + #include "llregionpresenceverifier.h" #include "llhttpclientinterface.h" #include <sstream> diff --git a/indra/llmessage/llsdmessage.cpp b/indra/llmessage/llsdmessage.cpp index 9967a6197f..2cb742e261 100644 --- a/indra/llmessage/llsdmessage.cpp +++ b/indra/llmessage/llsdmessage.cpp @@ -68,6 +68,7 @@ bool LLSDMessage::httpListener(const LLSD& request) } LLHTTPClient::post(url, payload, new LLSDMessage::EventResponder(LLEventPumps::instance(), + request, url, "POST", reply, error), LLSD(), // headers timeout); @@ -81,7 +82,9 @@ void LLSDMessage::EventResponder::result(const LLSD& data) // to the pump whose name is "". if (! mReplyPump.empty()) { - mPumps.obtain(mReplyPump).post(data); + LLSD response(data); + mReqID.stamp(response); + mPumps.obtain(mReplyPump).post(response); } else // default success handling { @@ -98,7 +101,7 @@ void LLSDMessage::EventResponder::errorWithContent(U32 status, const std::string // explicit pump name. if (! mErrorPump.empty()) { - LLSD info; + LLSD info(mReqID.makeResponse()); info["target"] = mTarget; info["message"] = mMessage; info["status"] = LLSD::Integer(status); diff --git a/indra/llmessage/llsdmessage.h b/indra/llmessage/llsdmessage.h index 65503756a8..6ee00fd41d 100644 --- a/indra/llmessage/llsdmessage.h +++ b/indra/llmessage/llsdmessage.h @@ -121,9 +121,11 @@ private: * (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), @@ -135,6 +137,7 @@ private: private: LLEventPumps& mPumps; + LLReqID mReqID; const std::string mTarget, mMessage, mReplyPump, mErrorPump; }; diff --git a/indra/llmessage/llsdmessagebuilder.cpp b/indra/llmessage/llsdmessagebuilder.cpp index 21937f022f..6e41b03895 100755 --- a/indra/llmessage/llsdmessagebuilder.cpp +++ b/indra/llmessage/llsdmessagebuilder.cpp @@ -37,6 +37,7 @@ #include "llmessagetemplate.h" #include "llquaternion.h" #include "llsdutil.h" +#include "llsdutil_math.h" #include "llsdserialize.h" #include "u64.h" #include "v3dmath.h" diff --git a/indra/llmessage/llsdmessagereader.cpp b/indra/llmessage/llsdmessagereader.cpp index e699ec9e28..845a12d23b 100755 --- a/indra/llmessage/llsdmessagereader.cpp +++ b/indra/llmessage/llsdmessagereader.cpp @@ -38,6 +38,7 @@ #include "llsdmessagebuilder.h" #include "llsdutil.h" +#include "llsdutil_math.h" #include "v3math.h" #include "v4math.h" #include "v3dmath.h" diff --git a/indra/llmessage/tests/llareslistener_test.cpp b/indra/llmessage/tests/llareslistener_test.cpp new file mode 100644 index 0000000000..ac4886ccf4 --- /dev/null +++ b/indra/llmessage/tests/llareslistener_test.cpp @@ -0,0 +1,200 @@ +/** + * @file llareslistener_test.cpp + * @author Mark Palange + * @date 2009-02-26 + * @brief Tests of llareslistener.h. + * + * $LicenseInfo:firstyear=2009&license=internal$ + * Copyright (c) 2009, Linden Research, Inc. + * $/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 "../llareslistener.h" +// STL headers +#include <iostream> +// std headers +// external library headers +#include <boost/bind.hpp> + +// other Linden headers +#include "llsd.h" +#include "llares.h" +#include "../test/lltut.h" +#include "llevents.h" +#include "tests/wrapllerrs.h" + +/***************************************************************************** +* Dummy stuff +*****************************************************************************/ +LLAres::LLAres(): + // Simulate this much of the real LLAres constructor: we need an + // LLAresListener instance. + mListener(new LLAresListener("LLAres", this)) +{} +LLAres::~LLAres() {} +void LLAres::rewriteURI(const std::string &uri, + LLAres::UriRewriteResponder *resp) +{ + // This is the only LLAres method I chose to implement. + // The effect is that LLAres returns immediately with + // a result that is equal to the input uri. + std::vector<std::string> result; + result.push_back(uri); + resp->rewriteResult(result); +} + +LLAres::QueryResponder::~QueryResponder() {} +void LLAres::QueryResponder::queryError(int) {} +void LLAres::QueryResponder::queryResult(char const*, size_t) {} +LLQueryResponder::LLQueryResponder() {} +void LLQueryResponder::queryResult(char const*, size_t) {} +void LLQueryResponder::querySuccess() {} +void LLAres::UriRewriteResponder::queryError(int) {} +void LLAres::UriRewriteResponder::querySuccess() {} +void LLAres::UriRewriteResponder::rewriteResult(const std::vector<std::string>& uris) {} + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct data + { + LLAres dummyAres; + }; + typedef test_group<data> llareslistener_group; + typedef llareslistener_group::object object; + llareslistener_group llareslistenergrp("llareslistener"); + + struct ResponseCallback + { + std::vector<std::string> mURIs; + bool operator()(const LLSD& response) + { + mURIs.clear(); + for (LLSD::array_const_iterator ri(response.beginArray()), rend(response.endArray()); + ri != rend; ++ri) + { + mURIs.push_back(*ri); + } + return false; + } + }; + + template<> template<> + void object::test<1>() + { + set_test_name("test event"); + // Tests the success and failure cases, since they both use + // the same code paths in the LLAres responder. + ResponseCallback response; + std::string pumpname("trigger"); + // Since we're asking LLEventPumps to obtain() the pump by the desired + // name, it will persist beyond the current scope, so ensure we + // disconnect from it when 'response' goes away. + LLTempBoundListener temp( + LLEventPumps::instance().obtain(pumpname).listen("rewriteURIresponse", + boost::bind(&ResponseCallback::operator(), &response, _1))); + // Now build an LLSD request that will direct its response events to + // that pump. + const std::string testURI("login.bar.com"); + LLSD request; + request["op"] = "rewriteURI"; + request["uri"] = testURI; + request["reply"] = pumpname; + LLEventPumps::instance().obtain("LLAres").post(request); + ensure_equals(response.mURIs.size(), 1); + ensure_equals(response.mURIs.front(), testURI); + } + + template<> template<> + void object::test<2>() + { + set_test_name("bad op"); + WrapLL_ERRS capture; + LLSD request; + request["op"] = "foo"; + std::string threw; + try + { + LLEventPumps::instance().obtain("LLAres").post(request); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("LLAresListener bad op", threw, "bad"); + } + + template<> template<> + void object::test<3>() + { + set_test_name("bad rewriteURI request"); + WrapLL_ERRS capture; + LLSD request; + request["op"] = "rewriteURI"; + std::string threw; + try + { + LLEventPumps::instance().obtain("LLAres").post(request); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("LLAresListener bad req", threw, "missing"); + ensure_contains("LLAresListener bad req", threw, "reply"); + ensure_contains("LLAresListener bad req", threw, "uri"); + } + + template<> template<> + void object::test<4>() + { + set_test_name("bad rewriteURI request"); + WrapLL_ERRS capture; + LLSD request; + request["op"] = "rewriteURI"; + request["reply"] = "nonexistent"; + std::string threw; + try + { + LLEventPumps::instance().obtain("LLAres").post(request); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("LLAresListener bad req", threw, "missing"); + ensure_contains("LLAresListener bad req", threw, "uri"); + ensure_does_not_contain("LLAresListener bad req", threw, "reply"); + } + + template<> template<> + void object::test<5>() + { + set_test_name("bad rewriteURI request"); + WrapLL_ERRS capture; + LLSD request; + request["op"] = "rewriteURI"; + request["uri"] = "foo.bar.com"; + std::string threw; + try + { + LLEventPumps::instance().obtain("LLAres").post(request); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("LLAresListener bad req", threw, "missing"); + ensure_contains("LLAresListener bad req", threw, "reply"); + ensure_does_not_contain("LLAresListener bad req", threw, "uri"); + } +} diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py index e62f20912b..86d5761b1b 100644 --- a/indra/llmessage/tests/test_llsdmessage_peer.py +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -16,16 +16,12 @@ import os import sys from threading import Thread from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler + mydir = os.path.dirname(__file__) # expected to be .../indra/llmessage/tests/ sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) from indra.util.fastest_elementtree import parse as xml_parse from indra.base import llsd - -def debug(*args): - sys.stdout.writelines(args) - sys.stdout.flush() -# comment out the line below to enable debug output -debug = lambda *args: None +from testrunner import run, debug class TestHTTPRequestHandler(BaseHTTPRequestHandler): """This subclass of BaseHTTPRequestHandler is to receive and echo @@ -106,25 +102,5 @@ class TestHTTPServer(Thread): debug("Starting HTTP server...\n") httpd.serve_forever() -def main(*args): - # Start HTTP server thread. Note that this and all other comm server - # threads should be daemon threads: we'll let them run "forever," - # confident that the whole process will terminate when the main thread - # terminates, which will be when the test executable child process - # terminates. - httpThread = TestHTTPServer(name="httpd") - httpThread.setDaemon(True) - httpThread.start() - # choice of os.spawnv(): - # - [v vs. l] pass a list of args vs. individual arguments, - # - [no p] don't use the PATH because we specifically want to invoke the - # executable passed as our first arg, - # - [no e] child should inherit this process's environment. - debug("Running %s...\n" % (" ".join(args))) - sys.stdout.flush() - rc = os.spawnv(os.P_WAIT, args[0], args) - debug("%s returned %s\n" % (args[0], rc)) - return rc - if __name__ == "__main__": - sys.exit(main(*sys.argv[1:])) + sys.exit(run(server=TestHTTPServer(name="httpd"), *sys.argv[1:])) diff --git a/indra/llmessage/tests/testrunner.py b/indra/llmessage/tests/testrunner.py new file mode 100644 index 0000000000..3b9c3a7a19 --- /dev/null +++ b/indra/llmessage/tests/testrunner.py @@ -0,0 +1,53 @@ +#!/usr/bin/python +"""\ +@file testrunner.py +@author Nat Goodspeed +@date 2009-03-20 +@brief Utilities for writing wrapper scripts for ADD_COMM_BUILD_TEST unit tests + +$LicenseInfo:firstyear=2009&license=viewergpl$ +Copyright (c) 2009, Linden Research, Inc. +$/LicenseInfo$ +""" + +import os +import sys + +def debug(*args): + sys.stdout.writelines(args) + sys.stdout.flush() +# comment out the line below to enable debug output +debug = lambda *args: None + +def run(*args, **kwds): + """All positional arguments collectively form a command line, executed as + a synchronous child process. + In addition, pass server=new_thread_instance as an explicit keyword (to + differentiate it from an additional command-line argument). + new_thread_instance should be an instantiated but not yet started Thread + subclass instance, e.g.: + run("python", "-c", 'print "Hello, world!"', server=TestHTTPServer(name="httpd")) + """ + # If there's no server= keyword arg, don't start a server thread: simply + # run a child process. + try: + thread = kwds.pop("server") + except KeyError: + pass + else: + # Start server thread. Note that this and all other comm server + # threads should be daemon threads: we'll let them run "forever," + # confident that the whole process will terminate when the main thread + # terminates, which will be when the child process terminates. + thread.setDaemon(True) + thread.start() + # choice of os.spawnv(): + # - [v vs. l] pass a list of args vs. individual arguments, + # - [no p] don't use the PATH because we specifically want to invoke the + # executable passed as our first arg, + # - [no e] child should inherit this process's environment. + debug("Running %s...\n" % (" ".join(args))) + sys.stdout.flush() + rc = os.spawnv(os.P_WAIT, args[0], args) + debug("%s returned %s\n" % (args[0], rc)) + return rc diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index 33807e7545..9b0fd81ed3 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -43,7 +43,7 @@ #include "llvolumemgr.h" #include "llstring.h" #include "lldatapacker.h" -#include "llsdutil.h" +#include "llsdutil_math.h" #include "llprimtexturelist.h" /** diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp index 3bcd831142..2736d54cc8 100644 --- a/indra/llprimitive/lltextureentry.cpp +++ b/indra/llprimitive/lltextureentry.cpp @@ -33,7 +33,7 @@ #include "linden_common.h" #include "lltextureentry.h" -#include "llsdutil.h" +#include "llsdutil_math.h" const U8 DEFAULT_BUMP_CODE = 0; // no bump or shininess diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index 7e1df0e565..69675e322a 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -590,8 +590,6 @@ void LLGLManager::shutdownGL() // these are used to turn software blending on. They appear in the Debug/Avatar menu // presence of vertex skinning/blending or vertex programs will set these to FALSE by default. -extern LLCPUInfo gSysCPU; - void LLGLManager::initExtensions() { #if LL_MESA_HEADLESS diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 8b0fcc68c4..753c1fe9e2 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -1,193 +1,197 @@ -# -*- cmake -*- - -project(llui) - -include(00-Common) -include(LLCommon) -include(LLImage) -include(LLMath) -include(LLMessage) -include(LLRender) -include(LLWindow) -include(LLVFS) -include(LLXML) -include(LLXUIXML) - -include_directories( - ${LLCOMMON_INCLUDE_DIRS} - ${LLIMAGE_INCLUDE_DIRS} - ${LLMATH_INCLUDE_DIRS} - ${LLMESSAGE_INCLUDE_DIRS} - ${LLRENDER_INCLUDE_DIRS} - ${LLWINDOW_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} - ${LLXML_INCLUDE_DIRS} - ${LLXUIXML_INCLUDE_DIRS} - ) - -set(llui_SOURCE_FILES - llalertdialog.cpp - llbutton.cpp - llcheckboxctrl.cpp - llclipboard.cpp - llcombobox.cpp - llconsole.cpp - llcontainerview.cpp - llctrlselectioninterface.cpp - lldraghandle.cpp - lleditmenuhandler.cpp - llf32uictrl.cpp - llfiltereditor.cpp - llfloater.cpp - llfloaterreg.cpp - llflyoutbutton.cpp - llfocusmgr.cpp - llfunctorregistry.cpp - lliconctrl.cpp - llkeywords.cpp - lllayoutstack.cpp - lllineeditor.cpp - lllink.cpp - llmenugl.cpp - llmodaldialog.cpp - llmultifloater.cpp - llmultislider.cpp - llmultisliderctrl.cpp - llnotifications.cpp - llpanel.cpp - llprogressbar.cpp - llradiogroup.cpp - llresizebar.cpp - llresizehandle.cpp - llresmgr.cpp - llrngwriter.cpp - llscrollbar.cpp - llscrollcontainer.cpp - llscrollingpanellist.cpp - llscrolllistcell.cpp - llscrolllistcolumn.cpp - llscrolllistctrl.cpp - llscrolllistitem.cpp - llsdparam.cpp - llsearcheditor.cpp - llslider.cpp - llsliderctrl.cpp - llspinctrl.cpp - llstatbar.cpp - llstatgraph.cpp - llstatview.cpp - llstyle.cpp - lltabcontainer.cpp - lltextbox.cpp - lltexteditor.cpp - lltextparser.cpp - lltransutil.cpp - llui.cpp - lluicolortable.cpp - lluictrl.cpp - lluictrlfactory.cpp - lluiimage.cpp - lluistring.cpp - llundo.cpp - llviewborder.cpp - llviewmodel.cpp - llview.cpp - llviewquery.cpp - ) - -set(llui_HEADER_FILES - CMakeLists.txt - - llalertdialog.h - llbutton.h - llcallbackmap.h - llcheckboxctrl.h - llclipboard.h - llcombobox.h - llconsole.h - llcontainerview.h - llctrlselectioninterface.h - lldraghandle.h - lleditmenuhandler.h - llf32uictrl.h - llfiltereditor.h - llfloater.h - llfloaterreg.h - llflyoutbutton.h - llfocusmgr.h - llfunctorregistry.h - llhandle.h - llhtmlhelp.h - lliconctrl.h - llkeywords.h - lllayoutstack.h - lllazyvalue.h - lllineeditor.h - lllink.h - llmenugl.h - llmodaldialog.h - llmultifloater.h - llmultisliderctrl.h - llmultislider.h - llnotifications.h - llpanel.h - llprogressbar.h - llradiogroup.h - llresizebar.h - llresizehandle.h - llresmgr.h - llrngwriter.h - llsearcheditor.h - llscrollbar.h - llscrollcontainer.h - llscrollingpanellist.h - llscrolllistcell.h - llscrolllistcolumn.h - llscrolllistctrl.h - llscrolllistitem.h - llsdparam.h - llsliderctrl.h - llslider.h - llspinctrl.h - llstatbar.h - llstatgraph.h - llstatview.h - llstyle.h - lltabcontainer.h - lltextbox.h - lltexteditor.h - lltextparser.h - lltransutil.h - lluicolortable.h - lluiconstants.h - lluictrlfactory.h - lluictrl.h - lluifwd.h - llui.h - lluiimage.h - lluistring.h - llundo.h - llviewborder.h - llviewmodel.h - llview.h - llviewquery.h - ) - -set_source_files_properties(${llui_HEADER_FILES} - PROPERTIES HEADER_FILE_ONLY TRUE) - -list(APPEND llui_SOURCE_FILES ${llui_HEADER_FILES}) - -add_library (llui ${llui_SOURCE_FILES}) -# Libraries on which this library depends, needed for Linux builds -# Sort by high-level to low-level -target_link_libraries(llui - llrender - llwindow - llimage - llvfs # ugh, just for LLDir - llxuixml - llxml - llcommon # must be after llimage, llwindow, llrender - llmath - ) +# -*- cmake -*-
+
+project(llui)
+
+include(00-Common)
+include(LLCommon)
+include(LLImage)
+include(LLMath)
+include(LLMessage)
+include(LLRender)
+include(LLWindow)
+include(LLVFS)
+include(LLXML)
+include(LLXUIXML)
+
+include_directories(
+ ${LLCOMMON_INCLUDE_DIRS}
+ ${LLIMAGE_INCLUDE_DIRS}
+ ${LLMATH_INCLUDE_DIRS}
+ ${LLMESSAGE_INCLUDE_DIRS}
+ ${LLRENDER_INCLUDE_DIRS}
+ ${LLWINDOW_INCLUDE_DIRS}
+ ${LLVFS_INCLUDE_DIRS}
+ ${LLXML_INCLUDE_DIRS}
+ ${LLXUIXML_INCLUDE_DIRS}
+ )
+
+set(llui_SOURCE_FILES
+ llalertdialog.cpp
+ llbutton.cpp
+ llcheckboxctrl.cpp
+ llclipboard.cpp
+ llcombobox.cpp
+ llconsole.cpp
+ llcontainerview.cpp
+ llctrlselectioninterface.cpp
+ lldraghandle.cpp
+ lleditmenuhandler.cpp
+ llf32uictrl.cpp
+ llfiltereditor.cpp
+ llfloater.cpp
+ llfloaterreg.cpp
+ llfloaterreglistener.cpp
+ llflyoutbutton.cpp
+ llfocusmgr.cpp
+ llfunctorregistry.cpp
+ lliconctrl.cpp
+ llkeywords.cpp
+ lllayoutstack.cpp
+ lllineeditor.cpp
+ lllink.cpp
+ llmenugl.cpp
+ llmodaldialog.cpp
+ llmultifloater.cpp
+ llmultislider.cpp
+ llmultisliderctrl.cpp
+ llnotifications.cpp
+ llnotificationslistener.cpp
+ llpanel.cpp
+ llprogressbar.cpp
+ llradiogroup.cpp
+ llresizebar.cpp
+ llresizehandle.cpp
+ llresmgr.cpp
+ llrngwriter.cpp
+ llscrollbar.cpp
+ llscrollcontainer.cpp
+ llscrollingpanellist.cpp
+ llscrolllistcell.cpp
+ llscrolllistcolumn.cpp
+ llscrolllistctrl.cpp
+ llscrolllistitem.cpp
+ llsdparam.cpp
+ llsearcheditor.cpp
+ llslider.cpp
+ llsliderctrl.cpp
+ llspinctrl.cpp
+ llstatbar.cpp
+ llstatgraph.cpp
+ llstatview.cpp
+ llstyle.cpp
+ lltabcontainer.cpp
+ lltextbox.cpp
+ lltexteditor.cpp
+ lltextparser.cpp
+ lltransutil.cpp
+ llui.cpp
+ lluicolortable.cpp
+ lluictrl.cpp
+ lluictrlfactory.cpp
+ lluiimage.cpp
+ lluistring.cpp
+ llundo.cpp
+ llviewborder.cpp
+ llviewmodel.cpp
+ llview.cpp
+ llviewquery.cpp
+ )
+
+set(llui_HEADER_FILES
+ CMakeLists.txt
+
+ llalertdialog.h
+ llbutton.h
+ llcallbackmap.h
+ llcheckboxctrl.h
+ llclipboard.h
+ llcombobox.h
+ llconsole.h
+ llcontainerview.h
+ llctrlselectioninterface.h
+ lldraghandle.h
+ lleditmenuhandler.h
+ llf32uictrl.h
+ llfiltereditor.h
+ llfloater.h
+ llfloaterreg.h
+ llfloaterreglistener.h
+ llflyoutbutton.h
+ llfocusmgr.h
+ llfunctorregistry.h
+ llhandle.h
+ llhtmlhelp.h
+ lliconctrl.h
+ llkeywords.h
+ lllayoutstack.h
+ lllazyvalue.h
+ lllineeditor.h
+ lllink.h
+ llmenugl.h
+ llmodaldialog.h
+ llmultifloater.h
+ llmultisliderctrl.h
+ llmultislider.h
+ llnotifications.h
+ llnotificationslistener.h
+ llpanel.h
+ llprogressbar.h
+ llradiogroup.h
+ llresizebar.h
+ llresizehandle.h
+ llresmgr.h
+ llrngwriter.h
+ llsearcheditor.h
+ llscrollbar.h
+ llscrollcontainer.h
+ llscrollingpanellist.h
+ llscrolllistcell.h
+ llscrolllistcolumn.h
+ llscrolllistctrl.h
+ llscrolllistitem.h
+ llsdparam.h
+ llsliderctrl.h
+ llslider.h
+ llspinctrl.h
+ llstatbar.h
+ llstatgraph.h
+ llstatview.h
+ llstyle.h
+ lltabcontainer.h
+ lltextbox.h
+ lltexteditor.h
+ lltextparser.h
+ lltransutil.h
+ lluicolortable.h
+ lluiconstants.h
+ lluictrlfactory.h
+ lluictrl.h
+ lluifwd.h
+ llui.h
+ lluiimage.h
+ lluistring.h
+ llundo.h
+ llviewborder.h
+ llviewmodel.h
+ llview.h
+ llviewquery.h
+ )
+
+set_source_files_properties(${llui_HEADER_FILES}
+ PROPERTIES HEADER_FILE_ONLY TRUE)
+
+list(APPEND llui_SOURCE_FILES ${llui_HEADER_FILES})
+
+add_library (llui ${llui_SOURCE_FILES})
+# Libraries on which this library depends, needed for Linux builds
+# Sort by high-level to low-level
+target_link_libraries(llui
+ llrender
+ llwindow
+ llimage
+ llvfs # ugh, just for LLDir
+ llxuixml
+ llxml
+ llcommon # must be after llimage, llwindow, llrender
+ llmath
+ )
diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index a63b1b085c..8617ba940e 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -36,6 +36,7 @@ #include "llfloater.h" #include "llmultifloater.h" +#include "llfloaterreglistener.h" //******************************************************* @@ -45,6 +46,8 @@ LLFloaterReg::instance_map_t LLFloaterReg::sInstanceMap; LLFloaterReg::build_map_t LLFloaterReg::sBuildMap; std::map<std::string,std::string> LLFloaterReg::sGroupMap; +static LLFloaterRegListener sFloaterRegListener("LLFloaterReg"); + //******************************************************* //static diff --git a/indra/llui/llfloaterreg.h b/indra/llui/llfloaterreg.h index 7edac43c96..451bd1dbe3 100644 --- a/indra/llui/llfloaterreg.h +++ b/indra/llui/llfloaterreg.h @@ -70,6 +70,7 @@ public: typedef std::map<std::string, BuildData> build_map_t; private: + friend class LLFloaterRegListener; static instance_list_t sNullInstanceList; static instance_map_t sInstanceMap; static build_map_t sBuildMap; diff --git a/indra/llui/llfloaterreglistener.cpp b/indra/llui/llfloaterreglistener.cpp new file mode 100644 index 0000000000..cb8fa6dfda --- /dev/null +++ b/indra/llui/llfloaterreglistener.cpp @@ -0,0 +1,66 @@ +/** + * @file llfloaterreglistener.cpp + * @author Nat Goodspeed + * @date 2009-08-12 + * @brief Implementation for llfloaterreglistener. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llfloaterreglistener.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llfloaterreg.h" + +LLFloaterRegListener::LLFloaterRegListener(const std::string& pumpName): + LLDispatchListener(pumpName, "op") +{ + add("getBuildMap", &LLFloaterRegListener::getBuildMap, LLSD().insert("reply", LLSD())); + LLSD requiredName; + requiredName["name"] = LLSD(); + add("showInstance", &LLFloaterRegListener::showInstance, requiredName); + add("hideInstance", &LLFloaterRegListener::hideInstance, requiredName); + add("toggleInstance", &LLFloaterRegListener::toggleInstance, requiredName); +} + +void LLFloaterRegListener::getBuildMap(const LLSD& event) const +{ + // Honor the "reqid" convention by echoing event["reqid"] in our reply packet. + LLReqID reqID(event); + LLSD reply(reqID.makeResponse()); + // Build an LLSD map that mirrors sBuildMap. Since we have no good way to + // represent a C++ callable in LLSD, the only part of BuildData we can + // store is the filename. For each LLSD map entry, it would be more + // extensible to store a nested LLSD map containing a single key "file" -- + // but we don't bother, simply storing the string filename instead. + for (LLFloaterReg::build_map_t::const_iterator mi(LLFloaterReg::sBuildMap.begin()), + mend(LLFloaterReg::sBuildMap.end()); + mi != mend; ++mi) + { + reply[mi->first] = mi->second.mFile; + } + // Send the reply to the LLEventPump named in event["reply"]. + LLEventPumps::instance().obtain(event["reply"]).post(reply); +} + +void LLFloaterRegListener::showInstance(const LLSD& event) const +{ + LLFloaterReg::showInstance(event["name"], event["key"], event["focus"]); +} + +void LLFloaterRegListener::hideInstance(const LLSD& event) const +{ + LLFloaterReg::hideInstance(event["name"], event["key"]); +} + +void LLFloaterRegListener::toggleInstance(const LLSD& event) const +{ + LLFloaterReg::toggleInstance(event["name"], event["key"]); +} diff --git a/indra/llui/llfloaterreglistener.h b/indra/llui/llfloaterreglistener.h new file mode 100644 index 0000000000..58d2c07936 --- /dev/null +++ b/indra/llui/llfloaterreglistener.h @@ -0,0 +1,35 @@ +/** + * @file llfloaterreglistener.h + * @author Nat Goodspeed + * @date 2009-08-12 + * @brief Wrap (subset of) LLFloaterReg API with an event API + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLFLOATERREGLISTENER_H) +#define LL_LLFLOATERREGLISTENER_H + +#include "lleventdispatcher.h" +#include <string> + +class LLSD; + +/// Event API wrapper for LLFloaterReg +class LLFloaterRegListener: public LLDispatchListener +{ +public: + /// As all public LLFloaterReg methods are static, there's no point in + /// binding an LLFloaterReg instance. + LLFloaterRegListener(const std::string& pumpName); + +private: + void getBuildMap(const LLSD& event) const; + void showInstance(const LLSD& event) const; + void hideInstance(const LLSD& event) const; + void toggleInstance(const LLSD& event) const; +}; + +#endif /* ! defined(LL_LLFLOATERREGLISTENER_H) */ diff --git a/indra/llui/llfunctorregistry.cpp b/indra/llui/llfunctorregistry.cpp index 0c5b1655b1..5f9644f258 100644 --- a/indra/llui/llfunctorregistry.cpp +++ b/indra/llui/llfunctorregistry.cpp @@ -31,6 +31,7 @@ * $/LicenseInfo$ **/ +#include "linden_common.h" #include "llfunctorregistry.h" // This is a default functor always resident in the system. diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 9845b7e2ce..2b4aad5e83 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1,1513 +1,1516 @@ -/** -* @file llnotifications.cpp -* @brief Non-UI queue manager for keeping a prioritized list of notifications -* -* $LicenseInfo:firstyear=2008&license=viewergpl$ -* -* Copyright (c) 2008-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 "linden_common.h" - -#include "llnotifications.h" - -#include "lluictrl.h" -#include "lluictrlfactory.h" -#include "lldir.h" -#include "llsdserialize.h" -#include "lltrans.h" - -#include <algorithm> -#include <boost/regex.hpp> - - -const std::string NOTIFICATION_PERSIST_VERSION = "0.93"; - -// local channel for notification history -class LLNotificationHistoryChannel : public LLNotificationChannel -{ - LOG_CLASS(LLNotificationHistoryChannel); -public: - LLNotificationHistoryChannel(const std::string& filename) : - LLNotificationChannel("History", "Visible", &historyFilter, LLNotificationComparators::orderByUUID()), - mFileName(filename) - { - connectChanged(boost::bind(&LLNotificationHistoryChannel::historyHandler, this, _1)); - loadPersistentNotifications(); - } - -private: - bool historyHandler(const LLSD& payload) - { - // we ignore "load" messages, but rewrite the persistence file on any other - std::string sigtype = payload["sigtype"]; - if (sigtype != "load") - { - savePersistentNotifications(); - } - return false; - } - - // The history channel gets all notifications except those that have been cancelled - static bool historyFilter(LLNotificationPtr pNotification) - { - return !pNotification->isCancelled(); - } - - void savePersistentNotifications() - { - llinfos << "Saving open notifications to " << mFileName << llendl; - - llofstream notify_file(mFileName.c_str()); - if (!notify_file.is_open()) - { - llwarns << "Failed to open " << mFileName << llendl; - return; - } - - LLSD output; - output["version"] = NOTIFICATION_PERSIST_VERSION; - LLSD& data = output["data"]; - - for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it) - { - if (!LLNotifications::instance().templateExists((*it)->getName())) continue; - - // only store notifications flagged as persisting - LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate((*it)->getName()); - if (!templatep->mPersist) continue; - - data.append((*it)->asLLSD()); - } - - LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter(); - formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY); - } - - void loadPersistentNotifications() - { - llinfos << "Loading open notifications from " << mFileName << llendl; - - llifstream notify_file(mFileName.c_str()); - if (!notify_file.is_open()) - { - llwarns << "Failed to open " << mFileName << llendl; - return; - } - - LLSD input; - LLPointer<LLSDParser> parser = new LLSDXMLParser(); - if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0) - { - llwarns << "Failed to parse open notifications" << llendl; - return; - } - - if (input.isUndefined()) return; - std::string version = input["version"]; - if (version != NOTIFICATION_PERSIST_VERSION) - { - llwarns << "Bad open notifications version: " << version << llendl; - return; - } - LLSD& data = input["data"]; - if (data.isUndefined()) return; - - LLNotifications& instance = LLNotifications::instance(); - for (LLSD::array_const_iterator notification_it = data.beginArray(); - notification_it != data.endArray(); - ++notification_it) - { - instance.add(LLNotificationPtr(new LLNotification(*notification_it))); - } - } - - //virtual - void onDelete(LLNotificationPtr pNotification) - { - // we want to keep deleted notifications in our log - mItems.insert(pNotification); - - return; - } - -private: - std::string mFileName; -}; - -bool filterIgnoredNotifications(LLNotificationPtr notification) -{ - // filter everything if we are to ignore ALL - if(LLNotifications::instance().getIgnoreAllNotifications()) - { - return false; - } - - LLNotificationFormPtr form = notification->getForm(); - // Check to see if the user wants to ignore this alert - if (form->getIgnoreType() != LLNotificationForm::IGNORE_NO) - { - return LLUI::sSettingGroups["ignores"]->getBOOL(notification->getName()); - } - - return true; -} - -bool handleIgnoredNotification(const LLSD& payload) -{ - if (payload["sigtype"].asString() == "add") - { - LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); - if (!pNotif) return false; - - LLNotificationFormPtr form = pNotif->getForm(); - LLSD response; - switch(form->getIgnoreType()) - { - case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE: - response = pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON); - break; - case LLNotificationForm::IGNORE_WITH_LAST_RESPONSE: - response = LLUI::sSettingGroups["ignores"]->getLLSD("Default" + pNotif->getName()); - break; - case LLNotificationForm::IGNORE_SHOW_AGAIN: - break; - default: - return false; - } - pNotif->setIgnored(true); - pNotif->respond(response); - return true; // don't process this item any further - } - return false; -} - -namespace LLNotificationFilters -{ - // a sample filter - bool includeEverything(LLNotificationPtr p) - { - return true; - } -}; - -LLNotificationForm::LLNotificationForm() -: mFormData(LLSD::emptyArray()), - mIgnore(IGNORE_NO) -{ -} - - -LLNotificationForm::LLNotificationForm(const std::string& name, const LLXMLNodePtr xml_node) -: mFormData(LLSD::emptyArray()), - mIgnore(IGNORE_NO) -{ - if (!xml_node->hasName("form")) - { - llwarns << "Bad xml node for form: " << xml_node->getName() << llendl; - } - LLXMLNodePtr child = xml_node->getFirstChild(); - while(child) - { - child = LLNotifications::instance().checkForXMLTemplate(child); - - LLSD item_entry; - std::string element_name = child->getName()->mString; - - if (element_name == "ignore" ) - { - bool save_option = false; - child->getAttribute_bool("save_option", save_option); - if (!save_option) - { - mIgnore = IGNORE_WITH_DEFAULT_RESPONSE; - } - else - { - // remember last option chosen by user and automatically respond with that in the future - mIgnore = IGNORE_WITH_LAST_RESPONSE; - LLUI::sSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name)); - } - child->getAttributeString("text", mIgnoreMsg); - BOOL show_notification = TRUE; - LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Ignore notification with this name", TRUE); - } - else - { - // flatten xml form entry into single LLSD map with type==name - item_entry["type"] = element_name; - const LLXMLAttribList::iterator attrib_end = child->mAttributes.end(); - for(LLXMLAttribList::iterator attrib_it = child->mAttributes.begin(); - attrib_it != attrib_end; - ++attrib_it) - { - item_entry[std::string(attrib_it->second->getName()->mString)] = attrib_it->second->getValue(); - } - item_entry["value"] = child->getTextContents(); - mFormData.append(item_entry); - } - - child = child->getNextSibling(); - } -} - -LLNotificationForm::LLNotificationForm(const LLSD& sd) -{ - if (sd.isArray()) - { - mFormData = sd; - } - else - { - llwarns << "Invalid form data " << sd << llendl; - mFormData = LLSD::emptyArray(); - } -} - -LLSD LLNotificationForm::asLLSD() const -{ - return mFormData; -} - -LLSD LLNotificationForm::getElement(const std::string& element_name) -{ - for (LLSD::array_const_iterator it = mFormData.beginArray(); - it != mFormData.endArray(); - ++it) - { - if ((*it)["name"].asString() == element_name) return (*it); - } - return LLSD(); -} - - -bool LLNotificationForm::hasElement(const std::string& element_name) -{ - for (LLSD::array_const_iterator it = mFormData.beginArray(); - it != mFormData.endArray(); - ++it) - { - if ((*it)["name"].asString() == element_name) return true; - } - return false; -} - -void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value) -{ - LLSD element; - element["type"] = type; - element["name"] = name; - element["text"] = name; - element["value"] = value; - element["index"] = mFormData.size(); - mFormData.append(element); -} - -void LLNotificationForm::append(const LLSD& sub_form) -{ - if (sub_form.isArray()) - { - for (LLSD::array_const_iterator it = sub_form.beginArray(); - it != sub_form.endArray(); - ++it) - { - mFormData.append(*it); - } - } -} - -void LLNotificationForm::formatElements(const LLSD& substitutions) -{ - for (LLSD::array_iterator it = mFormData.beginArray(); - it != mFormData.endArray(); - ++it) - { - // format "text" component of each form element - if ((*it).has("text")) - { - std::string text = (*it)["text"].asString(); - LLStringUtil::format(text, substitutions); - (*it)["text"] = text; - } - if ((*it)["type"].asString() == "text" && (*it).has("value")) - { - std::string value = (*it)["value"].asString(); - LLStringUtil::format(value, substitutions); - (*it)["value"] = value; - } - } -} - -std::string LLNotificationForm::getDefaultOption() -{ - for (LLSD::array_const_iterator it = mFormData.beginArray(); - it != mFormData.endArray(); - ++it) - { - if ((*it)["default"]) return (*it)["name"].asString(); - } - return ""; -} - -LLNotificationTemplate::LLNotificationTemplate() : - mExpireSeconds(0), - mExpireOption(-1), - mURLOption(-1), - mURLOpenExternally(-1), - mUnique(false), - mPriority(NOTIFICATION_PRIORITY_NORMAL) -{ - mForm = LLNotificationFormPtr(new LLNotificationForm()); -} - -LLNotification::LLNotification(const LLNotification::Params& p) : - mTimestamp(p.time_stamp), - mSubstitutions(p.substitutions), - mPayload(p.payload), - mExpiresAt(0), - mTemporaryResponder(false), - mRespondedTo(false), - mPriority(p.priority), - mCancelled(false), - mIgnored(false) -{ - if (p.functor.name.isChosen()) - { - mResponseFunctorName = p.functor.name; - } - else if (p.functor.function.isChosen()) - { - mResponseFunctorName = LLUUID::generateNewID().asString(); - LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, p.functor.function()); - - mTemporaryResponder = true; - } - - mId.generate(); - init(p.name, p.form_elements); -} - - -LLNotification::LLNotification(const LLSD& sd) : - mTemporaryResponder(false), - mRespondedTo(false), - mCancelled(false), - mIgnored(false) -{ - mId.generate(); - mSubstitutions = sd["substitutions"]; - mPayload = sd["payload"]; - mTimestamp = sd["time"]; - mExpiresAt = sd["expiry"]; - mPriority = (ENotificationPriority)sd["priority"].asInteger(); - mResponseFunctorName = sd["responseFunctor"].asString(); - std::string templatename = sd["name"].asString(); - init(templatename, LLSD()); - // replace form with serialized version - mForm = LLNotificationFormPtr(new LLNotificationForm(sd["form"])); -} - - -LLSD LLNotification::asLLSD() -{ - LLSD output; - output["name"] = mTemplatep->mName; - output["form"] = getForm()->asLLSD(); - output["substitutions"] = mSubstitutions; - output["payload"] = mPayload; - output["time"] = mTimestamp; - output["expiry"] = mExpiresAt; - output["priority"] = (S32)mPriority; - output["responseFunctor"] = mResponseFunctorName; - return output; -} - -void LLNotification::update() -{ - LLNotifications::instance().update(shared_from_this()); -} - -void LLNotification::updateFrom(LLNotificationPtr other) -{ - // can only update from the same notification type - if (mTemplatep != other->mTemplatep) return; - - // NOTE: do NOT change the ID, since it is the key to - // this given instance, just update all the metadata - //mId = other->mId; - - mPayload = other->mPayload; - mSubstitutions = other->mSubstitutions; - mTimestamp = other->mTimestamp; - mExpiresAt = other->mExpiresAt; - mCancelled = other->mCancelled; - mIgnored = other->mIgnored; - mPriority = other->mPriority; - mForm = other->mForm; - mResponseFunctorName = other->mResponseFunctorName; - mRespondedTo = other->mRespondedTo; - mTemporaryResponder = other->mTemporaryResponder; - - update(); -} - -const LLNotificationFormPtr LLNotification::getForm() -{ - return mForm; -} - -void LLNotification::cancel() -{ - mCancelled = true; -} - -LLSD LLNotification::getResponseTemplate(EResponseTemplateType type) -{ - LLSD response = LLSD::emptyMap(); - for (S32 element_idx = 0; - element_idx < mForm->getNumElements(); - ++element_idx) - { - LLSD element = mForm->getElement(element_idx); - if (element.has("name")) - { - response[element["name"].asString()] = element["value"]; - } - - if ((type == WITH_DEFAULT_BUTTON) - && element["default"].asBoolean()) - { - response[element["name"].asString()] = true; - } - } - return response; -} - -//static -S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response) -{ - LLNotificationForm form(notification["form"]); - - for (S32 element_idx = 0; - element_idx < form.getNumElements(); - ++element_idx) - { - LLSD element = form.getElement(element_idx); - - // only look at buttons - if (element["type"].asString() == "button" - && response[element["name"].asString()].asBoolean()) - { - return element["index"].asInteger(); - } - } - - return -1; -} - -//static -std::string LLNotification::getSelectedOptionName(const LLSD& response) -{ - for (LLSD::map_const_iterator response_it = response.beginMap(); - response_it != response.endMap(); - ++response_it) - { - if (response_it->second.isBoolean() && response_it->second.asBoolean()) - { - return response_it->first; - } - } - return ""; -} - - -void LLNotification::respond(const LLSD& response) -{ - mRespondedTo = true; - // look up the functor - LLNotificationFunctorRegistry::ResponseFunctor functor = - LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName); - // and then call it - functor(asLLSD(), response); - - if (mTemporaryResponder) - { - LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName); - mResponseFunctorName = ""; - mTemporaryResponder = false; - } - - if (mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO) - { - BOOL show_notification = mIgnored ? FALSE : TRUE; - LLUI::sSettingGroups["ignores"]->setBOOL(getName(), show_notification); - if (mIgnored && mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE) - { - LLUI::sSettingGroups["ignores"]->setLLSD("Default" + getName(), response); - } - } - - update(); -} - -void LLNotification::setIgnored(bool ignore) -{ - mIgnored = ignore; -} - -void LLNotification::setResponseFunctor(std::string const &responseFunctorName) -{ - if (mTemporaryResponder) - // get rid of the old one - LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName); - mResponseFunctorName = responseFunctorName; - mTemporaryResponder = false; -} - -bool LLNotification::payloadContainsAll(const std::vector<std::string>& required_fields) const -{ - for(std::vector<std::string>::const_iterator required_fields_it = required_fields.begin(); - required_fields_it != required_fields.end(); - required_fields_it++) - { - std::string required_field_name = *required_fields_it; - if( ! getPayload().has(required_field_name)) - { - return false; // a required field was not found - } - } - return true; // all required fields were found -} - -bool LLNotification::isEquivalentTo(LLNotificationPtr that) const -{ - if (this->mTemplatep->mName != that->mTemplatep->mName) - { - return false; // must have the same template name or forget it - } - if (this->mTemplatep->mUnique) - { - // highlander bit sez there can only be one of these - return - this->payloadContainsAll(that->mTemplatep->mUniqueContext) && - that->payloadContainsAll(this->mTemplatep->mUniqueContext); - } - return false; -} - -void LLNotification::init(const std::string& template_name, const LLSD& form_elements) -{ - mTemplatep = LLNotifications::instance().getTemplate(template_name); - if (!mTemplatep) return; - - // add default substitutions - const LLStringUtil::format_map_t& default_args = LLTrans::getDefaultArgs(); - for (LLStringUtil::format_map_t::const_iterator iter = default_args.begin(); - iter != default_args.end(); ++iter) - { - mSubstitutions[iter->first] = iter->second; - } - mSubstitutions["_URL"] = getURL(); - mSubstitutions["_NAME"] = template_name; - // TODO: something like this so that a missing alert is sensible: - //mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions); - - mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm)); - mForm->append(form_elements); - - // apply substitution to form labels - mForm->formatElements(mSubstitutions); - - LLDate rightnow = LLDate::now(); - if (mTemplatep->mExpireSeconds) - { - mExpiresAt = LLDate(rightnow.secondsSinceEpoch() + mTemplatep->mExpireSeconds); - } - - if (mPriority == NOTIFICATION_PRIORITY_UNSPECIFIED) - { - mPriority = mTemplatep->mPriority; - } -} - -std::string LLNotification::summarize() const -{ - std::string s = "Notification("; - s += getName(); - s += ") : "; - s += mTemplatep ? mTemplatep->mMessage : ""; - // should also include timestamp and expiration time (but probably not payload) - return s; -} - -std::string LLNotification::getMessage() const -{ - // all our callers cache this result, so it gives us more flexibility - // to do the substitution at call time rather than attempting to - // cache it in the notification - if (!mTemplatep) - return std::string(); - - std::string message = mTemplatep->mMessage; - LLStringUtil::format(message, mSubstitutions); - return message; -} - -std::string LLNotification::getLabel() const -{ - std::string label = mTemplatep->mLabel; - LLStringUtil::format(label, mSubstitutions); - return (mTemplatep ? label : ""); -} - -std::string LLNotification::getURL() const -{ - if (!mTemplatep) - return std::string(); - std::string url = mTemplatep->mURL; - LLStringUtil::format(url, mSubstitutions); - return (mTemplatep ? url : ""); -} - -// ========================================================= -// LLNotificationChannel implementation -// --- -LLBoundListener LLNotificationChannelBase::connectChangedImpl(const LLEventListener& slot) -{ - // when someone wants to connect to a channel, we first throw them - // all of the notifications that are already in the channel - // we use a special signal called "load" in case the channel wants to care - // only about new notifications - for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it) - { - slot(LLSD().insert("sigtype", "load").insert("id", (*it)->id())); - } - // and then connect the signal so that all future notifications will also be - // forwarded. - return mChanged.connect(slot); -} - -LLBoundListener LLNotificationChannelBase::connectAtFrontChangedImpl(const LLEventListener& slot) -{ - for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it) - { - slot(LLSD().insert("sigtype", "load").insert("id", (*it)->id())); - } - return mChanged.connect(slot, boost::signals2::at_front); -} - -LLBoundListener LLNotificationChannelBase::connectPassedFilterImpl(const LLEventListener& slot) -{ - // these two filters only fire for notifications added after the current one, because - // they don't participate in the hierarchy. - return mPassedFilter.connect(slot); -} - -LLBoundListener LLNotificationChannelBase::connectFailedFilterImpl(const LLEventListener& slot) -{ - return mFailedFilter.connect(slot); -} - -// external call, conforms to our standard signature -bool LLNotificationChannelBase::updateItem(const LLSD& payload) -{ - // first check to see if it's in the master list - LLNotificationPtr pNotification = LLNotifications::instance().find(payload["id"]); - if (!pNotification) - return false; // not found - - return updateItem(payload, pNotification); -} - - -//FIX QUIT NOT WORKING - - -// internal call, for use in avoiding lookup -bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPtr pNotification) -{ - std::string cmd = payload["sigtype"]; - LLNotificationSet::iterator foundItem = mItems.find(pNotification); - bool wasFound = (foundItem != mItems.end()); - bool passesFilter = mFilter(pNotification); - - // first, we offer the result of the filter test to the simple - // signals for pass/fail. One of these is guaranteed to be called. - // If either signal returns true, the change processing is NOT performed - // (so don't return true unless you know what you're doing!) - bool abortProcessing = false; - if (passesFilter) - { - abortProcessing = mPassedFilter(payload); - } - else - { - abortProcessing = mFailedFilter(payload); - } - - if (abortProcessing) - { - return true; - } - - if (cmd == "load") - { - // should be no reason we'd ever get a load if we already have it - // if passes filter send a load message, else do nothing - assert(!wasFound); - if (passesFilter) - { - // not in our list, add it and say so - mItems.insert(pNotification); - abortProcessing = mChanged(payload); - onLoad(pNotification); - } - } - else if (cmd == "change") - { - // if it passes filter now and was found, we just send a change message - // if it passes filter now and wasn't found, we have to add it - // if it doesn't pass filter and wasn't found, we do nothing - // if it doesn't pass filter and was found, we need to delete it - if (passesFilter) - { - if (wasFound) - { - // it already existed, so this is a change - // since it changed in place, all we have to do is resend the signal - abortProcessing = mChanged(payload); - onChange(pNotification); - } - else - { - // not in our list, add it and say so - mItems.insert(pNotification); - // our payload is const, so make a copy before changing it - LLSD newpayload = payload; - newpayload["sigtype"] = "add"; - abortProcessing = mChanged(newpayload); - onChange(pNotification); - } - } - else - { - if (wasFound) - { - // it already existed, so this is a delete - mItems.erase(pNotification); - // our payload is const, so make a copy before changing it - LLSD newpayload = payload; - newpayload["sigtype"] = "delete"; - abortProcessing = mChanged(newpayload); - onChange(pNotification); - } - // didn't pass, not on our list, do nothing - } - } - else if (cmd == "add") - { - // should be no reason we'd ever get an add if we already have it - // if passes filter send an add message, else do nothing - assert(!wasFound); - if (passesFilter) - { - // not in our list, add it and say so - mItems.insert(pNotification); - abortProcessing = mChanged(payload); - onAdd(pNotification); - } - } - else if (cmd == "delete") - { - // if we have it in our list, pass on the delete, then delete it, else do nothing - if (wasFound) - { - abortProcessing = mChanged(payload); - mItems.erase(pNotification); - onDelete(pNotification); - } - } - return abortProcessing; -} - -/* static */ -LLNotificationChannelPtr LLNotificationChannel::buildChannel(const std::string& name, - const std::string& parent, - LLNotificationFilter filter, - LLNotificationComparator comparator) -{ - // note: this is not a leak; notifications are self-registering. - // This factory helps to prevent excess deletions by making sure all smart - // pointers to notification channels come from the same source - new LLNotificationChannel(name, parent, filter, comparator); - return LLNotifications::instance().getChannel(name); -} - - -LLNotificationChannel::LLNotificationChannel(const std::string& name, - const std::string& parent, - LLNotificationFilter filter, - LLNotificationComparator comparator) : -LLNotificationChannelBase(filter, comparator), -mName(name), -mParent(parent) -{ - // store myself in the channel map - LLNotifications::instance().addChannel(LLNotificationChannelPtr(this)); - // bind to notification broadcast - if (parent.empty()) - { - LLNotifications::instance().connectChanged( - boost::bind(&LLNotificationChannelBase::updateItem, this, _1)); - } - else - { - LLNotificationChannelPtr p = LLNotifications::instance().getChannel(parent); - p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1)); - } -} - - -void LLNotificationChannel::setComparator(LLNotificationComparator comparator) -{ - mComparator = comparator; - LLNotificationSet s2(mComparator); - s2.insert(mItems.begin(), mItems.end()); - mItems.swap(s2); - - // notify clients that we've been resorted - mChanged(LLSD().insert("sigtype", "sort")); -} - -bool LLNotificationChannel::isEmpty() const -{ - return mItems.empty(); -} - -LLNotificationChannel::Iterator LLNotificationChannel::begin() -{ - return mItems.begin(); -} - -LLNotificationChannel::Iterator LLNotificationChannel::end() -{ - return mItems.end(); -} - -std::string LLNotificationChannel::summarize() -{ - std::string s("Channel '"); - s += mName; - s += "'\n "; - for (LLNotificationChannel::Iterator it = begin(); it != end(); ++it) - { - s += (*it)->summarize(); - s += "\n "; - } - return s; -} - - -// --- -// END OF LLNotificationChannel implementation -// ========================================================= - - -// ========================================================= -// LLNotifications implementation -// --- -LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything, - LLNotificationComparators::orderByUUID()), - mIgnoreAllNotifications(false) -{ - LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2)); -} - - -// The expiration channel gets all notifications that are cancelled -bool LLNotifications::expirationFilter(LLNotificationPtr pNotification) -{ - return pNotification->isCancelled() || pNotification->isRespondedTo(); -} - -bool LLNotifications::expirationHandler(const LLSD& payload) -{ - if (payload["sigtype"].asString() != "delete") - { - // anything added to this channel actually should be deleted from the master - cancel(find(payload["id"])); - return true; // don't process this item any further - } - return false; -} - -bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif) -{ - if (!pNotif->hasUniquenessConstraints()) - { - return true; - } - - // checks against existing unique notifications - for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName()); - existing_it != mUniqueNotifications.end(); - ++existing_it) - { - LLNotificationPtr existing_notification = existing_it->second; - if (pNotif != existing_notification - && pNotif->isEquivalentTo(existing_notification)) - { - return false; - } - } - - return true; -} - -bool LLNotifications::uniqueHandler(const LLSD& payload) -{ - LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); - if (pNotif && pNotif->hasUniquenessConstraints()) - { - if (payload["sigtype"].asString() == "add") - { - // not a duplicate according to uniqueness criteria, so we keep it - // and store it for future uniqueness checks - mUniqueNotifications.insert(std::make_pair(pNotif->getName(), pNotif)); - } - else if (payload["sigtype"].asString() == "delete") - { - mUniqueNotifications.erase(pNotif->getName()); - } - } - - return false; -} - -bool LLNotifications::failedUniquenessTest(const LLSD& payload) -{ - LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); - - if (!pNotif || !pNotif->hasUniquenessConstraints()) - { - return false; - } - - // checks against existing unique notifications - for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName()); - existing_it != mUniqueNotifications.end(); - ++existing_it) - { - LLNotificationPtr existing_notification = existing_it->second; - if (pNotif != existing_notification - && pNotif->isEquivalentTo(existing_notification)) - { - // copy notification instance data over to oldest instance - // of this unique notification and update it - existing_notification->updateFrom(pNotif); - // then delete the new one - pNotif->cancel(); - } - } - - return false; -} - - -void LLNotifications::addChannel(LLNotificationChannelPtr pChan) -{ - mChannels[pChan->getName()] = pChan; -} - -LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName) -{ - ChannelMap::iterator p = mChannels.find(channelName); - if(p == mChannels.end()) - { - llerrs << "Did not find channel named " << channelName << llendl; - } - return p->second; -} - - -// this function is called once at construction time, after the object is constructed. -void LLNotifications::initSingleton() -{ - loadTemplates(); - createDefaultChannels(); -} - -void LLNotifications::createDefaultChannels() -{ - // now construct the various channels AFTER loading the notifications, - // because the history channel is going to rewrite the stored notifications file - LLNotificationChannel::buildChannel("Expiration", "", - boost::bind(&LLNotifications::expirationFilter, this, _1)); - LLNotificationChannel::buildChannel("Unexpired", "", - !boost::bind(&LLNotifications::expirationFilter, this, _1)); // use negated bind - LLNotificationChannel::buildChannel("Unique", "Unexpired", - boost::bind(&LLNotifications::uniqueFilter, this, _1)); - LLNotificationChannel::buildChannel("Ignore", "Unique", - filterIgnoredNotifications); - LLNotificationChannel::buildChannel("Visible", "Ignore", - &LLNotificationFilters::includeEverything); - - // create special history channel - //std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" ); - // use ^^^ when done debugging notifications serialization - std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_USER_SETTINGS, "open_notifications.xml" ); - // this isn't a leak, don't worry about the empty "new" - new LLNotificationHistoryChannel(notifications_log_file); - - // connect action methods to these channels - LLNotifications::instance().getChannel("Expiration")-> - connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1)); - // uniqueHandler slot should be added as first slot of the signal due to - // usage LLStopWhenHandled combiner in LLStandardSignal - LLNotifications::instance().getChannel("Unique")-> - connectAtFrontChanged(boost::bind(&LLNotifications::uniqueHandler, this, _1)); -// failedUniquenessTest slot isn't necessary -// LLNotifications::instance().getChannel("Unique")-> -// connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1)); - LLNotifications::instance().getChannel("Ignore")-> - connectFailedFilter(&handleIgnoredNotification); -} - -bool LLNotifications::addTemplate(const std::string &name, - LLNotificationTemplatePtr theTemplate) -{ - if (mTemplates.count(name)) - { - llwarns << "LLNotifications -- attempted to add template '" << name << "' twice." << llendl; - return false; - } - mTemplates[name] = theTemplate; - return true; -} - -LLNotificationTemplatePtr LLNotifications::getTemplate(const std::string& name) -{ - if (mTemplates.count(name)) - { - return mTemplates[name]; - } - else - { - return mTemplates["MissingAlert"]; - } -} - -bool LLNotifications::templateExists(const std::string& name) -{ - return (mTemplates.count(name) != 0); -} - -void LLNotifications::clearTemplates() -{ - mTemplates.clear(); -} - -void LLNotifications::forceResponse(const LLNotification::Params& params, S32 option) -{ - LLNotificationPtr temp_notify(new LLNotification(params)); - LLSD response = temp_notify->getResponseTemplate(); - LLSD selected_item = temp_notify->getForm()->getElement(option); - - if (selected_item.isUndefined()) - { - llwarns << "Invalid option" << option << " for notification " << (std::string)params.name << llendl; - return; - } - response[selected_item["name"].asString()] = true; - - temp_notify->respond(response); -} - -LLNotifications::TemplateNames LLNotifications::getTemplateNames() const -{ - TemplateNames names; - for (TemplateMap::const_iterator it = mTemplates.begin(); it != mTemplates.end(); ++it) - { - names.push_back(it->first); - } - return names; -} - -typedef std::map<std::string, std::string> StringMap; -void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements) -{ - //llwarns << "replaceSubstitutionStrings" << llendl; - // walk the list of attributes looking for replacements - for (LLXMLAttribList::iterator it=node->mAttributes.begin(); - it != node->mAttributes.end(); ++it) - { - std::string value = it->second->getValue(); - if (value[0] == '$') - { - value.erase(0, 1); // trim off the $ - std::string replacement; - StringMap::const_iterator found = replacements.find(value); - if (found != replacements.end()) - { - replacement = found->second; - //llwarns << "replaceSubstituionStrings: value: " << value << " repl: " << replacement << llendl; - - it->second->setValue(replacement); - } - else - { - llwarns << "replaceSubstituionStrings FAILURE: value: " << value << " repl: " << replacement << llendl; - } - } - } - - // now walk the list of children and call this recursively. - for (LLXMLNodePtr child = node->getFirstChild(); - child.notNull(); child = child->getNextSibling()) - { - replaceSubstitutionStrings(child, replacements); - } -} - -// private to this file -// returns true if the template request was invalid and there's nothing else we -// can do with this node, false if you should keep processing (it may have -// replaced the contents of the node referred to) -LLXMLNodePtr LLNotifications::checkForXMLTemplate(LLXMLNodePtr item) -{ - if (item->hasName("usetemplate")) - { - std::string replacementName; - if (item->getAttributeString("name", replacementName)) - { - StringMap replacements; - for (LLXMLAttribList::const_iterator it=item->mAttributes.begin(); - it != item->mAttributes.end(); ++it) - { - replacements[it->second->getName()->mString] = it->second->getValue(); - } - if (mXmlTemplates.count(replacementName)) - { - item=LLXMLNode::replaceNode(item, mXmlTemplates[replacementName]); - - // walk the nodes looking for $(substitution) here and replace - replaceSubstitutionStrings(item, replacements); - } - else - { - llwarns << "XML template lookup failure on '" << replacementName << "' " << llendl; - } - } - } - return item; -} - -bool LLNotifications::loadTemplates() -{ - const std::string xml_filename = "notifications.xml"; - LLXMLNodePtr root; - - BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root); - - if (!success || root.isNull() || !root->hasName( "notifications" )) - { - llerrs << "Problem reading UI Notifications file: " << xml_filename << llendl; - return false; - } - - clearTemplates(); - - for (LLXMLNodePtr item = root->getFirstChild(); - item.notNull(); item = item->getNextSibling()) - { - // we do this FIRST so that item can be changed if we - // encounter a usetemplate -- we just replace the - // current xml node and keep processing - item = checkForXMLTemplate(item); - - if (item->hasName("global")) - { - std::string global_name; - if (item->getAttributeString("name", global_name)) - { - mGlobalStrings[global_name] = item->getTextContents(); - } - continue; - } - - if (item->hasName("template")) - { - // store an xml template; templates must have a single node (can contain - // other nodes) - std::string name; - item->getAttributeString("name", name); - LLXMLNodePtr ptr = item->getFirstChild(); - mXmlTemplates[name] = ptr; - continue; - } - - if (!item->hasName("notification")) - { - llwarns << "Unexpected entity " << item->getName()->mString << - " found in " << xml_filename << llendl; - continue; - } - - // now we know we have a notification entry, so let's build it - LLNotificationTemplatePtr pTemplate(new LLNotificationTemplate()); - - if (!item->getAttributeString("name", pTemplate->mName)) - { - llwarns << "Unable to parse notification with no name" << llendl; - continue; - } - - //llinfos << "Parsing " << pTemplate->mName << llendl; - - pTemplate->mMessage = item->getTextContents(); - pTemplate->mDefaultFunctor = pTemplate->mName; - item->getAttributeString("type", pTemplate->mType); - item->getAttributeString("icon", pTemplate->mIcon); - item->getAttributeString("label", pTemplate->mLabel); - item->getAttributeU32("duration", pTemplate->mExpireSeconds); - item->getAttributeU32("expireOption", pTemplate->mExpireOption); - - std::string priority; - item->getAttributeString("priority", priority); - pTemplate->mPriority = NOTIFICATION_PRIORITY_NORMAL; - if (!priority.empty()) - { - if (priority == "low") pTemplate->mPriority = NOTIFICATION_PRIORITY_LOW; - if (priority == "normal") pTemplate->mPriority = NOTIFICATION_PRIORITY_NORMAL; - if (priority == "high") pTemplate->mPriority = NOTIFICATION_PRIORITY_HIGH; - if (priority == "critical") pTemplate->mPriority = NOTIFICATION_PRIORITY_CRITICAL; - } - - item->getAttributeString("functor", pTemplate->mDefaultFunctor); - - BOOL persist = false; - item->getAttributeBOOL("persist", persist); - pTemplate->mPersist = persist; - - std::string sound; - item->getAttributeString("sound", sound); - if (!sound.empty()) - { - // test for bad sound effect name / missing effect - if (LLUI::sSettingGroups["config"]->controlExists(sound)) - { - pTemplate->mSoundEffect = - LLUUID(LLUI::sSettingGroups["config"]->getString(sound)); - } - else - { - llwarns << "Unknown sound effect control name " << sound - << llendl; - } - } - - for (LLXMLNodePtr child = item->getFirstChild(); - !child.isNull(); child = child->getNextSibling()) - { - child = checkForXMLTemplate(child); - - // <url> - if (child->hasName("url")) - { - pTemplate->mURL = child->getTextContents(); - child->getAttributeU32("option", pTemplate->mURLOption); - child->getAttributeU32("openexternally", pTemplate->mURLOpenExternally); - } - - if (child->hasName("unique")) - { - pTemplate->mUnique = true; - for (LLXMLNodePtr formitem = child->getFirstChild(); - !formitem.isNull(); formitem = formitem->getNextSibling()) - { - if (formitem->hasName("context")) - { - std::string key; - formitem->getAttributeString("key", key); - pTemplate->mUniqueContext.push_back(key); - //llwarns << "adding " << key << " to unique context" << llendl; - } - else - { - llwarns << "'unique' has unrecognized subelement " - << formitem->getName()->mString << llendl; - } - } - } - - // <form> - if (child->hasName("form")) - { - pTemplate->mForm = LLNotificationFormPtr(new LLNotificationForm(pTemplate->mName, child)); - } - } - addTemplate(pTemplate->mName, pTemplate); - } - - //std::ostringstream ostream; - //root->writeToOstream(ostream, "\n "); - //llwarns << ostream.str() << llendl; - - return true; -} - -// Add a simple notification (from XUI) -void LLNotifications::addFromCallback(const LLSD& name) -{ - add(LLNotification::Params().name(name.asString())); -} - -// we provide a couple of simple add notification functions so that it's reasonable to create notifications in one line -LLNotificationPtr LLNotifications::add(const std::string& name, - const LLSD& substitutions, - const LLSD& payload) -{ - LLNotification::Params::Functor functor_p; - functor_p.name = name; - return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p)); -} - -LLNotificationPtr LLNotifications::add(const std::string& name, - const LLSD& substitutions, - const LLSD& payload, - const std::string& functor_name) -{ - LLNotification::Params::Functor functor_p; - functor_p.name = functor_name; - return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p)); -} - -LLNotificationPtr LLNotifications::add(const std::string& name, - const LLSD& substitutions, - const LLSD& payload, - LLNotificationFunctorRegistry::ResponseFunctor functor) -{ - LLNotification::Params::Functor functor_p; - functor_p.function = functor; - return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p)); -} - -// generalized add function that takes a parameter block object for more complex instantiations -LLNotificationPtr LLNotifications::add(const LLNotification::Params& p) -{ - LLNotificationPtr pNotif(new LLNotification(p)); - add(pNotif); - return pNotif; -} - - -void LLNotifications::add(const LLNotificationPtr pNotif) -{ - // first see if we already have it -- if so, that's a problem - LLNotificationSet::iterator it=mItems.find(pNotif); - if (it != mItems.end()) - { - llerrs << "Notification added a second time to the master notification channel." << llendl; - } - - updateItem(LLSD().insert("sigtype", "add").insert("id", pNotif->id()), pNotif); -} - -void LLNotifications::cancel(LLNotificationPtr pNotif) -{ - LLNotificationSet::iterator it=mItems.find(pNotif); - if (it == mItems.end()) - { - llerrs << "Attempted to delete nonexistent notification " << pNotif->getName() << llendl; - } - updateItem(LLSD().insert("sigtype", "delete").insert("id", pNotif->id()), pNotif); - pNotif->cancel(); -} - -void LLNotifications::update(const LLNotificationPtr pNotif) -{ - LLNotificationSet::iterator it=mItems.find(pNotif); - if (it != mItems.end()) - { - updateItem(LLSD().insert("sigtype", "change").insert("id", pNotif->id()), pNotif); - } -} - - -LLNotificationPtr LLNotifications::find(LLUUID uuid) -{ - LLNotificationPtr target = LLNotificationPtr(new LLNotification(uuid)); - LLNotificationSet::iterator it=mItems.find(target); - if (it == mItems.end()) - { - llwarns << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << llendl; - return LLNotificationPtr((LLNotification*)NULL); - } - else - { - return *it; - } -} - -void LLNotifications::forEachNotification(NotificationProcess process) -{ - std::for_each(mItems.begin(), mItems.end(), process); -} - -std::string LLNotifications::getGlobalString(const std::string& key) const -{ - GlobalStringMap::const_iterator it = mGlobalStrings.find(key); - if (it != mGlobalStrings.end()) - { - return it->second; - } - else - { - // if we don't have the key as a global, return the key itself so that the error - // is self-diagnosing. - return key; - } -} - -void LLNotifications::setIgnoreAllNotifications(bool setting) -{ - mIgnoreAllNotifications = setting; -} -bool LLNotifications::getIgnoreAllNotifications() -{ - return mIgnoreAllNotifications; -} - -// --- -// END OF LLNotifications implementation -// ========================================================= - -std::ostream& operator<<(std::ostream& s, const LLNotification& notification) -{ - s << notification.summarize(); - return s; -} - +/**
+* @file llnotifications.cpp
+* @brief Non-UI queue manager for keeping a prioritized list of notifications
+*
+* $LicenseInfo:firstyear=2008&license=viewergpl$
+*
+* Copyright (c) 2008-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 "linden_common.h"
+
+#include "llnotifications.h"
+
+#include "lluictrl.h"
+#include "lluictrlfactory.h"
+#include "lldir.h"
+#include "llsdserialize.h"
+#include "lltrans.h"
+#include "llnotificationslistener.h"
+
+#include <algorithm>
+#include <boost/regex.hpp>
+
+
+const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
+
+// local channel for notification history
+class LLNotificationHistoryChannel : public LLNotificationChannel
+{
+ LOG_CLASS(LLNotificationHistoryChannel);
+public:
+ LLNotificationHistoryChannel(const std::string& filename) :
+ LLNotificationChannel("History", "Visible", &historyFilter, LLNotificationComparators::orderByUUID()),
+ mFileName(filename)
+ {
+ connectChanged(boost::bind(&LLNotificationHistoryChannel::historyHandler, this, _1));
+ loadPersistentNotifications();
+ }
+
+private:
+ bool historyHandler(const LLSD& payload)
+ {
+ // we ignore "load" messages, but rewrite the persistence file on any other
+ std::string sigtype = payload["sigtype"];
+ if (sigtype != "load")
+ {
+ savePersistentNotifications();
+ }
+ return false;
+ }
+
+ // The history channel gets all notifications except those that have been cancelled
+ static bool historyFilter(LLNotificationPtr pNotification)
+ {
+ return !pNotification->isCancelled();
+ }
+
+ void savePersistentNotifications()
+ {
+ llinfos << "Saving open notifications to " << mFileName << llendl;
+
+ llofstream notify_file(mFileName.c_str());
+ if (!notify_file.is_open())
+ {
+ llwarns << "Failed to open " << mFileName << llendl;
+ return;
+ }
+
+ LLSD output;
+ output["version"] = NOTIFICATION_PERSIST_VERSION;
+ LLSD& data = output["data"];
+
+ for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
+ {
+ if (!LLNotifications::instance().templateExists((*it)->getName())) continue;
+
+ // only store notifications flagged as persisting
+ LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate((*it)->getName());
+ if (!templatep->mPersist) continue;
+
+ data.append((*it)->asLLSD());
+ }
+
+ LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
+ formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY);
+ }
+
+ void loadPersistentNotifications()
+ {
+ llinfos << "Loading open notifications from " << mFileName << llendl;
+
+ llifstream notify_file(mFileName.c_str());
+ if (!notify_file.is_open())
+ {
+ llwarns << "Failed to open " << mFileName << llendl;
+ return;
+ }
+
+ LLSD input;
+ LLPointer<LLSDParser> parser = new LLSDXMLParser();
+ if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0)
+ {
+ llwarns << "Failed to parse open notifications" << llendl;
+ return;
+ }
+
+ if (input.isUndefined()) return;
+ std::string version = input["version"];
+ if (version != NOTIFICATION_PERSIST_VERSION)
+ {
+ llwarns << "Bad open notifications version: " << version << llendl;
+ return;
+ }
+ LLSD& data = input["data"];
+ if (data.isUndefined()) return;
+
+ LLNotifications& instance = LLNotifications::instance();
+ for (LLSD::array_const_iterator notification_it = data.beginArray();
+ notification_it != data.endArray();
+ ++notification_it)
+ {
+ instance.add(LLNotificationPtr(new LLNotification(*notification_it)));
+ }
+ }
+
+ //virtual
+ void onDelete(LLNotificationPtr pNotification)
+ {
+ // we want to keep deleted notifications in our log
+ mItems.insert(pNotification);
+
+ return;
+ }
+
+private:
+ std::string mFileName;
+};
+
+bool filterIgnoredNotifications(LLNotificationPtr notification)
+{
+ // filter everything if we are to ignore ALL
+ if(LLNotifications::instance().getIgnoreAllNotifications())
+ {
+ return false;
+ }
+
+ LLNotificationFormPtr form = notification->getForm();
+ // Check to see if the user wants to ignore this alert
+ if (form->getIgnoreType() != LLNotificationForm::IGNORE_NO)
+ {
+ return LLUI::sSettingGroups["ignores"]->getBOOL(notification->getName());
+ }
+
+ return true;
+}
+
+bool handleIgnoredNotification(const LLSD& payload)
+{
+ if (payload["sigtype"].asString() == "add")
+ {
+ LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
+ if (!pNotif) return false;
+
+ LLNotificationFormPtr form = pNotif->getForm();
+ LLSD response;
+ switch(form->getIgnoreType())
+ {
+ case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE:
+ response = pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON);
+ break;
+ case LLNotificationForm::IGNORE_WITH_LAST_RESPONSE:
+ response = LLUI::sSettingGroups["ignores"]->getLLSD("Default" + pNotif->getName());
+ break;
+ case LLNotificationForm::IGNORE_SHOW_AGAIN:
+ break;
+ default:
+ return false;
+ }
+ pNotif->setIgnored(true);
+ pNotif->respond(response);
+ return true; // don't process this item any further
+ }
+ return false;
+}
+
+namespace LLNotificationFilters
+{
+ // a sample filter
+ bool includeEverything(LLNotificationPtr p)
+ {
+ return true;
+ }
+};
+
+LLNotificationForm::LLNotificationForm()
+: mFormData(LLSD::emptyArray()),
+ mIgnore(IGNORE_NO)
+{
+}
+
+
+LLNotificationForm::LLNotificationForm(const std::string& name, const LLXMLNodePtr xml_node)
+: mFormData(LLSD::emptyArray()),
+ mIgnore(IGNORE_NO)
+{
+ if (!xml_node->hasName("form"))
+ {
+ llwarns << "Bad xml node for form: " << xml_node->getName() << llendl;
+ }
+ LLXMLNodePtr child = xml_node->getFirstChild();
+ while(child)
+ {
+ child = LLNotifications::instance().checkForXMLTemplate(child);
+
+ LLSD item_entry;
+ std::string element_name = child->getName()->mString;
+
+ if (element_name == "ignore" )
+ {
+ bool save_option = false;
+ child->getAttribute_bool("save_option", save_option);
+ if (!save_option)
+ {
+ mIgnore = IGNORE_WITH_DEFAULT_RESPONSE;
+ }
+ else
+ {
+ // remember last option chosen by user and automatically respond with that in the future
+ mIgnore = IGNORE_WITH_LAST_RESPONSE;
+ LLUI::sSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name));
+ }
+ child->getAttributeString("text", mIgnoreMsg);
+ BOOL show_notification = TRUE;
+ LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Ignore notification with this name", TRUE);
+ }
+ else
+ {
+ // flatten xml form entry into single LLSD map with type==name
+ item_entry["type"] = element_name;
+ const LLXMLAttribList::iterator attrib_end = child->mAttributes.end();
+ for(LLXMLAttribList::iterator attrib_it = child->mAttributes.begin();
+ attrib_it != attrib_end;
+ ++attrib_it)
+ {
+ item_entry[std::string(attrib_it->second->getName()->mString)] = attrib_it->second->getValue();
+ }
+ item_entry["value"] = child->getTextContents();
+ mFormData.append(item_entry);
+ }
+
+ child = child->getNextSibling();
+ }
+}
+
+LLNotificationForm::LLNotificationForm(const LLSD& sd)
+{
+ if (sd.isArray())
+ {
+ mFormData = sd;
+ }
+ else
+ {
+ llwarns << "Invalid form data " << sd << llendl;
+ mFormData = LLSD::emptyArray();
+ }
+}
+
+LLSD LLNotificationForm::asLLSD() const
+{
+ return mFormData;
+}
+
+LLSD LLNotificationForm::getElement(const std::string& element_name)
+{
+ for (LLSD::array_const_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ if ((*it)["name"].asString() == element_name) return (*it);
+ }
+ return LLSD();
+}
+
+
+bool LLNotificationForm::hasElement(const std::string& element_name)
+{
+ for (LLSD::array_const_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ if ((*it)["name"].asString() == element_name) return true;
+ }
+ return false;
+}
+
+void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value)
+{
+ LLSD element;
+ element["type"] = type;
+ element["name"] = name;
+ element["text"] = name;
+ element["value"] = value;
+ element["index"] = mFormData.size();
+ mFormData.append(element);
+}
+
+void LLNotificationForm::append(const LLSD& sub_form)
+{
+ if (sub_form.isArray())
+ {
+ for (LLSD::array_const_iterator it = sub_form.beginArray();
+ it != sub_form.endArray();
+ ++it)
+ {
+ mFormData.append(*it);
+ }
+ }
+}
+
+void LLNotificationForm::formatElements(const LLSD& substitutions)
+{
+ for (LLSD::array_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ // format "text" component of each form element
+ if ((*it).has("text"))
+ {
+ std::string text = (*it)["text"].asString();
+ LLStringUtil::format(text, substitutions);
+ (*it)["text"] = text;
+ }
+ if ((*it)["type"].asString() == "text" && (*it).has("value"))
+ {
+ std::string value = (*it)["value"].asString();
+ LLStringUtil::format(value, substitutions);
+ (*it)["value"] = value;
+ }
+ }
+}
+
+std::string LLNotificationForm::getDefaultOption()
+{
+ for (LLSD::array_const_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ if ((*it)["default"]) return (*it)["name"].asString();
+ }
+ return "";
+}
+
+LLNotificationTemplate::LLNotificationTemplate() :
+ mExpireSeconds(0),
+ mExpireOption(-1),
+ mURLOption(-1),
+ mURLOpenExternally(-1),
+ mUnique(false),
+ mPriority(NOTIFICATION_PRIORITY_NORMAL)
+{
+ mForm = LLNotificationFormPtr(new LLNotificationForm());
+}
+
+LLNotification::LLNotification(const LLNotification::Params& p) :
+ mTimestamp(p.time_stamp),
+ mSubstitutions(p.substitutions),
+ mPayload(p.payload),
+ mExpiresAt(0),
+ mTemporaryResponder(false),
+ mRespondedTo(false),
+ mPriority(p.priority),
+ mCancelled(false),
+ mIgnored(false)
+{
+ if (p.functor.name.isChosen())
+ {
+ mResponseFunctorName = p.functor.name;
+ }
+ else if (p.functor.function.isChosen())
+ {
+ mResponseFunctorName = LLUUID::generateNewID().asString();
+ LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, p.functor.function());
+
+ mTemporaryResponder = true;
+ }
+
+ mId.generate();
+ init(p.name, p.form_elements);
+}
+
+
+LLNotification::LLNotification(const LLSD& sd) :
+ mTemporaryResponder(false),
+ mRespondedTo(false),
+ mCancelled(false),
+ mIgnored(false)
+{
+ mId.generate();
+ mSubstitutions = sd["substitutions"];
+ mPayload = sd["payload"];
+ mTimestamp = sd["time"];
+ mExpiresAt = sd["expiry"];
+ mPriority = (ENotificationPriority)sd["priority"].asInteger();
+ mResponseFunctorName = sd["responseFunctor"].asString();
+ std::string templatename = sd["name"].asString();
+ init(templatename, LLSD());
+ // replace form with serialized version
+ mForm = LLNotificationFormPtr(new LLNotificationForm(sd["form"]));
+}
+
+
+LLSD LLNotification::asLLSD()
+{
+ LLSD output;
+ output["name"] = mTemplatep->mName;
+ output["form"] = getForm()->asLLSD();
+ output["substitutions"] = mSubstitutions;
+ output["payload"] = mPayload;
+ output["time"] = mTimestamp;
+ output["expiry"] = mExpiresAt;
+ output["priority"] = (S32)mPriority;
+ output["responseFunctor"] = mResponseFunctorName;
+ return output;
+}
+
+void LLNotification::update()
+{
+ LLNotifications::instance().update(shared_from_this());
+}
+
+void LLNotification::updateFrom(LLNotificationPtr other)
+{
+ // can only update from the same notification type
+ if (mTemplatep != other->mTemplatep) return;
+
+ // NOTE: do NOT change the ID, since it is the key to
+ // this given instance, just update all the metadata
+ //mId = other->mId;
+
+ mPayload = other->mPayload;
+ mSubstitutions = other->mSubstitutions;
+ mTimestamp = other->mTimestamp;
+ mExpiresAt = other->mExpiresAt;
+ mCancelled = other->mCancelled;
+ mIgnored = other->mIgnored;
+ mPriority = other->mPriority;
+ mForm = other->mForm;
+ mResponseFunctorName = other->mResponseFunctorName;
+ mRespondedTo = other->mRespondedTo;
+ mTemporaryResponder = other->mTemporaryResponder;
+
+ update();
+}
+
+const LLNotificationFormPtr LLNotification::getForm()
+{
+ return mForm;
+}
+
+void LLNotification::cancel()
+{
+ mCancelled = true;
+}
+
+LLSD LLNotification::getResponseTemplate(EResponseTemplateType type)
+{
+ LLSD response = LLSD::emptyMap();
+ for (S32 element_idx = 0;
+ element_idx < mForm->getNumElements();
+ ++element_idx)
+ {
+ LLSD element = mForm->getElement(element_idx);
+ if (element.has("name"))
+ {
+ response[element["name"].asString()] = element["value"];
+ }
+
+ if ((type == WITH_DEFAULT_BUTTON)
+ && element["default"].asBoolean())
+ {
+ response[element["name"].asString()] = true;
+ }
+ }
+ return response;
+}
+
+//static
+S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response)
+{
+ LLNotificationForm form(notification["form"]);
+
+ for (S32 element_idx = 0;
+ element_idx < form.getNumElements();
+ ++element_idx)
+ {
+ LLSD element = form.getElement(element_idx);
+
+ // only look at buttons
+ if (element["type"].asString() == "button"
+ && response[element["name"].asString()].asBoolean())
+ {
+ return element["index"].asInteger();
+ }
+ }
+
+ return -1;
+}
+
+//static
+std::string LLNotification::getSelectedOptionName(const LLSD& response)
+{
+ for (LLSD::map_const_iterator response_it = response.beginMap();
+ response_it != response.endMap();
+ ++response_it)
+ {
+ if (response_it->second.isBoolean() && response_it->second.asBoolean())
+ {
+ return response_it->first;
+ }
+ }
+ return "";
+}
+
+
+void LLNotification::respond(const LLSD& response)
+{
+ mRespondedTo = true;
+ // look up the functor
+ LLNotificationFunctorRegistry::ResponseFunctor functor =
+ LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName);
+ // and then call it
+ functor(asLLSD(), response);
+
+ if (mTemporaryResponder)
+ {
+ LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
+ mResponseFunctorName = "";
+ mTemporaryResponder = false;
+ }
+
+ if (mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO)
+ {
+ BOOL show_notification = mIgnored ? FALSE : TRUE;
+ LLUI::sSettingGroups["ignores"]->setBOOL(getName(), show_notification);
+ if (mIgnored && mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE)
+ {
+ LLUI::sSettingGroups["ignores"]->setLLSD("Default" + getName(), response);
+ }
+ }
+
+ update();
+}
+
+void LLNotification::setIgnored(bool ignore)
+{
+ mIgnored = ignore;
+}
+
+void LLNotification::setResponseFunctor(std::string const &responseFunctorName)
+{
+ if (mTemporaryResponder)
+ // get rid of the old one
+ LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
+ mResponseFunctorName = responseFunctorName;
+ mTemporaryResponder = false;
+}
+
+bool LLNotification::payloadContainsAll(const std::vector<std::string>& required_fields) const
+{
+ for(std::vector<std::string>::const_iterator required_fields_it = required_fields.begin();
+ required_fields_it != required_fields.end();
+ required_fields_it++)
+ {
+ std::string required_field_name = *required_fields_it;
+ if( ! getPayload().has(required_field_name))
+ {
+ return false; // a required field was not found
+ }
+ }
+ return true; // all required fields were found
+}
+
+bool LLNotification::isEquivalentTo(LLNotificationPtr that) const
+{
+ if (this->mTemplatep->mName != that->mTemplatep->mName)
+ {
+ return false; // must have the same template name or forget it
+ }
+ if (this->mTemplatep->mUnique)
+ {
+ // highlander bit sez there can only be one of these
+ return
+ this->payloadContainsAll(that->mTemplatep->mUniqueContext) &&
+ that->payloadContainsAll(this->mTemplatep->mUniqueContext);
+ }
+ return false;
+}
+
+void LLNotification::init(const std::string& template_name, const LLSD& form_elements)
+{
+ mTemplatep = LLNotifications::instance().getTemplate(template_name);
+ if (!mTemplatep) return;
+
+ // add default substitutions
+ const LLStringUtil::format_map_t& default_args = LLTrans::getDefaultArgs();
+ for (LLStringUtil::format_map_t::const_iterator iter = default_args.begin();
+ iter != default_args.end(); ++iter)
+ {
+ mSubstitutions[iter->first] = iter->second;
+ }
+ mSubstitutions["_URL"] = getURL();
+ mSubstitutions["_NAME"] = template_name;
+ // TODO: something like this so that a missing alert is sensible:
+ //mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions);
+
+ mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm));
+ mForm->append(form_elements);
+
+ // apply substitution to form labels
+ mForm->formatElements(mSubstitutions);
+
+ LLDate rightnow = LLDate::now();
+ if (mTemplatep->mExpireSeconds)
+ {
+ mExpiresAt = LLDate(rightnow.secondsSinceEpoch() + mTemplatep->mExpireSeconds);
+ }
+
+ if (mPriority == NOTIFICATION_PRIORITY_UNSPECIFIED)
+ {
+ mPriority = mTemplatep->mPriority;
+ }
+}
+
+std::string LLNotification::summarize() const
+{
+ std::string s = "Notification(";
+ s += getName();
+ s += ") : ";
+ s += mTemplatep ? mTemplatep->mMessage : "";
+ // should also include timestamp and expiration time (but probably not payload)
+ return s;
+}
+
+std::string LLNotification::getMessage() const
+{
+ // all our callers cache this result, so it gives us more flexibility
+ // to do the substitution at call time rather than attempting to
+ // cache it in the notification
+ if (!mTemplatep)
+ return std::string();
+
+ std::string message = mTemplatep->mMessage;
+ LLStringUtil::format(message, mSubstitutions);
+ return message;
+}
+
+std::string LLNotification::getLabel() const
+{
+ std::string label = mTemplatep->mLabel;
+ LLStringUtil::format(label, mSubstitutions);
+ return (mTemplatep ? label : "");
+}
+
+std::string LLNotification::getURL() const
+{
+ if (!mTemplatep)
+ return std::string();
+ std::string url = mTemplatep->mURL;
+ LLStringUtil::format(url, mSubstitutions);
+ return (mTemplatep ? url : "");
+}
+
+// =========================================================
+// LLNotificationChannel implementation
+// ---
+LLBoundListener LLNotificationChannelBase::connectChangedImpl(const LLEventListener& slot)
+{
+ // when someone wants to connect to a channel, we first throw them
+ // all of the notifications that are already in the channel
+ // we use a special signal called "load" in case the channel wants to care
+ // only about new notifications
+ for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
+ {
+ slot(LLSD().insert("sigtype", "load").insert("id", (*it)->id()));
+ }
+ // and then connect the signal so that all future notifications will also be
+ // forwarded.
+ return mChanged.connect(slot);
+}
+
+LLBoundListener LLNotificationChannelBase::connectAtFrontChangedImpl(const LLEventListener& slot)
+{
+ for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
+ {
+ slot(LLSD().insert("sigtype", "load").insert("id", (*it)->id()));
+ }
+ return mChanged.connect(slot, boost::signals2::at_front);
+}
+
+LLBoundListener LLNotificationChannelBase::connectPassedFilterImpl(const LLEventListener& slot)
+{
+ // these two filters only fire for notifications added after the current one, because
+ // they don't participate in the hierarchy.
+ return mPassedFilter.connect(slot);
+}
+
+LLBoundListener LLNotificationChannelBase::connectFailedFilterImpl(const LLEventListener& slot)
+{
+ return mFailedFilter.connect(slot);
+}
+
+// external call, conforms to our standard signature
+bool LLNotificationChannelBase::updateItem(const LLSD& payload)
+{
+ // first check to see if it's in the master list
+ LLNotificationPtr pNotification = LLNotifications::instance().find(payload["id"]);
+ if (!pNotification)
+ return false; // not found
+
+ return updateItem(payload, pNotification);
+}
+
+
+//FIX QUIT NOT WORKING
+
+
+// internal call, for use in avoiding lookup
+bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPtr pNotification)
+{
+ std::string cmd = payload["sigtype"];
+ LLNotificationSet::iterator foundItem = mItems.find(pNotification);
+ bool wasFound = (foundItem != mItems.end());
+ bool passesFilter = mFilter(pNotification);
+
+ // first, we offer the result of the filter test to the simple
+ // signals for pass/fail. One of these is guaranteed to be called.
+ // If either signal returns true, the change processing is NOT performed
+ // (so don't return true unless you know what you're doing!)
+ bool abortProcessing = false;
+ if (passesFilter)
+ {
+ abortProcessing = mPassedFilter(payload);
+ }
+ else
+ {
+ abortProcessing = mFailedFilter(payload);
+ }
+
+ if (abortProcessing)
+ {
+ return true;
+ }
+
+ if (cmd == "load")
+ {
+ // should be no reason we'd ever get a load if we already have it
+ // if passes filter send a load message, else do nothing
+ assert(!wasFound);
+ if (passesFilter)
+ {
+ // not in our list, add it and say so
+ mItems.insert(pNotification);
+ abortProcessing = mChanged(payload);
+ onLoad(pNotification);
+ }
+ }
+ else if (cmd == "change")
+ {
+ // if it passes filter now and was found, we just send a change message
+ // if it passes filter now and wasn't found, we have to add it
+ // if it doesn't pass filter and wasn't found, we do nothing
+ // if it doesn't pass filter and was found, we need to delete it
+ if (passesFilter)
+ {
+ if (wasFound)
+ {
+ // it already existed, so this is a change
+ // since it changed in place, all we have to do is resend the signal
+ abortProcessing = mChanged(payload);
+ onChange(pNotification);
+ }
+ else
+ {
+ // not in our list, add it and say so
+ mItems.insert(pNotification);
+ // our payload is const, so make a copy before changing it
+ LLSD newpayload = payload;
+ newpayload["sigtype"] = "add";
+ abortProcessing = mChanged(newpayload);
+ onChange(pNotification);
+ }
+ }
+ else
+ {
+ if (wasFound)
+ {
+ // it already existed, so this is a delete
+ mItems.erase(pNotification);
+ // our payload is const, so make a copy before changing it
+ LLSD newpayload = payload;
+ newpayload["sigtype"] = "delete";
+ abortProcessing = mChanged(newpayload);
+ onChange(pNotification);
+ }
+ // didn't pass, not on our list, do nothing
+ }
+ }
+ else if (cmd == "add")
+ {
+ // should be no reason we'd ever get an add if we already have it
+ // if passes filter send an add message, else do nothing
+ assert(!wasFound);
+ if (passesFilter)
+ {
+ // not in our list, add it and say so
+ mItems.insert(pNotification);
+ abortProcessing = mChanged(payload);
+ onAdd(pNotification);
+ }
+ }
+ else if (cmd == "delete")
+ {
+ // if we have it in our list, pass on the delete, then delete it, else do nothing
+ if (wasFound)
+ {
+ abortProcessing = mChanged(payload);
+ mItems.erase(pNotification);
+ onDelete(pNotification);
+ }
+ }
+ return abortProcessing;
+}
+
+/* static */
+LLNotificationChannelPtr LLNotificationChannel::buildChannel(const std::string& name,
+ const std::string& parent,
+ LLNotificationFilter filter,
+ LLNotificationComparator comparator)
+{
+ // note: this is not a leak; notifications are self-registering.
+ // This factory helps to prevent excess deletions by making sure all smart
+ // pointers to notification channels come from the same source
+ new LLNotificationChannel(name, parent, filter, comparator);
+ return LLNotifications::instance().getChannel(name);
+}
+
+
+LLNotificationChannel::LLNotificationChannel(const std::string& name,
+ const std::string& parent,
+ LLNotificationFilter filter,
+ LLNotificationComparator comparator) :
+LLNotificationChannelBase(filter, comparator),
+mName(name),
+mParent(parent)
+{
+ // store myself in the channel map
+ LLNotifications::instance().addChannel(LLNotificationChannelPtr(this));
+ // bind to notification broadcast
+ if (parent.empty())
+ {
+ LLNotifications::instance().connectChanged(
+ boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
+ }
+ else
+ {
+ LLNotificationChannelPtr p = LLNotifications::instance().getChannel(parent);
+ p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
+ }
+}
+
+
+void LLNotificationChannel::setComparator(LLNotificationComparator comparator)
+{
+ mComparator = comparator;
+ LLNotificationSet s2(mComparator);
+ s2.insert(mItems.begin(), mItems.end());
+ mItems.swap(s2);
+
+ // notify clients that we've been resorted
+ mChanged(LLSD().insert("sigtype", "sort"));
+}
+
+bool LLNotificationChannel::isEmpty() const
+{
+ return mItems.empty();
+}
+
+LLNotificationChannel::Iterator LLNotificationChannel::begin()
+{
+ return mItems.begin();
+}
+
+LLNotificationChannel::Iterator LLNotificationChannel::end()
+{
+ return mItems.end();
+}
+
+std::string LLNotificationChannel::summarize()
+{
+ std::string s("Channel '");
+ s += mName;
+ s += "'\n ";
+ for (LLNotificationChannel::Iterator it = begin(); it != end(); ++it)
+ {
+ s += (*it)->summarize();
+ s += "\n ";
+ }
+ return s;
+}
+
+
+// ---
+// END OF LLNotificationChannel implementation
+// =========================================================
+
+
+// =========================================================
+// LLNotifications implementation
+// ---
+LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything,
+ LLNotificationComparators::orderByUUID()),
+ mIgnoreAllNotifications(false)
+{
+ LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
+
+ mListener.reset(new LLNotificationsListener(*this));
+}
+
+
+// The expiration channel gets all notifications that are cancelled
+bool LLNotifications::expirationFilter(LLNotificationPtr pNotification)
+{
+ return pNotification->isCancelled() || pNotification->isRespondedTo();
+}
+
+bool LLNotifications::expirationHandler(const LLSD& payload)
+{
+ if (payload["sigtype"].asString() != "delete")
+ {
+ // anything added to this channel actually should be deleted from the master
+ cancel(find(payload["id"]));
+ return true; // don't process this item any further
+ }
+ return false;
+}
+
+bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif)
+{
+ if (!pNotif->hasUniquenessConstraints())
+ {
+ return true;
+ }
+
+ // checks against existing unique notifications
+ for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
+ existing_it != mUniqueNotifications.end();
+ ++existing_it)
+ {
+ LLNotificationPtr existing_notification = existing_it->second;
+ if (pNotif != existing_notification
+ && pNotif->isEquivalentTo(existing_notification))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool LLNotifications::uniqueHandler(const LLSD& payload)
+{
+ LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
+ if (pNotif && pNotif->hasUniquenessConstraints())
+ {
+ if (payload["sigtype"].asString() == "add")
+ {
+ // not a duplicate according to uniqueness criteria, so we keep it
+ // and store it for future uniqueness checks
+ mUniqueNotifications.insert(std::make_pair(pNotif->getName(), pNotif));
+ }
+ else if (payload["sigtype"].asString() == "delete")
+ {
+ mUniqueNotifications.erase(pNotif->getName());
+ }
+ }
+
+ return false;
+}
+
+bool LLNotifications::failedUniquenessTest(const LLSD& payload)
+{
+ LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
+
+ if (!pNotif || !pNotif->hasUniquenessConstraints())
+ {
+ return false;
+ }
+
+ // checks against existing unique notifications
+ for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
+ existing_it != mUniqueNotifications.end();
+ ++existing_it)
+ {
+ LLNotificationPtr existing_notification = existing_it->second;
+ if (pNotif != existing_notification
+ && pNotif->isEquivalentTo(existing_notification))
+ {
+ // copy notification instance data over to oldest instance
+ // of this unique notification and update it
+ existing_notification->updateFrom(pNotif);
+ // then delete the new one
+ pNotif->cancel();
+ }
+ }
+
+ return false;
+}
+
+
+void LLNotifications::addChannel(LLNotificationChannelPtr pChan)
+{
+ mChannels[pChan->getName()] = pChan;
+}
+
+LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName)
+{
+ ChannelMap::iterator p = mChannels.find(channelName);
+ if(p == mChannels.end())
+ {
+ llerrs << "Did not find channel named " << channelName << llendl;
+ }
+ return p->second;
+}
+
+
+// this function is called once at construction time, after the object is constructed.
+void LLNotifications::initSingleton()
+{
+ loadTemplates();
+ createDefaultChannels();
+}
+
+void LLNotifications::createDefaultChannels()
+{
+ // now construct the various channels AFTER loading the notifications,
+ // because the history channel is going to rewrite the stored notifications file
+ LLNotificationChannel::buildChannel("Expiration", "",
+ boost::bind(&LLNotifications::expirationFilter, this, _1));
+ LLNotificationChannel::buildChannel("Unexpired", "",
+ !boost::bind(&LLNotifications::expirationFilter, this, _1)); // use negated bind
+ LLNotificationChannel::buildChannel("Unique", "Unexpired",
+ boost::bind(&LLNotifications::uniqueFilter, this, _1));
+ LLNotificationChannel::buildChannel("Ignore", "Unique",
+ filterIgnoredNotifications);
+ LLNotificationChannel::buildChannel("Visible", "Ignore",
+ &LLNotificationFilters::includeEverything);
+
+ // create special history channel
+ //std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" );
+ // use ^^^ when done debugging notifications serialization
+ std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_USER_SETTINGS, "open_notifications.xml" );
+ // this isn't a leak, don't worry about the empty "new"
+ new LLNotificationHistoryChannel(notifications_log_file);
+
+ // connect action methods to these channels
+ LLNotifications::instance().getChannel("Expiration")->
+ connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1));
+ // uniqueHandler slot should be added as first slot of the signal due to
+ // usage LLStopWhenHandled combiner in LLStandardSignal
+ LLNotifications::instance().getChannel("Unique")->
+ connectAtFrontChanged(boost::bind(&LLNotifications::uniqueHandler, this, _1));
+// failedUniquenessTest slot isn't necessary
+// LLNotifications::instance().getChannel("Unique")->
+// connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1));
+ LLNotifications::instance().getChannel("Ignore")->
+ connectFailedFilter(&handleIgnoredNotification);
+}
+
+bool LLNotifications::addTemplate(const std::string &name,
+ LLNotificationTemplatePtr theTemplate)
+{
+ if (mTemplates.count(name))
+ {
+ llwarns << "LLNotifications -- attempted to add template '" << name << "' twice." << llendl;
+ return false;
+ }
+ mTemplates[name] = theTemplate;
+ return true;
+}
+
+LLNotificationTemplatePtr LLNotifications::getTemplate(const std::string& name)
+{
+ if (mTemplates.count(name))
+ {
+ return mTemplates[name];
+ }
+ else
+ {
+ return mTemplates["MissingAlert"];
+ }
+}
+
+bool LLNotifications::templateExists(const std::string& name)
+{
+ return (mTemplates.count(name) != 0);
+}
+
+void LLNotifications::clearTemplates()
+{
+ mTemplates.clear();
+}
+
+void LLNotifications::forceResponse(const LLNotification::Params& params, S32 option)
+{
+ LLNotificationPtr temp_notify(new LLNotification(params));
+ LLSD response = temp_notify->getResponseTemplate();
+ LLSD selected_item = temp_notify->getForm()->getElement(option);
+
+ if (selected_item.isUndefined())
+ {
+ llwarns << "Invalid option" << option << " for notification " << (std::string)params.name << llendl;
+ return;
+ }
+ response[selected_item["name"].asString()] = true;
+
+ temp_notify->respond(response);
+}
+
+LLNotifications::TemplateNames LLNotifications::getTemplateNames() const
+{
+ TemplateNames names;
+ for (TemplateMap::const_iterator it = mTemplates.begin(); it != mTemplates.end(); ++it)
+ {
+ names.push_back(it->first);
+ }
+ return names;
+}
+
+typedef std::map<std::string, std::string> StringMap;
+void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)
+{
+ //llwarns << "replaceSubstitutionStrings" << llendl;
+ // walk the list of attributes looking for replacements
+ for (LLXMLAttribList::iterator it=node->mAttributes.begin();
+ it != node->mAttributes.end(); ++it)
+ {
+ std::string value = it->second->getValue();
+ if (value[0] == '$')
+ {
+ value.erase(0, 1); // trim off the $
+ std::string replacement;
+ StringMap::const_iterator found = replacements.find(value);
+ if (found != replacements.end())
+ {
+ replacement = found->second;
+ //llwarns << "replaceSubstituionStrings: value: " << value << " repl: " << replacement << llendl;
+
+ it->second->setValue(replacement);
+ }
+ else
+ {
+ llwarns << "replaceSubstituionStrings FAILURE: value: " << value << " repl: " << replacement << llendl;
+ }
+ }
+ }
+
+ // now walk the list of children and call this recursively.
+ for (LLXMLNodePtr child = node->getFirstChild();
+ child.notNull(); child = child->getNextSibling())
+ {
+ replaceSubstitutionStrings(child, replacements);
+ }
+}
+
+// private to this file
+// returns true if the template request was invalid and there's nothing else we
+// can do with this node, false if you should keep processing (it may have
+// replaced the contents of the node referred to)
+LLXMLNodePtr LLNotifications::checkForXMLTemplate(LLXMLNodePtr item)
+{
+ if (item->hasName("usetemplate"))
+ {
+ std::string replacementName;
+ if (item->getAttributeString("name", replacementName))
+ {
+ StringMap replacements;
+ for (LLXMLAttribList::const_iterator it=item->mAttributes.begin();
+ it != item->mAttributes.end(); ++it)
+ {
+ replacements[it->second->getName()->mString] = it->second->getValue();
+ }
+ if (mXmlTemplates.count(replacementName))
+ {
+ item=LLXMLNode::replaceNode(item, mXmlTemplates[replacementName]);
+
+ // walk the nodes looking for $(substitution) here and replace
+ replaceSubstitutionStrings(item, replacements);
+ }
+ else
+ {
+ llwarns << "XML template lookup failure on '" << replacementName << "' " << llendl;
+ }
+ }
+ }
+ return item;
+}
+
+bool LLNotifications::loadTemplates()
+{
+ const std::string xml_filename = "notifications.xml";
+ LLXMLNodePtr root;
+
+ BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
+
+ if (!success || root.isNull() || !root->hasName( "notifications" ))
+ {
+ llerrs << "Problem reading UI Notifications file: " << xml_filename << llendl;
+ return false;
+ }
+
+ clearTemplates();
+
+ for (LLXMLNodePtr item = root->getFirstChild();
+ item.notNull(); item = item->getNextSibling())
+ {
+ // we do this FIRST so that item can be changed if we
+ // encounter a usetemplate -- we just replace the
+ // current xml node and keep processing
+ item = checkForXMLTemplate(item);
+
+ if (item->hasName("global"))
+ {
+ std::string global_name;
+ if (item->getAttributeString("name", global_name))
+ {
+ mGlobalStrings[global_name] = item->getTextContents();
+ }
+ continue;
+ }
+
+ if (item->hasName("template"))
+ {
+ // store an xml template; templates must have a single node (can contain
+ // other nodes)
+ std::string name;
+ item->getAttributeString("name", name);
+ LLXMLNodePtr ptr = item->getFirstChild();
+ mXmlTemplates[name] = ptr;
+ continue;
+ }
+
+ if (!item->hasName("notification"))
+ {
+ llwarns << "Unexpected entity " << item->getName()->mString <<
+ " found in " << xml_filename << llendl;
+ continue;
+ }
+
+ // now we know we have a notification entry, so let's build it
+ LLNotificationTemplatePtr pTemplate(new LLNotificationTemplate());
+
+ if (!item->getAttributeString("name", pTemplate->mName))
+ {
+ llwarns << "Unable to parse notification with no name" << llendl;
+ continue;
+ }
+
+ //llinfos << "Parsing " << pTemplate->mName << llendl;
+
+ pTemplate->mMessage = item->getTextContents();
+ pTemplate->mDefaultFunctor = pTemplate->mName;
+ item->getAttributeString("type", pTemplate->mType);
+ item->getAttributeString("icon", pTemplate->mIcon);
+ item->getAttributeString("label", pTemplate->mLabel);
+ item->getAttributeU32("duration", pTemplate->mExpireSeconds);
+ item->getAttributeU32("expireOption", pTemplate->mExpireOption);
+
+ std::string priority;
+ item->getAttributeString("priority", priority);
+ pTemplate->mPriority = NOTIFICATION_PRIORITY_NORMAL;
+ if (!priority.empty())
+ {
+ if (priority == "low") pTemplate->mPriority = NOTIFICATION_PRIORITY_LOW;
+ if (priority == "normal") pTemplate->mPriority = NOTIFICATION_PRIORITY_NORMAL;
+ if (priority == "high") pTemplate->mPriority = NOTIFICATION_PRIORITY_HIGH;
+ if (priority == "critical") pTemplate->mPriority = NOTIFICATION_PRIORITY_CRITICAL;
+ }
+
+ item->getAttributeString("functor", pTemplate->mDefaultFunctor);
+
+ BOOL persist = false;
+ item->getAttributeBOOL("persist", persist);
+ pTemplate->mPersist = persist;
+
+ std::string sound;
+ item->getAttributeString("sound", sound);
+ if (!sound.empty())
+ {
+ // test for bad sound effect name / missing effect
+ if (LLUI::sSettingGroups["config"]->controlExists(sound))
+ {
+ pTemplate->mSoundEffect =
+ LLUUID(LLUI::sSettingGroups["config"]->getString(sound));
+ }
+ else
+ {
+ llwarns << "Unknown sound effect control name " << sound
+ << llendl;
+ }
+ }
+
+ for (LLXMLNodePtr child = item->getFirstChild();
+ !child.isNull(); child = child->getNextSibling())
+ {
+ child = checkForXMLTemplate(child);
+
+ // <url>
+ if (child->hasName("url"))
+ {
+ pTemplate->mURL = child->getTextContents();
+ child->getAttributeU32("option", pTemplate->mURLOption);
+ child->getAttributeU32("openexternally", pTemplate->mURLOpenExternally);
+ }
+
+ if (child->hasName("unique"))
+ {
+ pTemplate->mUnique = true;
+ for (LLXMLNodePtr formitem = child->getFirstChild();
+ !formitem.isNull(); formitem = formitem->getNextSibling())
+ {
+ if (formitem->hasName("context"))
+ {
+ std::string key;
+ formitem->getAttributeString("key", key);
+ pTemplate->mUniqueContext.push_back(key);
+ //llwarns << "adding " << key << " to unique context" << llendl;
+ }
+ else
+ {
+ llwarns << "'unique' has unrecognized subelement "
+ << formitem->getName()->mString << llendl;
+ }
+ }
+ }
+
+ // <form>
+ if (child->hasName("form"))
+ {
+ pTemplate->mForm = LLNotificationFormPtr(new LLNotificationForm(pTemplate->mName, child));
+ }
+ }
+ addTemplate(pTemplate->mName, pTemplate);
+ }
+
+ //std::ostringstream ostream;
+ //root->writeToOstream(ostream, "\n ");
+ //llwarns << ostream.str() << llendl;
+
+ return true;
+}
+
+// Add a simple notification (from XUI)
+void LLNotifications::addFromCallback(const LLSD& name)
+{
+ add(LLNotification::Params().name(name.asString()));
+}
+
+// we provide a couple of simple add notification functions so that it's reasonable to create notifications in one line
+LLNotificationPtr LLNotifications::add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload)
+{
+ LLNotification::Params::Functor functor_p;
+ functor_p.name = name;
+ return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
+}
+
+LLNotificationPtr LLNotifications::add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ const std::string& functor_name)
+{
+ LLNotification::Params::Functor functor_p;
+ functor_p.name = functor_name;
+ return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
+}
+
+LLNotificationPtr LLNotifications::add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ LLNotificationFunctorRegistry::ResponseFunctor functor)
+{
+ LLNotification::Params::Functor functor_p;
+ functor_p.function = functor;
+ return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
+}
+
+// generalized add function that takes a parameter block object for more complex instantiations
+LLNotificationPtr LLNotifications::add(const LLNotification::Params& p)
+{
+ LLNotificationPtr pNotif(new LLNotification(p));
+ add(pNotif);
+ return pNotif;
+}
+
+
+void LLNotifications::add(const LLNotificationPtr pNotif)
+{
+ // first see if we already have it -- if so, that's a problem
+ LLNotificationSet::iterator it=mItems.find(pNotif);
+ if (it != mItems.end())
+ {
+ llerrs << "Notification added a second time to the master notification channel." << llendl;
+ }
+
+ updateItem(LLSD().insert("sigtype", "add").insert("id", pNotif->id()), pNotif);
+}
+
+void LLNotifications::cancel(LLNotificationPtr pNotif)
+{
+ LLNotificationSet::iterator it=mItems.find(pNotif);
+ if (it == mItems.end())
+ {
+ llerrs << "Attempted to delete nonexistent notification " << pNotif->getName() << llendl;
+ }
+ updateItem(LLSD().insert("sigtype", "delete").insert("id", pNotif->id()), pNotif);
+ pNotif->cancel();
+}
+
+void LLNotifications::update(const LLNotificationPtr pNotif)
+{
+ LLNotificationSet::iterator it=mItems.find(pNotif);
+ if (it != mItems.end())
+ {
+ updateItem(LLSD().insert("sigtype", "change").insert("id", pNotif->id()), pNotif);
+ }
+}
+
+
+LLNotificationPtr LLNotifications::find(LLUUID uuid)
+{
+ LLNotificationPtr target = LLNotificationPtr(new LLNotification(uuid));
+ LLNotificationSet::iterator it=mItems.find(target);
+ if (it == mItems.end())
+ {
+ llwarns << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << llendl;
+ return LLNotificationPtr((LLNotification*)NULL);
+ }
+ else
+ {
+ return *it;
+ }
+}
+
+void LLNotifications::forEachNotification(NotificationProcess process)
+{
+ std::for_each(mItems.begin(), mItems.end(), process);
+}
+
+std::string LLNotifications::getGlobalString(const std::string& key) const
+{
+ GlobalStringMap::const_iterator it = mGlobalStrings.find(key);
+ if (it != mGlobalStrings.end())
+ {
+ return it->second;
+ }
+ else
+ {
+ // if we don't have the key as a global, return the key itself so that the error
+ // is self-diagnosing.
+ return key;
+ }
+}
+
+void LLNotifications::setIgnoreAllNotifications(bool setting)
+{
+ mIgnoreAllNotifications = setting;
+}
+bool LLNotifications::getIgnoreAllNotifications()
+{
+ return mIgnoreAllNotifications;
+}
+
+// ---
+// END OF LLNotifications implementation
+// =========================================================
+
+std::ostream& operator<<(std::ostream& s, const LLNotification& notification)
+{
+ s << notification.summarize();
+ return s;
+}
+
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 19895c3293..0335a265b6 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -103,6 +103,7 @@ #include "llpointer.h" #include "llinitparam.h" #include "llxmlnode.h" +#include "llnotificationslistener.h" class LLNotification; typedef boost::shared_ptr<LLNotification> LLNotificationPtr; @@ -813,9 +814,19 @@ private: LLNotificationComparator mComparator; }; - +// An interface class to provide a clean linker seam to the LLNotifications class. +// Extend this interface as needed for your use of LLNotifications. +class LLNotificationsInterface +{ +public: + virtual LLNotificationPtr add(const std::string& name, + const LLSD& substitutions, + const LLSD& payload, + LLNotificationFunctorRegistry::ResponseFunctor functor) = 0; +}; class LLNotifications : + public LLNotificationsInterface, public LLSingleton<LLNotifications>, public LLNotificationChannelBase { @@ -839,7 +850,7 @@ public: const LLSD& substitutions, const LLSD& payload, const std::string& functor_name); - LLNotificationPtr add(const std::string& name, + /* virtual */ LLNotificationPtr add(const std::string& name, const LLSD& substitutions, const LLSD& payload, LLNotificationFunctorRegistry::ResponseFunctor functor); @@ -918,6 +929,8 @@ private: GlobalStringMap mGlobalStrings; bool mIgnoreAllNotifications; + + boost::scoped_ptr<LLNotificationsListener> mListener; }; diff --git a/indra/llui/llnotificationslistener.cpp b/indra/llui/llnotificationslistener.cpp new file mode 100644 index 0000000000..75f4d6177d --- /dev/null +++ b/indra/llui/llnotificationslistener.cpp @@ -0,0 +1,55 @@ +/** + * @file llnotificationslistener.cpp + * @author Brad Kittenbrink + * @date 2009-07-08 + * @brief Implementation for llnotificationslistener. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llnotificationslistener.h" + +#include "llnotifications.h" + +LLNotificationsListener::LLNotificationsListener(LLNotifications & notifications) : + LLDispatchListener("LLNotifications", "op"), + mNotifications(notifications) +{ + add("requestAdd", &LLNotificationsListener::requestAdd); +} + +void LLNotificationsListener::requestAdd(const LLSD& event_data) const +{ + if(event_data.has("reply")) + { + mNotifications.add(event_data["name"], + event_data["substitutions"], + event_data["payload"], + boost::bind(&LLNotificationsListener::NotificationResponder, + this, + event_data["reply"].asString(), + _1, _2 + ) + ); + } + else + { + mNotifications.add(event_data["name"], + event_data["substitutions"], + event_data["payload"]); + } +} + +void LLNotificationsListener::NotificationResponder(const std::string& reply_pump, + const LLSD& notification, + const LLSD& response) const +{ + LLSD reponse_event; + reponse_event["notification"] = notification; + reponse_event["response"] = response; + LLEventPumps::getInstance()->obtain(reply_pump).post(reponse_event); +} diff --git a/indra/llui/llnotificationslistener.h b/indra/llui/llnotificationslistener.h new file mode 100644 index 0000000000..6f71a7c781 --- /dev/null +++ b/indra/llui/llnotificationslistener.h @@ -0,0 +1,34 @@ +/** + * @file llnotificationslistener.h + * @author Brad Kittenbrink + * @date 2009-07-08 + * @brief Wrap subset of LLNotifications API in event API for test scripts. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#ifndef LL_LLNOTIFICATIONSLISTENER_H +#define LL_LLNOTIFICATIONSLISTENER_H + +#include "lleventdispatcher.h" + +class LLNotifications; +class LLSD; + +class LLNotificationsListener : public LLDispatchListener +{ +public: + LLNotificationsListener(LLNotifications & notifications); + + void requestAdd(LLSD const & event_data) const; + +private: + void NotificationResponder(const std::string& replypump, + const LLSD& notification, + const LLSD& response) const; + LLNotifications & mNotifications; +}; + +#endif // LL_LLNOTIFICATIONSLISTENER_H diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp index 1c394a71dd..840dd9b089 100644 --- a/indra/llui/llslider.cpp +++ b/indra/llui/llslider.cpp @@ -43,6 +43,8 @@ #include "lluictrlfactory.h" static LLDefaultChildRegistry::Register<LLSlider> r1("slider_bar"); +//FIXME: make this into an unregistered template so that code constructed sliders don't +// have ambigious template lookup problem LLSlider::Params::Params() : track_color("track_color"), diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index 15584c8dc7..675a29a8b4 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -396,4 +396,3 @@ void LLSliderCtrl::reportInvalidData() make_ui_sound("UISndBadKeystroke"); } - diff --git a/indra/llwindow/llwindowmesaheadless.cpp b/indra/llwindow/llwindowmesaheadless.cpp index 7ee09f4a24..48736d9207 100644 --- a/indra/llwindow/llwindowmesaheadless.cpp +++ b/indra/llwindow/llwindowmesaheadless.cpp @@ -45,7 +45,7 @@ U16 *gMesaBuffer = NULL; // LLWindowMesaHeadless // LLWindowMesaHeadless::LLWindowMesaHeadless(LLWindowCallbacks* callbacks, - const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, + const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth) : LLWindow(callbacks, fullscreen, flags) diff --git a/indra/llwindow/llwindowmesaheadless.h b/indra/llwindow/llwindowmesaheadless.h index 22e0ec126d..46b62b914c 100644 --- a/indra/llwindow/llwindowmesaheadless.h +++ b/indra/llwindow/llwindowmesaheadless.h @@ -99,7 +99,7 @@ public: /*virtual*/ void bringToFront() {}; LLWindowMesaHeadless(LLWindowCallbacks* callbacks, - const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, + const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth); ~LLWindowMesaHeadless(); diff --git a/indra/lscript/lscript_execute/llscriptresource.cpp b/indra/lscript/lscript_execute/llscriptresource.cpp index 6c4776c2e4..cd3696ab3f 100644 --- a/indra/lscript/lscript_execute/llscriptresource.cpp +++ b/indra/lscript/lscript_execute/llscriptresource.cpp @@ -30,6 +30,8 @@ * $/LicenseInfo$ */ +#include "linden_common.h" + #include "llscriptresource.h" #include "llerror.h" diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index ae3fa9a324..d7bb68a006 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -36,10 +36,7 @@ include(UI) include(UnixInstall) include(LLKDU) include(ViewerMiscLibs) - -if (WINDOWS) - include(CopyWinLibs) -endif (WINDOWS) +include(LLLogin) include_directories( ${DBUSGLIB_INCLUDE_DIRS} @@ -61,6 +58,7 @@ include_directories( ${LLXUIXML_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS}/lscript_compile + ${LLLOGIN_INCLUDE_DIRS} ) set(viewer_SOURCE_FILES @@ -68,6 +66,7 @@ set(viewer_SOURCE_FILES llaccordionctrl.cpp llactiveimwindow.cpp llagent.cpp + llagentlistener.cpp llagentaccess.cpp llagentdata.cpp llagentlanguage.cpp @@ -76,6 +75,7 @@ set(viewer_SOURCE_FILES llagentwearables.cpp llanimstatelabels.cpp llappviewer.cpp + llappviewerlistener.cpp llassetuploadresponders.cpp llassetuploadqueue.cpp llaudiosourcevo.cpp @@ -253,6 +253,7 @@ set(viewer_SOURCE_FILES llurllineeditorctrl.cpp lllogchat.cpp llloginhandler.cpp + lllogininstance.cpp llmanip.cpp llmaniprotate.cpp llmanipscale.cpp @@ -324,6 +325,7 @@ set(viewer_SOURCE_FILES llpanelpicks.cpp llpanelplace.cpp llpanelplaceinfo.cpp + llpanelshower.cpp llpanelplaces.cpp llpanelplacestab.cpp llpanelprofile.cpp @@ -356,7 +358,6 @@ set(viewer_SOURCE_FILES llspatialpartition.cpp llsplitbutton.cpp llsprite.cpp - llsrv.cpp llstartup.cpp llstatusbar.cpp llstylemap.cpp @@ -400,19 +401,20 @@ set(viewer_SOURCE_FILES lltoolselectland.cpp lltoolselectrect.cpp lltracker.cpp + lluilistener.cpp lluploaddialog.cpp llurl.cpp llurldispatcher.cpp llurlhistory.cpp llurlsimstring.cpp llurlwhitelist.cpp - lluserauth.cpp llvectorperfoptions.cpp llviewchildren.cpp llviewerassetstorage.cpp llvieweraudio.cpp llviewercamera.cpp llviewercontrol.cpp + llviewercontrollistener.cpp llviewerdisplay.cpp llviewerfloaterreg.cpp llviewergenericmessage.cpp @@ -452,6 +454,7 @@ set(viewer_SOURCE_FILES llviewerthrottle.cpp llviewervisualparam.cpp llviewerwindow.cpp + llviewerwindowlistener.cpp llvlcomposition.cpp llvlmanager.cpp llvoavatar.cpp @@ -490,6 +493,7 @@ set(viewer_SOURCE_FILES llworld.cpp llworldmap.cpp llworldmapview.cpp + llxmlrpclistener.cpp llxmlrpctransaction.cpp noise.cpp pipeline.cpp @@ -519,6 +523,7 @@ set(viewer_HEADER_FILES llaccordionctrl.h llactiveimwindow.h llagent.h + llagentlistener.h llagentaccess.h llagentdata.h llagentlanguage.h @@ -528,6 +533,7 @@ set(viewer_HEADER_FILES llanimstatelabels.h llappearance.h llappviewer.h + llappviewerlistener.h llassetuploadresponders.h llassetuploadqueue.h llaudiosourcevo.h @@ -707,6 +713,7 @@ set(viewer_HEADER_FILES llurllineeditorctrl.h lllogchat.h llloginhandler.h + lllogininstance.h llmanip.h llmaniprotate.h llmanipscale.h @@ -776,6 +783,7 @@ set(viewer_HEADER_FILES llpanelpicks.h llpanelplace.h llpanelplaceinfo.h + llpanelshower.h llpanelplaces.h llpanelplacestab.h llpanelprofile.h @@ -810,7 +818,6 @@ set(viewer_HEADER_FILES llspatialpartition.h llsplitbutton.h llsprite.h - llsrv.h llstartup.h llstatusbar.h llstylemap.h @@ -856,13 +863,13 @@ set(viewer_HEADER_FILES lltoolselectrect.h lltracker.h lluiconstants.h + lluilistener.h lluploaddialog.h llurl.h llurldispatcher.h llurlhistory.h llurlsimstring.h llurlwhitelist.h - lluserauth.h llvectorperfoptions.h llviewchildren.h llviewerassetstorage.h @@ -870,6 +877,7 @@ set(viewer_HEADER_FILES llviewerbuild.h llviewercamera.h llviewercontrol.h + llviewercontrollistener.h llviewerdisplay.h llviewerfloaterreg.h llviewergenericmessage.h @@ -907,6 +915,7 @@ set(viewer_HEADER_FILES llviewerthrottle.h llviewervisualparam.h llviewerwindow.h + llviewerwindowlistener.h llvlcomposition.h llvlmanager.h llvoavatar.h @@ -947,6 +956,7 @@ set(viewer_HEADER_FILES llworld.h llworldmap.h llworldmapview.h + llxmlrpclistener.h llxmlrpctransaction.h macmain.h noise.h @@ -1149,6 +1159,15 @@ file(GLOB DEFAULT_WIDGET_FILE_GLOB_LIST ${CMAKE_CURRENT_SOURCE_DIR}/skins/default/xui/en/widgets/*.xml) list(APPEND viewer_XUI_FILES ${DEFAULT_WIDGET_FILE_GLOB_LIST}) +file(GLOB SILVER_XUI_FILE_GLOB_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/skins/silver/xui/en-us/*.xml) +list(APPEND viewer_XUI_FILES ${SILVER_XUI_FILE_GLOB_LIST}) + +# Cannot append empty lists in CMake, wait until we have files here. +#file(GLOB SILVER_WIDGET_FILE_GLOB_LIST +# ${CMAKE_CURRENT_SOURCE_DIR}/skins/silver/xui/en-us/widgets/*.xml) +#list(APPEND viewer_XUI_FILES ${SILVER_WIDGET_FILE_GLOB_LIST}) + list(SORT viewer_XUI_FILES) source_group("XUI Files" FILES ${viewer_XUI_FILES}) @@ -1259,6 +1278,11 @@ if (LLKDU_LIBRARY) add_dependencies(${VIEWER_BINARY_NAME} ${LLKDU_LIBRARY}) endif (LLKDU_LIBRARY) +# add package files +file(GLOB EVENT_HOST_SCRIPT_GLOB_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/../viewer_components/*.py) +list(APPEND EVENT_HOST_SCRIPTS ${EVENT_HOST_SCRIPT_GLOB_LIST}) + set(PACKAGE OFF CACHE BOOL "Add a package target that builds an installer package.") @@ -1315,7 +1339,41 @@ if (WINDOWS) COMMENT "Copying message.xml to the runtime folder." ) - add_dependencies(${VIEWER_BINARY_NAME} copy_win_libs) + if(WINDOWS) + # Copy Win Libs... + # This happens at build time, not config time. We can't glob files in this cmake. + # *FIX:Mani Write a sub script to glob the files... + add_custom_command( + TARGET ${VIEWER_BINARY_NAME} PRE_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS + -E + copy_directory + ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR} + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} + COMMENT "Copying staged dlls." + ) + + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${VIEWER_BINARY_NAME}.exe.config + COMMAND ${PYTHON_EXECUTABLE} + ARGS + ${CMAKE_CURRENT_SOURCE_DIR}/build_win32_appConfig.py + ${CMAKE_CFG_INTDIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} + ${VIEWER_BINARY_NAME}.exe.config + COMMENT "Creating app.config file" + ) + + add_custom_target(create_app_config_file ALL + DEPENDS + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${VIEWER_BINARY_NAME}.exe.config + ) + + add_dependencies(${VIEWER_BINARY_NAME} create_app_config_file) + + endif(WINDOWS) if (EXISTS ${CMAKE_SOURCE_DIR}/copy_win_scripts) add_dependencies(${VIEWER_BINARY_NAME} copy_win_scripts) @@ -1341,8 +1399,27 @@ if (WINDOWS) add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_webkit media_plugin_flash_activex media_plugin_awesomium) if (PACKAGE) - add_custom_target(package ALL DEPENDS ${CMAKE_CFG_INTDIR}/touched.bat) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/event_host.tar.bz2 + COMMAND ${PYTHON_EXECUTABLE} + ARGS + ${CMAKE_CURRENT_SOURCE_DIR}/event_host_manifest.py + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CFG_INTDIR} + + DEPENDS + lleventhost + ${EVENT_HOST_SCRIPTS} + ${CMAKE_CURRENT_SOURCE_DIR}/event_host_manifest.py) + + add_custom_target(package ALL + DEPENDS + ${CMAKE_CFG_INTDIR}/touched.bat + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/event_host.tar.bz2) add_dependencies(package windows-updater windows-crash-logger) + + endif (PACKAGE) endif (WINDOWS) @@ -1379,6 +1456,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${WINDOWS_LIBRARIES} ${XMLRPCEPI_LIBRARIES} ${ELFIO_LIBRARIES} + ${LLLOGIN_LIBRARIES} ${GOOGLE_PERFTOOLS_LIBRARIES} ) @@ -1509,6 +1587,7 @@ endif (INSTALL) include(LLAddBuildTest) SET(viewer_TEST_SOURCE_FILES llagentaccess.cpp + lllogininstance.cpp # Not *actually* a unit test, it's an integration test. # Because it won't work in the new unit test iface, i've commented out # and notified Nat. Delete this when it's replaced! diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 4e64b8662c..2fe8fd5b00 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -5337,6 +5337,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>QAModeEventHostPort</key> + <map> + <key>Comment</key> + <string>Enable Testing Features.</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>-1</integer> + </map> <key>QuietSnapshotsToDisk</key> <map> <key>Comment</key> diff --git a/indra/newview/build_win32_appConfig.py b/indra/newview/build_win32_appConfig.py index fb6a0258bc..8eadf0068f 100644 --- a/indra/newview/build_win32_appConfig.py +++ b/indra/newview/build_win32_appConfig.py @@ -31,11 +31,7 @@ import sys, os, re from xml.dom.minidom import parse -def main(): - src_manifest_name = sys.argv[1] - src_config_name = sys.argv[2] - dst_config_name = sys.argv[3] - +def munge_binding_redirect_version(src_manifest_name, src_config_name, dst_config_name): manifest_dom = parse(src_manifest_name) node = manifest_dom.getElementsByTagName('assemblyIdentity')[0] manifest_assm_ver = node.getAttribute('version') @@ -47,11 +43,31 @@ def main(): node.setAttribute('oldVersion', src_old_ver + manifest_assm_ver) comment = config_dom.createComment("This file is automatically generated by the build. see indra/newview/build_win32_appConfig.py") config_dom.insertBefore(comment, config_dom.childNodes[0]) - + + print "Writing: " + dst_config_name f = open(dst_config_name, 'w') config_dom.writexml(f) f.close() + + + +def main(): + config = sys.argv[1] + src_dir = sys.argv[2] + dst_dir = sys.argv[3] + dst_name = sys.argv[4] + + if config.lower() == 'debug': + src_manifest_name = dst_dir + '/Microsoft.VC80.DebugCRT.manifest' + src_config_name = src_dir + '/SecondLifeDebug.exe.config' + else: + src_manifest_name = dst_dir + '/Microsoft.VC80.CRT.manifest' + src_config_name = src_dir + '/SecondLife.exe.config' + + dst_config_name = dst_dir + '/' + dst_name + munge_binding_redirect_version(src_manifest_name, src_config_name, dst_config_name) + return 0 if __name__ == "__main__": diff --git a/indra/newview/character/avatar_eye.llm b/indra/newview/character/avatar_eye.llm Binary files differnew file mode 100644 index 0000000000..8c6e74e1de --- /dev/null +++ b/indra/newview/character/avatar_eye.llm diff --git a/indra/newview/character/avatar_eye_1.llm b/indra/newview/character/avatar_eye_1.llm Binary files differnew file mode 100644 index 0000000000..7a3b0d6f28 --- /dev/null +++ b/indra/newview/character/avatar_eye_1.llm diff --git a/indra/newview/character/avatar_eyelashes.llm b/indra/newview/character/avatar_eyelashes.llm Binary files differnew file mode 100644 index 0000000000..99995b5758 --- /dev/null +++ b/indra/newview/character/avatar_eyelashes.llm diff --git a/indra/newview/character/avatar_hair.llm b/indra/newview/character/avatar_hair.llm Binary files differnew file mode 100644 index 0000000000..df99de8db7 --- /dev/null +++ b/indra/newview/character/avatar_hair.llm diff --git a/indra/newview/character/avatar_hair_1.llm b/indra/newview/character/avatar_hair_1.llm Binary files differnew file mode 100644 index 0000000000..6de31fdc23 --- /dev/null +++ b/indra/newview/character/avatar_hair_1.llm diff --git a/indra/newview/character/avatar_hair_2.llm b/indra/newview/character/avatar_hair_2.llm Binary files differnew file mode 100644 index 0000000000..47d02ba9ce --- /dev/null +++ b/indra/newview/character/avatar_hair_2.llm diff --git a/indra/newview/character/avatar_hair_3.llm b/indra/newview/character/avatar_hair_3.llm Binary files differnew file mode 100644 index 0000000000..107f9e2a53 --- /dev/null +++ b/indra/newview/character/avatar_hair_3.llm diff --git a/indra/newview/character/avatar_hair_4.llm b/indra/newview/character/avatar_hair_4.llm Binary files differnew file mode 100644 index 0000000000..1b9a12a0ca --- /dev/null +++ b/indra/newview/character/avatar_hair_4.llm diff --git a/indra/newview/character/avatar_hair_5.llm b/indra/newview/character/avatar_hair_5.llm Binary files differnew file mode 100644 index 0000000000..1b9a12a0ca --- /dev/null +++ b/indra/newview/character/avatar_hair_5.llm diff --git a/indra/newview/character/avatar_head.llm b/indra/newview/character/avatar_head.llm Binary files differnew file mode 100644 index 0000000000..8d8b5e0442 --- /dev/null +++ b/indra/newview/character/avatar_head.llm diff --git a/indra/newview/character/avatar_head_1.llm b/indra/newview/character/avatar_head_1.llm Binary files differnew file mode 100644 index 0000000000..26291e6584 --- /dev/null +++ b/indra/newview/character/avatar_head_1.llm diff --git a/indra/newview/character/avatar_head_2.llm b/indra/newview/character/avatar_head_2.llm Binary files differnew file mode 100644 index 0000000000..c2b808b1a6 --- /dev/null +++ b/indra/newview/character/avatar_head_2.llm diff --git a/indra/newview/character/avatar_head_3.llm b/indra/newview/character/avatar_head_3.llm Binary files differnew file mode 100644 index 0000000000..a0676b1f1c --- /dev/null +++ b/indra/newview/character/avatar_head_3.llm diff --git a/indra/newview/character/avatar_head_4.llm b/indra/newview/character/avatar_head_4.llm Binary files differnew file mode 100644 index 0000000000..5035585770 --- /dev/null +++ b/indra/newview/character/avatar_head_4.llm diff --git a/indra/newview/character/avatar_lower_body.llm b/indra/newview/character/avatar_lower_body.llm Binary files differnew file mode 100644 index 0000000000..0420899739 --- /dev/null +++ b/indra/newview/character/avatar_lower_body.llm diff --git a/indra/newview/character/avatar_lower_body_1.llm b/indra/newview/character/avatar_lower_body_1.llm Binary files differnew file mode 100644 index 0000000000..1394eb848b --- /dev/null +++ b/indra/newview/character/avatar_lower_body_1.llm diff --git a/indra/newview/character/avatar_lower_body_2.llm b/indra/newview/character/avatar_lower_body_2.llm Binary files differnew file mode 100644 index 0000000000..0da9c1249e --- /dev/null +++ b/indra/newview/character/avatar_lower_body_2.llm diff --git a/indra/newview/character/avatar_lower_body_3.llm b/indra/newview/character/avatar_lower_body_3.llm Binary files differnew file mode 100644 index 0000000000..f3c49a1568 --- /dev/null +++ b/indra/newview/character/avatar_lower_body_3.llm diff --git a/indra/newview/character/avatar_lower_body_4.llm b/indra/newview/character/avatar_lower_body_4.llm Binary files differnew file mode 100644 index 0000000000..e71721063e --- /dev/null +++ b/indra/newview/character/avatar_lower_body_4.llm diff --git a/indra/newview/character/avatar_skirt.llm b/indra/newview/character/avatar_skirt.llm Binary files differnew file mode 100644 index 0000000000..08ce3d1700 --- /dev/null +++ b/indra/newview/character/avatar_skirt.llm diff --git a/indra/newview/character/avatar_skirt_1.llm b/indra/newview/character/avatar_skirt_1.llm Binary files differnew file mode 100644 index 0000000000..88076c321f --- /dev/null +++ b/indra/newview/character/avatar_skirt_1.llm diff --git a/indra/newview/character/avatar_skirt_2.llm b/indra/newview/character/avatar_skirt_2.llm Binary files differnew file mode 100644 index 0000000000..73b3effbc6 --- /dev/null +++ b/indra/newview/character/avatar_skirt_2.llm diff --git a/indra/newview/character/avatar_skirt_3.llm b/indra/newview/character/avatar_skirt_3.llm Binary files differnew file mode 100644 index 0000000000..ded546fdea --- /dev/null +++ b/indra/newview/character/avatar_skirt_3.llm diff --git a/indra/newview/character/avatar_skirt_4.llm b/indra/newview/character/avatar_skirt_4.llm Binary files differnew file mode 100644 index 0000000000..b9d5cb945e --- /dev/null +++ b/indra/newview/character/avatar_skirt_4.llm diff --git a/indra/newview/character/avatar_upper_body.llm b/indra/newview/character/avatar_upper_body.llm Binary files differnew file mode 100644 index 0000000000..da7d990540 --- /dev/null +++ b/indra/newview/character/avatar_upper_body.llm diff --git a/indra/newview/character/avatar_upper_body_1.llm b/indra/newview/character/avatar_upper_body_1.llm Binary files differnew file mode 100644 index 0000000000..31e104cc20 --- /dev/null +++ b/indra/newview/character/avatar_upper_body_1.llm diff --git a/indra/newview/character/avatar_upper_body_2.llm b/indra/newview/character/avatar_upper_body_2.llm Binary files differnew file mode 100644 index 0000000000..c1f4199b9c --- /dev/null +++ b/indra/newview/character/avatar_upper_body_2.llm diff --git a/indra/newview/character/avatar_upper_body_3.llm b/indra/newview/character/avatar_upper_body_3.llm Binary files differnew file mode 100644 index 0000000000..9e89ed8b3e --- /dev/null +++ b/indra/newview/character/avatar_upper_body_3.llm diff --git a/indra/newview/character/avatar_upper_body_4.llm b/indra/newview/character/avatar_upper_body_4.llm Binary files differnew file mode 100644 index 0000000000..ec836d1dc3 --- /dev/null +++ b/indra/newview/character/avatar_upper_body_4.llm diff --git a/indra/newview/character/blush_alpha.tga b/indra/newview/character/blush_alpha.tga Binary files differnew file mode 100644 index 0000000000..05be7e7e3d --- /dev/null +++ b/indra/newview/character/blush_alpha.tga diff --git a/indra/newview/character/body_skingrain.tga b/indra/newview/character/body_skingrain.tga Binary files differnew file mode 100644 index 0000000000..7264baac14 --- /dev/null +++ b/indra/newview/character/body_skingrain.tga diff --git a/indra/newview/character/bodyfreckles_alpha.tga b/indra/newview/character/bodyfreckles_alpha.tga Binary files differnew file mode 100644 index 0000000000..d30ab3d122 --- /dev/null +++ b/indra/newview/character/bodyfreckles_alpha.tga diff --git a/indra/newview/character/bump_face_wrinkles.tga b/indra/newview/character/bump_face_wrinkles.tga Binary files differnew file mode 100644 index 0000000000..54bf7a55be --- /dev/null +++ b/indra/newview/character/bump_face_wrinkles.tga diff --git a/indra/newview/character/bump_head_base.tga b/indra/newview/character/bump_head_base.tga Binary files differnew file mode 100644 index 0000000000..fa3568573a --- /dev/null +++ b/indra/newview/character/bump_head_base.tga diff --git a/indra/newview/character/bump_lowerbody_base.tga b/indra/newview/character/bump_lowerbody_base.tga Binary files differnew file mode 100644 index 0000000000..498ea3c721 --- /dev/null +++ b/indra/newview/character/bump_lowerbody_base.tga diff --git a/indra/newview/character/bump_pants_wrinkles.tga b/indra/newview/character/bump_pants_wrinkles.tga Binary files differnew file mode 100644 index 0000000000..cca72415e8 --- /dev/null +++ b/indra/newview/character/bump_pants_wrinkles.tga diff --git a/indra/newview/character/bump_shirt_wrinkles.tga b/indra/newview/character/bump_shirt_wrinkles.tga Binary files differnew file mode 100644 index 0000000000..9e0d757a48 --- /dev/null +++ b/indra/newview/character/bump_shirt_wrinkles.tga diff --git a/indra/newview/character/bump_upperbody_base.tga b/indra/newview/character/bump_upperbody_base.tga Binary files differnew file mode 100644 index 0000000000..e57d6352e6 --- /dev/null +++ b/indra/newview/character/bump_upperbody_base.tga diff --git a/indra/newview/character/eyebrows_alpha.tga b/indra/newview/character/eyebrows_alpha.tga Binary files differnew file mode 100644 index 0000000000..c363e482e1 --- /dev/null +++ b/indra/newview/character/eyebrows_alpha.tga diff --git a/indra/newview/character/eyeliner_alpha.tga b/indra/newview/character/eyeliner_alpha.tga Binary files differnew file mode 100644 index 0000000000..1611eb3355 --- /dev/null +++ b/indra/newview/character/eyeliner_alpha.tga diff --git a/indra/newview/character/eyeshadow_inner_alpha.tga b/indra/newview/character/eyeshadow_inner_alpha.tga Binary files differnew file mode 100644 index 0000000000..37d7919395 --- /dev/null +++ b/indra/newview/character/eyeshadow_inner_alpha.tga diff --git a/indra/newview/character/eyeshadow_outer_alpha.tga b/indra/newview/character/eyeshadow_outer_alpha.tga Binary files differnew file mode 100644 index 0000000000..00eef9d9f7 --- /dev/null +++ b/indra/newview/character/eyeshadow_outer_alpha.tga diff --git a/indra/newview/character/eyewhite.tga b/indra/newview/character/eyewhite.tga Binary files differnew file mode 100644 index 0000000000..a720496988 --- /dev/null +++ b/indra/newview/character/eyewhite.tga diff --git a/indra/newview/character/facehair_chincurtains_alpha.tga b/indra/newview/character/facehair_chincurtains_alpha.tga Binary files differnew file mode 100644 index 0000000000..b10397063c --- /dev/null +++ b/indra/newview/character/facehair_chincurtains_alpha.tga diff --git a/indra/newview/character/facehair_moustache_alpha.tga b/indra/newview/character/facehair_moustache_alpha.tga Binary files differnew file mode 100644 index 0000000000..4068c4f2b1 --- /dev/null +++ b/indra/newview/character/facehair_moustache_alpha.tga diff --git a/indra/newview/character/facehair_sideburns_alpha.tga b/indra/newview/character/facehair_sideburns_alpha.tga Binary files differnew file mode 100644 index 0000000000..acddc2d9bd --- /dev/null +++ b/indra/newview/character/facehair_sideburns_alpha.tga diff --git a/indra/newview/character/facehair_soulpatch_alpha.tga b/indra/newview/character/facehair_soulpatch_alpha.tga Binary files differnew file mode 100644 index 0000000000..687091a29f --- /dev/null +++ b/indra/newview/character/facehair_soulpatch_alpha.tga diff --git a/indra/newview/character/freckles_alpha.tga b/indra/newview/character/freckles_alpha.tga Binary files differnew file mode 100644 index 0000000000..a9a4ec0735 --- /dev/null +++ b/indra/newview/character/freckles_alpha.tga diff --git a/indra/newview/character/glove_length_alpha.tga b/indra/newview/character/glove_length_alpha.tga Binary files differnew file mode 100644 index 0000000000..db89ad57e7 --- /dev/null +++ b/indra/newview/character/glove_length_alpha.tga diff --git a/indra/newview/character/gloves_fingers_alpha.tga b/indra/newview/character/gloves_fingers_alpha.tga Binary files differnew file mode 100644 index 0000000000..dba2eec277 --- /dev/null +++ b/indra/newview/character/gloves_fingers_alpha.tga diff --git a/indra/newview/character/head_alpha.tga b/indra/newview/character/head_alpha.tga Binary files differnew file mode 100644 index 0000000000..8164525353 --- /dev/null +++ b/indra/newview/character/head_alpha.tga diff --git a/indra/newview/character/head_color.tga b/indra/newview/character/head_color.tga Binary files differnew file mode 100644 index 0000000000..74b1b3078b --- /dev/null +++ b/indra/newview/character/head_color.tga diff --git a/indra/newview/character/head_hair.tga b/indra/newview/character/head_hair.tga Binary files differnew file mode 100644 index 0000000000..5321f35204 --- /dev/null +++ b/indra/newview/character/head_hair.tga diff --git a/indra/newview/character/head_highlights_alpha.tga b/indra/newview/character/head_highlights_alpha.tga Binary files differnew file mode 100644 index 0000000000..8dc5239f97 --- /dev/null +++ b/indra/newview/character/head_highlights_alpha.tga diff --git a/indra/newview/character/head_shading_alpha.tga b/indra/newview/character/head_shading_alpha.tga Binary files differnew file mode 100644 index 0000000000..e8ea490109 --- /dev/null +++ b/indra/newview/character/head_shading_alpha.tga diff --git a/indra/newview/character/head_skingrain.tga b/indra/newview/character/head_skingrain.tga Binary files differnew file mode 100644 index 0000000000..b42dee0809 --- /dev/null +++ b/indra/newview/character/head_skingrain.tga diff --git a/indra/newview/character/jacket_length_lower_alpha.tga b/indra/newview/character/jacket_length_lower_alpha.tga Binary files differnew file mode 100644 index 0000000000..722bc192a8 --- /dev/null +++ b/indra/newview/character/jacket_length_lower_alpha.tga diff --git a/indra/newview/character/jacket_length_upper_alpha.tga b/indra/newview/character/jacket_length_upper_alpha.tga Binary files differnew file mode 100644 index 0000000000..e9db7e7b1f --- /dev/null +++ b/indra/newview/character/jacket_length_upper_alpha.tga diff --git a/indra/newview/character/jacket_open_lower_alpha.tga b/indra/newview/character/jacket_open_lower_alpha.tga Binary files differnew file mode 100644 index 0000000000..db0c2fb0e3 --- /dev/null +++ b/indra/newview/character/jacket_open_lower_alpha.tga diff --git a/indra/newview/character/jacket_open_upper_alpha.tga b/indra/newview/character/jacket_open_upper_alpha.tga Binary files differnew file mode 100644 index 0000000000..71b8a0b805 --- /dev/null +++ b/indra/newview/character/jacket_open_upper_alpha.tga diff --git a/indra/newview/character/lipgloss_alpha.tga b/indra/newview/character/lipgloss_alpha.tga Binary files differnew file mode 100644 index 0000000000..78ceecaf85 --- /dev/null +++ b/indra/newview/character/lipgloss_alpha.tga diff --git a/indra/newview/character/lips_mask.tga b/indra/newview/character/lips_mask.tga Binary files differnew file mode 100644 index 0000000000..ae1401c006 --- /dev/null +++ b/indra/newview/character/lips_mask.tga diff --git a/indra/newview/character/lipstick_alpha.tga b/indra/newview/character/lipstick_alpha.tga Binary files differnew file mode 100644 index 0000000000..2795f1bd40 --- /dev/null +++ b/indra/newview/character/lipstick_alpha.tga diff --git a/indra/newview/character/lowerbody_color.tga b/indra/newview/character/lowerbody_color.tga Binary files differnew file mode 100644 index 0000000000..a63aa12fca --- /dev/null +++ b/indra/newview/character/lowerbody_color.tga diff --git a/indra/newview/character/lowerbody_highlights_alpha.tga b/indra/newview/character/lowerbody_highlights_alpha.tga Binary files differnew file mode 100644 index 0000000000..ae3413ac8b --- /dev/null +++ b/indra/newview/character/lowerbody_highlights_alpha.tga diff --git a/indra/newview/character/lowerbody_shading_alpha.tga b/indra/newview/character/lowerbody_shading_alpha.tga Binary files differnew file mode 100644 index 0000000000..0242663a7d --- /dev/null +++ b/indra/newview/character/lowerbody_shading_alpha.tga diff --git a/indra/newview/character/nailpolish_alpha.tga b/indra/newview/character/nailpolish_alpha.tga Binary files differnew file mode 100644 index 0000000000..91af762902 --- /dev/null +++ b/indra/newview/character/nailpolish_alpha.tga diff --git a/indra/newview/character/pants_length_alpha.tga b/indra/newview/character/pants_length_alpha.tga Binary files differnew file mode 100644 index 0000000000..3c4f21c0f2 --- /dev/null +++ b/indra/newview/character/pants_length_alpha.tga diff --git a/indra/newview/character/pants_waist_alpha.tga b/indra/newview/character/pants_waist_alpha.tga Binary files differnew file mode 100644 index 0000000000..35658c0896 --- /dev/null +++ b/indra/newview/character/pants_waist_alpha.tga diff --git a/indra/newview/character/rosyface_alpha.tga b/indra/newview/character/rosyface_alpha.tga Binary files differnew file mode 100644 index 0000000000..a0c8513da2 --- /dev/null +++ b/indra/newview/character/rosyface_alpha.tga diff --git a/indra/newview/character/rouge_alpha.tga b/indra/newview/character/rouge_alpha.tga Binary files differnew file mode 100644 index 0000000000..a0c8513da2 --- /dev/null +++ b/indra/newview/character/rouge_alpha.tga diff --git a/indra/newview/character/shirt_bottom_alpha.tga b/indra/newview/character/shirt_bottom_alpha.tga Binary files differnew file mode 100644 index 0000000000..7cce03dbe0 --- /dev/null +++ b/indra/newview/character/shirt_bottom_alpha.tga diff --git a/indra/newview/character/shirt_collar_alpha.tga b/indra/newview/character/shirt_collar_alpha.tga Binary files differnew file mode 100644 index 0000000000..f55f635473 --- /dev/null +++ b/indra/newview/character/shirt_collar_alpha.tga diff --git a/indra/newview/character/shirt_collar_back_alpha.tga b/indra/newview/character/shirt_collar_back_alpha.tga Binary files differnew file mode 100644 index 0000000000..43a6453107 --- /dev/null +++ b/indra/newview/character/shirt_collar_back_alpha.tga diff --git a/indra/newview/character/shirt_sleeve_alpha.tga b/indra/newview/character/shirt_sleeve_alpha.tga Binary files differnew file mode 100644 index 0000000000..e3b18f4fc6 --- /dev/null +++ b/indra/newview/character/shirt_sleeve_alpha.tga diff --git a/indra/newview/character/shoe_height_alpha.tga b/indra/newview/character/shoe_height_alpha.tga Binary files differnew file mode 100644 index 0000000000..d08dd750f3 --- /dev/null +++ b/indra/newview/character/shoe_height_alpha.tga diff --git a/indra/newview/character/skirt_length_alpha.tga b/indra/newview/character/skirt_length_alpha.tga Binary files differnew file mode 100644 index 0000000000..c86799469d --- /dev/null +++ b/indra/newview/character/skirt_length_alpha.tga diff --git a/indra/newview/character/skirt_slit_back_alpha.tga b/indra/newview/character/skirt_slit_back_alpha.tga Binary files differnew file mode 100644 index 0000000000..0e49688b14 --- /dev/null +++ b/indra/newview/character/skirt_slit_back_alpha.tga diff --git a/indra/newview/character/skirt_slit_front_alpha.tga b/indra/newview/character/skirt_slit_front_alpha.tga Binary files differnew file mode 100644 index 0000000000..888bbf71a1 --- /dev/null +++ b/indra/newview/character/skirt_slit_front_alpha.tga diff --git a/indra/newview/character/skirt_slit_left_alpha.tga b/indra/newview/character/skirt_slit_left_alpha.tga Binary files differnew file mode 100644 index 0000000000..210feac1ea --- /dev/null +++ b/indra/newview/character/skirt_slit_left_alpha.tga diff --git a/indra/newview/character/skirt_slit_right_alpha.tga b/indra/newview/character/skirt_slit_right_alpha.tga Binary files differnew file mode 100644 index 0000000000..ce11c64bf6 --- /dev/null +++ b/indra/newview/character/skirt_slit_right_alpha.tga diff --git a/indra/newview/character/underpants_trial_female.tga b/indra/newview/character/underpants_trial_female.tga Binary files differnew file mode 100644 index 0000000000..96bf732351 --- /dev/null +++ b/indra/newview/character/underpants_trial_female.tga diff --git a/indra/newview/character/underpants_trial_male.tga b/indra/newview/character/underpants_trial_male.tga Binary files differnew file mode 100644 index 0000000000..095695ca1c --- /dev/null +++ b/indra/newview/character/underpants_trial_male.tga diff --git a/indra/newview/character/undershirt_trial_female.tga b/indra/newview/character/undershirt_trial_female.tga Binary files differnew file mode 100644 index 0000000000..e17a309531 --- /dev/null +++ b/indra/newview/character/undershirt_trial_female.tga diff --git a/indra/newview/character/upperbody_color.tga b/indra/newview/character/upperbody_color.tga Binary files differnew file mode 100644 index 0000000000..85fcc41142 --- /dev/null +++ b/indra/newview/character/upperbody_color.tga diff --git a/indra/newview/character/upperbody_highlights_alpha.tga b/indra/newview/character/upperbody_highlights_alpha.tga Binary files differnew file mode 100644 index 0000000000..2d8102b583 --- /dev/null +++ b/indra/newview/character/upperbody_highlights_alpha.tga diff --git a/indra/newview/character/upperbody_shading_alpha.tga b/indra/newview/character/upperbody_shading_alpha.tga Binary files differnew file mode 100644 index 0000000000..b420506b3e --- /dev/null +++ b/indra/newview/character/upperbody_shading_alpha.tga diff --git a/indra/newview/character/upperbodyfreckles_alpha.tga b/indra/newview/character/upperbodyfreckles_alpha.tga Binary files differnew file mode 100644 index 0000000000..76c7ce8849 --- /dev/null +++ b/indra/newview/character/upperbodyfreckles_alpha.tga diff --git a/indra/newview/da.lproj/language.txt b/indra/newview/da.lproj/language.txt new file mode 100644 index 0000000000..316d25d903 --- /dev/null +++ b/indra/newview/da.lproj/language.txt @@ -0,0 +1 @@ +da
\ No newline at end of file diff --git a/indra/newview/es.lproj/language.txt b/indra/newview/es.lproj/language.txt new file mode 100644 index 0000000000..6c4381495c --- /dev/null +++ b/indra/newview/es.lproj/language.txt @@ -0,0 +1 @@ +es
\ No newline at end of file diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index 05bdf64b42..d557df3d92 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -1,4 +1,4 @@ -version 19 +version 20 // NOTE: This is mostly identical to featuretable_mac.txt with a few differences // Should be combined into one table @@ -245,6 +245,7 @@ RenderVBOEnable 1 0 list Intel RenderAnisotropic 1 0 RenderLightingDetail 1 0 +RenderUseImpostors 0 0 list GeForce2 RenderAnisotropic 1 0 diff --git a/indra/newview/featuretable_linux.txt b/indra/newview/featuretable_linux.txt index 09105b8d88..57d712ede7 100644 --- a/indra/newview/featuretable_linux.txt +++ b/indra/newview/featuretable_linux.txt @@ -1,4 +1,4 @@ -version 19 +version 20 // NOTE: This is mostly identical to featuretable_mac.txt with a few differences // Should be combined into one table diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index fd2496d8ad..15974c0691 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -1,4 +1,4 @@ -version 19 +version 20 // NOTE: This is mostly identical to featuretable_mac.txt with a few differences // Should be combined into one table diff --git a/indra/newview/fr.lproj/language.txt b/indra/newview/fr.lproj/language.txt new file mode 100644 index 0000000000..717280ac26 --- /dev/null +++ b/indra/newview/fr.lproj/language.txt @@ -0,0 +1 @@ +fr
\ No newline at end of file diff --git a/indra/newview/hu.lproj/language.txt b/indra/newview/hu.lproj/language.txt new file mode 100644 index 0000000000..6c604cf2a9 --- /dev/null +++ b/indra/newview/hu.lproj/language.txt @@ -0,0 +1 @@ +hu
\ No newline at end of file diff --git a/indra/newview/installers/windows/lang_fr.nsi b/indra/newview/installers/windows/lang_fr.nsi Binary files differindex d23615de91..008c240ed7 100644 --- a/indra/newview/installers/windows/lang_fr.nsi +++ b/indra/newview/installers/windows/lang_fr.nsi diff --git a/indra/newview/it.lproj/language.txt b/indra/newview/it.lproj/language.txt new file mode 100644 index 0000000000..c91971434c --- /dev/null +++ b/indra/newview/it.lproj/language.txt @@ -0,0 +1 @@ +it
\ No newline at end of file diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 214065f080..a4692dd38b 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -35,6 +35,7 @@ #include "llagent.h" #include "llagentwearables.h" +#include "llagentlistener.h" #include "llanimationstates.h" #include "llcallingcard.h" #include "llconsole.h" @@ -257,6 +258,7 @@ LLAgent::LLAgent() : mHUDTargetZoom(1.f), mHUDCurZoom(1.f), mInitialized(FALSE), + mListener(), mForceMouselook(FALSE), mDoubleTapRunTimer(), @@ -385,6 +387,8 @@ LLAgent::LLAgent() : } mFollowCam.setMaxCameraDistantFromSubject( MAX_CAMERA_DISTANCE_FROM_AGENT ); + + mListener.reset(new LLAgentListener(*this)); } // Requires gSavedSettings to be initialized. diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 9f71def63e..7e845cafb7 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -105,6 +105,8 @@ struct LLGroupData std::string mName; }; +class LLAgentListener; + //------------------------------------------------------------------------ // LLAgent //------------------------------------------------------------------------ @@ -142,6 +144,8 @@ public: BOOL mInitialized; BOOL mFirstLogin; std::string mMOTD; // Message of the day +private: + boost::shared_ptr<LLAgentListener> mListener; //-------------------------------------------------------------------- // Session diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp new file mode 100644 index 0000000000..0f00078b33 --- /dev/null +++ b/indra/newview/llagentlistener.cpp @@ -0,0 +1,78 @@ +/** + * @file llagentlistener.cpp + * @author Brad Kittenbrink + * @date 2009-07-10 + * @brief Implementation for llagentlistener. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llagentlistener.h" + +#include "llagent.h" +#include "llcommandhandler.h" +#include "llslurl.h" +#include "llurldispatcher.h" +#include "llviewerobject.h" +#include "llviewerobjectlist.h" +#include "llviewerregion.h" + +LLAgentListener::LLAgentListener(LLAgent &agent) + : LLDispatchListener("LLAgent", "op"), + mAgent(agent) +{ + add("requestTeleport", &LLAgentListener::requestTeleport); + add("requestSit", &LLAgentListener::requestSit); + add("requestStand", &LLAgentListener::requestStand); +} + +void LLAgentListener::requestTeleport(LLSD const & event_data) const +{ + if(event_data["skip_confirmation"].asBoolean()) + { + LLSD params(LLSD::emptyArray()); + params.append(event_data["regionname"]); + params.append(event_data["x"]); + params.append(event_data["y"]); + params.append(event_data["z"]); + LLCommandDispatcher::dispatch("teleport", params, LLSD(), NULL, true); + // *TODO - lookup other LLCommandHandlers for "agent", "classified", "event", "group", "floater", "objectim", "parcel", "login", login_refresh", "balance", "chat" + // should we just compose LLCommandHandler and LLDispatchListener? + } + else + { + std::string url = LLSLURL::buildSLURL(event_data["regionname"], event_data["x"], event_data["y"], event_data["z"]); + LLURLDispatcher::dispatch(url, NULL, false); + } +} + +void LLAgentListener::requestSit(LLSD const & event_data) const +{ + //mAgent.getAvatarObject()->sitOnObject(); + // shamelessly ripped from llviewermenu.cpp:handle_sit_or_stand() + // *TODO - find a permanent place to share this code properly. + LLViewerObject *object = gObjectList.findObject(event_data["obj_uuid"]); + + if (object && object->getPCode() == LL_PCODE_VOLUME) + { + gMessageSystem->newMessageFast(_PREHASH_AgentRequestSit); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, mAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, mAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_TargetObject); + gMessageSystem->addUUIDFast(_PREHASH_TargetID, object->mID); + gMessageSystem->addVector3Fast(_PREHASH_Offset, LLVector3(0,0,0)); + + object->getRegion()->sendReliableMessage(); + } +} + +void LLAgentListener::requestStand(LLSD const & event_data) const +{ + mAgent.setControlFlags(AGENT_CONTROL_STAND_UP); +} + diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h new file mode 100644 index 0000000000..6f0b5a54c5 --- /dev/null +++ b/indra/newview/llagentlistener.h @@ -0,0 +1,36 @@ +/** + * @file llagentlistener.h + * @author Brad Kittenbrink + * @date 2009-07-09 + * @brief Event API for subset of LLViewerControl methods + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + + +#ifndef LL_LLAGENTLISTENER_H +#define LL_LLAGENTLISTENER_H + +#include "lleventdispatcher.h" + +class LLAgent; +class LLSD; + +class LLAgentListener : public LLDispatchListener +{ +public: + LLAgentListener(LLAgent &agent); + +private: + void requestTeleport(LLSD const & event_data) const; + void requestSit(LLSD const & event_data) const; + void requestStand(LLSD const & event_data) const; + +private: + LLAgent & mAgent; +}; + +#endif // LL_LLAGENTLISTENER_H + diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 76c1d699f6..dd00001cd4 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -73,6 +73,7 @@ #include "llurlhistory.h" #include "llfirstuse.h" #include "llrender.h" +#include "llteleporthistory.h" #include "lllocationhistory.h" #include "llfasttimerview.h" #include "llweb.h" @@ -94,6 +95,10 @@ # include <sys/file.h> // For initMarkerFile support #endif +#include "llapr.h" +#include "apr_dso.h" +#include <boost/lexical_cast.hpp> + #include "llnotify.h" #include "llviewerkeyboard.h" #include "lllfsthread.h" @@ -149,7 +154,6 @@ #include "llfolderview.h" #include "lltoolbar.h" #include "llagentpilot.h" -#include "llsrv.h" #include "llvovolume.h" #include "llflexibleobject.h" #include "llvosurfacepatch.h" @@ -186,7 +190,10 @@ //---------------------------------------------------------------------------- // llviewernetwork.h #include "llviewernetwork.h" +// define a self-registering event API object +#include "llappviewerlistener.h" +static LLAppViewerListener sAppViewerListener("LLAppViewer", NULL); ////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor // @@ -212,9 +219,6 @@ BOOL gAllowTapTapHoldRun = TRUE; BOOL gShowObjectUpdates = FALSE; BOOL gUseQuickTime = TRUE; -BOOL gAcceptTOS = FALSE; -BOOL gAcceptCriticalMessage = FALSE; - eLastExecEvent gLastExecEvent = LAST_EXEC_NORMAL; LLSD gDebugInfo; @@ -560,9 +564,9 @@ LLAppViewer::LLAppViewer() : mYieldTime(-1), mMainloopTimeout(NULL), mAgentRegionLastAlive(false), - mFastTimerLogThread(NULL), mRandomizeFramerate(LLCachedControl<bool>(gSavedSettings,"Randomize Framerate", FALSE)), - mPeriodicSlowFrame(LLCachedControl<bool>(gSavedSettings,"Periodic Slow Frame", FALSE)) + mPeriodicSlowFrame(LLCachedControl<bool>(gSavedSettings,"Periodic Slow Frame", FALSE)), + mFastTimerLogThread(NULL) { if(NULL != sInstance) { @@ -863,6 +867,11 @@ bool LLAppViewer::init() LLViewerJoystick::getInstance()->init(false); + if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0) + { + loadEventHostModule(gSavedSettings.getS32("QAModeEventHostPort")); + } + return true; } @@ -1156,6 +1165,20 @@ bool LLAppViewer::mainLoop() bool LLAppViewer::cleanup() { + // *TODO - generalize this and move DSO wrangling to a helper class -brad + std::set<struct apr_dso_handle_t *>::const_iterator i; + for(i = mPlugins.begin(); i != mPlugins.end(); ++i) + { + int (*ll_plugin_stop_func)(void) = NULL; + apr_status_t rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll_plugin_stop_func, *i, "ll_plugin_stop"); + ll_plugin_stop_func(); + + // *NOTE - disabled unloading as partial solution to DEV-35406 crash on shutdown + //rv = apr_dso_unload(*i); + (void)rv; + } + mPlugins.clear(); + //---------------------------------------------- //this test code will be removed after the test //test manual call stack tracer @@ -3654,6 +3677,17 @@ void LLAppViewer::idleShutdown() { return; } + + // ProductEngine: Try moving this code to where we shut down sTextureCache in cleanup() + // *TODO: ugly + static bool saved_teleport_history = false; + if (!saved_teleport_history) + { + saved_teleport_history = true; + LLTeleportHistory::getInstance()->dump(); + LLLocationHistory::getInstance()->save(); // *TODO: find a better place for doing this + return; + } static bool saved_snapshot = false; if (!saved_snapshot) @@ -3952,7 +3986,7 @@ void LLAppViewer::forceErrorBadMemoryAccess() return; } -void LLAppViewer::forceErrorInifiniteLoop() +void LLAppViewer::forceErrorInfiniteLoop() { while(true) { @@ -4073,3 +4107,164 @@ void LLAppViewer::handleLoginComplete() writeDebugInfo(); } + +// *TODO - generalize this and move DSO wrangling to a helper class -brad +void LLAppViewer::loadEventHostModule(S32 listen_port) +{ + std::string dso_name = +#if LL_WINDOWS + "lleventhost.dll"; +#elif LL_DARWIN + "liblleventhost.dylib"; +#else + "liblleventhost.so"; +#endif + + std::string dso_path = gDirUtilp->findFile(dso_name, + gDirUtilp->getAppRODataDir(), + gDirUtilp->getExecutableDir()); + + if(dso_path == "") + { + llwarns << "QAModeEventHost requested but module \"" << dso_name << "\" not found!" << llendl; + return; + } + + apr_dso_handle_t * eventhost_dso_handle = NULL; + apr_pool_t * eventhost_dso_memory_pool = NULL; + + //attempt to load the shared library + apr_pool_create(&eventhost_dso_memory_pool, NULL); + apr_status_t rv = apr_dso_load(&eventhost_dso_handle, + dso_path.c_str(), + eventhost_dso_memory_pool); + ll_apr_assert_status(rv); + llassert_always(eventhost_dso_handle != NULL); + + int (*ll_plugin_start_func)(LLSD const &) = NULL; + rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll_plugin_start_func, eventhost_dso_handle, "ll_plugin_start"); + + ll_apr_assert_status(rv); + llassert_always(ll_plugin_start_func != NULL); + + LLSD args; + args["listen_port"] = listen_port; + + int status = ll_plugin_start_func(args); + + if(status != 0) + { + llwarns << "problem loading eventhost plugin, status: " << status << llendl; + } + + mPlugins.insert(eventhost_dso_handle); +} + +void LLAppViewer::launchUpdater() +{ + LLSD query_map = LLSD::emptyMap(); + // *TODO place os string in a global constant +#if LL_WINDOWS + query_map["os"] = "win"; +#elif LL_DARWIN + query_map["os"] = "mac"; +#elif LL_LINUX + query_map["os"] = "lnx"; +#elif LL_SOLARIS + query_map["os"] = "sol"; +#endif + // *TODO change userserver to be grid on both viewer and sim, since + // userserver no longer exists. + query_map["userserver"] = LLViewerLogin::getInstance()->getGridLabel(); + query_map["channel"] = gSavedSettings.getString("VersionChannelName"); + // *TODO constantize this guy + // *NOTE: This URL is also used in win_setup/lldownloader.cpp + LLURI update_url = LLURI::buildHTTP("secondlife.com", 80, "update.php", query_map); + + if(LLAppViewer::sUpdaterInfo) + { + delete LLAppViewer::sUpdaterInfo; + } + LLAppViewer::sUpdaterInfo = new LLAppViewer::LLUpdaterInfo() ; + +#if LL_WINDOWS + LLAppViewer::sUpdaterInfo->mUpdateExePath = gDirUtilp->getTempFilename(); + if (LLAppViewer::sUpdaterInfo->mUpdateExePath.empty()) + { + delete LLAppViewer::sUpdaterInfo ; + LLAppViewer::sUpdaterInfo = NULL ; + + // We're hosed, bail + LL_WARNS("AppInit") << "LLDir::getTempFilename() failed" << LL_ENDL; + return; + } + + LLAppViewer::sUpdaterInfo->mUpdateExePath += ".exe"; + + std::string updater_source = gDirUtilp->getAppRODataDir(); + updater_source += gDirUtilp->getDirDelimiter(); + updater_source += "updater.exe"; + + LL_DEBUGS("AppInit") << "Calling CopyFile source: " << updater_source + << " dest: " << LLAppViewer::sUpdaterInfo->mUpdateExePath + << LL_ENDL; + + + if (!CopyFileA(updater_source.c_str(), LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), FALSE)) + { + delete LLAppViewer::sUpdaterInfo ; + LLAppViewer::sUpdaterInfo = NULL ; + + LL_WARNS("AppInit") << "Unable to copy the updater!" << LL_ENDL; + + return; + } + + // if a sim name was passed in via command line parameter (typically through a SLURL) + if ( LLURLSimString::sInstance.mSimString.length() ) + { + // record the location to start at next time + gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); + }; + + LLAppViewer::sUpdaterInfo->mParams << "-url \"" << update_url.asString() << "\""; + + LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << " " << LLAppViewer::sUpdaterInfo->mParams.str() << LL_ENDL; + + //Explicitly remove the marker file, otherwise we pass the lock onto the child process and things get weird. + LLAppViewer::instance()->removeMarkerFile(); // In case updater fails + + // *NOTE:Mani The updater is spawned as the last thing before the WinMain exit. + // see LLAppViewerWin32.cpp + +#elif LL_DARWIN + // if a sim name was passed in via command line parameter (typically through a SLURL) + if ( LLURLSimString::sInstance.mSimString.length() ) + { + // record the location to start at next time + gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); + }; + + LLAppViewer::sUpdaterInfo->mUpdateExePath = "'"; + LLAppViewer::sUpdaterInfo->mUpdateExePath += gDirUtilp->getAppRODataDir(); + LLAppViewer::sUpdaterInfo->mUpdateExePath += "/mac-updater.app/Contents/MacOS/mac-updater' -url \""; + LLAppViewer::sUpdaterInfo->mUpdateExePath += update_url.asString(); + LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -name \""; + LLAppViewer::sUpdaterInfo->mUpdateExePath += LLAppViewer::instance()->getSecondLifeTitle(); + LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" &"; + + LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << LL_ENDL; + + // Run the auto-updater. + system(LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str()); /* Flawfinder: ignore */ + +#elif LL_LINUX || LL_SOLARIS + OSMessageBox("Automatic updating is not yet implemented for Linux.\n" + "Please download the latest version from www.secondlife.com.", + LLStringUtil::null, OSMB_OK); +#endif + + // *REMOVE:Mani - Saving for reference... + // LLAppViewer::instance()->forceQuit(); +} + diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 646b677264..f95d7cb412 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -47,6 +47,8 @@ class LLVFS; class LLWatchdogTimeout; class LLWorkerThread; +struct apr_dso_handle_t; + class LLAppViewer : public LLApp { @@ -124,7 +126,7 @@ public: virtual void forceErrorLLError(); virtual void forceErrorBreakpoint(); virtual void forceErrorBadMemoryAccess(); - virtual void forceErrorInifiniteLoop(); + virtual void forceErrorInfiniteLoop(); virtual void forceErrorSoftwareException(); virtual void forceErrorDriverCrash(); @@ -210,6 +212,8 @@ private: void sendLogoutRequest(); void disconnectViewer(); + void loadEventHostModule(S32 listen_port); + // *FIX: the app viewer class should be some sort of singleton, no? // Perhaps its child class is the singleton and this should be an abstract base. static LLAppViewer* sInstance; @@ -255,6 +259,8 @@ private: LLAllocator mAlloc; + std::set<struct apr_dso_handle_t*> mPlugins; + public: //some information for updater typedef struct @@ -263,6 +269,8 @@ public: std::ostringstream mParams; }LLUpdaterInfo ; static LLUpdaterInfo *sUpdaterInfo ; + + void launchUpdater(); }; // consts from viewer.h @@ -278,10 +286,6 @@ extern LLSD gDebugInfo; extern BOOL gAllowTapTapHoldRun; extern BOOL gShowObjectUpdates; -extern BOOL gAcceptTOS; -extern BOOL gAcceptCriticalMessage; - - typedef enum { LAST_EXEC_NORMAL = 0, diff --git a/indra/newview/llappviewerlistener.cpp b/indra/newview/llappviewerlistener.cpp new file mode 100644 index 0000000000..a3af251a3c --- /dev/null +++ b/indra/newview/llappviewerlistener.cpp @@ -0,0 +1,37 @@ +/** + * @file llappviewerlistener.cpp + * @author Nat Goodspeed + * @date 2009-06-23 + * @brief Implementation for llappviewerlistener. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "llviewerprecompiledheaders.h" +// associated header +#include "llappviewerlistener.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llappviewer.h" + +LLAppViewerListener::LLAppViewerListener(const std::string& pumpname, LLAppViewer* llappviewer): + LLDispatchListener(pumpname, "op"), + mAppViewer(llappviewer) +{ + // add() every method we want to be able to invoke via this event API. + add("requestQuit", &LLAppViewerListener::requestQuit); +} + +void LLAppViewerListener::requestQuit(const LLSD& event) +{ + if(mAppViewer == NULL) + { + mAppViewer = LLAppViewer::instance(); + } + mAppViewer->requestQuit(); +} diff --git a/indra/newview/llappviewerlistener.h b/indra/newview/llappviewerlistener.h new file mode 100644 index 0000000000..d702f605ef --- /dev/null +++ b/indra/newview/llappviewerlistener.h @@ -0,0 +1,34 @@ +/** + * @file llappviewerlistener.h + * @author Nat Goodspeed + * @date 2009-06-18 + * @brief Wrap subset of LLAppViewer API in event API + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLAPPVIEWERLISTENER_H) +#define LL_LLAPPVIEWERLISTENER_H + +#include "lleventdispatcher.h" + +class LLAppViewer; +class LLSD; + +/// Listen on an LLEventPump with specified name for LLAppViewer request events. +class LLAppViewerListener: public LLDispatchListener +{ +public: + /// Specify the pump name on which to listen, and bind the LLAppViewer + /// instance to use (e.g. LLAppViewer::instance()). + LLAppViewerListener(const std::string& pumpname, LLAppViewer* llappviewer); + +private: + void requestQuit(const LLSD& event); + + LLAppViewer* mAppViewer; +}; + +#endif /* ! defined(LL_LLAPPVIEWERLISTENER_H) */ diff --git a/indra/newview/llappviewermacosx.h b/indra/newview/llappviewermacosx.h index 0928916a57..bc841fc3a7 100644 --- a/indra/newview/llappviewermacosx.h +++ b/indra/newview/llappviewermacosx.h @@ -55,7 +55,6 @@ protected: virtual void handleSyncCrashTrace(); std::string generateSerialNumber(); - virtual bool initLogging(); virtual bool initParseCommandLine(LLCommandLineParser& clp); }; diff --git a/indra/newview/llcapabilitylistener.cpp b/indra/newview/llcapabilitylistener.cpp index 4134e9e0a4..785a647fa2 100644 --- a/indra/newview/llcapabilitylistener.cpp +++ b/indra/newview/llcapabilitylistener.cpp @@ -91,6 +91,7 @@ bool LLCapabilityListener::capListener(const LLSD& request) // 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 diff --git a/indra/newview/llclassifiedinfo.cpp b/indra/newview/llclassifiedinfo.cpp index 5cf1579d0e..5fcafbeca6 100644 --- a/indra/newview/llclassifiedinfo.cpp +++ b/indra/newview/llclassifiedinfo.cpp @@ -38,35 +38,19 @@ LLClassifiedInfo::cat_map LLClassifiedInfo::sCategories; // static -void LLClassifiedInfo::loadCategories(LLUserAuth::options_t classified_options) +void LLClassifiedInfo::loadCategories(const LLSD& options) { - LLUserAuth::options_t::iterator resp_it; - for (resp_it = classified_options.begin(); - resp_it != classified_options.end(); - ++resp_it) + for(LLSD::array_const_iterator resp_it = options.beginArray(), + end = options.endArray(); resp_it != end; ++resp_it) { - const LLUserAuth::response_t& response = *resp_it; - - LLUserAuth::response_t::const_iterator option_it; - - S32 cat_id = 0; - option_it = response.find("category_id"); - if (option_it != response.end()) + LLSD name = (*resp_it)["category_name"]; + if(name.isDefined()) { - cat_id = atoi(option_it->second.c_str()); + LLSD id = (*resp_it)["category_id"]; + if(id.isDefined()) + { + LLClassifiedInfo::sCategories[id.asInteger()] = name.asString(); + } } - else - { - continue; - } - - // Add the category id/name pair - option_it = response.find("category_name"); - if (option_it != response.end()) - { - LLClassifiedInfo::sCategories[cat_id] = option_it->second; - } - } - } diff --git a/indra/newview/llclassifiedinfo.h b/indra/newview/llclassifiedinfo.h index cc5a6bf28f..37134c7e5b 100644 --- a/indra/newview/llclassifiedinfo.h +++ b/indra/newview/llclassifiedinfo.h @@ -37,7 +37,6 @@ #include "v3dmath.h" #include "lluuid.h" -#include "lluserauth.h" class LLMessageSystem; @@ -46,7 +45,7 @@ class LLClassifiedInfo public: LLClassifiedInfo() {} - static void loadCategories(LLUserAuth::options_t event_options); + static void loadCategories(const LLSD& options); typedef std::map<U32, std::string> cat_map; static cat_map sCategories; diff --git a/indra/newview/lldrawpoolsky.h b/indra/newview/lldrawpoolsky.h index f35b114730..8595d73aee 100644 --- a/indra/newview/lldrawpoolsky.h +++ b/indra/newview/lldrawpoolsky.h @@ -36,14 +36,12 @@ #include "lldrawpool.h" class LLSkyTex; -class LLHeavenBody; class LLGLSLShader; class LLDrawPoolSky : public LLFacePool { private: LLSkyTex *mSkyTex; - LLHeavenBody *mHB[2]; // Sun and Moon LLGLSLShader *mShader; public: @@ -69,8 +67,6 @@ public: /*virtual*/ void renderForSelect(); /*virtual*/ void endRenderPass(S32 pass); void setSkyTex(LLSkyTex* const st) { mSkyTex = st; } - void setSun(LLHeavenBody* sun_flag) { mHB[0] = sun_flag; } - void setMoon(LLHeavenBody* moon) { mHB[1] = moon; } void renderSkyCubeFace(U8 side); void renderHeavenlyBody(U8 hb, LLFace* face); diff --git a/indra/newview/lleventinfo.cpp b/indra/newview/lleventinfo.cpp index d4175b6c84..9be45d18fb 100644 --- a/indra/newview/lleventinfo.cpp +++ b/indra/newview/lleventinfo.cpp @@ -87,35 +87,19 @@ void LLEventInfo::unpack(LLMessageSystem *msg) } // static -void LLEventInfo::loadCategories(LLUserAuth::options_t event_options) +void LLEventInfo::loadCategories(const LLSD& options) { - LLUserAuth::options_t::iterator resp_it; - for (resp_it = event_options.begin(); - resp_it != event_options.end(); - ++resp_it) + for(LLSD::array_const_iterator resp_it = options.beginArray(), + end = options.endArray(); resp_it != end; ++resp_it) { - const LLUserAuth::response_t& response = *resp_it; - - LLUserAuth::response_t::const_iterator option_it; - - S32 cat_id = 0; - option_it = response.find("category_id"); - if (option_it != response.end()) + LLSD name = (*resp_it)["category_name"]; + if(name.isDefined()) { - cat_id = atoi(option_it->second.c_str()); + LLSD id = (*resp_it)["category_id"]; + if(id.isDefined()) + { + LLEventInfo::sCategories[id.asInteger()] = name.asString(); + } } - else - { - continue; - } - - // Add the category id/name pair - option_it = response.find("category_name"); - if (option_it != response.end()) - { - LLEventInfo::sCategories[cat_id] = option_it->second; - } - } - } diff --git a/indra/newview/lleventinfo.h b/indra/newview/lleventinfo.h index 880517a9f4..493c659983 100644 --- a/indra/newview/lleventinfo.h +++ b/indra/newview/lleventinfo.h @@ -37,7 +37,6 @@ #include "v3dmath.h" #include "lluuid.h" -#include "lluserauth.h" class LLMessageSystem; @@ -48,7 +47,7 @@ public: void unpack(LLMessageSystem *msg); - static void loadCategories(LLUserAuth::options_t event_options); + static void loadCategories(const LLSD& options); public: std::string mName; diff --git a/indra/newview/lleventnotifier.cpp b/indra/newview/lleventnotifier.cpp index 2c52cf9565..80d4d21166 100644 --- a/indra/newview/lleventnotifier.cpp +++ b/indra/newview/lleventnotifier.cpp @@ -95,18 +95,16 @@ void LLEventNotifier::update() } } -void LLEventNotifier::load(const LLUserAuth::options_t& event_options) +void LLEventNotifier::load(const LLSD& event_options) { - LLUserAuth::options_t::const_iterator resp_it; - for (resp_it = event_options.begin(); - resp_it != event_options.end(); - ++resp_it) + for(LLSD::array_const_iterator resp_it = event_options.beginArray(), + end = event_options.endArray(); resp_it != end; ++resp_it) { - const LLUserAuth::response_t& response = *resp_it; + LLSD response = *resp_it; LLEventNotification *new_enp = new LLEventNotification(); - if (!new_enp->load(response)) + if(!new_enp->load(response)) { delete new_enp; continue; @@ -210,49 +208,46 @@ bool LLEventNotification::handleResponse(const LLSD& notification, const LLSD& r return false; } -BOOL LLEventNotification::load(const LLUserAuth::response_t &response) +BOOL LLEventNotification::load(const LLSD& response) { - - LLUserAuth::response_t::const_iterator option_it; BOOL event_ok = TRUE; - option_it = response.find("event_id"); - if (option_it != response.end()) + LLSD option = response.get("event_id"); + if (option.isDefined()) { - mEventID = atoi(option_it->second.c_str()); + mEventID = option.asInteger(); } else { event_ok = FALSE; } - option_it = response.find("event_name"); - if (option_it != response.end()) + option = response.get("event_name"); + if (option.isDefined()) { - llinfos << "Event: " << option_it->second << llendl; - mEventName = option_it->second; + llinfos << "Event: " << option.asString() << llendl; + mEventName = option.asString(); } else { event_ok = FALSE; } - - option_it = response.find("event_date"); - if (option_it != response.end()) + option = response.get("event_date"); + if (option.isDefined()) { - llinfos << "EventDate: " << option_it->second << llendl; - mEventDateStr = option_it->second; + llinfos << "EventDate: " << option.asString() << llendl; + mEventDateStr = option.asString(); } else { event_ok = FALSE; } - option_it = response.find("event_date_ut"); - if (option_it != response.end()) + option = response.get("event_date_ut"); + if (option.isDefined()) { - llinfos << "EventDate: " << option_it->second << llendl; - mEventDate = strtoul(option_it->second.c_str(), NULL, 10); + llinfos << "EventDate: " << option.asString() << llendl; + mEventDate = strtoul(option.asString().c_str(), NULL, 10); } else { @@ -264,44 +259,44 @@ BOOL LLEventNotification::load(const LLUserAuth::response_t &response) S32 x_region = 0; S32 y_region = 0; - option_it = response.find("grid_x"); - if (option_it != response.end()) + option = response.get("grid_x"); + if (option.isDefined()) { - llinfos << "GridX: " << option_it->second << llendl; - grid_x= atoi(option_it->second.c_str()); + llinfos << "GridX: " << option.asInteger() << llendl; + grid_x= option.asInteger(); } else { event_ok = FALSE; } - option_it = response.find("grid_y"); - if (option_it != response.end()) + option = response.get("grid_y"); + if (option.isDefined()) { - llinfos << "GridY: " << option_it->second << llendl; - grid_y = atoi(option_it->second.c_str()); + llinfos << "GridY: " << option.asInteger() << llendl; + grid_y = option.asInteger(); } else { event_ok = FALSE; } - option_it = response.find("x_region"); - if (option_it != response.end()) + option = response.get("x_region"); + if (option.isDefined()) { - llinfos << "RegionX: " << option_it->second << llendl; - x_region = atoi(option_it->second.c_str()); + llinfos << "RegionX: " << option.asInteger() << llendl; + x_region = option.asInteger(); } else { event_ok = FALSE; } - option_it = response.find("y_region"); - if (option_it != response.end()) + option = response.get("y_region"); + if (option.isDefined()) { - llinfos << "RegionY: " << option_it->second << llendl; - y_region = atoi(option_it->second.c_str()); + llinfos << "RegionY: " << option.asInteger() << llendl; + y_region = option.asInteger(); } else { diff --git a/indra/newview/lleventnotifier.h b/indra/newview/lleventnotifier.h index feb734948c..6fdde87646 100644 --- a/indra/newview/lleventnotifier.h +++ b/indra/newview/lleventnotifier.h @@ -34,7 +34,6 @@ #define LL_LLEVENTNOTIFIER_H #include "llframetimer.h" -#include "lluserauth.h" #include "v3dmath.h" class LLEventInfo; @@ -49,7 +48,7 @@ public: void update(); // Notify the user of the event if it's coming up - void load(const LLUserAuth::options_t& event_options); // In the format that it comes in from LLUserAuth + void load(const LLSD& event_options); // In the format that it comes in from login void add(LLEventInfo &event_info); // Add a new notification for an event void remove(U32 event_id); @@ -69,7 +68,7 @@ public: LLEventNotification(); virtual ~LLEventNotification(); - BOOL load(const LLUserAuth::response_t &en); // In the format it comes in from LLUserAuth + BOOL load(const LLSD& en); // In the format it comes in from login BOOL load(const LLEventInfo &event_info); // From existing event_info on the viewer. //void setEventID(const U32 event_id); //void setEventName(std::string &event_name); diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp index 8ceb41c27a..b406300a38 100644 --- a/indra/newview/llfasttimerview.cpp +++ b/indra/newview/llfasttimerview.cpp @@ -989,8 +989,12 @@ LLSD LLFastTimerView::analyzePerformanceLogDefault(std::istream& is) std::string label = iter->first; F64 time = iter->second["Time"].asReal(); - - total_time += time; + + // Skip the total figure + if(label.compare("Total") != 0) + { + total_time += time; + } if (time > 0.0) { diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index 2a8365b3f0..fb724f30e0 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -44,6 +44,7 @@ #include "llgl.h" #include "llsecondlifeurls.h" +#include "llappviewer.h" #include "llviewercontrol.h" #include "llworld.h" #include "lldrawpoolterrain.h" @@ -58,11 +59,6 @@ #include "lldxhardware.h" #endif -// -// externs -// -extern LLMemoryInfo gSysMemory; -extern LLCPUInfo gSysCPU; #if LL_DARWIN const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt"; diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index 56c5eaa70e..30a5f9f36a 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -1,267 +1,266 @@ -/** - * @file llfloaterabout.cpp - * @author James Cook - * @brief The about box from Help->About - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llfloaterabout.h" - -#include "llsys.h" -#include "llgl.h" -#include "llui.h" // for tr() -#include "v3dmath.h" - -#include "llcurl.h" -#include "llimagej2c.h" -#include "llaudioengine.h" - -#include "llviewertexteditor.h" -#include "llviewercontrol.h" -#include "llagent.h" -#include "llviewerstats.h" -#include "llviewerregion.h" -#include "llversionviewer.h" -#include "llviewerbuild.h" -#include "lluictrlfactory.h" -#include "lluri.h" -#include "llweb.h" -#include "llsecondlifeurls.h" -#include "lltrans.h" -#include "llappviewer.h" -#include "llglheaders.h" -#include "llwindow.h" - -#include "llbutton.h" - -#if LL_WINDOWS -#include "lldxhardware.h" -#endif - -extern LLCPUInfo gSysCPU; -extern LLMemoryInfo gSysMemory; -extern U32 gPacketsIn; - -static std::string get_viewer_release_notes_url(); - - -///---------------------------------------------------------------------------- -/// Class LLFloaterAbout -///---------------------------------------------------------------------------- - -// Default constructor -LLFloaterAbout::LLFloaterAbout(const LLSD& key) -: LLFloater(key) -{ - //LLUICtrlFactory::getInstance()->buildFloater(this, "floater_about.xml"); - -} - -// Destroys the object -LLFloaterAbout::~LLFloaterAbout() -{ -} - -BOOL LLFloaterAbout::postBuild() -{ - center(); - LLViewerTextEditor *support_widget = - getChild<LLViewerTextEditor>("support_editor", true); - - LLViewerTextEditor *credits_widget = - getChild<LLViewerTextEditor>("credits_editor", true); - - // For some reason, adding style doesn't work unless this is true. - support_widget->setParseHTML(TRUE); - - // Text styles for release notes hyperlinks - LLStyle::Params link_style_params; - link_style_params.color.control = "HTMLLinkColor"; - link_style_params.link_href = get_viewer_release_notes_url(); - - // Version string - std::string version = LLTrans::getString("APP_NAME") - + llformat(" %d.%d.%d (%d) %s %s (%s)\n", - LL_VERSION_MAJOR, LL_VERSION_MINOR, LL_VERSION_PATCH, LL_VIEWER_BUILD, - __DATE__, __TIME__, - gSavedSettings.getString("VersionChannelName").c_str()); - support_widget->appendColoredText(version, FALSE, FALSE, LLUIColorTable::instance().getColor("TextFgReadOnlyColor")); - support_widget->appendStyledText(LLTrans::getString("ReleaseNotes"), false, false, link_style_params); - - std::string support; - support.append("\n\n"); - -#if LL_MSVC - support.append(llformat("Built with MSVC version %d\n\n", _MSC_VER)); -#endif - -#if LL_GNUC - support.append(llformat("Built with GCC version %d\n\n", GCC_VERSION)); -#endif - - // Position - LLViewerRegion* region = gAgent.getRegion(); - if (region) - { - LLStyle::Params server_link_style_params; - server_link_style_params.color.control = "HTMLLinkColor"; - server_link_style_params.link_href = region->getCapability("ServerReleaseNotes"); - - const LLVector3d &pos = gAgent.getPositionGlobal(); - LLUIString pos_text = getString("you_are_at"); - pos_text.setArg("[POSITION]", - llformat("%.1f, %.1f, %.1f ", pos.mdV[VX], pos.mdV[VY], pos.mdV[VZ])); - support.append(pos_text); - - LLUIString region_text = getString ("in_region") + " "; - region_text.setArg("[REGION]", llformat ("%s", gAgent.getRegion()->getName().c_str())); - support.append(region_text); - - std::string buffer; - buffer = gAgent.getRegion()->getHost().getHostName(); - support.append(buffer); - support.append(" ("); - buffer = gAgent.getRegion()->getHost().getString(); - support.append(buffer); - support.append(")\n"); - support.append(gLastVersionChannel); - support.append("\n"); - - support_widget->appendColoredText(support, FALSE, FALSE, LLUIColorTable::instance().getColor("TextFgReadOnlyColor")); - support_widget->appendStyledText(LLTrans::getString("ReleaseNotes"), false, false, server_link_style_params); - - support = "\n\n"; - } - - // *NOTE: Do not translate text like GPU, Graphics Card, etc - - // Most PC users that know what these mean will be used to the english versions, - // and this info sometimes gets sent to support - - // CPU - support.append(getString("CPU") + " "); - support.append( gSysCPU.getCPUString() ); - support.append("\n"); - - U32 memory = gSysMemory.getPhysicalMemoryKB() / 1024; - // Moved hack adjustment to Windows memory size into llsys.cpp - - LLStringUtil::format_map_t args; - args["[MEM]"] = llformat ("%u", memory); - support.append(getString("Memory", args) + "\n"); - - support.append(getString("OSVersion") + " "); - support.append( LLAppViewer::instance()->getOSInfo().getOSString() ); - support.append("\n"); - - support.append(getString("GraphicsCardVendor") + " "); - support.append( (const char*) glGetString(GL_VENDOR) ); - support.append("\n"); - - support.append(getString("GraphicsCard") + " "); - support.append( (const char*) glGetString(GL_RENDERER) ); - support.append("\n"); - -#if LL_WINDOWS - getWindow()->incBusyCount(); - getWindow()->setCursor(UI_CURSOR_ARROW); - support.append("Windows Graphics Driver Version: "); - LLSD driver_info = gDXHardware.getDisplayInfo(); - if (driver_info.has("DriverVersion")) - { - support.append(driver_info["DriverVersion"]); - } - support.append("\n"); - getWindow()->decBusyCount(); - getWindow()->setCursor(UI_CURSOR_ARROW); -#endif - - support.append(getString("OpenGLVersion") + " "); - support.append( (const char*) glGetString(GL_VERSION) ); - support.append("\n"); - - support.append("\n"); - - support.append(getString("LibCurlVersion") + " "); - support.append( LLCurl::getVersionString() ); - support.append("\n"); - - support.append(getString("J2CDecoderVersion") + " "); - support.append( LLImageJ2C::getEngineInfo() ); - support.append("\n"); - - support.append(getString("AudioDriverVersion") + " "); - bool want_fullname = true; - support.append( gAudiop ? gAudiop->getDriverName(want_fullname) : getString("none") ); - support.append("\n"); - - // TODO: Implement media plugin version query - - support.append(getString("LLQtWebkitVersion") + " "); - support.append("\n"); - - if (gPacketsIn > 0) - { - args["[LOST]"] = llformat ("%.0f", LLViewerStats::getInstance()->mPacketsLostStat.getCurrent()); - args["[IN]"] = llformat ("%.0f", F32(gPacketsIn)); - args["[PCT]"] = llformat ("%.1f", 100.f*LLViewerStats::getInstance()->mPacketsLostStat.getCurrent() / F32(gPacketsIn) ); - support.append(getString ("PacketsLost", args) + "\n"); - } - - support_widget->appendColoredText(support, FALSE, FALSE, LLUIColorTable::instance().getColor("TextFgReadOnlyColor")); - - // Fix views - support_widget->setCursorPos(0); - support_widget->setEnabled(FALSE); - - credits_widget->setCursorPos(0); - credits_widget->setEnabled(FALSE); - - return TRUE; -} - - - static std::string get_viewer_release_notes_url() - { - std::ostringstream version; - version << LL_VERSION_MAJOR << "." - << LL_VERSION_MINOR << "." - << LL_VERSION_PATCH << "." - << LL_VERSION_BUILD; - - LLSD query; - query["channel"] = gSavedSettings.getString("VersionChannelName"); - query["version"] = version.str(); - - std::ostringstream url; - url << LLTrans::getString("RELEASE_NOTES_BASE_URL") << LLURI::mapToQueryString(query); - - return url.str(); - } +/**
+ * @file llfloaterabout.cpp
+ * @author James Cook
+ * @brief The about box from Help->About
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterabout.h"
+
+#include "llsys.h"
+#include "llgl.h"
+#include "llui.h" // for tr()
+#include "v3dmath.h"
+
+#include "llcurl.h"
+#include "llimagej2c.h"
+#include "llaudioengine.h"
+
+#include "llviewertexteditor.h"
+#include "llviewercontrol.h"
+#include "llagent.h"
+#include "llviewerstats.h"
+#include "llviewerregion.h"
+#include "llversionviewer.h"
+#include "llviewerbuild.h"
+#include "lluictrlfactory.h"
+#include "lluri.h"
+#include "llweb.h"
+#include "llsecondlifeurls.h"
+#include "lltrans.h"
+#include "llappviewer.h"
+#include "llglheaders.h"
+#include "llwindow.h"
+
+#include "llbutton.h"
+
+#if LL_WINDOWS
+#include "lldxhardware.h"
+#endif
+
+extern LLMemoryInfo gSysMemory;
+extern U32 gPacketsIn;
+
+static std::string get_viewer_release_notes_url();
+
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterAbout
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLFloaterAbout::LLFloaterAbout(const LLSD& key)
+: LLFloater(key)
+{
+ //LLUICtrlFactory::getInstance()->buildFloater(this, "floater_about.xml");
+
+}
+
+// Destroys the object
+LLFloaterAbout::~LLFloaterAbout()
+{
+}
+
+BOOL LLFloaterAbout::postBuild()
+{
+ center();
+ LLViewerTextEditor *support_widget =
+ getChild<LLViewerTextEditor>("support_editor", true);
+
+ LLViewerTextEditor *credits_widget =
+ getChild<LLViewerTextEditor>("credits_editor", true);
+
+ // For some reason, adding style doesn't work unless this is true.
+ support_widget->setParseHTML(TRUE);
+
+ // Text styles for release notes hyperlinks
+ LLStyle::Params link_style_params;
+ link_style_params.color.control = "HTMLLinkColor";
+ link_style_params.link_href = get_viewer_release_notes_url();
+
+ // Version string
+ std::string version = LLTrans::getString("APP_NAME")
+ + llformat(" %d.%d.%d (%d) %s %s (%s)\n",
+ LL_VERSION_MAJOR, LL_VERSION_MINOR, LL_VERSION_PATCH, LL_VIEWER_BUILD,
+ __DATE__, __TIME__,
+ gSavedSettings.getString("VersionChannelName").c_str());
+ support_widget->appendColoredText(version, FALSE, FALSE, LLUIColorTable::instance().getColor("TextFgReadOnlyColor"));
+ support_widget->appendStyledText(LLTrans::getString("ReleaseNotes"), false, false, link_style_params);
+
+ std::string support;
+ support.append("\n\n");
+
+#if LL_MSVC
+ support.append(llformat("Built with MSVC version %d\n\n", _MSC_VER));
+#endif
+
+#if LL_GNUC
+ support.append(llformat("Built with GCC version %d\n\n", GCC_VERSION));
+#endif
+
+ // Position
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ LLStyle::Params server_link_style_params;
+ server_link_style_params.color.control = "HTMLLinkColor";
+ server_link_style_params.link_href = region->getCapability("ServerReleaseNotes");
+
+ const LLVector3d &pos = gAgent.getPositionGlobal();
+ LLUIString pos_text = getString("you_are_at");
+ pos_text.setArg("[POSITION]",
+ llformat("%.1f, %.1f, %.1f ", pos.mdV[VX], pos.mdV[VY], pos.mdV[VZ]));
+ support.append(pos_text);
+
+ LLUIString region_text = getString ("in_region") + " ";
+ region_text.setArg("[REGION]", llformat ("%s", gAgent.getRegion()->getName().c_str()));
+ support.append(region_text);
+
+ std::string buffer;
+ buffer = gAgent.getRegion()->getHost().getHostName();
+ support.append(buffer);
+ support.append(" (");
+ buffer = gAgent.getRegion()->getHost().getString();
+ support.append(buffer);
+ support.append(")\n");
+ support.append(gLastVersionChannel);
+ support.append("\n");
+
+ support_widget->appendColoredText(support, FALSE, FALSE, LLUIColorTable::instance().getColor("TextFgReadOnlyColor"));
+ support_widget->appendStyledText(LLTrans::getString("ReleaseNotes"), false, false, server_link_style_params);
+
+ support = "\n\n";
+ }
+
+ // *NOTE: Do not translate text like GPU, Graphics Card, etc -
+ // Most PC users that know what these mean will be used to the english versions,
+ // and this info sometimes gets sent to support
+
+ // CPU
+ support.append(getString("CPU") + " ");
+ support.append( gSysCPU.getCPUString() );
+ support.append("\n");
+
+ U32 memory = gSysMemory.getPhysicalMemoryKB() / 1024;
+ // Moved hack adjustment to Windows memory size into llsys.cpp
+
+ LLStringUtil::format_map_t args;
+ args["[MEM]"] = llformat ("%u", memory);
+ support.append(getString("Memory", args) + "\n");
+
+ support.append(getString("OSVersion") + " ");
+ support.append( LLAppViewer::instance()->getOSInfo().getOSString() );
+ support.append("\n");
+
+ support.append(getString("GraphicsCardVendor") + " ");
+ support.append( (const char*) glGetString(GL_VENDOR) );
+ support.append("\n");
+
+ support.append(getString("GraphicsCard") + " ");
+ support.append( (const char*) glGetString(GL_RENDERER) );
+ support.append("\n");
+
+#if LL_WINDOWS
+ getWindow()->incBusyCount();
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ support.append("Windows Graphics Driver Version: ");
+ LLSD driver_info = gDXHardware.getDisplayInfo();
+ if (driver_info.has("DriverVersion"))
+ {
+ support.append(driver_info["DriverVersion"]);
+ }
+ support.append("\n");
+ getWindow()->decBusyCount();
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+#endif
+
+ support.append(getString("OpenGLVersion") + " ");
+ support.append( (const char*) glGetString(GL_VERSION) );
+ support.append("\n");
+
+ support.append("\n");
+
+ support.append(getString("LibCurlVersion") + " ");
+ support.append( LLCurl::getVersionString() );
+ support.append("\n");
+
+ support.append(getString("J2CDecoderVersion") + " ");
+ support.append( LLImageJ2C::getEngineInfo() );
+ support.append("\n");
+
+ support.append(getString("AudioDriverVersion") + " ");
+ bool want_fullname = true;
+ support.append( gAudiop ? gAudiop->getDriverName(want_fullname) : getString("none") );
+ support.append("\n");
+
+ // TODO: Implement media plugin version query
+
+ support.append(getString("LLQtWebkitVersion") + " ");
+ support.append("\n");
+
+ if (gPacketsIn > 0)
+ {
+ args["[LOST]"] = llformat ("%.0f", LLViewerStats::getInstance()->mPacketsLostStat.getCurrent());
+ args["[IN]"] = llformat ("%.0f", F32(gPacketsIn));
+ args["[PCT]"] = llformat ("%.1f", 100.f*LLViewerStats::getInstance()->mPacketsLostStat.getCurrent() / F32(gPacketsIn) );
+ support.append(getString ("PacketsLost", args) + "\n");
+ }
+
+ support_widget->appendColoredText(support, FALSE, FALSE, LLUIColorTable::instance().getColor("TextFgReadOnlyColor"));
+
+ // Fix views
+ support_widget->setCursorPos(0);
+ support_widget->setEnabled(FALSE);
+
+ credits_widget->setCursorPos(0);
+ credits_widget->setEnabled(FALSE);
+
+ return TRUE;
+}
+
+
+ static std::string get_viewer_release_notes_url()
+ {
+ std::ostringstream version;
+ version << LL_VERSION_MAJOR << "."
+ << LL_VERSION_MINOR << "."
+ << LL_VERSION_PATCH << "."
+ << LL_VERSION_BUILD;
+
+ LLSD query;
+ query["channel"] = gSavedSettings.getString("VersionChannelName");
+ query["version"] = version.str();
+
+ std::ostringstream url;
+ url << LLTrans::getString("RELEASE_NOTES_BASE_URL") << LLURI::mapToQueryString(query);
+
+ return url.str();
+ }
diff --git a/indra/newview/llfloaterfriends.h b/indra/newview/llfloaterfriends.h index 9242f00c91..9c6660c0dc 100644 --- a/indra/newview/llfloaterfriends.h +++ b/indra/newview/llfloaterfriends.h @@ -74,6 +74,9 @@ public: virtual BOOL postBuild(); + // *HACK Made public to remove friends from LLAvatarIconCtrl context menu + static bool handleRemove(const LLSD& notification, const LLSD& response); + private: enum FRIENDS_COLUMN_ORDER diff --git a/indra/newview/llfloatertos.cpp b/indra/newview/llfloatertos.cpp index 1060a5e031..d9bc43f3b4 100644 --- a/indra/newview/llfloatertos.cpp +++ b/indra/newview/llfloatertos.cpp @@ -35,8 +35,6 @@ #include "llfloatertos.h" // viewer includes -#include "llappviewer.h" -#include "llstartup.h" #include "llviewerstats.h" #include "llviewerwindow.h" @@ -52,11 +50,12 @@ #include "message.h" -LLFloaterTOS::LLFloaterTOS(const LLSD& message) -: LLModalDialog( message, 100, 100 ), - mMessage(message.asString()), +LLFloaterTOS::LLFloaterTOS(const LLSD& data) +: LLModalDialog( data["message"].asString(), 100, 100 ), + mMessage(data["message"].asString()), mWebBrowserWindowId( 0 ), - mLoadCompleteCount( 0 ) + mLoadCompleteCount( 0 ), + mReplyPumpName(data["reply_pump"].asString()) { } @@ -193,25 +192,12 @@ void LLFloaterTOS::onContinue( void* userdata ) { LLFloaterTOS* self = (LLFloaterTOS*) userdata; llinfos << "User agrees with TOS." << llendl; - if (self->getInstanceName() == "message_tos") - { - gAcceptTOS = TRUE; - } - else - { - gAcceptCriticalMessage = TRUE; - } - // Testing TOS dialog - #if ! LL_RELEASE_FOR_DOWNLOAD - if ( LLStartUp::getStartupState() == STATE_LOGIN_WAIT ) + if(self->mReplyPumpName != "") { - LLStartUp::setStartupState( STATE_LOGIN_SHOW ); + LLEventPumps::instance().obtain(self->mReplyPumpName).post(LLSD(true)); } - else - #endif - LLStartUp::setStartupState( STATE_LOGIN_AUTH_INIT ); // Go back and finish authentication self->closeFloater(); // destroys this object } @@ -220,8 +206,12 @@ void LLFloaterTOS::onCancel( void* userdata ) { LLFloaterTOS* self = (LLFloaterTOS*) userdata; llinfos << "User disagrees with TOS." << llendl; - LLNotifications::instance().add("MustAgreeToLogIn", LLSD(), LLSD(), login_alert_done); - LLStartUp::setStartupState( STATE_LOGIN_SHOW ); + + if(self->mReplyPumpName != "") + { + LLEventPumps::instance().obtain(self->mReplyPumpName).post(LLSD(false)); + } + self->mLoadCompleteCount = 0; // reset counter for next time we come to TOS self->closeFloater(); // destroys this object } @@ -241,3 +231,4 @@ void LLFloaterTOS::handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent ev } } } + diff --git a/indra/newview/llfloatertos.h b/indra/newview/llfloatertos.h index 49f982aa80..1d573e8170 100644 --- a/indra/newview/llfloatertos.h +++ b/indra/newview/llfloatertos.h @@ -36,6 +36,7 @@ #include "llmodaldialog.h" #include "llassetstorage.h" #include "llmediactrl.h" +#include <boost/function.hpp> class LLButton; class LLRadioGroup; @@ -48,7 +49,7 @@ class LLFloaterTOS : public LLViewerMediaObserver { public: - LLFloaterTOS(const LLSD& message); + LLFloaterTOS(const LLSD& data); virtual ~LLFloaterTOS(); BOOL postBuild(); @@ -68,6 +69,7 @@ private: std::string mMessage; int mWebBrowserWindowId; int mLoadCompleteCount; + std::string mReplyPumpName; }; #endif // LL_LLFLOATERTOS_H diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 4cbf88f3da..0d773a7c54 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -40,7 +40,7 @@ #include "llerror.h" #include "llbutton.h" #include "llhttpclient.h" -#include "llsdutil.h" +#include "llsdutil_math.h" #include "llstring.h" #include "lluictrlfactory.h" diff --git a/indra/newview/llinventoryactions.h b/indra/newview/llinventoryactions.h new file mode 100644 index 0000000000..79247e3abb --- /dev/null +++ b/indra/newview/llinventoryactions.h @@ -0,0 +1,47 @@ +/** + * @file llinventoryactions.h + * @brief inventory callback functions + * class definition + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLINVENTORYACTIONS_H +#define LL_LLINVENTORYACTIONS_H + +#include "lluictrl.h" + +class LLPanelInventory; +class LLInventoryView; +class LLInventoryPanel; + +void init_object_inventory_panel_actions(LLPanelInventory *panel, LLUICtrl::CommitCallbackRegistry::Registrar& registrar); +void init_inventory_actions(LLInventoryView *floater, LLUICtrl::CommitCallbackRegistry::Registrar& registrar); +void init_inventory_panel_actions(LLInventoryPanel *panel, LLUICtrl::CommitCallbackRegistry::Registrar& registrar); + +#endif // LL_LLINVENTORYACTIONS_H diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index aadda3fbfd..ef2ba5c11b 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1982,63 +1982,56 @@ bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const } bool LLInventoryModel::loadSkeleton( - const LLInventoryModel::options_t& options, + const LLSD& options, const LLUUID& owner_id) { lldebugs << "importing inventory skeleton for " << owner_id << llendl; typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t; cat_set_t temp_cats; - - update_map_t child_counts; - - LLUUID id; - LLAssetType::EType preferred_type; bool rv = true; - for(options_t::const_iterator it = options.begin(); it < options.end(); ++it) - { - LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(owner_id); - response_t::const_iterator no_response = (*it).end(); - response_t::const_iterator skel; - skel = (*it).find("name"); - if(skel == no_response) goto clean_cat; - cat->rename(std::string((*skel).second)); - skel = (*it).find("folder_id"); - if(skel == no_response) goto clean_cat; - id.set((*skel).second); - // if an id is null, it locks the viewer. - if(id.isNull()) goto clean_cat; - cat->setUUID(id); - skel = (*it).find("parent_id"); - if(skel == no_response) goto clean_cat; - id.set((*skel).second); - cat->setParent(id); - skel = (*it).find("type_default"); - if(skel == no_response) - { - preferred_type = LLAssetType::AT_NONE; + + for(LLSD::array_const_iterator it = options.beginArray(), + end = options.endArray(); it != end; ++it) + { + LLSD name = (*it)["name"]; + LLSD folder_id = (*it)["folder_id"]; + LLSD parent_id = (*it)["parent_id"]; + LLSD version = (*it)["version"]; + if(name.isDefined() + && folder_id.isDefined() + && parent_id.isDefined() + && version.isDefined() + && folder_id.asUUID().notNull() // if an id is null, it locks the viewer. + ) + { + LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(owner_id); + cat->rename(name.asString()); + cat->setUUID(folder_id.asUUID()); + cat->setParent(parent_id.asUUID()); + + LLAssetType::EType preferred_type = LLAssetType::AT_NONE; + LLSD type_default = (*it)["type_default"]; + if(type_default.isDefined()) + { + preferred_type = (LLAssetType::EType)type_default.asInteger(); + } + cat->setPreferredType(preferred_type); + cat->setVersion(version.asInteger()); + temp_cats.insert(cat); } else { - S32 t = atoi((*skel).second.c_str()); - preferred_type = (LLAssetType::EType)t; + llwarns << "Unable to import near " << name.asString() << llendl; + rv = false; } - cat->setPreferredType(preferred_type); - skel = (*it).find("version"); - if(skel == no_response) goto clean_cat; - cat->setVersion(atoi((*skel).second.c_str())); - temp_cats.insert(cat); - continue; - clean_cat: - llwarns << "Unable to import near " << cat->getName() << llendl; - rv = false; - //delete cat; // automatic when cat is reasigned or destroyed } S32 cached_category_count = 0; S32 cached_item_count = 0; if(!temp_cats.empty()) { + update_map_t child_counts; cat_array_t categories; item_array_t items; cat_set_t invalid_categories; // Used to mark categories that weren't successfully loaded. @@ -2212,85 +2205,84 @@ bool LLInventoryModel::loadSkeleton( return rv; } -bool LLInventoryModel::loadMeat( - const LLInventoryModel::options_t& options, const LLUUID& owner_id) +bool LLInventoryModel::loadMeat(const LLSD& options, const LLUUID& owner_id) { llinfos << "importing inventory for " << owner_id << llendl; - LLPermissions default_perm; - default_perm.init(LLUUID::null, owner_id, LLUUID::null, LLUUID::null); - LLPointer<LLViewerInventoryItem> item; - LLUUID id; - LLAssetType::EType type; - LLInventoryType::EType inv_type; bool rv = true; - for(options_t::const_iterator it = options.begin(); it < options.end(); ++it) - { - item = new LLViewerInventoryItem; - response_t::const_iterator no_response = (*it).end(); - response_t::const_iterator meat; - meat = (*it).find("name"); - if(meat == no_response) goto clean_item; - item->rename(std::string((*meat).second)); - meat = (*it).find("item_id"); - if(meat == no_response) goto clean_item; - id.set((*meat).second); - item->setUUID(id); - meat = (*it).find("parent_id"); - if(meat == no_response) goto clean_item; - id.set((*meat).second); - item->setParent(id); - meat = (*it).find("type"); - if(meat == no_response) goto clean_item; - type = (LLAssetType::EType)atoi((*meat).second.c_str()); - item->setType(type); - meat = (*it).find("inv_type"); - if(meat != no_response) - { - inv_type = (LLInventoryType::EType)atoi((*meat).second.c_str()); - item->setInventoryType(inv_type); - } - meat = (*it).find("data_id"); - if(meat == no_response) goto clean_item; - id.set((*meat).second); - if(LLAssetType::AT_CALLINGCARD == type) - { - LLPermissions perm; - perm.init(id, owner_id, LLUUID::null, LLUUID::null); - item->setPermissions(perm); + for(LLSD::array_const_iterator it = options.beginArray(), + end = options.endArray(); it != end; ++it) + { + LLSD name = (*it)["name"]; + LLSD item_id = (*it)["item_id"]; + LLSD parent_id = (*it)["parent_id"]; + LLSD asset_type = (*it)["type"]; + LLSD data_id = (*it)["data_id"]; + if(name.isDefined() + && item_id.isDefined() + && parent_id.isDefined() + && asset_type.isDefined() + && data_id.isDefined()) + { + LLPointer<LLViewerInventoryItem> item = new LLViewerInventoryItem; + item->rename(name.asString()); + item->setUUID(item_id.asUUID()); + item->setParent(parent_id.asUUID()); + LLAssetType::EType type = (LLAssetType::EType)asset_type.asInteger(); + item->setType(type); + + LLSD llsd_inv_type = (*it)["inv_type"]; + if(llsd_inv_type.isDefined()) + { + LLInventoryType::EType inv_type = (LLInventoryType::EType)llsd_inv_type.asInteger(); + item->setInventoryType(inv_type); + } + + if(LLAssetType::AT_CALLINGCARD == type) + { + LLPermissions perm; + perm.init(data_id.asUUID(), owner_id, LLUUID::null, LLUUID::null); + item->setPermissions(perm); + } + else + { + LLPermissions default_perm; + default_perm.init(LLUUID::null, owner_id, LLUUID::null, LLUUID::null); + LLSD llsd_perm_mask = (*it)["perm_mask"]; + if(llsd_perm_mask.isDefined()) + { + PermissionMask perm_mask = llsd_perm_mask.asInteger(); + default_perm.initMasks( + perm_mask, perm_mask, perm_mask, perm_mask, perm_mask); + } + else + { + default_perm.initMasks( + PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE); + } + item->setPermissions(default_perm); + item->setAssetUUID(data_id.asUUID()); + } + + LLSD flags = (*it)["flags"]; + if(flags.isDefined()) + { + // Not sure how well LLSD.asInteger() maps to + // unsigned long - using strtoul() + item->setFlags(strtoul(flags.asString().c_str(), NULL, 0)); + } + + LLSD time = (*it)["time"]; + if(time.isDefined()) + { + item->setCreationDate(time.asInteger()); + } + addItem(item); } else { - meat = (*it).find("perm_mask"); - if(meat != no_response) - { - PermissionMask perm_mask = atoi((*meat).second.c_str()); - default_perm.initMasks( - perm_mask, perm_mask, perm_mask, perm_mask, perm_mask); - } - else - { - default_perm.initMasks( - PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE); - } - item->setPermissions(default_perm); - item->setAssetUUID(id); - } - meat = (*it).find("flags"); - if(meat != no_response) - { - item->setFlags(strtoul((*meat).second.c_str(), NULL, 0)); - } - meat = (*it).find("time"); - if(meat != no_response) - { - item->setCreationDate(atoi((*meat).second.c_str())); + llwarns << "Unable to import near " << name.asString() << llendl; + rv = false; } - addItem(item); - continue; - clean_item: - llwarns << "Unable to import near " << item->getName() << llendl; - rv = false; - //delete item; // automatic when item is reassigned or destroyed } return rv; } diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 2ddc35b9ef..649a7e5b16 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -329,10 +329,8 @@ public: // methods to load up inventory skeleton & meat. These are used // during authentication. return true if everything parsed. - typedef std::map<std::string, std::string> response_t; - typedef std::vector<response_t> options_t; - bool loadSkeleton(const options_t& options, const LLUUID& owner_id); - bool loadMeat(const options_t& options, const LLUUID& owner_id); + bool loadSkeleton(const LLSD& options, const LLUUID& owner_id); + bool loadMeat(const LLSD& options, const LLUUID& owner_id); // This is a brute force method to rebuild the entire parent-child // relations. diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp new file mode 100644 index 0000000000..cb7dbc2de0 --- /dev/null +++ b/indra/newview/lllogininstance.cpp @@ -0,0 +1,468 @@ +/** + * @file lllogininstance.cpp + * @brief Viewer's host for a login connection. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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 "lllogininstance.h" + +// llcommon +#include "llevents.h" +#include "llmd5.h" +#include "stringize.h" + +// llmessage (!) +#include "llfiltersd2xmlrpc.h" // for xml_escape_string() + +// login +#include "lllogin.h" + +// newview +#include "llviewernetwork.h" +#include "llviewercontrol.h" +#include "llurlsimstring.h" +#include "llfloaterreg.h" +#include "llfloatertos.h" +#include "llwindow.h" +#if LL_LINUX || LL_SOLARIS +#include "lltrans.h" +#endif + +static const char * const TOS_REPLY_PUMP = "lllogininstance_tos_callback"; +static const char * const TOS_LISTENER_NAME = "lllogininstance_tos"; + +std::string construct_start_string(); + +LLLoginInstance::LLLoginInstance() : + mLoginModule(new LLLogin()), + mNotifications(NULL), + mLoginState("offline"), + mUserInteraction(true), + mSkipOptionalUpdate(false), + mAttemptComplete(false), + mTransferRate(0.0f) +{ + mLoginModule->getEventPump().listen("lllogininstance", + boost::bind(&LLLoginInstance::handleLoginEvent, this, _1)); +} + +LLLoginInstance::~LLLoginInstance() +{ +} + +void LLLoginInstance::connect(const LLSD& credentials) +{ + std::vector<std::string> uris; + LLViewerLogin::getInstance()->getLoginURIs(uris); + connect(uris.front(), credentials); +} + +void LLLoginInstance::connect(const std::string& uri, const LLSD& credentials) +{ + mAttemptComplete = false; // Reset attempt complete at this point! + constructAuthParams(credentials); + mLoginModule->connect(uri, mRequestData); +} + +void LLLoginInstance::reconnect() +{ + // Sort of like connect, only using the pre-existing + // request params. + std::vector<std::string> uris; + LLViewerLogin::getInstance()->getLoginURIs(uris); + mLoginModule->connect(uris.front(), mRequestData); +} + +void LLLoginInstance::disconnect() +{ + mAttemptComplete = false; // Reset attempt complete at this point! + mRequestData.clear(); + mLoginModule->disconnect(); +} + +LLSD LLLoginInstance::getResponse() +{ + return mResponseData; +} + +void LLLoginInstance::constructAuthParams(const LLSD& credentials) +{ + // Set up auth request options. +//#define LL_MINIMIAL_REQUESTED_OPTIONS + LLSD requested_options; + // *Note: this is where gUserAuth used to be created. + requested_options.append("inventory-root"); + requested_options.append("inventory-skeleton"); + //requested_options.append("inventory-meat"); + //requested_options.append("inventory-skel-targets"); +#if (!defined LL_MINIMIAL_REQUESTED_OPTIONS) + if(FALSE == gSavedSettings.getBOOL("NoInventoryLibrary")) + { + requested_options.append("inventory-lib-root"); + requested_options.append("inventory-lib-owner"); + requested_options.append("inventory-skel-lib"); + // requested_options.append("inventory-meat-lib"); + } + + requested_options.append("initial-outfit"); + requested_options.append("gestures"); + requested_options.append("event_categories"); + requested_options.append("event_notifications"); + requested_options.append("classified_categories"); + //requested_options.append("inventory-targets"); + requested_options.append("buddy-list"); + requested_options.append("ui-config"); +#endif + requested_options.append("tutorial_setting"); + requested_options.append("login-flags"); + requested_options.append("global-textures"); + if(gSavedSettings.getBOOL("ConnectAsGod")) + { + gSavedSettings.setBOOL("UseDebugMenus", TRUE); + requested_options.append("god-connect"); + } + + char hashed_mac_string[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */ + LLMD5 hashed_mac; + hashed_mac.update( gMACAddress, MAC_ADDRESS_BYTES ); + hashed_mac.finalize(); + hashed_mac.hex_digest(hashed_mac_string); + + // prepend "$1$" to the password to indicate its the md5'd version. + std::string dpasswd("$1$"); + dpasswd.append(credentials["passwd"].asString()); + + // (re)initialize the request params with creds. + LLSD request_params(credentials); + request_params["passwd"] = dpasswd; + request_params["start"] = construct_start_string(); + request_params["skipoptional"] = mSkipOptionalUpdate; + request_params["agree_to_tos"] = false; // Always false here. Set true in + request_params["read_critical"] = false; // handleTOSResponse + request_params["last_exec_event"] = mLastExecEvent; + request_params["mac"] = hashed_mac_string; + request_params["version"] = gCurrentVersion; // Includes channel name + request_params["channel"] = gSavedSettings.getString("VersionChannelName"); + request_params["id0"] = mSerialNumber; + + mRequestData.clear(); + mRequestData["method"] = "login_to_simulator"; + mRequestData["params"] = request_params; + mRequestData["options"] = requested_options; +} + +bool LLLoginInstance::handleLoginEvent(const LLSD& event) +{ + std::cout << "LoginListener called!: \n"; + std::cout << event << "\n"; + + if(!(event.has("state") && event.has("progress"))) + { + llerrs << "Unknown message from LLLogin!" << llendl; + } + + mLoginState = event["state"].asString(); + mResponseData = event["data"]; + + if(event.has("transfer_rate")) + { + mTransferRate = event["transfer_rate"].asReal(); + } + + if(mLoginState == "offline") + { + handleLoginFailure(event); + } + else if(mLoginState == "online") + { + handleLoginSuccess(event); + } + + return false; +} + +bool LLLoginInstance::handleLoginFailure(const LLSD& event) +{ + // Login has failed. + // Figure out why and respond... + LLSD response = event["data"]; + std::string reason_response = response["reason"].asString(); + std::string message_response = response["message"].asString(); + if(mUserInteraction) + { + // For the cases of critical message or TOS agreement, + // start the TOS dialog. The dialog response will be handled + // by the LLLoginInstance::handleTOSResponse() callback. + // The callback intiates the login attempt next step, either + // to reconnect or to end the attempt in failure. + if(reason_response == "tos") + { + LLSD data(LLSD::emptyMap()); + data["message"] = message_response; + data["reply_pump"] = TOS_REPLY_PUMP; + LLFloaterReg::showInstance("message_tos", data); + LLEventPumps::instance().obtain(TOS_REPLY_PUMP) + .listen(TOS_LISTENER_NAME, + boost::bind(&LLLoginInstance::handleTOSResponse, + this, _1, "agree_to_tos")); + } + else if(reason_response == "critical") + { + LLSD data(LLSD::emptyMap()); + data["message"] = message_response; + data["reply_pump"] = TOS_REPLY_PUMP; + LLFloaterReg::showInstance("message_critical", data); + LLEventPumps::instance().obtain(TOS_REPLY_PUMP) + .listen(TOS_LISTENER_NAME, + boost::bind(&LLLoginInstance::handleTOSResponse, + this, _1, "read_critical")); + } + else if(reason_response == "update" || gSavedSettings.getBOOL("ForceMandatoryUpdate")) + { + gSavedSettings.setBOOL("ForceMandatoryUpdate", FALSE); + updateApp(true, message_response); + } + else if(reason_response == "optional") + { + updateApp(false, message_response); + } + else + { + attemptComplete(); + } + } + else // no user interaction + { + attemptComplete(); + } + + return false; +} + +bool LLLoginInstance::handleLoginSuccess(const LLSD& event) +{ + if(gSavedSettings.getBOOL("ForceMandatoryUpdate")) + { + LLSD response = event["data"]; + std::string message_response = response["message"].asString(); + + // Testing update... + gSavedSettings.setBOOL("ForceMandatoryUpdate", FALSE); + + // Don't confuse startup by leaving login "online". + mLoginModule->disconnect(); + updateApp(true, message_response); + } + else + { + attemptComplete(); + } + return false; +} + +bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key) +{ + if(accepted) + { + // Set the request data to true and retry login. + mRequestData["params"][key] = true; + reconnect(); + } + else + { + attemptComplete(); + } + + LLEventPumps::instance().obtain(TOS_REPLY_PUMP).stopListening(TOS_LISTENER_NAME); + return true; +} + + +void LLLoginInstance::updateApp(bool mandatory, const std::string& auth_msg) +{ + // store off config state, as we might quit soon + gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE); + LLUIColorTable::instance().saveUserSettings(); + + std::ostringstream message; + std::string msg; + if (!auth_msg.empty()) + { + msg = "(" + auth_msg + ") \n"; + } + + LLSD args; + args["MESSAGE"] = msg; + + LLSD payload; + payload["mandatory"] = mandatory; + +/* + We're constructing one of the following 6 strings here: + "DownloadWindowsMandatory" + "DownloadWindowsReleaseForDownload" + "DownloadWindows" + "DownloadMacMandatory" + "DownloadMacReleaseForDownload" + "DownloadMac" + + I've called them out explicitly in this comment so that they can be grepped for. + + Also, we assume that if we're not Windows we're Mac. If we ever intend to support + Linux with autoupdate, this should be an explicit #elif LL_DARWIN, but + we'd rather deliver the wrong message than no message, so until Linux is supported + we'll leave it alone. + */ + std::string notification_name = "Download"; + +#if LL_WINDOWS + notification_name += "Windows"; +#else + notification_name += "Mac"; +#endif + + if (mandatory) + { + notification_name += "Mandatory"; + } + else + { +#if LL_RELEASE_FOR_DOWNLOAD + notification_name += "ReleaseForDownload"; +#endif + } + + if(mNotifications) + { + mNotifications->add(notification_name, args, payload, + boost::bind(&LLLoginInstance::updateDialogCallback, this, _1, _2)); + } + + /* *NOTE:Mani Experiment with Event API interface. + if(!mUpdateAppResponse) + { + bool make_unique = true; + mUpdateAppResponse.reset(new LLEventStream("logininstance_updateapp", make_unique)); + mUpdateAppResponse->listen("diaupdateDialogCallback", + boost::bind(&LLLoginInstance::updateDialogCallback, + this, _1 + ) + ); + } + + LLSD event; + event["op"] = "requestAdd"; + event["name"] = notification_name; + event["substitutions"] = args; + event["payload"] = payload; + event["reply"] = mUpdateAppResponse->getName(); + + LLEventPumps::getInstance()->obtain("LLNotifications").post(event); + */ +} + +bool LLLoginInstance::updateDialogCallback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + std::string update_exe_path; + bool mandatory = notification["payload"]["mandatory"].asBoolean(); + +#if !LL_RELEASE_FOR_DOWNLOAD + if (option == 2) + { + // This condition attempts to skip the + // update if using a dev build. + // The relog probably won't work if the + // update is mandatory. :) + + // *REMOVE:Mani - Saving for reference... + //LLStartUp::setStartupState( STATE_LOGIN_AUTH_INIT ); + mSkipOptionalUpdate = true; + reconnect(); + return false; + } +#endif + + if (option == 1) + { + // ...user doesn't want to do it + if (mandatory) + { + // Mandatory update, user chose to not to update... + // The login attemp is complete, startup should + // quit when detecting this. + attemptComplete(); + + // *REMOVE:Mani - Saving for reference... + //LLAppViewer::instance()->forceQuit(); + // // Bump them back to the login screen. + // //reset_login(); + } + else + { + // Optional update, user chose to skip + mSkipOptionalUpdate = true; + reconnect(); + } + return false; + } + + if(mUpdaterLauncher) + { + mUpdaterLauncher(); + } + + attemptComplete(); + + return false; +} + +std::string construct_start_string() +{ + std::string start; + if (LLURLSimString::parse()) + { + // a startup URL was specified + std::string unescaped_start = + STRINGIZE( "uri:" + << LLURLSimString::sInstance.mSimName << "&" + << LLURLSimString::sInstance.mX << "&" + << LLURLSimString::sInstance.mY << "&" + << LLURLSimString::sInstance.mZ); + start = xml_escape_string(unescaped_start); + } + else + { + start = gSavedSettings.getString("LoginLocation"); + } + return start; +} + diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h new file mode 100644 index 0000000000..6a2ccf919e --- /dev/null +++ b/indra/newview/lllogininstance.h @@ -0,0 +1,111 @@ +/** + * @file lllogininstance.h + * @brief A host for the viewer's login connection. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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_LLLOGININSTANCE_H +#define LL_LLLOGININSTANCE_H + +#include <boost/scoped_ptr.hpp> +#include <boost/function.hpp> +class LLLogin; +class LLEventStream; +class LLNotificationsInterface; + +// This class hosts the login module and is used to +// negotiate user authentication attempts. +class LLLoginInstance : public LLSingleton<LLLoginInstance> +{ +public: + LLLoginInstance(); + ~LLLoginInstance(); + + void connect(const LLSD& credential); // Connect to the current grid choice. + void connect(const std::string& uri, const LLSD& credential); // Connect to the given uri. + void reconnect(); // reconnect using the current credentials. + void disconnect(); + + bool authFailure() { return mAttemptComplete && mLoginState == "offline"; } + bool authSuccess() { return mAttemptComplete && mLoginState == "online"; } + + const std::string& getLoginState() { return mLoginState; } + LLSD getResponse(const std::string& key) { return getResponse()[key]; } + LLSD getResponse(); + + // Only valid when authSuccess == true. + const F64 getLastTransferRateBPS() { return mTransferRate; } + + // Set whether this class will drive user interaction. + // If not, login failures like 'need tos agreement' will + // end the login attempt. + void setUserInteraction(bool state) { mUserInteraction = state; } + bool getUserInteraction() { return mUserInteraction; } + + // Whether to tell login to skip optional update request. + // False by default. + void setSkipOptionalUpdate(bool state) { mSkipOptionalUpdate = state; } + void setSerialNumber(const std::string& sn) { mSerialNumber = sn; } + void setLastExecEvent(int lee) { mLastExecEvent = lee; } + + void setNotificationsInterface(LLNotificationsInterface* ni) { mNotifications = ni; } + + typedef boost::function<void()> UpdaterLauncherCallback; + void setUpdaterLauncher(const UpdaterLauncherCallback& ulc) { mUpdaterLauncher = ulc; } + +private: + void constructAuthParams(const LLSD& credentials); + void updateApp(bool mandatory, const std::string& message); + bool updateDialogCallback(const LLSD& notification, const LLSD& response); + + bool handleLoginEvent(const LLSD& event); + bool handleLoginFailure(const LLSD& event); + bool handleLoginSuccess(const LLSD& event); + + bool handleTOSResponse(bool v, const std::string& key); + + void attemptComplete() { mAttemptComplete = true; } // In the future an event? + + boost::scoped_ptr<LLLogin> mLoginModule; + LLNotificationsInterface* mNotifications; + + std::string mLoginState; + LLSD mRequestData; + LLSD mResponseData; + bool mUserInteraction; + bool mSkipOptionalUpdate; + bool mAttemptComplete; + F64 mTransferRate; + std::string mSerialNumber; + int mLastExecEvent; + UpdaterLauncherCallback mUpdaterLauncher; + boost::scoped_ptr<LLEventStream> mUpdateAppResponse; +}; + +#endif diff --git a/indra/newview/llmenucommands.cpp b/indra/newview/llmenucommands.cpp index efa15e05da..2433b6fac6 100644 --- a/indra/newview/llmenucommands.cpp +++ b/indra/newview/llmenucommands.cpp @@ -68,7 +68,6 @@ #include "llworldmap.h" #include "llfocusmgr.h" #include "llnearbychatbar.h" - void handle_pay_by_id(const LLUUID& agent_id) { const BOOL is_group = FALSE; diff --git a/indra/newview/llpanelplace.cpp b/indra/newview/llpanelplace.cpp index c6840721a3..61e18195b8 100644 --- a/indra/newview/llpanelplace.cpp +++ b/indra/newview/llpanelplace.cpp @@ -58,6 +58,7 @@ //#include "llviewermenu.h" // create_landmark() #include "llweb.h" #include "llsdutil.h" +#include "llsdutil_math.h" LLPanelPlace::LLPanelPlace() : LLPanel(), diff --git a/indra/newview/llpanelplaceinfo.cpp b/indra/newview/llpanelplaceinfo.cpp index a01977c9b5..3926ee0f07 100644 --- a/indra/newview/llpanelplaceinfo.cpp +++ b/indra/newview/llpanelplaceinfo.cpp @@ -59,6 +59,7 @@ #include "llviewerregion.h" #include "llviewertexteditor.h" #include "llworldmap.h" +#include "llsdutil_math.h" static LLRegisterPanelClassWrapper<LLPanelPlaceInfo> t_place_info("panel_place_info"); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 920ec0c65d..8859f1ffb5 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -56,7 +56,6 @@ #include "llcachename.h" #include "lldir.h" #include "llerrorcontrol.h" -#include "llfiltersd2xmlrpc.h" #include "llfloaterreg.h" #include "llfocusmgr.h" #include "llhttpsender.h" @@ -66,10 +65,11 @@ #include "llmemorystream.h" #include "llmessageconfig.h" #include "llmoveview.h" +#include "llteleporthistory.h" #include "llregionhandle.h" #include "llsd.h" #include "llsdserialize.h" -#include "llsdutil.h" +#include "llsdutil_math.h" #include "llsecondlifeurls.h" #include "llstring.h" #include "lluserrelations.h" @@ -101,7 +101,6 @@ #include "llfloaterland.h" #include "llfloaterpreference.h" #include "llfloatertopobjects.h" -#include "llfloatertos.h" #include "llfloaterworldmap.h" #include "llgesturemgr.h" #include "llgroupmgr.h" @@ -114,6 +113,7 @@ #include "llfloaterinventory.h" #include "llkeyboard.h" #include "llloginhandler.h" // gLoginHandler, SLURL support +#include "lllogininstance.h" // Host the login module. #include "llpanellogin.h" #include "llmutelist.h" #include "llnotify.h" @@ -133,7 +133,6 @@ #include "llsecondlifeurls.h" #include "llselectmgr.h" #include "llsky.h" -#include "llsrv.h" #include "llstatview.h" #include "lltrans.h" #include "llstatusbar.h" // sendMoneyBalanceRequest(), owns L$ balance @@ -146,7 +145,6 @@ #include "llurlsimstring.h" #include "llurlhistory.h" #include "llurlwhitelist.h" -#include "lluserauth.h" #include "llvieweraudio.h" #include "llviewerassetstorage.h" #include "llviewercamera.h" @@ -188,6 +186,9 @@ #include "llwearable.h" #include "llinventorybridge.h" +#include "lllogin.h" +#include "llevents.h" + #if LL_WINDOWS #include "llwindebug.h" #include "lldxhardware.h" @@ -197,12 +198,12 @@ // exported globals // bool gAgentMovementCompleted = false; -std::string gInitialOutfit; -std::string gInitialOutfitGender; std::string SCREEN_HOME_FILENAME = "screen_home.bmp"; std::string SCREEN_LAST_FILENAME = "screen_last.bmp"; +LLPointer<LLViewerTexture> gStartTexture; + // // Imported globals // @@ -212,12 +213,6 @@ extern S32 gStartImageHeight; // // local globals // - -LLPointer<LLViewerTexture> gStartTexture; - -static LLHost gAgentSimHost; -static BOOL gSkipOptionalUpdate = FALSE; - static bool gGotUseCircuitCodeAck = false; static std::string sInitialOutfit; static std::string sInitialOutfitGender; // "male" or "female" @@ -226,6 +221,18 @@ static bool gUseCircuitCallbackCalled = false; EStartupState LLStartUp::gStartupState = STATE_FIRST; +// *NOTE:Mani - to reconcile with giab changes... +static std::string gFirstname; +static std::string gLastname; +static std::string gPassword; + +static U64 gFirstSimHandle = 0; +static LLHost gFirstSim; +static std::string gFirstSimSeedCap; +static LLVector3 gAgentStartLookAt(1.0f, 0.f, 0.f); +static std::string gAgentStartLocation = "safe"; + +static LLEventStream sStartupStateWatcher("StartupState"); // // local function declaration @@ -238,8 +245,6 @@ void show_first_run_dialog(); bool first_run_dialog_callback(const LLSD& notification, const LLSD& response); void set_startup_status(const F32 frac, const std::string& string, const std::string& msg); bool login_alert_status(const LLSD& notification, const LLSD& response); -void update_app(BOOL mandatory, const std::string& message); -bool update_dialog_callback(const LLSD& notification, const LLSD& response); void login_packet_failed(void**, S32 result); void use_circuit_callback(void**, S32 result); void register_viewer_callbacks(LLMessageSystem* msg); @@ -249,6 +254,7 @@ void init_start_screen(S32 location_id); void release_start_screen(); void reset_login(); void apply_udp_blacklist(const std::string& csv); +bool process_login_success_response(); void callback_cache_name(const LLUUID& id, const std::string& firstname, const std::string& lastname, BOOL is_group) { @@ -308,9 +314,6 @@ void update_texture_fetch() gTextureList.updateImages(0.10f); } -static std::vector<std::string> sAuthUris; -static S32 sAuthUriNum = -1; - //Copies landmarks from the "Library" to "My Favorites" void populate_favorites_bar() { @@ -382,23 +385,11 @@ bool idle_startup() // auth/transform loop will do. static F32 progress = 0.10f; - static std::string auth_method; static std::string auth_desc; static std::string auth_message; - static std::string firstname; - static std::string lastname; - static LLUUID web_login_key; - static std::string password; - static std::vector<const char*> requested_options; - - static U64 first_sim_handle = 0; - static LLHost first_sim; - static std::string first_sim_seed_cap; static LLVector3 initial_sun_direction(1.f, 0.f, 0.f); static LLVector3 agent_start_position_region(10.f, 10.f, 10.f); // default for when no space server - static LLVector3 agent_start_look_at(1.0f, 0.f, 0.f); - static std::string agent_start_location = "safe"; // last location by default static S32 agent_location_id = START_LOCATION_ID_LAST; @@ -406,7 +397,7 @@ bool idle_startup() static bool show_connect_box = true; - static bool stipend_since_login = false; + //static bool stipend_since_login = false; // HACK: These are things from the main loop that usually aren't done // until initialization is complete, but need to be done here for things @@ -427,12 +418,7 @@ bool idle_startup() LLStringUtil::setLocale (LLTrans::getString(system)); - if (gNoRender) - { - // HACK, skip optional updates if you're running drones - gSkipOptionalUpdate = TRUE; - } - else + if (!gNoRender) { // Update images? gTextureList.updateImages(0.01f); @@ -748,24 +734,23 @@ bool idle_startup() || !gLoginHandler.getWebLoginKey().isNull() ) { // We have at least some login information on a SLURL - firstname = gLoginHandler.getFirstName(); - lastname = gLoginHandler.getLastName(); - web_login_key = gLoginHandler.getWebLoginKey(); + gFirstname = gLoginHandler.getFirstName(); + gLastname = gLoginHandler.getLastName(); // Show the login screen if we don't have everything show_connect_box = - firstname.empty() || lastname.empty() || web_login_key.isNull(); + gFirstname.empty() || gLastname.empty(); } else if(gSavedSettings.getLLSD("UserLoginInfo").size() == 3) { LLSD cmd_line_login = gSavedSettings.getLLSD("UserLoginInfo"); - firstname = cmd_line_login[0].asString(); - lastname = cmd_line_login[1].asString(); + gFirstname = cmd_line_login[0].asString(); + gLastname = cmd_line_login[1].asString(); LLMD5 pass((unsigned char*)cmd_line_login[2].asString().c_str()); char md5pass[33]; /* Flawfinder: ignore */ pass.hex_digest(md5pass); - password = md5pass; + gPassword = md5pass; #ifdef USE_VIEWER_AUTH show_connect_box = true; @@ -776,9 +761,9 @@ bool idle_startup() } else if (gSavedSettings.getBOOL("AutoLogin")) { - firstname = gSavedSettings.getString("FirstName"); - lastname = gSavedSettings.getString("LastName"); - password = LLStartUp::loadPasswordFromDisk(); + gFirstname = gSavedSettings.getString("FirstName"); + gLastname = gSavedSettings.getString("LastName"); + gPassword = LLStartUp::loadPasswordFromDisk(); gSavedSettings.setBOOL("RememberPassword", TRUE); #ifdef USE_VIEWER_AUTH @@ -791,9 +776,9 @@ bool idle_startup() { // if not automatically logging in, display login dialog // a valid grid is selected - firstname = gSavedSettings.getString("FirstName"); - lastname = gSavedSettings.getString("LastName"); - password = LLStartUp::loadPasswordFromDisk(); + gFirstname = gSavedSettings.getString("FirstName"); + gLastname = gSavedSettings.getString("LastName"); + gPassword = LLStartUp::loadPasswordFromDisk(); show_connect_box = true; } @@ -829,7 +814,7 @@ bool idle_startup() // Load all the name information out of the login view // NOTE: Hits "Attempted getFields with no login view shown" warning, since we don't // show the login view until login_show() is called below. - // LLPanelLogin::getFields(firstname, lastname, password); + // LLPanelLogin::getFields(gFirstname, gLastname, gPassword); if (gNoRender) { @@ -841,7 +826,7 @@ bool idle_startup() // Show the login dialog login_show(); // connect dialog is already shown, so fill in the names - LLPanelLogin::setFields( firstname, lastname, password); + LLPanelLogin::setFields( gFirstname, gLastname, gPassword); LLPanelLogin::giveFocus(); @@ -898,37 +883,29 @@ bool idle_startup() if (STATE_LOGIN_CLEANUP == LLStartUp::getStartupState()) { - //reset the values that could have come in from a slurl - if (!gLoginHandler.getWebLoginKey().isNull()) - { - firstname = gLoginHandler.getFirstName(); - lastname = gLoginHandler.getLastName(); - web_login_key = gLoginHandler.getWebLoginKey(); - } - if (show_connect_box) { // TODO if not use viewer auth // Load all the name information out of the login view - LLPanelLogin::getFields(&firstname, &lastname, &password); + LLPanelLogin::getFields(&gFirstname, &gLastname, &gPassword); // end TODO // HACK: Try to make not jump on login gKeyboard->resetKeys(); } - if (!firstname.empty() && !lastname.empty()) + if (!gFirstname.empty() && !gLastname.empty()) { - gSavedSettings.setString("FirstName", firstname); - gSavedSettings.setString("LastName", lastname); + gSavedSettings.setString("FirstName", gFirstname); + gSavedSettings.setString("LastName", gLastname); - LL_INFOS("AppInit") << "Attempting login as: " << firstname << " " << lastname << LL_ENDL; - gDebugInfo["LoginName"] = firstname + " " + lastname; + LL_INFOS("AppInit") << "Attempting login as: " << gFirstname << " " << gLastname << LL_ENDL; + gDebugInfo["LoginName"] = gFirstname + " " + gLastname; } // create necessary directories // *FIX: these mkdir's should error check - gDirUtilp->setLindenUserDir(firstname, lastname); + gDirUtilp->setLindenUserDir(gFirstname, gLastname); LLFile::mkdir(gDirUtilp->getLindenUserDir()); // Set PerAccountSettingsFile to the default value. @@ -962,7 +939,7 @@ bool idle_startup() gDirUtilp->setChatLogsDir(gSavedPerAccountSettings.getString("InstantMessageLogPath")); } - gDirUtilp->setPerAccountChatLogsDir(firstname, lastname); + gDirUtilp->setPerAccountChatLogsDir(gFirstname, gLastname); LLFile::mkdir(gDirUtilp->getChatLogsDir()); LLFile::mkdir(gDirUtilp->getPerAccountChatLogsDir()); @@ -983,13 +960,6 @@ bool idle_startup() if (show_connect_box) { - if ( LLPanelLogin::isGridComboDirty() ) - { - // User picked a grid from the popup, so clear the - // stored uris and they will be reacquired from the grid choice. - sAuthUris.clear(); - } - std::string location; LLPanelLogin::getLocation( location ); LLURLSimString::setString( location ); @@ -1018,7 +988,7 @@ bool idle_startup() agent_location_id = START_LOCATION_ID_URL; // doesn't really matter what location_which is, since - // agent_start_look_at will be overwritten when the + // gAgentStartLookAt will be overwritten when the // UserLoginLocationReply arrives location_which = START_LOCATION_ID_LAST; } @@ -1051,590 +1021,124 @@ bool idle_startup() gVFS->pokeFiles(); - // skipping over STATE_UPDATE_CHECK because that just waits for input LLStartUp::setStartupState( STATE_LOGIN_AUTH_INIT ); return FALSE; } - if (STATE_UPDATE_CHECK == LLStartUp::getStartupState()) - { - // wait for user to give input via dialog box - return FALSE; - } - if(STATE_LOGIN_AUTH_INIT == LLStartUp::getStartupState()) { -//#define LL_MINIMIAL_REQUESTED_OPTIONS gDebugInfo["GridName"] = LLViewerLogin::getInstance()->getGridLabel(); - // *Note: this is where gUserAuth used to be created. - requested_options.clear(); - requested_options.push_back("inventory-root"); - requested_options.push_back("inventory-skeleton"); - //requested_options.push_back("inventory-meat"); - //requested_options.push_back("inventory-skel-targets"); -#if (!defined LL_MINIMIAL_REQUESTED_OPTIONS) - if(FALSE == gSavedSettings.getBOOL("NoInventoryLibrary")) - { - requested_options.push_back("inventory-lib-root"); - requested_options.push_back("inventory-lib-owner"); - requested_options.push_back("inventory-skel-lib"); - // requested_options.push_back("inventory-meat-lib"); - } - - requested_options.push_back("initial-outfit"); - requested_options.push_back("gestures"); - requested_options.push_back("event_categories"); - requested_options.push_back("event_notifications"); - requested_options.push_back("classified_categories"); + // Update progress status and the display loop. requested_options.push_back("adult_compliant"); - //requested_options.push_back("inventory-targets"); - requested_options.push_back("buddy-list"); - requested_options.push_back("ui-config"); -#endif - requested_options.push_back("tutorial_setting"); - requested_options.push_back("login-flags"); - requested_options.push_back("global-textures"); - if(gSavedSettings.getBOOL("ConnectAsGod")) - { - gSavedSettings.setBOOL("UseDebugMenus", TRUE); - requested_options.push_back("god-connect"); - } - std::vector<std::string> uris; - LLViewerLogin::getInstance()->getLoginURIs(uris); - std::vector<std::string>::const_iterator iter, end; - for (iter = uris.begin(), end = uris.end(); iter != end; ++iter) - { - std::vector<std::string> rewritten; - rewritten = LLSRV::rewriteURI(*iter); - sAuthUris.insert(sAuthUris.end(), - rewritten.begin(), rewritten.end()); - } - sAuthUriNum = 0; - auth_method = "login_to_simulator"; - auth_desc = LLTrans::getString("LoginInProgress"); - LLStartUp::setStartupState( STATE_LOGIN_AUTHENTICATE ); - } - - if (STATE_LOGIN_AUTHENTICATE == LLStartUp::getStartupState()) - { - LL_DEBUGS("AppInit") << "STATE_LOGIN_AUTHENTICATE" << LL_ENDL; set_startup_status(progress, auth_desc, auth_message); progress += 0.02f; display_startup(); - - std::stringstream start; - if (LLURLSimString::parse()) - { - // a startup URL was specified - std::stringstream unescaped_start; - unescaped_start << "uri:" - << LLURLSimString::sInstance.mSimName << "&" - << LLURLSimString::sInstance.mX << "&" - << LLURLSimString::sInstance.mY << "&" - << LLURLSimString::sInstance.mZ; - start << xml_escape_string(unescaped_start.str()); - - } - else - { - start << gSavedSettings.getString("LoginLocation"); - } - - char hashed_mac_string[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */ - LLMD5 hashed_mac; - hashed_mac.update( gMACAddress, MAC_ADDRESS_BYTES ); - hashed_mac.finalize(); - hashed_mac.hex_digest(hashed_mac_string); - - // TODO if statement here to use web_login_key - sAuthUriNum = llclamp(sAuthUriNum, 0, (S32)sAuthUris.size()-1); - LLUserAuth::getInstance()->authenticate( - sAuthUris[sAuthUriNum], - auth_method, - firstname, - lastname, - password, // web_login_key, - start.str(), - gSkipOptionalUpdate, - gAcceptTOS, - gAcceptCriticalMessage, - gLastExecEvent, - requested_options, - hashed_mac_string, - LLAppViewer::instance()->getSerialNumber()); - - // reset globals - gAcceptTOS = FALSE; - gAcceptCriticalMessage = FALSE; - LLStartUp::setStartupState( STATE_LOGIN_NO_DATA_YET ); - return FALSE; - } - if(STATE_LOGIN_NO_DATA_YET == LLStartUp::getStartupState()) - { - LL_DEBUGS("AppInit") << "STATE_LOGIN_NO_DATA_YET" << LL_ENDL; - // If we get here we have gotten past the potential stall - // in curl, so take "may appear frozen" out of progress bar. JC - auth_desc = LLTrans::getString("LoginInProgressNoFrozen"); - set_startup_status(progress, auth_desc, auth_message); - // Process messages to keep from dropping circuit. - LLMessageSystem* msg = gMessageSystem; - while (msg->checkAllMessages(gFrameCount, gServicePump)) - { - } - msg->processAcks(); - LLUserAuth::UserAuthcode error = LLUserAuth::getInstance()->authResponse(); - if(LLUserAuth::E_NO_RESPONSE_YET == error) + // Setting initial values... + LLLoginInstance* login = LLLoginInstance::getInstance(); + login->setNotificationsInterface(LLNotifications::getInstance()); + if(gNoRender) { - LL_DEBUGS("AppInit") << "waiting..." << LL_ENDL; - return FALSE; + // HACK, skip optional updates if you're running drones + login->setSkipOptionalUpdate(true); } - LLStartUp::setStartupState( STATE_LOGIN_DOWNLOADING ); - progress += 0.01f; - set_startup_status(progress, auth_desc, auth_message); - return FALSE; - } - if(STATE_LOGIN_DOWNLOADING == LLStartUp::getStartupState()) - { - LL_DEBUGS("AppInit") << "STATE_LOGIN_DOWNLOADING" << LL_ENDL; - // Process messages to keep from dropping circuit. - LLMessageSystem* msg = gMessageSystem; - while (msg->checkAllMessages(gFrameCount, gServicePump)) - { - } - msg->processAcks(); - LLUserAuth::UserAuthcode error = LLUserAuth::getInstance()->authResponse(); - if(LLUserAuth::E_DOWNLOADING == error) - { - LL_DEBUGS("AppInit") << "downloading..." << LL_ENDL; - return FALSE; - } + login->setUserInteraction(show_connect_box); + login->setSerialNumber(LLAppViewer::instance()->getSerialNumber()); + login->setLastExecEvent(gLastExecEvent); + login->setUpdaterLauncher(boost::bind(&LLAppViewer::launchUpdater, LLAppViewer::instance())); + + // This call to LLLoginInstance::connect() starts the + // authentication process. + LLSD credentials; + credentials["first"] = gFirstname; + credentials["last"] = gLastname; + credentials["passwd"] = gPassword; + login->connect(credentials); + LLStartUp::setStartupState( STATE_LOGIN_PROCESS_RESPONSE ); - progress += 0.01f; - set_startup_status(progress, LLTrans::getString("LoginProcessingResponse"), auth_message); return FALSE; } - if(STATE_LOGIN_PROCESS_RESPONSE == LLStartUp::getStartupState()) + if(STATE_LOGIN_PROCESS_RESPONSE == LLStartUp::getStartupState()) { - LL_DEBUGS("AppInit") << "STATE_LOGIN_PROCESS_RESPONSE" << LL_ENDL; + bool transitionBackToLoginPanel = false; std::ostringstream emsg; - bool quit = false; - bool update = false; - std::string login_response; - std::string reason_response; - std::string message_response; - bool successful_login = false; - LLUserAuth::UserAuthcode error = LLUserAuth::getInstance()->authResponse(); - // reset globals - gAcceptTOS = FALSE; - gAcceptCriticalMessage = FALSE; - switch(error) - { - case LLUserAuth::E_OK: - login_response = LLUserAuth::getInstance()->getResponse("login"); - if(login_response == "true") - { - // Yay, login! - successful_login = true; - } - else if(login_response == "indeterminate") + if(LLLoginInstance::getInstance()->authFailure()) + { + // Still have error conditions that may need some + // sort of handling. + emsg << "Login failed.\n"; + std::string reason_response = LLLoginInstance::getInstance()->getResponse("reason"); + std::string message_response = LLLoginInstance::getInstance()->getResponse("message"); + + if(!message_response.empty()) { - LL_INFOS("AppInit") << "Indeterminate login..." << LL_ENDL; - sAuthUris = LLSRV::rewriteURI(LLUserAuth::getInstance()->getResponse("next_url")); - sAuthUriNum = 0; - auth_method = LLUserAuth::getInstance()->getResponse("next_method"); - auth_message = LLUserAuth::getInstance()->getResponse("message"); - if(auth_method.substr(0, 5) == "login") + // XUI: fix translation for strings returned during login + // We need a generic table for translations + std::string big_reason = LLAgent::sTeleportErrorMessages[ message_response ]; + if ( big_reason.size() == 0 ) { - auth_desc.assign(LLTrans::getString("LoginAuthenticating")); + emsg << message_response; } else { - auth_desc.assign(LLTrans::getString("LoginMaintenance")); - } - // ignoring the duration & options array for now. - // Go back to authenticate. - LLStartUp::setStartupState( STATE_LOGIN_AUTHENTICATE ); - return FALSE; - } - else - { - emsg << "Login failed.\n"; - reason_response = LLUserAuth::getInstance()->getResponse("reason"); - message_response = LLUserAuth::getInstance()->getResponse("message"); - - if (!message_response.empty()) - { - // XUI: fix translation for strings returned during login - // We need a generic table for translations - std::string big_reason = LLAgent::sTeleportErrorMessages[ message_response ]; - if ( big_reason.size() == 0 ) - { - emsg << message_response; - } - else - { - emsg << big_reason; - } - } - - if(reason_response == "tos") - { - if (show_connect_box) - { - LL_DEBUGS("AppInit") << "Need tos agreement" << LL_ENDL; - LLStartUp::setStartupState( STATE_UPDATE_CHECK ); - LLFloaterReg::showInstance("message_tos", LLSD(message_response)); - // LLFloaterTOS deletes itself. - return false; - } - else - { - quit = true; - } - } - if(reason_response == "critical") - { - if (show_connect_box) - { - LL_DEBUGS("AppInit") << "Need critical message" << LL_ENDL; - LLStartUp::setStartupState( STATE_UPDATE_CHECK ); - LLFloaterReg::showInstance("message_critical", LLSD(message_response)); - // LLFloaterTOS deletes itself. - return false; - } - else - { - quit = true; - } - } - if(reason_response == "key") - { - // Couldn't login because user/password is wrong - // Clear the password - password = ""; - } - if(reason_response == "update") - { - auth_message = LLUserAuth::getInstance()->getResponse("message"); - update = true; - } - if(reason_response == "optional") - { - LL_DEBUGS("AppInit") << "Login got optional update" << LL_ENDL; - auth_message = LLUserAuth::getInstance()->getResponse("message"); - if (show_connect_box) - { - update_app(FALSE, auth_message); - LLStartUp::setStartupState( STATE_UPDATE_CHECK ); - gSkipOptionalUpdate = TRUE; - return false; - } + emsg << big_reason; } } - break; - case LLUserAuth::E_COULDNT_RESOLVE_HOST: - case LLUserAuth::E_SSL_PEER_CERTIFICATE: - case LLUserAuth::E_UNHANDLED_ERROR: - case LLUserAuth::E_SSL_CACERT: - case LLUserAuth::E_SSL_CONNECT_ERROR: - default: - if (sAuthUriNum >= (int) sAuthUris.size() - 1) - { - emsg << "Unable to connect to " << LLAppViewer::instance()->getSecondLifeTitle() << ".\n"; - emsg << LLUserAuth::getInstance()->errorMessage(); - } else { - sAuthUriNum++; - std::ostringstream s; - LLStringUtil::format_map_t args; - args["[NUMBER]"] = llformat("%d", sAuthUriNum + 1); - auth_desc = LLTrans::getString("LoginAttempt", args); - LLStartUp::setStartupState( STATE_LOGIN_AUTHENTICATE ); - return FALSE; - } - break; - } - - if (update || gSavedSettings.getBOOL("ForceMandatoryUpdate")) - { - gSavedSettings.setBOOL("ForceMandatoryUpdate", FALSE); - update_app(TRUE, auth_message); - LLStartUp::setStartupState( STATE_UPDATE_CHECK ); - return false; - } - // Version update and we're not showing the dialog - if(quit) - { - LLUserAuth::getInstance()->reset(); - LLAppViewer::instance()->forceQuit(); - return false; - } - - if(successful_login) - { - std::string text; - text = LLUserAuth::getInstance()->getResponse("udp_blacklist"); - if(!text.empty()) + if(reason_response == "key") { - apply_udp_blacklist(text); + // Couldn't login because user/password is wrong + // Clear the password + gPassword = ""; } - // unpack login data needed by the application - text = LLUserAuth::getInstance()->getResponse("agent_id"); - if(!text.empty()) gAgentID.set(text); - gDebugInfo["AgentID"] = text; - - text = LLUserAuth::getInstance()->getResponse("session_id"); - if(!text.empty()) gAgentSessionID.set(text); - gDebugInfo["SessionID"] = text; - - text = LLUserAuth::getInstance()->getResponse("secure_session_id"); - if(!text.empty()) gAgent.mSecureSessionID.set(text); - - text = LLUserAuth::getInstance()->getResponse("first_name"); - if(!text.empty()) + if(reason_response == "update" + || reason_response == "optional") { - // Remove quotes from string. Login.cgi sends these to force - // names that look like numbers into strings. - firstname.assign(text); - LLStringUtil::replaceChar(firstname, '"', ' '); - LLStringUtil::trim(firstname); - } - text = LLUserAuth::getInstance()->getResponse("last_name"); - if(!text.empty()) lastname.assign(text); - gSavedSettings.setString("FirstName", firstname); - gSavedSettings.setString("LastName", lastname); - - if (gSavedSettings.getBOOL("RememberPassword")) - { - // Successful login means the password is valid, so save it. - LLStartUp::savePasswordToDisk(password); + // In the case of a needed update, quit. + // Its either downloading or declined. + // If optional was skipped this case shouldn't + // be reached. + LLLoginInstance::getInstance()->disconnect(); + LLAppViewer::instance()->forceQuit(); } else { - // Don't leave password from previous session sitting around - // during this login session. - LLStartUp::deletePasswordFromDisk(); - } - - // this is their actual ability to access content - text = LLUserAuth::getInstance()->getResponse("agent_access_max"); - if (!text.empty()) - { - // agent_access can be 'A', 'M', and 'PG'. - gAgent.setMaturity(text[0]); - } - - // this is the value of their preference setting for that content - // which will always be <= agent_access_max - text = LLUserAuth::getInstance()->getResponse("agent_region_access"); - if (!text.empty()) - { - int preferredMaturity = LLAgent::convertTextToMaturity(text[0]); - gSavedSettings.setU32("PreferredMaturity", preferredMaturity); - } - // During the AO transition, this flag will be true. Then the flag will - // go away. After the AO transition, this code and all the code that - // uses it can be deleted. - text = LLUserAuth::getInstance()->getResponse("ao_transition"); - if (!text.empty()) - { - if (text == "1") - { - gAgent.setAOTransition(); - } - } - - text = LLUserAuth::getInstance()->getResponse("start_location"); - if(!text.empty()) agent_start_location.assign(text); - text = LLUserAuth::getInstance()->getResponse("circuit_code"); - if(!text.empty()) - { - gMessageSystem->mOurCircuitCode = strtoul(text.c_str(), NULL, 10); - } - std::string sim_ip_str = LLUserAuth::getInstance()->getResponse("sim_ip"); - std::string sim_port_str = LLUserAuth::getInstance()->getResponse("sim_port"); - if(!sim_ip_str.empty() && !sim_port_str.empty()) - { - U32 sim_port = strtoul(sim_port_str.c_str(), NULL, 10); - first_sim.set(sim_ip_str, sim_port); - if (first_sim.isOk()) - { - gMessageSystem->enableCircuit(first_sim, TRUE); - } - } - std::string region_x_str = LLUserAuth::getInstance()->getResponse("region_x"); - std::string region_y_str = LLUserAuth::getInstance()->getResponse("region_y"); - if(!region_x_str.empty() && !region_y_str.empty()) - { - U32 region_x = strtoul(region_x_str.c_str(), NULL, 10); - U32 region_y = strtoul(region_y_str.c_str(), NULL, 10); - first_sim_handle = to_region_handle(region_x, region_y); - } - - const std::string look_at_str = LLUserAuth::getInstance()->getResponse("look_at"); - if (!look_at_str.empty()) - { - size_t len = look_at_str.size(); - LLMemoryStream mstr((U8*)look_at_str.c_str(), len); - LLSD sd = LLSDSerialize::fromNotation(mstr, len); - agent_start_look_at = ll_vector3_from_sd(sd); - } - - text = LLUserAuth::getInstance()->getResponse("seed_capability"); - if (!text.empty()) first_sim_seed_cap = text; - - text = LLUserAuth::getInstance()->getResponse("seconds_since_epoch"); - if(!text.empty()) - { - U32 server_utc_time = strtoul(text.c_str(), NULL, 10); - if(server_utc_time) - { - time_t now = time(NULL); - gUTCOffset = (server_utc_time - now); - } - } - - std::string home_location = LLUserAuth::getInstance()->getResponse("home"); - if(!home_location.empty()) - { - size_t len = home_location.size(); - LLMemoryStream mstr((U8*)home_location.c_str(), len); - LLSD sd = LLSDSerialize::fromNotation(mstr, len); - S32 region_x = sd["region_handle"][0].asInteger(); - S32 region_y = sd["region_handle"][1].asInteger(); - U64 region_handle = to_region_handle(region_x, region_y); - LLVector3 position = ll_vector3_from_sd(sd["position"]); - gAgent.setHomePosRegion(region_handle, position); - } - - gAgent.mMOTD.assign(LLUserAuth::getInstance()->getResponse("message")); - LLUserAuth::options_t options; - if(LLUserAuth::getInstance()->getOptions("inventory-root", options)) - { - LLUserAuth::response_t::iterator it; - it = options[0].find("folder_id"); - if(it != options[0].end()) - { - gInventory.setRootFolderID( LLUUID( (*it).second ) ); - } - } - - options.clear(); - if(LLUserAuth::getInstance()->getOptions("login-flags", options)) - { - LLUserAuth::response_t::iterator it; - LLUserAuth::response_t::iterator no_flag = options[0].end(); - it = options[0].find("ever_logged_in"); - if(it != no_flag) - { - if((*it).second == "N") gAgent.setFirstLogin(TRUE); - else gAgent.setFirstLogin(FALSE); - } - it = options[0].find("stipend_since_login"); - if(it != no_flag) - { - if((*it).second == "Y") stipend_since_login = true; - } - it = options[0].find("gendered"); - if(it != no_flag) - { - if((*it).second == "Y") gAgent.setGenderChosen(TRUE); - } - it = options[0].find("daylight_savings"); - if(it != no_flag) - { - if((*it).second == "Y") gPacificDaylightTime = TRUE; - else gPacificDaylightTime = FALSE; - } + transitionBackToLoginPanel = true; //setup map of datetime strings to codes and slt & local time offset from utc LLStringOps::setupDatetimeInfo (gPacificDaylightTime); } - options.clear(); - if (LLUserAuth::getInstance()->getOptions("initial-outfit", options) - && !options.empty()) - { - LLUserAuth::response_t::iterator it; - LLUserAuth::response_t::iterator it_end = options[0].end(); - it = options[0].find("folder_name"); - if(it != it_end) - { - // Initial outfit is a folder in your inventory, - // must be an exact folder-name match. - sInitialOutfit = (*it).second; - } - it = options[0].find("gender"); - if (it != it_end) - { - sInitialOutfitGender = (*it).second; - } - } - - options.clear(); - if(LLUserAuth::getInstance()->getOptions("global-textures", options)) - { - // Extract sun and moon texture IDs. These are used - // in the LLVOSky constructor, but I can't figure out - // how to pass them in. JC - LLUserAuth::response_t::iterator it; - LLUserAuth::response_t::iterator no_texture = options[0].end(); - it = options[0].find("sun_texture_id"); - if(it != no_texture) - { - gSunTextureID.set((*it).second); - } - it = options[0].find("moon_texture_id"); - if(it != no_texture) - { - gMoonTextureID.set((*it).second); - } - it = options[0].find("cloud_texture_id"); - if(it != no_texture) - { - gCloudTextureID.set((*it).second); - } - } - - - // JC: gesture loading done below, when we have an asset system - // in place. Don't delete/clear user_credentials until then. - - if(gAgentID.notNull() - && gAgentSessionID.notNull() - && gMessageSystem->mOurCircuitCode - && first_sim.isOk() - && gInventory.getRootFolderID().notNull()) + } + else if(LLLoginInstance::getInstance()->authSuccess()) + { + if(process_login_success_response()) { - LLStartUp::setStartupState( STATE_WORLD_INIT ); + // Pass the user information to the voice chat server interface. + gVoiceClient->userAuthorized(gFirstname, gLastname, gAgentID); + LLStartUp::setStartupState( STATE_WORLD_INIT); } else { - if (gNoRender) - { - LL_WARNS("AppInit") << "Bad login - missing return values" << LL_ENDL; - LL_WARNS("AppInit") << emsg << LL_ENDL; - exit(0); - } - // Bounce back to the login screen. - LLSD args; - args["ERROR_MESSAGE"] = emsg.str(); - LLNotifications::instance().add("ErrorMessage", args, LLSD(), login_alert_done); - reset_login(); - gSavedSettings.setBOOL("AutoLogin", FALSE); - show_connect_box = true; + transitionBackToLoginPanel = false; } - - // Pass the user information to the voice chat server interface. - gVoiceClient->userAuthorized(firstname, lastname, gAgentID); } - else // if(successful_login) + else + { + // Still waiting for response. + // *TODO:Mani - Actually check for login progress. + // If we get here we have gotten past the potential stall + // in curl, so take "may appear frozen" out of progress bar. JC + auth_desc = LLTrans::getString("LoginInProgressNoFrozen"); + set_startup_status(progress, auth_desc, auth_message); + } + + if(transitionBackToLoginPanel) { if (gNoRender) { @@ -1642,11 +1146,12 @@ bool idle_startup() LL_WARNS("AppInit") << emsg << LL_ENDL; exit(0); } + // Bounce back to the login screen. LLSD args; args["ERROR_MESSAGE"] = emsg.str(); LLNotifications::instance().add("ErrorMessage", args, LLSD(), login_alert_done); - reset_login(); + reset_login(); // calls LLStartUp::setStartupState( STATE_LOGIN_SHOW ); gSavedSettings.setBOOL("AutoLogin", FALSE); show_connect_box = true; } @@ -1709,14 +1214,14 @@ bool idle_startup() // This is necessary because creating objects before this is set will result in a // bad mPositionAgent cache. - gAgent.initOriginGlobal(from_region_handle(first_sim_handle)); + gAgent.initOriginGlobal(from_region_handle(gFirstSimHandle)); - LLWorld::getInstance()->addRegion(first_sim_handle, first_sim); + LLWorld::getInstance()->addRegion(gFirstSimHandle, gFirstSim); - LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(first_sim_handle); + LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(gFirstSimHandle); LL_INFOS("AppInit") << "Adding initial simulator " << regionp->getOriginGlobal() << LL_ENDL; - regionp->setSeedCapability(first_sim_seed_cap); + regionp->setSeedCapability(gFirstSimSeedCap); LL_DEBUGS("AppInit") << "Waiting for seed grant ...." << LL_ENDL; // Set agent's initial region to be the one we just created. @@ -1855,7 +1360,7 @@ bool idle_startup() // the coordinates handed to us to fit in the local region. gAgent.setPositionAgent(agent_start_position_region); - gAgent.resetAxes(agent_start_look_at); + gAgent.resetAxes(gAgentStartLookAt); gAgent.stopCameraAnimation(); gAgent.resetCamera(); @@ -1894,18 +1399,18 @@ bool idle_startup() LL_WARNS("AppInit") << "Attempting to connect to simulator with a zero circuit code!" << LL_ENDL; } - gUseCircuitCallbackCalled = FALSE; + gUseCircuitCallbackCalled = false; - msg->enableCircuit(first_sim, TRUE); + msg->enableCircuit(gFirstSim, TRUE); // now, use the circuit info to tell simulator about us! - LL_INFOS("AppInit") << "viewer: UserLoginLocationReply() Enabling " << first_sim << " with code " << msg->mOurCircuitCode << LL_ENDL; + LL_INFOS("AppInit") << "viewer: UserLoginLocationReply() Enabling " << gFirstSim << " with code " << msg->mOurCircuitCode << LL_ENDL; msg->newMessageFast(_PREHASH_UseCircuitCode); msg->nextBlockFast(_PREHASH_CircuitCode); msg->addU32Fast(_PREHASH_Code, msg->mOurCircuitCode); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->addUUIDFast(_PREHASH_ID, gAgent.getID()); msg->sendReliable( - first_sim, + gFirstSim, MAX_TIMEOUT_COUNT, FALSE, TIMEOUT_SECONDS, @@ -2016,105 +1521,99 @@ bool idle_startup() LLAgentLanguage::update(); // unpack thin inventory - LLUserAuth::options_t options; - options.clear(); + LLSD response = LLLoginInstance::getInstance()->getResponse(); //bool dump_buffer = false; - - if(LLUserAuth::getInstance()->getOptions("inventory-lib-root", options) - && !options.empty()) + + LLSD inv_lib_root = response["inventory-lib-root"]; + if(inv_lib_root.isDefined()) { // should only be one - LLUserAuth::response_t::iterator it; - it = options[0].find("folder_id"); - if(it != options[0].end()) + LLSD id = inv_lib_root[0]["folder_id"]; + if(id.isDefined()) { - gInventory.setLibraryRootFolderID( LLUUID( (*it).second ) ); + gInventory.setLibraryRootFolderID(id.asUUID()); } } - options.clear(); - if(LLUserAuth::getInstance()->getOptions("inventory-lib-owner", options) - && !options.empty()) + + LLSD inv_lib_owner = response["inventory-lib-owner"]; + if(inv_lib_owner.isDefined()) { // should only be one - LLUserAuth::response_t::iterator it; - it = options[0].find("agent_id"); - if(it != options[0].end()) + LLSD id = inv_lib_owner[0]["agent_id"]; + if(id.isDefined()) { - gInventory.setLibraryOwnerID( LLUUID( (*it).second ) ); + gInventory.setLibraryOwnerID( LLUUID(id.asUUID())); } } - options.clear(); - if(LLUserAuth::getInstance()->getOptions("inventory-skel-lib", options) - && gInventory.getLibraryOwnerID().notNull()) + + LLSD inv_skel_lib = response["inventory-skel-lib"]; + if(inv_skel_lib.isDefined() && gInventory.getLibraryOwnerID().notNull()) { - if(!gInventory.loadSkeleton(options, gInventory.getLibraryOwnerID())) + if(!gInventory.loadSkeleton(inv_skel_lib, gInventory.getLibraryOwnerID())) { LL_WARNS("AppInit") << "Problem loading inventory-skel-lib" << LL_ENDL; } } - options.clear(); - if(LLUserAuth::getInstance()->getOptions("inventory-skeleton", options)) + + LLSD inv_skeleton = response["inventory-skeleton"]; + if(inv_skeleton.isDefined()) { - if(!gInventory.loadSkeleton(options, gAgent.getID())) + if(!gInventory.loadSkeleton(inv_skeleton, gAgent.getID())) { LL_WARNS("AppInit") << "Problem loading inventory-skel-targets" << LL_ENDL; } } - options.clear(); - if(LLUserAuth::getInstance()->getOptions("buddy-list", options)) + LLSD buddy_list = response["buddy-list"]; + if(buddy_list.isDefined()) { - LLUserAuth::options_t::iterator it = options.begin(); - LLUserAuth::options_t::iterator end = options.end(); LLAvatarTracker::buddy_map_t list; LLUUID agent_id; S32 has_rights = 0, given_rights = 0; - for (; it != end; ++it) + for(LLSD::array_const_iterator it = buddy_list.beginArray(), + end = buddy_list.endArray(); it != end; ++it) { - LLUserAuth::response_t::const_iterator option_it; - option_it = (*it).find("buddy_id"); - if(option_it != (*it).end()) + LLSD buddy_id = (*it)["buddy_id"]; + if(buddy_id.isDefined()) { - agent_id.set((*option_it).second); + agent_id = buddy_id.asUUID(); } - option_it = (*it).find("buddy_rights_has"); - if(option_it != (*it).end()) + + LLSD buddy_rights_has = (*it)["buddy_rights_has"]; + if(buddy_rights_has.isDefined()) { - has_rights = atoi((*option_it).second.c_str()); + has_rights = buddy_rights_has.asInteger(); } - option_it = (*it).find("buddy_rights_given"); - if(option_it != (*it).end()) + + LLSD buddy_rights_given = (*it)["buddy_rights_given"]; + if(buddy_rights_given.isDefined()) { - given_rights = atoi((*option_it).second.c_str()); + given_rights = buddy_rights_given.asInteger(); } + list[agent_id] = new LLRelationship(given_rights, has_rights, false); } LLAvatarTracker::instance().addBuddyList(list); } - options.clear(); - bool show_hud = false; - if(LLUserAuth::getInstance()->getOptions("tutorial_setting", options)) + LLSD tutorial_setting = response["tutorial_setting"]; + if(tutorial_setting.isDefined()) { - LLUserAuth::options_t::iterator it = options.begin(); - LLUserAuth::options_t::iterator end = options.end(); - for (; it != end; ++it) + for(LLSD::array_const_iterator it = tutorial_setting.beginArray(), + end = tutorial_setting.endArray(); it != end; ++it) { - LLUserAuth::response_t::const_iterator option_it; - option_it = (*it).find("tutorial_url"); - if(option_it != (*it).end()) + LLSD tutorial_url = (*it)["tutorial_url"]; + if(tutorial_url.isDefined()) { // Tutorial floater will append language code - gSavedSettings.setString("TutorialURL", option_it->second); + gSavedSettings.setString("TutorialURL", tutorial_url.asString()); } - option_it = (*it).find("use_tutorial"); - if(option_it != (*it).end()) + + LLSD use_tutorial = (*it)["use_tutorial"]; + if(use_tutorial.asString() == "true") { - if (option_it->second == "true") - { - show_hud = true; - } + show_hud = true; } } } @@ -2126,19 +1625,22 @@ bool idle_startup() LLFloaterReg::showInstance("hud", LLSD(), FALSE); } - options.clear(); - if(LLUserAuth::getInstance()->getOptions("event_categories", options)) + LLSD event_categories = response["event_categories"]; + if(event_categories.isDefined()) { - LLEventInfo::loadCategories(options); + LLEventInfo::loadCategories(event_categories); } - if(LLUserAuth::getInstance()->getOptions("event_notifications", options)) + + LLSD event_notifications = response["event_notifications"]; + if(event_notifications.isDefined()) { - gEventNotifier.load(options); + gEventNotifier.load(event_notifications); } - options.clear(); - if(LLUserAuth::getInstance()->getOptions("classified_categories", options)) + + LLSD classified_categories = response["classified_categories"]; + if(classified_categories.isDefined()) { - LLClassifiedInfo::loadCategories(options); + LLClassifiedInfo::loadCategories(classified_categories); } @@ -2201,7 +1703,7 @@ bool idle_startup() // This is actually a pessimistic computation, because TCP may not have enough // time to ramp up on the (small) default inventory file to truly measure max // bandwidth. JC - F64 rate_bps = LLUserAuth::getInstance()->getLastTransferRateBPS(); + F64 rate_bps = LLLoginInstance::getInstance()->getLastTransferRateBPS(); const F32 FAST_RATE_BPS = 600.f * 1024.f; const F32 FASTER_RATE_BPS = 750.f * 1024.f; F32 max_bandwidth = gViewerThrottle.getMaxBandwidth(); @@ -2249,34 +1751,20 @@ bool idle_startup() // JC: Initialize "active" gestures. This may also trigger // many gesture downloads, if this is the user's first // time on this machine or -purge has been run. - LLUserAuth::options_t gesture_options; - if (LLUserAuth::getInstance()->getOptions("gestures", gesture_options)) + LLSD gesture_options + = LLLoginInstance::getInstance()->getResponse("gestures"); + if (gesture_options.isDefined()) { LL_DEBUGS("AppInit") << "Gesture Manager loading " << gesture_options.size() << LL_ENDL; std::vector<LLUUID> item_ids; - LLUserAuth::options_t::iterator resp_it; - for (resp_it = gesture_options.begin(); - resp_it != gesture_options.end(); - ++resp_it) + for(LLSD::array_const_iterator resp_it = gesture_options.beginArray(), + end = gesture_options.endArray(); resp_it != end; ++resp_it) { - const LLUserAuth::response_t& response = *resp_it; - LLUUID item_id; - LLUUID asset_id; - LLUserAuth::response_t::const_iterator option_it; - - option_it = response.find("item_id"); - if (option_it != response.end()) - { - const std::string& uuid_string = (*option_it).second; - item_id.set(uuid_string); - } - option_it = response.find("asset_id"); - if (option_it != response.end()) - { - const std::string& uuid_string = (*option_it).second; - asset_id.set(uuid_string); - } + // If the id is not specifed in the LLSD, + // the LLSD operator[]() will return a null LLUUID. + LLUUID item_id = (*resp_it)["item_id"]; + LLUUID asset_id = (*resp_it)["asset_id"]; if (item_id.notNull() && asset_id.notNull()) { @@ -2332,8 +1820,8 @@ bool idle_startup() if (!gAgent.isFirstLogin()) { bool url_ok = LLURLSimString::sInstance.parse(); - if ((url_ok && agent_start_location == "url") || - (!url_ok && ((agent_start_location == gSavedSettings.getString("LoginLocation"))))) + if ((url_ok && gAgentStartLocation == "url") || + (!url_ok && ((gAgentStartLocation == gSavedSettings.getString("LoginLocation"))))) { // Start location is OK // Disabled code to restore camera location and focus if logging in to default location @@ -2546,8 +2034,10 @@ bool idle_startup() // then the data is cached for the viewer's lifetime) LLProductInfoRequestManager::instance(); + // *FIX:Mani - What do I do here? + // Need we really clear the Auth response data? // Clean up the userauth stuff. - LLUserAuth::getInstance()->reset(); + // LLUserAuth::getInstance()->reset(); LLStartUp::setStartupState( STATE_STARTED ); @@ -2837,230 +2327,6 @@ bool login_alert_status(const LLSD& notification, const LLSD& response) return false; } -void update_app(BOOL mandatory, const std::string& auth_msg) -{ - // store off config state, as we might quit soon - gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE); - LLUIColorTable::instance().saveUserSettings(); - std::ostringstream message; - - std::string msg; - if (!auth_msg.empty()) - { - msg = "("+ auth_msg + ") \n"; - } - - LLSD args; - args["MESSAGE"] = msg; - - LLSD payload; - payload["mandatory"] = mandatory; - -/* - We're constructing one of the following 6 strings here: - "DownloadWindowsMandatory" - "DownloadWindowsReleaseForDownload" - "DownloadWindows" - "DownloadMacMandatory" - "DownloadMacReleaseForDownload" - "DownloadMac" - - I've called them out explicitly in this comment so that they can be grepped for. - - Also, we assume that if we're not Windows we're Mac. If we ever intend to support - Linux with autoupdate, this should be an explicit #elif LL_DARWIN, but - we'd rather deliver the wrong message than no message, so until Linux is supported - we'll leave it alone. - */ - std::string notification_name = "Download"; - -#if LL_WINDOWS - notification_name += "Windows"; -#elif LL_DARWIN - notification_name += "Mac"; -#else - notification_name += "Linux"; -#endif - - if (mandatory) - { - notification_name += "Mandatory"; - } - else - { -#if LL_RELEASE_FOR_DOWNLOAD - notification_name += "ReleaseForDownload"; -#endif - } - - LLNotifications::instance().add(notification_name, args, payload, update_dialog_callback); -} - -bool update_dialog_callback(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - std::string update_exe_path; - bool mandatory = notification["payload"]["mandatory"].asBoolean(); - -#if !LL_RELEASE_FOR_DOWNLOAD - if (option == 2) - { - LLStartUp::setStartupState( STATE_LOGIN_AUTH_INIT ); - return false; - } -#endif - - if (option == 1) - { - // ...user doesn't want to do it - if (mandatory) - { - LLAppViewer::instance()->forceQuit(); - // Bump them back to the login screen. - //reset_login(); - } - else - { - LLStartUp::setStartupState( STATE_LOGIN_AUTH_INIT ); - } - return false; - } - - // if a sim name was passed in via command line parameter (typically through a SLURL) - if ( LLURLSimString::sInstance.mSimString.length() ) - { - // record the location to start at next time - gSavedSettings.setString("NextLoginLocation", LLURLSimString::sInstance.mSimString); - } - - LLSD query_map = LLSD::emptyMap(); - // *TODO place os string in a global constant -#if LL_WINDOWS - query_map["os"] = "win"; -#elif LL_DARWIN - query_map["os"] = "mac"; -#elif LL_LINUX - query_map["os"] = "lnx"; -#elif LL_SOLARIS - query_map["os"] = "sol"; -#endif - // *TODO change userserver to be grid on both viewer and sim, since - // userserver no longer exists. - query_map["userserver"] = LLViewerLogin::getInstance()->getGridLabel(); - query_map["channel"] = gSavedSettings.getString("VersionChannelName"); - // *TODO constantize this guy - // *NOTE: This URL is also used in win_setup/lldownloader.cpp - LLURI update_url = LLURI::buildHTTP("secondlife.com", 80, "update.php", query_map); - - if(LLAppViewer::sUpdaterInfo) - { - delete LLAppViewer::sUpdaterInfo ; - } - LLAppViewer::sUpdaterInfo = new LLAppViewer::LLUpdaterInfo() ; - -#if LL_WINDOWS - LLAppViewer::sUpdaterInfo->mUpdateExePath = gDirUtilp->getTempFilename(); - if (LLAppViewer::sUpdaterInfo->mUpdateExePath.empty()) - { - delete LLAppViewer::sUpdaterInfo ; - LLAppViewer::sUpdaterInfo = NULL ; - - // We're hosed, bail - LL_WARNS("AppInit") << "LLDir::getTempFilename() failed" << LL_ENDL; - LLAppViewer::instance()->forceQuit(); - return false; - } - - LLAppViewer::sUpdaterInfo->mUpdateExePath += ".exe"; - - std::string updater_source = gDirUtilp->getAppRODataDir(); - updater_source += gDirUtilp->getDirDelimiter(); - updater_source += "updater.exe"; - - LL_DEBUGS("AppInit") << "Calling CopyFile source: " << updater_source - << " dest: " << LLAppViewer::sUpdaterInfo->mUpdateExePath - << LL_ENDL; - - - if (!CopyFileA(updater_source.c_str(), LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), FALSE)) - { - delete LLAppViewer::sUpdaterInfo ; - LLAppViewer::sUpdaterInfo = NULL ; - - LL_WARNS("AppInit") << "Unable to copy the updater!" << LL_ENDL; - LLAppViewer::instance()->forceQuit(); - return false; - } - - LLAppViewer::sUpdaterInfo->mParams << "-url \"" << update_url.asString() << "\""; - - LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << " " << LLAppViewer::sUpdaterInfo->mParams.str() << LL_ENDL; - - //Explicitly remove the marker file, otherwise we pass the lock onto the child process and things get weird. - LLAppViewer::instance()->removeMarkerFile(); // In case updater fails - -#elif LL_DARWIN - LLAppViewer::sUpdaterInfo->mUpdateExePath = "'"; - LLAppViewer::sUpdaterInfo->mUpdateExePath += gDirUtilp->getAppRODataDir(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "/mac-updater.app/Contents/MacOS/mac-updater' -url \""; - LLAppViewer::sUpdaterInfo->mUpdateExePath += update_url.asString(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -name \""; - LLAppViewer::sUpdaterInfo->mUpdateExePath += LLAppViewer::instance()->getSecondLifeTitle(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" &"; - - LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << LL_ENDL; - - // Run the auto-updater. - system(LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str()); /* Flawfinder: ignore */ - -#elif (LL_LINUX || LL_SOLARIS) && LL_GTK - // we tell the updater where to find the xml containing string - // translations which it can use for its own UI - std::string xml_strings_file = "strings.xml"; - std::vector<std::string> xui_path_vec = LLUI::getXUIPaths(); - std::string xml_search_paths; - std::vector<std::string>::const_iterator iter; - // build comma-delimited list of xml paths to pass to updater - for (iter = xui_path_vec.begin(); iter != xui_path_vec.end(); ) - { - std::string this_skin_dir = gDirUtilp->getDefaultSkinDir() - + gDirUtilp->getDirDelimiter() - + (*iter); - llinfos << "Got a XUI path: " << this_skin_dir << llendl; - xml_search_paths.append(this_skin_dir); - ++iter; - if (iter != xui_path_vec.end()) - xml_search_paths.append(","); // comma-delimit - } - // build the overall command-line to run the updater correctly - update_exe_path = - gDirUtilp->getExecutableDir() + "/" + "linux-updater.bin" + - " --url \"" + update_url.asString() + "\"" + - " --name \"" + LLAppViewer::instance()->getSecondLifeTitle() + "\"" + - " --dest \"" + gDirUtilp->getAppRODataDir() + "\"" + - " --stringsdir \"" + xml_search_paths + "\"" + - " --stringsfile \"" + xml_strings_file + "\""; - - LL_INFOS("AppInit") << "Calling updater: " - << update_exe_path << LL_ENDL; - - // *TODO: we could use the gdk equivilant to ensure the updater - // gets started on the same screen. - GError *error = NULL; - if (!g_spawn_command_line_async(update_exe_path.c_str(), &error)) - { - llerrs << "Failed to launch updater: " - << error->message - << llendl; - } - if (error) - g_error_free(error); -#else - OSMessageBox(LLTrans::getString("MBNoAutoUpdate"), LLStringUtil::null, OSMB_OK); -#endif - LLAppViewer::instance()->forceQuit(); - return false; -} void use_circuit_callback(void**, S32 result) { @@ -3416,11 +2682,7 @@ std::string LLStartUp::startupStateToString(EStartupState state) RTNENUM( STATE_LOGIN_SHOW ); RTNENUM( STATE_LOGIN_WAIT ); RTNENUM( STATE_LOGIN_CLEANUP ); - RTNENUM( STATE_UPDATE_CHECK ); RTNENUM( STATE_LOGIN_AUTH_INIT ); - RTNENUM( STATE_LOGIN_AUTHENTICATE ); - RTNENUM( STATE_LOGIN_NO_DATA_YET ); - RTNENUM( STATE_LOGIN_DOWNLOADING ); RTNENUM( STATE_LOGIN_PROCESS_RESPONSE ); RTNENUM( STATE_WORLD_INIT ); RTNENUM( STATE_SEED_GRANTED_WAIT ); @@ -3440,14 +2702,17 @@ std::string LLStartUp::startupStateToString(EStartupState state) #undef RTNENUM } - // static void LLStartUp::setStartupState( EStartupState state ) { LL_INFOS("AppInit") << "Startup state changing from " << - startupStateToString(gStartupState) << " to " << + getStartupStateString() << " to " << startupStateToString(state) << LL_ENDL; gStartupState = state; + LLSD stateInfo; + stateInfo["str"] = getStartupStateString(); + stateInfo["enum"] = state; + sStartupStateWatcher.post(stateInfo); } @@ -3561,3 +2826,252 @@ void apply_udp_blacklist(const std::string& csv) } +bool process_login_success_response() +{ + LLSD response = LLLoginInstance::getInstance()->getResponse(); + + std::string text(response["udp_blacklist"]); + if(!text.empty()) + { + apply_udp_blacklist(text); + } + + // unpack login data needed by the application + text = response["agent_id"].asString(); + if(!text.empty()) gAgentID.set(text); + gDebugInfo["AgentID"] = text; + + text = response["session_id"].asString(); + if(!text.empty()) gAgentSessionID.set(text); + gDebugInfo["SessionID"] = text; + + text = response["secure_session_id"].asString(); + if(!text.empty()) gAgent.mSecureSessionID.set(text); + + text = response["first_name"].asString(); + if(!text.empty()) + { + // Remove quotes from string. Login.cgi sends these to force + // names that look like numbers into strings. + gFirstname.assign(text); + LLStringUtil::replaceChar(gFirstname, '"', ' '); + LLStringUtil::trim(gFirstname); + } + text = response["last_name"].asString(); + if(!text.empty()) + { + gLastname.assign(text); + } + gSavedSettings.setString("FirstName", gFirstname); + gSavedSettings.setString("LastName", gLastname); + + if (gSavedSettings.getBOOL("RememberPassword")) + { + // Successful login means the password is valid, so save it. + LLStartUp::savePasswordToDisk(gPassword); + } + else + { + // Don't leave password from previous session sitting around + // during this login session. + LLStartUp::deletePasswordFromDisk(); + } + + // this is their actual ability to access content + text = response["agent_access_max"].asString(); + if (!text.empty()) + { + // agent_access can be 'A', 'M', and 'PG'. + gAgent.setMaturity(text[0]); + } + + // this is the value of their preference setting for that content + // which will always be <= agent_access_max + text = response["agent_region_access"].asString(); + if (!text.empty()) + { + int preferredMaturity = LLAgent::convertTextToMaturity(text[0]); + gSavedSettings.setU32("PreferredMaturity", preferredMaturity); + } + // During the AO transition, this flag will be true. Then the flag will + // go away. After the AO transition, this code and all the code that + // uses it can be deleted. + text = response["ao_transition"].asString(); + if (!text.empty()) + { + if (text == "1") + { + gAgent.setAOTransition(); + } + } + + text = response["start_location"].asString(); + if(!text.empty()) + { + gAgentStartLocation.assign(text); + } + + text = response["circuit_code"].asString(); + if(!text.empty()) + { + gMessageSystem->mOurCircuitCode = strtoul(text.c_str(), NULL, 10); + } + std::string sim_ip_str = response["sim_ip"]; + std::string sim_port_str = response["sim_port"]; + if(!sim_ip_str.empty() && !sim_port_str.empty()) + { + U32 sim_port = strtoul(sim_port_str.c_str(), NULL, 10); + gFirstSim.set(sim_ip_str, sim_port); + if (gFirstSim.isOk()) + { + gMessageSystem->enableCircuit(gFirstSim, TRUE); + } + } + std::string region_x_str = response["region_x"]; + std::string region_y_str = response["region_y"]; + if(!region_x_str.empty() && !region_y_str.empty()) + { + U32 region_x = strtoul(region_x_str.c_str(), NULL, 10); + U32 region_y = strtoul(region_y_str.c_str(), NULL, 10); + gFirstSimHandle = to_region_handle(region_x, region_y); + } + + const std::string look_at_str = response["look_at"]; + if (!look_at_str.empty()) + { + size_t len = look_at_str.size(); + LLMemoryStream mstr((U8*)look_at_str.c_str(), len); + LLSD sd = LLSDSerialize::fromNotation(mstr, len); + gAgentStartLookAt = ll_vector3_from_sd(sd); + } + + text = response["seed_capability"].asString(); + if (!text.empty()) gFirstSimSeedCap = text; + + text = response["seconds_since_epoch"].asString(); + if(!text.empty()) + { + U32 server_utc_time = strtoul(text.c_str(), NULL, 10); + if(server_utc_time) + { + time_t now = time(NULL); + gUTCOffset = (server_utc_time - now); + } + } + + std::string home_location = response["home"]; + if(!home_location.empty()) + { + size_t len = home_location.size(); + LLMemoryStream mstr((U8*)home_location.c_str(), len); + LLSD sd = LLSDSerialize::fromNotation(mstr, len); + S32 region_x = sd["region_handle"][0].asInteger(); + S32 region_y = sd["region_handle"][1].asInteger(); + U64 region_handle = to_region_handle(region_x, region_y); + LLVector3 position = ll_vector3_from_sd(sd["position"]); + gAgent.setHomePosRegion(region_handle, position); + } + + gAgent.mMOTD.assign(response["message"]); + + // Options... + // Each 'option' is an array of submaps. + // It appears that we only ever use the first element of the array. + LLUUID inv_root_folder_id = response["inventory-root"][0]["folder_id"]; + if(inv_root_folder_id.notNull()) + { + gInventory.setRootFolderID(inv_root_folder_id); + //gInventory.mock(gAgent.getInventoryRootID()); + } + + LLSD login_flags = response["login-flags"][0]; + if(login_flags.size()) + { + std::string flag = login_flags["ever_logged_in"]; + if(!flag.empty()) + { + gAgent.setFirstLogin((flag == "N") ? TRUE : FALSE); + } + + /* Flag is currently ignored by the viewer. + flag = login_flags["stipend_since_login"]; + if(flag == "Y") + { + stipend_since_login = true; + } + */ + + flag = login_flags["gendered"].asString(); + if(flag == "Y") + { + gAgent.setGenderChosen(TRUE); + } + + flag = login_flags["daylight_savings"].asString(); + if(flag == "Y") + { + gPacificDaylightTime = (flag == "Y") ? TRUE : FALSE; + } + + //setup map of datetime strings to codes and slt & local time offset from utc + LLStringOps::setupDatetimeInfo (gPacificDaylightTime); + } + + LLSD initial_outfit = response["initial-outfit"][0]; + if(initial_outfit.size()) + { + std::string flag = initial_outfit["folder_name"]; + if(!flag.empty()) + { + // Initial outfit is a folder in your inventory, + // must be an exact folder-name match. + sInitialOutfit = flag; + } + + flag = initial_outfit["gender"].asString(); + if(!flag.empty()) + { + sInitialOutfitGender = flag; + } + } + + LLSD global_textures = response["global-textures"][0]; + if(global_textures.size()) + { + // Extract sun and moon texture IDs. These are used + // in the LLVOSky constructor, but I can't figure out + // how to pass them in. JC + LLUUID id = global_textures["sun_texture_id"]; + if(id.notNull()) + { + gSunTextureID = id; + } + + id = global_textures["moon_texture_id"]; + if(id.notNull()) + { + gMoonTextureID = id; + } + + id = global_textures["cloud_texture_id"]; + if(id.notNull()) + { + gCloudTextureID = id; + } + } + + + bool success = false; + // JC: gesture loading done below, when we have an asset system + // in place. Don't delete/clear user_credentials until then. + if(gAgentID.notNull() + && gAgentSessionID.notNull() + && gMessageSystem->mOurCircuitCode + && gFirstSim.isOk() + && gInventory.getRootFolderID().notNull()) + { + success = true; + } + + return success; +} diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index 4532c5e586..7f869d014f 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -50,11 +50,7 @@ typedef enum { STATE_LOGIN_SHOW, // Show login screen STATE_LOGIN_WAIT, // Wait for user input at login screen STATE_LOGIN_CLEANUP, // Get rid of login screen and start login - STATE_UPDATE_CHECK, // Wait for user at a dialog box (updates, term-of-service, etc) STATE_LOGIN_AUTH_INIT, // Start login to SL servers - STATE_LOGIN_AUTHENTICATE, // Do authentication voodoo - STATE_LOGIN_NO_DATA_YET, // Waiting for authentication replies to start - STATE_LOGIN_DOWNLOADING, // Waiting for authentication replies to download STATE_LOGIN_PROCESS_RESPONSE, // Check authentication reply STATE_WORLD_INIT, // Start building the world STATE_MULTIMEDIA_INIT, // Init the rest of multimedia library @@ -75,8 +71,6 @@ typedef enum { // exported symbols extern bool gAgentMovementCompleted; extern LLPointer<LLViewerTexture> gStartTexture; -extern std::string gInitialOutfit; -extern std::string gInitialOutfitGender; // "male" or "female" class LLStartUp { diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp new file mode 100644 index 0000000000..9c643e78de --- /dev/null +++ b/indra/newview/lluilistener.cpp @@ -0,0 +1,50 @@ +/** + * @file lluilistener.cpp + * @author Nat Goodspeed + * @date 2009-08-18 + * @brief Implementation for lluilistener. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "llviewerprecompiledheaders.h" +// associated header +#include "lluilistener.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "lluictrl.h" +#include "llerror.h" + +LLUIListener::LLUIListener(const std::string& name): + LLDispatchListener(name, "op") +{ + add("call", &LLUIListener::call, LLSD().insert("function", LLSD())); +} + +void LLUIListener::call(const LLSD& event) const +{ + LLUICtrl::commit_callback_t* func = + LLUICtrl::CommitCallbackRegistry::getValue(event["function"]); + if (! func) + { + // This API is intended for use by a script. It's a fire-and-forget + // API: we provide no reply. Therefore, a typo in the script will + // provide no feedback whatsoever to that script. To rub the coder's + // nose in such an error, crump rather than quietly ignoring it. + LL_ERRS("LLUIListener") << "function '" << event["function"] << "' not found" << LL_ENDL; + } + else + { + // Interestingly, view_listener_t::addMenu() (addCommit(), + // addEnable()) constructs a commit_callback_t callable that accepts + // two parameters but discards the first. Only the second is passed to + // handleEvent(). Therefore we feel completely safe passing NULL for + // the first parameter. + (*func)(NULL, event["parameter"]); + } +} diff --git a/indra/newview/lluilistener.h b/indra/newview/lluilistener.h new file mode 100644 index 0000000000..ea904a99ff --- /dev/null +++ b/indra/newview/lluilistener.h @@ -0,0 +1,29 @@ +/** + * @file lluilistener.h + * @author Nat Goodspeed + * @date 2009-08-18 + * @brief Engage named functions as specified by XUI + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLUILISTENER_H) +#define LL_LLUILISTENER_H + +#include "lleventdispatcher.h" +#include <string> + +class LLSD; + +class LLUIListener: public LLDispatchListener +{ +public: + LLUIListener(const std::string& name); + +private: + void call(const LLSD& event) const; +}; + +#endif /* ! defined(LL_LLUILISTENER_H) */ diff --git a/indra/newview/llviewercontrollistener.cpp b/indra/newview/llviewercontrollistener.cpp new file mode 100644 index 0000000000..ecba1b8eb0 --- /dev/null +++ b/indra/newview/llviewercontrollistener.cpp @@ -0,0 +1,102 @@ +/** + * @file llviewercontrollistener.cpp + * @author Brad Kittenbrink + * @date 2009-07-09 + * @brief Implementation for llviewercontrollistener. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llviewercontrollistener.h" + +#include "llviewercontrol.h" + +LLViewerControlListener gSavedSettingsListener; + +LLViewerControlListener::LLViewerControlListener() + : LLDispatchListener("LLViewerControl", "group") +{ + add("Global", boost::bind(&LLViewerControlListener::set, &gSavedSettings, _1)); + add("PerAccount", boost::bind(&LLViewerControlListener::set, &gSavedPerAccountSettings, _1)); + add("Warning", boost::bind(&LLViewerControlListener::set, &gWarningSettings, _1)); + add("Crash", boost::bind(&LLViewerControlListener::set, &gCrashSettings, _1)); + +#if 0 + add(/*"toggleControl",*/ "Global", boost::bind(&LLViewerControlListener::toggleControl, &gSavedSettings, _1)); + add(/*"toggleControl",*/ "PerAccount", boost::bind(&LLViewerControlListener::toggleControl, &gSavedPerAccountSettings, _1)); + add(/*"toggleControl",*/ "Warning", boost::bind(&LLViewerControlListener::toggleControl, &gWarningSettings, _1)); + add(/*"toggleControl",*/ "Crash", boost::bind(&LLViewerControlListener::toggleControl, &gCrashSettings, _1)); + + add(/*"setDefault",*/ "Global", boost::bind(&LLViewerControlListener::setDefault, &gSavedSettings, _1)); + add(/*"setDefault",*/ "PerAccount", boost::bind(&LLViewerControlListener::setDefault, &gSavedPerAccountSettings, _1)); + add(/*"setDefault",*/ "Warning", boost::bind(&LLViewerControlListener::setDefault, &gWarningSettings, _1)); + add(/*"setDefault",*/ "Crash", boost::bind(&LLViewerControlListener::setDefault, &gCrashSettings, _1)); +#endif // 0 +} + +//static +void LLViewerControlListener::set(LLControlGroup * controls, LLSD const & event_data) +{ + if(event_data.has("key")) + { + std::string key(event_data["key"]); + + if(controls->controlExists(key)) + { + controls->setUntypedValue(key, event_data["value"]); + } + else + { + llwarns << "requested unknown control: \"" << key << '\"' << llendl; + } + } +} + +//static +void LLViewerControlListener::toggleControl(LLControlGroup * controls, LLSD const & event_data) +{ + if(event_data.has("key")) + { + std::string key(event_data["key"]); + + if(controls->controlExists(key)) + { + LLControlVariable * control = controls->getControl(key); + if(control->isType(TYPE_BOOLEAN)) + { + control->set(!control->get().asBoolean()); + } + else + { + llwarns << "requested toggle of non-boolean control: \"" << key << "\", type is " << control->type() << llendl; + } + } + else + { + llwarns << "requested unknown control: \"" << key << '\"' << llendl; + } + } +} + +//static +void LLViewerControlListener::setDefault(LLControlGroup * controls, LLSD const & event_data) +{ + if(event_data.has("key")) + { + std::string key(event_data["key"]); + + if(controls->controlExists(key)) + { + LLControlVariable * control = controls->getControl(key); + control->resetToDefault(); + } + else + { + llwarns << "requested unknown control: \"" << key << '\"' << llendl; + } + } +} diff --git a/indra/newview/llviewercontrollistener.h b/indra/newview/llviewercontrollistener.h new file mode 100644 index 0000000000..cacf97e908 --- /dev/null +++ b/indra/newview/llviewercontrollistener.h @@ -0,0 +1,33 @@ +/** + * @file llviewercontrollistener.h + * @author Brad Kittenbrink + * @date 2009-07-09 + * @brief Event API for subset of LLViewerControl methods + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#ifndef LL_LLVIEWERCONTROLLISTENER_H +#define LL_LLVIEWERCONTROLLISTENER_H + +#include "lleventdispatcher.h" + +class LLControlGroup; +class LLSD; + +class LLViewerControlListener : public LLDispatchListener +{ +public: + LLViewerControlListener(); + +private: + static void set(LLControlGroup *controls, LLSD const & event_data); + static void toggleControl(LLControlGroup *controls, LLSD const & event_data); + static void setDefault(LLControlGroup *controls, LLSD const & event_data); +}; + +extern LLViewerControlListener gSavedSettingsListener; + +#endif // LL_LLVIEWERCONTROLLISTENER_H diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index a06d913fd6..c3e6bd28da 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -763,6 +763,11 @@ void LLViewerMediaImpl::navigateStop() bool LLViewerMediaImpl::handleKeyHere(KEY key, MASK mask) { bool result = false; + // *NOTE:Mani - if this doesn't exist llmozlib goes crashy in the debug build. + // LLMozlib::init wants to write some files to <exe_dir>/components + std::string debug_init_component_dir( gDirUtilp->getExecutableDir() ); + debug_init_component_dir += "/components"; + LLAPRFile::makeDir(debug_init_component_dir.c_str()); if (mMediaSource) { diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 8220fff6b5..43e9a27484 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -210,6 +210,7 @@ #include "llwlparammanager.h" #include "llwaterparammanager.h" #include "llfloaternotificationsconsole.h" +#include "lluilistener.h" #include "lltexlayer.h" @@ -420,6 +421,8 @@ public: static LLMenuParcelObserver* gMenuParcelObserver = NULL; +static LLUIListener sUIListener("UI"); + LLMenuParcelObserver::LLMenuParcelObserver() { LLViewerParcelMgr::getInstance()->addObserver(this); @@ -6967,7 +6970,7 @@ void force_error_bad_memory_access(void *) void force_error_infinite_loop(void *) { - LLAppViewer::instance()->forceErrorInifiniteLoop(); + LLAppViewer::instance()->forceErrorInfiniteLoop(); } void force_error_software_exception(void *) diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp index 918b15ef09..801c46035a 100644 --- a/indra/newview/llviewernetwork.cpp +++ b/indra/newview/llviewernetwork.cpp @@ -35,6 +35,8 @@ #include "llviewernetwork.h" #include "llviewercontrol.h" +#include "llevents.h" +#include "lllogin.h" struct LLGridData { @@ -155,6 +157,10 @@ LLViewerLogin::LLViewerLogin() : { } + LLViewerLogin::~LLViewerLogin() + { + } + void LLViewerLogin::setGridChoice(EGridInfo grid) { if(grid < 0 || grid >= GRID_INFO_COUNT) diff --git a/indra/newview/llviewernetwork.h b/indra/newview/llviewernetwork.h index 4001ed05c1..edae6dc47b 100644 --- a/indra/newview/llviewernetwork.h +++ b/indra/newview/llviewernetwork.h @@ -34,7 +34,10 @@ #ifndef LL_LLVIEWERNETWORK_H #define LL_LLVIEWERNETWORK_H +#include <boost/scoped_ptr.hpp> + class LLHost; +class LLLogin; enum EGridInfo { @@ -74,6 +77,7 @@ class LLViewerLogin : public LLSingleton<LLViewerLogin> { public: LLViewerLogin(); + ~LLViewerLogin(); void setGridChoice(EGridInfo grid); void setGridChoice(const std::string& grid_name); diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index ca3061e083..d900ea9b33 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -57,6 +57,7 @@ #include "llparcelselection.h" #include "llresmgr.h" #include "llsdutil.h" +#include "llsdutil_math.h" #include "llstatusbar.h" #include "llui.h" #include "llviewertexture.h" diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 36499fd191..8b7bb83d0d 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -32,6 +32,10 @@ #include "llviewerprecompiledheaders.h" +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + // system library includes #include <stdio.h> #include <iostream> @@ -195,6 +199,7 @@ #include "llfloaternotificationsconsole.h" #include "llnearbychat.h" +#include "llviewerwindowlistener.h" #if LL_WINDOWS #include <tchar.h> // For Unicode conversion methods @@ -1259,7 +1264,8 @@ LLViewerWindow::LLViewerWindow( mResDirty(false), mStatesDirty(false), mIsFullscreenChecked(false), - mCurrResolutionIndex(0) + mCurrResolutionIndex(0), + mViewerWindowListener(new LLViewerWindowListener("LLViewerWindow", this)) { LLNotificationChannel::buildChannel("VW_alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert")); LLNotificationChannel::buildChannel("VW_alertmodal", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alertmodal")); diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 7afb77bed8..8502f7a719 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -52,6 +52,8 @@ #include "llcursortypes.h" #include "llhandle.h" +#include <boost/scoped_ptr.hpp> + class LLView; class LLViewerObject; class LLUUID; @@ -63,6 +65,7 @@ class LLImageRaw; class LLHUDIcon; class LLWindow; class LLRootView; +class LLViewerWindowListener; #define PICK_HALF_WIDTH 5 #define PICK_DIAMETER (2 * PICK_HALF_WIDTH + 1) @@ -458,6 +461,8 @@ protected: bool mIsFullscreenChecked; // Did the user check the fullscreen checkbox in the display settings U32 mCurrResolutionIndex; + boost::scoped_ptr<LLViewerWindowListener> mViewerWindowListener; + protected: static std::string sSnapshotBaseName; static std::string sSnapshotDir; diff --git a/indra/newview/llviewerwindowlistener.cpp b/indra/newview/llviewerwindowlistener.cpp new file mode 100644 index 0000000000..317e361c80 --- /dev/null +++ b/indra/newview/llviewerwindowlistener.cpp @@ -0,0 +1,87 @@ +/** + * @file llviewerwindowlistener.cpp + * @author Nat Goodspeed + * @date 2009-06-30 + * @brief Implementation for llviewerwindowlistener. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "llviewerprecompiledheaders.h" +// associated header +#include "llviewerwindowlistener.h" +// STL headers +#include <map> +// std headers +// external library headers +// other Linden headers +#include "llviewerwindow.h" + +LLViewerWindowListener::LLViewerWindowListener(const std::string& pumpname, LLViewerWindow* llviewerwindow): + LLDispatchListener(pumpname, "op"), + mViewerWindow(llviewerwindow) +{ + // add() every method we want to be able to invoke via this event API. + LLSD saveSnapshotArgs; + saveSnapshotArgs["filename"] = LLSD::String(); + saveSnapshotArgs["reply"] = LLSD::String(); + // The following are optional, so don't build them into required prototype. +// saveSnapshotArgs["width"] = LLSD::Integer(); +// saveSnapshotArgs["height"] = LLSD::Integer(); +// saveSnapshotArgs["showui"] = LLSD::Boolean(); +// saveSnapshotArgs["rebuild"] = LLSD::Boolean(); +// saveSnapshotArgs["type"] = LLSD::String(); + add("saveSnapshot", &LLViewerWindowListener::saveSnapshot, saveSnapshotArgs); + add("requestReshape", &LLViewerWindowListener::requestReshape); +} + +void LLViewerWindowListener::saveSnapshot(const LLSD& event) const +{ + LLReqID reqid(event); + typedef std::map<LLSD::String, LLViewerWindow::ESnapshotType> TypeMap; + TypeMap types; +#define tp(name) types[#name] = LLViewerWindow::SNAPSHOT_TYPE_##name + tp(COLOR); + tp(DEPTH); + tp(OBJECT_ID); +#undef tp + // Our add() call should ensure that the incoming LLSD does in fact + // contain our required arguments. Deal with the optional ones. + S32 width (mViewerWindow->getWindowDisplayWidth()); + S32 height(mViewerWindow->getWindowDisplayHeight()); + if (event.has("width")) + width = event["width"].asInteger(); + if (event.has("height")) + height = event["height"].asInteger(); + // showui defaults to true, requiring special treatment + bool showui = true; + if (event.has("showui")) + showui = event["showui"].asBoolean(); + bool rebuild(event["rebuild"]); // defaults to false + LLViewerWindow::ESnapshotType type(LLViewerWindow::SNAPSHOT_TYPE_COLOR); + if (event.has("type")) + { + TypeMap::const_iterator found = types.find(event["type"]); + if (found == types.end()) + { + LL_ERRS("LLViewerWindowListener") << "LLViewerWindowListener::saveSnapshot(): " + << "unrecognized type " << event["type"] << LL_ENDL; + } + type = found->second; + } + bool ok = mViewerWindow->saveSnapshot(event["filename"], width, height, showui, rebuild, type); + LLSD response(reqid.makeResponse()); + response["ok"] = ok; + LLEventPumps::instance().obtain(event["reply"]).post(response); +} + +void LLViewerWindowListener::requestReshape(LLSD const & event_data) const +{ + if(event_data.has("w") && event_data.has("h")) + { + mViewerWindow->reshape(event_data["w"].asInteger(), event_data["h"].asInteger()); + } +} diff --git a/indra/newview/llviewerwindowlistener.h b/indra/newview/llviewerwindowlistener.h new file mode 100644 index 0000000000..59c636ecec --- /dev/null +++ b/indra/newview/llviewerwindowlistener.h @@ -0,0 +1,35 @@ +/** + * @file llviewerwindowlistener.h + * @author Nat Goodspeed + * @date 2009-06-30 + * @brief Event API for subset of LLViewerWindow methods + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLVIEWERWINDOWLISTENER_H) +#define LL_LLVIEWERWINDOWLISTENER_H + +#include "lleventdispatcher.h" + +class LLViewerWindow; +class LLSD; + +/// Listen on an LLEventPump with specified name for LLViewerWindow request events. +class LLViewerWindowListener: public LLDispatchListener +{ +public: + /// Specify the pump name on which to listen, and bind the LLViewerWindow + /// instance to use (e.g. gViewerWindow). + LLViewerWindowListener(const std::string& pumpname, LLViewerWindow* llviewerwindow); + +private: + void saveSnapshot(const LLSD& event) const; + void requestReshape(LLSD const & event_data) const; + + LLViewerWindow* mViewerWindow; +}; + +#endif /* ! defined(LL_LLVIEWERWINDOWLISTENER_H) */ diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp new file mode 100644 index 0000000000..71e2427c99 --- /dev/null +++ b/indra/newview/llxmlrpclistener.cpp @@ -0,0 +1,496 @@ +/** + * @file llxmlrpclistener.cpp + * @author Nat Goodspeed + * @date 2009-03-18 + * @brief Implementation for llxmlrpclistener. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + + +// Precompiled header +#include "llviewerprecompiledheaders.h" +// associated header +#include "llxmlrpclistener.h" +// STL headers +#include <map> +#include <set> +// std headers +// external library headers +#include <boost/scoped_ptr.hpp> +#include <boost/range.hpp> // boost::begin(), boost::end() +// other Linden headers +#include "llerror.h" +#include "stringize.h" +#include "llxmlrpctransaction.h" + +#include <xmlrpc-epi/xmlrpc.h> + +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + +template <typename STATUS> +class StatusMapperBase +{ + typedef std::map<STATUS, std::string> MapType; + +public: + StatusMapperBase(const std::string& desc): + mDesc(desc) + {} + + std::string lookup(STATUS status) const + { + typename MapType::const_iterator found = mMap.find(status); + if (found != mMap.end()) + { + return found->second; + } + return STRINGIZE("<unknown " << mDesc << " " << status << ">"); + } + +protected: + std::string mDesc; + MapType mMap; +}; + +class StatusMapper: public StatusMapperBase<LLXMLRPCTransaction::Status> +{ +public: + StatusMapper(): StatusMapperBase<LLXMLRPCTransaction::Status>("Status") + { + mMap[LLXMLRPCTransaction::StatusNotStarted] = "NotStarted"; + mMap[LLXMLRPCTransaction::StatusStarted] = "Started"; + mMap[LLXMLRPCTransaction::StatusDownloading] = "Downloading"; + mMap[LLXMLRPCTransaction::StatusComplete] = "Complete"; + mMap[LLXMLRPCTransaction::StatusCURLError] = "CURLError"; + mMap[LLXMLRPCTransaction::StatusXMLRPCError] = "XMLRPCError"; + mMap[LLXMLRPCTransaction::StatusOtherError] = "OtherError"; + } +}; + +static const StatusMapper sStatusMapper; + +class CURLcodeMapper: public StatusMapperBase<CURLcode> +{ +public: + CURLcodeMapper(): StatusMapperBase<CURLcode>("CURLcode") + { + // from curl.h +// skip the "CURLE_" prefix for each of these strings +#define def(sym) (mMap[sym] = #sym + 6) + def(CURLE_OK); + def(CURLE_UNSUPPORTED_PROTOCOL); /* 1 */ + def(CURLE_FAILED_INIT); /* 2 */ + def(CURLE_URL_MALFORMAT); /* 3 */ + def(CURLE_URL_MALFORMAT_USER); /* 4 - NOT USED */ + def(CURLE_COULDNT_RESOLVE_PROXY); /* 5 */ + def(CURLE_COULDNT_RESOLVE_HOST); /* 6 */ + def(CURLE_COULDNT_CONNECT); /* 7 */ + def(CURLE_FTP_WEIRD_SERVER_REPLY); /* 8 */ + def(CURLE_FTP_ACCESS_DENIED); /* 9 a service was denied by the FTP server + due to lack of access - when login fails + this is not returned. */ + def(CURLE_FTP_USER_PASSWORD_INCORRECT); /* 10 - NOT USED */ + def(CURLE_FTP_WEIRD_PASS_REPLY); /* 11 */ + def(CURLE_FTP_WEIRD_USER_REPLY); /* 12 */ + def(CURLE_FTP_WEIRD_PASV_REPLY); /* 13 */ + def(CURLE_FTP_WEIRD_227_FORMAT); /* 14 */ + def(CURLE_FTP_CANT_GET_HOST); /* 15 */ + def(CURLE_FTP_CANT_RECONNECT); /* 16 */ + def(CURLE_FTP_COULDNT_SET_BINARY); /* 17 */ + def(CURLE_PARTIAL_FILE); /* 18 */ + def(CURLE_FTP_COULDNT_RETR_FILE); /* 19 */ + def(CURLE_FTP_WRITE_ERROR); /* 20 */ + def(CURLE_FTP_QUOTE_ERROR); /* 21 */ + def(CURLE_HTTP_RETURNED_ERROR); /* 22 */ + def(CURLE_WRITE_ERROR); /* 23 */ + def(CURLE_MALFORMAT_USER); /* 24 - NOT USED */ + def(CURLE_UPLOAD_FAILED); /* 25 - failed upload "command" */ + def(CURLE_READ_ERROR); /* 26 - could open/read from file */ + def(CURLE_OUT_OF_MEMORY); /* 27 */ + /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error + instead of a memory allocation error if CURL_DOES_CONVERSIONS + is defined + */ + def(CURLE_OPERATION_TIMEOUTED); /* 28 - the timeout time was reached */ + def(CURLE_FTP_COULDNT_SET_ASCII); /* 29 - TYPE A failed */ + def(CURLE_FTP_PORT_FAILED); /* 30 - FTP PORT operation failed */ + def(CURLE_FTP_COULDNT_USE_REST); /* 31 - the REST command failed */ + def(CURLE_FTP_COULDNT_GET_SIZE); /* 32 - the SIZE command failed */ + def(CURLE_HTTP_RANGE_ERROR); /* 33 - RANGE "command" didn't work */ + def(CURLE_HTTP_POST_ERROR); /* 34 */ + def(CURLE_SSL_CONNECT_ERROR); /* 35 - wrong when connecting with SSL */ + def(CURLE_BAD_DOWNLOAD_RESUME); /* 36 - couldn't resume download */ + def(CURLE_FILE_COULDNT_READ_FILE); /* 37 */ + def(CURLE_LDAP_CANNOT_BIND); /* 38 */ + def(CURLE_LDAP_SEARCH_FAILED); /* 39 */ + def(CURLE_LIBRARY_NOT_FOUND); /* 40 */ + def(CURLE_FUNCTION_NOT_FOUND); /* 41 */ + def(CURLE_ABORTED_BY_CALLBACK); /* 42 */ + def(CURLE_BAD_FUNCTION_ARGUMENT); /* 43 */ + def(CURLE_BAD_CALLING_ORDER); /* 44 - NOT USED */ + def(CURLE_INTERFACE_FAILED); /* 45 - CURLOPT_INTERFACE failed */ + def(CURLE_BAD_PASSWORD_ENTERED); /* 46 - NOT USED */ + def(CURLE_TOO_MANY_REDIRECTS ); /* 47 - catch endless re-direct loops */ + def(CURLE_UNKNOWN_TELNET_OPTION); /* 48 - User specified an unknown option */ + def(CURLE_TELNET_OPTION_SYNTAX ); /* 49 - Malformed telnet option */ + def(CURLE_OBSOLETE); /* 50 - NOT USED */ + def(CURLE_SSL_PEER_CERTIFICATE); /* 51 - peer's certificate wasn't ok */ + def(CURLE_GOT_NOTHING); /* 52 - when this is a specific error */ + def(CURLE_SSL_ENGINE_NOTFOUND); /* 53 - SSL crypto engine not found */ + def(CURLE_SSL_ENGINE_SETFAILED); /* 54 - can not set SSL crypto engine as + default */ + def(CURLE_SEND_ERROR); /* 55 - failed sending network data */ + def(CURLE_RECV_ERROR); /* 56 - failure in receiving network data */ + def(CURLE_SHARE_IN_USE); /* 57 - share is in use */ + def(CURLE_SSL_CERTPROBLEM); /* 58 - problem with the local certificate */ + def(CURLE_SSL_CIPHER); /* 59 - couldn't use specified cipher */ + def(CURLE_SSL_CACERT); /* 60 - problem with the CA cert (path?) */ + def(CURLE_BAD_CONTENT_ENCODING); /* 61 - Unrecognized transfer encoding */ + def(CURLE_LDAP_INVALID_URL); /* 62 - Invalid LDAP URL */ + def(CURLE_FILESIZE_EXCEEDED); /* 63 - Maximum file size exceeded */ + def(CURLE_FTP_SSL_FAILED); /* 64 - Requested FTP SSL level failed */ + def(CURLE_SEND_FAIL_REWIND); /* 65 - Sending the data requires a rewind + that failed */ + def(CURLE_SSL_ENGINE_INITFAILED); /* 66 - failed to initialise ENGINE */ + def(CURLE_LOGIN_DENIED); /* 67 - user); password or similar was not + accepted and we failed to login */ + def(CURLE_TFTP_NOTFOUND); /* 68 - file not found on server */ + def(CURLE_TFTP_PERM); /* 69 - permission problem on server */ + def(CURLE_TFTP_DISKFULL); /* 70 - out of disk space on server */ + def(CURLE_TFTP_ILLEGAL); /* 71 - Illegal TFTP operation */ + def(CURLE_TFTP_UNKNOWNID); /* 72 - Unknown transfer ID */ + def(CURLE_TFTP_EXISTS); /* 73 - File already exists */ + def(CURLE_TFTP_NOSUCHUSER); /* 74 - No such user */ + def(CURLE_CONV_FAILED); /* 75 - conversion failed */ + def(CURLE_CONV_REQD); /* 76 - caller must register conversion + callbacks using curl_easy_setopt options + CURLOPT_CONV_FROM_NETWORK_FUNCTION); + CURLOPT_CONV_TO_NETWORK_FUNCTION); and + CURLOPT_CONV_FROM_UTF8_FUNCTION */ + def(CURLE_SSL_CACERT_BADFILE); /* 77 - could not load CACERT file); missing + or wrong format */ + def(CURLE_REMOTE_FILE_NOT_FOUND); /* 78 - remote file not found */ + def(CURLE_SSH); /* 79 - error from the SSH layer); somewhat + generic so the error message will be of + interest when this has happened */ + + def(CURLE_SSL_SHUTDOWN_FAILED); /* 80 - Failed to shut down the SSL + connection */ +#undef def + } +}; + +static const CURLcodeMapper sCURLcodeMapper; + +LLXMLRPCListener::LLXMLRPCListener(const std::string& pumpname): + mBoundListener(LLEventPumps::instance(). + obtain(pumpname). + listen("LLXMLRPCListener", boost::bind(&LLXMLRPCListener::process, this, _1))) +{ +} + +/** + * Capture an outstanding LLXMLRPCTransaction and poll it periodically until + * done. + * + * The sequence is: + * # Instantiate Poller, which instantiates, populates and initiates an + * LLXMLRPCTransaction. Poller self-registers on the LLEventPump named + * "mainloop". + * # "mainloop" is conventionally pumped once per frame. On each such call, + * Poller checks its LLXMLRPCTransaction for completion. + * # When the LLXMLRPCTransaction completes, Poller collects results (if any) + * and sends notification. + * # The tricky part: Poller frees itself (and thus its LLXMLRPCTransaction) + * when done. The only external reference to it is the connection to the + * "mainloop" LLEventPump. + */ +class Poller +{ +public: + /// Validate the passed request for required fields, then use it to + /// populate an XMLRPC_REQUEST and an associated LLXMLRPCTransaction. Send + /// the request. + Poller(const LLSD& command): + mReqID(command), + mUri(command["uri"]), + mMethod(command["method"]), + mReplyPump(command["reply"]) + { + // LL_ERRS if any of these are missing + const char* required[] = { "uri", "method", "reply" }; + // optional: "options" (array of string) + // Validate the request + std::set<std::string> missing; + for (const char** ri = boost::begin(required); ri != boost::end(required); ++ri) + { + // If the command does not contain this required entry, add it to 'missing'. + if (! command.has(*ri)) + { + missing.insert(*ri); + } + } + if (! missing.empty()) + { + LL_ERRS("LLXMLRPCListener") << mMethod << " request missing params: "; + const char* separator = ""; + for (std::set<std::string>::const_iterator mi(missing.begin()), mend(missing.end()); + mi != mend; ++mi) + { + LL_CONT << separator << *mi; + separator = ", "; + } + LL_CONT << LL_ENDL; + } + + // Build the XMLRPC request. + XMLRPC_REQUEST request = XMLRPC_RequestNew(); + XMLRPC_RequestSetMethodName(request, mMethod.c_str()); + XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); + XMLRPC_VALUE xparams = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); + LLSD params(command["params"]); + if (params.isMap()) + { + for (LLSD::map_const_iterator pi(params.beginMap()), pend(params.endMap()); + pi != pend; ++pi) + { + std::string name(pi->first); + LLSD param(pi->second); + if (param.isString()) + { + XMLRPC_VectorAppendString(xparams, name.c_str(), param.asString().c_str(), 0); + } + else if (param.isInteger() || param.isBoolean()) + { + XMLRPC_VectorAppendInt(xparams, name.c_str(), param.asInteger()); + } + else if (param.isReal()) + { + XMLRPC_VectorAppendDouble(xparams, name.c_str(), param.asReal()); + } + else + { + LL_ERRS("LLXMLRPCListener") << mMethod << " request param " + << name << " has unknown type: " << param << LL_ENDL; + } + } + } + LLSD options(command["options"]); + if (options.isArray()) + { + XMLRPC_VALUE xoptions = XMLRPC_CreateVector("options", xmlrpc_vector_array); + for (LLSD::array_const_iterator oi(options.beginArray()), oend(options.endArray()); + oi != oend; ++oi) + { + XMLRPC_VectorAppendString(xoptions, NULL, oi->asString().c_str(), 0); + } + XMLRPC_AddValueToVector(xparams, xoptions); + } + XMLRPC_RequestSetData(request, xparams); + + mTransaction.reset(new LLXMLRPCTransaction(mUri, request)); + mPreviousStatus = mTransaction->status(NULL); + + // Free the XMLRPC_REQUEST object and the attached data values. + XMLRPC_RequestFree(request, 1); + + // Now ensure that we get regular callbacks to poll for completion. + mBoundListener = + LLEventPumps::instance(). + obtain("mainloop"). + listen(LLEventPump::inventName(), boost::bind(&Poller::poll, this, _1)); + + LL_INFOS("LLXMLRPCListener") << mMethod << " request sent to " << mUri << LL_ENDL; + } + + /// called by "mainloop" LLEventPump + bool poll(const LLSD&) + { + bool done = mTransaction->process(); + + CURLcode curlcode; + LLXMLRPCTransaction::Status status; + { + // LLXMLRPCTransaction::status() is defined to accept int* rather + // than CURLcode*. I don't feel the urge to fix the signature, but + // we want a CURLcode rather than an int. So fetch it as a local + // int, but then assign to a CURLcode for the remainder of this + // method. + int curlint; + status = mTransaction->status(&curlint); + curlcode = CURLcode(curlint); + } + + LLSD data(mReqID.makeResponse()); + data["status"] = sStatusMapper.lookup(status); + data["errorcode"] = sCURLcodeMapper.lookup(curlcode); + data["error"] = ""; + data["transfer_rate"] = 0.0; + LLEventPump& replyPump(LLEventPumps::instance().obtain(mReplyPump)); + if (! done) + { + // Not done yet, carry on. + if (status == LLXMLRPCTransaction::StatusDownloading + && status != mPreviousStatus) + { + // If a response has been received, send the + // 'downloading' status if it hasn't been sent. + replyPump.post(data); + } + + mPreviousStatus = status; + return false; + } + + // Here the transaction is complete. Check status. + data["error"] = mTransaction->statusMessage(); + data["transfer_rate"] = mTransaction->transferRate(); + LL_INFOS("LLXMLRPCListener") << mMethod << " result from " << mUri << ": status " + << data["status"].asString() << ", errorcode " + << data["errorcode"].asString() + << " (" << data["error"].asString() << ")" + << LL_ENDL; + // In addition to CURLE_OK, LLUserAuth distinguishes different error + // values of 'curlcode': + // CURLE_COULDNT_RESOLVE_HOST, + // CURLE_SSL_PEER_CERTIFICATE, + // CURLE_SSL_CACERT, + // CURLE_SSL_CONNECT_ERROR. + // Given 'message', need we care? + if (status == LLXMLRPCTransaction::StatusComplete) + { + // Success! Parse data. + std::string status_string(data["status"]); + data["responses"] = parseResponse(status_string); + data["status"] = status_string; + } + + // whether successful or not, send reply on requested LLEventPump + replyPump.post(data); + + // Because mTransaction is a boost::scoped_ptr, deleting this object + // frees our LLXMLRPCTransaction object. + // Because mBoundListener is an LLTempBoundListener, deleting this + // object disconnects it from "mainloop". + // *** MUST BE LAST *** + delete this; + return false; + } + +private: + /// Derived from LLUserAuth::parseResponse() and parseOptionInto() + LLSD parseResponse(std::string& status_string) + { + // Extract every member into data["responses"] (a map of string + // values). + XMLRPC_REQUEST response = mTransaction->response(); + if (! response) + { + LL_DEBUGS("LLXMLRPCListener") << "No response" << LL_ENDL; + return LLSD(); + } + + XMLRPC_VALUE param = XMLRPC_RequestGetData(response); + if (! param) + { + LL_DEBUGS("LLXMLRPCListener") << "Response contains no data" << LL_ENDL; + return LLSD(); + } + + // Now, parse everything + return parseValues(status_string, "", param); + } + + /** + * Parse key/value pairs from a given XMLRPC_VALUE into an LLSD map. + * @param key_pfx Used to describe a given key in log messages. At top + * level, pass "". When parsing an options array, pass the top-level key + * name of the array plus the index of the array entry; to this we'll + * append the subkey of interest. + * @param param XMLRPC_VALUE iterator. At top level, pass + * XMLRPC_RequestGetData(XMLRPC_REQUEST). + */ + LLSD parseValues(std::string& status_string, const std::string& key_pfx, XMLRPC_VALUE param) + { + LLSD responses; + for (XMLRPC_VALUE current = XMLRPC_VectorRewind(param); current; + current = XMLRPC_VectorNext(param)) + { + std::string key(XMLRPC_GetValueID(current)); + LL_DEBUGS("LLXMLRPCListener") << "key: " << key_pfx << key << LL_ENDL; + XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(current); + if (xmlrpc_type_string == type) + { + LLSD::String val(XMLRPC_GetValueString(current)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL; + responses.insert(key, val); + } + else if (xmlrpc_type_int == type) + { + LLSD::Integer val(XMLRPC_GetValueInt(current)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL; + responses.insert(key, val); + } + else if (xmlrpc_type_double == type) + { + LLSD::Real val(XMLRPC_GetValueDouble(current)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL; + responses.insert(key, val); + } + else if (xmlrpc_type_array == type) + { + // We expect this to be an array of submaps. Walk the array, + // recursively parsing each submap and collecting them. + LLSD array; + int i = 0; // for descriptive purposes + for (XMLRPC_VALUE row = XMLRPC_VectorRewind(current); row; + row = XMLRPC_VectorNext(current), ++i) + { + // Recursive call. For the lower-level key_pfx, if 'key' + // is "foo", pass "foo[0]:", then "foo[1]:", etc. In the + // nested call, a subkey "bar" will then be logged as + // "foo[0]:bar", and so forth. + // Parse the scalar subkey/value pairs from this array + // entry into a temp submap. Collect such submaps in 'array'. + array.append(parseValues(status_string, + STRINGIZE(key_pfx << key << '[' << i << "]:"), + row)); + } + // Having collected an 'array' of 'submap's, insert that whole + // 'array' as the value of this 'key'. + responses.insert(key, array); + } + else + { + // whoops - unrecognized type + LL_WARNS("LLXMLRPCListener") << "Unhandled xmlrpc type " << type << " for key " + << key_pfx << key << LL_ENDL; + responses.insert(key, STRINGIZE("<bad XMLRPC type " << type << '>')); + status_string = "BadType"; + } + } + return responses; + } + + const LLReqID mReqID; + const std::string mUri; + const std::string mMethod; + const std::string mReplyPump; + LLTempBoundListener mBoundListener; + boost::scoped_ptr<LLXMLRPCTransaction> mTransaction; + LLXMLRPCTransaction::Status mPreviousStatus; // To detect state changes. +}; + +bool LLXMLRPCListener::process(const LLSD& command) +{ + // Allocate a new heap Poller, but do not save a pointer to it. Poller + // will check its own status and free itself on completion of the request. + (new Poller(command)); + // conventional event listener return + return false; +} diff --git a/indra/newview/llxmlrpclistener.h b/indra/newview/llxmlrpclistener.h new file mode 100644 index 0000000000..120c2b329b --- /dev/null +++ b/indra/newview/llxmlrpclistener.h @@ -0,0 +1,35 @@ +/** + * @file llxmlrpclistener.h + * @author Nat Goodspeed + * @date 2009-03-18 + * @brief LLEventPump API for LLXMLRPCTransaction. This header doesn't + * actually define the API; the API is defined by the pump name on + * which this class listens, and by the expected content of LLSD it + * receives. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLXMLRPCLISTENER_H) +#define LL_LLXMLRPCLISTENER_H + +#include "llevents.h" + +/// Listen on an LLEventPump with specified name for LLXMLRPCTransaction +/// request events. +class LLXMLRPCListener +{ +public: + /// Specify the pump name on which to listen + LLXMLRPCListener(const std::string& pumpname); + + /// Handle request events on the event pump specified at construction time + bool process(const LLSD& command); + +private: + LLTempBoundListener mBoundListener; +}; + +#endif /* ! defined(LL_LLXMLRPCLISTENER_H) */ diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index a2fd0f0d9c..0e1beb377f 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -33,6 +33,7 @@ #include "llviewerprecompiledheaders.h" #include "llxmlrpctransaction.h" +#include "llxmlrpclistener.h" #include "llcurl.h" #include "llviewercontrol.h" @@ -42,6 +43,13 @@ #include "llappviewer.h" +// Static instance of LLXMLRPCListener declared here so that every time we +// bring in this code, we instantiate a listener. If we put the static +// instance of LLXMLRPCListener into llxmlrpclistener.cpp, the linker would +// simply omit llxmlrpclistener.o, and shouting on the LLEventPump would do +// nothing. +static LLXMLRPCListener listener("LLXMLRPCTransaction"); + LLXMLRPCValue LLXMLRPCValue::operator[](const char* id) const { return LLXMLRPCValue(XMLRPC_VectorGetValueWithID(mV, id)); @@ -213,6 +221,11 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, XMLRPC_RequestSetData(request, params.getValue()); init(request, useGzip); + // DEV-28398: without this XMLRPC_RequestFree() call, it looks as though + // the 'request' object is simply leaked. It's less clear to me whether we + // should also ask to free request value data (second param 1), since the + // data come from 'params'. + XMLRPC_RequestFree(request, 1); } diff --git a/indra/newview/nl.lproj/language.txt b/indra/newview/nl.lproj/language.txt new file mode 100644 index 0000000000..d5f5c2a19c --- /dev/null +++ b/indra/newview/nl.lproj/language.txt @@ -0,0 +1 @@ +nl
\ No newline at end of file diff --git a/indra/newview/pl.lproj/language.txt b/indra/newview/pl.lproj/language.txt new file mode 100644 index 0000000000..55239f3c18 --- /dev/null +++ b/indra/newview/pl.lproj/language.txt @@ -0,0 +1 @@ +pl
\ No newline at end of file diff --git a/indra/newview/pt.lproj/language.txt b/indra/newview/pt.lproj/language.txt new file mode 100644 index 0000000000..9e3340eca2 --- /dev/null +++ b/indra/newview/pt.lproj/language.txt @@ -0,0 +1 @@ +pt
\ No newline at end of file diff --git a/indra/newview/ru.lproj/language.txt b/indra/newview/ru.lproj/language.txt new file mode 100644 index 0000000000..adc719b423 --- /dev/null +++ b/indra/newview/ru.lproj/language.txt @@ -0,0 +1 @@ +ru
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/de/floater_snapshot.xml b/indra/newview/skins/default/xui/de/floater_snapshot.xml index 401aa74084..e94e22d9b8 100644 --- a/indra/newview/skins/default/xui/de/floater_snapshot.xml +++ b/indra/newview/skins/default/xui/de/floater_snapshot.xml @@ -21,12 +21,12 @@ <button label="Senden" name="send_btn"/> <button label="Speichern ([AMOUNT] L$)" name="upload_btn"/> <flyout_button label="Speichern" name="save_btn" tool_tip="Bild als Datei speichern"> - <flyout_button_item name="save_item"> - Speichern - </flyout_button_item> - <flyout_button_item name="saveas_item"> - Speichern unter... - </flyout_button_item> + <flyout_button.item name="save_item"> + Speichern + </flyout_button.item> + <flyout_button.item name="saveas_item"> + Speichern unter... + </flyout_button.item> </flyout_button> <button label="Abbrechen" name="discard_btn"/> <button label="Mehr >>" name="more_btn" tool_tip="Erweiterte Optionen"/> @@ -38,75 +38,33 @@ Format </text> <combo_box label="Auflösung" name="postcard_size_combo"> - <combo_item name="CurrentWindow"> - Aktuelles Fenster - </combo_item> - <combo_item name="640x480"> - 640x480 - </combo_item> - <combo_item name="800x600"> - 800x600 - </combo_item> - <combo_item name="1024x768"> - 1024x768 - </combo_item> - <combo_item name="Custom"> - Benutzerdefiniert - </combo_item> + <combo_box.item name="CurrentWindow" label="Aktuelles Fenster" /> + <combo_box.item name="640x480" label="640x480" /> + <combo_box.item name="800x600" label="800x600" /> + <combo_box.item name="1024x768" label="1024x768" /> + <combo_box.item name="Custom" label="Benutzerdefiniert" /> </combo_box> <combo_box label="Auflösung" name="texture_size_combo"> - <combo_item name="CurrentWindow"> - Aktuelles Fenster - </combo_item> - <combo_item name="Small(128x128)"> - Klein (128x128) - </combo_item> - <combo_item name="Medium(256x256)"> - Mittel (256x256) - </combo_item> - <combo_item name="Large(512x512)"> - Groß (512x512) - </combo_item> - <combo_item name="Custom"> - Benutzerdefiniert - </combo_item> + <combo_box.item name="CurrentWindow" label="Aktuelles Fenster" /> + <combo_box.item name="Small(128x128)" label="Klein (128x128)" /> + <combo_box.item name="Medium(256x256)" label="Mittel (256x256)" /> + <combo_box.item name="Large(512x512)" label="Groß (512x512)" /> + <combo_box.item name="Custom" label="Benutzerdefiniert" /> </combo_box> <combo_box label="Auflösung" name="local_size_combo"> - <combo_item name="CurrentWindow"> - Aktuelles Fenster - </combo_item> - <combo_item name="320x240"> - 320x240 - </combo_item> - <combo_item name="640x480"> - 640x480 - </combo_item> - <combo_item name="800x600"> - 800x600 - </combo_item> - <combo_item name="1024x768"> - 1024x768 - </combo_item> - <combo_item name="1280x1024"> - 1280x1024 - </combo_item> - <combo_item name="1600x1200"> - 1600x1200 - </combo_item> - <combo_item name="Custom"> - Benutzerdefiniert - </combo_item> + <combo_box.item name="CurrentWindow" label="Aktuelles Fenster" /> + <combo_box.item name="320x240" label="320x240" /> + <combo_box.item name="640x480" label="640x480" /> + <combo_box.item name="800x600" label="800x600" /> + <combo_box.item name="1024x768" label="1024x768" /> + <combo_box.item name="1280x1024" label="1280x1024" /> + <combo_box.item name="1600x1200" label="1600x1200" /> + <combo_box.item name="Custom" label="Benutzerdefiniert" /> </combo_box> <combo_box label="Format" name="local_format_combo"> - <combo_item name="PNG"> - PNG - </combo_item> - <combo_item name="JPEG"> - JPEG - </combo_item> - <combo_item name="BMP"> - BMP - </combo_item> + <combo_box.item name="PNG" label="PNG" /> + <combo_box.item name="JPEG" label="JPEG" /> + <combo_box.item name="BMP" label="BMP" /> </combo_box> <spinner label="Breite" name="snapshot_width"/> <spinner label="Höhe" name="snapshot_height"/> @@ -116,15 +74,9 @@ Aufnehmen: </text> <combo_box label="Bildlayer" name="layer_types" width="132" left="73"> - <combo_item name="Colors"> - Farben - </combo_item> - <combo_item name="Depth"> - Tiefe - </combo_item> - <combo_item name="ObjectMattes"> - Objektmasken - </combo_item> + <combo_box.item name="Colors" label="Farben" /> + <combo_box.item name="Depth" label="Tiefe" /> + <combo_box.item name="ObjectMattes" label="Objektmasken" /> </combo_box> <check_box label="Interface auf Foto anzeigen" name="ui_check"/> <check_box label="HUD-Objekte auf Foto anzeigen" name="hud_check"/> diff --git a/indra/newview/skins/default/xui/de/panel_preferences_general.xml b/indra/newview/skins/default/xui/de/panel_preferences_general.xml index 40b4909e84..92b45bf5a5 100644 --- a/indra/newview/skins/default/xui/de/panel_preferences_general.xml +++ b/indra/newview/skins/default/xui/de/panel_preferences_general.xml @@ -10,15 +10,9 @@ </radio_group> <check_box label="Startposition auf Anmeldebildschirm anzeigen" name="show_location_checkbox"/> <combo_box name="fade_out_combobox"> - <combo_item name="Never"> - Nie - </combo_item> - <combo_item name="Show Temporarily"> - Temporär anzeigen - </combo_item> - <combo_item name="Always"> - Immer - </combo_item> + <combo_box.item name="Never" label="Nie" /> + <combo_box.item name="Show Temporarily" label="Temporär anzeigen" /> + <combo_box.item name="Always" label="Immer" /> </combo_box> <check_box label="Kleine Avatarnamen" name="small_avatar_names_checkbox"/> <check_box label="Meinen Namen auf meinem Bildschirm ausblenden" name="show_my_name_checkbox"/> @@ -80,49 +74,29 @@ Alterseinstufung zugreifen: <Region eingeben> </string> <combo_box name="crash_behavior_combobox" width="166"> - <combo_item name="Askbeforesending"> - Vor dem Senden fragen - </combo_item> - <combo_item name="Alwayssend"> - Immer senden - </combo_item> - <combo_item name="Neversend"> - Nie senden - </combo_item> + <combo_box.item name="Askbeforesending" label="Vor dem Senden fragen" /> + <combo_box.item name="Alwayssend" label="Immer senden" /> + <combo_box.item name="Neversend" label="Nie senden" /> </combo_box> <combo_box name="language_combobox" width="166"> - <combo_item name="System Default Language"> - Betriebssystem-Einstellung - </combo_item> - <combo_item name="English"> - English (Englisch) - </combo_item> - <combo_item name="Danish"> - Danks (Dänisch) - Beta - </combo_item> - <combo_item name="Deutsch(German)"> - Deutsch - Beta - </combo_item> - <combo_item name="Spanish"> - Español (Spanisch) - Beta - </combo_item> - <combo_item name="French"> - Français (Französisch) - Beta - </combo_item> + <combo_box.item name="System Default Language" label="Betriebssystem-Einstellung" /> + <combo_box.item name="English" label="English (Englisch)" /> + <combo_box.item name="Danish" label="Danks (Dänisch) - Beta" /> + <combo_box.item name="Deutsch(German)" label="Deutsch - Beta" /> + <combo_box.item name="Spanish" label="Español (Spanisch) - Beta" /> + <combo_box.item name="French" label="Français (Französisch) - Beta" /> + <combo_box.item name="Hungarian" label="Magyar (Ungarisch) - Beta" /> + <combo_box.item name="Polish" label="Polski (Polnisch) - Beta" /> + <combo_box.item name="Portugese" label="Português (Portugiesisch) - Beta" /> + <combo_box.item name="Chinese" label="中文 (简体) (Chinesisch) - Beta" /> + <combo_box.item name="(Japanese)" label="日本語 (Japanisch) - Beta" /> + <combo_box.item name="(Korean)" label="한국어 (Koreanisch) - Beta" /> <combo_item name="Italian"> Italiano (Italienisch) - Beta </combo_item> - <combo_item name="Hungarian"> - Magyar (Ungarisch) - Beta - </combo_item> <combo_item name="Dutch"> Nederlands (Niederländisch) - Beta </combo_item> - <combo_item name="Polish"> - Polski (Polnisch) - Beta - </combo_item> - <combo_item name="Portugese"> - Português (Portugiesisch) - Beta </combo_item> <combo_item name="Russian"> Русский (Russian) - Beta @@ -132,16 +106,6 @@ Alterseinstufung zugreifen: </combo_item> <combo_item name="Ukrainian"> Українська (Ukrainisch) - Beta - </combo_item> - <combo_item name="Chinese"> - 中文 (简体) (Chinesisch) - Beta - </combo_item> - <combo_item name="(Japanese)"> - 日本語 (Japanisch) - Beta - </combo_item> - <combo_item name="(Korean)"> - 한국어 (Koreanisch) - Beta - </combo_item> </combo_box> <check_box label="Objekten Sprache mitteilen" name="language_is_public" tool_tip="In-Welt-Objekten wird Ihre bevorzugte Spracheinstellung mitgeteilt."/> </panel> diff --git a/indra/newview/skins/default/xui/es/floater_about_land.xml b/indra/newview/skins/default/xui/es/floater_about_land.xml index 767b384a2d..3e3b8df646 100644 --- a/indra/newview/skins/default/xui/es/floater_about_land.xml +++ b/indra/newview/skins/default/xui/es/floater_about_land.xml @@ -341,42 +341,18 @@ Sólo las parcelas más grandes pueden listarse en la búsqueda. </combo_item> </combo_box> <combo_box name="land category"> - <combo_item name="AnyCategory"> - Cualquier categoría - </combo_item> - <combo_item name="LindenLocation"> - Localización Linden - </combo_item> - <combo_item name="Arts&Culture"> - Arte y Cultura - </combo_item> - <combo_item name="Business"> - Negocios - </combo_item> - <combo_item name="Educational"> - Educativo - </combo_item> - <combo_item name="Gaming"> - Juegos de azar - </combo_item> - <combo_item name="Hangout"> - Entretenimiento - </combo_item> - <combo_item name="NewcomerFriendly"> - Para recién llegados - </combo_item> - <combo_item name="Parks&Nature"> - Parques y Naturaleza - </combo_item> - <combo_item name="Residential"> - Residencial - </combo_item> - <combo_item name="Shopping"> - Compras - </combo_item> - <combo_item name="Other"> - Otra - </combo_item> + <combo_box.item name="AnyCategory" label="Cualquier categoría"/> + <combo_box.item name="LindenLocation" label="Localización Linden"/> + <combo_box.item name="Arts&Culture" label="Arte y Cultura"/> + <combo_box.item name="Business" label="Negocios"/> + <combo_box.item name="Educational" label="Educativo"/> + <combo_box.item name="Gaming" label="Juegos de azar"/> + <combo_box.item name="Hangout" label="Entretenimiento"/> + <combo_box.item name="NewcomerFriendly" label="Para recién llegados"/> + <combo_box.item name="Parks&Nature" label="Parques y Naturaleza"/> + <combo_box.item name="Residential" label="Residencial"/> + <combo_box.item name="Shopping" label="Compras"/> + <combo_box.item name="Other" label="Otra"/> </combo_box> <button label="?" label_selected="?" name="?"/> <check_box label="Contenido 'Mature'" name="MatureCheck" tool_tip=""/> @@ -408,15 +384,9 @@ Sólo las parcelas más grandes pueden listarse en la búsqueda. Punto de teleporte: </text> <combo_box name="landing type" tool_tip="Punto de teleporte: defina cómo manejar en su terreno los teleportes."> - <combo_item length="1" name="Blocked" type="string"> - Bloqueado - </combo_item> - <combo_item length="1" name="LandingPoint" type="string"> - Punto de llegada - </combo_item> - <combo_item length="1" name="Anywhere" type="string"> - Cualquiera - </combo_item> + <combo_box.item length="1" name="Blocked" type="string" label="Bloqueado"/> + <combo_box.item length="1" name="LandingPoint" type="string" label="Punto de llegada"/> + <combo_box.item length="1" name="Anywhere" type="string" label="Cualquiera"/> </combo_box> <string name="push_restrict_text"> Sin 'empujones' @@ -497,12 +467,8 @@ música: <check_box label="Acceso permitido al grupo: [GROUP]" name="GroupCheck" tool_tip="Elija el grupo en la pestaña General."/> <check_box label="Vender pases a:" name="PassCheck" tool_tip="Permitir acceso temporal a esta parcela"/> <combo_box name="pass_combo"> - <combo_item name="Anyone"> - Cualquiera - </combo_item> - <combo_item name="Group"> - Grupo - </combo_item> + <combo_box.item name="Anyone" label="Cualquiera"/> + <combo_box.item name="Group" label="Grupo"/> </combo_box> <spinner label="Precio en L$:" name="PriceSpin"/> <spinner label="Horas de acceso:" name="HoursSpin"/> diff --git a/indra/newview/skins/default/xui/es/panel_preferences_general.xml b/indra/newview/skins/default/xui/es/panel_preferences_general.xml index 9e5cd88603..b711821e5f 100644 --- a/indra/newview/skins/default/xui/es/panel_preferences_general.xml +++ b/indra/newview/skins/default/xui/es/panel_preferences_general.xml @@ -10,15 +10,9 @@ </radio_group> <check_box label="Ver mi posición inicial en la pantalla de inicio de sesión" name="show_location_checkbox"/> <combo_box name="fade_out_combobox" width="166"> - <combo_item name="Never"> - Nunca - </combo_item> - <combo_item name="Show Temporarily"> - Mostrarlos temporalmente - </combo_item> - <combo_item name="Always"> - Siempre - </combo_item> + <combo_box.item name="Never" label="Nunca"/> + <combo_box.item name="Show Temporarily" label="Mostrarlos temporalmente"/> + <combo_box.item name="Always" label="Siempre"/> </combo_box> <check_box label="Nombres de avatar en pequeño" name="small_avatar_names_checkbox"/> <check_box label="No ver mi nombre en mi pantalla" name="show_my_name_checkbox"/> @@ -80,49 +74,29 @@ contenido calificado: <escriba el nombre de la región> </string> <combo_box name="crash_behavior_combobox" width="166"> - <combo_item length="1" name="Askbeforesending" type="string"> - Preguntar antes de enviar - </combo_item> - <combo_item length="1" name="Alwayssend" type="string"> - Enviar siempre - </combo_item> - <combo_item length="1" name="Neversend" type="string"> - No enviar nunca - </combo_item> + <combo_box.item length="1" name="Askbeforesending" type="string" label="Preguntar antes de enviar"/> + <combo_box.item length="1" name="Alwayssend" type="string" label="Enviar siempre"/> + <combo_box.item length="1" name="Neversend" type="string" label="No enviar nunca"/> </combo_box> <combo_box name="language_combobox" width="166"> - <combo_item name="System Default Language"> - Predeterminado del sistema - </combo_item> - <combo_item length="1" name="English" type="string"> - English (Inglés) - </combo_item> - <combo_item name="Danish"> - Dansk (Danés) - Beta - </combo_item> - <combo_item length="1" name="Deutsch(German)" type="string"> - Deutsch (Alemán) - Beta - </combo_item> - <combo_item name="Spanish" type="string"> - Español - Beta - </combo_item> - <combo_item length="1" name="French" type="string"> - Français (Francés) - Beta - </combo_item> + <combo_box.item name="System Default Language" label="Predeterminado del sistema"/> + <combo_box.item length="1" name="English" type="string" label="English (Inglés)"/> + <combo_box.item name="Danish" label="Dansk (Danés) - Beta"/> + <combo_box.item length="1" name="Deutsch(German)" type="string" label="Deutsch (Alemán) - Beta"/> + <combo_box.item name="Spanish" type="string" label="Español - Beta"/> + <combo_box.item length="1" name="French" type="string" label="Français (Francés) - Beta"/> + <combo_box.item name="Hungarian" label="Magyar (Húngaro) - Beta"/> + <combo_box.item name="Polish" label="Polski (Polaco) - Beta"/> + <combo_box.item name="Portugese" label="Portugués (Portugués) - Beta"/> + <combo_box.item length="1" name="Chinese" type="string" label="中文 (简体) (Chino) - Beta"/> + <combo_box.item length="1" name="(Japanese)" type="string" label="日本語 (Japonés) - Beta"/> + <combo_box.item length="1" name="(Korean)" type="string" label="한국어 (Coreano) - Beta"/> <combo_item name="Italian"> Italiano - Beta </combo_item> - <combo_item name="Hungarian"> - Magyar (Húngaro) - Beta - </combo_item> <combo_item name="Dutch"> Nederlands (Neerlandés) - Beta </combo_item> - <combo_item name="Polish"> - Polski (Polaco) - Beta - </combo_item> - <combo_item name="Portugese"> - Portugués (Portugués) - Beta </combo_item> <combo_item name="Russian"> Русский (Ruso) - Beta @@ -132,16 +106,6 @@ contenido calificado: </combo_item> <combo_item name="Ukrainian"> Українська (Ucraniano) - Beta - </combo_item> - <combo_item length="1" name="Chinese" type="string"> - 中文 (简体) (Chino) - Beta - </combo_item> - <combo_item length="1" name="(Japanese)" type="string"> - 日本語 (Japonés) - Beta - </combo_item> - <combo_item length="1" name="(Korean)" type="string"> - 한국어 (Coreano) - Beta - </combo_item> </combo_box> <check_box label="Compartir el idioma con los objetos" name="language_is_public" tool_tip="Hace que los objetos con script del mundo sepan su idioma preferido."/> </panel> diff --git a/indra/newview/skins/default/xui/fr/panel_preferences_general.xml b/indra/newview/skins/default/xui/fr/panel_preferences_general.xml index 23e1e74035..7c366e8bdc 100644 --- a/indra/newview/skins/default/xui/fr/panel_preferences_general.xml +++ b/indra/newview/skins/default/xui/fr/panel_preferences_general.xml @@ -10,15 +10,9 @@ </radio_group> <check_box label="Afficher le lieu de départ sur l'écran de connexion" name="show_location_checkbox"/> <combo_box name="fade_out_combobox" width="166"> - <combo_item name="Never"> - Jamais - </combo_item> - <combo_item name="Show Temporarily"> - Afficher temporairement - </combo_item> - <combo_item name="Always"> - Toujours - </combo_item> + <combo_box.item name="Never" label="Jamais"/> + <combo_box.item name="Show Temporarily" label="Afficher temporairement"/> + <combo_box.item name="Always" label="Toujours"/> </combo_box> <check_box label="Nom des avatars en petit" name="small_avatar_names_checkbox"/> <check_box label="Masquer mon nom sur mon écran" name="show_my_name_checkbox"/> @@ -81,68 +75,34 @@ de sélection : <Saisissez le nom de la région> </string> <combo_box name="crash_behavior_combobox" width="166"> - <combo_item length="1" name="Askbeforesending" type="string"> - Demander avant d'envoyer - </combo_item> - <combo_item length="1" name="Alwayssend" type="string"> - Toujours envoyer - </combo_item> - <combo_item length="1" name="Neversend" type="string"> - Ne jamais envoyer - </combo_item> + <combo_box.item length="1" name="Askbeforesending" type="string" label="Demander avant d'envoyer"/> + <combo_box.item length="1" name="Alwayssend" type="string" label="Toujours envoyer"/> + <combo_box.item length="1" name="Neversend" type="string" label="Ne jamais envoyer"/> </combo_box> <combo_box name="language_combobox" width="166"> - <combo_item name="System Default Language"> - Choix par défaut - </combo_item> - <combo_item length="1" name="English" type="string"> - English (Anglais) - </combo_item> - <combo_item length="1" name="Danish" type="string"> - Dansk (Danois) - Bêta - </combo_item> - <combo_item length="1" name="Deutsch(German)" type="string"> - Deutsch (Allemand) - Bêta - </combo_item> - <combo_item name="Spanish"> - Español (Espagnol) - Bêta - </combo_item> - <combo_item name="French"> - Français - Bêta - </combo_item> + <combo_box.item name="System Default Language" label="Choix par défaut"/> + <combo_box.item length="1" name="English" type="string" label="English (Anglais)"/> + <combo_box.item length="1" name="Danish" type="string" label="Dansk (Danois) - Bêta"/> + <combo_box.item length="1" name="Deutsch(German)" type="string" label="Deutsch (Allemand) - Bêta"/> + <combo_box.item name="Spanish" label="Español (Espagnol) - Bêta"/> + <combo_box.item name="French" label="Français - Bêta"/> + <combo_box.item name="Hungarian" label="Magyar (Hongrois) - Bêta"/> + <combo_box.item name="Polish" label="Polski (Polonais) - Bêta"/> + <combo_box.item name="Portugese" label="Portugués (Portugais) - Bêta"/> + <combo_box.item name="Russian" label="Русский (Russe) - Bêta"/> + <combo_box.item name="Ukrainian" label="Українська (Ukrainien) - Bêta"/> + <combo_box.item length="1" name="Chinese" type="string" label="中文 (简体) (Chinois) - Bêta"/> + <combo_box.item length="1" name="(Japanese)" type="string" label="日本語 (Japonais) - Bêta"/> + <combo_box.item length="1" name="(Korean)" type="string" label="한국어 (Coréen) - Bêta"/> <combo_item name="Italian"> Italiano (Italien) - Bêta </combo_item> - <combo_item name="Hungarian"> - Magyar (Hongrois) - Bêta - </combo_item> <combo_item name="Dutch"> Nederlands (Néerlandais) - Bêta </combo_item> - <combo_item name="Polish"> - Polski (Polonais) - Bêta - </combo_item> - <combo_item name="Portugese"> - Portugués (Portugais) - Bêta - </combo_item> - <combo_item name="Russian"> - Русский (Russe) - Bêta </combo_item> <combo_item name="Turkish"> Türkçe (Turc) - Bêta - </combo_item> - <combo_item name="Ukrainian"> - Українська (Ukrainien) - Bêta - </combo_item> - <combo_item length="1" name="Chinese" type="string"> - 中文 (简体) (Chinois) - Bêta - </combo_item> - <combo_item length="1" name="(Japanese)" type="string"> - 日本語 (Japonais) - Bêta - </combo_item> - <combo_item length="1" name="(Korean)" type="string"> - 한국어 (Coréen) - Bêta - </combo_item> </combo_box> <check_box label="Partager la langue avec les objets" name="language_is_public" tool_tip="Cette option permet de faire connaître aux objets du Monde votre langue favorite."/> </panel> diff --git a/indra/newview/skins/default/xui/it/floater_about_land.xml b/indra/newview/skins/default/xui/it/floater_about_land.xml index e4ad34bf50..4f57067cd2 100644 --- a/indra/newview/skins/default/xui/it/floater_about_land.xml +++ b/indra/newview/skins/default/xui/it/floater_about_land.xml @@ -345,42 +345,18 @@ Solamente terreni più grandi possono essere abilitati nella ricerca. </combo_item> </combo_box> <combo_box name="land category"> - <combo_item name="AnyCategory"> - Tutte le categorie - </combo_item> - <combo_item name="LindenLocation"> - Luogo dei Linden - </combo_item> - <combo_item name="Arts&Culture"> - Arte & Cultura - </combo_item> - <combo_item name="Business"> - Affari - </combo_item> - <combo_item name="Educational"> - Educazione - </combo_item> - <combo_item name="Gaming"> - Gioco - </combo_item> - <combo_item name="Hangout"> - Divertimento - </combo_item> - <combo_item name="NewcomerFriendly"> - Accoglienza nuovi residenti - </combo_item> - <combo_item name="Parks&Nature"> - Parchi & Natura - </combo_item> - <combo_item name="Residential"> - Residenziale - </combo_item> - <combo_item name="Shopping"> - Shopping - </combo_item> - <combo_item name="Other"> - Altro - </combo_item> + <combo_box.item name="AnyCategory" label="Tutte le categorie"/> + <combo_box.item name="LindenLocation" label="Luogo dei Linden"/> + <combo_box.item name="Arts&Culture" label="Arte & Cultura"/> + <combo_box.item name="Business" label="Affari"/> + <combo_box.item name="Educational" label="Educazione"/> + <combo_box.item name="Gaming" label="Gioco"/> + <combo_box.item name="Hangout" label="Divertimento"/> + <combo_box.item name="NewcomerFriendly" label="Accoglienza nuovi residenti"/> + <combo_box.item name="Parks&Nature" label="Parchi & Natura"/> + <combo_box.item name="Residential" label="Residenziale"/> + <combo_box.item name="Shopping" label="Shopping"/> + <combo_box.item name="Other" label="Altro"/> </combo_box> <button label="?" label_selected="?" name="?"/> <check_box label="Contenuto Mature" name="MatureCheck" tool_tip=" "/> @@ -412,15 +388,9 @@ Solamente terreni più grandi possono essere abilitati nella ricerca. Rotte dei teleport: </text> <combo_box width="140" name="landing type" tool_tip="Rotte dei teleport -- seleziona come vuoi organizzare i teleport nella tua terra."> - <combo_item name="Blocked"> - Bloccati - </combo_item> - <combo_item name="LandingPoint"> - Punto di atterraggio - </combo_item> - <combo_item name="Anywhere"> - Ovunque - </combo_item> + <combo_box.item name="Blocked" label="Bloccati"/> + <combo_box.item name="LandingPoint" label="Punto di atterraggio"/> + <combo_box.item name="Anywhere" label="Ovunque"/> </combo_box> <string name="push_restrict_text"> Nessuna spinta @@ -501,12 +471,8 @@ Media: <check_box label="Permetti accesso al gruppo: [GROUP]" name="GroupCheck" tool_tip="Imposta il gruppo nel pannello generale."/> <check_box label="Vendi pass a:" name="PassCheck" tool_tip="Permetti in questo terreno l'accesso temporaneo"/> <combo_box name="pass_combo"> - <combo_item name="Anyone"> - Chiunque - </combo_item> - <combo_item name="Group"> - Gruppo - </combo_item> + <combo_box.item name="Anyone" label="Chiunque"/> + <combo_box.item name="Group" label="Gruppo"/> </combo_box> <spinner label="Prezzo in L$:" name="PriceSpin"/> <spinner label="Ore di accesso:" name="HoursSpin"/> diff --git a/indra/newview/skins/default/xui/it/panel_preferences_general.xml b/indra/newview/skins/default/xui/it/panel_preferences_general.xml index aadd86d055..620cd33c0a 100644 --- a/indra/newview/skins/default/xui/it/panel_preferences_general.xml +++ b/indra/newview/skins/default/xui/it/panel_preferences_general.xml @@ -10,15 +10,9 @@ </radio_group> <check_box label="Mostra il punto di partenza nella schermata d'inizio" name="show_location_checkbox"/> <combo_box name="fade_out_combobox" width="166"> - <combo_item name="Never"> - Mai - </combo_item> - <combo_item name="Show Temporarily"> - Mostra temporanemente - </combo_item> - <combo_item name="Always"> - Sempre - </combo_item> + <combo_box.item name="Never" label="Mai"/> + <combo_box.item name="Show Temporarily" label="Mostra temporanemente"/> + <combo_box.item name="Always" label="Sempre"/> </combo_box> <check_box label="Nomi avatar in piccolo" name="small_avatar_names_checkbox"/> <check_box label="Nascondi il mio nome sul mio schermo" name="show_my_name_checkbox"/> @@ -80,50 +74,29 @@ contenuto di tipo: <Scrivi il nome della regione> </string> <combo_box name="crash_behavior_combobox" width="166"> - <combo_item name="Askbeforesending"> - Chiedi prima di inviare - </combo_item> - <combo_item name="Alwayssend"> - Invia sempre - </combo_item> - <combo_item name="Neversend"> - Non inviare mai - </combo_item> + <combo_box.item name="Askbeforesending" label="Chiedi prima di inviare"/> + <combo_box.item name="Alwayssend" label="Invia sempre"/> + <combo_box.item name="Neversend" label="Non inviare mai"/> </combo_box> <combo_box name="language_combobox" width="166"> - <combo_item name="System Default Language"> - Default di sistema - </combo_item> - <combo_item name="English"> - English - </combo_item> - <combo_item name="Danish"> - Dansk (Danese) - Beta - </combo_item> - <combo_item name="Deutsch(German)"> - Deutsch (Tedesco) - Beta - </combo_item> - <combo_item name="Spanish"> - Español (Spagnolo) - Beta - </combo_item> - <combo_item name="French"> - Français (Francese) - Beta - </combo_item> + <combo_box.item name="System Default Language" label="Default di sistema"/> + <combo_box.item name="English" label="English"/> + <combo_box.item name="Danish" label="Dansk (Danese) - Beta"/> + <combo_box.item name="Deutsch(German)" label="Deutsch (Tedesco) - Beta"/> + <combo_box.item name="Spanish" label="Español (Spagnolo) - Beta"/> + <combo_box.item name="French" label="Français (Francese) - Beta"/> + <combo_box.item name="Hungarian" label="Magyar (Ungherese) - Beta"/> + <combo_box.item name="Polish" label="Polski (Polacco) - Beta"/> + <combo_box.item name="Portugese" label="Portugués (Portoghese) - Beta"/> + <combo_box.item name="Chinese" label="中文 (简体) (Cinese) - Beta"/> + <combo_box.item name="(Japanese)" label="日本語 (Giapponese) - Beta"/> + <combo_box.item name="(Korean)" label="한국어 (Coreano) - Beta"/> <combo_item name="Italian"> Italiano - Beta </combo_item> - <combo_item name="Hungarian"> - Magyar (Ungherese) - Beta - </combo_item> <combo_item name="Dutch"> Nederlands (Olandese) - Beta </combo_item> - <combo_item name="Polish"> - Polski (Polacco) - Beta - </combo_item> - <combo_item name="Portugese"> - Portugués (Portoghese) - Beta - </combo_item> <combo_item name="Russian"> Русский (Russo) - Beta </combo_item> @@ -133,15 +106,6 @@ contenuto di tipo: <combo_item name="Ukrainian"> Українська (Ukraino) - Beta </combo_item> - <combo_item name="Chinese"> - 中文 (简体) (Cinese) - Beta - </combo_item> - <combo_item name="(Japanese)"> - 日本語 (Giapponese) - Beta - </combo_item> - <combo_item name="(Korean)"> - 한국어 (Coreano) - Beta - </combo_item> </combo_box> <check_box label="Condividi la tua lingua con gli oggetti" name="language_is_public" tool_tip="Questo fa in modo che gli oggetti inworld riconoscano la tua lingua."/> </panel> diff --git a/indra/newview/skins/default/xui/ja/floater_about_land.xml b/indra/newview/skins/default/xui/ja/floater_about_land.xml index c8195d8f38..26c8a46270 100644 --- a/indra/newview/skins/default/xui/ja/floater_about_land.xml +++ b/indra/newview/skins/default/xui/ja/floater_about_land.xml @@ -345,42 +345,18 @@ </combo_item> </combo_box> <combo_box name="land category"> - <combo_item name="AnyCategory"> - 全カテゴリー - </combo_item> - <combo_item name="LindenLocation"> - Linden所在地 - </combo_item> - <combo_item name="Arts&Culture"> - アート&カルチャー - </combo_item> - <combo_item name="Business"> - ビジネス - </combo_item> - <combo_item name="Educational"> - 教育的 - </combo_item> - <combo_item name="Gaming"> - ゲーム - </combo_item> - <combo_item name="Hangout"> - たまり場 - </combo_item> - <combo_item name="NewcomerFriendly"> - 新住人に好意的 - </combo_item> - <combo_item name="Parks&Nature"> - 公園と自然 - </combo_item> - <combo_item name="Residential"> - 住宅用 - </combo_item> - <combo_item name="Shopping"> - ショッピング - </combo_item> - <combo_item name="Other"> - その他 - </combo_item> + <combo_box.item name="AnyCategory" label="全カテゴリー"/> + <combo_box.item name="LindenLocation" label="Linden所在地"/> + <combo_box.item name="Arts&Culture" label="アート&カルチャー"/> + <combo_box.item name="Business" label="ビジネス"/> + <combo_box.item name="Educational" label="教育的"/> + <combo_box.item name="Gaming" label="ゲーム"/> + <combo_box.item name="Hangout" label="たまり場"/> + <combo_box.item name="NewcomerFriendly" label="新住人に好意的"/> + <combo_box.item name="Parks&Nature" label="公園と自然"/> + <combo_box.item name="Residential" label="住宅用"/> + <combo_box.item name="Shopping" label="ショッピング"/> + <combo_box.item name="Other" label="その他"/> </combo_box> <button label="?" label_selected="?" name="?"/> <check_box label="Matureコンテンツ" name="MatureCheck" tool_tip=""/> @@ -412,15 +388,9 @@ テレポート制限: </text> <combo_box name="landing type" tool_tip="Teleport Routing -- select how to handle teleports onto your land."> - <combo_item length="1" name="Blocked" type="string"> - 不可 - </combo_item> - <combo_item length="1" name="LandingPoint" type="string"> - 着地点 - </combo_item> - <combo_item length="1" name="Anywhere" type="string"> - どこでも - </combo_item> + <combo_box.item length="1" name="Blocked" type="string" label="不可"/> + <combo_box.item length="1" name="LandingPoint" type="string" label="着地点"/> + <combo_box.item length="1" name="Anywhere" type="string" label="どこでも"/> </combo_box> <string name="push_restrict_text"> プッシングを制限 @@ -499,12 +469,8 @@ <check_box label="グループ・アクセスを許可:[GROUP]" name="GroupCheck" tool_tip="[一般]タブで、グループを選択してください。"/> <check_box label="入場許可を販売:" name="PassCheck" tool_tip="この区画への一時的なアクセスを許可"/> <combo_box name="pass_combo"> - <combo_item name="Anyone"> - 誰でも - </combo_item> - <combo_item name="Group"> - グループ - </combo_item> + <combo_box.item name="Anyone" label="誰でも"/> + <combo_box.item name="Group" label="グループ"/> </combo_box> <spinner label="価格(L$):" name="PriceSpin"/> <spinner label="アクセス時間:" name="HoursSpin"/> diff --git a/indra/newview/skins/default/xui/ja/panel_preferences_general.xml b/indra/newview/skins/default/xui/ja/panel_preferences_general.xml index 83b21b87d2..3e3d62640f 100644 --- a/indra/newview/skins/default/xui/ja/panel_preferences_general.xml +++ b/indra/newview/skins/default/xui/ja/panel_preferences_general.xml @@ -10,15 +10,9 @@ </radio_group> <check_box label="ログイン画面にログイン位置を表示" name="show_location_checkbox"/> <combo_box name="fade_out_combobox"> - <combo_item name="Never"> - なし - </combo_item> - <combo_item name="Show Temporarily"> - 一時的に表示 - </combo_item> - <combo_item name="Always"> - いつも - </combo_item> + <combo_box.item name="Never" label="なし"/> + <combo_box.item name="Show Temporarily" label="一時的に表示"/> + <combo_box.item name="Always" label="いつも"/> </combo_box> <check_box label="小さなアバター名" name="small_avatar_names_checkbox"/> <check_box label="画面上で自分の名前を隠す" name="show_my_name_checkbox"/> @@ -79,68 +73,34 @@ <地域の名前入力> </string> <combo_box name="crash_behavior_combobox"> - <combo_item length="1" name="Askbeforesending" type="string"> - 送る前に確認する - </combo_item> - <combo_item length="1" name="Alwayssend" type="string"> - 常に送信 - </combo_item> - <combo_item length="1" name="Neversend" type="string"> - 送信しない - </combo_item> + <combo_box.item length="1" name="Askbeforesending" type="string" label="送る前に確認する"/> + <combo_box.item length="1" name="Alwayssend" type="string" label="常に送信"/> + <combo_box.item length="1" name="Neversend" type="string" label="送信しない"/> </combo_box> <combo_box name="language_combobox"> - <combo_item length="1" name="System Default Language" type="string"> - システム・デフォルト - </combo_item> - <combo_item length="1" name="English" type="string"> - English (英語) - </combo_item> - <combo_item length="1" name="Danish" type="string"> - Dansk (デンマーク語) – ベータ - </combo_item> - <combo_item length="1" name="Deutsch(German)" type="string"> - Deutsch (ドイツ語) – ベータ - </combo_item> - <combo_item length="1" name="Spanish" type="string"> - Español (スペイン語) – ベータ - </combo_item> - <combo_item length="1" name="French" type="string"> - Français (フランス語) – ベータ - </combo_item> + <combo_box.item length="1" name="System Default Language" type="string" label="システム・デフォルト"/> + <combo_box.item length="1" name="English" type="string" label="English (英語)"/> + <combo_box.item length="1" name="Danish" type="string" label="Dansk (デンマーク語) – ベータ"/> + <combo_box.item length="1" name="Deutsch(German)" type="string" label="Deutsch (ドイツ語) – ベータ"/> + <combo_box.item length="1" name="Spanish" type="string" label="Español (スペイン語) – ベータ"/> + <combo_box.item length="1" name="French" type="string" label="Français (フランス語) – ベータ"/> + <combo_box.item name="Hungarian" label="Magyar (ハンガリー語) - ベータ"/> + <combo_box.item name="Polish" label="Polski (ポーランド語) - ベータ"/> + <combo_box.item length="1" name="Portugese" type="string" label="Português (ポルトガル語) – ベータ"/> + <combo_box.item name="Russian" label="Русский (ロシア語) - ベータ"/> + <combo_box.item name="Ukrainian" label="Українська (ウクライナ語) - ベータ"/> + <combo_box.item length="1" name="Chinese" type="string" label="中文 (简体) (中国語) - ベータ"/> + <combo_box.item length="1" name="(Japanese)" type="string" label="日本語 – ベータ"/> + <combo_box.item length="1" name="(Korean)" type="string" label="한국어 (韓国語) – ベータ"/> <combo_item name="Italian"> Italiano (イタリア語) - ベータ </combo_item> - <combo_item name="Hungarian"> - Magyar (ハンガリー語) - ベータ - </combo_item> <combo_item name="Dutch"> Nederlands (オランダ語) - ベータ </combo_item> - <combo_item name="Polish"> - Polski (ポーランド語) - ベータ - </combo_item> - <combo_item length="1" name="Portugese" type="string"> - Português (ポルトガル語) – ベータ - </combo_item> - <combo_item name="Russian"> - Русский (ロシア語) - ベータ </combo_item> <combo_item name="Turkish"> Türkçe (トルコ語) - ベータ - </combo_item> - <combo_item name="Ukrainian"> - Українська (ウクライナ語) - ベータ - </combo_item> - <combo_item length="1" name="Chinese" type="string"> - 中文 (简体) (中国語) - ベータ - </combo_item> - <combo_item length="1" name="(Japanese)" type="string"> - 日本語 – ベータ - </combo_item> - <combo_item length="1" name="(Korean)" type="string"> - 한국어 (韓国語) – ベータ - </combo_item> </combo_box> <check_box label="言語をオブジェクトと共有" name="language_is_public" tool_tip="優先言語をインワールドのオブジェクトが認識する"/> </panel> diff --git a/indra/newview/skins/default/xui/nl/floater_about_land.xml b/indra/newview/skins/default/xui/nl/floater_about_land.xml index a3e3b246fe..44c2ebccf7 100644 --- a/indra/newview/skins/default/xui/nl/floater_about_land.xml +++ b/indra/newview/skins/default/xui/nl/floater_about_land.xml @@ -343,42 +343,18 @@ of opgedeeld. </combo_item> </combo_box> <combo_box name="land category"> - <combo_item name="AnyCategory"> - Alle categorieën - </combo_item> - <combo_item name="LindenLocation"> - Linden Locatie - </combo_item> - <combo_item name="Arts&Culture"> - Kunst & Cultuur - </combo_item> - <combo_item name="Business"> - Zakelijk - </combo_item> - <combo_item name="Educational"> - Educatief - </combo_item> - <combo_item name="Gaming"> - Spelen - </combo_item> - <combo_item name="Hangout"> - Ontmoetingsplaats - </combo_item> - <combo_item name="NewcomerFriendly"> - Nieuwkomervriendelijk - </combo_item> - <combo_item name="Parks&Nature"> - Parken & Natuur - </combo_item> - <combo_item name="Residential"> - Woongebied - </combo_item> - <combo_item name="Shopping"> - Winkelen - </combo_item> - <combo_item name="Other"> - Anders - </combo_item> + <combo_box.item name="AnyCategory" label="Alle categorieën"/> + <combo_box.item name="LindenLocation" label="Linden Locatie"/> + <combo_box.item name="Arts&Culture" label="Kunst & Cultuur"/> + <combo_box.item name="Business" label="Zakelijk"/> + <combo_box.item name="Educational" label="Educatief"/> + <combo_box.item name="Gaming" label="Spelen"/> + <combo_box.item name="Hangout" label="Ontmoetingsplaats"/> + <combo_box.item name="NewcomerFriendly" label="Nieuwkomervriendelijk"/> + <combo_box.item name="Parks&Nature" label="Parken & Natuur"/> + <combo_box.item name="Residential" label="Woongebied"/> + <combo_box.item name="Shopping" label="Winkelen"/> + <combo_box.item name="Other" label="Anders"/> </combo_box> <button label="?" label_selected="?" name="?"/> <check_box label="Mature inhoud" name="MatureCheck" tool_tip=" "/> @@ -410,15 +386,9 @@ of opgedeeld. Teleport routering: </text> <combo_box name="landing type" tool_tip="Teleport routering -- selecteer hoe teleports naar uw land moeten worden afgehandeld."> - <combo_item name="Blocked"> - Geblokkeerd - </combo_item> - <combo_item name="LandingPoint"> - Landingsplaats - </combo_item> - <combo_item name="Anywhere"> - Overal - </combo_item> + <combo_box.item name="Blocked" label="Geblokkeerd"/> + <combo_box.item name="LandingPoint" label="Landingsplaats"/> + <combo_box.item name="Anywhere" label="Overal"/> </combo_box> <string name="push_restrict_text"> Niet duwen @@ -496,12 +466,8 @@ hebt geklikt.) <check_box label="Groepstoegang toestaan: [GROUP]" name="GroupCheck" tool_tip="Stel de groep in op het tabblad Algemeen"/> <check_box label="Verkoop toegangspassen aan:" name="PassCheck" tool_tip="Tijdelijke toegang tot dit perceel toestaan"/> <combo_box name="pass_combo" left_delta="190"> - <combo_item name="Anyone"> - Iedereen - </combo_item> - <combo_item name="Group"> - Groep - </combo_item> + <combo_box.item name="Anyone" label="Iedereen"/> + <combo_box.item name="Group" label="Groep"/> </combo_box> <spinner label="Prijs in L$:" name="PriceSpin" label_width="168" width="228" /> <spinner label="Toegangsuren" name="HoursSpin" label_width="168" width="228" /> diff --git a/indra/newview/skins/default/xui/pl/floater_about_land.xml b/indra/newview/skins/default/xui/pl/floater_about_land.xml index 5f4bf4ece6..7612212105 100755 --- a/indra/newview/skins/default/xui/pl/floater_about_land.xml +++ b/indra/newview/skins/default/xui/pl/floater_about_land.xml @@ -343,42 +343,18 @@ Jedynie większe posiadłości mogą być umieszczone w bazie wyszukiwarki. </combo_item> </combo_box> <combo_box name="land category"> - <combo_item name="AnyCategory"> - Każda - </combo_item> - <combo_item name="LindenLocation"> - Linden Lokacja - </combo_item> - <combo_item name="Arts&Culture"> - Sztuka i Kultura - </combo_item> - <combo_item name="Business"> - Biznes - </combo_item> - <combo_item name="Educational"> - Edukacyjna - </combo_item> - <combo_item name="Gaming"> - Gra - </combo_item> - <combo_item name="Hangout"> - Poznawanie ludzi - </combo_item> - <combo_item name="NewcomerFriendly"> - Przyjazna dla nowych - </combo_item> - <combo_item name="Parks&Nature"> - Parki i Natura - </combo_item> - <combo_item name="Residential"> - Mieszkalna - </combo_item> - <combo_item name="Shopping"> - Komercja/Sklepy - </combo_item> - <combo_item name="Other"> - Inna - </combo_item> + <combo_box.item name="AnyCategory" label="Każda"/> + <combo_box.item name="LindenLocation" label="Linden Lokacja"/> + <combo_box.item name="Arts&Culture" label="Sztuka i Kultura"/> + <combo_box.item name="Business" label="Biznes"/> + <combo_box.item name="Educational" label="Edukacyjna"/> + <combo_box.item name="Gaming" label="Gra"/> + <combo_box.item name="Hangout" label="Poznawanie ludzi"/> + <combo_box.item name="NewcomerFriendly" label="Przyjazna dla nowych"/> + <combo_box.item name="Parks&Nature" label="Parki i Natura"/> + <combo_box.item name="Residential" label="Mieszkalna"/> + <combo_box.item name="Shopping" label="Komercja/Sklepy"/> + <combo_box.item name="Other" label="Inna"/> </combo_box> <button label="?" label_selected="?" name="?"/> <check_box label="Treść 'Mature'" name="MatureCheck" tool_tip=""/> @@ -410,15 +386,9 @@ Jedynie większe posiadłości mogą być umieszczone w bazie wyszukiwarki. Trasa Teleportacji: </text> <combo_box name="landing type" tool_tip="Trasa Teleportacj-ustaw w jaki sposób będzie sę odbywać proces telportacji w posiadłości."> - <combo_item length="1" name="Blocked" type="string"> - Zablokowana - </combo_item> - <combo_item length="1" name="LandingPoint" type="string"> - Punkt Lądowania - </combo_item> - <combo_item length="1" name="Anywhere" type="string"> - Gdziekolwiek - </combo_item> + <combo_box.item length="1" name="Blocked" type="string" label="Zablokowana"/> + <combo_box.item length="1" name="LandingPoint" type="string" label="Punkt Lądowania"/> + <combo_box.item length="1" name="Anywhere" type="string" label="Gdziekolwiek"/> </combo_box> <string name="push_restrict_text"> Popychanie niedozwolone @@ -497,12 +467,8 @@ Mediów: <check_box label="Udostępnij wejście grupie: [GROUP]" name="GroupCheck" tool_tip="Ustaw grupę w głównej zakładce"/> <check_box label="Sprzedaj wejściówki:" name="PassCheck" tool_tip="Otwórz tymczasowy dostęp do tej posiadłości"/> <combo_box name="pass_combo"> - <combo_item name="Anyone"> - Każdemu - </combo_item> - <combo_item name="Group"> - Grupie - </combo_item> + <combo_box.item name="Anyone" label="Każdemu"/> + <combo_box.item name="Group" label="Grupie"/> </combo_box> <spinner label="Cena w L$:" name="PriceSpin"/> <spinner label="Ilość godzin dostępu:" name="HoursSpin"/> diff --git a/indra/newview/skins/default/xui/pt/floater_about_land.xml b/indra/newview/skins/default/xui/pt/floater_about_land.xml index 4426378075..fee7ed60f5 100644 --- a/indra/newview/skins/default/xui/pt/floater_about_land.xml +++ b/indra/newview/skins/default/xui/pt/floater_about_land.xml @@ -343,42 +343,18 @@ Apenas lotes maiores podem ser listados na busca. </combo_item> </combo_box> <combo_box left="276" name="land category" width="146"> - <combo_item name="AnyCategory"> - Qualquer Categoria - </combo_item> - <combo_item name="LindenLocation"> - Locação Linden - </combo_item> - <combo_item name="Arts&Culture"> - Artes e Cultura - </combo_item> - <combo_item name="Business"> - Negócios - </combo_item> - <combo_item name="Educational"> - Educacional - </combo_item> - <combo_item name="Gaming"> - Jogo - </combo_item> - <combo_item name="Hangout"> - Lazer - </combo_item> - <combo_item name="NewcomerFriendly"> - Amigável para novatos - </combo_item> - <combo_item name="Parks&Nature"> - Parques e Natureza - </combo_item> - <combo_item name="Residential"> - Residencial - </combo_item> - <combo_item name="Shopping"> - Compras - </combo_item> - <combo_item name="Other"> - Outros - </combo_item> + <combo_box.item name="AnyCategory" label="Qualquer Categoria"/> + <combo_box.item name="LindenLocation" label="Locação Linden"/> + <combo_box.item name="Arts&Culture" label="Artes e Cultura"/> + <combo_box.item name="Business" label="Negócios"/> + <combo_box.item name="Educational" label="Educacional"/> + <combo_box.item name="Gaming" label="Jogo"/> + <combo_box.item name="Hangout" label="Lazer"/> + <combo_box.item name="NewcomerFriendly" label="Amigável para novatos"/> + <combo_box.item name="Parks&Nature" label="Parques e Natureza"/> + <combo_box.item name="Residential" label="Residencial"/> + <combo_box.item name="Shopping" label="Compras"/> + <combo_box.item name="Other" label="Outros"/> </combo_box> <button label="?" label_selected="?" left="426" name="?"/> <check_box label="Conteúdo Mature" name="MatureCheck" tool_tip=""/> @@ -410,15 +386,9 @@ Apenas lotes maiores podem ser listados na busca. Rota de Tele-transporte: </text> <combo_box left="140" name="landing type" tool_tip="Rota de Teletransporte -- Selecione como tratar os tele-transportes no seu lote." width="140"> - <combo_item length="1" name="Blocked" type="string"> - Bloqueado - </combo_item> - <combo_item length="1" name="LandingPoint" type="string"> - Ponto de Aterrissagem - </combo_item> - <combo_item length="1" name="Anywhere" type="string"> - Qualquer lugar - </combo_item> + <combo_box.item length="1" name="Blocked" type="string" label="Bloqueado"/> + <combo_box.item length="1" name="LandingPoint" type="string" label="Ponto de Aterrissagem"/> + <combo_box.item length="1" name="Anywhere" type="string" label="Qualquer lugar"/> </combo_box> <string name="push_restrict_text"> Sem Empurrar @@ -499,12 +469,8 @@ Mídia: <check_box label="Permitir Acesso do Grupo: [GROUP]" name="GroupCheck" tool_tip="Definir grupo na aba Geral."/> <check_box label="Vender passes para:" name="PassCheck" tool_tip="Permite acesso temporário a este terreno"/> <combo_box name="pass_combo"> - <combo_item name="Anyone"> - Qualquer um - </combo_item> - <combo_item name="Group"> - Grupo - </combo_item> + <combo_box.item name="Anyone" label="Qualquer um"/> + <combo_box.item name="Group" label="Grupo"/> </combo_box> <spinner label="Preço em L$:" name="PriceSpin"/> <spinner label="Horas de Acesso:" name="HoursSpin"/> diff --git a/indra/newview/skins/default/xui/pt/panel_preferences_general.xml b/indra/newview/skins/default/xui/pt/panel_preferences_general.xml index fde3551b8b..d82330cc78 100644 --- a/indra/newview/skins/default/xui/pt/panel_preferences_general.xml +++ b/indra/newview/skins/default/xui/pt/panel_preferences_general.xml @@ -10,15 +10,9 @@ </radio_group> <check_box label="Mostrar Posição Inicial na Tela de Login" name="show_location_checkbox"/> <combo_box name="fade_out_combobox" width="166"> - <combo_item name="Never"> - Nunca - </combo_item> - <combo_item name="Show Temporarily"> - Mostrar Temporariamente - </combo_item> - <combo_item name="Always"> - Sempre - </combo_item> + <combo_box.item name="Never" label="Nunca"/> + <combo_box.item name="Show Temporarily" label="Mostrar Temporariamente"/> + <combo_box.item name="Always" label="Sempre"/> </combo_box> <check_box label="Avatar com Nomes Pequenos" name="small_avatar_names_checkbox"/> <check_box label="Ocultar meu Nome na minha Tela" name="show_my_name_checkbox"/> @@ -80,49 +74,29 @@ conteúdo classificado: Digite o nome da Região </string> <combo_box name="crash_behavior_combobox" width="166"> - <combo_item length="1" name="Askbeforesending" type="string"> - Perguntar antes de enviar - </combo_item> - <combo_item length="1" name="Alwayssend" type="string"> - Sempre enviar - </combo_item> - <combo_item length="1" name="Neversend" type="string"> - Nunca Enviar - </combo_item> + <combo_box.item length="1" name="Askbeforesending" type="string" label="Perguntar antes de enviar"/> + <combo_box.item length="1" name="Alwayssend" type="string" label="Sempre enviar"/> + <combo_box.item length="1" name="Neversend" type="string" label="Nunca Enviar"/> </combo_box> <combo_box name="language_combobox" width="166"> - <combo_item name="System Default Language"> - Padrão do Sistema - </combo_item> - <combo_item name="English"> - English (Inglês) - </combo_item> - <combo_item name="Danish"> - Dansk (Dinamarquês) - Beta - </combo_item> - <combo_item name="Deutsch(German)"> - Deutsch (Alemão) - Beta - </combo_item> - <combo_item name="Spanish"> - Español (Espanhol) - Beta - </combo_item> - <combo_item name="French"> - Français (Francês) - Beta - </combo_item> + <combo_box.item name="System Default Language" label="Padrão do Sistema"/> + <combo_box.item name="English" label="English (Inglês)"/> + <combo_box.item name="Danish" label="Dansk (Dinamarquês) - Beta"/> + <combo_box.item name="Deutsch(German)" label="Deutsch (Alemão) - Beta"/> + <combo_box.item name="Spanish" label="Español (Espanhol) - Beta"/> + <combo_box.item name="French" label="Français (Francês) - Beta"/> + <combo_box.item name="Hungarian" label="Magyar (Húngaro) - Beta"/> + <combo_box.item name="Polish" label="Polski (Polonês) - Beta"/> + <combo_box.item name="Portugese" label="Português - Beta"/> + <combo_box.item name="Chinese" label="中文 (简体) (Chinês) - Beta"/> + <combo_box.item name="(Japanese)" label="日本語 (Japonês) - Beta"/> + <combo_box.item name="(Korean)" label="한국어 (Coreano) - Beta"/> <combo_item name="Italian"> Italiano - Beta </combo_item> - <combo_item name="Hungarian"> - Magyar (Húngaro) - Beta - </combo_item> <combo_item name="Dutch"> Nederlands (Holandês) - Beta </combo_item> - <combo_item name="Polish"> - Polski (Polonês) - Beta - </combo_item> - <combo_item name="Portugese"> - Português - Beta </combo_item> <combo_item name="Russian"> Русский (Russo) - Beta @@ -132,16 +106,6 @@ conteúdo classificado: </combo_item> <combo_item name="Ukrainian"> Українська (Ucraniano) - Beta - </combo_item> - <combo_item name="Chinese"> - 中文 (简体) (Chinês) - Beta - </combo_item> - <combo_item name="(Japanese)"> - 日本語 (Japonês) - Beta - </combo_item> - <combo_item name="(Korean)"> - 한국어 (Coreano) - Beta - </combo_item> </combo_box> <check_box label="Compartilhar a linguagem com objetos" name="language_is_public" tool_tip="Isto permite que os objetos no mundo conheçam sua linguagem preferida."/> </panel> diff --git a/indra/newview/tests/llagentaccess_test.cpp b/indra/newview/tests/llagentaccess_test.cpp index 42872d85fb..e08193f785 100644 --- a/indra/newview/tests/llagentaccess_test.cpp +++ b/indra/newview/tests/llagentaccess_test.cpp @@ -29,6 +29,8 @@ * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ + +#include "linden_common.h" #include "../test/lltut.h" #include "../llagentaccess.h" diff --git a/indra/newview/tests/llcapabilitylistener_test.cpp b/indra/newview/tests/llcapabilitylistener_test.cpp index f66ae9404f..b965379c9c 100644 --- a/indra/newview/tests/llcapabilitylistener_test.cpp +++ b/indra/newview/tests/llcapabilitylistener_test.cpp @@ -24,9 +24,9 @@ #include "../test/lltut.h" #include "../llcapabilityprovider.h" #include "lluuid.h" -#include "llerrorcontrol.h" #include "tests/networkio.h" #include "tests/commtest.h" +#include "tests/wrapllerrs.h" #include "stringize.h" #if defined(LL_WINDOWS) @@ -104,28 +104,6 @@ namespace tut typedef llcapears_group::object llcapears_object; llcapears_group llsdmgr("llcapabilitylistener"); - struct CaptureError: public LLError::OverrideFatalFunction - { - CaptureError(): - LLError::OverrideFatalFunction(boost::bind(&CaptureError::operator(), this, _1)) - { - LLError::setPrintLocation(false); - } - - struct FatalException: public std::runtime_error - { - FatalException(const std::string& what): std::runtime_error(what) {} - }; - - void operator()(const std::string& message) - { - error = message; - throw FatalException(message); - } - - std::string error; - }; - template<> template<> void llcapears_object::test<1>() { @@ -137,10 +115,10 @@ namespace tut std::string threw; try { - CaptureError capture; + WrapLL_ERRS capture; regionPump.post(request); } - catch (const CaptureError::FatalException& e) + catch (const WrapLL_ERRS::FatalException& e) { threw = e.what(); } @@ -184,10 +162,10 @@ namespace tut std::string threw; try { - CaptureError capture; + WrapLL_ERRS capture; regionPump.post(request); } - catch (const CaptureError::FatalException& e) + catch (const WrapLL_ERRS::FatalException& e) { threw = e.what(); } @@ -246,10 +224,10 @@ namespace tut std::string threw; try { - CaptureError capture; + WrapLL_ERRS capture; regionPump.post(request); } - catch (const CaptureError::FatalException& e) + catch (const WrapLL_ERRS::FatalException& e) { threw = e.what(); } diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp new file mode 100644 index 0000000000..a84e796159 --- /dev/null +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -0,0 +1,416 @@ +/**
+ * @file lllogininstance_test.cpp
+ * @brief Test for lllogininstance.cpp.
+ *
+ * $LicenseInfo:firstyear=2008&license=internal$
+ * Copyright (c) 2008, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "../llviewerprecompiledheaders.h"
+// Own header
+#include "../lllogininstance.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "../test/lltut.h"
+#include "llevents.h"
+
+#if defined(LL_WINDOWS)
+#pragma warning(disable: 4355) // using 'this' in base-class ctor initializer expr
+#endif
+
+// Constants
+const std::string VIEWERLOGIN_URI("viewerlogin_uri");
+const std::string VIEWERLOGIN_GRIDLABEL("viewerlogin_grid");
+
+const std::string APPVIEWER_SERIALNUMBER("appviewer_serialno");
+
+// Link seams.
+
+//-----------------------------------------------------------------------------
+static LLEventStream gTestPump("test_pump");
+
+#include "lllogin.h"
+static std::string gLoginURI;
+static LLSD gLoginCreds;
+static bool gDisconnectCalled = false;
+class LLLogin::Impl
+{
+};
+LLLogin::LLLogin() {}
+LLLogin::~LLLogin() {}
+LLEventPump& LLLogin::getEventPump() { return gTestPump; }
+void LLLogin::connect(const std::string& uri, const LLSD& credentials)
+{
+ gLoginURI = uri;
+ gLoginCreds = credentials;
+}
+
+void LLLogin::disconnect()
+{
+ gDisconnectCalled = true;
+}
+
+//-----------------------------------------------------------------------------
+#include "../llviewernetwork.h"
+unsigned char gMACAddress[MAC_ADDRESS_BYTES] = {'1','2','3','4','5','6'}; /* Flawfinder: ignore */
+
+LLViewerLogin::LLViewerLogin() {}
+LLViewerLogin::~LLViewerLogin() {}
+void LLViewerLogin::getLoginURIs(std::vector<std::string>& uris) const
+{
+ uris.push_back(VIEWERLOGIN_URI);
+}
+std::string LLViewerLogin::getGridLabel() const { return VIEWERLOGIN_GRIDLABEL; }
+
+//-----------------------------------------------------------------------------
+#include "../llviewercontrol.h"
+LLControlGroup gSavedSettings("Global");
+std::string gCurrentVersion = "invalid_version";
+
+LLControlGroup::LLControlGroup(const std::string& name) :
+ LLInstanceTracker<LLControlGroup, std::string>(name){}
+LLControlGroup::~LLControlGroup() {}
+void LLControlGroup::setBOOL(const std::string& name, BOOL val) {}
+BOOL LLControlGroup::getBOOL(const std::string& name) { return FALSE; }
+U32 LLControlGroup::saveToFile(const std::string& filename, BOOL nondefault_only) { return 1; }
+void LLControlGroup::setString(const std::string& name, const std::string& val) {}
+std::string LLControlGroup::getString(const std::string& name) { return "test_string"; }
+BOOL LLControlGroup::declareBOOL(const std::string& name, BOOL initial_val, const std::string& comment, BOOL persist) { return TRUE; }
+BOOL LLControlGroup::declareString(const std::string& name, const std::string &initial_val, const std::string& comment, BOOL persist) { return TRUE; }
+
+#include "lluicolortable.h"
+void LLUIColorTable::saveUserSettings(void)const {}
+
+//-----------------------------------------------------------------------------
+#include "../llurlsimstring.h"
+LLURLSimString LLURLSimString::sInstance;
+bool LLURLSimString::parse() { return true; }
+
+//-----------------------------------------------------------------------------
+#include "../llfloatertos.h"
+#include "llfloaterreg.h"
+static std::string gTOSType;
+static LLEventPump * gTOSReplyPump = NULL;
+
+//static
+LLFloater* LLFloaterReg::showInstance(const std::string& name, const LLSD& key, BOOL focus)
+{
+ gTOSType = name;
+ gTOSReplyPump = &LLEventPumps::instance().obtain(key["reply_pump"]);
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// LLNotifications
+class MockNotifications : public LLNotificationsInterface
+{
+ boost::function<void (const LLSD&, const LLSD&)> mResponder;
+ int mAddedCount;
+
+public:
+ MockNotifications() :
+ mResponder(0),
+ mAddedCount(0)
+ {
+ }
+
+ virtual ~MockNotifications() {}
+
+ /* virtual */ LLNotificationPtr add(
+ const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ LLNotificationFunctorRegistry::ResponseFunctor functor)
+ {
+ mResponder = functor;
+ mAddedCount++;
+ return LLNotificationPtr((LLNotification*)NULL);
+ }
+
+ void sendYesResponse()
+ {
+ LLSD notification;
+ LLSD response;
+ response = 1;
+ mResponder(notification, response);
+ }
+
+ void sendNoResponse()
+ {
+ LLSD notification;
+ LLSD response;
+ response = 2;
+ mResponder(notification, response);
+ }
+
+ void sendBogusResponse()
+ {
+ LLSD notification;
+ LLSD response;
+ response = 666;
+ mResponder(notification, response);
+ }
+
+ int addedCount() { return mAddedCount; }
+};
+
+S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response)
+{
+ return response.asInteger();
+}
+
+// misc
+std::string xml_escape_string(const std::string& in)
+{
+ return in;
+}
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct lllogininstance_data
+ {
+ lllogininstance_data() : logininstance(LLLoginInstance::getInstance())
+ {
+ // Global initialization
+ gLoginURI.clear();
+ gLoginCreds.clear();
+ gDisconnectCalled = false;
+
+ gTOSType = ""; // Set to invalid value.
+ gTOSReplyPump = 0; // clear the callback.
+
+
+ gSavedSettings.declareBOOL("NoInventoryLibrary", FALSE, "", FALSE);
+ gSavedSettings.declareBOOL("ConnectAsGod", FALSE, "", FALSE);
+ gSavedSettings.declareBOOL("UseDebugMenus", FALSE, "", FALSE);
+ gSavedSettings.declareBOOL("ForceMandatoryUpdate", FALSE, "", FALSE);
+ gSavedSettings.declareString("ClientSettingsFile", "test_settings.xml", "", FALSE);
+ gSavedSettings.declareString("VersionChannelName", "test_version_string", "", FALSE);
+ gSavedSettings.declareString("NextLoginLocation", "", "", FALSE);
+ gSavedSettings.declareBOOL("LoginLastLocation", FALSE, "", FALSE);
+
+ credentials["first"] = "testfirst";
+ credentials["last"] = "testlast";
+ credentials["passwd"] = "testpass";
+
+ logininstance->setNotificationsInterface(¬ifications);
+ }
+
+ LLLoginInstance* logininstance;
+ LLSD credentials;
+ MockNotifications notifications;
+ };
+
+ typedef test_group<lllogininstance_data> lllogininstance_group;
+ typedef lllogininstance_group::object lllogininstance_object;
+ lllogininstance_group llsdmgr("lllogininstance");
+
+ template<> template<>
+ void lllogininstance_object::test<1>()
+ {
+ set_test_name("Test Simple Success And Disconnect");
+
+ // Test default connect.
+ logininstance->connect(credentials);
+
+ ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI);
+
+ // Dummy success response.
+ LLSD response;
+ response["state"] = "online";
+ response["progress"] = 1.0;
+ response["transfer_rate"] = 7;
+ response["data"] = "test_data";
+
+ gTestPump.post(response);
+
+ ensure("Success response", logininstance->authSuccess());
+ ensure_equals("Test Response Data", logininstance->getResponse().asString(), "test_data");
+
+ logininstance->disconnect();
+
+ ensure_equals("Called Login Module Disconnect", gDisconnectCalled, true);
+
+ response.clear();
+ response["state"] = "offline";
+ response["progress"] = 0.0;
+ response["transfer_rate"] = 0;
+ response["data"] = "test_data";
+
+ gTestPump.post(response);
+
+ ensure("Disconnected", !(logininstance->authSuccess()));
+ }
+
+ template<> template<>
+ void lllogininstance_object::test<2>()
+ {
+ set_test_name("Test User TOS/Critical message Interaction");
+
+ const std::string test_uri = "testing-uri";
+
+ // Test default connect.
+ logininstance->connect(test_uri, credentials);
+
+ // connect should call LLLogin::connect to init gLoginURI and gLoginCreds.
+ ensure_equals("Default connect uri", gLoginURI, "testing-uri");
+ ensure_equals("Default for agree to tos", gLoginCreds["params"]["agree_to_tos"].asBoolean(), false);
+ ensure_equals("Default for read critical", gLoginCreds["params"]["read_critical"].asBoolean(), false);
+
+ // TOS failure response.
+ LLSD response;
+ response["state"] = "offline";
+ response["progress"] = 0.0;
+ response["transfer_rate"] = 7;
+ response["data"]["reason"] = "tos";
+ gTestPump.post(response);
+
+ ensure_equals("TOS Dialog type", gTOSType, "message_tos");
+ ensure("TOS callback given", gTOSReplyPump != 0);
+ gTOSReplyPump->post(false); // Call callback denying TOS.
+ ensure("No TOS, failed auth", logininstance->authFailure());
+
+ // Start again.
+ logininstance->connect(test_uri, credentials);
+ gTestPump.post(response); // Fail for tos again.
+ gTOSReplyPump->post(true); // Accept tos, should reconnect w/ agree_to_tos.
+ ensure_equals("Accepted agree to tos", gLoginCreds["params"]["agree_to_tos"].asBoolean(), true);
+ ensure("Incomplete login status", !logininstance->authFailure() && !logininstance->authSuccess());
+
+ // Fail connection, attempt connect again.
+ // The new request should have reset agree to tos to default.
+ response["data"]["reason"] = "key"; // bad creds.
+ gTestPump.post(response);
+ ensure("TOS auth failure", logininstance->authFailure());
+
+ logininstance->connect(test_uri, credentials);
+ ensure_equals("Reset to default for agree to tos", gLoginCreds["params"]["agree_to_tos"].asBoolean(), false);
+
+ // Critical Message failure response.
+ logininstance->connect(test_uri, credentials);
+ response["data"]["reason"] = "critical"; // Change response to "critical message"
+ gTestPump.post(response);
+
+ ensure_equals("TOS Dialog type", gTOSType, "message_critical");
+ ensure("TOS callback given", gTOSReplyPump != 0);
+ gTOSReplyPump->post(true);
+ ensure_equals("Accepted read critical message", gLoginCreds["params"]["read_critical"].asBoolean(), true);
+ ensure("Incomplete login status", !logininstance->authFailure() && !logininstance->authSuccess());
+
+ // Fail then attempt new connection
+ response["data"]["reason"] = "key"; // bad creds.
+ gTestPump.post(response);
+ ensure("TOS auth failure", logininstance->authFailure());
+ logininstance->connect(test_uri, credentials);
+ ensure_equals("Default for agree to tos", gLoginCreds["params"]["read_critical"].asBoolean(), false);
+ }
+
+ template<> template<>
+ void lllogininstance_object::test<3>()
+ {
+ set_test_name("Test Mandatory Update User Accepts");
+
+ // Part 1 - Mandatory Update, with User accepts response.
+ // Test connect with update needed.
+ logininstance->connect(credentials);
+
+ ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI);
+
+ // Update needed failure response.
+ LLSD response;
+ response["state"] = "offline";
+ response["progress"] = 0.0;
+ response["transfer_rate"] = 7;
+ response["data"]["reason"] = "update";
+ gTestPump.post(response);
+
+ ensure_equals("Notification added", notifications.addedCount(), 1);
+
+ notifications.sendYesResponse();
+
+ ensure("Disconnected", !(logininstance->authSuccess()));
+ }
+
+ template<> template<>
+ void lllogininstance_object::test<4>()
+ {
+ set_test_name("Test Mandatory Update User Decline");
+
+ // Test connect with update needed.
+ logininstance->connect(credentials);
+
+ ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI);
+
+ // Update needed failure response.
+ LLSD response;
+ response["state"] = "offline";
+ response["progress"] = 0.0;
+ response["transfer_rate"] = 7;
+ response["data"]["reason"] = "update";
+ gTestPump.post(response);
+
+ ensure_equals("Notification added", notifications.addedCount(), 1);
+ notifications.sendNoResponse();
+
+ ensure("Disconnected", !(logininstance->authSuccess()));
+ }
+
+ template<> template<>
+ void lllogininstance_object::test<6>()
+ {
+ set_test_name("Test Optional Update User Accept");
+
+ // Part 3 - Mandatory Update, with bogus response.
+ // Test connect with update needed.
+ logininstance->connect(credentials);
+
+ ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI);
+
+ // Update needed failure response.
+ LLSD response;
+ response["state"] = "offline";
+ response["progress"] = 0.0;
+ response["transfer_rate"] = 7;
+ response["data"]["reason"] = "optional";
+ gTestPump.post(response);
+
+ ensure_equals("Notification added", notifications.addedCount(), 1);
+ notifications.sendYesResponse();
+
+ ensure("Disconnected", !(logininstance->authSuccess()));
+ }
+
+ template<> template<>
+ void lllogininstance_object::test<7>()
+ {
+ set_test_name("Test Optional Update User Denies");
+
+ // Part 3 - Mandatory Update, with bogus response.
+ // Test connect with update needed.
+ logininstance->connect(credentials);
+
+ ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI);
+
+ // Update needed failure response.
+ LLSD response;
+ response["state"] = "offline";
+ response["progress"] = 0.0;
+ response["transfer_rate"] = 7;
+ response["data"]["reason"] = "optional";
+ gTestPump.post(response);
+
+ ensure_equals("Notification added", notifications.addedCount(), 1);
+ notifications.sendNoResponse();
+
+ // User skips, should be reconnecting.
+ ensure_equals("reconnect uri", gLoginURI, VIEWERLOGIN_URI);
+ ensure_equals("skipping optional update", gLoginCreds["params"]["skipoptional"].asBoolean(), true);
+ }
+}
diff --git a/indra/newview/tests/llxmlrpclistener_test.cpp b/indra/newview/tests/llxmlrpclistener_test.cpp new file mode 100644 index 0000000000..c94ba0a3e8 --- /dev/null +++ b/indra/newview/tests/llxmlrpclistener_test.cpp @@ -0,0 +1,230 @@ +/* + * @file llxmlrpclistener_test.cpp + * @author Nat Goodspeed + * @date 2009-03-20 + * @brief Test for llxmlrpclistener. + * + * $LicenseInfo:firstyear=2009&license=internal$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "../llviewerprecompiledheaders.h" +// associated header +#include "../llxmlrpclistener.h" +// STL headers +#include <iomanip> +// std headers +// external library headers +// other Linden headers +#include "../test/lltut.h" +#include "../llxmlrpctransaction.h" +#include "llevents.h" +#include "lleventfilter.h" +#include "llsd.h" +#include "llcontrol.h" +#include "tests/wrapllerrs.h" + +LLControlGroup gSavedSettings("Global"); + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct data + { + data(): + pumps(LLEventPumps::instance()), + uri("http://127.0.0.1:8000") + { + // These variables are required by machinery used by + // LLXMLRPCTransaction. The values reflect reality for this test + // executable; hopefully these values are correct. + gSavedSettings.declareBOOL("BrowserProxyEnabled", FALSE, "", FALSE); // don't persist + gSavedSettings.declareBOOL("NoVerifySSLCert", TRUE, "", FALSE); // don't persist + } + + // LLEventPump listener signature + bool captureReply(const LLSD& r) + { + reply = r; + return false; + } + + LLSD reply; + LLEventPumps& pumps; + std::string uri; + }; + typedef test_group<data> llxmlrpclistener_group; + typedef llxmlrpclistener_group::object object; + llxmlrpclistener_group llxmlrpclistenergrp("llxmlrpclistener"); + + template<> template<> + void object::test<1>() + { + set_test_name("request validation"); + WrapLL_ERRS capture; + LLSD request; + request["uri"] = uri; + std::string threw; + try + { + pumps.obtain("LLXMLRPCTransaction").post(request); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("threw exception", threw, "missing params"); + ensure_contains("identified missing", threw, "method"); + ensure_contains("identified missing", threw, "reply"); + } + + template<> template<> + void object::test<2>() + { + set_test_name("param types validation"); + WrapLL_ERRS capture; + LLSD request; + request["uri"] = uri; + request["method"] = "hello"; + request["reply"] = "reply"; + LLSD& params(request["params"]); + params["who"]["specifically"] = "world"; // LLXMLRPCListener only handles scalar params + std::string threw; + try + { + pumps.obtain("LLXMLRPCTransaction").post(request); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("threw exception", threw, "unknown type"); + } + + template<> template<> + void object::test<3>() + { + set_test_name("success case"); + LLSD request; + request["uri"] = uri; + request["method"] = "hello"; + request["reply"] = "reply"; + LLSD& params(request["params"]); + params["who"] = "world"; + // Set up a timeout filter so we don't spin forever waiting. + LLEventTimeout watchdog; + // Connect the timeout filter to the reply pump. + LLTempBoundListener temp( + pumps.obtain("reply"). + listen("watchdog", boost::bind(&LLEventTimeout::post, boost::ref(watchdog), _1))); + // Now connect our target listener to the timeout filter. + watchdog.listen("captureReply", boost::bind(&data::captureReply, this, _1)); + // Kick off the request... + reply.clear(); + pumps.obtain("LLXMLRPCTransaction").post(request); + // Set the timer + F32 timeout(10); + watchdog.eventAfter(timeout, LLSD().insert("timeout", 0)); + // and pump "mainloop" until we get something, whether from + // LLXMLRPCListener or from the watchdog filter. + LLTimer timer; + F32 start = timer.getElapsedTimeF32(); + LLEventPump& mainloop(pumps.obtain("mainloop")); + while (reply.isUndefined()) + { + mainloop.post(LLSD()); + } + ensure("timeout works", (timer.getElapsedTimeF32() - start) < (timeout + 1)); + ensure_equals(reply["responses"]["hi_there"].asString(), "Hello, world!"); + } + + template<> template<> + void object::test<4>() + { + set_test_name("bogus method"); + LLSD request; + request["uri"] = uri; + request["method"] = "goodbye"; + request["reply"] = "reply"; + LLSD& params(request["params"]); + params["who"] = "world"; + // Set up a timeout filter so we don't spin forever waiting. + LLEventTimeout watchdog; + // Connect the timeout filter to the reply pump. + LLTempBoundListener temp( + pumps.obtain("reply"). + listen("watchdog", boost::bind(&LLEventTimeout::post, boost::ref(watchdog), _1))); + // Now connect our target listener to the timeout filter. + watchdog.listen("captureReply", boost::bind(&data::captureReply, this, _1)); + // Kick off the request... + reply.clear(); + pumps.obtain("LLXMLRPCTransaction").post(request); + // Set the timer + F32 timeout(10); + watchdog.eventAfter(timeout, LLSD().insert("timeout", 0)); + // and pump "mainloop" until we get something, whether from + // LLXMLRPCListener or from the watchdog filter. + LLTimer timer; + F32 start = timer.getElapsedTimeF32(); + LLEventPump& mainloop(pumps.obtain("mainloop")); + while (reply.isUndefined()) + { + mainloop.post(LLSD()); + } + ensure("timeout works", (timer.getElapsedTimeF32() - start) < (timeout + 1)); + ensure_equals("XMLRPC error", reply["status"].asString(), "XMLRPCError"); + } + + template<> template<> + void object::test<5>() + { + set_test_name("bad type"); + LLSD request; + request["uri"] = uri; + request["method"] = "getdict"; + request["reply"] = "reply"; + (void)request["params"]; + // Set up a timeout filter so we don't spin forever waiting. + LLEventTimeout watchdog; + // Connect the timeout filter to the reply pump. + LLTempBoundListener temp( + pumps.obtain("reply"). + listen("watchdog", boost::bind(&LLEventTimeout::post, boost::ref(watchdog), _1))); + // Now connect our target listener to the timeout filter. + watchdog.listen("captureReply", boost::bind(&data::captureReply, this, _1)); + // Kick off the request... + reply.clear(); + pumps.obtain("LLXMLRPCTransaction").post(request); + // Set the timer + F32 timeout(10); + watchdog.eventAfter(timeout, LLSD().insert("timeout", 0)); + // and pump "mainloop" until we get something, whether from + // LLXMLRPCListener or from the watchdog filter. + LLTimer timer; + F32 start = timer.getElapsedTimeF32(); + LLEventPump& mainloop(pumps.obtain("mainloop")); + while (reply.isUndefined()) + { + mainloop.post(LLSD()); + } + ensure("timeout works", (timer.getElapsedTimeF32() - start) < (timeout + 1)); + ensure_equals(reply["status"].asString(), "BadType"); + ensure_contains("bad type", reply["responses"]["nested_dict"].asString(), "bad XMLRPC type"); + } +} // namespace tut + +/***************************************************************************** +* Resolve link errors: use real machinery here, since we intend to exchange +* actual XML with a peer process. +*****************************************************************************/ +// Including llxmlrpctransaction.cpp drags in the static LLXMLRPCListener +// instantiated there. That's why it works to post requests to the LLEventPump +// named "LLXMLRPCTransaction". +#include "../llxmlrpctransaction.cpp" +#include "llcontrol.cpp" +#include "llxmltree.cpp" +#include "llxmlparser.cpp" diff --git a/indra/newview/tests/test_llxmlrpc_peer.py b/indra/newview/tests/test_llxmlrpc_peer.py new file mode 100644 index 0000000000..cb8f7d26c4 --- /dev/null +++ b/indra/newview/tests/test_llxmlrpc_peer.py @@ -0,0 +1,59 @@ +#!/usr/bin/python +"""\ +@file test_llxmlrpc_peer.py +@author Nat Goodspeed +@date 2008-10-09 +@brief This script asynchronously runs the executable (with args) specified on + the command line, returning its result code. While that executable is + running, we provide dummy local services for use by C++ tests. + +$LicenseInfo:firstyear=2008&license=viewergpl$ +Copyright (c) 2008, Linden Research, Inc. +$/LicenseInfo$ +""" + +import os +import sys +from threading import Thread +from SimpleXMLRPCServer import SimpleXMLRPCServer + +mydir = os.path.dirname(__file__) # expected to be .../indra/newview/tests/ +sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) +sys.path.insert(1, os.path.join(mydir, os.pardir, os.pardir, "llmessage", "tests")) +from testrunner import run, debug + +class TestServer(SimpleXMLRPCServer): + def _dispatch(self, method, params): + try: + func = getattr(self, method) + except AttributeError: + raise Exception('method "%s" is not supported' % method) + else: + # LLXMLRPCListener constructs XMLRPC parameters that arrive as a + # 1-tuple containing a dict. + return func(**(params[0])) + + def hello(self, who): + # LLXMLRPCListener expects a dict return. + return {"hi_there": "Hello, %s!" % who} + + def getdict(self): + return dict(nested_dict=dict(a=17, b=5)) + + def log_request(self, code, size=None): + # For present purposes, we don't want the request splattered onto + # stderr, as it would upset devs watching the test run + pass + + def log_error(self, format, *args): + # Suppress error output as well + pass + +class ServerRunner(Thread): + def run(self): + server = TestServer(('127.0.0.1', 8000)) + debug("Starting XMLRPC server...\n") + server.serve_forever() + +if __name__ == "__main__": + sys.exit(run(server=ServerRunner(name="xmlrpc"), *sys.argv[1:])) diff --git a/indra/newview/tr.lproj/language.txt b/indra/newview/tr.lproj/language.txt new file mode 100644 index 0000000000..44266bf283 --- /dev/null +++ b/indra/newview/tr.lproj/language.txt @@ -0,0 +1 @@ +tr
\ No newline at end of file diff --git a/indra/newview/uk.lproj/language.txt b/indra/newview/uk.lproj/language.txt new file mode 100644 index 0000000000..fbc658f6f9 --- /dev/null +++ b/indra/newview/uk.lproj/language.txt @@ -0,0 +1 @@ +uk
\ No newline at end of file diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index d175a26531..6fc755b229 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1,736 +1,766 @@ -#!/usr/bin/python -# @file viewer_manifest.py -# @author Ryan Williams -# @brief Description of all installer viewer files, and methods for packaging -# them into installers for all supported platforms. -# -# $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$ -import sys -import os.path -import re -import tarfile -viewer_dir = os.path.dirname(__file__) -# add llmanifest library to our path so we don't have to muck with PYTHONPATH -sys.path.append(os.path.join(viewer_dir, '../lib/python/indra/util')) -from llmanifest import LLManifest, main, proper_windows_path, path_ancestors - -class ViewerManifest(LLManifest): - def construct(self): - super(ViewerManifest, self).construct() - self.exclude("*.svn*") - self.path(src="../../scripts/messages/message_template.msg", dst="app_settings/message_template.msg") - self.path(src="../../etc/message.xml", dst="app_settings/message.xml") - - if self.prefix(src="app_settings"): - self.exclude("logcontrol.xml") - self.exclude("logcontrol-dev.xml") - self.path("*.pem") - self.path("*.ini") - self.path("*.xml") - self.path("*.db2") - - # include the entire shaders directory recursively - self.path("shaders") - # ... and the entire windlight directory - self.path("windlight") - self.end_prefix("app_settings") - - if self.prefix(src="character"): - self.path("*.llm") - self.path("*.xml") - self.path("*.tga") - self.end_prefix("character") - - # Include our fonts - if self.prefix(src="fonts"): - self.path("*.ttf") - self.path("*.txt") - self.end_prefix("fonts") - - # skins - if self.prefix(src="skins"): - self.path("paths.xml") - # include the entire textures directory recursively - if self.prefix(src="*/textures"): - self.path("*/*.tga") - self.path("*/*.j2c") - self.path("*/*.jpg") - self.path("*/*.png") - self.path("*.tga") - self.path("*.j2c") - self.path("*.jpg") - self.path("*.png") - self.path("textures.xml") - self.end_prefix("*/textures") - self.path("*/xui/*/*.xml") - self.path("*/xui/*/widgets/*.xml") - self.path("*/*.xml") - - # Local HTML files (e.g. loading screen) - if self.prefix(src="*/html"): - self.path("*.png") - self.path("*/*/*.html") - self.path("*/*/*.gif") - self.end_prefix("*/html") - self.end_prefix("skins") - - # Files in the newview/ directory - self.path("gpu_table.txt") - - def login_channel(self): - """Channel reported for login and upgrade purposes ONLY; - used for A/B testing""" - # NOTE: Do not return the normal channel if login_channel - # is not specified, as some code may branch depending on - # whether or not this is present - return self.args.get('login_channel') - - def grid(self): - return self.args['grid'] - def channel(self): - return self.args['channel'] - def channel_unique(self): - return self.channel().replace("Second Life", "").strip() - def channel_oneword(self): - return "".join(self.channel_unique().split()) - def channel_lowerword(self): - return self.channel_oneword().lower() - - def flags_list(self): - """ Convenience function that returns the command-line flags - for the grid""" - - # Set command line flags relating to the target grid - grid_flags = '' - if not self.default_grid(): - grid_flags = "--grid %(grid)s "\ - "--helperuri http://preview-%(grid)s.secondlife.com/helpers/" %\ - {'grid':self.grid()} - - # set command line flags for channel - channel_flags = '' - if self.login_channel() and self.login_channel() != self.channel(): - # Report a special channel during login, but use default - channel_flags = '--channel "%s"' % (self.login_channel()) - elif not self.default_channel(): - channel_flags = '--channel "%s"' % self.channel() - - # Deal with settings - setting_flags = '' - if not self.default_channel() or not self.default_grid(): - if self.default_grid(): - setting_flags = '--settings settings_%s.xml'\ - % self.channel_lowerword() - else: - setting_flags = '--settings settings_%s_%s.xml'\ - % (self.grid(), self.channel_lowerword()) - - return " ".join((channel_flags, grid_flags, setting_flags)).strip() - - -class WindowsManifest(ViewerManifest): - def final_exe(self): - if self.default_channel(): - if self.default_grid(): - return "SecondLife.exe" - else: - return "SecondLifePreview.exe" - else: - return ''.join(self.channel().split()) + '.exe' - - - def construct(self): - super(WindowsManifest, self).construct() - # the final exe is complicated because we're not sure where it's coming from, - # nor do we have a fixed name for the executable - self.path(self.find_existing_file('debug/secondlife-bin.exe', 'release/secondlife-bin.exe', 'relwithdebinfo/secondlife-bin.exe'), dst=self.final_exe()) - # need to get the kdu dll from any of the build directories as well - try: - self.path(self.find_existing_file('../llkdu/%s/llkdu.dll' % self.args['configuration'], - '../../libraries/i686-win32/lib/release/llkdu.dll'), - dst='llkdu.dll') - pass - except: - print "Skipping llkdu.dll" - pass - self.path(src="licenses-win32.txt", dst="licenses.txt") - - self.path("featuretable.txt") - - # For use in crash reporting (generates minidumps) - self.path("dbghelp.dll") - - # For using FMOD for sound... DJS - self.path("fmod.dll") - - # For textures - if self.prefix(src="../../libraries/i686-win32/lib/release", dst=""): - self.path("openjpeg.dll") - self.end_prefix() - - # Plugin host application - if self.prefix(src='../llplugin/slplugin/%s' % self.args['configuration'], dst="llplugin"): - self.path("slplugin.exe") - self.end_prefix() - - # Media plugins - Flash (ActiveX) - if self.prefix(src='../media_plugins/flash_activex/%s' % self.args['configuration'], dst="llplugin"): - self.path("media_plugin_flash_activex.dll") - self.end_prefix() - - # Media plugins - QuickTime - if self.prefix(src='../media_plugins/quicktime/%s' % self.args['configuration'], dst="llplugin"): - self.path("media_plugin_quicktime.dll") - self.end_prefix() - - # Media plugins - WebKit/Qt - if self.prefix(src='../media_plugins/webkit/%s' % self.args['configuration'], dst="llplugin"): - self.path("media_plugin_webkit.dll") - self.end_prefix() - - # For WebKit/Qt plugin runtimes - if self.prefix(src="../../libraries/i686-win32/lib/release", dst="llplugin"): - self.path("libeay32.dll") - self.path("qtcore4.dll") - self.path("qtgui4.dll") - self.path("qtnetwork4.dll") - self.path("qtopengl4.dll") - self.path("qtwebkit4.dll") - self.path("ssleay32.dll") - self.end_prefix() - - # For WebKit/Qt plugin runtimes (image format plugins) - if self.prefix(src="../../libraries/i686-win32/lib/release/imageformats", dst="llplugin/imageformats"): - self.path("qgif4.dll") - self.path("qico4.dll") - self.path("qjpeg4.dll") - self.path("qmng4.dll") - self.path("qsvg4.dll") - self.path("qtiff4.dll") - self.end_prefix() - - # These need to be installed as a SxS assembly, currently a 'private' assembly. - # See http://msdn.microsoft.com/en-us/library/ms235291(VS.80).aspx - if self.prefix(src=self.args['configuration'], dst=""): - if self.args['configuration'] == 'Debug': - self.path("msvcr80d.dll") - self.path("msvcp80d.dll") - self.path("Microsoft.VC80.DebugCRT.manifest") - else: - self.path("msvcr80.dll") - self.path("msvcp80.dll") - self.path("Microsoft.VC80.CRT.manifest") - self.end_prefix() - - # The config file name needs to match the exe's name. - self.path(src="%s/secondlife-bin.exe.config" % self.args['configuration'], dst=self.final_exe() + ".config") - - # Vivox runtimes - if self.prefix(src="vivox-runtime/i686-win32", dst=""): - self.path("SLVoice.exe") - self.path("alut.dll") - self.path("vivoxsdk.dll") - self.path("ortp.dll") - self.path("wrap_oal.dll") - self.end_prefix() - - # pull in the crash logger and updater from other projects - self.path(src=self.find_existing_file( # tag:"crash-logger" here as a cue to the exporter - "../win_crash_logger/debug/windows-crash-logger.exe", - "../win_crash_logger/release/windows-crash-logger.exe", - "../win_crash_logger/relwithdebinfo/windows-crash-logger.exe"), - dst="win_crash_logger.exe") - self.path(src=self.find_existing_file( - "../win_updater/debug/windows-updater.exe", - "../win_updater/release/windows-updater.exe", - "../win_updater/relwithdebinfo/windows-updater.exe"), - dst="updater.exe") - - # For google-perftools tcmalloc allocator. - if self.prefix(src="../../libraries/i686-win32/lib/release", dst=""): - self.path("libtcmalloc_minimal.dll") - self.end_prefix() - - - def nsi_file_commands(self, install=True): - def wpath(path): - if path.endswith('/') or path.endswith(os.path.sep): - path = path[:-1] - path = path.replace('/', '\\') - return path - - result = "" - dest_files = [pair[1] for pair in self.file_list if pair[0] and os.path.isfile(pair[1])] - # sort deepest hierarchy first - dest_files.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b)) - dest_files.reverse() - out_path = None - for pkg_file in dest_files: - rel_file = os.path.normpath(pkg_file.replace(self.get_dst_prefix()+os.path.sep,'')) - installed_dir = wpath(os.path.join('$INSTDIR', os.path.dirname(rel_file))) - pkg_file = wpath(os.path.normpath(pkg_file)) - if installed_dir != out_path: - if install: - out_path = installed_dir - result += 'SetOutPath ' + out_path + '\n' - if install: - result += 'File ' + pkg_file + '\n' - else: - result += 'Delete ' + wpath(os.path.join('$INSTDIR', rel_file)) + '\n' - # at the end of a delete, just rmdir all the directories - if not install: - deleted_file_dirs = [os.path.dirname(pair[1].replace(self.get_dst_prefix()+os.path.sep,'')) for pair in self.file_list] - # find all ancestors so that we don't skip any dirs that happened to have no non-dir children - deleted_dirs = [] - for d in deleted_file_dirs: - deleted_dirs.extend(path_ancestors(d)) - # sort deepest hierarchy first - deleted_dirs.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b)) - deleted_dirs.reverse() - prev = None - for d in deleted_dirs: - if d != prev: # skip duplicates - result += 'RMDir ' + wpath(os.path.join('$INSTDIR', os.path.normpath(d))) + '\n' - prev = d - - return result - - def package_finish(self): - # a standard map of strings for replacing in the templates - substitution_strings = { - 'version' : '.'.join(self.args['version']), - 'version_short' : '.'.join(self.args['version'][:-1]), - 'version_dashes' : '-'.join(self.args['version']), - 'final_exe' : self.final_exe(), - 'grid':self.args['grid'], - 'grid_caps':self.args['grid'].upper(), - # escape quotes becase NSIS doesn't handle them well - 'flags':self.flags_list().replace('"', '$\\"'), - 'channel':self.channel(), - 'channel_oneword':self.channel_oneword(), - 'channel_unique':self.channel_unique(), - } - - version_vars = """ - !define INSTEXE "%(final_exe)s" - !define VERSION "%(version_short)s" - !define VERSION_LONG "%(version)s" - !define VERSION_DASHES "%(version_dashes)s" - """ % substitution_strings - if self.default_channel(): - if self.default_grid(): - # release viewer - installer_file = "Second_Life_%(version_dashes)s_Setup.exe" - grid_vars_template = """ - OutFile "%(installer_file)s" - !define INSTFLAGS "%(flags)s" - !define INSTNAME "SecondLife" - !define SHORTCUT "Second Life" - !define URLNAME "secondlife" - Caption "Second Life ${VERSION}" - """ - else: - # beta grid viewer - installer_file = "Second_Life_%(version_dashes)s_(%(grid_caps)s)_Setup.exe" - grid_vars_template = """ - OutFile "%(installer_file)s" - !define INSTFLAGS "%(flags)s" - !define INSTNAME "SecondLife%(grid_caps)s" - !define SHORTCUT "Second Life (%(grid_caps)s)" - !define URLNAME "secondlife%(grid)s" - !define UNINSTALL_SETTINGS 1 - Caption "Second Life %(grid)s ${VERSION}" - """ - else: - # some other channel on some grid - installer_file = "Second_Life_%(version_dashes)s_%(channel_oneword)s_Setup.exe" - grid_vars_template = """ - OutFile "%(installer_file)s" - !define INSTFLAGS "%(flags)s" - !define INSTNAME "SecondLife%(channel_oneword)s" - !define SHORTCUT "%(channel)s" - !define URLNAME "secondlife" - !define UNINSTALL_SETTINGS 1 - Caption "%(channel)s ${VERSION}" - """ - if 'installer_name' in self.args: - installer_file = self.args['installer_name'] - else: - installer_file = installer_file % substitution_strings - substitution_strings['installer_file'] = installer_file - - tempfile = "secondlife_setup_tmp.nsi" - # the following replaces strings in the nsi template - # it also does python-style % substitution - self.replace_in("installers/windows/installer_template.nsi", tempfile, { - "%%VERSION%%":version_vars, - "%%SOURCE%%":self.get_src_prefix(), - "%%GRID_VARS%%":grid_vars_template % substitution_strings, - "%%INSTALL_FILES%%":self.nsi_file_commands(True), - "%%DELETE_FILES%%":self.nsi_file_commands(False)}) - - # We use the Unicode version of NSIS, available from - # http://www.scratchpaper.com/ - NSIS_path = 'C:\\Program Files\\NSIS\\Unicode\\makensis.exe' - self.run_command('"' + proper_windows_path(NSIS_path) + '" ' + self.dst_path_of(tempfile)) - # self.remove(self.dst_path_of(tempfile)) - # If we're on a build machine, sign the code using our Authenticode certificate. JC - sign_py = 'C:\\buildscripts\\code-signing\\sign.py' - if os.path.exists(sign_py): - self.run_command(sign_py + ' ' + self.dst_path_of(installer_file)) - else: - print "Skipping code signing,", sign_py, "does not exist" - self.created_path(self.dst_path_of(installer_file)) - self.package_file = installer_file - - -class DarwinManifest(ViewerManifest): - def construct(self): - # copy over the build result (this is a no-op if run within the xcode script) - self.path(self.args['configuration'] + "/Second Life.app", dst="") - - if self.prefix(src="", dst="Contents"): # everything goes in Contents - self.path("Info-SecondLife.plist", dst="Info.plist") - - # copy additional libs in <bundle>/Contents/MacOS/ - self.path("../../libraries/universal-darwin/lib_release/libndofdev.dylib", dst="MacOS/libndofdev.dylib") - - # most everything goes in the Resources directory - if self.prefix(src="", dst="Resources"): - super(DarwinManifest, self).construct() - - if self.prefix("cursors_mac"): - self.path("*.tif") - self.end_prefix("cursors_mac") - - self.path("licenses-mac.txt", dst="licenses.txt") - self.path("featuretable_mac.txt") - self.path("SecondLife.nib") - - # If we are not using the default channel, use the 'Firstlook - # icon' to show that it isn't a stable release. - if self.default_channel() and self.default_grid(): - self.path("secondlife.icns") - else: - self.path("secondlife_firstlook.icns", "secondlife.icns") - self.path("SecondLife.nib") - - # Translations - self.path("English.lproj") - self.path("German.lproj") - self.path("Japanese.lproj") - self.path("Korean.lproj") - self.path("da.lproj") - self.path("es.lproj") - self.path("fr.lproj") - self.path("hu.lproj") - self.path("it.lproj") - self.path("nl.lproj") - self.path("pl.lproj") - self.path("pt.lproj") - self.path("ru.lproj") - self.path("tr.lproj") - self.path("uk.lproj") - self.path("zh-Hans.lproj") - - # SLVoice and vivox lols - self.path("vivox-runtime/universal-darwin/libalut.dylib", "libalut.dylib") - self.path("vivox-runtime/universal-darwin/libopenal.dylib", "libopenal.dylib") - self.path("vivox-runtime/universal-darwin/libortp.dylib", "libortp.dylib") - self.path("vivox-runtime/universal-darwin/libvivoxsdk.dylib", "libvivoxsdk.dylib") - self.path("vivox-runtime/universal-darwin/SLVoice", "SLVoice") - - # need to get the kdu dll from any of the build directories as well - try: - self.path(self.find_existing_file('../llkdu/%s/libllkdu.dylib' % self.args['configuration'], - "../../libraries/universal-darwin/lib_release/libllkdu.dylib"), - dst='libllkdu.dylib') - pass - except: - print "Skipping libllkdu.dylib" - pass - - #libfmodwrapper.dylib - self.path(self.args['configuration'] + "/libfmodwrapper.dylib", "libfmodwrapper.dylib") - - # our apps - self.path("../mac_crash_logger/" + self.args['configuration'] + "/mac-crash-logger.app", "mac-crash-logger.app") - self.path("../mac_updater/" + self.args['configuration'] + "/mac-updater.app", "mac-updater.app") - - # plugins - if self.prefix(src="", dst="llplugin"): - self.path("../llplugin/slplugin/" + self.args['configuration'] + "/SLPlugin", "SLPlugin") - self.path("../media_plugins/quicktime/" + self.args['configuration'] + "/media_plugin_quicktime.dylib", "media_plugin_quicktime.dylib") - self.path("../media_plugins/webkit/" + self.args['configuration'] + "/media_plugin_webkit.dylib", "media_plugin_webkit.dylib") - self.path("../../libraries/universal-darwin/lib_release/libllwebkitlib.dylib", "libllwebkitlib.dylib") - - self.path("../media_plugins/awesomium/" + self.args['configuration'] + "/media_plugin_awesomium.dylib", "media_plugin_awesomium.dylib") - self.path("../../libraries/universal-darwin/lib_release/Awesomium.framework", "Awesomium.framework") - - self.end_prefix("llplugin") - - # command line arguments for connecting to the proper grid - self.put_in_file(self.flags_list(), 'arguments.txt') - - self.end_prefix("Resources") - - self.end_prefix("Contents") - - # NOTE: the -S argument to strip causes it to keep enough info for - # annotated backtraces (i.e. function names in the crash log). 'strip' with no - # arguments yields a slightly smaller binary but makes crash logs mostly useless. - # This may be desirable for the final release. Or not. - if ("package" in self.args['actions'] or - "unpacked" in self.args['actions']): - self.run_command('strip -S "%(viewer_binary)s"' % - { 'viewer_binary' : self.dst_path_of('Contents/MacOS/Second Life')}) - - - def package_finish(self): - channel_standin = 'Second Life' # hah, our default channel is not usable on its own - if not self.default_channel(): - channel_standin = self.channel() - - imagename="SecondLife_" + '_'.join(self.args['version']) - - # MBW -- If the mounted volume name changes, it breaks the .DS_Store's background image and icon positioning. - # If we really need differently named volumes, we'll need to create multiple DS_Store file images, or use some other trick. - - volname="Second Life Installer" # DO NOT CHANGE without understanding comment above - - if self.default_channel(): - if not self.default_grid(): - # beta case - imagename = imagename + '_' + self.args['grid'].upper() - else: - # first look, etc - imagename = imagename + '_' + self.channel_oneword().upper() - - sparsename = imagename + ".sparseimage" - finalname = imagename + ".dmg" - # make sure we don't have stale files laying about - self.remove(sparsename, finalname) - - self.run_command('hdiutil create "%(sparse)s" -volname "%(vol)s" -fs HFS+ -type SPARSE -megabytes 400 -layout SPUD' % { - 'sparse':sparsename, - 'vol':volname}) - - # mount the image and get the name of the mount point and device node - hdi_output = self.run_command('hdiutil attach -private "' + sparsename + '"') - devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip() - volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip() - - # Copy everything in to the mounted .dmg - - if self.default_channel() and not self.default_grid(): - app_name = "Second Life " + self.args['grid'] - else: - app_name = channel_standin.strip() - - # Hack: - # Because there is no easy way to coerce the Finder into positioning - # the app bundle in the same place with different app names, we are - # adding multiple .DS_Store files to svn. There is one for release, - # one for release candidate and one for first look. Any other channels - # will use the release .DS_Store, and will look broken. - # - Ambroff 2008-08-20 - dmg_template = os.path.join( - 'installers', - 'darwin', - '%s-dmg' % "".join(self.channel_unique().split()).lower()) - - if not os.path.exists (self.src_path_of(dmg_template)): - dmg_template = os.path.join ('installers', 'darwin', 'release-dmg') - - for s,d in {self.get_dst_prefix():app_name + ".app", - os.path.join(dmg_template, "_VolumeIcon.icns"): ".VolumeIcon.icns", - os.path.join(dmg_template, "background.jpg"): "background.jpg", - os.path.join(dmg_template, "_DS_Store"): ".DS_Store"}.items(): - print "Copying to dmg", s, d - self.copy_action(self.src_path_of(s), os.path.join(volpath, d)) - - # Hide the background image, DS_Store file, and volume icon file (set their "visible" bit) - self.run_command('SetFile -a V "' + os.path.join(volpath, ".VolumeIcon.icns") + '"') - self.run_command('SetFile -a V "' + os.path.join(volpath, "background.jpg") + '"') - self.run_command('SetFile -a V "' + os.path.join(volpath, ".DS_Store") + '"') - - # Create the alias file (which is a resource file) from the .r - self.run_command('rez "' + self.src_path_of("installers/darwin/release-dmg/Applications-alias.r") + '" -o "' + os.path.join(volpath, "Applications") + '"') - - # Set the alias file's alias and custom icon bits - self.run_command('SetFile -a AC "' + os.path.join(volpath, "Applications") + '"') - - # Set the disk image root's custom icon bit - self.run_command('SetFile -a C "' + volpath + '"') - - # Unmount the image - self.run_command('hdiutil detach -force "' + devfile + '"') - - print "Converting temp disk image to final disk image" - self.run_command('hdiutil convert "%(sparse)s" -format UDZO -imagekey zlib-level=9 -o "%(final)s"' % {'sparse':sparsename, 'final':finalname}) - # get rid of the temp file - self.package_file = finalname - self.remove(sparsename) - -class LinuxManifest(ViewerManifest): - def construct(self): - super(LinuxManifest, self).construct() - self.path("licenses-linux.txt","licenses.txt") - self.path("res/ll_icon.png","secondlife_icon.png") - if self.prefix("linux_tools", dst=""): - self.path("client-readme.txt","README-linux.txt") - self.path("client-readme-voice.txt","README-linux-voice.txt") - self.path("client-readme-joystick.txt","README-linux-joystick.txt") - self.path("wrapper.sh","secondlife") - self.path("handle_secondlifeprotocol.sh", "etc/handle_secondlifeprotocol.sh") - self.path("register_secondlifeprotocol.sh", "etc/register_secondlifeprotocol.sh") - self.path("refresh_desktop_app_entry.sh", "etc/refresh_desktop_app_entry.sh") - self.path("launch_url.sh","etc/launch_url.sh") - self.path("install.sh") - self.end_prefix("linux_tools") - - # Create an appropriate gridargs.dat for this package, denoting required grid. - self.put_in_file(self.flags_list(), 'etc/gridargs.dat') - - - def package_finish(self): - if 'installer_name' in self.args: - installer_name = self.args['installer_name'] - else: - installer_name_components = ['SecondLife_', self.args.get('arch')] - installer_name_components.extend(self.args['version']) - installer_name = "_".join(installer_name_components) - if self.default_channel(): - if not self.default_grid(): - installer_name += '_' + self.args['grid'].upper() - else: - installer_name += '_' + self.channel_oneword().upper() - - # Fix access permissions - self.run_command(""" - find %(dst)s -type d | xargs --no-run-if-empty chmod 755; - find %(dst)s -type f -perm 0700 | xargs --no-run-if-empty chmod 0755; - find %(dst)s -type f -perm 0500 | xargs --no-run-if-empty chmod 0555; - find %(dst)s -type f -perm 0600 | xargs --no-run-if-empty chmod 0644; - find %(dst)s -type f -perm 0400 | xargs --no-run-if-empty chmod 0444; - true""" % {'dst':self.get_dst_prefix() }) - self.package_file = installer_name + '.tar.bz2' - - # temporarily move directory tree so that it has the right - # name in the tarfile - self.run_command("mv %(dst)s %(inst)s" % { - 'dst': self.get_dst_prefix(), - 'inst': self.build_path_of(installer_name)}) - try: - # --numeric-owner hides the username of the builder for - # security etc. - self.run_command('tar -C %(dir)s --numeric-owner -cjf ' - '%(inst_path)s.tar.bz2 %(inst_name)s' % { - 'dir': self.get_build_prefix(), - 'inst_name': installer_name, - 'inst_path':self.build_path_of(installer_name)}) - finally: - self.run_command("mv %(inst)s %(dst)s" % { - 'dst': self.get_dst_prefix(), - 'inst': self.build_path_of(installer_name)}) - -class Linux_i686Manifest(LinuxManifest): - def construct(self): - super(Linux_i686Manifest, self).construct() - - # install either the libllkdu we just built, or a prebuilt one, in - # decreasing order of preference. for linux package, this goes to bin/ - try: - self.path(self.find_existing_file('../llkdu/libllkdu.so', - '../../libraries/i686-linux/lib_release_client/libllkdu.so'), - dst='bin/libllkdu.so') - # keep this one to preserve syntax, open source mangling removes previous lines - pass - except: - print "Skipping libllkdu.so - not found" - pass - - self.path("secondlife-stripped","bin/do-not-directly-run-secondlife-bin") - self.path("../linux_crash_logger/linux-crash-logger-stripped","bin/linux-crash-logger.bin") - self.path("../linux_updater/linux-updater-stripped", "bin/linux-updater.bin") - if self.prefix("res-sdl"): - self.path("*") - # recurse - self.end_prefix("res-sdl") - - # plugins - if self.prefix(src="", dst="bin/llplugin"): - self.path("../llplugin/slplugin/SLPlugin", "SLPlugin") - self.path("../media_plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so") - self.path("../media_plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_quicktime.so") - self.end_prefix("bin/llplugin") - - self.path("featuretable_linux.txt") - #self.path("secondlife-i686.supp") - - if self.prefix("../../libraries/i686-linux/lib_release_client", dst="lib"): - #self.path("libkdu_v42R.so", "libkdu.so") - self.path("libfmod-3.75.so") - self.path("libapr-1.so.0") - self.path("libaprutil-1.so.0") - self.path("libdb-4.2.so") - self.path("libcrypto.so.0.9.7") - self.path("libexpat.so.1") - self.path("libssl.so.0.9.7") - self.path("libuuid.so", "libuuid.so.1") - self.path("libSDL-1.2.so.0") - self.path("libELFIO.so") - self.path("libopenjpeg.so.1.3.0", "libopenjpeg.so.1.3") - self.path("libalut.so") - self.path("libopenal.so", "libopenal.so.1") - self.end_prefix("lib") - - # Vivox runtimes - if self.prefix(src="vivox-runtime/i686-linux", dst="bin"): - self.path("SLVoice") - self.end_prefix() - if self.prefix(src="vivox-runtime/i686-linux", dst="lib"): - self.path("libortp.so") - self.path("libvivoxsdk.so") - self.end_prefix("lib") - -class Linux_x86_64Manifest(LinuxManifest): - def construct(self): - super(Linux_x86_64Manifest, self).construct() - self.path("secondlife-stripped","bin/do-not-directly-run-secondlife-bin") - self.path("../linux_crash_logger/linux-crash-logger-stripped","linux-crash-logger.bin") - if self.prefix("res-sdl"): - self.path("*") - # recurse - self.end_prefix("res-sdl") - - self.path("featuretable_linux.txt") - self.path("secondlife-i686.supp") - -if __name__ == "__main__": - main() +#!/usr/bin/python
+# @file viewer_manifest.py
+# @author Ryan Williams
+# @brief Description of all installer viewer files, and methods for packaging
+# them into installers for all supported platforms.
+#
+# $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$
+import sys
+import os.path
+import re
+import tarfile
+viewer_dir = os.path.dirname(__file__)
+# add llmanifest library to our path so we don't have to muck with PYTHONPATH
+sys.path.append(os.path.join(viewer_dir, '../lib/python/indra/util'))
+from llmanifest import LLManifest, main, proper_windows_path, path_ancestors
+
+class ViewerManifest(LLManifest):
+ def construct(self):
+ super(ViewerManifest, self).construct()
+ self.exclude("*.svn*")
+ self.path(src="../../scripts/messages/message_template.msg", dst="app_settings/message_template.msg")
+ self.path(src="../../etc/message.xml", dst="app_settings/message.xml")
+
+ if self.prefix(src="app_settings"):
+ self.exclude("logcontrol.xml")
+ self.exclude("logcontrol-dev.xml")
+ self.path("*.pem")
+ self.path("*.ini")
+ self.path("*.xml")
+ self.path("*.db2")
+
+ # include the entire shaders directory recursively
+ self.path("shaders")
+ # ... and the entire windlight directory
+ self.path("windlight")
+ self.end_prefix("app_settings")
+
+ if self.prefix(src="character"):
+ self.path("*.llm")
+ self.path("*.xml")
+ self.path("*.tga")
+ self.end_prefix("character")
+
+ # Include our fonts
+ if self.prefix(src="fonts"):
+ self.path("*.ttf")
+ self.path("*.txt")
+ self.end_prefix("fonts")
+
+ # skins
+ if self.prefix(src="skins"):
+ self.path("paths.xml")
+ # include the entire textures directory recursively
+ if self.prefix(src="*/textures"):
+ self.path("*/*.tga")
+ self.path("*/*.j2c")
+ self.path("*/*.jpg")
+ self.path("*/*.png")
+ self.path("*.tga")
+ self.path("*.j2c")
+ self.path("*.jpg")
+ self.path("*.png")
+ self.path("textures.xml")
+ self.end_prefix("*/textures")
+ self.path("*/xui/*/*.xml")
+ self.path("*/xui/*/widgets/*.xml")
+ self.path("*/*.xml")
+
+ # Local HTML files (e.g. loading screen)
+ if self.prefix(src="*/html"):
+ self.path("*.png")
+ self.path("*/*/*.html")
+ self.path("*/*/*.gif")
+ self.end_prefix("*/html")
+ self.end_prefix("skins")
+
+ # Files in the newview/ directory
+ self.path("gpu_table.txt")
+
+ def login_channel(self):
+ """Channel reported for login and upgrade purposes ONLY;
+ used for A/B testing"""
+ # NOTE: Do not return the normal channel if login_channel
+ # is not specified, as some code may branch depending on
+ # whether or not this is present
+ return self.args.get('login_channel')
+
+ def grid(self):
+ return self.args['grid']
+ def channel(self):
+ return self.args['channel']
+ def channel_unique(self):
+ return self.channel().replace("Second Life", "").strip()
+ def channel_oneword(self):
+ return "".join(self.channel_unique().split())
+ def channel_lowerword(self):
+ return self.channel_oneword().lower()
+
+ def flags_list(self):
+ """ Convenience function that returns the command-line flags
+ for the grid"""
+
+ # Set command line flags relating to the target grid
+ grid_flags = ''
+ if not self.default_grid():
+ grid_flags = "--grid %(grid)s "\
+ "--helperuri http://preview-%(grid)s.secondlife.com/helpers/" %\
+ {'grid':self.grid()}
+
+ # set command line flags for channel
+ channel_flags = ''
+ if self.login_channel() and self.login_channel() != self.channel():
+ # Report a special channel during login, but use default
+ channel_flags = '--channel "%s"' % (self.login_channel())
+ elif not self.default_channel():
+ channel_flags = '--channel "%s"' % self.channel()
+
+ # Deal with settings
+ setting_flags = ''
+ if not self.default_channel() or not self.default_grid():
+ if self.default_grid():
+ setting_flags = '--settings settings_%s.xml'\
+ % self.channel_lowerword()
+ else:
+ setting_flags = '--settings settings_%s_%s.xml'\
+ % (self.grid(), self.channel_lowerword())
+
+ return " ".join((channel_flags, grid_flags, setting_flags)).strip()
+
+
+class WindowsManifest(ViewerManifest):
+ def final_exe(self):
+ if self.default_channel():
+ if self.default_grid():
+ return "SecondLife.exe"
+ else:
+ return "SecondLifePreview.exe"
+ else:
+ return ''.join(self.channel().split()) + '.exe'
+
+
+ def construct(self):
+ super(WindowsManifest, self).construct()
+ # Find secondlife-bin.exe in the 'configuration' dir, then rename it to the result of final_exe.
+ self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe())
+
+ # need to get the llcommon.dll from the build directory as well
+ if self.prefix(src=self.args['configuration'], dst=""):
+ try:
+ self.path('llcommon.dll')
+ self.path('libapr-1.dll')
+ self.path('libaprutil-1.dll')
+ self.path('libapriconv-1.dll')
+ except:
+ print "Skipping llcommon.dll (assuming llcommon was linked statically)"
+ pass
+ self.end_prefix()
+
+ # need to get the kdu dll from the build directory as well
+ try:
+ self.path('%s/llkdu.dll' % self.args['configuration'], dst='llkdu.dll')
+ pass
+ except:
+ print "Skipping llkdu.dll"
+ pass
+ self.path(src="licenses-win32.txt", dst="licenses.txt")
+
+ self.path("featuretable.txt")
+
+ # For use in crash reporting (generates minidumps)
+ self.path("dbghelp.dll")
+
+ # For using FMOD for sound... DJS
+ self.path("fmod.dll")
+
+ # For textures
+ if self.prefix(src=self.args['configuration'], dst=""):
+ if(self.args['configuration'].lower() == 'debug'):
+ self.path("openjpegd.dll")
+ else:
+ self.path("openjpeg.dll")
+ self.end_prefix()
+
+ # Plugin host application
+ if self.prefix(src='../llplugin/slplugin/%s' % self.args['configuration'], dst="llplugin"):
+ self.path("slplugin.exe")
+ self.end_prefix()
+
+ # Media plugins - Flash (ActiveX)
+ if self.prefix(src='../media_plugins/flash_activex/%s' % self.args['configuration'], dst="llplugin"):
+ self.path("media_plugin_flash_activex.dll")
+ self.end_prefix()
+
+ # Media plugins - QuickTime
+ if self.prefix(src='../media_plugins/quicktime/%s' % self.args['configuration'], dst="llplugin"):
+ self.path("media_plugin_quicktime.dll")
+ self.end_prefix()
+
+ # Media plugins - WebKit/Qt
+ if self.prefix(src='../media_plugins/webkit/%s' % self.args['configuration'], dst="llplugin"):
+ self.path("media_plugin_webkit.dll")
+ self.end_prefix()
+
+ # For WebKit/Qt plugin runtimes
+ if self.prefix(src="../../libraries/i686-win32/lib/release", dst="llplugin"):
+ self.path("libeay32.dll")
+ self.path("qtcore4.dll")
+ self.path("qtgui4.dll")
+ self.path("qtnetwork4.dll")
+ self.path("qtopengl4.dll")
+ self.path("qtwebkit4.dll")
+ self.path("ssleay32.dll")
+ self.end_prefix()
+
+ # For WebKit/Qt plugin runtimes (image format plugins)
+ if self.prefix(src="../../libraries/i686-win32/lib/release/imageformats", dst="llplugin/imageformats"):
+ self.path("qgif4.dll")
+ self.path("qico4.dll")
+ self.path("qjpeg4.dll")
+ self.path("qmng4.dll")
+ self.path("qsvg4.dll")
+ self.path("qtiff4.dll")
+ self.end_prefix()
+
+ # These need to be installed as a SxS assembly, currently a 'private' assembly.
+ # See http://msdn.microsoft.com/en-us/library/ms235291(VS.80).aspx
+ if self.prefix(src=self.args['configuration'], dst=""):
+ if self.args['configuration'] == 'Debug':
+ self.path("msvcr80d.dll")
+ self.path("msvcp80d.dll")
+ self.path("Microsoft.VC80.DebugCRT.manifest")
+ else:
+ self.path("msvcr80.dll")
+ self.path("msvcp80.dll")
+ self.path("Microsoft.VC80.CRT.manifest")
+ self.end_prefix()
+
+ # The config file name needs to match the exe's name.
+ self.path(src="%s/secondlife-bin.exe.config" % self.args['configuration'], dst=self.final_exe() + ".config")
+
+ # Vivox runtimes
+ if self.prefix(src=self.args['configuration'], dst=""):
+ self.path("SLVoice.exe")
+ self.path("alut.dll")
+ self.path("vivoxsdk.dll")
+ self.path("ortp.dll")
+ self.path("wrap_oal.dll")
+ self.end_prefix()
+
+ # pull in the crash logger and updater from other projects
+ # tag:"crash-logger" here as a cue to the exporter
+ self.path(src='../win_crash_logger/%s/windows-crash-logger.exe' % self.args['configuration'],
+ dst="win_crash_logger.exe")
+ self.path(src='../win_updater/%s/windows-updater.exe' % self.args['configuration'],
+ dst="updater.exe")
+
+ # For google-perftools tcmalloc allocator.
+ try:
+ self.path('%s/libtcmalloc_minimal.dll' % self.args['configuration'])
+ except:
+ print "Skipping libtcmalloc_minimal.dll"
+ pass
+
+ def nsi_file_commands(self, install=True):
+ def wpath(path):
+ if path.endswith('/') or path.endswith(os.path.sep):
+ path = path[:-1]
+ path = path.replace('/', '\\')
+ return path
+
+ result = ""
+ dest_files = [pair[1] for pair in self.file_list if pair[0] and os.path.isfile(pair[1])]
+ # sort deepest hierarchy first
+ dest_files.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b))
+ dest_files.reverse()
+ out_path = None
+ for pkg_file in dest_files:
+ rel_file = os.path.normpath(pkg_file.replace(self.get_dst_prefix()+os.path.sep,''))
+ installed_dir = wpath(os.path.join('$INSTDIR', os.path.dirname(rel_file)))
+ pkg_file = wpath(os.path.normpath(pkg_file))
+ if installed_dir != out_path:
+ if install:
+ out_path = installed_dir
+ result += 'SetOutPath ' + out_path + '\n'
+ if install:
+ result += 'File ' + pkg_file + '\n'
+ else:
+ result += 'Delete ' + wpath(os.path.join('$INSTDIR', rel_file)) + '\n'
+ # at the end of a delete, just rmdir all the directories
+ if not install:
+ deleted_file_dirs = [os.path.dirname(pair[1].replace(self.get_dst_prefix()+os.path.sep,'')) for pair in self.file_list]
+ # find all ancestors so that we don't skip any dirs that happened to have no non-dir children
+ deleted_dirs = []
+ for d in deleted_file_dirs:
+ deleted_dirs.extend(path_ancestors(d))
+ # sort deepest hierarchy first
+ deleted_dirs.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b))
+ deleted_dirs.reverse()
+ prev = None
+ for d in deleted_dirs:
+ if d != prev: # skip duplicates
+ result += 'RMDir ' + wpath(os.path.join('$INSTDIR', os.path.normpath(d))) + '\n'
+ prev = d
+
+ return result
+
+ def package_finish(self):
+ # a standard map of strings for replacing in the templates
+ substitution_strings = {
+ 'version' : '.'.join(self.args['version']),
+ 'version_short' : '.'.join(self.args['version'][:-1]),
+ 'version_dashes' : '-'.join(self.args['version']),
+ 'final_exe' : self.final_exe(),
+ 'grid':self.args['grid'],
+ 'grid_caps':self.args['grid'].upper(),
+ # escape quotes becase NSIS doesn't handle them well
+ 'flags':self.flags_list().replace('"', '$\\"'),
+ 'channel':self.channel(),
+ 'channel_oneword':self.channel_oneword(),
+ 'channel_unique':self.channel_unique(),
+ }
+
+ version_vars = """
+ !define INSTEXE "%(final_exe)s"
+ !define VERSION "%(version_short)s"
+ !define VERSION_LONG "%(version)s"
+ !define VERSION_DASHES "%(version_dashes)s"
+ """ % substitution_strings
+ if self.default_channel():
+ if self.default_grid():
+ # release viewer
+ installer_file = "Second_Life_%(version_dashes)s_Setup.exe"
+ grid_vars_template = """
+ OutFile "%(installer_file)s"
+ !define INSTFLAGS "%(flags)s"
+ !define INSTNAME "SecondLife"
+ !define SHORTCUT "Second Life"
+ !define URLNAME "secondlife"
+ Caption "Second Life ${VERSION}"
+ """
+ else:
+ # beta grid viewer
+ installer_file = "Second_Life_%(version_dashes)s_(%(grid_caps)s)_Setup.exe"
+ grid_vars_template = """
+ OutFile "%(installer_file)s"
+ !define INSTFLAGS "%(flags)s"
+ !define INSTNAME "SecondLife%(grid_caps)s"
+ !define SHORTCUT "Second Life (%(grid_caps)s)"
+ !define URLNAME "secondlife%(grid)s"
+ !define UNINSTALL_SETTINGS 1
+ Caption "Second Life %(grid)s ${VERSION}"
+ """
+ else:
+ # some other channel on some grid
+ installer_file = "Second_Life_%(version_dashes)s_%(channel_oneword)s_Setup.exe"
+ grid_vars_template = """
+ OutFile "%(installer_file)s"
+ !define INSTFLAGS "%(flags)s"
+ !define INSTNAME "SecondLife%(channel_oneword)s"
+ !define SHORTCUT "%(channel)s"
+ !define URLNAME "secondlife"
+ !define UNINSTALL_SETTINGS 1
+ Caption "%(channel)s ${VERSION}"
+ """
+ if 'installer_name' in self.args:
+ installer_file = self.args['installer_name']
+ else:
+ installer_file = installer_file % substitution_strings
+ substitution_strings['installer_file'] = installer_file
+
+ tempfile = "secondlife_setup_tmp.nsi"
+ # the following replaces strings in the nsi template
+ # it also does python-style % substitution
+ self.replace_in("installers/windows/installer_template.nsi", tempfile, {
+ "%%VERSION%%":version_vars,
+ "%%SOURCE%%":self.get_src_prefix(),
+ "%%GRID_VARS%%":grid_vars_template % substitution_strings,
+ "%%INSTALL_FILES%%":self.nsi_file_commands(True),
+ "%%DELETE_FILES%%":self.nsi_file_commands(False)})
+
+ # We use the Unicode version of NSIS, available from
+ # http://www.scratchpaper.com/
+ # Check two paths, one for Program Files, and one for Program Files (x86).
+ # Yay 64bit windows.
+ NSIS_path = os.path.expandvars('${ProgramFiles}\\NSIS\\Unicode\\makensis.exe')
+ if not os.path.exists(NSIS_path):
+ NSIS_path = os.path.expandvars('${ProgramFiles(x86)}\\NSIS\\Unicode\\makensis.exe')
+ self.run_command('"' + proper_windows_path(NSIS_path) + '" ' + self.dst_path_of(tempfile))
+ # self.remove(self.dst_path_of(tempfile))
+ # If we're on a build machine, sign the code using our Authenticode certificate. JC
+ sign_py = 'C:\\buildscripts\\code-signing\\sign.py'
+ if os.path.exists(sign_py):
+ self.run_command(sign_py + ' ' + self.dst_path_of(installer_file))
+ else:
+ print "Skipping code signing,", sign_py, "does not exist"
+ self.created_path(self.dst_path_of(installer_file))
+ self.package_file = installer_file
+
+
+class DarwinManifest(ViewerManifest):
+ def construct(self):
+ # copy over the build result (this is a no-op if run within the xcode script)
+ self.path(self.args['configuration'] + "/Second Life.app", dst="")
+
+ if self.prefix(src="", dst="Contents"): # everything goes in Contents
+ self.path("Info-SecondLife.plist", dst="Info.plist")
+
+ # copy additional libs in <bundle>/Contents/MacOS/
+ self.path("../../libraries/universal-darwin/lib_release/libndofdev.dylib", dst="MacOS/libndofdev.dylib")
+
+ # most everything goes in the Resources directory
+ if self.prefix(src="", dst="Resources"):
+ super(DarwinManifest, self).construct()
+
+ if self.prefix("cursors_mac"):
+ self.path("*.tif")
+ self.end_prefix("cursors_mac")
+
+ self.path("licenses-mac.txt", dst="licenses.txt")
+ self.path("featuretable_mac.txt")
+ self.path("SecondLife.nib")
+
+ # If we are not using the default channel, use the 'Firstlook
+ # icon' to show that it isn't a stable release.
+ if self.default_channel() and self.default_grid():
+ self.path("secondlife.icns")
+ else:
+ self.path("secondlife_firstlook.icns", "secondlife.icns")
+ self.path("SecondLife.nib")
+
+ # Translations
+ self.path("English.lproj")
+ self.path("German.lproj")
+ self.path("Japanese.lproj")
+ self.path("Korean.lproj")
+ self.path("da.lproj")
+ self.path("es.lproj")
+ self.path("fr.lproj")
+ self.path("hu.lproj")
+ self.path("it.lproj")
+ self.path("nl.lproj")
+ self.path("pl.lproj")
+ self.path("pt.lproj")
+ self.path("ru.lproj")
+ self.path("tr.lproj")
+ self.path("uk.lproj")
+ self.path("zh-Hans.lproj")
+
+ # SLVoice and vivox lols
+ self.path("vivox-runtime/universal-darwin/libalut.dylib", "libalut.dylib")
+ self.path("vivox-runtime/universal-darwin/libopenal.dylib", "libopenal.dylib")
+ self.path("vivox-runtime/universal-darwin/libortp.dylib", "libortp.dylib")
+ self.path("vivox-runtime/universal-darwin/libvivoxsdk.dylib", "libvivoxsdk.dylib")
+ self.path("vivox-runtime/universal-darwin/SLVoice", "SLVoice")
+
+ libdir = "../../libraries/universal-darwin/lib_release"
+ dylibs = {}
+
+ # need to get the kdu dll from any of the build directories as well
+ for lib in "llkdu", "llcommon":
+ libfile = "lib%s.dylib" % lib
+ try:
+ self.path(self.find_existing_file(os.path.join(os.pardir,
+ lib,
+ self.args['configuration'],
+ libfile),
+ os.path.join(libdir, libfile)),
+ dst=libfile)
+ except RuntimeError:
+ print "Skipping %s" % libfile
+ dylibs[lib] = False
+ else:
+ dylibs[lib] = True
+
+ if dylibs["llcommon"]:
+ for libfile in ("libapr-1.0.3.7.dylib",
+ "libaprutil-1.0.3.8.dylib",
+ "libexpat.0.5.0.dylib"):
+ self.path(os.path.join(libdir, libfile), libfile)
+
+ #libfmodwrapper.dylib
+ self.path(self.args['configuration'] + "/libfmodwrapper.dylib", "libfmodwrapper.dylib")
+
+ # our apps
+ self.path("../mac_crash_logger/" + self.args['configuration'] + "/mac-crash-logger.app", "mac-crash-logger.app")
+ self.path("../mac_updater/" + self.args['configuration'] + "/mac-updater.app", "mac-updater.app")
+
+ # plugins
+ if self.prefix(src="", dst="llplugin"):
+ self.path("../llplugin/slplugin/" + self.args['configuration'] + "/SLPlugin", "SLPlugin")
+ self.path("../media_plugins/quicktime/" + self.args['configuration'] + "/media_plugin_quicktime.dylib", "media_plugin_quicktime.dylib")
+ self.path("../media_plugins/webkit/" + self.args['configuration'] + "/media_plugin_webkit.dylib", "media_plugin_webkit.dylib")
+ self.path("../../libraries/universal-darwin/lib_release/libllwebkitlib.dylib", "libllwebkitlib.dylib")
+
+ self.path("../media_plugins/awesomium/" + self.args['configuration'] + "/media_plugin_awesomium.dylib", "media_plugin_awesomium.dylib")
+ self.path("../../libraries/universal-darwin/lib_release/Awesomium.framework", "Awesomium.framework")
+
+ self.end_prefix("llplugin")
+
+ # command line arguments for connecting to the proper grid
+ self.put_in_file(self.flags_list(), 'arguments.txt')
+
+ self.end_prefix("Resources")
+
+ self.end_prefix("Contents")
+
+ # NOTE: the -S argument to strip causes it to keep enough info for
+ # annotated backtraces (i.e. function names in the crash log). 'strip' with no
+ # arguments yields a slightly smaller binary but makes crash logs mostly useless.
+ # This may be desirable for the final release. Or not.
+ if ("package" in self.args['actions'] or
+ "unpacked" in self.args['actions']):
+ self.run_command('strip -S "%(viewer_binary)s"' %
+ { 'viewer_binary' : self.dst_path_of('Contents/MacOS/Second Life')})
+
+
+ def package_finish(self):
+ channel_standin = 'Second Life' # hah, our default channel is not usable on its own
+ if not self.default_channel():
+ channel_standin = self.channel()
+
+ imagename="SecondLife_" + '_'.join(self.args['version'])
+
+ # MBW -- If the mounted volume name changes, it breaks the .DS_Store's background image and icon positioning.
+ # If we really need differently named volumes, we'll need to create multiple DS_Store file images, or use some other trick.
+
+ volname="Second Life Installer" # DO NOT CHANGE without understanding comment above
+
+ if self.default_channel():
+ if not self.default_grid():
+ # beta case
+ imagename = imagename + '_' + self.args['grid'].upper()
+ else:
+ # first look, etc
+ imagename = imagename + '_' + self.channel_oneword().upper()
+
+ sparsename = imagename + ".sparseimage"
+ finalname = imagename + ".dmg"
+ # make sure we don't have stale files laying about
+ self.remove(sparsename, finalname)
+
+ self.run_command('hdiutil create "%(sparse)s" -volname "%(vol)s" -fs HFS+ -type SPARSE -megabytes 400 -layout SPUD' % {
+ 'sparse':sparsename,
+ 'vol':volname})
+
+ # mount the image and get the name of the mount point and device node
+ hdi_output = self.run_command('hdiutil attach -private "' + sparsename + '"')
+ devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip()
+ volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip()
+
+ # Copy everything in to the mounted .dmg
+
+ if self.default_channel() and not self.default_grid():
+ app_name = "Second Life " + self.args['grid']
+ else:
+ app_name = channel_standin.strip()
+
+ # Hack:
+ # Because there is no easy way to coerce the Finder into positioning
+ # the app bundle in the same place with different app names, we are
+ # adding multiple .DS_Store files to svn. There is one for release,
+ # one for release candidate and one for first look. Any other channels
+ # will use the release .DS_Store, and will look broken.
+ # - Ambroff 2008-08-20
+ dmg_template = os.path.join(
+ 'installers',
+ 'darwin',
+ '%s-dmg' % "".join(self.channel_unique().split()).lower())
+
+ if not os.path.exists (self.src_path_of(dmg_template)):
+ dmg_template = os.path.join ('installers', 'darwin', 'release-dmg')
+
+ for s,d in {self.get_dst_prefix():app_name + ".app",
+ os.path.join(dmg_template, "_VolumeIcon.icns"): ".VolumeIcon.icns",
+ os.path.join(dmg_template, "background.jpg"): "background.jpg",
+ os.path.join(dmg_template, "_DS_Store"): ".DS_Store"}.items():
+ print "Copying to dmg", s, d
+ self.copy_action(self.src_path_of(s), os.path.join(volpath, d))
+
+ # Hide the background image, DS_Store file, and volume icon file (set their "visible" bit)
+ self.run_command('SetFile -a V "' + os.path.join(volpath, ".VolumeIcon.icns") + '"')
+ self.run_command('SetFile -a V "' + os.path.join(volpath, "background.jpg") + '"')
+ self.run_command('SetFile -a V "' + os.path.join(volpath, ".DS_Store") + '"')
+
+ # Create the alias file (which is a resource file) from the .r
+ self.run_command('rez "' + self.src_path_of("installers/darwin/release-dmg/Applications-alias.r") + '" -o "' + os.path.join(volpath, "Applications") + '"')
+
+ # Set the alias file's alias and custom icon bits
+ self.run_command('SetFile -a AC "' + os.path.join(volpath, "Applications") + '"')
+
+ # Set the disk image root's custom icon bit
+ self.run_command('SetFile -a C "' + volpath + '"')
+
+ # Unmount the image
+ self.run_command('hdiutil detach -force "' + devfile + '"')
+
+ print "Converting temp disk image to final disk image"
+ self.run_command('hdiutil convert "%(sparse)s" -format UDZO -imagekey zlib-level=9 -o "%(final)s"' % {'sparse':sparsename, 'final':finalname})
+ # get rid of the temp file
+ self.package_file = finalname
+ self.remove(sparsename)
+
+class LinuxManifest(ViewerManifest):
+ def construct(self):
+ super(LinuxManifest, self).construct()
+ self.path("licenses-linux.txt","licenses.txt")
+ self.path("res/ll_icon.png","secondlife_icon.png")
+ if self.prefix("linux_tools", dst=""):
+ self.path("client-readme.txt","README-linux.txt")
+ self.path("client-readme-voice.txt","README-linux-voice.txt")
+ self.path("client-readme-joystick.txt","README-linux-joystick.txt")
+ self.path("wrapper.sh","secondlife")
+ self.path("handle_secondlifeprotocol.sh", "etc/handle_secondlifeprotocol.sh")
+ self.path("register_secondlifeprotocol.sh", "etc/register_secondlifeprotocol.sh")
+ self.path("refresh_desktop_app_entry.sh", "etc/refresh_desktop_app_entry.sh")
+ self.path("launch_url.sh","etc/launch_url.sh")
+ self.path("install.sh")
+ self.end_prefix("linux_tools")
+
+ # Create an appropriate gridargs.dat for this package, denoting required grid.
+ self.put_in_file(self.flags_list(), 'etc/gridargs.dat')
+
+
+ def package_finish(self):
+ if 'installer_name' in self.args:
+ installer_name = self.args['installer_name']
+ else:
+ installer_name_components = ['SecondLife_', self.args.get('arch')]
+ installer_name_components.extend(self.args['version'])
+ installer_name = "_".join(installer_name_components)
+ if self.default_channel():
+ if not self.default_grid():
+ installer_name += '_' + self.args['grid'].upper()
+ else:
+ installer_name += '_' + self.channel_oneword().upper()
+
+ # Fix access permissions
+ self.run_command("""
+ find %(dst)s -type d | xargs --no-run-if-empty chmod 755;
+ find %(dst)s -type f -perm 0700 | xargs --no-run-if-empty chmod 0755;
+ find %(dst)s -type f -perm 0500 | xargs --no-run-if-empty chmod 0555;
+ find %(dst)s -type f -perm 0600 | xargs --no-run-if-empty chmod 0644;
+ find %(dst)s -type f -perm 0400 | xargs --no-run-if-empty chmod 0444;
+ true""" % {'dst':self.get_dst_prefix() })
+ self.package_file = installer_name + '.tar.bz2'
+
+ # temporarily move directory tree so that it has the right
+ # name in the tarfile
+ self.run_command("mv %(dst)s %(inst)s" % {
+ 'dst': self.get_dst_prefix(),
+ 'inst': self.build_path_of(installer_name)})
+ try:
+ # --numeric-owner hides the username of the builder for
+ # security etc.
+ self.run_command('tar -C %(dir)s --numeric-owner -cjf '
+ '%(inst_path)s.tar.bz2 %(inst_name)s' % {
+ 'dir': self.get_build_prefix(),
+ 'inst_name': installer_name,
+ 'inst_path':self.build_path_of(installer_name)})
+ finally:
+ self.run_command("mv %(inst)s %(dst)s" % {
+ 'dst': self.get_dst_prefix(),
+ 'inst': self.build_path_of(installer_name)})
+
+class Linux_i686Manifest(LinuxManifest):
+ def construct(self):
+ super(Linux_i686Manifest, self).construct()
+
+ # install either the libllkdu we just built, or a prebuilt one, in
+ # decreasing order of preference. for linux package, this goes to bin/
+ for lib, destdir in ("llkdu", "bin"), ("llcommon", "lib"):
+ libfile = "lib%s.so" % lib
+ try:
+ self.path(self.find_existing_file(os.path.join(os.pardir, lib, libfile),
+ '../../libraries/i686-linux/lib_release_client/%s' % libfile),
+ dst=os.path.join(destdir, libfile))
+ # keep this one to preserve syntax, open source mangling removes previous lines
+ pass
+ except RuntimeError:
+ print "Skipping %s - not found" % libfile
+ pass
+
+ self.path("secondlife-stripped","bin/do-not-directly-run-secondlife-bin")
+ self.path("../linux_crash_logger/linux-crash-logger-stripped","bin/linux-crash-logger.bin")
+ self.path("../linux_updater/linux-updater-stripped", "bin/linux-updater.bin")
+ if self.prefix("res-sdl"):
+ self.path("*")
+ # recurse
+ self.end_prefix("res-sdl")
+
+ # plugins
+ if self.prefix(src="", dst="bin/llplugin"):
+ self.path("../llplugin/slplugin/SLPlugin", "SLPlugin")
+ self.path("../media_plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so")
+ self.path("../media_plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_quicktime.so")
+ self.end_prefix("bin/llplugin")
+
+ self.path("featuretable_linux.txt")
+ #self.path("secondlife-i686.supp")
+
+ if self.prefix("../../libraries/i686-linux/lib_release_client", dst="lib"):
+ #self.path("libkdu_v42R.so", "libkdu.so")
+ self.path("libfmod-3.75.so")
+ self.path("libapr-1.so.0")
+ self.path("libaprutil-1.so.0")
+ self.path("libdb-4.2.so")
+ self.path("libcrypto.so.0.9.7")
+ self.path("libexpat.so.1")
+ self.path("libssl.so.0.9.7")
+ self.path("libuuid.so", "libuuid.so.1")
+ self.path("libSDL-1.2.so.0")
+ self.path("libELFIO.so")
+ self.path("libopenjpeg.so.1.3.0", "libopenjpeg.so.1.3")
+ self.path("libalut.so")
+ self.path("libopenal.so", "libopenal.so.1")
+ self.end_prefix("lib")
+
+ # Vivox runtimes
+ if self.prefix(src="vivox-runtime/i686-linux", dst="bin"):
+ self.path("SLVoice")
+ self.end_prefix()
+ if self.prefix(src="vivox-runtime/i686-linux", dst="lib"):
+ self.path("libortp.so")
+ self.path("libvivoxsdk.so")
+ self.end_prefix("lib")
+
+class Linux_x86_64Manifest(LinuxManifest):
+ def construct(self):
+ super(Linux_x86_64Manifest, self).construct()
+ self.path("secondlife-stripped","bin/do-not-directly-run-secondlife-bin")
+ self.path("../linux_crash_logger/linux-crash-logger-stripped","linux-crash-logger.bin")
+ if self.prefix("res-sdl"):
+ self.path("*")
+ # recurse
+ self.end_prefix("res-sdl")
+
+ self.path("featuretable_linux.txt")
+ self.path("secondlife-i686.supp")
+
+if __name__ == "__main__":
+ main()
diff --git a/indra/newview/zh-Hans.lproj/language.txt b/indra/newview/zh-Hans.lproj/language.txt new file mode 100644 index 0000000000..147d659917 --- /dev/null +++ b/indra/newview/zh-Hans.lproj/language.txt @@ -0,0 +1 @@ +zh
\ No newline at end of file diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index 463eedb4fd..d8c3d45c5f 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -13,7 +13,7 @@ include(LLXML) include(LScript) include(Linking) include(Tut) -include(Boost) + include_directories( ${LLCOMMON_INCLUDE_DIRS} @@ -93,6 +93,7 @@ set(test_SOURCE_FILES set(test_HEADER_FILES CMakeLists.txt + debug.h llpipeutil.h llsdtraits.h lltut.h @@ -126,6 +127,7 @@ target_link_libraries(test ${LLXML_LIBRARIES} ${LSCRIPT_LIBRARIES} ${LLCOMMON_LIBRARIES} + ${EXPAT_LIBRARIES} ${APRICONV_LIBRARIES} ${PTHREAD_LIBRARY} ${WINDOWS_LIBRARIES} @@ -145,16 +147,26 @@ endif (WINDOWS) get_target_property(TEST_EXE test LOCATION) -add_custom_command( +IF(WINDOWS) + set(LD_LIBRARY_PATH ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}) +ELSE(WINDOWS) + set(LD_LIBRARY_PATH ${ARCH_PREBUILT_DIRS}:${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}:/usr/lib) +ENDIF(WINDOWS) + +SET(TEST_CMD ${TEST_EXE} --output=${CMAKE_CURRENT_BINARY_DIR}/cpp_test_results.txt --touch=${CMAKE_CURRENT_BINARY_DIR}/cpp_tests_ok.txt) + +ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/cpp_tests_ok.txt - COMMAND ${TEST_EXE} + COMMAND ${CMAKE_COMMAND} ARGS - --output=${CMAKE_CURRENT_BINARY_DIR}/cpp_test_results.txt - --touch=${CMAKE_CURRENT_BINARY_DIR}/cpp_tests_ok.txt + -DLD_LIBRARY_PATH=${LD_LIBRARY_PATH} + "-DTEST_CMD:STRING=\"${TEST_CMD}\"" + -P ${CMAKE_SOURCE_DIR}/cmake/RunBuildTest.cmake + DEPENDS test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "C++ unit tests" - ) + ) set(test_results ${CMAKE_CURRENT_BINARY_DIR}/cpp_tests_ok.txt) diff --git a/indra/test/debug.h b/indra/test/debug.h new file mode 100644 index 0000000000..a00659d880 --- /dev/null +++ b/indra/test/debug.h @@ -0,0 +1,68 @@ +/** + * @file debug.h + * @author Nat Goodspeed + * @date 2009-05-28 + * @brief Debug output for unit test code + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_DEBUG_H) +#define LL_DEBUG_H + +#include <iostream> + +/***************************************************************************** +* Debugging stuff +*****************************************************************************/ +// This class is intended to illuminate entry to a given block, exit from the +// same block and checkpoints along the way. It also provides a convenient +// place to turn std::cout output on and off. +class Debug +{ +public: + Debug(const std::string& block): + mBlock(block) + { + (*this)("entry"); + } + + ~Debug() + { + (*this)("exit"); + } + + void operator()(const std::string& status) + { +#if defined(DEBUG_ON) + std::cout << mBlock << ' ' << status << std::endl; +#endif + } + +private: + const std::string mBlock; +}; + +// It's often convenient to use the name of the enclosing function as the name +// of the Debug block. +#define DEBUG Debug debug(__FUNCTION__) + +// These BEGIN/END macros are specifically for debugging output -- please +// don't assume you must use such for coroutines in general! They only help to +// make control flow (as well as exception exits) explicit. +#define BEGIN \ +{ \ + DEBUG; \ + try + +#define END \ + catch (...) \ + { \ + debug("*** exceptional "); \ + throw; \ + } \ +} + +#endif /* ! defined(LL_DEBUG_H) */ diff --git a/indra/test/llevents_tut.cpp b/indra/test/llevents_tut.cpp index e401f89b22..31130c3c79 100644 --- a/indra/test/llevents_tut.cpp +++ b/indra/test/llevents_tut.cpp @@ -32,96 +32,10 @@ // other Linden headers #include "lltut.h" #include "stringize.h" +#include "tests/listener.h" using boost::assign::list_of; -/***************************************************************************** -* test listener class -*****************************************************************************/ -class Listener; -std::ostream& operator<<(std::ostream&, const Listener&); - -class Listener -{ -public: - Listener(const std::string& name): - mName(name) - { -// std::cout << *this << ": ctor\n"; - } - Listener(const Listener& that): - mName(that.mName), - mLastEvent(that.mLastEvent) - { -// std::cout << *this << ": copy\n"; - } - virtual ~Listener() - { -// std::cout << *this << ": dtor\n"; - } - std::string getName() const { return mName; } - bool call(const LLSD& event) - { -// std::cout << *this << "::call(" << event << ")\n"; - mLastEvent = event; - return false; - } - bool callstop(const LLSD& event) - { -// std::cout << *this << "::callstop(" << event << ")\n"; - mLastEvent = event; - return true; - } - LLSD getLastEvent() const - { -// std::cout << *this << "::getLastEvent() -> " << mLastEvent << "\n"; - return mLastEvent; - } - void reset(const LLSD& to = LLSD()) - { -// std::cout << *this << "::reset(" << to << ")\n"; - mLastEvent = to; - } - -private: - std::string mName; - LLSD mLastEvent; -}; - -std::ostream& operator<<(std::ostream& out, const Listener& listener) -{ - out << "Listener(" << listener.getName() /* << "@" << &listener */ << ')'; - return out; -} - -struct Collect -{ - bool add(const std::string& bound, const LLSD& event) - { - result.push_back(bound); - return false; - } - void clear() { result.clear(); } - typedef std::vector<std::string> StringList; - StringList result; -}; - -std::ostream& operator<<(std::ostream& out, const Collect::StringList& strings) -{ - out << '('; - Collect::StringList::const_iterator begin(strings.begin()), end(strings.end()); - if (begin != end) - { - out << '"' << *begin << '"'; - while (++begin != end) - { - out << ", \"" << *begin << '"'; - } - } - out << ')'; - return out; -} - template<typename T> T make(const T& value) { return value; } @@ -174,14 +88,7 @@ namespace tut // default combiner is defined to return the value returned by the // last listener, which is meaningless if there were no listeners. per_frame.post(0); - // NOTE: boost::bind() saves its arguments by VALUE! If you pass an - // object instance rather than a pointer, you'll end up binding to an - // internal copy of that instance! Use boost::ref() to capture a - // reference instead. - LLBoundListener connection = per_frame.listen(listener0.getName(), - boost::bind(&Listener::call, - boost::ref(listener0), - _1)); + LLBoundListener connection = listener0.listenTo(per_frame); ensure("connected", connection.connected()); ensure("not blocked", ! connection.blocked()); per_frame.post(1); @@ -207,6 +114,10 @@ namespace tut bool threw = false; try { + // NOTE: boost::bind() saves its arguments by VALUE! If you pass + // an object instance rather than a pointer, you'll end up binding + // to an internal copy of that instance! Use boost::ref() to + // capture a reference instead. per_frame.listen(listener0.getName(), // note bug, dup name boost::bind(&Listener::call, boost::ref(listener1), _1)); } @@ -221,8 +132,7 @@ namespace tut } ensure("threw DupListenerName", threw); // do it right this time - per_frame.listen(listener1.getName(), - boost::bind(&Listener::call, boost::ref(listener1), _1)); + listener1.listenTo(per_frame); per_frame.post(5); check_listener("got", listener0, 5); check_listener("got", listener1, 5); @@ -252,16 +162,10 @@ namespace tut LLEventPump& per_frame(pumps.obtain("per-frame")); listener0.reset(0); listener1.reset(0); - LLBoundListener bound0 = per_frame.listen(listener0.getName(), - boost::bind(&Listener::callstop, - boost::ref(listener0), - _1)); - LLBoundListener bound1 = per_frame.listen(listener1.getName(), - boost::bind(&Listener::call, - boost::ref(listener1), - _1), - // after listener0 - make<LLEventPump::NameList>(list_of(listener0.getName()))); + LLBoundListener bound0 = listener0.listenTo(per_frame, &Listener::callstop); + LLBoundListener bound1 = listener1.listenTo(per_frame, &Listener::call, + // after listener0 + make<LLEventPump::NameList>(list_of(listener0.getName()))); ensure("enabled", per_frame.enabled()); ensure("connected 0", bound0.connected()); ensure("unblocked 0", ! bound0.blocked()); @@ -301,7 +205,7 @@ namespace tut // LLEventQueue. LLEventPump& mainloop(pumps.obtain("mainloop")); ensure("LLEventQueue leaf class", dynamic_cast<LLEventQueue*>(&login)); - login.listen(listener0.getName(), boost::bind(&Listener::call, boost::ref(listener0), _1)); + listener0.listenTo(login); listener0.reset(0); login.post(1); check_listener("waiting for queued event", listener0, 0); @@ -354,11 +258,10 @@ namespace tut { set_test_name("stopListening()"); LLEventPump& login(pumps.obtain("login")); - login.listen(listener0.getName(), boost::bind(&Listener::call, boost::ref(listener0), _1)); + listener0.listenTo(login); login.stopListening(listener0.getName()); // should not throw because stopListening() should have removed name - login.listen(listener0.getName(), - boost::bind(&Listener::callstop, boost::ref(listener0), _1)); + listener0.listenTo(login, &Listener::callstop); LLBoundListener wrong = login.getListener("bogus"); ensure("bogus connection disconnected", ! wrong.connected()); ensure("bogus connection blocked", wrong.blocked()); @@ -378,10 +281,8 @@ namespace tut boost::bind(&LLEventPump::post, boost::ref(filter0), _1)); upstream.listen(filter1.getName(), boost::bind(&LLEventPump::post, boost::ref(filter1), _1)); - filter0.listen(listener0.getName(), - boost::bind(&Listener::call, boost::ref(listener0), _1)); - filter1.listen(listener1.getName(), - boost::bind(&Listener::call, boost::ref(listener1), _1)); + listener0.listenTo(filter0); + listener1.listenTo(filter1); listener0.reset(0); listener1.reset(0); upstream.post(1); @@ -536,7 +437,7 @@ namespace tut // Passing a string LLEventPump name to LLListenerOrPumpName listener0.reset(0); LLEventStream random("random"); - random.listen(listener0.getName(), boost::bind(&Listener::call, boost::ref(listener0), _1)); + listener0.listenTo(random); eventSource("random"); check_listener("got by pump name", listener0, 17); bool threw = false; diff --git a/indra/test/llsdmessagebuilder_tut.cpp b/indra/test/llsdmessagebuilder_tut.cpp index 27ab127772..f4457b6d15 100755 --- a/indra/test/llsdmessagebuilder_tut.cpp +++ b/indra/test/llsdmessagebuilder_tut.cpp @@ -44,6 +44,8 @@ #include "v3dmath.h" #include "v3math.h" #include "v4math.h" +#include "llsdutil.h" +//#include "llsdutil.cpp" #include "llsdutil_math.cpp" #include "lltemplatemessagebuilder.h" diff --git a/indra/test/llsdmessagereader_tut.cpp b/indra/test/llsdmessagereader_tut.cpp index 36cfe5ebfc..f11e148cca 100755 --- a/indra/test/llsdmessagereader_tut.cpp +++ b/indra/test/llsdmessagereader_tut.cpp @@ -42,6 +42,7 @@ #include "message.h" #include "llsdmessagereader.h" #include "llsdutil.h" +#include "llsdutil_math.h" namespace tut { diff --git a/indra/test/llsdutil_tut.cpp b/indra/test/llsdutil_tut.cpp index 0c4bbc2e62..d125bb0005 100644 --- a/indra/test/llsdutil_tut.cpp +++ b/indra/test/llsdutil_tut.cpp @@ -44,12 +44,42 @@ #include "v4math.h" #include "llquaternion.h" #include "llsdutil.h" - +#include "llsdutil_math.h" +#include "stringize.h" +#include <set> +#include <boost/range.hpp> namespace tut { struct llsdutil_data { + void test_matches(const std::string& proto_key, const LLSD& possibles, + const char** begin, const char** end) + { + std::set<std::string> succeed(begin, end); + LLSD prototype(possibles[proto_key]); + for (LLSD::map_const_iterator pi(possibles.beginMap()), pend(possibles.endMap()); + pi != pend; ++pi) + { + std::string match(llsd_matches(prototype, pi->second)); + std::set<std::string>::const_iterator found = succeed.find(pi->first); + if (found != succeed.end()) + { + // This test is supposed to succeed. Comparing to the + // empty string ensures that if the test fails, it will + // display the string received so we can tell what failed. + ensure_equals("match", match, ""); + } + else + { + // This test is supposed to fail. If we get a false match, + // the string 'match' will be empty, which doesn't tell us + // much about which case went awry. So construct a more + // detailed description string. + ensure(proto_key + " shouldn't match " + pi->first, ! match.empty()); + } + } + } }; typedef test_group<llsdutil_data> llsdutil_test;; typedef llsdutil_test::object llsdutil_object; @@ -159,4 +189,207 @@ namespace tut LLSD sd1 = ll_sd_from_color4(c1); ensure_equals("sd -> LLColor4 -> sd", sd, sd1); } + + template<> template<> + void llsdutil_object::test<9>() + { + set_test_name("llsd_matches"); + + // for this test, construct a map of all possible LLSD types + LLSD map; + map.insert("empty", LLSD()); + map.insert("Boolean", LLSD::Boolean()); + map.insert("Integer", LLSD::Integer(0)); + map.insert("Real", LLSD::Real(0.0)); + map.insert("String", LLSD::String("bah")); + map.insert("NumString", LLSD::String("1")); + map.insert("UUID", LLSD::UUID()); + map.insert("Date", LLSD::Date()); + map.insert("URI", LLSD::URI()); + map.insert("Binary", LLSD::Binary()); + map.insert("Map", LLSD().insert("foo", LLSD())); + // Only an empty array can be constructed on the fly + LLSD array; + array.append(LLSD()); + map.insert("Array", array); + + // These iterators are declared outside our various for loops to avoid + // fatal MSVC warning: "I used to be broken, but I'm all better now!" + LLSD::map_const_iterator mi, mend(map.endMap()); + + /*-------------------------- llsd_matches --------------------------*/ + + // empty prototype matches anything + for (mi = map.beginMap(); mi != mend; ++mi) + { + ensure_equals(std::string("empty matches ") + mi->first, llsd_matches(LLSD(), mi->second), ""); + } + + LLSD proto_array, data_array; + for (int i = 0; i < 3; ++i) + { + proto_array.append(LLSD()); + data_array.append(LLSD()); + } + + // prototype array matches only array + for (mi = map.beginMap(); mi != mend; ++mi) + { + ensure(std::string("array doesn't match ") + mi->first, + ! llsd_matches(proto_array, mi->second).empty()); + } + + // data array must be at least as long as prototype array + proto_array.append(LLSD()); + ensure_equals("data array too short", llsd_matches(proto_array, data_array), + "Array size 4 required instead of Array size 3"); + data_array.append(LLSD()); + ensure_equals("data array just right", llsd_matches(proto_array, data_array), ""); + data_array.append(LLSD()); + ensure_equals("data array longer", llsd_matches(proto_array, data_array), ""); + + // array element matching + data_array[0] = LLSD::String(); + ensure_equals("undefined prototype array entry", llsd_matches(proto_array, data_array), ""); + proto_array[0] = LLSD::Binary(); + ensure_equals("scalar prototype array entry", llsd_matches(proto_array, data_array), + "[0]: Binary required instead of String"); + data_array[0] = LLSD::Binary(); + ensure_equals("matching prototype array entry", llsd_matches(proto_array, data_array), ""); + + // build a coupla maps + LLSD proto_map, data_map; + data_map["got"] = LLSD(); + data_map["found"] = LLSD(); + for (LLSD::map_const_iterator dmi(data_map.beginMap()), dmend(data_map.endMap()); + dmi != dmend; ++dmi) + { + proto_map[dmi->first] = dmi->second; + } + proto_map["foo"] = LLSD(); + proto_map["bar"] = LLSD(); + + // prototype map matches only map + for (mi = map.beginMap(); mi != mend; ++mi) + { + ensure(std::string("map doesn't match ") + mi->first, + ! llsd_matches(proto_map, mi->second).empty()); + } + + // data map must contain all keys in prototype map + std::string error(llsd_matches(proto_map, data_map)); + ensure_contains("missing keys", error, "missing keys"); + ensure_contains("missing foo", error, "foo"); + ensure_contains("missing bar", error, "bar"); + ensure_does_not_contain("found found", error, "found"); + ensure_does_not_contain("got got", error, "got"); + data_map["bar"] = LLSD(); + error = llsd_matches(proto_map, data_map); + ensure_contains("missing foo", error, "foo"); + ensure_does_not_contain("got bar", error, "bar"); + data_map["foo"] = LLSD(); + ensure_equals("data map just right", llsd_matches(proto_map, data_map), ""); + data_map["extra"] = LLSD(); + ensure_equals("data map with extra", llsd_matches(proto_map, data_map), ""); + + // map element matching + data_map["foo"] = LLSD::String(); + ensure_equals("undefined prototype map entry", llsd_matches(proto_map, data_map), ""); + proto_map["foo"] = LLSD::Binary(); + ensure_equals("scalar prototype map entry", llsd_matches(proto_map, data_map), + "['foo']: Binary required instead of String"); + data_map["foo"] = LLSD::Binary(); + ensure_equals("matching prototype map entry", llsd_matches(proto_map, data_map), ""); + + // String + { + static const char* matches[] = { "String", "NumString", "Boolean", "Integer", + "Real", "UUID", "Date", "URI" }; + test_matches("String", map, boost::begin(matches), boost::end(matches)); + } + + // Boolean, Integer, Real + static const char* numerics[] = { "Boolean", "Integer", "Real" }; + for (const char **ni = boost::begin(numerics), **nend = boost::end(numerics); + ni != nend; ++ni) + { + static const char* matches[] = { "Boolean", "Integer", "Real", "String", "NumString" }; + test_matches(*ni, map, boost::begin(matches), boost::end(matches)); + } + + // UUID + { + static const char* matches[] = { "UUID", "String", "NumString" }; + test_matches("UUID", map, boost::begin(matches), boost::end(matches)); + } + + // Date + { + static const char* matches[] = { "Date", "String", "NumString" }; + test_matches("Date", map, boost::begin(matches), boost::end(matches)); + } + + // URI + { + static const char* matches[] = { "URI", "String", "NumString" }; + test_matches("URI", map, boost::begin(matches), boost::end(matches)); + } + + // Binary + { + static const char* matches[] = { "Binary" }; + test_matches("Binary", map, boost::begin(matches), boost::end(matches)); + } + + /*-------------------------- llsd_equals ---------------------------*/ + + // Cross-product of each LLSD type with every other + for (LLSD::map_const_iterator lmi(map.beginMap()), lmend(map.endMap()); + lmi != lmend; ++lmi) + { + for (LLSD::map_const_iterator rmi(map.beginMap()), rmend(map.endMap()); + rmi != rmend; ++rmi) + { + // Name this test based on the map keys naming the types of + // interest, e.g "String::Integer". + // We expect the values (xmi->second) to be equal if and only + // if the type names (xmi->first) are equal. + ensure(STRINGIZE(lmi->first << "::" << rmi->first), + bool(lmi->first == rmi->first) == + bool(llsd_equals(lmi->second, rmi->second))); + } + } + + // Array cases + LLSD rarray; + rarray.append(1.0); + rarray.append(2); + rarray.append("3"); + LLSD larray(rarray); + ensure("llsd_equals(equal arrays)", llsd_equals(larray, rarray)); + rarray[2] = "4"; + ensure("llsd_equals(different [2])", ! llsd_equals(larray, rarray)); + rarray = larray; + rarray.append(LLSD::Date()); + ensure("llsd_equals(longer right array)", ! llsd_equals(larray, rarray)); + rarray = larray; + rarray.erase(2); + ensure("llsd_equals(shorter right array)", ! llsd_equals(larray, rarray)); + + // Map cases + LLSD rmap; + rmap["San Francisco"] = 65; + rmap["Phoenix"] = 92; + rmap["Boston"] = 77; + LLSD lmap(rmap); + ensure("llsd_equals(equal maps)", llsd_equals(lmap, rmap)); + rmap["Boston"] = 80; + ensure("llsd_equals(different [\"Boston\"])", ! llsd_equals(lmap, rmap)); + rmap = lmap; + rmap["Atlanta"] = 95; + ensure("llsd_equals(superset right map)", ! llsd_equals(lmap, rmap)); + rmap = lmap; + lmap["Seattle"] = 72; + ensure("llsd_equals(superset left map)", ! llsd_equals(lmap, rmap)); + } } diff --git a/indra/test/lltut.cpp b/indra/test/lltut.cpp index 201e174f9c..e4e0de1ff1 100644 --- a/indra/test/lltut.cpp +++ b/indra/test/lltut.cpp @@ -76,9 +76,13 @@ namespace tut void ensure_equals(const char* m, const LLSD& actual, const LLSD& expected) + { + ensure_equals(std::string(m), actual, expected); + } + + void ensure_equals(const std::string& msg, const LLSD& actual, + const LLSD& expected) { - const std::string& msg = m ? m : ""; - ensure_equals(msg + " type", actual.type(), expected.type()); switch (actual.type()) { @@ -128,7 +132,7 @@ namespace tut { ensure_equals(msg + " map keys", actual_iter->first, expected_iter->first); - ensure_equals((msg + "[" + actual_iter->first + "]").c_str(), + ensure_equals(msg + "[" + actual_iter->first + "]", actual_iter->second, expected_iter->second); ++actual_iter; ++expected_iter; @@ -141,7 +145,7 @@ namespace tut for(int i = 0; i < actual.size(); ++i) { - ensure_equals((msg + llformat("[%d]", i)).c_str(), + ensure_equals(msg + llformat("[%d]", i), actual[i], expected[i]); } return; diff --git a/indra/test/lltut.h b/indra/test/lltut.h index 47ea9d3f9e..ba3791cbd4 100644 --- a/indra/test/lltut.h +++ b/indra/test/lltut.h @@ -121,6 +121,9 @@ namespace tut void ensure_equals(const char* msg, const LLSD& actual, const LLSD& expected); + + void ensure_equals(const std::string& msg, + const LLSD& actual, const LLSD& expected); void ensure_starts_with(const std::string& msg, const std::string& actual, const std::string& expectedStart); diff --git a/indra/test/test.cpp b/indra/test/test.cpp index ba81c6e49e..0ba5758e15 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -64,13 +64,14 @@ namespace tut class LLTestCallback : public tut::callback { public: - LLTestCallback(bool verbose_mode, std::ostream *stream) : + LLTestCallback(bool verbose_mode, std::ostream *stream, bool wait) : mVerboseMode(verbose_mode), mTotalTests(0), mPassedTests(0), mFailedTests(0), mSkippedTests(0), - mStream(stream) + mStream(stream), + mWaitAtExit(wait) { } @@ -137,6 +138,11 @@ public: } run_completed_(std::cout); + if(mWaitAtExit) { + std::cerr << "Waiting for input before exiting..." << std::endl; + std::cin.get(); + } + if (mFailedTests > 0) { exit(1); @@ -176,6 +182,7 @@ protected: int mFailedTests; int mSkippedTests; std::ostream *mStream; + bool mWaitAtExit; }; static const apr_getopt_option_t TEST_CL_OPTIONS[] = @@ -328,7 +335,7 @@ int main(int argc, char **argv) } // run the tests - LLTestCallback callback(verbose_mode, output); + LLTestCallback callback(verbose_mode, output, wait_at_exit); tut::runner.get().set_callback(&callback); if(test_group.empty()) @@ -339,12 +346,6 @@ int main(int argc, char **argv) { tut::runner.get().run_tests(test_group); } - - if (wait_at_exit) - { - std::cerr << "Waiting for input before exiting..." << std::endl; - std::cin.get(); - } if (output) { diff --git a/indra/viewer_components/CMakeLists.txt b/indra/viewer_components/CMakeLists.txt new file mode 100644 index 0000000000..c95c854b7c --- /dev/null +++ b/indra/viewer_components/CMakeLists.txt @@ -0,0 +1,5 @@ +# -*- cmake -*- + +add_subdirectory(login) +add_subdirectory(eventhost) + diff --git a/indra/viewer_components/login/CMakeLists.txt b/indra/viewer_components/login/CMakeLists.txt new file mode 100644 index 0000000000..fb65779eb7 --- /dev/null +++ b/indra/viewer_components/login/CMakeLists.txt @@ -0,0 +1,56 @@ +# -*- cmake -*- + +project(login) + +include(00-Common) +include(LLAddBuildTest) +include(LLCommon) +include(LLMath) +include(LLXML) +include(Pth) + +include_directories( + ${LLCOMMON_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} + ${LLXML_INCLUDE_DIRS} + ${PTH_INCLUDE_DIRS} + ) + +set(login_SOURCE_FILES + lllogin.cpp + ) + +set(login_HEADER_FILES + lllogin.h + ) + +set_source_files_properties(${login_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND + login_SOURCE_FILES + ${login_HEADER_FILES} + ) + +add_library(lllogin + ${login_SOURCE_FILES} + ) + +target_link_libraries(lllogin + ${LLCOMMON_LIBRARIES} + ${LLMATH_LIBRARIES} + ${LLXML_LIBRARIES} + ${PTH_LIBRARIES} + ) + +SET(lllogin_TEST_SOURCE_FILES + lllogin.cpp + ) + +set_source_files_properties( + lllogin.cpp + PROPERTIES + LL_TEST_ADDITIONAL_LIBRARIES "${PTH_LIBRARIES}" + ) + +LL_ADD_PROJECT_UNIT_TESTS(lllogin "${lllogin_TEST_SOURCE_FILES}") diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp new file mode 100644 index 0000000000..c0d35f31d3 --- /dev/null +++ b/indra/viewer_components/login/lllogin.cpp @@ -0,0 +1,367 @@ +/** + * @file lllogin.cpp + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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 <boost/coroutine/coroutine.hpp> +#include "linden_common.h" +#include "llsd.h" +#include "llsdutil.h" + +/*==========================================================================*| +#ifdef LL_WINDOWS + // non-virtual destructor warning, boost::statechart does this intentionally. + #pragma warning (disable : 4265) +#endif +|*==========================================================================*/ + +#include "lllogin.h" + +#include <boost/bind.hpp> + +#include "llcoros.h" +#include "llevents.h" +#include "lleventfilter.h" +#include "lleventcoro.h" + +//********************* +// LLLogin +// *NOTE:Mani - Is this Impl needed now that the state machine runs the show? +class LLLogin::Impl +{ +public: + Impl(): + mPump("login", true) // Create the module's event pump with a tweaked (unique) name. + { + mValidAuthResponse["status"] = LLSD(); + mValidAuthResponse["errorcode"] = LLSD(); + mValidAuthResponse["error"] = LLSD(); + mValidAuthResponse["transfer_rate"] = LLSD(); + } + + void connect(const std::string& uri, const LLSD& credentials); + void disconnect(); + LLEventPump& getEventPump() { return mPump; } + +private: + void sendProgressEvent(const std::string& desc, const LLSD& data = LLSD::emptyMap()) + { + LLSD status_data; + status_data["state"] = desc; + status_data["progress"] = 0.0f; + + if(mAuthResponse.has("transfer_rate")) + { + status_data["transfer_rate"] = mAuthResponse["transfer_rate"]; + } + + if(data.size() != 0) + { + status_data["data"] = data; + } + + mPump.post(status_data); + } + + LLSD validateResponse(const std::string& pumpName, const LLSD& response) + { + // Validate the response. If we don't recognize it, things + // could get ugly. + std::string mismatch(llsd_matches(mValidAuthResponse, response)); + if (! mismatch.empty()) + { + LL_ERRS("LLLogin") << "Received unrecognized event (" << mismatch << ") on " + << pumpName << "pump: " << response + << LL_ENDL; + return LLSD(); + } + + return response; + } + + // In a coroutine's top-level function args, do NOT NOT NOT accept + // references (const or otherwise) to anything but the self argument! Pass + // by value only! + void login_(LLCoros::self& self, std::string uri, LLSD credentials); + + LLEventStream mPump; + LLSD mAuthResponse, mValidAuthResponse; +}; + +void LLLogin::Impl::connect(const std::string& uri, const LLSD& credentials) +{ + // Launch a coroutine with our login_() method. Run the coroutine until + // its first wait; at that point, return here. + std::string coroname = + LLCoros::instance().launch("LLLogin::Impl::login_", + boost::bind(&Impl::login_, this, _1, uri, credentials)); +} + +void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD credentials) +{ + LL_INFOS("LLLogin") << "Entering coroutine " << LLCoros::instance().getName(self) + << " with uri '" << uri << "', credentials " << credentials << LL_ENDL; + // Arriving in SRVRequest state + LLEventStream replyPump("reply", true); + // Should be an array of one or more uri strings. + LLSD rewrittenURIs; + { + LLEventTimeout filter(replyPump); + sendProgressEvent("srvrequest"); + + // Request SRV record. + LL_INFOS("LLLogin") << "Requesting SRV record from " << uri << LL_ENDL; + + // *NOTE:Mani - Completely arbitrary timeout value for SRV request. + filter.errorAfter(5, "SRV Request timed out!"); + + // Make request + LLSD request; + request["op"] = "rewriteURI"; + request["uri"] = uri; + request["reply"] = replyPump.getName(); + rewrittenURIs = postAndWait(self, request, "LLAres", filter); + } // we no longer need the filter + + LLEventPump& xmlrpcPump(LLEventPumps::instance().obtain("LLXMLRPCTransaction")); + + // Loop through the rewrittenURIs, counting attempts along the way. + // Because of possible redirect responses, we may make more than one + // attempt per rewrittenURIs entry. + LLSD::Integer attempts = 0; + for (LLSD::array_const_iterator urit(rewrittenURIs.beginArray()), + urend(rewrittenURIs.endArray()); + urit != urend; ++urit) + { + LLSD request(credentials); + request["reply"] = replyPump.getName(); + request["uri"] = *urit; + std::string status; + + // Loop back to here if login attempt redirects to a different + // request["uri"] + for (;;) + { + ++attempts; + LLSD progress_data; + progress_data["attempt"] = attempts; + progress_data["request"] = request; + sendProgressEvent("authenticating", progress_data); + + // We expect zero or more "Downloading" status events, followed by + // exactly one event with some other status. Use postAndWait() the + // first time, because -- at least in unit-test land -- it's + // possible for the reply to arrive before the post() call + // returns. Subsequent responses, of course, must be awaited + // without posting again. + for (mAuthResponse = validateResponse(replyPump.getName(), + postAndWait(self, request, xmlrpcPump, replyPump, "reply")); + mAuthResponse["status"].asString() == "Downloading"; + mAuthResponse = validateResponse(replyPump.getName(), + waitForEventOn(self, replyPump))) + { + // Still Downloading -- send progress update. + sendProgressEvent("downloading"); + } + status = mAuthResponse["status"].asString(); + + // Okay, we've received our final status event for this + // request. Unless we got a redirect response, break the retry + // loop for the current rewrittenURIs entry. + if (! (status == "Complete" && + mAuthResponse["responses"]["login"].asString() == "indeterminate")) + { + break; + } + + // Here the login service at the current URI is redirecting us + // to some other URI ("indeterminate" -- why not "redirect"?). + // The response should contain another uri to try, with its + // own auth method. + request["uri"] = mAuthResponse["next_url"]; + request["method"] = mAuthResponse["next_method"]; + } // loop back to try the redirected URI + + // Here we're done with redirects for the current rewrittenURIs + // entry. + if (status == "Complete") + { + // StatusComplete does not imply auth success. Check the + // actual outcome of the request. We've already handled the + // "indeterminate" case in the loop above. + sendProgressEvent((mAuthResponse["responses"]["login"].asString() == "true")? + "online" : "offline", + mAuthResponse["responses"]); + return; // Done! + } + // If we don't recognize status at all, trouble + if (! (status == "CURLError" + || status == "XMLRPCError" + || status == "OtherError")) + { + LL_ERRS("LLLogin") << "Unexpected status from " << xmlrpcPump.getName() << " pump: " + << mAuthResponse << LL_ENDL; + return; + } + + // Here status IS one of the errors tested above. + } // Retry if there are any more rewrittenURIs. + + // Here we got through all the rewrittenURIs without succeeding. Tell + // caller this didn't work out so well. Of course, the only failure data + // we can reasonably show are from the last of the rewrittenURIs. + sendProgressEvent("offline", mAuthResponse["responses"]); +} + +void LLLogin::Impl::disconnect() +{ + sendProgressEvent("offline", mAuthResponse["responses"]); +} + +//********************* +// LLLogin +LLLogin::LLLogin() : + mImpl(new LLLogin::Impl()) +{ +} + +LLLogin::~LLLogin() +{ +} + +void LLLogin::connect(const std::string& uri, const LLSD& credentials) +{ + mImpl->connect(uri, credentials); +} + + +void LLLogin::disconnect() +{ + mImpl->disconnect(); +} + +LLEventPump& LLLogin::getEventPump() +{ + return mImpl->getEventPump(); +} + +// The following is the list of important functions that happen in the +// current login process that we want to move to this login module. + +// The list associates to event with the original idle_startup() 'STATE'. + +// Rewrite URIs + // State_LOGIN_AUTH_INIT +// Given a vector of login uris (usually just one), perform a dns lookup for the +// SRV record from each URI. I think this is used to distribute login requests to +// a single URI to multiple hosts. +// This is currently a synchronous action. (See LLSRV::rewriteURI() implementation) +// On dns lookup error the output uris == the input uris. +// +// Input: A vector of login uris +// Output: A vector of login uris +// +// Code: +// std::vector<std::string> uris; +// LLViewerLogin::getInstance()->getLoginURIs(uris); +// std::vector<std::string>::const_iterator iter, end; +// for (iter = uris.begin(), end = uris.end(); iter != end; ++iter) +// { +// std::vector<std::string> rewritten; +// rewritten = LLSRV::rewriteURI(*iter); +// sAuthUris.insert(sAuthUris.end(), +// rewritten.begin(), rewritten.end()); +// } +// sAuthUriNum = 0; + +// Authenticate +// STATE_LOGIN_AUTHENTICATE +// Connect to the login server, presumably login.cgi, requesting the login +// and a slew of related initial connection information. +// This is an asynch action. The final response, whether success or error +// is handled by STATE_LOGIN_PROCESS_REPONSE. +// There is no immediate error or output from this call. +// +// Input: +// URI +// Credentials (first, last, password) +// Start location +// Bool Flags: +// skip optional update +// accept terms of service +// accept critical message +// Last exec event. (crash state of previous session) +// requested optional data (inventory skel, initial outfit, etc.) +// local mac address +// viewer serial no. (md5 checksum?) + +//sAuthUriNum = llclamp(sAuthUriNum, 0, (S32)sAuthUris.size()-1); +//LLUserAuth::getInstance()->authenticate( +// sAuthUris[sAuthUriNum], +// auth_method, +// firstname, +// lastname, +// password, // web_login_key, +// start.str(), +// gSkipOptionalUpdate, +// gAcceptTOS, +// gAcceptCriticalMessage, +// gLastExecEvent, +// requested_options, +// hashed_mac_string, +// LLAppViewer::instance()->getSerialNumber()); + +// +// Download the Response +// STATE_LOGIN_NO_REPONSE_YET and STATE_LOGIN_DOWNLOADING +// I had assumed that this was default behavior of the message system. However... +// During login, the message system is checked only by these two states in idle_startup(). +// I guess this avoids the overhead of checking network messages for those login states +// that don't need to do so, but geez! +// There are two states to do this one function just to update the login +// status text from 'Logging In...' to 'Downloading...' +// + +// +// Handle Login Response +// STATE_LOGIN_PROCESS_RESPONSE +// +// This state handle the result of the request to login. There is a metric ton of +// code in this case. This state will transition to: +// STATE_WORLD_INIT, on success. +// STATE_AUTHENTICATE, on failure. +// STATE_UPDATE_CHECK, to handle user during login interaction like TOS display. +// +// Much of the code in this case belongs on the viewer side of the fence and not in login. +// Login should probably return with a couple of events, success and failure. +// Failure conditions can be specified in the events data pacet to allow the viewer +// to re-engauge login as is appropriate. (Or should there be multiple failure messages?) +// Success is returned with the data requested from the login. According to OGP specs +// there may be intermediate steps before reaching this result in future login +// implementations. diff --git a/indra/viewer_components/login/lllogin.h b/indra/viewer_components/login/lllogin.h new file mode 100644 index 0000000000..0598b4e457 --- /dev/null +++ b/indra/viewer_components/login/lllogin.h @@ -0,0 +1,133 @@ +/** + * @file lllogin.h + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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_LLLOGIN_H +#define LL_LLLOGIN_H + +#include <boost/scoped_ptr.hpp> + +class LLSD; +class LLEventPump; + +/** + * @class LLLogin + * @brief Class to encapsulate the action and state of grid login. + */ +class LLLogin +{ +public: + LLLogin(); + ~LLLogin(); + + /** + * Make a connection to a grid. + * @param uri The 'well known and published' authentication URL. + * @param credentials LLSD data that contians the credentials. + * *NOTE:Mani The credential data can vary depending upon the authentication + * method used. The current interface matches the values passed to + * the XMLRPC login request. + { + method : string, + first : string, + last : string, + passwd : string, + start : string, + skipoptional : bool, + agree_to_tos : bool, + read_critical : bool, + last_exec_event : int, + version : string, + channel : string, + mac : string, + id0 : string, + options : [ strings ] + } + + */ + void connect(const std::string& uri, const LLSD& credentials); + + /** + * Disconnect from a the current connection. + */ + void disconnect(); + + /** + * Retrieve the event pump from this login class. + */ + LLEventPump& getEventPump(); + + /* + Event API + + LLLogin will issue multiple events to it pump to indicate the + progression of states through login. The most important + states are "offline" and "online" which indicate auth failure + and auth success respectively. + + pump: login (tweaked) + These are the events posted to the 'login' + event pump from the login module. + { + state : string, // See below for the list of states. + progress : real // for progress bar. + data : LLSD // Dependent upon state. + } + + States for method 'login_to_simulator' + offline - set initially state and upon failure. data is the server response. + srvrequest - upon uri rewrite request. no data. + authenticating - upon auth request. data, 'attempt' number and 'request' llsd. + downloading - upon ack from auth server, before completion. no data + online - upon auth success. data is server response. + + + Dependencies: + pump: LLAres + LLLogin makes a request for a SRV record from the uri provided by the connect method. + The following event pump should exist to service that request. + pump name: LLAres + request = { + op : "rewriteURI" + uri : string + reply : string + + pump: LLXMLRPCListener + The request merely passes the credentials LLSD along, with one additional + member, 'reply', which is the string name of the event pump to reply on. + + */ + +private: + class Impl; + boost::scoped_ptr<Impl> mImpl; +}; + +#endif // LL_LLLOGIN_H diff --git a/indra/viewer_components/login/tests/lllogin_test.cpp b/indra/viewer_components/login/tests/lllogin_test.cpp new file mode 100644 index 0000000000..e43065d49f --- /dev/null +++ b/indra/viewer_components/login/tests/lllogin_test.cpp @@ -0,0 +1,417 @@ +/** + * @file lllogin_test.cpp + * @author Mark Palange + * @date 2009-02-26 + * @brief Tests of lllogin.cpp. + * + * $LicenseInfo:firstyear=2009&license=internal$ + * Copyright (c) 2009, Linden Research, Inc. + * $/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 "../lllogin.h" +// STL headers +// std headers +#include <iostream> +// external library headers +// other Linden headers +#include "llsd.h" +#include "../../../test/lltut.h" +//#define DEBUG_ON +#include "../../../test/debug.h" +#include "llevents.h" +#include "stringize.h" + +/***************************************************************************** +* Helper classes +*****************************************************************************/ +// This is a listener to receive results from lllogin. +class LoginListener: public LLEventTrackable +{ + std::string mName; + LLSD mLastEvent; + Debug mDebug; +public: + LoginListener(const std::string& name) : + mName(name), + mDebug(stringize(*this)) + {} + + bool call(const LLSD& event) + { + mDebug(STRINGIZE("LoginListener called!: " << event)); + mLastEvent = event; + return false; + } + + LLBoundListener listenTo(LLEventPump& pump) + { + return pump.listen(mName, boost::bind(&LoginListener::call, this, _1)); + } + + LLSD lastEvent() { return mLastEvent; } + + friend std::ostream& operator<<(std::ostream& out, const LoginListener& listener) + { + return out << "LoginListener(" << listener.mName << ')'; + } +}; + +class LLAresListener: public LLEventTrackable +{ + std::string mName; + LLSD mEvent; + bool mImmediateResponse; + bool mMultipleURIResponse; + Debug mDebug; + +public: + LLAresListener(const std::string& name, + bool i = false, + bool m = false + ) : + mName(name), + mImmediateResponse(i), + mMultipleURIResponse(m), + mDebug(stringize(*this)) + {} + + bool handle_event(const LLSD& event) + { + mDebug(STRINGIZE("LLAresListener called!: " << event)); + mEvent = event; + if(mImmediateResponse) + { + sendReply(); + } + return false; + } + + void sendReply() + { + if(mEvent["op"].asString() == "rewriteURI") + { + LLSD result; + if(mMultipleURIResponse) + { + result.append(LLSD("login.foo.com")); + } + result.append(mEvent["uri"]); + LLEventPumps::instance().obtain(mEvent["reply"]).post(result); + } + } + + LLBoundListener listenTo(LLEventPump& pump) + { + return pump.listen(mName, boost::bind(&LLAresListener::handle_event, this, _1)); + } + + friend std::ostream& operator<<(std::ostream& out, const LLAresListener& listener) + { + return out << "LLAresListener(" << listener.mName << ')'; + } +}; + +class LLXMLRPCListener: public LLEventTrackable +{ + std::string mName; + LLSD mEvent; + bool mImmediateResponse; + LLSD mResponse; + Debug mDebug; + +public: + LLXMLRPCListener(const std::string& name, + bool i = false, + const LLSD& response = LLSD() + ) : + mName(name), + mImmediateResponse(i), + mResponse(response), + mDebug(stringize(*this)) + { + if(mResponse.isUndefined()) + { + mResponse["status"] = "Complete"; // StatusComplete + mResponse["errorcode"] = 0; + mResponse["error"] = "dummy response"; + mResponse["transfer_rate"] = 0; + mResponse["responses"]["login"] = true; + } + } + + void setResponse(const LLSD& r) + { + mResponse = r; + } + + bool handle_event(const LLSD& event) + { + mDebug(STRINGIZE("LLXMLRPCListener called!: " << event)); + mEvent = event; + if(mImmediateResponse) + { + sendReply(); + } + return false; + } + + void sendReply() + { + LLEventPumps::instance().obtain(mEvent["reply"]).post(mResponse); + } + + LLBoundListener listenTo(LLEventPump& pump) + { + return pump.listen(mName, boost::bind(&LLXMLRPCListener::handle_event, this, _1)); + } + + friend std::ostream& operator<<(std::ostream& out, const LLXMLRPCListener& listener) + { + return out << "LLXMLRPCListener(" << listener.mName << ')'; + } +}; + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct llviewerlogin_data + { + llviewerlogin_data() : + pumps(LLEventPumps::instance()) + {} + LLEventPumps& pumps; + }; + + typedef test_group<llviewerlogin_data> llviewerlogin_group; + typedef llviewerlogin_group::object llviewerlogin_object; + llviewerlogin_group llviewerlogingrp("llviewerlogin"); + + template<> template<> + void llviewerlogin_object::test<1>() + { + DEBUG; + // Testing login with immediate repsonses from Ares and XMLPRC + // The response from both requests will come before the post request exits. + // This tests an edge case of the login state handling. + LLEventStream llaresPump("LLAres"); // Dummy LLAres pump. + LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump + + bool respond_immediately = true; + // Have 'dummy ares' repsond immediately. + LLAresListener dummyLLAres("dummy_llares", respond_immediately); + dummyLLAres.listenTo(llaresPump); + + // Have dummy XMLRPC respond immediately. + LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc", respond_immediately); + dummyXMLRPC.listenTo(xmlrpcPump); + + LLLogin login; + + LoginListener listener("test_ear"); + listener.listenTo(login.getEventPump()); + + LLSD credentials; + credentials["first"] = "foo"; + credentials["last"] = "bar"; + credentials["passwd"] = "secret"; + + login.connect("login.bar.com", credentials); + + ensure_equals("Online state", listener.lastEvent()["state"].asString(), "online"); + } + + template<> template<> + void llviewerlogin_object::test<2>() + { + DEBUG; + // Tests a successful login in with delayed responses. + // Also includes 'failure' that cause the login module + // To re-attempt connection, once from a basic failure + // and once from the 'indeterminate' response. + + set_test_name("LLLogin multiple srv uris w/ success"); + + // Testing normal login procedure. + LLEventStream llaresPump("LLAres"); // Dummy LLAres pump. + LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump + + bool respond_immediately = false; + bool multiple_addresses = true; + LLAresListener dummyLLAres("dummy_llares", respond_immediately, multiple_addresses); + dummyLLAres.listenTo(llaresPump); + + LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc"); + dummyXMLRPC.listenTo(xmlrpcPump); + + LLLogin login; + + LoginListener listener("test_ear"); + listener.listenTo(login.getEventPump()); + + LLSD credentials; + credentials["first"] = "foo"; + credentials["last"] = "bar"; + credentials["passwd"] = "secret"; + + login.connect("login.bar.com", credentials); + + ensure_equals("SRV state", listener.lastEvent()["state"].asString(), "srvrequest"); + + dummyLLAres.sendReply(); + + // Test Authenticating State prior to first response. + ensure_equals("Auth state 1", listener.lastEvent()["state"].asString(), "authenticating"); + ensure_equals("Attempt 1", listener.lastEvent()["data"]["attempt"].asInteger(), 1); + ensure_equals("URI 1", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.foo.com"); + + // First send emulated LLXMLRPCListener failure, + // this should return login to the authenticating step and increase the attempt + // count. + LLSD data; + data["status"] = "OtherError"; + data["errorcode"] = 0; + data["error"] = "dummy response"; + data["transfer_rate"] = 0; + dummyXMLRPC.setResponse(data); + dummyXMLRPC.sendReply(); + + ensure_equals("Fail back to authenticate 1", listener.lastEvent()["state"].asString(), "authenticating"); + ensure_equals("Attempt 2", listener.lastEvent()["data"]["attempt"].asInteger(), 2); + ensure_equals("URI 2", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.bar.com"); + + // Now send the 'indeterminate' response. + data.clear(); + data["status"] = "Complete"; // StatusComplete + data["errorcode"] = 0; + data["error"] = "dummy response"; + data["transfer_rate"] = 0; + data["responses"]["login"] = "indeterminate"; + data["next_url"] = "login.indeterminate.com"; + data["next_method"] = "test_login_method"; + dummyXMLRPC.setResponse(data); + dummyXMLRPC.sendReply(); + + ensure_equals("Fail back to authenticate 2", listener.lastEvent()["state"].asString(), "authenticating"); + ensure_equals("Attempt 3", listener.lastEvent()["data"]["attempt"].asInteger(), 3); + ensure_equals("URI 3", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.indeterminate.com"); + + // Finally let the auth succeed. + data.clear(); + data["status"] = "Complete"; // StatusComplete + data["errorcode"] = 0; + data["error"] = "dummy response"; + data["transfer_rate"] = 0; + data["responses"]["login"] = "true"; + dummyXMLRPC.setResponse(data); + dummyXMLRPC.sendReply(); + + ensure_equals("Success state", listener.lastEvent()["state"].asString(), "online"); + + login.disconnect(); + + ensure_equals("Disconnected state", listener.lastEvent()["state"].asString(), "offline"); + } + + template<> template<> + void llviewerlogin_object::test<3>() + { + DEBUG; + // Test completed response, that fails to login. + set_test_name("LLLogin valid response, failure (eg. bad credentials)"); + + // Testing normal login procedure. + LLEventStream llaresPump("LLAres"); // Dummy LLAres pump. + LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump + + LLAresListener dummyLLAres("dummy_llares"); + dummyLLAres.listenTo(llaresPump); + + LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc"); + dummyXMLRPC.listenTo(xmlrpcPump); + + LLLogin login; + LoginListener listener("test_ear"); + listener.listenTo(login.getEventPump()); + + LLSD credentials; + credentials["first"] = "who"; + credentials["last"] = "what"; + credentials["passwd"] = "badpasswd"; + + login.connect("login.bar.com", credentials); + + ensure_equals("SRV state", listener.lastEvent()["state"].asString(), "srvrequest"); + + dummyLLAres.sendReply(); + + ensure_equals("Auth state", listener.lastEvent()["state"].asString(), "authenticating"); + + // Send the failed auth request reponse + LLSD data; + data["status"] = "Complete"; + data["errorcode"] = 0; + data["error"] = "dummy response"; + data["transfer_rate"] = 0; + data["responses"]["login"] = "false"; + dummyXMLRPC.setResponse(data); + dummyXMLRPC.sendReply(); + + ensure_equals("Failed to offline", listener.lastEvent()["state"].asString(), "offline"); + } + + template<> template<> + void llviewerlogin_object::test<4>() + { + DEBUG; + // Test incomplete response, that end the attempt. + set_test_name("LLLogin valid response, failure (eg. bad credentials)"); + + // Testing normal login procedure. + LLEventStream llaresPump("LLAres"); // Dummy LLAres pump. + LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump + + LLAresListener dummyLLAres("dummy_llares"); + dummyLLAres.listenTo(llaresPump); + + LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc"); + dummyXMLRPC.listenTo(xmlrpcPump); + + LLLogin login; + LoginListener listener("test_ear"); + listener.listenTo(login.getEventPump()); + + LLSD credentials; + credentials["first"] = "these"; + credentials["last"] = "don't"; + credentials["passwd"] = "matter"; + + login.connect("login.bar.com", credentials); + + ensure_equals("SRV state", listener.lastEvent()["state"].asString(), "srvrequest"); + + dummyLLAres.sendReply(); + + ensure_equals("Auth state", listener.lastEvent()["state"].asString(), "authenticating"); + + // Send the failed auth request reponse + LLSD data; + data["status"] = "OtherError"; + data["errorcode"] = 0; + data["error"] = "dummy response"; + data["transfer_rate"] = 0; + dummyXMLRPC.setResponse(data); + dummyXMLRPC.sendReply(); + + ensure_equals("Failed to offline", listener.lastEvent()["state"].asString(), "offline"); + } +} diff --git a/install.xml b/install.xml index 051213225e..af30cd6c4b 100644 --- a/install.xml +++ b/install.xml @@ -85,9 +85,9 @@ <key>darwin</key> <map> <key>md5sum</key> - <string>abd07d760cdc7d23da3b861f34b09c92</string> + <string>115d8ac44a91efdb173e9b3e478c46b6</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/apr_suite-1.2.8-darwin-20080812.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/apr_suite-1.3.7-darwin-20090805.tar.bz2</uri> </map> <key>linux</key> <map> @@ -106,9 +106,9 @@ <key>windows</key> <map> <key>md5sum</key> - <string>b9d23a69a25fdeed96dcc3bf696b6514</string> + <string>abb339b5147f90e20b6090ee235d77a6</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/apr_suite-1.2.12-windows-20080806.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/apr_suite-1.2.12-windows-20090702.tar.bz2</uri> </map> </map> </map> @@ -231,30 +231,30 @@ <key>darwin</key> <map> <key>md5sum</key> - <string>6be5bca5f4b031b1b79824da5cfd4ddf</string> + <string>74f3a765644927c93fa3bc7acc730552</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.34.1-darwin-20090702a.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.34.1-darwin-20090805.tar.bz2</uri> </map> <key>linux</key> <map> <key>md5sum</key> - <string>113ca35011c916660a8aa55bc1ca462a</string> + <string>8fb4151b883b5f5d2b12da19a6ff8e7d</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.34.1-linux-20090702.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.34.1-linux-20090804.tar.bz2</uri> </map> <key>linux64</key> <map> <key>md5sum</key> - <string>270f64d3aa416cec96f445d58dfcfb6d</string> + <string>77237f33b1740daef0dc1e6c801f68e1</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.34.1-linux64-20090702.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.34.1-linux64-20090804.tar.bz2</uri> </map> <key>windows</key> <map> <key>md5sum</key> - <string>44d4fdf386742b94419c93ad2baa1616</string> + <string>76a436d78f34286a7d13faca5f522f6b</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.34.1-windows-20090702.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.34.1-windows-20090803.tar.bz2</uri> </map> </map> </map> @@ -364,9 +364,9 @@ <key>darwin</key> <map> <key>md5sum</key> - <string>9c5603e328e9f543e0a599d6b25be973</string> + <string>c457a0a041ac4946265889a503d26c3d</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/expat-1.95.8-darwin-20080812.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/expat-1.95.8-darwin-20090805.tar.bz2</uri> </map> <key>linux</key> <map> @@ -1210,6 +1210,53 @@ anguage Infrstructure (CLI) international standard</string> </map> </map> </map> + <key>pth</key> + <map> + <key>copyright</key> + <string>Copyright (c) 1999-2006 Ralf S. Engelschall <rse@gnu.org></string> + <key>description</key> + <string>Portable cooperative threads package, used to support Boost.Coroutine on Mac OS X 10.4</string> + <key>license</key> + <string>lgpl</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>533f4c710a209a6c4f205f81ccc0cfce</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-darwin-20090427.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>c5c2f73847c126e679d925beab48c7d4</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-linux-20090427.tar.bz2</uri> + </map> + <key>linux32</key> + <map> + <key>md5sum</key> + <string>c5c2f73847c126e679d925beab48c7d4</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-linux32-20090427.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>c5c2f73847c126e679d925beab48c7d4</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-linux64-20090427.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>c5c2f73847c126e679d925beab48c7d4</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-windows-20090427.tar.bz2</uri> + </map> + </map> + </map> <key>quicktime</key> <map> <key>copyright</key> @@ -1394,9 +1441,9 @@ anguage Infrstructure (CLI) international standard</string> <key>windows</key> <map> <key>md5sum</key> - <string>789988ea1fd8615137f843fbbe5b6bfa</string> + <string>af723ab6a9b0222613b2de060bcec98c</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/xmlrpc-epi-0.51-windows-20080723.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/xmlrpc-epi-0.51-windows-20090727.tar.bz2</uri> </map> </map> </map> diff --git a/scripts/install.py b/scripts/install.py index 6278fba16c..78b8880b95 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -64,7 +64,6 @@ def add_indra_lib_path(): base_dir = add_indra_lib_path() import copy -import md5 import optparse import os import platform @@ -75,7 +74,12 @@ import tempfile import urllib2 import urlparse -from sets import Set as set, ImmutableSet as frozenset +try: + # Python 2.6 + from hashlib import md5 +except ImportError: + # Python 2.5 and earlier + from md5 import new as md5 from indra.base import llsd from indra.util import helpformatter @@ -106,7 +110,7 @@ class InstallFile(object): return "ifile{%s:%s}" % (self.pkgname, self.url) def _is_md5sum_match(self): - hasher = md5.new(file(self.filename, 'rb').read()) + hasher = md5(file(self.filename, 'rb').read()) if hasher.hexdigest() == self.md5sum: return True return False |