diff options
author | Steve Bennetts <steve@lindenlab.com> | 2009-10-20 10:52:53 -0700 |
---|---|---|
committer | Steve Bennetts <steve@lindenlab.com> | 2009-10-20 10:52:53 -0700 |
commit | afcccc3b36d96ababe61ca04dc6c9e02f4b20d81 (patch) | |
tree | f351783fb94e36ba6229bf4a9531eb4e42a4272f | |
parent | 91fe3e7c790317420619aff1aae9ae14d38fa606 (diff) | |
parent | aa75245a1aee3b64aeed31744e58fe96afa1ce7e (diff) |
merge
424 files changed, 21624 insertions, 10166 deletions
diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000000..eeb6d44996 --- /dev/null +++ b/.hgignore @@ -0,0 +1,45 @@ +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/browser_profile +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/linden_file.dat +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 +^indra/lib/python/mulib.* +^web/locale.* +^web/secondlife.com.* +^web/config.* +^indra/web/dataservice/locale.* +^indra/web/dataservice/lib/shared/vault.* +^indra/web/dataservice/vendor.* diff --git a/etc/message.xml b/etc/message.xml index d2ba9843e4..da08e12aa1 100644 --- a/etc/message.xml +++ b/etc/message.xml @@ -669,11 +669,14 @@ <key>FetchLib</key> <boolean>true</boolean> - <key>ObjectMedia</key> - <boolean>false</boolean> + <key>UploadBakedTexture</key> + <boolean>true</boolean> + + <key>ObjectMedia</key> + <boolean>false</boolean> - <key>ObjectMediaNavigate</key> - <boolean>false</boolean> + <key>ObjectMediaNavigate</key> + <boolean>false</boolean> </map> 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/00-Common.cmake b/indra/cmake/00-Common.cmake index 173e650961..db5495091e 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -182,11 +182,17 @@ endif (LINUX) if (DARWIN) - add_definitions(-DLL_DARWIN=1) + # NOTE (per http://lists.apple.com/archives/darwin-dev/2008/Jan/msg00232.html): + # > Why the bus error? What am I doing wrong? + # This is a known issue where getcontext(3) is writing past the end of the + # ucontext_t struct when _XOPEN_SOURCE is not defined (rdar://problem/5578699 ). + # As a workaround, define _XOPEN_SOURCE before including ucontext.h. + add_definitions(-DLL_DARWIN=1 -D_XOPEN_SOURCE) set(CMAKE_CXX_LINK_FLAGS "-Wl,-headerpad_max_install_names,-search_paths_first") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_CXX_LINK_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mlong-branch") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mlong-branch") + set(DARWIN_extra_cstar_flags "-mlong-branch") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DARWIN_extra_cstar_flags}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${DARWIN_extra_cstar_flags}") # NOTE: it's critical that the optimization flag is put in front. # NOTE: it's critical to have both CXX_FLAGS and C_FLAGS covered. set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O0 ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") @@ -195,13 +201,13 @@ endif (DARWIN) if (LINUX OR DARWIN) - set(GCC_WARNINGS "-Wall -Wno-sign-compare -Wno-trigraphs -Wno-non-virtual-dtor -Woverloaded-virtual") + set(GCC_WARNINGS "-Wall -Wno-sign-compare -Wno-trigraphs") if (NOT GCC_DISABLE_FATAL_WARNINGS) set(GCC_WARNINGS "${GCC_WARNINGS} -Werror") endif (NOT GCC_DISABLE_FATAL_WARNINGS) - set(GCC_CXX_WARNINGS "${GCC_WARNINGS} -Wno-reorder") + set(GCC_CXX_WARNINGS "${GCC_WARNINGS} -Wno-reorder -Wno-non-virtual-dtor -Woverloaded-virtual") set(CMAKE_C_FLAGS "${GCC_WARNINGS} ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "${GCC_CXX_WARNINGS} ${CMAKE_CXX_FLAGS}") 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/Boost.cmake b/indra/cmake/Boost.cmake index 0578ae95ff..efe9ad74d3 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -15,7 +15,7 @@ else (STANDALONE) set(Boost_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include) if (WINDOWS) - set(BOOST_VERSION 1_34_1) + set(BOOST_VERSION 1_39) if (MSVC71) set(BOOST_PROGRAM_OPTIONS_LIBRARY optimized libboost_program_options-vc71-mt-s-${BOOST_VERSION} diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 3ce393b659..4563b59ad2 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -53,6 +53,7 @@ set(cmake_SOURCE_FILES LLPrimitive.cmake LLRender.cmake LLScene.cmake + LLTestCommand.cmake LLUI.cmake LLVFS.cmake LLWindow.cmake @@ -69,7 +70,6 @@ set(cmake_SOURCE_FILES PNG.cmake Python.cmake Prebuilt.cmake - RunBuildTest.cmake TemplateCheck.cmake Tut.cmake UI.cmake 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/GoogleMock.cmake b/indra/cmake/GoogleMock.cmake new file mode 100644 index 0000000000..ca5a8034ba --- /dev/null +++ b/indra/cmake/GoogleMock.cmake @@ -0,0 +1,27 @@ +# -*- cmake -*- +include(Prebuilt) +include(Linking) + +use_prebuilt_binary(googlemock) + +set(GOOGLEMOCK_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include) + +if (LINUX) + set(GOOGLEMOCK_LIBRARIES + gmock + gtest) +elseif(WINDOWS) + set(GOOGLEMOCK_LIBRARIES + gmock) + set(GOOGLEMOCK_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_DIR}/include/gmock + ${LIBS_PREBUILT_DIR}/include/gmock/boost/tr1/tr1) +elseif(DARWIN) + set(GOOGLEMOCK_LIBRARIES + gmock + gtest) +endif(LINUX) + + diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index 008b277960..d8f64199cf 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -1,4 +1,6 @@ # -*- cmake -*- +include(LLTestCommand) +include(GoogleMock) MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources) # Given a project name and a list of sourcefiles (with optional properties on each), @@ -13,6 +15,8 @@ MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources) # # WARNING: do NOT modify this code without working with poppy - # there is another branch that will conflict heavily with any changes here. +INCLUDE(GoogleMock) + IF(LL_TEST_VERBOSE) MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} sources: ${sources}") @@ -41,8 +45,10 @@ MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources) ${LLMATH_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LIBS_OPEN_DIR}/test + ${GOOGLEMOCK_INCLUDE_DIRS} ) SET(alltest_LIBRARIES + ${GOOGLEMOCK_LIBRARIES} ${PTHREAD_LIBRARY} ${WINDOWS_LIBRARIES} ) @@ -51,6 +57,11 @@ MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources) ${CMAKE_SOURCE_DIR}/test/test.h ) + # Use the default flags + if (LINUX) + SET(CMAKE_EXE_LINKER_FLAGS "") + endif (LINUX) + # start the source test executable definitions SET(${project}_TEST_OUTPUT "") FOREACH (source ${sources}) @@ -93,9 +104,9 @@ MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources) MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_INCLUDE_DIRS ${${name}_test_additional_INCLUDE_DIRS}") ENDIF(LL_TEST_VERBOSE) + # Setup target ADD_EXECUTABLE(PROJECT_${project}_TEST_${name} ${${name}_test_SOURCE_FILES}) - # # Per-codefile additional / external project dep and lib dep property extraction # @@ -123,16 +134,20 @@ 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) + + LL_TEST_COMMAND("${LD_LIBRARY_PATH}" ${TEST_CMD}) + SET(TEST_SCRIPT_CMD ${LL_TEST_COMMAND_value}) IF(LL_TEST_VERBOSE) MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_script = ${TEST_SCRIPT_CMD}") ENDIF(LL_TEST_VERBOSE) @@ -176,6 +191,7 @@ FUNCTION(LL_ADD_INTEGRATION_TEST SET(libraries ${library_dependencies} + ${GOOGLEMOCK_LIBRARIES} ${PTHREAD_LIBRARY} ) @@ -212,12 +228,14 @@ FUNCTION(LL_ADD_INTEGRATION_TEST LIST(INSERT test_command test_exe_pos "${TEST_EXE}") ENDIF (test_exe_pos LESS 0) - SET(TEST_SCRIPT_CMD - ${CMAKE_COMMAND} - -DLD_LIBRARY_PATH=${ARCH_PREBUILT_DIRS}:/usr/lib - -DTEST_CMD:STRING="${test_command}" - -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) + + LL_TEST_COMMAND("${LD_LIBRARY_PATH}" ${test_command}) + SET(TEST_SCRIPT_CMD ${LL_TEST_COMMAND_value}) if(TEST_DEBUG) message(STATUS "TEST_SCRIPT_CMD: ${TEST_SCRIPT_CMD}") 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/LLTestCommand.cmake b/indra/cmake/LLTestCommand.cmake new file mode 100644 index 0000000000..fae5640493 --- /dev/null +++ b/indra/cmake/LLTestCommand.cmake @@ -0,0 +1,13 @@ +MACRO(LL_TEST_COMMAND LD_LIBRARY_PATH) + # nat wonders how Kitware can use the term 'function' for a construct that + # cannot return a value. And yet, variables you set inside a FUNCTION are + # local. Try a MACRO instead. + SET(LL_TEST_COMMAND_value + ${PYTHON_EXECUTABLE} + "${CMAKE_SOURCE_DIR}/cmake/run_build_test.py") + IF(LD_LIBRARY_PATH) + LIST(APPEND LL_TEST_COMMAND_value "-l${LD_LIBRARY_PATH}") + ENDIF(LD_LIBRARY_PATH) + LIST(APPEND LL_TEST_COMMAND_value ${ARGN}) +##MESSAGE(STATUS "Will run: ${LL_TEST_COMMAND_value}") +ENDMACRO(LL_TEST_COMMAND) 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/cmake/run_build_test.py b/indra/cmake/run_build_test.py new file mode 100644 index 0000000000..17bce6f434 --- /dev/null +++ b/indra/cmake/run_build_test.py @@ -0,0 +1,111 @@ +#!/usr/bin/python
+"""\
+@file run_build_test.py
+@author Nat Goodspeed
+@date 2009-09-03
+@brief Helper script to allow CMake to run some command after setting
+ environment variables.
+
+CMake has commands to run an external program. But remember that each CMake
+command must be backed by multiple build-system implementations. Unfortunately
+it seems CMake can't promise that every target build system can set specified
+environment variables before running the external program of interest.
+
+This helper script is a workaround. It simply sets the requested environment
+variables and then executes the program specified on the rest of its command
+line.
+
+Example:
+
+python run_build_test.py -DFOO=bar myprog somearg otherarg
+
+sets environment variable FOO=bar, then runs:
+myprog somearg otherarg
+
+$LicenseInfo:firstyear=2009&license=internal$
+Copyright (c) 2009, Linden Research, Inc.
+$/LicenseInfo$
+"""
+
+import os
+import sys
+import subprocess
+
+def main(command, libpath=[], vars={}):
+ """Pass:
+ command is a sequence (e.g. a list) of strings. The first item in the list
+ must be the command name, the rest are its arguments.
+
+ libpath is a sequence of directory pathnames. These will be appended to
+ the platform-specific dynamic library search path environment variable.
+
+ vars is a dict of arbitrary (var, value) pairs to be added to the
+ environment before running 'command'.
+
+ This function runs the specified command, waits for it to terminate and
+ returns its return code. This will be negative if the command terminated
+ with a signal, else it will be the process's specified exit code.
+ """
+ # Handle platform-dependent libpath first.
+ if sys.platform == "win32":
+ lpvars = ["PATH"]
+ elif sys.platform == "darwin":
+ lpvars = ["LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH"]
+ elif sys.platform.startswith("linux"):
+ lpvars = ["LD_LIBRARY_PATH"]
+ else:
+ # No idea what the right pathname might be! But only crump if this
+ # feature is requested.
+ if libpath:
+ raise NotImplemented("run_build_test: unknown platform %s" % sys.platform)
+ lpvars = []
+ for var in lpvars:
+ # Split the existing path. Bear in mind that the variable in question
+ # might not exist; instead of KeyError, just use an empty string.
+ dirs = os.environ.get(var, "").split(os.pathsep)
+ # Append the sequence in libpath
+## print "%s += %r" % (var, libpath)
+ dirs.extend(libpath)
+ # Now rebuild the path string. This way we use a minimum of separators
+ # -- and we avoid adding a pointless separator when libpath is empty.
+ os.environ[var] = os.pathsep.join(dirs)
+ # Now handle arbitrary environment variables. The tricky part is ensuring
+ # that all the keys and values we try to pass are actually strings.
+## if vars:
+## print "Setting:"
+## for key, value in vars.iteritems():
+## print "%s=%s" % (key, value)
+ os.environ.update(dict([(str(key), str(value)) for key, value in vars.iteritems()]))
+ # Run the child process.
+## print "Running: %s" % " ".join(command)
+ return subprocess.call(command)
+
+if __name__ == "__main__":
+ from optparse import OptionParser
+ parser = OptionParser(usage="usage: %prog [options] command args...")
+ # We want optparse support for the options we ourselves handle -- but we
+ # DO NOT want it looking at options for the executable we intend to run,
+ # rejecting them as invalid because we don't define them. So configure the
+ # parser to stop looking for options as soon as it sees the first
+ # positional argument (traditional Unix syntax).
+ parser.disable_interspersed_args()
+ parser.add_option("-D", "--define", dest="vars", default=[], action="append",
+ metavar="VAR=value",
+ help="Add VAR=value to the env variables defined")
+ parser.add_option("-l", "--libpath", dest="libpath", default=[], action="append",
+ metavar="DIR",
+ help="Add DIR to the platform-dependent DLL search path")
+ opts, args = parser.parse_args()
+ # What we have in opts.vars is a list of strings of the form "VAR=value"
+ # or possibly just "VAR". What we want is a dict. We can build that dict by
+ # constructing a list of ["VAR", "value"] pairs -- so split each
+ # "VAR=value" string on the '=' sign (but only once, in case we have
+ # "VAR=some=user=string"). To handle the case of just "VAR", append "" to
+ # the list returned by split(), then slice off anything after the pair we
+ # want.
+ rc = main(command=args, libpath=opts.libpath,
+ vars=dict([(pair.split('=', 1) + [""])[:2] for pair in opts.vars]))
+ if rc not in (None, 0):
+ print >>sys.stderr, "Failure running: %s" % " ".join(args)
+ print >>sys.stderr, "Error: %s" % rc
+ sys.exit((rc < 0) and 255 or rc)
diff --git a/indra/develop.py b/indra/develop.py index b2b494d1b3..7836c97473 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/llsd.py b/indra/lib/python/indra/base/llsd.py index 1190d88663..4527b115f9 100644 --- a/indra/lib/python/indra/base/llsd.py +++ b/indra/lib/python/indra/base/llsd.py @@ -238,7 +238,7 @@ class LLSDXMLFormatter(object): def MAP(self, v): return self.elt( 'map', - ''.join(["%s%s" % (self.elt('key', key), self.generate(value)) + ''.join(["%s%s" % (self.elt('key', self.xml_esc(str(key))), self.generate(value)) for key, value in v.items()])) typeof = type 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/lib/python/indra/util/llsubprocess.py b/indra/lib/python/indra/util/llsubprocess.py index c4c40739ec..7e0e115d14 100644 --- a/indra/lib/python/indra/util/llsubprocess.py +++ b/indra/lib/python/indra/util/llsubprocess.py @@ -90,6 +90,17 @@ all the output, and get the result. child.tochild.close() result = child.poll() if result != -1: + # At this point, the child process has exited and result + # is the return value from the process. Between the time + # we called select() and poll() the process may have + # exited so read all the data left on the child process + # stdout and stderr. + last = child.fromchild.read() + if last: + out.append(last) + last = child.childerr.read() + if last: + err.append(last) child.tochild.close() child.fromchild.close() child.childerr.close() diff --git a/indra/lib/python/indra/util/llversion.py b/indra/lib/python/indra/util/llversion.py index 770b861ddc..2718a85f41 100644 --- a/indra/lib/python/indra/util/llversion.py +++ b/indra/lib/python/indra/util/llversion.py @@ -1,7 +1,7 @@ """@file llversion.py @brief Utility for parsing llcommon/llversion${server}.h for the version string and channel string - Utility that parses svn info for branch and revision + Utility that parses hg or svn info for branch and revision $LicenseInfo:firstyear=2006&license=mit$ @@ -79,8 +79,8 @@ def get_svn_status_matching(regular_expression): status, output = commands.getstatusoutput('svn info %s' % get_src_root()) m = regular_expression.search(output) if not m: - print "Failed to parse svn info output, resultfollows:" - print output + print >> sys.stderr, "Failed to parse svn info output, result follows:" + print >> sys.stderr, output raise Exception, "No matching svn status in "+src_root return m.group(1) @@ -92,4 +92,35 @@ def get_svn_revision(): last_rev_re = re.compile('Last Changed Rev: (\d+)') return get_svn_status_matching(last_rev_re) - +def get_hg_repo(): + status, output = commands.getstatusoutput('hg showconfig paths.default') + if status: + print >> sys.stderr, output + sys.exit(1) + if not output: + print >> sys.stderr, 'ERROR: cannot find repo we cloned from' + sys.exit(1) + return output + +def get_hg_changeset(): + # The right thing to do: + # status, output = commands.getstatusoutput('hg id -i') + # if status: + # print >> sys.stderr, output + # sys.exit(1) + + # The temporary hack: + status, output = commands.getstatusoutput('hg parents --template "{rev}"') + if status: + print >> sys.stderr, output + sys.exit(1) + lines = output.splitlines() + if len(lines) > 1: + print >> sys.stderr, 'ERROR: working directory has %d parents' % len(lines) + return lines[0] + +def using_svn(): + return os.path.isdir(os.path.join(get_src_root(), '.svn')) + +def using_hg(): + return os.path.isdir(os.path.join(get_src_root(), '.hg')) diff --git a/indra/lib/python/indra/util/named_query.py b/indra/lib/python/indra/util/named_query.py index 693b483f79..5c19368240 100644 --- a/indra/lib/python/indra/util/named_query.py +++ b/indra/lib/python/indra/util/named_query.py @@ -48,8 +48,8 @@ from indra.base import llsd from indra.base import config DEBUG = False -NQ_FILE_SUFFIX = None -NQ_FILE_SUFFIX_LEN = None +NQ_FILE_SUFFIX = config.get('named-query-file-suffix', '.nq') +NQ_FILE_SUFFIX_LEN = len(NQ_FILE_SUFFIX) _g_named_manager = None diff --git a/indra/lib/python/indra/util/test_win32_manifest.py b/indra/lib/python/indra/util/test_win32_manifest.py new file mode 100644 index 0000000000..0149b9f43a --- /dev/null +++ b/indra/lib/python/indra/util/test_win32_manifest.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python +# @file test_win32_manifest.py +# @brief Test an assembly binding version and uniqueness in a windows dll or exe. +# +# $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$ + +import sys, os +import tempfile +from xml.dom.minidom import parse + +class AssemblyTestException(Exception): + pass + +class NoManifestException(AssemblyTestException): + pass + +class MultipleBindingsException(AssemblyTestException): + pass + +class UnexpectedVersionException(AssemblyTestException): + pass + +class NoMatchingAssemblyException(AssemblyTestException): + pass + +def get_HKLM_registry_value(key_str, value_str): + import _winreg + reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) + key = _winreg.OpenKey(reg, key_str) + value = _winreg.QueryValueEx(key, value_str)[0] + #print 'Found: %s' % value + return value + +def find_vc_dir(): + supported_versions = (r'8.0', r'9.0') + value_str = (r'ProductDir') + + for version in supported_versions: + key_str = (r'SOFTWARE\Microsoft\VisualStudio\%s\Setup\VC' % + version) + try: + return get_HKLM_registry_value(key_str, value_str) + except WindowsError, err: + x64_key_str = (r'SOFTWARE\Wow6432Node\Microsoft\VisualStudio\%s\Setup\VS' % + version) + try: + return get_HKLM_registry_value(x64_key_str, value_str) + except: + print >> sys.stderr, "Didn't find MS VC version %s " % version + + raise + +def find_mt_path(): + vc_dir = find_vc_dir() + mt_path = '\"%sbin\\mt.exe\"' % vc_dir + return mt_path + +def test_assembly_binding(src_filename, assembly_name, assembly_ver): + print "checking %s dependency %s..." % (src_filename, assembly_name) + + (tmp_file_fd, tmp_file_name) = tempfile.mkstemp(suffix='.xml') + tmp_file = os.fdopen(tmp_file_fd) + tmp_file.close() + + mt_path = find_mt_path() + resource_id = "" + if os.path.splitext(src_filename)[1].lower() == ".dll": + resource_id = ";#2" + system_call = '%s -nologo -inputresource:%s%s -out:%s > NUL' % (mt_path, src_filename, resource_id, tmp_file_name) + print "Executing: %s" % system_call + mt_result = os.system(system_call) + if mt_result == 31: + print "No manifest found in %s" % src_filename + raise NoManifestException() + + manifest_dom = parse(tmp_file_name) + nodes = manifest_dom.getElementsByTagName('assemblyIdentity') + + versions = list() + for node in nodes: + if node.getAttribute('name') == assembly_name: + versions.append(node.getAttribute('version')) + + if len(versions) == 0: + print "No matching assemblies found in %s" % src_filename + raise NoMatchingAssemblyException() + + elif len(versions) > 1: + print "Multiple bindings to %s found:" % assembly_name + print versions + print + raise MultipleBindingsException(versions) + + elif versions[0] != assembly_ver: + print "Unexpected version found for %s:" % assembly_name + print "Wanted %s, found %s" % (assembly_ver, versions[0]) + print + raise UnexpectedVersionException(assembly_ver, versions[0]) + + os.remove(tmp_file_name) + + print "SUCCESS: %s OK!" % src_filename + print + +if __name__ == '__main__': + + print + print "Running test_win32_manifest.py..." + + usage = 'test_win32_manfest <srcFileName> <assemblyName> <assemblyVersion>' + + try: + src_filename = sys.argv[1] + assembly_name = sys.argv[2] + assembly_ver = sys.argv[3] + except: + print "Usage:" + print usage + print + raise + + test_assembly_binding(src_filename, assembly_name, assembly_ver) + + diff --git a/indra/llcharacter/llkeyframestandmotion.cpp b/indra/llcharacter/llkeyframestandmotion.cpp index 1d42298f4d..1ae0ddeea0 100644 --- a/indra/llcharacter/llkeyframestandmotion.cpp +++ b/indra/llcharacter/llkeyframestandmotion.cpp @@ -190,7 +190,7 @@ BOOL LLKeyframeStandMotion::onUpdate(F32 time, U8* joint_mask) if (dot(mPelvisState->getJoint()->getWorldRotation(), mLastGoodPelvisRotation) < ROTATION_THRESHOLD) { mLastGoodPelvisRotation = mPelvisState->getJoint()->getWorldRotation(); - mLastGoodPelvisRotation.normQuat(); + mLastGoodPelvisRotation.normalize(); mTrackAnkles = TRUE; } else if ((mCharacter->getCharacterPosition() - mLastGoodPosition).magVecSquared() > POSITION_THRESHOLD) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 7468e3dde4..e7aaf3c984 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -4,12 +4,19 @@ project(llcommon) include(00-Common) 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) @@ -26,6 +33,7 @@ set(llcommon_SOURCE_FILES llbase32.cpp llbase64.cpp llcommon.cpp + llcoros.cpp llcrc.cpp llcriticaldamp.cpp llcursortypes.cpp @@ -34,6 +42,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 @@ -222,25 +239,63 @@ set_source_files_properties(${llcommon_HEADER_FILES} list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES}) -add_library (llcommon ${llcommon_SOURCE_FILES}) -target_link_libraries(llcommon +if(LLCOMMON_LINK_SHARED) + add_library (llcommon SHARED ${llcommon_SOURCE_FILES}) + + 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 unit tests -INCLUDE(LLAddBuildTest) +add_dependencies(llcommon stage_third_party_libs) + +include(LLAddBuildTest) SET(llcommon_TEST_SOURCE_FILES # unit-testing llcommon is not possible right now as the test-harness *itself* depends upon llcommon, causing a circular dependency. Add your 'unit' tests as integration tests for now. ) LL_ADD_PROJECT_UNIT_TESTS(llcommon "${llcommon_TEST_SOURCE_FILES}") #set(TEST_DEBUG on) -set(test_libs llcommon ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES}) +set(test_libs llcommon ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES} ${GOOGLEMOCK_LIBRARIES}) LL_ADD_INTEGRATION_TEST(commonmisc "" "${test_libs}") LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}") diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h index 8687a24655..c2eb867795 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/llallocator_heap_profile.cpp b/indra/llcommon/llallocator_heap_profile.cpp index d82ee9ed81..0a807702d0 100644 --- a/indra/llcommon/llallocator_heap_profile.cpp +++ b/indra/llcommon/llallocator_heap_profile.cpp @@ -38,6 +38,7 @@ // disable warning about boost::lexical_cast returning uninitialized data // when it fails to parse the string #pragma warning (disable:4701) +#pragma warning (disable:4702) #endif #include <boost/algorithm/string/split.hpp> diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index e32a293f1c..1a052ce62d 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -62,7 +62,7 @@ public: }; #endif -class LLApp : public LLOptionInterface +class LL_COMMON_API LLApp : public LLOptionInterface { 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.cpp b/indra/llcommon/llassettype.cpp index 0b016b81fb..b2a92861cc 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -289,6 +289,7 @@ bool LLAssetType::lookupIsEnsembleCategoryType(EType asset_type) asset_type <= AT_FOLDER_ENSEMBLE_END); } + // static. Generate a good default description void LLAssetType::generateDescriptionFor(LLAssetType::EType asset_type, std::string& description) diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index 5e51188541..33705cd2b1 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -1,204 +1,205 @@ -/** - * @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..377bfaa247 --- /dev/null +++ b/indra/llcommon/llcoros.cpp @@ -0,0 +1,137 @@ +/** + * @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::generateDistinctName(const std::string& prefix) const +{ + // Allowing empty name would make getName()'s not-found return ambiguous. + if (prefix.empty()) + { + LL_ERRS("LLCoros") << "LLCoros::launch(): pass non-empty name string" << LL_ENDL; + } + + // If the specified name isn't already in the map, just use that. + std::string name(prefix); + + // Find the lowest numeric suffix that doesn't collide with an existing + // entry. Start with 2 just to make it more intuitive for any interested + // parties: e.g. "joe", "joe2", "joe3"... + for (int i = 2; ; name = STRINGIZE(prefix << i++)) + { + if (mCoros.find(name) == mCoros.end()) + { + LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; + return name; + } + } +} + +bool LLCoros::kill(const std::string& name) +{ + CoroMap::iterator found = mCoros.find(name); + if (found == mCoros.end()) + { + return false; + } + // Because this is a boost::ptr_map, erasing the map entry also destroys + // the referenced heap object, in this case the boost::coroutine object, + // which will terminate the coroutine. + mCoros.erase(found); + return true; +} + +std::string LLCoros::getNameByID(const void* self_id) const +{ + // Walk the existing coroutines, looking for one from which the 'self_id' + // passed to us comes. + for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi) + { + namespace coro_private = boost::coroutines::detail; + if (static_cast<void*>(coro_private::coroutine_accessor::get_impl(const_cast<coro&>(*mi->second)).get()) + == self_id) + { + return mi->first; + } + } + return ""; +} + +/***************************************************************************** +* MUST BE LAST +*****************************************************************************/ +// Turn off MSVC optimizations for just LLCoros::launchImpl() -- see +// DEV-32777. But MSVC doesn't support push/pop for optimization flags as it +// does for warning suppression, and we really don't want to force +// optimization ON for other code even in Debug or RelWithDebInfo builds. + +#if LL_MSVC +// work around broken optimizations +#pragma warning(disable: 4748) +#pragma optimize("", off) +#endif // LL_MSVC + +std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro) +{ + std::string name(generateDistinctName(prefix)); + mCoros.insert(name, newCoro); + /* Run the coroutine until its first wait, then return here */ + (*newCoro)(std::nothrow); + return name; +} + +#if LL_MSVC +// reenable optimizations +#pragma optimize("", on) +#endif // LL_MSVC 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 35dbeaf16e..a1b8178bfe 100644 --- a/indra/llcommon/llcursortypes.h +++ b/indra/llcommon/llcursortypes.h @@ -71,6 +71,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 40b5f782d6..f8b2f2f163 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: /** @@ -156,10 +156,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); 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 37e922d4b7..5a4c644859 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 fab0a1ef9f..233e9d3389 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -63,12 +63,12 @@ public: 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 @@ -79,14 +79,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); - void setTagLevel(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); + LL_COMMON_API 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 @@ -96,21 +96,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): @@ -128,15 +128,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: @@ -150,17 +150,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(LLLineBuffer*); + LL_COMMON_API void logToFile(const std::string& filename); + LL_COMMON_API void logToFixedBuffer(LLLineBuffer*); // 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 @@ -169,11 +169,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..6b1413d054 --- /dev/null +++ b/indra/llcommon/lleventdispatcher.cpp @@ -0,0 +1,133 @@ +/** + * @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 +} + +LLEventDispatcher::Callable LLEventDispatcher::get(const std::string& name) const +{ + DispatchMap::const_iterator found = mDispatch.find(name); + if (found == mDispatch.end()) + { + return Callable(); + } + return found->second.first; +} + +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..671f2a4d1c --- /dev/null +++ b/indra/llcommon/lleventdispatcher.h @@ -0,0 +1,130 @@ +/**
+ * @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;
+
+ /// Fetch the Callable for the specified name. If no such name was
+ /// registered, return an empty() Callable.
+ Callable get(const std::string& name) 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..a6421ac696 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -38,6 +38,12 @@ #pragma warning (pop) #endif // other Linden headers +#include "stringize.h" +#include "llerror.h" +#include "llsdutil.h" +#if LL_MSVC +#pragma warning (disable : 4702) +#endif /***************************************************************************** * queue_names: specify LLEventPump names that should be instantiated as @@ -56,14 +62,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 +76,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 +258,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 +507,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 576e45d2ae..905d736d62 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -1,317 +1,317 @@ -/** - * @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 - -#if LL_WINDOWS - -// shift off lower 8 bits for lower resolution but longer term timing -// on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing -inline U32 get_cpu_clock_count_32() -{ - U32 ret_val; - __asm - { - _emit 0x0f - _emit 0x31 - shr eax,8 - shl edx,24 - or eax, edx - mov dword ptr [ret_val], eax - } - return ret_val; -} - -// return full timer value, still shifted by 8 bits -inline U64 get_cpu_clock_count_64() -{ - U64 ret_val; - __asm - { - _emit 0x0f - _emit 0x31 - mov eax,eax - mov edx,edx - mov dword ptr [ret_val+4], edx - mov dword ptr [ret_val], eax - } - return ret_val >> 8; -} - -#endif // LL_WINDOWS - -#if (LL_LINUX || LL_SOLARIS || LL_DARWIN) && (defined(__i386__) || defined(__amd64__)) -inline U32 get_cpu_clock_count_32() -{ - U64 x; - __asm__ volatile (".byte 0x0f, 0x31": "=A"(x)); - return (U32)x >> 8; -} - -inline U32 get_cpu_clock_count_64() -{ - U64 x; - __asm__ volatile (".byte 0x0f, 0x31": "=A"(x)); - return x >> 8; -} -#endif - -#if ( LL_DARWIN && !(defined(__i386__) || defined(__amd64__))) || (LL_SOLARIS && defined(__sparc__)) -// -// Mac PPC (deprecated) & Solaris SPARC implementation of CPU clock -// -// Just use gettimeofday implementation for now - -inline U32 get_cpu_clock_count_32() -{ - return (U32)get_clock_count(); -} - -inline U32 get_cpu_clock_count_64() -{ - return get_clock_count(); -} -#endif - -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> - { - friend class DeclareTimer; - public: - ~NamedTimer(); - - enum { HISTORY_NUM = 60 }; - - const std::string& getName() const { return mName; } - NamedTimer* getParent() const { 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; } - - U32 getCountAverage() const { return mCountAverage; } - U32 getCallAverage() const { return mCallAverage; } - - U32 getHistoricalCount(S32 history_index = 0) const; - U32 getHistoricalCalls(S32 history_index = 0) const; - - static NamedTimer& getRootNamedTimer(); - - struct FrameState - { - FrameState(NamedTimer* timerp); - - U32 mSelfTimeCounter; - 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 - }; - - 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(); - - // updates cumulative times and hierarchy, - // can be called multiple times in a frame, at any point - static void processTimes(); - - static void buildHierarchy(); - static void resetFrame(); - static void reset(); - - - // - // members - // - S32 mFrameStateIndex; - - std::string mName; - - U32 mTotalTimeCounter; - - U32 mCountAverage; - U32 mCallAverage; - - U32* mCountHistory; - U32* 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 - - }; - - // used to statically declare a new named timer - class DeclareTimer - : public LLInstanceTracker<DeclareTimer> - { - public: - DeclareTimer(const std::string& name, bool open); - DeclareTimer(const std::string& name); - - static void updateCachedPointers(); - - // convertable to NamedTimer::FrameState for convenient usage of LLFastTimer(declared_timer) - operator NamedTimer::FrameState&() { return *mFrameState; } - private: - NamedTimer& mTimer; - NamedTimer::FrameState* mFrameState; - }; - - -public: - static LLMutex* sLogLock; - static std::queue<LLSD> sLogQueue; - static BOOL sLog; - static BOOL sMetricLog; - - typedef std::vector<NamedTimer::FrameState> info_list_t; - static info_list_t& getFrameStateList(); - - enum RootTimerMarker { ROOT }; - LLFastTimer(RootTimerMarker); - - LLFastTimer(NamedTimer::FrameState& timer) - : mFrameState(&timer) - { -#if FAST_TIMER_ON - NamedTimer::FrameState* frame_state = &timer; - U32 cur_time = get_cpu_clock_count_32(); - mStartSelfTime = cur_time; - mStartTotalTime = cur_time; - - 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; - U32 cur_time = get_cpu_clock_count_32(); - 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 - U32 total_time = cur_time - mStartTotalTime; - last_timer->mStartSelfTime += total_time; -#endif - } - - - // call this once a frame to reset timers - static void nextFrame(); - - // dumps current cumulative frame stats to log - // call nextFrame() to reset timers - static void dumpCurTimes(); - - // 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 U64 sLastFrameTime; - static info_list_t* sTimerInfos; - - U32 mStartSelfTime; // start time + time of all child timers - U32 mStartTotalTime; // 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
+
+#if LL_WINDOWS
+
+// shift off lower 8 bits for lower resolution but longer term timing
+// on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing
+inline U32 get_cpu_clock_count_32()
+{
+ U32 ret_val;
+ __asm
+ {
+ _emit 0x0f
+ _emit 0x31
+ shr eax,8
+ shl edx,24
+ or eax, edx
+ mov dword ptr [ret_val], eax
+ }
+ return ret_val;
+}
+
+// return full timer value, still shifted by 8 bits
+inline U64 get_cpu_clock_count_64()
+{
+ U64 ret_val;
+ __asm
+ {
+ _emit 0x0f
+ _emit 0x31
+ mov eax,eax
+ mov edx,edx
+ mov dword ptr [ret_val+4], edx
+ mov dword ptr [ret_val], eax
+ }
+ return ret_val >> 8;
+}
+
+#endif // LL_WINDOWS
+
+#if (LL_LINUX || LL_SOLARIS || LL_DARWIN) && (defined(__i386__) || defined(__amd64__))
+inline U32 get_cpu_clock_count_32()
+{
+ U64 x;
+ __asm__ volatile (".byte 0x0f, 0x31": "=A"(x));
+ return (U32)x >> 8;
+}
+
+inline U32 get_cpu_clock_count_64()
+{
+ U64 x;
+ __asm__ volatile (".byte 0x0f, 0x31": "=A"(x));
+ return x >> 8;
+}
+#endif
+
+#if ( LL_DARWIN && !(defined(__i386__) || defined(__amd64__))) || (LL_SOLARIS && defined(__sparc__))
+//
+// Mac PPC (deprecated) & Solaris SPARC implementation of CPU clock
+//
+// Just use gettimeofday implementation for now
+
+inline U32 get_cpu_clock_count_32()
+{
+ return (U32)get_clock_count();
+}
+
+inline U32 get_cpu_clock_count_64()
+{
+ return get_clock_count();
+}
+#endif
+
+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>
+ {
+ friend class DeclareTimer;
+ public:
+ ~NamedTimer();
+
+ enum { HISTORY_NUM = 60 };
+
+ const std::string& getName() const { return mName; }
+ NamedTimer* getParent() const { 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; }
+
+ U32 getCountAverage() const { return mCountAverage; }
+ U32 getCallAverage() const { return mCallAverage; }
+
+ U32 getHistoricalCount(S32 history_index = 0) const;
+ U32 getHistoricalCalls(S32 history_index = 0) const;
+
+ static NamedTimer& getRootNamedTimer();
+
+ struct FrameState
+ {
+ FrameState(NamedTimer* timerp);
+
+ U32 mSelfTimeCounter;
+ 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
+ };
+
+ 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();
+
+ // updates cumulative times and hierarchy,
+ // can be called multiple times in a frame, at any point
+ static void processTimes();
+
+ static void buildHierarchy();
+ static void resetFrame();
+ static void reset();
+
+
+ //
+ // members
+ //
+ S32 mFrameStateIndex;
+
+ std::string mName;
+
+ U32 mTotalTimeCounter;
+
+ U32 mCountAverage;
+ U32 mCallAverage;
+
+ U32* mCountHistory;
+ U32* 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
+
+ };
+
+ // used to statically declare a new named timer
+ class LL_COMMON_API DeclareTimer
+ : public LLInstanceTracker<DeclareTimer>
+ {
+ public:
+ DeclareTimer(const std::string& name, bool open);
+ DeclareTimer(const std::string& name);
+
+ static void updateCachedPointers();
+
+ // convertable to NamedTimer::FrameState for convenient usage of LLFastTimer(declared_timer)
+ operator NamedTimer::FrameState&() { return *mFrameState; }
+ private:
+ NamedTimer& mTimer;
+ NamedTimer::FrameState* mFrameState;
+ };
+
+
+public:
+ static LLMutex* sLogLock;
+ static std::queue<LLSD> sLogQueue;
+ static BOOL sLog;
+ static BOOL sMetricLog;
+
+ typedef std::vector<NamedTimer::FrameState> info_list_t;
+ static info_list_t& getFrameStateList();
+
+ enum RootTimerMarker { ROOT };
+ LLFastTimer(RootTimerMarker);
+
+ LLFastTimer(NamedTimer::FrameState& timer)
+ : mFrameState(&timer)
+ {
+#if FAST_TIMER_ON
+ NamedTimer::FrameState* frame_state = &timer;
+ U32 cur_time = get_cpu_clock_count_32();
+ mStartSelfTime = cur_time;
+ mStartTotalTime = cur_time;
+
+ 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;
+ U32 cur_time = get_cpu_clock_count_32();
+ 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
+ U32 total_time = cur_time - mStartTotalTime;
+ last_timer->mStartSelfTime += total_time;
+#endif
+ }
+
+
+ // call this once a frame to reset timers
+ static void nextFrame();
+
+ // dumps current cumulative frame stats to log
+ // call nextFrame() to reset timers
+ static void dumpCurTimes();
+
+ // 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 U64 sLastFrameTime;
+ static info_list_t* sTimerInfos;
+
+ U32 mStartSelfTime; // start time + time of all child timers
+ U32 mStartTotalTime; // 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 01b46d327a..17fdef27d7 100644 --- a/indra/llcommon/llfixedbuffer.h +++ b/indra/llcommon/llfixedbuffer.h @@ -41,7 +41,7 @@ #include "llerrorcontrol.h" // fixed buffer implementation -class LLFixedBuffer : public LLLineBuffer +class LL_COMMON_API LLFixedBuffer : public LLLineBuffer { 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/llkeythrottle.h b/indra/llcommon/llkeythrottle.h index 873f50a65e..7544ab1d11 100644 --- a/indra/llcommon/llkeythrottle.h +++ b/indra/llcommon/llkeythrottle.h @@ -118,6 +118,63 @@ public: THROTTLE_BLOCKED, // rate exceed, block key }; + F64 getActionCount(const T& id) + { + U64 now = 0; + if ( mIsRealtime ) + { + now = LLKeyThrottleImpl<T>::getTime(); + } + else + { + now = LLKeyThrottleImpl<T>::getFrame(); + } + + if (now >= (m.startTime + m.intervalLength)) + { + if (now < (m.startTime + 2 * m.intervalLength)) + { + // prune old data + delete m.prevMap; + m.prevMap = m.currMap; + m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap; + + m.startTime += m.intervalLength; + } + else + { + // lots of time has passed, all data is stale + delete m.prevMap; + delete m.currMap; + m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap; + m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap; + + m.startTime = now; + } + } + + U32 prevCount = 0; + + typename LLKeyThrottleImpl<T>::EntryMap::const_iterator prev = m.prevMap->find(id); + if (prev != m.prevMap->end()) + { + prevCount = prev->second.count; + } + + typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id]; + + // curr.count is the number of keys in + // this current 'time slice' from the beginning of it until now + // prevCount is the number of keys in the previous + // time slice scaled to be one full time slice back from the current + // (now) time. + + // compute current, windowed rate + F64 timeInCurrent = ((F64)(now - m.startTime) / m.intervalLength); + F64 averageCount = curr.count + prevCount * (1.0 - timeInCurrent); + return averageCount; + } + // call each time the key wants use State noteAction(const T& id, S32 weight = 1) { 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 bb598a2be1..48baa50edb 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -1,142 +1,168 @@ -/** - * @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 -#ifndef XML_STATIC -#define XML_STATIC -#endif -#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
+#ifndef XML_STATIC
+#define XML_STATIC
+#endif
+#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
+// CMake automagically defines llcommon_EXPORTS only when building llcommon
+// sources, and only when llcommon is a shared library (i.e. when
+// LL_COMMON_LINK_SHARED). We must still test LL_COMMON_LINK_SHARED because
+// otherwise we can't distinguish between (non-llcommon source) and (llcommon
+// not shared).
+# if defined(llcommon_EXPORTS)
+# define LL_COMMON_API LL_DLLEXPORT
+# else //llcommon_EXPORTS
+# define LL_COMMON_API LL_DLLIMPORT
+# endif //llcommon_EXPORTS
+#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/llprocesslauncher.h b/indra/llcommon/llprocesslauncher.h index a1b8e22691..880562157f 100644 --- a/indra/llcommon/llprocesslauncher.h +++ b/indra/llcommon/llprocesslauncher.h @@ -42,7 +42,7 @@ It also keeps track of whether the process is still running, and can kill it if required. */ -class LLProcessLauncher +class LL_COMMON_API LLProcessLauncher { LOG_CLASS(LLProcessLauncher); public: 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/llsdserialize_xml.cpp b/indra/llcommon/llsdserialize_xml.cpp index c12ca350de..7e1c2e35e0 100644 --- a/indra/llcommon/llsdserialize_xml.cpp +++ b/indra/llcommon/llsdserialize_xml.cpp @@ -37,6 +37,7 @@ #include <deque> #include "apr_base64.h" +#include <boost/regex.hpp> extern "C" { @@ -777,10 +778,17 @@ void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name) case ELEMENT_BINARY: { - S32 len = apr_base64_decode_len(mCurrentContent.c_str()); + // Regex is expensive, but only fix for whitespace in base64, + // created by python and other non-linden systems - DEV-39358 + // Fortunately we have very little binary passing now, + // so performance impact shold be negligible. + poppy 2009-09-04 + boost::regex r; + r.assign("\\s"); + std::string stripped = boost::regex_replace(mCurrentContent, r, ""); + S32 len = apr_base64_decode_len(stripped.c_str()); std::vector<U8> data; data.resize(len); - len = apr_base64_decode_binary(&data[0], mCurrentContent.c_str()); + len = apr_base64_decode_binary(&data[0], stripped.c_str()); data.resize(len); value = data; break; 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.cpp b/indra/llcommon/llstat.cpp index 90dae11793..0bd2609f4a 100644 --- a/indra/llcommon/llstat.cpp +++ b/indra/llcommon/llstat.cpp @@ -43,7 +43,7 @@ // statics -BOOL LLPerfBlock::sStatsEnabled = FALSE; // Flag for detailed information +S32 LLPerfBlock::sStatsFlags = LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS; // Control what is being recorded LLPerfBlock::stat_map_t LLPerfBlock::sStatMap; // Map full path string to LLStatTime objects, tracks all active objects std::string LLPerfBlock::sCurrentStatPath = ""; // Something like "/total_time/physics/physics step" LLStat::stat_map_t LLStat::sStatList; @@ -130,6 +130,7 @@ bool LLStatsConfigFile::loadFile() F32 duration = 0.f; F32 interval = 0.f; + S32 flags = LLPerfBlock::LLSTATS_BASIC_STATS; const char * w = "duration"; if (stats_config.has(w)) @@ -141,8 +142,18 @@ bool LLStatsConfigFile::loadFile() { interval = (F32)stats_config[w].asReal(); } + w = "flags"; + if (stats_config.has(w)) + { + flags = (S32)stats_config[w].asInteger(); + if (flags == LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS && + duration > 0) + { // No flags passed in, but have a duration, so reset to basic stats + flags = LLPerfBlock::LLSTATS_BASIC_STATS; + } + } - mStatsp->setReportPerformanceDuration( duration ); + mStatsp->setReportPerformanceDuration( duration, flags ); mStatsp->setReportPerformanceInterval( interval ); if ( duration > 0 ) @@ -254,13 +265,14 @@ void LLPerfStats::dumpIntervalPerformanceStats() } } -// Set length of performance stat recording -void LLPerfStats::setReportPerformanceDuration( F32 seconds ) +// Set length of performance stat recording. +// If turning stats on, caller must provide flags +void LLPerfStats::setReportPerformanceDuration( F32 seconds, S32 flags /* = LLSTATS_NO_OPTIONAL_STATS */ ) { if ( seconds <= 0.f ) { mReportPerformanceStatEnd = 0.0; - LLPerfBlock::setStatsEnabled( FALSE ); + LLPerfBlock::setStatsFlags(LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS); // Make sure all recording is off mFrameStatsFile.close(); LLPerfBlock::clearDynamicStats(); } @@ -269,8 +281,8 @@ void LLPerfStats::setReportPerformanceDuration( F32 seconds ) mReportPerformanceStatEnd = LLFrameTimer::getElapsedSeconds() + ((F64) seconds); // Clear failure flag to try and create the log file once mFrameStatsFileFailure = FALSE; - LLPerfBlock::setStatsEnabled( TRUE ); mSkipFirstFrameStats = TRUE; // Skip the first report (at the end of this frame) + LLPerfBlock::setStatsFlags(flags); } } @@ -612,11 +624,26 @@ LLPerfBlock::LLPerfBlock(LLStatTime* stat ) : mPredefinedStat(stat), mDynamicSta } } -// Use this constructor for dynamically created LLStatTime objects (not pre-defined) with a multi-part key. -// These are also turned on or off via the switch passed in -LLPerfBlock::LLPerfBlock( const char* key1, const char* key2 ) : mPredefinedStat(NULL), mDynamicStat(NULL) +// Use this constructor for normal, optional LLPerfBlock time slices +LLPerfBlock::LLPerfBlock( const char* key ) : mPredefinedStat(NULL), mDynamicStat(NULL) { - if (!sStatsEnabled) return; + if ((sStatsFlags & LLSTATS_BASIC_STATS) == 0) + { // These are off unless the base set is enabled + return; + } + + initDynamicStat(key); +} + + +// Use this constructor for dynamically created LLPerfBlock time slices +// that are only enabled by specific control flags +LLPerfBlock::LLPerfBlock( const char* key1, const char* key2, S32 flags ) : mPredefinedStat(NULL), mDynamicStat(NULL) +{ + if ((sStatsFlags & flags) == 0) + { + return; + } if (NULL == key2 || strlen(key2) == 0) { @@ -630,10 +657,12 @@ LLPerfBlock::LLPerfBlock( const char* key1, const char* key2 ) : mPredefinedStat } } +// Set up the result data map if dynamic stats are enabled void LLPerfBlock::initDynamicStat(const std::string& key) { // Early exit if dynamic stats aren't enabled. - if (!sStatsEnabled) return; + if (sStatsFlags == LLSTATS_NO_OPTIONAL_STATS) + return; mLastPath = sCurrentStatPath; // Save and restore current path sCurrentStatPath += "/" + key; // Add key to current path diff --git a/indra/llcommon/llstat.h b/indra/llcommon/llstat.h index bad18f46a0..bd73c9a6bb 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 @@ -192,14 +192,23 @@ public: // Use this constructor for pre-defined LLStatTime objects LLPerfBlock(LLStatTime* stat); - // Use this constructor for dynamically created LLStatTime objects (not pre-defined) with a multi-part key - LLPerfBlock( const char* key1, const char* key2 = NULL); + // Use this constructor for normal, optional LLPerfBlock time slices + LLPerfBlock( const char* key ); + // Use this constructor for dynamically created LLPerfBlock time slices + // that are only enabled by specific control flags + LLPerfBlock( const char* key1, const char* key2, S32 flags = LLSTATS_BASIC_STATS ); ~LLPerfBlock(); - static void setStatsEnabled( BOOL enable ) { sStatsEnabled = enable; }; - static S32 getStatsEnabled() { return sStatsEnabled; }; + enum + { // Stats bitfield flags + LLSTATS_NO_OPTIONAL_STATS = 0x00, // No optional stats gathering, just pre-defined LLStatTime objects + LLSTATS_BASIC_STATS = 0x01, // Gather basic optional runtime stats + LLSTATS_SCRIPT_FUNCTIONS = 0x02, // Include LSL function calls + }; + static void setStatsFlags( S32 flags ) { sStatsFlags = flags; }; + static S32 getStatsFlags() { return sStatsFlags; }; static void clearDynamicStats(); // Reset maps to clear out dynamic objects static void addStatsToLLSDandReset( LLSD & stats, // Get current information and clear time bin @@ -213,14 +222,14 @@ private: LLStatTime * mPredefinedStat; // LLStatTime object to get data StatEntry * mDynamicStat; // StatEntryobject to get data - static BOOL sStatsEnabled; // Normally FALSE + static S32 sStatsFlags; // Control what is being recorded static stat_map_t sStatMap; // Map full path string to LLStatTime objects static std::string sCurrentStatPath; // Something like "frame/physics/physics step" }; // ---------------------------------------------------------------------------- -class LLPerfStats +class LL_COMMON_API LLPerfStats { public: LLPerfStats(const std::string& process_name = "unknown", S32 process_pid = 0); @@ -236,7 +245,7 @@ public: BOOL frameStatsIsRunning() { return (mReportPerformanceStatEnd > 0.); }; F32 getReportPerformanceInterval() const { return mReportPerformanceStatInterval; }; void setReportPerformanceInterval( F32 interval ) { mReportPerformanceStatInterval = interval; }; - void setReportPerformanceDuration( F32 seconds ); + void setReportPerformanceDuration( F32 seconds, S32 flags = LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS ); void setProcessName(const std::string& process_name) { mProcessName = process_name; } void setProcessPID(S32 process_pid) { mProcessPID = process_pid; } @@ -256,7 +265,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 eca7e922fd..0f2f05a0d8 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -1,1290 +1,1297 @@ -/** - * @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 <cstdio> -#include <locale> -#include <iomanip> -#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(const std::basic_string<T>& instr, std::vector<std::basic_string<T> >& tokens, const std::basic_string<T>& delims); - 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 void replaceString( std::basic_string<T>& string, std::basic_string<T> target, std::basic_string<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 - -private: - static size_type getSubstitution(const std::basic_string<T>& instr, size_type& start, std::vector<std::basic_string<T> >& tokens); -}; - -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); -} - -//////////////////////////////////////////////////////////// -// NOTE: LLStringUtil::format, getTokens, and support functions moved to llstring.cpp. -// There is no LLWStringUtil::format implementation currently. -// Calling thse for anything other than LLStringUtil will produce link errors. - -//////////////////////////////////////////////////////////// - - -// 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; - while( (found_pos = string.find(target, found_pos)) != std::basic_string<T>::npos ) - { - string[found_pos] = replacement; - found_pos++; // avoid infinite defeat if target == replacement - } -} - -//static -template<class T> -void LLStringUtilBase<T>::replaceString( std::basic_string<T>& string, std::basic_string<T> target, std::basic_string<T> replacement ) -{ - size_type found_pos = 0; - while( (found_pos = string.find(target, found_pos)) != std::basic_string<T>::npos ) - { - string.replace( found_pos, target.length(), replacement ); - found_pos += replacement.length(); // avoid infinite defeat if replacement contains target - } -} - -//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; - } - size_t src_size = string.size(); - char* c_string = new char[src_size + 1]; - if(c_string == NULL) - { - return; - } - copy(c_string, string.c_str(), src_size+1); - char* write_head = &c_string[0]; - for (size_type i = 0; i < src_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 <cstdio>
+#include <locale>
+#include <iomanip>
+#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;
+ LL_COMMON_API static void getTokens(const std::basic_string<T>& instr, std::vector<std::basic_string<T> >& tokens, const std::basic_string<T>& delims);
+ LL_COMMON_API static void formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals);
+ LL_COMMON_API static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, S32 secFromEpoch);
+ LL_COMMON_API static S32 format(std::basic_string<T>& s, const format_map_t& substitutions);
+ LL_COMMON_API static S32 format(std::basic_string<T>& s, const LLSD& substitutions);
+ LL_COMMON_API static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const format_map_t& substitutions);
+ LL_COMMON_API 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 void replaceString( std::basic_string<T>& string, std::basic_string<T> target, std::basic_string<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
+ LL_COMMON_API static void testHarness();
+#endif
+
+private:
+ LL_COMMON_API static size_type getSubstitution(const std::basic_string<T>& instr, size_type& start, std::vector<std::basic_string<T> >& tokens);
+};
+
+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);
+}
+
+////////////////////////////////////////////////////////////
+// NOTE: LLStringUtil::format, getTokens, and support functions moved to llstring.cpp.
+// There is no LLWStringUtil::format implementation currently.
+// Calling thse for anything other than LLStringUtil will produce link errors.
+
+////////////////////////////////////////////////////////////
+
+
+// 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;
+ while( (found_pos = string.find(target, found_pos)) != std::basic_string<T>::npos )
+ {
+ string[found_pos] = replacement;
+ found_pos++; // avoid infinite defeat if target == replacement
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::replaceString( std::basic_string<T>& string, std::basic_string<T> target, std::basic_string<T> replacement )
+{
+ size_type found_pos = 0;
+ while( (found_pos = string.find(target, found_pos)) != std::basic_string<T>::npos )
+ {
+ string.replace( found_pos, target.length(), replacement );
+ found_pos += replacement.length(); // avoid infinite defeat if replacement contains target
+ }
+}
+
+//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;
+ }
+ size_t src_size = string.size();
+ char* c_string = new char[src_size + 1];
+ if(c_string == NULL)
+ {
+ return;
+ }
+ copy(c_string, string.c_str(), src_size+1);
+ char* write_head = &c_string[0];
+ for (size_type i = 0; i < src_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 888361b0b9..d40c9d8dfd 100644 --- a/indra/llcommon/llstringtable.h +++ b/indra/llcommon/llstringtable.h @@ -48,15 +48,17 @@ //# define STRING_TABLE_HASH_MAP 1 #endif -#if LL_WINDOWS -#include <hash_map> -#else -#include <ext/hash_map> +#if STRING_TABLE_HASH_MAP +# if LL_WINDOWS +# include <hash_map> +# else +# include <ext/hash_map> +# endif #endif const U32 MAX_STRINGS_LENGTH = 256; -class LLStringTableEntry +class LL_COMMON_API LLStringTableEntry { public: LLStringTableEntry(const char *str); @@ -69,7 +71,7 @@ public: S32 mCount; }; -class LLStringTable +class LL_COMMON_API LLStringTable { public: LLStringTable(int tablesize); @@ -103,7 +105,7 @@ public: #endif }; -extern LLStringTable gStringTable; +extern LL_COMMON_API LLStringTable gStringTable; //============================================================================ @@ -113,7 +115,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 4737421289..3652eeba72 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -161,8 +161,16 @@ LLOSInfo::LLOSInfo() : 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..8e69e8558a 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(); @@ -178,6 +178,6 @@ private: }; // this operator required for tut -bool operator!=(const LLURI& first, const LLURI& second); +LL_COMMON_API bool operator!=(const LLURI& first, const LLURI& second); #endif // LL_LLURI_H 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/llversionserver.h b/indra/llcommon/llversionserver.h index 23e39ceb08..71c6fc0591 100644 --- a/indra/llcommon/llversionserver.h +++ b/indra/llcommon/llversionserver.h @@ -34,9 +34,9 @@ #define LL_LLVERSIONSERVER_H const S32 LL_VERSION_MAJOR = 1; -const S32 LL_VERSION_MINOR = 29; +const S32 LL_VERSION_MINOR = 31; const S32 LL_VERSION_PATCH = 0; -const S32 LL_VERSION_BUILD = 0; +const S32 LL_VERSION_BUILD = 3256; const char * const LL_CHANNEL = "Second Life Server"; diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h index 2c3e9c7333..082d054ba2 100644 --- a/indra/llcommon/llversionviewer.h +++ b/indra/llcommon/llversionviewer.h @@ -36,7 +36,7 @@ const S32 LL_VERSION_MAJOR = 2; const S32 LL_VERSION_MINOR = 0; const S32 LL_VERSION_PATCH = 0; -const S32 LL_VERSION_BUILD = 0; +const S32 LL_VERSION_BUILD = 3256; const char * const LL_CHANNEL = "Second Life Developer"; 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/metapropertyt.h b/indra/llcommon/metapropertyt.h index 79a536a224..5ad230d1d5 100644 --- a/indra/llcommon/metapropertyt.h +++ b/indra/llcommon/metapropertyt.h @@ -94,6 +94,13 @@ inline const LLReflective* LLMetaPropertyT<LLUUID>::get(const LLReflective* obje } template <> +inline const LLReflective* LLMetaPropertyT<bool>::get(const LLReflective* object) const +{ + checkObjectClass(object); + return NULL; +} + +template <> inline LLSD LLMetaPropertyT<S32>::getLLSD(const LLReflective* object) const { return *(getProperty(object)); @@ -111,6 +118,12 @@ inline LLSD LLMetaPropertyT<LLUUID>::getLLSD(const LLReflective* object) const return *(getProperty(object)); } +template <> +inline LLSD LLMetaPropertyT<bool>::getLLSD(const LLReflective* object) const +{ + return *(getProperty(object)); +} + template<class TObject, class TProperty> class LLMetaPropertyTT : public LLMetaPropertyT<TProperty> { 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/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index f13c69f1e3..6ab48ec34a 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -48,6 +48,18 @@ typedef U32 uint32_t; #endif +std::vector<U8> string_to_vector(std::string str) +{ + // bc LLSD can't... + size_t len = (size_t)str.length(); + std::vector<U8> v(len); + for (size_t i = 0; i < len ; i++) + { + v[i] = str[i]; + } + return v; +} + namespace tut { struct sd_xml_data @@ -107,7 +119,16 @@ namespace tut expected = "<llsd><date>2006-04-24T16:11:33Z</date></llsd>\n"; xml_test("date", expected); - // *FIX: test binary + // Generated by: echo -n 'hello' | openssl enc -e -base64 + std::vector<U8> hello; + hello.push_back('h'); + hello.push_back('e'); + hello.push_back('l'); + hello.push_back('l'); + hello.push_back('o'); + mSD = hello; + expected = "<llsd><binary encoding=\"base64\">aGVsbG8=</binary></llsd>\n"; + xml_test("binary", expected); } template<> template<> @@ -199,6 +220,21 @@ namespace tut xml_test("2 element map", expected); } + template<> template<> + void sd_xml_object::test<6>() + { + // tests with binary + std::string expected; + + // Generated by: echo -n 'hello' | openssl enc -e -base64 + mSD = string_to_vector("hello"); + expected = "<llsd><binary encoding=\"base64\">aGVsbG8=</binary></llsd>\n"; + xml_test("binary", expected); + + mSD = string_to_vector("6|6|asdfhappybox|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|00000000-0000-0000-0000-000000000000|7fffffff|7fffffff|0|0|82000|450fe394-2904-c9ad-214c-a07eb7feec29|(No Description)|0|10|0"); + expected = "<llsd><binary encoding=\"base64\">Nnw2fGFzZGZoYXBweWJveHw2MGU0NGVjNS0zMDVjLTQzYzItOWExOS1iNGI4OWIxYWUyYTZ8NjBlNDRlYzUtMzA1Yy00M2MyLTlhMTktYjRiODliMWFlMmE2fDYwZTQ0ZWM1LTMwNWMtNDNjMi05YTE5LWI0Yjg5YjFhZTJhNnwwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDB8N2ZmZmZmZmZ8N2ZmZmZmZmZ8MHwwfDgyMDAwfDQ1MGZlMzk0LTI5MDQtYzlhZC0yMTRjLWEwN2ViN2ZlZWMyOXwoTm8gRGVzY3JpcHRpb24pfDB8MTB8MA==</binary></llsd>\n"; + xml_test("binary", expected); + } class TestLLSDSerializeData { @@ -637,6 +673,42 @@ namespace tut v.size() + 1); } + template<> template<> + void TestLLSDXMLParsingObject::test<4>() + { + // test handling of binary object in XML + std::string xml; + LLSD expected; + + // Generated by: echo -n 'hello' | openssl enc -e -base64 + expected = string_to_vector("hello"); + xml = "<llsd><binary encoding=\"base64\">aGVsbG8=</binary></llsd>\n"; + ensureParse( + "the word 'hello' packed in binary encoded base64", + xml, + expected, + 1); + + expected = string_to_vector("6|6|asdfhappybox|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|00000000-0000-0000-0000-000000000000|7fffffff|7fffffff|0|0|82000|450fe394-2904-c9ad-214c-a07eb7feec29|(No Description)|0|10|0"); + xml = "<llsd><binary encoding=\"base64\">Nnw2fGFzZGZoYXBweWJveHw2MGU0NGVjNS0zMDVjLTQzYzItOWExOS1iNGI4OWIxYWUyYTZ8NjBlNDRlYzUtMzA1Yy00M2MyLTlhMTktYjRiODliMWFlMmE2fDYwZTQ0ZWM1LTMwNWMtNDNjMi05YTE5LWI0Yjg5YjFhZTJhNnwwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDB8N2ZmZmZmZmZ8N2ZmZmZmZmZ8MHwwfDgyMDAwfDQ1MGZlMzk0LTI5MDQtYzlhZC0yMTRjLWEwN2ViN2ZlZWMyOXwoTm8gRGVzY3JpcHRpb24pfDB8MTB8MA==</binary></llsd>\n"; + ensureParse( + "a common binary blob for object -> agent offline inv transfer", + xml, + expected, + 1); + + expected = string_to_vector("6|6|asdfhappybox|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|00000000-0000-0000-0000-000000000000|7fffffff|7fffffff|0|0|82000|450fe394-2904-c9ad-214c-a07eb7feec29|(No Description)|0|10|0"); + xml = "<llsd><binary encoding=\"base64\">Nnw2fGFzZGZoYXBweWJveHw2MGU0NGVjNS0zMDVjLTQzYzItOWExOS1iNGI4OWIxYWUyYTZ8NjBl\n"; + xml += "NDRlYzUtMzA1Yy00M2MyLTlhMTktYjRiODliMWFlMmE2fDYwZTQ0ZWM1LTMwNWMtNDNjMi05YTE5\n"; + xml += "LWI0Yjg5YjFhZTJhNnwwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDB8N2ZmZmZm\n"; + xml += "ZmZ8N2ZmZmZmZmZ8MHwwfDgyMDAwfDQ1MGZlMzk0LTI5MDQtYzlhZC0yMTRjLWEwN2ViN2ZlZWMy\n"; + xml += "OXwoTm8gRGVzY3JpcHRpb24pfDB8MTB8MA==</binary></llsd>\n"; + ensureParse( + "a common binary blob for object -> agent offline inv transfer", + xml, + expected, + 1); + } /* TODO: test XML parsing diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp index 6a2ebc61f5..beba55416a 100644 --- a/indra/llcommon/tests/llstring_test.cpp +++ b/indra/llcommon/tests/llstring_test.cpp @@ -32,6 +32,7 @@ * $/LicenseInfo$ */ +#include "linden_common.h" #include "../test/lltut.h" #include "../llstring.h" 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/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp index 363486fb9c..2352c8edd7 100644 --- a/indra/llimage/llimagej2c.cpp +++ b/indra/llimage/llimagej2c.cpp @@ -178,8 +178,8 @@ LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C), mMaxBytes(0), mRawDiscardLevel(-1), mRate(0.0f), - mReversible(FALSE) - + mReversible(FALSE), + mAreaUsedForDataSizeCalcs(0) { //We assume here that if we wanted to create via //a dynamic library that the approriate open calls were made @@ -195,6 +195,12 @@ LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C), } mImpl = j2cimpl_create_func(); + + // Clear data size table + for( S32 i = 0; i <= MAX_DISCARD_LEVEL; i++) + { // Array size is MAX_DISCARD_LEVEL+1 + mDataSizes[i] = 0; + } } // virtual @@ -367,9 +373,45 @@ S32 LLImageJ2C::calcHeaderSize() return calcHeaderSizeJ2C(); } + +// calcDataSize() returns how many bytes to read +// to load discard_level (including header and higher discard levels) S32 LLImageJ2C::calcDataSize(S32 discard_level) { - return calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), discard_level, mRate); + discard_level = llclamp(discard_level, 0, MAX_DISCARD_LEVEL); + + if ( mAreaUsedForDataSizeCalcs != (getHeight() * getWidth()) + || mDataSizes[0] == 0) + { + mAreaUsedForDataSizeCalcs = getHeight() * getWidth(); + + S32 level = MAX_DISCARD_LEVEL; // Start at the highest discard + while ( level >= 0 ) + { + mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate); + level--; + } + + /* This is technically a more correct way to calculate the size required + for each discard level, since they should include the size needed for + lower levels. Unfortunately, this doesn't work well and will lead to + download stalls. The true correct way is to parse the header. This will + all go away with http textures at some point. + + // Calculate the size for each discard level. Lower levels (higher quality) + // contain the cumulative size of higher levels + S32 total_size = calcHeaderSizeJ2C(); + + S32 level = MAX_DISCARD_LEVEL; // Start at the highest discard + while ( level >= 0 ) + { // Add in this discard level and all before it + total_size += calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate); + mDataSizes[level] = total_size; + level--; + } + */ + } + return mDataSizes[discard_level]; } S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes) diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h index 23f6ef5fd1..55df7f4429 100644 --- a/indra/llimage/llimagej2c.h +++ b/indra/llimage/llimagej2c.h @@ -87,6 +87,10 @@ protected: void updateRawDiscardLevel(); S32 mMaxBytes; // Maximum number of bytes of data to use... + + S32 mDataSizes[MAX_DISCARD_LEVEL+1]; // Size of data required to reach a given level + U32 mAreaUsedForDataSizeCalcs; // Height * width used to calculate mDataSizes + S8 mRawDiscardLevel; F32 mRate; BOOL mReversible; diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index e2a77f1d1e..5d3fbe5128 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -458,6 +458,39 @@ void LLInventoryItem::setCreationDate(time_t creation_date_utc) mCreationDate = creation_date_utc; } +void LLInventoryItem::accumulatePermissionSlamBits(const LLInventoryItem& old_item) +{ + // Remove any pre-existing II_FLAGS_PERM_OVERWRITE_MASK flags + // because we now detect when they should be set. + setFlags( old_item.getFlags() | (getFlags() & ~(LLInventoryItem::II_FLAGS_PERM_OVERWRITE_MASK)) ); + + // Enforce the PERM_OVERWRITE flags for any masks that are different + // but only for AT_OBJECT's since that is the only asset type that can + // exist in-world (instead of only in-inventory or in-object-contents). + if (LLAssetType::AT_OBJECT == getType()) + { + LLPermissions old_permissions = old_item.getPermissions(); + U32 flags_to_be_set = 0; + if(old_permissions.getMaskNextOwner() != getPermissions().getMaskNextOwner()) + { + flags_to_be_set |= LLInventoryItem::II_FLAGS_OBJECT_SLAM_PERM; + } + if(old_permissions.getMaskEveryone() != getPermissions().getMaskEveryone()) + { + flags_to_be_set |= LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE; + } + if(old_permissions.getMaskGroup() != getPermissions().getMaskGroup()) + { + flags_to_be_set |= LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP; + } + LLSaleInfo old_sale_info = old_item.getSaleInfo(); + if(old_sale_info != getSaleInfo()) + { + flags_to_be_set |= LLInventoryItem::II_FLAGS_OBJECT_SLAM_SALE; + } + setFlags(getFlags() | flags_to_be_set); + } +} const LLSaleInfo& LLInventoryItem::getSaleInfo() const { diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h index 64af6c94f5..bd581e860f 100644 --- a/indra/llinventory/llinventory.h +++ b/indra/llinventory/llinventory.h @@ -93,7 +93,6 @@ public: virtual const LLUUID& getUUID() const; const LLUUID& getParentUUID() const; virtual const LLUUID& getLinkedUUID() const; // get the inventoryID that this item points to, else this item's inventoryID - virtual const std::string& getName() const; virtual LLAssetType::EType getType() const; LLAssetType::EType getActualType() const; // bypasses indirection for linked items @@ -263,6 +262,10 @@ public: void setInventoryType(LLInventoryType::EType inv_type); void setFlags(U32 flags); void setCreationDate(time_t creation_date_utc); + + // Check for changes in permissions masks and sale info + // and set the corresponding bits in mFlags + void accumulatePermissionSlamBits(const LLInventoryItem& old_item); // This is currently only used in the Viewer to handle calling cards // where the creator is actually used to store the target. diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp index f208d82084..ec21ae40e7 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/llinventory/llparcel.h b/indra/llinventory/llparcel.h index aa8391230c..2a9a596912 100644 --- a/indra/llinventory/llparcel.h +++ b/indra/llinventory/llparcel.h @@ -136,9 +136,9 @@ class LLSD; class LLAccessEntry { public: - LLUUID mID; - S32 mTime; - U32 mFlags; + LLUUID mID; // Agent ID + S32 mTime; // Time (unix seconds) when entry expires + U32 mFlags; // Not used - currently should always be zero }; typedef std::map<LLUUID,LLAccessEntry>::iterator access_map_iterator; diff --git a/indra/llinventory/llpermissions.cpp b/indra/llinventory/llpermissions.cpp index 0babf26457..d2e5034734 100644 --- a/indra/llinventory/llpermissions.cpp +++ b/indra/llinventory/llpermissions.cpp @@ -288,6 +288,17 @@ BOOL LLPermissions::setOwnerAndGroup( return allowed; } +//Fix for DEV-33917, last owner isn't used much and has little impact on +//permissions so it's reasonably safe to do this, however, for now, +//limiting the functionality of this routine to objects which are +//group owned. +void LLPermissions::setLastOwner(const LLUUID& last_owner) +{ + if (isGroupOwned()) + mLastOwner = last_owner; +} + + // only call this if you know what you're doing // there are usually perm-bit consequences when the // ownerhsip changes @@ -895,6 +906,8 @@ void LLMetaClassT<LLPermissions>::reflectProperties(LLMetaClass& meta_class) { reflectProperty(meta_class, "mCreator", &LLPermissions::mCreator); reflectProperty(meta_class, "mOwner", &LLPermissions::mOwner); + reflectProperty(meta_class, "mGroup", &LLPermissions::mGroup); + reflectProperty(meta_class, "mIsGroupOwned", &LLPermissions::mIsGroupOwned); } // virtual diff --git a/indra/llinventory/llpermissions.h b/indra/llinventory/llpermissions.h index 864088148f..d5a0881c8f 100644 --- a/indra/llinventory/llpermissions.h +++ b/indra/llinventory/llpermissions.h @@ -232,6 +232,10 @@ public: // ownerhsip changes void yesReallySetOwner(const LLUUID& owner, bool group_owned); + // Last owner doesn't have much in the way of permissions so it's + //not too dangerous to do this. + void setLastOwner(const LLUUID& last_owner); + // saves last owner, sets owner to uuid null, sets group // owned. group_id must be the group of the object (that's who it // is being deeded to) and the object must be group diff --git a/indra/llinventory/lltransactiontypes.h b/indra/llinventory/lltransactiontypes.h index 1cb7308bd4..2c699bcb87 100644 --- a/indra/llinventory/lltransactiontypes.h +++ b/indra/llinventory/lltransactiontypes.h @@ -69,6 +69,12 @@ const S32 TRANS_PARCEL_DIR_FEE = 2003; const S32 TRANS_GROUP_TAX = 2004; // Taxes incurred as part of group membership const S32 TRANS_CLASSIFIED_RENEW = 2005; +// Codes 2100-2999 reserved for recurring billing services +// New codes can be created through an admin interface so may not +// automatically end up in the list below :-( +// So make sure you check the transaction_description table +const S32 TRANS_RECURRING_GENERIC = 2100; + // Codes 3000-3999 reserved for inventory transactions const S32 TRANS_GIVE_INVENTORY = 3000; @@ -84,6 +90,12 @@ const S32 TRANS_DWELL_BONUS = 5007; const S32 TRANS_PAY_OBJECT = 5008; const S32 TRANS_OBJECT_PAYS = 5009; +// Codes 5100-5999 reserved for recurring billing transfers between users +// New codes can be created through an admin interface so may not +// automatically end up in the list below :-( +// So make sure you check the transaction_description table +const S32 TRANS_RECURRING_GENERIC_USER = 5100; + // Codes 6000-6999 reserved for group transactions //const S32 TRANS_GROUP_JOIN = 6000; //reserved for future use const S32 TRANS_GROUP_LAND_DEED = 6001; diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 9006a8a284..7957c32be2 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -64,6 +64,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/llmath.h b/indra/llmath/llmath.h index f85c4f39f4..7a5d51ff76 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -35,6 +35,7 @@ #include <cmath> #include <cstdlib> +#include <complex> #include "lldefs.h" //#include "llstl.h" // *TODO: Remove when LLString is gone //#include "llstring.h" // *TODO: Remove when LLString is gone diff --git a/indra/llmath/llquaternion.cpp b/indra/llmath/llquaternion.cpp index cfd6183ec4..fdcc19d657 100644 --- a/indra/llmath/llquaternion.cpp +++ b/indra/llmath/llquaternion.cpp @@ -121,7 +121,7 @@ void LLQuaternion::quantize16(F32 lower, F32 upper) mQ[VZ] = z; mQ[VS] = s; - normQuat(); + normalize(); } void LLQuaternion::quantize8(F32 lower, F32 upper) @@ -131,7 +131,7 @@ void LLQuaternion::quantize8(F32 lower, F32 upper) mQ[VZ] = U8_to_F32(F32_to_U8_ROUND(mQ[VZ], lower, upper), lower, upper); mQ[VS] = U8_to_F32(F32_to_U8_ROUND(mQ[VS], lower, upper), lower, upper); - normQuat(); + normalize(); } // LLVector3 Magnitude and Normalization Functions @@ -346,7 +346,7 @@ const LLQuaternion& LLQuaternion::setQuat(const LLMatrix4 &mat) // mQ[VZ] = (F32)(cosX*cosY*sinZ - sinX*sinY*cosZ); //#endif // -// normQuat(); +// normalize(); // return (*this); } diff --git a/indra/llmath/llquaternion.h b/indra/llmath/llquaternion.h index 5db9c5be2e..0769f29f23 100644 --- a/indra/llmath/llquaternion.h +++ b/indra/llmath/llquaternion.h @@ -469,20 +469,30 @@ inline const LLQuaternion& operator*=(LLQuaternion &a, const LLQuaternion &b) return a; } +const F32 ONE_PART_IN_A_MILLION = 0.000001f; + inline F32 LLQuaternion::normalize() { F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]); if (mag > FP_MAG_THRESHOLD) { - F32 oomag = 1.f/mag; - mQ[VX] *= oomag; - mQ[VY] *= oomag; - mQ[VZ] *= oomag; - mQ[VS] *= oomag; + // Floating point error can prevent some quaternions from achieving + // exact unity length. When trying to renormalize such quaternions we + // can oscillate between multiple quantized states. To prevent such + // drifts we only renomalize if the length is far enough from unity. + if (fabs(1.f - mag) > ONE_PART_IN_A_MILLION) + { + F32 oomag = 1.f/mag; + mQ[VX] *= oomag; + mQ[VY] *= oomag; + mQ[VZ] *= oomag; + mQ[VS] *= oomag; + } } else { + // we were given a very bad quaternion so we set it to identity mQ[VX] = 0.f; mQ[VY] = 0.f; mQ[VZ] = 0.f; @@ -499,11 +509,15 @@ inline F32 LLQuaternion::normQuat() if (mag > FP_MAG_THRESHOLD) { - F32 oomag = 1.f/mag; - mQ[VX] *= oomag; - mQ[VY] *= oomag; - mQ[VZ] *= oomag; - mQ[VS] *= oomag; + if (fabs(1.f - mag) > ONE_PART_IN_A_MILLION) + { + // only renormalize if length not close enough to 1.0 already + F32 oomag = 1.f/mag; + mQ[VX] *= oomag; + mQ[VY] *= oomag; + mQ[VZ] *= oomag; + mQ[VS] *= oomag; + } } else { 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/llmath/tests/llbbox_test.cpp b/indra/llmath/tests/llbbox_test.cpp index b310baf07f..3031310a5d 100644 --- a/indra/llmath/tests/llbbox_test.cpp +++ b/indra/llmath/tests/llbbox_test.cpp @@ -32,6 +32,8 @@ * $/LicenseInfo$ */ +#include "linden_common.h" + #include "../test/lltut.h" #include "../llbbox.h" diff --git a/indra/llmath/tests/llbboxlocal_test.cpp b/indra/llmath/tests/llbboxlocal_test.cpp index ae75e056d1..fb51deab4a 100644 --- a/indra/llmath/tests/llbboxlocal_test.cpp +++ b/indra/llmath/tests/llbboxlocal_test.cpp @@ -32,11 +32,10 @@ * $/LicenseInfo$ */ +#include "linden_common.h" #include "../test/lltut.h" - #include "../llbboxlocal.h" - namespace tut { struct LLBBoxLocalData diff --git a/indra/llmath/tests/llrect_test.cpp b/indra/llmath/tests/llrect_test.cpp index 4b39f77772..c5e9e425bb 100644 --- a/indra/llmath/tests/llrect_test.cpp +++ b/indra/llmath/tests/llrect_test.cpp @@ -32,8 +32,8 @@ * $/LicenseInfo$ */ +#include "linden_common.h" #include "../test/lltut.h" - #include "../llrect.h" namespace tut diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 47fbc3ab69..a611de0cda 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -3,6 +3,7 @@ project(llmessage) include(00-Common) +include(GoogleMock) include(LLAddBuildTest) include(LLCommon) include(LLMath) @@ -22,6 +23,7 @@ include_directories( set(llmessage_SOURCE_FILES llares.cpp + llareslistener.cpp llassetstorage.cpp llblowfishcipher.cpp llbuffer.cpp @@ -104,6 +106,7 @@ set(llmessage_HEADER_FILES CMakeLists.txt llares.h + llareslistener.h llassetstorage.h llblowfishcipher.h llbuffer.h @@ -221,6 +224,7 @@ SET(llmessage_TEST_SOURCE_FILES llnamevalue.cpp lltrustedmessageservice.cpp lltemplatemessagedispatcher.cpp + llregionpresenceverifier.cpp ) LL_ADD_PROJECT_UNIT_TESTS(llmessage "${llmessage_TEST_SOURCE_FILES}") @@ -231,6 +235,7 @@ set(test_libs ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} ) LL_ADD_INTEGRATION_TEST( 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..8641437d86 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 @@ -100,7 +100,6 @@ public: // LEGACY boost::signals2::connection get(const LLUUID& id, BOOL is_group, old_callback_t callback, void* user_data); - // This method needs to be called from time to time to send out // requests. void processPending(); diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index a4af8e989b..5ff41322b7 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -131,7 +131,7 @@ void LLCurl::Responder::errorWithContent( // virtual void LLCurl::Responder::error(U32 status, const std::string& reason) { - llinfos << status << ": " << reason << llendl; + llinfos << mURL << " [" << status << "]: " << reason << llendl; } // virtual @@ -139,6 +139,11 @@ void LLCurl::Responder::result(const LLSD& content) { } +void LLCurl::Responder::setURL(const std::string& url) +{ + mURL = url; +} + // virtual void LLCurl::Responder::completedRaw( U32 status, @@ -148,7 +153,11 @@ void LLCurl::Responder::completedRaw( { LLSD content; LLBufferStream istr(channels, buffer.get()); - LLSDSerialize::fromXML(content, istr); + if (!LLSDSerialize::fromXML(content, istr)) + { + llinfos << "Failed to deserialize LLSD. " << mURL << " [" << status << "]: " << reason << llendl; + } + completed(status, reason, content); } diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index fbd3077cbf..0b58e7c4a5 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -120,8 +120,14 @@ public: // of the header can be parsed. In the ::completed call above only the body is contained in the LLSD. virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content); + // Used internally to set the url for debugging later. + void setURL(const std::string& url); + public: /* but not really -- don't touch this */ U32 mReferenceCount; + + private: + std::string mURL; }; typedef boost::intrusive_ptr<Responder> ResponderPtr; diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 8b90a4c5ca..12ecbb36eb 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -265,6 +265,11 @@ static void request( } } + if (responder) + { + responder->setURL(url); + } + req->setCallback(new LLHTTPClientURLAdaptor(responder)); if (method == LLURLRequest::HTTP_POST && gMessageSystem) diff --git a/indra/llmessage/llhttpclientadapter.cpp b/indra/llmessage/llhttpclientadapter.cpp index b6988224ce..9d3c83f828 100644 --- a/indra/llmessage/llhttpclientadapter.cpp +++ b/indra/llmessage/llhttpclientadapter.cpp @@ -1,5 +1,5 @@ /** - * @file + * @file llhttpclientadapter.cpp * @brief * * $LicenseInfo:firstyear=2009&license=viewergpl$ diff --git a/indra/llmessage/llhttpclientadapter.h b/indra/llmessage/llhttpclientadapter.h index 7f76390d0c..a205a2f260 100644 --- a/indra/llmessage/llhttpclientadapter.h +++ b/indra/llmessage/llhttpclientadapter.h @@ -1,5 +1,5 @@ /** - * @file + * @file llhttpclientadepter.h * @brief * * $LicenseInfo:firstyear=2008&license=viewergpl$ diff --git a/indra/llmessage/llhttpclientinterface.h b/indra/llmessage/llhttpclientinterface.h index 42a8e5cd0a..085a59cf27 100644 --- a/indra/llmessage/llhttpclientinterface.h +++ b/indra/llmessage/llhttpclientinterface.h @@ -1,5 +1,5 @@ /** - * @file + * @file llhttpclientinterface.h * @brief * * $LicenseInfo:firstyear=2008&license=viewergpl$ 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/lliohttpserver.cpp b/indra/llmessage/lliohttpserver.cpp index a00dbd1809..97134bd336 100644 --- a/indra/llmessage/lliohttpserver.cpp +++ b/indra/llmessage/lliohttpserver.cpp @@ -521,7 +521,7 @@ protected: * seek orfor string assignment. * @returns Returns true if a line was found. */ - bool readLine( + bool readHeaderLine( const LLChannelDescriptors& channels, buffer_ptr_t buffer, U8* dest, @@ -592,7 +592,7 @@ LLHTTPResponder::~LLHTTPResponder() //lldebugs << "destroying LLHTTPResponder" << llendl; } -bool LLHTTPResponder::readLine( +bool LLHTTPResponder::readHeaderLine( const LLChannelDescriptors& channels, buffer_ptr_t buffer, U8* dest, @@ -670,7 +670,7 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl( #endif PUMP_DEBUG; - if(readLine(channels, buffer, (U8*)buf, len)) + if(readHeaderLine(channels, buffer, (U8*)buf, len)) { bool read_next_line = false; bool parse_all = true; @@ -734,7 +734,13 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl( if(read_next_line) { len = HEADER_BUFFER_SIZE; - readLine(channels, buffer, (U8*)buf, len); + if (!readHeaderLine(channels, buffer, (U8*)buf, len)) + { + // Failed to read the header line, probably too long. + // readHeaderLine already marked the channel/buffer as bad. + keep_parsing = false; + break; + } } if(0 == len) { diff --git a/indra/llmessage/llmail.cpp b/indra/llmessage/llmail.cpp index d52ff6c7e8..ce206d8d7d 100644 --- a/indra/llmessage/llmail.cpp +++ b/indra/llmessage/llmail.cpp @@ -265,7 +265,7 @@ std::string LLMail::buildSMTPTransaction( // static bool LLMail::send( const std::string& header, - const std::string& message, + const std::string& raw_message, const char* from_address, const char* to_address) { @@ -276,8 +276,20 @@ bool LLMail::send( return false; } - // *FIX: this translation doesn't deal with a single period on a - // line by itself. + // remove any "." SMTP commands to prevent injection (DEV-35777) + // we don't need to worry about "\r\n.\r\n" because of the + // "\n" --> "\n\n" conversion going into rfc2822_msg below + std::string message = raw_message; + std::string bad_string = "\n.\n"; + std::string good_string = "\n..\n"; + while (1) + { + int index = message.find(bad_string); + if (index == std::string::npos) break; + message.replace(index, bad_string.size(), good_string); + } + + // convert all "\n" into "\r\n" std::ostringstream rfc2822_msg; for(U32 i = 0; i < message.size(); ++i) { diff --git a/indra/llmessage/llmessagesenderinterface.h b/indra/llmessage/llmessagesenderinterface.h index 119eb1d7f7..af6733fa05 100644 --- a/indra/llmessage/llmessagesenderinterface.h +++ b/indra/llmessage/llmessagesenderinterface.h @@ -1,5 +1,5 @@ /** - * @file + * @file llmessagesenderinterface.h * @brief * * $LicenseInfo:firstyear=2008&license=viewergpl$ 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..b1868e6a00 100644 --- a/indra/llmessage/llregionpresenceverifier.cpp +++ b/indra/llmessage/llregionpresenceverifier.cpp @@ -1,5 +1,5 @@ /** - * @file + * @file llregionpresenceverifier.cpp * @brief * * $LicenseInfo:firstyear=2008&license=viewergpl$ @@ -30,17 +30,48 @@ * $/LicenseInfo$ */ +#include "linden_common.h" + #include "llregionpresenceverifier.h" #include "llhttpclientinterface.h" #include <sstream> #include "net.h" #include "message.h" +namespace boost +{ + void intrusive_ptr_add_ref(LLRegionPresenceVerifier::Response* p) + { + ++p->mReferenceCount; + } + + void intrusive_ptr_release(LLRegionPresenceVerifier::Response* p) + { + if(p && 0 == --p->mReferenceCount) + { + delete p; + } + } +}; -LLRegionPresenceVerifier::RegionResponder::RegionResponder(ResponsePtr data) : mSharedData(data) +LLRegionPresenceVerifier::Response::~Response() { } +LLRegionPresenceVerifier::RegionResponder::RegionResponder(const std::string& + uri, + ResponsePtr data, + S32 retry_count) : + mUri(uri), + mSharedData(data), + mRetryCount(retry_count) +{ +} + +//virtual +LLRegionPresenceVerifier::RegionResponder::~RegionResponder() +{ +} void LLRegionPresenceVerifier::RegionResponder::result(const LLSD& content) { @@ -49,30 +80,36 @@ void LLRegionPresenceVerifier::RegionResponder::result(const LLSD& content) LLHost destination(host, port); LLUUID id = content["region_id"]; - llinfos << "Verifying " << destination.getString() << " is region " << id << llendl; + lldebugs << "Verifying " << destination.getString() << " is region " << id << llendl; std::stringstream uri; uri << "http://" << destination.getString() << "/state/basic/"; - mSharedData->getHttpClient().get(uri.str(), new VerifiedDestinationResponder(mSharedData, content)); + mSharedData->getHttpClient().get( + uri.str(), + new VerifiedDestinationResponder(mUri, mSharedData, content, mRetryCount)); } -void LLRegionPresenceVerifier::RegionResponder::completed( - U32 status, - const std::string& reason, - const LLSD& content) +void LLRegionPresenceVerifier::RegionResponder::error(U32 status, + const std::string& reason) { - LLHTTPClient::Responder::completed(status, reason, content); - - mSharedData->onCompletedRegionRequest(); + // TODO: babbage: distinguish between region presence service and + // region verification errors? + mSharedData->onRegionVerificationFailed(); } - -LLRegionPresenceVerifier::VerifiedDestinationResponder::VerifiedDestinationResponder(ResponsePtr data, const LLSD& content) : mSharedData(data), mContent(content) +LLRegionPresenceVerifier::VerifiedDestinationResponder::VerifiedDestinationResponder(const std::string& uri, ResponsePtr data, const LLSD& content, + S32 retry_count): + mUri(uri), + mSharedData(data), + mContent(content), + mRetryCount(retry_count) { } - - +//virtual +LLRegionPresenceVerifier::VerifiedDestinationResponder::~VerifiedDestinationResponder() +{ +} void LLRegionPresenceVerifier::VerifiedDestinationResponder::result(const LLSD& content) { @@ -87,13 +124,14 @@ void LLRegionPresenceVerifier::VerifiedDestinationResponder::result(const LLSD& { mSharedData->onRegionVerified(mContent); } - else if (mSharedData->shouldRetry()) + else if (mRetryCount > 0) { retry(); } else { - llwarns << "Could not correctly look up region from region presence service. Region: " << mSharedData->getRegionUri() << llendl; + llwarns << "Simulator verification failed. Region: " << mUri << llendl; + mSharedData->onRegionVerificationFailed(); } } @@ -101,13 +139,21 @@ void LLRegionPresenceVerifier::VerifiedDestinationResponder::retry() { LLSD headers; headers["Cache-Control"] = "no-cache, max-age=0"; - llinfos << "Requesting region information, get uncached for region " << mSharedData->getRegionUri() << llendl; - mSharedData->decrementRetries(); - mSharedData->getHttpClient().get(mSharedData->getRegionUri(), new RegionResponder(mSharedData), headers); + llinfos << "Requesting region information, get uncached for region " + << mUri << llendl; + --mRetryCount; + mSharedData->getHttpClient().get(mUri, new RegionResponder(mUri, mSharedData, mRetryCount), headers); } void LLRegionPresenceVerifier::VerifiedDestinationResponder::error(U32 status, const std::string& reason) { - retry(); + if(mRetryCount > 0) + { + retry(); + } + else + { + llwarns << "Failed to contact simulator for verification. Region: " << mUri << llendl; + mSharedData->onRegionVerificationFailed(); + } } - diff --git a/indra/llmessage/llregionpresenceverifier.h b/indra/llmessage/llregionpresenceverifier.h index 54ad6226d6..f57a62a731 100644 --- a/indra/llmessage/llregionpresenceverifier.h +++ b/indra/llmessage/llregionpresenceverifier.h @@ -1,5 +1,5 @@ /** - * @file + * @file llregionpresenceverifier.cpp * @brief * * $LicenseInfo:firstyear=2008&license=viewergpl$ @@ -37,7 +37,7 @@ #include "llhttpclient.h" #include <string> #include "llsd.h" -#include <boost/shared_ptr.hpp> +#include <boost/intrusive_ptr.hpp> class LLHTTPClientInterface; @@ -47,49 +47,58 @@ public: class Response { public: - virtual ~Response() {} + virtual ~Response() = 0; virtual bool checkValidity(const LLSD& content) const = 0; virtual void onRegionVerified(const LLSD& region_details) = 0; - - virtual void decrementRetries() = 0; + virtual void onRegionVerificationFailed() = 0; virtual LLHTTPClientInterface& getHttpClient() = 0; - virtual std::string getRegionUri() const = 0; - virtual bool shouldRetry() const = 0; - virtual void onCompletedRegionRequest() {} + public: /* but not really -- don't touch this */ + U32 mReferenceCount; }; - typedef boost::shared_ptr<Response> ResponsePtr; + typedef boost::intrusive_ptr<Response> ResponsePtr; class RegionResponder : public LLHTTPClient::Responder { public: - RegionResponder(ResponsePtr data); + RegionResponder(const std::string& uri, ResponsePtr data, + S32 retry_count); + virtual ~RegionResponder(); virtual void result(const LLSD& content); - virtual void completed( - U32 status, - const std::string& reason, - const LLSD& content); + virtual void error(U32 status, const std::string& reason); private: ResponsePtr mSharedData; + std::string mUri; + S32 mRetryCount; }; class VerifiedDestinationResponder : public LLHTTPClient::Responder { public: - VerifiedDestinationResponder(ResponsePtr data, const LLSD& content); + VerifiedDestinationResponder(const std::string& uri, ResponsePtr data, + const LLSD& content, S32 retry_count); + virtual ~VerifiedDestinationResponder(); virtual void result(const LLSD& content); virtual void error(U32 status, const std::string& reason); + private: void retry(); ResponsePtr mSharedData; LLSD mContent; + std::string mUri; + S32 mRetryCount; }; }; +namespace boost +{ + void intrusive_ptr_add_ref(LLRegionPresenceVerifier::Response* p); + void intrusive_ptr_release(LLRegionPresenceVerifier::Response* p); +}; #endif //LL_LLREGIONPRESENCEVERIFIER_H 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/llstoredmessage.cpp b/indra/llmessage/llstoredmessage.cpp index 32cbb15cb3..d6b2f45d04 100644 --- a/indra/llmessage/llstoredmessage.cpp +++ b/indra/llmessage/llstoredmessage.cpp @@ -1,5 +1,5 @@ /** - * @file + * @file llstoredmessage.cpp * @brief * * $LicenseInfo:firstyear=2009&license=viewergpl$ diff --git a/indra/llmessage/llstoredmessage.h b/indra/llmessage/llstoredmessage.h index 5069c2cb2e..359e4c5aea 100644 --- a/indra/llmessage/llstoredmessage.h +++ b/indra/llmessage/llstoredmessage.h @@ -1,5 +1,5 @@ /** - * @file + * @file llstoredmessage.h * @brief * * $LicenseInfo:firstyear=2009&license=viewergpl$ diff --git a/indra/llmessage/llthrottle.cpp b/indra/llmessage/llthrottle.cpp index 70279a3c62..0872efba50 100644 --- a/indra/llmessage/llthrottle.cpp +++ b/indra/llmessage/llthrottle.cpp @@ -265,6 +265,31 @@ BOOL LLThrottleGroup::setNominalBPS(F32* throttle_vec) return changed; } +// Return bits available in the channel +S32 LLThrottleGroup::getAvailable(S32 throttle_cat) +{ + S32 retval = 0; + + F32 category_bps = mCurrentBPS[throttle_cat]; + F32 lookahead_bits = category_bps * THROTTLE_LOOKAHEAD_TIME; + + // use a temporary bits_available + // since we don't want to change mBitsAvailable every time + F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime[throttle_cat]); + F32 bits_available = mBitsAvailable[throttle_cat] + (category_bps * elapsed_time); + + if (bits_available >= lookahead_bits) + { + retval = (S32) gThrottleMaximumBPS[throttle_cat]; + } + else + { + retval = (S32) bits_available; + } + + return retval; +} + BOOL LLThrottleGroup::checkOverflow(S32 throttle_cat, F32 bits) { diff --git a/indra/llmessage/llthrottle.h b/indra/llmessage/llthrottle.h index 7d1679beb2..47a7c653b2 100644 --- a/indra/llmessage/llthrottle.h +++ b/indra/llmessage/llthrottle.h @@ -84,6 +84,8 @@ public: BOOL dynamicAdjust(); // Shift bandwidth from idle channels to busy channels, TRUE if adjustment occurred BOOL setNominalBPS(F32* throttle_vec); // TRUE if any value was different, resets adjustment system if was different + S32 getAvailable(S32 throttle_cat); // Return bits available in the channel + void packThrottle(LLDataPacker &dp) const; void unpackThrottle(LLDataPacker &dp); public: diff --git a/indra/llmessage/lltransfersourceasset.cpp b/indra/llmessage/lltransfersourceasset.cpp index 5a1cd95ffc..41f3f3f607 100644 --- a/indra/llmessage/lltransfersourceasset.cpp +++ b/indra/llmessage/lltransfersourceasset.cpp @@ -270,7 +270,6 @@ bool is_asset_fetch_by_id_allowed(LLAssetType::EType type) case LLAssetType::AT_BODYPART: case LLAssetType::AT_ANIMATION: case LLAssetType::AT_GESTURE: - case LLAssetType::AT_FAVORITE: rv = true; break; default: diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 3ab8057abb..81b7761ed5 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -51,6 +51,7 @@ static const U32 HTTP_STATUS_PIPE_ERROR = 499; * String constants */ const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri"); +const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes"); static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user); @@ -247,7 +248,29 @@ LLIOPipe::EStatus LLURLRequest::process_impl( PUMP_DEBUG; LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); //llinfos << "LLURLRequest::process_impl()" << llendl; - if(!buffer) return STATUS_ERROR; + if (!buffer) return STATUS_ERROR; + + // we're still waiting or prcessing, check how many + // bytes we have accumulated. + const S32 MIN_ACCUMULATION = 100000; + if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION)) + { + // This is a pretty sloppy calculation, but this + // tries to make the gross assumption that if data + // is coming in at 56kb/s, then this transfer will + // probably succeed. So, if we're accumlated + // 100,000 bytes (MIN_ACCUMULATION) then let's + // give this client another 2s to complete. + const F32 TIMEOUT_ADJUSTMENT = 2.0f; + mDetail->mByteAccumulator = 0; + pump->adjustTimeoutSeconds(TIMEOUT_ADJUSTMENT); + lldebugs << "LLURLRequest adjustTimeoutSeconds for request: " << mDetail->mURL << llendl; + if (mState == STATE_INITIALIZED) + { + llinfos << "LLURLRequest adjustTimeoutSeconds called during upload" << llendl; + } + } + switch(mState) { case STATE_INITIALIZED: @@ -286,27 +309,14 @@ LLIOPipe::EStatus LLURLRequest::process_impl( bool newmsg = mDetail->mCurlRequest->getResult(&result); if(!newmsg) { - // we're still waiting or prcessing, check how many - // bytes we have accumulated. - const S32 MIN_ACCUMULATION = 100000; - if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION)) - { - // This is a pretty sloppy calculation, but this - // tries to make the gross assumption that if data - // is coming in at 56kb/s, then this transfer will - // probably succeed. So, if we're accumlated - // 100,000 bytes (MIN_ACCUMULATION) then let's - // give this client another 2s to complete. - const F32 TIMEOUT_ADJUSTMENT = 2.0f; - mDetail->mByteAccumulator = 0; - pump->adjustTimeoutSeconds(TIMEOUT_ADJUSTMENT); - } - // keep processing break; } mState = STATE_HAVE_RESPONSE; + context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; + context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; + lldebugs << this << "Setting context to " << context << llendl; switch(result) { case CURLE_OK: @@ -353,10 +363,16 @@ LLIOPipe::EStatus LLURLRequest::process_impl( // we already stuffed everything into channel in in the curl // callback, so we are done. eos = true; + context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; + context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; + lldebugs << this << "Setting context to " << context << llendl; return STATUS_DONE; default: PUMP_DEBUG; + context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; + context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; + lldebugs << this << "Setting context to " << context << llendl; return STATUS_ERROR; } } @@ -369,6 +385,8 @@ void LLURLRequest::initialize() mDetail->mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); mDetail->mCurlRequest->setWriteCallback(&downCallback, (void*)this); mDetail->mCurlRequest->setReadCallback(&upCallback, (void*)this); + mRequestTransferedBytes = 0; + mResponseTransferedBytes = 0; } bool LLURLRequest::configure() @@ -471,6 +489,7 @@ size_t LLURLRequest::downCallback( req->mDetail->mChannels.out(), (U8*)data, bytes); + req->mResponseTransferedBytes += bytes; req->mDetail->mByteAccumulator += bytes; return bytes; } @@ -494,6 +513,7 @@ size_t LLURLRequest::upCallback( req->mDetail->mLastRead, (U8*)data, bytes); + req->mRequestTransferedBytes += bytes; return bytes; } diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index 86ef71f085..cb3c466440 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -45,6 +45,12 @@ #include "llchainio.h" #include "llerror.h" + +extern const std::string CONTEXT_REQUEST; +extern const std::string CONTEXT_DEST_URI_SD_LABEL; +extern const std::string CONTEXT_RESPONSE; +extern const std::string CONTEXT_TRANSFERED_BYTES; + class LLURLRequestDetail; class LLURLRequestComplete; @@ -208,6 +214,8 @@ protected: ERequestAction mAction; LLURLRequestDetail* mDetail; LLIOPipe::ptr_t mCompletionCallback; + S32 mRequestTransferedBytes; + S32 mResponseTransferedBytes; private: /** diff --git a/indra/llmessage/llxfermanager.cpp b/indra/llmessage/llxfermanager.cpp index 08c9192c9f..209bdb2249 100644 --- a/indra/llmessage/llxfermanager.cpp +++ b/indra/llmessage/llxfermanager.cpp @@ -760,30 +760,36 @@ static bool remove_prefix(std::string& filename, const std::string& prefix) static bool verify_cache_filename(const std::string& filename) { //NOTE: This routine is only used to check file names that our own - // code places in the cache directory. As such, it can be limited - // to this very restrictive file name pattern. It does not need to - // handle other characters. - + // code places in the cache directory. As such, it can be limited + // to this very restrictive file name pattern. It does not need to + // handle other characters. The only known uses of this are (with examples): + // sim to sim object pass: fc0b72d8-9456-63d9-a802-a557ef847313.tmp + // sim to viewer mute list: mute_b78eacd0-1244-448e-93ca-28ede242f647.tmp + // sim to viewer task inventory: inventory_d8ab59d2-baf0-0e79-c4c2-a3f99b9fcf45.tmp + + //IMPORTANT: Do not broaden the filenames accepted by this routine + // without careful analysis. Anything allowed by this function can + // be downloaded by the viewer. + size_t len = filename.size(); - //const boost::regex expr("[a-zA-Z0-9][-_.a-zA-Z0-9]<0,49>"); - if (len < 1 || len > 50) - { + //const boost::regex expr("[0-9a-zA-Z_-]<1,46>\.tmp"); + if (len < 5 || len > 50) + { return false; } - for(unsigned i=0; i<len; ++i) - { + for(size_t i=0; i<(len-4); ++i) + { char c = filename[i]; - bool ok = isalnum(c); - if (!ok && i > 0) - { - ok = '_'==c || '-'==c || '.'==c; - } + bool ok = isalnum(c) || '_'==c || '-'==c; if (!ok) { return false; } } - return true; + return filename[len-4] == '.' + && filename[len-3] == 't' + && filename[len-2] == 'm' + && filename[len-1] == 'p'; } void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user_data*/) 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/llcurl_stub.cpp b/indra/llmessage/tests/llcurl_stub.cpp index 5dc5932fde..e6a5ad9946 100644 --- a/indra/llmessage/tests/llcurl_stub.cpp +++ b/indra/llmessage/tests/llcurl_stub.cpp @@ -22,6 +22,7 @@ #include "linden_common.h" LLCurl::Responder::Responder() + : mReferenceCount(0) { } diff --git a/indra/llmessage/tests/llhost_test.cpp b/indra/llmessage/tests/llhost_test.cpp index 6dbdcb86e7..4c9e75456a 100644 --- a/indra/llmessage/tests/llhost_test.cpp +++ b/indra/llmessage/tests/llhost_test.cpp @@ -166,7 +166,21 @@ namespace tut // the main domain name and not do the exact compare std::string hostname = host.getHostName(); - ensure("getHostName failed", hostname.find(hostStr) != std::string::npos); +/*==========================================================================*| + // nat 2009-10-20: not sure this ensure() is such a good idea, at + // least with "google.com". The logic below is failing for me with: + // set 'google.com'; reported 'yx-in-f100.1e100.net' + // Disabling test until we can replace it with something more robust. + try + { + ensure("getHostName failed", hostname.find(hostStr) != std::string::npos); + } + catch (const std::exception&) + { + std::cerr << "set '" << hostStr << "'; reported '" << hostname << "'" << std::endl; + throw; + } +|*==========================================================================*/ } // setHostByName for dotted IP diff --git a/indra/llmessage/tests/llmockhttpclient.h b/indra/llmessage/tests/llmockhttpclient.h new file mode 100644 index 0000000000..2f55e97fcc --- /dev/null +++ b/indra/llmessage/tests/llmockhttpclient.h @@ -0,0 +1,61 @@ +/** + * @file + * @brief + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008, Linden Research, Inc. + * + * The following source code is PROPRIETARY AND CONFIDENTIAL. Use of + * this source code is governed by the Linden Lab Source Code Disclosure + * Agreement ("Agreement") previously entered between you and Linden + * Lab. By accessing, using, copying, modifying or distributing this + * software, you acknowledge that you have been informed of your + * obligations under the Agreement 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$ + */ + +/* Macro Definitions */ +#ifndef LL_LLMOCKHTTPCLIENT_H +#define LL_LLMOCKHTTPCLIENT_H + +#include "linden_common.h" +#include "llhttpclientinterface.h" + +#include <gmock/gmock.h> + +class LLMockHTTPClient : public LLHTTPClientInterface +{ +public: + MOCK_METHOD2(get, void(const std::string& url, LLCurl::ResponderPtr responder)); + MOCK_METHOD3(get, void(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers)); + MOCK_METHOD3(put, void(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder)); +}; + +// A helper to match responder types +template<typename T> +struct ResponderType +{ + bool operator()(LLCurl::ResponderPtr ptr) const + { + T* p = dynamic_cast<T*>(ptr.get()); + return p != NULL; + } +}; + +inline bool operator==(const LLSD& l, const LLSD& r) +{ + std::ostringstream ls, rs; + ls << l; + rs << r; + return ls.str() == rs.str(); + +} + + +#endif //LL_LLMOCKHTTPCLIENT_H + diff --git a/indra/llmessage/tests/llregionpresenceverifier_test.cpp b/indra/llmessage/tests/llregionpresenceverifier_test.cpp new file mode 100644 index 0000000000..c86126406e --- /dev/null +++ b/indra/llmessage/tests/llregionpresenceverifier_test.cpp @@ -0,0 +1,113 @@ +/** + * @file + * @brief + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2001-2008, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "../test/lltut.h" +#include "llregionpresenceverifier.h" +#include "llcurl_stub.cpp" +#include "llhost.cpp" +#include "net.cpp" +#include "lltesthttpclientadapter.cpp" + +class LLTestResponse : public LLRegionPresenceVerifier::Response +{ +public: + + virtual bool checkValidity(const LLSD& content) const + { + return true; + } + + virtual void onRegionVerified(const LLSD& region_details) + { + } + + virtual void onRegionVerificationFailed() + { + } + + virtual LLHTTPClientInterface& getHttpClient() + { + return mHttpInterface; + } + + LLTestHTTPClientAdapter mHttpInterface; +}; + +namespace tut +{ + struct LLRegionPresenceVerifierData + { + LLRegionPresenceVerifierData() : + mResponse(new LLTestResponse()), + mResponder("", LLRegionPresenceVerifier::ResponsePtr(mResponse), + LLSD(), 3) + { + } + + LLTestResponse* mResponse; + LLRegionPresenceVerifier::VerifiedDestinationResponder mResponder; + }; + + typedef test_group<LLRegionPresenceVerifierData> factory; + typedef factory::object object; +} + +namespace +{ + tut::factory tf("LLRegionPresenceVerifier test"); +} + +namespace tut +{ + // Test that VerifiedDestinationResponder does retry + // on error when shouldRetry returns true. + template<> template<> + void object::test<1>() + { + mResponder.error(500, "Internal server error"); + ensure_equals(mResponse->mHttpInterface.mGetUrl.size(), 1); + } + + // Test that VerifiedDestinationResponder only retries + // on error until shouldRetry returns false. + template<> template<> + void object::test<2>() + { + mResponder.error(500, "Internal server error"); + mResponder.error(500, "Internal server error"); + mResponder.error(500, "Internal server error"); + mResponder.error(500, "Internal server error"); + ensure_equals(mResponse->mHttpInterface.mGetUrl.size(), 3); + } +} + 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/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index 7299ede22d..fc58b48a7b 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -133,6 +133,7 @@ void LLPluginClassMedia::reset() mCurrentTime = 0.0f; mDuration = 0.0f; mCurrentRate = 0.0f; + mLoadedDuration = 0.0f; } void LLPluginClassMedia::idle(void) @@ -705,6 +706,7 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) bool time_duration_updated = false; + int previous_percent = mProgressPercent; if(message.hasValue("current_time")) { @@ -722,11 +724,32 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) mCurrentRate = message.getValueReal("current_rate"); } + if(message.hasValue("loaded_duration")) + { + mLoadedDuration = message.getValueReal("loaded_duration"); + time_duration_updated = true; + } + else + { + // If the message doesn't contain a loaded_duration param, assume it's equal to duration + mLoadedDuration = mDuration; + } + + // Calculate a percentage based on the loaded duration and total duration. + if(mDuration != 0.0f) // Don't divide by zero. + { + mProgressPercent = (int)((mLoadedDuration * 100.0f)/mDuration); + } + if(time_duration_updated) { mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_TIME_DURATION_UPDATED); } + if(previous_percent != mProgressPercent) + { + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_PROGRESS_UPDATED); + } } else if(message_name == "media_status") { @@ -812,6 +835,11 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) mCanPaste = message.getValueBoolean("paste"); } } + else if(message_name == "name_text") + { + mMediaName = message.getValue("name"); + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_NAME_CHANGED); + } else { LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL; @@ -884,6 +912,12 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) } /* virtual */ +void LLPluginClassMedia::pluginLaunchFailed() +{ + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_PLUGIN_FAILED_LAUNCH); +} + +/* virtual */ void LLPluginClassMedia::pluginDied() { mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_PLUGIN_FAILED); diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index 331ca5f6dc..697deec353 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -135,6 +135,7 @@ public: // Inherited from LLPluginProcessParentOwner /* virtual */ void receivePluginMessage(const LLPluginMessage &message); + /* virtual */ void pluginLaunchFailed(); /* virtual */ void pluginDied(); @@ -230,6 +231,7 @@ public: F64 getCurrentTime(void) const { return mCurrentTime; }; F64 getDuration(void) const { return mDuration; }; F64 getCurrentPlayRate(void) { return mCurrentRate; }; + F64 getLoadedDuration(void) const { return mLoadedDuration; }; // Initialize the URL history of the plugin by sending // "init_history" message @@ -338,6 +340,7 @@ protected: F64 mCurrentTime; F64 mDuration; F64 mCurrentRate; + F64 mLoadedDuration; }; diff --git a/indra/llplugin/llpluginclassmediaowner.h b/indra/llplugin/llpluginclassmediaowner.h index df6de0925e..4690f09172 100644 --- a/indra/llplugin/llpluginclassmediaowner.h +++ b/indra/llplugin/llpluginclassmediaowner.h @@ -52,11 +52,13 @@ public: MEDIA_EVENT_NAVIGATE_COMPLETE, // browser has finished navigation MEDIA_EVENT_PROGRESS_UPDATED, // browser has updated loading progress MEDIA_EVENT_STATUS_TEXT_CHANGED, // browser has updated the status text + MEDIA_EVENT_NAME_CHANGED, // browser has updated the name of the media (typically <title> tag) MEDIA_EVENT_LOCATION_CHANGED, // browser location (URL) has changed (maybe due to internal navagation/frames/etc) MEDIA_EVENT_CLICK_LINK_HREF, // I'm not entirely sure what the semantics of these two are MEDIA_EVENT_CLICK_LINK_NOFOLLOW, - MEDIA_EVENT_PLUGIN_FAILED // The plugin failed to launch or died unexpectedly + MEDIA_EVENT_PLUGIN_FAILED_LAUNCH, // The plugin failed to launch + MEDIA_EVENT_PLUGIN_FAILED // The plugin died unexpectedly } EMediaEvent; diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index 41784a713c..f3b4c6bdc6 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -83,6 +83,14 @@ void LLPluginProcessParent::killSockets(void) mSocket.reset(); } +void LLPluginProcessParent::errorState(void) +{ + if(mState < STATE_RUNNING) + setState(STATE_LAUNCH_FAILURE); + else + setState(STATE_ERROR); +} + void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_filename) { mProcess.setExecutable(launcher_filename); @@ -132,7 +140,7 @@ bool LLPluginProcessParent::accept() ll_apr_warn_status(status); // Some other error. - setState(STATE_ERROR); + errorState(); } return result; @@ -150,15 +158,15 @@ void LLPluginProcessParent::idle(void) if(!mMessagePipe->pump()) { // LL_WARNS("Plugin") << "Message pipe hit an error state" << LL_ENDL; - setState(STATE_ERROR); + errorState(); } } - if((mSocketError != APR_SUCCESS) && (mState < STATE_ERROR)) + if((mSocketError != APR_SUCCESS) && (mState <= STATE_RUNNING)) { // The socket is in an error state -- the plugin is gone. LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL; - setState(STATE_ERROR); + errorState(); } // If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState(). @@ -191,7 +199,7 @@ void LLPluginProcessParent::idle(void) if(ll_apr_warn_status(status)) { killSockets(); - setState(STATE_ERROR); + errorState(); break; } @@ -202,7 +210,7 @@ void LLPluginProcessParent::idle(void) if(ll_apr_warn_status(status)) { killSockets(); - setState(STATE_ERROR); + errorState(); break; } @@ -212,7 +220,7 @@ void LLPluginProcessParent::idle(void) if(ll_apr_warn_status(apr_socket_addr_get(&bound_addr, APR_LOCAL, mListenSocket->getSocket()))) { killSockets(); - setState(STATE_ERROR); + errorState(); break; } mBoundPort = bound_addr->port; @@ -222,7 +230,7 @@ void LLPluginProcessParent::idle(void) LL_WARNS("Plugin") << "Bound port number unknown, bailing out." << LL_ENDL; killSockets(); - setState(STATE_ERROR); + errorState(); break; } } @@ -234,7 +242,7 @@ void LLPluginProcessParent::idle(void) if(ll_apr_warn_status(status)) { killSockets(); - setState(STATE_ERROR); + errorState(); break; } @@ -242,7 +250,7 @@ void LLPluginProcessParent::idle(void) if(ll_apr_warn_status(status)) { killSockets(); - setState(STATE_ERROR); + errorState(); break; } @@ -255,7 +263,7 @@ void LLPluginProcessParent::idle(void) if(ll_apr_warn_status(status)) { killSockets(); - setState(STATE_ERROR); + errorState(); break; } @@ -274,7 +282,7 @@ void LLPluginProcessParent::idle(void) mProcess.addArgument(stream.str()); if(mProcess.launch() != 0) { - setState(STATE_ERROR); + errorState(); } else { @@ -290,7 +298,7 @@ void LLPluginProcessParent::idle(void) // waiting for the plugin to connect if(pluginLockedUpOrQuit()) { - setState(STATE_ERROR); + errorState(); } else { @@ -309,7 +317,7 @@ void LLPluginProcessParent::idle(void) if(pluginLockedUpOrQuit()) { - setState(STATE_ERROR); + errorState(); } break; @@ -330,14 +338,14 @@ void LLPluginProcessParent::idle(void) // The load_plugin_response message will kick us from here into STATE_RUNNING if(pluginLockedUpOrQuit()) { - setState(STATE_ERROR); + errorState(); } break; case STATE_RUNNING: if(pluginLockedUpOrQuit()) { - setState(STATE_ERROR); + errorState(); } break; @@ -349,8 +357,16 @@ void LLPluginProcessParent::idle(void) else if(pluginLockedUp()) { LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << llendl; - setState(STATE_ERROR); + errorState(); + } + break; + + case STATE_LAUNCH_FAILURE: + if(mOwner != NULL) + { + mOwner->pluginLaunchFailed(); } + setState(STATE_CLEANUP); break; case STATE_ERROR: @@ -467,7 +483,7 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) else { LL_WARNS("Plugin") << "received hello message in wrong state -- bailing out" << LL_ENDL; - setState(STATE_ERROR); + errorState(); } } @@ -477,6 +493,9 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) { // Plugin has been loaded. + mPluginVersionString = message.getValue("plugin_version"); + LL_INFOS("Plugin") << "plugin version string: " << mPluginVersionString << LL_ENDL; + // Check which message classes/versions the plugin supports. // TODO: check against current versions // TODO: kill plugin on major mismatches? @@ -487,8 +506,6 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) LL_INFOS("Plugin") << "message class: " << iter->first << " -> version: " << iter->second.asString() << LL_ENDL; } - mPluginVersionString = message.getValue("plugin_version"); - // Send initial sleep time setSleepTime(mSleepTime, true); @@ -497,7 +514,7 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) else { LL_WARNS("Plugin") << "received load_plugin_response message in wrong state -- bailing out" << LL_ENDL; - setState(STATE_ERROR); + errorState(); } } else if(message_name == "heartbeat") diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h index 0d0b047c88..754ebeb946 100644 --- a/indra/llplugin/llpluginprocessparent.h +++ b/indra/llplugin/llpluginprocessparent.h @@ -45,6 +45,7 @@ public: virtual ~LLPluginProcessParentOwner(); virtual void receivePluginMessage(const LLPluginMessage &message) = 0; // This will only be called when the plugin has died unexpectedly + virtual void pluginLaunchFailed() {}; virtual void pluginDied() {}; }; @@ -68,6 +69,9 @@ public: bool isDone(void); void killSockets(void); + + // Go to the proper error state + void errorState(void); void setSleepTime(F64 sleep_time, bool force_send = false); F64 getSleepTime(void) const { return mSleepTime; }; @@ -110,6 +114,7 @@ private: STATE_HELLO, // first message from the plugin process has been received STATE_LOADING, // process has been asked to load the plugin STATE_RUNNING, // + STATE_LAUNCH_FAILURE, // Failure before plugin loaded STATE_ERROR, // generic bailout state STATE_CLEANUP, // clean everything up STATE_EXITING, // Tried to kill process, waiting for it to exit diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index b102254b62..5ad758072c 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" /** @@ -746,16 +746,201 @@ BOOL LLPrimitive::setVolume(const LLVolumeParams &volume_params, const S32 detai U32 old_face_mask = mVolumep->mFaceMask; + S32 face_bit = 0; + S32 cur_mask = 0; + + // Grab copies of the old faces from the original shape, ordered by type. + // We will use these to figure out what old texture info gets mapped to new + // faces in the new shape. + std::vector<LLProfile::Face> old_faces; + for (S32 face = 0; face < mVolumep->getNumFaces(); face++) + { + old_faces.push_back(mVolumep->getProfile().mFaces[face]); + } + + // Copy the old texture info off to the side, but not in the order in which + // they live in the mTextureList, rather in order of ther "face id" which + // is the corresponding value of LLVolueParams::LLProfile::mFaces::mIndex. + // + // Hence, some elements of old_tes::mEntryList will be invalid. It is + // initialized to a size of 9 (max number of possible faces on a volume?) + // and only the ones with valid types are filled in. + LLPrimTextureList old_tes; + old_tes.setSize(9); + for (face_bit = 0; face_bit < 9; face_bit++) + { + cur_mask = 0x1 << face_bit; + if (old_face_mask & cur_mask) + { + S32 te_index = face_index_from_id(cur_mask, old_faces); + old_tes.copyTexture(face_bit, *(getTE(te_index))); + //llinfos << face_bit << ":" << te_index << ":" << old_tes[face_bit].getID() << llendl; + } + } + + // build the new object sVolumeManager->unrefVolume(mVolumep); mVolumep = volumep; U32 new_face_mask = mVolumep->mFaceMask; - if (old_face_mask != new_face_mask) + S32 i; + + if (old_face_mask == new_face_mask) { + // nothing to do + return TRUE; + } + + if (mVolumep->getNumFaces() == 0 && new_face_mask != 0) + { + llwarns << "Object with 0 faces found...INCORRECT!" << llendl; setNumTEs(mVolumep->getNumFaces()); + return TRUE; + } + + // initialize face_mapping + S32 face_mapping[9]; + for (face_bit = 0; face_bit < 9; face_bit++) + { + face_mapping[face_bit] = face_bit; + } + + // The new shape may have more faces than the original, but we can't just + // add them to the end -- the ordering matters and it may be that we must + // insert the new faces in the middle of the list. When we add a face it + // will pick up the texture/color info of one of the old faces an so we + // now figure out which old face info gets mapped to each new face, and + // store in the face_mapping lookup table. + for (face_bit = 0; face_bit < 9; face_bit++) + { + cur_mask = 0x1 << face_bit; + if (!(new_face_mask & cur_mask)) + { + // Face doesn't exist in new map. + face_mapping[face_bit] = -1; + continue; + } + else if (old_face_mask & cur_mask) + { + // Face exists in new and old map. + face_mapping[face_bit] = face_bit; + continue; + } + + // OK, how we've got a mismatch, where we have to fill a new face with one from + // the old face. + if (cur_mask & (LL_FACE_PATH_BEGIN | LL_FACE_PATH_END | LL_FACE_INNER_SIDE)) + { + // It's a top/bottom/hollow interior face. + if (old_face_mask & LL_FACE_PATH_END) + { + face_mapping[face_bit] = 1; + continue; + } + else + { + S32 cur_outer_mask = LL_FACE_OUTER_SIDE_0; + for (i = 0; i < 4; i++) + { + if (old_face_mask & cur_outer_mask) + { + face_mapping[face_bit] = 5 + i; + break; + } + cur_outer_mask <<= 1; + } + if (i == 4) + { + llwarns << "No path end or outer face in volume!" << llendl; + } + continue; + } + } + + if (cur_mask & (LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END)) + { + // A cut slice. Use the hollow interior if we have it. + if (old_face_mask & LL_FACE_INNER_SIDE) + { + face_mapping[face_bit] = 2; + continue; + } + + // No interior, use the bottom face. + // Could figure out which of the outer faces was nearest, but that would be harder. + if (old_face_mask & LL_FACE_PATH_END) + { + face_mapping[face_bit] = 1; + continue; + } + else + { + S32 cur_outer_mask = LL_FACE_OUTER_SIDE_0; + for (i = 0; i < 4; i++) + { + if (old_face_mask & cur_outer_mask) + { + face_mapping[face_bit] = 5 + i; + break; + } + cur_outer_mask <<= 1; + } + if (i == 4) + { + llwarns << "No path end or outer face in volume!" << llendl; + } + continue; + } + } + + // OK, the face that's missing is an outer face... + // Pull from the nearest adjacent outer face (there's always guaranteed to be one... + S32 cur_outer = face_bit - 5; + S32 min_dist = 5; + S32 min_outer_bit = -1; + S32 i; + for (i = 0; i < 4; i++) + { + if (old_face_mask & (LL_FACE_OUTER_SIDE_0 << i)) + { + S32 dist = abs(i - cur_outer); + if (dist < min_dist) + { + min_dist = dist; + min_outer_bit = i + 5; + } + } + } + if (-1 == min_outer_bit) + { + llinfos << (LLVolume *)mVolumep << llendl; + llwarns << "Bad! No outer faces, impossible!" << llendl; + } + face_mapping[face_bit] = min_outer_bit; } + + setNumTEs(mVolumep->getNumFaces()); + for (face_bit = 0; face_bit < 9; face_bit++) + { + // For each possible face type on the new shape we check to see if that + // face exists and if it does we create a texture entry that is a copy + // of one of the originals. Since the originals might not have a + // matching face, we use the face_mapping lookup table to figure out + // which face information to copy. + cur_mask = 0x1 << face_bit; + if (new_face_mask & cur_mask) + { + if (-1 == face_mapping[face_bit]) + { + llwarns << "No mapping from old face to new face!" << llendl; + } + + S32 te_num = face_index_from_id(cur_mask, mVolumep->getProfile().mFaces); + setTE(te_num, *(old_tes.getTexture(face_mapping[face_bit]))); + } + } return TRUE; } @@ -1715,10 +1900,10 @@ LLSD LLLightImageParams::asLLSD() const bool LLLightImageParams::fromLLSD(LLSD& sd) { - if (sd.has("texture") && sd.has("params") && sd["params"].size() == 3) + if (sd.has("texture")) { setLightTexture( sd["texture"] ); - setParams( LLVector3(sd["params"][0].asReal(), sd["params"][1].asReal(), sd["params"][2].asReal()) ); + setParams( LLVector3( sd["params"] ) ); return true; } diff --git a/indra/llprimitive/llprimtexturelist.cpp b/indra/llprimitive/llprimtexturelist.cpp index d03150fc78..1c7de95975 100644 --- a/indra/llprimitive/llprimtexturelist.cpp +++ b/indra/llprimitive/llprimtexturelist.cpp @@ -135,13 +135,12 @@ S32 LLPrimTextureList::copyTexture(const U8 index, const LLTextureEntry& te) { if (S32(index) >= mEntryList.size()) { - // TODO -- assert here S32 current_size = mEntryList.size(); - llerrs << "index = " << S32(index) << " current_size = " << current_size << llendl; + llwarns << "ignore copy of index = " << S32(index) << " into texture entry list of size = " << current_size << llendl; return TEM_CHANGE_NONE; } - // we're changing an existing entry + // we're changing an existing entry llassert(mEntryList[index]); delete (mEntryList[index]); if (&te) @@ -387,8 +386,18 @@ void LLPrimTextureList::setSize(S32 new_size) mEntryList.resize(new_size); for (S32 index = current_size; index < new_size; ++index) { - LLTextureEntry* new_entry = LLPrimTextureList::newTextureEntry(); - mEntryList[index] = new_entry; + if (current_size > 0 + && mEntryList[current_size - 1]) + { + // copy the last valid entry for the new one + mEntryList[index] = mEntryList[current_size - 1]->newCopy(); + } + else + { + // no valid enries to copy, so we new one up + LLTextureEntry* new_entry = LLPrimTextureList::newTextureEntry(); + mEntryList[index] = new_entry; + } } } else if (new_size < current_size) diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp index b534939dfc..f75f1d9b8c 100644 --- a/indra/llprimitive/lltextureentry.cpp +++ b/indra/llprimitive/lltextureentry.cpp @@ -35,7 +35,7 @@ #include "lluuid.h" #include "llmediaentry.h" #include "lltextureentry.h" -#include "llsdutil.h" +#include "llsdutil_math.h" #include "v4color.h" const U8 DEFAULT_BUMP_CODE = 0; // no bump or shininess @@ -646,3 +646,9 @@ LLUUID LLTextureEntry::getAgentIDFromMediaVersionString(const std::string &versi } return id; } + +//static +bool LLTextureEntry::isMediaVersionString(const std::string &version_string) +{ + return std::string::npos != version_string.find(MEDIA_VERSION_STRING_PREFIX); +} diff --git a/indra/llprimitive/lltextureentry.h b/indra/llprimitive/lltextureentry.h index 8d2834f78c..d6366b9bb2 100644 --- a/indra/llprimitive/lltextureentry.h +++ b/indra/llprimitive/lltextureentry.h @@ -171,7 +171,9 @@ public: static U32 getVersionFromMediaVersionString(const std::string &version_string); // Given a media version string, return the UUID of the agent static LLUUID getAgentIDFromMediaVersionString(const std::string &version_string); - + // Return whether or not the given string is actually a media version + static bool isMediaVersionString(const std::string &version_string); + // Media flags enum { MF_NONE = 0x0, MF_HAS_MEDIA = 0x1 }; diff --git a/indra/llprimitive/tests/llmediaentry_test.cpp b/indra/llprimitive/tests/llmediaentry_test.cpp index 72478d0459..9ce6560923 100644 --- a/indra/llprimitive/tests/llmediaentry_test.cpp +++ b/indra/llprimitive/tests/llmediaentry_test.cpp @@ -157,7 +157,7 @@ namespace tut void ensure_llsd_equals(const std::string& msg, const LLSD& expected, const LLSD& actual) { - if (! llsd_equals(expected, actual)) + if (!tut::llsd_equals(expected, actual)) { std::string message = msg; message += ": actual: "; diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index c9163d2890..793a526c26 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -893,6 +893,13 @@ LLFontGL* LLFontGL::getFontByName(const std::string& name) } } +//static +LLFontGL* LLFontGL::getFontDefault() +{ + return getFontSansSerif(); // Fallback to sans serif as default font +} + + // static std::string LLFontGL::getFontPathSystem() { diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index a278d88287..5f2c86c6c1 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -165,6 +165,7 @@ public: static LLFontGL* getFont(const LLFontDescriptor& desc); // Use with legacy names like "SANSSERIF_SMALL" or "OCRA" static LLFontGL* getFontByName(const std::string& name); + static LLFontGL* getFontDefault(); // default fallback font static std::string getFontPathLocal(); static std::string getFontPathSystem(); diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index 45573cd817..7a3d6ec4f2 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -380,7 +380,10 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc) LLFontDescriptor nearest_exact_desc = *match_desc; nearest_exact_desc.setSize(norm_desc.getSize()); font_reg_map_t::iterator it = mFontMap.find(nearest_exact_desc); - if (it != mFontMap.end()) + // If we fail to find a font in the fonts directory, it->second might be NULL. + // We shouldn't construcnt a font with a NULL mFontFreetype. + // This may not be the best solution, but it at least prevents a crash. + if (it != mFontMap.end() && it->second != NULL) { llinfos << "-- matching font exists: " << nearest_exact_desc.getName() << " size " << nearest_exact_desc.getSize() << " style " << ((S32) nearest_exact_desc.getStyle()) << llendl; diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index 10315bacf1..3400a72385 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -593,8 +593,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 d9169f57f9..4bd5a83e37 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -1,226 +1,230 @@ -# -*- 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 - lldockablefloater.cpp - lldockcontrol.cpp - lldraghandle.cpp - lleditmenuhandler.cpp - llf32uictrl.cpp - llfiltereditor.cpp - llflatlistview.cpp - llfloater.cpp - llfloaterreg.cpp - llflyoutbutton.cpp - llfocusmgr.cpp - llfunctorregistry.cpp - lliconctrl.cpp - llkeywords.cpp - lllayoutstack.cpp - lllineeditor.cpp - lllocalcliprect.cpp - llmenubutton.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 - lltextbase.cpp - lltextbox.cpp - lltexteditor.cpp - lltextparser.cpp - lltransientfloatermgr.cpp - lltransutil.cpp - lltoggleablemenu.cpp - lltooltip.cpp - llui.cpp - lluicolortable.cpp - lluictrl.cpp - lluictrlfactory.cpp - lluiimage.cpp - lluistring.cpp - llundo.cpp - llurlaction.cpp - llurlentry.cpp - llurlmatch.cpp - llurlregistry.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 - lldockablefloater.h - lldockcontrol.h - lleditmenuhandler.h - llf32uictrl.h - llfiltereditor.h - llflatlistview.h - llfloater.h - llfloaterreg.h - llflyoutbutton.h - llfocusmgr.h - llfunctorregistry.h - llhandle.h - llhelp.h - lliconctrl.h - llkeywords.h - lllayoutstack.h - lllazyvalue.h - lllineeditor.h - lllocalcliprect.h - llmenubutton.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 - lltextbase.h - lltextbox.h - lltexteditor.h - lltextparser.h - lltoggleablemenu.h - lltooltip.h - lltransientfloatermgr.h - lltransutil.h - lluicolortable.h - lluiconstants.h - lluictrlfactory.h - lluictrl.h - lluifwd.h - llui.h - lluiimage.h - lluistring.h - llundo.h - llurlaction.h - llurlentry.h - llurlmatch.h - llurlregistry.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 - ${LLMESSAGE_LIBRARIES} - ${LLRENDER_LIBRARIES} - ${LLWINDOW_LIBRARIES} - ${LLIMAGE_LIBRARIES} - ${LLVFS_LIBRARIES} # ugh, just for LLDir - ${LLXUIXML_LIBRARIES} - ${LLXML_LIBRARIES} - ${LLMATH_LIBRARIES} - ${LLCOMMON_LIBRARIES} # must be after llimage, llwindow, llrender - ) - -# Add tests -include(LLAddBuildTest) -SET(llui_TEST_SOURCE_FILES - llurlmatch.cpp - llurlentry.cpp - ) -LL_ADD_PROJECT_UNIT_TESTS(llui "${llui_TEST_SOURCE_FILES}") +# -*- 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
+ lldockablefloater.cpp
+ lldockcontrol.cpp
+ lldraghandle.cpp
+ lleditmenuhandler.cpp
+ llf32uictrl.cpp
+ llfiltereditor.cpp
+ llflatlistview.cpp
+ llfloater.cpp
+ llfloaterreg.cpp
+ llfloaterreglistener.cpp
+ llflyoutbutton.cpp
+ llfocusmgr.cpp
+ llfunctorregistry.cpp
+ lliconctrl.cpp
+ llkeywords.cpp
+ lllayoutstack.cpp
+ lllineeditor.cpp
+ lllocalcliprect.cpp
+ llmenubutton.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
+ lltextbase.cpp
+ lltextbox.cpp
+ lltexteditor.cpp
+ lltextparser.cpp
+ lltransientfloatermgr.cpp
+ lltransutil.cpp
+ lltoggleablemenu.cpp
+ lltooltip.cpp
+ llui.cpp
+ lluicolortable.cpp
+ lluictrl.cpp
+ lluictrlfactory.cpp
+ lluiimage.cpp
+ lluistring.cpp
+ llundo.cpp
+ llurlaction.cpp
+ llurlentry.cpp
+ llurlmatch.cpp
+ llurlregistry.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
+ lldockablefloater.h
+ lldockcontrol.h
+ lleditmenuhandler.h
+ llf32uictrl.h
+ llfiltereditor.h
+ llflatlistview.h
+ llfloater.h
+ llfloaterreg.h
+ llfloaterreglistener.h
+ llflyoutbutton.h
+ llfocusmgr.h
+ llfunctorregistry.h
+ llhandle.h
+ llhelp.h
+ lliconctrl.h
+ llkeywords.h
+ lllayoutstack.h
+ lllazyvalue.h
+ lllineeditor.h
+ lllocalcliprect.h
+ llmenubutton.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
+ lltextbase.h
+ lltextbox.h
+ lltexteditor.h
+ lltextparser.h
+ lltoggleablemenu.h
+ lltooltip.h
+ lltransientfloatermgr.h
+ lltransutil.h
+ lluicolortable.h
+ lluiconstants.h
+ lluictrlfactory.h
+ lluictrl.h
+ lluifwd.h
+ llui.h
+ lluiimage.h
+ lluistring.h
+ llundo.h
+ llurlaction.h
+ llurlentry.h
+ llurlmatch.h
+ llurlregistry.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
+ ${LLMESSAGE_LIBRARIES}
+ ${LLRENDER_LIBRARIES}
+ ${LLWINDOW_LIBRARIES}
+ ${LLIMAGE_LIBRARIES}
+ ${LLVFS_LIBRARIES} # ugh, just for LLDir
+ ${LLXUIXML_LIBRARIES}
+ ${LLXML_LIBRARIES}
+ ${LLMATH_LIBRARIES}
+ ${LLCOMMON_LIBRARIES} # must be after llimage, llwindow, llrender
+ )
+
+# Add tests
+include(LLAddBuildTest)
+SET(llui_TEST_SOURCE_FILES
+ llurlmatch.cpp
+ llurlentry.cpp
+ )
+LL_ADD_PROJECT_UNIT_TESTS(llui "${llui_TEST_SOURCE_FILES}")
diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp index 285ce82d2d..e0053b4cc7 100644 --- a/indra/llui/llconsole.cpp +++ b/indra/llui/llconsole.cpp @@ -120,6 +120,11 @@ void LLConsole::setFontSize(S32 size_index) { mFont = LLFontGL::getFontSansSerifHuge(); } + // Make sure the font exists + if (mFont == NULL) + { + mFont = LLFontGL::getFontDefault(); + } for(paragraph_t::iterator paragraph_it = mParagraphs.begin(); paragraph_it != mParagraphs.end(); paragraph_it++) { diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 0c0c5921ce..021e2e94ac 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -61,6 +61,7 @@ #include "lltrans.h" #include "llhelp.h" #include "llmultifloater.h" +#include "llsdutil.h" // use this to control "jumping" behavior when Ctrl-Tabbing const S32 TABBED_FLOATER_OFFSET = 0; @@ -132,6 +133,16 @@ LLFloater::handle_map_t LLFloater::sFloaterMap; LLFloaterView* gFloaterView = NULL; +/*==========================================================================*| +// DEV-38598: The fundamental problem with this operation is that it can only +// support a subset of LLSD values. While it's plausible to compare two arrays +// lexicographically, what strict ordering can you impose on maps? +// (LLFloaterTOS's current key is an LLSD map.) + +// Of course something like this is necessary if you want to build a std::set +// or std::map with LLSD keys. Fortunately we're getting by with other +// container types for now. + //static bool LLFloater::KeyCompare::compare(const LLSD& a, const LLSD& b) { @@ -159,32 +170,11 @@ bool LLFloater::KeyCompare::compare(const LLSD& a, const LLSD& b) else return false; // no valid operation for Binary } +|*==========================================================================*/ bool LLFloater::KeyCompare::equate(const LLSD& a, const LLSD& b) { - if (a.type() != b.type()) - { - //llerrs << "Mismatched LLSD types: (" << a << ") mismatches (" << b << ")" << llendl; - return false; - } - else if (a.isUndefined()) - return true; - else if (a.isInteger()) - return a.asInteger() == b.asInteger(); - else if (a.isReal()) - return a.asReal() == b.asReal(); - else if (a.isString()) - return a.asString() == b.asString(); - else if (a.isUUID()) - return a.asUUID() == b.asUUID(); - else if (a.isDate()) - return a.asDate() == b.asDate(); - else if (a.isURI()) - return a.asString() == b.asString(); // compare URIs as strings - else if (a.isBoolean()) - return a.asBoolean() == b.asBoolean(); - else - return false; // no valid operation for Binary + return llsd_equals(a, b); } //************************************ @@ -2629,3 +2619,13 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr o return true; // *TODO: Error checking } +bool LLFloater::isShown() const +{ + return ! isMinimized() && isInVisibleChain(); +} + +/* static */ +bool LLFloater::isShown(const LLFloater* floater) +{ + return floater && floater->isShown(); +} diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 123de12398..2fdaecf59a 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -87,12 +87,14 @@ friend class LLMultiFloater; public: struct KeyCompare { - static bool compare(const LLSD& a, const LLSD& b); +// static bool compare(const LLSD& a, const LLSD& b); static bool equate(const LLSD& a, const LLSD& b); +/*==========================================================================*| bool operator()(const LLSD& a, const LLSD& b) const { return compare(a, b); } +|*==========================================================================*/ }; enum EFloaterButtons @@ -183,7 +185,13 @@ public: void addDependentFloater(LLHandle<LLFloater> dependent_handle, BOOL reposition = TRUE); LLFloater* getDependee() { return (LLFloater*)mDependeeHandle.get(); } void removeDependentFloater(LLFloater* dependent); - BOOL isMinimized() { return mMinimized; } + BOOL isMinimized() const { return mMinimized; } + /// isShown() differs from getVisible() in that isShown() also considers + /// isMinimized(). isShown() is true only if visible and not minimized. + bool isShown() const; + /// The static isShown() can accept a NULL pointer (which of course + /// returns false). When non-NULL, it calls the non-static isShown(). + static bool isShown(const LLFloater* floater); BOOL isFrontmost(); BOOL isDependent() { return !mDependeeHandle.isDead(); } void setCanMinimize(BOOL can_minimize); diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index 815260dff3..3c5a8a6921 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 @@ -249,7 +252,7 @@ bool LLFloaterReg::hideInstance(const std::string& name, const LLSD& key) bool LLFloaterReg::toggleInstance(const std::string& name, const LLSD& key) { LLFloater* instance = findInstance(name, key); - if (instance && !instance->isMinimized() && instance->isInVisibleChain()) + if (LLFloater::isShown(instance)) { // When toggling *visibility*, close the host instead of the floater when hosted if (instance->getHost()) @@ -269,14 +272,7 @@ bool LLFloaterReg::toggleInstance(const std::string& name, const LLSD& key) bool LLFloaterReg::instanceVisible(const std::string& name, const LLSD& key) { LLFloater* instance = findInstance(name, key); - if (instance && !instance->isMinimized() && instance->isInVisibleChain()) - { - return true; - } - else - { - return false; - } + return LLFloater::isShown(instance); } //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..57d148b5af --- /dev/null +++ b/indra/llui/llfloaterreglistener.cpp @@ -0,0 +1,114 @@ +/** + * @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" +#include "llfloater.h" +#include "llbutton.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); + LLSD requiredNameButton; + requiredNameButton["name"] = LLSD(); + requiredNameButton["button"] = LLSD(); + add("clickButton", &LLFloaterRegListener::clickButton, requiredNameButton); +} + +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"]); +} + +void LLFloaterRegListener::clickButton(const LLSD& event) const +{ + // If the caller requests a reply, build the reply. + LLReqID reqID(event); + LLSD reply(reqID.makeResponse()); + + LLFloater* floater = LLFloaterReg::findInstance(event["name"], event["key"]); + if (! LLFloater::isShown(floater)) + { + reply["type"] = "LLFloater"; + reply["name"] = event["name"]; + reply["key"] = event["key"]; + reply["error"] = floater? "!isShown()" : "NULL"; + } + else + { + // Here 'floater' points to an LLFloater instance with the specified + // name and key which isShown(). + LLButton* button = floater->findChild<LLButton>(event["button"]); + if (! LLButton::isAvailable(button)) + { + reply["type"] = "LLButton"; + reply["name"] = event["button"]; + reply["error"] = button? "!isAvailable()" : "NULL"; + } + else + { + // Here 'button' points to an isAvailable() LLButton child of + // 'floater' with the specified button name. Pretend to click it. + button->onCommit(); + // Leave reply["error"] isUndefined(): no error, i.e. success. + } + } + + // Send a reply only if caller asked for a reply. + LLSD replyPump(event["reply"]); + if (replyPump.isString()) // isUndefined() if absent + { + LLEventPumps::instance().obtain(replyPump).post(reply); + } +} diff --git a/indra/llui/llfloaterreglistener.h b/indra/llui/llfloaterreglistener.h new file mode 100644 index 0000000000..304ecd1090 --- /dev/null +++ b/indra/llui/llfloaterreglistener.h @@ -0,0 +1,36 @@ +/** + * @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; + void clickButton(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/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp index f77ec5f4c7..387af05935 100644 --- a/indra/llui/llmodaldialog.cpp +++ b/indra/llui/llmodaldialog.cpp @@ -297,5 +297,16 @@ void LLModalDialog::onAppFocusGained() } } - - +void LLModalDialog::shutdownModals() +{ + // This method is only for use during app shutdown. ~LLModalDialog() + // checks sModalStack, and if the dialog instance is still there, it + // crumps with "Attempt to delete dialog while still in sModalStack!" But + // at app shutdown, all bets are off. If the user asks to shut down the + // app, we shouldn't have to care WHAT's open. Put differently, if a modal + // dialog is so crucial that we can't let the user terminate until s/he + // addresses it, we should reject a termination request. The current state + // of affairs is that we accept it, but then produce an llerrs popup that + // simply makes our software look unreliable. + sModalStack.clear(); +} diff --git a/indra/llui/llmodaldialog.h b/indra/llui/llmodaldialog.h index 9d716a1880..863572fb5a 100644 --- a/indra/llui/llmodaldialog.h +++ b/indra/llui/llmodaldialog.h @@ -73,7 +73,8 @@ public: static void onAppFocusGained(); static S32 activeCount() { return sModalStack.size(); } - + static void shutdownModals(); + protected: void centerOnScreen(); diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 25e2475f59..a68b9cae57 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; - } - pNotif->cancel(); - updateItem(LLSD().insert("sigtype", "delete").insert("id", pNotif->id()), pNotif); -} - -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;
+ }
+ pNotif->cancel();
+ updateItem(LLSD().insert("sigtype", "delete").insert("id", pNotif->id()), pNotif);
+}
+
+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 07b6895378..f86776384a 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 3ecf629082..ed22c0a47f 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/llui/llui.cpp b/indra/llui/llui.cpp index f253857851..da9384f876 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -1952,7 +1952,12 @@ namespace LLInitParam return fontp; } } - + + if (mData.mValue == NULL) + { + mData.mValue = LLFontGL::getFontDefault(); + } + // default to current value return mData.mValue; } diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 31f12fe312..fe7fd59de8 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -438,6 +438,18 @@ void LLView::setEnabled(BOOL enabled) } //virtual +bool LLView::isAvailable() const +{ + return isInEnabledChain() && isInVisibleChain(); +} + +//static +bool LLView::isAvailable(const LLView* view) +{ + return view && view->isAvailable(); +} + +//virtual BOOL LLView::setLabelArg( const std::string& key, const LLStringExplicit& text ) { return FALSE; diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 73146b2c1f..c3b442e022 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -304,6 +304,11 @@ public: BOOL getVisible() const { return mVisible; } virtual void setEnabled(BOOL enabled); BOOL getEnabled() const { return mEnabled; } + /// 'available' in this context means 'visible and enabled': in other + /// words, can a user actually interact with this? + virtual bool isAvailable() const; + /// The static isAvailable() tests an LLView* that could be NULL. + static bool isAvailable(const LLView* view); U8 getSoundFlags() const { return mSoundFlags; } virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); diff --git a/indra/llvfs/llpidlock.cpp b/indra/llvfs/llpidlock.cpp index 93ac120302..315baa001d 100755 --- a/indra/llvfs/llpidlock.cpp +++ b/indra/llvfs/llpidlock.cpp @@ -62,7 +62,7 @@ class LLPidLockFile mSaving(FALSE), mWaiting(FALSE), mClean(TRUE), mPID(getpid()) { - mLockName = gDirUtilp->getTempDir() + "/savelock"; + mLockName = gDirUtilp->getTempDir() + gDirUtilp->getDirDelimiter() + "savelock"; } bool requestLock(LLNameTable<void *> *name_table, bool autosave, bool force_immediate=FALSE, F32 timeout=300.0); 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/llxuixml/lluicolor.cpp b/indra/llxuixml/lluicolor.cpp index 856c05cf4a..424d878a6b 100644 --- a/indra/llxuixml/lluicolor.cpp +++ b/indra/llxuixml/lluicolor.cpp @@ -7,6 +7,8 @@ * $/LicenseInfo$ */ +#include "linden_common.h" + #include "lluicolor.h" LLUIColor::LLUIColor() diff --git a/indra/lscript/lscript_byteformat.h b/indra/lscript/lscript_byteformat.h index ba2c46bef2..a54ebd5831 100644 --- a/indra/lscript/lscript_byteformat.h +++ b/indra/lscript/lscript_byteformat.h @@ -556,7 +556,7 @@ const U32 LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_EOF] = // http_request string constants extern const char* URL_REQUEST_GRANTED; extern const char* URL_REQUEST_DENIED; -extern const U64 LSL_HTTP_REQUEST_TIMEOUT; +extern const U64 LSL_HTTP_REQUEST_TIMEOUT_USEC; #endif diff --git a/indra/lscript/lscript_compile/lscript_tree.cpp b/indra/lscript/lscript_compile/lscript_tree.cpp index 98146d6f2b..3b8bbbe805 100644 --- a/indra/lscript/lscript_compile/lscript_tree.cpp +++ b/indra/lscript/lscript_compile/lscript_tree.cpp @@ -631,9 +631,7 @@ static void print_cil_cast(LLFILE* fp, LSCRIPTType srcType, LSCRIPTType targetTy switch(targetType) { case LST_INTEGER: - //fprintf(fp, "call int32 [LslLibrary]LindenLab.SecondLife.LslRunTime::ToInteger(float32)\n"); - fprintf(fp, "conv.i4\n"); // TODO replace this line with the above - // we the entire grid is > 1.25.1 + fprintf(fp, "call int32 [LslLibrary]LindenLab.SecondLife.LslRunTime::ToInteger(float32)\n"); break; case LST_STRING: fprintf(fp, "call string [LslLibrary]LindenLab.SecondLife.LslRunTime::ToString(float32)\n"); @@ -8375,10 +8373,18 @@ void LLScriptStateChange::recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTComp chunk->addInteger(mIdentifier->mScopeEntry->mCount); } break; + case LSCP_TYPE: + mReturnType = basetype; + break; case LSCP_EMIT_CIL_ASSEMBLY: fprintf(fp, "ldarg.0\n"); fprintf(fp, "ldstr \"%s\"\n", mIdentifier->mName); fprintf(fp, "call instance void class [LslUserScript]LindenLab.SecondLife.LslUserScript::ChangeState(string)\n"); + // We are doing a state change. In the LSL interpreter, this is basically a longjmp. We emulate it + // here using a call to the ChangeState followed by a short cut return of the current method. To + // maintain type safety we need to push an arbitrary variable of the current method's return type + // onto the stack before returning. This will be ignored and discarded. + print_cil_init_variable(fp, mReturnType); fprintf(fp, "ret\n"); break; default: diff --git a/indra/lscript/lscript_compile/lscript_tree.h b/indra/lscript/lscript_compile/lscript_tree.h index 12c16908af..a667e1eb5b 100644 --- a/indra/lscript/lscript_compile/lscript_tree.h +++ b/indra/lscript/lscript_compile/lscript_tree.h @@ -1888,6 +1888,7 @@ public: S32 getSize(); LLScriptIdentifier *mIdentifier; + LSCRIPTType mReturnType; }; class LLScriptJump : public LLScriptStatement diff --git a/indra/lscript/lscript_execute.h b/indra/lscript/lscript_execute.h index 245fc320d1..96855abea0 100644 --- a/indra/lscript/lscript_execute.h +++ b/indra/lscript/lscript_execute.h @@ -371,8 +371,7 @@ class LLScriptExecute { public: LLScriptExecute(); - virtual ~LLScriptExecute() {;} - + virtual ~LLScriptExecute() = 0; virtual S32 getVersion() const = 0; virtual void deleteAllEvents() = 0; virtual void addEvent(LLScriptDataCollection* event) = 0; 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/lscript/lscript_execute/lscript_execute.cpp b/indra/lscript/lscript_execute/lscript_execute.cpp index 2fd81210c0..e849fa9a6e 100644 --- a/indra/lscript/lscript_execute/lscript_execute.cpp +++ b/indra/lscript/lscript_execute/lscript_execute.cpp @@ -41,6 +41,8 @@ #include "lscript_library.h" #include "lscript_heapruntime.h" #include "lscript_alloc.h" +#include "llstat.h" + // Static const S32 DEFAULT_SCRIPT_TIMER_CHECK_SKIP = 4; @@ -72,7 +74,7 @@ const char* URL_REQUEST_GRANTED = "URL_REQUEST_GRANTED"; const char* URL_REQUEST_DENIED = "URL_REQUEST_DENIED"; // HTTP Requests to LSL scripts will time out after 25 seconds. -const U64 LSL_HTTP_REQUEST_TIMEOUT = 25 * USEC_PER_SEC; +const U64 LSL_HTTP_REQUEST_TIMEOUT_USEC = 25 * USEC_PER_SEC; LLScriptExecuteLSL2::LLScriptExecuteLSL2(LLFILE *fp) { @@ -110,6 +112,7 @@ LLScriptExecuteLSL2::LLScriptExecuteLSL2(const U8* bytecode, U32 bytecode_size) init(); } +LLScriptExecute::~LLScriptExecute() {} LLScriptExecuteLSL2::~LLScriptExecuteLSL2() { delete[] mBuffer; @@ -4234,19 +4237,16 @@ S32 lscript_push_variable(LLScriptLibData *data, U8 *buffer) return 4; } -BOOL run_calllib(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id) + +// Shared code for run_calllib() and run_calllib_two_byte() +BOOL run_calllib_common(U8 *buffer, S32 &offset, const LLUUID &id, U16 arg) { - if (b_print) - printf("[0x%X]\tCALLLIB ", offset); - offset++; - U8 arg = safe_instruction_bytestream2byte(buffer, offset); - if (arg >= (U8)gScriptLibrary.mFunctions.size()) + if (arg >= gScriptLibrary.mFunctions.size()) { set_fault(buffer, LSRF_BOUND_CHECK_ERROR); return FALSE; } - if (b_print) - printf("%d (%s)\n", (U32)arg, gScriptLibrary.mFunctions[arg].mName); + LLScriptLibraryFunction const & function = gScriptLibrary.mFunctions[arg]; // pull out the arguments and the return values LLScriptLibData *arguments = NULL; @@ -4254,14 +4254,14 @@ BOOL run_calllib(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id) S32 i, number; - if (gScriptLibrary.mFunctions[arg].mReturnType) + if (function.mReturnType) { returnvalue = new LLScriptLibData; } - if (gScriptLibrary.mFunctions[arg].mArgs) + if (function.mArgs) { - number = (S32)strlen(gScriptLibrary.mFunctions[arg].mArgs); /*Flawfinder: ignore*/ + number = (S32)strlen(function.mArgs); //Flawfinder: ignore arguments = new LLScriptLibData[number]; } else @@ -4271,23 +4271,18 @@ BOOL run_calllib(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id) for (i = number - 1; i >= 0; i--) { - lscript_pop_variable(&arguments[i], buffer, gScriptLibrary.mFunctions[arg].mArgs[i]); + lscript_pop_variable(&arguments[i], buffer, function.mArgs[i]); } - if (b_print) - { - printf("See LSLTipText_%s in strings.xml for usage\n", gScriptLibrary.mFunctions[arg].mName); - } + // Actually execute the function call + function.mExecFunc(returnvalue, arguments, id); - { - gScriptLibrary.mFunctions[arg].mExecFunc(returnvalue, arguments, id); - } - add_register_fp(buffer, LREG_ESR, -gScriptLibrary.mFunctions[arg].mEnergyUse); - add_register_fp(buffer, LREG_SLR, gScriptLibrary.mFunctions[arg].mSleepTime); + add_register_fp(buffer, LREG_ESR, -(function.mEnergyUse)); + add_register_fp(buffer, LREG_SLR, function.mSleepTime); if (returnvalue) { - returnvalue->mType = char2type(*gScriptLibrary.mFunctions[arg].mReturnType); + returnvalue->mType = char2type(*function.mReturnType); lscript_push_return_variable(returnvalue, buffer); } @@ -4304,71 +4299,32 @@ BOOL run_calllib(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id) } -BOOL run_calllib_two_byte(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id) +BOOL run_calllib(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id) { - if (b_print) - printf("[0x%X]\tCALLLIB ", offset); offset++; - U16 arg = safe_instruction_bytestream2u16(buffer, offset); - if (arg >= (U16)gScriptLibrary.mFunctions.size()) + U16 arg = (U16) safe_instruction_bytestream2byte(buffer, offset); + if (b_print && + arg < gScriptLibrary.mFunctions.size()) { - set_fault(buffer, LSRF_BOUND_CHECK_ERROR); - return FALSE; - } - if (b_print) - printf("%d (%s)\n", (U32)arg, gScriptLibrary.mFunctions[arg].mName); - - // pull out the arguments and the return values - LLScriptLibData *arguments = NULL; - LLScriptLibData *returnvalue = NULL; - - S32 i, number; - - if (gScriptLibrary.mFunctions[arg].mReturnType) - { - returnvalue = new LLScriptLibData; - } - - if (gScriptLibrary.mFunctions[arg].mArgs) - { - number = (S32)strlen(gScriptLibrary.mFunctions[arg].mArgs); /*Flawfinder: ignore*/ - arguments = new LLScriptLibData[number]; - } - else - { - number = 0; - } - - for (i = number - 1; i >= 0; i--) - { - lscript_pop_variable(&arguments[i], buffer, gScriptLibrary.mFunctions[arg].mArgs[i]); - } - - if (b_print) - { - printf("See LSLTipText_%s in strings.xml for usage\n", gScriptLibrary.mFunctions[arg].mName); - } - - { - gScriptLibrary.mFunctions[arg].mExecFunc(returnvalue, arguments, id); + printf("[0x%X]\tCALLLIB ", offset); + LLScriptLibraryFunction const & function = gScriptLibrary.mFunctions[arg]; + printf("%d (%s)\n", (U32)arg, function.mName); + //printf("%s\n", function.mDesc); } - add_register_fp(buffer, LREG_ESR, -gScriptLibrary.mFunctions[arg].mEnergyUse); - add_register_fp(buffer, LREG_SLR, gScriptLibrary.mFunctions[arg].mSleepTime); + return run_calllib_common(buffer, offset, id, arg); +} - if (returnvalue) +BOOL run_calllib_two_byte(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id) +{ + offset++; + U16 arg = safe_instruction_bytestream2u16(buffer, offset); + if (b_print && + arg < gScriptLibrary.mFunctions.size()) { - returnvalue->mType = char2type(*gScriptLibrary.mFunctions[arg].mReturnType); - lscript_push_return_variable(returnvalue, buffer); + printf("[0x%X]\tCALLLIB ", (offset-1)); + LLScriptLibraryFunction const & function = gScriptLibrary.mFunctions[arg]; + printf("%d (%s)\n", (U32)arg, function.mName); + //printf("%s\n", function.mDesc); } - - delete [] arguments; - delete returnvalue; - - // reset the BP after calling the library files - S32 bp = lscript_pop_int(buffer); - set_bp(buffer, bp); - - // pop off the spot for the instruction pointer - lscript_poparg(buffer, 4); - return FALSE; + return run_calllib_common(buffer, offset, id, arg); } diff --git a/indra/media_plugins/quicktime/CMakeLists.txt b/indra/media_plugins/quicktime/CMakeLists.txt index db11c9ae21..f0b8f0d167 100644 --- a/indra/media_plugins/quicktime/CMakeLists.txt +++ b/indra/media_plugins/quicktime/CMakeLists.txt @@ -56,6 +56,14 @@ add_dependencies(media_plugin_quicktime ${LLCOMMON_LIBRARIES} ) +if (WINDOWS) + set_target_properties( + media_plugin_quicktime + PROPERTIES + LINK_FLAGS "/MANIFEST:NO" + ) +endif (WINDOWS) + if (QUICKTIME) add_definitions(-DLL_QUICKTIME_ENABLED=1) diff --git a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp index fbda65120d..7100d03f05 100644 --- a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp +++ b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp @@ -1,981 +1,1077 @@ -/** - * @file media_plugin_quicktime.cpp - * @brief QuickTime plugin for LLMedia API plugin system - * - * $LicenseInfo:firstyear=2008&license=viewergpl$ - * - * Copyright (c) 2008, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llgl.h" - -#include "llplugininstance.h" -#include "llpluginmessage.h" -#include "llpluginmessageclasses.h" -#include "media_plugin_base.h" - -#if LL_QUICKTIME_ENABLED - -#if defined(LL_DARWIN) - #include <QuickTime/QuickTime.h> -#elif defined(LL_WINDOWS) - #include "MacTypes.h" - #include "QTML.h" - #include "Movies.h" - #include "QDoffscreen.h" - #include "FixMath.h" -#endif - -// TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint -//////////////////////////////////////////////////////////////////////////////// -// -class MediaPluginQuickTime : public MediaPluginBase -{ -public: - MediaPluginQuickTime(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); - ~MediaPluginQuickTime(); - - /* virtual */ void receiveMessage(const char *message_string); - -private: - - int mNaturalWidth; - int mNaturalHeight; - Movie mMovieHandle; - GWorldPtr mGWorldHandle; - ComponentInstance mMovieController; - int mCurVolume; - bool mMediaSizeChanging; - bool mIsLooping; - const int mMinWidth; - const int mMaxWidth; - const int mMinHeight; - const int mMaxHeight; - F64 mPlayRate; - - enum ECommand { - COMMAND_NONE, - COMMAND_STOP, - COMMAND_PLAY, - COMMAND_FAST_FORWARD, - COMMAND_FAST_REWIND, - COMMAND_PAUSE, - COMMAND_SEEK, - }; - ECommand mCommand; - - // Override this to add current time and duration to the message - /*virtual*/ void setDirty(int left, int top, int right, int bottom) - { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated"); - - message.setValueS32("left", left); - message.setValueS32("top", top); - message.setValueS32("right", right); - message.setValueS32("bottom", bottom); - - if(mMovieHandle) - { - message.setValueReal("current_time", getCurrentTime()); - message.setValueReal("duration", getDuration()); - message.setValueReal("current_rate", Fix2X(GetMovieRate(mMovieHandle))); - } - - sendMessage(message); - } - - - static Rect rectFromSize(int width, int height) - { - Rect result; - - - result.left = 0; - result.top = 0; - result.right = width; - result.bottom = height; - - return result; - } - - Fixed getPlayRate(void) - { - Fixed result; - if(mPlayRate == 0.0f) - { - // Default to the movie's preferred rate - result = GetMoviePreferredRate(mMovieHandle); - if(result == 0) - { - // Don't return a 0 play rate, ever. - std::cerr << "Movie's preferred rate is 0, forcing to 1.0." << std::endl; - result = X2Fix(1.0f); - } - } - else - { - result = X2Fix(mPlayRate); - } - - return result; - } - - void load( const std::string url ) - { - if ( url.empty() ) - return; - - // Stop and unload any existing movie before starting another one. - unload(); - - setStatus(STATUS_LOADING); - - //In case std::string::c_str() makes a copy of the url data, - //make sure there is memory to hold it before allocating memory for handle. - //if fails, NewHandleClear(...) should return NULL. - const char* url_string = url.c_str() ; - Handle handle = NewHandleClear( ( Size )( url.length() + 1 ) ); - if ( NULL == handle || noErr != MemError() || NULL == *handle ) - { - setStatus(STATUS_ERROR); - return; - } - - BlockMove( url_string, *handle, ( Size )( url.length() + 1 ) ); - - OSErr err = NewMovieFromDataRef( &mMovieHandle, newMovieActive | newMovieDontInteractWithUser | newMovieAsyncOK | newMovieIdleImportOK, nil, handle, URLDataHandlerSubType ); - DisposeHandle( handle ); - if ( noErr != err ) - { - setStatus(STATUS_ERROR); - return; - }; - - // do pre-roll actions (typically fired for streaming movies but not always) - PrePrerollMovie( mMovieHandle, 0, getPlayRate(), moviePrePrerollCompleteCallback, ( void * )this ); - - Rect movie_rect = rectFromSize(mWidth, mHeight); - - // make a new movie controller - mMovieController = NewMovieController( mMovieHandle, &movie_rect, mcNotVisible | mcTopLeftMovie ); - - // movie controller - MCSetActionFilterWithRefCon( mMovieController, mcActionFilterCallBack, ( long )this ); - - SetMoviePlayHints( mMovieHandle, hintsAllowDynamicResize, hintsAllowDynamicResize ); - - // function that gets called when a frame is drawn - SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, movieDrawingCompleteCallback, ( long )this ); - - setStatus(STATUS_LOADED); - - sizeChanged(); - }; - - bool unload() - { - if ( mMovieHandle ) - { - StopMovie( mMovieHandle ); - if ( mMovieController ) - { - MCMovieChanged( mMovieController, mMovieHandle ); - }; - }; - - if ( mMovieController ) - { - MCSetActionFilterWithRefCon( mMovieController, NULL, (long)this ); - DisposeMovieController( mMovieController ); - mMovieController = NULL; - }; - - if ( mMovieHandle ) - { - SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, nil, ( long )this ); - DisposeMovie( mMovieHandle ); - mMovieHandle = NULL; - }; - - if ( mGWorldHandle ) - { - DisposeGWorld( mGWorldHandle ); - mGWorldHandle = NULL; - }; - - setStatus(STATUS_NONE); - - return true; - } - - bool navigateTo( const std::string url ) - { - unload(); - load( url ); - - return true; - }; - - bool sizeChanged() - { - if ( ! mMovieHandle ) - return false; - - // Check to see whether the movie's natural size has updated - { - int width, height; - getMovieNaturalSize(&width, &height); - if((width != 0) && (height != 0) && ((width != mNaturalWidth) || (height != mNaturalHeight))) - { - mNaturalWidth = width; - mNaturalHeight = height; - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request"); - message.setValue("name", mTextureSegmentName); - message.setValueS32("width", width); - message.setValueS32("height", height); - sendMessage(message); - //std::cerr << "<--- Sending size change request to application with name: " << mTextureSegmentName << " - size is " << width << " x " << height << std::endl; - } - } - - // sanitize destination size - Rect dest_rect = rectFromSize(mWidth, mHeight); - - // media depth won't change - int depth_bits = mDepth * 8; - long rowbytes = mDepth * mTextureWidth; - - GWorldPtr old_gworld_handle = mGWorldHandle; - - if(mPixels != NULL) - { - // We have pixels. Set up a GWorld pointing at the texture. - OSErr result = NewGWorldFromPtr( &mGWorldHandle, depth_bits, &dest_rect, NULL, NULL, 0, (Ptr)mPixels, rowbytes); - if ( noErr != result ) - { - // TODO: unrecoverable?? throw exception? return something? - return false; - } - } - else - { - // We don't have pixels. Create a fake GWorld we can point the movie at when it's not safe to render normally. - Rect tempRect = rectFromSize(1, 1); - OSErr result = NewGWorld( &mGWorldHandle, depth_bits, &tempRect, NULL, NULL, 0); - if ( noErr != result ) - { - // TODO: unrecoverable?? throw exception? return something? - return false; - } - } - - SetMovieGWorld( mMovieHandle, mGWorldHandle, GetGWorldDevice( mGWorldHandle ) ); - - // If the GWorld was already set up, delete it. - if(old_gworld_handle != NULL) - { - DisposeGWorld( old_gworld_handle ); - } - - // Set up the movie display matrix - { - // scale movie to fit rect and invert vertically to match opengl image format - MatrixRecord transform; - SetIdentityMatrix( &transform ); // transforms are additive so start from identify matrix - double scaleX = (double) mWidth / mNaturalWidth; - double scaleY = -1.0 * (double) mHeight / mNaturalHeight; - double centerX = mWidth / 2.0; - double centerY = mHeight / 2.0; - ScaleMatrix( &transform, X2Fix( scaleX ), X2Fix( scaleY ), X2Fix( centerX ), X2Fix( centerY ) ); - SetMovieMatrix( mMovieHandle, &transform ); - } - - // update movie controller - if ( mMovieController ) - { - MCSetControllerPort( mMovieController, mGWorldHandle ); - MCPositionController( mMovieController, &dest_rect, &dest_rect, - mcTopLeftMovie | mcPositionDontInvalidate ); - MCMovieChanged( mMovieController, mMovieHandle ); - } - - - // Emit event with size change so the calling app knows about it too - // TODO: - //LLMediaEvent event( this ); - //mEventEmitter.update( &LLMediaObserver::onMediaSizeChange, event ); - - return true; - } - - static Boolean mcActionFilterCallBack( MovieController mc, short action, void *params, long ref ) - { - Boolean result = false; - - MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; - - switch( action ) - { - // handle window resizing - case mcActionControllerSizeChanged: - // Ensure that the movie draws correctly at the new size - self->sizeChanged(); - break; - - // Block any movie controller actions that open URLs. - case mcActionLinkToURL: - case mcActionGetNextURL: - case mcActionLinkToURLExtended: - // Prevent the movie controller from handling the message - result = true; - break; - - default: - break; - }; - - return result; - }; - - static OSErr movieDrawingCompleteCallback( Movie call_back_movie, long ref ) - { - MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; - - // IMPORTANT: typically, a consumer who is observing this event will set a flag - // when this event is fired then render later. Be aware that the media stream - // can change during this period - dimensions, depth, format etc. - //LLMediaEvent event( self ); -// self->updateQuickTime(); - // TODO ^^^ - - if ( self->mWidth > 0 && self->mHeight > 0 ) - self->setDirty( 0, 0, self->mWidth, self->mHeight ); - - return noErr; - }; - - static void moviePrePrerollCompleteCallback( Movie movie, OSErr preroll_err, void *ref ) - { - //MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; - - // TODO: - //LLMediaEvent event( self ); - //self->mEventEmitter.update( &LLMediaObserver::onMediaPreroll, event ); - }; - - - void rewind() - { - GoToBeginningOfMovie( mMovieHandle ); - MCMovieChanged( mMovieController, mMovieHandle ); - }; - - bool processState() - { - if ( mCommand == COMMAND_PLAY ) - { - if ( mStatus == STATUS_LOADED || mStatus == STATUS_PAUSED || mStatus == STATUS_PLAYING ) - { - long state = GetMovieLoadState( mMovieHandle ); - - if ( state >= kMovieLoadStatePlaythroughOK ) - { - // if the movie is at the end (generally because it reached it naturally) - // and we play is requested, jump back to the start of the movie. - // note: this is different from having loop flag set. - if ( IsMovieDone( mMovieHandle ) ) - { - Fixed rate = X2Fix( 0.0 ); - MCDoAction( mMovieController, mcActionPlay, (void*)rate ); - rewind(); - }; - - MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() ); - MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); - setStatus(STATUS_PLAYING); - mCommand = COMMAND_NONE; - }; - }; - } - else - if ( mCommand == COMMAND_STOP ) - { - if ( mStatus == STATUS_PLAYING || mStatus == STATUS_PAUSED ) - { - if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK ) - { - Fixed rate = X2Fix( 0.0 ); - MCDoAction( mMovieController, mcActionPlay, (void*)rate ); - rewind(); - - setStatus(STATUS_LOADED); - mCommand = COMMAND_NONE; - }; - }; - } - else - if ( mCommand == COMMAND_PAUSE ) - { - if ( mStatus == STATUS_PLAYING ) - { - if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK ) - { - Fixed rate = X2Fix( 0.0 ); - MCDoAction( mMovieController, mcActionPlay, (void*)rate ); - setStatus(STATUS_PAUSED); - mCommand = COMMAND_NONE; - }; - }; - }; - - return true; - }; - - void play(F64 rate) - { - mPlayRate = rate; - mCommand = COMMAND_PLAY; - }; - - void stop() - { - mCommand = COMMAND_STOP; - }; - - void pause() - { - mCommand = COMMAND_PAUSE; - }; - - void getMovieNaturalSize(int *movie_width, int *movie_height) - { - Rect rect; - - GetMovieNaturalBoundsRect( mMovieHandle, &rect ); - - int width = ( rect.right - rect.left ); - int height = ( rect.bottom - rect.top ); - - // make sure width and height fall in valid range - if ( width < mMinWidth ) - width = mMinWidth; - - if ( width > mMaxWidth ) - width = mMaxWidth; - - if ( height < mMinHeight ) - height = mMinHeight; - - if ( height > mMaxHeight ) - height = mMaxHeight; - - // return the new rect - *movie_width = width; - *movie_height = height; - } - - void updateQuickTime(int milliseconds) - { - if ( ! mMovieHandle ) - return; - - if ( ! mMovieController ) - return; - - // service QuickTime - // Calling it this way doesn't have good behavior on Windows... -// MoviesTask( mMovieHandle, milliseconds ); - // This was the original, but I think using both MoviesTask and MCIdle is redundant. Trying with only MCIdle. -// MoviesTask( mMovieHandle, 0 ); - - MCIdle( mMovieController ); - - if ( ! mGWorldHandle ) - return; - - if ( mMediaSizeChanging ) - return; - - // update state machine - processState(); - - // special code for looping - need to rewind at the end of the movie - if ( mIsLooping ) - { - // QT call to see if we are at the end - can't do with controller - if ( IsMovieDone( mMovieHandle ) ) - { - // go back to start - rewind(); - - if ( mMovieController ) - { - // kick off new play - MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() ); - - // set the volume - MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); - }; - }; - }; - }; - - int getDataWidth() const - { - if ( mGWorldHandle ) - { - int depth = mDepth; - - if (depth < 1) - depth = 1; - - // ALWAYS use the row bytes from the PixMap if we have a GWorld because - // sometimes it's not the same as mMediaDepth * mMediaWidth ! - PixMapHandle pix_map_handle = GetGWorldPixMap( mGWorldHandle ); - return QTGetPixMapHandleRowBytes( pix_map_handle ) / depth; - } - else - { - // TODO : return LLMediaImplCommon::getaDataWidth(); - return 0; - } - }; - - void seek( F64 time ) - { - if ( mMovieController ) - { - TimeRecord when; - when.scale = GetMovieTimeScale( mMovieHandle ); - when.base = 0; - - // 'time' is in (floating point) seconds. The timebase time will be in 'units', where - // there are 'scale' units per second. - SInt64 raw_time = ( SInt64 )( time * (double)( when.scale ) ); - - when.value.hi = ( SInt32 )( raw_time >> 32 ); - when.value.lo = ( SInt32 )( ( raw_time & 0x00000000FFFFFFFF ) ); - - MCDoAction( mMovieController, mcActionGoToTime, &when ); - }; - }; - - F64 getDuration() - { - TimeValue duration = GetMovieDuration( mMovieHandle ); - TimeValue scale = GetMovieTimeScale( mMovieHandle ); - - return (F64)duration / (F64)scale; - }; - - F64 getCurrentTime() - { - TimeValue curr_time = GetMovieTime( mMovieHandle, 0 ); - TimeValue scale = GetMovieTimeScale( mMovieHandle ); - - return (F64)curr_time / (F64)scale; - }; - - void setVolume( F64 volume ) - { - mCurVolume = (short)(volume * ( double ) 0x100 ); - - if ( mMovieController ) - { - MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); - }; - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void update(int milliseconds = 0) - { - updateQuickTime(milliseconds); - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void mouseDown( int x, int y ) - { - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void mouseUp( int x, int y ) - { - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void mouseMove( int x, int y ) - { - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void keyPress( unsigned char key ) - { - }; - -}; - -MediaPluginQuickTime::MediaPluginQuickTime( - LLPluginInstance::sendMessageFunction host_send_func, - void *host_user_data ) : - MediaPluginBase(host_send_func, host_user_data), - mMinWidth( 0 ), - mMaxWidth( 2048 ), - mMinHeight( 0 ), - mMaxHeight( 2048 ) -{ -// std::cerr << "MediaPluginQuickTime constructor" << std::endl; - - mNaturalWidth = -1; - mNaturalHeight = -1; - mMovieHandle = 0; - mGWorldHandle = 0; - mMovieController = 0; - mCurVolume = 0x99; - mMediaSizeChanging = false; - mIsLooping = false; - mCommand = COMMAND_NONE; - mPlayRate = 0.0f; - mStatus = STATUS_NONE; -} - -MediaPluginQuickTime::~MediaPluginQuickTime() -{ -// std::cerr << "MediaPluginQuickTime destructor" << std::endl; - - ExitMovies(); - -#ifdef LL_WINDOWS - TerminateQTML(); -// std::cerr << "QuickTime closing down" << std::endl; -#endif -} - - -void MediaPluginQuickTime::receiveMessage(const char *message_string) -{ -// std::cerr << "MediaPluginQuickTime::receiveMessage: received message: \"" << message_string << "\"" << std::endl; - LLPluginMessage message_in; - - if(message_in.parse(message_string) >= 0) - { - std::string message_class = message_in.getClass(); - std::string message_name = message_in.getName(); - if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE) - { - if(message_name == "init") - { - LLPluginMessage message("base", "init_response"); - LLSD versions = LLSD::emptyMap(); - versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; - versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; - // Normally a plugin would only specify one of these two subclasses, but this is a demo... -// versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION; - versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION; - message.setValueLLSD("versions", versions); - - #ifdef LL_WINDOWS - if ( InitializeQTML( 0L ) != noErr ) - { - //TODO: If no QT on Windows, this fails - respond accordingly. - //return false; - } - else - { -// std::cerr << "QuickTime initialized" << std::endl; - }; - #endif - - EnterMovies(); - - std::string plugin_version = "QuickTime media plugin, QuickTime version "; - - long version = 0; - Gestalt( gestaltQuickTimeVersion, &version ); - std::ostringstream codec( "" ); - codec << std::hex << version << std::dec; - plugin_version += codec.str(); - message.setValue("plugin_version", plugin_version); - sendMessage(message); - - // Plugin gets to decide the texture parameters to use. - message.setMessage(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); - #if defined(LL_WINDOWS) - // Values for Windows - mDepth = 3; - message.setValueU32("format", GL_RGB); - message.setValueU32("type", GL_UNSIGNED_BYTE); - - // We really want to pad the texture width to a multiple of 32 bytes, but since we're using 3-byte pixels, it doesn't come out even. - // Padding to a multiple of 3*32 guarantees it'll divide out properly. - message.setValueU32("padding", 32 * 3); - #else - // Values for Mac - mDepth = 4; - message.setValueU32("format", GL_BGRA_EXT); - #ifdef __BIG_ENDIAN__ - message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8_REV ); - #else - message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8); - #endif - - // Pad texture width to a multiple of 32 bytes, to line up with cache lines. - message.setValueU32("padding", 32); - #endif - message.setValueS32("depth", mDepth); - message.setValueU32("internalformat", GL_RGB); - message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left. - message.setValueBoolean("allow_downsample", true); - sendMessage(message); - } - else if(message_name == "idle") - { - // no response is necessary here. - F64 time = message_in.getValueReal("time"); - - // Convert time to milliseconds for update() - update((int)(time * 1000.0f)); - } - else if(message_name == "cleanup") - { - // TODO: clean up here - } - else if(message_name == "shm_added") - { - SharedSegmentInfo info; - info.mAddress = message_in.getValuePointer("address"); - info.mSize = (size_t)message_in.getValueS32("size"); - std::string name = message_in.getValue("name"); - - -// std::cerr << "MediaPluginQuickTime::receiveMessage: shared memory added, name: " << name -// << ", size: " << info.mSize -// << ", address: " << info.mAddress -// << std::endl; - - mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); - - } - else if(message_name == "shm_remove") - { - std::string name = message_in.getValue("name"); - -// std::cerr << "MediaPluginQuickTime::receiveMessage: shared memory remove, name = " << name << std::endl; - - SharedSegmentMap::iterator iter = mSharedSegments.find(name); - if(iter != mSharedSegments.end()) - { - if(mPixels == iter->second.mAddress) - { - // This is the currently active pixel buffer. Make sure we stop drawing to it. - mPixels = NULL; - mTextureSegmentName.clear(); - - // Make sure the movie GWorld is no longer pointed at the shared segment. - sizeChanged(); - } - mSharedSegments.erase(iter); - } - else - { -// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown shared memory region!" << std::endl; - } - - // Send the response so it can be cleaned up. - LLPluginMessage message("base", "shm_remove_response"); - message.setValue("name", name); - sendMessage(message); - } - else - { -// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown base message: " << message_name << std::endl; - } - } - else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) - { - if(message_name == "size_change") - { - std::string name = message_in.getValue("name"); - S32 width = message_in.getValueS32("width"); - S32 height = message_in.getValueS32("height"); - S32 texture_width = message_in.getValueS32("texture_width"); - S32 texture_height = message_in.getValueS32("texture_height"); - - //std::cerr << "---->Got size change instruction from application with name: " << name << " - size is " << width << " x " << height << std::endl; - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response"); - message.setValue("name", name); - message.setValueS32("width", width); - message.setValueS32("height", height); - message.setValueS32("texture_width", texture_width); - message.setValueS32("texture_height", texture_height); - sendMessage(message); - - if(!name.empty()) - { - // Find the shared memory region with this name - SharedSegmentMap::iterator iter = mSharedSegments.find(name); - if(iter != mSharedSegments.end()) - { -// std::cerr << "%%% Got size change, new size is " << width << " by " << height << std::endl; -// std::cerr << "%%%% texture size is " << texture_width << " by " << texture_height << std::endl; - - mPixels = (unsigned char*)iter->second.mAddress; - mTextureSegmentName = name; - mWidth = width; - mHeight = height; - - mTextureWidth = texture_width; - mTextureHeight = texture_height; - - mMediaSizeChanging = false; - - sizeChanged(); - - update(); - }; - }; - } - else if(message_name == "load_uri") - { - std::string uri = message_in.getValue("uri"); - load( uri ); - sendStatus(); - } - else if(message_name == "mouse_event") - { - std::string event = message_in.getValue("event"); - S32 x = message_in.getValueS32("x"); - S32 y = message_in.getValueS32("y"); - - if(event == "down") - { - mouseDown(x, y); - } - else if(event == "up") - { - mouseUp(x, y); - } - else if(event == "move") - { - mouseMove(x, y); - }; - }; - } - else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) - { - if(message_name == "stop") - { - stop(); - } - else if(message_name == "start") - { - F64 rate = 0.0; - if(message_in.hasValue("rate")) - { - rate = message_in.getValueReal("rate"); - } - play(rate); - } - else if(message_name == "pause") - { - pause(); - } - else if(message_name == "seek") - { - F64 time = message_in.getValueReal("time"); - seek(time); - } - else if(message_name == "set_loop") - { - bool loop = message_in.getValueBoolean("loop"); - mIsLooping = loop; - } - else if(message_name == "set_volume") - { - F64 volume = message_in.getValueReal("volume"); - setVolume(volume); - } - } - else - { -// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown message class: " << message_class << std::endl; - }; - }; -} - -int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) -{ - MediaPluginQuickTime *self = new MediaPluginQuickTime(host_send_func, host_user_data); - *plugin_send_func = MediaPluginQuickTime::staticReceiveMessage; - *plugin_user_data = (void*)self; - - return 0; -} - -#else // LL_QUICKTIME_ENABLED - -// Stubbed-out class with constructor/destructor (necessary or windows linker -// will just think its dead code and optimize it all out) -class MediaPluginQuickTime : public MediaPluginBase -{ -public: - MediaPluginQuickTime(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); - ~MediaPluginQuickTime(); - /* virtual */ void receiveMessage(const char *message_string); -}; - -MediaPluginQuickTime::MediaPluginQuickTime( - LLPluginInstance::sendMessageFunction host_send_func, - void *host_user_data ) : - MediaPluginBase(host_send_func, host_user_data) -{ - // no-op -} - -MediaPluginQuickTime::~MediaPluginQuickTime() -{ - // no-op -} - -void MediaPluginQuickTime::receiveMessage(const char *message_string) -{ - // no-op -} - -// We're building without quicktime enabled. Just refuse to initialize. -int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) -{ - return -1; -} - -#endif // LL_QUICKTIME_ENABLED +/**
+ * @file media_plugin_quicktime.cpp
+ * @brief QuickTime plugin for LLMedia API plugin system
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ *
+ * Copyright (c) 2008, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llgl.h"
+
+#include "llplugininstance.h"
+#include "llpluginmessage.h"
+#include "llpluginmessageclasses.h"
+#include "media_plugin_base.h"
+
+#if LL_QUICKTIME_ENABLED
+
+#if defined(LL_DARWIN)
+ #include <QuickTime/QuickTime.h>
+#elif defined(LL_WINDOWS)
+ #include "MacTypes.h"
+ #include "QTML.h"
+ #include "Movies.h"
+ #include "QDoffscreen.h"
+ #include "FixMath.h"
+ #include "QTLoadLibraryUtils.h"
+#endif
+
+// TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint
+////////////////////////////////////////////////////////////////////////////////
+//
+class MediaPluginQuickTime : public MediaPluginBase
+{
+public:
+ MediaPluginQuickTime(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
+ ~MediaPluginQuickTime();
+
+ /* virtual */ void receiveMessage(const char *message_string);
+
+private:
+
+ int mNaturalWidth;
+ int mNaturalHeight;
+ Movie mMovieHandle;
+ GWorldPtr mGWorldHandle;
+ ComponentInstance mMovieController;
+ int mCurVolume;
+ bool mMediaSizeChanging;
+ bool mIsLooping;
+ std::string mMovieTitle;
+ bool mReceivedTitle;
+ const int mMinWidth;
+ const int mMaxWidth;
+ const int mMinHeight;
+ const int mMaxHeight;
+ F64 mPlayRate;
+
+ enum ECommand {
+ COMMAND_NONE,
+ COMMAND_STOP,
+ COMMAND_PLAY,
+ COMMAND_FAST_FORWARD,
+ COMMAND_FAST_REWIND,
+ COMMAND_PAUSE,
+ COMMAND_SEEK,
+ };
+ ECommand mCommand;
+
+ // Override this to add current time and duration to the message
+ /*virtual*/ void setDirty(int left, int top, int right, int bottom)
+ {
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated");
+
+ message.setValueS32("left", left);
+ message.setValueS32("top", top);
+ message.setValueS32("right", right);
+ message.setValueS32("bottom", bottom);
+
+ if(mMovieHandle)
+ {
+ message.setValueReal("current_time", getCurrentTime());
+ message.setValueReal("duration", getDuration());
+ message.setValueReal("current_rate", Fix2X(GetMovieRate(mMovieHandle)));
+ }
+
+ sendMessage(message);
+ }
+
+
+ static Rect rectFromSize(int width, int height)
+ {
+ Rect result;
+
+
+ result.left = 0;
+ result.top = 0;
+ result.right = width;
+ result.bottom = height;
+
+ return result;
+ }
+
+ Fixed getPlayRate(void)
+ {
+ Fixed result;
+ if(mPlayRate == 0.0f)
+ {
+ // Default to the movie's preferred rate
+ result = GetMoviePreferredRate(mMovieHandle);
+ if(result == 0)
+ {
+ // Don't return a 0 play rate, ever.
+ std::cerr << "Movie's preferred rate is 0, forcing to 1.0." << std::endl;
+ result = X2Fix(1.0f);
+ }
+ }
+ else
+ {
+ result = X2Fix(mPlayRate);
+ }
+
+ return result;
+ }
+
+ void load( const std::string url )
+ {
+
+ if ( url.empty() )
+ return;
+
+ // Stop and unload any existing movie before starting another one.
+ unload();
+
+ setStatus(STATUS_LOADING);
+
+ //In case std::string::c_str() makes a copy of the url data,
+ //make sure there is memory to hold it before allocating memory for handle.
+ //if fails, NewHandleClear(...) should return NULL.
+ const char* url_string = url.c_str() ;
+ Handle handle = NewHandleClear( ( Size )( url.length() + 1 ) );
+
+ if ( NULL == handle || noErr != MemError() || NULL == *handle )
+ {
+ setStatus(STATUS_ERROR);
+ return;
+ }
+
+ BlockMove( url_string, *handle, ( Size )( url.length() + 1 ) );
+
+ OSErr err = NewMovieFromDataRef( &mMovieHandle, newMovieActive | newMovieDontInteractWithUser | newMovieAsyncOK | newMovieIdleImportOK, nil, handle, URLDataHandlerSubType );
+ DisposeHandle( handle );
+ if ( noErr != err )
+ {
+ setStatus(STATUS_ERROR);
+ return;
+ };
+
+ // do pre-roll actions (typically fired for streaming movies but not always)
+ PrePrerollMovie( mMovieHandle, 0, getPlayRate(), moviePrePrerollCompleteCallback, ( void * )this );
+
+ Rect movie_rect = rectFromSize(mWidth, mHeight);
+
+ // make a new movie controller
+ mMovieController = NewMovieController( mMovieHandle, &movie_rect, mcNotVisible | mcTopLeftMovie );
+
+ // movie controller
+ MCSetActionFilterWithRefCon( mMovieController, mcActionFilterCallBack, ( long )this );
+
+ SetMoviePlayHints( mMovieHandle, hintsAllowDynamicResize, hintsAllowDynamicResize );
+
+ // function that gets called when a frame is drawn
+ SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, movieDrawingCompleteCallback, ( long )this );
+
+ setStatus(STATUS_LOADED);
+
+ sizeChanged();
+ };
+
+ bool unload()
+ {
+ // new movie and have to get title again
+ mReceivedTitle = false;
+
+ if ( mMovieHandle )
+ {
+ StopMovie( mMovieHandle );
+ if ( mMovieController )
+ {
+ MCMovieChanged( mMovieController, mMovieHandle );
+ };
+ };
+
+ if ( mMovieController )
+ {
+ MCSetActionFilterWithRefCon( mMovieController, NULL, (long)this );
+ DisposeMovieController( mMovieController );
+ mMovieController = NULL;
+ };
+
+ if ( mMovieHandle )
+ {
+ SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, nil, ( long )this );
+ DisposeMovie( mMovieHandle );
+ mMovieHandle = NULL;
+ };
+
+ if ( mGWorldHandle )
+ {
+ DisposeGWorld( mGWorldHandle );
+ mGWorldHandle = NULL;
+ };
+
+ setStatus(STATUS_NONE);
+
+ return true;
+ }
+
+ bool navigateTo( const std::string url )
+ {
+ unload();
+ load( url );
+
+ return true;
+ };
+
+ bool sizeChanged()
+ {
+ if ( ! mMovieHandle )
+ return false;
+
+ // Check to see whether the movie's natural size has updated
+ {
+ int width, height;
+ getMovieNaturalSize(&width, &height);
+ if((width != 0) && (height != 0) && ((width != mNaturalWidth) || (height != mNaturalHeight)))
+ {
+ mNaturalWidth = width;
+ mNaturalHeight = height;
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request");
+ message.setValue("name", mTextureSegmentName);
+ message.setValueS32("width", width);
+ message.setValueS32("height", height);
+ sendMessage(message);
+ //std::cerr << "<--- Sending size change request to application with name: " << mTextureSegmentName << " - size is " << width << " x " << height << std::endl;
+ }
+ }
+
+ // sanitize destination size
+ Rect dest_rect = rectFromSize(mWidth, mHeight);
+
+ // media depth won't change
+ int depth_bits = mDepth * 8;
+ long rowbytes = mDepth * mTextureWidth;
+
+ GWorldPtr old_gworld_handle = mGWorldHandle;
+
+ if(mPixels != NULL)
+ {
+ // We have pixels. Set up a GWorld pointing at the texture.
+ OSErr result = NewGWorldFromPtr( &mGWorldHandle, depth_bits, &dest_rect, NULL, NULL, 0, (Ptr)mPixels, rowbytes);
+ if ( noErr != result )
+ {
+ // TODO: unrecoverable?? throw exception? return something?
+ return false;
+ }
+ }
+ else
+ {
+ // We don't have pixels. Create a fake GWorld we can point the movie at when it's not safe to render normally.
+ Rect tempRect = rectFromSize(1, 1);
+ OSErr result = NewGWorld( &mGWorldHandle, depth_bits, &tempRect, NULL, NULL, 0);
+ if ( noErr != result )
+ {
+ // TODO: unrecoverable?? throw exception? return something?
+ return false;
+ }
+ }
+
+ SetMovieGWorld( mMovieHandle, mGWorldHandle, GetGWorldDevice( mGWorldHandle ) );
+
+ // If the GWorld was already set up, delete it.
+ if(old_gworld_handle != NULL)
+ {
+ DisposeGWorld( old_gworld_handle );
+ }
+
+ // Set up the movie display matrix
+ {
+ // scale movie to fit rect and invert vertically to match opengl image format
+ MatrixRecord transform;
+ SetIdentityMatrix( &transform ); // transforms are additive so start from identify matrix
+ double scaleX = (double) mWidth / mNaturalWidth;
+ double scaleY = -1.0 * (double) mHeight / mNaturalHeight;
+ double centerX = mWidth / 2.0;
+ double centerY = mHeight / 2.0;
+ ScaleMatrix( &transform, X2Fix( scaleX ), X2Fix( scaleY ), X2Fix( centerX ), X2Fix( centerY ) );
+ SetMovieMatrix( mMovieHandle, &transform );
+ }
+
+ // update movie controller
+ if ( mMovieController )
+ {
+ MCSetControllerPort( mMovieController, mGWorldHandle );
+ MCPositionController( mMovieController, &dest_rect, &dest_rect,
+ mcTopLeftMovie | mcPositionDontInvalidate );
+ MCMovieChanged( mMovieController, mMovieHandle );
+ }
+
+
+ // Emit event with size change so the calling app knows about it too
+ // TODO:
+ //LLMediaEvent event( this );
+ //mEventEmitter.update( &LLMediaObserver::onMediaSizeChange, event );
+
+ return true;
+ }
+ static Boolean mcActionFilterCallBack( MovieController mc, short action, void *params, long ref )
+ {
+ Boolean result = false;
+
+ MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref;
+
+ switch( action )
+ {
+ // handle window resizing
+ case mcActionControllerSizeChanged:
+ // Ensure that the movie draws correctly at the new size
+ self->sizeChanged();
+ break;
+
+ // Block any movie controller actions that open URLs.
+ case mcActionLinkToURL:
+ case mcActionGetNextURL:
+ case mcActionLinkToURLExtended:
+ // Prevent the movie controller from handling the message
+ result = true;
+ break;
+
+ default:
+ break;
+ };
+
+ return result;
+ };
+
+ static OSErr movieDrawingCompleteCallback( Movie call_back_movie, long ref )
+ {
+ MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref;
+
+ // IMPORTANT: typically, a consumer who is observing this event will set a flag
+ // when this event is fired then render later. Be aware that the media stream
+ // can change during this period - dimensions, depth, format etc.
+ //LLMediaEvent event( self );
+// self->updateQuickTime();
+ // TODO ^^^
+
+
+ if ( self->mWidth > 0 && self->mHeight > 0 )
+ self->setDirty( 0, 0, self->mWidth, self->mHeight );
+
+ return noErr;
+ };
+
+ static void moviePrePrerollCompleteCallback( Movie movie, OSErr preroll_err, void *ref )
+ {
+ //MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref;
+
+ // TODO:
+ //LLMediaEvent event( self );
+ //self->mEventEmitter.update( &LLMediaObserver::onMediaPreroll, event );
+ };
+
+
+ void rewind()
+ {
+ GoToBeginningOfMovie( mMovieHandle );
+ MCMovieChanged( mMovieController, mMovieHandle );
+ };
+
+ bool processState()
+ {
+ if ( mCommand == COMMAND_PLAY )
+ {
+ if ( mStatus == STATUS_LOADED || mStatus == STATUS_PAUSED || mStatus == STATUS_PLAYING )
+ {
+ long state = GetMovieLoadState( mMovieHandle );
+
+ if ( state >= kMovieLoadStatePlaythroughOK )
+ {
+ // if the movie is at the end (generally because it reached it naturally)
+ // and we play is requested, jump back to the start of the movie.
+ // note: this is different from having loop flag set.
+ if ( IsMovieDone( mMovieHandle ) )
+ {
+ Fixed rate = X2Fix( 0.0 );
+ MCDoAction( mMovieController, mcActionPlay, (void*)rate );
+ rewind();
+ };
+
+ MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() );
+ MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume );
+ setStatus(STATUS_PLAYING);
+ mCommand = COMMAND_NONE;
+ };
+ };
+ }
+ else
+ if ( mCommand == COMMAND_STOP )
+ {
+ if ( mStatus == STATUS_PLAYING || mStatus == STATUS_PAUSED )
+ {
+ if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK )
+ {
+ Fixed rate = X2Fix( 0.0 );
+ MCDoAction( mMovieController, mcActionPlay, (void*)rate );
+ rewind();
+
+ setStatus(STATUS_LOADED);
+ mCommand = COMMAND_NONE;
+ };
+ };
+ }
+ else
+ if ( mCommand == COMMAND_PAUSE )
+ {
+ if ( mStatus == STATUS_PLAYING )
+ {
+ if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK )
+ {
+ Fixed rate = X2Fix( 0.0 );
+ MCDoAction( mMovieController, mcActionPlay, (void*)rate );
+ setStatus(STATUS_PAUSED);
+ mCommand = COMMAND_NONE;
+ };
+ };
+ };
+
+ return true;
+ };
+
+ void play(F64 rate)
+ {
+ mPlayRate = rate;
+ mCommand = COMMAND_PLAY;
+ };
+
+ void stop()
+ {
+ mCommand = COMMAND_STOP;
+ };
+
+ void pause()
+ {
+ mCommand = COMMAND_PAUSE;
+ };
+
+ void getMovieNaturalSize(int *movie_width, int *movie_height)
+ {
+ Rect rect;
+
+ GetMovieNaturalBoundsRect( mMovieHandle, &rect );
+
+ int width = ( rect.right - rect.left );
+ int height = ( rect.bottom - rect.top );
+
+ // make sure width and height fall in valid range
+ if ( width < mMinWidth )
+ width = mMinWidth;
+
+ if ( width > mMaxWidth )
+ width = mMaxWidth;
+
+ if ( height < mMinHeight )
+ height = mMinHeight;
+
+ if ( height > mMaxHeight )
+ height = mMaxHeight;
+
+ // return the new rect
+ *movie_width = width;
+ *movie_height = height;
+ }
+
+ void updateQuickTime(int milliseconds)
+ {
+ if ( ! mMovieHandle )
+ return;
+
+ if ( ! mMovieController )
+ return;
+
+ // service QuickTime
+ // Calling it this way doesn't have good behavior on Windows...
+// MoviesTask( mMovieHandle, milliseconds );
+ // This was the original, but I think using both MoviesTask and MCIdle is redundant. Trying with only MCIdle.
+// MoviesTask( mMovieHandle, 0 );
+
+ MCIdle( mMovieController );
+
+ if ( ! mGWorldHandle )
+ return;
+
+ if ( mMediaSizeChanging )
+ return;
+
+ // update state machine
+ processState();
+
+ // see if title arrived and if so, update member variable with contents
+ checkTitle();
+
+ // special code for looping - need to rewind at the end of the movie
+ if ( mIsLooping )
+ {
+ // QT call to see if we are at the end - can't do with controller
+ if ( IsMovieDone( mMovieHandle ) )
+ {
+ // go back to start
+ rewind();
+
+ if ( mMovieController )
+ {
+ // kick off new play
+ MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() );
+
+ // set the volume
+ MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume );
+ };
+ };
+ };
+ };
+
+ int getDataWidth() const
+ {
+ if ( mGWorldHandle )
+ {
+ int depth = mDepth;
+
+ if (depth < 1)
+ depth = 1;
+
+ // ALWAYS use the row bytes from the PixMap if we have a GWorld because
+ // sometimes it's not the same as mMediaDepth * mMediaWidth !
+ PixMapHandle pix_map_handle = GetGWorldPixMap( mGWorldHandle );
+ return QTGetPixMapHandleRowBytes( pix_map_handle ) / depth;
+ }
+ else
+ {
+ // TODO : return LLMediaImplCommon::getaDataWidth();
+ return 0;
+ }
+ };
+
+ void seek( F64 time )
+ {
+ if ( mMovieController )
+ {
+ TimeRecord when;
+ when.scale = GetMovieTimeScale( mMovieHandle );
+ when.base = 0;
+
+ // 'time' is in (floating point) seconds. The timebase time will be in 'units', where
+ // there are 'scale' units per second.
+ SInt64 raw_time = ( SInt64 )( time * (double)( when.scale ) );
+
+ when.value.hi = ( SInt32 )( raw_time >> 32 );
+ when.value.lo = ( SInt32 )( ( raw_time & 0x00000000FFFFFFFF ) );
+
+ MCDoAction( mMovieController, mcActionGoToTime, &when );
+ };
+ };
+
+ F64 getLoadedDuration()
+ {
+ TimeValue duration;
+ if(GetMaxLoadedTimeInMovie( mMovieHandle, &duration ) != noErr)
+ {
+ // If GetMaxLoadedTimeInMovie returns an error, return the full duration of the movie.
+ duration = GetMovieDuration( mMovieHandle );
+ }
+ TimeValue scale = GetMovieTimeScale( mMovieHandle );
+
+ return (F64)duration / (F64)scale;
+ };
+
+ F64 getDuration()
+ {
+ TimeValue duration = GetMovieDuration( mMovieHandle );
+ TimeValue scale = GetMovieTimeScale( mMovieHandle );
+
+ return (F64)duration / (F64)scale;
+ };
+
+ F64 getCurrentTime()
+ {
+ TimeValue curr_time = GetMovieTime( mMovieHandle, 0 );
+ TimeValue scale = GetMovieTimeScale( mMovieHandle );
+
+ return (F64)curr_time / (F64)scale;
+ };
+
+ void setVolume( F64 volume )
+ {
+ mCurVolume = (short)(volume * ( double ) 0x100 );
+
+ if ( mMovieController )
+ {
+ MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume );
+ };
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ void update(int milliseconds = 0)
+ {
+ updateQuickTime(milliseconds);
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ void mouseDown( int x, int y )
+ {
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ void mouseUp( int x, int y )
+ {
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ void mouseMove( int x, int y )
+ {
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ void keyPress( unsigned char key )
+ {
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Grab movie title into mMovieTitle - should be called repeatedly
+ // until it returns true since movie title takes a while to become
+ // available.
+ const bool getMovieTitle()
+ {
+ // grab meta data from movie
+ QTMetaDataRef media_data_ref;
+ OSErr result = QTCopyMovieMetaData( mMovieHandle, &media_data_ref );
+ if ( noErr != result )
+ return false;
+
+ // look up "Display Name" in meta data
+ OSType meta_data_key = kQTMetaDataCommonKeyDisplayName;
+ QTMetaDataItem item = kQTMetaDataItemUninitialized;
+ result = QTMetaDataGetNextItem( media_data_ref, kQTMetaDataStorageFormatWildcard,
+ 0, kQTMetaDataKeyFormatCommon,
+ (const UInt8 *)&meta_data_key,
+ sizeof( meta_data_key ), &item );
+ if ( noErr != result )
+ return false;
+
+ // find the size of the title
+ ByteCount size;
+ result = QTMetaDataGetItemValue( media_data_ref, item, NULL, 0, &size );
+ if ( noErr != result || size <= 0 )
+ return false;
+
+ // allocate some space and grab it
+ UInt8* item_data = new UInt8( size );
+ memset( item_data, 0, size * sizeof( UInt8* ) );
+ result = QTMetaDataGetItemValue( media_data_ref, item, item_data, size, NULL );
+ if ( noErr != result )
+ return false;
+
+ // save it
+ mMovieTitle = std::string( (char* )item_data );
+
+ // clean up
+ delete [] item_data;
+
+ return true;
+ };
+
+ // called regularly to see if title changed
+ void checkTitle()
+ {
+ // we did already receive title so keep checking
+ if ( ! mReceivedTitle )
+ {
+ // grab title from movie meta data
+ if ( getMovieTitle() )
+ {
+ // pass back to host application
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text");
+ message.setValue("name", mMovieTitle );
+ sendMessage( message );
+
+ // stop looking once we find a title for this movie.
+ // TODO: this may to be reset if movie title changes
+ // during playback but this is okay for now
+ mReceivedTitle = true;
+ };
+ };
+ };
+};
+
+MediaPluginQuickTime::MediaPluginQuickTime(
+ LLPluginInstance::sendMessageFunction host_send_func,
+ void *host_user_data ) :
+ MediaPluginBase(host_send_func, host_user_data),
+ mMinWidth( 0 ),
+ mMaxWidth( 2048 ),
+ mMinHeight( 0 ),
+ mMaxHeight( 2048 )
+{
+// std::cerr << "MediaPluginQuickTime constructor" << std::endl;
+
+ mNaturalWidth = -1;
+ mNaturalHeight = -1;
+ mMovieHandle = 0;
+ mGWorldHandle = 0;
+ mMovieController = 0;
+ mCurVolume = 0x99;
+ mMediaSizeChanging = false;
+ mIsLooping = false;
+ mMovieTitle = std::string();
+ mReceivedTitle = false;
+ mCommand = COMMAND_NONE;
+ mPlayRate = 0.0f;
+ mStatus = STATUS_NONE;
+}
+
+MediaPluginQuickTime::~MediaPluginQuickTime()
+{
+// std::cerr << "MediaPluginQuickTime destructor" << std::endl;
+
+ ExitMovies();
+
+#ifdef LL_WINDOWS
+ TerminateQTML();
+// std::cerr << "QuickTime closing down" << std::endl;
+#endif
+}
+
+
+void MediaPluginQuickTime::receiveMessage(const char *message_string)
+{
+// std::cerr << "MediaPluginQuickTime::receiveMessage: received message: \"" << message_string << "\"" << std::endl;
+ LLPluginMessage message_in;
+
+ if(message_in.parse(message_string) >= 0)
+ {
+ std::string message_class = message_in.getClass();
+ std::string message_name = message_in.getName();
+ if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE)
+ {
+ if(message_name == "init")
+ {
+ LLPluginMessage message("base", "init_response");
+ LLSD versions = LLSD::emptyMap();
+ versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION;
+ versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION;
+ // Normally a plugin would only specify one of these two subclasses, but this is a demo...
+ versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION;
+ message.setValueLLSD("versions", versions);
+
+ #ifdef LL_WINDOWS
+
+ // QuickTime 7.6.4 has an issue (that was not present in 7.6.2) with initializing QuickTime
+ // according to this article: http://lists.apple.com/archives/QuickTime-API/2009/Sep/msg00097.html
+ // The solution presented there appears to work.
+ QTLoadLibrary("qtcf.dll");
+
+ // main initialization for QuickTime - only required on Windows
+ OSErr result = InitializeQTML( 0L );
+ if ( result != noErr )
+ {
+ //TODO: If no QT on Windows, this fails - respond accordingly.
+ }
+ else
+ {
+ //std::cerr << "QuickTime initialized" << std::endl;
+ };
+ #endif
+
+ // required for both Windows and Mac
+ EnterMovies();
+
+ std::string plugin_version = "QuickTime media plugin, QuickTime version ";
+
+ long version = 0;
+ Gestalt( gestaltQuickTimeVersion, &version );
+ std::ostringstream codec( "" );
+ codec << std::hex << version << std::dec;
+ plugin_version += codec.str();
+ message.setValue("plugin_version", plugin_version);
+ sendMessage(message);
+
+ // Plugin gets to decide the texture parameters to use.
+ message.setMessage(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params");
+ #if defined(LL_WINDOWS)
+ // Values for Windows
+ mDepth = 3;
+ message.setValueU32("format", GL_RGB);
+ message.setValueU32("type", GL_UNSIGNED_BYTE);
+
+ // We really want to pad the texture width to a multiple of 32 bytes, but since we're using 3-byte pixels, it doesn't come out even.
+ // Padding to a multiple of 3*32 guarantees it'll divide out properly.
+ message.setValueU32("padding", 32 * 3);
+ #else
+ // Values for Mac
+ mDepth = 4;
+ message.setValueU32("format", GL_BGRA_EXT);
+ #ifdef __BIG_ENDIAN__
+ message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8_REV );
+ #else
+ message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8);
+ #endif
+
+ // Pad texture width to a multiple of 32 bytes, to line up with cache lines.
+ message.setValueU32("padding", 32);
+ #endif
+ message.setValueS32("depth", mDepth);
+ message.setValueU32("internalformat", GL_RGB);
+ message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left.
+ message.setValueBoolean("allow_downsample", true);
+ sendMessage(message);
+ }
+ else if(message_name == "idle")
+ {
+ // no response is necessary here.
+ F64 time = message_in.getValueReal("time");
+
+ // Convert time to milliseconds for update()
+ update((int)(time * 1000.0f));
+ }
+ else if(message_name == "cleanup")
+ {
+ // TODO: clean up here
+ }
+ else if(message_name == "shm_added")
+ {
+ SharedSegmentInfo info;
+ info.mAddress = message_in.getValuePointer("address");
+ info.mSize = (size_t)message_in.getValueS32("size");
+ std::string name = message_in.getValue("name");
+// std::cerr << "MediaPluginQuickTime::receiveMessage: shared memory added, name: " << name
+// << ", size: " << info.mSize
+// << ", address: " << info.mAddress
+// << std::endl;
+
+ mSharedSegments.insert(SharedSegmentMap::value_type(name, info));
+
+ }
+ else if(message_name == "shm_remove")
+ {
+ std::string name = message_in.getValue("name");
+
+// std::cerr << "MediaPluginQuickTime::receiveMessage: shared memory remove, name = " << name << std::endl;
+
+ SharedSegmentMap::iterator iter = mSharedSegments.find(name);
+ if(iter != mSharedSegments.end())
+ {
+ if(mPixels == iter->second.mAddress)
+ {
+ // This is the currently active pixel buffer. Make sure we stop drawing to it.
+ mPixels = NULL;
+ mTextureSegmentName.clear();
+
+ // Make sure the movie GWorld is no longer pointed at the shared segment.
+ sizeChanged();
+ }
+ mSharedSegments.erase(iter);
+ }
+ else
+ {
+// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown shared memory region!" << std::endl;
+ }
+
+ // Send the response so it can be cleaned up.
+ LLPluginMessage message("base", "shm_remove_response");
+ message.setValue("name", name);
+ sendMessage(message);
+ }
+ else
+ {
+// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown base message: " << message_name << std::endl;
+ }
+ }
+ else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA)
+ {
+ if(message_name == "size_change")
+ {
+ std::string name = message_in.getValue("name");
+ S32 width = message_in.getValueS32("width");
+ S32 height = message_in.getValueS32("height");
+ S32 texture_width = message_in.getValueS32("texture_width");
+ S32 texture_height = message_in.getValueS32("texture_height");
+
+ //std::cerr << "---->Got size change instruction from application with name: " << name << " - size is " << width << " x " << height << std::endl;
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response");
+ message.setValue("name", name);
+ message.setValueS32("width", width);
+ message.setValueS32("height", height);
+ message.setValueS32("texture_width", texture_width);
+ message.setValueS32("texture_height", texture_height);
+ sendMessage(message);
+
+ if(!name.empty())
+ {
+ // Find the shared memory region with this name
+ SharedSegmentMap::iterator iter = mSharedSegments.find(name);
+ if(iter != mSharedSegments.end())
+ {
+// std::cerr << "%%% Got size change, new size is " << width << " by " << height << std::endl;
+// std::cerr << "%%%% texture size is " << texture_width << " by " << texture_height << std::endl;
+
+ mPixels = (unsigned char*)iter->second.mAddress;
+ mTextureSegmentName = name;
+ mWidth = width;
+ mHeight = height;
+
+ mTextureWidth = texture_width;
+ mTextureHeight = texture_height;
+
+ mMediaSizeChanging = false;
+
+ sizeChanged();
+
+ update();
+ };
+ };
+ }
+ else if(message_name == "load_uri")
+ {
+ std::string uri = message_in.getValue("uri");
+ load( uri );
+ sendStatus();
+ }
+ else if(message_name == "mouse_event")
+ {
+ std::string event = message_in.getValue("event");
+ S32 x = message_in.getValueS32("x");
+ S32 y = message_in.getValueS32("y");
+
+ if(event == "down")
+ {
+ mouseDown(x, y);
+ }
+ else if(event == "up")
+ {
+ mouseUp(x, y);
+ }
+ else if(event == "move")
+ {
+ mouseMove(x, y);
+ };
+ };
+ }
+ else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME)
+ {
+ if(message_name == "stop")
+ {
+ stop();
+ }
+ else if(message_name == "start")
+ {
+ F64 rate = 0.0;
+ if(message_in.hasValue("rate"))
+ {
+ rate = message_in.getValueReal("rate");
+ }
+ play(rate);
+ }
+ else if(message_name == "pause")
+ {
+ pause();
+ }
+ else if(message_name == "seek")
+ {
+ F64 time = message_in.getValueReal("time");
+ seek(time);
+ }
+ else if(message_name == "set_loop")
+ {
+ bool loop = message_in.getValueBoolean("loop");
+ mIsLooping = loop;
+ }
+ else if(message_name == "set_volume")
+ {
+ F64 volume = message_in.getValueReal("volume");
+ setVolume(volume);
+ }
+ }
+ else
+ {
+// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown message class: " << message_class << std::endl;
+ };
+ };
+}
+
+int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
+{
+ MediaPluginQuickTime *self = new MediaPluginQuickTime(host_send_func, host_user_data);
+ *plugin_send_func = MediaPluginQuickTime::staticReceiveMessage;
+ *plugin_user_data = (void*)self;
+
+ return 0;
+}
+
+#else // LL_QUICKTIME_ENABLED
+
+// Stubbed-out class with constructor/destructor (necessary or windows linker
+// will just think its dead code and optimize it all out)
+class MediaPluginQuickTime : public MediaPluginBase
+{
+public:
+ MediaPluginQuickTime(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
+ ~MediaPluginQuickTime();
+ /* virtual */ void receiveMessage(const char *message_string);
+};
+
+MediaPluginQuickTime::MediaPluginQuickTime(
+ LLPluginInstance::sendMessageFunction host_send_func,
+ void *host_user_data ) :
+ MediaPluginBase(host_send_func, host_user_data)
+{
+ // no-op
+}
+
+MediaPluginQuickTime::~MediaPluginQuickTime()
+{
+ // no-op
+}
+
+void MediaPluginQuickTime::receiveMessage(const char *message_string)
+{
+ // no-op
+}
+
+// We're building without quicktime enabled. Just refuse to initialize.
+int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
+{
+ return -1;
+}
+
+#endif // LL_QUICKTIME_ENABLED
diff --git a/indra/media_plugins/webkit/CMakeLists.txt b/indra/media_plugins/webkit/CMakeLists.txt index d96477279d..5bccd589d8 100644 --- a/indra/media_plugins/webkit/CMakeLists.txt +++ b/indra/media_plugins/webkit/CMakeLists.txt @@ -52,6 +52,14 @@ add_dependencies(media_plugin_webkit ${LLCOMMON_LIBRARIES} ) +if (WINDOWS) + set_target_properties( + media_plugin_webkit + PROPERTIES + LINK_FLAGS "/MANIFEST:NO" + ) +endif (WINDOWS) + if (DARWIN) # Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name set_target_properties( diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp index eb2457744a..65872e1596 100644 --- a/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp @@ -48,6 +48,18 @@ #include <stdlib.h> #endif +#if LL_WINDOWS + // *NOTE:Mani - This captures the module handle fo rthe dll. This is used below + // to get the path to this dll for webkit initialization. + // I don't know how/if this can be done with apr... + namespace { HMODULE gModuleHandle;}; + BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+ {
+ gModuleHandle = (HMODULE) hinstDLL;
+ return TRUE;
+ }
+#endif + //////////////////////////////////////////////////////////////////////////////// // class MediaPluginWebKit : @@ -126,7 +138,31 @@ private: return false; } std::string application_dir = std::string( cwd ); + +#if LL_WINDOWS + //*NOTE:Mani - On windows, at least, the component path is the + // location of this dll's image file. + std::string component_dir; + char dll_path[_MAX_PATH]; + DWORD len = GetModuleFileNameA(gModuleHandle, (LPCH)&dll_path, _MAX_PATH); + while(len && dll_path[ len ] != ('\\') )
+ {
+ len--;
+ }
+ if(len >= 0) + { + dll_path[len] = 0; + component_dir = dll_path; + } + else + { + // *NOTE:Mani - This case should be an rare exception. + // GetModuleFileNameA should always give you a full path, no? + component_dir = application_dir; + } +#else std::string component_dir = application_dir; +#endif std::string profileDir = application_dir + "/" + "browser_profile"; // cross platform? // window handle - needed on Windows and must be app window. @@ -147,7 +183,7 @@ private: #if LL_WINDOWS // Enable plugins - LLQtWebKit::getInstance()->enablePlugins(false); + LLQtWebKit::getInstance()->enablePlugins(true); #elif LL_DARWIN // Disable plugins LLQtWebKit::getInstance()->enablePlugins(false); @@ -272,7 +308,16 @@ private: message.setValue("status", event.getStringValue()); sendMessage(message); } - + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onTitleChange(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); + message.setValue("name", event.getStringValue()); + sendMessage(message); + } + //////////////////////////////////////////////////////////////////////////////// // virtual void onLocationChange(const EventType& event) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index d0f4dc88e7..1e5a798202 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -36,10 +36,8 @@ include(UI) include(UnixInstall) include(LLKDU) include(ViewerMiscLibs) - -if (WINDOWS) - include(CopyWinLibs) -endif (WINDOWS) +include(LLLogin) +include(CMakeCopyIfDifferent) include_directories( ${DBUSGLIB_INCLUDE_DIRS} @@ -61,12 +59,14 @@ include_directories( ${LLXUIXML_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS}/lscript_compile + ${LLLOGIN_INCLUDE_DIRS} ) set(viewer_SOURCE_FILES llaccordionctrltab.cpp llaccordionctrl.cpp llagent.cpp + llagentlistener.cpp llagentaccess.cpp llagentdata.cpp llagentlanguage.cpp @@ -76,6 +76,7 @@ set(viewer_SOURCE_FILES llanimstatelabels.cpp llappearancemgr.cpp llappviewer.cpp + llappviewerlistener.cpp llassetuploadresponders.cpp llassetuploadqueue.cpp llaudiosourcevo.cpp @@ -260,6 +261,7 @@ set(viewer_SOURCE_FILES llurllineeditorctrl.cpp lllogchat.cpp llloginhandler.cpp + lllogininstance.cpp llmanip.cpp llmaniprotate.cpp llmanipscale.cpp @@ -331,6 +333,7 @@ set(viewer_SOURCE_FILES llpanelpicks.cpp llpanelplace.cpp llpanelplaceinfo.cpp + llpanelshower.cpp llpanelplaces.cpp llpanelplacestab.cpp llpanelprofile.cpp @@ -367,7 +370,6 @@ set(viewer_SOURCE_FILES llspatialpartition.cpp llsplitbutton.cpp llsprite.cpp - llsrv.cpp llstartup.cpp llstatusbar.cpp llstylemap.cpp @@ -412,19 +414,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 @@ -466,6 +469,7 @@ set(viewer_SOURCE_FILES llviewerthrottle.cpp llviewervisualparam.cpp llviewerwindow.cpp + llviewerwindowlistener.cpp llvlcomposition.cpp llvlmanager.cpp llvoavatar.cpp @@ -503,6 +507,7 @@ set(viewer_SOURCE_FILES llworld.cpp llworldmap.cpp llworldmapview.cpp + llxmlrpclistener.cpp llxmlrpctransaction.cpp noise.cpp pipeline.cpp @@ -531,6 +536,7 @@ set(viewer_HEADER_FILES llaccordionctrltab.h llaccordionctrl.h llagent.h + llagentlistener.h llagentaccess.h llagentdata.h llagentlanguage.h @@ -540,6 +546,7 @@ set(viewer_HEADER_FILES llanimstatelabels.h llappearance.h llappviewer.h + llappviewerlistener.h llassetuploadresponders.h llassetuploadqueue.h llaudiosourcevo.h @@ -727,6 +734,7 @@ set(viewer_HEADER_FILES llurllineeditorctrl.h lllogchat.h llloginhandler.h + lllogininstance.h llmanip.h llmaniprotate.h llmanipscale.h @@ -794,6 +802,7 @@ set(viewer_HEADER_FILES llpanelpicks.h llpanelplace.h llpanelplaceinfo.h + llpanelshower.h llpanelplaces.h llpanelplacestab.h llpanelprofile.h @@ -832,7 +841,6 @@ set(viewer_HEADER_FILES llspatialpartition.h llsplitbutton.h llsprite.h - llsrv.h llstartup.h llstatusbar.h llstylemap.h @@ -879,13 +887,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 @@ -893,6 +901,7 @@ set(viewer_HEADER_FILES llviewerbuild.h llviewercamera.h llviewercontrol.h + llviewercontrollistener.h llviewerdisplay.h llviewerfloaterreg.h llviewergenericmessage.h @@ -931,6 +940,7 @@ set(viewer_HEADER_FILES llviewerthrottle.h llviewervisualparam.h llviewerwindow.h + llviewerwindowlistener.h llvlcomposition.h llvlmanager.h llvoavatar.h @@ -971,6 +981,7 @@ set(viewer_HEADER_FILES llworld.h llworldmap.h llworldmapview.h + llxmlrpclistener.h llxmlrpctransaction.h macmain.h noise.h @@ -1127,7 +1138,6 @@ if (WINDOWS) comdlg32 ${DINPUT_LIBRARY} ${DXGUID_LIBRARY} - fmodvc kernel32 odbc32 odbccp32 @@ -1171,6 +1181,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}) @@ -1245,23 +1264,23 @@ endif (OPENAL) if (FMOD) set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMOD") - if (NOT WINDOWS) + if (DARWIN) set(fmodwrapper_SOURCE_FILES fmodwrapper.cpp) add_library(fmodwrapper SHARED ${fmodwrapper_SOURCE_FILES}) - set(fmodwrapper_needed_LIBRARIES ${FMOD_LIBRARY}) - if (DARWIN) - list(APPEND fmodwrapper_needed_LIBRARIES ${CARBON_LIBRARY}) - set_target_properties( - fmodwrapper - PROPERTIES - BUILD_WITH_INSTALL_RPATH 1 - INSTALL_NAME_DIR "@executable_path/../Resources" - LINK_FLAGS "-unexported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/fmod_hidden_symbols.exp" - ) - endif (DARWIN) + set(fmodwrapper_needed_LIBRARIES ${FMOD_LIBRARY} ${CARBON_LIBRARY}) + set_target_properties( + fmodwrapper + PROPERTIES + BUILD_WITH_INSTALL_RPATH 1 + INSTALL_NAME_DIR "@executable_path/../Resources" + LINK_FLAGS "-unexported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/fmod_hidden_symbols.exp" + ) set(FMODWRAPPER_LIBRARY fmodwrapper) target_link_libraries(fmodwrapper ${fmodwrapper_needed_LIBRARIES}) - endif (NOT WINDOWS) + else (DARWIN) + # fmodwrapper unnecessary on linux or windows + set(FMODWRAPPER_LIBRARY ${FMOD_LIBRARY}) + endif (DARWIN) endif (FMOD) set_source_files_properties(llstartup.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}") @@ -1282,6 +1301,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.") @@ -1338,7 +1362,24 @@ 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... + # *FIX:Mani Use actually dependencies rather than bulk copy. + 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_dependencies(${VIEWER_BINARY_NAME} stage_third_party_libs llcommon llkdu) + endif(WINDOWS) if (EXISTS ${CMAKE_SOURCE_DIR}/copy_win_scripts) add_dependencies(${VIEWER_BINARY_NAME} copy_win_scripts) @@ -1364,8 +1405,29 @@ if (WINDOWS) add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_webkit) 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) + # temporarily disable packaging of event_host until hg subrepos get + # sorted out on the parabuild cluster... + #${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/event_host.tar.bz2) add_dependencies(package windows-updater windows-crash-logger) + + endif (PACKAGE) endif (WINDOWS) @@ -1402,6 +1464,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${WINDOWS_LIBRARIES} ${XMLRPCEPI_LIBRARIES} ${ELFIO_LIBRARIES} + ${LLLOGIN_LIBRARIES} ${GOOGLE_PERFTOOLS_LIBRARIES} ) @@ -1535,7 +1598,9 @@ include(LLAddBuildTest) SET(viewer_TEST_SOURCE_FILES llagentaccess.cpp lldateutil.cpp + llmediadataclient.cpp llviewerhelputil.cpp + lllogininstance.cpp ) set_source_files_properties( ${viewer_TEST_SOURCE_FILES} @@ -1552,6 +1617,7 @@ set(test_libs ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} ) LL_ADD_INTEGRATION_TEST(llcapabilitylistener @@ -1566,6 +1632,16 @@ LL_ADD_INTEGRATION_TEST(llcapabilitylistener # Don't do these for DARWIN or LINUX here -- they're taken care of by viewer_manifest.py if (WINDOWS) + add_custom_command( + TARGET ${VIEWER_BINARY_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS + -E + make_directory + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/llplugin + COMMENT "Creating llplugin dir." + ) + get_target_property(BUILT_SLPLUGIN SLPlugin LOCATION) add_custom_command( TARGET ${VIEWER_BINARY_NAME} POST_BUILD @@ -1601,5 +1677,72 @@ if (WINDOWS) ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/llplugin COMMENT "Copying Quicktime Plugin to the runtime folder." ) + + #******************************* + # Copy media plugin support dlls + # Debug config runtime files required for the plugins + set(plugins_debug_src_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/debug") + set(plugins_debug_files + libeay32.dll + qtcored4.dll + qtguid4.dll + qtnetworkd4.dll + qtopengld4.dll + qtwebkitd4.dll + ssleay32.dll + ) + copy_if_different( + ${plugins_debug_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/Debug/llplugin" + out_targets + ${plugins_debug_files} + ) + set(media_plugin_targets ${media_plugin_targets} ${out_targets}) + + # Release & ReleaseDebInfo config runtime files required for the plugins + set(plugins_release_src_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/release") + set(plugins_release_files + libeay32.dll + qtcore4.dll + qtgui4.dll + qtnetwork4.dll + qtopengl4.dll + qtwebkit4.dll + ssleay32.dll + ) + copy_if_different( + ${plugins_release_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/Release/llplugin" + out_targets + ${plugins_release_files} + ) + set(media_plugin_targets ${media_plugin_targets} ${out_targets}) + + copy_if_different( + ${plugins_release_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo/llplugin" + out_targets + ${plugins_release_files} + ) + set(media_plugin_targets ${media_plugin_targets} ${out_targets}) + + add_custom_target(copy_media_plugin_libs ALL + DEPENDS + ${media_plugin_targets} + ) + + add_custom_command( + TARGET ${VIEWER_BINARY_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS + -E + copy_directory + ${CMAKE_BINARY_DIR}/test_apps/llplugintest/${CMAKE_CFG_INTDIR}/imageformats + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/llplugin/imageformats + COMMENT "Copying llpluging imageformat libs." + ) + + add_dependencies(${VIEWER_BINARY_NAME} llmediaplugintest copy_media_plugin_libs) + endif (WINDOWS) diff --git a/indra/newview/English.lproj/InfoPlist.strings b/indra/newview/English.lproj/InfoPlist.strings index 735424c647..dceaba9a43 100644 --- a/indra/newview/English.lproj/InfoPlist.strings +++ b/indra/newview/English.lproj/InfoPlist.strings @@ -2,6 +2,6 @@ CFBundleName = "Second Life"; -CFBundleShortVersionString = "Second Life version 2.0.0.0"; -CFBundleGetInfoString = "Second Life version 2.0.0.0, Copyright 2004-2009 Linden Research, Inc."; +CFBundleShortVersionString = "Second Life version 2.0.0.3256"; +CFBundleGetInfoString = "Second Life version 2.0.0.3256, Copyright 2004-2009 Linden Research, Inc."; diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist index 7264044d37..7aec8a343d 100644 --- a/indra/newview/Info-SecondLife.plist +++ b/indra/newview/Info-SecondLife.plist @@ -32,7 +32,7 @@ </dict> </array> <key>CFBundleVersion</key> - <string>2.0.0.0</string> + <string>2.0.0.3256</string> <key>CSResourcesFileMapped</key> <true/> </dict> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2da0e29393..91d5e04665 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -5305,6 +5305,17 @@ <key>Value</key> <integer>13</integer> </map> + <key>PrimMediaMaxRetries</key> + <map> + <key>Comment</key> + <string>Maximum number of retries for media queries.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>4</integer> + </map> <key>PrimMediaRequestQueueDelay</key> <map> <key>Comment</key> @@ -5393,6 +5404,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> @@ -10003,7 +10025,7 @@ <key>Comment</key> <string>Versioning Channel Name.</string> <key>Persist</key> - <integer>1</integer> + <integer>0</integer> <key>Type</key> <string>String</string> <key>Value</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/licenses-mac.txt b/indra/newview/licenses-mac.txt index d488c7487e..1324fa1a86 100644 --- a/indra/newview/licenses-mac.txt +++ b/indra/newview/licenses-mac.txt @@ -315,6 +315,515 @@ This product includes cryptographic software written by Eric Young Hudson (tjh@cryptsoft.com). +=========== +Pth License +=========== + ____ _ _ + | _ \| |_| |__ ``Ian Fleming was a UNIX fan! + | |_) | __| '_ \ How do I know? Well, James Bond + | __/| |_| | | | had the (license to kill) number 007, + |_| \__|_| |_| i.e., he could execute anyone!'' + + GNU Pth - The GNU Portable Threads + + LICENSE + ======= + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + For some people, it is not clear, what is the real intention of the + author by using the GNU Lesser General Public License (LGPL) as the + distribution license for GNU Pth. This is, because the LGPL and the + GPL can be (and are often) interpreted very differently and some + interpretations seem to be not compatible with others. So an explicit + clarification for the use of the LGPL for GNU Pth from the authors + point of view might be useful. + + The author places this library under the LGPL to make sure that it + can be used both commercially and non-commercially provided that + modifications to the code base are always donated back to the official + code base under the same license conditions. Please keep in mind that + especially using this library in code not staying under the GPL or + the LGPL _is_ allowed and that any taint or license creap into code + that uses the library is not the authors intention. It is just the + case that _including_ this library into the source tree of other + applications is a little bit more inconvinient because of the LGPL. + But it has to be this way for good reasons. And keep in mind that + inconvinient doesn't mean not allowed or even impossible. + + Even if you want to use this library in some BSD-style licensed + packages, this _is_ possible as long as you are a little bit + carefully. Usually this means you have to make sure that the code is + still clearly separated into the source tree and that modifications to + this source area are done under the conditions of the LGPL. Read below + for more details on the conditions. Contact the author if you have + more questions. + + The license text of the GNU Lesser General Public License follows: + __________________________________________________________________________ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + ======================= Original SSLeay License diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 0fa3b1f04d..f62606cc50 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" @@ -255,6 +256,7 @@ LLAgent::LLAgent() : mHUDTargetZoom(1.f), mHUDCurZoom(1.f), mInitialized(FALSE), + mListener(), mForceMouselook(FALSE), mDoubleTapRunTimer(), @@ -383,6 +385,8 @@ LLAgent::LLAgent() : } mFollowCam.setMaxCameraDistantFromSubject( MAX_CAMERA_DISTANCE_FROM_AGENT ); + + mListener.reset(new LLAgentListener(*this)); } // Requires gSavedSettings to be initialized. @@ -5971,7 +5975,7 @@ bool LLAgent::teleportCore(bool is_local) LLFloaterReg::hideInstance("about_land"); LLViewerParcelMgr::getInstance()->deselectLand(); - LLViewerMediaFocus::getInstance()->setFocusFace(false, NULL, 0, NULL); + LLViewerMediaFocus::getInstance()->clearFocus(); // Close all pie menus, deselect land, etc. // Don't change the camera until we know teleport succeeded. JC diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index b334874e6e..99a9bdd8e6 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 c673db2034..5b769dcab4 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" @@ -91,14 +92,15 @@ #if LL_WINDOWS #include "llwindebug.h" -#endif - -#if LL_WINDOWS # include <share.h> // For _SH_DENYWR in initMarkerFile #else # 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" @@ -154,7 +156,6 @@ #include "llfolderview.h" #include "lltoolbar.h" #include "llagentpilot.h" -#include "llsrv.h" #include "llvovolume.h" #include "llflexibleobject.h" #include "llvosurfacepatch.h" @@ -192,7 +193,19 @@ //---------------------------------------------------------------------------- // llviewernetwork.h #include "llviewernetwork.h" +// define a self-registering event API object +#include "llappviewerlistener.h" + +#if (LL_LINUX || LL_SOLARIS) && LL_GTK +#include "glib.h" +#endif // (LL_LINUX || LL_SOLARIS) && LL_GTK +#if LL_MSVC +// disable boost::lexical_cast warning +#pragma warning (disable:4702) +#endif + +static LLAppViewerListener sAppViewerListener("LLAppViewer", LLAppViewer::instance); ////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor // @@ -218,9 +231,6 @@ BOOL gAllowTapTapHoldRun = TRUE; BOOL gShowObjectUpdates = FALSE; BOOL gUseQuickTime = TRUE; -BOOL gAcceptTOS = FALSE; -BOOL gAcceptCriticalMessage = FALSE; - eLastExecEvent gLastExecEvent = LAST_EXEC_NORMAL; LLSD gDebugInfo; @@ -565,9 +575,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) { @@ -877,6 +887,11 @@ bool LLAppViewer::init() LLViewerJoystick::getInstance()->init(false); gGLActive = FALSE; + if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0) + { + loadEventHostModule(gSavedSettings.getS32("QAModeEventHostPort")); + } + return true; } @@ -1191,6 +1206,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 @@ -1498,7 +1527,6 @@ bool LLAppViewer::cleanup() //Note: //LLViewerMedia::cleanupClass() has to be put before gTextureList.shutdown() //because some new image might be generated during cleaning up media. --bao - LLViewerMediaFocus::cleanupClass(); LLViewerMedia::cleanupClass(); LLViewerParcelMedia::cleanupClass(); gTextureList.shutdown(); // shutdown again in case a callback added something @@ -3694,6 +3722,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) @@ -3988,7 +4027,7 @@ void LLAppViewer::forceErrorBadMemoryAccess() return; } -void LLAppViewer::forceErrorInifiniteLoop() +void LLAppViewer::forceErrorInfiniteLoop() { while(true) { @@ -4109,3 +4148,198 @@ 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 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 ); + }; + +#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; + } + + 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 + 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 + LLAppViewer::sUpdaterInfo->mUpdateExePath = + 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: " + << LLAppViewer::sUpdaterInfo->mUpdateExePath << LL_ENDL; + + // *TODO: we could use the gdk equivalent to ensure the updater + // gets started on the same screen. + GError *error = NULL; + if (!g_spawn_command_line_async(LLAppViewer::sUpdaterInfo->mUpdateExePath.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 + + // *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..3259309eee --- /dev/null +++ b/indra/newview/llappviewerlistener.cpp @@ -0,0 +1,40 @@ +/** + * @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, + const LLAppViewerGetter& getter): + LLDispatchListener(pumpname, "op"), + mAppViewerGetter(getter) +{ + // add() every method we want to be able to invoke via this event API. + add("requestQuit", &LLAppViewerListener::requestQuit); + add("forceQuit", &LLAppViewerListener::forceQuit); +} + +void LLAppViewerListener::requestQuit(const LLSD& event) +{ + mAppViewerGetter()->requestQuit(); +} + +void LLAppViewerListener::forceQuit(const LLSD& event) +{ + mAppViewerGetter()->forceQuit(); +} diff --git a/indra/newview/llappviewerlistener.h b/indra/newview/llappviewerlistener.h new file mode 100644 index 0000000000..73227cb95a --- /dev/null +++ b/indra/newview/llappviewerlistener.h @@ -0,0 +1,37 @@ +/** + * @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" +#include <boost/function.hpp> + +class LLAppViewer; +class LLSD; + +/// Listen on an LLEventPump with specified name for LLAppViewer request events. +class LLAppViewerListener: public LLDispatchListener +{ +public: + typedef boost::function<LLAppViewer*(void)> LLAppViewerGetter; + /// Specify the pump name on which to listen, and bind the LLAppViewer + /// instance to use (e.g. LLAppViewer::instance()). + LLAppViewerListener(const std::string& pumpname, const LLAppViewerGetter& getter); + +private: + void requestQuit(const LLSD& event); + void forceQuit(const LLSD& event); + + LLAppViewerGetter mAppViewerGetter; +}; + +#endif /* ! defined(LL_LLAPPVIEWERLISTENER_H) */ 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/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 867fb0255a..da20766e7e 100644 --- a/indra/newview/lleventnotifier.cpp +++ b/indra/newview/lleventnotifier.cpp @@ -94,18 +94,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; @@ -208,49 +206,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 { @@ -262,44 +257,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 f44cdc1a98..2ab17d6220 100644 --- a/indra/newview/llfasttimerview.cpp +++ b/indra/newview/llfasttimerview.cpp @@ -996,8 +996,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 a4c38d03aa..92ad28a105 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -1,293 +1,292 @@ -/** - * @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" - -// Viewer includes -#include "llagent.h" -#include "llappviewer.h" -#include "llsecondlifeurls.h" -#include "lluictrlfactory.h" -#include "llviewertexteditor.h" -#include "llviewercontrol.h" -#include "llviewerstats.h" -#include "llviewerregion.h" -#include "llversionviewer.h" -#include "llviewerbuild.h" -#include "llweb.h" - -// Linden library includes -#include "llaudioengine.h" -#include "llbutton.h" -#include "llcurl.h" -#include "llglheaders.h" -#include "llfloater.h" -#include "llfloaterreg.h" -#include "llimagej2c.h" -#include "llsys.h" -#include "lltrans.h" -#include "lluri.h" -#include "v3dmath.h" -#include "llwindow.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 -///---------------------------------------------------------------------------- -class LLFloaterAbout - : public LLFloater -{ - friend class LLFloaterReg; -private: - LLFloaterAbout(const LLSD& key); - virtual ~LLFloaterAbout(); - -public: - /*virtual*/ BOOL postBuild(); - void onClickCopyToClipboard(); -}; - - -// 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); - - getChild<LLUICtrl>("copy_btn")->setCommitCallback( - boost::bind(&LLFloaterAbout::onClickCopyToClipboard, this)); - - // 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()); - - std::string support; - support.append(version); - support.append("[" + get_viewer_release_notes_url() + " " + - LLTrans::getString("ReleaseNotes") + "]"); - 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) - { - 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.append("[" + LLWeb::escapeURL(region->getCapability("ServerReleaseNotes")) + - " " + LLTrans::getString("ReleaseNotes") + "]"); - support.append("\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->appendText(support, - FALSE, - LLStyle::Params() - .color(LLUIColorTable::instance().getColor("TextFgReadOnlyColor"))); - support_widget->blockUndo(); - - // 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 LLWeb::escapeURL(url.str()); -} - -void LLFloaterAbout::onClickCopyToClipboard() -{ - LLViewerTextEditor *support_widget = - getChild<LLViewerTextEditor>("support_editor", true); - support_widget->selectAll(); - support_widget->copy(); - support_widget->deselect(); -} - -///---------------------------------------------------------------------------- -/// LLFloaterAboutUtil -///---------------------------------------------------------------------------- -void LLFloaterAboutUtil::registerFloater() -{ - LLFloaterReg::add("sl_about", "floater_about.xml", - &LLFloaterReg::build<LLFloaterAbout>); - -} +/**
+ * @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"
+
+// Viewer includes
+#include "llagent.h"
+#include "llappviewer.h"
+#include "llsecondlifeurls.h"
+#include "lluictrlfactory.h"
+#include "llviewertexteditor.h"
+#include "llviewercontrol.h"
+#include "llviewerstats.h"
+#include "llviewerregion.h"
+#include "llversionviewer.h"
+#include "llviewerbuild.h"
+#include "llweb.h"
+
+// Linden library includes
+#include "llaudioengine.h"
+#include "llbutton.h"
+#include "llcurl.h"
+#include "llglheaders.h"
+#include "llfloater.h"
+#include "llfloaterreg.h"
+#include "llimagej2c.h"
+#include "llsys.h"
+#include "lltrans.h"
+#include "lluri.h"
+#include "v3dmath.h"
+#include "llwindow.h"
+
+#if LL_WINDOWS
+#include "lldxhardware.h"
+#endif
+
+extern LLMemoryInfo gSysMemory;
+extern U32 gPacketsIn;
+
+static std::string get_viewer_release_notes_url();
+
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterAbout
+///----------------------------------------------------------------------------
+class LLFloaterAbout
+ : public LLFloater
+{
+ friend class LLFloaterReg;
+private:
+ LLFloaterAbout(const LLSD& key);
+ virtual ~LLFloaterAbout();
+
+public:
+ /*virtual*/ BOOL postBuild();
+ void onClickCopyToClipboard();
+};
+
+
+// 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);
+
+ getChild<LLUICtrl>("copy_btn")->setCommitCallback(
+ boost::bind(&LLFloaterAbout::onClickCopyToClipboard, this));
+
+ // 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());
+
+ std::string support;
+ support.append(version);
+ support.append("[" + get_viewer_release_notes_url() + " " +
+ LLTrans::getString("ReleaseNotes") + "]");
+ 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)
+ {
+ 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.append("[" + LLWeb::escapeURL(region->getCapability("ServerReleaseNotes")) +
+ " " + LLTrans::getString("ReleaseNotes") + "]");
+ support.append("\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->appendText(support,
+ FALSE,
+ LLStyle::Params()
+ .color(LLUIColorTable::instance().getColor("TextFgReadOnlyColor")));
+ support_widget->blockUndo();
+
+ // 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 LLWeb::escapeURL(url.str());
+}
+
+void LLFloaterAbout::onClickCopyToClipboard()
+{
+ LLViewerTextEditor *support_widget =
+ getChild<LLViewerTextEditor>("support_editor", true);
+ support_widget->selectAll();
+ support_widget->copy();
+ support_widget->deselect();
+}
+
+///----------------------------------------------------------------------------
+/// LLFloaterAboutUtil
+///----------------------------------------------------------------------------
+void LLFloaterAboutUtil::registerFloater()
+{
+ LLFloaterReg::add("sl_about", "floater_about.xml",
+ &LLFloaterReg::build<LLFloaterAbout>);
+
+}
diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp index cb0d304aa0..262dc1804d 100644 --- a/indra/newview/llfloaterauction.cpp +++ b/indra/newview/llfloaterauction.cpp @@ -58,6 +58,7 @@ #include "llui.h" #include "llrender.h" #include "llsdutil.h" +#include "llsdutil_math.h" ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs 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/llfloatermediasettings.cpp b/indra/newview/llfloatermediasettings.cpp index aa457de2d8..d941f24f49 100644 --- a/indra/newview/llfloatermediasettings.cpp +++ b/indra/newview/llfloatermediasettings.cpp @@ -118,6 +118,7 @@ BOOL LLFloaterMediaSettings::postBuild() mTabContainer->addTabPanel( LLTabContainer::TabPanelParams(). panel(mPanelMediaSettingsSecurity)); + mPanelMediaSettingsSecurity->setParent( this ); // restore the last tab viewed from persistance variable storage if (!mTabContainer->selectTab(gSavedSettings.getS32("LastMediaSettingsTab"))) @@ -248,3 +249,28 @@ void LLFloaterMediaSettings::enableOkApplyBtns( bool enable ) childSetEnabled( "OK", enable ); childSetEnabled( "Apply", enable ); } + +//////////////////////////////////////////////////////////////////////////////// +// +const std::string LLFloaterMediaSettings::getHomeUrl() +{ + if ( mPanelMediaSettingsGeneral ) + return mPanelMediaSettingsGeneral->getHomeUrl(); + else + return std::string( "" ); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLFloaterMediaSettings::passesWhiteList( const std::string& test_url ) +{ + // sanity check - don't think this can happen + if ( mPanelMediaSettingsSecurity ) + // version in security dialog code is specialized so we pass in + // empty string for first parameter since it's not used + return mPanelMediaSettingsSecurity->passesWhiteList( "", test_url ); + else + // this is all we can do + return false; +} diff --git a/indra/newview/llfloatermediasettings.h b/indra/newview/llfloatermediasettings.h index e2683039cc..17a47cb0f5 100644 --- a/indra/newview/llfloatermediasettings.h +++ b/indra/newview/llfloatermediasettings.h @@ -56,6 +56,8 @@ public: static void clearValues( bool editable); void enableOkApplyBtns( bool enable ); LLPanelMediaSettingsSecurity* getPanelSecurity(){return mPanelMediaSettingsSecurity;}; + const std::string getHomeUrl(); + bool passesWhiteList( const std::string& test_url ); bool mIdenticalHasMediaInfo; bool mMultipleMedia; diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index e00b352c9b..92980c22c7 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -1038,12 +1038,13 @@ void LLFloaterTools::getMediaState() &&first_object->permModify() )) { - childSetEnabled("media_tex", FALSE); + childSetEnabled("Add_Media", FALSE); +/* childSetEnabled("media_tex", FALSE); childSetEnabled("add_media", FALSE); childSetEnabled("delete_media", FALSE); childSetEnabled("edit_media", FALSE); childSetEnabled("media_info", FALSE); - media_info->setEnabled(FALSE); + media_info->setEnabled(FALSE);*/ media_info->clear(); clearMediaSettings(); return; @@ -1054,13 +1055,14 @@ void LLFloaterTools::getMediaState() if(!has_media_capability) { - childSetEnabled("media_tex", FALSE); + childSetEnabled("Add_Media", FALSE); + /* childSetEnabled("media_tex", FALSE); childSetEnabled("add_media", FALSE); childSetEnabled("delete_media", FALSE); childSetEnabled("edit_media", FALSE); childSetEnabled("media_info", FALSE); media_info->setEnabled(FALSE); - media_info->clear(); + media_info->clear();*/ clearMediaSettings(); return; } @@ -1081,6 +1083,7 @@ void LLFloaterTools::getMediaState() LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo = selected_objects->getSelectedTEValue( &func, has_media ); bool bool_has_media = (has_media & LLTextureEntry::MF_HAS_MEDIA); + const LLMediaEntry default_media_data; struct functor_getter_media_data : public LLSelectedTEGetFunctor< LLMediaEntry> @@ -1108,6 +1111,7 @@ void LLFloaterTools::getMediaState() // update UI depending on whether "object" (prim or face) has media // and whether or not you are allowed to edit it. + childSetEnabled("Add_Media", editable); // IF all the faces have media (or all dont have media) if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo ) { @@ -1120,7 +1124,7 @@ void LLFloaterTools::getMediaState() if(media_data_get!=default_media_data) { //TODO: get media title - //media_title = media_data_get->getTile(); + //media_title = media_data_get->getTitle(); //LLFloaterMediaSettings::getInstance()->mIdenticalValidMedia = true; media_title = media_data_get.getHomeURL(); } @@ -1155,7 +1159,7 @@ void LLFloaterTools::getMediaState() if(media_data_get!=default_media_data) { //TODO: get media title - //media_title = media_data_get->getTile(); + //media_title = media_data_get->getTitle(); media_title = media_data_get.getHomeURL(); } diff --git a/indra/newview/llfloatertos.cpp b/indra/newview/llfloatertos.cpp index 1c3443ae80..8d2d48f1af 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" @@ -50,13 +48,15 @@ #include "lluictrlfactory.h" #include "llvfile.h" #include "message.h" +#include "llstartup.h" // login_alert_done -LLFloaterTOS::LLFloaterTOS(const LLSD& message) -: LLModalDialog( message ), - mMessage(message.asString()), +LLFloaterTOS::LLFloaterTOS(const LLSD& data) +: LLModalDialog( data["message"].asString() ), + mMessage(data["message"].asString()), mWebBrowserWindowId( 0 ), - mLoadCompleteCount( 0 ) + mLoadCompleteCount( 0 ), + mReplyPumpName(data["reply_pump"].asString()) { } @@ -193,25 +193,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 } @@ -221,7 +208,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 +233,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/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index 0d74641c5f..9208beec9e 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -71,6 +71,10 @@ LLFontGL* LLFolderViewItem::getLabelFontForStyle(U8 style) { LLFontDescriptor labelfontdesc("SansSerif", "Small", style); rtn = LLFontGL::getFont(labelfontdesc); + if (!rtn) + { + rtn = LLFontGL::getFontDefault(); + } sFonts[style] = rtn; } return rtn; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 6aa6c3f461..619e7044b4 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/llinspect.h b/indra/newview/llinspect.h index 68accca3a3..a461c2fa16 100644 --- a/indra/newview/llinspect.h +++ b/indra/newview/llinspect.h @@ -58,3 +58,4 @@ private: }; #endif + 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 2f3171a868..9a8647aba4 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1985,63 +1985,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. @@ -2216,85 +2209,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 91a1906fab..e7f9db9221 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..e5f347ddc4 --- /dev/null +++ b/indra/newview/lllogininstance.cpp @@ -0,0 +1,475 @@ +/** + * @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 "llnotifications.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), + mDispatcher("LLLoginInstance", "change") +{ + mLoginModule->getEventPump().listen("lllogininstance", + boost::bind(&LLLoginInstance::handleLoginEvent, this, _1)); + mDispatcher.add("fail.login", boost::bind(&LLLoginInstance::handleLoginFailure, this, _1)); + mDispatcher.add("connect", boost::bind(&LLLoginInstance::handleLoginSuccess, this, _1)); + mDispatcher.add("disconnect", boost::bind(&LLLoginInstance::handleDisconnect, this, _1)); +} + +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("adult_compliant"); + //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("change") && event.has("progress"))) + { + llerrs << "Unknown message from LLLogin: " << event << llendl; + } + + mLoginState = event["state"].asString(); + mResponseData = event["data"]; + + if(event.has("transfer_rate")) + { + mTransferRate = event["transfer_rate"].asReal(); + } + + // Call the method registered in constructor, if any, for more specific + // handling + LLEventDispatcher::Callable method(mDispatcher.get(event["change"])); + if (! method.empty()) + { + method(event); + } + return false; +} + +void 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(); + } +} + +void 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(); + } +} + +void LLLoginInstance::handleDisconnect(const LLSD& event) +{ + // placeholder +} + +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"; +#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 + } + + 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..19d7449bc1 --- /dev/null +++ b/indra/newview/lllogininstance.h @@ -0,0 +1,114 @@ +/** + * @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 "lleventdispatcher.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); + void handleLoginFailure(const LLSD& event); + void handleLoginSuccess(const LLSD& event); + void handleDisconnect(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; + LLEventDispatcher mDispatcher; +}; + +#endif diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index 4fa7f62c68..8005cd1180 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -905,6 +905,18 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PLUGIN_FAILED" << LL_ENDL; }; break; + + case MEDIA_EVENT_PLUGIN_FAILED_LAUNCH: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PLUGIN_FAILED_LAUNCH" << LL_ENDL; + }; + break; + + case MEDIA_EVENT_NAME_CHANGED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAME_CHANGED" << LL_ENDL; + }; + break; }; // chain all events to any potential observers of this object. diff --git a/indra/newview/llmediadataclient.cpp b/indra/newview/llmediadataclient.cpp index 6a932d76bf..6ae42d23d3 100755 --- a/indra/newview/llmediadataclient.cpp +++ b/indra/newview/llmediadataclient.cpp @@ -34,15 +34,18 @@ #include "llmediadataclient.h" +#if LL_MSVC +// disable boost::lexical_cast warning +#pragma warning (disable:4702) +#endif + #include <boost/lexical_cast.hpp> #include "llhttpstatuscodes.h" -#include "llnotifications.h" #include "llsdutil.h" #include "llmediaentry.h" #include "lltextureentry.h" #include "llviewerregion.h" -#include "llvovolume.h" // // When making a request @@ -55,7 +58,9 @@ // - Any request that gets a 503 still goes through the retry logic // -// Some helpful logging macros +const F32 LLMediaDataClient::QUEUE_TIMER_DELAY = 1.0; // seconds(s) +const F32 LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY = 5.0; // secs +const U32 LLMediaDataClient::MAX_RETRIES = 4; ////////////////////////////////////////////////////////////////////////////////////// // @@ -66,7 +71,7 @@ LLMediaDataClient::Request::Request(const std::string &cap_name, const LLSD& sd_payload, - LLVOVolume *obj, + LLMediaDataClientObject *obj, LLMediaDataClient *mdc) : mCapName(cap_name), mPayload(sd_payload), @@ -79,38 +84,39 @@ LLMediaDataClient::Request::Request(const std::string &cap_name, LLMediaDataClient::Request::~Request() { + LL_DEBUGS("LLMediaDataClient") << "~Request" << (*this) << LL_ENDL; mMDC = NULL; - mObject = NULL; + mObject = NULL; } std::string LLMediaDataClient::Request::getCapability() const { - return getObject()->getRegion()->getCapability(getCapName()); + return getObject()->getCapabilityUrl(getCapName()); } // Helper function to get the "type" of request, which just pokes around to // discover it. LLMediaDataClient::Request::Type LLMediaDataClient::Request::getType() const { - if (mCapName == "ObjectMediaNavigate") - { - return NAVIGATE; - } - else if (mCapName == "ObjectMedia") - { - const std::string &verb = mPayload["verb"]; - if (verb == "GET") - { - return GET; - } - else if (verb == "UPDATE") - { - return UPDATE; - } - } - llassert(false); - return GET; + if (mCapName == "ObjectMediaNavigate") + { + return NAVIGATE; + } + else if (mCapName == "ObjectMedia") + { + const std::string &verb = mPayload["verb"]; + if (verb == "GET") + { + return GET; + } + else if (verb == "UPDATE") + { + return UPDATE; + } + } + llassert(false); + return GET; } const char *LLMediaDataClient::Request::getTypeAsString() const @@ -138,6 +144,17 @@ void LLMediaDataClient::Request::reEnqueue() const mMDC->enqueue(this); } +F32 LLMediaDataClient::Request::getRetryTimerDelay() const +{ + return (mMDC == NULL) ? LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY : + mMDC->mRetryTimerDelay; +} + +U32 LLMediaDataClient::Request::getMaxNumRetries() const +{ + return (mMDC == NULL) ? LLMediaDataClient::MAX_RETRIES : mMDC->mMaxNumRetries; +} + std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r) { s << "<request>" @@ -164,15 +181,21 @@ LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr) // virtual LLMediaDataClient::Responder::RetryTimer::~RetryTimer() { + LL_DEBUGS("LLMediaDataClient") << "~RetryTimer" << *(mResponder->getRequest()) << LL_ENDL; + + // XXX This is weird: Instead of doing the work in tick() (which re-schedules + // a timer, which might be risky), do it here, in the destructor. Yes, it is very odd. + // Instead of retrying, we just put the request back onto the queue + LL_INFOS("LLMediaDataClient") << "RetryTimer fired for: " << *(mResponder->getRequest()) << "retrying" << LL_ENDL; + mResponder->getRequest()->reEnqueue(); + + // Release the ref to the responder. mResponder = NULL; } // virtual BOOL LLMediaDataClient::Responder::RetryTimer::tick() { - // Instead of retrying, we just put the request back onto the queue - LL_INFOS("LLMediaDataClient") << "RetryTimer fired for: " << *(mResponder->getRequest()) << "retrying" << LL_ENDL; - mResponder->getRequest()->reEnqueue(); // Don't fire again return TRUE; } @@ -191,35 +214,35 @@ LLMediaDataClient::Responder::Responder(const request_ptr_t &request) LLMediaDataClient::Responder::~Responder() { - mRequest = NULL; + LL_DEBUGS("LLMediaDataClient") << "~Responder" << *(getRequest()) << LL_ENDL; + mRequest = NULL; } /*virtual*/ void LLMediaDataClient::Responder::error(U32 status, const std::string& reason) { - extern LLControlGroup gSavedSettings; - if (status == HTTP_SERVICE_UNAVAILABLE) { - F32 retry_timeout = gSavedSettings.getF32("PrimMediaRetryTimerDelay"); - if (retry_timeout <= 0.0) - { - retry_timeout = (F32)UNAVAILABLE_RETRY_TIMER_DELAY; - } - LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL; + F32 retry_timeout = mRequest->getRetryTimerDelay(); mRequest->incRetryCount(); - // Start timer (instances are automagically tracked by - // InstanceTracker<> and LLEventTimer) - new RetryTimer(F32(retry_timeout/*secs*/), this); + if (mRequest->getRetryCount() < mRequest->getMaxNumRetries()) + { + LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL; + + // Start timer (instances are automagically tracked by + // InstanceTracker<> and LLEventTimer) + new RetryTimer(F32(retry_timeout/*secs*/), this); + } + else { + LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retry count " << + mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL; + } } else { std::string msg = boost::lexical_cast<std::string>(status) + ": " + reason; - LL_INFOS("LLMediaDataClient") << *mRequest << " error(" << msg << ")" << LL_ENDL; - LLSD args; - args["ERROR"] = msg; - LLNotifications::instance().add("ObjectMediaFailure", args); + LL_WARNS("LLMediaDataClient") << *mRequest << " http error(" << msg << ")" << LL_ENDL; } } @@ -227,7 +250,7 @@ void LLMediaDataClient::Responder::error(U32 status, const std::string& reason) /*virtual*/ void LLMediaDataClient::Responder::result(const LLSD& content) { - LL_INFOS("LLMediaDataClient") << *mRequest << "result : " << ll_pretty_print_sd(content) << LL_ENDL; + LL_INFOS("LLMediaDataClient") << *mRequest << "result : " << ll_print_sd(content) << LL_ENDL; } @@ -256,29 +279,25 @@ bool LLMediaDataClient::Comparator::operator() (const request_ptr_t &o1, const r // Calculate the scores for each. F64 o1_score = Comparator::getObjectScore(o1->getObject()); F64 o2_score = Comparator::getObjectScore(o2->getObject()); - - return ( o1_score > o2_score ); + + // XXX Weird: a higher score should go earlier, but by observation I notice + // that this causes further-away objects load first. This is counterintuitive + // to the priority_queue Comparator, which states that this function should + // return 'true' if o1 should be *before* o2. + // In other words, I'd have expected that the following should return + // ( o1_score > o2_score). + return ( o1_score < o2_score ); } // static -F64 LLMediaDataClient::Comparator::getObjectScore(const ll_vo_volume_ptr_t &obj) +F64 LLMediaDataClient::Comparator::getObjectScore(const LLMediaDataClientObject::ptr_t &obj) { // *TODO: make this less expensive? - F32 dist = obj->getRenderPosition().length() + 0.1; // avoids div by 0 + F64 dist = obj->getDistanceFromAvatar() + 0.1; // avoids div by 0 // square the distance so that they are in the same "unit magnitude" as // the interest (which is an area) dist *= dist; - F64 interest = (F64)1; - int i = 0; - int end = obj->getNumTEs(); - for ( ; i < end; ++i) - { - const viewer_media_t &impl = obj->getMediaImpl(i); - if (!impl.isNull()) - { - interest += impl->getInterest(); - } - } + F64 interest = obj->getTotalMediaInterest() + 1.0; return interest/dist; } @@ -286,7 +305,7 @@ F64 LLMediaDataClient::Comparator::getObjectScore(const ll_vo_volume_ptr_t &obj) ////////////////////////////////////////////////////////////////////////////////////// // // LLMediaDataClient::PriorityQueue -// Queue of LLVOVolume smart pointers to request media for. +// Queue of LLMediaDataClientObject smart pointers to request media for. // ////////////////////////////////////////////////////////////////////////////////////// @@ -308,7 +327,7 @@ std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::PriorityQueue ////////////////////////////////////////////////////////////////////////////////////// // // LLMediaDataClient::QueueTimer -// Queue of LLVOVolume smart pointers to request media for. +// Queue of LLMediaDataClientObject smart pointers to request media for. // ////////////////////////////////////////////////////////////////////////////////////// @@ -320,6 +339,7 @@ LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc) LLMediaDataClient::QueueTimer::~QueueTimer() { + LL_DEBUGS("LLMediaDataClient") << "~QueueTimer" << LL_ENDL; mMDC->setIsRunning(false); mMDC = NULL; } @@ -342,15 +362,15 @@ BOOL LLMediaDataClient::QueueTimer::tick() return TRUE; } - LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, queue is: " << queue << LL_ENDL; + LL_INFOS("LLMediaDataClient") << "QueueTimer::tick() started, queue is: " << queue << LL_ENDL; // Peel one off of the items from the queue, and execute request request_ptr_t request = queue.top(); llassert(!request.isNull()); - const ll_vo_volume_ptr_t &object = request->getObject(); + const LLMediaDataClientObject *object = request->getObject(); bool performed_request = false; - llassert(!object.isNull()); - if (!object.isNull() && object->hasMedia()) + llassert(NULL != object); + if (NULL != object && object->hasMedia()) { std::string url = request->getCapability(); if (!url.empty()) @@ -367,17 +387,22 @@ BOOL LLMediaDataClient::QueueTimer::tick() } } else { - if (!object->hasMedia()) + if (NULL == object) { - LL_INFOS("LLMediaDataClient") << "Not Sending request for " << *request << " hasMedia() is false!" << LL_ENDL; + LL_WARNS("LLMediaDataClient") << "Not Sending request for " << *request << " NULL object!" << LL_ENDL; + } + else if (!object->hasMedia()) + { + LL_WARNS("LLMediaDataClient") << "Not Sending request for " << *request << " hasMedia() is false!" << LL_ENDL; } } - bool exceeded_retries = request->getRetryCount() > LLMediaDataClient::MAX_RETRIES; + bool exceeded_retries = request->getRetryCount() > mMDC->mMaxNumRetries; if (performed_request || exceeded_retries) // Try N times before giving up { if (exceeded_retries) { - LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " << LLMediaDataClient::MAX_RETRIES << " tries...popping object id " << object->getID() << LL_ENDL; + LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " + << mMDC->mMaxNumRetries << " tries...popping object id " << object->getID() << LL_ENDL; // XXX Should we bring up a warning dialog?? } queue.pop(); @@ -394,15 +419,12 @@ void LLMediaDataClient::startQueueTimer() { if (! mQueueTimerIsRunning) { - extern LLControlGroup gSavedSettings; - F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay"); - if (queue_timer_delay <= 0.0f) - { - queue_timer_delay = (F32)LLMediaDataClient::QUEUE_TIMER_DELAY; - } - LL_INFOS("LLMediaDataClient") << "starting queue timer (delay=" << queue_timer_delay << " seconds)" << LL_ENDL; + LL_INFOS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL; // LLEventTimer automagically takes care of the lifetime of this object - new QueueTimer(queue_timer_delay, this); + new QueueTimer(mQueueTimerDelay, this); + } + else { + LL_DEBUGS("LLMediaDataClient") << "not starting queue timer (it's already running, right???)" << LL_ENDL; } } @@ -411,9 +433,9 @@ void LLMediaDataClient::stopQueueTimer() mQueueTimerIsRunning = false; } -void LLMediaDataClient::request(LLVOVolume *object, const LLSD &payload) +void LLMediaDataClient::request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload) { - if (NULL == object || ! object->hasMedia()) return; + if (object.isNull() || ! object->hasMedia()) return; // Push the object on the priority queue enqueue(new Request(getCapabilityName(), payload, object, this)); @@ -422,8 +444,8 @@ void LLMediaDataClient::request(LLVOVolume *object, const LLSD &payload) void LLMediaDataClient::enqueue(const Request *request) { LL_INFOS("LLMediaDataClient") << "Queuing request for " << *request << LL_ENDL; - // Push the request on the priority queue - // Sadly, we have to const-cast because items put into the queue are not const + // Push the request on the priority queue + // Sadly, we have to const-cast because items put into the queue are not const pRequestQueue->push(const_cast<LLMediaDataClient::Request*>(request)); LL_DEBUGS("LLMediaDataClient") << "Queue:" << (*pRequestQueue) << LL_ENDL; // Start the timer if not already running @@ -436,21 +458,31 @@ void LLMediaDataClient::enqueue(const Request *request) // ////////////////////////////////////////////////////////////////////////////////////// -LLMediaDataClient::LLMediaDataClient() +LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay, + F32 retry_timer_delay, + U32 max_retries) + : mQueueTimerDelay(queue_timer_delay), + mRetryTimerDelay(retry_timer_delay), + mMaxNumRetries(max_retries), + mQueueTimerIsRunning(false) { pRequestQueue = new PriorityQueue(); } - LLMediaDataClient::~LLMediaDataClient() { stopQueueTimer(); - + // This should clear the queue, and hopefully call all the destructors. - while (! pRequestQueue->empty()) pRequestQueue->pop(); - + LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClient destructor: queue: " << + (pRequestQueue->empty() ? "<empty> " : "<not empty> ") << (*pRequestQueue) << LL_ENDL; delete pRequestQueue; - pRequestQueue = NULL; + pRequestQueue = NULL; +} + +bool LLMediaDataClient::isEmpty() const +{ + return (NULL == pRequestQueue) ? true : pRequestQueue->empty(); } ////////////////////////////////////////////////////////////////////////////////////// @@ -462,79 +494,78 @@ LLMediaDataClient::~LLMediaDataClient() LLMediaDataClient::Responder *LLObjectMediaDataClient::createResponder(const request_ptr_t &request) const { - return new LLObjectMediaDataClient::Responder(request); + return new LLObjectMediaDataClient::Responder(request); } const char *LLObjectMediaDataClient::getCapabilityName() const { - return "ObjectMedia"; + return "ObjectMedia"; } -void LLObjectMediaDataClient::fetchMedia(LLVOVolume *object) +void LLObjectMediaDataClient::fetchMedia(LLMediaDataClientObject *object) { - LLSD sd_payload; - sd_payload["verb"] = "GET"; - sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); - request(object, sd_payload); + LLSD sd_payload; + sd_payload["verb"] = "GET"; + sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); + request(object, sd_payload); } -void LLObjectMediaDataClient::updateMedia(LLVOVolume *object) +void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject *object) { - LLSD sd_payload; - sd_payload["verb"] = "UPDATE"; - sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); - LLSD object_media_data; - for (int i=0; i < object->getNumTEs(); i++) { - LLTextureEntry *texture_entry = object->getTE(i); - llassert((texture_entry->getMediaData() != NULL) == texture_entry->hasMedia()); - const LLSD &media_data = - (texture_entry->getMediaData() == NULL) ? LLSD() : texture_entry->getMediaData()->asLLSD(); - object_media_data.append(media_data); - } - sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data; - - LL_INFOS("LLMediaDataClient") << "update media data: " << object->getID() << " " << ll_pretty_print_sd(sd_payload) << LL_ENDL; - - request(object, sd_payload); + LLSD sd_payload; + sd_payload["verb"] = "UPDATE"; + sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); + LLSD object_media_data; + int i = 0; + int end = object->getMediaDataCount(); + for ( ; i < end ; ++i) + { + object_media_data.append(object->getMediaDataLLSD(i)); + } + sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data; + + LL_INFOS("LLMediaDataClient") << "update media data: " << object->getID() << " " << ll_print_sd(sd_payload) << LL_ENDL; + + request(object, sd_payload); } /*virtual*/ void LLObjectMediaDataClient::Responder::result(const LLSD& content) { - const LLMediaDataClient::Request::Type type = getRequest()->getType(); - llassert(type == LLMediaDataClient::Request::GET || type == LLMediaDataClient::Request::UPDATE) - if (type == LLMediaDataClient::Request::GET) - { - LL_INFOS("LLMediaDataClient") << *(getRequest()) << "GET returned: " << ll_pretty_print_sd(content) << LL_ENDL; - - // Look for an error - if (content.has("error")) - { - const LLSD &error = content["error"]; - LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error getting media data for object: code=" << + const LLMediaDataClient::Request::Type type = getRequest()->getType(); + llassert(type == LLMediaDataClient::Request::GET || type == LLMediaDataClient::Request::UPDATE) + if (type == LLMediaDataClient::Request::GET) + { + LL_INFOS("LLMediaDataClient") << *(getRequest()) << "GET returned: " << ll_print_sd(content) << LL_ENDL; + + // Look for an error + if (content.has("error")) + { + const LLSD &error = content["error"]; + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error getting media data for object: code=" << error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; - - // XXX Warn user? - } - else { - // Check the data - const LLUUID &object_id = content[LLTextureEntry::OBJECT_ID_KEY]; - if (object_id != getRequest()->getObject()->getID()) - { - // NOT good, wrong object id!! - LL_WARNS("LLMediaDataClient") << *(getRequest()) << "DROPPING response with wrong object id (" << object_id << ")" << LL_ENDL; - return; - } - - // Otherwise, update with object media data - getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY]); - } - } - else if (type == LLMediaDataClient::Request::UPDATE) - { - // just do what our superclass does - LLMediaDataClient::Responder::result(content); - } + + // XXX Warn user? + } + else { + // Check the data + const LLUUID &object_id = content[LLTextureEntry::OBJECT_ID_KEY]; + if (object_id != getRequest()->getObject()->getID()) + { + // NOT good, wrong object id!! + LL_WARNS("LLMediaDataClient") << *(getRequest()) << "DROPPING response with wrong object id (" << object_id << ")" << LL_ENDL; + return; + } + + // Otherwise, update with object media data + getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY]); + } + } + else if (type == LLMediaDataClient::Request::UPDATE) + { + // just do what our superclass does + LLMediaDataClient::Responder::result(content); + } } ////////////////////////////////////////////////////////////////////////////////////// @@ -545,21 +576,21 @@ void LLObjectMediaDataClient::Responder::result(const LLSD& content) ////////////////////////////////////////////////////////////////////////////////////// LLMediaDataClient::Responder *LLObjectMediaNavigateClient::createResponder(const request_ptr_t &request) const { - return new LLObjectMediaNavigateClient::Responder(request); + return new LLObjectMediaNavigateClient::Responder(request); } const char *LLObjectMediaNavigateClient::getCapabilityName() const { - return "ObjectMediaNavigate"; + return "ObjectMediaNavigate"; } -void LLObjectMediaNavigateClient::navigate(LLVOVolume *object, U8 texture_index, const std::string &url) +void LLObjectMediaNavigateClient::navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url) { - LLSD sd_payload; - sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); + LLSD sd_payload; + sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); sd_payload[LLMediaEntry::CURRENT_URL_KEY] = url; sd_payload[LLTextureEntry::TEXTURE_INDEX_KEY] = (LLSD::Integer)texture_index; - request(object, sd_payload); + request(object, sd_payload); } /*virtual*/ @@ -573,16 +604,18 @@ void LLObjectMediaNavigateClient::Responder::error(U32 status, const std::string } else { // bounce the face back - bounceBack(); LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: http code=" << status << LL_ENDL; + const LLSD &payload = getRequest()->getPayload(); + // bounce the face back + getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]); } } /*virtual*/ void LLObjectMediaNavigateClient::Responder::result(const LLSD& content) { - LL_DEBUGS("LLMediaDataClient") << *(getRequest()) << " NAVIGATE returned " << ll_pretty_print_sd(content) << LL_ENDL; - + LL_INFOS("LLMediaDataClient") << *(getRequest()) << " NAVIGATE returned " << ll_print_sd(content) << LL_ENDL; + if (content.has("error")) { const LLSD &error = content["error"]; @@ -590,38 +623,19 @@ void LLObjectMediaNavigateClient::Responder::result(const LLSD& content) if (ERROR_PERMISSION_DENIED_CODE == error_code) { - LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Navigation denied: bounce back" << LL_ENDL; + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Navigation denied: bounce back" << LL_ENDL; + const LLSD &payload = getRequest()->getPayload(); // bounce the face back - bounceBack(); + getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]); } else { - LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: code=" << + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: code=" << error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; - } + } // XXX Warn user? } - else { - // just do what our superclass does - LLMediaDataClient::Responder::result(content); - } -} - - -void LLObjectMediaNavigateClient::Responder::bounceBack() -{ - const LLSD &payload = getRequest()->getPayload(); - U8 texture_index = (U8)(LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]; - viewer_media_t impl = getRequest()->getObject()->getMediaImpl(texture_index); - // Find the media entry for this navigate - LLMediaEntry* mep = NULL; - LLTextureEntry *te = getRequest()->getObject()->getTE(texture_index); - if(te) - { - mep = te->getMediaData(); - } - - if (mep && impl) - { -// impl->navigateTo(mep->getCurrentURL()); - } + else { + // just do what our superclass does + LLMediaDataClient::Responder::result(content); + } } diff --git a/indra/newview/llmediadataclient.h b/indra/newview/llmediadataclient.h index 59c4334251..9d0aa0981e 100755 --- a/indra/newview/llmediadataclient.h +++ b/indra/newview/llmediadataclient.h @@ -36,12 +36,36 @@ #include "llhttpclient.h" #include <queue> #include "llrefcount.h" +#include "llpointer.h" #include "lltimer.h" -// Forward decls -class LLVOVolume; -typedef LLPointer<LLVOVolume> ll_vo_volume_ptr_t; +// Link seam for LLVOVolume +class LLMediaDataClientObject : public LLRefCount +{ +public: + // Get the number of media data items + virtual U8 getMediaDataCount() const = 0; + // Get the media data at index, as an LLSD + virtual LLSD getMediaDataLLSD(U8 index) const = 0; + // Get this object's UUID + virtual LLUUID getID() const = 0; + // Navigate back to previous URL + virtual void mediaNavigateBounceBack(U8 index) = 0; + // Does this object have media? + virtual bool hasMedia() const = 0; + // Update the object's media data to the given array + virtual void updateObjectMediaData(LLSD const &media_data_array) = 0; + // Return the distance from the object to the avatar + virtual F64 getDistanceFromAvatar() const = 0; + // Return the total "interest" of the media (on-screen area) + virtual F64 getTotalMediaInterest() const = 0; + // Return the given cap url + virtual std::string getCapabilityUrl(const std::string &name) const = 0; + + // smart pointer + typedef LLPointer<LLMediaDataClientObject> ptr_t; +}; // This object creates a priority queue for requests. // Abstracts the Cap URL, the request, and the responder @@ -50,15 +74,23 @@ class LLMediaDataClient : public LLRefCount public: LOG_CLASS(LLMediaDataClient); - const static int QUEUE_TIMER_DELAY = 1; // seconds(s) - const static int MAX_RETRIES = 4; + const static F32 QUEUE_TIMER_DELAY;// = 1.0; // seconds(s) + const static F32 UNAVAILABLE_RETRY_TIMER_DELAY;// = 5.0; // secs + const static U32 MAX_RETRIES;// = 4; // Constructor - LLMediaDataClient(); + LLMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES); // Make the request - void request(LLVOVolume *object, const LLSD &payload); - + void request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload); + + F32 getRetryTimerDelay() const { return mRetryTimerDelay; } + + // Returns true iff the queue is empty + bool isEmpty() const; + protected: // Destructor virtual ~LLMediaDataClient(); // use unref @@ -73,10 +105,10 @@ protected: NAVIGATE }; - Request(const std::string &cap_name, const LLSD& sd_payload, LLVOVolume *obj, LLMediaDataClient *mdc); + Request(const std::string &cap_name, const LLSD& sd_payload, LLMediaDataClientObject *obj, LLMediaDataClient *mdc); const std::string &getCapName() const { return mCapName; } const LLSD &getPayload() const { return mPayload; } - LLVOVolume *getObject() const { return mObject; } + LLMediaDataClientObject *getObject() const { return mObject; } U32 getNum() const { return mNum; } @@ -92,6 +124,9 @@ protected: // Re-enqueue thyself void reEnqueue() const; + F32 getRetryTimerDelay() const; + U32 getMaxNumRetries() const; + public: friend std::ostream& operator<<(std::ostream &s, const Request &q); @@ -101,7 +136,7 @@ protected: private: std::string mCapName; LLSD mPayload; - ll_vo_volume_ptr_t mObject; + LLMediaDataClientObject::ptr_t mObject; // Simple tracking const U32 mNum; static U32 sNum; @@ -115,8 +150,6 @@ protected: // Responder class Responder : public LLHTTPClient::Responder { - static const int UNAVAILABLE_RETRY_TIMER_DELAY = 5; // secs - public: Responder(const request_ptr_t &request); //If we get back an error (not found, etc...), handle it here @@ -163,9 +196,10 @@ private: public: bool operator() (const request_ptr_t &o1, const request_ptr_t &o2) const; private: - static F64 getObjectScore(const ll_vo_volume_ptr_t &obj); + static F64 getObjectScore(const LLMediaDataClientObject::ptr_t &obj); }; + // PriorityQueue class PriorityQueue : public std::priority_queue< request_ptr_t, std::vector<request_ptr_t>, @@ -193,6 +227,10 @@ private: void startQueueTimer(); void stopQueueTimer(); void setIsRunning(bool val) { mQueueTimerIsRunning = val; } + + const F32 mQueueTimerDelay; + const F32 mRetryTimerDelay; + const U32 mMaxNumRetries; bool mQueueTimerIsRunning; @@ -204,11 +242,15 @@ private: class LLObjectMediaDataClient : public LLMediaDataClient { public: - LLObjectMediaDataClient() {} + LLObjectMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES) + : LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries) + {} ~LLObjectMediaDataClient() {} - void fetchMedia(LLVOVolume *object); - void updateMedia(LLVOVolume *object); + void fetchMedia(LLMediaDataClientObject *object); + void updateMedia(LLMediaDataClientObject *object); protected: // Subclasses must override this factory method to return a new responder @@ -230,14 +272,18 @@ protected: // MediaDataResponder specific for the ObjectMediaNavigate cap class LLObjectMediaNavigateClient : public LLMediaDataClient { +public: // NOTE: from llmediaservice.h static const int ERROR_PERMISSION_DENIED_CODE = 8002; -public: - LLObjectMediaNavigateClient() {} + LLObjectMediaNavigateClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES) + : LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries) + {} ~LLObjectMediaNavigateClient() {} - void navigate(LLVOVolume *object, U8 texture_index, const std::string &url); + void navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url); protected: // Subclasses must override this factory method to return a new responder @@ -254,7 +300,7 @@ protected: virtual void error(U32 status, const std::string& reason); virtual void result(const LLSD &content); private: - void bounceBack(); + void mediaNavigateBounceBack(); }; }; diff --git a/indra/newview/llmenucommands.cpp b/indra/newview/llmenucommands.cpp index 8dd09d5ec7..28ddaa61c4 100644 --- a/indra/newview/llmenucommands.cpp +++ b/indra/newview/llmenucommands.cpp @@ -66,7 +66,6 @@ #include "llfocusmgr.h" #include "llnearbychatbar.h" - void handle_mouselook(void*) { gAgent.changeCameraToMouselook(); diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 2cbd80a72d..521e145816 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -36,6 +36,7 @@ #include "llbutton.h" #include "llfloaterreg.h" #include "llsdutil.h" +#include "llsdutil_math.h" #include "llaccordionctrltab.h" #include "llagent.h" diff --git a/indra/newview/llpanelmediasettingsgeneral.cpp b/indra/newview/llpanelmediasettingsgeneral.cpp index 3177ef9a21..ab5d8601d0 100644 --- a/indra/newview/llpanelmediasettingsgeneral.cpp +++ b/indra/newview/llpanelmediasettingsgeneral.cpp @@ -1,440 +1,457 @@ -/** - * @file llpanelmediasettingsgeneral.cpp - * @brief LLPanelMediaSettingsGeneral class implementation - * - * $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 "llpanelmediasettingsgeneral.h" -#include "llcombobox.h" -#include "llcheckboxctrl.h" -#include "llspinctrl.h" -#include "lluictrlfactory.h" -#include "llviewerwindow.h" -#include "llviewermedia.h" -#include "llsdutil.h" -#include "llselectmgr.h" -#include "llbutton.h" -#include "lltexturectrl.h" -#include "llurl.h" -#include "llwindow.h" -#include "llmediaentry.h" -#include "llmediactrl.h" -#include "llpanelcontents.h" -#include "llpluginclassmedia.h" -#include "llfloatermediasettings.h" -#include "llfloatertools.h" -#include "lltrans.h" - -//////////////////////////////////////////////////////////////////////////////// -// -LLPanelMediaSettingsGeneral::LLPanelMediaSettingsGeneral() : - mControls( NULL ), - mAutoLoop( NULL ), - mFirstClick( NULL ), - mAutoZoom( NULL ), - mAutoPlay( NULL ), - mAutoScale( NULL ), - mWidthPixels( NULL ), - mHeightPixels( NULL ), - mHomeURL( NULL ), - mCurrentURL( NULL ), - mAltImageEnable( NULL ), - mParent( NULL ), - mMediaEditable(false) -{ - // build dialog from XML - LLUICtrlFactory::getInstance()->buildPanel(this, "panel_media_settings_general.xml"); -// mCommitCallbackRegistrar.add("Media.ResetCurrentUrl", boost::bind(&LLPanelMediaSettingsGeneral::onBtnResetCurrentUrl, this)); -// mCommitCallbackRegistrar.add("Media.CommitHomeURL", boost::bind(&LLPanelMediaSettingsGeneral::onCommitHomeURL, this)); - -} - -//////////////////////////////////////////////////////////////////////////////// -// -BOOL LLPanelMediaSettingsGeneral::postBuild() -{ - // connect member vars with UI widgets - mAltImageEnable = getChild< LLCheckBoxCtrl >( LLMediaEntry::ALT_IMAGE_ENABLE_KEY ); - mAutoLoop = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_LOOP_KEY ); - mAutoPlay = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_PLAY_KEY ); - mAutoScale = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_SCALE_KEY ); - mAutoZoom = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_ZOOM_KEY ); - mControls = getChild< LLComboBox >( LLMediaEntry::CONTROLS_KEY ); - mCurrentURL = getChild< LLLineEditor >( LLMediaEntry::CURRENT_URL_KEY ); - mFirstClick = getChild< LLCheckBoxCtrl >( LLMediaEntry::FIRST_CLICK_INTERACT_KEY ); - mHeightPixels = getChild< LLSpinCtrl >( LLMediaEntry::HEIGHT_PIXELS_KEY ); - mHomeURL = getChild< LLLineEditor >( LLMediaEntry::HOME_URL_KEY ); - mWidthPixels = getChild< LLSpinCtrl >( LLMediaEntry::WIDTH_PIXELS_KEY ); - mPreviewMedia = getChild<LLMediaCtrl>("preview_media"); - - // watch commit action for HOME URL - childSetCommitCallback( LLMediaEntry::HOME_URL_KEY, onCommitHomeURL, this); - childSetCommitCallback( "current_url_reset_btn",onBtnResetCurrentUrl, this); - // interrogates controls and updates widgets as required - updateMediaPreview(); - updateCurrentURL(); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -// virtual -LLPanelMediaSettingsGeneral::~LLPanelMediaSettingsGeneral() -{ -} - -//////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsGeneral::draw() -{ - // housekeeping - LLPanel::draw(); - - // enable/disable pixel values image entry based on auto scale checkbox - if ( mAutoScale->getValue().asBoolean() == false ) - { - childSetEnabled( LLMediaEntry::WIDTH_PIXELS_KEY, true ); - childSetEnabled( LLMediaEntry::HEIGHT_PIXELS_KEY, true ); - } - else - { - childSetEnabled( LLMediaEntry::WIDTH_PIXELS_KEY, false ); - childSetEnabled( LLMediaEntry::HEIGHT_PIXELS_KEY, false ); - }; - - // enable/disable UI based on type of media - bool reset_button_is_active = true; - if( mPreviewMedia ) - { - LLPluginClassMedia* media_plugin = mPreviewMedia->getMediaPlugin(); - if( media_plugin ) - { - // some controls are only appropriate for time or browser type plugins - // so we selectively enable/disable them - need to do it in draw - // because the information from plugins arrives assynchronously - bool show_time_controls = media_plugin->pluginSupportsMediaTime(); - if ( show_time_controls ) - { - childSetEnabled( LLMediaEntry::CURRENT_URL_KEY, false ); - reset_button_is_active = false; - childSetEnabled( "current_url_label", false ); - childSetEnabled( LLMediaEntry::AUTO_LOOP_KEY, true ); - } - else - { - childSetEnabled( LLMediaEntry::CURRENT_URL_KEY, true ); - reset_button_is_active = true; - childSetEnabled( "current_url_label", true ); - childSetEnabled( LLMediaEntry::AUTO_LOOP_KEY, false ); - }; - }; - }; - - // current URL can change over time. -// updateCurrentURL(); - - // enable/disable RESRET button depending on permissions - // since this is the same as a navigate action - bool user_can_press_reset = mMediaEditable; - - // several places modify this widget so we must collect states in one place - if ( reset_button_is_active ) - { - // user has perms to press reset button and it is active - if ( user_can_press_reset ) - { - childSetEnabled( "current_url_reset_btn", true ); - } - // user does not has perms to press reset button and it is active - else - { - childSetEnabled( "current_url_reset_btn", false ); - }; - } - else - // reset button is inactive so we just slam it to off - other states don't matter - { - childSetEnabled( "current_url_reset_btn", false ); - }; -} - -//////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsGeneral::clearValues( void* userdata, bool editable) -{ - LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata; - self->mAltImageEnable ->clear(); - self->mAutoLoop->clear(); - self->mAutoPlay->clear(); - self->mAutoScale->clear(); - self->mAutoZoom ->clear(); - self->mControls->clear(); - self->mCurrentURL->clear(); - self->mFirstClick->clear(); - self->mHeightPixels->clear(); - self->mHomeURL->clear(); - self->mWidthPixels->clear(); - self->mAltImageEnable ->setEnabled(editable); - self->mAutoLoop ->setEnabled(editable); - self->mAutoPlay ->setEnabled(editable); - self->mAutoScale ->setEnabled(editable); - self->mAutoZoom ->setEnabled(editable); - self->mControls ->setEnabled(editable); - self->mCurrentURL ->setEnabled(editable); - self->mFirstClick ->setEnabled(editable); - self->mHeightPixels ->setEnabled(editable); - self->mHomeURL ->setEnabled(editable); - self->mWidthPixels ->setEnabled(editable); - self->mPreviewMedia->unloadMediaSource(); -} - -//////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& media_settings ,bool editable) -{ - LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata; - self->mMediaEditable = editable; - - //llinfos << "---------------" << llendl; - //llinfos << ll_pretty_print_sd(media_settings) << llendl; - //llinfos << "---------------" << llendl; - - // IF all the faces have media (or all dont have media) - if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo ) - { - if(LLFloaterMediaSettings::getInstance()->mMultipleMedia) - { - self->clearValues(self, self->mMediaEditable); - // only show multiple - self->mHomeURL ->setText(LLTrans::getString("Multiple Media")); - return; - } - - } - else - { - if(LLFloaterMediaSettings::getInstance()->mMultipleValidMedia) - { - self->clearValues(self, self->mMediaEditable); - // only show multiple - self->mHomeURL ->setText(LLTrans::getString("Multiple Media")); - return; - } - - } - std::string base_key( "" ); - std::string tentative_key( "" ); - - struct - { - std::string key_name; - LLUICtrl* ctrl_ptr; - std::string ctrl_type; - - } data_set [] = - { - { LLMediaEntry::AUTO_LOOP_KEY, self->mAutoLoop, "LLCheckBoxCtrl" }, - { LLMediaEntry::AUTO_PLAY_KEY, self->mAutoPlay, "LLCheckBoxCtrl" }, - { LLMediaEntry::AUTO_SCALE_KEY, self->mAutoScale, "LLCheckBoxCtrl" }, - { LLMediaEntry::AUTO_ZOOM_KEY, self->mAutoZoom, "LLCheckBoxCtrl" }, - { LLMediaEntry::CONTROLS_KEY, self->mControls, "LLComboBox" }, - { LLMediaEntry::CURRENT_URL_KEY, self->mCurrentURL, "LLLineEditor" }, - { LLMediaEntry::HEIGHT_PIXELS_KEY, self->mHeightPixels, "LLSpinCtrl" }, - { LLMediaEntry::HOME_URL_KEY, self->mHomeURL, "LLLineEditor" }, - { LLMediaEntry::FIRST_CLICK_INTERACT_KEY, self->mFirstClick, "LLCheckBoxCtrl" }, - { LLMediaEntry::WIDTH_PIXELS_KEY, self->mWidthPixels, "LLSpinCtrl" }, - { LLMediaEntry::ALT_IMAGE_ENABLE_KEY, self->mAltImageEnable, "LLCheckBoxCtrl" }, - { "", NULL , "" } - }; - - for( int i = 0; data_set[ i ].key_name.length() > 0; ++i ) - { - base_key = std::string( data_set[ i ].key_name ); - tentative_key = base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ); - // TODO: CP - I bet there is a better way to do this using Boost - if ( media_settings[ base_key ].isDefined() ) - { - if ( data_set[ i ].ctrl_type == "LLLineEditor" ) - { - static_cast< LLLineEditor* >( data_set[ i ].ctrl_ptr )-> - setText( media_settings[ base_key ].asString() ); - } - else - if ( data_set[ i ].ctrl_type == "LLCheckBoxCtrl" ) - static_cast< LLCheckBoxCtrl* >( data_set[ i ].ctrl_ptr )-> - setValue( media_settings[ base_key ].asBoolean() ); - else - if ( data_set[ i ].ctrl_type == "LLComboBox" ) - static_cast< LLComboBox* >( data_set[ i ].ctrl_ptr )-> - setCurrentByIndex( media_settings[ base_key ].asInteger() ); - else - if ( data_set[ i ].ctrl_type == "LLSpinCtrl" ) - static_cast< LLSpinCtrl* >( data_set[ i ].ctrl_ptr )-> - setValue( media_settings[ base_key ].asInteger() ); - - data_set[ i ].ctrl_ptr->setEnabled(self->mMediaEditable); - data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() ); - }; - }; - - // interrogates controls and updates widgets as required - self->updateMediaPreview(); - self->updateCurrentURL(); -} - -//////////////////////////////////////////////////////////////////////////////// -// Helper to set media control to media URL as required -void LLPanelMediaSettingsGeneral::updateMediaPreview() -{ - if ( mHomeURL->getValue().asString().length() > 0 ) - { - mPreviewMedia->navigateTo( mHomeURL->getValue().asString() ); - } - else - // new home URL will be empty if media is deleted but - // we still need to clean out the preview. - { - mPreviewMedia->unloadMediaSource(); - }; -} - -//////////////////////////////////////////////////////////////////////////////// -// Helper to set current URL -void LLPanelMediaSettingsGeneral::updateCurrentURL() -{ - if( mCurrentURL->getText().empty() ) - { - childSetText( "current_url", mHomeURL->getText() ); - } - -} - -//////////////////////////////////////////////////////////////////////////////// - -// virtual -void LLPanelMediaSettingsGeneral::onClose(bool app_quitting) -{ - if(mPreviewMedia) - { - mPreviewMedia->unloadMediaSource(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsGeneral::onCommitHomeURL( LLUICtrl* ctrl, void *userdata ) -{ - LLPanelMediaSettingsGeneral* self =(LLPanelMediaSettingsGeneral *)userdata; - self->updateMediaPreview(); -} - -//////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsGeneral::onBtnResetCurrentUrl(LLUICtrl* ctrl, void *userdata) -{ - LLPanelMediaSettingsGeneral* self =(LLPanelMediaSettingsGeneral *)userdata; - self->navigateHomeSelectedFace(); -} - -//////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsGeneral::apply( void* userdata ) -{ - LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata; - self->mHomeURL->onCommit(); - // build LLSD Fragment - LLSD media_data_general; - self->getValues(media_data_general); - - // this merges contents of LLSD passed in with what's there so this is ok - LLSelectMgr::getInstance()->selectionSetMediaData( media_data_general ); -} - -//////////////////////////////////////////////////////////////////////////////// -// -void LLPanelMediaSettingsGeneral::getValues( LLSD &fill_me_in ) -{ - fill_me_in[LLMediaEntry::ALT_IMAGE_ENABLE_KEY] = mAltImageEnable->getValue(); - fill_me_in[LLMediaEntry::AUTO_LOOP_KEY] = mAutoLoop->getValue(); - fill_me_in[LLMediaEntry::AUTO_PLAY_KEY] = mAutoPlay->getValue(); - fill_me_in[LLMediaEntry::AUTO_SCALE_KEY] = mAutoScale->getValue(); - fill_me_in[LLMediaEntry::AUTO_ZOOM_KEY] = mAutoZoom->getValue(); - fill_me_in[LLMediaEntry::CONTROLS_KEY] = mControls->getCurrentIndex(); - // XXX Don't send current URL! - //fill_me_in[LLMediaEntry::CURRENT_URL_KEY] = mCurrentURL->getValue(); - fill_me_in[LLMediaEntry::HEIGHT_PIXELS_KEY] = mHeightPixels->getValue(); - fill_me_in[LLMediaEntry::HOME_URL_KEY] = mHomeURL->getValue(); - fill_me_in[LLMediaEntry::FIRST_CLICK_INTERACT_KEY] = mFirstClick->getValue(); - fill_me_in[LLMediaEntry::WIDTH_PIXELS_KEY] = mWidthPixels->getValue(); -} - -//////////////////////////////////////////////////////////////////////////////// -// -void LLPanelMediaSettingsGeneral::setParent( LLFloaterMediaSettings* parent ) -{ - mParent = parent; -}; - -bool LLPanelMediaSettingsGeneral::navigateHomeSelectedFace() -{ - // HACK: This is directly referencing an impl name. BAD! - // This can be removed when we have a truly generic media browser that only - // builds an impl based on the type of url it is passed. - struct functor_navigate_media : public LLSelectedTEGetFunctor< bool> - { - bool get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - { - if(object->permModify()) - { - viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(object->getTE(face)->getMediaData()->getMediaID()); - if(media_impl) - { - media_impl->navigateHome(); - return true; - } - } - } - return false; - }; - - } functor_navigate_media; - - bool all_face_media_navigated = false; - LLObjectSelectionHandle selected_objects =LLSelectMgr::getInstance()->getSelection(); - selected_objects->getSelectedTEValue( &functor_navigate_media, all_face_media_navigated ); - - return all_face_media_navigated; -} - +/**
+ * @file llpanelmediasettingsgeneral.cpp
+ * @brief LLPanelMediaSettingsGeneral class implementation
+ *
+ * $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 "llagent.h"
+#include "llpanelmediasettingsgeneral.h"
+#include "llcombobox.h"
+#include "llcheckboxctrl.h"
+#include "llspinctrl.h"
+#include "lluictrlfactory.h"
+#include "llviewerwindow.h"
+#include "llviewermedia.h"
+#include "llsdutil.h"
+#include "llselectmgr.h"
+#include "llbutton.h"
+#include "lltexturectrl.h"
+#include "llurl.h"
+#include "llwindow.h"
+#include "llmediaentry.h"
+#include "llmediactrl.h"
+#include "llpanelcontents.h"
+#include "llpermissions.h"
+#include "llpluginclassmedia.h"
+#include "llfloatermediasettings.h"
+#include "llfloatertools.h"
+#include "lltrans.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//
+LLPanelMediaSettingsGeneral::LLPanelMediaSettingsGeneral() :
+ mControls( NULL ),
+ mAutoLoop( NULL ),
+ mFirstClick( NULL ),
+ mAutoZoom( NULL ),
+ mAutoPlay( NULL ),
+ mAutoScale( NULL ),
+ mWidthPixels( NULL ),
+ mHeightPixels( NULL ),
+ mHomeURL( NULL ),
+ mCurrentURL( NULL ),
+ mAltImageEnable( NULL ),
+ mParent( NULL ),
+ mMediaEditable(false)
+{
+ // build dialog from XML
+ LLUICtrlFactory::getInstance()->buildPanel(this, "panel_media_settings_general.xml");
+// mCommitCallbackRegistrar.add("Media.ResetCurrentUrl", boost::bind(&LLPanelMediaSettingsGeneral::onBtnResetCurrentUrl, this));
+// mCommitCallbackRegistrar.add("Media.CommitHomeURL", boost::bind(&LLPanelMediaSettingsGeneral::onCommitHomeURL, this));
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+BOOL LLPanelMediaSettingsGeneral::postBuild()
+{
+ // connect member vars with UI widgets
+ mAltImageEnable = getChild< LLCheckBoxCtrl >( LLMediaEntry::ALT_IMAGE_ENABLE_KEY );
+ mAutoLoop = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_LOOP_KEY );
+ mAutoPlay = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_PLAY_KEY );
+ mAutoScale = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_SCALE_KEY );
+ mAutoZoom = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_ZOOM_KEY );
+ mControls = getChild< LLComboBox >( LLMediaEntry::CONTROLS_KEY );
+ mCurrentURL = getChild< LLLineEditor >( LLMediaEntry::CURRENT_URL_KEY );
+ mFirstClick = getChild< LLCheckBoxCtrl >( LLMediaEntry::FIRST_CLICK_INTERACT_KEY );
+ mHeightPixels = getChild< LLSpinCtrl >( LLMediaEntry::HEIGHT_PIXELS_KEY );
+ mHomeURL = getChild< LLLineEditor >( LLMediaEntry::HOME_URL_KEY );
+ mWidthPixels = getChild< LLSpinCtrl >( LLMediaEntry::WIDTH_PIXELS_KEY );
+ mPreviewMedia = getChild<LLMediaCtrl>("preview_media");
+
+ // watch commit action for HOME URL
+ childSetCommitCallback( LLMediaEntry::HOME_URL_KEY, onCommitHomeURL, this);
+ childSetCommitCallback( "current_url_reset_btn",onBtnResetCurrentUrl, this);
+ // interrogates controls and updates widgets as required
+ updateMediaPreview();
+ updateCurrentURL();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// virtual
+LLPanelMediaSettingsGeneral::~LLPanelMediaSettingsGeneral()
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static
+void LLPanelMediaSettingsGeneral::draw()
+{
+ // housekeeping
+ LLPanel::draw();
+
+ // enable/disable pixel values image entry based on auto scale checkbox
+ if ( mAutoScale->getValue().asBoolean() == false )
+ {
+ childSetEnabled( LLMediaEntry::WIDTH_PIXELS_KEY, true );
+ childSetEnabled( LLMediaEntry::HEIGHT_PIXELS_KEY, true );
+ }
+ else
+ {
+ childSetEnabled( LLMediaEntry::WIDTH_PIXELS_KEY, false );
+ childSetEnabled( LLMediaEntry::HEIGHT_PIXELS_KEY, false );
+ };
+
+ // enable/disable UI based on type of media
+ bool reset_button_is_active = true;
+ if( mPreviewMedia )
+ {
+ LLPluginClassMedia* media_plugin = mPreviewMedia->getMediaPlugin();
+ if( media_plugin )
+ {
+ // some controls are only appropriate for time or browser type plugins
+ // so we selectively enable/disable them - need to do it in draw
+ // because the information from plugins arrives assynchronously
+ bool show_time_controls = media_plugin->pluginSupportsMediaTime();
+ if ( show_time_controls )
+ {
+ childSetEnabled( LLMediaEntry::CURRENT_URL_KEY, false );
+ reset_button_is_active = false;
+ childSetEnabled( "current_url_label", false );
+ childSetEnabled( LLMediaEntry::AUTO_LOOP_KEY, true );
+ }
+ else
+ {
+ childSetEnabled( LLMediaEntry::CURRENT_URL_KEY, true );
+ reset_button_is_active = true;
+ childSetEnabled( "current_url_label", true );
+ childSetEnabled( LLMediaEntry::AUTO_LOOP_KEY, false );
+ };
+ };
+ };
+
+ // current URL can change over time.
+// updateCurrentURL();
+
+ LLPermissions perm;
+ bool user_can_press_reset = mMediaEditable;
+
+ // several places modify this widget so we must collect states in one place
+ if ( reset_button_is_active )
+ {
+ // user has perms to press reset button and it is active
+ if ( user_can_press_reset )
+ {
+ childSetEnabled( "current_url_reset_btn", true );
+ }
+ // user does not has perms to press reset button and it is active
+ else
+ {
+ childSetEnabled( "current_url_reset_btn", false );
+ };
+ }
+ else
+ // reset button is inactive so we just slam it to off - other states don't matter
+ {
+ childSetEnabled( "current_url_reset_btn", false );
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static
+void LLPanelMediaSettingsGeneral::clearValues( void* userdata, bool editable)
+{
+ LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata;
+ self->mAltImageEnable ->clear();
+ self->mAutoLoop->clear();
+ self->mAutoPlay->clear();
+ self->mAutoScale->clear();
+ self->mAutoZoom ->clear();
+ self->mControls->clear();
+ self->mCurrentURL->clear();
+ self->mFirstClick->clear();
+ self->mHeightPixels->clear();
+ self->mHomeURL->clear();
+ self->mWidthPixels->clear();
+ self->mAltImageEnable ->setEnabled(editable);
+ self->mAutoLoop ->setEnabled(editable);
+ self->mAutoPlay ->setEnabled(editable);
+ self->mAutoScale ->setEnabled(editable);
+ self->mAutoZoom ->setEnabled(editable);
+ self->mControls ->setEnabled(editable);
+ self->mCurrentURL ->setEnabled(editable);
+ self->mFirstClick ->setEnabled(editable);
+ self->mHeightPixels ->setEnabled(editable);
+ self->mHomeURL ->setEnabled(editable);
+ self->mWidthPixels ->setEnabled(editable);
+ self->mPreviewMedia->unloadMediaSource();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static
+void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& media_settings ,bool editable)
+{
+ LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata;
+ self->mMediaEditable = editable;
+
+ //llinfos << "---------------" << llendl;
+ //llinfos << ll_pretty_print_sd(media_settings) << llendl;
+ //llinfos << "---------------" << llendl;
+
+ // IF all the faces have media (or all dont have media)
+ if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo )
+ {
+ if(LLFloaterMediaSettings::getInstance()->mMultipleMedia)
+ {
+ self->clearValues(self, self->mMediaEditable);
+ // only show multiple
+ self->mHomeURL ->setText(LLTrans::getString("Multiple Media"));
+ return;
+ }
+
+ }
+ else
+ {
+ if(LLFloaterMediaSettings::getInstance()->mMultipleValidMedia)
+ {
+ self->clearValues(self, self->mMediaEditable);
+ // only show multiple
+ self->mHomeURL ->setText(LLTrans::getString("Multiple Media"));
+ return;
+ }
+
+ }
+ std::string base_key( "" );
+ std::string tentative_key( "" );
+
+ struct
+ {
+ std::string key_name;
+ LLUICtrl* ctrl_ptr;
+ std::string ctrl_type;
+
+ } data_set [] =
+ {
+ { LLMediaEntry::AUTO_LOOP_KEY, self->mAutoLoop, "LLCheckBoxCtrl" },
+ { LLMediaEntry::AUTO_PLAY_KEY, self->mAutoPlay, "LLCheckBoxCtrl" },
+ { LLMediaEntry::AUTO_SCALE_KEY, self->mAutoScale, "LLCheckBoxCtrl" },
+ { LLMediaEntry::AUTO_ZOOM_KEY, self->mAutoZoom, "LLCheckBoxCtrl" },
+ { LLMediaEntry::CONTROLS_KEY, self->mControls, "LLComboBox" },
+ { LLMediaEntry::CURRENT_URL_KEY, self->mCurrentURL, "LLLineEditor" },
+ { LLMediaEntry::HEIGHT_PIXELS_KEY, self->mHeightPixels, "LLSpinCtrl" },
+ { LLMediaEntry::HOME_URL_KEY, self->mHomeURL, "LLLineEditor" },
+ { LLMediaEntry::FIRST_CLICK_INTERACT_KEY, self->mFirstClick, "LLCheckBoxCtrl" },
+ { LLMediaEntry::WIDTH_PIXELS_KEY, self->mWidthPixels, "LLSpinCtrl" },
+ { LLMediaEntry::ALT_IMAGE_ENABLE_KEY, self->mAltImageEnable, "LLCheckBoxCtrl" },
+ { "", NULL , "" }
+ };
+
+ for( int i = 0; data_set[ i ].key_name.length() > 0; ++i )
+ {
+ base_key = std::string( data_set[ i ].key_name );
+ tentative_key = base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX );
+ // TODO: CP - I bet there is a better way to do this using Boost
+ if ( media_settings[ base_key ].isDefined() )
+ {
+ if ( data_set[ i ].ctrl_type == "LLLineEditor" )
+ {
+ static_cast< LLLineEditor* >( data_set[ i ].ctrl_ptr )->
+ setText( media_settings[ base_key ].asString() );
+ }
+ else
+ if ( data_set[ i ].ctrl_type == "LLCheckBoxCtrl" )
+ static_cast< LLCheckBoxCtrl* >( data_set[ i ].ctrl_ptr )->
+ setValue( media_settings[ base_key ].asBoolean() );
+ else
+ if ( data_set[ i ].ctrl_type == "LLComboBox" )
+ static_cast< LLComboBox* >( data_set[ i ].ctrl_ptr )->
+ setCurrentByIndex( media_settings[ base_key ].asInteger() );
+ else
+ if ( data_set[ i ].ctrl_type == "LLSpinCtrl" )
+ static_cast< LLSpinCtrl* >( data_set[ i ].ctrl_ptr )->
+ setValue( media_settings[ base_key ].asInteger() );
+
+ data_set[ i ].ctrl_ptr->setEnabled(self->mMediaEditable);
+ data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() );
+ };
+ };
+
+ // interrogates controls and updates widgets as required
+ self->updateMediaPreview();
+ self->updateCurrentURL();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Helper to set media control to media URL as required
+void LLPanelMediaSettingsGeneral::updateMediaPreview()
+{
+ if ( mHomeURL->getValue().asString().length() > 0 )
+ {
+ mPreviewMedia->navigateTo( mHomeURL->getValue().asString() );
+ }
+ else
+ // new home URL will be empty if media is deleted but
+ // we still need to clean out the preview.
+ {
+ mPreviewMedia->unloadMediaSource();
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Helper to set current URL
+void LLPanelMediaSettingsGeneral::updateCurrentURL()
+{
+ if( mCurrentURL->getText().empty() )
+ {
+ childSetText( "current_url", mHomeURL->getText() );
+ }
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// virtual
+void LLPanelMediaSettingsGeneral::onClose(bool app_quitting)
+{
+ if(mPreviewMedia)
+ {
+ mPreviewMedia->unloadMediaSource();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static
+void LLPanelMediaSettingsGeneral::onCommitHomeURL( LLUICtrl* ctrl, void *userdata )
+{
+ LLPanelMediaSettingsGeneral* self =(LLPanelMediaSettingsGeneral *)userdata;
+
+ // check url user is trying to enter for home URL will pass whitelist
+ // and decline to accept it if it doesn't.
+ std::string home_url = self->mHomeURL->getValue().asString();
+ if ( ! self->mParent->passesWhiteList( home_url ) )
+ {
+ LLNotifications::instance().add("WhiteListInvalidatesHomeUrl");
+ return;
+ };
+
+ self->updateMediaPreview();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static
+void LLPanelMediaSettingsGeneral::onBtnResetCurrentUrl(LLUICtrl* ctrl, void *userdata)
+{
+ LLPanelMediaSettingsGeneral* self =(LLPanelMediaSettingsGeneral *)userdata;
+ self->navigateHomeSelectedFace();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static
+void LLPanelMediaSettingsGeneral::apply( void* userdata )
+{
+ LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata;
+ self->mHomeURL->onCommit();
+ // build LLSD Fragment
+ LLSD media_data_general;
+ self->getValues(media_data_general);
+
+ // this merges contents of LLSD passed in with what's there so this is ok
+ LLSelectMgr::getInstance()->selectionSetMediaData( media_data_general );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLPanelMediaSettingsGeneral::getValues( LLSD &fill_me_in )
+{
+ fill_me_in[LLMediaEntry::ALT_IMAGE_ENABLE_KEY] = mAltImageEnable->getValue();
+ fill_me_in[LLMediaEntry::AUTO_LOOP_KEY] = mAutoLoop->getValue();
+ fill_me_in[LLMediaEntry::AUTO_PLAY_KEY] = mAutoPlay->getValue();
+ fill_me_in[LLMediaEntry::AUTO_SCALE_KEY] = mAutoScale->getValue();
+ fill_me_in[LLMediaEntry::AUTO_ZOOM_KEY] = mAutoZoom->getValue();
+ fill_me_in[LLMediaEntry::CONTROLS_KEY] = mControls->getCurrentIndex();
+ fill_me_in[LLMediaEntry::CURRENT_URL_KEY] = mCurrentURL->getValue();
+ fill_me_in[LLMediaEntry::HEIGHT_PIXELS_KEY] = mHeightPixels->getValue();
+ fill_me_in[LLMediaEntry::HOME_URL_KEY] = mHomeURL->getValue();
+ fill_me_in[LLMediaEntry::FIRST_CLICK_INTERACT_KEY] = mFirstClick->getValue();
+ fill_me_in[LLMediaEntry::WIDTH_PIXELS_KEY] = mWidthPixels->getValue();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLPanelMediaSettingsGeneral::setParent( LLFloaterMediaSettings* parent )
+{
+ mParent = parent;
+};
+
+bool LLPanelMediaSettingsGeneral::navigateHomeSelectedFace()
+{
+ // HACK: This is directly referencing an impl name. BAD!
+ // This can be removed when we have a truly generic media browser that only
+ // builds an impl based on the type of url it is passed.
+ struct functor_navigate_media : public LLSelectedTEGetFunctor< bool>
+ {
+ bool get( LLViewerObject* object, S32 face )
+ {
+ if ( object )
+ if ( object->getTE(face) )
+ if ( object->getTE(face)->getMediaData() )
+ {
+ if(object->permModify())
+ {
+ viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(object->getTE(face)->getMediaData()->getMediaID());
+ if(media_impl)
+ {
+ media_impl->navigateHome();
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ } functor_navigate_media;
+
+ bool all_face_media_navigated = false;
+ LLObjectSelectionHandle selected_objects =LLSelectMgr::getInstance()->getSelection();
+ selected_objects->getSelectedTEValue( &functor_navigate_media, all_face_media_navigated );
+
+ return all_face_media_navigated;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+const std::string LLPanelMediaSettingsGeneral::getHomeUrl()
+{
+ return mHomeURL->getValue().asString();
+}
+
diff --git a/indra/newview/llpanelmediasettingsgeneral.h b/indra/newview/llpanelmediasettingsgeneral.h index 1b60909786..5eb42ffaf4 100644 --- a/indra/newview/llpanelmediasettingsgeneral.h +++ b/indra/newview/llpanelmediasettingsgeneral.h @@ -64,6 +64,8 @@ public: bool navigateHomeSelectedFace(); void updateMediaPreview(); void updateCurrentURL(); + + const std::string getHomeUrl(); protected: LLFloaterMediaSettings* mParent; @@ -76,7 +78,6 @@ private: LLComboBox* mControls; LLCheckBoxCtrl* mAutoLoop; LLCheckBoxCtrl* mFirstClick; -// LLTextureCtrl* mMediaPreview; LLCheckBoxCtrl* mAutoZoom; LLCheckBoxCtrl* mAutoPlay; LLCheckBoxCtrl* mAutoScale; diff --git a/indra/newview/llpanelmediasettingssecurity.cpp b/indra/newview/llpanelmediasettingssecurity.cpp index cea105d7de..f5607aa287 100644 --- a/indra/newview/llpanelmediasettingssecurity.cpp +++ b/indra/newview/llpanelmediasettingssecurity.cpp @@ -1,255 +1,343 @@ -/** - * @file llpanelmediasettingssecurity.cpp - * @brief LLPanelMediaSettingsSecurity class implementation - * - * $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 "llfloaterreg.h" -#include "llpanelmediasettingssecurity.h" -#include "llpanelcontents.h" -#include "llcheckboxctrl.h" -#include "llscrolllistctrl.h" -#include "llscrolllistitem.h" -#include "lluictrlfactory.h" -#include "llwindow.h" -#include "llviewerwindow.h" -#include "llsdutil.h" -#include "llselectmgr.h" -#include "llmediaentry.h" -#include "llfloaterwhitelistentry.h" -#include "llfloatermediasettings.h" -//////////////////////////////////////////////////////////////////////////////// -// -LLPanelMediaSettingsSecurity::LLPanelMediaSettingsSecurity() -{ - // build dialog from XML - LLUICtrlFactory::getInstance()->buildPanel(this, "panel_media_settings_security.xml"); - mCommitCallbackRegistrar.add("Media.whitelistAdd", boost::bind(&LLPanelMediaSettingsSecurity::onBtnAdd, this)); - mCommitCallbackRegistrar.add("Media.whitelistDelete", boost::bind(&LLPanelMediaSettingsSecurity::onBtnDel, this)); -} - -//////////////////////////////////////////////////////////////////////////////// -// -BOOL LLPanelMediaSettingsSecurity::postBuild() -{ - mEnableWhiteList = getChild< LLCheckBoxCtrl >( LLMediaEntry::WHITELIST_ENABLE_KEY ); - mWhiteListList = getChild< LLScrollListCtrl >( LLMediaEntry::WHITELIST_KEY ); - - childSetAction("whitelist_add", onBtnAdd, this); - childSetAction("whitelist_del", onBtnDel, this); - - setDefaultBtn("whitelist_add"); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -// virtual -LLPanelMediaSettingsSecurity::~LLPanelMediaSettingsSecurity() -{ -} - -//////////////////////////////////////////////////////////////////////////////// -// -void LLPanelMediaSettingsSecurity::draw() -{ - // housekeeping - LLPanel::draw(); - - // if list is empty, disable DEL button and checkbox to enable use of list - if ( mWhiteListList->isEmpty() ) - { - childSetEnabled( "whitelist_del", false ); - childSetEnabled( LLMediaEntry::WHITELIST_KEY, false ); - childSetEnabled( LLMediaEntry::WHITELIST_ENABLE_KEY, false ); - } - else - { - childSetEnabled( "whitelist_del", true ); - childSetEnabled( LLMediaEntry::WHITELIST_KEY, true ); - childSetEnabled( LLMediaEntry::WHITELIST_ENABLE_KEY, true ); - }; - - // if nothing is selected, disable DEL button - if ( mWhiteListList->getSelectedValue().asString().empty() ) - { - childSetEnabled( "whitelist_del", false ); - } - else - { - childSetEnabled( "whitelist_del", true ); - }; -} - -//////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsSecurity::initValues( void* userdata, const LLSD& media_settings , bool editable) -{ - LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; - - if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo ) - { - if(LLFloaterMediaSettings::getInstance()->mMultipleMedia) - { - self->clearValues(self, editable); - // only show multiple - return; - } - - } - else - { - if(LLFloaterMediaSettings::getInstance()->mMultipleValidMedia) - { - self->clearValues(self, editable); - // only show multiple - return; - } - - } - std::string base_key( "" ); - std::string tentative_key( "" ); - - struct - { - std::string key_name; - LLUICtrl* ctrl_ptr; - std::string ctrl_type; - - } data_set [] = +/**
+ * @file llpanelmediasettingssecurity.cpp
+ * @brief LLPanelMediaSettingsSecurity class implementation
+ *
+ * $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 "llfloaterreg.h"
+#include "llpanelmediasettingssecurity.h"
+#include "llpanelcontents.h"
+#include "llcheckboxctrl.h"
+#include "llscrolllistctrl.h"
+#include "llscrolllistitem.h"
+#include "lluictrlfactory.h"
+#include "llwindow.h"
+#include "llviewerwindow.h"
+#include "llsdutil.h"
+#include "llselectmgr.h"
+#include "llmediaentry.h"
+#include "llfloaterwhitelistentry.h"
+#include "llfloatermediasettings.h"
+////////////////////////////////////////////////////////////////////////////////
+//
+LLPanelMediaSettingsSecurity::LLPanelMediaSettingsSecurity() :
+ mParent( NULL )
+{
+ // build dialog from XML
+ LLUICtrlFactory::getInstance()->buildPanel(this, "panel_media_settings_security.xml");
+ mCommitCallbackRegistrar.add("Media.whitelistAdd", boost::bind(&LLPanelMediaSettingsSecurity::onBtnAdd, this));
+ mCommitCallbackRegistrar.add("Media.whitelistDelete", boost::bind(&LLPanelMediaSettingsSecurity::onBtnDel, this));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+BOOL LLPanelMediaSettingsSecurity::postBuild()
+{
+ mEnableWhiteList = getChild< LLCheckBoxCtrl >( LLMediaEntry::WHITELIST_ENABLE_KEY );
+ mWhiteListList = getChild< LLScrollListCtrl >( LLMediaEntry::WHITELIST_KEY );
+
+ childSetAction("whitelist_add", onBtnAdd, this);
+ childSetAction("whitelist_del", onBtnDel, this);
+
+ setDefaultBtn("whitelist_add");
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// virtual
+LLPanelMediaSettingsSecurity::~LLPanelMediaSettingsSecurity()
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLPanelMediaSettingsSecurity::draw()
+{
+ // housekeeping
+ LLPanel::draw();
+
+ // if list is empty, disable DEL button and checkbox to enable use of list
+ if ( mWhiteListList->isEmpty() )
+ {
+ childSetEnabled( "whitelist_del", false );
+ childSetEnabled( LLMediaEntry::WHITELIST_KEY, false );
+ childSetEnabled( LLMediaEntry::WHITELIST_ENABLE_KEY, false );
+ }
+ else
+ {
+ childSetEnabled( "whitelist_del", true );
+ childSetEnabled( LLMediaEntry::WHITELIST_KEY, true );
+ childSetEnabled( LLMediaEntry::WHITELIST_ENABLE_KEY, true );
+ };
+
+ // if nothing is selected, disable DEL button
+ if ( mWhiteListList->getSelectedValue().asString().empty() )
+ {
+ childSetEnabled( "whitelist_del", false );
+ }
+ else
+ {
+ childSetEnabled( "whitelist_del", true );
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static
+void LLPanelMediaSettingsSecurity::initValues( void* userdata, const LLSD& media_settings , bool editable)
+{
+ LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata;
+
+ if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo )
+ {
+ if(LLFloaterMediaSettings::getInstance()->mMultipleMedia)
+ {
+ self->clearValues(self, editable);
+ // only show multiple
+ return;
+ }
+
+ }
+ else
+ {
+ if(LLFloaterMediaSettings::getInstance()->mMultipleValidMedia)
+ {
+ self->clearValues(self, editable);
+ // only show multiple
+ return;
+ }
+
+ }
+ std::string base_key( "" );
+ std::string tentative_key( "" );
+
+ struct
+ {
+ std::string key_name;
+ LLUICtrl* ctrl_ptr;
+ std::string ctrl_type;
+
+ } data_set [] =
+ {
+ { LLMediaEntry::WHITELIST_ENABLE_KEY, self->mEnableWhiteList, "LLCheckBoxCtrl" },
+ { LLMediaEntry::WHITELIST_KEY, self->mWhiteListList, "LLScrollListCtrl" },
+ { "", NULL , "" }
+ };
+
+ for( int i = 0; data_set[ i ].key_name.length() > 0; ++i )
+ {
+ base_key = std::string( data_set[ i ].key_name );
+ tentative_key = base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX );
+
+ // TODO: CP - I bet there is a better way to do this using Boost
+ if ( media_settings[ base_key ].isDefined() )
+ {
+ if ( data_set[ i ].ctrl_type == "LLCheckBoxCtrl" )
+ {
+ static_cast< LLCheckBoxCtrl* >( data_set[ i ].ctrl_ptr )->
+ setValue( media_settings[ base_key ].asBoolean() );
+ }
+ else
+ if ( data_set[ i ].ctrl_type == "LLScrollListCtrl" )
+ {
+ // get control
+ LLScrollListCtrl* list = static_cast< LLScrollListCtrl* >( data_set[ i ].ctrl_ptr );
+ list->deleteAllItems();
+
+ // points to list of white list URLs
+ LLSD url_list = media_settings[ base_key ];
+
+ // iterate over them and add to scroll list
+ LLSD::array_iterator iter = url_list.beginArray();
+ while( iter != url_list.endArray() )
+ {
+ // TODO: is iter guaranteed to be valid here?
+ std::string url = *iter;
+ list->addSimpleElement( url );
+ ++iter;
+ };
+ };
+ data_set[ i ].ctrl_ptr->setEnabled(editable);
+ data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() );
+ };
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static
+void LLPanelMediaSettingsSecurity::clearValues( void* userdata , bool editable)
+{
+ LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata;
+ self->mEnableWhiteList->clear();
+ self->mWhiteListList->deleteAllItems();
+ self->mEnableWhiteList->setEnabled(editable);
+ self->mWhiteListList->setEnabled(editable);
+}
+////////////////////////////////////////////////////////////////////////////////
+// static
+void LLPanelMediaSettingsSecurity::apply( void* userdata )
+{
+ LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata;
+
+ // build LLSD Fragment
+ LLSD media_data_security;
+ self->getValues(media_data_security);
+ // this merges contents of LLSD passed in with what's there so this is ok
+ LLSelectMgr::getInstance()->selectionSetMediaData( media_data_security );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLPanelMediaSettingsSecurity::getValues( LLSD &fill_me_in )
+{
+ fill_me_in[LLMediaEntry::WHITELIST_ENABLE_KEY] = mEnableWhiteList->getValue();
+
+ // iterate over white list and extract items
+ std::vector< LLScrollListItem* > white_list_items = mWhiteListList->getAllData();
+ std::vector< LLScrollListItem* >::iterator iter = white_list_items.begin();
+ fill_me_in[LLMediaEntry::WHITELIST_KEY].clear();
+ while( iter != white_list_items.end() )
+ {
+ std::string white_list_url = (*iter)->getValue().asString();
+ fill_me_in[ LLMediaEntry::WHITELIST_KEY ].append( white_list_url );
+ ++iter;
+ };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Try to make a valid URL if a fragment (
+// white list list box widget and build a list to test against. Can also
+const std::string LLPanelMediaSettingsSecurity::makeValidUrl( const std::string& src_url )
+{
+ // use LLURI to determine if we have a valid scheme
+ LLURI candidate_url( src_url ); + if ( candidate_url.scheme().empty() ) { - { LLMediaEntry::WHITELIST_ENABLE_KEY, self->mEnableWhiteList, "LLCheckBoxCtrl" }, - { LLMediaEntry::WHITELIST_KEY, self->mWhiteListList, "LLScrollListCtrl" }, - { "", NULL , "" } + // build a URL comprised of default scheme and the original fragment
+ const std::string default_scheme( "http://" );
+ return default_scheme + src_url;
}; - for( int i = 0; data_set[ i ].key_name.length() > 0; ++i ) - { - base_key = std::string( data_set[ i ].key_name ); - tentative_key = base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ); - - // TODO: CP - I bet there is a better way to do this using Boost - if ( media_settings[ base_key ].isDefined() ) - { - if ( data_set[ i ].ctrl_type == "LLCheckBoxCtrl" ) - { - static_cast< LLCheckBoxCtrl* >( data_set[ i ].ctrl_ptr )-> - setValue( media_settings[ base_key ].asBoolean() ); - } - else - if ( data_set[ i ].ctrl_type == "LLScrollListCtrl" ) - { - // get control - LLScrollListCtrl* list = static_cast< LLScrollListCtrl* >( data_set[ i ].ctrl_ptr ); - list->deleteAllItems(); - - // points to list of white list URLs - LLSD url_list = media_settings[ base_key ]; - - // iterate over them and add to scroll list - LLSD::array_iterator iter = url_list.beginArray(); - while( iter != url_list.endArray() ) - { - // TODO: is iter guaranteed to be valid here? - std::string url = *iter; - list->addSimpleElement( url ); - ++iter; - }; - }; - data_set[ i ].ctrl_ptr->setEnabled(editable); - data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() ); - }; - }; -} - -//////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsSecurity::clearValues( void* userdata , bool editable) -{ - LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; - self->mEnableWhiteList->clear(); - self->mWhiteListList->deleteAllItems(); - self->mEnableWhiteList->setEnabled(editable); - self->mWhiteListList->setEnabled(editable); -} -//////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsSecurity::apply( void* userdata ) -{ - LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; - - // build LLSD Fragment - LLSD media_data_security; - self->getValues(media_data_security); - // this merges contents of LLSD passed in with what's there so this is ok - LLSelectMgr::getInstance()->selectionSetMediaData( media_data_security ); -} - + // we *could* test the "default scheme" + "original fragment" URL again + // using LLURI to see if it's valid but I think the outcome is the same + // in either case - our only option is to return the original URL +
+ // we *think* the original url passed in was valid
+ return src_url;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// wrapper for testing a URL against the whitelist. We grab entries from
+// white list list box widget and build a list to test against. Can also
+// optionally pass the URL that you are trying to add to the widget since
+// it won't be added until this call returns.
+bool LLPanelMediaSettingsSecurity::passesWhiteList( const std::string& added_url,
+ const std::string& test_url )
+{
+ // the checkUrlAgainstWhitelist(..) function works on a vector
+ // of strings for the white list entries - in this panel, the white list
+ // is stored in the widgets themselves so we need to build something compatible.
+ std::vector< std::string > whitelist_strings;
+ whitelist_strings.clear(); // may not be required - I forget what the spec says.
+
+ // step through whitelist widget entries and grab them as strings
+ std::vector< LLScrollListItem* > white_list_items = mWhiteListList->getAllData();
+ std::vector< LLScrollListItem* >::iterator iter = white_list_items.begin();
+ while( iter != white_list_items.end() )
+ {
+ const std::string whitelist_url = (*iter)->getValue().asString();
+ whitelist_strings.push_back( whitelist_url );
+
+ ++iter;
+ };
+
+ // add in the URL that might be added to the whitelist so we can test that too
+ if ( added_url.length() )
+ whitelist_strings.push_back( added_url );
+
+ // possible the URL is just a fragment so we validize it
+ const std::string valid_url = makeValidUrl( test_url );
+
+ // indicate if the URL passes whitelist
+ return LLMediaEntry::checkUrlAgainstWhitelist( valid_url, whitelist_strings );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+void LLPanelMediaSettingsSecurity::addWhiteListItem(const std::string& url)
+{
+ // grab home URL from the general panel (via the parent floater)
+ std::string home_url( "" );
+ if ( mParent )
+ home_url = mParent->getHomeUrl();
+
+ // if the home URL is blank (user hasn't entered it yet) then
+ // don't bother to check if it passes the white list
+ if ( home_url.empty() )
+ {
+ mWhiteListList->addSimpleElement( url );
+ return;
+ };
+
+ // if the URL passes the white list, add it
+ if ( passesWhiteList( url, home_url ) )
+ {
+ mWhiteListList->addSimpleElement( url );
+ }
+ else
+ // display a message indicating you can't do that
+ {
+ LLNotifications::instance().add("WhiteListInvalidatesHomeUrl");
+ };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// static
+void LLPanelMediaSettingsSecurity::onBtnAdd( void* userdata )
+{
+ LLFloaterReg::showInstance("whitelist_entry");
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// static
+void LLPanelMediaSettingsSecurity::onBtnDel( void* userdata )
+{
+ LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata;
+
+ self->mWhiteListList->deleteSelectedItems();
+}
+
//////////////////////////////////////////////////////////////////////////////// // -void LLPanelMediaSettingsSecurity::getValues( LLSD &fill_me_in ) -{ - fill_me_in[LLMediaEntry::WHITELIST_ENABLE_KEY] = mEnableWhiteList->getValue(); - - // iterate over white list and extract items - std::vector< LLScrollListItem* > white_list_items = mWhiteListList->getAllData(); - std::vector< LLScrollListItem* >::iterator iter = white_list_items.begin(); - fill_me_in[LLMediaEntry::WHITELIST_KEY].clear(); - while( iter != white_list_items.end() ) - { - std::string white_list_url = (*iter)->getValue().asString(); - fill_me_in[ LLMediaEntry::WHITELIST_KEY ].append( white_list_url ); - ++iter; - }; -} - - -/////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsSecurity::addWhiteListItem(const std::string& url) -{ - mWhiteListList->addSimpleElement( url ); -} - -/////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsSecurity::onBtnAdd( void* userdata ) -{ - LLFloaterReg::showInstance("whitelist_entry"); -} - -/////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsSecurity::onBtnDel( void* userdata ) +void LLPanelMediaSettingsSecurity::setParent( LLFloaterMediaSettings* parent ) { - LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; + mParent = parent; +}; - self->mWhiteListList->deleteSelectedItems(); -} diff --git a/indra/newview/llpanelmediasettingssecurity.h b/indra/newview/llpanelmediasettingssecurity.h index b7cf67c039..b78ee92193 100644 --- a/indra/newview/llpanelmediasettingssecurity.h +++ b/indra/newview/llpanelmediasettingssecurity.h @@ -37,6 +37,7 @@ class LLCheckBoxCtrl; class LLScrollListCtrl; +class LLFloaterMediaSettings; class LLPanelMediaSettingsSecurity : public LLPanel { @@ -52,6 +53,12 @@ class LLPanelMediaSettingsSecurity : public LLPanel static void initValues( void* userdata, const LLSD& media_settings,bool editable ); static void clearValues( void* userdata, bool editable); void addWhiteListItem(const std::string& url); + void setParent( LLFloaterMediaSettings* parent ); + const std::string makeValidUrl( const std::string& src_url ); + bool passesWhiteList( const std::string& added_url, const std::string& test_url );
+ + protected: + LLFloaterMediaSettings* mParent; private: LLCheckBoxCtrl* mEnableWhiteList; 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 2372063fbd..6d1a5fb184 100644 --- a/indra/newview/llpanelplaceinfo.cpp +++ b/indra/newview/llpanelplaceinfo.cpp @@ -65,6 +65,7 @@ #include "llviewerregion.h" #include "llviewertexteditor.h" #include "llworldmap.h" +#include "llsdutil_math.h" //---------------------------------------------------------------------------- // Aux types and methods diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index a7f0ce16d3..d68897b64f 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -4905,7 +4905,6 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) { inspect_item_id = inspect_instance->getSelectedUUID(); } - LLUUID focus_item_id = LLViewerMediaFocus::getInstance()->getSelectedUUID(); for (S32 pass = 0; pass < 2; pass++) { for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); @@ -4919,11 +4918,7 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) { continue; } - if (objectp->getID() == focus_item_id) - { - node->renderOneSilhouette(gFocusMgr.getFocusColor()); - } - else if(objectp->getID() == inspect_item_id) + if(objectp->getID() == inspect_item_id) { node->renderOneSilhouette(sHighlightInspectColor); } @@ -4977,6 +4972,19 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) } } +#if 0 + // Hilight focused media object + { + LLViewerObject* objectp = LLViewerMediaFocus::getInstance()->getFocusedObject(); + if(objectp) + { + // FIXME: how do I construct a silhouette for an object that's not selected? + // Would we need to add another LLObjectSelectionHandle for this purpose? + node->renderOneSilhouette(gFocusMgr.getFocusColor()); + } + } +#endif + if (for_hud && avatar) { glMatrixMode(GL_PROJECTION); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index ac1d66157a..3d0e25a734 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" @@ -102,7 +102,6 @@ #include "llfloaterland.h" #include "llfloaterpreference.h" #include "llfloatertopobjects.h" -#include "llfloatertos.h" #include "llfloaterworldmap.h" #include "llgesturemgr.h" #include "llgroupmgr.h" @@ -115,6 +114,7 @@ #include "llfriendcard.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" @@ -132,7 +132,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 @@ -145,7 +144,6 @@ #include "llurlsimstring.h" #include "llurlhistory.h" #include "llurlwhitelist.h" -#include "lluserauth.h" #include "llvieweraudio.h" #include "llviewerassetstorage.h" #include "llviewercamera.h" @@ -189,6 +187,9 @@ #include "llappearancemgr.h" #include "llavatariconctrl.h" +#include "lllogin.h" +#include "llevents.h" + #if LL_WINDOWS #include "llwindebug.h" #include "lldxhardware.h" @@ -202,12 +203,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 // @@ -217,12 +218,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" @@ -231,6 +226,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 @@ -243,8 +250,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); @@ -254,6 +259,8 @@ 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 transition_back_to_login_panel(const std::string& emsg); void callback_cache_name(const LLUUID& id, const std::string& firstname, const std::string& lastname, BOOL is_group) { @@ -313,9 +320,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() { @@ -387,23 +391,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; @@ -411,7 +403,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 @@ -432,12 +424,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) { //note: Removing this line will cause incorrect button size in the login screen. -- bao. gTextureList.updateImages(0.01f) ; @@ -753,24 +740,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; @@ -781,9 +767,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 @@ -796,9 +782,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; } @@ -834,7 +820,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) { @@ -846,7 +832,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(); @@ -910,34 +896,34 @@ bool idle_startup() //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(); + gFirstname = gLoginHandler.getFirstName(); + gLastname = gLoginHandler.getLastName(); +// gWebLoginKey = 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. @@ -971,7 +957,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()); @@ -999,13 +985,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 ); @@ -1037,7 +1016,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; } @@ -1070,616 +1049,138 @@ 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"); - 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"; - + // Update progress status and the display loop. 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; 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") + emsg << "Login failed.\n"; + if(LLLoginInstance::getInstance()->authFailure()) + { + LL_INFOS("LLStartup") << "Login failed, LLLoginInstance::getResponse(): " + << LLLoginInstance::getInstance()->getResponse() << LL_ENDL; + // Still have error conditions that may need some + // sort of handling. + 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")); + emsg << big_reason; } - // 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; - } - } - } - 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()) - { - apply_udp_blacklist(text); } - // 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 == "key") { - // 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); + // Couldn't login because user/password is wrong + // Clear the password + gPassword = ""; } - text = LLUserAuth::getInstance()->getResponse("last_name"); - if(!text.empty()) lastname.assign(text); - gSavedSettings.setString("FirstName", firstname); - gSavedSettings.setString("LastName", lastname); - if (gSavedSettings.getBOOL("RememberPassword")) + if(reason_response == "update" + || reason_response == "optional") { - // 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 the base used to construct help URLs - text = LLUserAuth::getInstance()->getResponse("help_url_format"); - if (!text.empty()) - { - // replace the default help URL format - gSavedSettings.setString("HelpURLFormat",text); - - // don't fall back to Nebraska's pre-connection static help - gSavedSettings.setBOOL("HelpUseLocal", false); - } - - // 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) + // Don't pop up a notification in the TOS case because + // LLFloaterTOS::onCancel() already scolded the user. + if (reason_response != "tos") { - 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; + LLSD args; + args["ERROR_MESSAGE"] = emsg.str(); + LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; + LLNotifications::instance().add("ErrorMessage", args, LLSD(), login_alert_done); } //setup map of datetime strings to codes and slt & local time offset from utc LLStringOps::setupDatetimeInfo (gPacificDaylightTime); + transition_back_to_login_panel(emsg.str()); + show_connect_box = true; } - 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(); + LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; LLNotifications::instance().add("ErrorMessage", args, LLSD(), login_alert_done); - reset_login(); - gSavedSettings.setBOOL("AutoLogin", FALSE); + transition_back_to_login_panel(emsg.str()); show_connect_box = true; } - - // Pass the user information to the voice chat server interface. - gVoiceClient->userAuthorized(firstname, lastname, gAgentID); } - else // if(successful_login) + else { - if (gNoRender) - { - LL_WARNS("AppInit") << "Failed to login!" << 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; + // 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); } + return FALSE; } @@ -1739,14 +1240,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. @@ -1885,7 +1386,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(); @@ -1924,18 +1425,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, @@ -2046,105 +1547,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; } } } @@ -2156,19 +1651,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); } @@ -2232,7 +1730,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(); @@ -2280,34 +1778,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()) { @@ -2363,8 +1847,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 @@ -2577,8 +2061,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 ); @@ -2868,230 +2354,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) { @@ -3438,11 +2700,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 ); @@ -3462,14 +2720,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); } @@ -3583,3 +2844,277 @@ 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); + } + } + + // this is the base used to construct help URLs + text = response["help_url_format"].asString(); + if (!text.empty()) + { + // replace the default help URL format + gSavedSettings.setString("HelpURLFormat",text); + + // don't fall back to Nebraska's pre-connection static help + gSavedSettings.setBOOL("HelpUseLocal", false); + } + + 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; +} + +void transition_back_to_login_panel(const std::string& emsg) +{ + if (gNoRender) + { + LL_WARNS("AppInit") << "Failed to login!" << LL_ENDL; + LL_WARNS("AppInit") << emsg << LL_ENDL; + exit(0); + } + + // Bounce back to the login screen. + reset_login(); // calls LLStartUp::setStartupState( STATE_LOGIN_SHOW ); + gSavedSettings.setBOOL("AutoLogin", FALSE); +} 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/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index b015f668e4..b035fd53fd 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -518,11 +518,7 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) if(!object) { - // We need to clear media hover flag - if (LLViewerMediaFocus::getInstance()->getMouseOverFlag()) - { - LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); - } + LLViewerMediaFocus::getInstance()->clearHover(); } } @@ -1029,7 +1025,6 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick) pick.mObjectFace < 0 || pick.mObjectFace >= objectp->getNumTEs()) { - LLSelectMgr::getInstance()->deselect(); LLViewerMediaFocus::getInstance()->clearFocus(); return false; @@ -1037,11 +1032,7 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick) - // HACK: This is directly referencing an impl name. BAD! - // This can be removed when we have a truly generic media browser that only - // builds an impl based on the type of url it is passed. - - // is media playing on this face? + // Does this face have media? const LLTextureEntry* tep = objectp->getTE(pick.mObjectFace); LLMediaEntry* mep = (tep->hasMedia()) ? tep->getMediaData() : NULL; viewer_media_t media_impl = mep ? LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID()) : NULL; @@ -1051,11 +1042,9 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick) && gSavedSettings.getBOOL("MediaOnAPrimUI") && media_impl.notNull()) { - // LLObjectSelectionHandle selection = /*LLViewerMediaFocus::getInstance()->getSelection()*/ LLSelectMgr::getInstance()->getSelection(); - if (/*! selection->contains(pick.getObject(), pick.mObjectFace)*/ - ! LLViewerMediaFocus::getInstance()->isFocusedOnFace(pick.getObject(), pick.mObjectFace) ) + if (!LLViewerMediaFocus::getInstance()->isFocusedOnFace(pick.getObject(), pick.mObjectFace) ) { - LLViewerMediaFocus::getInstance()->setFocusFace(TRUE, pick.getObject(), pick.mObjectFace, media_impl); + LLViewerMediaFocus::getInstance()->setFocusFace(pick.getObject(), pick.mObjectFace, media_impl, pick.mNormal); } else { @@ -1067,7 +1056,6 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick) return true; } - LLSelectMgr::getInstance()->deselect(); LLViewerMediaFocus::getInstance()->clearFocus(); return false; @@ -1081,50 +1069,50 @@ bool LLToolPie::handleMediaHover(const LLPickInfo& pick) LLPointer<LLViewerObject> objectp = pick.getObject(); - // Early out cases. Must clear mouse over media focus flag + // Early out cases. Must clear media hover. // did not hit an object or did not hit a valid face if ( objectp.isNull() || pick.mObjectFace < 0 || pick.mObjectFace >= objectp->getNumTEs() ) { - LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); + LLViewerMediaFocus::getInstance()->clearHover(); return false; } - - // HACK: This is directly referencing an impl name. BAD! - // This can be removed when we have a truly generic media browser that only - // builds an impl based on the type of url it is passed. - - // is media playing on this face? + // Does this face have media? const LLTextureEntry* tep = objectp->getTE(pick.mObjectFace); const LLMediaEntry* mep = tep->hasMedia() ? tep->getMediaData() : NULL; if (mep && gSavedSettings.getBOOL("MediaOnAPrimUI")) { viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID()); - if(LLViewerMediaFocus::getInstance()->getFocus() && media_impl.notNull()) - { - media_impl->mouseMove(pick.mUVCoords); - - gViewerWindow->setCursor(media_impl->getLastSetCursor()); - } - else + + if(media_impl.notNull()) { - gViewerWindow->setCursor(UI_CURSOR_ARROW); - } + // Update media hover object + if (!LLViewerMediaFocus::getInstance()->isHoveringOverFace(objectp, pick.mObjectFace)) + { + LLViewerMediaFocus::getInstance()->setHoverFace(objectp, pick.mObjectFace, media_impl, pick.mNormal); + } + + // If this is the focused media face, send mouse move events. + if (LLViewerMediaFocus::getInstance()->isFocusedOnFace(objectp, pick.mObjectFace)) + { + media_impl->mouseMove(pick.mUVCoords); + gViewerWindow->setCursor(media_impl->getLastSetCursor()); + } + else + { + // This is not the focused face -- set the default cursor. + gViewerWindow->setCursor(UI_CURSOR_ARROW); + } - // Set mouse over flag if unset - if (! LLViewerMediaFocus::getInstance()->getMouseOverFlag()) - { - LLSelectMgr::getInstance()->setHoverObject(objectp, pick.mObjectFace); - LLViewerMediaFocus::getInstance()->setMouseOverFlag(true, media_impl); - LLViewerMediaFocus::getInstance()->setPickInfo(pick); + return true; } - - return true; } - LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); + + // In all other cases, clear media hover. + LLViewerMediaFocus::getInstance()->clearHover(); return false; } 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 b8e945a7b8..c9bfc1d296 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -588,7 +588,9 @@ LLViewerMediaImpl::LLViewerMediaImpl( const LLUUID& texture_id, mHasFocus(false), mPriority(LLPluginClassMedia::PRIORITY_UNLOADED), mDoNavigateOnLoad(false), + mDoNavigateOnLoadRediscoverType(false), mDoNavigateOnLoadServerRequest(false), + mMediaSourceFailedInit(false), mIsUpdated(false) { @@ -664,7 +666,7 @@ void LLViewerMediaImpl::createMediaSource() { if(! mMediaURL.empty()) { - navigateTo(mMediaURL, mMimeType, false, mDoNavigateOnLoadServerRequest); + navigateTo(mMediaURL, mMimeType, mDoNavigateOnLoadRediscoverType, mDoNavigateOnLoadServerRequest); } else if(! mMimeType.empty()) { @@ -703,7 +705,7 @@ void LLViewerMediaImpl::setMediaType(const std::string& media_type) LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height) { std::string plugin_basename = LLMIMETypes::implType(media_type); - + if(plugin_basename.empty()) { LL_WARNS("Media") << "Couldn't find plugin for media type " << media_type << LL_ENDL; @@ -774,6 +776,9 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) return false; } + // If we got here, we want to ignore previous init failures. + mMediaSourceFailedInit = false; + LLPluginClassMedia* media_source = newSourceFromMediaType(mMimeType, this, mMediaWidth, mMediaHeight); if (media_source) @@ -787,6 +792,9 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) return true; } + // Make sure the timer doesn't try re-initing this plugin repeatedly until something else changes. + mMediaSourceFailedInit = true; + return false; } @@ -828,7 +836,15 @@ void LLViewerMediaImpl::stop() { if(mMediaSource) { - mMediaSource->stop(); + if(mMediaSource->pluginSupportsMediaBrowser()) + { + mMediaSource->browse_stop(); + } + else + { + mMediaSource->stop(); + } + // destroyMediaSource(); } } @@ -1002,21 +1018,69 @@ BOOL LLViewerMediaImpl::handleMouseUp(S32 x, S32 y, MASK mask) } ////////////////////////////////////////////////////////////////////////////////////////// -void LLViewerMediaImpl::navigateHome() +void LLViewerMediaImpl::navigateBack() { - mMediaURL = mHomeURL; - mDoNavigateOnLoad = !mMediaURL.empty(); - mDoNavigateOnLoadServerRequest = false; - - if(mMediaSource) + if (mMediaSource) + { + if(mMediaSource->pluginSupportsMediaTime()) + { + F64 step_scale = 0.02; // temp , can be changed + F64 back_step = mMediaSource->getCurrentTime() - (mMediaSource->getDuration()*step_scale); + if(back_step < 0.0) + { + back_step = 0.0; + } + mMediaSource->seek(back_step); + //mMediaSource->start(-2.0); + } + else + { + mMediaSource->browse_back(); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateForward() +{ + if (mMediaSource) { - mMediaSource->loadURI( mHomeURL ); + if(mMediaSource->pluginSupportsMediaTime()) + { + F64 step_scale = 0.02; // temp , can be changed + F64 forward_step = mMediaSource->getCurrentTime() + (mMediaSource->getDuration()*step_scale); + if(forward_step > mMediaSource->getDuration()) + { + forward_step = mMediaSource->getDuration(); + } + mMediaSource->seek(forward_step); + //mMediaSource->start(2.0); + } + else + { + mMediaSource->browse_forward(); + } } } ////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateReload() +{ + navigateTo(mMediaURL, "", true, false); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateHome() +{ + navigateTo(mHomeURL, "", true, false); +} + +////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mime_type, bool rediscover_type, bool server_request) { + // Helpful to have media urls in log file. Shouldn't be spammy. + llinfos << "url=" << url << " mime_type=" << mime_type << llendl; + if(server_request) { setNavState(MEDIANAVSTATE_SERVER_SENT); @@ -1026,12 +1090,16 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi setNavState(MEDIANAVSTATE_NONE); } - // Always set the current URL. + // Always set the current URL and MIME type. mMediaURL = url; + mMimeType = mime_type; // If the current URL is not null, make the instance do a navigate on load. mDoNavigateOnLoad = !mMediaURL.empty(); + // if mime type discovery was requested, we'll need to do it when the media loads + mDoNavigateOnLoadRediscoverType = rediscover_type; + // and if this was a server request, the navigate on load will also need to be one. mDoNavigateOnLoadServerRequest = server_request; @@ -1042,6 +1110,21 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi return; } + + // If the caller has specified a non-empty MIME type, look that up in our MIME types list. + // If we have a plugin for that MIME type, use that instead of attempting auto-discovery. + // This helps in supporting legacy media content where the server the media resides on returns a bogus MIME type + // but the parcel owner has correctly set the MIME type in the parcel media settings. + + if(!mMimeType.empty() && (mMimeType != "none/none")) + { + std::string plugin_basename = LLMIMETypes::implType(mMimeType); + if(!plugin_basename.empty()) + { + // We have a plugin for this mime type + rediscover_type = false; + } + } if(rediscover_type) { @@ -1101,6 +1184,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) { @@ -1148,7 +1236,7 @@ bool LLViewerMediaImpl::canNavigateBack() ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::update() { - if(mMediaSource == NULL) + if(mMediaSource == NULL && !mMediaSourceFailedInit) { if(mPriority != LLPluginClassMedia::PRIORITY_UNLOADED) { @@ -1375,6 +1463,18 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla { switch(event) { + case MEDIA_EVENT_PLUGIN_FAILED_LAUNCH: + { + // The plugin failed to load properly. Make sure the timer doesn't retry. + mMediaSourceFailedInit = true; + + // TODO: may want a different message for this case? + LLSD args; + args["PLUGIN"] = LLMIMETypes::implType(mMimeType); + LLNotifications::instance().add("MediaPluginFailed", args); + } + break; + case MEDIA_EVENT_PLUGIN_FAILED: { LLSD args; @@ -1423,7 +1523,19 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla case LLViewerMediaObserver::MEDIA_EVENT_NAVIGATE_COMPLETE: { LL_DEBUGS("Media") << "MEDIA_EVENT_NAVIGATE_COMPLETE, uri is: " << plugin->getNavigateURI() << LL_ENDL; - setNavState(MEDIANAVSTATE_NONE); + + if(getNavState() == MEDIANAVSTATE_BEGUN) + { + setNavState(MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED); + } + else if(getNavState() == MEDIANAVSTATE_SERVER_BEGUN) + { + setNavState(MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED); + } + else + { + // all other cases need to leave the state alone. + } } break; @@ -1626,9 +1738,11 @@ void LLViewerMediaImpl::setNavState(EMediaNavState state) case MEDIANAVSTATE_NONE: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_NONE" << llendl; break; case MEDIANAVSTATE_BEGUN: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_BEGUN" << llendl; break; case MEDIANAVSTATE_FIRST_LOCATION_CHANGED: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_FIRST_LOCATION_CHANGED" << llendl; break; + case MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED" << llendl; break; case MEDIANAVSTATE_SERVER_SENT: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_SERVER_SENT" << llendl; break; case MEDIANAVSTATE_SERVER_BEGUN: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_SERVER_BEGUN" << llendl; break; case MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED" << llendl; break; + case MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED" << llendl; break; } } diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index c9ba5841e9..fc2776ee91 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -141,7 +141,10 @@ public: void mouseMove(const LLVector2& texture_coords); void mouseLeftDoubleClick(S32 x,S32 y ); void mouseCapture(); - + + void navigateBack(); + void navigateForward(); + void navigateReload(); void navigateHome(); void navigateTo(const std::string& url, const std::string& mime_type = "", bool rediscover_type = false, bool server_request = false); void navigateStop(); @@ -240,12 +243,14 @@ public: typedef enum { - MEDIANAVSTATE_NONE, // State is outside what we need to track for navigation. - MEDIANAVSTATE_BEGUN, // a MEDIA_EVENT_NAVIGATE_BEGIN has been received which was not server-directed - MEDIANAVSTATE_FIRST_LOCATION_CHANGED, // first LOCATION_CHANGED event after a non-server-directed BEGIN - MEDIANAVSTATE_SERVER_SENT, // server-directed nav has been requested, but MEDIA_EVENT_NAVIGATE_BEGIN hasn't been received yet - MEDIANAVSTATE_SERVER_BEGUN, // MEDIA_EVENT_NAVIGATE_BEGIN has been received which was server-directed - MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED // first LOCATION_CHANGED event after a server-directed BEGIN + MEDIANAVSTATE_NONE, // State is outside what we need to track for navigation. + MEDIANAVSTATE_BEGUN, // a MEDIA_EVENT_NAVIGATE_BEGIN has been received which was not server-directed + MEDIANAVSTATE_FIRST_LOCATION_CHANGED, // first LOCATION_CHANGED event after a non-server-directed BEGIN + MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED, // we received a NAVIGATE_COMPLETE event before the first LOCATION_CHANGED + MEDIANAVSTATE_SERVER_SENT, // server-directed nav has been requested, but MEDIA_EVENT_NAVIGATE_BEGIN hasn't been received yet + MEDIANAVSTATE_SERVER_BEGUN, // MEDIA_EVENT_NAVIGATE_BEGIN has been received which was server-directed + MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED, // first LOCATION_CHANGED event after a server-directed BEGIN + MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED // we received a NAVIGATE_COMPLETE event before the first LOCATION_CHANGED }EMediaNavState; @@ -278,7 +283,9 @@ public: bool mHasFocus; LLPluginClassMedia::EPriority mPriority; bool mDoNavigateOnLoad; + bool mDoNavigateOnLoadRediscoverType; bool mDoNavigateOnLoadServerRequest; + bool mMediaSourceFailedInit; private: diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp index f9377ab37b..cad8b5f0ce 100644 --- a/indra/newview/llviewermediafocus.cpp +++ b/indra/newview/llviewermediafocus.cpp @@ -53,7 +53,8 @@ // LLViewerMediaFocus::LLViewerMediaFocus() -: mMouseOverFlag(false) +: mFocusedObjectFace(0), + mHoverObjectFace(0) { } @@ -63,110 +64,100 @@ LLViewerMediaFocus::~LLViewerMediaFocus() // Clean up in cleanupClass() instead. } -void LLViewerMediaFocus::cleanupClass() -{ - LLViewerMediaFocus *self = LLViewerMediaFocus::getInstance(); - - if(self) - { - // mMediaHUD will have been deleted by this point -- don't try to delete it. - - /* Richard says: - all widgets are supposed to be destroyed at the same time - you shouldn't hold on to pointer to them outside of ui code - you can use the LLHandle approach - if you want to be type safe, you'll need to add a LLRootHandle to whatever derived class you are pointing to - look at llview::gethandle - its our version of a weak pointer - */ - if(self->mMediaHUD.get()) - { - self->mMediaHUD.get()->setMediaImpl(NULL); - } - self->mMediaImpl = NULL; - } - -} - - -void LLViewerMediaFocus::setFocusFace( BOOL b, LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl ) -{ +void LLViewerMediaFocus::setFocusFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) +{ LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - - if(mMediaImpl.notNull()) + + LLViewerMediaImpl *old_media_impl = getFocusedMediaImpl(); + if(old_media_impl) { - mMediaImpl->focus(false); + old_media_impl->focus(false); } - if (b && media_impl.notNull()) + if (media_impl.notNull() && objectp.notNull()) { bool face_auto_zoom = false; - mMediaImpl = media_impl; - mMediaImpl->focus(true); + media_impl->focus(true); - LLSelectMgr::getInstance()->deselectAll(); - LLSelectMgr::getInstance()->selectObjectOnly(objectp, face); + mFocusedImplID = media_impl->getMediaTextureID(); + mFocusedObjectID = objectp->getID(); + mFocusedObjectFace = face; + mFocusedObjectNormal = pick_normal; - if(objectp.notNull()) + LLTextureEntry* tep = objectp->getTE(face); + if(tep->hasMedia()) { - LLTextureEntry* tep = objectp->getTE(face); - if(! tep->hasMedia()) - { - // Error condition - } LLMediaEntry* mep = tep->getMediaData(); face_auto_zoom = mep->getAutoZoom(); - if(! mep->getAutoPlay()) + if(!media_impl->hasMedia()) { std::string url = mep->getCurrentURL().empty() ? mep->getHomeURL() : mep->getCurrentURL(); media_impl->navigateTo(url, "", true); } } - mFocus = LLSelectMgr::getInstance()->getSelection(); + else + { + // This should never happen. + llwarns << "Can't find media entry for focused face" << llendl; + } + + gFocusMgr.setKeyboardFocus(this); + + // We must do this before processing the media HUD zoom, or it may zoom to the wrong face. + update(); + if(mMediaHUD.get() && face_auto_zoom && ! parcel->getMediaPreventCameraZoom()) { mMediaHUD.get()->resetZoomLevel(); mMediaHUD.get()->nextZoomLevel(); } - if (!mFocus->isEmpty()) - { - gFocusMgr.setKeyboardFocus(this); - } - mObjectID = objectp->getID(); - mObjectFace = face; - // LLViewerMedia::addObserver(this, mObjectID); - - } else { - gFocusMgr.setKeyboardFocus(NULL); - if(! parcel->getMediaPreventCameraZoom()) + if(hasFocus()) { - if (!mFocus->isEmpty()) + if(mMediaHUD.get()) { - gAgent.setFocusOnAvatar(TRUE, ANIMATE); + mMediaHUD.get()->resetZoomLevel(); } + + gFocusMgr.setKeyboardFocus(NULL); } - mFocus = NULL; - // LLViewerMedia::remObserver(this, mObjectID); - // Null out the media hud media pointer - if(mMediaHUD.get()) - { - mMediaHUD.get()->setMediaImpl(NULL); - } + mFocusedImplID = LLUUID::null; + mFocusedObjectID = LLUUID::null; + mFocusedObjectFace = 0; + } +} - // and null out the media impl - mMediaImpl = NULL; - mObjectID = LLUUID::null; - mObjectFace = 0; +void LLViewerMediaFocus::clearFocus() +{ + setFocusFace(NULL, 0, NULL); +} + +void LLViewerMediaFocus::setHoverFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) +{ + if (media_impl.notNull()) + { + mHoverImplID = media_impl->getMediaTextureID(); + mHoverObjectID = objectp->getID(); + mHoverObjectFace = face; + mHoverObjectNormal = pick_normal; } - if(mMediaHUD.get()) + else { - mMediaHUD.get()->setMediaFocus(b); + mHoverObjectID = LLUUID::null; + mHoverObjectFace = 0; + mHoverImplID = LLUUID::null; } } + +void LLViewerMediaFocus::clearHover() +{ + setHoverFace(NULL, 0, NULL); +} + + bool LLViewerMediaFocus::getFocus() { if (gFocusMgr.getKeyboardFocus() == this) @@ -176,22 +167,15 @@ bool LLViewerMediaFocus::getFocus() return false; } -// This function selects an ideal viewing distance given a selection bounding box, normal, and padding value -void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) +// This function selects an ideal viewing distance based on the focused object, pick normal, and padding value +void LLViewerMediaFocus::setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor) { - LLPickInfo& pick = LLToolPie::getInstance()->getPick(); - - if(LLSelectMgr::getInstance()->getSelection()->isEmpty()) - { - pick = mPickInfo; - setFocusFace(true, pick.getObject(), pick.mObjectFace, mMediaImpl); - } - - if (!LLSelectMgr::getInstance()->getSelection()->isEmpty()) + if (object) { gAgent.setFocusOnAvatar(FALSE, ANIMATE); - LLBBox selection_bbox = LLSelectMgr::getInstance()->getBBoxOfSelection(); + LLBBox bbox = object->getBoundingBoxAgent(); + LLVector3d center = gAgent.getPosGlobalFromAgent(bbox.getCenterAgent()); F32 height; F32 width; F32 depth; @@ -199,7 +183,7 @@ void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) F32 distance; // We need the aspect ratio, and the 3 components of the bbox as height, width, and depth. - F32 aspect_ratio = getBBoxAspectRatio(selection_bbox, pick.mNormal, &height, &width, &depth); + F32 aspect_ratio = getBBoxAspectRatio(bbox, normal, &height, &width, &depth); F32 camera_aspect = LLViewerCamera::getInstance()->getAspect(); // We will normally use the side of the volume aligned with the short side of the screen (i.e. the height for @@ -227,84 +211,97 @@ void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) distance += depth * 0.5; // Finally animate the camera to this new position and focal point - gAgent.setCameraPosAndFocusGlobal(LLSelectMgr::getInstance()->getSelectionCenterGlobal() + LLVector3d(pick.mNormal * distance), - LLSelectMgr::getInstance()->getSelectionCenterGlobal(), LLSelectMgr::getInstance()->getSelection()->getFirstObject()->mID ); + LLVector3d camera_pos, target_pos; + // The target lookat position is the center of the selection (in global coords) + target_pos = center; + // Target look-from (camera) position is "distance" away from the target along the normal + LLVector3d pickNormal = LLVector3d(normal); + pickNormal.normalize(); + camera_pos = target_pos + pickNormal * distance; + if (pickNormal == LLVector3d::z_axis || pickNormal == LLVector3d::z_axis_neg) + { + // If the normal points directly up, the camera will "flip" around. + // We try to avoid this by adjusting the target camera position a + // smidge towards current camera position + // *NOTE: this solution is not perfect. All it attempts to solve is the + // "looking down" problem where the camera flips around when it animates + // to that position. You still are not guaranteed to be looking at the + // media in the correct orientation. What this solution does is it will + // put the camera into position keeping as best it can the current + // orientation with respect to the face. In other words, if before zoom + // the media appears "upside down" from the camera, after zooming it will + // still be upside down, but at least it will not flip. + LLVector3d cur_camera_pos = LLVector3d(gAgent.getCameraPositionGlobal()); + LLVector3d delta = (cur_camera_pos - camera_pos); + F64 len = delta.length(); + delta.normalize(); + // Move 1% of the distance towards original camera location + camera_pos += 0.01 * len * delta; + } + + gAgent.setCameraPosAndFocusGlobal(camera_pos, target_pos, object->getID() ); + } + else + { + // If we have no object, focus back on the avatar. + gAgent.setFocusOnAvatar(TRUE, ANIMATE); } } void LLViewerMediaFocus::onFocusReceived() { - if(mMediaImpl.notNull()) - mMediaImpl->focus(true); + // Don't do this here -- this doesn't change "inworld media focus", it just changes whether the viewer's input is focused on the media. +// LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); +// if(media_impl.notNull()) +// media_impl->focus(true); LLFocusableElement::onFocusReceived(); } void LLViewerMediaFocus::onFocusLost() { - if(mMediaImpl.notNull()) - mMediaImpl->focus(false); + // Don't do this here -- this doesn't change "inworld media focus", it just changes whether the viewer's input is focused on the media. +// LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); +// if(media_impl.notNull()) +// media_impl->focus(false); + gViewerWindow->focusClient(); - mFocus = NULL; LLFocusableElement::onFocusLost(); } -void LLViewerMediaFocus::setMouseOverFlag(bool b, viewer_media_t media_impl) + +BOOL LLViewerMediaFocus::handleKey(KEY key, MASK mask, BOOL called_from_parent) { - if (b && media_impl.notNull()) + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl) { - if(! mMediaHUD.get()) - { - LLPanelMediaHUD* media_hud = new LLPanelMediaHUD(mMediaImpl); - mMediaHUD = media_hud->getHandle(); - gHUDView->addChild(media_hud); - } - mMediaHUD.get()->setMediaImpl(media_impl); + media_impl->handleKeyHere(key, mask); - if(mMediaImpl.notNull() && (mMediaImpl != media_impl)) + if (key == KEY_ESCAPE) { - mMediaImpl->focus(false); + clearFocus(); } - - mMediaImpl = media_impl; } - mMouseOverFlag = b; -} -LLUUID LLViewerMediaFocus::getSelectedUUID() -{ - LLViewerObject* object = mFocus->getFirstObject(); - return object ? object->getID() : LLUUID::null; -} -#if 0 // Must re-implement when the new media api event system is ready -void LLViewerMediaFocus::onNavigateComplete( const EventType& event_in ) -{ - if (hasFocus() && mLastURL != event_in.getStringValue()) - { - LLViewerMedia::focus(true, mObjectID); - // spoof mouse event to reassert focus - LLViewerMedia::mouseDown(1,1, mObjectID); - LLViewerMedia::mouseUp(1,1, mObjectID); - } - mLastURL = event_in.getStringValue(); -} -#endif -BOOL LLViewerMediaFocus::handleKey(KEY key, MASK mask, BOOL called_from_parent) -{ - if(mMediaImpl.notNull()) - mMediaImpl->handleKeyHere(key, mask); + return true; } BOOL LLViewerMediaFocus::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) { - if(mMediaImpl.notNull()) - mMediaImpl->handleUnicodeCharHere(uni_char); + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl) + media_impl->handleUnicodeCharHere(uni_char); return true; } BOOL LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks) { BOOL retval = FALSE; - if(mFocus.notNull() && mMediaImpl.notNull() && mMediaImpl->hasMedia()) + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl && media_impl->hasMedia()) { - mMediaImpl->getMediaPlugin()->scrollEvent(x, y, clicks); + // the scrollEvent() API's x and y are not the same as handleScrollWheel's x and y. + // The latter is the position of the mouse at the time of the event + // The former is the 'scroll amount' in x and y, respectively. + // All we have for 'scroll amount' here is 'clicks', and no mask. + media_impl->getMediaPlugin()->scrollEvent(0, clicks, /*mask*/0); retval = TRUE; } return retval; @@ -312,19 +309,45 @@ BOOL LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks) void LLViewerMediaFocus::update() { - if (mMediaHUD.get()) + LLViewerMediaImpl *media_impl = getFocusedMediaImpl(); + LLViewerObject *viewer_object = getFocusedObject(); + S32 face = mFocusedObjectFace; + LLVector3 normal = mFocusedObjectNormal; + bool focus = true; + + if(!media_impl || !viewer_object) { - if(mFocus.notNull() || mMouseOverFlag || mMediaHUD.get()->isMouseOver()) + focus = false; + media_impl = getHoverMediaImpl(); + viewer_object = getHoverObject(); + face = mHoverObjectFace; + normal = mHoverObjectNormal; + } + + if(media_impl && viewer_object) + { + // We have an object and impl to point at. + + // Make sure the media HUD object exists. + if(! mMediaHUD.get()) { - // mMediaHUD.get()->setVisible(true); - mMediaHUD.get()->updateShape(); + LLPanelMediaHUD* media_hud = new LLPanelMediaHUD(); + mMediaHUD = media_hud->getHandle(); + gHUDView->addChild(media_hud); } - else + mMediaHUD.get()->setMediaFace(viewer_object, face, media_impl, normal); + } + else + { + // The media HUD is no longer needed. + if(mMediaHUD.get()) { - mMediaHUD.get()->setVisible(false); + mMediaHUD.get()->setMediaFace(NULL, 0, NULL); } } } + + // This function calculates the aspect ratio and the world aligned components of a selection bounding box. F32 LLViewerMediaFocus::getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth) { @@ -393,5 +416,31 @@ F32 LLViewerMediaFocus::getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& bool LLViewerMediaFocus::isFocusedOnFace(LLPointer<LLViewerObject> objectp, S32 face) { - return objectp->getID() == mObjectID && face == mObjectFace; + return objectp->getID() == mFocusedObjectID && face == mFocusedObjectFace; +} + +bool LLViewerMediaFocus::isHoveringOverFace(LLPointer<LLViewerObject> objectp, S32 face) +{ + return objectp->getID() == mHoverObjectID && face == mHoverObjectFace; +} + + +LLViewerMediaImpl* LLViewerMediaFocus::getFocusedMediaImpl() +{ + return LLViewerMedia::getMediaImplFromTextureID(mFocusedImplID); +} + +LLViewerObject* LLViewerMediaFocus::getFocusedObject() +{ + return gObjectList.findObject(mFocusedObjectID); +} + +LLViewerMediaImpl* LLViewerMediaFocus::getHoverMediaImpl() +{ + return LLViewerMedia::getMediaImplFromTextureID(mHoverImplID); +} + +LLViewerObject* LLViewerMediaFocus::getHoverObject() +{ + return gObjectList.findObject(mHoverObjectID); } diff --git a/indra/newview/llviewermediafocus.h b/indra/newview/llviewermediafocus.h index 2688a8b708..c77533ba5a 100644 --- a/indra/newview/llviewermediafocus.h +++ b/indra/newview/llviewermediafocus.h @@ -50,44 +50,55 @@ public: LLViewerMediaFocus(); ~LLViewerMediaFocus(); - static void cleanupClass(); - - void setFocusFace(BOOL b, LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl); - void clearFocus() { setFocusFace(false, NULL, 0, NULL); } + // Set/clear the face that has media focus (takes keyboard input and has the full set of controls) + void setFocusFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3::zero); + void clearFocus(); + + // Set/clear the face that has "media hover" (has the mimimal set of controls to zoom in or pop out into a media browser). + // If a media face has focus, the media hover will be ignored. + void setHoverFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3::zero); + void clearHover(); + /*virtual*/ bool getFocus(); - /*virtual*/ // void onNavigateComplete( const EventType& event_in ); - /*virtual*/ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); /*virtual*/ BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - LLUUID getSelectedUUID(); - LLObjectSelectionHandle getSelection() { return mFocus; } - void update(); + + static void setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor); + static F32 getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth); - void setCameraZoom(F32 padding_factor); - void setMouseOverFlag(bool b, viewer_media_t media_impl = NULL); - bool getMouseOverFlag() { return mMouseOverFlag; } - void setPickInfo(LLPickInfo pick_info) { mPickInfo = pick_info; } - F32 getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth); - - // TODO: figure out why selection mgr hates me bool isFocusedOnFace(LLPointer<LLViewerObject> objectp, S32 face); + bool isHoveringOverFace(LLPointer<LLViewerObject> objectp, S32 face); + + // These look up (by uuid) and return the values that were set with setFocusFace. They will return null if the objects have been destroyed. + LLViewerMediaImpl* getFocusedMediaImpl(); + LLViewerObject* getFocusedObject(); + S32 getFocusedFace() { return mFocusedObjectFace; } + + // These look up (by uuid) and return the values that were set with setHoverFace. They will return null if the objects have been destroyed. + LLViewerMediaImpl* getHoverMediaImpl(); + LLViewerObject* getHoverObject(); + S32 getHoverFace() { return mHoverObjectFace; } protected: /*virtual*/ void onFocusReceived(); /*virtual*/ void onFocusLost(); private: - LLObjectSelectionHandle mFocus; - std::string mLastURL; - bool mMouseOverFlag; - LLPickInfo mPickInfo; + LLHandle<LLPanelMediaHUD> mMediaHUD; - LLUUID mObjectID; - S32 mObjectFace; - viewer_media_t mMediaImpl; + + LLUUID mFocusedObjectID; + S32 mFocusedObjectFace; + LLUUID mFocusedImplID; + LLVector3 mFocusedObjectNormal; + + LLUUID mHoverObjectID; + S32 mHoverObjectFace; + LLUUID mHoverImplID; + LLVector3 mHoverObjectNormal; }; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 806085dc24..d61de77f58 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -206,6 +206,7 @@ #include "llwaterparammanager.h" #include "llfloaternotificationsconsole.h" #include "llfloatercamera.h" +#include "lluilistener.h" #include "lltexlayer.h" #include "llappearancemgr.h" @@ -413,6 +414,8 @@ public: static LLMenuParcelObserver* gMenuParcelObserver = NULL; +static LLUIListener sUIListener("UI"); + LLMenuParcelObserver::LLMenuParcelObserver() { LLViewerParcelMgr::getInstance()->addObserver(this); @@ -7065,7 +7068,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/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp index a3f9c839a0..6233a337a6 100644 --- a/indra/newview/llviewerparcelmedia.cpp +++ b/indra/newview/llviewerparcelmedia.cpp @@ -226,11 +226,13 @@ void LLViewerParcelMedia::play(LLParcel* parcel) media_height, media_auto_scale, media_loop); - sMediaImpl->navigateTo(media_url); + sMediaImpl->navigateTo(media_url, mime_type, true); } } else { + LL_DEBUGS("Media") << "new media impl with mime type " << mime_type << ", url " << media_url << LL_ENDL; + // There is no media impl, make a new one sMediaImpl = LLViewerMedia::newMediaImpl( placeholder_texture_id, @@ -238,7 +240,7 @@ void LLViewerParcelMedia::play(LLParcel* parcel) media_height, media_auto_scale, media_loop); - sMediaImpl->navigateTo(media_url); + sMediaImpl->navigateTo(media_url, mime_type, true); } LLFirstUse::useMedia(); @@ -544,6 +546,18 @@ void LLViewerParcelMedia::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PLUGIN_FAILED" << LL_ENDL; }; break; + + case MEDIA_EVENT_PLUGIN_FAILED_LAUNCH: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PLUGIN_FAILED_LAUNCH" << LL_ENDL; + }; + break; + + case MEDIA_EVENT_NAME_CHANGED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAME_CHANGED" << LL_ENDL; + }; + break; }; } diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 7ca11d8364..44de848d19 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -56,6 +56,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 5f95e9ccf1..c659e58e47 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 @@ -1204,7 +1209,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")); @@ -1627,7 +1633,11 @@ void LLViewerWindow::shutdownViews() { gMorphView->setVisible(FALSE); } - + + // DEV-40930: Clear sModalStack. Otherwise, any LLModalDialog left open + // will crump with LL_ERRS. + LLModalDialog::shutdownModals(); + // Delete all child views. delete mRootView; mRootView = NULL; diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 231b857d1f..d7c403739e 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -53,6 +53,8 @@ #include <boost/function.hpp> #include <boost/signals2.hpp> +#include <boost/scoped_ptr.hpp> + class LLView; class LLViewerObject; @@ -65,6 +67,7 @@ class LLImageRaw; class LLHUDIcon; class LLWindow; class LLRootView; +class LLViewerWindowListener; #define PICK_HALF_WIDTH 5 #define PICK_DIAMETER (2 * PICK_HALF_WIDTH + 1) @@ -456,6 +459,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/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index d793ea0ea5..76e00db91c 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -30,6 +30,12 @@ * $/LicenseInfo$ */ +#if LL_MSVC +// disable warning about boost::lexical_cast returning uninitialized data +// when it fails to parse the string +#pragma warning (disable:4701) +#endif + #include "llviewerprecompiledheaders.h" #include "llvoavatar.h" @@ -86,7 +92,12 @@ #include "llvoiceclient.h" #include "llvoicevisualizer.h" // Ventrella -#include "boost/lexical_cast.hpp" +#if LL_MSVC +// disable boost::lexical_cast warning +#pragma warning (disable:4702) +#endif + +#include <boost/lexical_cast.hpp> using namespace LLVOAvatarDefines; diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 03f9bd4b7c..99e358f409 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -30,6 +30,12 @@ * $/LicenseInfo$ */ +#if LL_MSVC +// disable warning about boost::lexical_cast returning uninitialized data +// when it fails to parse the string +#pragma warning (disable:4701) +#endif + #include "llviewerprecompiledheaders.h" #include "llvoavatarself.h" @@ -84,7 +90,12 @@ #include "llvoicevisualizer.h" // Ventrella #include "llappearancemgr.h" -#include "boost/lexical_cast.hpp" +#if LL_MSVC +// disable boost::lexical_cast warning +#pragma warning (disable:4702) +#endif + +#include <boost/lexical_cast.hpp> using namespace LLVOAvatarDefines; diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index ca028269fe..02f63a848b 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -6921,7 +6921,8 @@ void LLVoiceClient::notifyFriendObservers() void LLVoiceClient::lookupName(const LLUUID &id) { - gCacheName->get(id, FALSE, &LLVoiceClient::onAvatarNameLookup); + BOOL is_group = FALSE; + gCacheName->get(id, is_group, &LLVoiceClient::onAvatarNameLookup); } //static diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 583246c23e..98b3ba8f45 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -91,6 +91,57 @@ LLPointer<LLObjectMediaNavigateClient> LLVOVolume::sObjectMediaNavigateClient = static LLFastTimer::DeclareTimer FTM_GEN_TRIANGLES("Generate Triangles"); static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes"); +// Implementation class of LLMediaDataClientObject. See llmediadataclient.h +class LLMediaDataClientObjectImpl : public LLMediaDataClientObject +{ +public: + LLMediaDataClientObjectImpl(LLVOVolume *obj) : mObject(obj) {} + LLMediaDataClientObjectImpl() { mObject = NULL; } + + virtual U8 getMediaDataCount() const + { return mObject->getNumTEs(); } + + virtual LLSD getMediaDataLLSD(U8 index) const + { + LLSD result; + LLTextureEntry *te = mObject->getTE(index); + if (NULL != te) + { + llassert((te->getMediaData() != NULL) == te->hasMedia()); + if (te->getMediaData() != NULL) + { + result = te->getMediaData()->asLLSD(); + } + } + return result; + } + + virtual LLUUID getID() const + { return mObject->getID(); } + + virtual void mediaNavigateBounceBack(U8 index) + { mObject->mediaNavigateBounceBack(index); } + + virtual bool hasMedia() const + { return mObject->hasMedia(); } + + virtual void updateObjectMediaData(LLSD const &data) + { mObject->updateObjectMediaData(data); } + + virtual F64 getDistanceFromAvatar() const + { return mObject->getRenderPosition().length(); } + + virtual F64 getTotalMediaInterest() const + { return mObject->getTotalMediaInterest(); } + + virtual std::string getCapabilityUrl(const std::string &name) const + { return mObject->getRegion()->getCapability(name); } + +private: + LLPointer<LLVOVolume> mObject; +}; + + LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp) : LLViewerObject(id, pcode, regionp), mVolumeImpl(NULL) @@ -134,8 +185,12 @@ LLVOVolume::~LLVOVolume() // static void LLVOVolume::initClass() { - sObjectMediaClient = new LLObjectMediaDataClient(); - sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(); + // gSavedSettings better be around + const F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay"); + const F32 retry_timer_delay = gSavedSettings.getF32("PrimMediaRetryTimerDelay"); + const U32 max_retries = gSavedSettings.getU32("PrimMediaMaxRetries"); + sObjectMediaClient = new LLObjectMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries); + sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(queue_timer_delay, retry_timer_delay, max_retries); } // static @@ -319,13 +374,23 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, } } } - if (retval & (MEDIA_URL_REMOVED | MEDIA_URL_ADDED | MEDIA_URL_UPDATED | MEDIA_FLAGS_CHANGED)) { - // If the media changed at all, request new media data - if(mMedia) + if (retval & (MEDIA_URL_REMOVED | MEDIA_URL_ADDED | MEDIA_URL_UPDATED | MEDIA_FLAGS_CHANGED)) + { + // If only the media URL changed, and it isn't a media version URL, + // ignore it + if ( ! ( retval & (MEDIA_URL_ADDED | MEDIA_URL_UPDATED) && + mMedia && ! mMedia->mMediaURL.empty() && + ! LLTextureEntry::isMediaVersionString(mMedia->mMediaURL) ) ) { - llinfos << "Media URL: " << mMedia->mMediaURL << llendl; + // If the media changed at all, request new media data + LL_DEBUGS("MediaOnAPrim") << "Media update: " << getID() << ": retval=" << retval << " Media URL: " << + ((mMedia) ? mMedia->mMediaURL : std::string("")) << LL_ENDL; + requestMediaDataUpdate(); } - requestMediaDataUpdate(); + else { + LL_INFOS("MediaOnAPrim") << "Ignoring media update for: " << getID() << " Media URL: " << + ((mMedia) ? mMedia->mMediaURL : std::string("")) << LL_ENDL; + } } // ...and clean up any media impls cleanUpMediaImpls(); @@ -1624,7 +1689,7 @@ bool LLVOVolume::hasMedia() const void LLVOVolume::requestMediaDataUpdate() { - sObjectMediaClient->fetchMedia(this); + sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this)); } void LLVOVolume::cleanUpMediaImpls() @@ -1703,6 +1768,119 @@ void LLVOVolume::syncMediaData(S32 texture_index, const LLSD &media_data, bool m // << ((NULL == te->getMediaData()) ? "NULL MEDIA DATA" : ll_pretty_print_sd(te->getMediaData()->asLLSD())) << llendl; } +void LLVOVolume::mediaNavigateBounceBack(U8 texture_index) +{ + // Find the media entry for this navigate + const LLMediaEntry* mep = NULL; + viewer_media_t impl = getMediaImpl(texture_index); + LLTextureEntry *te = getTE(texture_index); + if(te) + { + mep = te->getMediaData(); + } + + if (mep && impl) + { + std::string url = mep->getCurrentURL(); + if (url.empty()) + { + url = mep->getHomeURL(); + } + if (! url.empty()) + { + LL_INFOS("LLMediaDataClient") << "bouncing back to URL: " << url << LL_ENDL; + impl->navigateTo(url, "", false, true); + } + } +} + +bool LLVOVolume::hasNavigatePermission(const LLMediaEntry* media_entry) +{ + // NOTE: This logic duplicates the logic in the server (in particular, in llmediaservice.cpp). + if (NULL == media_entry ) return false; // XXX should we assert here? + + // The agent has permissions to navigate if: + // - agent has edit permissions, or + // - world permissions are on, or + // - group permissions are on, and agent_id is in the group, or + // - agent permissions are on, and agent_id is the owner + + if (permModify()) + { + return true; + } + + U8 media_perms = media_entry->getPermsInteract(); + + // World permissions + if (0 != (media_perms & LLMediaEntry::PERM_ANYONE)) + { + return true; + } + + // Group permissions + else if (0 != (media_perms & LLMediaEntry::PERM_GROUP) && permGroupOwner()) + { + return true; + } + + // Owner permissions + else if (0 != (media_perms & LLMediaEntry::PERM_OWNER) && permYouOwner()) + { + return true; + } + + return false; + +} + +void LLVOVolume::mediaNavigated(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, std::string new_location) +{ + bool block_navigation = false; + // FIXME: if/when we allow the same media impl to be used by multiple faces, the logic here will need to be fixed + // to deal with multiple face indices. + int face_index = getFaceIndexWithMediaImpl(impl, -1); + + // Find the media entry for this navigate + LLMediaEntry* mep = NULL; + LLTextureEntry *te = getTE(face_index); + if(te) + { + mep = te->getMediaData(); + } + + if(mep) + { + if(!mep->checkCandidateUrl(new_location)) + { + block_navigation = true; + } + if (!block_navigation && !hasNavigatePermission(mep)) + { + block_navigation = true; + } + } + else + { + llwarns << "Couldn't find media entry!" << llendl; + } + + if(block_navigation) + { + llinfos << "blocking navigate to URI " << new_location << llendl; + + // "bounce back" to the current URL from the media entry + mediaNavigateBounceBack(face_index); + } + else + { + + llinfos << "broadcasting navigate with URI " << new_location << llendl; + + sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this), face_index, new_location); + } +} + void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event) { switch(event) @@ -1714,49 +1892,8 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, { case LLViewerMediaImpl::MEDIANAVSTATE_FIRST_LOCATION_CHANGED: { - // This is the first location changed event after the start of a non-server-directed nav. It may need to be broadcast. - - bool block_navigation = false; - // FIXME: if/when we allow the same media impl to be used by multiple faces, the logic here will need to be fixed - // to deal with multiple face indices. - int face_index = getFaceIndexWithMediaImpl(impl, -1); - std::string new_location = plugin->getLocation(); - - // Find the media entry for this navigate - LLMediaEntry* mep = NULL; - LLTextureEntry *te = getTE(face_index); - if(te) - { - mep = te->getMediaData(); - } - - if(mep) - { - if(!mep->checkCandidateUrl(new_location)) - { - block_navigation = true; - } - } - else - { - llwarns << "Couldn't find media entry!" << llendl; - } - - if(block_navigation) - { - llinfos << "blocking navigate to URI " << new_location << llendl; - - // "bounce back" to the current URL from the media entry - // NOTE: the only way block_navigation can be true is if we found the media entry, so we're guaranteed here that mep is not NULL. - impl->navigateTo(mep->getCurrentURL()); - } - else - { - - llinfos << "broadcasting navigate with URI " << new_location << llendl; - - sObjectMediaNavigateClient->navigate(this, face_index, new_location); - } + // This is the first location changed event after the start of a non-server-directed nav. It may need to be broadcast or bounced back. + mediaNavigated(impl, plugin, plugin->getLocation()); } break; @@ -1773,6 +1910,29 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, } break; + case LLViewerMediaObserver::MEDIA_EVENT_NAVIGATE_COMPLETE: + { + switch(impl->getNavState()) + { + case LLViewerMediaImpl::MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED: + { + // This is the first location changed event after the start of a non-server-directed nav. It may need to be broadcast or bounced back. + mediaNavigated(impl, plugin, plugin->getNavigateURI()); + } + break; + + case LLViewerMediaImpl::MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED: + // This is the the navigate complete event from a server-directed nav. Don't broadcast it. + llinfos << " NOT broadcasting navigate (server-directed)" << llendl; + break; + + default: + // For all other states, the navigate should have been handled by LOCATION_CHANGED events already. + break; + } + } + break; + default: break; } @@ -1781,7 +1941,7 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, void LLVOVolume::sendMediaDataUpdate() { - sObjectMediaClient->updateMedia(this); + sObjectMediaClient->updateMedia(new LLMediaDataClientObjectImpl(this)); } void LLVOVolume::removeMediaImpl(S32 texture_index) @@ -1874,6 +2034,22 @@ viewer_media_t LLVOVolume::getMediaImpl(U8 face_id) const return NULL; } +F64 LLVOVolume::getTotalMediaInterest() const +{ + F64 interest = (F64)0.0; + int i = 0; + const int end = getNumTEs(); + for ( ; i < end; ++i) + { + const viewer_media_t &impl = getMediaImpl(i); + if (!impl.isNull()) + { + interest += impl->getInterest(); + } + } + return interest; +} + S32 LLVOVolume::getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id) { S32 end = (S32)mMediaImplList.size() ; diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 250c3ed917..d8ae9a5453 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -233,7 +233,19 @@ public: BOOL canBeFlexible() const; BOOL setIsFlexible(BOOL is_flexible); - void updateObjectMediaData(const LLSD &media_data_duples); + // Functions that deal with media, or media navigation + + // Update this object's media data with the given media data array + // (typically this is only called upon a response from a server request) + void updateObjectMediaData(const LLSD &media_data_array); + + // Bounce back media at the given index to its current URL (or home URL, if current URL is empty) + void mediaNavigateBounceBack(U8 texture_index); + + // Returns whether or not this object has permission to navigate the given media entry + bool hasNavigatePermission(const LLMediaEntry* media_entry); + + void mediaNavigated(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, std::string new_location); void mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event); // Sync the given media data with the impl and the given te @@ -244,6 +256,7 @@ public: viewer_media_t getMediaImpl(U8 face_id) const; S32 getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id); + F64 getTotalMediaInterest() const; bool hasMedia() const; diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp new file mode 100644 index 0000000000..af8cb6b9fb --- /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::EStatus> +{ +public: + StatusMapper(): StatusMapperBase<LLXMLRPCTransaction::EStatus>("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::EStatus 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::EStatus 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 58ff84a8a6..70859e8ea5 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/res/viewerRes.rc b/indra/newview/res/viewerRes.rc index 38570e1a7e..433070ce34 100644 --- a/indra/newview/res/viewerRes.rc +++ b/indra/newview/res/viewerRes.rc @@ -134,8 +134,8 @@ TOOLMEDIAOPEN CURSOR "toolmediaopen.cur" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,0,0,0 - PRODUCTVERSION 2,0,0,0 + FILEVERSION 2,0,0,3256 + PRODUCTVERSION 2,0,0,3256 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -152,12 +152,12 @@ BEGIN BEGIN VALUE "CompanyName", "Linden Lab" VALUE "FileDescription", "Second Life" - VALUE "FileVersion", "2.0.0.0" + VALUE "FileVersion", "2.0.0.3256" VALUE "InternalName", "Second Life" VALUE "LegalCopyright", "Copyright © 2001-2008, Linden Research, Inc." VALUE "OriginalFilename", "SecondLife.exe" VALUE "ProductName", "Second Life" - VALUE "ProductVersion", "2.0.0.0" + VALUE "ProductVersion", "2.0.0.3256" END END BLOCK "VarFileInfo" diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml index 4d7433233a..b194b533af 100644 --- a/indra/newview/skins/default/xui/en/floater_about.xml +++ b/indra/newview/skins/default/xui/en/floater_about.xml @@ -157,11 +157,13 @@ DBus/dbus-glib Copyright (C) 2002, 2003 CodeFactory AB / Copyright (C) 2003, 20 expat Copyright (C) 1998, 1999, 2000 Thai Open Source Software Center Ltd. FreeType Copyright (C) 1996-2002, The FreeType Project (www.freetype.org). GL Copyright (C) 1999-2004 Brian Paul. +google-perftools Copyright (c) 2005, Google Inc. Havok.com(TM) Copyright (C) 1999-2001, Telekinesys Research Limited. jpeg2000 Copyright (C) 2001, David Taubman, The University of New South Wales (UNSW) jpeglib Copyright (C) 1991-1998, Thomas G. Lane. ogg/vorbis Copyright (C) 2001, Xiphophorus OpenSSL Copyright (C) 1998-2002 The OpenSSL Project. +Pth Copyright (C) 1999-2006 Ralf S. Engelschall <rse@gnu.org> SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) xmlrpc-epi Copyright (C) 2000 Epinions, Inc. diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml index 7829a4fa93..b898fd7c93 100644 --- a/indra/newview/skins/default/xui/en/floater_tools.xml +++ b/indra/newview/skins/default/xui/en/floater_tools.xml @@ -207,71 +207,71 @@ layout="topleft" name="move_radio_group"> <radio_item - top_pad="6" + top_pad="6" label="Move" layout="topleft" name="radio move" /> <radio_item - top_pad="6" + top_pad="6" label="Lift (Ctrl)" layout="topleft" name="radio lift" /> <radio_item - top_pad="6" + top_pad="6" label="Spin (Ctrl+Shift)" layout="topleft" name="radio spin" /> <radio_group.commit_callback - function="BuildTool.commitRadioMove"/> - </radio_group> - <radio_group + function="BuildTool.commitRadioMove"/> + </radio_group> + <radio_group follows="left|top" - left="10" - top="54" - height="70" + left="10" + top="54" + height="70" layout="topleft" - name="edit_radio_group"> + name="edit_radio_group"> <radio_item - label="Move" - layout="topleft" - name="radio position" /> + label="Move" + layout="topleft" + name="radio position" /> <radio_item - top_pad="6" + top_pad="6" label="Rotate (Ctrl)" layout="topleft" name="radio rotate" /> <radio_item - top_pad="6" + top_pad="6" label="Stretch (Ctrl+Shift)" layout="topleft" name="radio stretch" /> <radio_item - top_pad="6" + top_pad="6" label="Select Texture" layout="topleft" name="radio select face" /> - <radio_group.commit_callback - function="BuildTool.commitRadioEdit"/> + <radio_group.commit_callback + function="BuildTool.commitRadioEdit"/> </radio_group> <check_box left="10" follows="left|top" height="16" - control_name="EditLinkedParts" + control_name="EditLinkedParts" label="Edit linked" layout="topleft" name="checkbox edit linked parts" > <check_box.commit_callback - function="BuildTool.selectComponent"/> + function="BuildTool.selectComponent"/> </check_box> - <check_box + <check_box control_name="ScaleUniform" height="19" label="Stretch Both Sides" layout="topleft" left="143" name="checkbox uniform" - top="50" + top="50" width="134" /> <check_box control_name="ScaleStretchTextures" @@ -312,7 +312,7 @@ <combo_box.commit_callback function="BuildTool.gridMode"/> </combo_box> - <button + <button left_pad="0" image_disabled="ForwardArrow_Disabled" image_selected="ForwardArrow_Press" @@ -754,24 +754,24 @@ tab_height="25" top="170" width="280"> - <panel - border="false" - follows="all" - label="General" - layout="topleft" - mouse_opaque="false" - help_topic="toolbox_general_tab" - name="General" - top="16" - width="280"> - <panel.string - name="text deed continued"> - Deed - </panel.string> - <panel.string - name="text deed"> - Deed - </panel.string> + <panel + border="false" + follows="all" + label="General" + layout="topleft" + mouse_opaque="false" + help_topic="toolbox_general_tab" + name="General" + top="16" + width="280"> + <panel.string + name="text deed continued"> + Deed + </panel.string> + <panel.string + name="text deed"> + Deed + </panel.string> <panel.string name="text modify info 1"> You can modify this object @@ -912,21 +912,21 @@ width="75"> Group: </text> - <button - follows="top|left" - height="10" - image_disabled="Activate_Checkmark" - image_selected="Activate_Checkmark" - image_unselected="Activate_Checkmark" - image_color="White_50" - layout="topleft" - left_pad="0" - top_delta="0" - name="button set group" - picture_style="true" - tab_stop="false" - tool_tip="Choose a group to share this object's permissions" - width="10" /> + <button + follows="top|left" + height="10" + image_disabled="Activate_Checkmark" + image_selected="Activate_Checkmark" + image_unselected="Activate_Checkmark" + image_color="White_50" + layout="topleft" + left_pad="0" + top_delta="0" + name="button set group" + picture_style="true" + tab_stop="false" + tool_tip="Choose a group to share this object's permissions" + width="10" /> <name_box follows="left|top" height="18" @@ -1046,15 +1046,15 @@ height="20" max_val="999999999" /> <check_box - height="15" - width="110" - top_pad="3" - label="Show in search" + height="15" + width="110" + top_pad="3" + label="Show in search" layout="topleft" - left="100" + left="100" name="search_check" tool_tip="Let people see this object in search results" /> -<panel + <panel border="false" follows="left|top" layout="topleft" @@ -2353,7 +2353,7 @@ left_delta="0" name="combobox texgen" top_pad="4" - width="87"> + width="60"> <combo_box.item label="Default" name="Default" @@ -2372,7 +2372,7 @@ name="label shininess" left_pad="5" top_pad="-36" - width="80"> + width="60"> Shininess </text> <combo_box @@ -2381,7 +2381,7 @@ left_delta="0" name="combobox shininess" top_pad="4" - width="80"> + width="60"> <combo_box.item label="None" name="None" @@ -2584,9 +2584,9 @@ height="19" increment="1" initial_value="0" - label="RotationËš" + label="RotationËš" layout="topleft" - label_width="100" + label_width="100" left="10" max_val="9999" min_val="-9999" @@ -2599,9 +2599,9 @@ follows="left|top" height="19" initial_value="1" - label="Repeats / Meter" + label="Repeats / Meter" layout="topleft" - label_width="100" + label_width="100" left="10" max_val="10" min_val="0.1" @@ -2654,7 +2654,7 @@ name="TexOffsetV" top_pad="1" width="160" /> - <panel + <panel border="false" follows="left|top" layout="topleft" @@ -2667,88 +2667,88 @@ height="75" width="278"> <text - type="string" - length="1" - follows="left|top" - height="12" - layout="topleft" - left="10" - top_pad="5" - name="media_tex" - width="260"> - Media URL - </text> - <line_editor - follows="left|top|right" - height="18" - layout="topleft" - left="10" - read_only="true" - name="media_info" - select_on_focus="true" - width="220" /> - <button - follows="top|left" - height="18" - image_selected="AddItem_Press" - image_unselected="AddItem_Off" - image_disabled="AddItem_Disabled" - layout="topleft" - left_pad="0" - name="add_media" - picture_style="true" - tab_stop="false" - top_delta="0" - tool_tip="Add media" - width="18"> - <button.commit_callback - function="BuildTool.AddMedia"/> - </button> - <button - follows="bottom|left" - height="18" - image_selected="TrashItem_Press" - image_unselected="TrashItem_Off" - layout="topleft" - left_pad="5" - name="delete_media" - picture_style="true" - right="-10" - tool_tip="Delete this media texture" - top_delta="0" - width="18"> - <button.commit_callback - function="BuildTool.DeleteMedia"/> - </button> - <button + type="string" + length="1" follows="left|top" - font="SansSerifSmall" - height="19" - label="Align" - label_selected="Align Media" + height="12" layout="topleft" left="10" - name="button align" top_pad="5" - width="100" /> - <button - follows="bottom|left" - tool_tip="Change sort and view of recent residents list" - height="18" - image_disabled="OptionsMenu_Disabled" - image_selected="OptionsMenu_Press" - image_unselected="OptionsMenu_Off" - layout="topleft" - left_pad="10" - name="edit_media" - picture_style="true" - top_delta="0" - width="18"> - <button.commit_callback - function="BuildTool.EditMedia"/> - </button> - </panel> - </panel> + name="media_tex" + width="260"> + Media URL + </text> + <line_editor + follows="left|top|right" + height="18" + layout="topleft" + left="10" + read_only="true" + name="media_info" + select_on_focus="true" + width="180" /> + <button + follows="top|left" + height="18" + image_selected="AddItem_Press" + image_unselected="AddItem_Off" + image_disabled="AddItem_Disabled" + layout="topleft" + left_pad="0" + name="add_media" + picture_style="true" + tab_stop="false" + top_delta="0" + tool_tip="Add Media" + width="18"> + <button.commit_callback + function="BuildTool.AddMedia"/> + </button> + <button + follows="top|left" + height="18" + image_selected="TrashItem_Press" + image_unselected="TrashItem_Off" + layout="topleft" + left_pad="5" + name="delete_media" + picture_style="true" + tool_tip="Delete this media texture" + top_delta="0" + width="18"> + <button.commit_callback + function="BuildTool.DeleteMedia"/> + </button> + <button + follows="top|left" + tool_tip="Edit this Media" + height="18" + image_disabled="OptionsMenu_Disabled" + image_selected="OptionsMenu_Press" + image_unselected="OptionsMenu_Off" + layout="topleft" + left_pad="10" + name="edit_media" + picture_style="true" + top_delta="0" + width="18"> + <button.commit_callback + function="BuildTool.EditMedia"/> + </button> + + <button + follows="left|top" + font="SansSerifSmall" + height="19" + label="Align" + label_selected="Align Media" + layout="topleft" + left="10" + name="button align" + top_pad="5" + width="100" /> + </panel> + </panel> <panel border="false" follows="all" @@ -2786,10 +2786,10 @@ name="contents_inventory" top="50" width="260" /> - </panel> + </panel> </tab_container> -<panel - follows="left|top" + <panel + follows="left|top" height="384" layout="topleft" left_delta="0" diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 116d8ae12a..5979bfab4f 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -1091,6 +1091,14 @@ <menu_item_call.on_click function="Advanced.RebakeTextures" /> </menu_item_call> + <menu_item_call + label="Set UI Size to Default" + layout="topleft" + name="Set UI Size to Default"> + <menu_item_call.on_click + function="View.DefaultUISize" /> + </menu_item_call> + <menu_item_separator/> <menu_item_check label="Limit Select Distance" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index c61c59042c..36a4798367 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -675,6 +675,19 @@ To place the media on only one face, choose Select Texture and click on the desi <notification icon="alertmodal.tga" + name="WhiteListInvalidatesHomeUrl" + type="alertmodal"> +Adding this entry to the whitelist will invalidate the home URL you +specified for this instance of media. You are not allowed to do this +so the entry cannot be added to the whitelist. + <usetemplate + name="okbutton" + yestext="Ok"/> + </notification> + + + <notification + icon="alertmodal.tga" name="MustBeInParcel" type="alertmodal"> You must be standing inside the land parcel to set its Landing Point. diff --git a/indra/newview/skins/default/xui/en/panel_media_settings_general.xml b/indra/newview/skins/default/xui/en/panel_media_settings_general.xml index a56f1cf8fe..c725334fc0 100644 --- a/indra/newview/skins/default/xui/en/panel_media_settings_general.xml +++ b/indra/newview/skins/default/xui/en/panel_media_settings_general.xml @@ -234,7 +234,7 @@ label="" label_width="0" left_delta="40" - max_val="2000" + max_val="2048" min_val="0" mouse_opaque="true" name="width_pixels" @@ -246,7 +246,7 @@ <spinner bottom_delta="0" decimal_digits="0" enabled="true" follows="left|top" height="16" increment="1" initial_val="256" label="" label_width="0" - left_delta="20" max_val="2000" min_val="0" mouse_opaque="true" + left_delta="20" max_val="2048" min_val="0" mouse_opaque="true" name="height_pixels" width="50" /> </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/lldateutil_test.cpp b/indra/newview/tests/lldateutil_test.cpp index 30e39a3bcf..ed753b6ff7 100644 --- a/indra/newview/tests/lldateutil_test.cpp +++ b/indra/newview/tests/lldateutil_test.cpp @@ -28,6 +28,9 @@ * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ + +#include "linden_common.h" + #include "../test/lltut.h" #include "../lldateutil.h" diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp new file mode 100644 index 0000000000..009be35f64 --- /dev/null +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -0,0 +1,423 @@ +/**
+ * @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 "llnotifications.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["change"] = "connect";
+ 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["change"] = "disconnect";
+ 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["change"] = "fail.login";
+ 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["change"] = "fail.login";
+ 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["change"] = "fail.login";
+ 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["change"] = "fail.login";
+ 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["change"] = "fail.login";
+ 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/llmediadataclient_test.cpp b/indra/newview/tests/llmediadataclient_test.cpp new file mode 100644 index 0000000000..445ec7aa34 --- /dev/null +++ b/indra/newview/tests/llmediadataclient_test.cpp @@ -0,0 +1,501 @@ +/** + * @file llmediadataclient_test.cpp + * @brief LLMediaDatClient tests + * + * $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 "../llviewerprecompiledheaders.h" + +#include <iostream> +#include "../test/lltut.h" + +#include "llsdserialize.h" +#include "llsdutil.h" +#include "llerrorcontrol.h" +#include "llhttpstatuscodes.h" + +#include "../llmediadataclient.h" +#include "../llvovolume.h" + +#include "../../llprimitive/llmediaentry.cpp" +#include "../../llprimitive/lltextureentry.cpp" +#include "../../llmessage/tests/llcurl_stub.cpp" + +#if LL_WINDOWS +#pragma warning (push) +#pragma warning (disable : 4702) // boost::lexical_cast generates this warning +#endif +#include <boost/lexical_cast.hpp> +#if LL_WINDOWS +#pragma warning (pop) +#endif + +#define VALID_OBJECT_ID "3607d5c4-644b-4a8a-871a-8b78471af2a2" +#define VALID_OBJECT_ID_1 "11111111-1111-1111-1111-111111111111" +#define VALID_OBJECT_ID_2 "22222222-2222-2222-2222-222222222222" +#define VALID_OBJECT_ID_3 "33333333-3333-3333-3333-333333333333" +#define VALID_OBJECT_ID_4 "44444444-4444-4444-4444-444444444444" + +#define FAKE_OBJECT_MEDIA_CAP_URL "foo_ObjectMedia" +#define FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL "foo_ObjectMediaNavigate" +#define FAKE_OBJECT_MEDIA_CAP_URL_503 "foo_ObjectMedia_503" +#define FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR "foo_ObjectMediaNavigate_ERROR" + +#define MEDIA_DATA "\ +<array> \ +<string>foo</string> \ +<string>bar</string> \ +<string>baz</string> \ +</array>" + +#define _DATA_URLS(ID,DIST,INT,URL1,URL2) " \ +<llsd> \ + <map> \ + <key>uuid</key> \ + <string>" ID "</string> \ + <key>distance</key> \ + <real>" DIST "</real> \ + <key>interest</key> \ + <real>" INT "</real> \ + <key>cap_urls</key> \ + <map> \ + <key>ObjectMedia</key> \ + <string>" URL1 "</string> \ + <key>ObjectMediaNavigate</key> \ + <string>" URL2 "</string> \ + </map> \ + <key>media_data</key> \ + " MEDIA_DATA " \ + </map> \ +</llsd>" + +#define _DATA(ID,DIST,INT) _DATA_URLS(ID,DIST,INT,FAKE_OBJECT_MEDIA_CAP_URL,FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL) + +const char *DATA = _DATA(VALID_OBJECT_ID,"1.0","1.0"); + +#define STR(I) boost::lexical_cast<std::string>(I) + +#define LOG_TEST(N) LL_DEBUGS("LLMediaDataClient") << "\n" << \ +"================================================================================\n" << \ +"===================================== TEST " #N " ===================================\n" << \ +"================================================================================\n" << LL_ENDL; + +LLSD *gPostRecords = NULL; + +// stubs: +void LLHTTPClient::post( + const std::string& url, + const LLSD& body, + LLHTTPClient::ResponderPtr responder, + const LLSD& headers, + const F32 timeout) +{ + LLSD record; + record["url"] = url; + record["body"] = body; + record["headers"] = headers; + record["timeout"] = timeout; + gPostRecords->append(record); + + // Magic URL that triggers a 503: + if ( url == FAKE_OBJECT_MEDIA_CAP_URL_503 ) + { + responder->error(HTTP_SERVICE_UNAVAILABLE, "fake reason"); + } + else if (url == FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR) + { + LLSD result; + LLSD error; + error["code"] = LLObjectMediaNavigateClient::ERROR_PERMISSION_DENIED_CODE; + result["error"] = error; + responder->result(result); + } + else { + responder->result(LLSD()); + } +} + +const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; + +class LLMediaDataClientObjectTest : public LLMediaDataClientObject +{ +public: + LLMediaDataClientObjectTest(const char *data) + { + std::istringstream d(data); + LLSDSerialize::fromXML(mRep, d); + mNumBounceBacks = 0; + + // std::cout << ll_pretty_print_sd(mRep) << std::endl; + // std::cout << "ID: " << getID() << std::endl; + } + LLMediaDataClientObjectTest(const LLSD &rep) + : mRep(rep), mNumBounceBacks(0) {} + ~LLMediaDataClientObjectTest() + { LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClientObjectTest" << LL_ENDL; } + + virtual U8 getMediaDataCount() const + { return mRep["media_data"].size(); } + virtual LLSD getMediaDataLLSD(U8 index) const + { return mRep["media_data"][(LLSD::Integer)index]; } + virtual LLUUID getID() const + { return mRep["uuid"]; } + virtual void mediaNavigateBounceBack(U8 index) + { + mNumBounceBacks++; + } + + virtual bool hasMedia() const + { return mRep.has("media_data"); } + + virtual void updateObjectMediaData(LLSD const &media_data_array) + { mRep["media_data"] = media_data_array; } + + virtual F64 getDistanceFromAvatar() const + { return (LLSD::Real)mRep["distance"]; } + + virtual F64 getTotalMediaInterest() const + { return (LLSD::Real)mRep["interest"]; } + + virtual std::string getCapabilityUrl(const std::string &name) const + { return mRep["cap_urls"][name]; } + + int getNumBounceBacks() const + { return mNumBounceBacks; } + +private: + LLSD mRep; + int mNumBounceBacks; +}; + +// This special timer delay should ensure that the timer will fire on the very +// next pump, no matter what (though this does make an assumption about the +// implementation of LLEventTimer::updateClass()): +const F32 NO_PERIOD = -1000.0f; + +static void pump_timers() +{ + LLEventTimer::updateClass(); +} + +namespace tut +{ + struct mediadataclient + { + mediadataclient() { + gPostRecords = &mLLSD; + + //LLError::setDefaultLevel(LLError::LEVEL_DEBUG); + //LLError::setClassLevel("LLMediaDataClient", LLError::LEVEL_DEBUG); + //LLError::setTagLevel("MediaOnAPrim", LLError::LEVEL_DEBUG); + } + LLSD mLLSD; + }; + + typedef test_group<mediadataclient> mediadataclient_t; + typedef mediadataclient_t::object mediadataclient_object_t; + tut::mediadataclient_t tut_mediadataclient("mediadataclient"); + + void ensure(const std::string &msg, int value, int expected) + { + std::string m = msg; + m += " value: " + STR(value); + m += ", expected: " + STR(expected); + ensure(m, value == expected); + } + + void ensure(const std::string &msg, const std::string & value, const std::string & expected) + { + std::string m = msg; + m += " value: " + value; + m += ", expected: " + expected; + ensure(m, value == expected); + } + + void ensure(const std::string &msg, const LLUUID & value, const LLUUID & expected) + { + std::string m = msg; + m += " value: " + value.asString(); + m += ", expected: " + expected.asString(); + ensure(m, value == expected); + } + + void ensure_llsd(const std::string &msg, const LLSD & value, const char *expected) + { + LLSD expected_llsd; + std::istringstream e(expected); + LLSDSerialize::fromXML(expected_llsd, e); + + std::string value_str = ll_pretty_print_sd(value); + std::string expected_str = ll_pretty_print_sd(expected_llsd); + std::string m = msg; + m += " value: " + value_str; + m += ", expected: " + expected_str; + ensure(m, value_str == expected_str); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<1>() + { + // + // Test fetchMedia() + // + LOG_TEST(1); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA); + int num_refs_start = o->getNumRefs(); + { + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD); + mdc->fetchMedia(o); + + // Make sure no posts happened yet... + ensure("post records", gPostRecords->size(), 0); + + ::pump_timers(); + + ensure("post records", gPostRecords->size(), 1); + ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_CAP_URL); + ensure("post GET", (*gPostRecords)[0]["body"]["verb"], "GET"); + ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + ensure("queue empty", mdc->isEmpty()); + } + + // Make sure everyone's destroyed properly + ensure("REF COUNT", o->getNumRefs(), num_refs_start); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<2>() + { + // + // Test updateMedia() + // + LOG_TEST(2); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA); + { + // queue time w/ no delay ensures that ::pump_timers() will hit the tick() + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD); + mdc->updateMedia(o); + ensure("post records", gPostRecords->size(), 0); + ::pump_timers(); + + ensure("post records", gPostRecords->size(), 1); + ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_CAP_URL); + ensure("post UPDATE", (*gPostRecords)[0]["body"]["verb"], "UPDATE"); + ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + ensure_llsd("post data llsd", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_MEDIA_DATA_KEY], + "<llsd>" MEDIA_DATA "</llsd>"); + ensure("queue empty", mdc->isEmpty()); + } + + ensure("REF COUNT", o->getNumRefs(), 1); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<3>() + { + // + // Test navigate() + // + LOG_TEST(3); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA); + { + LLPointer<LLObjectMediaNavigateClient> mdc = new LLObjectMediaNavigateClient(NO_PERIOD,NO_PERIOD); + const char *TEST_URL = "http://example.com"; + mdc->navigate(o, 0, TEST_URL); + ensure("post records", gPostRecords->size(), 0); + ::pump_timers(); + + // ensure no bounce back + ensure("bounce back", dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o))->getNumBounceBacks(), 0); + + ensure("post records", gPostRecords->size(), 1); + ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL); + ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + ensure("post data", (*gPostRecords)[0]["body"][LLTextureEntry::TEXTURE_INDEX_KEY], 0); + ensure("post data", (*gPostRecords)[0]["body"][LLMediaEntry::CURRENT_URL_KEY], TEST_URL); + ensure("queue empty", mdc->isEmpty()); + } + ensure("REF COUNT", o->getNumRefs(), 1); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<4>() + { + // + // Test queue ordering + // + LOG_TEST(4); + + LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest( + _DATA(VALID_OBJECT_ID_1,"3.0","1.0")); + LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest( + _DATA(VALID_OBJECT_ID_2,"1.0","1.0")); + LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest( + _DATA(VALID_OBJECT_ID_3,"2.0","1.0")); + { + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD); + const char *ORDERED_OBJECT_IDS[] = { VALID_OBJECT_ID_2, VALID_OBJECT_ID_3, VALID_OBJECT_ID_1 }; + mdc->fetchMedia(o1); + mdc->fetchMedia(o2); + mdc->fetchMedia(o3); + + // Make sure no posts happened yet... + ensure("post records", gPostRecords->size(), 0); + + // tick 3 times... + ::pump_timers(); + ensure("post records", gPostRecords->size(), 1); + ::pump_timers(); + ensure("post records", gPostRecords->size(), 2); + ::pump_timers(); + ensure("post records", gPostRecords->size(), 3); + + for( int i=0; i < 3; i++ ) + { + ensure("[" + STR(i) + "] post url", (*gPostRecords)[i]["url"], FAKE_OBJECT_MEDIA_CAP_URL); + ensure("[" + STR(i) + "] post GET", (*gPostRecords)[i]["body"]["verb"], "GET"); + ensure("[" + STR(i) + "] post object id", (*gPostRecords)[i]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), + LLUUID(ORDERED_OBJECT_IDS[i])); + } + + ensure("queue empty", mdc->isEmpty()); + } + ensure("refcount of o1", o1->getNumRefs(), 1); + ensure("refcount of o2", o2->getNumRefs(), 1); + ensure("refcount of o3", o3->getNumRefs(), 1); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<5>() + { + // + // Test fetchMedia() getting a 503 error + // + LOG_TEST(5); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest( + _DATA_URLS(VALID_OBJECT_ID, + "1.0", + "1.0", + FAKE_OBJECT_MEDIA_CAP_URL_503, + FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL)); + int num_refs_start = o->getNumRefs(); + { + const int NUM_RETRIES = 5; + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD,NUM_RETRIES); + + // This should generate a retry + mdc->fetchMedia(o); + + // Make sure no posts happened yet... + ensure("post records before", gPostRecords->size(), 0); + + // Once, causes retry + // Second, fires retry timer + // Third, fires queue timer again + for (int i=0; i<NUM_RETRIES; ++i) + { + ::pump_timers(); // Should pump (fire) the queue timer, causing a retry timer to be scheduled + // XXX This ensure is not guaranteed, because scheduling a timer might actually get it pumped in the same loop + //ensure("post records " + STR(i), gPostRecords->size(), i+1); + ::pump_timers(); // Should pump (fire) the retry timer, scheduling the queue timer + } + + // Do some extra pumps to make sure no other timer work occurs. + ::pump_timers(); + ::pump_timers(); + ::pump_timers(); + + // Make sure there were 2 posts + ensure("post records after", gPostRecords->size(), NUM_RETRIES); + for (int i=0; i<NUM_RETRIES; ++i) + { + ensure("[" + STR(i) + "] post url", (*gPostRecords)[i]["url"], FAKE_OBJECT_MEDIA_CAP_URL_503); + ensure("[" + STR(i) + "] post GET", (*gPostRecords)[i]["body"]["verb"], "GET"); + ensure("[" + STR(i) + "] post object id", (*gPostRecords)[i]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + } + ensure("queue empty", mdc->isEmpty()); + } + + // Make sure everyone's destroyed properly + ensure("REF COUNT", o->getNumRefs(), num_refs_start); + } + + template<> template<> + void mediadataclient_object_t::test<6>() + { + // + // Test navigate() with a bounce back + // + LOG_TEST(6); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest( + _DATA_URLS(VALID_OBJECT_ID, + "1.0", + "1.0", + FAKE_OBJECT_MEDIA_CAP_URL, + FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR)); + { + LLPointer<LLObjectMediaNavigateClient> mdc = new LLObjectMediaNavigateClient(NO_PERIOD,NO_PERIOD); + const char *TEST_URL = "http://example.com"; + mdc->navigate(o, 0, TEST_URL); + ensure("post records", gPostRecords->size(), 0); + ::pump_timers(); + + // ensure bounce back + ensure("bounce back", + dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o))->getNumBounceBacks(), + 1); + + ensure("post records", gPostRecords->size(), 1); + ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR); + ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + ensure("post data", (*gPostRecords)[0]["body"][LLTextureEntry::TEXTURE_INDEX_KEY], 0); + ensure("post data", (*gPostRecords)[0]["body"][LLMediaEntry::CURRENT_URL_KEY], TEST_URL); + ensure("queue empty", mdc->isEmpty()); + } + ensure("REF COUNT", o->getNumRefs(), 1); + } + + +} diff --git a/indra/newview/tests/llviewerhelputil_test.cpp b/indra/newview/tests/llviewerhelputil_test.cpp index 40f7d532bc..988d28c301 100644 --- a/indra/newview/tests/llviewerhelputil_test.cpp +++ b/indra/newview/tests/llviewerhelputil_test.cpp @@ -30,6 +30,9 @@ * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ +// Precompiled header +#include "../llviewerprecompiledheaders.h" + #include "../test/lltut.h" #include "../llviewerhelputil.h" 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/viewer_manifest.py b/indra/newview/viewer_manifest.py index 89f916937b..06eb1e1265 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -164,28 +164,103 @@ class WindowsManifest(ViewerManifest): return ''.join(self.channel().split()) + '.exe' + def test_msvcrt_and_copy_action(self, src, dst): + # This is used to test a dll manifest. + # It is used as a temporary override during the construct method + from test_win32_manifest import test_assembly_binding + if src and (os.path.exists(src) or os.path.islink(src)): + # ensure that destination path exists + self.cmakedirs(os.path.dirname(dst)) + self.created_paths.append(dst) + if not os.path.isdir(src): + if(self.args['configuration'].lower() == 'debug'): + test_assembly_binding(src, "Microsoft.VC80.DebugCRT", "8.0.50727.4053") + else: + test_assembly_binding(src, "Microsoft.VC80.CRT", "8.0.50727.4053") + self.ccopy(src,dst) + else: + raise Exception("Directories are not supported by test_CRT_and_copy_action()") + else: + print "Doesn't exist:", src + + def test_for_no_msvcrt_manifest_and_copy_action(self, src, dst): + # This is used to test that no manifest for the msvcrt exists. + # It is used as a temporary override during the construct method + from test_win32_manifest import test_assembly_binding + from test_win32_manifest import NoManifestException, NoMatchingAssemblyException + if src and (os.path.exists(src) or os.path.islink(src)): + # ensure that destination path exists + self.cmakedirs(os.path.dirname(dst)) + self.created_paths.append(dst) + if not os.path.isdir(src): + try: + if(self.args['configuration'].lower() == 'debug'): + test_assembly_binding(src, "Microsoft.VC80.DebugCRT", "") + else: + test_assembly_binding(src, "Microsoft.VC80.CRT", "") + raise Exception("Unknown condition") + except NoManifestException, err: + pass + except NoMatchingAssemblyException, err: + pass + + self.ccopy(src,dst) + else: + raise Exception("Directories are not supported by test_CRT_and_copy_action()") + else: + print "Doesn't exist:", src + + def enable_crt_manifest_check(self): + WindowsManifest.copy_action = WindowsManifest.test_msvcrt_and_copy_action + + def enable_no_crt_manifest_check(self): + WindowsManifest.copy_action = WindowsManifest.test_for_no_msvcrt_manifest_and_copy_action + + def disable_manifest_check(self): + del WindowsManifest.copy_action + 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()) + # 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()) + + self.enable_crt_manifest_check() # Plugin host application self.path(os.path.join(os.pardir, 'llplugin', 'slplugin', self.args['configuration'], "slplugin.exe"), "slplugin.exe") - - # need to get the kdu dll from any of the build directories as well + + # 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 RuntimeError, err: + print err.message + print "Skipping llcommon.dll (assuming llcommon was linked statically)" + + self.end_prefix() + + # need to get the kdu dll from the build directory 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: + self.path('%s/llkdu.dll' % self.args['configuration'], dst='llkdu.dll') + except RuntimeError: print "Skipping llkdu.dll" - pass - self.path(src="licenses-win32.txt", dst="licenses.txt") + self.disable_manifest_check() + + # 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() + + self.path(src="licenses-win32.txt", dst="licenses.txt") self.path("featuretable.txt") # For use in crash reporting (generates minidumps) @@ -194,11 +269,7 @@ class WindowsManifest(ViewerManifest): # 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() - + self.enable_no_crt_manifest_check() # Media plugins - QuickTime if self.prefix(src='../media_plugins/quicktime/%s' % self.args['configuration'], dst="llplugin"): self.path("media_plugin_quicktime.dll") @@ -209,7 +280,6 @@ class WindowsManifest(ViewerManifest): 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") @@ -230,6 +300,8 @@ class WindowsManifest(ViewerManifest): self.path("qtiff4.dll") self.end_prefix() + self.disable_manifest_check() + # 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=""): @@ -243,11 +315,8 @@ class WindowsManifest(ViewerManifest): 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=""): + if self.prefix(src=self.args['configuration'], dst=""): self.path("SLVoice.exe") self.path("alut.dll") self.path("vivoxsdk.dll") @@ -256,22 +325,22 @@ class WindowsManifest(ViewerManifest): 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"), + # 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=self.find_existing_file( - "../win_updater/debug/windows-updater.exe", - "../win_updater/release/windows-updater.exe", - "../win_updater/relwithdebinfo/windows-updater.exe"), + self.path(src='../win_updater/%s/windows-updater.exe' % self.args['configuration'], 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() - + if self.prefix(src=self.args['configuration'], dst=""): + try: + if self.args['configuration'] == 'Debug': + self.path('libtcmalloc_minimal-debug.dll') + else: + self.path('libtcmalloc_minimal.dll') + except: + print "Skipping libtcmalloc_minimal.dll" + self.end_prefix() def nsi_file_commands(self, install=True): def wpath(path): @@ -392,7 +461,11 @@ class WindowsManifest(ViewerManifest): # We use the Unicode version of NSIS, available from # http://www.scratchpaper.com/ - NSIS_path = 'C:\\Program Files\\NSIS\\Unicode\\makensis.exe' + # 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 @@ -468,16 +541,31 @@ class DarwinManifest(ViewerManifest): 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 - 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 - + 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") @@ -538,7 +626,7 @@ class DarwinManifest(ViewerManifest): # 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 500 -layout SPUD' % { + self.run_command('hdiutil create "%(sparse)s" -volname "%(vol)s" -fs HFS+ -type SPARSE -megabytes 700 -layout SPUD' % { 'sparse':sparsename, 'vol':volname}) @@ -667,15 +755,17 @@ class Linux_i686Manifest(LinuxManifest): # 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 + 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") diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index 920211ce39..cc9fa598f2 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -13,7 +13,8 @@ include(LLXML) include(LScript) include(Linking) include(Tut) -include(Boost) + +include(GoogleMock) include_directories( ${LLCOMMON_INCLUDE_DIRS} @@ -24,6 +25,7 @@ include_directories( ${LLVFS_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS} + ${GOOGLEMOCK_INCLUDE_DIRS} ) set(test_SOURCE_FILES @@ -60,6 +62,7 @@ set(test_SOURCE_FILES set(test_HEADER_FILES CMakeLists.txt + debug.h llpipeutil.h llsdtraits.h lltut.h @@ -93,6 +96,8 @@ target_link_libraries(test ${LLXML_LIBRARIES} ${LSCRIPT_LIBRARIES} ${LLCOMMON_LIBRARIES} + ${EXPAT_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} ${APRICONV_LIBRARIES} ${PTHREAD_LIBRARY} ${WINDOWS_LIBRARIES} @@ -112,16 +117,21 @@ 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) + +LL_TEST_COMMAND("${LD_LIBRARY_PATH}" + "${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} - ARGS - --output=${CMAKE_CURRENT_BINARY_DIR}/cpp_test_results.txt - --touch=${CMAKE_CURRENT_BINARY_DIR}/cpp_tests_ok.txt + COMMAND ${LL_TEST_COMMAND_value} 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 34f3530308..ca15314e69 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 bbb437c3f9..6322753253 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 c2e0a11156..7dfe8f40b7 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -54,6 +54,11 @@ # include "ctype_workaround.h" #endif +#ifndef LL_WINDOWS +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#endif + namespace tut { std::string sSourceDir; @@ -235,6 +240,11 @@ void wouldHaveCrashed(const std::string& message) int main(int argc, char **argv) { + // The following line must be executed to initialize Google Mock + // (and Google Test) before running the tests. +#ifndef LL_WINDOWS + ::testing::InitGoogleMock(&argc, argv); +#endif LLError::initForApplication("."); LLError::setFatalFunction(wouldHaveCrashed); LLError::setDefaultLevel(LLError::LEVEL_ERROR); diff --git a/indra/test_apps/llplugintest/CMakeLists.txt b/indra/test_apps/llplugintest/CMakeLists.txt index 789ead04fe..88c4ba8ad9 100644 --- a/indra/test_apps/llplugintest/CMakeLists.txt +++ b/indra/test_apps/llplugintest/CMakeLists.txt @@ -28,7 +28,7 @@ include_directories( if (DARWIN) include(CMakeFindFrameworks) - find_library(CARBON_LIBRARY Carbon) + find_library(COREFOUNDATION_LIBRARY CoreFoundation) endif (DARWIN) ### demo_plugin @@ -136,7 +136,7 @@ endif (DARWIN) # ${media_simple_test_SOURCE_FILES} #) # -#add_dependencies(media_simple_test copy_win_libs) +#add_dependencies(media_simple_test stage_third_party_libs) # #set_target_properties(media_simple_test # PROPERTIES @@ -177,7 +177,7 @@ endif (DARWIN) #) # #add_dependencies(media_plugin_test -# copy_win_libs +# stage_third_party_libs # SLPlugin # demo_media_plugin # ${LLPLUGIN_LIBRARIES} @@ -261,6 +261,7 @@ set(llmediaplugintest_SOURCE_FILES add_executable(llmediaplugintest WIN32 + MACOSX_BUNDLE ${llmediaplugintest_SOURCE_FILES} ) @@ -280,8 +281,15 @@ target_link_libraries(llmediaplugintest ${PLUGIN_API_WINDOWS_LIBRARIES} ) +if (DARWIN) + # The testbed needs to use a couple of CoreFoundation calls now, to deal with being a bundled app. + target_link_libraries(llmediaplugintest + ${COREFOUNDATION_LIBRARY} + ) +endif (DARWIN) + add_dependencies(llmediaplugintest - copy_win_libs + stage_third_party_libs SLPlugin media_plugin_quicktime media_plugin_webkit @@ -300,22 +308,64 @@ endif (DARWIN OR LINUX) # Gather build products of the various dependencies into the build directory for the testbed. +if (DARWIN) + # path inside the app bundle where we'll need to copy plugins and other related files + set(PLUGINS_DESTINATION_DIR + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/llmediaplugintest.app/Contents/Resources + ) + + # create the Contents/Resources directory + add_custom_command( + TARGET llmediaplugintest POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS + -E + make_directory + ${PLUGINS_DESTINATION_DIR} + COMMENT "Creating Resources directory in app bundle." + ) + + # copy the llcommon dylib and its dependencies to Contents/Resources. + get_target_property(BUILT_LLCOMMON llcommon LOCATION) + add_custom_command(TARGET llmediaplugintest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_LLCOMMON} ${PLUGINS_DESTINATION_DIR} + DEPENDS ${BUILT_LLCOMMON} + ) + # FIXME: these paths should come from somewhere reliable. The canonical list seems to be in indra/newview/viewer_manifest.py + add_custom_command(TARGET llmediaplugintest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.3.7.dylib ${PLUGINS_DESTINATION_DIR} + DEPENDS ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.3.7.dylib + ) + add_custom_command(TARGET llmediaplugintest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.0.3.8.dylib ${PLUGINS_DESTINATION_DIR} + DEPENDS ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.0.3.8.dylib + ) + add_custom_command(TARGET llmediaplugintest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${ARCH_PREBUILT_DIRS_RELEASE}/libexpat.0.5.0.dylib ${PLUGINS_DESTINATION_DIR} + DEPENDS ${ARCH_PREBUILT_DIRS_RELEASE}/libexpat.0.5.0.dylib + ) +else (DARWIN) + set(PLUGINS_DESTINATION_DIR + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + ) +endif (DARWIN) + get_target_property(BUILT_SLPLUGIN SLPlugin LOCATION) add_custom_command(TARGET llmediaplugintest POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_SLPLUGIN} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_SLPLUGIN} ${PLUGINS_DESTINATION_DIR} DEPENDS ${BUILT_SLPLUGIN} ) if (DARWIN OR WINDOWS) get_target_property(BUILT_WEBKIT_PLUGIN media_plugin_webkit LOCATION) add_custom_command(TARGET llmediaplugintest POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_WEBKIT_PLUGIN} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_WEBKIT_PLUGIN} ${PLUGINS_DESTINATION_DIR} DEPENDS ${BUILT_WEBKIT_PLUGIN} ) get_target_property(BUILT_QUICKTIME_PLUGIN media_plugin_quicktime LOCATION) add_custom_command(TARGET llmediaplugintest POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_QUICKTIME_PLUGIN} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_QUICKTIME_PLUGIN} ${PLUGINS_DESTINATION_DIR} DEPENDS ${BUILT_QUICKTIME_PLUGIN} ) @@ -325,18 +375,137 @@ if (DARWIN OR WINDOWS) COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bookmarks.txt ${CMAKE_CURRENT_BINARY_DIR}/ DEPENDS ${BUILT_LLMEDIAPLUGINTEST} ) - # also copy it to the build configuration directory, which is what the mac wants... + # also copy it to the same place as SLPlugin, which is what the mac wants... add_custom_command(TARGET llmediaplugintest POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bookmarks.txt ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bookmarks.txt ${PLUGINS_DESTINATION_DIR} DEPENDS ${BUILT_LLMEDIAPLUGINTEST} ) endif (DARWIN OR WINDOWS) if (DARWIN) add_custom_command(TARGET llmediaplugintest POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/libllqtwebkit.dylib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/libllqtwebkit.dylib ${PLUGINS_DESTINATION_DIR} DEPENDS ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/libllqtwebkit.dylib ) endif (DARWIN) +if(WINDOWS) + #******************** + # Plugin test library deploy + # + # Debug config runtime files required for the plugin test mule + set(plugintest_debug_src_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/debug") + set(plugintest_debug_files + libeay32.dll + libglib-2.0-0.dll + libgmodule-2.0-0.dll + libgobject-2.0-0.dll + libgthread-2.0-0.dll + qtcored4.dll + qtguid4.dll + qtnetworkd4.dll + qtopengld4.dll + qtwebkitd4.dll + ssleay32.dll + ) + copy_if_different( + ${plugintest_debug_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/Debug" + out_targets + ${plugintest_debug_files} + ) + set(plugin_test_targets ${plugin_test_targets} ${out_targets}) + + # Debug config runtime files required for the plugin test mule (Qt image format plugins) + set(plugintest_debug_src_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/debug/imageformats") + set(plugintest_debug_files + qgifd4.dll + qicod4.dll + qjpegd4.dll + qmngd4.dll + qsvgd4.dll + qtiffd4.dll + ) + copy_if_different( + ${plugintest_debug_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/Debug/imageformats" + out_targets + ${plugintest_debug_files} + ) + set(plugin_test_targets ${plugin_test_targets} ${out_targets}) + + # Release & ReleaseDebInfo config runtime files required for the plugin test mule + set(plugintest_release_src_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/release") + set(plugintest_release_files + libeay32.dll + libglib-2.0-0.dll + libgmodule-2.0-0.dll + libgobject-2.0-0.dll + libgthread-2.0-0.dll + qtcore4.dll + qtgui4.dll + qtnetwork4.dll + qtopengl4.dll + qtwebkit4.dll + ssleay32.dll + ) + copy_if_different( + ${plugintest_release_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/Release" + out_targets + ${plugintest_release_files} + ) + set(plugin_test_targets ${plugin_test_targets} ${out_targets}) + + copy_if_different( + ${plugintest_release_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" + out_targets + ${plugintest_release_files} + ) + set(plugin_test_targets ${plugin_test_targets} ${out_targets}) + + # Release & ReleaseDebInfo config runtime files required for the plugin test mule (Qt image format plugins) + set(plugintest_release_src_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/release/imageformats") + set(plugintest_release_files + qgif4.dll + qico4.dll + qjpeg4.dll + qmng4.dll + qsvg4.dll + qtiff4.dll + ) + copy_if_different( + ${plugintest_release_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/Release/imageformats" + out_targets + ${plugintest_release_files} + ) + set(plugin_test_targets ${plugin_test_targets} ${out_targets}) + + copy_if_different( + ${plugintest_release_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo/imageformats" + out_targets + ${plugintest_release_files} + ) + set(plugin_test_targets ${plugin_test_targets} ${out_targets}) + + copy_if_different( + "${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}" + "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}" + out_targets + llcommon.dll libapr-1.dll libaprutil-1.dll libapriconv-1.dll + ) + set(plugin_test_targets ${plugin_test_targets} ${out_targets}) + + add_custom_target(copy_plugintest_libs ALL + DEPENDS + ${plugin_test_targets} + llcommon + ) + + add_dependencies(llmediaplugintest copy_plugintest_libs) + +endif(WINDOWS) diff --git a/indra/test_apps/llplugintest/llmediaplugintest.cpp b/indra/test_apps/llplugintest/llmediaplugintest.cpp index 7869763302..234422b68a 100644 --- a/indra/test_apps/llplugintest/llmediaplugintest.cpp +++ b/indra/test_apps/llplugintest/llmediaplugintest.cpp @@ -44,6 +44,7 @@ #if __APPLE__ #include <GLUT/glut.h> + #include <CoreFoundation/CoreFoundation.h> #else #define FREEGLUT_STATIC #include "GL/freeglut.h" @@ -137,6 +138,8 @@ LLMediaPluginTest::LLMediaPluginTest( int app_window, int window_width, int wind mMediaBrowserControlBackButtonFlag( true ), mMediaBrowserControlForwardButtonFlag( true ), mHomeWebUrl( "http://www.google.com/" ) + //mHomeWebUrl( "file:///C|/Program Files/QuickTime/Sample.mov" ) + //mHomeWebUrl( "http://movies.apple.com/movies/wb/watchmen/watchmen-tlr2_480p.mov" ) { // debugging spam std::cout << std::endl << " GLUT version: " << "3.7.6" << std::endl; // no way to get real version from GLUT @@ -2007,6 +2010,11 @@ void LLMediaPluginTest::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent e std::cerr << "Media event: MEDIA_EVENT_STATUS_TEXT_CHANGED, new status text is: " << self->getStatusText() << std::endl; break; + case MEDIA_EVENT_NAME_CHANGED: + std::cerr << "Media event: MEDIA_EVENT_NAME_CHANGED, new name is: " << self->getMediaName() << std::endl; + glutSetWindowTitle( self->getMediaName().c_str() ); + break; + case MEDIA_EVENT_LOCATION_CHANGED: { std::cerr << "Media event: MEDIA_EVENT_LOCATION_CHANGED, new uri is: " << self->getLocation() << std::endl; @@ -2033,6 +2041,10 @@ void LLMediaPluginTest::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent e case MEDIA_EVENT_PLUGIN_FAILED: std::cerr << "Media event: MEDIA_EVENT_PLUGIN_FAILED" << std::endl; break; + + case MEDIA_EVENT_PLUGIN_FAILED_LAUNCH: + std::cerr << "Media event: MEDIA_EVENT_PLUGIN_FAILED_LAUNCH" << std::endl; + break; } } @@ -2107,6 +2119,25 @@ void glutMouseButton( int button, int state, int x, int y ) // int main( int argc, char* argv[] ) { +#if LL_DARWIN + // Set the current working directory to <application bundle>/Contents/Resources/ + CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle()); + if(resources_url != NULL) + { + CFStringRef resources_string = CFURLCopyFileSystemPath(resources_url, kCFURLPOSIXPathStyle); + CFRelease(resources_url); + if(resources_string != NULL) + { + char buffer[PATH_MAX] = ""; + if(CFStringGetCString(resources_string, buffer, sizeof(buffer), kCFStringEncodingUTF8)) + { + chdir(buffer); + } + CFRelease(resources_string); + } + } +#endif + glutInit( &argc, argv ); glutInitDisplayMode( GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB ); 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..7a30315b9a --- /dev/null +++ b/indra/viewer_components/login/lllogin.cpp @@ -0,0 +1,374 @@ +/** + * @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& state, const std::string& change, + const LLSD& data = LLSD()) + { + LLSD status_data; + status_data["state"] = state; + status_data["change"] = change; + status_data["progress"] = 0.0f; + + if(mAuthResponse.has("transfer_rate")) + { + status_data["transfer_rate"] = mAuthResponse["transfer_rate"]; + } + + if(data.isDefined()) + { + 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("offline", "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("offline", "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("offline", "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. + if (mAuthResponse["responses"]["login"].asString() == "true") + { + sendProgressEvent("online", "connect", mAuthResponse["responses"]); + } + else + { + sendProgressEvent("offline", "fail.login", 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", "fail.login", mAuthResponse["responses"]); +} + +void LLLogin::Impl::disconnect() +{ + sendProgressEvent("offline", "disconnect"); +} + +//********************* +// 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..a8ae2883d5 --- /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() const { 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()["change"].asString(), "srvrequest"); + + dummyLLAres.sendReply(); + + // Test Authenticating State prior to first response. + ensure_equals("Auth state 1", listener.lastEvent()["change"].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()["change"].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()["change"].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()["change"].asString(), "srvrequest"); + + dummyLLAres.sendReply(); + + ensure_equals("Auth state", listener.lastEvent()["change"].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()["change"].asString(), "srvrequest"); + + dummyLLAres.sendReply(); + + ensure_equals("Auth state", listener.lastEvent()["change"].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 44224664ca..2ab458d88b 100644 --- a/install.xml +++ b/install.xml @@ -71,9 +71,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> @@ -92,9 +92,9 @@ <key>windows</key> <map> <key>md5sum</key> - <string>b9d23a69a25fdeed96dcc3bf696b6514</string> + <string>a02619c1e30a3db02d3883bf1ad7a1e6</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.3.8-windows-20090911.tar.bz2</uri> </map> </map> </map> @@ -132,9 +132,9 @@ <key>windows</key> <map> <key>md5sum</key> - <string>6a53b02a07527de680f1336e20f74f08</string> + <string>4e2d4de03dce8a991a5727c15a284e8d</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ares-1.4.0-windows-20080723.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ares-1.3.0-windows-20090917.tar.bz2</uri> </map> </map> </map> @@ -193,30 +193,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>4f05166629caa4c132a7448eefb8d592</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.39.0-windows-20090917.tar.bz2</uri> </map> </map> </map> @@ -254,9 +254,9 @@ <key>windows</key> <map> <key>md5sum</key> - <string>8c9d135f0e7cd1fae5681d4595942ee3</string> + <string>8dc4e818c2d6fbde76e9a5e34f4ffa72</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/curl-7.16.4-windows-20090306.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/curl-7.19.6-windows-20090917b.tar.bz2</uri> </map> </map> </map> @@ -326,9 +326,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> @@ -347,9 +347,9 @@ <key>windows</key> <map> <key>md5sum</key> - <string>cca5ca3759f645d4a124d4b96df7f717</string> + <string>5dbbdb4a9b5bec86d180ef20a5f8ccfb</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/expat-1.95.8-windows-20080715.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/expat-1.95.8-windows-20090917.tar.bz2</uri> </map> </map> </map> @@ -450,9 +450,9 @@ <key>windows</key> <map> <key>md5sum</key> - <string>953027c7718176aace3fd0766e3c4a73</string> + <string>88980fd6d91ac541b62dea877ebe6ba6</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/freetype-2.3.9-windows-20090814.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/freetype-2.3.9-windows-20090917.tar.bz2</uri> </map> </map> </map> @@ -583,6 +583,39 @@ </map> </map> </map> + <key>googlemock</key> + <map> + <key>copyright</key> + <string>Copyright 2008, Google Inc.</string> + <key>description</key> + <string>Google C++ Mocking Framework (or Google Mock for short) is a library for writing and using C++ mock classes.</string> + <key>license</key> + <string>bsd</string> + <key>packages</key> + <map> + <key>darwin</key> + <map> + <key>md5sum</key> + <string>4863e9fea433d0a4be761ea5d3e8346a</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/googlemock-1.1.0-darwin-20090626.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>877dabecf84339690191c6115c76366e</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/googlemock-1.1.0-linux32-20090527.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>f601a82ea91030974072da8924cae41e</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/googlemock-1.1.0-windows-20090921.tar.bz2</uri> + </map> + </map> + </map> <key>gstreamer</key> <map> <key>license</key> @@ -705,9 +738,9 @@ <key>windows</key> <map> <key>md5sum</key> - <string>e40d2df9aaefb3bd57289fe96766353a</string> + <string>6a6bb0143a2561e3276dab4bcfa425ef</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/jpeglib-6b-windows-20080723.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/jpeglib-6b-windows-20090917a.tar.bz2</uri> </map> </map> </map> @@ -859,9 +892,9 @@ anguage Infrstructure (CLI) international standard</string> <key>windows</key> <map> <key>md5sum</key> - <string>a3975dcdb9a3ba0ca179b673b5e9b55e</string> + <string>c781cd9846cf20afb90ac40ad1a0ce9d</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/libpng-1.2.35-windows-20090306.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/libpng-1.2.35-windows-20090917.tar.bz2</uri> </map> </map> </map> @@ -929,9 +962,9 @@ anguage Infrstructure (CLI) international standard</string> <key>windows</key> <map> <key>md5sum</key> - <string>0edde7f54c0229cd6f615917198becaf</string> + <string>6c6282025d1b8cd9e70c0f858a14fdca</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/llqtwebkit-windows-20090920.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/llqtwebkit-windows-20091001.tar.bz2</uri> </map> </map> </map> @@ -1024,9 +1057,9 @@ anguage Infrstructure (CLI) international standard</string> <key>windows</key> <map> <key>md5sum</key> - <string>8ac100b5b711231b06be891c91cb68f3</string> + <string>f0df8a1e60991095e3adca1450b8c9c0</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ndofdev-windows-20080723.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/ndofdev-windows-20090917.tar.bz2</uri> </map> </map> </map> @@ -1186,6 +1219,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>216cb6217a06c64dfae30a55ab8b975c</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-darwin-20090923.tar.bz2</uri> + </map> + <key>linux</key> + <map> + <key>md5sum</key> + <string>c5c2f73847c126e679d925beab48c7d4</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-linux-20090427.tar.bz2</uri> + </map> + <key>linux32</key> + <map> + <key>md5sum</key> + <string>c5c2f73847c126e679d925beab48c7d4</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-linux32-20090427.tar.bz2</uri> + </map> + <key>linux64</key> + <map> + <key>md5sum</key> + <string>c5c2f73847c126e679d925beab48c7d4</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-linux64-20090427.tar.bz2</uri> + </map> + <key>windows</key> + <map> + <key>md5sum</key> + <string>c5c2f73847c126e679d925beab48c7d4</string> + <key>url</key> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/pth-2.0.7-windows-20090427.tar.bz2</uri> + </map> + </map> + </map> <key>quicktime</key> <map> <key>copyright</key> @@ -1341,9 +1421,9 @@ anguage Infrstructure (CLI) international standard</string> <key>windows</key> <map> <key>md5sum</key> - <string>789988ea1fd8615137f843fbbe5b6bfa</string> + <string>262629bcaa39dcf7266caa50da01a599</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-20091016.tar.bz2</uri> </map> </map> </map> @@ -1381,9 +1461,9 @@ anguage Infrstructure (CLI) international standard</string> <key>windows</key> <map> <key>md5sum</key> - <string>ddb09c2dd7d2caed1bd7cc1686c4214c</string> + <string>73baf52a740d151fddbc2a008369c462</string> <key>url</key> - <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/zlib-1.2.3-windows-20090505.tar.bz2</uri> + <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/zlib-1.2.3-windows-20090921.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 diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg index 8dce6bef6d..a5a99d79f0 100644 --- a/scripts/messages/message_template.msg +++ b/scripts/messages/message_template.msg @@ -5288,6 +5288,10 @@ version 2.0 { AgentLegacyAccess U8 } { AgentMaxAccess U8 } } + { + AgentInfo Variable + { Flags U32 } + } } // ChildAgentAlive diff --git a/scripts/update_version_files.py b/scripts/update_version_files.py index 9081941521..ee1ce69a15 100755 --- a/scripts/update_version_files.py +++ b/scripts/update_version_files.py @@ -241,23 +241,17 @@ def main(): if update_server: server_version = new_version else: - # Assume we're updating just the build number - cl = '%s info "%s"' % (svn, src_root) - status, output = _getstatusoutput(cl) - if verbose: - print - print "svn info output:" - print "----------------" - print output - - branch_match = svn_branch_re.search(output) - revision_match = svn_revision_re.search(output) - if not branch_match or not revision_match: - print "Failed to execute svn info, output follows:" - print output + + if llversion.using_svn(): + revision = llversion.get_svn_revision() + branch = llversion.get_svn_branch() + elif llversion.using_hg(): + revision = llversion.get_hg_changeset() + branch = llversion.get_hg_repo() + else: + print >>sys.stderr, "ERROR: could not determine revision and branch" return -1 - branch = branch_match.group(1) - revision = revision_match.group(1) + if skip_on_branch_re and skip_on_branch_re.match(branch): print "Release Candidate Build, leaving version files untouched." return 0 |