diff --git a/.gitignore b/.gitignore index 4b9a7a4cd6..b47f28773e 100755 --- a/.gitignore +++ b/.gitignore @@ -111,3 +111,5 @@ compile_commands.json # ignore tracy for now indra/tracy firestorm.code-workspace + +.cache/clangd/index/ diff --git a/BuildParams b/BuildParams index 27ae40767a..dda25e3e63 100644 --- a/BuildParams +++ b/BuildParams @@ -16,6 +16,9 @@ build_Linux_Doxygen = true # Need viewer-build-variables as well as other shared repositories buildscripts_shared_more_NAMEs="build_secrets build_variables git_hooks" +# Python 3 / SL-15742 +BUILDSCRIPTS_PY3 = "true" + ################################################################ #### Examples of how to set the viewer_channel #### # @@ -36,6 +39,7 @@ buildscripts_shared_more_NAMEs="build_secrets build_variables git_hooks" ################################################################ viewer_channel = "Second Life Test" + ################################################################ # Special packaging parameters. # These parameters can be used to create additional packages diff --git a/README_BUILD_FIRESTORM_LINUX.txt b/README_BUILD_FIRESTORM_LINUX.txt index 2e856043a1..3aa979da25 100755 --- a/README_BUILD_FIRESTORM_LINUX.txt +++ b/README_BUILD_FIRESTORM_LINUX.txt @@ -1,4 +1,4 @@ -First, make sure gcc-5.4 and g++-5.4 are installed. +First, make sure gcc-7.5.0 and g++-7.5.0 are installed. If you want to use licensed FMOD or KDU build libraries (they are optional) you have to provision these yourself. If you're licensing these with Phoenix/Firestorm, ask for the libraries for fmod and kdu. Put them into: @@ -27,6 +27,12 @@ Other examples: autobuild build -A64 -c ReleaseFS --no-configure -- --clean # Clean rebuild autobuild build -A64 -c ReleaseFS -- --package # Complete a build and package it into a tarball +When using the --package switch you can set the XZ_DEFAULTS variable to -T0 to use all available CPU cores +to create the .tar.xz file. This can significantly reduce the time needed to create the archive, but it will +use a lot more memory. For example: + export XZ_DEFAULTS="-T0" + autobuild build -A64 -c ReleaseFS_open -- --package + If you want to build with clang you can call autobuild like this: CC=clang CXX=clang++ autobuild configure -A64 -c ReleaseFS diff --git a/autobuild.xml b/autobuild.xml index c66dde9b47..9f3a56a688 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3,6 +3,118 @@ installables + gstreamer10 + + copyright + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + license + LGPL + license_file + LICENSES/gstreamer.txt + name + gstreamer10 + platforms + + linux + + archive + + hash + 01f39ecf80dae64e30402ac384035b3e + url + http://downloads.phoenixviewer.com/gstreamer10-1.6.3.201605191852-linux-201605191852.tar.bz2 + + name + linux + + + version + 0.10.6.294903 + + breakpad + + copyright + Copyright (C) google + license + BSD + license_file + LICENSES/google_breakpad.txt + name + breakpad + platforms + + linux64 + + archive + + hash + 482c2b25bbfd25edc058a02f82da39b2 + url + http://3p.firestormviewer.org/breakpad-4708e6fb-linux64_bionic-220392253.tar.bz2 + + name + linux + + + version + 2.48.0 + + glib + + copyright + Copyright (C) glib project + license + LGPL + license_file + LICENSES/glib.txt + name + glib + platforms + + linux64 + + archive + + hash + 9c93ba8b8af97fc8379f77de77e1540a + url + http://3p.firestormviewer.org/glib-2.48.0.202301938-linux64-202301938.tar.bz2 + + name + linux + + + version + 2.48.0 + + fltk + + copyright + Copyright (C) fltk project + license + LGPL/fltk + license_file + LICENSES/fltk.txt + name + fltk + platforms + + linux64 + + archive + + hash + 81fe1e927e4fe3c5e5f15ce6219ca883 + url + http://3p.firestormviewer.org/fltk-1.3.5.202282121-linux64-202282121.tar.bz2 + + name + linux + + + version + 1.3.5 + jemalloc copyright @@ -424,9 +536,9 @@ archive hash - 29eb0d338020242454b77b278c11d3e7 + 401f317fbb67623c97a7b9d8b6627ef1 url - http://3p.firestormviewer.org/boost-1.72-linux64-202021511.tar.bz2 + http://3p.firestormviewer.org/boost-1.72-linux64_bionic-220402045.tar.bz2 name linux64 @@ -549,26 +661,14 @@ name darwin64 - linux - - archive - - hash - d83896f28716f34b7b49d61a23283c4c - url - http://3p.firestormviewer.org/colladadom-2.3.180871403-linux-180871403.tar.bz2 - - name - linux - linux64 archive hash - df6fe4c80b96ec20b5ee6f56419e6b9f + 88ee58d6548deae6c306f125b6461d61 url - http://3p.firestormviewer.org/colladadom-2.3.202021526-linux64-202021526.tar.bz2 + http://3p.firestormviewer.org/colladadom-2.3.220402056-linux64_bionic-220402056.tar.bz2 name linux64 @@ -720,9 +820,9 @@ archive hash - 96dd770f246917589b776300a2d07f9e + 0bcccd248a5e4084af4026eee439816b url - http://3p.firestormviewer.org/curl-7.54.1.212891029-linux64-212891029.tar.bz2 + http://3p.firestormviewer.org/curl-7.81.0.220511906-linux64_bionic-220511906.tar.bz2 name linux64 @@ -787,48 +887,6 @@ version 5.1.25 - dbus_glib - - copyright - Copyright (C) Red Hat Inc. - description - D-Bus bindings for glib - license - Academic Free License v. 2.1 - license_file - LICENSES/dbus-glib.txt - name - dbus_glib - platforms - - linux - - archive - - hash - 9591dcb7efce2a770d77e907705e1492 - url - http://3p.firestormviewer.org/dbus_glib-0.76-linux-180871236.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 5a685a65a7066937ef580dcd5a90f9dc - url - http://3p.firestormviewer.org/dbus_glib-0.76-linux64-180841549.tar.bz2 - - name - linux64 - - - version - 0.76 - dictionaries copyright @@ -914,9 +972,9 @@ archive hash - 2f8fdc6950620cee3a526ede27e068aa + 8d532edd648448d78e6daa0cc5f821f9 url - http://3p.firestormviewer.org/dullahan-1.12.2.202109282040_91.1.23_g04c8d56_chromium-91.0.4472.164-linux64-212711840.tar.bz2 + http://3p.firestormviewer.org/dullahan-1.12.2.202202071031_91.1.23_g04c8d56_chromium-91.0.4472.164-linux64-220380931.tar.bz2 name linux64 @@ -1058,11 +1116,11 @@ archive hash - bde9eb3e53001584edb1af44e3b265a2 + 2db00aa4126d4ee8152fc49b03bb3fe1 hash_algorithm md5 url - file:///opt/firestorm/fmodstudio-2.02.04-darwin-213491614.tar.bz2 + file:///opt/firestorm/fmodstudio-2.02.05-darwin-220160006.tar.bz2 name darwin @@ -1577,46 +1635,6 @@ version 0.10.6.314267 - gtk-atk-pango-glib - - copyright - Copyright (various, see sources) - license - lgpl - license_file - LICENSES/gtk-atk-pango-glib.txt - name - gtk-atk-pango-glib - platforms - - linux - - archive - - hash - fb047d496c32cc3b9f99793ee6ebb1e3 - url - http://3p.firestormviewer.org/gtk_atk_pango_glib-2.1-linux-180871647.tar.bz2 - - name - linux - - linux64 - - archive - - hash - d918d894430c1d1d869b9f2e06570c65 - url - http://3p.firestormviewer.org/gtk_atk_pango_glib-2.1-linux64-180841902.tar.bz2 - - name - linux64 - - - version - 2.1 - havok-source copyright @@ -2443,18 +2461,18 @@ archive hash - 35f42f538f4dc3abdfc2b2c4a915d004 + 95cb09a712b7b61e992fe68ab7bf8c72 hash_algorithm md5 url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/87228/802959/llca-202109010216.563493-common-563493.tar.bz2 + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/92744/837149/llca-202201010217.567162-common-567162.tar.bz2 name common version - 202109010216.563493 + 202201010217.567162 llphysicsextensions_source @@ -2473,9 +2491,9 @@ archive hash - 14fac452271ebfba37ba5ddcf5bffa54 + da57838d80cf332f4a3026713a13f086 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/54842/510078/llphysicsextensions_source-1.0.538972-darwin64-538972.tar.bz2 + https://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/90708/824484/llphysicsextensions_source-1.0.565754-darwin64-565754.tar.bz2 name darwin64 @@ -2497,16 +2515,16 @@ archive hash - f3c066c1aebed8a6519a3e5ce64b9a3c + 28ad884012aa0bb70cf4101853af2f9a url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/54982/511796/llphysicsextensions_source-1.0.538972-windows-538972.tar.bz2 + https://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/90733/824570/llphysicsextensions_source-1.0.565768-windows-565768.tar.bz2 name windows version - 1.0.538972 + 1.0.565768 llphysicsextensions_stub @@ -3424,9 +3442,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - c42575ac8997de979eadb082c33a578e + b97d0f6570104277de92d0d3f2d1111d url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/81322/765512/uriparser-0.9.4-darwin64-559132.tar.bz2 + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/89474/816487/uriparser-0.9.4-darwin64-564957.tar.bz2 name darwin64 @@ -3460,9 +3478,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 901b1063556fc6b2575e745eef2bf744 + e2600c798e220cc98c1cc77341aee00d url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/81323/765528/uriparser-0.9.4-windows-559132.tar.bz2 + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/89476/816496/uriparser-0.9.4-windows-564957.tar.bz2 name windows @@ -3472,9 +3490,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 962c01d553f286c430102998129fb0d6 + 50d857117d31844fc8b84b07b795fd00 url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/81324/765527/uriparser-0.9.4-windows64-559132.tar.bz2 + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/89475/816497/uriparser-0.9.4-windows64-564957.tar.bz2 name windows64 @@ -3502,9 +3520,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - a3c8357a2f5a62cd7de43181b02553bc + 33ed1bb3e24fbd3462da04fb3e917e94 url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/91396/829032/viewer_manager-2.0.566227-darwin64-566227.tar.bz2 + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/94814/850320/viewer_manager-3.0.568552-darwin64-568552.tar.bz2 name darwin64 @@ -3538,9 +3556,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 0654b449d9bdf3507664cf5caa67336f + 2ad8e04965ac8bddb7d351abe09bee07 url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/91397/829041/viewer_manager-2.0.566227-windows-566227.tar.bz2 + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/94813/850316/viewer_manager-3.0.568552-windows-568552.tar.bz2 name windows @@ -3551,7 +3569,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors source_type hg version - 2.0.566227 + 3.0.568552 vlc-bin @@ -3577,30 +3595,6 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors name darwin64 - linux - - archive - - hash - 9f9582031af3b78d6153ec296d6dde9f - url - http://3p.firestormviewer.org/vlc_bin-2.2.3-linux-201607071822-r16.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 5eace400c487011a678493fc520be24d - url - http://3p.firestormviewer.org/vlc_bin-2.2.3-linux-x64-201610182130-r16.tar.bz2 - - name - linux64 - windows archive diff --git a/doc/contributions.txt b/doc/contributions.txt index efe6e933e4..1f3ad67bb1 100755 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -281,6 +281,7 @@ Beq Janus SL-14766 SL-14927 SL-11300 + SL-16021 Beth Walcher Bezilon Kasei Biancaluce Robbiani @@ -1113,6 +1114,7 @@ Nicky Dasmijn OPEN-187 STORM-1937 OPEN-187 + SL-15234 STORM-2010 STORM-2082 MAINT-6665 @@ -1122,6 +1124,7 @@ Nicky Dasmijn SL-11072 SL-13141 SL-13642 + SL-16438 Nicky Perian OPEN-1 STORM-1087 diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 9d76be92c8..646ff1cfa2 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -112,6 +112,10 @@ if (NOT USE_BUGSPLAT) add_subdirectory(${LIBS_OPEN_PREFIX}llcrashlogger) endif (NOT USE_BUGSPLAT) +if( LINUX ) + add_subdirectory(${VIEWER_PREFIX}linux_crash_logger) +endif() + add_subdirectory(${LIBS_OPEN_PREFIX}llplugin) add_subdirectory(${LIBS_OPEN_PREFIX}llui) add_subdirectory(${LIBS_OPEN_PREFIX}viewer_components) @@ -159,6 +163,10 @@ endif (USE_BUGSPLAT) add_subdirectory(${VIEWER_PREFIX}newview) add_dependencies(viewer firestorm-bin) +set_target_properties( + firestorm-bin PROPERTIES + VS_DEBUGGER_WORKING_DIRECTORY "..\\..\\indra\\newview") + add_subdirectory(${VIEWER_PREFIX}doxygen EXCLUDE_FROM_ALL) if (LL_TESTS) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 6909a94e4b..fe319a587b 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -165,11 +165,6 @@ endif (WINDOWS) if (LINUX) set(CMAKE_SKIP_RPATH TRUE) - - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0.0 ) - message( FATAL_ERROR "GCC greater 9.4.0 is not supported. Recompile boost for support of GCC 10.0.0 and up." ) - endif() - # # And another hack for FORTIFY_SOURCE. Some distributions (for example Gentoo) define FORTIFY_SOURCE by default. # Check if this is the case, if yes, do not define it again. @@ -225,10 +220,6 @@ if (LINUX) endif (NOT USESYSTEMLIBS) set(CMAKE_CXX_FLAGS_DEBUG "-fno-inline ${CMAKE_CXX_FLAGS_DEBUG}") - - if( NOT (CMAKE_CXX_COMPILER MATCHES ".*clang") ) - set( CMAKE_CXX_FLAGS "-fabi-version=9 ${CMAKE_CXX_FLAGS}" ) - endif() endif (LINUX) diff --git a/indra/cmake/BuildVersion.cmake b/indra/cmake/BuildVersion.cmake index 3406e5fd15..ef67f9af90 100644 --- a/indra/cmake/BuildVersion.cmake +++ b/indra/cmake/BuildVersion.cmake @@ -19,34 +19,23 @@ if (NOT DEFINED VIEWER_SHORT_VERSION) # will be true in indra/, false in indra/n message(STATUS "Revision (from autobuild environment): ${VIEWER_VERSION_REVISION}") else (DEFINED ENV{revision}) - find_program(MERCURIAL hg) - find_program(SED sed) - if (DEFINED MERCURIAL AND DEFINED SED) + find_program(GIT git) + if (DEFINED GIT ) execute_process( - # FIRE-11737: Reverting to old revisions shows tip in build string - # This command gets the revision number of the current - # repository tip. This leads to confusion when - # building an earlier revision. Instead, we use - # "hg identify -n" to get the local revision number - # of the actual state of the repository. - #COMMAND ${MERCURIAL} log -r tip:0 --template '\\n' - #COMMAND ${WORDCOUNT} -l - #COMMAND ${SED} "s/ //g" - COMMAND ${MERCURIAL} identify -n - COMMAND ${SED} "s/+//" # [CR] Strip off any + from the revision number + COMMAND ${GIT} rev-list --count HEAD OUTPUT_VARIABLE VIEWER_VERSION_REVISION OUTPUT_STRIP_TRAILING_WHITESPACE ) if ("${VIEWER_VERSION_REVISION}" MATCHES "^[0-9]+$") - message(STATUS "Revision (from hg) ${VIEWER_VERSION_REVISION}") + message(STATUS "Revision (from git) ${VIEWER_VERSION_REVISION}") else ("${VIEWER_VERSION_REVISION}" MATCHES "^[0-9]+$") message(STATUS "Revision not set (repository not found?); using 0") set(VIEWER_VERSION_REVISION 0 ) endif ("${VIEWER_VERSION_REVISION}" MATCHES "^[0-9]+$") - else (DEFINED MERCURIAL AND DEFINED SED) - message(STATUS "Revision not set: 'hg' or 'sed' not found; using 0") + else (DEFINED GIT ) + message(STATUS "Revision not set: 'git' found; using 0") set(VIEWER_VERSION_REVISION 0) - endif (DEFINED MERCURIAL AND DEFINED SED) + endif (DEFINED GIT) endif (DEFINED ENV{revision}) message(STATUS "Building '${VIEWER_CHANNEL}' Version ${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}") else ( EXISTS ${VIEWER_VERSION_BASE_FILE} ) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 74d32defea..2d4031d101 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -20,7 +20,7 @@ set(cmake_SOURCE_FILES ConfigurePkgConfig.cmake CURL.cmake Copy3rdPartyLibs.cmake - DBusGlib.cmake + GLIB.cmake DeploySharedLibs.cmake Discord.cmake # Discord rich presence DragDrop.cmake diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index c262d7f209..519f68b53e 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -245,16 +245,9 @@ elseif(LINUX) # have to deal with if (NOT USESYSTEMLIBS) set(release_files - #libapr-1.so.0 - #libaprutil-1.so.0 - libatk-1.0.so #libdb-5.1.so ${EXPAT_COPY} - #libfreetype.so.6.6.2 - #libfreetype.so.6 #libGLOD.so - libgmodule-2.0.so - libgobject-2.0.so libhunspell-1.3.so.0.0.0 libopenal.so #libopenjpeg.so diff --git a/indra/cmake/DBusGlib.cmake b/indra/cmake/DBusGlib.cmake deleted file mode 100644 index 5e46b6711a..0000000000 --- a/indra/cmake/DBusGlib.cmake +++ /dev/null @@ -1,29 +0,0 @@ -# -*- cmake -*- -include(Prebuilt) - -if (USESYSTEMLIBS) - include(FindPkgConfig) - - pkg_check_modules(DBUSGLIB REQUIRED dbus-glib-1) - -elseif (LINUX) - use_prebuilt_binary(dbus_glib) - set(DBUSGLIB_FOUND ON FORCE BOOL) - set(DBUSGLIB_INCLUDE_DIRS - ${LIBS_PREBUILT_DIR}/include/dbus - ) - # We don't need to explicitly link against dbus-glib itself, because - # the viewer probes for the system's copy at runtime. - set(DBUSGLIB_LIBRARIES - gobject-2.0 - glib-2.0 - ) -endif (USESYSTEMLIBS) - -if (DBUSGLIB_FOUND) - set(DBUSGLIB ON CACHE BOOL "Build with dbus-glib message bus support.") -endif (DBUSGLIB_FOUND) - -if (DBUSGLIB) - add_definitions(-DLL_DBUS_ENABLED=1) -endif (DBUSGLIB) diff --git a/indra/cmake/FreeType.cmake b/indra/cmake/FreeType.cmake index df267985c4..79159843e1 100644 --- a/indra/cmake/FreeType.cmake +++ b/indra/cmake/FreeType.cmake @@ -9,7 +9,11 @@ else (USESYSTEMLIBS) use_prebuilt_binary(freetype) set(FREETYPE_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include) set(FREETYPE_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/freetype2) # Also add freetype2 to search dir, or some includes will fail. - set(FREETYPE_LIBRARIES freetype) + # Linux links this via UI.cmake + if( NOT LINUX ) + set(FREETYPE_LIBRARIES freetype) + endif() + # endif (USESYSTEMLIBS) link_directories(${FREETYPE_LIBRARY_DIRS}) diff --git a/indra/cmake/GLIB.cmake b/indra/cmake/GLIB.cmake new file mode 100644 index 0000000000..186195e078 --- /dev/null +++ b/indra/cmake/GLIB.cmake @@ -0,0 +1,11 @@ + +include(Prebuilt) + +if( LINUX ) + use_prebuilt_binary(glib) + set(GLIB_FOUND ON CACHE BOOL "Build against glib 2") + set(GLIB_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/glib-2.0 ${LIBS_PREBUILT_DIR}/lib/release/glib-2.0/include ) + set(GLIB_LIBRARIES libgobject-2.0.a libglib-2.0.a libffi.a libpcre.a) + set(GIO_LIBRARIES libgio-2.0.a libgmodule-2.0.a -lresolv ${GLIB_LIBRARIES} ) + add_definitions(-DLL_GLIB=1) +endif() diff --git a/indra/cmake/GStreamer10Plugin.cmake b/indra/cmake/GStreamer10Plugin.cmake new file mode 100644 index 0000000000..ded45da610 --- /dev/null +++ b/indra/cmake/GStreamer10Plugin.cmake @@ -0,0 +1,32 @@ +# -*- cmake -*- +include(Prebuilt) +include(GLIB) + +if (USESYSTEMLIBS) + include(FindPkgConfig) + + pkg_check_modules(GSTREAMER10 REQUIRED gstreamer-1.0) + pkg_check_modules(GSTREAMER10_PLUGINS_BASE REQUIRED gstreamer-plugins-base-1.0) +elseif (LINUX OR WINDOWS) + use_prebuilt_binary(gstreamer10) + use_prebuilt_binary(libxml2) + set(GSTREAMER10_FOUND ON FORCE BOOL) + set(GSTREAMER10_PLUGINS_BASE_FOUND ON FORCE BOOL) + set(GSTREAMER10_INCLUDE_DIRS + ${GLIB_INCLUDE_DIRS} + ${LIBS_PREBUILT_DIR}/include/gstreamer-1.0 + ${LIBS_PREBUILT_DIR}/include/libxml2 + ) + # We don't need to explicitly link against gstreamer itself, because + # LLMediaImplGStreamer probes for the system's copy at runtime. + set(GSTREAMER10_LIBRARIES) +endif (USESYSTEMLIBS) + +if (GSTREAMER10_FOUND AND GSTREAMER10_PLUGINS_BASE_FOUND) + set(GSTREAMER10 ON CACHE BOOL "Build with GStreamer-1.0 streaming media support.") +endif (GSTREAMER10_FOUND AND GSTREAMER10_PLUGINS_BASE_FOUND) + +if (GSTREAMER10) + add_definitions(-DLL_GSTREAMER10_ENABLED=1) +endif (GSTREAMER10) + diff --git a/indra/cmake/Hunspell.cmake b/indra/cmake/Hunspell.cmake index 06227b3fe2..34e0fb3c35 100644 --- a/indra/cmake/Hunspell.cmake +++ b/indra/cmake/Hunspell.cmake @@ -13,7 +13,7 @@ else (USESYSTEMLIBS) elseif(DARWIN) set(HUNSPELL_LIBRARY hunspell-1.3) elseif(LINUX) - set(HUNSPELL_LIBRARY hunspell-1.3) + set(HUNSPELL_LIBRARY libhunspell-1.3.a) else() message(FATAL_ERROR "Invalid platform") endif() diff --git a/indra/cmake/MediaPluginBase.cmake b/indra/cmake/MediaPluginBase.cmake index 2be035b641..17890c25fd 100644 --- a/indra/cmake/MediaPluginBase.cmake +++ b/indra/cmake/MediaPluginBase.cmake @@ -1,5 +1,26 @@ # -*- cmake -*- +# Try to find pulse header, if we got them we can use the linux volume catcher +if (LINUX) + include(GLIB) + + include_directories( ${GLIB_INCLUDE_DIRS} ) + + foreach( PULSE_FILE pulse/introspect.h pulse/context.h pulse/subscribe.h pulse/glib-mainloop.h ) + find_path( PULSE_FILE_${PULSE_FILE}_FOUND ${PULSE_FILE} NO_CACHE) + if( NOT PULSE_FILE_${PULSE_FILE}_FOUND ) + message( "Looking for ${PULSE_FILE} ... not found") + message( FATAL_ERROR "Pulse header not found" ) + else() + message( "Looking for ${PULSE_FILE} ... found") + endif() + endforeach() + + message( "Building with linux volume catcher" ) + set(LINUX_VOLUME_CATCHER linux_volume_catcher.cpp) + +endif() + set(MEDIA_PLUGIN_BASE_INCLUDE_DIRS ${LIBS_OPEN_DIR}/media_plugins/base/ diff --git a/indra/cmake/OPENAL.cmake b/indra/cmake/OPENAL.cmake index 1bbfff6f98..fe52d1a9c9 100644 --- a/indra/cmake/OPENAL.cmake +++ b/indra/cmake/OPENAL.cmake @@ -3,12 +3,14 @@ include(Linking) include(Prebuilt) if (LINUX) - set(OPENAL ON CACHE BOOL "Enable OpenAL") + use_prebuilt_binary(openal) #Always need the .so for voice + set(OPENAL OFF CACHE BOOL "Enable OpenAL") else (LINUX) set(OPENAL OFF CACHE BOOL "Enable OpenAL") endif (LINUX) if (OPENAL) + message( WARNING "Using OpenAL is discouraged due to no maintenance of the viewers openal integration, possible memory leaks and no support for streaming audio. Switch to fmodstudio if possible" ) set(OPENAL_LIB_INCLUDE_DIRS "${LIBS_PREBUILT_DIR}/include/AL") if (USESYSTEMLIBS) include(FindPkgConfig) diff --git a/indra/cmake/OpenGL.cmake b/indra/cmake/OpenGL.cmake index 3cb47a072c..c6ff24414b 100644 --- a/indra/cmake/OpenGL.cmake +++ b/indra/cmake/OpenGL.cmake @@ -16,3 +16,21 @@ endif (BUILD_HEADLESS) include(FindOpenGL) +if(LINUX) + if( NOT OPENGL_FOUND ) + message( FATAL_ERROR "OpenGL not found, install mesa-common-dev libgl1-mesa-dev" ) + endif() + + find_file( GLUH "GL/glu.h" ) + if( NOT GLUH ) + message( FATAL_ERROR "GL/glu.h not found, install libglu1-mesa-dev" ) + endif() + find_library( XINERAMA Xinerama ) + if( NOT XINERAMA ) + message( FATAL_ERROR "Cannot link with -lXinerame, install libxinerama-dev" ) + endif() + find_library( XRANDR Xrandr) + if( NOT XRANDR ) + message( FATAL_ERROR "Cannot link with -lXrandr, install libxrandr-dev" ) + endif() +endif() diff --git a/indra/cmake/Python.cmake b/indra/cmake/Python.cmake index 6ac5cf3fc2..162eaf6a6c 100644 --- a/indra/cmake/Python.cmake +++ b/indra/cmake/Python.cmake @@ -14,50 +14,28 @@ if (WINDOWS) ) else() find_program(PYTHON_EXECUTABLE - NAMES python25.exe python23.exe python.exe + NAMES python.exe NO_DEFAULT_PATH # added so that cmake does not find cygwin python PATHS - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\2.8\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\2.7\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\2.6\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\2.3\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.8\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.7\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.6\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.3\\InstallPath] + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\3.7\\InstallPath] + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\3.8\\InstallPath] + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\3.9\\InstallPath] + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\3.10\\InstallPath] + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\3.11\\InstallPath] + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\3.7\\InstallPath] + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\3.8\\InstallPath] + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\3.9\\InstallPath] + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\3.10\\InstallPath] + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\3.11\\InstallPath] ) endif() -elseif (EXISTS /etc/debian_version) - # On Debian and Ubuntu, avoid Python 2.4 if possible. - - find_program(PYTHON_EXECUTABLE python PATHS /usr/bin) + include(FindPythonInterp) +else() + find_program(PYTHON_EXECUTABLE python3) if (PYTHON_EXECUTABLE) set(PYTHONINTERP_FOUND ON) endif (PYTHON_EXECUTABLE) -elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - # On MAC OS X be sure to search standard locations first - - string(REPLACE ":" ";" PATH_LIST "$ENV{PATH}") - find_program(PYTHON_EXECUTABLE - NAMES python python25 python24 python23 - NO_DEFAULT_PATH # Avoid searching non-standard locations first - PATHS - /bin - /usr/bin - /usr/local/bin - ${PATH_LIST} - ) - - if (PYTHON_EXECUTABLE) - set(PYTHONINTERP_FOUND ON) - endif (PYTHON_EXECUTABLE) -else (WINDOWS) - include(FindPythonInterp) endif (WINDOWS) if (NOT PYTHON_EXECUTABLE) diff --git a/indra/cmake/UI.cmake b/indra/cmake/UI.cmake index f1a8b68900..de567bdba0 100644 --- a/indra/cmake/UI.cmake +++ b/indra/cmake/UI.cmake @@ -1,27 +1,34 @@ # -*- cmake -*- include(Prebuilt) include(FreeType) +include(GLIB) if (USESYSTEMLIBS) include(FindPkgConfig) + if( NOT GTK_VERSION ) + set( GTK_VERSION 2.0 ) + endif() if (LINUX) set(PKGCONFIG_PACKAGES atk cairo - gdk-2.0 + gdk-${GTK_VERSION} gdk-pixbuf-2.0 glib-2.0 gmodule-2.0 - gtk+-2.0 + gtk+-${GTK_VERSION} gthread-2.0 libpng pango pangoft2 - pangox - pangoxft - sdl + sdl2 ) + if( GTK_VERSION LESS "3.0" ) + LIST( APPEND PKGCONFIG_PACKAGES pangoxft ) + else() + add_definitions( -DGTK_DISABLE_DEPRECATED) + endif() endif (LINUX) foreach(pkg ${PKGCONFIG_PACKAGES}) @@ -31,29 +38,16 @@ if (USESYSTEMLIBS) list(APPEND UI_LIBRARIES ${${pkg}_LIBRARIES}) add_definitions(${${pkg}_CFLAGS_OTHERS}) endforeach(pkg) + list(APPEND UI_LIBRARIES X11) else (USESYSTEMLIBS) if (LINUX) - use_prebuilt_binary(gtk-atk-pango-glib) + use_prebuilt_binary(fltk) endif (LINUX) if (LINUX) set(UI_LIB_NAMES - freetype - atk-1.0 - gdk-x11-2.0 - gdk_pixbuf-2.0 - glib-2.0 - gmodule-2.0 - gobject-2.0 - gthread-2.0 - gtk-x11-2.0 - pango-1.0 - pangoft2-1.0 - pangox-1.0 - #pangoxft-1.0 - gio-2.0 - pangocairo-1.0 - ffi + libfltk.a + libfreetype.a ) foreach(libname ${UI_LIB_NAMES}) @@ -68,6 +62,7 @@ else (USESYSTEMLIBS) endforeach(libname) set(UI_LIBRARIES ${UI_LIBRARIES} Xinerama) + include_directories ( ${GLIB_INCLUDE_DIRS} ) endif (LINUX) include_directories ( @@ -80,5 +75,5 @@ else (USESYSTEMLIBS) endif (USESYSTEMLIBS) if (LINUX) - add_definitions(-DLL_GTK=1 -DLL_X11=1) + add_definitions(-DLL_X11=1 -DLL_FLTK=1) endif (LINUX) diff --git a/indra/cmake/bugsplat.cmake b/indra/cmake/bugsplat.cmake index f709f35403..ce61b51b9b 100644 --- a/indra/cmake/bugsplat.cmake +++ b/indra/cmake/bugsplat.cmake @@ -12,17 +12,19 @@ endif (INSTALL_PROPRIETARY) if (USE_BUGSPLAT) if (NOT USESYSTEMLIBS) include(Prebuilt) - use_prebuilt_binary(bugsplat) if (WINDOWS) + use_prebuilt_binary(bugsplat) set(BUGSPLAT_LIBRARIES ${ARCH_PREBUILT_DIRS_RELEASE}/bugsplat.lib ) elseif (DARWIN) + use_prebuilt_binary(bugsplat) find_library(BUGSPLAT_LIBRARIES BugsplatMac REQUIRED NO_DEFAULT_PATH PATHS "${ARCH_PREBUILT_DIRS_RELEASE}") message("Bugsplat for OSX not fully implemented, please adapt llappdelegate-objc.mm to honor options of sending user name and settings.xml.") else (WINDOWS) - message(FATAL_ERROR "BugSplat is not supported; add -DUSE_BUGSPLAT=OFF") + use_prebuilt_binary(breakpad) + set(BUGSPLAT_LIBRARIES ${ARCH_PREBUILT_DIRS_RELEASE}/libbreakpad.a ${ARCH_PREBUILT_DIRS_RELEASE}/libbreakpad_client.a ) endif (WINDOWS) else (NOT USESYSTEMLIBS) set(BUGSPLAT_FIND_QUIETLY ON) @@ -32,7 +34,14 @@ if (USE_BUGSPLAT) set(BUGSPLAT_DB "" CACHE STRING "BugSplat crash database name") - set(BUGSPLAT_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/bugsplat) + if( LINUX ) + set(BUGSPLAT_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/breakpad) + # Sadly we cannot have the nice things yet and need add_definitions for older cmake + #add_compile_definitions(__STDC_FORMAT_MACROS) + add_definitions(-D__STDC_FORMAT_MACROS) + else() + set(BUGSPLAT_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/bugsplat) + endif() set(BUGSPLAT_DEFINE "LL_BUGSPLAT") endif (USE_BUGSPLAT) diff --git a/indra/cmake/run_build_test.py b/indra/cmake/run_build_test.py index dd4b9d2c1b..6d442496c5 100755 --- a/indra/cmake/run_build_test.py +++ b/indra/cmake/run_build_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """\ @file run_build_test.py @author Nat Goodspeed @@ -17,7 +17,7 @@ line. Example: -python run_build_test.py -DFOO=bar myprog somearg otherarg +python3 run_build_test.py -DFOO=bar myprog somearg otherarg sets environment variable FOO=bar, then runs: myprog somearg otherarg @@ -47,7 +47,7 @@ $/LicenseInfo$ import os import sys import errno -import HTMLParser +import html.parser import re import signal import subprocess @@ -111,10 +111,10 @@ def main(command, arguments=[], libpath=[], vars={}): # 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: - for key, value in vars.items(): + for key, value in list(vars.items()): # As noted a few lines above, facilitate copy-paste rerunning. log.info("%s='%s' \\" % (key, value)) - os.environ.update(dict([(str(key), str(value)) for key, value in vars.iteritems()])) + os.environ.update(dict([(str(key), str(value)) for key, value in vars.items()])) # Run the child process. command_list = [command] command_list.extend(arguments) @@ -194,7 +194,7 @@ def translate_rc(rc): strc = str(rc) return "terminated by signal %s" % strc -class TableParser(HTMLParser.HTMLParser): +class TableParser(html.parser.HTMLParser): """ This HTMLParser subclass is designed to parse the table we know exists in windows-rcs.html, hopefully without building in too much knowledge of @@ -204,9 +204,7 @@ class TableParser(HTMLParser.HTMLParser): whitespace = re.compile(r'\s*$') def __init__(self): - # Because Python 2.x's HTMLParser is an old-style class, we must use - # old-style syntax to forward the __init__() call -- not super(). - HTMLParser.HTMLParser.__init__(self) + super().__init__() # this will collect all the data, eventually self.table = [] # Stack whose top (last item) indicates where to append current diff --git a/indra/copy_win_scripts/start-client.py b/indra/copy_win_scripts/start-client.py index 2027ee6106..172cbe9bdd 100755 --- a/indra/copy_win_scripts/start-client.py +++ b/indra/copy_win_scripts/start-client.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """\ @file start-client.py @@ -28,12 +28,12 @@ import os import llstart def usage(): - print """start-client.py + print("""start-client.py --grid --farm --region - """ + """) def start_client(grid, slurl, build_config, my_args): login_url = "https://login.%s.lindenlab.com/cgi-bin/login.cgi" % (grid) @@ -42,7 +42,7 @@ def start_client(grid, slurl, build_config, my_args): "--loginuri" : login_url } viewer_args.update(my_args) # *sigh* We must put --url at the end of the argument list. - if viewer_args.has_key("--url"): + if "--url" in viewer_args: slurl = viewer_args["--url"] del(viewer_args["--url"]) viewer_args = llstart.get_args_from_dict(viewer_args) @@ -54,7 +54,7 @@ def start_client(grid, slurl, build_config, my_args): # but the exe is at indra/build-/newview/ build_path = os.path.dirname(os.getcwd()); f = open("start-client.log", "w") - print >>f, "Viewer startup arguments:" + print("Viewer startup arguments:", file=f) llstart.start("viewer", "../../newview", "%s/newview/%s/firestorm-bin.exe" % (build_path, build_config), viewer_args, f) diff --git a/indra/doxygen/CMakeLists.txt b/indra/doxygen/CMakeLists.txt index 84188bd32f..616b5cd09c 100644 --- a/indra/doxygen/CMakeLists.txt +++ b/indra/doxygen/CMakeLists.txt @@ -4,7 +4,7 @@ # other commands to guarantee full compatibility # with the version specified ## prior to 2.8, the add_custom_target commands used in setting the version did not work correctly -cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR) +cmake_minimum_required(VERSION 3.8.0 FATAL_ERROR) set(ROOT_PROJECT_NAME "SecondLife" CACHE STRING "The root project/makefile/solution name. Defaults to SecondLife.") diff --git a/indra/fix-incredibuild.py b/indra/fix-incredibuild.py index 171e4c7889..8da2a168a1 100755 --- a/indra/fix-incredibuild.py +++ b/indra/fix-incredibuild.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 ## ## $LicenseInfo:firstyear=2011&license=viewerlgpl$ ## Second Life Viewer Source Code @@ -27,7 +27,7 @@ import glob def delete_file_types(path, filetypes): if os.path.exists(path): - print 'Cleaning: ' + path + print('Cleaning: ' + path) orig_dir = os.getcwd(); os.chdir(path) filelist = [] diff --git a/indra/lib/python/indra/ipc/llmessage.py b/indra/lib/python/indra/ipc/llmessage.py index 91fb36b72c..663e2d9c63 100755 --- a/indra/lib/python/indra/ipc/llmessage.py +++ b/indra/lib/python/indra/ipc/llmessage.py @@ -26,8 +26,8 @@ THE SOFTWARE. $/LicenseInfo$ """ -from compatibility import Incompatible, Older, Newer, Same -from tokenstream import TokenStream +from .compatibility import Incompatible, Older, Newer, Same +from .tokenstream import TokenStream ### ### Message Template @@ -42,8 +42,8 @@ class Template: def compatibleWithBase(self, base): messagenames = ( - frozenset(self.messages.keys()) - | frozenset(base.messages.keys()) + frozenset(list(self.messages.keys())) + | frozenset(list(base.messages.keys())) ) compatibility = Same() @@ -142,7 +142,7 @@ class Message: baselen = len(base.blocks) samelen = min(selflen, baselen) - for i in xrange(0, samelen): + for i in range(0, samelen): selfblock = self.blocks[i] baseblock = base.blocks[i] @@ -196,7 +196,7 @@ class Block(object): selflen = len(self.variables) baselen = len(base.variables) - for i in xrange(0, min(selflen, baselen)): + for i in range(0, min(selflen, baselen)): selfvar = self.variables[i] basevar = base.variables[i] diff --git a/indra/lib/python/indra/ipc/tokenstream.py b/indra/lib/python/indra/ipc/tokenstream.py index b96f26d3ff..ab97e94846 100755 --- a/indra/lib/python/indra/ipc/tokenstream.py +++ b/indra/lib/python/indra/ipc/tokenstream.py @@ -60,7 +60,7 @@ class ParseError(Exception): return "line %d: %s @ ... %s" % ( self.line, self.reason, self._contextString()) - def __nonzero__(self): + def __bool__(self): return False diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py index dd331a4556..5f60c4c7fd 100755 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -28,7 +28,7 @@ $/LicenseInfo$ """ from collections import namedtuple, defaultdict -import commands +import subprocess import errno import filecmp import fnmatch @@ -85,6 +85,7 @@ def proper_windows_path(path, current_platform = sys.platform): def get_default_platform(dummy): return {'linux2':'linux', 'linux1':'linux', + 'linux':'linux', 'cygwin':'windows', 'win32':'windows', 'darwin':'darwin' @@ -164,20 +165,20 @@ BASE_ARGUMENTS=[ def usage(arguments, srctree=""): nd = {'name':sys.argv[0]} - print """Usage: + print("""Usage: %(name)s [options] [destdir] Options: - """ % nd + """ % nd) for arg in arguments: default = arg['default'] if hasattr(default, '__call__'): default = "(computed value) \"" + str(default(srctree)) + '"' elif default is not None: default = '"' + default + '"' - print "\t--%s Default: %s\n\t%s\n" % ( + print("\t--%s Default: %s\n\t%s\n" % ( arg['name'], default, - arg['description'] % nd) + arg['description'] % nd)) def main(extra=[]): ## print ' '.join((("'%s'" % item) if ' ' in item else item) @@ -202,10 +203,10 @@ def main(extra=[]): for k in 'artwork build dest source'.split(): args[k] = os.path.normpath(args[k]) - print "Source tree:", args['source'] - print "Artwork tree:", args['artwork'] - print "Build tree:", args['build'] - print "Destination tree:", args['dest'] + print("Source tree:", args['source']) + print("Artwork tree:", args['artwork']) + print("Build tree:", args['build']) + print("Destination tree:", args['dest']) # early out for help if 'help' in args: @@ -228,7 +229,7 @@ def main(extra=[]): vf = open(args['versionfile'], 'r') args['version'] = vf.read().strip().split('.') except: - print "Unable to read versionfile '%s'" % args['versionfile'] + print("Unable to read versionfile '%s'" % args['versionfile']) # This will break copy_w_viewer_manifest on Windows 32 and 64 bit builds, the versionfile will not create until the firestorm project. # As copy_w_viewer_manifest does not seem to need the version attribute, we supress the exception for now. # raise @@ -242,7 +243,7 @@ def main(extra=[]): # debugging for opt in args: - print "Option:", opt, "=", args[opt] + print("Option:", opt, "=", args[opt]) # pass in sourceid as an argument now instead of an environment variable args['sourceid'] = os.environ.get("sourceid", "") @@ -250,18 +251,18 @@ def main(extra=[]): # Build base package. touch = args.get('touch') if touch: - print '================ Creating base package' + print('================ Creating base package') else: - print '================ Starting base copy' + print('================ Starting base copy') wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args) wm.do(*args['actions']) # Store package file for later if making touched file. base_package_file = "" if touch: - print '================ Created base package ', wm.package_file + print('================ Created base package ', wm.package_file) base_package_file = "" + wm.package_file else: - print '================ Finished base copy' + print('================ Finished base copy') # handle multiple packages if set # ''.split() produces empty list @@ -288,26 +289,26 @@ def main(extra=[]): args['sourceid'] = os.environ.get(package_id + "_sourceid") args['dest'] = base_dest_template.format(package_id) if touch: - print '================ Creating additional package for "', package_id, '" in ', args['dest'] + print('================ Creating additional package for "', package_id, '" in ', args['dest']) else: - print '================ Starting additional copy for "', package_id, '" in ', args['dest'] + print('================ Starting additional copy for "', package_id, '" in ', args['dest']) try: wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args) wm.do(*args['actions']) except Exception as err: sys.exit(str(err)) if touch: - print '================ Created additional package ', wm.package_file, ' for ', package_id + print('================ Created additional package ', wm.package_file, ' for ', package_id) with open(base_touch_template.format(package_id), 'w') as fp: fp.write('set package_file=%s\n' % wm.package_file) else: - print '================ Finished additional copy "', package_id, '" in ', args['dest'] + print('================ Finished additional copy "', package_id, '" in ', args['dest']) # Write out the package file in this format, so that it can easily be called # and used in a .bat file - yeah, it sucks, but this is the simplest... if touch: with open(touch, 'w') as fp: fp.write('set package_file=%s\n' % base_package_file) - print 'touched', touch + print('touched', touch) return 0 class LLManifestRegistry(type): @@ -319,8 +320,7 @@ class LLManifestRegistry(type): MissingFile = namedtuple("MissingFile", ("pattern", "tried")) -class LLManifest(object): - __metaclass__ = LLManifestRegistry +class LLManifest(object, metaclass=LLManifestRegistry): manifests = {} def for_platform(self, platform, arch = None): if arch: @@ -412,8 +412,8 @@ class LLManifest(object): def display_stacks(self): width = 1 + max(len(stack) for stack in self.PrefixManager.stacks) for stack in self.PrefixManager.stacks: - print "{} {}".format((stack + ':').ljust(width), - os.path.join(*getattr(self, stack))) + print("{} {}".format((stack + ':').ljust(width), + os.path.join(*getattr(self, stack)))) class PrefixManager(object): # stack attributes we manage in this LLManifest (sub)class @@ -430,7 +430,7 @@ class LLManifest(object): self.prevlen = { stack: len(getattr(self.manifest, stack)) - 1 for stack in self.stacks } - def __nonzero__(self): + def __bool__(self): # If the caller wrote: # if self.prefix(...): # then a value of this class had better evaluate as 'True'. @@ -456,7 +456,7 @@ class LLManifest(object): # if we restore the length of each stack to what it was before the # current prefix() block, it doesn't matter whether end_prefix() # was called or not. - for stack, prevlen in self.prevlen.items(): + for stack, prevlen in list(self.prevlen.items()): # find the attribute in 'self.manifest' named by 'stack', and # truncate that list back to 'prevlen' del getattr(self.manifest, stack)[prevlen:] @@ -475,7 +475,7 @@ class LLManifest(object): build = self.build_prefix.pop() dst = self.dst_prefix.pop() if descr and not(src == descr or build == descr or dst == descr): - raise ValueError, "End prefix '" + descr + "' didn't match '" +src+ "' or '" +dst + "'" + raise ValueError("End prefix '" + descr + "' didn't match '" +src+ "' or '" +dst + "'") def get_src_prefix(self): """ Returns the current source prefix.""" @@ -542,7 +542,7 @@ class LLManifest(object): Runs an external command. Raises ManifestError exception if the command returns a nonzero status. """ - print "Running command:", command + print("Running command:", command) sys.stdout.flush() try: subprocess.check_call(command) @@ -556,7 +556,7 @@ class LLManifest(object): Runs an external command. Raises ManifestError exception if the command returns a nonzero status. """ - print "Running command:", command + print("Running command:", command) sys.stdout.flush() try: subprocess.check_call(command, shell=True) @@ -570,18 +570,15 @@ class LLManifest(object): a) verify that you really have created it b) schedule it for cleanup""" if not os.path.exists(path): - raise ManifestError, "Should be something at path " + path + raise ManifestError("Should be something at path " + path) self.created_paths.append(path) def put_in_file(self, contents, dst, src=None): # write contents as dst dst_path = self.dst_path_of(dst) self.cmakedirs(os.path.dirname(dst_path)) - f = open(dst_path, "wb") - try: + with open(dst_path, 'wb') as f: f.write(contents) - finally: - f.close() # Why would we create a file in the destination tree if not to include # it in the installer? The default src=None (plus the fact that the @@ -594,13 +591,12 @@ class LLManifest(object): if dst == None: dst = src # read src - f = open(self.src_path_of(src), "rbU") - contents = f.read() - f.close() + with open(self.src_path_of(src), "r") as f: + contents = f.read() # apply dict replacements - for old, new in searchdict.iteritems(): + for old, new in searchdict.items(): contents = contents.replace(old, new) - self.put_in_file(contents, dst) + self.put_in_file(contents.encode(), dst) self.created_paths.append(dst) def copy_action(self, src, dst): @@ -610,7 +606,7 @@ class LLManifest(object): self.created_paths.append(dst) self.ccopymumble(src, dst) else: - print "Doesn't exist:", src + print("Doesn't exist:", src) def package_action(self, src, dst): pass @@ -628,8 +624,8 @@ class LLManifest(object): # file error until all were resolved. This way permits the developer # to resolve them all at once. if self.missing: - print '*' * 72 - print "Missing files:" + print('*' * 72) + print("Missing files:") # Instead of just dumping each missing file and all the places we # looked for it, group by common sets of places we looked. Use a # set to store the 'tried' directories, to avoid mismatches due to @@ -640,13 +636,13 @@ class LLManifest(object): organize[frozenset(missingfile.tried)].add(missingfile.pattern) # Now dump all the patterns sought in each group of 'tried' # directories. - for tried, patterns in organize.items(): - print " Could not find in:" + for tried, patterns in list(organize.items()): + print(" Could not find in:") for dir in sorted(tried): - print " %s" % dir + print(" %s" % dir) for pattern in sorted(patterns): - print " %s" % pattern - print '*' * 72 + print(" %s" % pattern) + print('*' * 72) raise MissingError('%s patterns could not be found' % len(self.missing)) def copy_finish(self): @@ -659,7 +655,7 @@ class LLManifest(object): unpacked_file_name = "unpacked_%(plat)s_%(vers)s.tar" % { 'plat':self.args['platform'], 'vers':'_'.join(self.args['version'])} - print "Creating unpacked file:", unpacked_file_name + print("Creating unpacked file:", unpacked_file_name) # could add a gz here but that doubles the time it takes to do this step tf = tarfile.open(self.src_path_of(unpacked_file_name), 'w:') # add the entire installation package, at the very top level @@ -670,7 +666,7 @@ class LLManifest(object): """ Delete paths that were specified to have been created by this script""" for c in self.created_paths: # *TODO is this gonna be useful? - print "Cleaning up " + c + print("Cleaning up " + c) def process_either(self, src, dst): # If it's a real directory, recurse through it -- @@ -719,7 +715,7 @@ class LLManifest(object): def remove(self, *paths): for path in paths: if os.path.exists(path): - print "Removing path", path + print("Removing path", path) if os.path.isdir(path): shutil.rmtree(path) else: @@ -781,7 +777,7 @@ class LLManifest(object): except (IOError, os.error) as why: errors.append((srcname, dstname, why)) if errors: - raise ManifestError, errors + raise ManifestError(errors) def cmakedirs(self, path): @@ -893,13 +889,13 @@ class LLManifest(object): break else: # no more prefixes left to try - print("\nunable to find '%s'; looked in:\n %s" % (src, '\n '.join(try_prefixes))) + print(("\nunable to find '%s'; looked in:\n %s" % (src, '\n '.join(try_prefixes)))) self.missing.append(MissingFile(pattern=src, tried=try_prefixes)) # At this point 'count' might never have been successfully # assigned! Even if it was, though, we can be sure it is 0. return 0 - print "%d files" % count + print("%d files" % count) # Let caller check whether we processed as many files as expected. In # particular, let caller notice 0. diff --git a/indra/lib/python/indra/util/test_win32_manifest.py b/indra/lib/python/indra/util/test_win32_manifest.py index e7ba607be4..367e787546 100755 --- a/indra/lib/python/indra/util/test_win32_manifest.py +++ b/indra/lib/python/indra/util/test_win32_manifest.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """\ @file test_win32_manifest.py @brief Test an assembly binding version and uniqueness in a windows dll or exe. @@ -44,10 +44,10 @@ 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] + 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 @@ -64,12 +64,12 @@ def find_vc_dir(): (product, version)) try: return get_HKLM_registry_value(key_str, value_str) - except WindowsError, err: + except WindowsError as err: x64_key_str = (r'SOFTWARE\Wow6432Node\Microsoft\%s\%s\Setup\VC' % (product, version)) try: return get_HKLM_registry_value(x64_key_str, value_str) except: - print >> sys.stderr, "Didn't find MS %s version %s " % (product,version) + print("Didn't find MS %s version %s " % (product,version), file=sys.stderr) raise @@ -79,7 +79,7 @@ def find_mt_path(): return mt_path def test_assembly_binding(src_filename, assembly_name, assembly_ver): - print "checking %s dependency %s..." % (src_filename, assembly_name) + 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) @@ -90,10 +90,10 @@ def test_assembly_binding(src_filename, assembly_name, assembly_ver): 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 + print("Executing: %s" % system_call) mt_result = os.system(system_call) if mt_result == 31: - print "No manifest found in %s" % src_filename + print("No manifest found in %s" % src_filename) raise NoManifestException() manifest_dom = parse(tmp_file_name) @@ -105,30 +105,30 @@ def test_assembly_binding(src_filename, assembly_name, assembly_ver): versions.append(node.getAttribute('version')) if len(versions) == 0: - print "No matching assemblies found in %s" % src_filename + 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 + # 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 + # 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 + print("SUCCESS: %s OK!" % src_filename) + print() if __name__ == '__main__': - print - print "Running test_win32_manifest.py..." + print() + print("Running test_win32_manifest.py...") usage = 'test_win32_manfest ' @@ -137,9 +137,9 @@ if __name__ == '__main__': assembly_name = sys.argv[2] assembly_ver = sys.argv[3] except: - print "Usage:" - print usage - print + print("Usage:") + print(usage) + print() raise test_assembly_binding(src_filename, assembly_name, assembly_ver) diff --git a/indra/linux_crash_logger/CMakeLists.txt b/indra/linux_crash_logger/CMakeLists.txt index aa82ed12cc..ab8ef14fce 100644 --- a/indra/linux_crash_logger/CMakeLists.txt +++ b/indra/linux_crash_logger/CMakeLists.txt @@ -3,18 +3,11 @@ project(linux_crash_logger) include(00-Common) -include(GLH) -include(LLCoreHttp) -include(LLCommon) -include(LLCrashLogger) -include(LLMath) -include(LLMessage) -include(LLFileSystem) -include(LLXML) include(Linking) include(UI) -include(FreeType) -include(Boost) +include(CURL) +include(OpenSSL) +include(ZLIB) include_directories( ${LLCOREHTTP_INCLUDE_DIRS} @@ -36,7 +29,6 @@ include_directories(SYSTEM set(linux_crash_logger_SOURCE_FILES linux_crash_logger.cpp - llcrashloggerlinux.cpp ) set(linux_crash_logger_HEADER_FILES @@ -61,19 +53,13 @@ set(LIBRT_LIBRARY rt) target_link_libraries(linux-crash-logger - ${LLCRASHLOGGER_LIBRARIES} - ${LLFILESYSTEM_LIBRARIES} - ${LLXML_LIBRARIES} - ${LLMESSAGE_LIBRARIES} - ${LLMATH_LIBRARIES} - ${LLCOREHTTP_LIBRARIES} - ${LLCOMMON_LIBRARIES} - ${BOOST_FIBER_LIBRARY} - ${BOOST_CONTEXT_LIBRARY} ${UI_LIBRARIES} - ${DB_LIBRARIES} - ${FREETYPE_LIBRARIES} + ${CURL_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ${ZLIB_LIBRARIES} ${LIBRT_LIBRARY} + X11 ) add_custom_target(linux-crash-logger-target ALL diff --git a/indra/linux_crash_logger/linux_crash_logger.cpp b/indra/linux_crash_logger/linux_crash_logger.cpp index 63e5409876..5d55e551a4 100644 --- a/indra/linux_crash_logger/linux_crash_logger.cpp +++ b/indra/linux_crash_logger/linux_crash_logger.cpp @@ -24,35 +24,77 @@ * $/LicenseInfo$ */ -#include "linden_common.h" -#include "llcrashloggerlinux.h" -#include "llsdutil.h" +#include +#include +#include +#include +/* Called via + execl( gCrashLogger.c_str(), gCrashLogger.c_str(), descriptor.path(), gVersion.c_str(), gBugsplatDB.c_str(), nullptr ); +*/ int main(int argc, char **argv) { - LL_INFOS() << "Starting crash reporter." << LL_ENDL; + std::cerr << "linux crash logger called: "; + for( int i = 1; i < argc; ++i ) + std::cerr << argv[i] << " "; - LLCrashLoggerLinux app; - app.parseCommandOptions(argc, argv); + std::cerr << std::endl; - LLSD options = LLApp::instance()->getOptionData( - LLApp::PRIORITY_COMMAND_LINE); - //LLApp::PRIORITY_RUNTIME_OVERRIDE); - - - if (!(options.has("pid") && options.has("dumpdir"))) + if( argc < 4 ) { - LL_WARNS() << "Insufficient parameters to crash report." << LL_ENDL; + std::cerr << argv[0] << "Not enough arguments" << std::endl; + return 1; } - if (! app.init()) + std::string dmpFile{ argv[1] }; + std::string version{ argv[2] }; + std::string strDb{ argv[3] }; + std::string strAsk{ argv[4] }; + + if( strAsk == "ask" ) { - LL_WARNS() << "Unable to initialize application." << LL_ENDL; - return 1; + auto choice = fl_choice( "Firestorm has crashed, submit the minidump?", "No", "Yes", nullptr ); + if( choice == 0 ) + { + std::cerr << "Abort send due to users choice" << std::endl; + return 0; + } } - app.frame(); - app.cleanup(); - LL_INFOS() << "Crash reporter finished normally." << LL_ENDL; - return 0; + std::string url{ "https://" }; + url += strDb; + url += ".bugsplat.com/post/bp/crash/crashpad.php"; + + curl_global_init(CURL_GLOBAL_ALL); + + auto curl = curl_easy_init(); + if( curl) + { + auto form = curl_mime_init(curl); + + auto field = curl_mime_addpart(form); + curl_mime_name(field, "upload_file_minidump"); + curl_mime_filedata(field, dmpFile.c_str() ); + + field = curl_mime_addpart(form); + curl_mime_name(field, "product"); + curl_mime_data(field, "Firestorm-Releasex64", CURL_ZERO_TERMINATED); + + field = curl_mime_addpart(form); + curl_mime_name(field, "version"); + curl_mime_data(field, version.c_str(), CURL_ZERO_TERMINATED); + + curl_easy_setopt(curl, CURLOPT_URL, url.c_str() ); + curl_easy_setopt(curl, CURLOPT_MIMEPOST, form); + + auto res = curl_easy_perform(curl); + + if(res != CURLE_OK) + std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl; + + curl_easy_cleanup(curl); + + curl_mime_free(form); + } + return 0; } diff --git a/indra/linux_crash_logger/llcrashloggerlinux.cpp b/indra/linux_crash_logger/llcrashloggerlinux.cpp deleted file mode 100644 index f6fd71e849..0000000000 --- a/indra/linux_crash_logger/llcrashloggerlinux.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/** - * @file llcrashloggerlinux.cpp - * @brief Linux crash logger implementation - * - * $LicenseInfo:firstyear=2003&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llcrashloggerlinux.h" - -#include - -#include "linden_common.h" - -#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME -#include "llerror.h" -#include "llfile.h" -#include "lltimer.h" -#include "llstring.h" -#include "lldir.h" -#include "llsdserialize.h" - -#if LL_GTK -# include "gtk/gtk.h" -#endif // LL_GTK - -#define MAX_LOADSTRING 100 - -// Fire-901 / Crashreporting: Brand for FS, add URL to privacy policy - -// These need to be localized. -// static const char dialog_text[] = -// "Second Life appears to have crashed or frozen last time it ran.\n" -// "This crash reporter collects information about your computer's hardware, operating system, and some Second Life logs, all of which are used for debugging purposes only.\n" -// "\n" -// "Send crash report?"; -// -// static const char dialog_title[] = -// "Second Life Crash Logger"; - -static const char dialog_text[] = -"Firestorm appears to have crashed or frozen last time it ran.\n" -"This crash reporter collects information about your computer's hardware, operating system which are used for debugging purposes only. SecondLife logs are not collected.\n" -"This report will be send to firestormviewer.org. Review our privacy policy at http://www.firestormviewer.org/privacy-policy for more information.\n" -"\n" -"Send crash report?"; - -static const char dialog_title[] = -"Firestorm Crash Logger"; - -// - -#if 0 - -#if LL_GTK -static void response_callback (GtkDialog *dialog, - gint arg1, - gpointer user_data) -{ - gint *response = (gint*)user_data; - *response = arg1; - gtk_widget_destroy(GTK_WIDGET(dialog)); - gtk_main_quit(); -} -#endif // LL_GTK - -static BOOL do_ask_dialog(void) -{ -#if LL_GTK - gtk_disable_setlocale(); - if (!gtk_init_check(NULL, NULL)) { - LL_INFOS() << "Could not initialize GTK for 'ask to send crash report' dialog; not sending report." << LL_ENDL; - return FALSE; - } - - GtkWidget *win = NULL; - GtkDialogFlags flags = GTK_DIALOG_MODAL; - GtkMessageType messagetype = GTK_MESSAGE_QUESTION; - GtkButtonsType buttons = GTK_BUTTONS_YES_NO; - gint response = GTK_RESPONSE_NONE; - - win = gtk_message_dialog_new(NULL, - flags, messagetype, buttons, - "%s", dialog_text); - gtk_window_set_type_hint(GTK_WINDOW(win), - GDK_WINDOW_TYPE_HINT_DIALOG); - gtk_window_set_title(GTK_WINDOW(win), dialog_title); - g_signal_connect (win, - "response", - G_CALLBACK (response_callback), - &response); - gtk_widget_show_all (win); - gtk_main(); - - return (GTK_RESPONSE_OK == response || - GTK_RESPONSE_YES == response || - GTK_RESPONSE_APPLY == response); -#else - return FALSE; -#endif // LL_GTK -} -#endif - -LLCrashLoggerLinux::LLCrashLoggerLinux(void) -{ -} - -LLCrashLoggerLinux::~LLCrashLoggerLinux(void) -{ -} - -void LLCrashLoggerLinux::gatherPlatformSpecificFiles() -{ -} - -bool LLCrashLoggerLinux::frame() -{ - // Get around the crash logger popping up all the time. - // Right now there seems to be no easy way to test if there's logs from a real crash to send. Which - // would be preferred, as then asking for sending in data makes sense. Right now the dialog will just open always. - - // bool send_logs = true; - // if(CRASH_BEHAVIOR_ASK == getCrashBehavior()) - // { - // send_logs = do_ask_dialog(); - // } - // else if(CRASH_BEHAVIOR_NEVER_SEND == getCrashBehavior()) - // { - // send_logs = false; - // } - - bool send_logs = (CRASH_BEHAVIOR_NEVER_SEND == getCrashBehavior()); - - // - - if(send_logs) - { - sendCrashLogs(); - } - return true; -} - -bool LLCrashLoggerLinux::cleanup() -{ - commonCleanup(); - mKeyMaster.releaseMaster(); - return true; -} - -void LLCrashLoggerLinux::updateApplication(const std::string& message) -{ - LLCrashLogger::updateApplication(message); -} diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt index 92a5cfe22f..804b36b226 100644 --- a/indra/llaudio/CMakeLists.txt +++ b/indra/llaudio/CMakeLists.txt @@ -11,7 +11,7 @@ include(LLMath) include(LLMessage) include(LLFileSystem) -include_directories( +include_directories( SYSTEM ${LLAUDIO_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 02b1d38fa9..0ec4898b77 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -13,7 +13,7 @@ include(Copy3rdPartyLibs) include(ZLIB) include(URIPARSER) -include_directories( +include_directories( SYSTEM ${EXPAT_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${JSONCPP_INCLUDE_DIR} diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 41c4e5d97c..420c7c26c8 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -544,8 +544,6 @@ namespace protected: Globals(); public: - std::ostringstream messageStream; - bool messageStreamInUse; std::string mFatalMessage; void addCallSite(LLError::CallSite&); @@ -562,7 +560,9 @@ namespace }; Globals::Globals() - : mSettingsConfig(new SettingsConfig()) + : + callSites(), + mSettingsConfig(new SettingsConfig()) { } @@ -1449,7 +1449,10 @@ namespace LLError if (site.mLevel == LEVEL_ERROR) { g->mFatalMessage = message; - s->mCrashFunction(message); + if (s->mCrashFunction) + { + s->mCrashFunction(message); + } } } } diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 3a58c029b0..d4c4d29f50 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -80,7 +80,7 @@ const int LL_ERR_NOERR = 0; #endif // !_DEBUG -#define llassert_always_msg(func, msg) if (LL_UNLIKELY(!(func))) LL_ERRS() << "ASSERT (" << msg << ")" << LL_ENDL +#define llassert_always_msg(func, msg) if (LL_UNLIKELY(!(func))) { LL_ERRS() << "ASSERT (" << msg << ")" << LL_ENDL; /* call abort, this will not be reached but signaled GCC after this line the program exists*/ std::abort(); } #define llassert_always(func) llassert_always_msg(func, #func) diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index e8ea0ab398..2704f8b6de 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -86,7 +86,7 @@ public: // notice Python specially: we provide Python LLSD serialization // support, so there's a pretty good reason to implement plugins // in that language. - if (cparams.args.size() && (desclower == "python" || desclower == "python.exe")) + if (cparams.args.size() && (desclower == "python" || desclower == "python3" || desclower == "python.exe")) { mDesc = LLProcess::basename(cparams.args()[0]); } diff --git a/indra/llcommon/llmetricperformancetester.cpp b/indra/llcommon/llmetricperformancetester.cpp index f8a93baf45..100eb57555 100644 --- a/indra/llcommon/llmetricperformancetester.cpp +++ b/indra/llcommon/llmetricperformancetester.cpp @@ -189,7 +189,6 @@ LLMetricPerformanceTesterBasic::~LLMetricPerformanceTesterBasic() void LLMetricPerformanceTesterBasic::preOutputTestResults(LLSD* sd) { incrementCurrentCount() ; - (*sd)[getCurrentLabelName()]["Name"] = mName ; } void LLMetricPerformanceTesterBasic::postOutputTestResults(LLSD* sd) diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp index d5cc14ff55..a1ee1cb7ba 100644 --- a/indra/llcommon/llsdutil.cpp +++ b/indra/llcommon/llsdutil.cpp @@ -47,6 +47,12 @@ #include #include +// Suppress warnings about the string fiddling +#if LL_LINUX +#pragma GCC diagnostic ignored "-Wstringop-truncation" +#endif +// + // U32 LLSD ll_sd_from_U32(const U32 val) { diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index c9f17e29b8..2dc18a39e1 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -107,7 +107,7 @@ LLOSInfo::LLOSInfo() : #if LL_WINDOWS - if (IsWindows10OrGreater()) + if (IsWindows10OrGreater()) { mMajorVer = 10; mMinorVer = 0; @@ -248,13 +248,24 @@ LLOSInfo::LLOSInfo() : } } - // Windows 11 detection - if (mBuild >= 22000) - { - mMajorVer = 11; - LLStringUtil::replaceString(mOSStringSimple, "10", "11"); - } - // + if (mBuild >= 22000) + { + // At release Windows 11 version was 10.0.22000.194 + // Windows 10 version was 10.0.19043.1266 + // There is no warranty that Win10 build won't increase, + // so until better solution is found or Microsoft updates + // SDK with IsWindows11OrGreater(), indicate "10/11" + // + // Current alternatives: + // Query WMI's Win32_OperatingSystem for OS string. Slow + // and likely to return 'compatibility' string. + // Check presence of dlls/libs or may be their version. + // Windows 11 detection + //mOSStringSimple = "Microsoft Windows 10/11"; + mMajorVer = 11; + LLStringUtil::replaceString(mOSStringSimple, "10", "11"); + // + } } mOSString = mOSStringSimple; @@ -1262,18 +1273,12 @@ BOOL gunzip_file(const std::string& srcfile, const std::string& dstfile) LLFILE *dst = NULL; S32 bytes = 0; tmpfile = dstfile + ".t"; - - // Proper UTF8->UTF16 handling for Windows - // src = gzopen(srcfile.c_str(), "rb"); -#if LL_WINDOWS - std::string utf8filename = srcfile; - llutf16string utf16filename = utf8str_to_utf16str(utf8filename); - src = gzopen_w(utf16filename.c_str(), "rb"); +#ifdef LL_WINDOWS + llutf16string utf16filename = utf8str_to_utf16str(srcfile); + src = gzopen_w(utf16filename.c_str(), "rb"); #else - src = gzopen(srcfile.c_str(), "rb");/* Flawfinder: ignore */ + src = gzopen(srcfile.c_str(), "rb"); #endif - // - if (! src) goto err; dst = LLFile::fopen(tmpfile, "wb"); /* Flawfinder: ignore */ if (! dst) goto err; @@ -1308,17 +1313,13 @@ BOOL gzip_file(const std::string& srcfile, const std::string& dstfile) S32 bytes = 0; tmpfile = dstfile + ".t"; - // Proper UTF8->UTF16 handling for Windows - // dst = gzopen(tmpfile.c_str(), "wb"); /* Flawfinder: ignore */ -#if LL_WINDOWS - std::string utf8filename = tmpfile; - llutf16string utf16filename = utf8str_to_utf16str(utf8filename); - dst = gzopen_w(utf16filename.c_str(), "wb"); +#ifdef LL_WINDOWS + llutf16string utf16filename = utf8str_to_utf16str(tmpfile); + dst = gzopen_w(utf16filename.c_str(), "wb"); #else - dst = gzopen(tmpfile.c_str(), "wb");/* Flawfinder: ignore */ + dst = gzopen(tmpfile.c_str(), "wb"); #endif - // - + if (! dst) goto err; src = LLFile::fopen(srcfile, "rb"); /* Flawfinder: ignore */ if (! src) goto err; diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp index 9d71e327d8..9754353ab0 100644 --- a/indra/llcommon/tests/llleap_test.cpp +++ b/indra/llcommon/tests/llleap_test.cpp @@ -145,13 +145,13 @@ namespace tut " data = ''.join(parts)\n" " assert len(data) == length\n" " try:\n" - " return llsd.parse(data)\n" + " return llsd.parse(data.encode())\n" // Seems the old indra.base.llsd module didn't properly // convert IndexError (from running off end of string) to // LLSDParseError. - " except (IndexError, llsd.LLSDParseError), e:\n" + " except (IndexError, llsd.LLSDParseError) as e:\n" " msg = 'Bad received packet (%s)' % e\n" - " print >>sys.stderr, '%s, %s bytes:' % (msg, len(data))\n" + " print('%s, %s bytes:' % (msg, len(data)), file=sys.stderr)\n" " showmax = 40\n" // We've observed failures with very large packets; // dumping the entire packet wastes time and space. @@ -167,12 +167,12 @@ namespace tut " data = data[:trunc]\n" " ellipsis = '... (%s more)' % (length - trunc)\n" " offset = -showmax\n" - " for offset in xrange(0, len(data)-showmax, showmax):\n" - " print >>sys.stderr, '%04d: %r +' % \\\n" - " (offset, data[offset:offset+showmax])\n" + " for offset in range(0, len(data)-showmax, showmax):\n" + " print('%04d: %r +' % \\\n" + " (offset, data[offset:offset+showmax]), file=sys.stderr)\n" " offset += showmax\n" - " print >>sys.stderr, '%04d: %r%s' % \\\n" - " (offset, data[offset:], ellipsis)\n" + " print('%04d: %r%s' % \\\n" + " (offset, data[offset:], ellipsis), file=sys.stderr)\n" " raise ParseError(msg, data)\n" "\n" "# deal with initial stdin message\n" @@ -189,7 +189,7 @@ namespace tut " sys.stdout.flush()\n" "\n" "def send(pump, data):\n" - " put(llsd.format_notation(dict(pump=pump, data=data)))\n" + " put(llsd.format_notation(dict(pump=pump, data=data)).decode())\n" "\n" "def request(pump, data):\n" " # we expect 'data' is a dict\n" @@ -253,7 +253,7 @@ namespace tut { set_test_name("bad stdout protocol"); NamedTempFile script("py", - "print 'Hello from Python!'\n"); + "print('Hello from Python!')\n"); CaptureLog log(LLError::LEVEL_WARN); waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName())))); @@ -438,8 +438,8 @@ namespace tut // guess how many messages it will take to // accumulate BUFFERED_LENGTH "count = int(" << BUFFERED_LENGTH << "/samplen)\n" - "print >>sys.stderr, 'Sending %s requests' % count\n" - "for i in xrange(count):\n" + "print('Sending %s requests' % count, file=sys.stderr)\n" + "for i in range(count):\n" " request('" << api.getName() << "', dict(reqid=i))\n" // The assumption in this specific test that // replies will arrive in the same order as @@ -450,7 +450,7 @@ namespace tut // arbitrary order, and we'd have to tick them // off from a set. "result = ''\n" - "for i in xrange(count):\n" + "for i in range(count):\n" " resp = get()\n" " if resp['data']['reqid'] != i:\n" " result = 'expected reqid=%s in %s' % (i, resp)\n" @@ -476,13 +476,13 @@ namespace tut "desired = int(sys.argv[1])\n" // 7 chars per item: 6 digits, 1 comma "count = int((desired - 50)/7)\n" - "large = ''.join('%06d,' % i for i in xrange(count))\n" + "large = ''.join('%06d,' % i for i in range(count))\n" // Pass 'large' as reqid because we know the API // will echo reqid, and we want to receive it back. "request('" << api.getName() << "', dict(reqid=large))\n" "try:\n" " resp = get()\n" - "except ParseError, e:\n" + "except ParseError as e:\n" " # try to find where e.data diverges from expectation\n" // Normally we'd expect a 'pump' key in there, // too, with value replypump(). But Python @@ -493,17 +493,18 @@ namespace tut // strange. " expect = llsd.format_notation(dict(data=dict(reqid=large)))\n" " chunk = 40\n" - " for offset in xrange(0, max(len(e.data), len(expect)), chunk):\n" + " for offset in range(0, max(len(e.data), len(expect)), chunk):\n" " if e.data[offset:offset+chunk] != \\\n" " expect[offset:offset+chunk]:\n" - " print >>sys.stderr, 'Offset %06d: expect %r,\\n'\\\n" + " print('Offset %06d: expect %r,\\n'\\\n" " ' get %r' %\\\n" " (offset,\n" " expect[offset:offset+chunk],\n" - " e.data[offset:offset+chunk])\n" + " e.data[offset:offset+chunk]),\n" + " file=sys.stderr)\n" " break\n" " else:\n" - " print >>sys.stderr, 'incoming data matches expect?!'\n" + " print('incoming data matches expect?!', file=sys.stderr)\n" " send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n" " sys.exit(1)\n" "\n" @@ -512,7 +513,7 @@ namespace tut " send('" << result.getName() << "', '')\n" " sys.exit(0)\n" // Here we know echoed did NOT match; try to find where - "for i in xrange(count):\n" + "for i in range(count):\n" " start = 7*i\n" " end = 7*(i+1)\n" " if end > len(echoed)\\\n" diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index f0eafa8201..e530975e86 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -360,10 +360,10 @@ namespace tut "import time" EOL EOL "time.sleep(2)" EOL - "print >>sys.stdout, 'stdout after wait'" EOL + "print('stdout after wait', file=sys.stdout)" EOL "sys.stdout.flush()" EOL "time.sleep(2)" EOL - "print >>sys.stderr, 'stderr after wait'" EOL + "print('stderr after wait', file=sys.stderr)" EOL "sys.stderr.flush()" EOL ); @@ -381,7 +381,11 @@ namespace tut std::vector argv; apr_proc_t child; +#if defined(LL_WINDOWS) argv.push_back("python"); +#else + argv.push_back("python3"); +#endif // Have to have a named copy of this std::string so its c_str() value // will persist. std::string scriptname(script.getName()); @@ -573,7 +577,7 @@ namespace tut // note nonstandard output-file arg! "with open(sys.argv[3], 'w') as f:\n" " for arg in sys.argv[1:]:\n" - " print >>f, arg\n"); + " print(arg, file=f)\n"); // We expect that PythonProcessLauncher has already appended // its own NamedTempFile to mParams.args (sys.argv[0]). py.mParams.args.add("first arg"); // sys.argv[1] @@ -742,7 +746,7 @@ namespace tut "with open(sys.argv[1], 'w') as f:\n" " f.write('ok')\n" "# wait for 'go' from test program\n" - "for i in xrange(60):\n" + "for i in range(60):\n" " time.sleep(1)\n" " with open(sys.argv[2]) as f:\n" " go = f.read()\n" @@ -804,7 +808,7 @@ namespace tut "with open(sys.argv[1], 'w') as f:\n" " f.write('ok')\n" "# wait for 'go' from test program\n" - "for i in xrange(60):\n" + "for i in range(60):\n" " time.sleep(1)\n" " with open(sys.argv[2]) as f:\n" " go = f.read()\n" @@ -857,7 +861,7 @@ namespace tut set_test_name("'bogus' test"); CaptureLog recorder; PythonProcessLauncher py(get_test_name(), - "print 'Hello world'\n"); + "print('Hello world')\n"); py.mParams.files.add(LLProcess::FileParam("bogus")); py.mPy = LLProcess::create(py.mParams); ensure("should have rejected 'bogus'", ! py.mPy); @@ -872,7 +876,7 @@ namespace tut // Replace this test with one or more real 'file' tests when we // implement 'file' support PythonProcessLauncher py(get_test_name(), - "print 'Hello world'\n"); + "print('Hello world')\n"); py.mParams.files.add(LLProcess::FileParam()); py.mParams.files.add(LLProcess::FileParam("file")); py.mPy = LLProcess::create(py.mParams); @@ -887,7 +891,7 @@ namespace tut // implement 'tpipe' support CaptureLog recorder; PythonProcessLauncher py(get_test_name(), - "print 'Hello world'\n"); + "print('Hello world')\n"); py.mParams.files.add(LLProcess::FileParam()); py.mParams.files.add(LLProcess::FileParam("tpipe")); py.mPy = LLProcess::create(py.mParams); @@ -904,7 +908,7 @@ namespace tut // implement 'npipe' support CaptureLog recorder; PythonProcessLauncher py(get_test_name(), - "print 'Hello world'\n"); + "print('Hello world')\n"); py.mParams.files.add(LLProcess::FileParam()); py.mParams.files.add(LLProcess::FileParam()); py.mParams.files.add(LLProcess::FileParam("npipe")); @@ -980,7 +984,7 @@ namespace tut { set_test_name("get*Pipe() validation"); PythonProcessLauncher py(get_test_name(), - "print 'this output is expected'\n"); + "print('this output is expected)'\n"); py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stdin py.mParams.files.add(LLProcess::FileParam()); // inherit stdout py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stderr @@ -1001,13 +1005,13 @@ namespace tut set_test_name("talk to stdin/stdout"); PythonProcessLauncher py(get_test_name(), "import sys, time\n" - "print 'ok'\n" + "print('ok')\n" "sys.stdout.flush()\n" "# wait for 'go' from test program\n" "go = sys.stdin.readline()\n" "if go != 'go\\n':\n" " sys.exit('expected \"go\", saw %r' % go)\n" - "print 'ack'\n"); + "print('ack')\n"); py.mParams.files.add(LLProcess::FileParam("pipe")); // stdin py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout py.launch(); @@ -1118,7 +1122,7 @@ namespace tut { set_test_name("ReadPipe \"eof\" event"); PythonProcessLauncher py(get_test_name(), - "print 'Hello from Python!'\n"); + "print('Hello from Python!')\n"); py.mParams.files.add(LLProcess::FileParam()); // stdin py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout py.launch(); diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index 642c1c3879..c246f5ee56 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -1795,7 +1795,7 @@ namespace tut set_test_name("verify NamedTempFile"); python("platform", "import sys\n" - "print 'Running on', sys.platform\n"); + "print('Running on', sys.platform)\n"); } // helper for test<3> @@ -1825,14 +1825,14 @@ namespace tut const char pydata[] = "def verify(iterable):\n" " it = iter(iterable)\n" - " assert it.next() == 17\n" - " assert abs(it.next() - 3.14) < 0.01\n" - " assert it.next() == '''\\\n" + " assert next(it) == 17\n" + " assert abs(next(it) - 3.14) < 0.01\n" + " assert next(it) == '''\\\n" "This string\n" "has several\n" "lines.'''\n" " try:\n" - " it.next()\n" + " next(it)\n" " except StopIteration:\n" " pass\n" " else:\n" @@ -1855,7 +1855,7 @@ namespace tut " yield llsd.parse(item)\n" << pydata << // Don't forget raw-string syntax for Windows pathnames. - "verify(parse_each(open(r'" << file.getName() << "')))\n"); + "verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n"); } template<> template<> @@ -1870,7 +1870,6 @@ namespace tut python("write Python notation", placeholders::arg1 << - "from __future__ import with_statement\n" << import_llsd << "DATA = [\n" " 17,\n" @@ -1884,7 +1883,7 @@ namespace tut // N.B. Using 'print' implicitly adds newlines. "with open(r'" << file.getName() << "', 'w') as f:\n" " for item in DATA:\n" - " print >>f, llsd.format_notation(item)\n"); + " print(llsd.format_notation(item).decode(), file=f)\n"); std::ifstream inf(file.getName().c_str()); LLSD item; diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 8eccae00c7..b94a243fb2 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -508,7 +508,12 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) check_curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1); check_curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); check_curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, getHandle()); + +// Newer versions of curl are stricter with checkinng Cotent-Encoding: header +// Aws returns Content-Encoding: binary/octet-stream which is no valid scheme defined by HTTP/1.1 (compress,deflate, gzip) +#if LIBCURL_VERSION_NUM < 0x075100 check_curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); +#endif check_curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); check_curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT); diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index 61ba83594e..8012da4370 100644 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -289,8 +289,12 @@ CURL *getCurlTemplateHandle() check_curl_code(result, CURLOPT_NOSIGNAL); result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_NOPROGRESS, 1); check_curl_code(result, CURLOPT_NOPROGRESS); +// Newer versions of curl are stricter with checkinng Cotent-Encoding: header +// Aws returns Content-Encoding: binary/octet-stream which is no valid scheme defined by HTTP/1.1 (compress,deflate, gzip) +#if LIBCURL_VERSION_NUM < 0x075100 result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_ENCODING, ""); check_curl_code(result, CURLOPT_ENCODING); +#endif result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_AUTOREFERER, 1); check_curl_code(result, CURLOPT_AUTOREFERER); result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_FOLLOWLOCATION, 1); diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 3cdd17919d..154f6b12e9 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -135,7 +135,9 @@ public: } } std::ostringstream str; - str << "Required header # " << i << " found in response"; + str << "Required header #" << i << " " + << mHeadersRequired[i].first << "=" << mHeadersRequired[i].second + << " not found in response"; ensure(str.str(), found); } } @@ -154,7 +156,9 @@ public: mHeadersDisallowed[i].second)) { std::ostringstream str; - str << "Disallowed header # " << i << " not found in response"; + str << "Disallowed header #" << i << " " + << mHeadersDisallowed[i].first << "=" << mHeadersDisallowed[i].second + << " found in response"; ensure(str.str(), false); } } @@ -2127,6 +2131,17 @@ void HttpRequestTestObjectType::test<18>() template <> template <> void HttpRequestTestObjectType::test<19>() { + // It appears that HttpRequest is fully capable of sending duplicate header values in violation of + // this test's expectations. Something needs to budge: is sending duplicate header values desired? + // + // Test server /reflect/ response headers (mirrored from request) + // + // X-Reflect-content-type: text/plain + // X-Reflect-content-type: text/html + // X-Reflect-content-type: application/llsd+xml + // + skip("FIXME: Bad assertions or broken functionality."); + ScopedCurlInit ready; // Warmup boost::regex to pre-alloc memory for memory size tests @@ -2307,6 +2322,17 @@ void HttpRequestTestObjectType::test<19>() template <> template <> void HttpRequestTestObjectType::test<20>() { + // It appears that HttpRequest is fully capable of sending duplicate header values in violation of + // this test's expectations. Something needs to budge: is sending duplicate header values desired? + // + // Test server /reflect/ response headers (mirrored from request) + // + // X-Reflect-content-type: text/plain + // X-Reflect-content-type: text/html + // X-Reflect-content-type: application/llsd+xml + // + skip("FIXME: Bad assertions or broken functionality."); + ScopedCurlInit ready; // Warmup boost::regex to pre-alloc memory for memory size tests @@ -2512,6 +2538,17 @@ void HttpRequestTestObjectType::test<20>() template <> template <> void HttpRequestTestObjectType::test<21>() { + // It appears that HttpRequest is fully capable of sending duplicate header values in violation of + // this test's expectations. Something needs to budge: is sending duplicate header values desired? + // + // Test server /reflect/ response headers (mirrored from request) + // + // X-Reflect-content-type: text/plain + // X-Reflect-content-type: text/html + // X-Reflect-content-type: application/llsd+xml + // + skip("FIXME: Bad assertions or broken functionality."); + ScopedCurlInit ready; // Warmup boost::regex to pre-alloc memory for memory size tests diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 493143641b..778de90962 100755 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """\ @file test_llsdmessage_peer.py @author Nat Goodspeed @@ -34,11 +34,9 @@ import sys import time import select import getopt -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO -from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +from io import StringIO +from http.server import HTTPServer, BaseHTTPRequestHandler + from llbase.fastest_elementtree import parse as xml_parse from llbase import llsd @@ -97,13 +95,13 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): except (KeyError, ValueError): return "" max_chunk_size = 10*1024*1024 - L = [] + L = bytes() while size_remaining: chunk_size = min(size_remaining, max_chunk_size) chunk = self.rfile.read(chunk_size) - L.append(chunk) + L += chunk size_remaining -= len(chunk) - return ''.join(L) + return L.decode("utf-8") # end of swiped read() logic def read_xml(self): @@ -127,8 +125,8 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): try: self.answer(dict(reply="success", status=200, reason="Your GET operation worked")) - except self.ignore_exceptions, e: - print >> sys.stderr, "Exception during GET (ignoring): %s" % str(e) + except self.ignore_exceptions as e: + print("Exception during GET (ignoring): %s" % str(e), file=sys.stderr) def do_POST(self): # Read the provided POST data. @@ -136,8 +134,8 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): try: self.answer(dict(reply="success", status=200, reason=self.read())) - except self.ignore_exceptions, e: - print >> sys.stderr, "Exception during POST (ignoring): %s" % str(e) + except self.ignore_exceptions as e: + print("Exception during POST (ignoring): %s" % str(e), file=sys.stderr) def do_PUT(self): # Read the provided PUT data. @@ -145,8 +143,8 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): try: self.answer(dict(reply="success", status=200, reason=self.read())) - except self.ignore_exceptions, e: - print >> sys.stderr, "Exception during PUT (ignoring): %s" % str(e) + except self.ignore_exceptions as e: + print("Exception during PUT (ignoring): %s" % str(e), file=sys.stderr) def answer(self, data, withdata=True): debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path) @@ -221,7 +219,7 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): self.send_header("Content-type", "text/plain") self.end_headers() if body: - self.wfile.write(body) + self.wfile.write(body.encode("utf-8")) elif "fail" not in self.path: data = data.copy() # we're going to modify # Ensure there's a "reply" key in data, even if there wasn't before @@ -255,9 +253,9 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): self.end_headers() def reflect_headers(self): - for name in self.headers.keys(): - # print "Header: %s: %s" % (name, self.headers[name]) - self.send_header("X-Reflect-" + name, self.headers[name]) + for (name, val) in self.headers.items(): + # print("Header: %s %s" % (name, val), file=sys.stderr) + self.send_header("X-Reflect-" + name, val) if not VERBOSE: # When VERBOSE is set, skip both these overrides because they exist to @@ -283,10 +281,10 @@ class Server(HTTPServer): # default behavior which *shouldn't* cause the program to return # a failure status. def handle_error(self, request, client_address): - print '-'*40 - print 'Ignoring exception during processing of request from', - print client_address - print '-'*40 + print('-'*40) + print('Ignoring exception during processing of request from %' % (client_address)) + print('-'*40) + if __name__ == "__main__": do_valgrind = False @@ -307,7 +305,7 @@ if __name__ == "__main__": # "Then there's Windows" # Instantiate a Server(TestHTTPRequestHandler) on the first free port # in the specified port range. - httpd, port = freeport(xrange(8000, 8020), make_server) + httpd, port = freeport(range(8000, 8020), make_server) # Pass the selected port number to the subject test program via the # environment. We don't want to impose requirements on the test program's diff --git a/indra/llfilesystem/lldir.cpp b/indra/llfilesystem/lldir.cpp index 9580c846fc..75ce6aa478 100644 --- a/indra/llfilesystem/lldir.cpp +++ b/indra/llfilesystem/lldir.cpp @@ -648,7 +648,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd llassert(0); } - if (prefix.empty()) + if (prefix.empty() && location != LL_PATH_NONE) // avoid pointless warning when LL_PATH_NONE is used. { LL_WARNS() << ELLPathToString(location) << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp index ec4aec10d9..650d8dda25 100644 --- a/indra/llinventory/llfoldertype.cpp +++ b/indra/llinventory/llfoldertype.cpp @@ -37,15 +37,22 @@ struct FolderEntry : public LLDictionaryEntry { FolderEntry(const std::string &type_name, // 8 character limit! - bool is_protected) // can the viewer change categories of this type? + bool is_protected, // can the viewer change categories of this type? + bool is_automatic, // always made before first login? + bool is_singleton // should exist as a unique copy under root + ) : LLDictionaryEntry(type_name), - mIsProtected(is_protected) + mIsProtected(is_protected), + mIsAutomatic(is_automatic), + mIsSingleton(is_singleton) { llassert(type_name.length() <= 8); } const bool mIsProtected; + const bool mIsAutomatic; + const bool mIsSingleton; }; class LLFolderDictionary : public LLSingleton, @@ -59,52 +66,66 @@ protected: } }; +// Folder types +// +// PROTECTED means that folders of this type can't be moved, deleted +// or otherwise modified by the viewer. +// +// SINGLETON means that there should always be exactly one folder of +// this type, and it should be the root or a child of the root. This +// is true for most types of folders. +// +// AUTOMATIC means that a copy of this folder should be created under +// the root before the user ever logs in, and should never be created +// from the viewer. A missing AUTOMATIC folder should be treated as a +// fatal error by the viewer, since it indicates either corrupted +// inventory or a failure in the inventory services. +// LLFolderDictionary::LLFolderDictionary() { - // TYPE NAME PROTECTED - // |-----------|---------| - addEntry(LLFolderType::FT_TEXTURE, new FolderEntry("texture", TRUE)); - addEntry(LLFolderType::FT_SOUND, new FolderEntry("sound", TRUE)); - addEntry(LLFolderType::FT_CALLINGCARD, new FolderEntry("callcard", TRUE)); - addEntry(LLFolderType::FT_LANDMARK, new FolderEntry("landmark", TRUE)); - addEntry(LLFolderType::FT_CLOTHING, new FolderEntry("clothing", TRUE)); - addEntry(LLFolderType::FT_OBJECT, new FolderEntry("object", TRUE)); - addEntry(LLFolderType::FT_NOTECARD, new FolderEntry("notecard", TRUE)); - addEntry(LLFolderType::FT_ROOT_INVENTORY, new FolderEntry("root_inv", TRUE)); - addEntry(LLFolderType::FT_LSL_TEXT, new FolderEntry("lsltext", TRUE)); - addEntry(LLFolderType::FT_BODYPART, new FolderEntry("bodypart", TRUE)); - addEntry(LLFolderType::FT_TRASH, new FolderEntry("trash", TRUE)); - addEntry(LLFolderType::FT_SNAPSHOT_CATEGORY, new FolderEntry("snapshot", TRUE)); - addEntry(LLFolderType::FT_LOST_AND_FOUND, new FolderEntry("lstndfnd", TRUE)); - addEntry(LLFolderType::FT_ANIMATION, new FolderEntry("animatn", TRUE)); - addEntry(LLFolderType::FT_GESTURE, new FolderEntry("gesture", TRUE)); - addEntry(LLFolderType::FT_FAVORITE, new FolderEntry("favorite", TRUE)); + // TYPE NAME, PROTECTED, AUTOMATIC, SINGLETON + addEntry(LLFolderType::FT_TEXTURE, new FolderEntry("texture", TRUE, TRUE, TRUE)); + addEntry(LLFolderType::FT_SOUND, new FolderEntry("sound", TRUE, TRUE, TRUE)); + addEntry(LLFolderType::FT_CALLINGCARD, new FolderEntry("callcard", TRUE, TRUE, FALSE)); + addEntry(LLFolderType::FT_LANDMARK, new FolderEntry("landmark", TRUE, FALSE, FALSE)); + addEntry(LLFolderType::FT_CLOTHING, new FolderEntry("clothing", TRUE, TRUE, TRUE)); + addEntry(LLFolderType::FT_OBJECT, new FolderEntry("object", TRUE, TRUE, TRUE)); + addEntry(LLFolderType::FT_NOTECARD, new FolderEntry("notecard", TRUE, TRUE, TRUE)); + addEntry(LLFolderType::FT_ROOT_INVENTORY, new FolderEntry("root_inv", TRUE, TRUE, TRUE)); + addEntry(LLFolderType::FT_LSL_TEXT, new FolderEntry("lsltext", TRUE, TRUE, TRUE)); + addEntry(LLFolderType::FT_BODYPART, new FolderEntry("bodypart", TRUE, TRUE, TRUE)); + addEntry(LLFolderType::FT_TRASH, new FolderEntry("trash", TRUE, FALSE, TRUE)); + addEntry(LLFolderType::FT_SNAPSHOT_CATEGORY, new FolderEntry("snapshot", TRUE, TRUE, TRUE)); + addEntry(LLFolderType::FT_LOST_AND_FOUND, new FolderEntry("lstndfnd", TRUE, TRUE, TRUE)); + addEntry(LLFolderType::FT_ANIMATION, new FolderEntry("animatn", TRUE, TRUE, TRUE)); + addEntry(LLFolderType::FT_GESTURE, new FolderEntry("gesture", TRUE, TRUE, TRUE)); + addEntry(LLFolderType::FT_FAVORITE, new FolderEntry("favorite", TRUE, FALSE, TRUE)); for (S32 ensemble_num = S32(LLFolderType::FT_ENSEMBLE_START); ensemble_num <= S32(LLFolderType::FT_ENSEMBLE_END); ensemble_num++) { - addEntry(LLFolderType::EType(ensemble_num), new FolderEntry("ensemble", FALSE)); + addEntry(LLFolderType::EType(ensemble_num), new FolderEntry("ensemble", FALSE, FALSE, FALSE)); // Not used } - addEntry(LLFolderType::FT_CURRENT_OUTFIT, new FolderEntry("current", TRUE)); - addEntry(LLFolderType::FT_OUTFIT, new FolderEntry("outfit", FALSE)); - addEntry(LLFolderType::FT_MY_OUTFITS, new FolderEntry("my_otfts", TRUE)); + addEntry(LLFolderType::FT_CURRENT_OUTFIT, new FolderEntry("current", TRUE, FALSE, TRUE)); + addEntry(LLFolderType::FT_OUTFIT, new FolderEntry("outfit", FALSE, FALSE, FALSE)); + addEntry(LLFolderType::FT_MY_OUTFITS, new FolderEntry("my_otfts", TRUE, FALSE, TRUE)); - addEntry(LLFolderType::FT_MESH, new FolderEntry("mesh", TRUE)); + addEntry(LLFolderType::FT_MESH, new FolderEntry("mesh", TRUE, FALSE, FALSE)); // Not used? - addEntry(LLFolderType::FT_INBOX, new FolderEntry("inbox", TRUE)); - addEntry(LLFolderType::FT_OUTBOX, new FolderEntry("outbox", FALSE)); // Make obsolete Merchant Outbox folder deletable + addEntry(LLFolderType::FT_INBOX, new FolderEntry("inbox", TRUE, FALSE, TRUE)); + addEntry(LLFolderType::FT_OUTBOX, new FolderEntry("outbox", FALSE, FALSE, FALSE)); // Make obsolete Merchant Outbox folder deletable - addEntry(LLFolderType::FT_BASIC_ROOT, new FolderEntry("basic_rt", TRUE)); + addEntry(LLFolderType::FT_BASIC_ROOT, new FolderEntry("basic_rt", TRUE, FALSE, FALSE)); - addEntry(LLFolderType::FT_MARKETPLACE_LISTINGS, new FolderEntry("merchant", FALSE)); - addEntry(LLFolderType::FT_MARKETPLACE_STOCK, new FolderEntry("stock", FALSE)); - addEntry(LLFolderType::FT_MARKETPLACE_VERSION, new FolderEntry("version", FALSE)); + addEntry(LLFolderType::FT_MARKETPLACE_LISTINGS, new FolderEntry("merchant", FALSE, FALSE, FALSE)); + addEntry(LLFolderType::FT_MARKETPLACE_STOCK, new FolderEntry("stock", FALSE, FALSE, FALSE)); + addEntry(LLFolderType::FT_MARKETPLACE_VERSION, new FolderEntry("version", FALSE, FALSE, FALSE)); - addEntry(LLFolderType::FT_SETTINGS, new FolderEntry("settings", TRUE)); + addEntry(LLFolderType::FT_SETTINGS, new FolderEntry("settings", TRUE, FALSE, TRUE)); - addEntry(LLFolderType::FT_MY_SUITCASE, new FolderEntry("suitcase", TRUE)); // OpenSim HG-support + addEntry(LLFolderType::FT_MY_SUITCASE, new FolderEntry("suitcase", TRUE, FALSE, TRUE)); // OpenSim HG-support - addEntry(LLFolderType::FT_NONE, new FolderEntry("-1", FALSE)); + addEntry(LLFolderType::FT_NONE, new FolderEntry("-1", FALSE, FALSE, FALSE)); }; // static @@ -128,8 +149,8 @@ const std::string &LLFolderType::lookup(LLFolderType::EType folder_type) } // static -// Only ensembles and plain folders aren't protected. "Protected" means -// you can't change certain properties such as their type. +// Only plain folders and a few other types aren't protected. "Protected" means +// you can't move, deleted, or change certain properties such as their type. bool LLFolderType::lookupIsProtectedType(EType folder_type) { const LLFolderDictionary *dict = LLFolderDictionary::getInstance(); @@ -140,6 +161,32 @@ bool LLFolderType::lookupIsProtectedType(EType folder_type) } return true; } + +// static +// Is this folder type automatically created outside the viewer? +bool LLFolderType::lookupIsAutomaticType(EType folder_type) +{ + const LLFolderDictionary *dict = LLFolderDictionary::getInstance(); + const FolderEntry *entry = dict->lookup(folder_type); + if (entry) + { + return entry->mIsAutomatic; + } + return true; +} + +// static +// Should this folder always exist as a single copy under (or as) the root? +bool LLFolderType::lookupIsSingletonType(EType folder_type) +{ + const LLFolderDictionary *dict = LLFolderDictionary::getInstance(); + const FolderEntry *entry = dict->lookup(folder_type); + if (entry) + { + return entry->mIsSingleton; + } + return true; +} // static bool LLFolderType::lookupIsEnsembleType(EType folder_type) diff --git a/indra/llinventory/llfoldertype.h b/indra/llinventory/llfoldertype.h index 0f231a76dd..c3edac9dbf 100644 --- a/indra/llinventory/llfoldertype.h +++ b/indra/llinventory/llfoldertype.h @@ -110,6 +110,8 @@ public: static const std::string& lookup(EType folder_type); static bool lookupIsProtectedType(EType folder_type); + static bool lookupIsAutomaticType(EType folder_type); + static bool lookupIsSingletonType(EType folder_type); static bool lookupIsEnsembleType(EType folder_type); static LLAssetType::EType folderTypeToAssetType(LLFolderType::EType folder_type); diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 7c6a5a46c4..a613ef0c59 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -2458,7 +2458,14 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl) //copy out indices - face.resizeIndices(idx.size()/2); + S32 num_indices = idx.size() / 2; + face.resizeIndices(num_indices); + + if (num_indices > 2 && !face.mIndices) + { + LL_WARNS() << "Failed to allocate " << num_indices << " indices for face index: " << i << " Total: " << face_count << LL_ENDL; + continue; + } if (idx.empty() || face.mNumIndices < 3) { //why is there an empty index list? @@ -2477,6 +2484,13 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl) U32 num_verts = pos.size()/(3*2); face.resizeVertices(num_verts); + if (num_verts > 0 && !face.mPositions) + { + LL_WARNS() << "Failed to allocate " << num_verts << " vertices for face index: " << i << " Total: " << face_count << LL_ENDL; + face.resizeIndices(0); + continue; + } + LLVector3 minp; LLVector3 maxp; LLVector2 min_tc; @@ -2578,6 +2592,13 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl) if (mdl[i].has("Weights")) { face.allocateWeights(num_verts); + if (!face.mWeights && num_verts) + { + LL_WARNS() << "Failed to allocate " << num_verts << " weights for face index: " << i << " Total: " << face_count << LL_ENDL; + face.resizeIndices(0); + face.resizeVertices(0); + continue; + } LLSD::Binary weights = mdl[i]["Weights"]; @@ -5376,22 +5397,23 @@ bool LLVolumeFace::cacheOptimize() { triangle_data.resize(mNumIndices / 3); vertex_data.resize(mNumVertices); - } - catch (std::bad_alloc&) - { - LL_WARNS("LLVOLUME") << "Resize failed" << LL_ENDL; - return false; - } - for (U32 i = 0; i < mNumIndices; i++) - { //populate vertex data and triangle data arrays - U16 idx = mIndices[i]; - U32 tri_idx = i/3; + for (U32 i = 0; i < mNumIndices; i++) + { //populate vertex data and triangle data arrays + U16 idx = mIndices[i]; + U32 tri_idx = i / 3; - vertex_data[idx].mTriangles.push_back(&(triangle_data[tri_idx])); - vertex_data[idx].mIdx = idx; - triangle_data[tri_idx].mVertex[i%3] = &(vertex_data[idx]); - } + vertex_data[idx].mTriangles.push_back(&(triangle_data[tri_idx])); + vertex_data[idx].mIdx = idx; + triangle_data[tri_idx].mVertex[i % 3] = &(vertex_data[idx]); + } + } + catch (std::bad_alloc&) + { + // resize or push_back failed + LL_WARNS("LLVOLUME") << "Resize for " << mNumVertices << " vertices failed" << LL_ENDL; + return false; + } /*F32 pre_acmr = 1.f; //measure cache misses from before rebuild @@ -6435,8 +6457,18 @@ void LLVolumeFace::resizeVertices(S32 num_verts) mTexCoords = NULL; } - mNumVertices = num_verts; - mNumAllocatedVertices = num_verts; + + if (mPositions) + { + mNumVertices = num_verts; + mNumAllocatedVertices = num_verts; + } + else + { + // Either num_verts is zero or allocation failure + mNumVertices = 0; + mNumAllocatedVertices = 0; + } // Force update mJointRiggingInfoTab.clear(); @@ -6537,7 +6569,15 @@ void LLVolumeFace::resizeIndices(S32 num_indices) mIndices = NULL; } - mNumIndices = num_indices; + if (mIndices) + { + mNumIndices = num_indices; + } + else + { + // Either num_indices is zero or allocation failure + mNumIndices = 0; + } } void LLVolumeFace::pushIndex(const U16& idx) diff --git a/indra/llmessage/llnamevalue.cpp b/indra/llmessage/llnamevalue.cpp index c51883ee3d..6076439518 100644 --- a/indra/llmessage/llnamevalue.cpp +++ b/indra/llmessage/llnamevalue.cpp @@ -35,6 +35,12 @@ #include "llstring.h" #include "llstringtable.h" +// Suppress warnings about the string fiddling +#if LL_LINUX +#pragma GCC diagnostic ignored "-Wstringop-truncation" +#endif +// + // Anonymous enumeration to provide constants in this file. // *NOTE: These values may be used in sscanf statements below as their // value-1, so search for '2047' if you cange NV_BUFFER_LEN or '63' if diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py index 9cd2959ea1..5ba0749e31 100755 --- a/indra/llmessage/tests/test_llsdmessage_peer.py +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """\ @file test_llsdmessage_peer.py @author Nat Goodspeed @@ -31,7 +31,7 @@ $/LicenseInfo$ import os import sys -from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +from http.server import HTTPServer, BaseHTTPRequestHandler from llbase.fastest_elementtree import parse as xml_parse from llbase import llsd @@ -165,7 +165,7 @@ if __name__ == "__main__": # "Then there's Windows" # Instantiate a Server(TestHTTPRequestHandler) on the first free port # in the specified port range. - httpd, port = freeport(xrange(8000, 8020), make_server) + httpd, port = freeport(range(8000, 8020), make_server) # Pass the selected port number to the subject test program via the # environment. We don't want to impose requirements on the test program's diff --git a/indra/llmessage/tests/testrunner.py b/indra/llmessage/tests/testrunner.py index c25945067e..47c09ca245 100755 --- a/indra/llmessage/tests/testrunner.py +++ b/indra/llmessage/tests/testrunner.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """\ @file testrunner.py @author Nat Goodspeed @@ -41,7 +41,7 @@ VERBOSE = not re.match(r"(0|off|false|quiet)$", VERBOSE, re.IGNORECASE) if VERBOSE: def debug(fmt, *args): - print fmt % args + print(fmt % args) sys.stdout.flush() else: debug = lambda *args: None @@ -99,14 +99,14 @@ def freeport(portlist, expr): # error because we can't return meaningful values. We have no 'port', # therefore no 'expr(port)'. portiter = iter(portlist) - port = portiter.next() + port = next(portiter) while True: try: # If this value of port works, return as promised. value = expr(port) - except socket.error, err: + except socket.error as err: # Anything other than 'Address already in use', propagate if err.args[0] != errno.EADDRINUSE: raise @@ -117,9 +117,9 @@ def freeport(portlist, expr): type, value, tb = sys.exc_info() try: try: - port = portiter.next() + port = next(portiter) except StopIteration: - raise type, value, tb + raise type(value).with_traceback(tb) finally: # Clean up local traceback, see docs for sys.exc_info() del tb @@ -138,7 +138,7 @@ def freeport(portlist, expr): # If we've actually arrived at this point, portiter.next() delivered a # new port value. Loop back to pass that to expr(port). - except Exception, err: + except Exception as err: debug("*** freeport() raising %s: %s", err.__class__.__name__, err) raise @@ -227,13 +227,13 @@ def test_freeport(): def exc(exception_class, *args): try: yield - except exception_class, err: + except exception_class as err: for i, expected_arg in enumerate(args): assert expected_arg == err.args[i], \ "Raised %s, but args[%s] is %r instead of %r" % \ (err.__class__.__name__, i, err.args[i], expected_arg) - print "Caught expected exception %s(%s)" % \ - (err.__class__.__name__, ', '.join(repr(arg) for arg in err.args)) + print("Caught expected exception %s(%s)" % \ + (err.__class__.__name__, ', '.join(repr(arg) for arg in err.args))) else: assert False, "Failed to raise " + exception_class.__class__.__name__ @@ -270,18 +270,18 @@ def test_freeport(): # This is the magic exception that should prompt us to retry inuse = socket.error(errno.EADDRINUSE, 'Address already in use') # Get the iterator to our ports list so we can check later if we've used all - ports = iter(xrange(5)) + ports = iter(range(5)) with exc(socket.error, errno.EADDRINUSE): freeport(ports, lambda port: raiser(inuse)) # did we entirely exhaust 'ports'? with exc(StopIteration): - ports.next() + next(ports) - ports = iter(xrange(2)) + ports = iter(range(2)) # Any exception but EADDRINUSE should quit immediately with exc(SomeError): freeport(ports, lambda port: raiser(SomeError())) - assert_equals(ports.next(), 1) + assert_equals(next(ports), 1) # ----------- freeport() with platform-dependent socket stuff ------------ # This is what we should've had unit tests to begin with (see CHOP-661). @@ -290,14 +290,14 @@ def test_freeport(): sock.bind(('127.0.0.1', port)) return sock - bound0, port0 = freeport(xrange(7777, 7780), newbind) + bound0, port0 = freeport(range(7777, 7780), newbind) assert_equals(port0, 7777) - bound1, port1 = freeport(xrange(7777, 7780), newbind) + bound1, port1 = freeport(range(7777, 7780), newbind) assert_equals(port1, 7778) - bound2, port2 = freeport(xrange(7777, 7780), newbind) + bound2, port2 = freeport(range(7777, 7780), newbind) assert_equals(port2, 7779) with exc(socket.error, errno.EADDRINUSE): - bound3, port3 = freeport(xrange(7777, 7780), newbind) + bound3, port3 = freeport(range(7777, 7780), newbind) if __name__ == "__main__": test_freeport() diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index 7088cf4d4b..7ebd73f313 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -82,8 +82,29 @@ protected: }; + +class LLPluginProcessCreationThread : public LLThread +{ +public: + LLPluginProcessCreationThread(LLPluginProcessParent *parent) : + LLThread("LLPluginProcessCreationThread", gAPRPoolp), + pParent(parent) + { + } +protected: + // Inherited from LLThread, should run once + /*virtual*/ void run(void) + { + pParent->createPluginProcess(); + } +private: + LLPluginProcessParent *pParent; + +}; + LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner): - mIncomingQueueMutex() + mIncomingQueueMutex(), + pProcessCreationThread(NULL) { if(!sInstancesMutex) { @@ -112,6 +133,18 @@ LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner): LLPluginProcessParent::~LLPluginProcessParent() { LL_DEBUGS("Plugin") << "destructor" << LL_ENDL; + if (pProcessCreationThread) + { + if (!pProcessCreationThread->isStopped()) + { + // Shouldn't happen at this stage + LL_WARNS("Plugin") << "Shutting down active pProcessCreationThread" << LL_ENDL; + pProcessCreationThread->shutdown(); + ms_sleep(20); + } + delete pProcessCreationThread; + pProcessCreationThread = NULL; + } // Destroy any remaining shared memory regions sharedMemoryRegionsType::iterator iter; @@ -162,6 +195,7 @@ void LLPluginProcessParent::shutdown() && state != STATE_ERROR) { (*it).second->setState(STATE_GOODBYE); + (*it).second->mOwner = NULL; } if (state != STATE_DONE) { @@ -321,6 +355,35 @@ bool LLPluginProcessParent::accept() return result; } +bool LLPluginProcessParent::createPluginProcess() +{ + if (!mProcess) + { + // Only argument to the launcher is the port number we're listening on + mProcessParams.args.add(stringize(mBoundPort)); + mProcess = LLProcess::create(mProcessParams); + return mProcess != NULL; + } + + return false; +} + +void LLPluginProcessParent::clearProcessCreationThread() +{ + if (pProcessCreationThread) + { + if (!pProcessCreationThread->isStopped()) + { + pProcessCreationThread->shutdown(); + } + else + { + delete pProcessCreationThread; + pProcessCreationThread = NULL; + } + } +} + void LLPluginProcessParent::idle(void) { bool idle_again; @@ -328,8 +391,9 @@ void LLPluginProcessParent::idle(void) do { // process queued messages - mIncomingQueueMutex.lock(); - while(!mIncomingQueue.empty()) + // Inside main thread, it is preferable not to block it on mutex. + bool locked = mIncomingQueueMutex.trylock(); + while(locked && !mIncomingQueue.empty()) { LLPluginMessage message = mIncomingQueue.front(); mIncomingQueue.pop(); @@ -337,10 +401,13 @@ void LLPluginProcessParent::idle(void) receiveMessage(message); - mIncomingQueueMutex.lock(); + locked = mIncomingQueueMutex.trylock(); } - mIncomingQueueMutex.unlock(); + if (locked) + { + mIncomingQueueMutex.unlock(); + } // Give time to network processing if(mMessagePipe) @@ -349,7 +416,10 @@ void LLPluginProcessParent::idle(void) mMessagePipe->pumpOutput(); // Only do input processing here if this instance isn't in a pollset. - if(!mPolledInput) + // If viewer and plugin are both shutting down, don't process further + // input, viewer won't be able to handle it. + if(!mPolledInput + && !(mState >= STATE_GOODBYE && LLApp::isExiting())) { mMessagePipe->pumpInput(); } @@ -488,15 +558,30 @@ void LLPluginProcessParent::idle(void) case STATE_LISTENING: { // Launch the plugin process. + if (mDebug && !pProcessCreationThread) + { + createPluginProcess(); + if (!mProcess) + { + errorState(); + } + } + else if (pProcessCreationThread == NULL) + { + // exe plugin process allocation can be hindered by a number + // of factors, don't hold whole viewer because of it, use thread + pProcessCreationThread = new LLPluginProcessCreationThread(this); + pProcessCreationThread->start(); + } + else if (!mProcess && pProcessCreationThread->isStopped()) + { + delete pProcessCreationThread; + pProcessCreationThread = NULL; + errorState(); + } + - // Only argument to the launcher is the port number we're listening on - mProcessParams.args.add(stringize(mBoundPort)); - - if (! (mProcess = LLProcess::create(mProcessParams))) - { - errorState(); - } - else + if (mProcess) { if(mDebug) { @@ -525,6 +610,15 @@ void LLPluginProcessParent::idle(void) // This will allow us to time out if the process never starts. mHeartbeat.start(); mHeartbeat.setTimerExpirySec(mPluginLaunchTimeout); + + // pProcessCreationThread should have stopped by this point, + // but check just in case it paused on statistics sync + if (pProcessCreationThread && pProcessCreationThread->isStopped()) + { + delete pProcessCreationThread; + pProcessCreationThread = NULL; + } + setState(STATE_LAUNCHED); } } @@ -627,6 +721,7 @@ void LLPluginProcessParent::idle(void) killSockets(); setState(STATE_DONE); dirtyPollSet(); + clearProcessCreationThread(); break; case STATE_DONE: diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h index 7fde43a149..b70dff86c3 100644 --- a/indra/llplugin/llpluginprocessparent.h +++ b/indra/llplugin/llpluginprocessparent.h @@ -69,6 +69,11 @@ public: const std::string &plugin_filename, bool debug); + // Creates a process + // returns true if process already exists or if created, + // false if failed to create + bool createPluginProcess(); + void idle(void); // returns true if the plugin is on its way to steady state @@ -163,12 +168,15 @@ private: bool accept(); + void clearProcessCreationThread(); + LLSocket::ptr_t mListenSocket; LLSocket::ptr_t mSocket; U32 mBoundPort; LLProcess::Params mProcessParams; LLProcessPtr mProcess; + LLThread *pProcessCreationThread; std::string mPluginFile; std::string mPluginDir; diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index bc133d3692..0a9d4bef03 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -242,6 +242,17 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector& fa } LLVolumeFace::VertexMapData::PointMap point_map; + + if (idx_stride <= 0 + || (pos_source && pos_offset >= idx_stride) + || (tc_source && tc_offset >= idx_stride) + || (norm_source && norm_offset >= idx_stride)) + { + // Looks like these offsets should fit inside idx_stride + // Might be good idea to also check idx.getCount()%idx_stride != 0 + LL_WARNS() << "Invalid pos_offset " << pos_offset << ", tc_offset " << tc_offset << " or norm_offset " << norm_offset << LL_ENDL; + return LLModel::BAD_ELEMENT; + } for (U32 i = 0; i < idx.getCount(); i += idx_stride) { diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index 5193b16a1e..da375292e8 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -606,6 +606,58 @@ void LLFloaterReg::toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& } } +// static +// Same as toggleInstanceOrBringToFront but does not close floater. +// unlike showInstance() does not trigger onOpen() if already open +void LLFloaterReg::showInstanceOrBringToFront(const LLSD& sdname, const LLSD& key) +{ + std::string name = sdname.asString(); + LLFloater* instance = getInstance(name, key); + + + if (!instance) + { + LL_DEBUGS() << "Unable to get instance of floater '" << name << "'" << LL_ENDL; + return; + } + + // If hosted, we need to take that into account + LLFloater* host = instance->getHost(); + + if (host) + { + if (host->isMinimized() || !host->isShown() || !host->isFrontmost()) + { + host->setMinimized(FALSE); + instance->openFloater(key); + instance->setVisibleAndFrontmost(true, key); + } + else if (!instance->getVisible()) + { + instance->openFloater(key); + instance->setVisibleAndFrontmost(true, key); + instance->setFocus(TRUE); + } + } + else + { + if (instance->isMinimized()) + { + instance->setMinimized(FALSE); + instance->setVisibleAndFrontmost(true, key); + } + else if (!instance->isShown()) + { + instance->openFloater(key); + instance->setVisibleAndFrontmost(true, key); + } + else if (!instance->isFrontmost()) + { + instance->setVisibleAndFrontmost(true, key); + } + } +} + // static U32 LLFloaterReg::getVisibleFloaterInstanceCount() { diff --git a/indra/llui/llfloaterreg.h b/indra/llui/llfloaterreg.h index a76eb5c79b..6ebebf3fe3 100644 --- a/indra/llui/llfloaterreg.h +++ b/indra/llui/llfloaterreg.h @@ -158,6 +158,7 @@ public: // Callback wrappers static void toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD()); + static void showInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD()); // Typed find / get / show template diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index de77a2c98c..6028986eaf 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -261,6 +261,8 @@ LLFolderView::LLFolderView(const Params& p) mPopupMenuHandle = menu->getHandle(); mViewModelItem->openItem(); + + mAreChildrenInited = true; // root folder is a special case due to not being loaded normally, assume that it's inited. } // Destroys the object diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 64d702a612..3378bcaadb 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -139,7 +139,6 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) mCutGeneration(0), mLabelStyle( LLFontGL::NORMAL ), mHasVisibleChildren(FALSE), - mIsFolderComplete(true), mLocalIndentation(p.folder_indentation), mIndentation(0), mItemHeight(p.item_height), @@ -1118,11 +1117,11 @@ LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): mCurHeight(0.f), mTargetHeight(0.f), mAutoOpenCountdown(0.f), + mIsFolderComplete(false), // folder might have children that are not loaded yet. + mAreChildrenInited(false), // folder might have children that are not built yet. mLastArrangeGeneration( -1 ), mLastCalculatedWidth(0) { - // folder might have children that are not loaded yet. Mark it as incomplete until chance to check it. - mIsFolderComplete = false; } void LLFolderViewFolder::updateLabelRotation() @@ -1178,13 +1177,16 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height ) { // Sort before laying out contents // Note that we sort from the root (CHUI-849) - getRoot()->getFolderViewModel()->sort(this); + if (mAreChildrenInited) + { + getRoot()->getFolderViewModel()->sort(this); + } LL_RECORD_BLOCK_TIME(FTM_ARRANGE); // evaluate mHasVisibleChildren mHasVisibleChildren = false; - if (getViewModelItem()->descendantsPassedFilter()) + if (mAreChildrenInited && getViewModelItem()->descendantsPassedFilter()) { // We have to verify that there's at least one child that's not filtered out bool found = false; @@ -1210,7 +1212,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height ) mHasVisibleChildren = found; } - if (!mIsFolderComplete) + if (!mIsFolderComplete && mAreChildrenInited) { mIsFolderComplete = getFolderViewModel()->isFolderComplete(this); } diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index 72bb926163..2d36616e25 100644 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -120,7 +120,6 @@ protected: F32 mControlLabelRotation; LLFolderView* mRoot; bool mHasVisibleChildren, - mIsFolderComplete, // indicates that some children were not loaded/added yet mIsCurSelection, mDragAndDropTarget, mIsMouseOverTitle, @@ -229,7 +228,10 @@ public: BOOL hasVisibleChildren() { return mHasVisibleChildren; } // true if object can't have children - BOOL isFolderComplete() { return mIsFolderComplete; } + virtual bool isFolderComplete() { return true; } + // true if object can't have children + virtual bool areChildrenInited() { return true; } + virtual void setChildrenInited(bool inited) { } // Call through to the viewed object and return true if it can be // removed. Returns true if it's removed. @@ -349,6 +351,8 @@ protected: S32 mLastArrangeGeneration; S32 mLastCalculatedWidth; // bool mNeedsSort; Unused. + bool mIsFolderComplete; // indicates that some children were not loaded/added yet + bool mAreChildrenInited; // indicates that no children were initialized public: typedef enum e_recurse_type @@ -400,6 +404,13 @@ public: // destroys this folder, and all children virtual void destroyView(); + // whether known children are fully loaded (arrange sets to true) + virtual bool isFolderComplete() { return mIsFolderComplete; } + + // whether known children are fully built + virtual bool areChildrenInited() { return mAreChildrenInited; } + virtual void setChildrenInited(bool inited) { mAreChildrenInited = inited; } + // extractItem() removes the specified item from the folder, but // doesn't delete it. virtual void extractItem( LLFolderViewItem* item, bool deparent_model = true); diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 34d8ed6fef..d8d23ccd7d 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -500,8 +500,7 @@ LLNotification::LLNotification(const LLSDParamAdapter& p) : mResponderObj(NULL), mId(p.id.isProvided() ? p.id : LLUUID::generateNewID()), mOfferFromAgent(p.offer_from_agent), - mIsDND(p.is_dnd), - mIsFromStorage(false)// FIRE-11339: Persisted group notifications get logged to IM on each login + mIsDND(p.is_dnd) { if (p.functor.name.isChosen()) { @@ -1710,6 +1709,20 @@ void LLNotifications::add(const LLNotificationPtr pNotif) updateItem(LLSD().with("sigtype", "add").with("id", pNotif->id()), pNotif); } +void LLNotifications::load(const LLNotificationPtr pNotif) +{ + if (pNotif == NULL) return; + + // first see if we already have it -- if so, that's a problem + LLNotificationSet::iterator it=mItems.find(pNotif); + if (it != mItems.end()) + { + LL_ERRS() << "Notification loaded a second time to the master notification channel." << LL_ENDL; + } + + updateItem(LLSD().with("sigtype", "load").with("id", pNotif->id()), pNotif); +} + void LLNotifications::cancel(LLNotificationPtr pNotif) { if (pNotif == NULL || pNotif->isCancelled()) return; diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 624c267f28..300a124d68 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -399,7 +399,6 @@ private: LLNotificationResponderPtr mResponder; bool mOfferFromAgent; bool mIsDND; - bool mIsFromStorage; // FIRE-11339: Persisted group notifications get logged to IM on each login // a reference to the template LLNotificationTemplatePtr mTemplatep; @@ -557,18 +556,6 @@ public: mIsDND = flag; } - // FIRE-11339: Persisted group notifications get logged to IM on each login - bool isFromStorage() const - { - return mIsFromStorage; - } - - void setIsFromStorage(bool logged) - { - mIsFromStorage = logged; - } - // - std::string getType() const; std::string getMessage() const; std::string getFooter() const; @@ -936,6 +923,7 @@ public: LLNotificationPtr add(const LLNotification::Params& p); void add(const LLNotificationPtr pNotif); + void load(const LLNotificationPtr pNotif); void cancel(LLNotificationPtr pNotif); void cancelByName(const std::string& name); void cancelByOwner(const LLUUID ownerId); @@ -1139,6 +1127,11 @@ private: mHistory.push_back(p); } + void onLoad(LLNotificationPtr p) + { + mHistory.push_back(p); + } + std::vector mHistory; }; diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp index 61470d1440..f73c9aa539 100644 --- a/indra/llui/llscrolllistcell.cpp +++ b/indra/llui/llscrolllistcell.cpp @@ -83,6 +83,14 @@ const LLSD LLScrollListCell::getValue() const return LLStringUtil::null; } + +// virtual +const LLSD LLScrollListCell::getAltValue() const +{ + return LLStringUtil::null; +} + + // // LLScrollListIcon // @@ -245,6 +253,7 @@ U32 LLScrollListText::sCount = 0; LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p) : LLScrollListCell(p), mText(p.label.isProvided() ? p.label() : p.value().asString()), + mAltText(p.alt_value().asString()), mFont(p.font), mColor(p.color), mUseColor(p.color.isProvided()), @@ -347,10 +356,22 @@ void LLScrollListText::setValue(const LLSD& text) setText(text.asString()); } +//virtual +void LLScrollListText::setAltValue(const LLSD& text) +{ + mAltText = text.asString(); +} + //virtual const LLSD LLScrollListText::getValue() const { - return LLSD(mText.getString()); + return LLSD(mText.getString()); +} + +//virtual +const LLSD LLScrollListText::getAltValue() const +{ + return LLSD(mAltText.getString()); } diff --git a/indra/llui/llscrolllistcell.h b/indra/llui/llscrolllistcell.h index 9a659dfd0d..2588da2331 100644 --- a/indra/llui/llscrolllistcell.h +++ b/indra/llui/llscrolllistcell.h @@ -61,6 +61,7 @@ public: Optional userdata; Optional value; // state of checkbox, icon id/name, date + Optional alt_value; Optional label; // description or text Optional tool_tip; @@ -77,6 +78,7 @@ public: enabled("enabled", true), visible("visible", true), value("value"), + alt_value("alt_value", ""), label("label"), tool_tip("tool_tip", ""), font("font", LLFontGL::getFontSansSerifSmall()), @@ -99,7 +101,9 @@ public: virtual S32 getContentWidth() const { return 0; } virtual S32 getHeight() const { return 0; } virtual const LLSD getValue() const; + virtual const LLSD getAltValue() const; virtual void setValue(const LLSD& value) { } + virtual void setAltValue(const LLSD& value) { } virtual const std::string &getToolTip() const { return mToolTip; } virtual void setToolTip(const std::string &str) { mToolTip = str; } virtual BOOL getVisible() const { return TRUE; } @@ -139,7 +143,9 @@ public: /*virtual*/ S32 getContentWidth() const; /*virtual*/ S32 getHeight() const; /*virtual*/ void setValue(const LLSD& value); + /*virtual*/ void setAltValue(const LLSD& value); /*virtual*/ const LLSD getValue() const; + /*virtual*/ const LLSD getAltValue() const; /*virtual*/ BOOL getVisible() const; /*virtual*/ void highlightText(S32 offset, S32 num_chars); @@ -158,6 +164,7 @@ public: protected: LLUIString mText; + LLUIString mAltText; S32 mTextWidth; const LLFontGL* mFont; LLColor4 mColor; diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 8407b30e4c..64ca9723cf 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -68,9 +68,10 @@ static LLDefaultChildRegistry::Register r("scroll_list"); // local structures & classes. struct SortScrollListItem { - SortScrollListItem(const std::vector >& sort_orders,const LLScrollListCtrl::sort_signal_t* sort_signal) + SortScrollListItem(const std::vector >& sort_orders,const LLScrollListCtrl::sort_signal_t* sort_signal, bool alternate_sort) : mSortOrders(sort_orders) , mSortSignal(sort_signal) + , mAltSort(alternate_sort) {} bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2) @@ -95,7 +96,14 @@ struct SortScrollListItem } else { - sort_result = order * LLStringUtil::compareDict(cell1->getValue().asString(), cell2->getValue().asString()); + if (mAltSort && !cell1->getAltValue().asString().empty() && !cell2->getAltValue().asString().empty()) + { + sort_result = order * LLStringUtil::compareDict(cell1->getAltValue().asString(), cell2->getAltValue().asString()); + } + else + { + sort_result = order * LLStringUtil::compareDict(cell1->getValue().asString(), cell2->getValue().asString()); + } } if (sort_result != 0) { @@ -111,6 +119,7 @@ struct SortScrollListItem typedef std::vector > sort_order_t; const LLScrollListCtrl::sort_signal_t* mSortSignal; const sort_order_t& mSortOrders; + const bool mAltSort; }; //--------------------------------------------------------------------------- @@ -219,6 +228,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p) mSearchColumn(p.search_column), mColumnPadding(p.column_padding), mRowPadding(p.row_padding), + mAlternateSort(false), mContextMenuType(MENU_NONE), mIsFriendSignal(NULL), // Fix for FS-specific people list (radar) @@ -400,8 +410,7 @@ LLScrollListCtrl::~LLScrollListCtrl() std::for_each(mItemList.begin(), mItemList.end(), DeletePointer()); mItemList.clear(); - std::for_each(mColumns.begin(), mColumns.end(), DeletePairedPointer()); - mColumns.clear(); + clearColumns(); //clears columns and deletes headers delete mIsFriendSignal; } @@ -3098,7 +3107,7 @@ void LLScrollListCtrl::updateSort() const std::stable_sort( mItemList.begin(), mItemList.end(), - SortScrollListItem(mSortColumns,mSortCallback)); + SortScrollListItem(mSortColumns,mSortCallback, mAlternateSort)); mSorted = true; } @@ -3114,7 +3123,7 @@ void LLScrollListCtrl::sortOnce(S32 column, BOOL ascending) std::stable_sort( mItemList.begin(), mItemList.end(), - SortScrollListItem(sort_column,mSortCallback)); + SortScrollListItem(sort_column,mSortCallback,mAlternateSort)); } void LLScrollListCtrl::dirtyColumns() @@ -3480,6 +3489,8 @@ void LLScrollListCtrl::clearColumns() // Reset number of dynamic columns, too mNumDynamicWidthColumns = 0; + + dirtyColumns(); // Clears mColumnsIndexed } void LLScrollListCtrl::setColumnLabel(const std::string& column, const std::string& label) diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index af00f56db5..99ffa2a94f 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -421,6 +421,8 @@ public: BOOL hasSortOrder() const; void clearSortOrder(); + void setAlternateSort() { mAlternateSort = true; } + S32 selectMultiple( uuid_vec_t ids ); // conceptually const, but mutates mItemList void updateSort() const; @@ -519,6 +521,8 @@ private: bool mColumnsDirty; bool mColumnWidthsDirty; + bool mAlternateSort; + mutable item_list mItemList; LLScrollListItem *mLastSelected; diff --git a/indra/llui/llscrolllistitem.cpp b/indra/llui/llscrolllistitem.cpp index 51c615dd00..e1360f80cd 100644 --- a/indra/llui/llscrolllistitem.cpp +++ b/indra/llui/llscrolllistitem.cpp @@ -44,7 +44,8 @@ LLScrollListItem::LLScrollListItem( const Params& p ) mSelectedIndex(-1), mEnabled(p.enabled), mUserdata(p.userdata), - mItemValue(p.value) + mItemValue(p.value), + mItemAltValue(p.alt_value) { } diff --git a/indra/llui/llscrolllistitem.h b/indra/llui/llscrolllistitem.h index d2c3dd7721..a3398305b1 100644 --- a/indra/llui/llscrolllistitem.h +++ b/indra/llui/llscrolllistitem.h @@ -55,6 +55,7 @@ public: Optional enabled; Optional userdata; Optional value; + Optional alt_value; Ignored name; // use for localization tools Ignored type; @@ -65,6 +66,7 @@ public: Params() : enabled("enabled", true), value("value"), + alt_value("alt_value"), name("name"), type("type"), length("length"), @@ -97,6 +99,7 @@ public: virtual LLUUID getUUID() const { return mItemValue.asUUID(); } LLSD getValue() const { return mItemValue; } + LLSD getAltValue() const { return mItemAltValue; } void setRect(LLRect rect) { mRectangle = rect; } LLRect getRect() const { return mRectangle; } @@ -131,6 +134,7 @@ private: BOOL mEnabled; void* mUserdata; LLSD mItemValue; + LLSD mItemAltValue; std::vector mColumns; LLRect mRectangle; }; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index e927142275..da152d96b6 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -1705,11 +1705,14 @@ void LLTextBase::reflow() { // find first element whose end comes after start_index line_list_t::iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), start_index, line_end_compare()); - line_start_index = iter->mDocIndexStart; - line_count = iter->mLineNum; - cur_top = iter->mRect.mTop; - getSegmentAndOffset(iter->mDocIndexStart, &seg_iter, &seg_offset); - mLineInfoList.erase(iter, mLineInfoList.end()); + if (iter != mLineInfoList.end()) + { + line_start_index = iter->mDocIndexStart; + line_count = iter->mLineNum; + cur_top = iter->mRect.mTop; + getSegmentAndOffset(iter->mDocIndexStart, &seg_iter, &seg_offset); + mLineInfoList.erase(iter, mLineInfoList.end()); + } } S32 line_height = 0; diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp index ee5873885b..dcce2aa1fd 100644 --- a/indra/llui/lltoolbar.cpp +++ b/indra/llui/lltoolbar.cpp @@ -1177,7 +1177,8 @@ LLToolBarButton* LLToolBar::createButton(const LLCommandId& id) else { button->setFunctionName(commandp->executeFunctionName()); - LL_DEBUGS("UIUsage") << "button function name b -> " << commandp->executeFunctionName() << LL_ENDL; // Check enabled state of button before executing! + LL_DEBUGS("UIUsage") << "button function name b -> " << commandp->executeFunctionName() << LL_ENDL; + // Check enabled state of button before executing! //button->setCommitCallback(executeParam); LLUICtrl::commit_callback_t execute_func = initCommitCallback(executeParam); button->setCommitCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, execute_func, _1, _2)); diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index 493060886c..b407f4a3df 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -197,6 +197,7 @@ mHelpImpl(NULL) reg.add("Floater.Toggle", boost::bind(&LLFloaterReg::toggleInstance, _2, LLSD())); reg.add("Floater.ToggleOrBringToFront", boost::bind(&LLFloaterReg::toggleInstanceOrBringToFront, _2, LLSD())); reg.add("Floater.Show", boost::bind(&LLFloaterReg::showInstance, _2, LLSD(), FALSE)); + reg.add("Floater.ShowOrBringToFront", boost::bind(&LLFloaterReg::showInstanceOrBringToFront, _2, LLSD())); reg.add("Floater.Hide", boost::bind(&LLFloaterReg::hideInstance, _2, LLSD())); // Button initialization callback for toggle buttons diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index bb003a77e3..53e4df3825 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -77,7 +77,8 @@ if (LINUX) ${LLXML_LIBRARIES} ${UI_LIBRARIES} # for GTK ${SDL_LIBRARY} - fontconfig # For FCInit and other FC* functions. + libfontconfig.a # For FCInit and other FC* functions. + libfreetype.a ) list(APPEND viewer_SOURCE_FILES diff --git a/indra/llwindow/llcursortypes.cpp b/indra/llwindow/llcursortypes.cpp index 86f4d898bc..c5db67532d 100644 --- a/indra/llwindow/llcursortypes.cpp +++ b/indra/llwindow/llcursortypes.cpp @@ -42,6 +42,7 @@ ECursorType getCursorFromString(const std::string& cursor_string) cursor_string_table["UI_CURSOR_SIZENESW"] = UI_CURSOR_SIZENESW; cursor_string_table["UI_CURSOR_SIZEWE"] = UI_CURSOR_SIZEWE; cursor_string_table["UI_CURSOR_SIZENS"] = UI_CURSOR_SIZENS; + cursor_string_table["UI_CURSOR_SIZEALL"] = UI_CURSOR_SIZEALL; cursor_string_table["UI_CURSOR_NO"] = UI_CURSOR_NO; cursor_string_table["UI_CURSOR_WORKING"] = UI_CURSOR_WORKING; cursor_string_table["UI_CURSOR_TOOLGRAB"] = UI_CURSOR_TOOLGRAB; @@ -61,6 +62,7 @@ ECursorType getCursorFromString(const std::string& cursor_string) cursor_string_table["UI_CURSOR_TOOLCAMERA"] = UI_CURSOR_TOOLCAMERA; cursor_string_table["UI_CURSOR_TOOLPAN"] = UI_CURSOR_TOOLPAN; cursor_string_table["UI_CURSOR_TOOLZOOMIN"] = UI_CURSOR_TOOLZOOMIN; + cursor_string_table["UI_CURSOR_TOOLZOOMOUT"] = UI_CURSOR_TOOLZOOMOUT; cursor_string_table["UI_CURSOR_TOOLPICKOBJECT3"] = UI_CURSOR_TOOLPICKOBJECT3; cursor_string_table["UI_CURSOR_TOOLPLAY"] = UI_CURSOR_TOOLPLAY; cursor_string_table["UI_CURSOR_TOOLPAUSE"] = UI_CURSOR_TOOLPAUSE; diff --git a/indra/llwindow/llcursortypes.h b/indra/llwindow/llcursortypes.h index 73f3fe6cdf..10bd38d448 100644 --- a/indra/llwindow/llcursortypes.h +++ b/indra/llwindow/llcursortypes.h @@ -38,6 +38,7 @@ enum ECursorType { UI_CURSOR_SIZENESW, UI_CURSOR_SIZEWE, UI_CURSOR_SIZENS, + UI_CURSOR_SIZEALL, UI_CURSOR_NO, UI_CURSOR_WORKING, UI_CURSOR_TOOLGRAB, @@ -57,6 +58,7 @@ enum ECursorType { UI_CURSOR_TOOLCAMERA, UI_CURSOR_TOOLPAN, UI_CURSOR_TOOLZOOMIN, + UI_CURSOR_TOOLZOOMOUT, UI_CURSOR_TOOLPICKOBJECT3, UI_CURSOR_TOOLPLAY, UI_CURSOR_TOOLPAUSE, diff --git a/indra/llwindow/llwindowmacosx-objc.mm b/indra/llwindow/llwindowmacosx-objc.mm index 7d54bdaee2..41db6ae98e 100644 --- a/indra/llwindow/llwindowmacosx-objc.mm +++ b/indra/llwindow/llwindowmacosx-objc.mm @@ -106,13 +106,13 @@ const unsigned short *copyFromPBoard() CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - + // extra retain on the NSCursor since we want it to live for the lifetime of the app. NSCursor *cursor = [[[NSCursor alloc] initWithImage: [[[NSImage alloc] initWithContentsOfFile: - [NSString stringWithFormat:@"%s", fullpath] + [NSString stringWithUTF8String:fullpath] ]autorelease] hotSpot:NSMakePoint(hotspotX, hotspotY) ]retain]; diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index e0ddb6b07b..6c85f2c21b 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -1443,6 +1443,7 @@ const char* cursorIDToName(int id) case UI_CURSOR_SIZENESW: return "UI_CURSOR_SIZENESW"; case UI_CURSOR_SIZEWE: return "UI_CURSOR_SIZEWE"; case UI_CURSOR_SIZENS: return "UI_CURSOR_SIZENS"; + case UI_CURSOR_SIZEALL: return "UI_CURSOR_SIZEALL"; case UI_CURSOR_NO: return "UI_CURSOR_NO"; case UI_CURSOR_WORKING: return "UI_CURSOR_WORKING"; case UI_CURSOR_TOOLGRAB: return "UI_CURSOR_TOOLGRAB"; @@ -1462,6 +1463,7 @@ const char* cursorIDToName(int id) case UI_CURSOR_TOOLCAMERA: return "UI_CURSOR_TOOLCAMERA"; case UI_CURSOR_TOOLPAN: return "UI_CURSOR_TOOLPAN"; case UI_CURSOR_TOOLZOOMIN: return "UI_CURSOR_TOOLZOOMIN"; + case UI_CURSOR_TOOLZOOMOUT: return "UI_CURSOR_TOOLZOOMOUT"; case UI_CURSOR_TOOLPICKOBJECT3: return "UI_CURSOR_TOOLPICKOBJECT3"; case UI_CURSOR_TOOLPLAY: return "UI_CURSOR_TOOLPLAY"; case UI_CURSOR_TOOLPAUSE: return "UI_CURSOR_TOOLPAUSE"; @@ -1640,6 +1642,7 @@ void LLWindowMacOSX::initCursors(BOOL useLegacyCursors) initPixmapCursor(UI_CURSOR_TOOLCAMERA, 7, 6); initPixmapCursor(UI_CURSOR_TOOLPAN, 7, 6); initPixmapCursor(UI_CURSOR_TOOLZOOMIN, 7, 6); + initPixmapCursor(UI_CURSOR_TOOLZOOMOUT, 7, 6); initPixmapCursor(UI_CURSOR_TOOLPICKOBJECT3, 1, 1); initPixmapCursor(UI_CURSOR_TOOLPLAY, 1, 1); initPixmapCursor(UI_CURSOR_TOOLPAUSE, 1, 1); @@ -1674,6 +1677,7 @@ void LLWindowMacOSX::initCursors(BOOL useLegacyCursors) initPixmapCursor(UI_CURSOR_SIZENESW, 10, 10); initPixmapCursor(UI_CURSOR_SIZEWE, 10, 10); initPixmapCursor(UI_CURSOR_SIZENS, 10, 10); + initPixmapCursor(UI_CURSOR_SIZEALL, 10, 10); } @@ -1708,7 +1712,7 @@ void LLWindowMacOSX::hideCursor() void LLWindowMacOSX::showCursor() { - if(mCursorHidden) + if(mCursorHidden || !isCGCursorVisible()) { // LL_INFOS() << "showCursor: showing" << LL_ENDL; mCursorHidden = FALSE; diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index b84d74a244..e0cd0bfb46 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -43,10 +43,15 @@ #if LL_GTK extern "C" { # include "gtk/gtk.h" +#error "Direct use of GTK is deprecated" } #include #endif // LL_GTK +#ifdef LL_GLIB +#include +#endif + extern "C" { # include "fontconfig/fontconfig.h" } @@ -186,6 +191,248 @@ Display* LLWindowSDL::get_SDL_Display(void) } #endif // LL_X11 +#if LL_X11 + +// Clipboard handing via native X11, base on the implementation in Cool VL by Henri Beauchamp + +namespace +{ + std::array gSupportedAtoms; + + Atom XA_CLIPBOARD; + Atom XA_TARGETS; + Atom PVT_PASTE_BUFFER; + long const MAX_PASTE_BUFFER_SIZE = 16383; + + void filterSelectionRequest( XEvent aEvent ) + { + auto *display = LLWindowSDL::getSDLDisplay(); + auto &request = aEvent.xselectionrequest; + + XSelectionEvent reply { SelectionNotify, aEvent.xany.serial, aEvent.xany.send_event, display, + request.requestor, request.selection, request.target, + request.property,request.time }; + + if (request.target == XA_TARGETS) + { + XChangeProperty(display, request.requestor, request.property, + XA_ATOM, 32, PropModeReplace, + (unsigned char *) &gSupportedAtoms.front(), gSupportedAtoms.size()); + } + else if (std::find(gSupportedAtoms.begin(), gSupportedAtoms.end(), request.target) != + gSupportedAtoms.end()) + { + std::string utf8; + if (request.selection == XA_PRIMARY) + utf8 = wstring_to_utf8str(gWindowImplementation->getPrimaryText()); + else + utf8 = wstring_to_utf8str(gWindowImplementation->getSecondaryText()); + + XChangeProperty(display, request.requestor, request.property, + request.target, 8, PropModeReplace, + (unsigned char *) utf8.c_str(), utf8.length()); + } + else if (request.selection == XA_CLIPBOARD) + { + // Did not have what they wanted, so no property set + reply.property = None; + } + else + return; + + XSendEvent(request.display, request.requestor, False, NoEventMask, (XEvent *) &reply); + XSync(display, False); + } + + void filterSelectionClearRequest( XEvent aEvent ) + { + auto &request = aEvent.xselectionrequest; + if (request.selection == XA_PRIMARY) + gWindowImplementation->clearPrimaryText(); + else if (request.selection == XA_CLIPBOARD) + gWindowImplementation->clearSecondaryText(); + } + + int x11_clipboard_filter(const SDL_Event *evt) + { + Display *display = LLWindowSDL::getSDLDisplay(); + if (!display) + return 1; + + if (evt->type != SDL_SYSWMEVENT) + return 1; + + auto xevent = evt->syswm.msg->event.xevent; + + if (xevent.type == SelectionRequest) + filterSelectionRequest( xevent ); + else if (xevent.type == SelectionClear) + filterSelectionClearRequest( xevent ); + return 1; + } + + bool grab_property(Display* display, Window window, Atom selection, Atom target) + { + if( !display ) + return false; + + maybe_lock_display(); + + XDeleteProperty(display, window, PVT_PASTE_BUFFER); + XFlush(display); + + XConvertSelection(display, selection, target, PVT_PASTE_BUFFER, window, CurrentTime); + + // Unlock the connection so that the SDL event loop may function + maybe_unlock_display(); + + const auto start{ SDL_GetTicks() }; + const auto end{ start + 1000 }; + + XEvent xevent {}; + bool response = false; + + do + { + SDL_Event event {}; + + // Wait for an event + SDL_WaitEvent(&event); + + // If the event is a window manager event + if (event.type == SDL_SYSWMEVENT) + { + xevent = event.syswm.msg->event.xevent; + + if (xevent.type == SelectionNotify && xevent.xselection.requestor == window) + response = true; + } + } while (!response && SDL_GetTicks() < end ); + + return response && xevent.xselection.property != None; + } +} + +void LLWindowSDL::initialiseX11Clipboard() +{ + if (!mSDL_Display) + return; + + SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); + SDL_SetEventFilter(x11_clipboard_filter); + + maybe_lock_display(); + + XA_CLIPBOARD = XInternAtom(mSDL_Display, "CLIPBOARD", False); + + gSupportedAtoms[0] = XA_STRING; + + gSupportedAtoms[1] = XInternAtom(mSDL_Display, "COMPOUND_TEXT", False); + gSupportedAtoms[2] = XInternAtom(mSDL_Display, "UTF8_STRING", False); + + // TARGETS atom + XA_TARGETS = XInternAtom(mSDL_Display, "TARGETS", False); + + // SL_PASTE_BUFFER atom + PVT_PASTE_BUFFER = XInternAtom(mSDL_Display, "FS_PASTE_BUFFER", False); + + maybe_unlock_display(); +} + +bool LLWindowSDL::getSelectionText( Atom aSelection, Atom aType, LLWString &text ) +{ + if( !mSDL_Display ) + return false; + + if( !grab_property(mSDL_Display, mSDL_XWindowID, aSelection,aType ) ) + return false; + + maybe_lock_display(); + + Atom type; + int format{}; + unsigned long len{},remaining {}; + unsigned char* data = nullptr; + int res = XGetWindowProperty(mSDL_Display, mSDL_XWindowID, + PVT_PASTE_BUFFER, 0, MAX_PASTE_BUFFER_SIZE, False, + AnyPropertyType, &type, &format, &len, + &remaining, &data); + if (data && len) + { + text = LLWString( + utf8str_to_wstring(reinterpret_cast< char const *>( data ) ) + ); + XFree(data); + } + + maybe_unlock_display(); + return res == Success; +} + +bool LLWindowSDL::getSelectionText(Atom selection, LLWString& text) +{ + if (!mSDL_Display) + return false; + + maybe_lock_display(); + + Window owner = XGetSelectionOwner(mSDL_Display, selection); + if (owner == None) + { + if (selection == XA_PRIMARY) + { + owner = DefaultRootWindow(mSDL_Display); + selection = XA_CUT_BUFFER0; + } + else + { + maybe_unlock_display(); + return false; + } + } + + maybe_unlock_display(); + + for( Atom atom : gSupportedAtoms ) + { + if(getSelectionText(selection, atom, text ) ) + return true; + } + + return false; +} + +bool LLWindowSDL::setSelectionText(Atom selection, const LLWString& text) +{ + maybe_lock_display(); + + if (selection == XA_PRIMARY) + { + std::string utf8 = wstring_to_utf8str(text); + XStoreBytes(mSDL_Display, utf8.c_str(), utf8.length() + 1); + mPrimaryClipboard = text; + } + else + mSecondaryClipboard = text; + + XSetSelectionOwner(mSDL_Display, selection, mSDL_XWindowID, CurrentTime); + + auto owner = XGetSelectionOwner(mSDL_Display, selection); + + maybe_unlock_display(); + + return owner == mSDL_XWindowID; +} + +Display* LLWindowSDL::getSDLDisplay() +{ + if (gWindowImplementation) + return gWindowImplementation->mSDL_Display; + return nullptr; +} + +#endif + LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, const std::string& title, S32 x, S32 y, S32 width, @@ -253,6 +500,7 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, #if LL_X11 mFlashing = FALSE; + initialiseX11Clipboard(); #endif // LL_X11 mKeyScanCode = 0; @@ -1356,33 +1604,34 @@ BOOL LLWindowSDL::copyTextToPrimary(const LLWString &text) #else BOOL LLWindowSDL::isClipboardTextAvailable() -{ - return FALSE; // unsupported +{ + return mSDL_Display && XGetSelectionOwner(mSDL_Display, XA_CLIPBOARD) != None; } BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst) { - return FALSE; // unsupported + return getSelectionText(XA_CLIPBOARD, dst); } BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s) { - return FALSE; // unsupported + return setSelectionText(XA_CLIPBOARD, s); } BOOL LLWindowSDL::isPrimaryTextAvailable() { - return FALSE; // unsupported + LLWString text; + return getSelectionText(XA_PRIMARY, text) && !text.empty(); } BOOL LLWindowSDL::pasteTextFromPrimary(LLWString &dst) { - return FALSE; // unsupported + return getSelectionText(XA_PRIMARY, dst); } BOOL LLWindowSDL::copyTextToPrimary(const LLWString &s) { - return FALSE; // unsupported + return setSelectionText(XA_PRIMARY, s); } #endif // LL_GTK @@ -1747,7 +1996,18 @@ void LLWindowSDL::processMiscNativeEvents() setlocale(LC_ALL, saved_locale.c_str() ); } #endif // LL_GTK - +#if LL_GLIB + // Pump until we've nothing left to do or passed 1/15th of a + // second pumping for this frame. + static LLTimer pump_timer; + pump_timer.reset(); + pump_timer.setTimerExpirySec(1.0f / 15.0f); + do + { + g_main_context_iteration(g_main_context_default(), FALSE); + } while( g_main_context_pending(g_main_context_default()) && !pump_timer.hasExpired()); +#endif + // hack - doesn't belong here - but this is just for debugging if (getenv("LL_DEBUG_BLOAT")) { @@ -2109,6 +2369,7 @@ void LLWindowSDL::initCursors(BOOL useLegacyCursors) // Legacy cursor se mSDLCursors[UI_CURSOR_SIZENESW] = makeSDLCursorFromBMP("sizenesw.BMP",17,17); mSDLCursors[UI_CURSOR_SIZEWE] = makeSDLCursorFromBMP("sizewe.BMP",16,14); mSDLCursors[UI_CURSOR_SIZENS] = makeSDLCursorFromBMP("sizens.BMP",17,16); + mSDLCursors[UI_CURSOR_SIZEALL] = makeSDLCursorFromBMP("sizeall.BMP", 17, 17); mSDLCursors[UI_CURSOR_NO] = makeSDLCursorFromBMP("llno.BMP",8,8); mSDLCursors[UI_CURSOR_WORKING] = makeSDLCursorFromBMP("working.BMP",12,15); mSDLCursors[UI_CURSOR_TOOLGRAB] = makeSDLCursorFromBMP("lltoolgrab.BMP",2,13); @@ -2128,6 +2389,7 @@ void LLWindowSDL::initCursors(BOOL useLegacyCursors) // Legacy cursor se mSDLCursors[UI_CURSOR_TOOLCAMERA] = makeSDLCursorFromBMP("lltoolcamera.BMP",7,5); mSDLCursors[UI_CURSOR_TOOLPAN] = makeSDLCursorFromBMP("lltoolpan.BMP",7,5); mSDLCursors[UI_CURSOR_TOOLZOOMIN] = makeSDLCursorFromBMP("lltoolzoomin.BMP",7,5); + mSDLCursors[UI_CURSOR_TOOLZOOMOUT] = makeSDLCursorFromBMP("lltoolzoomout.BMP", 7, 5); mSDLCursors[UI_CURSOR_TOOLPICKOBJECT3] = makeSDLCursorFromBMP("toolpickobject3.BMP",0,0); mSDLCursors[UI_CURSOR_TOOLPLAY] = makeSDLCursorFromBMP("toolplay.BMP",0,0); mSDLCursors[UI_CURSOR_TOOLPAUSE] = makeSDLCursorFromBMP("toolpause.BMP",0,0); @@ -2396,39 +2658,6 @@ static void color_changed_callback(GtkWidget *widget, gtk_color_selection_get_current_color(colorsel, colorp); } - -/* - Make the raw keyboard data available - used to poke through to LLQtWebKit so - that Qt/Webkit has access to the virtual keycodes etc. that it needs -*/ -LLSD LLWindowSDL::getNativeKeyData() -{ - LLSD result = LLSD::emptyMap(); - - U32 modifiers = 0; // pretend-native modifiers... oh what a tangled web we weave! - - // we go through so many levels of device abstraction that I can't really guess - // what a plugin under GDK under Qt under SL under SDL under X11 considers - // a 'native' modifier mask. this has been sort of reverse-engineered... they *appear* - // to match GDK consts, but that may be co-incidence. - modifiers |= (mKeyModifiers & KMOD_LSHIFT) ? 0x0001 : 0; - modifiers |= (mKeyModifiers & KMOD_RSHIFT) ? 0x0001 : 0;// munge these into the same shift - modifiers |= (mKeyModifiers & KMOD_CAPS) ? 0x0002 : 0; - modifiers |= (mKeyModifiers & KMOD_LCTRL) ? 0x0004 : 0; - modifiers |= (mKeyModifiers & KMOD_RCTRL) ? 0x0004 : 0;// munge these into the same ctrl - modifiers |= (mKeyModifiers & KMOD_LALT) ? 0x0008 : 0;// untested - modifiers |= (mKeyModifiers & KMOD_RALT) ? 0x0008 : 0;// untested - // *todo: test ALTs - I don't have a case for testing these. Do you? - // *todo: NUM? - I don't care enough right now (and it's not a GDK modifier). - - result["scan_code"] = (S32)mKeyScanCode; - result["virtual_key"] = (S32)mKeyVirtualKey; - result["modifiers"] = (S32)modifiers; - result[ "sdl_sym" ] = (S32)mSDLSym; // Store the SDL Keysym too. - return result; -} - - BOOL LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b) { BOOL rtn = FALSE; @@ -2513,6 +2742,37 @@ BOOL LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b) } #endif // LL_GTK +/* + Make the raw keyboard data available - used to poke through to LLQtWebKit so + that Qt/Webkit has access to the virtual keycodes etc. that it needs +*/ +LLSD LLWindowSDL::getNativeKeyData() +{ + LLSD result = LLSD::emptyMap(); + + U32 modifiers = 0; // pretend-native modifiers... oh what a tangled web we weave! + + // we go through so many levels of device abstraction that I can't really guess + // what a plugin under GDK under Qt under SL under SDL under X11 considers + // a 'native' modifier mask. this has been sort of reverse-engineered... they *appear* + // to match GDK consts, but that may be co-incidence. + modifiers |= (mKeyModifiers & KMOD_LSHIFT) ? 0x0001 : 0; + modifiers |= (mKeyModifiers & KMOD_RSHIFT) ? 0x0001 : 0;// munge these into the same shift + modifiers |= (mKeyModifiers & KMOD_CAPS) ? 0x0002 : 0; + modifiers |= (mKeyModifiers & KMOD_LCTRL) ? 0x0004 : 0; + modifiers |= (mKeyModifiers & KMOD_RCTRL) ? 0x0004 : 0;// munge these into the same ctrl + modifiers |= (mKeyModifiers & KMOD_LALT) ? 0x0008 : 0;// untested + modifiers |= (mKeyModifiers & KMOD_RALT) ? 0x0008 : 0;// untested + // *todo: test ALTs - I don't have a case for testing these. Do you? + // *todo: NUM? - I don't care enough right now (and it's not a GDK modifier). + + result["scan_code"] = (S32)mKeyScanCode; + result["virtual_key"] = (S32)mKeyVirtualKey; + result["modifiers"] = (S32)modifiers; + result[ "sdl_sym" ] = (S32)mSDLSym; // Store the SDL Keysym too. + return result; +} + #if LL_LINUX // extracted from spawnWebBrowser for clarity and to eliminate // compiler confusion regarding close(int fd) vs. LLWindow::close() diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h index de0ee173ec..a0ca9839a5 100644 --- a/indra/llwindow/llwindowsdl.h +++ b/indra/llwindow/llwindowsdl.h @@ -220,6 +220,24 @@ private: U32 mSDLSym; // Store the SDL Keysym too. BOOL mUseLegacyCursors; // Legacy cursor setting from main program + +public: +#if LL_X11 + static Display* getSDLDisplay(); + LLWString const& getPrimaryText() const { return mPrimaryClipboard; } + LLWString const& getSecondaryText() const { return mSecondaryClipboard; } + void clearPrimaryText() { mPrimaryClipboard.clear(); } + void clearSecondaryText() { mSecondaryClipboard.clear(); } +private: + void initialiseX11Clipboard(); + + bool getSelectionText(Atom selection, LLWString& text); + bool getSelectionText( Atom selection, Atom type, LLWString &text ); + + bool setSelectionText(Atom selection, const LLWString& text); +#endif + LLWString mPrimaryClipboard; + LLWString mSecondaryClipboard; }; diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index d2d24bb730..005d5e25ac 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -1918,8 +1918,9 @@ void LLWindowWin32::initCursors(BOOL useLegacyCursors) // Legacy cursor mCursor[ UI_CURSOR_CROSS ] = LoadCursor(NULL, IDC_CROSS); mCursor[ UI_CURSOR_SIZENWSE ] = LoadCursor(NULL, IDC_SIZENWSE); mCursor[ UI_CURSOR_SIZENESW ] = LoadCursor(NULL, IDC_SIZENESW); - mCursor[ UI_CURSOR_SIZEWE ] = LoadCursor(NULL, IDC_SIZEWE); - mCursor[ UI_CURSOR_SIZENS ] = LoadCursor(NULL, IDC_SIZENS); + mCursor[ UI_CURSOR_SIZEWE ] = LoadCursor(NULL, IDC_SIZEWE); + mCursor[ UI_CURSOR_SIZENS ] = LoadCursor(NULL, IDC_SIZENS); + mCursor[ UI_CURSOR_SIZEALL ] = LoadCursor(NULL, IDC_SIZEALL); mCursor[ UI_CURSOR_NO ] = LoadCursor(NULL, IDC_NO); mCursor[ UI_CURSOR_WORKING ] = LoadCursor(NULL, IDC_APPSTARTING); @@ -1941,6 +1942,7 @@ void LLWindowWin32::initCursors(BOOL useLegacyCursors) // Legacy cursor mCursor[ UI_CURSOR_TOOLCAMERA ] = LoadCursor(module, TEXT("TOOLCAMERA")); mCursor[ UI_CURSOR_TOOLPAN ] = LoadCursor(module, TEXT("TOOLPAN")); mCursor[ UI_CURSOR_TOOLZOOMIN ] = LoadCursor(module, TEXT("TOOLZOOMIN")); + mCursor[ UI_CURSOR_TOOLZOOMOUT ] = LoadCursor(module, TEXT("TOOLZOOMOUT")); mCursor[ UI_CURSOR_TOOLPICKOBJECT3 ] = LoadCursor(module, TEXT("TOOLPICKOBJECT3")); mCursor[ UI_CURSOR_PIPETTE ] = LoadCursor(module, TEXT("TOOLPIPETTE")); /* Legacy cursor setting from main program diff --git a/indra/media_plugins/CMakeLists.txt b/indra/media_plugins/CMakeLists.txt index 32e440a829..cde480a46d 100644 --- a/indra/media_plugins/CMakeLists.txt +++ b/indra/media_plugins/CMakeLists.txt @@ -2,10 +2,9 @@ add_subdirectory(base) if (LINUX) -# add_subdirectory(gstreamer010) - add_subdirectory(cef) - add_subdirectory(libvlc) - #add_subdirectory(example) + add_subdirectory(gstreamer10) + add_subdirectory(cef) + #add_subdirectory(libvlc) endif (LINUX) if (DARWIN) diff --git a/indra/media_plugins/base/CMakeLists.txt b/indra/media_plugins/base/CMakeLists.txt index 7f2b82ffdd..197c41bc20 100644 --- a/indra/media_plugins/base/CMakeLists.txt +++ b/indra/media_plugins/base/CMakeLists.txt @@ -49,4 +49,3 @@ set(media_plugin_base_HEADER_FILES add_library(media_plugin_base ${media_plugin_base_SOURCE_FILES} ) - diff --git a/indra/media_plugins/cef/CMakeLists.txt b/indra/media_plugins/cef/CMakeLists.txt index a846c04626..3f85cfc6a4 100644 --- a/indra/media_plugins/cef/CMakeLists.txt +++ b/indra/media_plugins/cef/CMakeLists.txt @@ -58,7 +58,8 @@ set (media_plugin_cef_LINK_LIBRARIES if (LINUX) # message(FATAL_ERROR "CEF plugin has been enabled for a Linux compile.\n" # " Please create a volume_catcher implementation for this platform.") - list(APPEND media_plugin_cef_SOURCE_FILES dummy_volume_catcher.cpp) + + list(APPEND media_plugin_cef_SOURCE_FILES ${LINUX_VOLUME_CATCHER}) set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--build-id -Wl,-rpath,'$ORIGIN:$ORIGIN/../../lib'") elseif (DARWIN) list(APPEND media_plugin_cef_SOURCE_FILES mac_volume_catcher_null.cpp) diff --git a/indra/media_plugins/cef/linux_volume_catcher.cpp b/indra/media_plugins/cef/linux_volume_catcher.cpp index 91be3a89e9..fdea6fb15f 100755 --- a/indra/media_plugins/cef/linux_volume_catcher.cpp +++ b/indra/media_plugins/cef/linux_volume_catcher.cpp @@ -53,6 +53,7 @@ extern "C" { #include "apr_dso.h" } + //////////////////////////////////////////////////// #define DEBUGMSG(...) do {} while(0) @@ -81,7 +82,7 @@ bool grab_pa_syms(std::string pulse_dso_name) apr_status_t rv; apr_dso_handle_t *sSymPADSOHandle = NULL; -#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) do{rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll##PASYM, sSymPADSOHandle, #PASYM); if (rv != APR_SUCCESS) {INFOMSG("Failed to grab symbol: %s", #PASYM); if (REQUIRED) sym_error = true;} else DEBUGMSG("grabbed symbol: %s from %p", #PASYM, (void*)ll##PASYM);}while(0) +#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) do{rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll##PASYM, sSymPADSOHandle, #PASYM); if (rv != APR_SUCCESS) { if (REQUIRED) sym_error = true;} } while(0); //attempt to load the shared library apr_pool_create(&sSymPADSOMemoryPool, NULL); @@ -216,17 +217,16 @@ void VolumeCatcherImpl::init() mGotSyms = loadsyms("libpulse-mainloop-glib.so.0"); if (!mGotSyms) return; - // better make double-sure glib itself is initialized properly. - if (!g_thread_supported ()) g_thread_init (NULL); - g_type_init(); - mMainloop = llpa_glib_mainloop_new(g_main_context_default()); + if (mMainloop) { pa_mainloop_api *api = llpa_glib_mainloop_get_api(mMainloop); + if (api) { pa_proplist *proplist = llpa_proplist_new(); + if (proplist) { llpa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "multimedia-player"); @@ -236,6 +236,7 @@ void VolumeCatcherImpl::init() // plain old pa_context_new() is broken! mPAContext = llpa_context_new_with_proplist(api, NULL, proplist); + llpa_proplist_free(proplist); } } @@ -350,6 +351,51 @@ void VolumeCatcherImpl::update_index_volume(U32 index, F32 volume) } } +pid_t getParentPid( pid_t aPid ) +{ + std::stringstream strm; + strm << "/proc/" << aPid << "/status"; + std::ifstream in{ strm.str() }; + + if( !in.is_open() ) + return 0; + + pid_t res {0}; + while( !in.eof() && res == 0 ) + { + std::string line; + line.resize( 1024, 0 ); + in.getline( &line[0], line.length() ); + + auto i = line.find( "PPid:" ); + + if( i == std::string::npos ) + continue; + + char const *pIn = line.c_str() + 5; // Skip over pid; + while( *pIn != 0 && isspace( *pIn ) ) + ++pIn; + + if( *pIn ) + res = atoll( pIn ); + } + return res; +} + + +bool isPluginPid( pid_t aPid ) +{ + auto myPid = getpid(); + + do + { + if( aPid == myPid ) + return true; + aPid = getParentPid( aPid ); + } while( aPid > 1 ); + + return false; +} void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *sii, int eol, void *userdata) { @@ -360,11 +406,10 @@ void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info { pa_proplist *proplist = sii->proplist; pid_t sinkpid = atoll(llpa_proplist_gets(proplist, PA_PROP_APPLICATION_PROCESS_ID)); - - if (sinkpid == getpid()) // does the discovered sinkinput belong to this process? + + if (isPluginPid( sinkpid )) // does the discovered sinkinput belong to this process? { - bool is_new = (impl->mSinkInputIndices.find(sii->index) == - impl->mSinkInputIndices.end()); + bool is_new = (impl->mSinkInputIndices.find(sii->index) == impl->mSinkInputIndices.end()); impl->mSinkInputIndices.insert(sii->index); impl->mSinkInputNumChannels[sii->index] = sii->channel_map.channels; @@ -387,32 +432,31 @@ void callback_subscription_alert(pa_context *context, pa_subscription_event_type VolumeCatcherImpl *impl = dynamic_cast((VolumeCatcherImpl*)userdata); llassert(impl); - switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { + switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) + { case PA_SUBSCRIPTION_EVENT_SINK_INPUT: - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == - PA_SUBSCRIPTION_EVENT_REMOVE) - { - // forget this sinkinput, if we were caring about it - impl->mSinkInputIndices.erase(index); - impl->mSinkInputNumChannels.erase(index); - } - else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == - PA_SUBSCRIPTION_EVENT_NEW) - { - // ask for more info about this new sinkinput - pa_operation *op; - if ((op = llpa_context_get_sink_input_info(impl->mPAContext, index, callback_discovered_sinkinput, impl))) + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - llpa_operation_unref(op); + // forget this sinkinput, if we were caring about it + impl->mSinkInputIndices.erase(index); + impl->mSinkInputNumChannels.erase(index); } - } - else - { - // property change on this sinkinput - we don't care. - } - break; + else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) + { + // ask for more info about this new sinkinput + pa_operation *op; + if ((op = llpa_context_get_sink_input_info(impl->mPAContext, index, callback_discovered_sinkinput, impl))) + { + llpa_operation_unref(op); + } + } + else + { + // property change on this sinkinput - we don't care. + } + break; - default:; + default:; } } diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp index 7dbceba2d8..ca185049ce 100644 --- a/indra/media_plugins/cef/media_plugin_cef.cpp +++ b/indra/media_plugins/cef/media_plugin_cef.cpp @@ -87,6 +87,9 @@ private: bool mCookiesEnabled; bool mPluginsEnabled; bool mJavascriptEnabled; + bool mProxyEnabled; + std::string mProxyHost; + int mProxyPort; bool mDisableGPU; bool mDisableNetworkService; bool mUseMockKeyChain; @@ -124,6 +127,9 @@ MediaPluginBase(host_send_func, host_user_data) mCookiesEnabled = true; mPluginsEnabled = false; mJavascriptEnabled = true; + mProxyEnabled = false; + mProxyHost = ""; + mProxyPort = 0; mDisableGPU = false; mDisableNetworkService = true; mUseMockKeyChain = true; @@ -397,21 +403,86 @@ void MediaPluginCEF::onCursorChangedCallback(dullahan::ECursorType type) switch (type) { - case dullahan::CT_POINTER: - name = "arrow"; - break; + case dullahan::CT_POINTER: + name = "UI_CURSOR_ARROW"; + break; + case dullahan::CT_CROSS: + name = "UI_CURSOR_CROSS"; + break; + case dullahan::CT_HAND: + name = "UI_CURSOR_HAND"; + break; case dullahan::CT_IBEAM: - name = "ibeam"; - break; - case dullahan::CT_NORTHSOUTHRESIZE: - name = "splitv"; - break; - case dullahan::CT_EASTWESTRESIZE: - name = "splith"; - break; - case dullahan::CT_HAND: - name = "hand"; + name = "UI_CURSOR_IBEAM"; break; + case dullahan::CT_WAIT: + name = "UI_CURSOR_WAIT"; + break; + //case dullahan::CT_HELP: + case dullahan::CT_ROWRESIZE: + case dullahan::CT_NORTHRESIZE: + case dullahan::CT_SOUTHRESIZE: + case dullahan::CT_NORTHSOUTHRESIZE: + name = "UI_CURSOR_SIZENS"; + break; + case dullahan::CT_COLUMNRESIZE: + case dullahan::CT_EASTRESIZE: + case dullahan::CT_WESTRESIZE: + case dullahan::CT_EASTWESTRESIZE: + name = "UI_CURSOR_SIZEWE"; + break; + case dullahan::CT_NORTHEASTRESIZE: + case dullahan::CT_SOUTHWESTRESIZE: + case dullahan::CT_NORTHEASTSOUTHWESTRESIZE: + name = "UI_CURSOR_SIZENESW"; + break; + case dullahan::CT_SOUTHEASTRESIZE: + case dullahan::CT_NORTHWESTRESIZE: + case dullahan::CT_NORTHWESTSOUTHEASTRESIZE: + name = "UI_CURSOR_SIZENWSE"; + break; + case dullahan::CT_MOVE: + name = "UI_CURSOR_SIZEALL"; + break; + //case dullahan::CT_MIDDLEPANNING: + //case dullahan::CT_EASTPANNING: + //case dullahan::CT_NORTHPANNING: + //case dullahan::CT_NORTHEASTPANNING: + //case dullahan::CT_NORTHWESTPANNING: + //case dullahan::CT_SOUTHPANNING: + //case dullahan::CT_SOUTHEASTPANNING: + //case dullahan::CT_SOUTHWESTPANNING: + //case dullahan::CT_WESTPANNING: + //case dullahan::CT_VERTICALTEXT: + //case dullahan::CT_CELL: + //case dullahan::CT_CONTEXTMENU: + case dullahan::CT_ALIAS: + name = "UI_CURSOR_TOOLMEDIAOPEN"; + break; + case dullahan::CT_PROGRESS: + name = "UI_CURSOR_WORKING"; + break; + case dullahan::CT_COPY: + name = "UI_CURSOR_ARROWCOPY"; + break; + case dullahan::CT_NONE: + name = "UI_CURSOR_NO"; + break; + case dullahan::CT_NODROP: + case dullahan::CT_NOTALLOWED: + name = "UI_CURSOR_NOLOCKED"; + break; + case dullahan::CT_ZOOMIN: + name = "UI_CURSOR_TOOLZOOMIN"; + break; + case dullahan::CT_ZOOMOUT: + name = "UI_CURSOR_TOOLZOOMOUT"; + break; + case dullahan::CT_GRAB: + name = "UI_CURSOR_TOOLGRAB"; + break; + //case dullahan::CT_GRABING: + //case dullahan::CT_CUSTOM: default: LL_WARNS() << "Unknown cursor ID: " << (int)type << LL_ENDL; @@ -518,50 +589,60 @@ void MediaPluginCEF::receiveMessage(const char* message_string) } else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) { - if (message_name == "init") - { - // event callbacks from Dullahan - mCEFLib->setOnPageChangedCallback(std::bind(&MediaPluginCEF::onPageChangedCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - mCEFLib->setOnCustomSchemeURLCallback(std::bind(&MediaPluginCEF::onCustomSchemeURLCallback, this, std::placeholders::_1)); - mCEFLib->setOnConsoleMessageCallback(std::bind(&MediaPluginCEF::onConsoleMessageCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - mCEFLib->setOnStatusMessageCallback(std::bind(&MediaPluginCEF::onStatusMessageCallback, this, std::placeholders::_1)); - mCEFLib->setOnTitleChangeCallback(std::bind(&MediaPluginCEF::onTitleChangeCallback, this, std::placeholders::_1)); - mCEFLib->setOnTooltipCallback(std::bind(&MediaPluginCEF::onTooltipCallback, this, std::placeholders::_1)); - mCEFLib->setOnLoadStartCallback(std::bind(&MediaPluginCEF::onLoadStartCallback, this)); - mCEFLib->setOnLoadEndCallback(std::bind(&MediaPluginCEF::onLoadEndCallback, this, std::placeholders::_1, std::placeholders::_2)); - mCEFLib->setOnLoadErrorCallback(std::bind(&MediaPluginCEF::onLoadError, this, std::placeholders::_1, std::placeholders::_2)); - mCEFLib->setOnAddressChangeCallback(std::bind(&MediaPluginCEF::onAddressChangeCallback, this, std::placeholders::_1)); - mCEFLib->setOnOpenPopupCallback(std::bind(&MediaPluginCEF::onOpenPopupCallback, this, std::placeholders::_1, std::placeholders::_2)); - mCEFLib->setOnHTTPAuthCallback(std::bind(&MediaPluginCEF::onHTTPAuthCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); - mCEFLib->setOnFileDialogCallback(std::bind(&MediaPluginCEF::onFileDialog, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - mCEFLib->setOnCursorChangedCallback(std::bind(&MediaPluginCEF::onCursorChangedCallback, this, std::placeholders::_1)); - mCEFLib->setOnRequestExitCallback(std::bind(&MediaPluginCEF::onRequestExitCallback, this)); - mCEFLib->setOnJSDialogCallback(std::bind(&MediaPluginCEF::onJSDialogCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - mCEFLib->setOnJSBeforeUnloadCallback(std::bind(&MediaPluginCEF::onJSBeforeUnloadCallback, this)); - - dullahan::dullahan_settings settings; + if (message_name == "init") + { + // event callbacks from Dullahan + mCEFLib->setOnPageChangedCallback(std::bind(&MediaPluginCEF::onPageChangedCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); + mCEFLib->setOnCustomSchemeURLCallback(std::bind(&MediaPluginCEF::onCustomSchemeURLCallback, this, std::placeholders::_1)); + mCEFLib->setOnConsoleMessageCallback(std::bind(&MediaPluginCEF::onConsoleMessageCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + mCEFLib->setOnStatusMessageCallback(std::bind(&MediaPluginCEF::onStatusMessageCallback, this, std::placeholders::_1)); + mCEFLib->setOnTitleChangeCallback(std::bind(&MediaPluginCEF::onTitleChangeCallback, this, std::placeholders::_1)); + mCEFLib->setOnTooltipCallback(std::bind(&MediaPluginCEF::onTooltipCallback, this, std::placeholders::_1)); + mCEFLib->setOnLoadStartCallback(std::bind(&MediaPluginCEF::onLoadStartCallback, this)); + mCEFLib->setOnLoadEndCallback(std::bind(&MediaPluginCEF::onLoadEndCallback, this, std::placeholders::_1, std::placeholders::_2)); + mCEFLib->setOnLoadErrorCallback(std::bind(&MediaPluginCEF::onLoadError, this, std::placeholders::_1, std::placeholders::_2)); + mCEFLib->setOnAddressChangeCallback(std::bind(&MediaPluginCEF::onAddressChangeCallback, this, std::placeholders::_1)); + mCEFLib->setOnOpenPopupCallback(std::bind(&MediaPluginCEF::onOpenPopupCallback, this, std::placeholders::_1, std::placeholders::_2)); + mCEFLib->setOnHTTPAuthCallback(std::bind(&MediaPluginCEF::onHTTPAuthCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + mCEFLib->setOnFileDialogCallback(std::bind(&MediaPluginCEF::onFileDialog, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); + mCEFLib->setOnCursorChangedCallback(std::bind(&MediaPluginCEF::onCursorChangedCallback, this, std::placeholders::_1)); + mCEFLib->setOnRequestExitCallback(std::bind(&MediaPluginCEF::onRequestExitCallback, this)); + mCEFLib->setOnJSDialogCallback(std::bind(&MediaPluginCEF::onJSDialogCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + mCEFLib->setOnJSBeforeUnloadCallback(std::bind(&MediaPluginCEF::onJSBeforeUnloadCallback, this)); + + dullahan::dullahan_settings settings; #if LL_WINDOWS - // As of CEF version 83+, for Windows versions, we need to tell CEF - // where the host helper process is since this DLL is not in the same - // dir as the executable that loaded it (SLPlugin.exe). The code in - // Dullahan that tried to figure out the location automatically uses - // the location of the exe which isn't helpful so we tell it explicitly. - char cur_dir_str[MAX_PATH]; - GetCurrentDirectoryA(MAX_PATH, cur_dir_str); - settings.host_process_path = std::string(cur_dir_str); + // As of CEF version 83+, for Windows versions, we need to tell CEF + // where the host helper process is since this DLL is not in the same + // dir as the executable that loaded it (SLPlugin.exe). The code in + // Dullahan that tried to figure out the location automatically uses + // the location of the exe which isn't helpful so we tell it explicitly. + char cur_dir_str[MAX_PATH]; + GetCurrentDirectoryA(MAX_PATH, cur_dir_str); + settings.host_process_path = std::string(cur_dir_str); #endif - settings.accept_language_list = mHostLanguage; + settings.accept_language_list = mHostLanguage; - // SL-15560: Product team overruled my change to set the default - // embedded background color to match the floater background - // and set it to white - settings.background_color = 0xffffffff; // white + // SL-15560: Product team overruled my change to set the default + // embedded background color to match the floater background + // and set it to white + settings.background_color = 0xffffffff; // white - settings.cache_enabled = true; - settings.root_cache_path = mRootCachePath; - settings.cache_path = mCachePath; - settings.context_cache_path = mContextCachePath; - settings.cookies_enabled = mCookiesEnabled; + settings.cache_enabled = true; + settings.root_cache_path = mRootCachePath; + settings.cache_path = mCachePath; + settings.context_cache_path = mContextCachePath; + settings.cookies_enabled = mCookiesEnabled; + +#ifndef LL_LINUX + // configure proxy argument if enabled and valid + if (mProxyEnabled && mProxyHost.length()) + { + std::ostringstream proxy_url; + proxy_url << mProxyHost << ":" << mProxyPort; + settings.proxy_host_port = proxy_url.str(); + } +#endif settings.disable_gpu = mDisableGPU; #if LL_DARWIN settings.disable_network_service = mDisableNetworkService; @@ -909,6 +990,12 @@ void MediaPluginCEF::receiveMessage(const char* message_string) { mDisableGPU = message_in.getValueBoolean("disable"); } + else if (message_name == "proxy_setup") + { + mProxyEnabled = message_in.getValueBoolean("enable"); + mProxyHost = message_in.getValue("host"); + mProxyPort = message_in.getValueS32("port"); + } else if (message_name == "web_security_disabled") { mDisableWebSecurity = message_in.getValueBoolean("disabled"); diff --git a/indra/media_plugins/gstreamer10/CMakeLists.txt b/indra/media_plugins/gstreamer10/CMakeLists.txt new file mode 100644 index 0000000000..730a2c27fb --- /dev/null +++ b/indra/media_plugins/gstreamer10/CMakeLists.txt @@ -0,0 +1,78 @@ +# -*- cmake -*- + +project(media_plugin_gstreamer10) + +include(00-Common) +include(LLCommon) +include(LLImage) +include(LLPlugin) +include(LLMath) +include(LLRender) +include(LLWindow) +include(Linking) +include(PluginAPI) +include(MediaPluginBase) +include(OpenGL) +include(GLIB) + +include(GStreamer10Plugin) + +include_directories( + ${LLPLUGIN_INCLUDE_DIRS} + ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} + ${LLIMAGE_INCLUDE_DIRS} + ${LLRENDER_INCLUDE_DIRS} + ${LLWINDOW_INCLUDE_DIRS} + ${GSTREAMER10_INCLUDE_DIRS} + ${GSTREAMER10_PLUGINS_BASE_INCLUDE_DIRS} +) +include_directories(SYSTEM + ${LLCOMMON_SYSTEM_INCLUDE_DIRS} + ) + +### media_plugin_gstreamer10 + +if(NOT WORD_SIZE EQUAL 32) + if(NOT WINDOWS) # not windows therefore gcc LINUX and DARWIN + add_definitions(-fPIC) + endif() +endif(NOT WORD_SIZE EQUAL 32) + +set(media_plugin_gstreamer10_SOURCE_FILES + media_plugin_gstreamer10.cpp + llmediaimplgstreamer_syms.cpp + ) + +set(media_plugin_gstreamer10_HEADER_FILES + llmediaimplgstreamer_syms.h + llmediaimplgstreamertriviallogging.h + ) + +add_library(media_plugin_gstreamer10 + SHARED + ${media_plugin_gstreamer10_SOURCE_FILES} +) + +target_link_libraries(media_plugin_gstreamer10 + ${LLPLUGIN_LIBRARIES} + ${MEDIA_PLUGIN_BASE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${PLUGIN_API_WINDOWS_LIBRARIES} + ${GSTREAMER10_LIBRARIES} +) + +#add_dependencies(media_plugin_gstreamer10 +# ${LLPLUGIN_LIBRARIES} +# ${MEDIA_PLUGIN_BASE_LIBRARIES} +# ${LLCOMMON_LIBRARIES} +#) + +if (WINDOWS) + set_target_properties( + media_plugin_gstreamer10 + PROPERTIES + LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /NODEFAULTLIB:LIBCMT" + ) +endif (WINDOWS) diff --git a/indra/newview/llappviewerlinux_api_dbus.h b/indra/media_plugins/gstreamer10/llmediaimplgstreamer.h similarity index 60% rename from indra/newview/llappviewerlinux_api_dbus.h rename to indra/media_plugins/gstreamer10/llmediaimplgstreamer.h index 3eee25b53d..6bc272c009 100644 --- a/indra/newview/llappviewerlinux_api_dbus.h +++ b/indra/media_plugins/gstreamer10/llmediaimplgstreamer.h @@ -1,8 +1,10 @@ /** - * @file llappviewerlinux_api_dbus.h - * @brief DBus-glib symbol handling + * @file llmediaimplgstreamer.h + * @author Tofu Linden + * @brief implementation that supports media playback via GStreamer. * - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * @cond + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * @@ -22,23 +24,30 @@ * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ + * @endcond */ -#include "linden_common.h" +// header guard +#ifndef llmediaimplgstreamer_h +#define llmediaimplgstreamer_h -#if LL_DBUS_ENABLED +#if LL_GSTREAMER010_ENABLED extern "C" { -#include +#include +#include + +#include "apr_pools.h" +#include "apr_dso.h" } -#define DBUSGLIB_DYLIB_DEFAULT_NAME "libdbus-glib-1.so.2" -bool grab_dbus_syms(std::string dbus_dso_name); -void ungrab_dbus_syms(); +extern "C" { +gboolean llmediaimplgstreamer_bus_callback (GstBus *bus, + GstMessage *message, + gpointer data); +} -#define LL_DBUS_SYM(REQUIRED, DBUSSYM, RTN, ...) extern RTN (*ll##DBUSSYM)(__VA_ARGS__) -#include "llappviewerlinux_api_dbus_syms_raw.inc" -#undef LL_DBUS_SYM +#endif // LL_GSTREAMER010_ENABLED -#endif // LL_DBUS_ENABLED +#endif // llmediaimplgstreamer_h diff --git a/indra/media_plugins/gstreamer10/llmediaimplgstreamer_syms.cpp b/indra/media_plugins/gstreamer10/llmediaimplgstreamer_syms.cpp new file mode 100644 index 0000000000..e5e5c1c9a3 --- /dev/null +++ b/indra/media_plugins/gstreamer10/llmediaimplgstreamer_syms.cpp @@ -0,0 +1,197 @@ +/** + * @file llmediaimplgstreamer_syms.cpp + * @brief dynamic GStreamer symbol-grabbing code + * + * @cond + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + * @endcond + */ + +#include +#include +#include + +#ifdef LL_WINDOWS +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0502 +#include +#endif + +#include "linden_common.h" + +extern "C" { +#include +#include +} + +#include "apr_pools.h" +#include "apr_dso.h" + +#ifdef LL_WINDOWS + +#ifndef _M_AMD64 +#define GSTREAMER_REG_KEY "Software\\GStreamer1.0\\x86" +#define GSTREAMER_DIR_SUFFIX "1.0\\x86\\bin\\" +#else +#define GSTREAMER_REG_KEY "Software\\GStreamer1.0\\x86_64" +#define GSTREAMER_DIR_SUFFIX "1.0\\x86_64\\bin\\" +#endif + +bool openRegKey( HKEY &aKey ) +{ + // Try native (32 bit view/64 bit view) of registry first. + if( ERROR_SUCCESS == ::RegOpenKeyExA( HKEY_LOCAL_MACHINE, GSTREAMER_REG_KEY, 0, KEY_QUERY_VALUE, &aKey ) ) + return true; + + // If native view fails, use 32 bit view or registry. + if( ERROR_SUCCESS == ::RegOpenKeyExA( HKEY_LOCAL_MACHINE, GSTREAMER_REG_KEY, 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &aKey ) ) + return true; + + return false; +} + +std::string getGStreamerDir() +{ + std::string ret; + HKEY hKey; + + if( openRegKey( hKey ) ) + { + DWORD dwLen(0); + ::RegQueryValueExA( hKey, "InstallDir", nullptr, nullptr, nullptr, &dwLen ); + + if( dwLen > 0 ) + { + std::vector< char > vctBuffer; + vctBuffer.resize( dwLen ); + ::RegQueryValueExA( hKey, "InstallDir", nullptr, nullptr, reinterpret_cast< LPBYTE>(&vctBuffer[ 0 ]), &dwLen ); + ret = &vctBuffer[0]; + + if( ret[ dwLen-1 ] != '\\' ) + ret += "\\"; + ret += GSTREAMER_DIR_SUFFIX; + + SetDllDirectoryA( ret.c_str() ); + } + ::RegCloseKey( hKey ); + } + return ret; +} +#else +std::string getGStreamerDir() { return ""; } +#endif + +#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) RTN (*ll##GSTSYM)(__VA_ARGS__) = NULL; +#include "llmediaimplgstreamer_syms_raw.inc" +#undef LL_GST_SYM + +struct Symloader +{ + bool mRequired; + char const *mName; + apr_dso_handle_sym_t *mPPFunc; +}; + +#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) { REQ, #GSTSYM , (apr_dso_handle_sym_t*)&ll##GSTSYM}, +Symloader sSyms[] = { +#include "llmediaimplgstreamer_syms_raw.inc" +{ false, 0, 0 } }; +#undef LL_GST_SYM + +// a couple of stubs for disgusting reasons +GstDebugCategory* +ll_gst_debug_category_new(gchar *name, guint color, gchar *description) +{ + static GstDebugCategory dummy; + return &dummy; +} +void ll_gst_debug_register_funcptr(GstDebugFuncPtr func, gchar* ptrname) +{ +} + +static bool sSymsGrabbed = false; +static apr_pool_t *sSymGSTDSOMemoryPool = NULL; + +std::vector< apr_dso_handle_t* > sLoadedLibraries; + +bool grab_gst_syms( std::vector< std::string > const &aDSONames ) +{ + if (sSymsGrabbed) + return true; + + //attempt to load the shared libraries + apr_pool_create(&sSymGSTDSOMemoryPool, NULL); + + for( std::vector< std::string >::const_iterator itr = aDSONames.begin(); itr != aDSONames.end(); ++itr ) + { + apr_dso_handle_t *pDSO(NULL); + std::string strDSO = getGStreamerDir() + *itr; + if( APR_SUCCESS == apr_dso_load( &pDSO, strDSO.c_str(), sSymGSTDSOMemoryPool )) + sLoadedLibraries.push_back( pDSO ); + + for( int i = 0; sSyms[ i ].mName; ++i ) + { + if( !*sSyms[ i ].mPPFunc ) + { + apr_dso_sym( sSyms[ i ].mPPFunc, pDSO, sSyms[ i ].mName ); + } + } + } + + std::stringstream strm; + bool sym_error = false; + for( int i = 0; sSyms[ i ].mName; ++i ) + { + if( sSyms[ i ].mRequired && ! *sSyms[ i ].mPPFunc ) + { + sym_error = true; + strm << sSyms[ i ].mName << std::endl; + } + } + + sSymsGrabbed = !sym_error; + return sSymsGrabbed; +} + + +void ungrab_gst_syms() +{ + // should be safe to call regardless of whether we've + // actually grabbed syms. + + for( std::vector< apr_dso_handle_t* >::iterator itr = sLoadedLibraries.begin(); itr != sLoadedLibraries.end(); ++itr ) + apr_dso_unload( *itr ); + + sLoadedLibraries.clear(); + + if ( sSymGSTDSOMemoryPool ) + { + apr_pool_destroy(sSymGSTDSOMemoryPool); + sSymGSTDSOMemoryPool = NULL; + } + + for( int i = 0; sSyms[ i ].mName; ++i ) + *sSyms[ i ].mPPFunc = NULL; + + sSymsGrabbed = false; +} + diff --git a/indra/media_plugins/gstreamer10/llmediaimplgstreamer_syms.h b/indra/media_plugins/gstreamer10/llmediaimplgstreamer_syms.h new file mode 100644 index 0000000000..0874644ee6 --- /dev/null +++ b/indra/media_plugins/gstreamer10/llmediaimplgstreamer_syms.h @@ -0,0 +1,68 @@ +/** + * @file llmediaimplgstreamer_syms.h + * @brief dynamic GStreamer symbol-grabbing code + * + * @cond + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + * @endcond + */ + +#include "linden_common.h" +#include +extern "C" { +#include +} + +bool grab_gst_syms( std::vector< std::string > const&); +void ungrab_gst_syms(); + +#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) extern RTN (*ll##GSTSYM)(__VA_ARGS__); +#include "llmediaimplgstreamer_syms_raw.inc" +#undef LL_GST_SYM + +// regrettable hacks to give us better runtime compatibility with older systems +#define llg_return_if_fail(COND) do{if (!(COND)) return;}while(0) +#define llg_return_val_if_fail(COND,V) do{if (!(COND)) return V;}while(0) + +// regrettable hacks because GStreamer was not designed for runtime loading +#undef GST_TYPE_MESSAGE +#define GST_TYPE_MESSAGE (llgst_message_get_type()) +#undef GST_TYPE_OBJECT +#define GST_TYPE_OBJECT (llgst_object_get_type()) +#undef GST_TYPE_PIPELINE +#define GST_TYPE_PIPELINE (llgst_pipeline_get_type()) +#undef GST_TYPE_ELEMENT +#define GST_TYPE_ELEMENT (llgst_element_get_type()) +#undef GST_TYPE_VIDEO_SINK +#define GST_TYPE_VIDEO_SINK (llgst_video_sink_get_type()) +// more regrettable hacks to stub-out these .h-exposed GStreamer internals +void ll_gst_debug_register_funcptr(GstDebugFuncPtr func, gchar* ptrname); +#undef _gst_debug_register_funcptr +#define _gst_debug_register_funcptr ll_gst_debug_register_funcptr +GstDebugCategory* ll_gst_debug_category_new(gchar *name, guint color, gchar *description); +#undef _gst_debug_category_new +#define _gst_debug_category_new ll_gst_debug_category_new +#undef __gst_debug_enabled +#define __gst_debug_enabled (0) + +// more hacks +#define LLGST_MESSAGE_TYPE_NAME(M) (llgst_message_type_get_name(GST_MESSAGE_TYPE(M))) diff --git a/indra/media_plugins/gstreamer10/llmediaimplgstreamer_syms_raw.inc b/indra/media_plugins/gstreamer10/llmediaimplgstreamer_syms_raw.inc new file mode 100644 index 0000000000..da1aa8ec1d --- /dev/null +++ b/indra/media_plugins/gstreamer10/llmediaimplgstreamer_syms_raw.inc @@ -0,0 +1,68 @@ +LL_GST_SYM(true, gst_buffer_new, GstBuffer*, void) +LL_GST_SYM(true, gst_structure_set_value, void, GstStructure *, const gchar *, const GValue*) +LL_GST_SYM(true, gst_init_check, gboolean, int *argc, char **argv[], GError ** err) +LL_GST_SYM(true, gst_message_get_type, GType, void) +LL_GST_SYM(true, gst_message_type_get_name, const gchar*, GstMessageType type) +LL_GST_SYM(true, gst_message_parse_error, void, GstMessage *message, GError **gerror, gchar **debug) +LL_GST_SYM(true, gst_message_parse_warning, void, GstMessage *message, GError **gerror, gchar **debug) +LL_GST_SYM(true, gst_message_parse_state_changed, void, GstMessage *message, GstState *oldstate, GstState *newstate, GstState *pending) +LL_GST_SYM(true, gst_element_set_state, GstStateChangeReturn, GstElement *element, GstState state) +LL_GST_SYM(true, gst_object_unref, void, gpointer object) +LL_GST_SYM(true, gst_object_get_type, GType, void) +LL_GST_SYM(true, gst_pipeline_get_type, GType, void) +LL_GST_SYM(true, gst_pipeline_get_bus, GstBus*, GstPipeline *pipeline) +LL_GST_SYM(true, gst_bus_add_watch, guint, GstBus * bus, GstBusFunc func, gpointer user_data) +LL_GST_SYM(true, gst_element_factory_make, GstElement*, const gchar *factoryname, const gchar *name) +LL_GST_SYM(true, gst_element_get_type, GType, void) +LL_GST_SYM(true, gst_static_pad_template_get, GstPadTemplate*, GstStaticPadTemplate *pad_template) +LL_GST_SYM(true, gst_element_class_add_pad_template, void, GstElementClass *klass, GstPadTemplate *temp) +LL_GST_SYM(true, gst_caps_from_string, GstCaps *, const gchar *string) +LL_GST_SYM(true, gst_caps_get_structure, GstStructure *, const GstCaps *caps, guint index) +LL_GST_SYM(true, gst_element_register, gboolean, GstPlugin *plugin, const gchar *name, guint rank, GType type) +LL_GST_SYM(true, gst_structure_get_int, gboolean, const GstStructure *structure, const gchar *fieldname, gint *value) +LL_GST_SYM(true, gst_structure_get_value, G_CONST_RETURN GValue *, const GstStructure *structure, const gchar *fieldname) +LL_GST_SYM(true, gst_value_get_fraction_numerator, gint, const GValue *value) +LL_GST_SYM(true, gst_value_get_fraction_denominator, gint, const GValue *value) +LL_GST_SYM(true, gst_structure_get_name, G_CONST_RETURN gchar *, const GstStructure *structure) +LL_GST_SYM(true, gst_element_seek, bool, GstElement *, gdouble, GstFormat, GstSeekFlags, GstSeekType, gint64, GstSeekType, gint64) + +LL_GST_SYM(false, gst_registry_fork_set_enabled, void, gboolean enabled) +LL_GST_SYM(false, gst_segtrap_set_enabled, void, gboolean enabled) +LL_GST_SYM(false, gst_message_parse_buffering, void, GstMessage *message, gint *percent) +LL_GST_SYM(false, gst_message_parse_info, void, GstMessage *message, GError **gerror, gchar **debug) +LL_GST_SYM(false, gst_element_query_position, gboolean, GstElement *element, GstFormat *format, gint64 *cur) +LL_GST_SYM(false, gst_version, void, guint *major, guint *minor, guint *micro, guint *nano) + +LL_GST_SYM( true, gst_message_parse_tag, void, GstMessage *, GstTagList **) +LL_GST_SYM( true, gst_tag_list_foreach, void, const GstTagList *, GstTagForeachFunc, gpointer) +LL_GST_SYM( true, gst_tag_list_get_tag_size, guint, const GstTagList *, const gchar *) +LL_GST_SYM( true, gst_tag_list_get_value_index, const GValue *, const GstTagList *, const gchar *, guint) + +LL_GST_SYM( true, gst_caps_new_simple, GstCaps*, const char *, const char*, ... ) + +LL_GST_SYM( true, gst_sample_get_caps, GstCaps*, GstSample* ) +LL_GST_SYM( true, gst_sample_get_buffer, GstBuffer*, GstSample* ) +LL_GST_SYM( true, gst_buffer_map, gboolean, GstBuffer*, GstMapInfo*, GstMapFlags ) +LL_GST_SYM( true, gst_buffer_unmap, void, GstBuffer*, GstMapInfo* ) + +LL_GST_SYM( true, gst_app_sink_set_caps, void, GstAppSink*, GstCaps const* ) +LL_GST_SYM( true, gst_app_sink_pull_sample, GstSample*, GstAppSink* ) + +LL_GST_SYM( true, g_free, void, gpointer ) +LL_GST_SYM( true, g_error_free, void, GError* ) + +LL_GST_SYM( true, g_main_context_pending, gboolean, GMainContext* ) +LL_GST_SYM( true, g_main_loop_get_context, GMainContext*, GMainLoop* ) +LL_GST_SYM( true, g_main_context_iteration, gboolean, GMainContext*, gboolean ) +LL_GST_SYM( true, g_main_loop_new, GMainLoop*, GMainContext*, gboolean ) +LL_GST_SYM( true, g_main_loop_quit, void, GMainLoop* ) +LL_GST_SYM( true, gst_mini_object_unref, void, GstMiniObject* ) +LL_GST_SYM( true, g_object_set, void, gpointer, gchar const*, ... ) +LL_GST_SYM( true, g_source_remove, gboolean, guint ) +LL_GST_SYM( true, g_value_get_string, gchar const*, GValue const* ) + + +LL_GST_SYM( true, gst_debug_set_active, void, gboolean ) +LL_GST_SYM( true, gst_debug_add_log_function, void, GstLogFunction, gpointer, GDestroyNotify ) +LL_GST_SYM( true, gst_debug_set_default_threshold, void, GstDebugLevel ) +LL_GST_SYM( true, gst_debug_message_get , gchar const*, GstDebugMessage * ) \ No newline at end of file diff --git a/indra/media_plugins/gstreamer10/media_plugin_gstreamer10.cpp b/indra/media_plugins/gstreamer10/media_plugin_gstreamer10.cpp new file mode 100644 index 0000000000..5c931ea8f0 --- /dev/null +++ b/indra/media_plugins/gstreamer10/media_plugin_gstreamer10.cpp @@ -0,0 +1,980 @@ +/** + * @file media_plugin_gstreamer10.cpp + * @brief GStreamer-1.0 plugin for LLMedia API plugin system + * + * @cond + * $LicenseInfo:firstyear=2016&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2016, Linden Research, Inc. / Nicky Dasmijn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + * @endcond + */ + +#define FLIP_Y + +#include "linden_common.h" + +#include "llgl.h" + +#include "llplugininstance.h" +#include "llpluginmessage.h" +#include "llpluginmessageclasses.h" +#include "media_plugin_base.h" + +#define G_DISABLE_CAST_CHECKS +extern "C" { +#include +#include + +} + +#include "llmediaimplgstreamer.h" +#include "llmediaimplgstreamer_syms.h" + +static inline void llgst_caps_unref( GstCaps * caps ) +{ + llgst_mini_object_unref( GST_MINI_OBJECT_CAST( caps ) ); +} + +static inline void llgst_sample_unref( GstSample *aSample ) +{ + llgst_mini_object_unref( GST_MINI_OBJECT_CAST( aSample ) ); +} + +////////////////////////////////////////////////////////////////////////////// +// +class MediaPluginGStreamer10 : public MediaPluginBase +{ +public: + MediaPluginGStreamer10(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + ~MediaPluginGStreamer10(); + + /* virtual */ void receiveMessage(const char *message_string); + + static bool startup(); + static bool closedown(); + + gboolean processGSTEvents(GstBus *bus, GstMessage *message); + +private: + std::string getVersion(); + bool navigateTo( const std::string urlIn ); + bool seek( double time_sec ); + bool setVolume( float volume ); + + // misc + bool pause(); + bool stop(); + bool play(double rate); + bool getTimePos(double &sec_out); + + double MIN_LOOP_SEC = 1.0F; + U32 INTERNAL_TEXTURE_SIZE = 1024; + + bool mIsLooping; + + enum ECommand { + COMMAND_NONE, + COMMAND_STOP, + COMMAND_PLAY, + COMMAND_FAST_FORWARD, + COMMAND_FAST_REWIND, + COMMAND_PAUSE, + COMMAND_SEEK, + }; + ECommand mCommand; + +private: + bool unload(); + bool load(); + + bool update(int milliseconds); + void mouseDown( int x, int y ); + void mouseUp( int x, int y ); + void mouseMove( int x, int y ); + + static bool mDoneInit; + + guint mBusWatchID; + + float mVolume; + + int mDepth; + + // padded texture size we need to write into + int mTextureWidth; + int mTextureHeight; + + bool mSeekWanted; + double mSeekDestination; + + // Very GStreamer-specific + GMainLoop *mPump; // event pump for this media + GstElement *mPlaybin; + GstAppSink *mAppSink; +}; + +//static +bool MediaPluginGStreamer10::mDoneInit = false; + +MediaPluginGStreamer10::MediaPluginGStreamer10( LLPluginInstance::sendMessageFunction host_send_func, + void *host_user_data ) + : MediaPluginBase(host_send_func, host_user_data) + , mBusWatchID ( 0 ) + , mSeekWanted(false) + , mSeekDestination(0.0) + , mPump ( NULL ) + , mPlaybin ( NULL ) + , mAppSink ( NULL ) + , mCommand ( COMMAND_NONE ) +{ +} + +gboolean MediaPluginGStreamer10::processGSTEvents(GstBus *bus, GstMessage *message) +{ + if (!message) + return TRUE; // shield against GStreamer bug + + switch (GST_MESSAGE_TYPE (message)) + { + case GST_MESSAGE_BUFFERING: + { + // NEEDS GST 0.10.11+ + if (llgst_message_parse_buffering) + { + gint percent = 0; + llgst_message_parse_buffering(message, &percent); + } + break; + } + case GST_MESSAGE_STATE_CHANGED: + { + GstState old_state; + GstState new_state; + GstState pending_state; + llgst_message_parse_state_changed(message, + &old_state, + &new_state, + &pending_state); + + switch (new_state) + { + case GST_STATE_VOID_PENDING: + break; + case GST_STATE_NULL: + break; + case GST_STATE_READY: + setStatus(STATUS_LOADED); + break; + case GST_STATE_PAUSED: + setStatus(STATUS_PAUSED); + break; + case GST_STATE_PLAYING: + setStatus(STATUS_PLAYING); + break; + } + break; + } + case GST_MESSAGE_ERROR: + { + GError *err = NULL; + gchar *debug = NULL; + + llgst_message_parse_error (message, &err, &debug); + if (err) + llg_error_free (err); + llg_free (debug); + + mCommand = COMMAND_STOP; + + setStatus(STATUS_ERROR); + + break; + } + case GST_MESSAGE_INFO: + { + if (llgst_message_parse_info) + { + GError *err = NULL; + gchar *debug = NULL; + + llgst_message_parse_info (message, &err, &debug); + if (err) + llg_error_free (err); + llg_free (debug); + } + break; + } + case GST_MESSAGE_WARNING: + { + GError *err = NULL; + gchar *debug = NULL; + + llgst_message_parse_warning (message, &err, &debug); + if (err) + llg_error_free (err); + llg_free (debug); + + break; + } + case GST_MESSAGE_EOS: + /* end-of-stream */ + if (mIsLooping) + { + double eos_pos_sec = 0.0F; + bool got_eos_position = getTimePos(eos_pos_sec); + + if (got_eos_position && eos_pos_sec < MIN_LOOP_SEC) + { + // if we know that the movie is really short, don't + // loop it else it can easily become a time-hog + // because of GStreamer spin-up overhead + // inject a COMMAND_PAUSE + mCommand = COMMAND_PAUSE; + } + else + { + stop(); + play(1.0); + } + } + else // not a looping media + { + // inject a COMMAND_STOP + mCommand = COMMAND_STOP; + } + break; + default: + /* unhandled message */ + break; + } + + /* we want to be notified again the next time there is a message + * on the bus, so return true (false means we want to stop watching + * for messages on the bus and our callback should not be called again) + */ + return TRUE; +} + +extern "C" { + gboolean llmediaimplgstreamer_bus_callback (GstBus *bus, + GstMessage *message, + gpointer data) + { + MediaPluginGStreamer10 *impl = (MediaPluginGStreamer10*)data; + return impl->processGSTEvents(bus, message); + } +} // extern "C" + + + +bool MediaPluginGStreamer10::navigateTo ( const std::string urlIn ) +{ + if (!mDoneInit) + return false; // error + + setStatus(STATUS_LOADING); + + mSeekWanted = false; + + if (NULL == mPump || NULL == mPlaybin) + { + setStatus(STATUS_ERROR); + return false; // error + } + + llg_object_set (G_OBJECT (mPlaybin), "uri", urlIn.c_str(), NULL); + + // navigateTo implicitly plays, too. + play(1.0); + + return true; +} + + +class GstSampleUnref +{ + GstSample *mT; +public: + GstSampleUnref( GstSample *aT ) + : mT( aT ) + { llassert_always( mT ); } + + ~GstSampleUnref( ) + { llgst_sample_unref( mT ); } +}; + +bool MediaPluginGStreamer10::update(int milliseconds) +{ + if (!mDoneInit) + return false; // error + + // DEBUGMSG("updating media..."); + + // sanity check + if (NULL == mPump || NULL == mPlaybin) + { + return false; + } + + // see if there's an outstanding seek wanted + if (mSeekWanted && + // bleh, GST has to be happy that the movie is really truly playing + // or it may quietly ignore the seek (with rtsp:// at least). + (GST_STATE(mPlaybin) == GST_STATE_PLAYING)) + { + seek(mSeekDestination); + mSeekWanted = false; + } + + // *TODO: time-limit - but there isn't a lot we can do here, most + // time is spent in gstreamer's own opaque worker-threads. maybe + // we can do something sneaky like only unlock the video object + // for 'milliseconds' and otherwise hold the lock. + while (llg_main_context_pending(llg_main_loop_get_context(mPump))) + { + llg_main_context_iteration(llg_main_loop_get_context(mPump), FALSE); + } + + // check for availability of a new frame + + if( !mAppSink ) + return true; + + if( GST_STATE(mPlaybin) != GST_STATE_PLAYING) // Do not try to pull a sample if not in playing state + return true; + + GstSample *pSample = llgst_app_sink_pull_sample( mAppSink ); + if(!pSample) + return false; // Done playing + + GstSampleUnref oSampleUnref( pSample ); + GstCaps *pCaps = llgst_sample_get_caps ( pSample ); + if (!pCaps) + return false; + + gint width, height; + GstStructure *pStruct = llgst_caps_get_structure ( pCaps, 0); + + int res = llgst_structure_get_int ( pStruct, "width", &width); + res |= llgst_structure_get_int ( pStruct, "height", &height); + + if( !mPixels ) + return true; + + GstBuffer *pBuffer = llgst_sample_get_buffer ( pSample ); + GstMapInfo map; + llgst_buffer_map ( pBuffer, &map, GST_MAP_READ); + + // Our render buffer is always 1kx1k + + U32 rowSkip = INTERNAL_TEXTURE_SIZE / mTextureHeight; + U32 colSkip = INTERNAL_TEXTURE_SIZE / mTextureWidth; + + for (int row = 0; row < mTextureHeight; ++row) + { + U8 const *pTexelIn = map.data + (row*rowSkip * width *3); +#ifndef FLIP_Y + U8 *pTexelOut = mPixels + (row * mTextureWidth * mDepth ); +#else + U8 *pTexelOut = mPixels + ((mTextureHeight-row-1) * mTextureWidth * mDepth ); +#endif + for( int col = 0; col < mTextureWidth; ++col ) + { + pTexelOut[ 0 ] = pTexelIn[0]; + pTexelOut[ 1 ] = pTexelIn[1]; + pTexelOut[ 2 ] = pTexelIn[2]; + pTexelOut += mDepth; + pTexelIn += colSkip*3; + } + } + + llgst_buffer_unmap( pBuffer, &map ); + setDirty(0,0,mTextureWidth,mTextureHeight); + + return true; +} + +void MediaPluginGStreamer10::mouseDown( int x, int y ) +{ + // do nothing +} + +void MediaPluginGStreamer10::mouseUp( int x, int y ) +{ + // do nothing +} + +void MediaPluginGStreamer10::mouseMove( int x, int y ) +{ + // do nothing +} + + +bool MediaPluginGStreamer10::pause() +{ + // todo: error-check this? + if (mDoneInit && mPlaybin) + { + llgst_element_set_state(mPlaybin, GST_STATE_PAUSED); + return true; + } + return false; +} + +bool MediaPluginGStreamer10::stop() +{ + // todo: error-check this? + if (mDoneInit && mPlaybin) + { + llgst_element_set_state(mPlaybin, GST_STATE_READY); + return true; + } + return false; +} + +bool MediaPluginGStreamer10::play(double rate) +{ + // NOTE: we don't actually support non-natural rate. + + // todo: error-check this? + if (mDoneInit && mPlaybin) + { + llgst_element_set_state(mPlaybin, GST_STATE_PLAYING); + return true; + } + return false; +} + +bool MediaPluginGStreamer10::setVolume( float volume ) +{ + // we try to only update volume as conservatively as + // possible, as many gst-plugins-base versions up to at least + // November 2008 have critical race-conditions in setting volume - sigh + if (mVolume == volume) + return true; // nothing to do, everything's fine + + mVolume = volume; + if (mDoneInit && mPlaybin) + { + llg_object_set(mPlaybin, "volume", mVolume, NULL); + return true; + } + + return false; +} + +bool MediaPluginGStreamer10::seek(double time_sec) +{ + bool success = false; + if (mDoneInit && mPlaybin) + { + success = llgst_element_seek(mPlaybin, 1.0F, GST_FORMAT_TIME, + GstSeekFlags(GST_SEEK_FLAG_FLUSH | + GST_SEEK_FLAG_KEY_UNIT), + GST_SEEK_TYPE_SET, gint64(time_sec*GST_SECOND), + GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); + } + return success; +} + +bool MediaPluginGStreamer10::getTimePos(double &sec_out) +{ + bool got_position = false; + if (mDoneInit && mPlaybin) + { + gint64 pos(0); + GstFormat timefmt = GST_FORMAT_TIME; + got_position = + llgst_element_query_position && + llgst_element_query_position(mPlaybin, + &timefmt, + &pos); + got_position = got_position + && (timefmt == GST_FORMAT_TIME); + // GStreamer may have other ideas, but we consider the current position + // undefined if not PLAYING or PAUSED + got_position = got_position && + (GST_STATE(mPlaybin) == GST_STATE_PLAYING || + GST_STATE(mPlaybin) == GST_STATE_PAUSED); + if (got_position && !GST_CLOCK_TIME_IS_VALID(pos)) + { + if (GST_STATE(mPlaybin) == GST_STATE_PLAYING) + { + // if we're playing then we treat an invalid clock time + // as 0, for complicated reasons (insert reason here) + pos = 0; + } + else + { + got_position = false; + } + + } + // If all the preconditions succeeded... we can trust the result. + if (got_position) + { + sec_out = double(pos) / double(GST_SECOND); // gst to sec + } + } + return got_position; +} + +bool MediaPluginGStreamer10::load() +{ + if (!mDoneInit) + return false; // error + + setStatus(STATUS_LOADING); + + mIsLooping = false; + mVolume = 0.1234567f; // minor hack to force an initial volume update + + // Create a pumpable main-loop for this media + mPump = llg_main_loop_new (NULL, FALSE); + if (!mPump) + { + setStatus(STATUS_ERROR); + return false; // error + } + + // instantiate a playbin element to do the hard work + mPlaybin = llgst_element_factory_make ("playbin", ""); + if (!mPlaybin) + { + setStatus(STATUS_ERROR); + return false; // error + } + + // get playbin's bus + GstBus *bus = llgst_pipeline_get_bus (GST_PIPELINE (mPlaybin)); + if (!bus) + { + setStatus(STATUS_ERROR); + return false; // error + } + mBusWatchID = llgst_bus_add_watch (bus, + llmediaimplgstreamer_bus_callback, + this); + llgst_object_unref (bus); + + mAppSink = (GstAppSink*)(llgst_element_factory_make ("appsink", "")); + + GstCaps* pCaps = llgst_caps_new_simple( "video/x-raw", + "format", G_TYPE_STRING, "RGB", + "width", G_TYPE_INT, INTERNAL_TEXTURE_SIZE, + "height", G_TYPE_INT, INTERNAL_TEXTURE_SIZE, + NULL ); + + llgst_app_sink_set_caps( mAppSink, pCaps ); + llgst_caps_unref( pCaps ); + + if (!mAppSink) + { + setStatus(STATUS_ERROR); + return false; + } + + llg_object_set(mPlaybin, "video-sink", mAppSink, NULL); + + return true; +} + +bool MediaPluginGStreamer10::unload () +{ + if (!mDoneInit) + return false; // error + + // stop getting callbacks for this bus + llg_source_remove(mBusWatchID); + mBusWatchID = 0; + + if (mPlaybin) + { + llgst_element_set_state (mPlaybin, GST_STATE_NULL); + llgst_object_unref (GST_OBJECT (mPlaybin)); + mPlaybin = NULL; + } + + if (mPump) + { + llg_main_loop_quit(mPump); + mPump = NULL; + } + + mAppSink = NULL; + + setStatus(STATUS_NONE); + + return true; +} + +void LogFunction(GstDebugCategory *category, GstDebugLevel level, const gchar *file, const gchar *function, gint line, GObject *object, GstDebugMessage *message, gpointer user_data ) +#ifndef LL_LINUX // Docu says we need G_GNUC_NO_INSTRUMENT, but GCC says 'error' + G_GNUC_NO_INSTRUMENT +#endif +{ +#ifdef LL_LINUX + std::cerr << file << ":" << line << "(" << function << "): " << llgst_debug_message_get( message ) << std::endl; +#endif +} + +//static +bool MediaPluginGStreamer10::startup() +{ + // first - check if GStreamer is explicitly disabled + if (NULL != getenv("LL_DISABLE_GSTREAMER")) + return false; + + // only do global GStreamer initialization once. + if (!mDoneInit) + { + ll_init_apr(); + + // Get symbols! + std::vector< std::string > vctDSONames; +#if LL_DARWIN +#elif LL_WINDOWS + vctDSONames.push_back( "libgstreamer-1.0-0.dll" ); + vctDSONames.push_back( "libgstapp-1.0-0.dll" ); + vctDSONames.push_back( "libglib-2.0-0.dll" ); + vctDSONames.push_back( "libgobject-2.0-0.dll" ); +#else // linux or other ELFy unixoid + vctDSONames.push_back( "libgstreamer-1.0.so.0" ); + vctDSONames.push_back( "libgstapp-1.0.so.0" ); + vctDSONames.push_back( "libglib-2.0.so.0" ); + vctDSONames.push_back( "libgobject-2.0.so" ); +#endif + if( !grab_gst_syms( vctDSONames ) ) + { + return false; + } + + if (llgst_segtrap_set_enabled) + { + llgst_segtrap_set_enabled(FALSE); + } +#if LL_LINUX + // Gstreamer tries a fork during init, waitpid-ing on it, + // which conflicts with any installed SIGCHLD handler... + struct sigaction tmpact, oldact; + if (llgst_registry_fork_set_enabled ) { + // if we can disable SIGCHLD-using forking behaviour, + // do it. + llgst_registry_fork_set_enabled(false); + } + else { + // else temporarily install default SIGCHLD handler + // while GStreamer initialises + tmpact.sa_handler = SIG_DFL; + sigemptyset( &tmpact.sa_mask ); + tmpact.sa_flags = SA_SIGINFO; + sigaction(SIGCHLD, &tmpact, &oldact); + } +#endif // LL_LINUX + // Protect against GStreamer resetting the locale, yuck. + static std::string saved_locale; + saved_locale = setlocale(LC_ALL, NULL); + +// _putenv_s( "GST_PLUGIN_PATH", "E:\\gstreamer\\1.0\\x86\\lib\\gstreamer-1.0" ); + + llgst_debug_set_default_threshold( GST_LEVEL_WARNING ); + llgst_debug_add_log_function( LogFunction, NULL, NULL ); + llgst_debug_set_active( false ); + + // finally, try to initialize GStreamer! + GError *err = NULL; + gboolean init_gst_success = llgst_init_check(NULL, NULL, &err); + + // restore old locale + setlocale(LC_ALL, saved_locale.c_str() ); + +#if LL_LINUX + // restore old SIGCHLD handler + if (!llgst_registry_fork_set_enabled) + sigaction(SIGCHLD, &oldact, NULL); +#endif // LL_LINUX + + if (!init_gst_success) // fail + { + if (err) + { + llg_error_free(err); + } + return false; + } + + mDoneInit = true; + } + + return true; +} + +//static +bool MediaPluginGStreamer10::closedown() +{ + if (!mDoneInit) + return false; // error + + ungrab_gst_syms(); + + mDoneInit = false; + + return true; +} + +MediaPluginGStreamer10::~MediaPluginGStreamer10() +{ + closedown(); +} + + +std::string MediaPluginGStreamer10::getVersion() +{ + std::string plugin_version = "GStreamer10 media plugin, GStreamer version "; + if (mDoneInit && + llgst_version) + { + guint major, minor, micro, nano; + llgst_version(&major, &minor, µ, &nano); + plugin_version += llformat("%u.%u.%u.%u (runtime), %u.%u.%u.%u (headers)", (unsigned int)major, (unsigned int)minor, + (unsigned int)micro, (unsigned int)nano, (unsigned int)GST_VERSION_MAJOR, (unsigned int)GST_VERSION_MINOR, + (unsigned int)GST_VERSION_MICRO, (unsigned int)GST_VERSION_NANO); + } + else + { + plugin_version += "(unknown)"; + } + return plugin_version; +} + +void MediaPluginGStreamer10::receiveMessage(const char *message_string) +{ + 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; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION; + message.setValueLLSD("versions", versions); + + load(); + + message.setValue("plugin_version", getVersion()); + sendMessage(message); + } + else if(message_name == "idle") + { + // no response is necessary here. + double time = message_in.getValueReal("time"); + + // Convert time to milliseconds for update() + update((int)(time * 1000.0f)); + } + else if(message_name == "cleanup") + { + unload(); + closedown(); + } + 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"); + + mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); + } + else if(message_name == "shm_remove") + { + std::string name = message_in.getValue("name"); + + 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(); + } + mSharedSegments.erase(iter); + } + + // Send the response so it can be cleaned up. + LLPluginMessage message("base", "shm_remove_response"); + message.setValue("name", name); + sendMessage(message); + } + } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) + { + if(message_name == "init") + { + // Plugin gets to decide the texture parameters to use. + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); + // lame to have to decide this now, it depends on the movie. Oh well. + mDepth = 4; + + mTextureWidth = 1; + mTextureHeight = 1; + + message.setValueU32("format", GL_RGBA); + message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8_REV); + + message.setValueS32("depth", mDepth); + message.setValueS32("default_width", INTERNAL_TEXTURE_SIZE ); + message.setValueS32("default_height", INTERNAL_TEXTURE_SIZE ); + message.setValueU32("internalformat", GL_RGBA8); + message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left. + message.setValueBoolean("allow_downsample", true); // we respond with grace and performance if asked to downscale + sendMessage(message); + } + else 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"); + + 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()) + { + mPixels = (unsigned char*)iter->second.mAddress; + mTextureSegmentName = name; + + mTextureWidth = texture_width; + mTextureHeight = texture_height; + memset( mPixels, 0, mTextureWidth*mTextureHeight*mDepth ); + } + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request"); + message.setValue("name", mTextureSegmentName); + message.setValueS32("width", INTERNAL_TEXTURE_SIZE ); + message.setValueS32("height", INTERNAL_TEXTURE_SIZE ); + sendMessage(message); + + } + } + else if(message_name == "load_uri") + { + std::string uri = message_in.getValue("uri"); + navigateTo( 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") + { + double rate = 0.0; + if(message_in.hasValue("rate")) + { + rate = message_in.getValueReal("rate"); + } + // NOTE: we don't actually support rate. + play(rate); + } + else if(message_name == "pause") + { + pause(); + } + else if(message_name == "seek") + { + double time = message_in.getValueReal("time"); + // defer the actual seek in case we haven't + // really truly started yet in which case there + // is nothing to seek upon + mSeekWanted = true; + mSeekDestination = time; + } + else if(message_name == "set_loop") + { + bool loop = message_in.getValueBoolean("loop"); + mIsLooping = loop; + } + else if(message_name == "set_volume") + { + double volume = message_in.getValueReal("volume"); + setVolume(volume); + } + } + } +} + +int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +{ + if( MediaPluginGStreamer10::startup() ) + { + MediaPluginGStreamer10 *self = new MediaPluginGStreamer10(host_send_func, host_user_data); + *plugin_send_func = MediaPluginGStreamer10::staticReceiveMessage; + *plugin_user_data = (void*)self; + + return 0; // okay + } + else + { + return -1; // failed to init + } +} diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 3220b89774..6a32d90a65 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -13,7 +13,7 @@ include(BuildPackagesInfo) include(BuildVersion) include(CMakeCopyIfDifferent) include(CubemapToEquirectangularJS) -include(DBusGlib) +include(GLIB) include(DragDrop) include(EXPAT) include(FMODSTUDIO) @@ -1797,7 +1797,6 @@ if (LINUX) # fsversionstrings.h with the right numbers in it. # COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}" ) - LIST(APPEND viewer_SOURCE_FILES llappviewerlinux_api_dbus.cpp) # [FS] Growl support LIST(APPEND viewer_HEADER_FILES desktopnotifierlinux.h growlmanager.h) LIST(APPEND viewer_SOURCE_FILES desktopnotifierlinux.cpp growlmanager.cpp) @@ -1865,6 +1864,8 @@ if (WINDOWS) COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}" ) + set_source_files_properties( llappviewer.cpp llviewermenu.cpp PROPERTIES COMPILE_FLAGS /bigobj ) + list(APPEND viewer_HEADER_FILES llappviewerwin32.h llwindebug.h @@ -2518,7 +2519,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${BOOST_WAVE_LIBRARY} #FS specific ${BOOST_THREAD_LIBRARY} #FS specific ${BOOST_CONTEXT_LIBRARY} - ${DBUSGLIB_LIBRARIES} + ${GLIB_LIBRARIES} ${OPENGL_LIBRARIES} ${FMODWRAPPER_LIBRARY} # must come after LLAudio ${OPENAL_LIBRARIES} @@ -2538,6 +2539,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLPHYSICSEXTENSIONS_LIBRARIES} ${LLAPPEARANCE_LIBRARIES} ${GROWL_LIBRARY} + ${GIO_LIBRARIES} ) target_link_libraries(${VIEWER_BINARY_NAME} ${DISCORD_LIBRARY} ) @@ -2565,26 +2567,30 @@ if (LINUX) set(product Firestorm-${ARCH}-${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}) # These are the generated targets that are copied to package/ -if (NOT ENABLE_MEDIA_PLUGINS) - set(COPY_INPUT_DEPENDENCIES - ${VIEWER_BINARY_NAME} - SLPlugin - media_plugin_cef - #media_plugin_gstreamer010 - media_plugin_libvlc - llcommon - ) -else (NOT ENABLE_MEDIA_PLUGINS) - set(COPY_INPUT_DEPENDENCIES - ${VIEWER_BINARY_NAME} - #linux-crash-logger - SLPlugin - media_plugin_cef - #media_plugin_gstreamer010 - llcommon - ) -endif (NOT ENABLE_MEDIA_PLUGINS) + if (NOT ENABLE_MEDIA_PLUGINS) + set(COPY_INPUT_DEPENDENCIES + ${VIEWER_BINARY_NAME} + SLPlugin + media_plugin_cef + media_plugin_gstreamer10 + #media_plugin_libvlc + llcommon + linux-crash-logger + ) + else (NOT ENABLE_MEDIA_PLUGINS) + set(COPY_INPUT_DEPENDENCIES + ${VIEWER_BINARY_NAME} + #linux-crash-logger + SLPlugin + media_plugin_cef + media_plugin_gstreamer10 + llcommon + linux-crash-logger + ) + endif (NOT ENABLE_MEDIA_PLUGINS) + add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_gstreamer10 media_plugin_cef linux-crash-logger) + add_custom_command( OUTPUT ${product}.tar.bz2 COMMAND ${PYTHON_EXECUTABLE} @@ -2610,7 +2616,6 @@ endif (NOT ENABLE_MEDIA_PLUGINS) ${COPY_INPUT_DEPENDENCIES} ) - add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/.${product}.copy_touched COMMAND ${PYTHON_EXECUTABLE} @@ -2861,8 +2866,10 @@ if (PACKAGE AND (RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) AND VIE endif (LINUX) endif (USE_BUGSPLAT) - # for both Bugsplat and Breakpad - add_dependencies(llpackage generate_symbols) + if (NOT LINUX) #Linux generates symbols via viewer_manifest.py/fs_viewer_manifest.py + # for both Bugsplat and Breakpad + add_dependencies(llpackage generate_symbols) + endif() endif () if (LL_TESTS) diff --git a/indra/newview/SecondLife.xib b/indra/newview/SecondLife.xib index ef25c648a7..fbff8fe307 100644 --- a/indra/newview/SecondLife.xib +++ b/indra/newview/SecondLife.xib @@ -340,7 +340,7 @@ {10000000000000, 10000000000000} Second Life 128 - YES + NO 31 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index f5f5318627..0d26ab67d4 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -11123,7 +11123,7 @@ Change of this parameter will affect the layout of buttons in notification toast PushToTalkToggle Comment - Should the push-to-talk button behave as a toggle + Should the push-to-talk toolbar button behave as a toggle Persist 1 Type @@ -11167,7 +11167,7 @@ Change of this parameter will affect the layout of buttons in notification toast QAModeMetrics Comment - "Enables QA features (logging, faster cycling) for metrics collector" + Enables QA features (logging, faster cycling) for metrics collector Persist 1 Type @@ -11177,8 +11177,18 @@ Change of this parameter will affect the layout of buttons in notification toast Backup 0 - - QuitAfterSeconds + QAModeFakeSystemFolderIssues + + Comment + Simulates system folder issues in inventory + Persist + 1 + Type + Boolean + Value + 0 + + QuitAfterSeconds Comment The duration allowed before quitting. @@ -15699,6 +15709,17 @@ Change of this parameter will affect the layout of buttons in notification toast Backup 0 + TextureFetchMinTimeToLog + + Comment + If texture fetching time exceeds this value, texture fetch tester will log info + Persist + 1 + Type + F32 + Value + 2.0 + TextureFetchFakeFailureRate Comment @@ -15801,6 +15822,17 @@ Change of this parameter will affect the layout of buttons in notification toast Backup 0 + TextureListFetchingThreshold + + Comment + If the ratio between fetched and all textures in the list is greater than this threshold, which we assume that almost all textures are fetched + Persist + 1 + Type + F32 + Value + 0.97 + TextureLoadFullRes Comment @@ -25914,6 +25946,17 @@ Change of this parameter will affect the layout of buttons in notification toast Type Boolean Value + 0 + + FSAutoTuneLock + + Comment + When enabled the viewer will dynamically change settings until auto tune is explicitly turned off. + Persist + 1 + Type + Boolean + Value 1 FSAllowSelfImpostor @@ -25993,6 +26036,17 @@ Change of this parameter will affect the layout of buttons in notification toast Value 0 + FSUserTargetReflections + + Comment + Set by auto tune floater on build + Persist + 0 + Type + S32 + Value + 4 + FSReportRegionRestartToChat Comment diff --git a/indra/newview/build_win32_appConfig.py b/indra/newview/build_win32_appConfig.py index 9fdceee1be..d18d7b88cb 100755 --- a/indra/newview/build_win32_appConfig.py +++ b/indra/newview/build_win32_appConfig.py @@ -38,7 +38,7 @@ def munge_binding_redirect_version(src_manifest_name, src_config_name, dst_confi 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 + print("Writing: " + dst_config_name) f = open(dst_config_name, 'w') config_dom.writexml(f) f.close() diff --git a/indra/newview/cursors_mac/UI_CURSOR_SIZEALL.tif b/indra/newview/cursors_mac/UI_CURSOR_SIZEALL.tif new file mode 100644 index 0000000000..85fec76fca Binary files /dev/null and b/indra/newview/cursors_mac/UI_CURSOR_SIZEALL.tif differ diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMOUT.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMOUT.tif new file mode 100644 index 0000000000..d64a7f2b68 Binary files /dev/null and b/indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMOUT.tif differ diff --git a/indra/newview/fs_viewer_manifest.py b/indra/newview/fs_viewer_manifest.py index 5bea417026..04d75e6c1d 100644 --- a/indra/newview/fs_viewer_manifest.py +++ b/indra/newview/fs_viewer_manifest.py @@ -9,7 +9,7 @@ class FSViewerManifest: def fs_splice_grid_substitution_strings( self, subst_strings ): ret = subst_strings - if self.args.has_key( 'grid' ) and self.args['grid'] != None: + if 'grid' in self.args and self.args['grid'] != None: ret[ 'grid' ] = self.args['grid'] ret[ 'grid_caps' ] = self.args['grid'].upper() else: @@ -51,15 +51,15 @@ class FSViewerManifest: stderr=subprocess.PIPE,stdout=subprocess.PIPE) subprocess.check_call(["signtool.exe","sign","/n","Phoenix","/d","Firestorm","/du","http://www.phoenixviewer.com","/t","http://timestamp.verisign.com/scripts/timstamp.dll",self.args['configuration']+"\\"+self.final_exe()], stderr=subprocess.PIPE,stdout=subprocess.PIPE) - except Exception, e: - print "Couldn't sign final binary. Tried to sign %s" % self.args['configuration']+"\\"+self.final_exe() + except Exception as e: + print("Couldn't sign final binary. Tried to sign %s" % self.args['configuration']+"\\"+self.final_exe()) def fs_sign_win_installer( self, substitution_strings ): try: subprocess.check_call(["signtool.exe","sign","/n","Phoenix","/d","Firestorm","/du","http://www.phoenixviewer.com",self.args['configuration']+"\\"+substitution_strings['installer_file']],stderr=subprocess.PIPE,stdout=subprocess.PIPE) - except Exception, e: - print "Working directory: %s" % os.getcwd() - print "Couldn't sign windows installer. Tried to sign %s" % self.args['configuration']+"\\"+substitution_strings['installer_file'] + except Exception as e: + print("Working directory: %s" % os.getcwd()) + print("Couldn't sign windows installer. Tried to sign %s" % self.args['configuration']+"\\"+substitution_strings['installer_file']) def fs_delete_linux_symbols( self ): debugDir = os.path.join( self.get_dst_prefix(), "bin", ".debug" ) @@ -76,7 +76,7 @@ class FSViewerManifest: def fs_save_linux_symbols( self ): #AO: Try to package up symbols # New Method, for reading cross platform stack traces on a linux/mac host - print( "Packaging symbols" ) + print("Packaging symbols") self.fs_save_symbols("linux") @@ -97,7 +97,7 @@ class FSViewerManifest: ], stderr=subprocess.PIPE,stdout=subprocess.PIPE ) pdbName = "firestorm-bin-public.pdb" except: - print( "Cannot run pdbcopy, packaging private symbols" ) + print("Cannot run pdbcopy, packaging private symbols") # Store windows symbols we want to keep for debugging in a tar file, this will be later compressed with xz (lzma) # Using tat+xz gives far superior compression than zip (~half the size of the zip archive). @@ -162,3 +162,63 @@ class FSViewerManifest: if self.path( src,dst ) == 0: self.missing.pop() + def fs_generate_breakpad_symbols_for_file( self, aFile ): + from os import makedirs, remove + from os.path import join, isfile + import subprocess + from shutil import move + + dumpSym = join( self.args["build"], "..", "packages", "bin", "dump_syms" ) + if not isfile( dumpSym ): + return + + symbolFile = aFile +".sym" + + with open( symbolFile, "w") as outfile: + subprocess.call( [dumpSym, aFile ], stdout=outfile ) + + firstline = open( symbolFile ).readline().strip() + + if firstline != "": + module, os, bitness, hash, filename = firstline.split(" ") + symbolDir = join( "symbols", filename, hash ) + try: + makedirs( symbolDir ) + except: + pass + move( symbolFile, symbolDir ) + + if isfile( symbolFile ): + remove( symbolFile ) + + def fs_save_breakpad_symbols(self, osname): + from glob import glob + import sys + from os.path import isdir + from shutil import rmtree + import tarfile + + if isdir( "symbols" ): + rmtree( "symbols" ) + + files = glob( "%s/bin/*" % self.args['dest'] ) + for f in files: + self.fs_generate_breakpad_symbols_for_file( f ) + + files = glob( "%s/lib/*.so" % self.args['dest'] ) + for f in files: + if f.find( "libcef.so" ) == -1: + self.fs_generate_breakpad_symbols_for_file( f ) + + if isdir( "symbols" ): + for a in self.args: + print("%s: %s" % (a, self.args[a])) + symbolsName = "%s/Phoenix_%s_%s_%s_symbols-%s-%d.tar.bz2" % (self.args['configuration'].lower(), + self.fs_channel_legacy_oneword(), + '-'.join( self.args['version'] ), + self.args['viewer_flavor'], + osname, + self.address_size) + + fTar = tarfile.open( symbolsName, "w:bz2") + fTar.add("symbols", arcname=".") diff --git a/indra/newview/fsfloaterperformance.cpp b/indra/newview/fsfloaterperformance.cpp index c8f5d64d76..7e11d1dadc 100644 --- a/indra/newview/fsfloaterperformance.cpp +++ b/indra/newview/fsfloaterperformance.cpp @@ -51,8 +51,12 @@ #include "pipeline.h" #include "llviewercontrol.h" #include "fsavatarrenderpersistence.h" +#include "llpresetsmanager.h" #include "fsperfstats.h" // performance stats support #include "fslslbridge.h" +#include + +extern F32 gSavedDrawDistance; const F32 REFRESH_INTERVAL = 1.0f; const S32 BAR_LEFT_PAD = 2; @@ -119,8 +123,11 @@ BOOL FSFloaterPerformance::postBuild() auto tgt_panel = findChild("target_subpanel"); if (tgt_panel) { - tgt_panel->getChild("target_btn")->setCommitCallback(boost::bind(&FSFloaterPerformance::showSelectedPanel, this, mAutoTunePanel)); - tgt_panel->getChild("FSTuningFPSStrategy")->setCurrentByIndex(gSavedSettings.getU32("FSTuningFPSStrategy")); + tgt_panel->getChild("target_btn")->setCommitCallback(boost::bind(&FSFloaterPerformance::showSelectedPanel, this, mAutoTunePanel)); + tgt_panel->getChild("FSTuningFPSStrategy")->setCurrentByIndex(gSavedSettings.getU32("FSTuningFPSStrategy")); + tgt_panel->getChild("PrefSaveButton")->setCommitCallback(boost::bind(&FSFloaterPerformance::savePreset, this)); + tgt_panel->getChild("PrefLoadButton")->setCommitCallback(boost::bind(&FSFloaterPerformance::loadPreset, this)); + tgt_panel->getChild("Defaults")->setCommitCallback(boost::bind(&FSFloaterPerformance::setHardwareDefaults, this)); } initBackBtn(mNearbyPanel); @@ -139,13 +146,14 @@ BOOL FSFloaterPerformance::postBuild() mObjectList->setHoverIconName("StopReload_Off"); mObjectList->setIconClickedCallback(boost::bind(&FSFloaterPerformance::detachItem, this, _1)); - mSettingsPanel->getChild("graphics_quality")->setCommitCallback(boost::bind(&FSFloaterPerformance::onChangeQuality, this, _2)); + mSettingsPanel->getChild("quality_vs_perf_selection")->setCommitCallback(boost::bind(&FSFloaterPerformance::onChangeQuality, this, _2)); mNearbyPanel->getChild("exceptions_btn")->setCommitCallback(boost::bind(&FSFloaterPerformance::onClickExceptions, this)); mNearbyPanel->getChild("hide_avatars")->setCommitCallback(boost::bind(&FSFloaterPerformance::onClickHideAvatars, this)); mNearbyPanel->getChild("hide_avatars")->set(!LLPipeline::hasRenderTypeControl(LLPipeline::RENDER_TYPE_AVATAR)); mNearbyList = mNearbyPanel->getChild("nearby_list"); mNearbyList->setRightMouseDownCallback(boost::bind(&FSFloaterPerformance::onAvatarListRightClick, this, _1, _2, _3)); + updateComplexityText(); mComplexityChangedSignal = gSavedSettings.getControl("RenderAvatarMaxComplexity")->getCommitSignal()->connect(boost::bind(&FSFloaterPerformance::updateComplexityText, this)); @@ -155,10 +163,53 @@ BOOL FSFloaterPerformance::postBuild() mNearbyPanel->getChild("FSRenderAvatarMaxART")->setCommitCallback(boost::bind(&FSFloaterPerformance::updateMaxRenderTime, this)); LLAvatarComplexityControls::setIndirectMaxArc(); + // store the current setting as the users desired reflection detail and DD + gSavedSettings.setS32("FSUserTargetReflections", LLPipeline::RenderReflectionDetail); + if(!FSPerfStats::tunables.userAutoTuneEnabled) + { + if (gSavedDrawDistance) + { + gSavedSettings.setF32("FSAutoTuneRenderFarClipTarget", gSavedDrawDistance); + } + else + { + gSavedSettings.setF32("FSAutoTuneRenderFarClipTarget", LLPipeline::RenderFarClip); + } + } return TRUE; } +void FSFloaterPerformance::resetMaxArtSlider() +{ + FSPerfStats::renderAvatarMaxART_ns = 0; + FSPerfStats::tunables.updateSettingsFromRenderCostLimit(); + FSPerfStats::tunables.applyUpdates(); + updateMaxRenderTime(); +} + +void FSFloaterPerformance::savePreset() +{ + LLFloaterReg::showInstance("save_pref_preset", "graphic" ); +} + +void FSFloaterPerformance::loadPreset() +{ + LLFloaterReg::showInstance("load_pref_preset", "graphic"); + resetMaxArtSlider(); +} + +void FSFloaterPerformance::setHardwareDefaults() +{ + LLFeatureManager::getInstance()->applyRecommendedSettings(); + // reset indirects before refresh because we may have changed what they control + LLAvatarComplexityControls::setIndirectControls(); + gSavedSettings.setString("PresetGraphicActive", ""); + LLPresetsManager::getInstance()->triggerChangeSignal(); + resetMaxArtSlider(); +} + + void FSFloaterPerformance::showSelectedPanel(LLPanel* selected_panel) { hidePanels(); @@ -240,104 +291,111 @@ void FSFloaterPerformance::draw() // tot_frame_time_ns -= tot_limit_time_ns; // tot_frame_time_ns -= tot_sleep_time_ns; - if(tot_frame_time_ns == 0) + if(tot_frame_time_ns != 0) { - LL_WARNS("performance") << "things went wrong, quit while we can." << LL_ENDL; - return; - } - auto pct_avatar_time = (tot_avatar_time_ns * 100)/tot_frame_time_ns; - auto pct_huds_time = (tot_huds_time_ns * 100)/tot_frame_time_ns; - auto pct_ui_time = (tot_ui_time_ns * 100)/tot_frame_time_ns; - auto pct_idle_time = (tot_idle_time_ns * 100)/tot_frame_time_ns; - auto pct_swap_time = (tot_swap_time_ns * 100)/tot_frame_time_ns; - auto pct_scene_render_time = (tot_scene_time_ns * 100)/tot_frame_time_ns; - pct_avatar_time = llclamp(pct_avatar_time,0.,100.); - pct_huds_time = llclamp(pct_huds_time,0.,100.); - pct_ui_time = llclamp(pct_ui_time,0.,100.); - pct_idle_time = llclamp(pct_idle_time,0.,100.); - pct_swap_time = llclamp(pct_swap_time,0.,100.); - pct_scene_render_time = llclamp(pct_scene_render_time,0.,100.); + auto pct_avatar_time = (tot_avatar_time_ns * 100)/tot_frame_time_ns; + auto pct_huds_time = (tot_huds_time_ns * 100)/tot_frame_time_ns; + auto pct_ui_time = (tot_ui_time_ns * 100)/tot_frame_time_ns; + auto pct_idle_time = (tot_idle_time_ns * 100)/tot_frame_time_ns; + auto pct_swap_time = (tot_swap_time_ns * 100)/tot_frame_time_ns; + auto pct_scene_render_time = (tot_scene_time_ns * 100)/tot_frame_time_ns; + pct_avatar_time = llclamp(pct_avatar_time,0.,100.); + pct_huds_time = llclamp(pct_huds_time,0.,100.); + pct_ui_time = llclamp(pct_ui_time,0.,100.); + pct_idle_time = llclamp(pct_idle_time,0.,100.); + pct_swap_time = llclamp(pct_swap_time,0.,100.); + pct_scene_render_time = llclamp(pct_scene_render_time,0.,100.); - args["AV_FRAME_PCT"] = llformat("%02u", (U32)llround(pct_avatar_time)); - args["HUDS_FRAME_PCT"] = llformat("%02u", (U32)llround(pct_huds_time)); - args["UI_FRAME_PCT"] = llformat("%02u", (U32)llround(pct_ui_time)); - args["IDLE_FRAME_PCT"] = llformat("%02u", (U32)llround(pct_idle_time)); - args["SWAP_FRAME_PCT"] = llformat("%02u", (U32)llround(pct_swap_time)); - args["SCENERY_FRAME_PCT"] = llformat("%02u", (U32)llround(pct_scene_render_time)); - args["TOT_FRAME_TIME"] = llformat("%02u", (U32)llround(tot_frame_time_ns/1000000)); - args["FPSCAP"] = llformat("%02u", (U32)fpsCap); - args["FPSTARGET"] = llformat("%02u", (U32)targetFPS); + args["AV_FRAME_PCT"] = llformat("%02u", (U32)llround(pct_avatar_time)); + args["HUDS_FRAME_PCT"] = llformat("%02u", (U32)llround(pct_huds_time)); + args["UI_FRAME_PCT"] = llformat("%02u", (U32)llround(pct_ui_time)); + args["IDLE_FRAME_PCT"] = llformat("%02u", (U32)llround(pct_idle_time)); + args["SWAP_FRAME_PCT"] = llformat("%02u", (U32)llround(pct_swap_time)); + args["SCENERY_FRAME_PCT"] = llformat("%02u", (U32)llround(pct_scene_render_time)); + args["TOT_FRAME_TIME"] = llformat("%02u", (U32)llround(tot_frame_time_ns/1000000)); + args["FPSCAP"] = llformat("%02u", (U32)fpsCap); + args["FPSTARGET"] = llformat("%02u", (U32)targetFPS); - getChild("av_frame_stats")->setText(getString("av_frame_pct", args)); - getChild("huds_frame_stats")->setText(getString("huds_frame_pct", args)); - getChild("frame_breakdown")->setText(getString("frame_stats", args)); - - auto textbox = getChild("fps_warning"); - if (tot_sleep_time_raw > 0) // We are sleeping because view is not focussed - { - textbox->setVisible(true); - textbox->setText(getString("focus_fps")); - textbox->setColor(LLUIColorTable::instance().getColor("DrYellow")); - unreliable = true; - } - else if (tot_limit_time_raw > 0) - { - textbox->setVisible(true); - textbox->setText(getString("limit_fps", args)); - textbox->setColor(LLUIColorTable::instance().getColor("DrYellow")); - unreliable = true; - } - else if (FSPerfStats::autoTune) - { - textbox->setVisible(true); - textbox->setText(getString("tuning_fps", args)); - textbox->setColor(LLUIColorTable::instance().getColor("green")); + getChild("av_frame_stats")->setText(getString("av_frame_pct", args)); + getChild("huds_frame_stats")->setText(getString("huds_frame_pct", args)); + getChild("frame_breakdown")->setText(getString("frame_stats", args)); + + auto textbox = getChild("fps_warning"); + if (tot_sleep_time_raw > 0) // We are sleeping because view is not focussed + { + textbox->setVisible(true); + textbox->setText(getString("focus_fps")); + textbox->setColor(LLUIColorTable::instance().getColor("DrYellow")); + unreliable = true; + } + else if (tot_limit_time_raw > 0) + { + textbox->setVisible(true); + textbox->setText(getString("limit_fps", args)); + textbox->setColor(LLUIColorTable::instance().getColor("DrYellow")); + unreliable = true; + } + else if (FSPerfStats::tunables.userAutoTuneEnabled) + { + textbox->setVisible(true); + textbox->setText(getString("tuning_fps", args)); + textbox->setColor(LLUIColorTable::instance().getColor("green")); + } + else + { + textbox->setVisible(false); + } + + auto button = getChild("AutoTuneFPS"); + if((bool)button->getToggleState() != FSPerfStats::tunables.userAutoTuneEnabled) + { + button->toggleState(); + } + if (FSPerfStats::tunables.userAutoTuneEnabled && !unreliable ) + { + // the tuning itself is managed from another thread but we can report progress here + + // Is our target frame time lower than current? If so we need to take action to reduce draw overheads. + if (target_frame_time_ns <= tot_frame_time_ns) + { + U32 non_avatar_time_ns = tot_frame_time_ns - tot_avatar_time_ns; + // If the target frame time < non avatar frame time then we can pototentially reach it. + if (non_avatar_time_ns < target_frame_time_ns) + { + textbox->setColor(LLUIColorTable::instance().getColor("orange")); + } + else + { + // TODO(Beq): Set advisory text for further actions + textbox->setColor(LLUIColorTable::instance().getColor("red")); + } + } + else if (target_frame_time_ns > (tot_frame_time_ns + FSPerfStats::renderAvatarMaxART_ns)) + { + // if we have more time to spare. Display this (the service will update things) + textbox->setColor(LLUIColorTable::instance().getColor("green")); + } + } + + if (mHUDsPanel->getVisible()) + { + populateHUDList(); + } + else if (mNearbyPanel->getVisible()) + { + populateNearbyList(); + mNearbyPanel->getChild("hide_avatars")->set(!LLPipeline::hasRenderTypeControl(LLPipeline::RENDER_TYPE_AVATAR)); + } + else if (mComplexityPanel->getVisible()) + { + populateObjectList(); + } } else { - textbox->setVisible(false); + LL_WARNS("performance") << "Scene time 0. Skipping til we have data." << LL_ENDL; } - - if (FSPerfStats::autoTune && !unreliable ) - { - // the tuning itself is managed from another thread but we can report progress here - - // Is our target frame time lower than current? If so we need to take action to reduce draw overheads. - if (target_frame_time_ns <= tot_frame_time_ns) - { - U32 non_avatar_time_ns = tot_frame_time_ns - tot_avatar_time_ns; - // If the target frame time < non avatar frame time then we can pototentially reach it. - if (non_avatar_time_ns < target_frame_time_ns) - { - textbox->setColor(LLUIColorTable::instance().getColor("orange")); - } - else - { - // TODO(Beq): Set advisory text for further actions - textbox->setColor(LLUIColorTable::instance().getColor("red")); - } - } - else if (target_frame_time_ns > (tot_frame_time_ns + FSPerfStats::renderAvatarMaxART_ns)) - { - // if we have more time to spare. Display this (the service will update things) - textbox->setColor(LLUIColorTable::instance().getColor("green")); - } - } - - if (mHUDsPanel->getVisible()) - { - populateHUDList(); - } - else if (mNearbyPanel->getVisible()) - { - populateNearbyList(); - mNearbyPanel->getChild("hide_avatars")->set(!LLPipeline::hasRenderTypeControl(LLPipeline::RENDER_TYPE_AVATAR)); - } - else if (mComplexityPanel->getVisible()) - { - populateObjectList(); - } - + mUpdateTimer->setTimerExpirySec(REFRESH_INTERVAL); } LLFloater::draw(); @@ -732,11 +790,9 @@ void FSFloaterPerformance::detachItem(const LLUUID& item_id) void FSFloaterPerformance::onChangeQuality(const LLSD& data) { - LLFloaterPreference* instance = LLFloaterReg::getTypedInstance("preferences"); - if (instance) - { - instance->onChangeQuality(data); - } + U32 level = (U32)(data.asReal()); + LLFeatureManager::getInstance()->setGraphicsLevel(level, true); + refresh(); } void FSFloaterPerformance::onClickHideAvatars() diff --git a/indra/newview/fsfloaterperformance.h b/indra/newview/fsfloaterperformance.h index 034c772ae2..f60de2da84 100644 --- a/indra/newview/fsfloaterperformance.h +++ b/indra/newview/fsfloaterperformance.h @@ -55,6 +55,10 @@ public: void onCustomAction (const LLSD& userdata, const LLUUID& av_id); bool isActionChecked(const LLSD& userdata, const LLUUID& av_id); void onExtendedAction(const LLSD& userdata, const LLUUID& av_id); + void resetMaxArtSlider(); + void savePreset(); + void loadPreset(); + void setHardwareDefaults(); private: void initBackBtn(LLPanel* panel); diff --git a/indra/newview/fspanellogin.cpp b/indra/newview/fspanellogin.cpp index ca6387f036..0b6802a406 100644 --- a/indra/newview/fspanellogin.cpp +++ b/indra/newview/fspanellogin.cpp @@ -873,6 +873,15 @@ void FSPanelLogin::loadLoginPage() std::string force_login_url = gSavedSettings.getString("ForceLoginURL"); if ( force_login_url.length() > 0 ) { + LLNotificationsUtil::add("WarnForceLoginURL", LLSD(), LLSD(), [](const LLSD¬if, const LLSD&resp) + { + S32 opt = LLNotificationsUtil::getSelectedOption(notif, resp); + if (opt == 0) + { + gSavedSettings.setString("ForceLoginURL", ""); + loadLoginPage(); + } + }); login_page = LLURI(force_login_url); } diff --git a/indra/newview/fsperfstats.cpp b/indra/newview/fsperfstats.cpp index 1febba5f35..1238bd786d 100644 --- a/indra/newview/fsperfstats.cpp +++ b/indra/newview/fsperfstats.cpp @@ -32,6 +32,7 @@ #include "llagentcamera.h" #include "llvoavatar.h" #include "llworld.h" +#include extern LLControlGroup gSavedSettings; @@ -47,12 +48,12 @@ namespace FSPerfStats #endif std::atomic tunedAvatars{0}; - U32 targetFPS; // desired FPS - U64 renderAvatarMaxART_ns{(U64)(ART_UNLIMITED_NANOS)}; // highest render time we'll allow without culling features - U32 fpsTuningStrategy{0}; // linked to FSTuningFPSStrategy + std::atomic renderAvatarMaxART_ns{(U64)(ART_UNLIMITED_NANOS)}; // highest render time we'll allow without culling features + bool belowTargetFPS{false}; U32 lastGlobalPrefChange{0}; std::mutex bufferToggleLock{}; - bool autoTune{false}; + + Tunables tunables; std::atomic StatsRecorder::writeBuffer{0}; bool StatsRecorder::collectionEnabled{true}; @@ -61,17 +62,80 @@ namespace FSPerfStats std::array StatsRecorder::max{ {} }; std::array StatsRecorder::sum{ {} }; + void Tunables::applyUpdates() + { + assert_main_thread(); + // these following variables are proxies for pipeline statics we do not need a two way update (no llviewercontrol handler) + if( tuningFlag & NonImpostors ){ gSavedSettings.setU32("IndirectMaxNonImpostors", nonImpostors); }; + if( tuningFlag & ReflectionDetail ){ gSavedSettings.setS32("RenderReflectionDetail", reflectionDetail); }; + if( tuningFlag & FarClip ){ gSavedSettings.setF32("RenderFarClip", farClip); }; + if( tuningFlag & UserMinDrawDistance ){ gSavedSettings.setF32("FSAutoTuneRenderFarClipMin", userMinDrawDistance); }; + if( tuningFlag & UserTargetDrawDistance ){ gSavedSettings.setF32("FSAutoTuneRenderFarClipTarget", userTargetDrawDistance); }; + if( tuningFlag & UserImpostorDistance ){ gSavedSettings.setF32("FSAutoTuneImpostorFarAwayDistance", userImpostorDistance); }; + if( tuningFlag & UserImpostorDistanceTuningEnabled ){ gSavedSettings.setBOOL("FSAutoTuneImpostorByDistEnabled", userImpostorDistanceTuningEnabled); }; + if( tuningFlag & UserFPSTuningStrategy ){ gSavedSettings.setU32("FSTuningFPSStrategy", userFPSTuningStrategy); }; + if( tuningFlag & UserAutoTuneEnabled ){ gSavedSettings.setBOOL("FSAutoTuneFPS", userAutoTuneEnabled); }; + if( tuningFlag & UserAutoTuneLock ){ gSavedSettings.setBOOL("FSAutoTuneLock", userAutoTuneLock); }; + if( tuningFlag & UserTargetFPS ){ gSavedSettings.setU32("FSTargetFPS", userTargetFPS); }; + if( tuningFlag & UserTargetReflections ){ gSavedSettings.setS32("FSUserTargetReflections", userTargetReflections); }; + // Note: The Max ART slider is logarithmic and thus we have an intermediate proxy value + if( tuningFlag & UserARTCutoff ){ gSavedSettings.setF32("FSRenderAvatarMaxART", userARTCutoffSliderValue); }; + resetChanges(); + } + + void Tunables::updateRenderCostLimitFromSettings() + { + assert_main_thread(); + const auto newval = gSavedSettings.getF32("FSRenderAvatarMaxART"); + if(newval < log10(FSPerfStats::ART_UNLIMITED_NANOS/1000)) + { + FSPerfStats::renderAvatarMaxART_ns = pow(10,newval)*1000; + } + else + { + FSPerfStats::renderAvatarMaxART_ns = 0; + }; + } + + // static + void Tunables::updateSettingsFromRenderCostLimit() + { + if( userARTCutoffSliderValue != log10( ( (F32)FSPerfStats::renderAvatarMaxART_ns )/1000 ) ) + { + if( FSPerfStats::renderAvatarMaxART_ns != 0 ) + { + updateUserARTCutoffSlider(log10( ( (F32)FSPerfStats::renderAvatarMaxART_ns )/1000 ) ); + } + else + { + updateUserARTCutoffSlider(log10( (F32)FSPerfStats::ART_UNLIMITED_NANOS/1000 ) ); + } + } + } + + void Tunables::initialiseFromSettings() + { + assert_main_thread(); + // the following variables are two way and have "push" in llviewercontrol + FSPerfStats::tunables.userMinDrawDistance = gSavedSettings.getF32("FSAutoTuneRenderFarClipMin"); + FSPerfStats::tunables.userTargetDrawDistance = gSavedSettings.getF32("FSAutoTuneRenderFarClipTarget"); + FSPerfStats::tunables.userImpostorDistance = gSavedSettings.getF32("FSAutoTuneImpostorFarAwayDistance"); + FSPerfStats::tunables.userImpostorDistanceTuningEnabled = gSavedSettings.getBOOL("FSAutoTuneImpostorByDistEnabled"); + FSPerfStats::tunables.userFPSTuningStrategy = gSavedSettings.getU32("FSTuningFPSStrategy"); + FSPerfStats::tunables.userTargetFPS = gSavedSettings.getU32("FSTargetFPS"); + FSPerfStats::tunables.userTargetReflections = gSavedSettings.getU32("FSUserTargetReflections"); + FSPerfStats::tunables.userAutoTuneEnabled = gSavedSettings.getBOOL("FSAutoTuneFPS"); + FSPerfStats::tunables.userAutoTuneLock = gSavedSettings.getBOOL("FSAutoTuneLock"); + // Note: The Max ART slider is logarithmic and thus we have an intermediate proxy value + updateRenderCostLimitFromSettings(); + resetChanges(); + } StatsRecorder::StatsRecorder():q(1024*16),t(&StatsRecorder::run) { // create a queue // create a thread to consume from the queue - - FSPerfStats::targetFPS = gSavedSettings.getU32("FSTargetFPS"); - FSPerfStats::autoTune = gSavedSettings.getBOOL("FSAutoTuneFPS"); - - updateRenderCostLimitFromSettings(); - + tunables.initialiseFromSettings(); t.detach(); } @@ -82,8 +146,7 @@ namespace FSPerfStats using ST = StatType_t; bool unreliable{false}; - static LLCachedControl smoothingPeriods(gSavedSettings, "FSPerfFloaterSmoothingPeriods"); - + FSPerfStats::StatsRecorder::getSceneStat(FSPerfStats::StatType_t::RENDER_FRAME); auto& sceneStats = statsDoubleBuffer[writeBuffer][static_cast(ObjType_t::OT_GENERAL)][LLUUID::null]; auto& lastStats = statsDoubleBuffer[writeBuffer ^ 1][static_cast(ObjType_t::OT_GENERAL)][LLUUID::null]; @@ -109,6 +172,7 @@ namespace FSPerfStats unreliable = true; lastStats[static_cast(StatType_t::RENDER_FPSLIMIT)] = sceneStats[static_cast(StatType_t::RENDER_FPSLIMIT)]; lastStats[static_cast(StatType_t::RENDER_SLEEP)] = sceneStats[static_cast(StatType_t::RENDER_SLEEP)]; + lastStats[static_cast(StatType_t::RENDER_FRAME)] = sceneStats[static_cast(StatType_t::RENDER_FRAME)]; // bring over the total frame render time to deal with region crossing overlap issues } if(!unreliable) @@ -119,7 +183,7 @@ namespace FSPerfStats { auto avg = lastStats[static_cast(statEntry)]; auto val = sceneStats[static_cast(statEntry)]; - sceneStats[static_cast(statEntry)] = avg + (val/smoothingPeriods) - (avg/smoothingPeriods); + sceneStats[static_cast(statEntry)] = avg + (val / SMOOTHING_PERIODS) - (avg / SMOOTHING_PERIODS); // LL_INFOS("scenestats") << "Scenestat: " << static_cast(statEntry) << " before=" << avg << " new=" << val << " newavg=" << statsDoubleBuffer[writeBuffer][static_cast(ObjType_t::OT_GENERAL)][LLUUID::null][static_cast(statEntry)] << LL_ENDL; } @@ -127,9 +191,9 @@ namespace FSPerfStats for(auto& stat_entry : statsMap) { auto val = stat_entry.second[static_cast(ST::RENDER_COMBINED)]; - if(val>smoothingPeriods){ + if(val > SMOOTHING_PERIODS){ auto avg = statsDoubleBuffer[writeBuffer ^ 1][static_cast(ObjType_t::OT_ATTACHMENT)][stat_entry.first][static_cast(ST::RENDER_COMBINED)]; - stat_entry.second[static_cast(ST::RENDER_COMBINED)] = avg + (val/smoothingPeriods) - (avg/smoothingPeriods); + stat_entry.second[static_cast(ST::RENDER_COMBINED)] = avg + (val / SMOOTHING_PERIODS) - (avg / SMOOTHING_PERIODS); } } @@ -140,10 +204,10 @@ namespace FSPerfStats for(auto& stat : avatarStatsToAvg) { auto val = stat_entry.second[static_cast(stat)]; - if(val>smoothingPeriods) + if(val > SMOOTHING_PERIODS) { auto avg = statsDoubleBuffer[writeBuffer ^ 1][static_cast(ObjType_t::OT_AVATAR)][stat_entry.first][static_cast(stat)]; - stat_entry.second[static_cast(stat)] = avg + (val/smoothingPeriods) - (avg/smoothingPeriods); + stat_entry.second[static_cast(stat)] = avg + (val / SMOOTHING_PERIODS) - (avg / SMOOTHING_PERIODS); } } } @@ -174,8 +238,8 @@ namespace FSPerfStats sum[writeBuffer][i].fill(0); } - // and now adjust the visuals. - if(autoTune) + // and now adjust the proxy vars so that the main thread can adjust the visuals. + if(tunables.userAutoTuneEnabled) { updateAvatarParams(); } @@ -228,37 +292,6 @@ namespace FSPerfStats } } - // static - void StatsRecorder::updateSettingsFromRenderCostLimit() - { - static LLCachedControl maxRenderCost_us(gSavedSettings, "FSRenderAvatarMaxART"); - if( (F32)maxRenderCost_us != log10( ( (F32)FSPerfStats::renderAvatarMaxART_ns )/1000 ) ) - { - if( FSPerfStats::renderAvatarMaxART_ns != 0 ) - { - gSavedSettings.setF32( "FSRenderAvatarMaxART", log10( ( (F32)FSPerfStats::renderAvatarMaxART_ns )/1000 ) ); - } - else - { - gSavedSettings.setF32( "FSRenderAvatarMaxART",log10( FSPerfStats::ART_UNLIMITED_NANOS/1000 ) ); - } - } - } - - // static - void StatsRecorder::updateRenderCostLimitFromSettings() - { - const auto newval = gSavedSettings.getF32("FSRenderAvatarMaxART"); - if(newval < log10(FSPerfStats::ART_UNLIMITED_NANOS/1000)) - { - FSPerfStats::renderAvatarMaxART_ns = pow(10,newval)*1000; - } - else - { - FSPerfStats::renderAvatarMaxART_ns = 0; - }; - } - //static int StatsRecorder::countNearbyAvatars(S32 distance) { @@ -273,22 +306,16 @@ namespace FSPerfStats // static void StatsRecorder::updateAvatarParams() { - static LLCachedControl drawDistance(gSavedSettings, "RenderFarClip"); - static LLCachedControl userMinDrawDistance(gSavedSettings, "FSAutoTuneRenderFarClipMin"); - static LLCachedControl userTargetDrawDistance(gSavedSettings, "FSAutoTuneRenderFarClipTarget"); - static LLCachedControl impostorDistance(gSavedSettings, "FSAutoTuneImpostorFarAwayDistance"); - static LLCachedControl impostorDistanceTuning(gSavedSettings, "FSAutoTuneImpostorByDistEnabled"); - static LLCachedControl maxNonImpostors (gSavedSettings, "IndirectMaxNonImpostors"); - if(impostorDistanceTuning) + if(tunables.userImpostorDistanceTuningEnabled) { // if we have less than the user's "max Non-Impostors" avatars within the desired range then adjust the limit. // also adjusts back up again for nearby crowds. - auto count = countNearbyAvatars(std::min(drawDistance, impostorDistance)); - if( count != maxNonImpostors ) + auto count = countNearbyAvatars(std::min(LLPipeline::RenderFarClip, tunables.userImpostorDistance)); + if( count != tunables.nonImpostors ) { - gSavedSettings.setU32("IndirectMaxNonImpostors", (count < LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER)?count : LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER); - LL_DEBUGS("AutoTune") << "There are " << count << "avatars within " << std::min(drawDistance, impostorDistance) << "m of the camera" << LL_ENDL; + tunables.updateNonImposters( (count < LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER)?count : LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER ); + LL_DEBUGS("AutoTune") << "There are " << count << "avatars within " << std::min(LLPipeline::RenderFarClip, tunables.userImpostorDistance) << "m of the camera" << LL_ENDL; } } @@ -315,71 +342,80 @@ namespace FSPerfStats } // The frametime budget we have based on the target FPS selected - auto target_frame_time_raw = (U64)llround((F64)LLTrace::BlockTimer::countsPerSecond()/(targetFPS==0?1:targetFPS)); + auto target_frame_time_raw = (U64)llround((F64)LLTrace::BlockTimer::countsPerSecond()/(tunables.userTargetFPS==0?1:tunables.userTargetFPS)); // LL_INFOS() << "Effective FPS(raw):" << tot_frame_time_raw << " Target:" << target_frame_time_raw << LL_ENDL; - + auto inferredFPS{1000/(U32)std::max(raw_to_ms(tot_frame_time_raw),1.0)}; + U32 settingsChangeFrequency{inferredFPS > 25?inferredFPS:25}; if( tot_limit_time_raw != 0) { // This could be problematic. tot_frame_time_raw -= tot_limit_time_raw; } - // 1) Is the target frame tim lower than current? + // 1) Is the target frame time lower than current? if( target_frame_time_raw <= tot_frame_time_raw ) { + if(belowTargetFPS == false) + { + // this is the first frame under. hold fire to add a little hysteresis + belowTargetFPS = true; + FSPerfStats::lastGlobalPrefChange = gFrameCount; + } // if so we've got work to do // how much of the frame was spent on non avatar related work? U32 non_avatar_time_raw = tot_frame_time_raw - tot_avatar_time_raw; - // If the target frame time < non avatar frame time thne adjusting avatars is only goin gto get us so far. + // If the target frame time < scene time (estimated as non_avatar time) U64 target_avatar_time_raw; if(target_frame_time_raw < non_avatar_time_raw) { // we cannnot do this by avatar adjustment alone. - if((gFrameCount - FSPerfStats::lastGlobalPrefChange) > 10) // give changes a short time to take effect. + if((gFrameCount - FSPerfStats::lastGlobalPrefChange) > settingsChangeFrequency) // give changes a short time to take effect. { - if(FSPerfStats::fpsTuningStrategy == 1) + if(tunables.userFPSTuningStrategy == TUNE_SCENE_AND_AVATARS) { // 1 - hack the water to opaque. all non opaque have a significant hit, this is a big boost for (arguably) a minor visual hit. - // the other reflection options make comparatively little change and iof this overshoots we'll be stepping back up later + // the other reflection options make comparatively little change and if this overshoots we'll be stepping back up later if(LLPipeline::RenderReflectionDetail != -2) { - gSavedSettings.setS32("RenderReflectionDetail", -2); + FSPerfStats::tunables.updateReflectionDetail(-2); FSPerfStats::lastGlobalPrefChange = gFrameCount; return; } - else // deliberately "else" here so we only do these in steps + else // deliberately "else" here so we only do one of these in any given frame { // step down the DD by 10m per update - auto new_dd = (drawDistance-10>userMinDrawDistance)?(drawDistance - 10) : userMinDrawDistance; - if(new_dd != drawDistance) + auto new_dd = (LLPipeline::RenderFarClip - DD_STEP > tunables.userMinDrawDistance)?(LLPipeline::RenderFarClip - DD_STEP) : tunables.userMinDrawDistance; + if(new_dd != LLPipeline::RenderFarClip) { - gSavedSettings.setF32("RenderFarClip", new_dd); + FSPerfStats::tunables.updateFarClip( new_dd ); FSPerfStats::lastGlobalPrefChange = gFrameCount; return; } } } + // if we reach here, we've no more changes to make to tune scenery so we'll resort to agressive Avatar tuning + // Note: moved from outside "if changefrequency elapsed" to stop fallthrough and allow scenery changes time to take effect. + target_avatar_time_raw = 0; + } + else + { + // we made a settings change recently so let's give it time. + return; } - target_avatar_time_raw = 0; } else { - // desired avatar budget. + // set desired avatar budget. target_avatar_time_raw = target_frame_time_raw - non_avatar_time_raw; } if( target_avatar_time_raw < tot_avatar_time_raw ) { // we need to spend less time drawing avatars to meet our budget - // Note: working in usecs now cos reasons. - auto new_render_limit_ns {renderAvatarMaxART_ns}; + auto new_render_limit_ns {FSPerfStats::raw_to_ns(av_render_max_raw)}; // max render this frame may be higher than the last (cos new entrants and jitter) so make sure we are heading in the right direction - if(FSPerfStats::raw_to_ns(av_render_max_raw) < renderAvatarMaxART_ns) - { - new_render_limit_ns = FSPerfStats::raw_to_ns(av_render_max_raw); - } - else + if( new_render_limit_ns > renderAvatarMaxART_ns ) { new_render_limit_ns = renderAvatarMaxART_ns; } @@ -389,31 +425,55 @@ namespace FSPerfStats new_render_limit_ns = std::max((U64)new_render_limit_ns, (U64)FSPerfStats::ART_MINIMUM_NANOS); // assign the new value - renderAvatarMaxART_ns = new_render_limit_ns; + if(renderAvatarMaxART_ns != new_render_limit_ns) + { + renderAvatarMaxART_ns = new_render_limit_ns; + tunables.updateSettingsFromRenderCostLimit(); + } // LL_DEBUGS() << "AUTO_TUNE: avatar_budget adjusted to:" << new_render_limit_ns << LL_ENDL; } // LL_DEBUGS() << "AUTO_TUNE: Target frame time:"<< FSPerfStats::raw_to_us(target_frame_time_raw) << "usecs (non_avatar is " << FSPerfStats::raw_to_us(non_avatar_time_raw) << "usecs) Max cost limited=" << renderAvatarMaxART_ns << LL_ENDL; } else if( FSPerfStats::raw_to_ns(target_frame_time_raw) > (FSPerfStats::raw_to_ns(tot_frame_time_raw) + renderAvatarMaxART_ns) ) { - if( FSPerfStats::tunedAvatars >= 0 ) + if(belowTargetFPS == true) { - // if we have more time to spare let's shift up little in the hope we'll restore an avatar. - renderAvatarMaxART_ns += FSPerfStats::ART_MIN_ADJUST_UP_NANOS; + // we reached target, force a pause + lastGlobalPrefChange = gFrameCount; + belowTargetFPS = false; } - if( drawDistance < userTargetDrawDistance ) + + // once we're over the FPS target we slow down further + if((gFrameCount - lastGlobalPrefChange) > settingsChangeFrequency*3) { - gSavedSettings.setF32("RenderFarClip", drawDistance + 10.); - } - if( (target_frame_time_raw * 1.5) > tot_frame_time_raw && - FSPerfStats::tunedAvatars == 0 && - drawDistance >= userTargetDrawDistance) - { - // if everything else is "max" and we have 50% headroom let's knock the water quality up a notch at a time. - auto water = gSavedSettings.getS32("RenderReflectionDetail"); - gSavedSettings.setS32("RenderReflectionDetail", water+1); + if(!tunables.userAutoTuneLock) + { + // we've reached the target and stayed long enough to consider stable. + // turn off if we are not locked. + tunables.updateUserAutoTuneEnabled(false); + } + if( FSPerfStats::tunedAvatars > 0 ) + { + // if we have more time to spare let's shift up little in the hope we'll restore an avatar. + renderAvatarMaxART_ns += FSPerfStats::ART_MIN_ADJUST_UP_NANOS; + tunables.updateSettingsFromRenderCostLimit(); + return; + } + if(tunables.userFPSTuningStrategy == TUNE_SCENE_AND_AVATARS) + { + if( LLPipeline::RenderFarClip < tunables.userTargetDrawDistance ) + { + FSPerfStats::tunables.updateFarClip( std::min(LLPipeline::RenderFarClip + DD_STEP, tunables.userTargetDrawDistance) ); + FSPerfStats::lastGlobalPrefChange = gFrameCount; + return; + } + if( (tot_frame_time_raw * 1.5) < target_frame_time_raw ) + { + // if everything else is "max" and we have >50% headroom let's knock the water quality up a notch at a time. + FSPerfStats::tunables.updateReflectionDetail( std::min(LLPipeline::RenderReflectionDetail + 1, tunables.userTargetReflections) ); + } + } } } - updateSettingsFromRenderCostLimit(); } } \ No newline at end of file diff --git a/indra/newview/fsperfstats.h b/indra/newview/fsperfstats.h index 49a7e8e6d1..ced3785342 100644 --- a/indra/newview/fsperfstats.h +++ b/indra/newview/fsperfstats.h @@ -64,20 +64,23 @@ namespace FSPerfStats extern std::atomic inUseAttachmentUnRigged; #endif // Note if changing these, they should correspond with the log range of the correpsonding sliders - constexpr U64 ART_UNLIMITED_NANOS{50000000}; - constexpr U64 ART_MINIMUM_NANOS{100000}; - constexpr U64 ART_MIN_ADJUST_UP_NANOS{10000}; - constexpr U64 ART_MIN_ADJUST_DOWN_NANOS{10000}; + static constexpr U64 ART_UNLIMITED_NANOS{50000000}; + static constexpr U64 ART_MINIMUM_NANOS{100000}; + static constexpr U64 ART_MIN_ADJUST_UP_NANOS{10000}; + static constexpr U64 ART_MIN_ADJUST_DOWN_NANOS{10000}; - constexpr F32 PREFERRED_DD{180}; + static constexpr F32 PREFERRED_DD{180}; + static constexpr U32 SMOOTHING_PERIODS{50}; + static constexpr U32 DD_STEP{10}; + + static constexpr U32 TUNE_AVATARS_ONLY{0}; + static constexpr U32 TUNE_SCENE_AND_AVATARS{1}; extern std::atomic tunedAvatars; - extern U32 targetFPS; // desired FPS - extern U64 renderAvatarMaxART_ns; - extern U32 fpsTuningStrategy; + extern std::atomic renderAvatarMaxART_ns; + extern bool belowTargetFPS; extern U32 lastGlobalPrefChange; extern std::mutex bufferToggleLock; - extern bool autoTune; enum class ObjType_t{ OT_GENERAL=0, // Also Unknown. Used for n/a type stats such as scenery @@ -116,6 +119,63 @@ namespace FSPerfStats bool isHUD; }; + struct Tunables + { + static constexpr U32 Nothing{0}; + static constexpr U32 NonImpostors{1}; + static constexpr U32 ReflectionDetail{2}; + static constexpr U32 FarClip{4}; + static constexpr U32 UserMinDrawDistance{8}; + static constexpr U32 UserTargetDrawDistance{16}; + static constexpr U32 UserImpostorDistance{32}; + static constexpr U32 UserImpostorDistanceTuningEnabled{64}; + static constexpr U32 UserFPSTuningStrategy{128}; + static constexpr U32 UserAutoTuneEnabled{256}; + static constexpr U32 UserTargetFPS{512}; + static constexpr U32 UserARTCutoff{1024}; + static constexpr U32 UserTargetReflections{2048}; + static constexpr U32 UserAutoTuneLock{4096}; + + U32 tuningFlag{0}; // bit mask for changed settings + + // proxy variables, used to pas the new value to be set via the mainthread + U32 nonImpostors{0}; + S32 reflectionDetail{0}; + F32 farClip{0.0}; + F32 userMinDrawDistance{0.0}; + F32 userTargetDrawDistance{0.0}; + F32 userImpostorDistance{0.0}; + bool userImpostorDistanceTuningEnabled{false}; + U32 userFPSTuningStrategy{0}; + bool userAutoTuneEnabled{false}; + bool userAutoTuneLock{true}; + U32 userTargetFPS{0}; + F32 userARTCutoffSliderValue{0}; + S32 userTargetReflections{0}; + + void updateNonImposters(U32 nv){nonImpostors=nv; tuningFlag |= NonImpostors;}; + void updateReflectionDetail(S32 nv){reflectionDetail=nv; tuningFlag |= ReflectionDetail;}; + void updateFarClip(F32 nv){farClip=nv; tuningFlag |= FarClip;}; + void updateUserMinDrawDistance(F32 nv){userMinDrawDistance=nv; tuningFlag |= UserMinDrawDistance;}; + void updateUserTargetDrawDistance(F32 nv){userTargetDrawDistance=nv; tuningFlag |= UserTargetDrawDistance;}; + void updateImposterDistance(F32 nv){userImpostorDistance=nv; tuningFlag |= UserImpostorDistance;}; + void updateImposterDistanceTuningEnabled(bool nv){userImpostorDistanceTuningEnabled=nv; tuningFlag |= UserImpostorDistanceTuningEnabled;}; + void updateUserFPSTuningStrategy(U32 nv){userFPSTuningStrategy=nv; tuningFlag |= UserFPSTuningStrategy;}; + void updateTargetFps(U32 nv){userTargetFPS=nv; tuningFlag |= UserTargetFPS;}; + void updateUserARTCutoffSlider(F32 nv){userARTCutoffSliderValue=nv; tuningFlag |= UserARTCutoff;}; + void updateUserAutoTuneEnabled(bool nv){userAutoTuneEnabled=nv; tuningFlag |= UserAutoTuneEnabled;}; + void updateUserAutoTuneLock(bool nv){userAutoTuneLock=nv; tuningFlag |= UserAutoTuneLock;}; + void updateUserTargetReflections(S32 nv){userTargetReflections=nv; tuningFlag |= UserTargetReflections;}; + + void resetChanges(){tuningFlag=Nothing;}; + void initialiseFromSettings(); + void updateRenderCostLimitFromSettings(); + void updateSettingsFromRenderCostLimit(); + void applyUpdates(); + }; + + extern Tunables tunables; + class StatsRecorder{ using Queue = moodycamel::BlockingConcurrentQueue; public: @@ -156,8 +216,6 @@ namespace FSPerfStats { return max[getReadBufferIndex()][static_cast(otype)][static_cast(type)]; } - static void updateSettingsFromRenderCostLimit(); - static void updateRenderCostLimitFromSettings(); static void updateAvatarParams(); private: StatsRecorder(); @@ -181,7 +239,6 @@ namespace FSPerfStats { FSZone; // LL_INFOS("perfstats") << "processing update:" << LL_ENDL; - using ST = StatType_t; // Note: nullptr is used as the key for global stats #ifdef FS_HAS_TELEMETRY_SUPPORT static char avstr[36]; diff --git a/indra/newview/installers/darwin/apple-notarize.sh b/indra/newview/installers/darwin/apple-notarize.sh index e019437924..d90772ec0e 100755 --- a/indra/newview/installers/darwin/apple-notarize.sh +++ b/indra/newview/installers/darwin/apple-notarize.sh @@ -5,12 +5,12 @@ if [[ $SKIP_NOTARIZATION == "true" ]]; then fi CONFIG_FILE="$build_secrets_checkout/code-signing-osx/notarize_creds.sh" -if [ -f "$CONFIG_FILE" ]; then - source $CONFIG_FILE +if [[ -f "$CONFIG_FILE" ]]; then + source "$CONFIG_FILE" app_file="$1" zip_file=${app_file/app/zip} ditto -c -k --keepParent "$app_file" "$zip_file" - if [ -f "$zip_file" ]; then + if [[ -f "$zip_file" ]]; then res=$(xcrun altool --notarize-app --primary-bundle-id "com.secondlife.viewer" \ --username $USERNAME \ --password $PASSWORD \ @@ -19,37 +19,39 @@ if [ -f "$CONFIG_FILE" ]; then echo $res requestUUID=$(echo $res | awk '/RequestUUID/ { print $NF; }') - echo "Apple Notarization RequestUUID: $requestUUID" - if [[ -n $requestUUID ]]; then - status="in progress" - while [[ "$status" == "in progress" ]]; do + in_progress=1 + while [[ $in_progress -eq 1 ]]; do sleep 30 - status=$(xcrun altool --notarization-info "$requestUUID" \ + res=$(xcrun altool --notarization-info "$requestUUID" \ --username $USERNAME \ - --password $PASSWORD 2>&1 \ - | awk -F ': ' '/Status:/ { print $2; }' ) - echo "$status" + --password $PASSWORD 2>&1) + if [[ $res != *"in progress"* ]]; then + in_progress=0 + fi + echo "." done # log results - xcrun altool --notarization-info "$requestUUID" \ - --username $USERNAME \ - --password $PASSWORD + echo $res #remove temporary file rm "$zip_file" - if [["$status" == "success"]]; then + if [[ $res == *"success"* ]]; then xcrun stapler staple "$app_file" - elif [["$status" == "invalid"]]; then + exit 0 + elif [[ $res == *"invalid"* ]]; then echo "Notarization error: failed to process the app file" exit 1 + else + echo "Notarization error: unknown response status" fi else echo "Notarization error: couldn't get request UUID" - echo $res exit 1 fi + else + echo "Notarization error: ditto failed" + exit 1 fi fi - diff --git a/indra/newview/installers/linux/appimage.sh b/indra/newview/installers/linux/appimage.sh new file mode 100755 index 0000000000..b06a096fa1 --- /dev/null +++ b/indra/newview/installers/linux/appimage.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +SCRIPT_PATH=`readlink -f $0` +SCRIPT_PATH=`dirname $SCRIPT_PATH` + +echo "Trying to build AppImage in directory $1 into file $3" + +# All hope is lost if there is no lsb_release command +command -v lsb_release >/dev/null 2>/dev/null || exit 0 + +if [ `lsb_release -is` != "Ubuntu" ] +then + echo "Distribution is not Ubuntu, skipping AppImage creation" + exit 0 +fi + +set -e + +cd $1 +pushd packaged + +wget -q https://github.com/AppImage/AppImages/raw/master/functions.sh -O ./functions.sh +. ./functions.sh +rm functions.sh + +cp firestorm AppRun +cp ${SCRIPT_PATH}/firestorm.desktop firestorm.desktop + +copy_deps +copy_deps +copy_deps +delete_blacklisted + +# Now copy everything to ./lib. The viewer binaries got build with a rpath pointing to ./lib so all so will be magically found there. +#find ./usr/lib/ -type f -print0 | xargs -0 -Ifile cp file ./lib/ +#find ./lib/x86_64-linux-gnu/ -type f -print0 | xargs -0 -Ifile cp file ./lib/ + +#rm -rf ./usr/lib/ +#rm -rf ./lib/x86_64-linux-gnu/ + +find . -empty -type d -delete + +popd + +wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" +chmod a+x appimagetool-x86_64.AppImage +./appimagetool-x86_64.AppImage --appimage-extract +rm appimagetool-x86_64.AppImage +ARCH=x86_64 squashfs-root/AppRun packaged + +if [ -f $2 ] +then + mv $2 $3 +fi diff --git a/indra/newview/installers/linux/firestorm.desktop b/indra/newview/installers/linux/firestorm.desktop new file mode 100644 index 0000000000..8604a208bc --- /dev/null +++ b/indra/newview/installers/linux/firestorm.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=Firestorm +Exec=firestorm +Comment=Client for virtual worlds +Icon=firestorm_icon +Categories=Development; +Terminal=true diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index 82bd7c12fd..83160b8453 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -660,10 +660,17 @@ Function CloseSecondLife LOOP: FindWindow $0 "Second Life" "" - IntCmp $0 0 DONE + IntCmp $0 0 SLEEP Sleep 500 Goto LOOP - + + SLEEP: + # Second life window just closed, but program might not be fully done yet + # and OS might have not released some locks, wait a bit more to make sure + # all file handles were released. + # If something still isn't unlocked, it will trigger a notification from + # RemoveProgFilesOnInst + Sleep 1000 DONE: Pop $0 Return @@ -704,6 +711,18 @@ FunctionEnd ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Function RemoveProgFilesOnInst +# We do not remove whole pervious install folder on install, since +# there is a chance that viewer was installed into some important +# folder by intent or accident +# RMDir /r $INSTDIR is especially unsafe if user installed somewhere +# like Program Files + +# Set retry counter. All integers are strings. +Push $0 +StrCpy $0 0 + +PREINSTALLREMOVE: + # Remove old SecondLife.exe to invalidate any old shortcuts to it that may be in non-standard locations. See MAINT-3575 # Remove VMP #Delete "$INSTDIR\$INSTEXE" @@ -712,13 +731,35 @@ Delete "$INSTDIR\$VIEWER_EXE" # Remove old shader files first so fallbacks will work. See DEV-5663 RMDir /r "$INSTDIR\app_settings\shaders" -# Remove skins folder to clean up files removed during development +# Remove folders to clean up files removed during development +RMDir /r "$INSTDIR\app_settings" RMDir /r "$INSTDIR\skins" +RMDir /r "$INSTDIR\vmp_icons" + +# Remove llplugin, plugins can crash or malfunction if they +# find modules from different versions +RMDir /r "$INSTDIR\llplugin" + +IntOp $0 $0 + 1 + +IfErrors 0 PREINSTALLDONE + IntCmp $0 1 PREINSTALLREMOVE #try again once + StrCmp $SKIP_DIALOGS "true" PREINSTALLDONE + MessageBox MB_ABORTRETRYIGNORE $(CloseSecondLifeInstRM) IDABORT PREINSTALLFAIL IDRETRY PREINSTALLREMOVE + # MB_ABORTRETRYIGNORE does not accept IDIGNORE + Goto PREINSTALLDONE + +PREINSTALLFAIL: + Quit + +PREINSTALLDONE: # We are no longer including release notes with the viewer, so remove them. ;Delete "$SMPROGRAMS\$INSTSHORTCUT\SL Release Notes.lnk" ;Delete "$INSTDIR\releasenotes.txt" +Pop $0 + FunctionEnd ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/indra/newview/installers/windows/lang_da.nsi b/indra/newview/installers/windows/lang_da.nsi index f803b509ee..0160af157e 100644 Binary files a/indra/newview/installers/windows/lang_da.nsi and b/indra/newview/installers/windows/lang_da.nsi differ diff --git a/indra/newview/installers/windows/lang_de.nsi b/indra/newview/installers/windows/lang_de.nsi index a49fb02737..d36873a462 100755 Binary files a/indra/newview/installers/windows/lang_de.nsi and b/indra/newview/installers/windows/lang_de.nsi differ diff --git a/indra/newview/installers/windows/lang_en-us.nsi b/indra/newview/installers/windows/lang_en-us.nsi index 17cd63d242..9eb0e4d618 100644 Binary files a/indra/newview/installers/windows/lang_en-us.nsi and b/indra/newview/installers/windows/lang_en-us.nsi differ diff --git a/indra/newview/installers/windows/lang_es.nsi b/indra/newview/installers/windows/lang_es.nsi index 9150cbf3fe..bfdbe6e9c7 100755 Binary files a/indra/newview/installers/windows/lang_es.nsi and b/indra/newview/installers/windows/lang_es.nsi differ diff --git a/indra/newview/installers/windows/lang_fr.nsi b/indra/newview/installers/windows/lang_fr.nsi index bbe204eda7..f3de969c98 100755 Binary files a/indra/newview/installers/windows/lang_fr.nsi and b/indra/newview/installers/windows/lang_fr.nsi differ diff --git a/indra/newview/installers/windows/lang_it.nsi b/indra/newview/installers/windows/lang_it.nsi index ad79c360f4..7388d62f49 100755 Binary files a/indra/newview/installers/windows/lang_it.nsi and b/indra/newview/installers/windows/lang_it.nsi differ diff --git a/indra/newview/installers/windows/lang_ja.nsi b/indra/newview/installers/windows/lang_ja.nsi index 8584327aca..3c01ecebc4 100755 Binary files a/indra/newview/installers/windows/lang_ja.nsi and b/indra/newview/installers/windows/lang_ja.nsi differ diff --git a/indra/newview/installers/windows/lang_pl.nsi b/indra/newview/installers/windows/lang_pl.nsi index 8779c30ac0..5cb00d6270 100644 Binary files a/indra/newview/installers/windows/lang_pl.nsi and b/indra/newview/installers/windows/lang_pl.nsi differ diff --git a/indra/newview/installers/windows/lang_pt-br.nsi b/indra/newview/installers/windows/lang_pt-br.nsi index aad994dfd7..f2ab414639 100755 Binary files a/indra/newview/installers/windows/lang_pt-br.nsi and b/indra/newview/installers/windows/lang_pt-br.nsi differ diff --git a/indra/newview/installers/windows/lang_ru.nsi b/indra/newview/installers/windows/lang_ru.nsi index b554270bd7..131b5377de 100755 Binary files a/indra/newview/installers/windows/lang_ru.nsi and b/indra/newview/installers/windows/lang_ru.nsi differ diff --git a/indra/newview/installers/windows/lang_tr.nsi b/indra/newview/installers/windows/lang_tr.nsi index 31e805a143..19006b9540 100755 Binary files a/indra/newview/installers/windows/lang_tr.nsi and b/indra/newview/installers/windows/lang_tr.nsi differ diff --git a/indra/newview/installers/windows/lang_zh.nsi b/indra/newview/installers/windows/lang_zh.nsi index 375803e51f..0c5ead4b1a 100755 Binary files a/indra/newview/installers/windows/lang_zh.nsi and b/indra/newview/installers/windows/lang_zh.nsi differ diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 007b957777..10217f4884 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -4678,16 +4678,6 @@ bool LLAgent::teleportCore(bool is_local) // hide the Region/Estate floater LLFloaterReg::hideInstance("region_info"); - // minimize the Search floater (STORM-1474) - { - LLFloater* instance = LLFloaterReg::getInstance("search"); - - if (instance && instance->getVisible()) - { - instance->setMinimized(TRUE); - } - } - LLViewerParcelMgr::getInstance()->deselectLand(); LLViewerMediaFocus::getInstance()->clearFocus(); diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index 551e480e7e..f586a64575 100644 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -535,6 +535,11 @@ void LLAppCoreHttp::refreshSettings(bool initial) LLCore::HttpStatus LLAppCoreHttp::sslVerify(const std::string &url, const LLCore::HttpHandler::ptr_t &handler, void *appdata) { + if (gDisconnected) + { + return LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); + } + LLCore::HttpStatus result; try { diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index d44a8b1919..2fad445380 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -146,10 +146,10 @@ #include "stringize.h" #include "llcoros.h" #include "llexception.h" -//#if !LL_LINUX #include "cef/dullahan_version.h" +#if !LL_LINUX #include "vlc/libvlc_version.h" -//#endif // LL_LINUX +#endif // LL_LINUX // Third party library includes #include @@ -1376,47 +1376,45 @@ bool LLAppViewer::init() gGLActive = FALSE; - // Disable updater -//#if LL_RELEASE_FOR_DOWNLOAD -// if (!gSavedSettings.getBOOL("CmdLineSkipUpdater")) -// { -// LLProcess::Params updater; -// updater.desc = "updater process"; -// // Because it's the updater, it MUST persist beyond the lifespan of the -// // viewer itself. -// updater.autokill = false; -// std::string updater_file; + // Disable updater +//#if LL_RELEASE_FOR_DOWNLOAD +// if (!gSavedSettings.getBOOL("CmdLineSkipUpdater")) +// { +// LLProcess::Params updater; +// updater.desc = "updater process"; +// // Because it's the updater, it MUST persist beyond the lifespan of the +// // viewer itself. +// updater.autokill = false; +// std::string updater_file; //#if LL_WINDOWS -// updater_file = "SLVersionChecker.exe"; -// updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, updater_file); +// updater_file = "SLVersionChecker.exe"; +// updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, updater_file); //#elif LL_DARWIN -// // explicitly run the system Python interpreter on SLVersionChecker.py -// updater.executable = "python"; -// updater_file = "SLVersionChecker.py"; -// updater.args.add(gDirUtilp->add(gDirUtilp->getAppRODataDir(), "updater", updater_file)); +// updater_file = "SLVersionChecker"; +// updater.executable = gDirUtilp->add(gDirUtilp->getAppRODataDir(), "updater", updater_file); //#else -// updater_file = "SLVersionChecker"; -// updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, updater_file); +// updater_file = "SLVersionChecker"; +// updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, updater_file); //#endif -// // add LEAP mode command-line argument to whichever of these we selected -// updater.args.add("leap"); -// // UpdaterServiceSettings -// if (gSavedSettings.getBOOL("FirstLoginThisInstall")) -// { -// // Befor first login, treat this as 'manual' updates, -// // updater won't install anything, but required updates -// updater.args.add("0"); -// } -// else -// { -// updater.args.add(stringize(gSavedSettings.getU32("UpdaterServiceSetting"))); -// } -// // channel -// updater.args.add(LLVersionInfo::instance().getChannel()); -// // testok -// updater.args.add(stringize(gSavedSettings.getBOOL("UpdaterWillingToTest"))); -// // ForceAddressSize -// updater.args.add(stringize(gSavedSettings.getU32("ForceAddressSize"))); +// // add LEAP mode command-line argument to whichever of these we selected +// updater.args.add("leap"); +// // UpdaterServiceSettings +// if (gSavedSettings.getBOOL("FirstLoginThisInstall")) +// { +// // Befor first login, treat this as 'manual' updates, +// // updater won't install anything, but required updates +// updater.args.add("0"); +// } +// else +// { +// updater.args.add(stringize(gSavedSettings.getU32("UpdaterServiceSetting"))); +// } +// // channel +// updater.args.add(LLVersionInfo::instance().getChannel()); +// // testok +// updater.args.add(stringize(gSavedSettings.getBOOL("UpdaterWillingToTest"))); +// // ForceAddressSize +// updater.args.add(stringize(gSavedSettings.getU32("ForceAddressSize"))); // // try // { @@ -1434,11 +1432,11 @@ bool LLAppViewer::init() // OSMB_OK); // mUpdaterNotFound = true; // } -// } -// else -// { -// LL_WARNS("InitInfo") << "Skipping updater check." << LL_ENDL; -// } +// } +// else +// { +// LL_WARNS("InitInfo") << "Skipping updater check." << LL_ENDL; +// } // // if (mUpdaterNotFound) // { @@ -1471,14 +1469,14 @@ bool LLAppViewer::init() // } // } // -// if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0) -// { -// LL_WARNS("InitInfo") << "QAModeEventHostPort DEPRECATED: " -// << "lleventhost no longer supported as a dynamic library" -// << LL_ENDL; -// } +// if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0) +// { +// LL_WARNS("InitInfo") << "QAModeEventHostPort DEPRECATED: " +// << "lleventhost no longer supported as a dynamic library" +// << LL_ENDL; +// } //#endif //LL_RELEASE_FOR_DOWNLOAD - // + // LLTextUtil::TextHelpers::iconCallbackCreationFunction = create_text_segment_icon_from_url_match; @@ -1644,6 +1642,12 @@ bool LLAppViewer::doFrame() { // Perfstats collection Frame boundary { + // and now adjust the visuals from previous frame. + if(FSPerfStats::tunables.userAutoTuneEnabled && FSPerfStats::tunables.tuningFlag != FSPerfStats::Tunables::Nothing) + { + FSPerfStats::tunables.applyUpdates(); + } + FSPerfStats::RecordSceneTime T (FSPerfStats::StatType_t::RENDER_FRAME); LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); @@ -1701,7 +1705,7 @@ bool LLAppViewer::doFrame() } // Check if we need to temporarily enable rendering. //F32 periodic_rendering = gSavedSettings.getF32("ForcePeriodicRenderingTime"); - static LLCachedControl periodic_rendering(gSavedSettings, "ForcePeriodicRenderingTime"); + static LLCachedControl periodic_rendering(gSavedSettings, "ForcePeriodicRenderingTime", -1.f); if (periodic_rendering > F_APPROXIMATELY_ZERO && periodicRenderingTimer.getElapsedTimeF64() > periodic_rendering) { periodicRenderingTimer.reset(); @@ -2069,12 +2073,14 @@ bool LLAppViewer::cleanup() // one because it happens just after mFastTimerLogThread is deleted. This // comment is in case we guessed wrong, so we can move it here instead. +#if LL_LINUX // remove any old breakpad minidump files from the log directory if (! isError()) { std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); gDirUtilp->deleteFilesInDir(logdir, "*-*-*-*-*.dmp"); } +#endif // Kill off LLLeap objects. We can find them all because LLLeap is derived // from LLInstanceTracker. @@ -2199,6 +2205,8 @@ bool LLAppViewer::cleanup() if (gAudiop) { + LL_INFOS() << "Shutting down audio" << LL_ENDL; + // be sure to stop the internet stream cleanly BEFORE destroying the interface to stop it. gAudiop->stopInternetStream(); // shut down the streaming audio sub-subsystem first, in case it relies on not outliving the general audio subsystem. @@ -2698,7 +2706,7 @@ void LLAppViewer::initLoggingAndGetLastDuration() // Remove the last ".old" log file. std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, - APP_NAME + ".log"); + APP_NAME + ".old"); LLFile::remove(old_log_file); // Get name of the log file @@ -3961,7 +3969,7 @@ LLSD LLAppViewer::getViewerInfo() const // info["LIBCEF_VERSION"] = "Undefined"; //#endif -//#if !LL_LINUX +#if !LL_LINUX std::ostringstream vlc_ver_codec; vlc_ver_codec << LIBVLC_VERSION_MAJOR; vlc_ver_codec << "."; @@ -3969,9 +3977,9 @@ LLSD LLAppViewer::getViewerInfo() const vlc_ver_codec << "."; vlc_ver_codec << LIBVLC_VERSION_REVISION; info["LIBVLC_VERSION"] = vlc_ver_codec.str(); -//#else -// info["LIBVLC_VERSION"] = "Undefined"; -//#endif +#else + info["LIBVLC_VERSION"] = "Using gstreamer 1.0"; +#endif S32 packets_in = LLViewerStats::instance().getRecording().getSum(LLStatViewer::PACKETS_IN); if (packets_in > 0) @@ -5189,120 +5197,6 @@ void LLAppViewer::addOnIdleCallback(const boost::function& cb) void LLAppViewer::loadKeyBindings() { std::string key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "key_bindings.xml"); -#if 1 - // Legacy support - // Remove #if-#endif section half a year after DRTVWR-501 releases. - // Mouse actions are part of keybinding file since DRTVWR-501 instead of being stored in - // settings.xml. To support legacy viewers that were storing in settings.xml we need to - // transfer old variables to new format. - // Also part of backward compatibility is present in LLKeyConflictHandler to modify - // legacy variables on changes in new system (to make sure we won't enforce - // legacy values again if user dropped to defaults in new system) - if (LLVersionInfo::getInstance()->getChannelAndVersion() != gLastRunVersion - || !gDirUtilp->fileExists(key_bindings_file)) // if file is missing, assume that there were no changes by user yet - { - // copy mouse actions and voice key changes to new file - LL_INFOS("InitInfo") << "Converting legacy mouse bindings to new format" << LL_ENDL; - // Load settings from file - LLKeyConflictHandler third_person_view(LLKeyConflictHandler::MODE_THIRD_PERSON); - LLKeyConflictHandler sitting_view(LLKeyConflictHandler::MODE_SITTING); - - // Since we are only modifying keybindings if personal file doesn't exist yet, - // it should be safe to just overwrite the value - // If key is already in use somewhere by default, LLKeyConflictHandler should resolve it. - BOOL value = gSavedSettings.getBOOL("DoubleClickAutoPilot"); - third_person_view.registerControl("walk_to", - 0, - value ? EMouseClickType::CLICK_DOUBLELEFT : EMouseClickType::CLICK_NONE, - KEY_NONE, - MASK_NONE, - value); - - U32 index = value ? 1 : 0; // we can store multiple combinations per action, so if first is in use by doubleclick, go to second - value = gSavedSettings.getBOOL("ClickToWalk"); - third_person_view.registerControl("walk_to", - index, - value ? EMouseClickType::CLICK_LEFT : EMouseClickType::CLICK_NONE, - KEY_NONE, - MASK_NONE, - value); - - value = gSavedSettings.getBOOL("DoubleClickTeleport"); - third_person_view.registerControl("teleport_to", - 0, - value ? EMouseClickType::CLICK_DOUBLELEFT : EMouseClickType::CLICK_NONE, - KEY_NONE, - MASK_NONE, - value); - - // sitting also supports teleport - sitting_view.registerControl("teleport_to", - 0, - value ? EMouseClickType::CLICK_DOUBLELEFT : EMouseClickType::CLICK_NONE, - KEY_NONE, - MASK_NONE, - value); - - std::string key_string = gSavedSettings.getString("PushToTalkButton"); - EMouseClickType mouse = EMouseClickType::CLICK_NONE; - KEY key = KEY_NONE; - if (key_string == "MiddleMouse") - { - mouse = EMouseClickType::CLICK_MIDDLE; - } - else if (key_string == "MouseButton4") - { - mouse = EMouseClickType::CLICK_BUTTON4; - } - else if (key_string == "MouseButton5") - { - mouse = EMouseClickType::CLICK_BUTTON5; - } - else - { - LLKeyboard::keyFromString(key_string, &key); - } - - value = gSavedSettings.getBOOL("PushToTalkToggle"); - std::string control_name = value ? "toggle_voice" : "voice_follow_key"; - third_person_view.registerControl(control_name, 0, mouse, key, MASK_NONE, true); - sitting_view.registerControl(control_name, 0, mouse, key, MASK_NONE, true); - - if (third_person_view.hasUnsavedChanges()) - { - // calls loadBindingsXML() - third_person_view.saveToSettings(); - } - - if (sitting_view.hasUnsavedChanges()) - { - // calls loadBindingsXML() - sitting_view.saveToSettings(); - } - - // in case of voice we need to repeat this in other modes - - for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) - { - // edit and first person modes; MODE_SAVED_SETTINGS not in use at the moment - if (i != LLKeyConflictHandler::MODE_THIRD_PERSON && i != LLKeyConflictHandler::MODE_SITTING) - { - LLKeyConflictHandler handler((LLKeyConflictHandler::ESourceMode)i); - - handler.registerControl(control_name, 0, mouse, key, MASK_NONE, true); - - if (handler.hasUnsavedChanges()) - { - // calls loadBindingsXML() - handler.saveToSettings(); - } - } - } - } - // since something might have gone wrong or there might have been nothing to save - // (and because otherwise following code will have to be encased in else{}), - // load everything one last time -#endif if (!gDirUtilp->fileExists(key_bindings_file) || !gViewerInput.loadBindingsXML(key_bindings_file)) { // Failed to load custom bindings, try default ones @@ -5645,6 +5539,10 @@ void LLAppViewer::idle() // // Special case idle if still starting up // + if (LLStartUp::getStartupState() >= STATE_WORLD_INIT) + { + update_texture_time(); + } if (LLStartUp::getStartupState() < STATE_STARTED) { // Skip rest if idle startup returns false (essentially, no world yet) diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index e961871908..99716e9ede 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -42,17 +42,37 @@ #include "json/reader.h" // To parse manifest.json from pepperflash -#if LL_DBUS_ENABLED -# include "llappviewerlinux_api_dbus.h" -// regrettable hacks to give us better runtime compatibility with older systems inside llappviewerlinux_api.h: -#define llg_return_if_fail(COND) do{if (!(COND)) return;}while(0) -#undef g_return_if_fail -#define g_return_if_fail(COND) llg_return_if_fail(COND) -// The generated API -# include "llappviewerlinux_api.h" +#include + +#if LL_SEND_CRASH_REPORTS +#include "breakpad/client/linux/handler/exception_handler.h" +#include "breakpad/common/linux/http_upload.h" +#include "lldir.h" +#include "../llcrashlogger/llcrashlogger.h" #endif +#include "fsversionvalues.h" + +#define VIEWERAPI_SERVICE "com.secondlife.ViewerAppAPIService" +#define VIEWERAPI_PATH "/com/secondlife/ViewerAppAPI" +#define VIEWERAPI_INTERFACE "com.secondlife.ViewerAppAPI" + +static const char * DBUS_SERVER = "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + +typedef struct +{ + GObject parent; +} ViewerAppAPI; + namespace { int gArgC = 0; @@ -120,6 +140,59 @@ LLAppViewerLinux::~LLAppViewerLinux() { } +#if LL_SEND_CRASH_REPORTS +std::string gCrashLogger; +std::string gVersion; +std::string gBugsplatDB; +std::string gCrashBehavior; + +static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded) +{ + if( fork() == 0 ) + execl( gCrashLogger.c_str(), gCrashLogger.c_str(), descriptor.path(), gVersion.c_str(), gBugsplatDB.c_str(), gCrashBehavior.c_str(), nullptr ); + return succeeded; +} + +void setupBreadpad() +{ + std::string build_data_fname(gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "build_data.json")); + gCrashLogger = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "linux-crash-logger.bin"); + + llifstream inf(build_data_fname.c_str()); + if(!inf.is_open()) + { + LL_WARNS("BUGSPLAT") << "Can't initialize BugSplat, can't read '" << build_data_fname << "'" << LL_ENDL; + return; + } + + Json::Reader reader; + Json::Value build_data; + if(!reader.parse(inf, build_data, false)) + { + LL_WARNS("BUGSPLAT") << "Can't initialize BugSplat, can't parse '" << build_data_fname << "': " + << reader.getFormatedErrorMessages() << LL_ENDL; + return; + } + + Json::Value BugSplat_DB = build_data["BugSplat DB"]; + if(!BugSplat_DB) + { + LL_WARNS("BUGSPLAT") << "Can't initialize BugSplat, no 'BugSplat DB' entry in '" << build_data_fname + << "'" << LL_ENDL; + return; + } + gVersion = STRINGIZE( + LL_VIEWER_VERSION_MAJOR << '.' << LL_VIEWER_VERSION_MINOR << '.' << LL_VIEWER_VERSION_PATCH + << '.' << LL_VIEWER_VERSION_BUILD); + gBugsplatDB = BugSplat_DB.asString(); + + LL_INFOS("BUGSPLAT") << "Initializing with crash logger: " << gCrashLogger << " database: " << gBugsplatDB << " version: " << gVersion << LL_ENDL; + + google_breakpad::MinidumpDescriptor *descriptor = new google_breakpad::MinidumpDescriptor(gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "")); + google_breakpad::ExceptionHandler *eh = new google_breakpad::ExceptionHandler(*descriptor, NULL, dumpCallback, NULL, true, -1); +} +#endif + bool LLAppViewerLinux::init() { // g_thread_init() must be called before *any* use of glib, *and* @@ -127,18 +200,20 @@ bool LLAppViewerLinux::init() // libraries likes to use glib functions; in short, do this here // really early in app startup! -#if ( !defined(GLIB_MAJOR_VERSION) && !defined(GLIB_MINOR_VERSION) ) || ( GLIB_MAJOR_VERSION < 2 ) || ( GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32 ) - if (!g_thread_supported ()) g_thread_init (NULL); -#endif - bool success = LLAppViewer::init(); #if LL_SEND_CRASH_REPORTS - if (success) - { - LLAppViewer* pApp = LLAppViewer::instance(); - pApp->initCrashReporting(); - } + S32 nCrashSubmitBehavior = gCrashSettings.getS32("CrashSubmitBehavior"); + + // For the first version we just consider always send and create a nice dialog for CRASH_BEHAVIOR_ASK later. + if (success && nCrashSubmitBehavior != CRASH_BEHAVIOR_NEVER_SEND ) + { + if( nCrashSubmitBehavior == CRASH_BEHAVIOR_ASK ) + gCrashBehavior = "ask"; + else + gCrashBehavior = "send"; + setupBreadpad(); + } #endif return success; @@ -152,40 +227,17 @@ bool LLAppViewerLinux::restoreErrorTrap() } ///////////////////////////////////////// -#if LL_DBUS_ENABLED +#if LL_GLIB typedef struct { GObjectClass parent_class; } ViewerAppAPIClass; -static void viewerappapi_init(ViewerAppAPI *server); -static void viewerappapi_class_init(ViewerAppAPIClass *klass); - /// -// regrettable hacks to give us better runtime compatibility with older systems in general -static GType llg_type_register_static_simple_ONCE(GType parent_type, - const gchar *type_name, - guint class_size, - GClassInitFunc class_init, - guint instance_size, - GInstanceInitFunc instance_init, - GTypeFlags flags) -{ - static GTypeInfo type_info; - memset(&type_info, 0, sizeof(type_info)); - - type_info.class_size = class_size; - type_info.class_init = class_init; - type_info.instance_size = instance_size; - type_info.instance_init = instance_init; - - return g_type_register_static(parent_type, type_name, &type_info, flags); -} -#define llg_intern_static_string(S) (S) -#define g_intern_static_string(S) llg_intern_static_string(S) -#define g_type_register_static_simple(parent_type, type_name, class_size, class_init, instance_size, instance_init, flags) llg_type_register_static_simple_ONCE(parent_type, type_name, class_size, class_init, instance_size, instance_init, flags) +static void viewerappapi_init(ViewerAppAPI *server); +static void viewerappapi_class_init(ViewerAppAPIClass *klass); G_DEFINE_TYPE(ViewerAppAPI, viewerappapi, G_TYPE_OBJECT); @@ -193,79 +245,71 @@ void viewerappapi_class_init(ViewerAppAPIClass *klass) { } -static bool dbus_server_init = false; - -void viewerappapi_init(ViewerAppAPI *server) +static void dispatchSLURL(gchar const *slurl) { - // Connect to the default DBUS, register our service/API. + LL_INFOS() << "Was asked to go to slurl: " << slurl << LL_ENDL; - if (!dbus_server_init) - { - GError *error = NULL; - - server->connection = lldbus_g_bus_get(DBUS_BUS_SESSION, &error); - if (server->connection) - { - lldbus_g_object_type_install_info(viewerappapi_get_type(), &dbus_glib_viewerapp_object_info); - - lldbus_g_connection_register_g_object(server->connection, VIEWERAPI_PATH, G_OBJECT(server)); - - DBusGProxy *serverproxy = lldbus_g_proxy_new_for_name(server->connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); - - guint request_name_ret_unused; - // akin to org_freedesktop_DBus_request_name - if (lldbus_g_proxy_call(serverproxy, "RequestName", &error, G_TYPE_STRING, VIEWERAPI_SERVICE, G_TYPE_UINT, 0, G_TYPE_INVALID, G_TYPE_UINT, &request_name_ret_unused, G_TYPE_INVALID)) - { - // total success. - dbus_server_init = true; - } - else - { - LL_WARNS() << "Unable to register service name: " << error->message << LL_ENDL; - } - - g_object_unref(serverproxy); - } - else - { - g_warning("Unable to connect to dbus: %s", error->message); - } - - if (error) - g_error_free(error); - } + std::string url = slurl; + LLMediaCtrl* web = NULL; + const bool trusted_browser = false; + LLURLDispatcher::dispatch(url, "", web, trusted_browser); } -// FIRE-5417; The xml manifest for dbus claims success_rtn is a boolean, not a boolean array -//gboolean viewer_app_api_GoSLURL(ViewerAppAPI *obj, gchar *slurl, gboolean **success_rtn, GError **error) -gboolean viewer_app_api_GoSLURL(ViewerAppAPI *obj, gchar *slurl, gboolean *success_rtn, GError **error) -// +static void DoMethodeCall (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) { - bool success = false; + LL_INFOS() << "DBUS message " << method_name << " from: " << sender << " interface: " << interface_name << LL_ENDL; + const gchar *slurl; - LL_INFOS() << "Was asked to go to slurl: " << slurl << LL_ENDL; + g_variant_get (parameters, "(&s)", &slurl); + dispatchSLURL(slurl); +} - std::string url = slurl; - LLMediaCtrl* web = NULL; - const bool trusted_browser = false; - if (LLURLDispatcher::dispatch(url, "", web, trusted_browser)) - { - // bring window to foreground, as it has just been "launched" from a URL - // todo: hmm, how to get there from here? - //xxx->mWindow->bringToFront(); - success = true; - } +GDBusNodeInfo *gBusNodeInfo = nullptr; +static const GDBusInterfaceVTable interface_vtable = + { + DoMethodeCall + }; +static void busAcquired(GDBusConnection *connection, const gchar *name, gpointer user_data) +{ + auto id = g_dbus_connection_register_object(connection, + VIEWERAPI_PATH, + gBusNodeInfo->interfaces[0], + &interface_vtable, + NULL, /* user_data */ + NULL, /* user_data_free_func */ + NULL); /* GError** */ + g_assert (id > 0); +} - // FIRE-5417; The xml manifest for dbus claims success_rtn is a boolean, not a boolean array +static void nameAcquired(GDBusConnection *connection, const gchar *name, gpointer user_data) +{ +} - // *success_rtn = g_new (gboolean, 1); - // (*success_rtn)[0] = (gboolean)success; +static void nameLost(GDBusConnection *connection, const gchar *name, gpointer user_data) +{ - *success_rtn = (gboolean)success; +} +void viewerappapi_init(ViewerAppAPI *server) +{ + gBusNodeInfo = g_dbus_node_info_new_for_xml (DBUS_SERVER, NULL); + g_assert (gBusNodeInfo != NULL); - // + g_bus_own_name(G_BUS_TYPE_SESSION, + VIEWERAPI_SERVICE, + G_BUS_NAME_OWNER_FLAGS_NONE, + busAcquired, + nameAcquired, + nameLost, + NULL, + NULL); - return TRUE; // the invokation succeeded, even if the actual dispatch didn't. } /// @@ -273,15 +317,6 @@ gboolean viewer_app_api_GoSLURL(ViewerAppAPI *obj, gchar *slurl, gboolean *succe //virtual bool LLAppViewerLinux::initSLURLHandler() { - if (!grab_dbus_syms(DBUSGLIB_DYLIB_DEFAULT_NAME)) - { - return false; // failed - } - -#if ( !defined(GLIB_MAJOR_VERSION) && !defined(GLIB_MINOR_VERSION) ) || ( GLIB_MAJOR_VERSION < 2 ) || ( GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 35 ) - g_type_init(); -#endif - //ViewerAppAPI *api_server = (ViewerAppAPI*) g_object_new(viewerappapi_get_type(), NULL); @@ -291,51 +326,49 @@ bool LLAppViewerLinux::initSLURLHandler() //virtual bool LLAppViewerLinux::sendURLToOtherInstance(const std::string& url) { - if (!grab_dbus_syms(DBUSGLIB_DYLIB_DEFAULT_NAME)) - { - return false; // failed - } + auto *pBus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, nullptr); - bool success = false; - DBusGConnection *bus; - GError *error = NULL; + if( !pBus ) + { + LL_WARNS() << "Getting dbus failed." << LL_ENDL; + return false; + } -#if ( !defined(GLIB_MAJOR_VERSION) && !defined(GLIB_MINOR_VERSION) ) || ( GLIB_MAJOR_VERSION < 2 ) || ( GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 35 ) - g_type_init(); -#endif - - bus = lldbus_g_bus_get (DBUS_BUS_SESSION, &error); - if (bus) - { - gboolean rtn = FALSE; - DBusGProxy *remote_object = - lldbus_g_proxy_new_for_name(bus, VIEWERAPI_SERVICE, VIEWERAPI_PATH, VIEWERAPI_INTERFACE); + auto pProxy = g_dbus_proxy_new_sync(pBus, G_DBUS_PROXY_FLAGS_NONE, nullptr, + VIEWERAPI_SERVICE, VIEWERAPI_PATH, + VIEWERAPI_INTERFACE, nullptr, nullptr); - if (lldbus_g_proxy_call(remote_object, "GoSLURL", &error, - G_TYPE_STRING, url.c_str(), G_TYPE_INVALID, - G_TYPE_BOOLEAN, &rtn, G_TYPE_INVALID)) - { - success = rtn; - } - else - { - LL_INFOS() << "Call-out to other instance failed (perhaps not running): " << error->message << LL_ENDL; - } + if( !pProxy ) + { + LL_WARNS() << "Cannot create new dbus proxy." << LL_ENDL; + g_object_unref( pBus ); + return false; + } - g_object_unref(G_OBJECT(remote_object)); - } - else - { - LL_WARNS() << "Couldn't connect to session bus: " << error->message << LL_ENDL; - } + auto *pArgs = g_variant_new( "(s)", url.c_str() ); + if( !pArgs ) + { + LL_WARNS() << "Cannot create new variant." << LL_ENDL; + g_object_unref( pBus ); + return false; + } - if (error) - g_error_free(error); - - return success; + auto pRes = g_dbus_proxy_call_sync(pProxy, + "GoSLURL", + pArgs, + G_DBUS_CALL_FLAGS_NONE, + -1, nullptr, nullptr); + + + + if( pRes ) + g_variant_unref( pRes ); + g_object_unref( pProxy ); + g_object_unref( pBus ); + return true; } -#else // LL_DBUS_ENABLED +#else // LL_GLIB bool LLAppViewerLinux::initSLURLHandler() { return false; // not implemented without dbus @@ -344,7 +377,7 @@ bool LLAppViewerLinux::sendURLToOtherInstance(const std::string& url) { return false; // not implemented without dbus } -#endif // LL_DBUS_ENABLED +#endif // LL_GLIB void LLAppViewerLinux::initCrashReporting(bool reportFreeze) { diff --git a/indra/newview/llappviewerlinux.h b/indra/newview/llappviewerlinux.h index ed71ff36fd..abe62c9289 100644 --- a/indra/newview/llappviewerlinux.h +++ b/indra/newview/llappviewerlinux.h @@ -27,17 +27,6 @@ #ifndef LL_LLAPPVIEWERLINUX_H #define LL_LLAPPVIEWERLINUX_H -extern "C" { -# include -} - -#if LL_DBUS_ENABLED -extern "C" { -# include -# include -} -#endif - #ifndef LL_LLAPPVIEWER_H #include "llappviewer.h" #endif @@ -70,26 +59,4 @@ protected: virtual bool sendURLToOtherInstance(const std::string& url); }; -#if LL_DBUS_ENABLED -typedef struct -{ - GObject parent; - DBusGConnection *connection; -} ViewerAppAPI; - -extern "C" { - // FIRE-5417; The xml manifest for dbus claims success_rtn is a boolean, not a boolean array - - // gboolean viewer_app_api_GoSLURL(ViewerAppAPI *obj, gchar *slurl, gboolean **success_rtn, GError **error); - gboolean viewer_app_api_GoSLURL(ViewerAppAPI *obj, gchar *slurl, gboolean *success_rtn, GError **error); - - // -} - -#define VIEWERAPI_SERVICE "com.secondlife.ViewerAppAPIService" -#define VIEWERAPI_PATH "/com/secondlife/ViewerAppAPI" -#define VIEWERAPI_INTERFACE "com.secondlife.ViewerAppAPI" - -#endif // LL_DBUS_ENABLED - #endif // LL_LLAPPVIEWERLINUX_H diff --git a/indra/newview/llappviewerlinux_api.h b/indra/newview/llappviewerlinux_api.h deleted file mode 100644 index 5d5fcaa3d6..0000000000 --- a/indra/newview/llappviewerlinux_api.h +++ /dev/null @@ -1,143 +0,0 @@ -/* Generated by dbus-binding-tool; do not edit! */ -/** - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef __dbus_glib_marshal_viewerapp_MARSHAL_H__ -#define __dbus_glib_marshal_viewerapp_MARSHAL_H__ - -#include - -G_BEGIN_DECLS - -#ifdef G_ENABLE_DEBUG -#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) -#define g_marshal_value_peek_char(v) g_value_get_char (v) -#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) -#define g_marshal_value_peek_int(v) g_value_get_int (v) -#define g_marshal_value_peek_uint(v) g_value_get_uint (v) -#define g_marshal_value_peek_long(v) g_value_get_long (v) -#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) -#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) -#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) -#define g_marshal_value_peek_enum(v) g_value_get_enum (v) -#define g_marshal_value_peek_flags(v) g_value_get_flags (v) -#define g_marshal_value_peek_float(v) g_value_get_float (v) -#define g_marshal_value_peek_double(v) g_value_get_double (v) -#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) -#define g_marshal_value_peek_param(v) g_value_get_param (v) -#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) -#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) -#define g_marshal_value_peek_object(v) g_value_get_object (v) -#else /* !G_ENABLE_DEBUG */ -/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. - * Do not access GValues directly in your code. Instead, use the - * g_value_get_*() functions - */ -#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int -#define g_marshal_value_peek_char(v) (v)->data[0].v_int -#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint -#define g_marshal_value_peek_int(v) (v)->data[0].v_int -#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint -#define g_marshal_value_peek_long(v) (v)->data[0].v_long -#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong -#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 -#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 -#define g_marshal_value_peek_enum(v) (v)->data[0].v_long -#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong -#define g_marshal_value_peek_float(v) (v)->data[0].v_float -#define g_marshal_value_peek_double(v) (v)->data[0].v_double -#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer -#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer -#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer -#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer -#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer -#endif /* !G_ENABLE_DEBUG */ - - -/* BOOLEAN:STRING,POINTER,POINTER (/tmp/dbus-binding-tool-c-marshallers.5XXD8T:1) */ -extern void dbus_glib_marshal_viewerapp_BOOLEAN__STRING_POINTER_POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); -void -dbus_glib_marshal_viewerapp_BOOLEAN__STRING_POINTER_POINTER (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - typedef gboolean (*GMarshalFunc_BOOLEAN__STRING_POINTER_POINTER) (gpointer data1, - gpointer arg_1, - gpointer arg_2, - gpointer arg_3, - gpointer data2); - register GMarshalFunc_BOOLEAN__STRING_POINTER_POINTER callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - gboolean v_return; - - g_return_if_fail (return_value != NULL); - g_return_if_fail (n_param_values == 4); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_BOOLEAN__STRING_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback); - - v_return = callback (data1, - g_marshal_value_peek_string (param_values + 1), - g_marshal_value_peek_pointer (param_values + 2), - g_marshal_value_peek_pointer (param_values + 3), - data2); - - g_value_set_boolean (return_value, v_return); -} - -G_END_DECLS - -#endif /* __dbus_glib_marshal_viewerapp_MARSHAL_H__ */ - -#include -static const DBusGMethodInfo dbus_glib_viewerapp_methods[] = { - { (GCallback) viewer_app_api_GoSLURL, dbus_glib_marshal_viewerapp_BOOLEAN__STRING_POINTER_POINTER, 0 }, -}; - -const DBusGObjectInfo dbus_glib_viewerapp_object_info = { - 0, - dbus_glib_viewerapp_methods, - 1, -"com.secondlife.ViewerAppAPI\0GoSLURL\0S\0slurl\0I\0s\0success_ret\0O\0F\0N\0b\0\0\0", -"\0", -"\0" -}; - diff --git a/indra/newview/llappviewerlinux_api.xml b/indra/newview/llappviewerlinux_api.xml deleted file mode 100644 index fac35b7adc..0000000000 --- a/indra/newview/llappviewerlinux_api.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/indra/newview/llappviewerlinux_api_dbus.cpp b/indra/newview/llappviewerlinux_api_dbus.cpp deleted file mode 100644 index 6ac30bd9b8..0000000000 --- a/indra/newview/llappviewerlinux_api_dbus.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/** - * @file llappviewerlinux_api_dbus.cpp - * @brief dynamic DBus symbol-grabbing code - * - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#if LL_DBUS_ENABLED - -#include "linden_common.h" - -extern "C" { -#include - -#include "apr_pools.h" -#include "apr_dso.h" -} - -#define DEBUGMSG(...) do { LL_DEBUGS() << llformat(__VA_ARGS__) << LL_ENDL; } while(0) -#define INFOMSG(...) do { LL_INFOS() << llformat(__VA_ARGS__) << LL_ENDL; } while(0) -#define WARNMSG(...) do { LL_WARNS() << llformat(__VA_ARGS__) << LL_ENDL; } while(0) - -#define LL_DBUS_SYM(REQUIRED, DBUSSYM, RTN, ...) RTN (*ll##DBUSSYM)(__VA_ARGS__) = NULL -#include "llappviewerlinux_api_dbus_syms_raw.inc" -#undef LL_DBUS_SYM - -static bool sSymsGrabbed = false; -static apr_pool_t *sSymDBUSDSOMemoryPool = NULL; -static apr_dso_handle_t *sSymDBUSDSOHandleG = NULL; - -bool grab_dbus_syms(std::string dbus_dso_name) -{ - if (sSymsGrabbed) - { - // already have grabbed good syms - return TRUE; - } - - bool sym_error = false; - bool rtn = false; - apr_status_t rv; - apr_dso_handle_t *sSymDBUSDSOHandle = NULL; - -#define LL_DBUS_SYM(REQUIRED, DBUSSYM, RTN, ...) do{rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll##DBUSSYM, sSymDBUSDSOHandle, #DBUSSYM); if (rv != APR_SUCCESS) {INFOMSG("Failed to grab symbol: %s", #DBUSSYM); if (REQUIRED) sym_error = true;} else DEBUGMSG("grabbed symbol: %s from %p", #DBUSSYM, (void*)ll##DBUSSYM);}while(0) - - //attempt to load the shared library - apr_pool_create(&sSymDBUSDSOMemoryPool, NULL); - - if ( APR_SUCCESS == (rv = apr_dso_load(&sSymDBUSDSOHandle, - dbus_dso_name.c_str(), - sSymDBUSDSOMemoryPool) )) - { - INFOMSG("Found DSO: %s", dbus_dso_name.c_str()); - -#include "llappviewerlinux_api_dbus_syms_raw.inc" - - if ( sSymDBUSDSOHandle ) - { - sSymDBUSDSOHandleG = sSymDBUSDSOHandle; - sSymDBUSDSOHandle = NULL; - } - - rtn = !sym_error; - } - else - { - INFOMSG("Couldn't load DSO: %s", dbus_dso_name.c_str()); - rtn = false; // failure - } - - if (sym_error) - { - WARNMSG("Failed to find necessary symbols in DBUS-GLIB libraries."); - } -#undef LL_DBUS_SYM - - sSymsGrabbed = rtn; - return rtn; -} - - -void ungrab_dbus_syms() -{ - // should be safe to call regardless of whether we've - // actually grabbed syms. - - if ( sSymDBUSDSOHandleG ) - { - apr_dso_unload(sSymDBUSDSOHandleG); - sSymDBUSDSOHandleG = NULL; - } - - if ( sSymDBUSDSOMemoryPool ) - { - apr_pool_destroy(sSymDBUSDSOMemoryPool); - sSymDBUSDSOMemoryPool = NULL; - } - - // NULL-out all of the symbols we'd grabbed -#define LL_DBUS_SYM(REQUIRED, DBUSSYM, RTN, ...) do{ll##DBUSSYM = NULL;}while(0) -#include "llappviewerlinux_api_dbus_syms_raw.inc" -#undef LL_DBUS_SYM - - sSymsGrabbed = false; -} - -#endif // LL_DBUS_ENABLED diff --git a/indra/newview/llappviewerlinux_api_dbus_syms_raw.inc b/indra/newview/llappviewerlinux_api_dbus_syms_raw.inc deleted file mode 100644 index c0548e2fba..0000000000 --- a/indra/newview/llappviewerlinux_api_dbus_syms_raw.inc +++ /dev/null @@ -1,9 +0,0 @@ - -// required symbols to grab -LL_DBUS_SYM(true, dbus_g_bus_get, DBusGConnection*, DBusBusType, GError**); -LL_DBUS_SYM(true, dbus_g_proxy_new_for_name, DBusGProxy*, DBusGConnection*, const char *, const char*, const char*); -LL_DBUS_SYM(true, dbus_g_proxy_call, gboolean, DBusGProxy*, const char*, GError**, GType, ...); -LL_DBUS_SYM(true, dbus_g_object_type_install_info, void, GType, const DBusGObjectInfo*); -LL_DBUS_SYM(true, dbus_g_connection_register_g_object, void, DBusGConnection*, const char*, GObject*); - -// optional symbols to grab diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp index 22f815dccb..e80bc3fe7a 100644 --- a/indra/newview/llattachmentsmgr.cpp +++ b/indra/newview/llattachmentsmgr.cpp @@ -154,22 +154,22 @@ void LLAttachmentsMgr::onIdle() return; } - if (LLApp::isExiting()) - { - return; - } + if (LLApp::isExiting()) + { + return; + } requestPendingAttachments(); - linkRecentlyArrivedAttachments(); + linkRecentlyArrivedAttachments(); - expireOldAttachmentRequests(); + expireOldAttachmentRequests(); - expireOldDetachRequests(); + expireOldDetachRequests(); -// checkInvalidCOFLinks(); - - spamStatusInfo(); + //checkInvalidCOFLinks(); + + spamStatusInfo(); } void LLAttachmentsMgr::requestPendingAttachments() @@ -581,51 +581,55 @@ bool LLAttachmentsMgr::isAttachmentStateComplete() const // //void LLAttachmentsMgr::checkInvalidCOFLinks() //{ -// LLInventoryModel::cat_array_t cat_array; -// LLInventoryModel::item_array_t item_array; -// gInventory.collectDescendents(LLAppearanceMgr::instance().getCOF(), -// cat_array,item_array,LLInventoryModel::EXCLUDE_TRASH); -// for (S32 i=0; igetLinkedUUID(); -// if (inv_item->getType() == LLAssetType::AT_OBJECT) -// { -// LLTimer timer; -// bool is_flagged_questionable = mQuestionableCOFLinks.getTime(item_id,timer); -// bool is_wearing_attachment = isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item_id); -// if (is_wearing_attachment && is_flagged_questionable) -// { -// LL_DEBUGS("Avatar") << "ATT was flagged questionable but is now " -// << (is_wearing_attachment ? "attached " : "") -// <<"removing flag after " -// << timer.getElapsedTimeF32() << " item " -// << inv_item->getName() << " id " << item_id << LL_ENDL; -// mQuestionableCOFLinks.removeTime(item_id); -// } -// } -// } -// -// for(LLItemRequestTimes::iterator it = mQuestionableCOFLinks.begin(); -// it != mQuestionableCOFLinks.end(); ) -// { -// LLItemRequestTimes::iterator curr_it = it; -// ++it; -// const LLUUID& item_id = curr_it->first; -// LLViewerInventoryItem *inv_item = gInventory.getItem(item_id); -// if (curr_it->second.getElapsedTimeF32() > MAX_BAD_COF_TIME) -// { -// if (LLAppearanceMgr::instance().isLinkedInCOF(item_id)) -// { -// LL_DEBUGS("Avatar") << "ATT Linked in COF but not attached or requested, deleting link after " -// << curr_it->second.getElapsedTimeF32() << " seconds for " -// << (inv_item ? inv_item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL; -// LLAppearanceMgr::instance().removeCOFItemLinks(item_id); -// } -// mQuestionableCOFLinks.erase(curr_it); -// continue; -// } -// } +// if (!gInventory.isInventoryUsable()) +// { +// return; +// } +// LLInventoryModel::cat_array_t cat_array; +// LLInventoryModel::item_array_t item_array; +// gInventory.collectDescendents(LLAppearanceMgr::instance().getCOF(), +// cat_array,item_array,LLInventoryModel::EXCLUDE_TRASH); +// for (S32 i=0; igetLinkedUUID(); +// if (inv_item->getType() == LLAssetType::AT_OBJECT) +// { +// LLTimer timer; +// bool is_flagged_questionable = mQuestionableCOFLinks.getTime(item_id,timer); +// bool is_wearing_attachment = isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item_id); +// if (is_wearing_attachment && is_flagged_questionable) +// { +// LL_DEBUGS("Avatar") << "ATT was flagged questionable but is now " +// << (is_wearing_attachment ? "attached " : "") +// <<"removing flag after " +// << timer.getElapsedTimeF32() << " item " +// << inv_item->getName() << " id " << item_id << LL_ENDL; +// mQuestionableCOFLinks.removeTime(item_id); +// } +// } +// } + +// for(LLItemRequestTimes::iterator it = mQuestionableCOFLinks.begin(); +// it != mQuestionableCOFLinks.end(); ) +// { +// LLItemRequestTimes::iterator curr_it = it; +// ++it; +// const LLUUID& item_id = curr_it->first; +// LLViewerInventoryItem *inv_item = gInventory.getItem(item_id); +// if (curr_it->second.getElapsedTimeF32() > MAX_BAD_COF_TIME) +// { +// if (LLAppearanceMgr::instance().isLinkedInCOF(item_id)) +// { +// LL_DEBUGS("Avatar") << "ATT Linked in COF but not attached or requested, deleting link after " +// << curr_it->second.getElapsedTimeF32() << " seconds for " +// << (inv_item ? inv_item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL; +// LLAppearanceMgr::instance().removeCOFItemLinks(item_id); +// } +// mQuestionableCOFLinks.erase(curr_it); +// continue; +// } +// } //} void LLAttachmentsMgr::spamStatusInfo() diff --git a/indra/newview/llbrowsernotification.cpp b/indra/newview/llbrowsernotification.cpp index 0460bff1b4..7614624306 100644 --- a/indra/newview/llbrowsernotification.cpp +++ b/indra/newview/llbrowsernotification.cpp @@ -40,7 +40,7 @@ LLBrowserNotification::LLBrowserNotification() { } -bool LLBrowserNotification::processNotification(const LLNotificationPtr& notification) +bool LLBrowserNotification::processNotification(const LLNotificationPtr& notification, bool should_log) { LLUUID media_id = notification->getPayload()["media_id"].asUUID(); LLMediaCtrl* media_instance = LLMediaCtrl::getInstance(media_id); diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp index 4c7e1a3803..feae95cf76 100644 --- a/indra/newview/llcallingcard.cpp +++ b/indra/newview/llcallingcard.cpp @@ -881,6 +881,7 @@ static void on_avatar_name_cache_notify(const LLUUID& agent_id, } else { + args["ONLINE_STATUS"] = true; LLNotificationsUI::LLNotificationManager::instance().onChat(chat, args); } diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h index 3ea58b3067..598678f6ea 100644 --- a/indra/newview/llchiclet.h +++ b/indra/newview/llchiclet.h @@ -911,6 +911,7 @@ protected: static bool filterNotification(LLNotificationPtr notify); // connect counter updaters to the corresponding signals /*virtual*/ void onAdd(LLNotificationPtr p) { mChiclet->setCounter(++mChiclet->mUreadSystemNotifications); } + /*virtual*/ void onLoad(LLNotificationPtr p) { mChiclet->setCounter(++mChiclet->mUreadSystemNotifications); } /*virtual*/ void onDelete(LLNotificationPtr p) { mChiclet->setCounter(--mChiclet->mUreadSystemNotifications); } LLNotificationChiclet* const mChiclet; diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index d0fc9766c5..e31f731fde 100644 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -91,6 +91,7 @@ LLConversationViewSession::LLConversationViewSession(const LLConversationViewSes mFlashStarted(false) { mFlashTimer = new LLFlashTimer(); + mAreChildrenInited = true; // inventory only } LLConversationViewSession::~LLConversationViewSession() diff --git a/indra/newview/lldirpicker.cpp b/indra/newview/lldirpicker.cpp index 01790ad19e..12dbc53f60 100644 --- a/indra/newview/lldirpicker.cpp +++ b/indra/newview/lldirpicker.cpp @@ -41,6 +41,11 @@ # include "llfilepicker.h" #endif +#ifdef LL_FLTK + #include "FL/Fl.H" + #include "FL/Fl_Native_File_Chooser.H" +#endif + // // Globals // @@ -193,32 +198,41 @@ LLDirPicker::LLDirPicker() : mFileName(NULL), mLocked(false) { +#ifndef LL_FLTK mFilePicker = new LLFilePicker(); +#endif + reset(); } LLDirPicker::~LLDirPicker() { +#ifndef LL_FLTK delete mFilePicker; +#endif } void LLDirPicker::reset() { +#ifndef LL_FLTK if (mFilePicker) - mFilePicker->reset(); + mFilePicker->reset(); +#else + mDir = ""; +#endif } BOOL LLDirPicker::getDir(std::string* filename, bool blocking) { reset(); - // if local file browsing is turned off, return without opening dialog if ( check_local_file_access_enabled() == false ) { return FALSE; } +#ifndef LL_FLTK #if !LL_MESA_HEADLESS if (mFilePicker) @@ -237,15 +251,42 @@ BOOL LLDirPicker::getDir(std::string* filename, bool blocking) #endif // !LL_MESA_HEADLESS return FALSE; +#else + gViewerWindow->getWindow()->beforeDialog(); + Fl_Native_File_Chooser flDlg; + flDlg.title(LLTrans::getString("choose_the_directory").c_str()); + flDlg.type(Fl_Native_File_Chooser::BROWSE_DIRECTORY ); + + int res = flDlg.show(); + + gViewerWindow->getWindow()->afterDialog(); + + if( res == 0 ) + { + char const *pDir = flDlg.filename(0); + if( pDir ) + mDir = pDir; + } + else if( res == -1 ) + { + LL_WARNS() << "FLTK failed: " << flDlg.errmsg() << LL_ENDL; + } + + return !mDir.empty(); +#endif } std::string LLDirPicker::getDirName() { +#ifndef LL_FLTK if (mFilePicker) { return mFilePicker->getFirstFile(); } return ""; +#else + return mDir; +#endif } #else // not implemented diff --git a/indra/newview/lldirpicker.h b/indra/newview/lldirpicker.h index 52febe4523..3406488494 100644 --- a/indra/newview/lldirpicker.h +++ b/indra/newview/lldirpicker.h @@ -79,9 +79,12 @@ private: bool check_local_file_access_enabled(); #if LL_LINUX || LL_DARWIN - // On Linux we just implement LLDirPicker on top of LLFilePicker + // On Linux we just implement LLDirPicker on top of LLFilePicker + // Seems like on OSX too ... Unless FLTK is used, then nope +#ifndef LL_FLTK LLFilePicker *mFilePicker; #endif +#endif std::string* mFileName; diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index d7ebf28354..f65ef398f4 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -54,6 +54,13 @@ #include "llcontrolavatar.h" #include "lldrawpoolavatar.h" +// Tentatively ignoring mismatched new/delete from the MemTrackableNonVirtual. I think according to the docs +// they are properly matched +#if LL_LINUX +#pragma GCC diagnostic ignored "-Wmismatched-new-delete" +#endif +// + const F32 MIN_INTERPOLATE_DISTANCE_SQUARED = 0.001f * 0.001f; const F32 MAX_INTERPOLATE_DISTANCE_SQUARED = 10.f * 10.f; const F32 OBJECT_DAMPING_TIME_CONSTANT = 0.06f; @@ -973,7 +980,7 @@ void LLDrawable::updateTexture() BOOL LLDrawable::updateGeometry(BOOL priority) { llassert(mVObjp.notNull()); - BOOL res = mVObjp->updateGeometry(this); + BOOL res = mVObjp && mVObjp->updateGeometry(this); return res; } diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 31d4c3c94e..527e9c6eeb 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -585,15 +585,12 @@ void LLDrawPoolAvatar::renderShadow(S32 pass) LLVOAvatar::AvatarOverallAppearance oa = avatarp->getOverallAppearance(); BOOL impostor = !LLPipeline::sImpostorRender && avatarp->isImpostor(); - // plain old impostors are passing through the shadow pipeline - // if (oa == LLVOAvatar::AOA_INVISIBLE || - // (impostor && oa == LLVOAvatar::AOA_JELLYDOLL)) - // Note: Impostors should not cast shadows, also all JDs are impostor nowadays so we do not need the extra check at all. - // also no shadows if the shadows are causing this avatar to breach the limit. - if ( avatarp->isTooSlowWithShadows() || impostor || (oa == LLVOAvatar::AOA_INVISIBLE) ) + // Performance floater + //if (impostor || (oa == LLVOAvatar::AOA_INVISIBLE)) + if (avatarp->isTooSlowWithShadows() || impostor || (oa == LLVOAvatar::AOA_INVISIBLE)) // { - // No shadows for jellydolled or invisible avs. + // No shadows for impostored (including jellydolled) or invisible avs. return; } diff --git a/indra/newview/lleventnotifier.cpp b/indra/newview/lleventnotifier.cpp index 0f4cecacb2..926b988f3c 100644 --- a/indra/newview/lleventnotifier.cpp +++ b/indra/newview/lleventnotifier.cpp @@ -36,6 +36,7 @@ #include "llfloaterevent.h" #include "llagent.h" #include "llcommandhandler.h" // secondlife:///app/... support +#include "lltrans.h" // FIRE-6310 - Legacy Search #include "fsfloatersearch.h" #include "llviewerfloaterreg.h" @@ -269,8 +270,40 @@ void LLEventNotifier::load(const LLSD& event_options) end = event_options.endArray(); resp_it != end; ++resp_it) { LLSD response = *resp_it; + LLDate date; + bool is_iso8601_date = false; - add(response["event_id"].asInteger(), response["event_date_ut"], response["event_date"].asString(), response["event_name"].asString()); + if (response["event_date"].isDate()) + { + date = response["event_date"].asDate(); + is_iso8601_date = true; + } + else if (date.fromString(response["event_date"].asString())) + { + is_iso8601_date = true; + } + + if (is_iso8601_date) + { + std::string dateStr; + + dateStr = "[" + LLTrans::getString("LTimeYear") + "]-[" + + LLTrans::getString("LTimeMthNum") + "]-[" + + LLTrans::getString("LTimeDay") + "] [" + + LLTrans::getString("LTimeHour") + "]:[" + + LLTrans::getString("LTimeMin") + "]:[" + + LLTrans::getString("LTimeSec") + "]"; + + LLSD substitution; + substitution["datetime"] = date; + LLStringUtil::format(dateStr, substitution); + + add(response["event_id"].asInteger(), response["event_date_ut"], dateStr, response["event_name"].asString()); + } + else + { + add(response["event_id"].asInteger(), response["event_date_ut"], response["event_date"].asString(), response["event_name"].asString()); + } } } diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index c9d29e1a76..f556218e4e 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -2121,9 +2121,17 @@ BOOL LLFavoritesOrderStorage::saveFavoritesRecord(bool pref_changed) pref_changed |= mRecreateFavoriteStorage; mRecreateFavoriteStorage = false; + // Can get called before inventory is done initializing. + if (!gInventory.isInventoryUsable()) + { + return FALSE; + } + LLUUID favorite_folder= gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); if (favorite_folder.isNull()) - return FALSE; + { + return FALSE; + } LLInventoryModel::item_array_t items; LLInventoryModel::cat_array_t cats; diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp index be62f7b3a9..48ca376a7a 100644 --- a/indra/newview/llfilepicker.cpp +++ b/indra/newview/llfilepicker.cpp @@ -36,10 +36,16 @@ #include "llviewercontrol.h" #include "llwindow.h" // beforeDialog() +#undef LL_GTK #if LL_SDL #include "llwindowsdl.h" // for some X/GTK utils to help with filepickers #endif // LL_SDL +#ifdef LL_FLTK + #include "FL/Fl.H" + #include "FL/Fl_Native_File_Chooser.H" +#endif + #if LL_LINUX #include "llhttpconstants.h" // file picker uses some of thes constants on Linux #endif @@ -1566,12 +1572,243 @@ BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter, bool blocking) return rtn; } +#elif LL_FLTK +BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename, bool blocking ) +{ + return openFileDialog( filter, blocking, eSaveFile ); +} + +BOOL LLFilePicker::getOpenFile( ELoadFilter filter, bool blocking ) +{ + return openFileDialog( filter, blocking, eOpenFile ); +} + +BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter, bool blocking) +{ + return openFileDialog( filter, blocking, eOpenMultiple ); +} + +bool LLFilePicker::openFileDialog( int32_t filter, bool blocking, EType aType ) +{ + if ( check_local_file_access_enabled() == false ) + return false; + + gViewerWindow->getWindow()->beforeDialog(); + reset(); + Fl_Native_File_Chooser::Type flType = Fl_Native_File_Chooser::BROWSE_FILE; + + if( aType == eOpenMultiple ) + flType = Fl_Native_File_Chooser::BROWSE_MULTI_FILE; + else if( aType == eSaveFile ) + flType = Fl_Native_File_Chooser::BROWSE_SAVE_FILE; + + Fl_Native_File_Chooser flDlg; + + std::string file_dialog_title; + std::string file_dialog_filter; + + if (aType == EType::eSaveFile) + { + std::string file_type("all_files"); + + switch ((ESaveFilter) filter) + { + case FFSAVE_ALL: + break; + case FFSAVE_TGA: + file_type = "targa_image_files"; + file_dialog_filter = "*.tga"; + break; + case FFSAVE_BMP: + file_type = "bitmap_image_files"; + file_dialog_filter = "*.bmp"; + break; + case FFSAVE_AVI: + file_type = "avi_movie_file"; + file_dialog_filter = "*.avi"; + break; + case FFSAVE_ANIM: + file_type = "xaf_animation_file"; + file_dialog_filter = "*.xaf"; + break; + case FFSAVE_XML: + file_type = "xml_file"; + file_dialog_filter = "*.xml"; + break; + case FFSAVE_COLLADA: + file_type = "collada_files"; + file_dialog_filter = "*.dae"; + break; + case FFSAVE_RAW: + file_type = "raw_file"; + file_dialog_filter = "*.raw"; + break; + case FFSAVE_J2C: + file_type = "compressed_image_files"; + file_dialog_filter = "*.j2c"; + break; + case FFSAVE_PNG: + file_type = "png_image_files"; + file_dialog_filter = "*.png"; + break; + case FFSAVE_JPEG: + file_type = "jpeg_image_files"; + file_dialog_filter = "*.{jpg,jpeg}"; + break; + case FFSAVE_SCRIPT: + file_type = "script_files"; + file_dialog_filter = "*.lsl"; + break; + case FFSAVE_TGAPNG: + file_type = "save_texture_image_files"; + file_dialog_filter = "*.{tga,png}"; + break; + case FFSAVE_WAV: + file_type = "sound_files"; + file_dialog_filter = "*.wav"; + break; + + // Firestorm additions + case FFSAVE_BEAM: + file_type = "xml_file"; + file_dialog_filter = "*.xml"; + break; + case FFSAVE_EXPORT: + file_type = "backup_files"; + file_dialog_filter = "*.oxp"; + break; + case FFSAVE_CSV: + file_type = "csv_files"; + file_dialog_filter = "*.csv"; + break; + +#ifdef _CORY_TESTING + case FFSAVE_GEOMETRY: + // no file type translation for this, so using the default "all_files" for now + file_dialog_filter = "*.slg"; + break; +#endif + } + + // can't say I like this combining of verb+type, it might not work too well in all languages -Zi + file_dialog_title = LLTrans::getString("save_file_verb") + " " + LLTrans::getString(file_type); + file_dialog_filter = LLTrans::getString(file_type) + " \t" + file_dialog_filter; + } + else + { + std::string file_type("all_files"); + + switch ((ELoadFilter) filter) + { + case FFLOAD_ALL: + break; + case FFLOAD_WAV: + file_type = "sound_files"; + file_dialog_filter = "*.wav"; + break; + case FFLOAD_IMAGE: + file_type = "image_files"; + file_dialog_filter = "*.{tga,bmp,jpg,jpeg,png}"; + break; + case FFLOAD_ANIM: + file_type = "animation_files"; + file_dialog_filter = "*.{bvh,anim}"; + break; + case FFLOAD_XML: + file_type = "xml_file"; + file_dialog_filter = "*.xml"; + break; + case FFLOAD_SLOBJECT: + file_type = "xml_file"; + file_dialog_filter = "*.slobject"; + break; + case FFLOAD_RAW: + file_type = "raw_file"; + file_dialog_filter = "*.raw"; + break; + case FFLOAD_MODEL: + case FFLOAD_COLLADA: + file_type = "collada_files"; + file_dialog_filter = "*.dae"; + break; + case FFLOAD_SCRIPT: + file_type = "script_files"; + file_dialog_filter = "*.lsl"; + break; + case FFLOAD_DICTIONARY: + file_type = "dictionary_files"; + file_dialog_filter = "*.{dic,xcu}"; + break; + case FFLOAD_DIRECTORY: + file_type = "choose_the_directory"; + break; + case FFLOAD_EXE: + file_type = "executable_files"; + break; + + // Firestorm additions + case FFLOAD_IMPORT: + file_type = "backup_files"; + file_dialog_filter = "*.oxp"; + break; + +#ifdef _CORY_TESTING + case FFLOAD_GEOMETRY: + // no file type translation for this, so using the default "all_files" for now + file_dialog_filter = "*.slg"; + break; +#endif + } + + if (aType == EType::eOpenMultiple) + { + file_dialog_title = LLTrans::getString("load_files"); + } + else + { + // can't say I like this combining of verb+type, it might not work too well in all languages -Zi + file_dialog_title = LLTrans::getString("load_file_verb") + " " + LLTrans::getString(file_type); + file_dialog_filter = LLTrans::getString(file_type) + " \t" + file_dialog_filter; + } + } + + flDlg.title(file_dialog_title.c_str()); + flDlg.type(flType); + + if (!file_dialog_filter.empty()) + { + flDlg.filter(file_dialog_filter.c_str()); + } + + int res = flDlg.show(); + gViewerWindow->getWindow()->afterDialog(); + + if( res == 0 ) + { + int32_t count = flDlg.count(); + if( count < 0 ) + count = 0; + for( int32_t i = 0; i < count; ++i ) + { + char const *pFile = flDlg.filename(i); + if( pFile && strlen(pFile) > 0 ) + mFiles.push_back( pFile ); + } + } + else if( res == -1 ) + { + LL_WARNS() << "FLTK failed: " << flDlg.errmsg() << LL_ENDL; + } + + return mFiles.empty()?FALSE:TRUE; +} + # else // LL_GTK // Hacky stubs designed to facilitate fake getSaveFile and getOpenFile with // static results, when we don't have a real filepicker. -BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename ) +BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename, bool blocking ) { // if local file browsing is turned off, return without opening dialog // (Even though this is a stub, I think we still should not return anything at all) @@ -1634,7 +1871,7 @@ BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter, bool blocking) #else // not implemented -BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename ) +BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename, bool blockin ) { reset(); return FALSE; diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h index 53ea7fd316..1aa5802d02 100644 --- a/indra/newview/llfilepicker.h +++ b/indra/newview/llfilepicker.h @@ -33,6 +33,12 @@ #ifndef LL_LLFILEPICKER_H #define LL_LLFILEPICKER_H +#if LL_FLTK + #if LL_GTK + #undef LL_GTK + #endif +#endif + #include "stdtypes.h" #if LL_DARWIN @@ -58,12 +64,13 @@ extern "C" { // mostly for Linux, possible on others #if LL_GTK # include "gtk/gtk.h" +#error "Direct use of GTK is deprecated" #endif // LL_GTK } class LLFilePicker { -#ifdef LL_GTK +#if LL_GTK friend class LLDirPicker; friend void chooser_responder(GtkWidget *, gint, gpointer); #endif // LL_GTK @@ -187,7 +194,14 @@ private: // we also remember the extension of the last added file. std::string mCurrentExtension; #endif - +#if LL_FLTK + enum EType + { + eSaveFile, eOpenFile, eOpenMultiple + }; + bool openFileDialog( int32_t filter, bool blocking, EType aType ); +#endif + std::vector mFiles; S32 mCurrentFile; bool mLocked; diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp index e075a311c2..a24d1d1436 100644 --- a/indra/newview/llflexibleobject.cpp +++ b/indra/newview/llflexibleobject.cpp @@ -734,11 +734,14 @@ void LLVolumeImplFlexible::preRebuild() void LLVolumeImplFlexible::doFlexibleRebuild(bool rebuild_volume) { LLVolume* volume = mVO->getVolume(); - if(rebuild_volume) - { - volume->setDirty(); - } - volume->regen(); + if (volume) + { + if (rebuild_volume) + { + volume->setDirty(); + } + volume->regen(); + } mUpdated = TRUE; } diff --git a/indra/newview/llfloater360capture.cpp b/indra/newview/llfloater360capture.cpp index 4d9935c74d..6eec980da8 100644 --- a/indra/newview/llfloater360capture.cpp +++ b/indra/newview/llfloater360capture.cpp @@ -102,6 +102,7 @@ BOOL LLFloater360Capture::postBuild() mWebBrowser = getChild("360capture_contents"); mWebBrowser->addObserver(this); + mWebBrowser->setAllowFileDownload(true); // There is a group of radio buttons that define the quality // by each having a 'value' that is returns equal to the pixel diff --git a/indra/newview/llfloatereditenvironmentbase.h b/indra/newview/llfloatereditenvironmentbase.h index 7c7cf5bdcd..d900d7f003 100644 --- a/indra/newview/llfloatereditenvironmentbase.h +++ b/indra/newview/llfloatereditenvironmentbase.h @@ -107,7 +107,7 @@ protected: void onAssetLoaded(LLUUID asset_id, LLSettingsBase::ptr_t settins, S32 status); -private: +protected: LLUUID mExpectingAssetId; // for asset load confirmation }; diff --git a/indra/newview/llfloatereditextdaycycle.cpp b/indra/newview/llfloatereditextdaycycle.cpp index eb0cd28190..24673d5a7c 100644 --- a/indra/newview/llfloatereditextdaycycle.cpp +++ b/indra/newview/llfloatereditextdaycycle.cpp @@ -1596,7 +1596,8 @@ void LLFloaterEditExtDayCycle::onIdlePlay(void* user_data) F32 new_frame = fmod(self->mPlayStartFrame + prcnt_played, 1.f); self->mTimeSlider->setCurSliderValue(new_frame); // will do the rounding - + self->mSkyBlender->setPosition(new_frame); + self->mWaterBlender->setPosition(new_frame); self->synchronizeTabs(); self->updateTimeAndLabel(); self->updateButtons(); diff --git a/indra/newview/llfloatereditextdaycycle.h b/indra/newview/llfloatereditextdaycycle.h index 9a30fb199f..ab5d12fa36 100644 --- a/indra/newview/llfloatereditextdaycycle.h +++ b/indra/newview/llfloatereditextdaycycle.h @@ -194,8 +194,6 @@ private: std::string mLastFrameSlider; bool mShiftCopyEnabled; - LLUUID mExpectingAssetId; - LLButton* mAddFrameButton; LLButton* mDeleteFrameButton; LLButton* mImportButton; diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index ea82165ad5..1fba47ddac 100644 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -1699,7 +1699,7 @@ BOOL LLFloaterIMContainer::selectConversationPair(const LLUUID& session_id, bool /* floater processing */ - if (NULL != session_floater) + if (NULL != session_floater && !session_floater->isDead()) { if (session_id != getSelectedSession()) { @@ -1871,11 +1871,14 @@ bool LLFloaterIMContainer::removeConversationListItem(const LLUUID& uuid, bool c if (widget) { is_widget_selected = widget->isSelected(); - new_selection = mConversationsRoot->getNextFromChild(widget, FALSE); - if (!new_selection) - { - new_selection = mConversationsRoot->getPreviousFromChild(widget, FALSE); - } + if (mConversationsRoot) + { + new_selection = mConversationsRoot->getNextFromChild(widget, FALSE); + if (!new_selection) + { + new_selection = mConversationsRoot->getPreviousFromChild(widget, FALSE); + } + } // Will destroy views and delete models that are not assigned to any views widget->destroyView(); diff --git a/indra/newview/llfloaterimnearbychathandler.cpp b/indra/newview/llfloaterimnearbychathandler.cpp index 3136f96fe4..2031fc6a4e 100644 --- a/indra/newview/llfloaterimnearbychathandler.cpp +++ b/indra/newview/llfloaterimnearbychathandler.cpp @@ -569,7 +569,7 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg, RlvUtil::filterLocation(tmp_chat.mText); tmp_chat.mRlvLocFiltered = TRUE; } - if ( (!RlvActions::canShowName(RlvActions::SNC_DEFAULT)) && (!tmp_chat.mRlvNamesFiltered) && (CHAT_SOURCE_AGENT != tmp_chat.mSourceType) ) + if ( (!RlvActions::canShowName(RlvActions::SNC_DEFAULT)) && (!tmp_chat.mRlvNamesFiltered) && (CHAT_SOURCE_AGENT != tmp_chat.mSourceType) && (!args.has("ONLINE_STATUS") || !args["ONLINE_STATUS"].asBoolean()) ) { RlvUtil::filterNames(tmp_chat.mText); tmp_chat.mRlvNamesFiltered = TRUE; diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index d0cb0ca798..e3404ec9f4 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -386,13 +386,16 @@ void LLFloaterIMSessionTab::draw() void LLFloaterIMSessionTab::enableDisableCallBtn() { - mVoiceButton->setEnabled( - mSessionID.notNull() - && mSession - && mSession->mSessionInitialized - && LLVoiceClient::getInstance()->voiceEnabled() - && LLVoiceClient::getInstance()->isVoiceWorking() - && mSession->mCallBackEnabled); + if (LLVoiceClient::instanceExists()) + { + mVoiceButton->setEnabled( + mSessionID.notNull() + && mSession + && mSession->mSessionInitialized + && LLVoiceClient::getInstance()->voiceEnabled() + && LLVoiceClient::getInstance()->isVoiceWorking() + && mSession->mCallBackEnabled); + } } void LLFloaterIMSessionTab::onFocusReceived() diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index e9552ad208..0f6d07800a 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -2682,6 +2682,7 @@ BOOL LLPanelLandAccess::postBuild() //mListBanned->setContextMenu(LLScrollListCtrl::MENU_AVATAR); mListBanned->setContextMenu(&gFSNameListAvatarMenu); // + mListBanned->setAlternateSort(); } return TRUE; @@ -2784,11 +2785,12 @@ void LLPanelLandAccess::refresh() { const LLAccessEntry& entry = (*cit).second; std::string duration; + S32 seconds = -1; if (entry.mTime != 0) { LLStringUtil::format_map_t args; S32 now = time(NULL); - S32 seconds = entry.mTime - now; + seconds = entry.mTime - now; if (seconds < 0) seconds = 0; if (seconds >= 7200) @@ -2825,6 +2827,7 @@ void LLPanelLandAccess::refresh() columns[0]["column"] = "name"; // to be populated later columns[1]["column"] = "duration"; columns[1]["value"] = duration; + columns[1]["alt_value"] = entry.mTime != 0 ? std::to_string(seconds) : "Always"; mListBanned->addElement(item); } mListBanned->sortByName(TRUE); diff --git a/indra/newview/llfloaterlandholdings.cpp b/indra/newview/llfloaterlandholdings.cpp index 749a3d2686..8633fe4e5e 100644 --- a/indra/newview/llfloaterlandholdings.cpp +++ b/indra/newview/llfloaterlandholdings.cpp @@ -108,6 +108,9 @@ LLFloaterLandHoldings::~LLFloaterLandHoldings() void LLFloaterLandHoldings::onOpen(const LLSD& key) { + LLScrollListCtrl *list = getChild("parcel list"); + list->clearRows(); + // query_id null is known to be us const LLUUID& query_id = LLUUID::null; diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index edd3a57595..b74b718c65 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -40,6 +40,7 @@ #include "llagent.h" #include "llbutton.h" #include "llcombobox.h" +#include "llfloaterreg.h" #include "llfocusmgr.h" #include "llmeshrepository.h" #include "llnotificationsutil.h" @@ -391,10 +392,21 @@ void LLFloaterModelPreview::initModelPreview() mModelPreview = new LLModelPreview(tex_width, tex_height, this); mModelPreview->setPreviewTarget(PREVIEW_CAMERA_DISTANCE); - mModelPreview->setDetailsCallback(boost::bind(&LLFloaterModelPreview::setDetails, this, _1, _2, _3, _4, _5)); + mModelPreview->setDetailsCallback(boost::bind(&LLFloaterModelPreview::setDetails, this, _1, _2, _3)); mModelPreview->setModelUpdatedCallback(boost::bind(&LLFloaterModelPreview::modelUpdated, this, _1)); } +//static +bool LLFloaterModelPreview::showModelPreview() +{ + LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)LLFloaterReg::getInstance("upload_model"); + if (fmp && !fmp->isModelLoading()) + { + fmp->loadHighLodModel(); + } + return true; +} + void LLFloaterModelPreview::onUploadOptionChecked(LLUICtrl* ctrl) { if (mModelPreview) @@ -899,9 +911,6 @@ void LLFloaterModelPreview::draw() } } - childSetTextArg("prim_cost", "[PRIM_COST]", llformat("%d", mModelPreview->mResourceCost)); - childSetTextArg("description_label", "[TEXTURES]", llformat("%d", mModelPreview->mTextureSet.size())); - if (!isMinimized() && mModelPreview->lodsReady()) { draw3dPreview(); @@ -1661,7 +1670,7 @@ void LLFloaterModelPreview::addStringToLogTab(const std::string& str, bool flash } } -void LLFloaterModelPreview::setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost) +void LLFloaterModelPreview::setDetails(F32 x, F32 y, F32 z) { assert_main_thread(); childSetTextArg("import_dimensions", "[X]", llformat("%.3f", x)); @@ -1834,9 +1843,20 @@ void LLFloaterModelPreview::toggleCalculateButton(bool visible) void LLFloaterModelPreview::onLoDSourceCommit(S32 lod) { mModelPreview->updateLodControls(lod); - refresh(); LLComboBox* lod_source_combo = getChild("lod_source_" + lod_name[lod]); + + if (lod_source_combo->getCurrentIndex() == LLModelPreview::LOD_FROM_FILE + && mModelPreview->mLODFile[lod].empty()) + { + // File wasn't selected, so nothing to do yet, refreshing + // hovewer will cause a small freeze with large meshes + // Might be good idea to open filepicker here + return; + } + + refresh(); + if (lod_source_combo->getCurrentIndex() == LLModelPreview::GENERATE) { //rebuild LoD to update triangle counts onLODParamCommit(lod, true); diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h index 8a01b0c307..b5dab262a3 100644 --- a/indra/newview/llfloatermodelpreview.h +++ b/indra/newview/llfloatermodelpreview.h @@ -73,6 +73,7 @@ public: /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); void initModelPreview(); + static bool showModelPreview(); BOOL handleMouseDown(S32 x, S32 y, MASK mask); BOOL handleMouseUp(S32 x, S32 y, MASK mask); @@ -90,7 +91,7 @@ public: void clearAvatarTab(); // clears table void updateAvatarTab(bool highlight_overrides); // populates table and data as nessesary - void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost); + void setDetails(F32 x, F32 y, F32 z); void setPreviewLOD(S32 lod); void onBrowseLOD(S32 lod); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 6c3b06ad60..2987593788 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -3139,6 +3139,16 @@ void LLFloaterPreference::updateUISoundsControls() getChild("PlayModeUISndNewIncomingGroupIMSession")->setValue((int)gSavedSettings.getU32("PlayModeUISndNewIncomingGroupIMSession")); // 0, 1, 2, 3. Shared with Chat > Notifications > "When receiving Group Instant Messages" getChild("PlayModeUISndNewIncomingConfIMSession")->setValue((int)gSavedSettings.getU32("PlayModeUISndNewIncomingConfIMSession")); // 0, 1, 2, 3. Shared with Chat > Notifications > "When receiving AdHoc Instant Messages" #ifdef OPENSIM + // OpenSim has option to not attenuate nearby local voice by distance + auto earPosGroup = findChild("ear_location"); + if (earPosGroup) + { + // It seems there is no better way to do this than with magic numbers short of importing the enums in vivoxvoice (which aren't necessarily the same thing). + // Index 2 here is the opensim only option to hear nearby chat without attenuation. + constexpr int hearNearbyVoiceFullVolume{2}; + earPosGroup->setIndexEnabled(hearNearbyVoiceFullVolume, LLGridManager::instance().isInOpenSim()); + } + // getChild("textFSRestartOpenSim")->setVisible(TRUE); getChild("UISndRestartOpenSim")->setVisible(TRUE); getChild("Prev_UISndRestartOpenSim")->setVisible(TRUE); @@ -4371,6 +4381,9 @@ void LLPanelPreferenceControls::populateControlTable() LL_WARNS() << "Unimplemented mode" << LL_ENDL; } + // explicit update to make sure table is ready for llsearchableui + pControlsTable->updateColumns(); + // Searchable columns were removed and readded, mark searchables for an update // Note: at the moment tables/lists lack proper llsearchableui support LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); diff --git a/indra/newview/llfloatertexturefetchdebugger.cpp b/indra/newview/llfloatertexturefetchdebugger.cpp index 9a23d99802..cda4dc8bcc 100644 --- a/indra/newview/llfloatertexturefetchdebugger.cpp +++ b/indra/newview/llfloatertexturefetchdebugger.cpp @@ -38,6 +38,7 @@ #include "llappviewer.h" #include "lltexturefetch.h" #include "llviewercontrol.h" +#include "llviewerassetstats.h" //gTextureTimer LLFloaterTextureFetchDebugger::LLFloaterTextureFetchDebugger(const LLSD& key) : LLFloater(key), @@ -50,6 +51,7 @@ LLFloaterTextureFetchDebugger::LLFloaterTextureFetchDebugger(const LLSD& key) mCommitCallbackRegistrar.add("TexFetchDebugger.Start", boost::bind(&LLFloaterTextureFetchDebugger::onClickStart, this)); mCommitCallbackRegistrar.add("TexFetchDebugger.Clear", boost::bind(&LLFloaterTextureFetchDebugger::onClickClear, this)); mCommitCallbackRegistrar.add("TexFetchDebugger.Close", boost::bind(&LLFloaterTextureFetchDebugger::onClickClose, this)); + mCommitCallbackRegistrar.add("TexFetchDebugger.ResetFetchTime", boost::bind(&LLFloaterTextureFetchDebugger::onClickResetFetchTime, this)); mCommitCallbackRegistrar.add("TexFetchDebugger.CacheRead", boost::bind(&LLFloaterTextureFetchDebugger::onClickCacheRead, this)); mCommitCallbackRegistrar.add("TexFetchDebugger.CacheWrite", boost::bind(&LLFloaterTextureFetchDebugger::onClickCacheWrite, this)); @@ -228,6 +230,12 @@ void LLFloaterTextureFetchDebugger::onClickClose() delete this; } +void LLFloaterTextureFetchDebugger::onClickResetFetchTime() +{ + gTextureTimer.start(); + gTextureTimer.pause(); +} + void LLFloaterTextureFetchDebugger::onClickClear() { mButtonStateMap["start_btn"] = true; diff --git a/indra/newview/llfloatertexturefetchdebugger.h b/indra/newview/llfloatertexturefetchdebugger.h index 096ad88e07..637f3b03e5 100644 --- a/indra/newview/llfloatertexturefetchdebugger.h +++ b/indra/newview/llfloatertexturefetchdebugger.h @@ -44,6 +44,7 @@ public: void onClickStart(); void onClickClear(); void onClickClose(); + void onClickResetFetchTime(); void onClickCacheRead(); void onClickCacheWrite(); diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index c60ab8ca2a..500ec37738 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -334,15 +334,15 @@ BOOL LLFloaterTools::postBuild() sShowObjectCost = gSavedSettings.getBOOL("ShowObjectRenderingCost"); // Added back more/less button - LLButton* btnExpand = getChild("btnExpand"); + LLButton* btnExpand = findChild("btnExpand"); if (btnExpand && mTab) { mExpandedHeight = getRect().getHeight(); mCollapsedHeight = mExpandedHeight - mTab->getRect().getHeight() + btnExpand->getRect().getHeight(); - if(!gSavedSettings.getBOOL("FSToolboxExpanded")) + if (!gSavedSettings.getBOOL("FSToolboxExpanded")) { mTab->setVisible(FALSE); - reshape( getRect().getWidth(), mCollapsedHeight); + reshape(getRect().getWidth(), mCollapsedHeight); btnExpand->setImageOverlay("Arrow_Down", btnExpand->getImageOverlayHAlign()); } } diff --git a/indra/newview/llfloaterurlentry.cpp b/indra/newview/llfloaterurlentry.cpp index 63bce3d2eb..d5c2ad5f81 100644 --- a/indra/newview/llfloaterurlentry.cpp +++ b/indra/newview/llfloaterurlentry.cpp @@ -204,6 +204,7 @@ void LLFloaterURLEntry::onBtnOK( void* userdata ) self->getChildView("ok_btn")->setEnabled(false); self->getChildView("cancel_btn")->setEnabled(false); self->getChildView("media_entry")->setEnabled(false); + self->getChildView("clear_btn")->setEnabled(false); } // static diff --git a/indra/newview/llfolderviewmodelinventory.cpp b/indra/newview/llfolderviewmodelinventory.cpp index d40a7234e2..b6d856e31b 100644 --- a/indra/newview/llfolderviewmodelinventory.cpp +++ b/indra/newview/llfolderviewmodelinventory.cpp @@ -66,7 +66,7 @@ void LLFolderViewModelInventory::sort( LLFolderViewFolder* folder ) { LL_RECORD_BLOCK_TIME(FTM_INVENTORY_SORT); - if (!needsSort(folder->getViewModelItem())) return; + if (!folder->areChildrenInited() || !needsSort(folder->getViewModelItem())) return; LLFolderViewModelItemInventory* modelp = static_cast(folder->getViewModelItem()); if (modelp->getUUID().isNull()) return; @@ -132,6 +132,16 @@ bool LLFolderViewModelInventory::isFolderComplete(LLFolderViewFolder* folder) return false; } +//virtual +void LLFolderViewModelItemInventory::addChild(LLFolderViewModelItem* child) +{ + LLFolderViewModelItemInventory* model_child = static_cast(child); + mLastAddedChildCreationDate = model_child->getCreationDate(); + + // this will requestSort() + LLFolderViewModelItemCommon::addChild(child); +} + void LLFolderViewModelItemInventory::requestSort() { LLFolderViewModelItemCommon::requestSort(); @@ -140,15 +150,31 @@ void LLFolderViewModelItemInventory::requestSort() { folderp->requestArrange(); } - if (static_cast(mRootViewModel).getSorter().isByDate()) - { - // sort by date potentially affects parent folders which use a date - // derived from newest item in them - if (mParent) - { - mParent->requestSort(); - } - } + LLInventorySort sorter = static_cast(mRootViewModel).getSorter(); + + // Sort by date potentially affects parent folders which use a date + // derived from newest item in them + if (sorter.isByDate() && mParent) + { + // If this is an item, parent needs to be resorted + // This case shouldn't happen, unless someone calls item->requestSort() + if (!folderp) + { + mParent->requestSort(); + } + // if this is a folder, check sort rules for folder first + else if (sorter.isFoldersByDate()) + { + if (mLastAddedChildCreationDate == -1 // nothing was added, some other reason for resort + || mLastAddedChildCreationDate > getCreationDate()) // newer child + { + LLFolderViewModelItemInventory* model_parent = static_cast(mParent); + model_parent->mLastAddedChildCreationDate = mLastAddedChildCreationDate; + mParent->requestSort(); + } + } + } + mLastAddedChildCreationDate = -1; } void LLFolderViewModelItemInventory::setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset, std::string::size_type string_size) @@ -387,6 +413,7 @@ bool LLInventorySort::operator()(const LLFolderViewModelItemInventory* const& a, LLFolderViewModelItemInventory::LLFolderViewModelItemInventory( class LLFolderViewModelInventory& root_view_model ) : LLFolderViewModelItemCommon(root_view_model), - mPrevPassedAllFilters(false) + mPrevPassedAllFilters(false), + mLastAddedChildCreationDate(-1) { } diff --git a/indra/newview/llfolderviewmodelinventory.h b/indra/newview/llfolderviewmodelinventory.h index 51b98339c4..de28091c32 100644 --- a/indra/newview/llfolderviewmodelinventory.h +++ b/indra/newview/llfolderviewmodelinventory.h @@ -47,6 +47,7 @@ public: virtual BOOL isItemInTrash( void) const { return FALSE; } // TODO: make into pure virtual. virtual BOOL isAgentInventory() const { return FALSE; } virtual BOOL isUpToDate() const = 0; + virtual void addChild(LLFolderViewModelItem* child); virtual bool hasChildren() const = 0; virtual LLInventoryType::EType getInventoryType() const = 0; virtual void performAction(LLInventoryModel* model, std::string action) = 0; @@ -63,6 +64,7 @@ public: virtual LLToolDragAndDrop::ESource getDragSource() const = 0; protected: bool mPrevPassedAllFilters; + time_t mLastAddedChildCreationDate; // -1 if nothing was added }; class LLInventorySort @@ -83,6 +85,8 @@ public: } bool isByDate() const { return mByDate; } + bool isFoldersByName() const { return (!mByDate || mFoldersByName) && !mFoldersByWeight; } + bool isFoldersByDate() const { return mByDate && !mFoldersByName && !mFoldersByWeight; } U32 getSortOrder() const { return mSortOrder; } void toParams(Params& p) { p.order(mSortOrder);} void fromParams(Params& p) diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index 0aaed1d611..4a6c0c75ce 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -438,6 +438,11 @@ void LLGroupActions::show(const LLUUID& group_id) // Standalone group floaters //LLFloaterSidePanelContainer::showPanel("people", "panel_group_info_sidetray", params); + //LLFloater *floater = LLFloaterReg::getTypedInstance("people"); + //if (!floater->isFrontmost()) + //{ + // floater->setVisibleAndFrontmost(TRUE, params); + //} LLFloater* floater = NULL; if (gSavedSettings.getBOOL("FSUseStandaloneGroupFloater")) { diff --git a/indra/newview/llhudtext.cpp b/indra/newview/llhudtext.cpp index ef1897ae30..102aafadb3 100644 --- a/indra/newview/llhudtext.cpp +++ b/indra/newview/llhudtext.cpp @@ -374,7 +374,7 @@ void LLHUDText::updateVisibility() if (!mSourceObject) { - //LL_WARNS() << "LLHUDText::updateScreenPos -- mSourceObject is NULL!" << LL_ENDL; + LL_WARNS() << "HUD text: mSourceObject is NULL, mOnHUDAttachment: " << mOnHUDAttachment << LL_ENDL; mVisible = TRUE; if (mOnHUDAttachment) { diff --git a/indra/newview/llimhandler.cpp b/indra/newview/llimhandler.cpp index c2b29f36e8..0fa3dc1110 100644 --- a/indra/newview/llimhandler.cpp +++ b/indra/newview/llimhandler.cpp @@ -60,7 +60,7 @@ void LLIMHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLIMHandler::processNotification(const LLNotificationPtr& notification) +bool LLIMHandler::processNotification(const LLNotificationPtr& notification, bool should_log) { if(notification->isDND()) { diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 2a09211517..13e153150a 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -1041,7 +1041,7 @@ bool LLIMModel::LLIMSession::isOutgoingAdHoc() const bool LLIMModel::LLIMSession::isAdHoc() { - return IM_SESSION_CONFERENCE_START == mType || (IM_SESSION_INVITE == mType && !gAgent.isInGroup(mSessionID)); + return IM_SESSION_CONFERENCE_START == mType || (IM_SESSION_INVITE == mType && !gAgent.isInGroup(mSessionID, TRUE)); } bool LLIMModel::LLIMSession::isP2P() @@ -1051,7 +1051,7 @@ bool LLIMModel::LLIMSession::isP2P() bool LLIMModel::LLIMSession::isGroupChat() { - return IM_SESSION_GROUP_START == mType || (IM_SESSION_INVITE == mType && gAgent.isInGroup(mSessionID)); + return IM_SESSION_GROUP_START == mType || (IM_SESSION_INVITE == mType && gAgent.isInGroup(mSessionID, TRUE)); } bool LLIMModel::LLIMSession::isOtherParticipantAvaline() @@ -1980,7 +1980,7 @@ LLUUID LLIMMgr::computeSessionID( } } - if (gAgent.isInGroup(session_id) && (session_id != other_participant_id)) + if (gAgent.isInGroup(session_id, TRUE) && (session_id != other_participant_id)) { LL_WARNS() << "Group session id different from group id: IM type = " << dialog << ", session id = " << session_id << ", group id = " << other_participant_id << LL_ENDL; } @@ -2317,7 +2317,7 @@ void LLCallDialog::setIcon(const LLSD& session_id, const LLSD& participant_id) // *NOTE: 12/28/2009: check avaline calls: LLVoiceClient::isParticipantAvatar returns false for them bool participant_is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id); - bool is_group = participant_is_avatar && gAgent.isInGroup(session_id); + bool is_group = participant_is_avatar && gAgent.isInGroup(session_id, TRUE); LLAvatarIconCtrl* avatar_icon = getChild("avatar_icon"); LLGroupIconCtrl* group_icon = getChild("group_icon"); @@ -2612,7 +2612,7 @@ BOOL LLIncomingCallDialog::postBuild() } std::string call_type; - if (gAgent.isInGroup(session_id)) + if (gAgent.isInGroup(session_id, TRUE)) { LLStringUtil::format_map_t args; LLGroupData data; @@ -2792,8 +2792,8 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload switch(type){ case IM_SESSION_CONFERENCE_START: case IM_SESSION_GROUP_START: - case IM_SESSION_INVITE: - if (gAgent.isInGroup(session_id)) + case IM_SESSION_INVITE: + if (gAgent.isInGroup(session_id, TRUE)) { LLGroupData data; if (!gAgent.getGroupData(session_id, data)) break; @@ -3553,7 +3553,7 @@ void LLIMMgr::inviteToSession( notify_box_type = "VoiceInviteP2P"; voice_invite = TRUE; } - else if ( gAgent.isInGroup(session_id) ) + else if ( gAgent.isInGroup(session_id, TRUE) ) { //only really old school groups have voice invitations notify_box_type = "VoiceInviteGroup"; diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 4be59857e1..f2092f8e09 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -2099,7 +2099,8 @@ void LLItemBridge::buildDisplayName() const LLStringUtil::toUpper(mSearchableName); //Name set, so trigger a sort - if(mParent) + LLInventorySort sorter = static_cast(mRootViewModel).getSorter(); + if(mParent && !sorter.isByDate()) { mParent->requestSort(); } @@ -2448,7 +2449,8 @@ void LLFolderBridge::buildDisplayName() const LLStringUtil::toUpper(mSearchableName); //Name set, so trigger a sort - if(mParent) + LLInventorySort sorter = static_cast(mRootViewModel).getSorter(); + if(mParent && sorter.isFoldersByName()) { mParent->requestSort(); } @@ -3773,9 +3775,22 @@ void LLFolderBridge::copyOutfitToClipboard() void LLFolderBridge::openItem() { LL_DEBUGS() << "LLFolderBridge::openItem()" << LL_ENDL; - LLInventoryModel* model = getInventoryModel(); - if(!model) return; - if(mUUID.isNull()) return; + + LLInventoryPanel* panel = mInventoryPanel.get(); + if (!panel) + { + return; + } + LLInventoryModel* model = getInventoryModel(); + if (!model) + { + return; + } + if (mUUID.isNull()) + { + return; + } + panel->onFolderOpening(mUUID); bool fetching_inventory = model->fetchDescendentsOf(mUUID); // Only change folder type if we have the folder contents. if (!fetching_inventory) diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 9468581c70..a12b06685b 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -265,7 +265,7 @@ void update_marketplace_folder_hierarchy(const LLUUID cat_id) return; } -void update_marketplace_category(const LLUUID& cur_uuid, bool perform_consistency_enforcement) +void update_marketplace_category(const LLUUID& cur_uuid, bool perform_consistency_enforcement, bool skip_clear_listing) { // When changing the marketplace status of an item, we usually have to change the status of all // folders in the same listing. This is because the display of each folder is affected by the @@ -337,7 +337,7 @@ void update_marketplace_category(const LLUUID& cur_uuid, bool perform_consistenc else { // If the folder is outside the marketplace listings root, clear its SLM data if needs be - if (perform_consistency_enforcement && LLMarketplaceData::instance().isListed(cur_uuid)) + if (perform_consistency_enforcement && !skip_clear_listing && LLMarketplaceData::instance().isListed(cur_uuid)) { LL_INFOS("SLM") << "Disassociate as the listing folder is not under the marketplace folder anymore!!" << LL_ENDL; LLMarketplaceData::instance().clearListing(cur_uuid); @@ -1970,7 +1970,7 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ result &= validate_marketplacelistings(category, cb, fix_hierarchy, depth + 1); } - update_marketplace_category(cat->getUUID()); + update_marketplace_category(cat->getUUID(), true, true); gInventory.notifyObservers(); return result && !has_bad_items; } diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index ee3b58a5fc..79dd948d3d 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -72,7 +72,7 @@ void show_item_original(const LLUUID& item_uuid); void reset_inventory_filter(); // Nudge the listing categories in the inventory to signal that their marketplace status changed -void update_marketplace_category(const LLUUID& cat_id, bool perform_consistency_enforcement = true); +void update_marketplace_category(const LLUUID& cat_id, bool perform_consistency_enforcement = true, bool skip_clear_listing = false); // Nudge all listing categories to signal that their marketplace status changed void update_all_marketplace_count(); diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index aa1a6972b6..80f47d5cba 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include +#include #include "llinventorymodel.h" @@ -76,6 +77,9 @@ #include "process.h" #endif +#include +#include + #include "aoengine.h" #include "fsfloaterwearablefavorites.h" #include "fslslbridge.h" @@ -144,6 +148,60 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item) return rv; } +///---------------------------------------------------------------------------- +/// Class LLInventoryValidationInfo +///---------------------------------------------------------------------------- +LLInventoryValidationInfo::LLInventoryValidationInfo(): + mFatalErrorCount(0), + mWarningCount(0), + mInitialized(false), + mFatalNoRootFolder(false), + mFatalNoLibraryRootFolder(false), + mFatalQADebugMode(false) +{ +} + +void LLInventoryValidationInfo::toOstream(std::ostream& os) const +{ + os << "mFatalErrorCount " << mFatalErrorCount << " mWarningCount " << mWarningCount; +} + + +std::ostream& operator<<(std::ostream& os, const LLInventoryValidationInfo& v) +{ + v.toOstream(os); + return os; +} + +void LLInventoryValidationInfo::asLLSD(LLSD& sd) const +{ + sd["fatal_error_count"] = mFatalErrorCount; + sd["warning_count"] = mWarningCount; + sd["initialized"] = mInitialized; + sd["missing_system_folders_count"] = LLSD::Integer(mMissingRequiredSystemFolders.size()); + sd["fatal_no_root_folder"] = mFatalNoRootFolder; + sd["fatal_no_library_root_folder"] = mFatalNoLibraryRootFolder; + sd["fatal_qa_debug_mode"] = mFatalQADebugMode; + if (mMissingRequiredSystemFolders.size()>0) + { + sd["missing_system_folders"] = LLSD::emptyArray(); + for(auto ft: mMissingRequiredSystemFolders) + { + sd["missing_system_folders"].append(LLFolderType::lookup(ft)); + } + } + sd["duplicate_system_folders_count"] = LLSD::Integer(mDuplicateRequiredSystemFolders.size()); + if (mDuplicateRequiredSystemFolders.size()>0) + { + sd["duplicate_system_folders"] = LLSD::emptyArray(); + for(auto ft: mDuplicateRequiredSystemFolders) + { + sd["duplicate_system_folders"].append(LLFolderType::lookup(ft)); + } + } + +} + ///---------------------------------------------------------------------------- /// Class LLInventoryModel ///---------------------------------------------------------------------------- @@ -175,13 +233,10 @@ LLInventoryModel::LLInventoryModel() mHttpHeaders(), mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpPriorityFG(0), - // 3.7.19 merge fix -// mHttpPriorityBG(0), - mHttpPriorityBG(0) -#ifdef LL_DEBUG + mHttpPriorityBG(0), mCategoryLock(), - mItemLock() -#endif + mItemLock(), + mValidationInfo(new LLInventoryValidationInfo) {} @@ -425,7 +480,6 @@ void LLInventoryModel::lockDirectDescendentArrays(const LLUUID& cat_id, { getDirectDescendentsOf(cat_id, categories, items); -#ifdef LL_DEBUG if (categories) { mCategoryLock[cat_id] = true; @@ -434,15 +488,12 @@ void LLInventoryModel::lockDirectDescendentArrays(const LLUUID& cat_id, { mItemLock[cat_id] = true; } -#endif } void LLInventoryModel::unlockDirectDescendentArrays(const LLUUID& cat_id) { -#ifdef LL_DEBUG mCategoryLock[cat_id] = false; mItemLock[cat_id] = false; -#endif } void LLInventoryModel::consolidateForType(const LLUUID& main_id, LLFolderType::EType type) @@ -533,12 +584,18 @@ const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot( } } - if(rv.isNull() && isInventoryUsable() && create_folder) + if(rv.isNull() && create_folder && root_id.notNull()) { - if(root_id.notNull()) + + if (isInventoryUsable()) { return createNewCategory(root_id, preferred_type, LLStringUtil::null); } + else + { + LL_WARNS("Inventory") << "Can't create requested folder, type " << preferred_type + << " because inventory is not usable" << LL_ENDL; + } } return rv; } @@ -624,20 +681,30 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, const std::string& pname, inventory_func_type callback) { - LLUUID id; - if(!isInventoryUsable()) + if (!isInventoryUsable()) { - LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL; + LL_WARNS(LOG_INV) << "Inventory is not usable; can't create requested category of type " + << preferred_type << LL_ENDL; + // FIXME failing but still returning an id? return id; } if(LLFolderType::lookup(preferred_type) == LLFolderType::badLookup()) { LL_DEBUGS(LOG_INV) << "Attempt to create undefined category." << LL_ENDL; + // FIXME failing but still returning an id? return id; } + if (preferred_type != LLFolderType::FT_NONE) + { + // Ultimately this should only be done for non-singleton + // types. Requires back-end changes to guarantee that others + // already exist. + LL_WARNS(LOG_INV) << "Creating new system folder, type " << preferred_type << LL_ENDL; + } + id.generate(); std::string name = pname; if(!pname.empty()) @@ -667,7 +734,7 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, request["message"] = "CreateInventoryCategory"; request["payload"] = body; - LL_DEBUGS(LOG_INV) << "create category request: " << ll_pretty_print_sd(request) << LL_ENDL; + LL_DEBUGS(LOG_INV) << "Creating category via request: " << ll_pretty_print_sd(request) << LL_ENDL; LLCoros::instance().launch("LLInventoryModel::createNewCategoryCoro", boost::bind(&LLInventoryModel::createNewCategoryCoro, this, url, body, callback)); @@ -679,6 +746,10 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, return LLUUID::null; } + // FIXME this UDP code path needs to be removed. Requires + // reworking many of the callers to use callbacks rather than + // assuming instant success. + // Add the category to the internal representation LLPointer cat = new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID()); @@ -688,6 +759,8 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, accountForUpdate(update); updateCategory(cat); + LL_DEBUGS(LOG_INV) << "Creating category via UDP message CreateInventoryFolder, type " << preferred_type << LL_ENDL; + // Create the category on the server. We do this to prevent people // from munging their protected folders. LLMessageSystem* msg = gMessageSystem; @@ -1234,12 +1307,10 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask) LLInventoryModel::cat_array_t* LLInventoryModel::getUnlockedCatArray(const LLUUID& id) { cat_array_t* cat_array = get_ptr_in_map(mParentChildCategoryTree, id); -#ifdef LL_DEBUG if (cat_array) { llassert_always(mCategoryLock[id] == false); } -#endif return cat_array; } @@ -1247,12 +1318,10 @@ LLInventoryModel::cat_array_t* LLInventoryModel::getUnlockedCatArray(const LLUUI LLInventoryModel::item_array_t* LLInventoryModel::getUnlockedItemArray(const LLUUID& id) { item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, id); -#ifdef LL_DEBUG if (item_array) { llassert_always(mItemLock[id] == false); } -#endif return item_array; } @@ -1323,10 +1392,8 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32 } // make space in the tree for this category's children. -#ifdef LL_DEBUG llassert_always(mCategoryLock[new_cat->getUUID()] == false); llassert_always(mItemLock[new_cat->getUUID()] == false); -#endif cat_array_t* catsp = new cat_array_t; item_array_t* itemsp = new item_array_t; mParentChildCategoryTree[new_cat->getUUID()] = catsp; @@ -1869,7 +1936,20 @@ void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) mModifyMask |= mask; } - if (referent.notNull() && (mChangedItemIDs.find(referent) == mChangedItemIDs.end())) + bool needs_update = false; + if (referent.notNull()) + { + if (mIsNotifyObservers) + { + needs_update = mChangedItemIDsBacklog.find(referent) == mChangedItemIDsBacklog.end(); + } + else + { + needs_update = mChangedItemIDs.find(referent) == mChangedItemIDs.end(); + } + } + + if (needs_update) { if (mIsNotifyObservers) { @@ -1880,6 +1960,8 @@ void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) mChangedItemIDs.insert(referent); } + // Fix me: From DD-81, probably shouldn't be here, instead + // should be somewhere in an observer update_marketplace_category(referent, false); if (mask & LLInventoryObserver::ADD) @@ -2490,11 +2572,11 @@ bool LLInventoryModel::loadSkeleton( } } - LL_INFOS(LOG_INV) << "Attempted to add " << bad_link_count - << " cached link items without baseobj present. " - << good_link_count << " link items were successfully added. " - << recovered_link_count << " links added in recovery. " - << "The corresponding categories were invalidated." << LL_ENDL; + LL_DEBUGS(LOG_INV) << "Attempted to add " << bad_link_count + << " cached link items without baseobj present. " + << good_link_count << " link items were successfully added. " + << recovered_link_count << " links added in recovery. " + << "The corresponding categories were invalidated." << LL_ENDL; } } @@ -2520,7 +2602,10 @@ bool LLInventoryModel::loadSkeleton( cat->setVersion(NO_VERSION); LL_DEBUGS(LOG_INV) << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << LL_ENDL; } - LL_INFOS(LOG_INV) << "Invalidated " << invalid_categories.size() << " categories due to invalid descendents cache" << LL_ENDL; + if (invalid_categories.size() > 0) + { + LL_DEBUGS(LOG_INV) << "Invalidated " << invalid_categories.size() << " categories due to invalid descendents cache" << LL_ENDL; + } // At this point, we need to set the known descendents for each // category which successfully cached so that we do not @@ -2590,17 +2675,13 @@ void LLInventoryModel::buildParentChildMap() cats.push_back(cat); if (mParentChildCategoryTree.count(cat->getUUID()) == 0) { -#ifdef LL_DEBUG llassert_always(mCategoryLock[cat->getUUID()] == false); -#endif catsp = new cat_array_t; mParentChildCategoryTree[cat->getUUID()] = catsp; } if (mParentChildItemTree.count(cat->getUUID()) == 0) { -#ifdef LL_DEBUG llassert_always(mItemLock[cat->getUUID()] == false); -#endif itemsp = new item_array_t; mParentChildItemTree[cat->getUUID()] = itemsp; } @@ -2818,10 +2899,22 @@ void LLInventoryModel::buildParentChildMap() } } - // 'My Inventory', - // root of the agent's inv found. - // The inv tree is built. - mIsAgentInvUsable = true; + LLPointer validation_info = validate(); + if (validation_info->mFatalErrorCount > 0) + { + // Fatal inventory error. Will not be able to engage in many inventory operations. + // This should be followed by an error dialog leading to logout. + LL_WARNS("Inventory") << "Fatal errors were found in validate(): unable to initialize inventory! " + << "Will not be able to do normal inventory operations in this session." + << LL_ENDL; + mIsAgentInvUsable = false; + } + else + { + mIsAgentInvUsable = true; + } + validation_info->mInitialized = true; + mValidationInfo = validation_info; // notifyObservers() has been moved to // llstartup/idle_startup() after this func completes. @@ -2829,13 +2922,6 @@ void LLInventoryModel::buildParentChildMap() // observers start firing. } } - -#ifdef LL_DEBUG - if (!gInventory.validate()) - { - LL_WARNS(LOG_INV) << "model failed validity check!" << LL_ENDL; - } -#endif } // Would normally do this at construction but that's too early @@ -4212,68 +4298,71 @@ void LLInventoryModel::dumpInventory() const LL_INFOS() << "\n**********************\nEnd Inventory Dump" << LL_ENDL; } -#ifdef LL_DEBUG // Do various integrity checks on model, logging issues found and -// returning an overall good/bad flag. -bool LLInventoryModel::validate() const +// returning an overall good/bad flag. +LLPointer LLInventoryModel::validate() const { - const S32 MAX_VERBOSE_ERRORS = 40; // too many errors can cause disconect or freeze - S32 error_count = 0; + LLPointer validation_info = new LLInventoryValidationInfo; + S32 fatalities = 0; + S32 warnings = 0; if (getRootFolderID().isNull()) { - LL_WARNS() << "no root folder id" << LL_ENDL; - error_count++; + LL_WARNS("Inventory") << "Fatal inventory corruption: no root folder id" << LL_ENDL; + validation_info->mFatalNoRootFolder = true; + fatalities++; } if (getLibraryRootFolderID().isNull()) { - LL_WARNS() << "no root folder id" << LL_ENDL; - error_count++; + LL_WARNS("Inventory") << "Fatal inventory corruption: no library root folder id" << LL_ENDL; + validation_info->mFatalNoLibraryRootFolder = true; + fatalities++; } if (mCategoryMap.size() + 1 != mParentChildCategoryTree.size()) { // ParentChild should be one larger because of the special entry for null uuid. - LL_INFOS() << "unexpected sizes: cat map size " << mCategoryMap.size() - << " parent/child " << mParentChildCategoryTree.size() << LL_ENDL; - error_count++; + LL_INFOS("Inventory") << "unexpected sizes: cat map size " << mCategoryMap.size() + << " parent/child " << mParentChildCategoryTree.size() << LL_ENDL; + warnings++; } S32 cat_lock = 0; S32 item_lock = 0; S32 desc_unknown_count = 0; S32 version_unknown_count = 0; + + typedef std::map ft_count_map; + ft_count_map ft_counts_under_root; + ft_count_map ft_counts_elsewhere; + + // Loop over all categories and check. for(cat_map_t::const_iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit) { const LLUUID& cat_id = cit->first; const LLViewerInventoryCategory *cat = cit->second; if (!cat) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "invalid cat" << LL_ENDL; - } - error_count++; + LL_WARNS("Inventory") << "null cat" << LL_ENDL; + warnings++; continue; } + LLUUID topmost_ancestor_id; + // Will leave as null uuid on failure + getObjectTopmostAncestor(cat_id, topmost_ancestor_id); if (cat_id != cat->getUUID()) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "cat id/index mismatch " << cat_id << " " << cat->getUUID() << LL_ENDL; - } - error_count++; + LL_WARNS("Inventory") << "cat id/index mismatch " << cat_id << " " << cat->getUUID() << LL_ENDL; + warnings++; } if (cat->getParentUUID().isNull()) { if (cat_id != getRootFolderID() && cat_id != getLibraryRootFolderID()) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "cat " << cat_id << " has no parent, but is not root (" - << getRootFolderID() << ") or library root (" - << getLibraryRootFolderID() << ")" << LL_ENDL; - } + LL_WARNS("Inventory") << "cat " << cat_id << " has no parent, but is not root (" + << getRootFolderID() << ") or library root (" + << getLibraryRootFolderID() << ")" << LL_ENDL; + warnings++; } } cat_array_t* cats; @@ -4281,11 +4370,8 @@ bool LLInventoryModel::validate() const getDirectDescendentsOf(cat_id,cats,items); if (!cats || !items) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "invalid direct descendents for " << cat_id << LL_ENDL; - } - error_count++; + LL_WARNS("Inventory") << "invalid direct descendents for " << cat_id << LL_ENDL; + warnings++; continue; } if (cat->getDescendentCount() == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN) @@ -4294,25 +4380,29 @@ bool LLInventoryModel::validate() const } else if (cats->size() + items->size() != cat->getDescendentCount()) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "invalid desc count for " << cat_id << " name [" << cat->getName() - << "] parent " << cat->getParentUUID() - << " cached " << cat->getDescendentCount() - << " expected " << cats->size() << "+" << items->size() - << "=" << cats->size() + items->size() << LL_ENDL; - } - error_count++; + // In the case of library this is not unexpected, since + // different user accounts may be getting the library + // contents from different inventory hosts. + if (topmost_ancestor_id.isNull() || topmost_ancestor_id != getLibraryRootFolderID()) + { + LL_WARNS("Inventory") << "invalid desc count for " << cat_id << " [" << getFullPath(cat) << "]" + << " cached " << cat->getDescendentCount() + << " expected " << cats->size() << "+" << items->size() + << "=" << cats->size() +items->size() << LL_ENDL; + warnings++; + } } if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) { version_unknown_count++; } - if (mCategoryLock.count(cat_id)) + auto cat_lock_it = mCategoryLock.find(cat_id); + if (cat_lock_it != mCategoryLock.end() && cat_lock_it->second) { cat_lock++; } - if (mItemLock.count(cat_id)) + auto item_lock_it = mItemLock.find(cat_id); + if (item_lock_it != mItemLock.end() && item_lock_it->second) { item_lock++; } @@ -4322,11 +4412,8 @@ bool LLInventoryModel::validate() const if (!item) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "null item at index " << i << " for cat " << cat_id << LL_ENDL; - } - error_count++; + LL_WARNS("Inventory") << "null item at index " << i << " for cat " << cat_id << LL_ENDL; + warnings++; continue; } @@ -4334,13 +4421,10 @@ bool LLInventoryModel::validate() const if (item->getParentUUID() != cat_id) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "wrong parent for " << item_id << " found " - << item->getParentUUID() << " expected " << cat_id - << LL_ENDL; - } - error_count++; + LL_WARNS("Inventory") << "wrong parent for " << item_id << " found " + << item->getParentUUID() << " expected " << cat_id + << LL_ENDL; + warnings++; } @@ -4348,24 +4432,17 @@ bool LLInventoryModel::validate() const item_map_t::const_iterator it = mItemMap.find(item_id); if (it == mItemMap.end()) { - - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "item " << item_id << " found as child of " - << cat_id << " but not in top level mItemMap" << LL_ENDL; - } - error_count++; + LL_WARNS("Inventory") << "item " << item_id << " found as child of " + << cat_id << " but not in top level mItemMap" << LL_ENDL; + warnings++; } else { LLViewerInventoryItem *top_item = it->second; if (top_item != item) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "item mismatch, item_id " << item_id - << " top level entry is different, uuid " << top_item->getUUID() << LL_ENDL; - } + LL_WARNS("Inventory") << "item mismatch, item_id " << item_id + << " top level entry is different, uuid " << top_item->getUUID() << LL_ENDL; } } @@ -4374,25 +4451,19 @@ bool LLInventoryModel::validate() const bool found = getObjectTopmostAncestor(item_id, topmost_ancestor_id); if (!found) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "unable to find topmost ancestor for " << item_id << LL_ENDL; - } - error_count++; + LL_WARNS("Inventory") << "unable to find topmost ancestor for " << item_id << LL_ENDL; + warnings++; } else { if (topmost_ancestor_id != getRootFolderID() && topmost_ancestor_id != getLibraryRootFolderID()) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "unrecognized top level ancestor for " << item_id - << " got " << topmost_ancestor_id - << " expected " << getRootFolderID() - << " or " << getLibraryRootFolderID() << LL_ENDL; - } - error_count++; + LL_WARNS("Inventory") << "unrecognized top level ancestor for " << item_id + << " got " << topmost_ancestor_id + << " expected " << getRootFolderID() + << " or " << getLibraryRootFolderID() << LL_ENDL; + warnings++; } } } @@ -4406,12 +4477,9 @@ bool LLInventoryModel::validate() const getDirectDescendentsOf(parent_id,cats,items); if (!cats) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "cat " << cat_id << " name [" << cat->getName() - << "] orphaned - no child cat array for alleged parent " << parent_id << LL_ENDL; - } - error_count++; + LL_WARNS("Inventory") << "cat " << cat_id << " name [" << cat->getName() + << "] orphaned - no child cat array for alleged parent " << parent_id << LL_ENDL; + warnings++; } else { @@ -4427,49 +4495,66 @@ bool LLInventoryModel::validate() const } if (!found) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "cat " << cat_id << " name [" << cat->getName() - << "] orphaned - not found in child cat array of alleged parent " << parent_id << LL_ENDL; - } + LL_WARNS("Inventory") << "cat " << cat_id << " name [" << cat->getName() + << "] orphaned - not found in child cat array of alleged parent " << parent_id << LL_ENDL; + } + } + } + + // Update count of preferred types + LLFolderType::EType folder_type = cat->getPreferredType(); + bool cat_is_in_library = false; + LLUUID topmost_id; + if (getObjectTopmostAncestor(cat->getUUID(),topmost_id) && topmost_id == getLibraryRootFolderID()) + { + cat_is_in_library = true; + } + if (!cat_is_in_library) + { + if (getRootFolderID().notNull() && (cat->getUUID()==getRootFolderID() || cat->getParentUUID()==getRootFolderID())) + { + ft_counts_under_root[folder_type]++; + if (folder_type != LLFolderType::FT_NONE) + { + LL_DEBUGS("Inventory") << "Under root cat: " << getFullPath(cat) << " folder_type " << folder_type << LL_ENDL; + } + } + else + { + ft_counts_elsewhere[folder_type]++; + if (folder_type != LLFolderType::FT_NONE) + { + LL_DEBUGS("Inventory") << "Elsewhere cat: " << getFullPath(cat) << " folder_type " << folder_type << LL_ENDL; } } } } + // Loop over all items and check for(item_map_t::const_iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit) { const LLUUID& item_id = iit->first; LLViewerInventoryItem *item = iit->second; if (item->getUUID() != item_id) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "item_id " << item_id << " does not match " << item->getUUID() << LL_ENDL; - } - error_count++; + LL_WARNS("Inventory") << "item_id " << item_id << " does not match " << item->getUUID() << LL_ENDL; + warnings++; } const LLUUID& parent_id = item->getParentUUID(); if (parent_id.isNull()) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "item " << item_id << " name [" << item->getName() << "] has null parent id!" << LL_ENDL; - } + LL_WARNS("Inventory") << "item " << item_id << " name [" << item->getName() << "] has null parent id!" << LL_ENDL; } - else if (error_count < MAX_VERBOSE_ERRORS) + else { cat_array_t* cats; item_array_t* items; getDirectDescendentsOf(parent_id,cats,items); if (!items) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "item " << item_id << " name [" << item->getName() - << "] orphaned - alleged parent has no child items list " << parent_id << LL_ENDL; - } + LL_WARNS("Inventory") << "item " << item_id << " name [" << item->getName() + << "] orphaned - alleged parent has no child items list " << parent_id << LL_ENDL; } else { @@ -4484,91 +4569,172 @@ bool LLInventoryModel::validate() const } if (!found) { - if (error_count < MAX_VERBOSE_ERRORS) - { - LL_WARNS() << "item " << item_id << " name [" << item->getName() - << "] orphaned - not found as child of alleged parent " << parent_id << LL_ENDL; - } + LL_WARNS("Inventory") << "item " << item_id << " name [" << item->getName() + << "] orphaned - not found as child of alleged parent " << parent_id << LL_ENDL; } } } // Link checking - if (error_count < MAX_VERBOSE_ERRORS) - { - if (item->getIsLinkType()) - { - const LLUUID& link_id = item->getUUID(); - const LLUUID& target_id = item->getLinkedUUID(); - LLViewerInventoryItem *target_item = getItem(target_id); - LLViewerInventoryCategory *target_cat = getCategory(target_id); - // Linked-to UUID should have back reference to this link. - if (!hasBacklinkInfo(link_id, target_id)) - { - LL_WARNS() << "link " << item->getUUID() << " type " << item->getActualType() - << " missing backlink info at target_id " << target_id - << LL_ENDL; - } - // Links should have referents. - if (item->getActualType() == LLAssetType::AT_LINK && !target_item) - { - LL_WARNS() << "broken item link " << item->getName() << " id " << item->getUUID() << LL_ENDL; - } - else if (item->getActualType() == LLAssetType::AT_LINK_FOLDER && !target_cat) - { - LL_WARNS() << "broken folder link " << item->getName() << " id " << item->getUUID() << LL_ENDL; - } - if (target_item && target_item->getIsLinkType()) - { - LL_WARNS() << "link " << item->getName() << " references a link item " - << target_item->getName() << " " << target_item->getUUID() << LL_ENDL; - } + if (item->getIsLinkType()) + { + const LLUUID& link_id = item->getUUID(); + const LLUUID& target_id = item->getLinkedUUID(); + LLViewerInventoryItem *target_item = getItem(target_id); + LLViewerInventoryCategory *target_cat = getCategory(target_id); + // Linked-to UUID should have back reference to this link. + if (!hasBacklinkInfo(link_id, target_id)) + { + LL_WARNS("Inventory") << "link " << item->getUUID() << " type " << item->getActualType() + << " missing backlink info at target_id " << target_id + << LL_ENDL; + } + // Links should have referents. + if (item->getActualType() == LLAssetType::AT_LINK && !target_item) + { + LL_WARNS("Inventory") << "broken item link " << item->getName() << " id " << item->getUUID() << LL_ENDL; + } + else if (item->getActualType() == LLAssetType::AT_LINK_FOLDER && !target_cat) + { + LL_WARNS("Inventory") << "broken folder link " << item->getName() << " id " << item->getUUID() << LL_ENDL; + } + if (target_item && target_item->getIsLinkType()) + { + LL_WARNS("Inventory") << "link " << item->getName() << " references a link item " + << target_item->getName() << " " << target_item->getUUID() << LL_ENDL; + } - // Links should not have backlinks. - std::pair range = mBacklinkMMap.equal_range(link_id); - if (range.first != range.second) - { - LL_WARNS() << "Link item " << item->getName() << " has backlinks!" << LL_ENDL; - } - } - else - { - // Check the backlinks of a non-link item. - const LLUUID& target_id = item->getUUID(); - std::pair range = mBacklinkMMap.equal_range(target_id); - for (backlink_mmap_t::const_iterator it = range.first; it != range.second; ++it) - { - const LLUUID& link_id = it->second; - LLViewerInventoryItem *link_item = getItem(link_id); - if (!link_item || !link_item->getIsLinkType()) - { - LL_WARNS() << "invalid backlink from target " << item->getName() << " to " << link_id << LL_ENDL; - } - } - } - } + // Links should not have backlinks. + std::pair range = mBacklinkMMap.equal_range(link_id); + if (range.first != range.second) + { + LL_WARNS("Inventory") << "Link item " << item->getName() << " has backlinks!" << LL_ENDL; + } + } + else + { + // Check the backlinks of a non-link item. + const LLUUID& target_id = item->getUUID(); + std::pair range = mBacklinkMMap.equal_range(target_id); + for (backlink_mmap_t::const_iterator it = range.first; it != range.second; ++it) + { + const LLUUID& link_id = it->second; + LLViewerInventoryItem *link_item = getItem(link_id); + if (!link_item || !link_item->getIsLinkType()) + { + LL_WARNS("Inventory") << "invalid backlink from target " << item->getName() << " to " << link_id << LL_ENDL; + } + } + } } - + + // Check system folders + for (auto fit=ft_counts_under_root.begin(); fit != ft_counts_under_root.end(); ++fit) + { + LL_DEBUGS("Inventory") << "Folder type " << fit->first << " count " << fit->second << " under root" << LL_ENDL; + } + for (auto fit=ft_counts_elsewhere.begin(); fit != ft_counts_elsewhere.end(); ++fit) + { + LL_DEBUGS("Inventory") << "Folder type " << fit->first << " count " << fit->second << " elsewhere" << LL_ENDL; + } + + static LLCachedControl fake_system_folder_issues(gSavedSettings, "QAModeFakeSystemFolderIssues", false); + static std::default_random_engine e{}; + static std::uniform_int_distribution<> distrib(0, 1); + for (S32 ft=LLFolderType::FT_TEXTURE; ft(ft); + if (LLFolderType::lookup(folder_type)==LLFolderType::badLookup()) + { + continue; + } + bool is_automatic = LLFolderType::lookupIsAutomaticType(folder_type); + bool is_singleton = LLFolderType::lookupIsSingletonType(folder_type); + S32 count_under_root = ft_counts_under_root[folder_type]; + S32 count_elsewhere = ft_counts_elsewhere[folder_type]; + if (fake_system_folder_issues) + { + // Force all counts to be either 0 or 2, thus flagged as an error. + count_under_root = 2*distrib(e); + count_elsewhere = 2*distrib(e); + validation_info->mFatalQADebugMode = true; + } + if (is_singleton) + { + if (count_under_root==0) + { + LL_WARNS("Inventory") << "Expected system folder type " << ft << " was not found under root" << LL_ENDL; + // Need to create, if allowed. + if (is_automatic) + { + LL_WARNS("Inventory") << "Fatal inventory corruption: cannot create system folder of type " << ft << LL_ENDL; + fatalities++; + validation_info->mMissingRequiredSystemFolders.insert(LLFolderType::EType(ft)); + } + else + { + // Can create, and will when needed. + warnings++; + } + } + else if (count_under_root > 1) + { + LL_WARNS("Inventory") << "Fatal inventory corruption: system folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL; + validation_info->mDuplicateRequiredSystemFolders.insert(LLFolderType::EType(ft)); + fatalities++; + } + if (count_elsewhere > 0) + { + LL_WARNS("Inventory") << "Found " << count_elsewhere << " extra folders of type " << ft << " outside of root" << LL_ENDL; + warnings++; + } + } + } + + if (cat_lock > 0 || item_lock > 0) { - LL_INFOS() << "Found locks on some categories: sub-cat arrays " + LL_INFOS("Inventory") << "Found locks on some categories: sub-cat arrays " << cat_lock << ", item arrays " << item_lock << LL_ENDL; } if (desc_unknown_count != 0) { - LL_INFOS() << "Found " << desc_unknown_count << " cats with unknown descendent count" << LL_ENDL; + LL_DEBUGS() << "Found " << desc_unknown_count << " cats with unknown descendent count" << LL_ENDL; } if (version_unknown_count != 0) { - LL_INFOS() << "Found " << version_unknown_count << " cats with unknown version" << LL_ENDL; + LL_DEBUGS("Inventory") << "Found " << version_unknown_count << " cats with unknown version" << LL_ENDL; } - LL_INFOS() << "Validate done, found " << error_count << " errors" << LL_ENDL; + // FIXME need to fail login and tell user to retry, contact support if problem persists. + bool valid = (fatalities == 0); + LL_INFOS("Inventory") << "Validate done, fatal errors: " << fatalities << ", warnings: " << warnings << ", valid: " << valid << LL_ENDL; - return error_count == 0; + validation_info->mFatalErrorCount = fatalities; + validation_info->mWarningCount = warnings; + + return validation_info; +} + +// Provides a unix-style path from root, like "/My Inventory/Clothing/.../myshirt" +std::string LLInventoryModel::getFullPath(const LLInventoryObject *obj) const +{ + std::vector path_elts; + std::map visited; + while (obj != NULL && !visited[obj->getUUID()]) + { + path_elts.push_back(obj->getName()); + // avoid infinite loop in the unlikely event of a cycle + visited[obj->getUUID()] = true; + obj = getObject(obj->getParentUUID()); + } + std::stringstream s; + std::string delim("/"); + std::reverse(path_elts.begin(), path_elts.end()); + std::string result = "/" + boost::algorithm::join(path_elts, delim); + return result; } -#endif // [UDP-Msg] // static diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 19b8f123cb..d64590db87 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -59,7 +59,29 @@ class LLInventoryCategory; class LLMessageSystem; class LLInventoryCollectFunctor; -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +///---------------------------------------------------------------------------- +/// LLInventoryValidationInfo +///---------------------------------------------------------------------------- +class LLInventoryValidationInfo: public LLRefCount +{ +public: + LLInventoryValidationInfo(); + void toOstream(std::ostream& os) const; + void asLLSD(LLSD& sd) const; + + + S32 mFatalErrorCount; + S32 mWarningCount; + bool mInitialized; + bool mFatalNoRootFolder; + bool mFatalNoLibraryRootFolder; + bool mFatalQADebugMode; + std::set mMissingRequiredSystemFolders; + std::set mDuplicateRequiredSystemFolders; +}; +std::ostream& operator<<(std::ostream& s, const LLInventoryValidationInfo& v); + +///---------------------------------------------------------------------------- // LLInventoryModel // // Represents a collection of inventory, and provides efficient ways to access @@ -67,7 +89,7 @@ class LLInventoryCollectFunctor; // NOTE: This class could in theory be used for any place where you need // inventory, though it optimizes for time efficiency - not space efficiency, // probably making it inappropriate for use on tasks. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +///---------------------------------------------------------------------------- class LLInventoryModel { LOG_CLASS(LLInventoryModel); @@ -712,19 +734,17 @@ protected: cat_array_t* getUnlockedCatArray(const LLUUID& id); item_array_t* getUnlockedItemArray(const LLUUID& id); private: -#ifdef LL_DEBUG std::map mCategoryLock; std::map mItemLock; -#endif //-------------------------------------------------------------------- // Debugging //-------------------------------------------------------------------- public: void dumpInventory() const; -#ifdef LL_DEBUG - bool validate() const; -#endif + LLPointer validate() const; + LLPointer mValidationInfo; + std::string getFullPath(const LLInventoryObject *obj) const; /** Miscellaneous ** ** diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 4836c950a6..8a25b40b9d 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -309,17 +309,18 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) mCompletionObserver = new LLInvPanelComplObserver(boost::bind(&LLInventoryPanel::onItemsCompletion, this)); mInventory->addObserver(mCompletionObserver); - if (mBuildViewsOnInit) + if (mBuildViewsOnInit && mViewsInitialized == VIEWS_UNINITIALIZED) { // Build view of inventory if we need default full hierarchy and inventory is ready, otherwise do in onIdle. // Initializing views takes a while so always do it onIdle if viewer already loaded. - if (mInventory->isInventoryUsable() - && mViewsInitialized == VIEWS_UNINITIALIZED + if (mInventory->isInventoryUsable() && LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT) { - initializeViews(); + // Usually this happens on login, so we have less time constraits, but too long and we can cause a disconnect + const F64 max_time = 20.f; + initializeViews(max_time); } - else if (mViewsInitialized != VIEWS_INITIALIZING) + else { mViewsInitialized = VIEWS_INITIALIZING; gIdleCallbacks.addFunction(onIdle, (void*)this); @@ -558,6 +559,19 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve view_folder = dynamic_cast(view_item); } + // if folder is not fully initialized (likely due to delayed load on idle) + // and we are not rebuilding, try updating children + if (view_folder + && !view_folder->areChildrenInited() + && ( (mask & LLInventoryObserver::REBUILD) == 0)) + { + LLInventoryObject const* objectp = mInventory->getObject(item_id); + if (objectp) + { + view_item = buildNewViews(item_id, objectp, view_item, BUILD_ONE_FOLDER); + } + } + ////////////////////////////// // LABEL Operation // Empty out the display name for relabel. @@ -598,7 +612,7 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve if (objectp) { // providing NULL directly avoids unnessesary getItemByID calls - view_item = buildNewViews(item_id, objectp, NULL); + view_item = buildNewViews(item_id, objectp, NULL, BUILD_ONE_FOLDER); } else { @@ -651,7 +665,7 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve if (objectp) { // providing NULL directly avoids unnessesary getItemByID calls - buildNewViews(item_id, objectp, NULL); + buildNewViews(item_id, objectp, NULL, BUILD_ONE_FOLDER); } // Select any newly created object that has the auto rename at top of folder root set. @@ -803,12 +817,12 @@ void LLInventoryPanel::onIdle(void *userdata) return; LLInventoryPanel *self = (LLInventoryPanel*)userdata; - // Inventory just initialized, do complete build - if (self->mViewsInitialized != VIEWS_INITIALIZED) + if (self->mViewsInitialized <= VIEWS_INITIALIZING) { - self->initializeViews(); + const F64 max_time = 0.001f; // 1 ms, in this case we need only root folders + self->initializeViews(max_time); // Shedules LLInventoryPanel::idle() } - if (self->mViewsInitialized == VIEWS_INITIALIZED) + if (self->mViewsInitialized >= VIEWS_BUILDING) { gIdleCallbacks.deleteFunction(onIdle, (void*)self); } @@ -843,6 +857,49 @@ void LLInventoryPanel::idle(void* user_data) } + bool in_visible_chain = panel->isInVisibleChain(); + + if (!panel->mBuildViewsQueue.empty()) + { + const F64 max_time = in_visible_chain ? 0.006f : 0.001f; // 6 ms + F64 curent_time = LLTimer::getTotalSeconds(); + panel->mBuildViewsEndTime = curent_time + max_time; + + // things added last are closer to root thus of higher priority + std::deque priority_list; + priority_list.swap(panel->mBuildViewsQueue); + + while (curent_time < panel->mBuildViewsEndTime + && !priority_list.empty()) + { + LLUUID item_id = priority_list.back(); + priority_list.pop_back(); + + LLInventoryObject const* objectp = panel->mInventory->getObject(item_id); + if (objectp && panel->typedViewsFilter(item_id, objectp)) + { + LLFolderViewItem* folder_view_item = panel->getItemByID(item_id); + if (!folder_view_item || !folder_view_item->areChildrenInited()) + { + const LLUUID &parent_id = objectp->getParentUUID(); + LLFolderViewFolder* parent_folder = (LLFolderViewFolder*)panel->getItemByID(parent_id); + panel->buildViewsTree(item_id, parent_id, objectp, folder_view_item, parent_folder, BUILD_TIMELIMIT); + } + } + curent_time = LLTimer::getTotalSeconds(); + } + while (!priority_list.empty()) + { + // items in priority_list are of higher priority + panel->mBuildViewsQueue.push_back(priority_list.front()); + priority_list.pop_front(); + } + if (panel->mBuildViewsQueue.empty()) + { + panel->mViewsInitialized = VIEWS_INITIALIZED; + } + } + // Take into account the fact that the root folder might be invalidated if (panel->mFolderRoot.get()) { @@ -873,10 +930,16 @@ void LLInventoryPanel::idle(void* user_data) } -void LLInventoryPanel::initializeViews() +void LLInventoryPanel::initializeViews(F64 max_time) { if (!gInventory.isInventoryUsable()) return; + mViewsInitialized = VIEWS_BUILDING; + + F64 curent_time = LLTimer::getTotalSeconds(); + mBuildViewsEndTime = curent_time + max_time; + + // init everything LLUUID root_id = getRootFolderID(); if (root_id.notNull()) { @@ -884,14 +947,18 @@ void LLInventoryPanel::initializeViews() } else { - // Default case: always add "My Inventory" first, "Library" second + // Default case: always add "My Inventory" root first, "Library" root second + // If we run out of time, this still should create root folders buildNewViews(gInventory.getRootFolderID()); // My Inventory buildNewViews(gInventory.getLibraryRootFolderID()); // Library } - gIdleCallbacks.addFunction(idle, this); + if (mBuildViewsQueue.empty()) + { + mViewsInitialized = VIEWS_INITIALIZED; + } - mViewsInitialized = VIEWS_INITIALIZED; + gIdleCallbacks.addFunction(idle, this); openStartFolderOrMyInventory(); @@ -988,10 +1055,13 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id, LLInventoryO LLFolderViewItem* folder_view_item = getItemByID(id); LLFolderViewFolder* parent_folder = (LLFolderViewFolder*)getItemByID(parent_id); - return buildViewsTree(id, parent_id, objectp, folder_view_item, parent_folder); + return buildViewsTree(id, parent_id, objectp, folder_view_item, parent_folder, BUILD_TIMELIMIT); } -LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id, LLInventoryObject const* objectp, LLFolderViewItem *folder_view_item) +LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id, + LLInventoryObject const* objectp, + LLFolderViewItem *folder_view_item, + const EBuildModes &mode) { if (!objectp) { @@ -1006,14 +1076,15 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id, LLInventoryO const LLUUID &parent_id = objectp->getParentUUID(); LLFolderViewFolder* parent_folder = (LLFolderViewFolder*)getItemByID(parent_id); - return buildViewsTree(id, parent_id, objectp, folder_view_item, parent_folder); + return buildViewsTree(id, parent_id, objectp, folder_view_item, parent_folder, mode); } LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, const LLUUID& parent_id, LLInventoryObject const* objectp, LLFolderViewItem *folder_view_item, - LLFolderViewFolder *parent_folder) + LLFolderViewFolder *parent_folder, + const EBuildModes &mode) { // Force the creation of an extra root level folder item if required by the inventory panel (default is "false") bool allow_drop = true; @@ -1114,9 +1185,64 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, } } + bool create_children = folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY; + + if (create_children) + { + switch (mode) + { + case BUILD_TIMELIMIT: + { + F64 curent_time = LLTimer::getTotalSeconds(); + // If function is out of time, we want to shedule it into mBuildViewsQueue + // If we have time, no matter how little, create views for all children + // + // This creates children in 'bulk' to make sure folder has either + // 'empty and incomplete' or 'complete' states with nothing in between. + // Folders are marked as mIsFolderComplete == false by default, + // later arrange() will update mIsFolderComplete by child count + if (mBuildViewsEndTime < curent_time) + { + create_children = false; + // run it again for the sake of creating children + mBuildViewsQueue.push_back(id); + } + else + { + create_children = true; + folder_view_item->setChildrenInited(true); + } + break; + } + case BUILD_NO_CHILDREN: + { + create_children = false; + // run it to create children, current caller is only interested in current view + mBuildViewsQueue.push_back(id); + break; + } + case BUILD_ONE_FOLDER: + { + // This view loads chindren, following ones don't + // Note: Might be better idea to do 'depth' instead, + // It also will help to prioritize root folder's content + create_children = true; + folder_view_item->setChildrenInited(true); + break; + } + case BUILD_NO_LIMIT: + default: + { + // keep working till everything exists + create_children = true; + folder_view_item->setChildrenInited(true); + } + } + } + // If this is a folder, add the children of the folder and recursively add any // child folders. - if (folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY) + if (create_children) { LLViewerInventoryCategory::cat_array_t* categories; LLViewerInventoryItem::item_array_t* items; @@ -1132,7 +1258,7 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, ++cat_iter) { const LLViewerInventoryCategory* cat = (*cat_iter); - if (typedViewsFilter(cat->getUUID(), cat)) + if (typedViewsFilter(cat->getUUID(), cat)) { if (has_folders) { @@ -1140,11 +1266,11 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, // each time, especially since content is growing, we can just // iter over copy of mItemMap in some way LLFolderViewItem* view_itemp = getItemByID(cat->getUUID()); - buildViewsTree(cat->getUUID(), id, cat, view_itemp, parentp); + buildViewsTree(cat->getUUID(), id, cat, view_itemp, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode)); } else { - buildViewsTree(cat->getUUID(), id, cat, NULL, parentp); + buildViewsTree(cat->getUUID(), id, cat, NULL, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode)); } } } @@ -1156,17 +1282,16 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, item_iter != items->end(); ++item_iter) { + // At the moment we have to build folder's items in bulk and ignore mBuildViewsEndTime const LLViewerInventoryItem* item = (*item_iter); if (typedViewsFilter(item->getUUID(), item)) { - // This can be optimized: we don't need to call getItemByID() // each time, especially since content is growing, we can just // iter over copy of mItemMap in some way LLFolderViewItem* view_itemp = getItemByID(item->getUUID()); - buildViewsTree(item->getUUID(), id, item, view_itemp, parentp); + buildViewsTree(item->getUUID(), id, item, view_itemp, parentp, mode); } - } } mInventory->unlockDirectDescendentArrays(id); @@ -1294,6 +1419,18 @@ void LLInventoryPanel::onFocusReceived() LLPanel::onFocusReceived(); } +void LLInventoryPanel::onFolderOpening(const LLUUID &id) +{ + LLFolderViewItem* folder = getItemByID(id); + if (folder && !folder->areChildrenInited()) + { + // Last item in list will be processed first. + // This might result in dupplicates in list, but it + // isn't critical, views won't be created twice + mBuildViewsQueue.push_back(id); + } +} + bool LLInventoryPanel::addBadge(LLBadge * badge) { bool badge_added = false; @@ -1321,7 +1458,7 @@ void LLInventoryPanel::closeAllFolders() void LLInventoryPanel::setSelection(const LLUUID& obj_id, BOOL take_keyboard_focus) { // Don't select objects in COF (e.g. to prevent refocus when items are worn). - const LLInventoryObject *obj = gInventory.getObject(obj_id); + const LLInventoryObject *obj = mInventory->getObject(obj_id); if (obj && obj->getParentUUID() == LLAppearanceMgr::instance().getCOF()) { return; @@ -1358,6 +1495,12 @@ void LLInventoryPanel::onSelectionChange(const std::deque& it if (view_model) { LLUUID id = view_model->getUUID(); + if (!(*it)->areChildrenInited()) + { + const F64 max_time = 0.0001f; + mBuildViewsEndTime = LLTimer::getTotalSeconds() + max_time; + buildNewViews(id); + } LLViewerInventoryItem* inv_item = mInventory->getItem(id); if (inv_item && !inv_item->isFinished()) @@ -1920,6 +2063,16 @@ LLFolderViewFolder* LLInventoryPanel::getFolderByID(const LLUUID& id) void LLInventoryPanel::setSelectionByID( const LLUUID& obj_id, BOOL take_keyboard_focus ) { LLFolderViewItem* itemp = getItemByID(obj_id); + + if (itemp && !itemp->areChildrenInited()) + { + LLInventoryObject const* objectp = mInventory->getObject(obj_id); + if (objectp) + { + buildNewViews(obj_id, objectp, itemp, BUILD_ONE_FOLDER); + } + } + if(itemp && itemp->getViewModelItem() && itemp->passedFilter()) { itemp->arrangeAndSet(TRUE, take_keyboard_focus); diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 141c6ad72b..67c437a926 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -171,6 +171,7 @@ public: // LLUICtrl methods /*virtual*/ void onFocusLost(); /*virtual*/ void onFocusReceived(); + void onFolderOpening(const LLUUID &id); // LLBadgeHolder methods bool addBadge(LLBadge * badge); @@ -331,12 +332,9 @@ public: public: void addHideFolderType(LLFolderType::EType folder_type); -public: - bool getViewsInitialized() const { return mViewsInitialized == VIEWS_INITIALIZED; } - protected: // Builds the UI. Call this once the inventory is usable. - void initializeViews(); + void initializeViews(F64 max_time); // Specific inventory colors static bool sColorSetInitialized; @@ -344,13 +342,25 @@ protected: static LLUIColor sDefaultHighlightColor; static LLUIColor sLibraryColor; static LLUIColor sLinkColor; - + + enum EBuildModes + { + BUILD_NO_LIMIT, + BUILD_TIMELIMIT, // requires mBuildViewsEndTime + BUILD_ONE_FOLDER, + BUILD_NO_CHILDREN, + }; + + // All buildNewViews() use BUILD_TIMELIMIT by default + // and expect time limit mBuildViewsEndTime to be set LLFolderViewItem* buildNewViews(const LLUUID& id); LLFolderViewItem* buildNewViews(const LLUUID& id, LLInventoryObject const* objectp); LLFolderViewItem* buildNewViews(const LLUUID& id, LLInventoryObject const* objectp, - LLFolderViewItem *target_view); + LLFolderViewItem *target_view, + const EBuildModes &mode = BUILD_TIMELIMIT); + // if certain types are not allowed, no reason to create views virtual bool typedViewsFilter(const LLUUID& id, LLInventoryObject const* objectp) { return true; } @@ -367,17 +377,21 @@ private: const LLUUID& parent_id, LLInventoryObject const* objectp, LLFolderViewItem *target_view, - LLFolderViewFolder *parent_folder_view); + LLFolderViewFolder *parent_folder_view, + const EBuildModes &mode); typedef enum e_views_initialization_state { VIEWS_UNINITIALIZED = 0, VIEWS_INITIALIZING, + VIEWS_BUILDING, // Root folder exists VIEWS_INITIALIZED, } EViewsInitializationState; bool mBuildViewsOnInit; EViewsInitializationState mViewsInitialized; // Whether views have been generated + F64 mBuildViewsEndTime; // Stop building views past this timestamp + std::deque mBuildViewsQueue; }; /************************************************************************/ diff --git a/indra/newview/llkeyconflict.cpp b/indra/newview/llkeyconflict.cpp index b6107eeedf..b5ac94b1cd 100644 --- a/indra/newview/llkeyconflict.cpp +++ b/indra/newview/llkeyconflict.cpp @@ -619,74 +619,11 @@ void LLKeyConflictHandler::saveToSettings(bool temporary) } } -#if 1 - // Legacy support - // Remove #if-#endif section half a year after DRTVWR-501 releases. - // Update legacy settings in settings.xml - // We only care for third person view since legacy settings can't store - // more than one mode. - // We are saving this even if we are in temporary mode - preferences - // will restore values on cancel - if (mLoadMode == MODE_THIRD_PERSON && mHasUnsavedChanges) - { - bool value = canHandleMouse("walk_to", CLICK_DOUBLELEFT, MASK_NONE); - gSavedSettings.setBOOL("DoubleClickAutoPilot", value); - - value = canHandleMouse("walk_to", CLICK_LEFT, MASK_NONE); - gSavedSettings.setBOOL("ClickToWalk", value); - - // new method can save both toggle and push-to-talk values simultaneously, - // but legacy one can save only one. It also doesn't support mask. - LLKeyData data = getControl("toggle_voice", 0); - bool can_toggle = !data.isEmpty(); - if (!can_toggle) - { - data = getControl("voice_follow_key", 0); - } - - gSavedSettings.setBOOL("PushToTalkToggle", can_toggle); - if (data.isEmpty()) - { - // legacy viewer has a bug that might crash it if NONE value is assigned. - // just reset to default - gSavedSettings.getControl("PushToTalkButton")->resetToDefault(false); - } - else - { - if (data.mKey != KEY_NONE) - { - gSavedSettings.setString("PushToTalkButton", LLKeyboard::stringFromKey(data.mKey)); - } - else - { - std::string ctrl_value; - switch (data.mMouse) - { - case CLICK_MIDDLE: - ctrl_value = "MiddleMouse"; - break; - case CLICK_BUTTON4: - ctrl_value = "MouseButton4"; - break; - case CLICK_BUTTON5: - ctrl_value = "MouseButton5"; - break; - default: - ctrl_value = "MiddleMouse"; - break; - } - gSavedSettings.setString("PushToTalkButton", ctrl_value); - } - } - } -#endif - if (mLoadMode == MODE_THIRD_PERSON && mHasUnsavedChanges) { // Map floater should react to doubleclick if doubleclick for teleport is set - // Todo: Seems conterintuitive for map floater to share inworld controls - // after these changes release, discuss with UI UX engineer if this should just - // be set to 1 by default (before release this also doubles as legacy support) + // Todo: Seems conterintuitive for map floater to share inworld controls, + // discuss with UI UX engineer if this should just be set to 1 by default bool value = canHandleMouse("teleport_to", CLICK_DOUBLELEFT, MASK_NONE); gSavedSettings.setBOOL("DoubleClickTeleport", value); } diff --git a/indra/newview/llmanip.cpp b/indra/newview/llmanip.cpp index bf4579c756..e974c00c37 100644 --- a/indra/newview/llmanip.cpp +++ b/indra/newview/llmanip.cpp @@ -615,9 +615,11 @@ void LLManip::renderTickValue(const LLVector3& pos, F32 value, const std::string gGL.scalef(inv_zoom_amt, inv_zoom_amt, inv_zoom_amt); } - LLColor4 shadow_color = LLColor4::black; - shadow_color.mV[VALPHA] = color.mV[VALPHA] * 0.5f; - + // Unused and GCC does not like this ... + //LLColor4 shadow_color = LLColor4::black; + //shadow_color.mV[VALPHA] = color.mV[VALPHA] * 0.5f; + // getDecimalPoint(), fractional_portion, suffix.c_str()); diff --git a/indra/newview/llmaniprotate.cpp b/indra/newview/llmaniprotate.cpp index 7c942e8b53..3566ca7a35 100644 --- a/indra/newview/llmaniprotate.cpp +++ b/indra/newview/llmaniprotate.cpp @@ -1556,7 +1556,10 @@ LLQuaternion LLManipRotate::dragConstrained( S32 x, S32 y ) LLVector3 object_axis; getObjectAxisClosestToMouse(object_axis); - object_axis = object_axis * first_object_node->mSavedRotation; + if (first_object_node) + { + object_axis = object_axis * first_object_node->mSavedRotation; + } // project onto constraint plane object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis(); diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index 51fae3ffd2..6c89d394ee 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -1026,6 +1026,12 @@ void LLMarketplaceData::createSLMListingCoro(LLUUID folderId, LLUUID versionId, log_SLM_infos("Post /listings", status.getType(), result); + if (!result.has("listings") || !result["listings"].isArray() || result["listings"].size() == 0) + { + LL_INFOS("SLM") << "Received an empty response for folder " << folderId << LL_ENDL; + return; + } + // Extract the info from the results for (LLSD::array_iterator it = result["listings"].beginArray(); it != result["listings"].endArray(); ++it) @@ -1093,6 +1099,19 @@ void LLMarketplaceData::updateSLMListingCoro(LLUUID folderId, S32 listingId, LLU log_SLM_infos("Put /listing", status.getType(), result); + if (!result.has("listings") || !result["listings"].isArray() || result["listings"].size() == 0) + { + LL_INFOS("SLM") << "Received an empty response for listing " << listingId << " folder " << folderId << LL_ENDL; + // Try to get listing more directly after a delay + const float FORCE_UPDATE_TIMEOUT = 5.0; + llcoro::suspendUntilTimeout(FORCE_UPDATE_TIMEOUT); + if (!LLApp::isExiting() && LLMarketplaceData::instanceExists()) + { + getSLMListing(listingId); + } + return; + } + // Extract the info from the Json string for (LLSD::array_iterator it = result["listings"].beginArray(); it != result["listings"].endArray(); ++it) diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index b6bd6ce76e..4a5b6243f3 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -46,6 +46,7 @@ #include "lluictrlfactory.h" // LLDefaultChildRegistry #include "llkeyboard.h" #include "llviewermenu.h" +#include "llviewermenufile.h" // LLFilePickerThread // linden library includes #include "llfocusmgr.h" @@ -105,7 +106,8 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) : mTrusted(p.trusted_content), mWindowShade(NULL), mHoverTextChanged(false), - mContextMenu(NULL) + mContextMenu(NULL), + mAllowFileDownload(false) { { LLColor4 color = p.caret_color().get(); @@ -1165,8 +1167,23 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) case MEDIA_EVENT_FILE_DOWNLOAD: { - //llinfos << "Media event - file download requested - filename is " << self->getFileDownloadFilename() << llendl; - //LLNotificationsUtil::add("MediaFileDownloadUnsupported"); + if (mAllowFileDownload) + { + // pick a file from SAVE FILE dialog + // for now the only thing that should be allowed to save is 360s + std::string suggested_filename = self->getFileDownloadFilename(); + LLFilePicker::ESaveFilter filter = LLFilePicker::FFSAVE_ALL; + if (suggested_filename.find(".jpg") != std::string::npos || suggested_filename.find(".jpeg") != std::string::npos) + filter = LLFilePicker::FFSAVE_JPEG; + if (suggested_filename.find(".png") != std::string::npos) + filter = LLFilePicker::FFSAVE_PNG; + + (new LLMediaFilePicker(self, filter, suggested_filename))->getFile(); + } + else + { + LLNotificationsUtil::add("MediaFileDownloadUnsupported"); + } }; break; diff --git a/indra/newview/llmediactrl.h b/indra/newview/llmediactrl.h index b0baeb0ca6..2372e3d7b0 100644 --- a/indra/newview/llmediactrl.h +++ b/indra/newview/llmediactrl.h @@ -151,6 +151,8 @@ public: void setTrustedContent(bool trusted); + void setAllowFileDownload(bool allow) { mAllowFileDownload = allow; } + // over-rides virtual BOOL handleKeyHere(KEY key, MASK mask); virtual BOOL handleKeyUpHere(KEY key, MASK mask); @@ -206,7 +208,8 @@ public: mClearCache, mHoverTextChanged, mDecoupleTextureSize, - mUpdateScrolls; + mUpdateScrolls, + mAllowFileDownload; std::string mHomePageUrl, mHomePageMimeType, diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 6409b29e00..aa2a1325e8 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -259,27 +259,14 @@ LLModelPreview::~LLModelPreview() } } -U32 LLModelPreview::calcResourceCost() +void LLModelPreview::updateDimentionsAndOffsets() { assert_main_thread(); rebuildUploadData(); - //Upload skin is selected BUT check to see if the joints coming in from the asset were malformed. - if (mFMP && mFMP->childGetValue("upload_skin").asBoolean()) - { - bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean(); - if (uploadingJointPositions && !isRigValidForJointPositionUpload()) - { - mFMP->childDisable("ok_btn"); - } - } - std::set accounted; - U32 num_points = 0; - U32 num_hulls = 0; - F32 debug_scale = mFMP ? mFMP->childGetValue("import_scale").asReal() : 1.f; mPelvisZOffset = mFMP ? mFMP->childGetValue("pelvis_offset").asReal() : 3.0f; if (mFMP && mFMP->childGetValue("upload_joints").asBoolean()) @@ -291,8 +278,6 @@ U32 LLModelPreview::calcResourceCost() getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id); } - F32 streaming_cost = 0.f; - F32 physics_cost = 0.f; for (U32 i = 0; i < mUploadData.size(); ++i) { LLModelInstance& instance = mUploadData[i]; @@ -301,11 +286,6 @@ U32 LLModelPreview::calcResourceCost() { accounted.insert(instance.mModel); - LLModel::Decomposition& decomp = - instance.mLOD[LLModel::LOD_PHYSICS] ? - instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : - instance.mModel->mPhysics; - //update instance skin info for each lods pelvisZoffset for (int j = 0; jmSkinInfo.mPelvisOffset = mPelvisZOffset; } } - - std::stringstream ostr; - LLSD ret = LLModel::writeModel(ostr, - instance.mLOD[4], - instance.mLOD[3], - instance.mLOD[2], - instance.mLOD[1], - instance.mLOD[0], - decomp, - mFMP->childGetValue("upload_skin").asBoolean(), - mFMP->childGetValue("upload_joints").asBoolean(), - mFMP->childGetValue("lock_scale_if_joint_position").asBoolean(), - TRUE, - FALSE, - instance.mModel->mSubmodelID); - - num_hulls += decomp.mHull.size(); - for (U32 i = 0; i < decomp.mHull.size(); ++i) - { - num_points += decomp.mHull[i].size(); - } - - //calculate streaming cost - LLMatrix4 transformation = instance.mTransform; - - LLVector3 position = LLVector3(0, 0, 0) * transformation; - - LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; - LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; - LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; - F32 x_length = x_transformed.normalize(); - F32 y_length = y_transformed.normalize(); - F32 z_length = z_transformed.normalize(); - LLVector3 scale = LLVector3(x_length, y_length, z_length); - - F32 radius = scale.length()*0.5f*debug_scale; - - LLMeshCostData costs; - if (gMeshRepo.getCostData(ret, costs)) - { - streaming_cost += costs.getRadiusBasedStreamingCost(radius); - } } } F32 scale = mFMP ? mFMP->childGetValue("import_scale").asReal()*2.f : 2.f; - mDetailsSignal(mPreviewScale[0] * scale, mPreviewScale[1] * scale, mPreviewScale[2] * scale, streaming_cost, physics_cost); + mDetailsSignal((F32)(mPreviewScale[0] * scale), (F32)(mPreviewScale[1] * scale), (F32)(mPreviewScale[2] * scale)); updateStatusMessages(); - - return (U32)streaming_cost; } // relocate from llmodel and rewrite so it does what it is meant to @@ -390,6 +326,18 @@ bool LLModelPreview::matchMaterialOrder(LLModel* lod, LLModel* ref, int& refFace return false; } + if (lod->mMaterialList.size() > ref->mMaterialList.size()) + { + LL_DEBUGS("MESHSKININFO") << "Material of model has more materials than a reference." << LL_ENDL; + std::ostringstream out; + out << "LOD model " << lod->getName() << " has more materials than the High LOD (reference) model " << ref->getName(); + LL_DEBUGS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + // We passed isMaterialListSubset, so materials are a subset, but subset isn't supposed to be + // larger than original and if we keep going, reordering will cause a crash + return false; + } + LL_DEBUGS("MESHSKININFO") << "subset check passed." << LL_ENDL; std::map index_map; @@ -1942,8 +1890,6 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim } } - mResourceCost = calcResourceCost(); - LLVertexBuffer::unbind(); LLGLSLShader::sNoFixedFunction = no_ff; if (shader) @@ -2913,9 +2859,8 @@ void LLModelPreview::update() if (mDirty && mLodsQuery.empty()) { mDirty = false; - mResourceCost = calcResourceCost(); + updateDimentionsAndOffsets(); refresh(); - updateStatusMessages(); } } @@ -3248,8 +3193,6 @@ BOOL LLModelPreview::render() // auto enable weight upload if weights are present // (note: all these UI updates need to be somewhere that is not render) // BUG-229632 auto enable weights slows manual workflow - // mViewOption["show_skin_weight"] = true; - // skin_weight = true; // fmp->childSetValue("upload_skin", true); LL_DEBUGS("MeshUpload") << "FSU auto_enable_weights_upload = " << auto_enable_weight_upload() << LL_ENDL; LL_DEBUGS("MeshUpload") << "FSU auto_enable_show_weights = " << auto_enable_show_weights() << LL_ENDL; diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h index 2cfee9e3cb..7c1a0efb95 100644 --- a/indra/newview/llmodelpreview.h +++ b/indra/newview/llmodelpreview.h @@ -115,7 +115,7 @@ class LLModelPreview : public LLViewerDynamicTexture, public LLMutex { LOG_CLASS(LLModelPreview); - typedef boost::signals2::signal details_signal_t; + typedef boost::signals2::signal details_signal_t; typedef boost::signals2::signal model_loaded_signal_t; typedef boost::signals2::signal model_updated_signal_t; @@ -159,7 +159,7 @@ public: void genLODs(S32 which_lod = -1, U32 decimation = 3, bool enforce_tri_limit = false); void generateNormals(); void restoreNormals(); - U32 calcResourceCost(); + void updateDimentionsAndOffsets(); void rebuildUploadData(); void saveUploadData(bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position); void saveUploadData(const std::string& filename, bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position); @@ -241,7 +241,6 @@ protected: LLVector3 mPreviewScale; S32 mPreviewLOD; S32 mPhysicsSearchLOD; - U32 mResourceCost; std::string mLODFile[LLModel::NUM_LODS]; bool mLoading; U32 mLoadState; diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index de04ec5e63..25ed7f662f 100644 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -427,6 +427,7 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow( if (cell) { cell->setValue(prefix + fullname); + cell->setAltValue(name_item.alt_value()); } dirtyColumns(); diff --git a/indra/newview/llnotificationalerthandler.cpp b/indra/newview/llnotificationalerthandler.cpp index 1058c3836c..e5c0199065 100644 --- a/indra/newview/llnotificationalerthandler.cpp +++ b/indra/newview/llnotificationalerthandler.cpp @@ -73,7 +73,7 @@ void LLAlertHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLAlertHandler::processNotification(const LLNotificationPtr& notification) +bool LLAlertHandler::processNotification(const LLNotificationPtr& notification, bool should_log) { if(mChannel.isDead()) { @@ -146,7 +146,7 @@ LLViewerAlertHandler::LLViewerAlertHandler(const std::string& name, const std::s { } -bool LLViewerAlertHandler::processNotification(const LLNotificationPtr& p) +bool LLViewerAlertHandler::processNotification(const LLNotificationPtr& p, bool should_log) { if (gHeadlessClient) { diff --git a/indra/newview/llnotificationgrouphandler.cpp b/indra/newview/llnotificationgrouphandler.cpp index aec9dff923..4615518831 100644 --- a/indra/newview/llnotificationgrouphandler.cpp +++ b/indra/newview/llnotificationgrouphandler.cpp @@ -62,7 +62,7 @@ void LLGroupHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLGroupHandler::processNotification(const LLNotificationPtr& notification) +bool LLGroupHandler::processNotification(const LLNotificationPtr& notification, bool should_log) { if(mChannel.isDead()) { diff --git a/indra/newview/llnotificationhandler.h b/indra/newview/llnotificationhandler.h index 7be8c5d0cb..9703bb7626 100644 --- a/indra/newview/llnotificationhandler.h +++ b/indra/newview/llnotificationhandler.h @@ -101,10 +101,10 @@ public: // base interface functions virtual void onAdd(LLNotificationPtr p) { processNotification(p); } virtual void onChange(LLNotificationPtr p) { processNotification(p); } - virtual void onLoad(LLNotificationPtr p) { processNotification(p); } + virtual void onLoad(LLNotificationPtr p) { processNotification(p, false); } virtual void onDelete(LLNotificationPtr p) { if (mChannel.get()) mChannel.get()->removeToastByNotificationID(p->getID());} - virtual bool processNotification(const LLNotificationPtr& notify) = 0; + virtual bool processNotification(const LLNotificationPtr& notify, bool should_log = true) = 0; }; class LLSystemNotificationHandler : public LLNotificationHandler @@ -141,7 +141,7 @@ class LLIMHandler : public LLCommunicationNotificationHandler public: LLIMHandler(); virtual ~LLIMHandler(); - bool processNotification(const LLNotificationPtr& p); + bool processNotification(const LLNotificationPtr& p, bool should_log = true); protected: virtual void initChannel(); @@ -157,7 +157,7 @@ public: LLTipHandler(); virtual ~LLTipHandler(); - virtual bool processNotification(const LLNotificationPtr& p); + virtual bool processNotification(const LLNotificationPtr& p, bool should_log = true); protected: virtual void initChannel(); @@ -175,7 +175,7 @@ public: virtual void onDelete(LLNotificationPtr p); virtual void onChange(LLNotificationPtr p); - virtual bool processNotification(const LLNotificationPtr& p); + virtual bool processNotification(const LLNotificationPtr& p, bool should_log = true); virtual void addToastWithNotification(const LLNotificationPtr& p); protected: @@ -193,7 +193,7 @@ public: LLGroupHandler(); virtual ~LLGroupHandler(); - virtual bool processNotification(const LLNotificationPtr& p); + virtual bool processNotification(const LLNotificationPtr& p, bool should_log = true); protected: virtual void initChannel(); @@ -209,7 +209,7 @@ public: virtual ~LLAlertHandler(); virtual void onChange(LLNotificationPtr p); - virtual bool processNotification(const LLNotificationPtr& p); + virtual bool processNotification(const LLNotificationPtr& p, bool should_log = true); protected: virtual void initChannel(); @@ -225,7 +225,7 @@ public: virtual ~LLViewerAlertHandler() {}; virtual void onDelete(LLNotificationPtr p) {}; - virtual bool processNotification(const LLNotificationPtr& p); + virtual bool processNotification(const LLNotificationPtr& p, bool should_log = true); protected: virtual void initChannel() {}; @@ -243,7 +243,7 @@ public: virtual void onChange(LLNotificationPtr p); virtual void onDelete(LLNotificationPtr notification); - virtual bool processNotification(const LLNotificationPtr& p); + virtual bool processNotification(const LLNotificationPtr& p, bool should_log = true); protected: virtual void initChannel(); @@ -261,7 +261,7 @@ public: virtual void onAdd(LLNotificationPtr p); virtual void onLoad(LLNotificationPtr p); virtual void onDelete(LLNotificationPtr p); - virtual bool processNotification(const LLNotificationPtr& p); + virtual bool processNotification(const LLNotificationPtr& p, bool should_log = true); protected: virtual void initChannel() {}; @@ -276,7 +276,7 @@ public: LLBrowserNotification(); virtual ~LLBrowserNotification() {} - virtual bool processNotification(const LLNotificationPtr& p); + virtual bool processNotification(const LLNotificationPtr& p, bool should_log = true); protected: virtual void initChannel() {}; diff --git a/indra/newview/llnotificationhandlerutil.cpp b/indra/newview/llnotificationhandlerutil.cpp index 265fe6af48..512b7a28c7 100644 --- a/indra/newview/llnotificationhandlerutil.cpp +++ b/indra/newview/llnotificationhandlerutil.cpp @@ -209,12 +209,6 @@ void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification, bool to_fi void LLHandlerUtil::logGroupNoticeToIMGroup( const LLNotificationPtr& notification) { - // FIRE-11339: Persisted group notifications get logged to IM on each login - if (notification->isFromStorage()) - { - return; - } - // const LLSD& payload = notification->getPayload(); LLGroupData groupData; diff --git a/indra/newview/llnotificationhinthandler.cpp b/indra/newview/llnotificationhinthandler.cpp index f1226c53ff..44ebc5ed47 100644 --- a/indra/newview/llnotificationhinthandler.cpp +++ b/indra/newview/llnotificationhinthandler.cpp @@ -53,7 +53,7 @@ void LLHintHandler::onDelete(LLNotificationPtr p) LLHints::getInstance()->hide(p); } -bool LLHintHandler::processNotification(const LLNotificationPtr& p) +bool LLHintHandler::processNotification(const LLNotificationPtr& p, bool should_log) { return false; } diff --git a/indra/newview/llnotificationofferhandler.cpp b/indra/newview/llnotificationofferhandler.cpp index faa0cf2e23..16e9ceae63 100644 --- a/indra/newview/llnotificationofferhandler.cpp +++ b/indra/newview/llnotificationofferhandler.cpp @@ -71,7 +71,7 @@ void LLOfferHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLOfferHandler::processNotification(const LLNotificationPtr& notification) +bool LLOfferHandler::processNotification(const LLNotificationPtr& notification, bool should_log) { if(mChannel.isDead()) { diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp index 149d50c93a..01d20ac6fb 100644 --- a/indra/newview/llnotificationscripthandler.cpp +++ b/indra/newview/llnotificationscripthandler.cpp @@ -96,7 +96,7 @@ void LLScriptHandler::addToastWithNotification(const LLNotificationPtr& notifica } //-------------------------------------------------------------------------- -bool LLScriptHandler::processNotification(const LLNotificationPtr& notification) +bool LLScriptHandler::processNotification(const LLNotificationPtr& notification, bool should_log) { if(mChannel.isDead()) { @@ -109,7 +109,7 @@ bool LLScriptHandler::processNotification(const LLNotificationPtr& notification) initChannel(); } - if (notification->canLogToIM()) + if (should_log && notification->canLogToIM()) { LLHandlerUtil::logToIMP2P(notification); } diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp index 4415b60632..469a946d20 100644 --- a/indra/newview/llnotificationtiphandler.cpp +++ b/indra/newview/llnotificationtiphandler.cpp @@ -69,7 +69,7 @@ void LLTipHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLTipHandler::processNotification(const LLNotificationPtr& notification) +bool LLTipHandler::processNotification(const LLNotificationPtr& notification, bool should_log) { if(mChannel.isDead()) { diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp index dfcfab5f1c..11fe7a82c7 100644 --- a/indra/newview/lloutfitgallery.cpp +++ b/indra/newview/lloutfitgallery.cpp @@ -61,6 +61,8 @@ static LLPanelInjector t_outfit_gallery("outfit_gallery"); #define MAX_OUTFIT_PHOTO_WIDTH 256 #define MAX_OUTFIT_PHOTO_HEIGHT 256 +const S32 GALLERY_ITEMS_PER_ROW_MIN = 2; + LLOutfitGallery::LLOutfitGallery(const LLOutfitGallery::Params& p) : LLOutfitListBase(), mTexturesObserver(NULL), @@ -95,7 +97,7 @@ LLOutfitGallery::Params::Params() item_width("item_width", 150), item_height("item_height", 175), item_horizontal_gap("item_horizontal_gap", 16), - items_in_row("items_in_row", 3), + items_in_row("items_in_row", GALLERY_ITEMS_PER_ROW_MIN), row_panel_width_factor("row_panel_width_factor", 166), gallery_width_factor("gallery_width_factor", 163) { @@ -153,10 +155,7 @@ void LLOutfitGallery::updateRowsIfNeeded() { reArrangeRows(1); } - // Fix scroll bars if appearance floater at minimum width - //else if((mRowPanelWidth > (getRect().getWidth() + mItemHorizontalGap)) && mItemsInRow > 2) - else if((mRowPanelWidth > (getRect().getWidth() + mItemHorizontalGap)) && mItemsInRow > 2) - // + else if((mRowPanelWidth > (getRect().getWidth() + mItemHorizontalGap)) && mItemsInRow > GALLERY_ITEMS_PER_ROW_MIN) { reArrangeRows(-1); } diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp index 4d244b5225..49aefade8e 100644 --- a/indra/newview/llpanelgroupnotices.cpp +++ b/indra/newview/llpanelgroupnotices.cpp @@ -313,8 +313,11 @@ BOOL LLPanelGroupNotices::postBuild() void LLPanelGroupNotices::activate() { - if(mNoticesList) - mNoticesList->deleteAllItems(); + if (mNoticesList) + { + mNoticesList->deleteAllItems(); + mKnownNoticeIds.clear(); + } mPrevSelectedNotice = LLUUID(); @@ -422,6 +425,7 @@ void LLPanelGroupNotices::onClickSendMessage(void* data) row["columns"][4]["value"] = llformat( "%u", timestamp); self->mNoticesList->addElement(row, ADD_BOTTOM); + self->mKnownNoticeIds.insert(id); self->mCreateMessage->clear(); self->mCreateSubject->clear(); @@ -452,29 +456,13 @@ void LLPanelGroupNotices::onClickNewMessage(void* data) void LLPanelGroupNotices::refreshNotices() { onClickRefreshNotices(this); - /* - LL_DEBUGS() << "LLPanelGroupNotices::onClickGetPastNotices" << LL_ENDL; - - mNoticesList->deleteAllItems(); - - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("GroupNoticesListRequest"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID",gAgent.getID()); - msg->addUUID("SessionID",gAgent.getSessionID()); - msg->nextBlock("Data"); - msg->addUUID("GroupID",self->mGroupID); - gAgent.sendReliableMessage(); - */ - } void LLPanelGroupNotices::clearNoticeList() { mPrevSelectedNotice = mNoticesList->getStringUUIDSelectedItem(); mNoticesList->deleteAllItems(); - // FIRE-30766 group hang prevention. - mNoticeIDs.clear(); + mKnownNoticeIds.clear(); } void LLPanelGroupNotices::onClickRefreshNotices(void* data) @@ -553,16 +541,14 @@ void LLPanelGroupNotices::processNotices(LLMessageSystem* msg) return; } - //with some network delays we can receive notice list more then once... - //so add only unique notices - // FIRE-30667 et al. add a set for tracking to avoid the linear time lookup - // S32 pos = mNoticesList->getItemIndex(id); - // if(pos!=-1)//if items with this ID already in the list - skip it - auto exists = mNoticeIDs.emplace(id); - if (!exists.second) - // - continue; - + // Due to some network delays we can receive notice list more than once... + // So add only unique notices + if (mKnownNoticeIds.find(id) != mKnownNoticeIds.end()) + { + // If items with this ID already in the list - skip it + continue; + } + msg->getString("Data","Subject",subj,i); msg->getString("Data","FromName",name,i); msg->getBOOL("Data","HasAttachment",has_attachment,i); @@ -600,6 +586,7 @@ void LLPanelGroupNotices::processNotices(LLMessageSystem* msg) row["columns"][4]["value"] = llformat( "%u", timestamp); mNoticesList->addElement(row, ADD_BOTTOM); + mKnownNoticeIds.insert(id); } mNoticesList->setNeedsSort(true); diff --git a/indra/newview/llpanelgroupnotices.h b/indra/newview/llpanelgroupnotices.h index 3eafdd3e36..23ffd9d5bd 100644 --- a/indra/newview/llpanelgroupnotices.h +++ b/indra/newview/llpanelgroupnotices.h @@ -112,7 +112,7 @@ private: //LLIconCtrl *mViewInventoryIcon; LLScrollListCtrl *mNoticesList; - std::set mNoticeIDs; // FS:Beq FIRE-30667 group notices hang + std::set mKnownNoticeIds; // Dupplicate avoidance, to avoid searching and inserting dupplciates into mNoticesList std::string mNoNoticesStr; diff --git a/indra/newview/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp index 880323ce16..834e664723 100644 --- a/indra/newview/llpanellandmarkinfo.cpp +++ b/indra/newview/llpanellandmarkinfo.cpp @@ -371,6 +371,11 @@ void LLPanelLandmarkInfo::toggleLandmarkEditMode(BOOL enabled) setFocus(TRUE); } +void LLPanelLandmarkInfo::setCanEdit(BOOL enabled) +{ + getChild("edit_btn")->setEnabled(enabled); +} + const std::string& LLPanelLandmarkInfo::getLandmarkTitle() const { return mLandmarkTitleEditor->getText(); diff --git a/indra/newview/llpanellandmarkinfo.h b/indra/newview/llpanellandmarkinfo.h index f727f286b5..46e2a1935b 100644 --- a/indra/newview/llpanellandmarkinfo.h +++ b/indra/newview/llpanellandmarkinfo.h @@ -56,6 +56,7 @@ public: void displayItemInfo(const LLInventoryItem* pItem); void toggleLandmarkEditMode(BOOL enabled); + void setCanEdit(BOOL enabled); const std::string& getLandmarkTitle() const; const std::string getLandmarkNotes() const; diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 64952fad4f..c8d144ef8a 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -486,8 +486,14 @@ void LLLandmarksPanel::initLandmarksPanel(LLPlacesInventoryPanel* inventory_list LLPlacesFolderView* root_folder = dynamic_cast(inventory_list->getRootFolder()); if (root_folder) { - root_folder->setupMenuHandle(LLInventoryType::IT_CATEGORY, mGearFolderMenu->getHandle()); - root_folder->setupMenuHandle(LLInventoryType::IT_LANDMARK, mGearLandmarkMenu->getHandle()); + if (mGearFolderMenu) + { + root_folder->setupMenuHandle(LLInventoryType::IT_CATEGORY, mGearFolderMenu->getHandle()); + } + if (mGearLandmarkMenu) + { + root_folder->setupMenuHandle(LLInventoryType::IT_LANDMARK, mGearLandmarkMenu->getHandle()); + } root_folder->setParentLandmarksPanel(this); } @@ -510,13 +516,23 @@ void LLLandmarksPanel::initListCommandsHandlers() mSortingMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_places_gear_sorting.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); mAddMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_place_add_button.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - mGearLandmarkMenu->setVisibilityChangeCallback(boost::bind(&LLLandmarksPanel::onMenuVisibilityChange, this, _1, _2)); - mGearFolderMenu->setVisibilityChangeCallback(boost::bind(&LLLandmarksPanel::onMenuVisibilityChange, this, _1, _2)); + if (mGearLandmarkMenu) + { + mGearLandmarkMenu->setVisibilityChangeCallback(boost::bind(&LLLandmarksPanel::onMenuVisibilityChange, this, _1, _2)); + // show menus even if all items are disabled + mGearLandmarkMenu->setAlwaysShowMenu(TRUE); + } // Else corrupted files? - // show menus even if all items are disabled - mGearLandmarkMenu->setAlwaysShowMenu(TRUE); - mGearFolderMenu->setAlwaysShowMenu(TRUE); - mAddMenu->setAlwaysShowMenu(TRUE); + if (mGearFolderMenu) + { + mGearFolderMenu->setVisibilityChangeCallback(boost::bind(&LLLandmarksPanel::onMenuVisibilityChange, this, _1, _2)); + mGearFolderMenu->setAlwaysShowMenu(TRUE); + } + + if (mAddMenu) + { + mAddMenu->setAlwaysShowMenu(TRUE); + } } void LLLandmarksPanel::updateMenuVisibility(LLUICtrl* menu) @@ -1132,7 +1148,10 @@ void LLLandmarksPanel::doShowOnMap(LLLandmark* landmark) } mShowOnMapBtn->setEnabled(TRUE); // FIRE-31033: Keep Teleport/Map/Profile buttons on places floater - mGearLandmarkMenu->setItemEnabled("show_on_map", TRUE); + if (mGearLandmarkMenu) + { + mGearLandmarkMenu->setItemEnabled("show_on_map", TRUE); + } } void LLLandmarksPanel::doProcessParcelInfo(LLLandmark* landmark, diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 66327eb7dc..b46d79ac43 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -238,7 +238,6 @@ BOOL LLPanelMainInventory::postBuild() recent_items_panel->setSortOrder(gSavedSettings.getU32(LLInventoryPanel::RECENTITEMS_SORT_ORDER)); // recent_items_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - //recent_items_panel->setFilterLinks(LLInventoryFilter::FILTERLINK_EXCLUDE_LINKS); // Undo nonsense change LLInventoryFilter& recent_filter = recent_items_panel->getFilter(); recent_filter.setFilterObjectTypes(recent_filter.getFilterObjectTypes() & ~(0x1 << LLInventoryType::IT_CATEGORY)); recent_filter.setEmptyLookupMessage("InventoryNoMatchingRecentItems"); diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index a670abf086..d5afe9a853 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -1793,6 +1793,7 @@ void LLPanelObjectInventory::createViewsForCategory(LLInventoryObject::object_li child_categories[i]->second ); delete child_categories[i]; } + folder->setChildrenInited(true); } void LLPanelObjectInventory::refresh() diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index 1ab1179361..75318e4491 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -511,10 +511,15 @@ void LLPanelPlaces::onOpen(const LLSD& key) { mLandmarkInfo->setInfoType(LLPanelPlaceInfo::LANDMARK); - LLInventoryItem* item = gInventory.getItem(key["id"].asUUID()); + LLUUID id = key["id"].asUUID(); + LLInventoryItem* item = gInventory.getItem(id); if (!item) return; + BOOL is_editable = gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID()) + && item->getPermissions().allowModifyBy(gAgent.getID()); + mLandmarkInfo->setCanEdit(is_editable); + setItem(item); } else if (mPlaceInfoType == REMOTE_PLACE_INFO_TYPE) diff --git a/indra/newview/llpersistentnotificationstorage.cpp b/indra/newview/llpersistentnotificationstorage.cpp index 0020bab035..ed68a1cc08 100644 --- a/indra/newview/llpersistentnotificationstorage.cpp +++ b/indra/newview/llpersistentnotificationstorage.cpp @@ -153,10 +153,7 @@ void LLPersistentNotificationStorage::loadNotifications() LLNotificationResponderPtr responder(createResponder(notification_params["name"], notification_params["responder"])); notification->setResponseFunctor(responder); - // FIRE-11339: Persisted group notifications get logged to IM on each login - notification->setIsFromStorage(true); - - instance.add(notification); + instance.load(notification); // hide script floaters so they don't confuse the user and don't overlap startup toast LLScriptFloaterManager::getInstance()->setFloaterVisible(notification->getID(), false); diff --git a/indra/newview/llpresetsmanager.cpp b/indra/newview/llpresetsmanager.cpp index b25b79c5a2..459f9ea275 100644 --- a/indra/newview/llpresetsmanager.cpp +++ b/indra/newview/llpresetsmanager.cpp @@ -711,7 +711,7 @@ void LLPresetsManager::handleGraphicPresetControlChanged(LLControlVariablePtr co if (!mIsLoadingPreset && (!mIsDrawDistanceSteppingActive || control->getName() != "RenderFarClip") && - (!FSPerfStats::autoTune) ) + (!FSPerfStats::tunables.userAutoTuneEnabled) ) { LL_DEBUGS() << "Trigger graphic preset control changed signal" << LL_ENDL; diff --git a/indra/newview/llpreview.h b/indra/newview/llpreview.h index 869d46231b..9ac15d1639 100644 --- a/indra/newview/llpreview.h +++ b/indra/newview/llpreview.h @@ -104,10 +104,7 @@ public: // llview /*virtual*/ void draw(); -// void refreshFromItem(); -// [SL:KB] - Patch: Build-ScriptRecover | Checked: 2012-02-06 (Catznip-3.2.1) | Added: Catznip-3.2.1 virtual void refreshFromItem(); -// [/SL:KB] // We can't modify Item or description in preview if either in-world Object // or Item itself is unmodifiable diff --git a/indra/newview/llpreviewanim.cpp b/indra/newview/llpreviewanim.cpp index b9a27dc799..cfc7b8aa9b 100644 --- a/indra/newview/llpreviewanim.cpp +++ b/indra/newview/llpreviewanim.cpp @@ -44,8 +44,7 @@ extern LLAgent gAgent; //const S32 ADVANCED_VPAD = 3; // Improved animation preview LLPreviewAnim::LLPreviewAnim(const LLSD& key) - : LLPreview( key ), - pMotion(NULL) + : LLPreview( key ) { mCommitCallbackRegistrar.add("PreviewAnim.Play", boost::bind(&LLPreviewAnim::play, this, _2)); } @@ -53,13 +52,6 @@ LLPreviewAnim::LLPreviewAnim(const LLSD& key) // virtual BOOL LLPreviewAnim::postBuild() { - const LLInventoryItem* item = getItem(); - if(item) - { - pMotion = gAgentAvatarp->createMotion(item->getAssetUUID()); // preload the animation - getChild("desc")->setValue(item->getDescription()); - } - childSetCommitCallback("desc", LLPreview::onText, this); getChild("desc")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe); // Improved animation preview @@ -70,23 +62,11 @@ BOOL LLPreviewAnim::postBuild() //pAdvancedStatsTextBox->setVisible(FALSE); //LLRect rect = getRect(); //reshape(rect.getWidth(), rect.getHeight() - pAdvancedStatsTextBox->getRect().getHeight() - ADVANCED_VPAD, FALSE); - if (pMotion) - { - LLTextBox* stats_box_left = getChild("AdvancedStatsLeft"); - LLTextBox* stats_box_right = getChild("AdvancedStatsRight"); - stats_box_left->setTextArg("[PRIORITY]", llformat("%d", pMotion->getPriority())); - stats_box_left->setTextArg("[DURATION]", llformat("%.2f", pMotion->getDuration())); - stats_box_left->setTextArg("[IS_LOOP]", (pMotion->getLoop() ? LLTrans::getString("PermYes") : LLTrans::getString("PermNo"))); - stats_box_right->setTextArg("[EASE_IN]", llformat("%.2f", pMotion->getEaseInDuration())); - stats_box_right->setTextArg("[EASE_OUT]", llformat("%.2f", pMotion->getEaseOutDuration())); - stats_box_right->setTextArg("[NUM_JOINTS]", llformat("%d", pMotion->getNumJointMotions())); - } // return LLPreview::postBuild(); } -// static // llinventorybridge also calls into here void LLPreviewAnim::play(const LLSD& param) { @@ -184,6 +164,34 @@ void LLPreviewAnim::draw() } // virtual +void LLPreviewAnim::refreshFromItem() +{ + const LLInventoryItem* item = getItem(); + if (!item) + { + return; + } + + // Preload motion + // Improved animation preview + //gAgentAvatarp->createMotion(item->getAssetUUID()); + LLMotion *motion = gAgentAvatarp->createMotion(item->getAssetUUID()); + if (motion) + { + LLTextBox* stats_box_left = getChild("AdvancedStatsLeft"); + LLTextBox* stats_box_right = getChild("AdvancedStatsRight"); + stats_box_left->setTextArg("[PRIORITY]", llformat("%d", motion->getPriority())); + stats_box_left->setTextArg("[DURATION]", llformat("%.2f", motion->getDuration())); + stats_box_left->setTextArg("[IS_LOOP]", (motion->getLoop() ? LLTrans::getString("PermYes") : LLTrans::getString("PermNo"))); + stats_box_right->setTextArg("[EASE_IN]", llformat("%.2f", motion->getEaseInDuration())); + stats_box_right->setTextArg("[EASE_OUT]", llformat("%.2f", motion->getEaseOutDuration())); + stats_box_right->setTextArg("[NUM_JOINTS]", llformat("%d", motion->getNumJointMotions())); + } + // + + LLPreview::refreshFromItem(); +} + void LLPreviewAnim::cleanup() { this->mItemID = LLUUID::null; @@ -223,8 +231,17 @@ void LLPreviewAnim::onClose(bool app_quitting) // LLRect rect = getRect(); // reshape(rect.getWidth(), rect.getHeight() + pAdvancedStatsTextBox->getRect().getHeight() + ADVANCED_VPAD, FALSE); // +// LLMotion *motion = NULL; +// const LLInventoryItem* item = getItem(); +// if (item) +// { +// // if motion exists, will return existing one. +// // Needed because viewer can purge motions +// motion = gAgentAvatarp->createMotion(item->getAssetUUID()); +// } +// // // set text -// if (pMotion) +// if (motion) // { // pAdvancedStatsTextBox->setTextArg("[PRIORITY]", llformat("%d", pMotion->getPriority())); // pAdvancedStatsTextBox->setTextArg("[DURATION]", llformat("%.2f", pMotion->getDuration())); @@ -235,28 +252,4 @@ void LLPreviewAnim::onClose(bool app_quitting) // } // } //} - -// virtual -void LLPreviewAnim::refreshFromItem() -{ - LLPreview::refreshFromItem(); - - const LLInventoryItem* item = getItem(); - if (item) - { - pMotion = gAgentAvatarp->createMotion(item->getAssetUUID()); // preload the animation - - if (pMotion) - { - LLTextBox* stats_box_left = getChild("AdvancedStatsLeft"); - LLTextBox* stats_box_right = getChild("AdvancedStatsRight"); - stats_box_left->setTextArg("[PRIORITY]", llformat("%d", pMotion->getPriority())); - stats_box_left->setTextArg("[DURATION]", llformat("%.2f", pMotion->getDuration())); - stats_box_left->setTextArg("[IS_LOOP]", (pMotion->getLoop() ? LLTrans::getString("PermYes") : LLTrans::getString("PermNo"))); - stats_box_right->setTextArg("[EASE_IN]", llformat("%.2f", pMotion->getEaseInDuration())); - stats_box_right->setTextArg("[EASE_OUT]", llformat("%.2f", pMotion->getEaseOutDuration())); - stats_box_right->setTextArg("[NUM_JOINTS]", llformat("%d", pMotion->getNumJointMotions())); - } - } -} // diff --git a/indra/newview/llpreviewanim.h b/indra/newview/llpreviewanim.h index 7ce6cfc371..8f604f274a 100644 --- a/indra/newview/llpreviewanim.h +++ b/indra/newview/llpreviewanim.h @@ -38,20 +38,20 @@ class LLPreviewAnim : public LLPreview public: LLPreviewAnim(const LLSD& key); - /*virtual*/ BOOL postBuild(); - /*virtual*/ void onClose(bool app_quitting); - void draw(); - void cleanup(); + BOOL postBuild() override; + void onClose(bool app_quitting) override; + void draw() override; + void refreshFromItem() override; + + void cleanup(); // cleanup 'playing' state void play(const LLSD& param); // Improved animation preview //void showAdvanced(); - /*virtual*/ void refreshFromItem(); protected: - LLUUID mItemID; + LLUUID mItemID; // Not an item id, but a playing asset id bool mDidStart; - LLMotion* pMotion; // Improved animation preview //LLTextBox* pAdvancedStatsTextBox; }; diff --git a/indra/newview/llsetkeybinddialog.cpp b/indra/newview/llsetkeybinddialog.cpp index 4b36822e9a..74844a80e8 100644 --- a/indra/newview/llsetkeybinddialog.cpp +++ b/indra/newview/llsetkeybinddialog.cpp @@ -96,7 +96,7 @@ BOOL LLSetKeyBindDialog::postBuild() getChild("Cancel")->setFocus(TRUE); pCheckBox = getChild("apply_all"); - pDesription = getChild("descritption"); + pDescription = getChild("description"); gFocusMgr.setKeystrokesOnly(TRUE); @@ -160,8 +160,8 @@ void LLSetKeyBindDialog::setParent(LLKeyBindResponderInterface* parent, LLView* } input += getString("keyboard"); } - pDesription->setText(getString("basic_description")); - pDesription->setTextArg("[INPUT]", input); + pDescription->setText(getString("basic_description")); + pDescription->setTextArg("[INPUT]", input); } // static @@ -257,8 +257,8 @@ bool LLSetKeyBindDialog::recordAndHandleKey(KEY key, MASK mask, BOOL down) if (LLKeyConflictHandler::isReservedByMenu(key, mask)) { - pDesription->setText(getString("reserved_by_menu")); - pDesription->setTextArg("[KEYSTR]", LLKeyboard::stringFromAccelerator(mask,key)); + pDescription->setText(getString("reserved_by_menu")); + pDescription->setTextArg("[KEYSTR]", LLKeyboard::stringFromAccelerator(mask,key)); mLastMaskKey = 0; return true; } diff --git a/indra/newview/llsetkeybinddialog.h b/indra/newview/llsetkeybinddialog.h index 24dfa1dbfd..18e2601723 100644 --- a/indra/newview/llsetkeybinddialog.h +++ b/indra/newview/llsetkeybinddialog.h @@ -85,7 +85,7 @@ private: void setKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes); LLKeyBindResponderInterface *pParent; LLCheckBoxCtrl *pCheckBox; - LLTextBase *pDesription; + LLTextBase *pDescription; U32 mKeyFilterMask; Updater *pUpdater; diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index 8a61c2f00f..bcf5041530 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -706,7 +706,12 @@ bool LLSidepanelInventory::canWearSelected() LLInventoryItem *LLSidepanelInventory::getSelectedItem() { - LLFolderViewItem* current_item = mPanelMainInventory->getActivePanel()->getRootFolder()->getCurSelectedItem(); + LLFolderView* root = mPanelMainInventory->getActivePanel()->getRootFolder(); + if (!root) + { + return NULL; + } + LLFolderViewItem* current_item = root->getCurSelectedItem(); if (!current_item) { diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 48d2504f26..8c51a83ce3 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -1790,6 +1790,10 @@ bool idle_startup() } } + else if (reason_response == "BadType") + { + LLNotificationsUtil::add("LoginFailedToParse", LLSD(), LLSD(), login_alert_done); + } else if (!message.empty()) { // This wasn't a certificate error, so throw up the normal @@ -2574,6 +2578,17 @@ bool idle_startup() // This method MUST be called before gInventory.findCategoryUUIDForType because of // gInventory.mIsAgentInvUsable is set to true in the gInventory.buildParentChildMap. gInventory.buildParentChildMap(); + + // If buildParentChildMap succeeded, inventory will now be in + // a usable state and gInventory.isInventoryUsable() will be + // true. + + // if inventory is unusable, show warning. + if (!gInventory.isInventoryUsable()) + { + LLNotificationsUtil::add("InventoryUnusable"); + } + gInventory.createCommonSystemCategories(); // It's debatable whether this flag is a good idea - sets all @@ -2617,6 +2632,7 @@ bool idle_startup() LLStartUp::setStartupState( STATE_MISC ); display_startup(); + return FALSE; } @@ -2969,7 +2985,10 @@ bool idle_startup() if (gAgent.isOutfitChosen() && (wearables_time > max_wearables_time)) { - LLNotificationsUtil::add("ClothingLoading"); + if (gInventory.isInventoryUsable()) + { + LLNotificationsUtil::add("ClothingLoading"); + } record(LLStatViewer::LOADING_WEARABLES_LONG_DELAY, wearables_time); LLStartUp::setStartupState( STATE_CLEANUP ); } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index f7b1a0d3d0..e52f09d4b3 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -72,8 +72,11 @@ LLTrace::EventStatHandle > LLTextureFetch::sCacheH LLTrace::SampleStatHandle LLTextureFetch::sCacheReadLatency("texture_cache_read_latency"); LLTrace::SampleStatHandle LLTextureFetch::sTexDecodeLatency("texture_decode_latency"); +LLTrace::SampleStatHandle LLTextureFetch::sCacheWriteLatency("texture_write_latency"); LLTrace::SampleStatHandle LLTextureFetch::sTexFetchLatency("texture_fetch_latency"); +LLTextureFetchTester* LLTextureFetch::sTesterp = NULL ; +const std::string sTesterName("TextureFetchTester"); ////////////////////////////////////////////////////////////////////////////// // @@ -441,6 +444,29 @@ public: // Threads: Ttf virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + enum e_state // mState + { + // *NOTE: Do not change the order/value of state variables, some code + // depends upon specific ordering/adjacency. + + // NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack) + INVALID = 0, + INIT, + LOAD_FROM_TEXTURE_CACHE, + CACHE_POST, + LOAD_FROM_NETWORK, + LOAD_FROM_SIMULATOR, + WAIT_HTTP_RESOURCE, // Waiting for HTTP resources + WAIT_HTTP_RESOURCE2, // Waiting for HTTP resources + SEND_HTTP_REQ, // Commit to sending as HTTP + WAIT_HTTP_REQ, // Request sent, wait for completion + DECODE_IMAGE, + DECODE_IMAGE_UPDATE, + WRITE_TO_CACHE, + WAIT_ON_WRITE, + DONE + }; + protected: LLTextureFetchWorker(LLTextureFetch* fetcher, FTType f_type, const std::string& url, const LLUUID& id, const LLHost& host, @@ -520,28 +546,6 @@ private: } private: - enum e_state // mState - { - // *NOTE: Do not change the order/value of state variables, some code - // depends upon specific ordering/adjacency. - - // NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack) - INVALID = 0, - INIT, - LOAD_FROM_TEXTURE_CACHE, - CACHE_POST, - LOAD_FROM_NETWORK, - LOAD_FROM_SIMULATOR, - WAIT_HTTP_RESOURCE, // Waiting for HTTP resources - WAIT_HTTP_RESOURCE2, // Waiting for HTTP resources - SEND_HTTP_REQ, // Commit to sending as HTTP - WAIT_HTTP_REQ, // Request sent, wait for completion - DECODE_IMAGE, - DECODE_IMAGE_UPDATE, - WRITE_TO_CACHE, - WAIT_ON_WRITE, - DONE - }; enum e_request_state // mSentRequest { UNSENT = 0, @@ -554,7 +558,7 @@ private: CAN_WRITE = 1, SHOULD_WRITE = 2 }; - static const char* sStateDescs[]; + e_state mState; void setState(e_state new_state); @@ -582,10 +586,15 @@ private: LLFrameTimer mFetchDeltaTimer; LLTimer mCacheReadTimer; LLTimer mDecodeTimer; + LLTimer mCacheWriteTimer; LLTimer mFetchTimer; + LLTimer mStateTimer; F32 mCacheReadTime; // time for cache read only F32 mDecodeTime; // time for decode only + F32 mCacheWriteTime; F32 mFetchTime; // total time from req to finished fetch + std::map mStateTimersMap; + F32 mSkippedStatesTime; LLTextureCache::handle_t mCacheReadHandle, mCacheWriteHandle; S32 mRequestedSize, @@ -869,8 +878,7 @@ bool truncate_viewer_metrics(int max_regions, LLSD & metrics); ////////////////////////////////////////////////////////////////////////////// -//static -const char* LLTextureFetchWorker::sStateDescs[] = { +const char* sStateDescs[] = { "INVALID", "INIT", "LOAD_FROM_TEXTURE_CACHE", @@ -888,6 +896,9 @@ const char* LLTextureFetchWorker::sStateDescs[] = { "DONE" }; +const std::set LOGGED_STATES = { LLTextureFetchWorker::LOAD_FROM_TEXTURE_CACHE, LLTextureFetchWorker::LOAD_FROM_NETWORK, LLTextureFetchWorker::LOAD_FROM_SIMULATOR, + LLTextureFetchWorker::WAIT_HTTP_REQ, LLTextureFetchWorker::DECODE_IMAGE_UPDATE, LLTextureFetchWorker::WAIT_ON_WRITE }; + // static volatile bool LLTextureFetch::svMetricsDataBreak(true); // Start with a data break @@ -919,6 +930,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mLoadedDiscard(-1), mDecodedDiscard(-1), mCacheReadTime(0.f), + mCacheWriteTime(0.f), mDecodeTime(0.f), mFetchTime(0.f), mCacheReadHandle(LLTextureCache::nullHandle()), @@ -927,6 +939,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mRequestedOffset(0), mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE), mFileSize(0), + mSkippedStatesTime(0), mCachedSize(0), mLoaded(FALSE), mSentRequest(UNSENT), @@ -1196,6 +1209,13 @@ bool LLTextureFetchWorker::doWork(S32 param) } // Asset Blacklist + mStateTimer.reset(); + mFetchTimer.reset(); + for(auto i : LOGGED_STATES) + { + mStateTimersMap[i] = 0; + } + mSkippedStatesTime = 0; mRawImage = NULL ; mRequestedDiscard = -1; mLoadedDiscard = -1; @@ -1253,9 +1273,10 @@ bool LLTextureFetchWorker::doWork(S32 param) ++mCacheReadCount; std::string filename = mUrl.substr(7, std::string::npos); CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); + mCacheReadTimer.reset(); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority, offset, size, responder); - mCacheReadTimer.reset(); + } else if ((mUrl.empty() || mFTType==FTT_SERVER_BAKE) && mFetcher->canLoadFromCache()) { @@ -1263,9 +1284,9 @@ bool LLTextureFetchWorker::doWork(S32 param) ++mCacheReadCount; CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); - mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, - offset, size, responder); mCacheReadTimer.reset(); + mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, + offset, size, responder);; } else if(!mUrl.empty() && mCanUseHTTP) { @@ -1287,6 +1308,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mCacheReadHandle = LLTextureCache::nullHandle(); setState(CACHE_POST); add(LLTextureFetch::sCacheHit, 1.0); + mCacheReadTime = mCacheReadTimer.getElapsedTimeF32(); // fall through } else @@ -1909,7 +1931,7 @@ bool LLTextureFetchWorker::doWork(S32 param) LL_DEBUGS(LOG_TXT) << mID << " DECODE_IMAGE abort: mLoadedDiscard < 0" << LL_ENDL; return true; } - + mDecodeTimer.reset(); mRawImage = NULL; mAuxImage = NULL; llassert_always(mFormattedImage.notNull()); @@ -2003,6 +2025,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // be protected by work mutex and won't be safe to use here nor in cache worker. // So make sure users of getRequestFinished() does not attempt to modify image while // fetcher is working + mCacheWriteTimer.reset(); mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority, mFormattedImage->getData(), datasize, mFileSize, mRawImage, mDecodedDiscard, responder); @@ -2013,6 +2036,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { if (writeToCacheComplete()) { + mCacheWriteTime = mCacheWriteTimer.getElapsedTimeF32(); setState(DONE); // fall through } @@ -2521,7 +2545,6 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag mDecoded = TRUE; // LL_INFOS(LOG_TXT) << mID << " : DECODE COMPLETE " << LL_ENDL; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mCacheReadTime = mCacheReadTimer.getElapsedTimeF32(); } // -Mw ////////////////////////////////////////////////////////////////////////////// @@ -2649,6 +2672,17 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image } mOriginFetchSource = mFetchSource; } + + // If that test log has ben requested but not yet created, create it + if (LLMetricPerformanceTesterBasic::isMetricLogRequested(sTesterName) && !LLMetricPerformanceTesterBasic::getTester(sTesterName)) + { + sTesterp = new LLTextureFetchTester() ; + if (!sTesterp->isValid()) + { + delete sTesterp; + sTesterp = NULL; + } + } } LLTextureFetch::~LLTextureFetch() @@ -2990,20 +3024,51 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, } else if (worker->checkWork()) { + F32 decode_time; + F32 fetch_time; + F32 cache_read_time; + F32 cache_write_time; + S32 file_size; + std::map logged_state_timers; + F32 skipped_states_time; worker->lockWorkMutex(); // +Mw last_http_get_status = worker->mGetStatus; discard_level = worker->mDecodedDiscard; raw = worker->mRawImage; aux = worker->mAuxImage; - sample(sTexDecodeLatency, worker->mDecodeTime); - sample(sTexFetchLatency, worker->mFetchTime); - sample(sCacheReadLatency, worker->mCacheReadTime); + + decode_time = worker->mDecodeTime; + fetch_time = worker->mFetchTime; + cache_read_time = worker->mCacheReadTime; + cache_write_time = worker->mCacheWriteTime; + file_size = worker->mFileSize; worker->mCacheReadTimer.reset(); worker->mDecodeTimer.reset(); + worker->mCacheWriteTimer.reset(); worker->mFetchTimer.reset(); + logged_state_timers = worker->mStateTimersMap; + skipped_states_time = worker->mSkippedStatesTime; + worker->mStateTimer.reset(); res = true; LL_DEBUGS(LOG_TXT) << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL; worker->unlockWorkMutex(); // -Mw + + sample(sTexDecodeLatency, decode_time); + sample(sTexFetchLatency, fetch_time); + sample(sCacheReadLatency, cache_read_time); + sample(sCacheWriteLatency, cache_write_time); + + static LLCachedControl min_time_to_log(gSavedSettings, "TextureFetchMinTimeToLog", 2.f); + if (fetch_time > min_time_to_log) + { + //LL_INFOS() << "fetch_time: " << fetch_time << " cache_read_time: " << cache_read_time << " decode_time: " << decode_time << " cache_write_time: " << cache_write_time << LL_ENDL; + + LLTextureFetchTester* tester = (LLTextureFetchTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); + if (tester) + { + tester->updateStats(logged_state_timers, fetch_time, skipped_states_time, file_size) ; + } + } } else { @@ -3488,6 +3553,21 @@ void LLTextureFetchWorker::setState(e_state new_state) // LL_INFOS(LOG_TXT) << "id: " << mID << " FTType: " << mFTType << " disc: " << mDesiredDiscard << " sz: " << mDesiredSize << " state: " << e_state_name[mState] << " => " << e_state_name[new_state] << LL_ENDL; } + + F32 d_time = mStateTimer.getElapsedTimeF32(); + if (d_time >= 0.0001F) + { + if (LOGGED_STATES.count(mState)) + { + mStateTimersMap[mState] = d_time; + } + else + { + mSkippedStatesTime += d_time; + } + } + + mStateTimer.reset(); mState = new_state; } @@ -3703,7 +3783,7 @@ void LLTextureFetch::dump() LLTextureFetchWorker* worker = (LLTextureFetchWorker*)wreq->getWorkerClass(); LL_INFOS(LOG_TXT) << " ID: " << worker->mID << " PRI: " << llformat("0x%08x",wreq->getPriority()) - << " STATE: " << worker->sStateDescs[worker->mState] + << " STATE: " << sStateDescs[worker->mState] << LL_ENDL; } @@ -5151,4 +5231,40 @@ void LLTextureFetchDebugger::callbackHTTP(FetchEntry & fetch, LLCore::HttpRespon //End LLTextureFetchDebugger /////////////////////////////////////////////////////////////////////////////////////////// +LLTextureFetchTester::LLTextureFetchTester() : LLMetricPerformanceTesterBasic(sTesterName) +{ + mTextureFetchTime = 0; + mSkippedStatesTime = 0; + mFileSize = 0; +} + +LLTextureFetchTester::~LLTextureFetchTester() +{ + outputTestResults(); + LLTextureFetch::sTesterp = NULL; +} + +//virtual +void LLTextureFetchTester::outputTestRecord(LLSD *sd) +{ + std::string currentLabel = getCurrentLabelName(); + + (*sd)[currentLabel]["Texture Fetch Time"] = (LLSD::Real)mTextureFetchTime; + (*sd)[currentLabel]["File Size"] = (LLSD::Integer)mFileSize; + (*sd)[currentLabel]["Skipped States Time"] = (LLSD::String)llformat("%.6f", mSkippedStatesTime); + + for(auto i : LOGGED_STATES) + { + (*sd)[currentLabel][sStateDescs[i]] = mStateTimersMap[i]; + } +} + +void LLTextureFetchTester::updateStats(const std::map state_timers, const F32 fetch_time, const F32 skipped_states_time, const S32 file_size) +{ + mTextureFetchTime = fetch_time; + mStateTimersMap = state_timers; + mFileSize = file_size; + mSkippedStatesTime = skipped_states_time; + outputTestResults(); +} diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 2aa194e141..bf6732963f 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -50,6 +50,7 @@ class LLHost; class LLViewerAssetStats; class LLTextureFetchDebugger; class LLTextureCache; +class LLTextureFetchTester; // Interface class @@ -312,6 +313,7 @@ public: static LLTrace::CountStatHandle sCacheAttempt; static LLTrace::SampleStatHandle sCacheReadLatency; static LLTrace::SampleStatHandle sTexDecodeLatency; + static LLTrace::SampleStatHandle sCacheWriteLatency; static LLTrace::SampleStatHandle sTexFetchLatency; static LLTrace::EventStatHandle > sCacheHitRate; @@ -403,6 +405,9 @@ public: FROM_HTTP_ONLY, INVALID_SOURCE }; + + static LLTextureFetchTester* sTesterp; + private: //debug use LLTextureFetchDebugger* mFetchDebugger; @@ -635,5 +640,26 @@ private: public: static bool isEnabled() {return sDebuggerEnabled;} }; + + +class LLTextureFetchTester : public LLMetricPerformanceTesterBasic +{ +public: + LLTextureFetchTester(); + ~LLTextureFetchTester(); + + void updateStats(const std::map states_timers, const F32 fetch_time, const F32 other_states_time, const S32 file_size); + +protected: + /*virtual*/ void outputTestRecord(LLSD* sd); + +private: + + F32 mTextureFetchTime; + F32 mSkippedStatesTime; + S32 mFileSize; + + std::map mStateTimersMap; +}; #endif // LL_LLTEXTUREFETCH_H diff --git a/indra/newview/lltoolface.cpp b/indra/newview/lltoolface.cpp index 83e5587552..0fa7c6c3a0 100644 --- a/indra/newview/lltoolface.cpp +++ b/indra/newview/lltoolface.cpp @@ -89,10 +89,9 @@ BOOL LLToolFace::handleDoubleClick(S32 x, S32 y, MASK mask) BOOL LLToolFace::handleMouseDown(S32 x, S32 y, MASK mask) { - mGrabX=x; - mGrabY=y; - gViewerWindow->pickAsync(x, y, mask, pickCallback); + mGrabX = x; + mGrabY = y; return TRUE; } diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index 321b3e9898..1833c78136 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -188,7 +188,9 @@ BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask) mMouseButtonDown = true; - return handleLeftClickPick(); + // If nothing clickable is picked, needs to return + // false for click-to-walk or click-to-teleport to work. + return handleLeftClickPick(); } // Spawn context menus on right mouse down so you can drag over and select @@ -921,7 +923,7 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB); LL_DEBUGS("UserInput") << "hover handled by LLToolPie (inactive)" << LL_ENDL; } - else if ((!object || !object->isAttachment() || object->getClickAction() != CLICK_ACTION_DISABLED) + else if ((!object || object->getClickAction() != CLICK_ACTION_DISABLED) && ((object && object->flagHandleTouch()) || (parent && parent->flagHandleTouch())) && (!object || !object->isAvatar())) { diff --git a/indra/newview/llurl.cpp b/indra/newview/llurl.cpp index 01a81c5f83..46cdbf9ed3 100644 --- a/indra/newview/llurl.cpp +++ b/indra/newview/llurl.cpp @@ -28,6 +28,11 @@ #include "llurl.h" #include "llerror.h" +#if LL_LINUX +#pragma GCC diagnostic ignored "-Wstringop-truncation" +#endif + + LLURL::LLURL() { init(""); diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 25a43f094b..cb09f52d97 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -104,10 +104,11 @@ public: /// LLViewerAssetStorage ///---------------------------------------------------------------------------- +S32 LLViewerAssetStorage::sAssetCoroCount = 0; + // Unused? LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, const LLHost &upstream_host) : LLAssetStorage(msg, xfer, upstream_host), - mAssetCoroCount(0), mCountRequests(0), mCountStarted(0), mCountCompleted(0), @@ -119,7 +120,6 @@ LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager * LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer) : LLAssetStorage(msg, xfer), - mAssetCoroCount(0), mCountRequests(0), mCountStarted(0), mCountCompleted(0), @@ -484,8 +484,7 @@ void LLViewerAssetStorage::assetRequestCoro( LLGetAssetCallback callback, void *user_data) { - LLScopedIncrement coro_count_boost(mAssetCoroCount); - mCountStarted++; + LLScopedIncrement coro_count_boost(sAssetCoroCount); // static counter since corotine can outlive LLViewerAssetStorage S32 result_code = LL_ERR_NOERR; LLExtStat ext_status = LLExtStat::NONE; @@ -495,6 +494,9 @@ void LLViewerAssetStorage::assetRequestCoro( LL_WARNS_ONCE("ViewerAsset") << "Asset request fails: asset storage no longer exists" << LL_ENDL; return; } + + mCountStarted++; + if (!gAgent.getRegion()) { LL_WARNS_ONCE("ViewerAsset") << "Asset request fails: no region set" << LL_ENDL; @@ -602,6 +604,18 @@ void LLViewerAssetStorage::assetRequestCoro( result_code = LL_ERR_ASSET_REQUEST_FAILED; ext_status = LLExtStat::NONE; } + else if (!result.has(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW)) + { + LL_DEBUGS("ViewerAsset") << "request failed, no data returned!" << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LLExtStat::NONE; + } + else if (!result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW].isBinary()) + { + LL_DEBUGS("ViewerAsset") << "request failed, invalid data format!" << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LLExtStat::NONE; + } else { LL_DEBUGS("ViewerAsset") << "request succeeded, url " << url << LL_ENDL; @@ -661,7 +675,7 @@ std::string LLViewerAssetStorage::getAssetURL(const std::string& cap_url, const void LLViewerAssetStorage::logAssetStorageInfo() { LLMemory::logMemoryInfo(true); - LL_INFOS("AssetStorage") << "Active coros " << mAssetCoroCount << LL_ENDL; + LL_INFOS("AssetStorage") << "Active coros " << sAssetCoroCount << LL_ENDL; LL_INFOS("AssetStorage") << "mPendingDownloads size " << mPendingDownloads.size() << LL_ENDL; LL_INFOS("AssetStorage") << "mCountStarted " << mCountStarted << LL_ENDL; LL_INFOS("AssetStorage") << "mCountCompleted " << mCountCompleted << LL_ENDL; diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index af7fbb5fbf..0965a17ce1 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -122,12 +122,13 @@ protected: wait_list_t mCoroWaitList; std::string mViewerAssetUrl; - S32 mAssetCoroCount; S32 mCountRequests; S32 mCountStarted; S32 mCountCompleted; S32 mCountSucceeded; S64 mTotalBytesFetched; + + static S32 sAssetCoroCount; // coroutine count, static since coroutines can outlive LLViewerAssetStorage }; #endif diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp index 3f89e8a174..960fff004c 100644 --- a/indra/newview/llvieweraudio.cpp +++ b/indra/newview/llvieweraudio.cpp @@ -85,6 +85,8 @@ void LLViewerAudio::registerIdleListener() void LLViewerAudio::startInternetStreamWithAutoFade(const std::string &streamURI) { + LL_DEBUGS("AudioEngine") << "Start with outo fade: " << streamURI << LL_ENDL; + // Old and new stream are identical if (mNextStreamURI == streamURI) { @@ -180,6 +182,7 @@ bool LLViewerAudio::onIdleUpdate() if (gAudiop) { // Clear URI + LL_DEBUGS("AudioEngine") << "Done with audio fade" << LL_ENDL; gAudiop->startInternetStream(LLStringUtil::null); gAudiop->stopInternetStream(); } @@ -190,6 +193,7 @@ bool LLViewerAudio::onIdleUpdate() if (gAudiop) { + LL_DEBUGS("AudioEngine") << "Audio fade in: " << mNextStreamURI << LL_ENDL; LLStreamingAudioInterface *stream = gAudiop->getStreamingAudioImpl(); if(stream && stream->supportsAdjustableBufferSizes()) stream->setBufferSizes(gSavedSettings.getU32("FMODStreamBufferSize"),gSavedSettings.getU32("FMODDecodeBufferSize")); @@ -242,6 +246,7 @@ void LLViewerAudio::stopInternetStreamWithAutoFade() if (gAudiop) { + LL_DEBUGS("AudioEngine") << "Stop audio fade" << LL_ENDL; gAudiop->startInternetStream(LLStringUtil::null); gAudiop->stopInternetStream(); } diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index a7227c7f58..3ef82e5e26 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -1094,14 +1094,20 @@ void handleDiskCacheSizeChanged(const LLSD& newValue) void handleTargetFPSChanged(const LLSD& newValue) { const auto targetFPS = gSavedSettings.getU32("FSTargetFPS"); - FSPerfStats::targetFPS = targetFPS; + FSPerfStats::tunables.userTargetFPS = targetFPS; +} +// perf floater stuffs +void handleAutoTuneLockChanged(const LLSD& newValue) +{ + const auto newval = gSavedSettings.getBOOL("FSAutoTuneLock"); + FSPerfStats::tunables.userAutoTuneLock = newval; } // perrf floater stuffs void handleAutoTuneFPSChanged(const LLSD& newValue) { const auto newval = gSavedSettings.getBOOL("FSAutoTuneFPS"); - FSPerfStats::autoTune = newval; + FSPerfStats::tunables.userAutoTuneEnabled = newval; if(newval && FSPerfStats::renderAvatarMaxART_ns == 0) // If we've enabled autotune we override "unlimited" to max { gSavedSettings.setF32("FSRenderAvatarMaxART",log10(FSPerfStats::ART_UNLIMITED_NANOS-1000));//triggers callback to update static var @@ -1110,18 +1116,42 @@ void handleAutoTuneFPSChanged(const LLSD& newValue) void handleRenderAvatarMaxARTChanged(const LLSD& newValue) { - FSPerfStats::StatsRecorder::updateRenderCostLimitFromSettings(); + FSPerfStats::tunables.updateRenderCostLimitFromSettings(); } -void handleFPSTuningStrategyChanged(const LLSD& newValue) + +void handleUserTargetDrawDistanceChanged(const LLSD& newValue) { - const auto newval = gSavedSettings.getU32("FSTuningFPSStrategy"); - FSPerfStats::fpsTuningStrategy = newval; + const auto newval = gSavedSettings.getF32("FSAutoTuneRenderFarClipTarget"); + FSPerfStats::tunables.userTargetDrawDistance = newval; } + +void handleUserTargetReflectionsChanged(const LLSD& newValue) +{ + const auto newval = gSavedSettings.getF32("FSUserTargetReflections"); + FSPerfStats::tunables.userTargetReflections = newval; +} + void handlePerformanceStatsEnabledChanged(const LLSD& newValue) { const auto newval = gSavedSettings.getBOOL("FSPerfStatsCaptureEnabled"); FSPerfStats::StatsRecorder::setEnabled(newval); } +void handleUserImpostorByDistEnabledChanged(const LLSD& newValue) +{ + const auto newval = gSavedSettings.getBOOL("FSAutoTuneImpostorByDistEnabled"); + FSPerfStats::tunables.userImpostorDistanceTuningEnabled = newval; +} +void handleUserImpostorDistanceChanged(const LLSD& newValue) +{ + const auto newval = gSavedSettings.getF32("FSAutoTuneImpostorFarAwayDistance"); + FSPerfStats::tunables.userImpostorDistance = newval; +} +void handleFPSTuningStrategyChanged(const LLSD& newValue) +{ + const auto newval = gSavedSettings.getU32("FSTuningFPSStrategy"); + FSPerfStats::tunables.userFPSTuningStrategy = newval; +} + // //////////////////////////////////////////////////////////////////////////// @@ -1382,9 +1412,14 @@ void settings_setup_listeners() // perf floater controls gSavedSettings.getControl("FSTargetFPS")->getSignal()->connect(boost::bind(&handleTargetFPSChanged, _2)); gSavedSettings.getControl("FSAutoTuneFPS")->getSignal()->connect(boost::bind(&handleAutoTuneFPSChanged, _2)); + gSavedSettings.getControl("FSAutoTuneLock")->getSignal()->connect(boost::bind(&handleAutoTuneLockChanged, _2)); gSavedSettings.getControl("FSRenderAvatarMaxART")->getSignal()->connect(boost::bind(&handleRenderAvatarMaxARTChanged, _2)); - gSavedSettings.getControl("FSTuningFPSStrategy")->getSignal()->connect(boost::bind(&handleFPSTuningStrategyChanged, _2)); gSavedSettings.getControl("FSPerfStatsCaptureEnabled")->getSignal()->connect(boost::bind(&handlePerformanceStatsEnabledChanged, _2)); + gSavedSettings.getControl("FSUserTargetReflections")->getSignal()->connect(boost::bind(&handleUserTargetReflectionsChanged, _2)); + gSavedSettings.getControl("FSAutoTuneRenderFarClipTarget")->getSignal()->connect(boost::bind(&handleUserTargetDrawDistanceChanged, _2)); + gSavedSettings.getControl("FSAutoTuneImpostorFarAwayDistance")->getSignal()->connect(boost::bind(&handleUserImpostorDistanceChanged, _2)); + gSavedSettings.getControl("FSAutoTuneImpostorByDistEnabled")->getSignal()->connect(boost::bind(&handleUserImpostorByDistEnabledChanged, _2)); + gSavedSettings.getControl("FSTuningFPSStrategy")->getSignal()->connect(boost::bind(&handleFPSTuningStrategyChanged, _2)); // } diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index d147560011..c81fa22b13 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -493,7 +493,16 @@ void LLViewerJoystick::initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8 #if LIB_NDOF mLastDeviceUUID = guid; +#ifdef LL_LINUX +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" +#endif strncpy(mNdofDev->product, name.c_str(), sizeof(mNdofDev->product)); +#ifdef LL_LINUX +#pragma GCC diagnostic pop +#endif + + mNdofDev->product[ sizeof(mNdofDev->product)-1 ] = 0; mNdofDev->manufacturer[0] = '\0'; initDevice(preffered_device); diff --git a/indra/newview/llviewerjoystick.h b/indra/newview/llviewerjoystick.h index a6e6413b15..f3129412c7 100644 --- a/indra/newview/llviewerjoystick.h +++ b/indra/newview/llviewerjoystick.h @@ -55,6 +55,7 @@ class LLViewerJoystick : public LLSingleton { LLSINGLETON(LLViewerJoystick); virtual ~LLViewerJoystick(); + LOG_CLASS(LLViewerJoystick); public: void init(bool autoenable); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 9911c8ea99..b9b222e256 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -78,41 +78,12 @@ #include // for SkinFolder listener #include -class LLMediaFilePicker : public LLFilePickerThread // deletes itself when done -{ -public: - LLMediaFilePicker(LLPluginClassMedia* plugin, LLFilePicker::ELoadFilter filter, bool get_multiple) - : LLFilePickerThread(filter, get_multiple), - mPlugin(plugin->getSharedPrt()) - { - } - - LLMediaFilePicker(LLPluginClassMedia* plugin, LLFilePicker::ESaveFilter filter, const std::string &proposed_name) - : LLFilePickerThread(filter, proposed_name), - mPlugin(plugin->getSharedPrt()) - { - } - - virtual void notify(const std::vector& filenames) - { - mPlugin->sendPickFileResponse(mResponses); - mPlugin = NULL; - } - -private: - boost::shared_ptr mPlugin; -}; void init_threaded_picker_load_dialog(LLPluginClassMedia* plugin, LLFilePicker::ELoadFilter filter, bool get_multiple) { (new LLMediaFilePicker(plugin, filter, get_multiple))->getFile(); // will delete itself } -void init_threaded_picker_save_dialog(LLPluginClassMedia* plugin, LLFilePicker::ESaveFilter filter, std::string &proposed_name) -{ - (new LLMediaFilePicker(plugin, filter, proposed_name))->getFile(); // will delete itself -} - /////////////////////////////////////////////////////////////////////////////// // Move this to its own file. @@ -1814,6 +1785,10 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ // need to set agent string here before instance created media_source->setBrowserUserAgent(LLViewerMedia::getInstance()->getCurrentUserAgent()); + // configure and pass proxy setup based on debug settings that are + // configured by UI in prefs -> setup + media_source->proxy_setup(gSavedSettings.getBOOL("BrowserProxyEnabled"), gSavedSettings.getString("BrowserProxyAddress"), gSavedSettings.getS32("BrowserProxyPort")); + media_source->setTarget(target); const std::string plugin_dir = gDirUtilp->getLLPluginDir(); @@ -1898,8 +1873,6 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) std::string ca_path = gDirUtilp->getCAFile(); media_source->addCertificateFilePath( ca_path ); - media_source->proxy_setup(gSavedSettings.getBOOL("BrowserProxyEnabled"), gSavedSettings.getString("BrowserProxyAddress"), gSavedSettings.getS32("BrowserProxyPort")); - if(mClearCache) { mClearCache = false; @@ -3282,37 +3255,17 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << plugin->getCursorName() << LL_ENDL; std::string cursor = plugin->getCursorName(); - - if(cursor == "arrow") - mLastSetCursor = UI_CURSOR_ARROW; - else if(cursor == "ibeam") - mLastSetCursor = UI_CURSOR_IBEAM; - else if(cursor == "splith") - mLastSetCursor = UI_CURSOR_SIZEWE; - else if(cursor == "splitv") - mLastSetCursor = UI_CURSOR_SIZENS; - else if(cursor == "hand") - mLastSetCursor = UI_CURSOR_HAND; - else // for anything else, default to the arrow - mLastSetCursor = UI_CURSOR_ARROW; + mLastSetCursor = getCursorFromString(cursor); } break; case LLViewerMediaObserver::MEDIA_EVENT_FILE_DOWNLOAD: { LL_DEBUGS("Media") << "Media event - file download requested - filename is " << plugin->getFileDownloadFilename() << LL_ENDL; - // pick a file from SAVE FILE dialog - // need a better algorithm that this or else, pass in type of save type - // from event that initiated it - this is okay for now - only thing - // that saves is 360s - std::string suggested_filename = plugin->getFileDownloadFilename(); - LLFilePicker::ESaveFilter filter = LLFilePicker::FFSAVE_ALL; - if (suggested_filename.find(".jpg") != std::string::npos || suggested_filename.find(".jpeg") != std::string::npos) - filter = LLFilePicker::FFSAVE_JPEG; - if (suggested_filename.find(".png") != std::string::npos) - filter = LLFilePicker::FFSAVE_PNG; - init_threaded_picker_save_dialog(plugin, filter, suggested_filename); + //unblock media plugin + const std::vector empty_response; + plugin->sendPickFileResponse(empty_response); } break; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 9548eab453..73996178ba 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -261,6 +261,7 @@ LLMenuItemCallGL* gAutorespondNonFriendsMenu = NULL; // File Menu void handle_compress_image(void*); +void handle_compress_file_test(void*); // Edit menu @@ -2534,6 +2535,21 @@ class LLAdvancedCompressImage : public view_listener_t }; + +//////////////////////// +// COMPRESS FILE TEST // +//////////////////////// + +class LLAdvancedCompressFileTest : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_compress_file_test(NULL); + return true; + } +}; + + ///////////////////////// // SHOW DEBUG SETTINGS // ///////////////////////// @@ -7779,6 +7795,32 @@ class LLAvatarTogglePicks : public view_listener_t } }; +class LLAvatarToggleSearch : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLFloater* instance = LLFloaterReg::findInstance("search"); + if (LLFloater::isMinimized(instance)) + { + instance->setMinimized(FALSE); + instance->setFocus(TRUE); + } + else if (!LLFloater::isShown(instance)) + { + LLFloaterReg::showInstance("search"); + } + else if (!instance->hasFocus() && !instance->getIsChrome()) + { + instance->setFocus(TRUE); + } + else + { + instance->closeFloater(); + } + return true; + } +}; + class LLAvatarResetSkeleton: public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -12030,6 +12072,7 @@ void initialize_menus() view_listener_t::addMenu(new LLAdvancedToggleShowObjectUpdates(), "Advanced.ToggleShowObjectUpdates"); view_listener_t::addMenu(new LLAdvancedCheckShowObjectUpdates(), "Advanced.CheckShowObjectUpdates"); view_listener_t::addMenu(new LLAdvancedCompressImage(), "Advanced.CompressImage"); + view_listener_t::addMenu(new LLAdvancedCompressFileTest(), "Advanced.CompressFileTest"); view_listener_t::addMenu(new LLAdvancedShowDebugSettings(), "Advanced.ShowDebugSettings"); view_listener_t::addMenu(new LLAdvancedEnableViewAdminOptions(), "Advanced.EnableViewAdminOptions"); view_listener_t::addMenu(new LLAdvancedToggleViewAdminOptions(), "Advanced.ToggleViewAdminOptions"); @@ -12121,6 +12164,7 @@ void initialize_menus() view_listener_t::addMenu(new LLAvatarToggleMyProfile(), "Avatar.ToggleMyProfile"); view_listener_t::addMenu(new LLAvatarTogglePicks(), "Avatar.TogglePicks"); + view_listener_t::addMenu(new LLAvatarToggleSearch(), "Avatar.ToggleSearch"); view_listener_t::addMenu(new LLAvatarResetSkeleton(), "Avatar.ResetSkeleton"); view_listener_t::addMenu(new LLAvatarEnableResetSkeleton(), "Avatar.EnableResetSkeleton"); view_listener_t::addMenu(new LLAvatarResetSkeletonAndAnimations(), "Avatar.ResetSkeletonAndAnimations"); diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 9cc8e46187..23c150ae29 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -46,6 +46,7 @@ #include "llimagejpeg.h" #include "llimagetga.h" #include "llinventorymodel.h" // gInventory +#include "llpluginclassmedia.h" #include "llresourcedata.h" #include "lltoast.h" #include "llfloaterperms.h" @@ -259,6 +260,25 @@ void LLFilePickerReplyThread::notify(const std::vector& filenames) } } + +LLMediaFilePicker::LLMediaFilePicker(LLPluginClassMedia* plugin, LLFilePicker::ELoadFilter filter, bool get_multiple) + : LLFilePickerThread(filter, get_multiple), + mPlugin(plugin->getSharedPrt()) +{ +} + +LLMediaFilePicker::LLMediaFilePicker(LLPluginClassMedia* plugin, LLFilePicker::ESaveFilter filter, const std::string &proposed_name) + : LLFilePickerThread(filter, proposed_name), + mPlugin(plugin->getSharedPrt()) +{ +} + +void LLMediaFilePicker::notify(const std::vector& filenames) +{ + mPlugin->sendPickFileResponse(mResponses); + mPlugin = NULL; +} + //============================================================================ #if LL_WINDOWS @@ -573,13 +593,8 @@ class LLFileUploadModel : public view_listener_t { bool handleEvent(const LLSD& userdata) { - LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) LLFloaterReg::getInstance("upload_model"); - if (fmp && !fmp->isModelLoading()) - { - fmp->loadHighLodModel(); - } - - return TRUE; + LLFloaterModelPreview::showModelPreview(); + return TRUE; } }; @@ -853,6 +868,94 @@ void handle_compress_image(void*) } } +// No convinient check in LLFile, and correct way would be something +// like GetFileSizeEx, which is too OS specific for current purpose +// so doing dirty, but OS independent fopen and fseek +size_t get_file_size(std::string &filename) +{ + LLFILE* file = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/ + if (!file) + { + LL_WARNS() << "Error opening " << filename << LL_ENDL; + return 0; + } + + // read in the whole file + fseek(file, 0L, SEEK_END); + size_t file_length = (size_t)ftell(file); + fclose(file); + return file_length; +} + +void handle_compress_file_test(void*) +{ + LLFilePicker& picker = LLFilePicker::instance(); + if (picker.getOpenFile()) + { + std::string infile = picker.getFirstFile(); + if (!infile.empty()) + { + std::string packfile = infile + ".pack_test"; + std::string unpackfile = infile + ".unpack_test"; + + S64Bytes initial_size = S64Bytes(get_file_size(infile)); + + BOOL success; + + F64 total_seconds = LLTimer::getTotalSeconds(); + success = gzip_file(infile, packfile); + F64 result_pack_seconds = LLTimer::getTotalSeconds() - total_seconds; + + if (success) + { + S64Bytes packed_size = S64Bytes(get_file_size(packfile)); + + LL_INFOS() << "Packing complete, time: " << result_pack_seconds << " size: " << packed_size << LL_ENDL; + total_seconds = LLTimer::getTotalSeconds(); + success = gunzip_file(packfile, unpackfile); + F64 result_unpack_seconds = LLTimer::getTotalSeconds() - total_seconds; + + if (success) + { + S64Bytes unpacked_size = S64Bytes(get_file_size(unpackfile)); + + LL_INFOS() << "Unpacking complete, time: " << result_unpack_seconds << " size: " << unpacked_size << LL_ENDL; + + LLSD args; + args["FILE"] = infile; + args["PACK_TIME"] = result_pack_seconds; + args["UNPACK_TIME"] = result_unpack_seconds; + args["SIZE"] = LLSD::Integer(initial_size.valueInUnits()); + args["PSIZE"] = LLSD::Integer(packed_size.valueInUnits()); + args["USIZE"] = LLSD::Integer(unpacked_size.valueInUnits()); + LLNotificationsUtil::add("CompressionTestResults", args); + + LLFile::remove(packfile); + LLFile::remove(unpackfile); + } + else + { + LL_INFOS() << "Failed to uncompress file: " << packfile << LL_ENDL; + LLFile::remove(packfile); + } + + } + else + { + LL_INFOS() << "Failed to compres file: " << infile << LL_ENDL; + } + } + else + { + LL_INFOS() << "Failed to open file" << LL_ENDL; + } + } + else + { + LL_INFOS() << "Failed to open file" << LL_ENDL; + } +} + LLUUID upload_new_resource( const std::string& src_filename, diff --git a/indra/newview/llviewermenufile.h b/indra/newview/llviewermenufile.h index bcde38d591..b3b4ad71b9 100644 --- a/indra/newview/llviewermenufile.h +++ b/indra/newview/llviewermenufile.h @@ -38,6 +38,7 @@ #include // class LLTransactionID; +class LLPluginClassMedia; void init_menu_file(); @@ -72,6 +73,7 @@ void assign_defaults_and_show_upload_message( const std::string& display_name, std::string& description); +//consider moving all file pickers below to more suitable place class LLFilePickerThread : public LLThread { //multi-threaded file picker (runs system specific file picker in background and calls "notify" from main thread) public: @@ -128,4 +130,17 @@ private: file_picked_signal_t* mFailureSignal; }; +class LLMediaFilePicker : public LLFilePickerThread +{ +public: + LLMediaFilePicker(LLPluginClassMedia* plugin, LLFilePicker::ELoadFilter filter, bool get_multiple); + LLMediaFilePicker(LLPluginClassMedia* plugin, LLFilePicker::ESaveFilter filter, const std::string &proposed_name); + + virtual void notify(const std::vector& filenames); + +private: + boost::shared_ptr mPlugin; +}; + + #endif diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index c40fd3be29..e4eea0a510 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -4890,13 +4890,16 @@ void process_sound_trigger(LLMessageSystem *msg, void **) FSRadar::getInstance()->requestRadarChannelAlertSync(); return; } - // - // Don't play sounds from gestures if they are not enabled. - // ...TS: Unless they're your own. - if ((!gSavedSettings.getBOOL("EnableGestureSounds")) && - (owner_id != gAgent.getID()) && - (owner_id == object_id)) return; + // Do play sounds triggered by avatar, since muting your own + // gesture sounds and your own sounds played inworld from + // Inventory can cause confusion. + if (object_id == owner_id + && owner_id != gAgentID + && !gSavedSettings.getBOOL("EnableGestureSounds")) + { + return; + } // NaCl - Antispam Registry //if (LLMaterialTable::basic.isCollisionSound(sound_id) && !gSavedSettings.getBOOL("EnableCollisionSounds")) @@ -6836,7 +6839,7 @@ void mean_name_callback(const LLUUID &id, const LLAvatarName& av_name) LLMeanCollisionData *mcd = *iter; if (mcd->mPerp == id) { - mcd->mFullName = av_name.getUserName(); + mcd->mFullName = gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES) ? RlvStrings::getAnonym(av_name) : av_name.getUserName(); } } // Instant bump list floater update @@ -6883,7 +6886,14 @@ void process_mean_collision_alert_message(LLMessageSystem *msgsystem, void **use { std::string action; LLStringUtil::format_map_t args; - args["NAME"] = llformat("secondlife:///app/agent/%s/inspect", perp.asString().c_str()); + if (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) + { + args["NAME"] = llformat("secondlife:///app/agent/%s/inspect", perp.asString().c_str()); + } + else + { + args["NAME"] = llformat("secondlife:///app/agent/%s/rlvanonym", perp.asString().c_str()); + } switch (type) { @@ -6917,10 +6927,10 @@ void process_mean_collision_alert_message(LLMessageSystem *msgsystem, void **use LLMessageSystem* msgs = gMessageSystem; msgs->newMessage(_PREHASH_ScriptDialogReply); msgs->nextBlock(_PREHASH_AgentData); - msgs->addUUID(_PREHASH_AgentID, gAgent.getID()); - msgs->addUUID(_PREHASH_SessionID, gAgent.getSessionID()); + msgs->addUUID(_PREHASH_AgentID, gAgentID); + msgs->addUUID(_PREHASH_SessionID, gAgentSessionID); msgs->nextBlock(_PREHASH_Data); - msgs->addUUID(_PREHASH_ObjectID, gAgent.getID()); + msgs->addUUID(_PREHASH_ObjectID, gAgentID); msgs->addS32(_PREHASH_ChatChannel, gSavedSettings.getS32("FSReportCollisionMessagesChannel")); msgs->addS32(_PREHASH_ButtonIndex, 1); msgs->addString(_PREHASH_ButtonLabel, collision_data.c_str()); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index a2c8ba80aa..a0ae4a9f9b 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -249,9 +249,9 @@ public: LLVector3 mLastCameraOrigin; U32 mLastCameraUpdate; - void requestBaseCapabilitiesCoro(U64 regionHandle); - void requestBaseCapabilitiesCompleteCoro(U64 regionHandle); - void requestSimulatorFeatureCoro(std::string url, U64 regionHandle); + static void requestBaseCapabilitiesCoro(U64 regionHandle); + static void requestBaseCapabilitiesCompleteCoro(U64 regionHandle); + static void requestSimulatorFeatureCoro(std::string url, U64 regionHandle); }; void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) @@ -279,6 +279,7 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) LL_WARNS("AppInit", "Capabilities") << "Attempting to get capabilities for region that no longer exists!" << LL_ENDL; return; // this error condition is not recoverable. } + LLViewerRegionImpl* impl = regionp->getRegionImplNC(); LL_DEBUGS("AppInit", "Capabilities") << "requesting seed caps for handle " << regionHandle << " name " << regionp->getName() << LL_ENDL; @@ -293,32 +294,33 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) newRegionEntry(*regionp); // After a few attempts, continue login. But keep trying to get the caps: - if (mSeedCapAttempts >= mSeedCapMaxAttemptsBeforeLogin && + if (impl->mSeedCapAttempts >= impl->mSeedCapMaxAttemptsBeforeLogin && STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState()) { LLStartUp::setStartupState(STATE_SEED_CAP_GRANTED); } - if (mSeedCapAttempts > mSeedCapMaxAttempts) + if (impl->mSeedCapAttempts > impl->mSeedCapMaxAttempts) { // *TODO: Give a user pop-up about this error? - LL_WARNS("AppInit", "Capabilities") << "Failed to get seed capabilities from '" << url << "' after " << mSeedCapAttempts << " attempts. Giving up!" << LL_ENDL; + LL_WARNS("AppInit", "Capabilities") << "Failed to get seed capabilities from '" << url << "' after " << impl->mSeedCapAttempts << " attempts. Giving up!" << LL_ENDL; return; // this error condition is not recoverable. } - S32 id = ++mHttpResponderID; + S32 id = ++(impl->mHttpResponderID); LLSD capabilityNames = LLSD::emptyArray(); - buildCapabilityNames(capabilityNames); + impl->buildCapabilityNames(capabilityNames); LL_INFOS("AppInit", "Capabilities") << "Requesting seed from " << url << " region name " << regionp->getName() << " region id " << regionp->getRegionID() << " handle " << regionp->getHandle() - << " (attempt #" << mSeedCapAttempts + 1 << ")" << LL_ENDL; + << " (attempt #" << impl->mSeedCapAttempts + 1 << ")" << LL_ENDL; LL_DEBUGS("AppInit", "Capabilities") << "Capabilities requested: " << capabilityNames << LL_ENDL; regionp = NULL; + impl = NULL; result = httpAdapter->postAndSuspend(httpRequest, url, capabilityNames); if (STATE_WORLD_INIT > LLStartUp::getStartupState()) @@ -332,9 +334,6 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) return; } - // Fix seed cap retry count - //++mSeedCapAttempts; - regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle); if (!regionp) //region was removed { @@ -342,11 +341,16 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) return; // this error condition is not recoverable. } - if (id != mHttpResponderID) // region is no longer referring to this request + impl = regionp->getRegionImplNC(); + + // Fix seed cap retry count + //++impl->mSeedCapAttempts; + + if (id != impl->mHttpResponderID) // region is no longer referring to this request { LL_WARNS("AppInit", "Capabilities") << "Received results for a stale capabilities request!" << LL_ENDL; // setup for retry. - ++mSeedCapAttempts; // Fix seed cap retry count + ++(impl->mSeedCapAttempts); // Fix seed cap retry count continue; } @@ -354,7 +358,7 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) { LL_WARNS("AppInit", "Capabilities") << "Malformed response" << LL_ENDL; // setup for retry. - ++mSeedCapAttempts; // Fix seed cap retry count + ++(impl->mSeedCapAttempts); // Fix seed cap retry count continue; } @@ -364,7 +368,7 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) { LL_WARNS("AppInit", "Capabilities") << "HttpStatus error " << LL_ENDL; // setup for retry. - ++mSeedCapAttempts; // Fix seed cap retry count + ++(impl->mSeedCapAttempts); // Fix seed cap retry count continue; } @@ -402,7 +406,6 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) { // *HACK: we're waiting for the ServerReleaseNotes regionp->showReleaseNotes(); } - } @@ -463,6 +466,7 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCompleteCoro(U64 regionHandle) LL_WARNS("AppInit", "Capabilities") << "Received capabilities for region that no longer exists!" << LL_ENDL; break; // this error condition is not recoverable. } + LLViewerRegionImpl* impl = regionp->getRegionImplNC(); // remove the http_result from the llsd result.erase("http_result"); @@ -475,30 +479,30 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCompleteCoro(U64 regionHandle) } #if 0 - log_capabilities(mCapabilities); + log_capabilities(impl->mCapabilities); #endif - if (mCapabilities.size() != mSecondCapabilitiesTracker.size()) + if (impl->mCapabilities.size() != impl->mSecondCapabilitiesTracker.size()) { LL_WARNS("AppInit", "Capabilities") << "Sim sent duplicate base caps that differ in size from what we initially received - most likely content. " - << "mCapabilities == " << mCapabilities.size() - << " mSecondCapabilitiesTracker == " << mSecondCapabilitiesTracker.size() + << "mCapabilities == " << impl->mCapabilities.size() + << " mSecondCapabilitiesTracker == " << impl->mSecondCapabilitiesTracker.size() << LL_ENDL; #ifdef DEBUG_CAPS_GRANTS LL_WARNS("AppInit", "Capabilities") << "Initial Base capabilities: " << LL_ENDL; - log_capabilities(mCapabilities); + log_capabilities(impl->mCapabilities); LL_WARNS("AppInit", "Capabilities") << "Latest base capabilities: " << LL_ENDL; - log_capabilities(mSecondCapabilitiesTracker); + log_capabilities(impl->mSecondCapabilitiesTracker); #endif - if (mSecondCapabilitiesTracker.size() > mCapabilities.size()) + if (impl->mSecondCapabilitiesTracker.size() > impl->mCapabilities.size()) { // *HACK Since we were granted more base capabilities in this grant request than the initial, replace // the old with the new. This shouldn't happen i.e. we should always get the same capabilities from a @@ -506,19 +510,17 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCompleteCoro(U64 regionHandle) // inventory api capability grants. // Need to clear a std::map before copying into it because old keys take precedence. - mCapabilities.clear(); - mCapabilities = mSecondCapabilitiesTracker; + impl->mCapabilities.clear(); + impl->mCapabilities = impl->mSecondCapabilitiesTracker; } } else { LL_DEBUGS("CrossingCaps") << "Sim sent multiple base cap grants with matching sizes." << LL_ENDL; } - mSecondCapabilitiesTracker.clear(); + impl->mSecondCapabilitiesTracker.clear(); } while (false); - - } void LLViewerRegionImpl::requestSimulatorFeatureCoro(std::string url, U64 regionHandle) @@ -1538,7 +1540,12 @@ void LLViewerRegion::clearCachedVisibleObjects() for(LLVOCacheEntry::vocache_entry_set_t::iterator iter = mImpl->mActiveSet.begin(); iter != mImpl->mActiveSet.end(); ++iter) { - LLDrawable* drawablep = (LLDrawable*)(*iter)->getEntry()->getDrawable(); + LLVOCacheEntry* vo_entry = *iter; + if (!vo_entry || !vo_entry->getEntry()) + { + continue; + } + LLDrawable* drawablep = (LLDrawable*)vo_entry->getEntry()->getDrawable(); if(drawablep && !drawablep->getParent()) { @@ -2400,7 +2407,7 @@ void LLViewerRegion::requestSimulatorFeatures() { std::string coroname = LLCoros::instance().launch("LLViewerRegionImpl::requestSimulatorFeatureCoro", - boost::bind(&LLViewerRegionImpl::requestSimulatorFeatureCoro, mImpl, url, getHandle())); + boost::bind(&LLViewerRegionImpl::requestSimulatorFeatureCoro, url, getHandle())); LL_INFOS("AppInit", "SimulatorFeatures") << "Launching " << coroname << " requesting simulator features from " << url << " for region " << getRegionID() << LL_ENDL; } @@ -3278,7 +3285,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) //to the "original" seed cap received and determine why there is problem! std::string coroname = LLCoros::instance().launch("LLEnvironmentRequest::requestBaseCapabilitiesCompleteCoro", - boost::bind(&LLViewerRegionImpl::requestBaseCapabilitiesCompleteCoro, mImpl, getHandle())); + boost::bind(&LLViewerRegionImpl::requestBaseCapabilitiesCompleteCoro, getHandle())); return; } @@ -3290,7 +3297,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) std::string coroname = LLCoros::instance().launch("LLViewerRegionImpl::requestBaseCapabilitiesCoro", - boost::bind(&LLViewerRegionImpl::requestBaseCapabilitiesCoro, mImpl, getHandle())); + boost::bind(&LLViewerRegionImpl::requestBaseCapabilitiesCoro, getHandle())); LL_INFOS("AppInit", "Capabilities") << "Launching " << coroname << " requesting seed capabilities from " << url << " for region " << getRegionID() << LL_ENDL; } diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 18fba85be1..e27205d3ef 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -62,6 +62,7 @@ #include "llsdutil.h" #include "llcorehttputil.h" #include "llvoicevivox.h" +#include "llinventorymodel.h" #include "lluiusage.h" namespace LLStatViewer @@ -211,6 +212,7 @@ LLTrace::EventStatHandle AVATAR_EDIT_TIME("avataredittime", "Second LLTrace::EventStatHandle > OBJECT_CACHE_HIT_RATE("object_cache_hits"); +LLTrace::EventStatHandle TEXTURE_FETCH_TIME("texture_fetch_time"); } LLViewerStats::LLViewerStats() @@ -391,15 +393,6 @@ void update_statistics() add(LLStatViewer::ASSET_UDP_DATA_RECEIVED, F64Bits(gTransferManager.getTransferBitsIn(LLTCT_ASSET))); gTransferManager.resetTransferBitsIn(LLTCT_ASSET); - if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) - { - gTextureTimer.pause(); - } - else - { - gTextureTimer.unpause(); - } - sample(LLStatViewer::VISIBLE_AVATARS, LLVOAvatar::sNumVisibleAvatars); LLWorld::getInstance()->updateNetStats(); LLWorld::getInstance()->requestCacheMisses(); @@ -421,6 +414,19 @@ void update_statistics() } } +void update_texture_time() +{ + if (gTextureList.isPrioRequestsFetched()) + { + gTextureTimer.pause(); + } + else + { + gTextureTimer.unpause(); + } + + record(LLStatViewer::TEXTURE_FETCH_TIME, gTextureTimer.getElapsedTimeF32()); +} /* * The sim-side LLSD is in newsim/llagentinfo.cpp:forwardViewerStats. * @@ -581,6 +587,11 @@ void send_viewer_stats(bool include_preferences) fail["invalid"] = (S32) gMessageSystem->mInvalidOnCircuitPackets; fail["missing_updater"] = (S32) LLAppViewer::instance()->isUpdaterMissing(); + LLSD &inventory = body["inventory"]; + inventory["usable"] = gInventory.isInventoryUsable(); + LLSD& validation_info = inventory["validation_info"]; + gInventory.mValidationInfo->asLLSD(validation_info); + // We do not need to send UI usage stats according to Jess... //body["ui"] = LLUIUsage::instance().asLLSD(); diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index b596c744c5..9ee52dba04 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -299,6 +299,7 @@ static const F32 SEND_STATS_PERIOD = 300.0f; // The following are from (older?) statistics code found in appviewer. void update_statistics(); void send_viewer_stats(bool include_preferences); +void update_texture_time(); extern LLFrameTimer gTextureTimer; extern U32Bytes gTotalTextureData; diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index cc9e208957..6022f875aa 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -454,6 +454,7 @@ public: BOOL isFullyLoaded() const; BOOL hasFetcher() const { return mHasFetcher;} + bool isFetching() const { return mIsFetching;} void setCanUseHTTP(bool can_use_http) {mCanUseHTTP = can_use_http;} void forceToDeleteRequest(); diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 16610206d0..a713246161 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -206,6 +206,9 @@ static std::string get_texture_list_name() void LLViewerTextureList::doPrefetchImages() { + gTextureTimer.start(); + gTextureTimer.pause(); + if (LLAppViewer::instance()->getPurgeCache()) { // cache was purged, no point @@ -1400,7 +1403,8 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename, LLPointer image = LLImageFormatted::createFromType(codec); if (image.isNull()) { - image->setLastError("Couldn't open the image to be uploaded."); + // Pointer is null! + //image->setLastError("Couldn't open the image to be uploaded."); return FALSE; } if (!image->load(filename)) @@ -1559,6 +1563,33 @@ S32Megabytes LLViewerTextureList::getMaxVideoRamSetting(bool get_recommended, fl return max_texmem; } +bool LLViewerTextureList::isPrioRequestsFetched() +{ + static LLCachedControl prio_threshold(gSavedSettings, "TextureFetchUpdatePriorityThreshold", 0.0f); + static LLCachedControl fetching_textures_threshold(gSavedSettings, "TextureListFetchingThreshold", 0.97f); + S32 fetching_tex_count = 0; + S32 tex_count_threshold = gTextureList.mImageList.size() * (1 - fetching_textures_threshold); + + for (LLViewerTextureList::image_priority_list_t::iterator iter = gTextureList.mImageList.begin(); + iter != gTextureList.mImageList.end(); ) + { + LLPointer imagep = *iter++; + if (imagep->getDecodePriority() > prio_threshold) + { + if (imagep->hasFetcher() || imagep->isFetching()) + { + fetching_tex_count++; + if (fetching_tex_count >= tex_count_threshold) + { + return false; + } + } + } + } + + return true; +} + const S32Megabytes VIDEO_CARD_FRAMEBUFFER_MEM(12); const S32Megabytes MIN_MEM_FOR_NON_TEXTURE(512); void LLViewerTextureList::updateMaxResidentTexMem(S32Megabytes mem) diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index ca26883763..7af3ed7e8b 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -145,6 +145,8 @@ public: // Proper texture memory calculation //static S32Megabytes getMaxVideoRamSetting(bool get_recommended, float mem_multiplier); static S32Megabytes getMaxVideoRamSetting(bool get_recommended, float mem_multiplier, bool clamp_upper_limit = true); + + static bool isPrioRequestsFetched(); private: void updateImagesDecodePriorities(); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 74506414c9..74b665eefc 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -3310,60 +3310,64 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) if (keyboard_focus && !gFocusMgr.getKeystrokesOnly()) { -#ifdef LL_WINDOWS - // On windows Alt Gr key generates additional Ctrl event, as result handling situations - // like 'AltGr + D' will result in 'Alt+Ctrl+D'. If it results in WM_CHAR, don't let it - // pass into menu or it will trigger 'develop' menu assigned to this combination on top - // of character handling. - // Alt Gr can be additionally modified by Shift - const MASK alt_gr = MASK_CONTROL | MASK_ALT; - LLWindowWin32 *window = static_cast(mWindow); - U32 raw_key = window->getRawWParam(); - if ((mask & alt_gr) != 0 - && ((raw_key >= 0x30 && raw_key <= 0x5A) //0-9, plus normal chartacters - || (raw_key >= 0xBA && raw_key <= 0xE4)) // Misc/OEM characters that can be covered by AltGr, ex: -, =, ~ - && (GetKeyState(VK_RMENU) & 0x8000) != 0 - && (GetKeyState(VK_RCONTROL) & 0x8000) == 0) // ensure right control is not pressed, only left one + LLUICtrl* cur_focus = dynamic_cast(keyboard_focus); + if (cur_focus && cur_focus->acceptsTextInput()) { - // Alt Gr key is represented as right alt and left control. - // Any alt+ctrl combination is treated as Alt Gr by TranslateMessage() and - // will generate a WM_CHAR message, but here we only treat virtual Alt Graph - // key by checking if this specific combination has unicode char. - // - // I decided to handle only virtual RAlt+LCtrl==AltGr combination to minimize - // impact on menu, but the right way might be to handle all Alt+Ctrl calls. - - BYTE keyboard_state[256]; - if (GetKeyboardState(keyboard_state)) +#ifdef LL_WINDOWS + // On windows Alt Gr key generates additional Ctrl event, as result handling situations + // like 'AltGr + D' will result in 'Alt+Ctrl+D'. If it results in WM_CHAR, don't let it + // pass into menu or it will trigger 'develop' menu assigned to this combination on top + // of character handling. + // Alt Gr can be additionally modified by Shift + const MASK alt_gr = MASK_CONTROL | MASK_ALT; + LLWindowWin32 *window = static_cast(mWindow); + U32 raw_key = window->getRawWParam(); + if ((mask & alt_gr) != 0 + && ((raw_key >= 0x30 && raw_key <= 0x5A) //0-9, plus normal chartacters + || (raw_key >= 0xBA && raw_key <= 0xE4)) // Misc/OEM characters that can be covered by AltGr, ex: -, =, ~ + && (GetKeyState(VK_RMENU) & 0x8000) != 0 + && (GetKeyState(VK_RCONTROL) & 0x8000) == 0) // ensure right control is not pressed, only left one { - const int char_count = 6; - wchar_t chars[char_count]; - HKL layout = GetKeyboardLayout(0); - // ToUnicodeEx changes buffer state on OS below Win10, which is undesirable, - // but since we already did a TranslateMessage() in gatherInput(), this - // should have no negative effect + // Alt Gr key is represented as right alt and left control. + // Any alt+ctrl combination is treated as Alt Gr by TranslateMessage() and + // will generate a WM_CHAR message, but here we only treat virtual Alt Graph + // key by checking if this specific combination has unicode char. + // + // I decided to handle only virtual RAlt+LCtrl==AltGr combination to minimize + // impact on menu, but the right way might be to handle all Alt+Ctrl calls. + + BYTE keyboard_state[256]; + if (GetKeyboardState(keyboard_state)) + { + const int char_count = 6; + wchar_t chars[char_count]; + HKL layout = GetKeyboardLayout(0); + // ToUnicodeEx changes buffer state on OS below Win10, which is undesirable, + // but since we already did a TranslateMessage() in gatherInput(), this + // should have no negative effect // ToUnicodeEx works with virtual key codes int res = ToUnicodeEx(raw_key, 0, keyboard_state, chars, char_count, 1 << 2 /*do not modify buffer flag*/, layout); - if (res == 1 && chars[0] >= 0x20) - { - // Let it fall through to character handler and get a WM_CHAR. - return TRUE; + if (res == 1 && chars[0] >= 0x20) + { + // Let it fall through to character handler and get a WM_CHAR. + return TRUE; + } } } - } #endif - if (!(mask & (MASK_CONTROL | MASK_ALT))) - { - // We have keyboard focus, and it's not an accelerator - if (keyboard_focus && keyboard_focus->wantsKeyUpKeyDown()) + if (!(mask & (MASK_CONTROL | MASK_ALT))) { - return keyboard_focus->handleKey(key, mask, FALSE); - } - else if (key < 0x80) - { - // Not a special key, so likely (we hope) to generate a character. Let it fall through to character handler first. - return TRUE; + // We have keyboard focus, and it's not an accelerator + if (keyboard_focus && keyboard_focus->wantsKeyUpKeyDown()) + { + return keyboard_focus->handleKey(key, mask, FALSE); + } + else if (key < 0x80) + { + // Not a special key, so likely (we hope) to generate a character. Let it fall through to character handler first. + return TRUE; + } } } } diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 422f130057..2035f8b46b 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -1272,7 +1272,7 @@ void LLVOAvatarSelf::removeMissingBakedTextures() // Check whether the BOM capability is different to last time we changed region (even across login) void LLVOAvatarSelf::checkBOMRebakeRequired() { - if(!getRegion()) + if(getRegion()) { auto newBOMStatus = getRegion()->bakesOnMeshEnabled(); if(!gSavedSettings.getBOOL("CurrentlyUsingBakesOnMesh") != newBOMStatus) diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 0591fd898d..4b0a3c8259 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -89,6 +89,7 @@ namespace { // Don't retry connecting to the daemon more frequently than this: const F32 DAEMON_CONNECT_THROTTLE_SECONDS = 1.0f; + const int DAEMON_CONNECT_RETRY_MAX = 3; // Don't send positional updates more frequently than this: const F32 UPDATE_THROTTLE_SECONDS = 0.5f; @@ -765,6 +766,11 @@ void LLVivoxVoiceClient::voiceControlCoro() void LLVivoxVoiceClient::voiceControlStateMachine(S32 &coro_state) { + if (sShuttingDown) + { + return; + } + LL_DEBUGS("Voice") << "starting" << LL_ENDL; mIsCoroutineActive = true; LLCoros::set_consuming(true); @@ -920,6 +926,12 @@ void LLVivoxVoiceClient::voiceControlStateMachine(S32 &coro_state) } } while (coro_state > 0); + if (sShuttingDown) + { + // LLVivoxVoiceClient might be already dead + return; + } + mIsCoroutineActive = false; LL_INFOS("Voice") << "exiting" << LL_ENDL; } @@ -1149,8 +1161,9 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << LL_ENDL; + int retryCount(0); LLVoiceVivoxStats::getInstance()->reset(); - while (!mConnected && !sShuttingDown) + while (!mConnected && !sShuttingDown && retryCount++ <= DAEMON_CONNECT_RETRY_MAX) { LLVoiceVivoxStats::getInstance()->connectionAttemptStart(); LL_DEBUGS("Voice") << "Attempting to connect to vivox daemon: " << mDaemonHost << LL_ENDL; @@ -1276,7 +1289,7 @@ bool LLVivoxVoiceClient::provisionVoiceAccount() { provisioned = true; } - } while (!provisioned && retryCount <= PROVISION_RETRY_MAX && !sShuttingDown); + } while (!provisioned && ++retryCount <= PROVISION_RETRY_MAX && !sShuttingDown); if (sShuttingDown && !provisioned) { @@ -1459,6 +1472,12 @@ bool LLVivoxVoiceClient::loginToVivox() } LLSD result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, LOGIN_ATTEMPT_TIMEOUT, timeoutResult); + + if (sShuttingDown) + { + return false; + } + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; if (result.has("login")) @@ -1521,6 +1540,11 @@ bool LLVivoxVoiceClient::loginToVivox() } while ((!response_ok || !account_login) && !sShuttingDown); + if (sShuttingDown) + { + return false; + } + mRelogRequested = false; mIsLoggedIn = true; notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); @@ -3373,17 +3397,31 @@ void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void) earVelocity = mAvatarVelocity; earRot = mCameraRot; break; +// reimplement hear voice equally + case earLocSpeaker: + // we leave EarPos/Vel/Rot as empty + LL_DEBUGS("Voice") << "EarLoc Speaker in use" << LL_ENDL; + break; +// } +// reimplement hear voice equally + if(mEarLocation != earLocSpeaker) + { + // for all spatial sources we need to do the transform +// l = earRot.getLeftRow(); u = earRot.getUpRow(); a = earRot.getFwdRow(); pos = earPosition; vel = earVelocity; - + oldSDKTransform(l, u, a, pos, vel); +// reimplement hear voice equally + } +// if (mHidden) { diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index d19e6ca718..23a04b723c 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -803,6 +803,7 @@ private: { earLocCamera = 0, // ear at camera earLocAvatar, // ear at avatar + earLocSpeaker, // re-add equal voice based loosely on original patch from Tigh MacFanatic earLocMixed // ear at avatar location/camera direction }; diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 29cdd84baa..a9bad65b56 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -464,7 +464,6 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, if (TEM_INVALID == result) { // There's something bogus in the data that we're unpacking. - dp->dumpBufferToLog(); std::string region_name = "unknown region"; if (getRegion()) { @@ -6429,14 +6428,25 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group) if (debug_logging_on) // { - if (vobj->isAnimatedObject() && vobj->isRiggedMesh()) + if (vobj && vobj->isAnimatedObject() && vobj->isRiggedMesh()) { std::string vobj_name = llformat("Vol%p", vobj); F32 est_tris = vobj->getEstTrianglesMax(); - LL_DEBUGS("AnimatedObjectsLinkset") << vobj_name << " rebuildMesh, tris " << est_tris << LL_ENDL; + LL_DEBUGS("AnimatedObjectsLinkset") << vobj_name << " rebuildMesh, tris " << est_tris << LL_ENDL; } } - if (vobj->isNoLOD()) continue; + + if (!vobj || vobj->isNoLOD()) + { + continue; + } + + LLVolume* volume = vobj->getVolume(); + + if (!volume) + { + continue; + } vobj->preRebuild(); @@ -6445,7 +6455,6 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group) vobj->updateRelativeXform(true); } - LLVolume* volume = vobj->getVolume(); for (S32 i = 0; i < drawablep->getNumFaces(); ++i) { LLFace* face = drawablep->getFace(i); diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp index bae615232e..a041edb855 100644 --- a/indra/newview/llxmlrpclistener.cpp +++ b/indra/newview/llxmlrpclistener.cpp @@ -344,7 +344,10 @@ public: switch (curlcode) { case CURLE_SSL_PEER_CERTIFICATE: +// CURLE_SSL_CACERT has been deprecated, the LIBCURL-VERSION_NUM check is probably no checking for the lowest curl vesion this did happen +#if LIBCURL_VERSION_NUM < 0x075100 case CURLE_SSL_CACERT: +#endif data["certificate"] = mTransaction->getErrorCertData(); break; @@ -421,59 +424,109 @@ private: 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) + switch (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) + case xmlrpc_type_empty: + LL_INFOS("LLXMLRPCListener") << "Empty result for key " << key_pfx << key << LL_ENDL; + responses.insert(key, LLSD()); + break; + case xmlrpc_type_base64: { - // 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)); + S32 len = XMLRPC_GetValueStringLen(current); + const char* buf = XMLRPC_GetValueBase64(current); + if ((len > 0) && buf) + { + // During implementation this code was not tested + // If you encounter this, please make sure this is correct, + // then remove llassert + llassert(0); + + LLSD::Binary data; + data.resize(len); + memcpy((void*)&data[0], (void*)buf, len); + responses.insert(key, data); + } + else + { + LL_WARNS("LLXMLRPCListener") << "Potentially malformed xmlrpc_type_base64 for key " + << key_pfx << key << LL_ENDL; + responses.insert(key, LLSD()); + } + break; } - // Having collected an 'array' of 'submap's, insert that whole - // 'array' as the value of this 'key'. - responses.insert(key, array); - } - else if (xmlrpc_type_struct == type) - { - LLSD submap = parseValues(status_string, - STRINGIZE(key_pfx << key << ':'), - current); - responses.insert(key, submap); - } - else - { + case xmlrpc_type_boolean: + { + LLSD::Boolean val(XMLRPC_GetValueBoolean(current)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL; + responses.insert(key, val); + break; + } + case xmlrpc_type_datetime: + { + std::string iso8601_date(XMLRPC_GetValueDateTime_ISO8601(current)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << iso8601_date << LL_ENDL; + responses.insert(key, LLSD::Date(iso8601_date)); + break; + } + case xmlrpc_type_double: + { + LLSD::Real val(XMLRPC_GetValueDouble(current)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL; + responses.insert(key, val); + break; + } + case xmlrpc_type_int: + { + LLSD::Integer val(XMLRPC_GetValueInt(current)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL; + responses.insert(key, val); + break; + } + case xmlrpc_type_string: + { + LLSD::String val(XMLRPC_GetValueString(current)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL; + responses.insert(key, val); + break; + } + case xmlrpc_type_mixed: + case xmlrpc_type_array: + { + // 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); + break; + } + case xmlrpc_type_struct: + { + LLSD submap = parseValues(status_string, + STRINGIZE(key_pfx << key << ':'), + current); + responses.insert(key, submap); + break; + } + case xmlrpc_type_none: // Not expected + default: // whoops - unrecognized type LL_WARNS("LLXMLRPCListener") << "Unhandled xmlrpc type " << type << " for key " - << key_pfx << key << LL_ENDL; + << key_pfx << key << LL_ENDL; responses.insert(key, STRINGIZE("')); status_string = "BadType"; } diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 4d7ec57ace..5916b43539 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -517,7 +517,10 @@ void LLXMLRPCTransaction::Impl::setHttpStatus(const LLCore::HttpStatus &status) message = LLTrans::getString("ssl_peer_certificate"); break; +// CURLE_SSL_CACERT has been deprecated, the LIBCURL-VERSION_NUM check is probably no checking for the lowest curl vesion this did happen +#if LIBCURL_VERSION_NUM < 0x075100 case CURLE_SSL_CACERT: +#endif case CURLE_SSL_CONNECT_ERROR: message = LLTrans::getString("ssl_connect_error"); break; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 7b5c595f15..8acd6191e2 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -7374,6 +7374,7 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector4a& start, { if ((j == LLViewerRegion::PARTITION_VOLUME) || (j == LLViewerRegion::PARTITION_BRIDGE) || + (j == LLViewerRegion::PARTITION_AVATAR) || // for attachments (j == LLViewerRegion::PARTITION_CONTROL_AV) || (j == LLViewerRegion::PARTITION_TERRAIN) || (j == LLViewerRegion::PARTITION_TREE) || diff --git a/indra/newview/res-sdl/lltoolzoomout.BMP b/indra/newview/res-sdl/lltoolzoomout.BMP index 7f958383ab..5bdf96f80d 100644 Binary files a/indra/newview/res-sdl/lltoolzoomout.BMP and b/indra/newview/res-sdl/lltoolzoomout.BMP differ diff --git a/indra/newview/res-sdl/sizeall.BMP b/indra/newview/res-sdl/sizeall.BMP new file mode 100644 index 0000000000..03d9bf4654 Binary files /dev/null and b/indra/newview/res-sdl/sizeall.BMP differ diff --git a/indra/newview/res/lltoolzoomout.cur b/indra/newview/res/lltoolzoomout.cur index b33e68d1a6..21e0ee9702 100644 Binary files a/indra/newview/res/lltoolzoomout.cur and b/indra/newview/res/lltoolzoomout.cur differ diff --git a/indra/newview/res/viewerRes.rc b/indra/newview/res/viewerRes.rc index afbb5e1b1b..1f7a2b71e8 100755 --- a/indra/newview/res/viewerRes.rc +++ b/indra/newview/res/viewerRes.rc @@ -99,6 +99,7 @@ END TOOLGRAB CURSOR "lltoolgrab.cur" TOOLLAND CURSOR "lltoolland.cur" TOOLZOOMIN CURSOR "lltoolzoomin.cur" +TOOLZOOMOUT CURSOR "lltoolzoomout.cur" TOOLCREATE CURSOR "lltoolcreate.cur" ARROWDRAG CURSOR "llarrowdrag.cur" ARROW CURSOR "llarrow.cur" diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 3d135e4fc0..e900e09cdf 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -61,6 +61,9 @@ with the same filename but different name + + + diff --git a/indra/newview/skins/default/xui/da/floater_world_map.xml b/indra/newview/skins/default/xui/da/floater_world_map.xml index 0086fb0cbe..6794d73381 100644 --- a/indra/newview/skins/default/xui/da/floater_world_map.xml +++ b/indra/newview/skins/default/xui/da/floater_world_map.xml @@ -1,14 +1,6 @@ - - - + + + + + + + + Tuning Strategy + + + + + + + - - Auto-Tune Frame Rate - - - - - Automatically adjust settings to maintain FPS - - - - - - - - - - - - + + width="180"> Graphics settings - width="395"> Choose settings for distance, water, lighting and more. - name="icon_arrow3" follows="right|top" top="19" - right="-20"/> - - + right="-20" /> + + + follows="left|top" + font="SansSerifLarge" + text_color="White" + height="20" + layout="topleft" + left="10" + name="avatars_nearby_lbl" + top="7" + width="205"> Avatars nearby + follows="left|top" + font="SansSerif" + text_color="White" + height="20" + layout="topleft" + left="10" + name="avatars_nearby_desc" + top_pad="0" + width="345"> Manage which nearby avatars are fully displayed. - + Time spent drawing avatars + follows="top|right" + font="SansSerifHuge" + text_color="White" + height="20" + layout="topleft" + left_pad="10" + top="14" + name="av_frame_stats" + width="62"> 00% + height="16" + width="16" + image_name="Arrow_Right_Off" + mouse_opaque="true" + name="icon_arrow2" + follows="right|top" + top="19" + right="-20" /> + bg_alpha_color="PanelGray" + background_visible="true" + background_opaque="false" + border="true" + bevel_style="none" + follows="left|top|right" + height="50" + width="560" + name="complexity_subpanel" + layout="topleft" + top_pad="10"> + follows="left|top" + font="SansSerifLarge" + text_color="White" + height="20" + layout="topleft" + left="10" + name="complexity_lbl" + top="7" + width="180"> Your avatar complexity + follows="left|top" + font="SansSerif" + text_color="White" + height="20" + layout="topleft" + left="10" + name="complexity_info" + top_pad="0" + width="455"> Be a good citizen. Manage the impact of your avatar + height="16" + width="16" + image_name="Arrow_Right_Off" + mouse_opaque="true" + name="icon_arrow4" + follows="right|top" + top="19" + right="-20" /> + bg_alpha_color="PanelGray" + background_visible="true" + background_opaque="false" + border="true" + bevel_style="none" + follows="left|top|right" + height="50" + width="560" + name="huds_subpanel" + layout="topleft" + top_pad="10"> + follows="left|top" + font="SansSerifLarge" + text_color="White" + height="20" + layout="topleft" + left="10" + name="huds_lbl" + top="7" + width="135"> Your active HUDs + follows="left|top" + font="SansSerif" + text_color="White" + height="20" + layout="topleft" + left="10" + name="huds_desc" + top_pad="0" + width="345"> Removing unnecessary HUDs may improve speed. + follows="top|right" + font="SansSerifSmall" + text_color="White" + height="28" + layout="topleft" + left_pad="10" + name="huds_frme_pct_lbl" + top="8" + width="75"> Time spent drawing HUDs - + 00% + height="16" + width="16" + image_name="Arrow_Right_Off" + mouse_opaque="true" + name="icon_arrow4" + follows="right|top" + top="19" + right="-20" /> + filename="panel_performance_nearby.xml" + follows="all" + layout="topleft" + left="0" + name="panel_performance_nearby" + visible="false" + top="115" /> + filename="panel_performance_complexity.xml" + follows="all" + layout="topleft" + left="0" + name="panel_performance_complexity" + visible="false" + top="115" /> + filename="panel_performance_preferences.xml" + follows="all" + layout="topleft" + left="0" + name="panel_performance_preferences" + visible="false" + top="115" /> + filename="panel_performance_huds.xml" + follows="all" + layout="topleft" + left="0" + name="panel_performance_huds" + visible="false" + top="115" /> + filename="panel_performance_autotune.xml" + follows="all" + layout="topleft" + left="0" + name="panel_performance_autotune" + visible="false" + top="115" /> diff --git a/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml index f72791c9fb..6b311c8d62 100644 --- a/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml @@ -36,15 +36,13 @@ stat="FramePixelDifference" bar_max="100" tick_spacing="10" - unit_scale="100" - precision="0"/> + unit_scale="100"/> + tick_spacing="500"/> + show_bar="false"/> + + + + @@ -217,77 +240,61 @@ label="Objects" orientation="horizontal" stat="simobjects" - precision="0" bar_max="30000.f" tick_spacing="5000.f" - show_bar="false" - show_mean="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> @@ -296,81 +303,65 @@ orientation="horizontal" stat="simframemsec" unit_label="ms" - precision="3" bar_max="40.f" tick_spacing="10.f" - show_bar="false" - show_mean="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> + show_bar="false"/> diff --git a/indra/newview/skins/default/xui/en/floater_select_key.xml b/indra/newview/skins/default/xui/en/floater_select_key.xml index e133cf6637..670d702dc5 100644 --- a/indra/newview/skins/default/xui/en/floater_select_key.xml +++ b/indra/newview/skins/default/xui/en/floater_select_key.xml @@ -35,7 +35,7 @@ Combination [KEYSTR] is reserved by menu. height="30" layout="topleft" left="5" - name="descritption" + name="description" top="25" word_wrap="true" right="-5"> diff --git a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml index e2cca76d44..5cfa142635 100644 --- a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml +++ b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml @@ -317,6 +317,18 @@ + + + Note: Your own avatar includes viewer overheads. Use the attachment tab to see how you affect others. + label_text.text_color="White" layout="topleft" name="display_friends" - top_pad="3" + top_pad="2" left="18" width="256"> + - - - - Your system's existing proxy settings will be used. - - + + width="400"> unknown Release Notes - https://wiki.firestormviewer.org/firestorm_change_log_ + https://wiki.firestormviewer.org/changelog:firestorm_change_log_ Loading... @@ -655,6 +655,10 @@ https://www.firestormviewer.org/support for help fixing this problem. Comma separated values Recompile + + JPEG Images + Executable Program Files + Sleeps script for [SLEEP_TIME] seconds. diff --git a/indra/newview/skins/default/xui/es/floater_import_collada.xml b/indra/newview/skins/default/xui/es/floater_import_collada.xml index 7e9a00797a..24df8e41a3 100644 --- a/indra/newview/skins/default/xui/es/floater_import_collada.xml +++ b/indra/newview/skins/default/xui/es/floater_import_collada.xml @@ -1,13 +1,13 @@ - Redes: [RECUENTO] + Redes: [COUNT] - Texturas: [RECUENTO] + Texturas: [COUNT] - Estado: [ESTADO] + Estado: [STATUS]