diff options
418 files changed, 21267 insertions, 9954 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..499bf19afd 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -1,4 +1,5 @@  # -*- cmake -*- +include(LLTestCommand)  MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources)    # Given a project name and a list of sourcefiles (with optional properties on each), @@ -13,6 +14,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 +44,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 +56,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 +103,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 +133,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) @@ -212,12 +226,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..b7d8b32030 100644 --- a/indra/llcommon/lldate.h +++ b/indra/llcommon/lldate.h @@ -35,6 +35,8 @@  #ifndef LL_LLDATE_H  #define LL_LLDATE_H +#include "linden_common.h" +  #include <iosfwd>  #include <string> @@ -46,7 +48,7 @@   *   * The date class represents a point in time after epoch - 1970-01-01.   */ -class LLDate +class LL_COMMON_API LLDate  {  public:  	/**  @@ -156,10 +158,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/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/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..cad1fd1a33 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);  }  //************************************ diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 123de12398..366f146821 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 diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index 815260dff3..8e56951dbf 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -36,6 +36,7 @@  #include "llfloater.h"  #include "llmultifloater.h" +#include "llfloaterreglistener.h"  //******************************************************* @@ -45,6 +46,8 @@ LLFloaterReg::instance_map_t LLFloaterReg::sInstanceMap;  LLFloaterReg::build_map_t LLFloaterReg::sBuildMap;  std::map<std::string,std::string> LLFloaterReg::sGroupMap; +static LLFloaterRegListener sFloaterRegListener("LLFloaterReg"); +  //*******************************************************  //static diff --git a/indra/llui/llfloaterreg.h b/indra/llui/llfloaterreg.h index 7edac43c96..451bd1dbe3 100644 --- a/indra/llui/llfloaterreg.h +++ b/indra/llui/llfloaterreg.h @@ -70,6 +70,7 @@ public:  	typedef std::map<std::string, BuildData> build_map_t;  private: +	friend class LLFloaterRegListener;  	static instance_list_t sNullInstanceList;  	static instance_map_t sInstanceMap;  	static build_map_t sBuildMap; diff --git a/indra/llui/llfloaterreglistener.cpp b/indra/llui/llfloaterreglistener.cpp new file mode 100644 index 0000000000..cb8fa6dfda --- /dev/null +++ b/indra/llui/llfloaterreglistener.cpp @@ -0,0 +1,66 @@ +/** + * @file   llfloaterreglistener.cpp + * @author Nat Goodspeed + * @date   2009-08-12 + * @brief  Implementation for llfloaterreglistener. + *  + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llfloaterreglistener.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llfloaterreg.h" + +LLFloaterRegListener::LLFloaterRegListener(const std::string& pumpName): +    LLDispatchListener(pumpName, "op") +{ +    add("getBuildMap",  &LLFloaterRegListener::getBuildMap,  LLSD().insert("reply", LLSD())); +    LLSD requiredName; +    requiredName["name"] = LLSD(); +    add("showInstance", &LLFloaterRegListener::showInstance, requiredName); +    add("hideInstance", &LLFloaterRegListener::hideInstance, requiredName); +    add("toggleInstance", &LLFloaterRegListener::toggleInstance, requiredName); +} + +void LLFloaterRegListener::getBuildMap(const LLSD& event) const +{ +    // Honor the "reqid" convention by echoing event["reqid"] in our reply packet. +    LLReqID reqID(event); +    LLSD reply(reqID.makeResponse()); +    // Build an LLSD map that mirrors sBuildMap. Since we have no good way to +    // represent a C++ callable in LLSD, the only part of BuildData we can +    // store is the filename. For each LLSD map entry, it would be more +    // extensible to store a nested LLSD map containing a single key "file" -- +    // but we don't bother, simply storing the string filename instead. +    for (LLFloaterReg::build_map_t::const_iterator mi(LLFloaterReg::sBuildMap.begin()), +                                                   mend(LLFloaterReg::sBuildMap.end()); +         mi != mend; ++mi) +    { +        reply[mi->first] = mi->second.mFile; +    } +    // Send the reply to the LLEventPump named in event["reply"]. +    LLEventPumps::instance().obtain(event["reply"]).post(reply); +} + +void LLFloaterRegListener::showInstance(const LLSD& event) const +{ +    LLFloaterReg::showInstance(event["name"], event["key"], event["focus"]); +} + +void LLFloaterRegListener::hideInstance(const LLSD& event) const +{ +    LLFloaterReg::hideInstance(event["name"], event["key"]); +} + +void LLFloaterRegListener::toggleInstance(const LLSD& event) const +{ +    LLFloaterReg::toggleInstance(event["name"], event["key"]); +} diff --git a/indra/llui/llfloaterreglistener.h b/indra/llui/llfloaterreglistener.h new file mode 100644 index 0000000000..58d2c07936 --- /dev/null +++ b/indra/llui/llfloaterreglistener.h @@ -0,0 +1,35 @@ +/** + * @file   llfloaterreglistener.h + * @author Nat Goodspeed + * @date   2009-08-12 + * @brief  Wrap (subset of) LLFloaterReg API with an event API + *  + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLFLOATERREGLISTENER_H) +#define LL_LLFLOATERREGLISTENER_H + +#include "lleventdispatcher.h" +#include <string> + +class LLSD; + +/// Event API wrapper for LLFloaterReg +class LLFloaterRegListener: public LLDispatchListener +{ +public: +    /// As all public LLFloaterReg methods are static, there's no point in +    /// binding an LLFloaterReg instance. +    LLFloaterRegListener(const std::string& pumpName); + +private: +    void getBuildMap(const LLSD& event) const; +    void showInstance(const LLSD& event) const; +    void hideInstance(const LLSD& event) const; +    void toggleInstance(const LLSD& event) const; +}; + +#endif /* ! defined(LL_LLFLOATERREGLISTENER_H) */ diff --git a/indra/llui/llfunctorregistry.cpp b/indra/llui/llfunctorregistry.cpp index 0c5b1655b1..5f9644f258 100644 --- a/indra/llui/llfunctorregistry.cpp +++ b/indra/llui/llfunctorregistry.cpp @@ -31,6 +31,7 @@   * $/LicenseInfo$   **/ +#include "linden_common.h"  #include "llfunctorregistry.h"  // This is a default functor always resident in the system. diff --git a/indra/llui/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/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..4cb3b92476 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", NULL);  ////// 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..a3af251a3c --- /dev/null +++ b/indra/newview/llappviewerlistener.cpp @@ -0,0 +1,37 @@ +/** + * @file   llappviewerlistener.cpp + * @author Nat Goodspeed + * @date   2009-06-23 + * @brief  Implementation for llappviewerlistener. + *  + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "llviewerprecompiledheaders.h" +// associated header +#include "llappviewerlistener.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llappviewer.h" + +LLAppViewerListener::LLAppViewerListener(const std::string& pumpname, LLAppViewer* llappviewer): +    LLDispatchListener(pumpname, "op"), +    mAppViewer(llappviewer) +{ +    // add() every method we want to be able to invoke via this event API. +    add("requestQuit", &LLAppViewerListener::requestQuit); +} + +void LLAppViewerListener::requestQuit(const LLSD& event) +{ +    if(mAppViewer == NULL) +    { +        mAppViewer = LLAppViewer::instance(); +    } +    mAppViewer->requestQuit(); +} diff --git a/indra/newview/llappviewerlistener.h b/indra/newview/llappviewerlistener.h new file mode 100644 index 0000000000..d702f605ef --- /dev/null +++ b/indra/newview/llappviewerlistener.h @@ -0,0 +1,34 @@ +/** + * @file   llappviewerlistener.h + * @author Nat Goodspeed + * @date   2009-06-18 + * @brief  Wrap subset of LLAppViewer API in event API + *  + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLAPPVIEWERLISTENER_H) +#define LL_LLAPPVIEWERLISTENER_H + +#include "lleventdispatcher.h" + +class LLAppViewer; +class LLSD; + +/// Listen on an LLEventPump with specified name for LLAppViewer request events. +class LLAppViewerListener: public LLDispatchListener +{ +public: +    /// Specify the pump name on which to listen, and bind the LLAppViewer +    /// instance to use (e.g. LLAppViewer::instance()). +    LLAppViewerListener(const std::string& pumpname, LLAppViewer* llappviewer); + +private: +    void requestQuit(const LLSD& event); + +    LLAppViewer* mAppViewer; +}; + +#endif /* ! defined(LL_LLAPPVIEWERLISTENER_H) */ diff --git a/indra/newview/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 68a4f31730..f6f23055ac 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..63953580fe 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -1120,7 +1120,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 +1155,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/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..1a183d61f3 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)  	{ -		mMediaSource->loadURI( mHomeURL ); +		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) +	{ +		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,6 @@ 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);  		}  		break; diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index c9ba5841e9..05c67eda47 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(); @@ -278,7 +281,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..1704f63376 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,72 @@ 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::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event)  {  	switch(event) @@ -1736,6 +1867,10 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin,  						{  							block_navigation = true;  						} +                        if (!block_navigation && !hasNavigatePermission(mep)) +                        { +                            block_navigation = true; +                        }  					}  					else  					{ @@ -1747,15 +1882,14 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin,  						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()); +						mediaNavigateBounceBack(face_index);  					}  					else  					{  						llinfos << "broadcasting navigate with URI " << new_location << llendl; -						sObjectMediaNavigateClient->navigate(this, face_index, new_location); +						sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this), face_index, new_location);  					}  				}  				break; @@ -1781,7 +1915,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 +2008,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..90dfa2204b 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -233,7 +233,18 @@ 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 mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event);  	// Sync the given media data with the impl and the given te @@ -244,6 +255,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/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  | 
