diff options
Diffstat (limited to 'indra')
35 files changed, 1288 insertions, 109 deletions
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 42e211c84d..d02e6b123b 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -16,6 +16,7 @@ include_guard() include(Variables) include(Linker) +include(UnixInstall) # We go to some trouble to set LL_BUILD to the set of relevant compiler flags. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} $ENV{LL_BUILD}") @@ -127,7 +128,7 @@ endif (NOT CMAKE_CXX_COMPILER_ID MATCHES GNU AND WINDOWS) if (LINUX OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD") set( CMAKE_BUILD_WITH_INSTALL_RPATH TRUE ) - set( CMAKE_INSTALL_RPATH $ORIGIN $ORIGIN/../lib ) + set( CMAKE_INSTALL_RPATH ${INSTALL_LIBRARY_DIR} ) set(CMAKE_EXE_LINKER_FLAGS "-Wl,--exclude-libs,ALL") find_program(CCACHE_EXE ccache) diff --git a/indra/cmake/CEFPlugin.cmake b/indra/cmake/CEFPlugin.cmake index 31ed86213a..117c83353e 100644 --- a/indra/cmake/CEFPlugin.cmake +++ b/indra/cmake/CEFPlugin.cmake @@ -1,54 +1,187 @@ # -*- cmake -*- include(Linking) include(Prebuilt) +include(UnixInstall) include_guard() add_library( ll::cef INTERFACE IMPORTED ) -if (CMAKE_SYSTEM_PROCESSOR MATCHES aarch64) +if (${LINUX_DISTRO} MATCHES arch) if (${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKING_DIR}/dullahan_installed OR NOT ${dullahan_installed} EQUAL 0) - if (NOT EXISTS ${CMAKE_BINARY_DIR}/v1.14.0-r3.tar.gz) + file( + COPY /usr/src/cef/libcef_dll + DESTINATION ${CMAKE_BINARY_DIR} + ) + execute_process( + COMMAND sed -i "s/macro(L/cmake_minimum_required(VERSION 3.28)\\nmacro(SET_LIBRARY_TARGET_PROPERTIES)\\nendmacro()\\nmacro(L/" CMakeLists.txt + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/libcef_dll + ) + try_compile(LIBCEF_DLL_RESULT + PROJECT libcef_dll + SOURCE_DIR ${CMAKE_BINARY_DIR}/libcef_dll + BINARY_DIR ${CMAKE_BINARY_DIR}/libcef_dll + CMAKE_FLAGS + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + "-DCMAKE_CXX_FLAGS:STRING=-I/usr/include/cef -I/usr/src/cef -fPIC" + ) + if (${LIBCEF_DLL_RESULT}) + file( + COPY ${CMAKE_BINARY_DIR}/libcef_dll/libcef_dll_wrapper.a + DESTINATION ${ARCH_PREBUILT_DIRS_RELEASE} + ) + endif () + if (NOT EXISTS ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10.tar.gz) file(DOWNLOAD - https://github.com/secondlife/dullahan/archive/refs/tags/v1.14.0-r3.tar.gz - ${CMAKE_BINARY_DIR}/v1.14.0-r3.tar.gz + https://github.com/secondlife/dullahan/archive/refs/tags/v1.30.0-CEF_147.0.10.tar.gz + ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10.tar.gz + ) + endif () + file(ARCHIVE_EXTRACT + INPUT ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10.tar.gz + DESTINATION ${CMAKE_BINARY_DIR} + ) + try_compile(DULLAHAN_RESULT + PROJECT dullahan + SOURCE_DIR ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10 + BINARY_DIR ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10 + CMAKE_FLAGS + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX:PATH=${LIBS_PREBUILT_DIR} + -DCMAKE_INSTALL_LIBDIR:PATH=${ARCH_PREBUILT_DIRS_RELEASE} + -DCEF_WRAPPER_DIR:PATH=/usr/include/cef + -DCEF_WRAPPER_BUILD_DIR:PATH=${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10 + -DCEF_LIBRARY_RELEASE:FILEPATH=${INSTALL_PREFIX}/${_LIB}/cef/libcef.so + -DCEF_DLL_LIBRARY_RELEASE:FILEPATH=${ARCH_PREBUILT_DIRS_RELEASE}/libcef_dll_wrapper.a + "-DCMAKE_CXX_FLAGS:STRING=-I/usr/include/cef -I/usr/src/cef -DWRAPPING_CEF_SHARED" + ) + if (${DULLAHAN_RESULT}) + file(MAKE_DIRECTORY ${LIBS_PREBUILT_DIR}/bin/release) + file( + COPY ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10/dullahan_host + DESTINATION ${LIBS_PREBUILT_DIR}/bin/release + ) + file( + COPY ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10/libdullahan.a + DESTINATION ${ARCH_PREBUILT_DIRS_RELEASE} + ) + file(MAKE_DIRECTORY ${LIBS_PREBUILT_DIR}/include/cef) + file( + COPY + ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10/src/dullahan.h + ${CMAKE_BINARY_DIR}/dullahan-1.30.0-CEF_147.0.10/src/dullahan_version.h + DESTINATION ${LIBS_PREBUILT_DIR}/include/cef + ) + file(WRITE ${PREBUILD_TRACKING_DIR}/dullahan_installed "0") + endif () + endif () +elseif (${LINUX_DISTRO} MATCHES fedora) + if (${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKING_DIR}/dullahan_installed OR NOT ${dullahan_installed} EQUAL 0) + file( + COPY /usr/src/cef-146.0.11/libcef_dll + DESTINATION ${CMAKE_BINARY_DIR} + ) + execute_process( + COMMAND sed -i "s/macro(L/cmake_minimum_required(VERSION 3.28)\\nmacro(SET_LIBRARY_TARGET_PROPERTIES)\\nendmacro()\\nmacro(L/" CMakeLists.txt + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/libcef_dll + ) + try_compile(LIBCEF_DLL_RESULT + PROJECT libcef_dll + SOURCE_DIR ${CMAKE_BINARY_DIR}/libcef_dll + BINARY_DIR ${CMAKE_BINARY_DIR}/libcef_dll + CMAKE_FLAGS + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + "-DCMAKE_CXX_FLAGS:STRING=-I/usr/include/cef -I/usr/src/cef-146.0.11 -fPIC" + ) + if (${LIBCEF_DLL_RESULT}) + file( + COPY ${CMAKE_BINARY_DIR}/libcef_dll/libcef_dll_wrapper.a + DESTINATION ${ARCH_PREBUILT_DIRS_RELEASE} + ) + endif () + if (NOT EXISTS ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12.tar.gz) + file(DOWNLOAD + https://github.com/secondlife/dullahan/archive/refs/tags/v1.29.0-CEF_146.0.12.tar.gz + ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12.tar.gz + ) + endif () + file(ARCHIVE_EXTRACT + INPUT ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12.tar.gz + DESTINATION ${CMAKE_BINARY_DIR} + ) + try_compile(DULLAHAN_RESULT + PROJECT dullahan + SOURCE_DIR ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12 + BINARY_DIR ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12 + CMAKE_FLAGS + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX:PATH=${LIBS_PREBUILT_DIR} + -DCMAKE_INSTALL_LIBDIR:PATH=${ARCH_PREBUILT_DIRS_RELEASE} + -DCEF_WRAPPER_DIR:PATH=/usr/include/cef + -DCEF_WRAPPER_BUILD_DIR:PATH=${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12 + -DCEF_LIBRARY_RELEASE:FILEPATH=${INSTALL_PREFIX}/${_LIB}/cef/libcef.so + -DCEF_DLL_LIBRARY_RELEASE:FILEPATH=${ARCH_PREBUILT_DIRS_RELEASE}/libcef_dll_wrapper.a + "-DCMAKE_CXX_FLAGS:STRING=-I/usr/include/cef -I/usr/src/cef-146.0.11 -DWRAPPING_CEF_SHARED" + ) + if (${DULLAHAN_RESULT}) + file(MAKE_DIRECTORY ${LIBS_PREBUILT_DIR}/bin/release) + file( + COPY ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12/dullahan_host + DESTINATION ${LIBS_PREBUILT_DIR}/bin/release + ) + file( + COPY ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12/libdullahan.a + DESTINATION ${ARCH_PREBUILT_DIRS_RELEASE} + ) + file(MAKE_DIRECTORY ${LIBS_PREBUILT_DIR}/include/cef) + file( + COPY + ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12/src/dullahan.h + ${CMAKE_BINARY_DIR}/dullahan-1.29.0-CEF_146.0.12/src/dullahan_version.h + DESTINATION ${LIBS_PREBUILT_DIR}/include/cef + ) + file(WRITE ${PREBUILD_TRACKING_DIR}/dullahan_installed "0") + endif () + endif () +elseif (CMAKE_SYSTEM_PROCESSOR MATCHES aarch64) + if (${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKING_DIR}/dullahan_installed OR NOT ${dullahan_installed} EQUAL 0) + if (NOT EXISTS ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40.tar.gz) + file(DOWNLOAD + https://github.com/secondlife/dullahan/archive/refs/tags/v1.24.0-CEF_139.0.40.tar.gz + ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40.tar.gz ) endif () file(ARCHIVE_EXTRACT - INPUT ${CMAKE_BINARY_DIR}/v1.14.0-r3.tar.gz + INPUT ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40.tar.gz DESTINATION ${CMAKE_BINARY_DIR} ) execute_process( COMMAND sed -i "/#include <vector>/a #include <cstdint>" dullahan.h - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/dullahan-1.14.0-r3/src + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40/src ) file(MAKE_DIRECTORY ${LIBS_PREBUILT_DIR}/include/cef) try_compile(DULLAHAN_RESULT PROJECT dullahan - SOURCE_DIR ${CMAKE_BINARY_DIR}/dullahan-1.14.0-r3 - BINARY_DIR ${CMAKE_BINARY_DIR}/dullahan-1.14.0-r3 + SOURCE_DIR ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40 + BINARY_DIR ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40 CMAKE_FLAGS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} - -DCMAKE_OSX_ARCHITECTURES:STRING=${CMAKE_OSX_ARCHITECTURES} - -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=${CMAKE_OSX_DEPLOYMENT_TARGET} -DCMAKE_INSTALL_PREFIX:PATH=${LIBS_PREBUILT_DIR} -DCMAKE_INSTALL_LIBDIR:PATH=${ARCH_PREBUILT_DIRS_RELEASE} -DCMAKE_BUILD_WITH_INSTALL_RPATH:BOOL=ON - -DBUILD_SHARED_LIBS:BOOL=${BUILD_SHARED_LIBS} -DUSE_SPOTIFY_CEF:BOOL=ON - -DSPOTIFY_CEF_URL:STRING=https://cef-builds.spotifycdn.com/cef_binary_118.4.1%2Bg3dd6078%2Bchromium-118.0.5993.54_linuxarm64_beta_minimal.tar.bz2 + -DSPOTIFY_CEF_URL:STRING=https://cef-builds.spotifycdn.com/cef_binary_139.0.40%2Bg465474a%2Bchromium-139.0.7258.139_linuxarm64_minimal.tar.bz2 -DPROJECT_ARCH:STRING=${CMAKE_SYSTEM_PROCESSOR} - -DENABLE_CXX11_ABI:BOOL=ON ) if (${DULLAHAN_RESULT}) execute_process( COMMAND ${CMAKE_MAKE_PROGRAM} install - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/dullahan-1.14.0-r3 + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40 OUTPUT_VARIABLE dullahan_installed ) file( COPY - ${CMAKE_BINARY_DIR}/dullahan-1.14.0-r3/src/dullahan.h - ${CMAKE_BINARY_DIR}/dullahan-1.14.0-r3/src/dullahan_version.h + ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40/src/dullahan.h + ${CMAKE_BINARY_DIR}/dullahan-1.24.0-CEF_139.0.40/src/dullahan_version.h DESTINATION ${LIBS_PREBUILT_DIR}/include/cef ) file(WRITE ${PREBUILD_TRACKING_DIR}/dullahan_installed "${dullahan_installed}") @@ -59,9 +192,17 @@ use_prebuilt_binary(dullahan) endif () execute_process( - COMMAND patchelf --remove-rpath bin/release/dullahan_host + COMMAND patchelf --set-rpath ${INSTALL_LIBRARY_DIR} bin/release/dullahan_host WORKING_DIRECTORY ${LIBS_PREBUILT_DIR} +) + +if (${LINUX_DISTRO} MATCHES arch OR (${LINUX_DISTRO} MATCHES fedora)) + target_include_directories( ll::cef SYSTEM INTERFACE /usr/include/cef/include) + execute_process( + COMMAND patchelf --add-rpath ${INSTALL_PREFIX}/${_LIB}/cef bin/release/dullahan_host + WORKING_DIRECTORY ${LIBS_PREBUILT_DIR} ) +endif () target_include_directories( ll::cef SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/cef) @@ -129,6 +270,9 @@ elseif (DARWIN) ) elseif (LINUX) + if (${LINUX_DISTRO} MATCHES arch OR (${LINUX_DISTRO} MATCHES fedora)) + target_link_directories( ll::cef INTERFACE ${INSTALL_PREFIX}/${_LIB}/cef ) + endif () target_link_libraries( ll::cef INTERFACE libdullahan.a cef diff --git a/indra/cmake/CURL.cmake b/indra/cmake/CURL.cmake index 99045e3aa3..09fc43dff4 100644 --- a/indra/cmake/CURL.cmake +++ b/indra/cmake/CURL.cmake @@ -27,20 +27,20 @@ use_prebuilt_binary(curl) endif () endif () elseif (${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKING_DIR}/curl_installed OR NOT ${curl_installed} EQUAL 0) - if (NOT EXISTS ${CMAKE_BINARY_DIR}/3p-curl-7.54.1-r3.tar.gz) + if (NOT EXISTS ${CMAKE_BINARY_DIR}/3p-curl-7.54.1-r4.tar.gz) file(DOWNLOAD - https://github.com/secondlife/3p-curl/archive/refs/tags/v7.54.1-r3.tar.gz - ${CMAKE_BINARY_DIR}/3p-curl-7.54.1-r3.tar.gz + https://github.com/secondlife/3p-curl/archive/refs/tags/v7.54.1-r4.tar.gz + ${CMAKE_BINARY_DIR}/3p-curl-7.54.1-r4.tar.gz ) endif () file(ARCHIVE_EXTRACT - INPUT ${CMAKE_BINARY_DIR}/3p-curl-7.54.1-r3.tar.gz + INPUT ${CMAKE_BINARY_DIR}/3p-curl-7.54.1-r4.tar.gz DESTINATION ${CMAKE_BINARY_DIR} ) if (CMAKE_SYSTEM_PROCESSOR MATCHES aarch64) execute_process( COMMAND sed -i netrc.c -e "s/defined(HAVE_GETPWUID_R)/0/g" netrc.c - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-curl-7.54.1-r3/curl/lib + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-curl-7.54.1-r4/curl/lib ) endif () file( @@ -60,16 +60,16 @@ elseif (${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRA set(ENV{CFLAGS} "-std=c90") execute_process( COMMAND ./configure --disable-alt-svc --disable-dict --disable-doh --disable-file --disable-gopher --disable-headers-api --disable-hsts --disable-imap --disable-ldap --disable-ldaps --disable-libcurl-option --disable-manual --disable-mqtt --disable-ntlm --disable-ntlm-wb --disable-pop3 --disable-rtsp --disable-shared --disable-smb --disable-smtp --disable-sspi --disable-telnet --disable-tftp --disable-tls-srp --disable-unix-sockets --disable-verbose --disable-versioned-symbols --enable-threaded-resolver --with-ssl=${LIBS_PREBUILT_DIR} --without-libidn2 --without-libpsl --without-libssh2 --prefix=${LIBS_PREBUILT_DIR} --libdir=${ARCH_PREBUILT_DIRS_RELEASE} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-curl-7.54.1-r3/curl + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-curl-7.54.1-r4/curl ) execute_process( COMMAND make -j${MAKE_JOBS} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-curl-7.54.1-r3/curl + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-curl-7.54.1-r4/curl ) unset(ENV{CFLAGS}) execute_process( COMMAND make install - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-curl-7.54.1-r3/curl + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-curl-7.54.1-r4/curl RESULT_VARIABLE curl_installed ) if (CMAKE_SYSTEM_NAME MATCHES FreeBSD) diff --git a/indra/cmake/LLPrimitive.cmake b/indra/cmake/LLPrimitive.cmake index 81b40cb0b5..df8d96109f 100644 --- a/indra/cmake/LLPrimitive.cmake +++ b/indra/cmake/LLPrimitive.cmake @@ -28,25 +28,25 @@ if (TRUE) target_link_libraries( ll::minizip-ng INTERFACE ${Minizip_LIBRARIES} ) target_link_libraries( ll::libxml INTERFACE ${Libxml2_LIBRARIES} ) if (${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKING_DIR}/colladadom_installed OR NOT ${colladadom_installed} EQUAL 0) - if (NOT EXISTS ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10.tar.gz) + if (NOT EXISTS ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11.tar.gz) file(DOWNLOAD - https://github.com/secondlife/3p-colladadom/archive/refs/tags/v2.3-r10.tar.gz - ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10.tar.gz + https://github.com/secondlife/3p-colladadom/archive/refs/tags/v2.3-r11.tar.gz + ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11.tar.gz ) endif () file(ARCHIVE_EXTRACT - INPUT ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10.tar.gz + INPUT ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11.tar.gz DESTINATION ${CMAKE_BINARY_DIR} ) if (WINDOWS OR CMAKE_COMMAND MATCHES /usr/bin/cmake) execute_process( COMMAND sed -i "s/include_directories/cmake_minimum_required(VERSION 3.28)\\ninclude_directories/" CMakeLists.txt - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10 + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11 ) else () execute_process( COMMAND sed -i "" -e "s/include_directories/cmake_minimum_required(VERSION 3.28)\\ninclude_directories/" CMakeLists.txt - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10 + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11 ) endif () if (WINDOWS) @@ -54,12 +54,12 @@ if (TRUE) COMMAND sed -i "s/SHARED/STATIC/" 1.4/CMakeLists.txt COMMAND sed -i "/#include <cstdarg>/a #define WIN32" dae/daeUtils.cpp COMMAND sed -i "/using namespace cdom;/a namespace boost{void boost::throw_exception(class std::exception const &){}}" dae/daeURI.cpp - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10/src + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11/src ) else () execute_process( COMMAND sed -i "" -e "s/SHARED/STATIC/" src/1.4/CMakeLists.txt - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10 + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11 ) endif () if (DARWIN) @@ -79,14 +79,14 @@ if (TRUE) execute_process( COMMAND sed -i "" -e "s/endif 0/endif/" dae/daeUtils.cpp COMMAND sed -i "" -e "s/linux/FreeBSD/" dae/daeUtils.cpp - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10/src + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11/src ) endif () file(MAKE_DIRECTORY ${LIBS_PREBUILT_DIR}/include/collada/1.4) try_compile(COLLADADOM_RESULT PROJECT colladadom - SOURCE_DIR ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10 - BINARY_DIR ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10 + SOURCE_DIR ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11 + BINARY_DIR ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11 TARGET collada14dom CMAKE_FLAGS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} @@ -106,13 +106,13 @@ if (TRUE) ) if (WINDOWS) execute_process( - COMMAND MSBuild.exe ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10/Project.slnx -p:Configuration=${CMAKE_BUILD_TYPE} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10 + COMMAND MSBuild.exe ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11/Project.slnx -p:Configuration=${CMAKE_BUILD_TYPE} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11 OUTPUT_VARIABLE colladadom_installed ) file(REMOVE_RECURSE ${LIBS_PREBUILT_DIR}/include/collada) file( - COPY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10/include + COPY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11/include DESTINATION ${LIBS_PREBUILT_DIR}/include ) file(RENAME @@ -121,13 +121,13 @@ if (TRUE) ) file(MAKE_DIRECTORY ${ARCH_PREBUILT_DIRS_RELEASE}) file(RENAME - ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10/src/1.4/${CMAKE_BUILD_TYPE}/collada14dom.lib + ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11/src/1.4/${CMAKE_BUILD_TYPE}/collada14dom.lib ${ARCH_PREBUILT_DIRS_RELEASE}/libcollada14dom23-s.lib ) elseif (${COLLADADOM_RESULT}) execute_process( COMMAND ${CMAKE_MAKE_PROGRAM} install - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r10 + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3p-colladadom-2.3-r11 OUTPUT_VARIABLE colladadom_installed ) file(RENAME diff --git a/indra/cmake/UnixInstall.cmake b/indra/cmake/UnixInstall.cmake index 1fd17a0142..b82cbbcc2f 100644 --- a/indra/cmake/UnixInstall.cmake +++ b/indra/cmake/UnixInstall.cmake @@ -22,8 +22,8 @@ if (INSTALL) set(_LIB lib) endif () - set(INSTALL_LIBRARY_DIR ${INSTALL_PREFIX}/${_LIB} CACHE PATH - "Installation directory for read-only shared files.") + set(INSTALL_LIBRARY_DIR ${INSTALL_PREFIX}/${_LIB}/${VIEWER_BINARY_NAME} CACHE PATH + "Installation directory for dynamic library files and their resources.") set(INSTALL_SHARE_DIR ${INSTALL_PREFIX}/share CACHE PATH "Installation directory for read-only shared files.") diff --git a/indra/llwebrtc/CMakeLists.txt b/indra/llwebrtc/CMakeLists.txt index a01d9fc632..1c53b0263c 100644 --- a/indra/llwebrtc/CMakeLists.txt +++ b/indra/llwebrtc/CMakeLists.txt @@ -77,11 +77,11 @@ if (INSTALL) if (DARWIN) set(_LIB ../Frameworks) elseif (${LINUX_DISTRO} MATCHES debian OR (${LINUX_DISTRO} MATCHES ubuntu)) - set(_LIB lib/${ARCH}-linux-gnu) + set(_LIB lib/${ARCH}-linux-gnu/${VIEWER_BINARY_NAME}) elseif (${LINUX_DISTRO} MATCHES fedora OR (${LINUX_DISTRO} MATCHES opensuse-tumbleweed) OR (${LINUX_DISTRO} MATCHES gentoo)) - set(_LIB lib${ADDRESS_SIZE}) + set(_LIB lib${ADDRESS_SIZE}/${VIEWER_BINARY_NAME}) else () - set(_LIB lib) + set(_LIB lib/${VIEWER_BINARY_NAME}) endif () if (WINDOWS) diff --git a/indra/media_plugins/cef/CMakeLists.txt b/indra/media_plugins/cef/CMakeLists.txt index 236e117aa7..ee715cce03 100644 --- a/indra/media_plugins/cef/CMakeLists.txt +++ b/indra/media_plugins/cef/CMakeLists.txt @@ -50,7 +50,10 @@ if (LINUX) ) list(APPEND media_plugin_cef_SOURCE_FILES ${LINUX_VOLUME_CATCHER}) - set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--build-id -Wl,-rpath,'$ORIGIN:$ORIGIN/../../lib'") + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--build-id") + if (${LINUX_DISTRO} MATCHES arch OR (${LINUX_DISTRO} MATCHES fedora)) + set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH};${INSTALL_PREFIX}/${_LIB}/cef) + endif () list(APPEND media_plugin_cef_LINK_LIBRARIES llwindow ) elseif (DARWIN) list(APPEND media_plugin_cef_SOURCE_FILES volume_catcher_null.cpp) @@ -145,45 +148,47 @@ if (INSTALL) ) elseif (LINUX) if (${LINUX_DISTRO} MATCHES debian OR (${LINUX_DISTRO} MATCHES ubuntu)) - set(_LIB lib/${ARCH}-linux-gnu) + set(_LIB lib/${ARCH}-linux-gnu/${VIEWER_BINARY_NAME}) elseif (${LINUX_DISTRO} MATCHES fedora OR (${LINUX_DISTRO} MATCHES opensuse-tumbleweed) OR (${LINUX_DISTRO} MATCHES gentoo)) - set(_LIB lib${ADDRESS_SIZE}) + set(_LIB lib${ADDRESS_SIZE}/${VIEWER_BINARY_NAME}) else () - set(_LIB lib) + set(_LIB lib/${VIEWER_BINARY_NAME}) endif () if (${LINUX_DISTRO} MATCHES arch) install( - PROGRAMS - ${AUTOBUILD_INSTALL_DIR}/bin/release/chrome-sandbox - ${AUTOBUILD_INSTALL_DIR}/bin/release/dullahan_host + PROGRAMS ${AUTOBUILD_INSTALL_DIR}/bin/release/dullahan_host DESTINATION lib/${VIEWER_BINARY_NAME} - #PERMISSIONS SETUID OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) - else (${LINUX_DISTRO} MATCHES arch) + elseif (${LINUX_DISTRO} MATCHES fedora) + install( + PROGRAMS ${AUTOBUILD_INSTALL_DIR}/bin/release/dullahan_host + DESTINATION libexec/${VIEWER_BINARY_NAME} + ) + else () install( PROGRAMS ${AUTOBUILD_INSTALL_DIR}/bin/release/chrome-sandbox ${AUTOBUILD_INSTALL_DIR}/bin/release/dullahan_host DESTINATION libexec/${VIEWER_BINARY_NAME} - #PERMISSIONS SETUID OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) - endif (${LINUX_DISTRO} MATCHES arch) - install( - FILES - ${ARCH_PREBUILT_DIRS_RELEASE}/libcef.so - ${ARCH_PREBUILT_DIRS_RELEASE}/libvk_swiftshader.so - ${AUTOBUILD_INSTALL_DIR}/bin/release/snapshot_blob.bin - ${AUTOBUILD_INSTALL_DIR}/bin/release/v8_context_snapshot.bin - ${AUTOBUILD_INSTALL_DIR}/resources/chrome_100_percent.pak - ${AUTOBUILD_INSTALL_DIR}/resources/chrome_200_percent.pak - ${AUTOBUILD_INSTALL_DIR}/resources/icudtl.dat - ${AUTOBUILD_INSTALL_DIR}/resources/resources.pak - DESTINATION ${_LIB} + endif () + if (NOT (${LINUX_DISTRO} MATCHES arch OR (${LINUX_DISTRO} MATCHES fedora))) + install( + FILES + ${ARCH_PREBUILT_DIRS_RELEASE}/libcef.so + ${ARCH_PREBUILT_DIRS_RELEASE}/libvk_swiftshader.so + ${AUTOBUILD_INSTALL_DIR}/lib/release/v8_context_snapshot.bin + ${AUTOBUILD_INSTALL_DIR}/resources/chrome_100_percent.pak + ${AUTOBUILD_INSTALL_DIR}/resources/chrome_200_percent.pak + ${AUTOBUILD_INSTALL_DIR}/resources/icudtl.dat + ${AUTOBUILD_INSTALL_DIR}/resources/resources.pak + DESTINATION ${_LIB} ) - install( - DIRECTORY ${AUTOBUILD_INSTALL_DIR}/resources/locales - DESTINATION ${_LIB} + install( + DIRECTORY ${AUTOBUILD_INSTALL_DIR}/resources/locales + DESTINATION ${_LIB} ) + endif () elseif (WINDOWS) set(_LIB llplugin) install( @@ -213,7 +218,7 @@ if (INSTALL) DESTINATION llplugin ) else () - set(_LIB lib) + set(_LIB lib/${VIEWER_BINARY_NAME}) endif () if (NOT WINDOWS) install(TARGETS ${PROJECT_NAME} DESTINATION ${_LIB}) diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp index a2b664c755..d5ec3dbb20 100644 --- a/indra/media_plugins/cef/media_plugin_cef.cpp +++ b/indra/media_plugins/cef/media_plugin_cef.cpp @@ -957,7 +957,6 @@ void MediaPluginCEF::receiveMessage(const char* message_string) { authResponse(message_in); } -#if !LL_LINUX if (message_name == "edit_undo") { mCEFLib->editUndo(); @@ -966,7 +965,6 @@ void MediaPluginCEF::receiveMessage(const char* message_string) { mCEFLib->editRedo(); } -#endif if (message_name == "edit_cut") { mCEFLib->editCut(); @@ -979,7 +977,6 @@ void MediaPluginCEF::receiveMessage(const char* message_string) { mCEFLib->editPaste(); } -#if !LL_LINUX if (message_name == "edit_delete") { mCEFLib->editDelete(); @@ -992,7 +989,6 @@ void MediaPluginCEF::receiveMessage(const char* message_string) { mCEFLib->viewSource(); } -#endif } else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER) { @@ -1176,28 +1172,19 @@ void MediaPluginCEF::unicodeInput(std::string event, LLSD native_key_data = LLSD // void MediaPluginCEF::checkEditState() { -#if !LL_LINUX bool can_undo = mCEFLib->editCanUndo(); bool can_redo = mCEFLib->editCanRedo(); -#endif bool can_cut = mCEFLib->editCanCut(); bool can_copy = mCEFLib->editCanCopy(); bool can_paste = mCEFLib->editCanPaste(); -#if !LL_LINUX bool can_delete = mCEFLib->editCanDelete(); bool can_select_all = mCEFLib->editCanSelectAll(); -#endif -#if LL_LINUX - if ((can_cut != mCanCut) || (can_copy != mCanCopy) || (can_paste != mCanPaste)) -#else if ((can_undo != mCanUndo) || (can_redo != mCanRedo) || (can_cut != mCanCut) || (can_copy != mCanCopy) || (can_paste != mCanPaste) || (can_delete != mCanDelete) || (can_select_all != mCanSelectAll)) -#endif { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "edit_state"); -#if !LL_LINUX if (can_undo != mCanUndo) { mCanUndo = can_undo; @@ -1209,7 +1196,6 @@ void MediaPluginCEF::checkEditState() mCanRedo = can_redo; message.setValueBoolean("redo", can_redo); } -#endif if (can_cut != mCanCut) { @@ -1229,7 +1215,6 @@ void MediaPluginCEF::checkEditState() message.setValueBoolean("paste", can_paste); } -#if !LL_LINUX if (can_delete != mCanDelete) { mCanDelete = can_delete; @@ -1241,7 +1226,6 @@ void MediaPluginCEF::checkEditState() mCanSelectAll = can_select_all; message.setValueBoolean("select_all", can_select_all); } -#endif sendMessage(message); } diff --git a/indra/media_plugins/libvlc/CMakeLists.txt b/indra/media_plugins/libvlc/CMakeLists.txt index b8b3bd68f3..96790a8037 100644 --- a/indra/media_plugins/libvlc/CMakeLists.txt +++ b/indra/media_plugins/libvlc/CMakeLists.txt @@ -88,11 +88,11 @@ if (INSTALL) DESTINATION ${_LIB}/plugins ) elseif (${LINUX_DISTRO} MATCHES debian OR (${LINUX_DISTRO} MATCHES ubuntu)) - set(_LIB lib/${ARCH}-linux-gnu) + set(_LIB lib/${ARCH}-linux-gnu/${VIEWER_BINARY_NAME}) elseif (${LINUX_DISTRO} MATCHES fedora OR (${LINUX_DISTRO} MATCHES opensuse-tumbleweed) OR (${LINUX_DISTRO} MATCHES gentoo)) - set(_LIB lib${ADDRESS_SIZE}) + set(_LIB lib${ADDRESS_SIZE}/${VIEWER_BINARY_NAME}) else () - set(_LIB lib) + set(_LIB lib/${VIEWER_BINARY_NAME}) endif () if (WINDOWS) install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${PROJECT_NAME}.dll DESTINATION llplugin) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9a2778f853..f19716af7e 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -342,6 +342,7 @@ set(viewer_SOURCE_FILES llgltfmateriallist.cpp llgltfmaterialpreviewmgr.cpp llgroupactions.cpp + llgroupcolormap.cpp # group-based nameplate tinting llgroupiconctrl.cpp llgrouplist.cpp llgroupmgr.cpp @@ -560,6 +561,7 @@ set(viewer_SOURCE_FILES llpreviewtexture.cpp llproductinforequest.cpp llprogressview.cpp + llquickprefs.cpp # Firestorm port: Quick Preferences floater llrecentpeople.cpp llreflectionmap.cpp llreflectionmapmanager.cpp @@ -1036,6 +1038,7 @@ set(viewer_HEADER_FILES llgltfmateriallist.h llgltfmaterialpreviewmgr.h llgroupactions.h + llgroupcolormap.h # group-based nameplate tinting llgroupiconctrl.h llgrouplist.h llgroupmgr.h @@ -1239,6 +1242,7 @@ set(viewer_HEADER_FILES llpreviewtexture.h llproductinforequest.h llprogressview.h + llquickprefs.h # Firestorm port: Quick Preferences floater llrecentpeople.h llreflectionmap.h llreflectionmapmanager.h @@ -2395,7 +2399,7 @@ if (LINUX) set(CPACK_RPM_PACKAGE_DESCRIPTION ${VIEWER_PACKAGE_DESCRIPTION} CACHE STRING "RPM package description.") if (${LINUX_DISTRO} MATCHES fedora) - set(CPACK_RPM_PACKAGE_REQUIRES "freealut, apr-util, boost-fiber, boost-program-options, boost-regex, boost-thread, boost-url, expat, fltk, mesa-libGLU, hunspell, libnghttp2, openjpeg, sdl2-compat, vlc-libs, vlc-plugins-base, libvorbis" + set(CPACK_RPM_PACKAGE_REQUIRES "freealut, apr-util, boost-fiber, boost-program-options, boost-regex, boost-thread, boost-url, cef, expat, fltk, mesa-libGLU, hunspell, libnghttp2, openjpeg, sdl2-compat, vlc-libs, vlc-plugins-base, libvorbis" CACHE STRING "RPM package requirements.") else () set(CPACK_RPM_PACKAGE_REQUIRES "libalut0, libapr-util1-0, libboost_fiber1_91_0, libboost_program_options1_91_0, libboost_regex1_91_0, libboost_thread1_91_0, libboost_url1_91_0, libboost_url1_91_0-x86-64-v3, libpng16-16 expat, libfltk1_3, libGLU1, libhunspell-1_7-0, libnghttp2-14, openjpeg2, libSDL2-2_0-0, libvlc5, libvorbis0" diff --git a/indra/newview/PKGBUILD.in b/indra/newview/PKGBUILD.in index 3de9558000..b2830ed145 100644 --- a/indra/newview/PKGBUILD.in +++ b/indra/newview/PKGBUILD.in @@ -6,7 +6,7 @@ pkgdesc="${VIEWER_PACKAGE_COMMENT}" arch=('${CMAKE_SYSTEM_PROCESSOR}') url="https://${VIEWER_PACKAGE_DOMAIN_NAME}" license=('LGPL-2.1') -depends=(freealut apr-util at-spi2-core boost-libs fltk glu hunspell libnghttp2 openjpeg2 sdl2 vlc libvorbis) +depends=(freealut apr-util at-spi2-core boost-libs cef fltk glu hunspell libnghttp2 openjpeg2 sdl2 vlc libvorbis) package() { cd "$startdir" diff --git a/indra/newview/ViewerInstall.cmake b/indra/newview/ViewerInstall.cmake index 56a3394943..8f2827e1bc 100644 --- a/indra/newview/ViewerInstall.cmake +++ b/indra/newview/ViewerInstall.cmake @@ -201,7 +201,7 @@ if (LINUX) if (USE_DISCORD) install( FILES ${ARCH_PREBUILT_DIRS_RELEASE}/libdiscord_partner_sdk.so - DESTINATION ${_LIB} + DESTINATION ${_LIB}/${VIEWER_BINARY_NAME} ) endif () if (USE_FMODSTUDIO) @@ -209,7 +209,7 @@ if (LINUX) ${ARCH_PREBUILT_DIRS_RELEASE}/libfmod.so ${ARCH_PREBUILT_DIRS_RELEASE}/libfmod.so.13 ${ARCH_PREBUILT_DIRS_RELEASE}/libfmod.so.13.34 - DESTINATION ${_LIB}) + DESTINATION ${_LIB}/${VIEWER_BINARY_NAME}) endif (USE_FMODSTUDIO) endif (LINUX) diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml index 6a05466e06..9b80b40008 100644 --- a/indra/newview/app_settings/commands.xml +++ b/indra/newview/app_settings/commands.xml @@ -310,4 +310,15 @@ tooltip_ref="Command_ResyncAnimations_Tooltip" execute_function="Tools.ResyncAnimations" /> + <!-- Firestorm port: Quick Preferences toolbar button --> + <command name="quick_prefs" + available_in_toybox="true" + icon="Command_Preferences_Icon" + label_ref="Command_QuickPrefs_Label" + tooltip_ref="Command_QuickPrefs_Tooltip" + execute_function="Floater.ToggleOrBringToFront" + execute_parameters="quick_prefs" + is_running_function="Floater.IsOpen" + is_running_parameters="quick_prefs" + /> </commands> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index f1e5217afd..481cafafd1 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1337,6 +1337,34 @@ <key>Value</key> <real>1.047197551</real> </map> + <!-- NaCl/Firestorm port: mouselook right-click zoom --> + <key>_NACL_MLFovValues</key> + <map> + <key>Comment</key> + <string>Mouselook FOV zoom state: VX=normal FOV, VY=zoomed FOV, VZ=1 if currently zoomed</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Vector3</string> + <key>Value</key> + <array> + <real>1.047197551</real> + <real>1.047197551</real> + <real>0.0</real> + </array> + </map> + <key>FSScrollWheelExitsMouselook</key> + <map> + <key>Comment</key> + <string>If enabled, scrolling up while in mouselook (without right mouse held) exits mouselook</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <!-- End NaCl/Firestorm port --> <key>CameraOffset</key> <map> <key>Comment</key> diff --git a/indra/newview/llavatarpropertiesprocessor.cpp b/indra/newview/llavatarpropertiesprocessor.cpp index 9d9948731f..aa24c69831 100644 --- a/indra/newview/llavatarpropertiesprocessor.cpp +++ b/indra/newview/llavatarpropertiesprocessor.cpp @@ -527,12 +527,28 @@ void LLAvatarPropertiesProcessor::processAvatarGroupsReply(LLMessageSystem* msg, AvatarGroupsReply is automatically sent by the server in response to the AvatarPropertiesRequest in addition to the AvatarPropertiesReply message. */ - LLUUID agent_id; - LLUUID avatar_id; - msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); - msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AvatarID, avatar_id); + LLAvatarGroups avatar_groups; + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, avatar_groups.agent_id); + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AvatarID, avatar_groups.avatar_id); + + LL_DEBUGS("AvatarProperties") << "Received AvatarGroupsReply for " + << avatar_groups.avatar_id << LL_ENDL; + + S32 group_count = msg->getNumberOfBlocksFast(_PREHASH_GroupData); + for (S32 i = 0; i < group_count; ++i) + { + LLAvatarGroups::LLGroupData gd; + msg->getU64Fast( _PREHASH_GroupData, _PREHASH_GroupPowers, gd.group_powers, i); + msg->getBOOLFast( _PREHASH_GroupData, _PREHASH_AcceptNotices, gd.accept_notices, i); + msg->getStringFast( _PREHASH_GroupData, _PREHASH_GroupTitle, gd.group_title, i); + msg->getUUIDFast( _PREHASH_GroupData, _PREHASH_GroupID, gd.group_id, i); + msg->getStringFast( _PREHASH_GroupData, _PREHASH_GroupName, gd.group_name, i); + msg->getUUIDFast( _PREHASH_GroupData, _PREHASH_GroupInsigniaID, gd.group_insignia_id, i); + avatar_groups.group_list.push_back(gd); + } - LL_DEBUGS("AvatarProperties") << "Received AvatarGroupsReply for " << avatar_id << LL_ENDL; + LLAvatarPropertiesProcessor* self = getInstance(); + self->notifyObservers(avatar_groups.avatar_id, &avatar_groups, APT_GROUPS); } void LLAvatarPropertiesProcessor::notifyObservers(const LLUUID& id, void* data, EAvatarProcessorType type) diff --git a/indra/newview/llavatarpropertiesprocessor.h b/indra/newview/llavatarpropertiesprocessor.h index 1592629fca..bfafe780ff 100644 --- a/indra/newview/llavatarpropertiesprocessor.h +++ b/indra/newview/llavatarpropertiesprocessor.h @@ -55,7 +55,8 @@ enum EAvatarProcessorType APT_PICK_INFO, APT_TEXTURES, APT_CLASSIFIEDS, - APT_CLASSIFIED_INFO + APT_CLASSIFIED_INFO, + APT_GROUPS // Group membership list with per-group role titles }; // legacy data is supposed to match AvatarPropertiesReply, @@ -117,6 +118,28 @@ struct LLAvatarData::LLGroupData LLUUID group_insignia_id; }; +/** Sent by the server automatically alongside AvatarPropertiesReply (UDP). + * Contains every group the avatar belongs to, including their selected + * role title in each group. The active group is the one whose group_title + * matches the avatar's current "Title" NameValue. */ +struct LLAvatarGroups +{ + LLUUID agent_id; + LLUUID avatar_id; + + struct LLGroupData + { + U64 group_powers { 0 }; + bool accept_notices{ false }; + std::string group_title; // role title the avatar has selected in this group + LLUUID group_id; + std::string group_name; + LLUUID group_insignia_id; + }; + typedef std::list<LLGroupData> group_list_t; + group_list_t group_list; +}; + struct LLPickData { LLUUID agent_id; diff --git a/indra/newview/llgroupcolormap.cpp b/indra/newview/llgroupcolormap.cpp new file mode 100644 index 0000000000..36fde1ce38 --- /dev/null +++ b/indra/newview/llgroupcolormap.cpp @@ -0,0 +1,150 @@ +/** + * @file llgroupcolormap.cpp + * @brief Per-group nameplate tint registry. + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llgroupcolormap.h" + +#include "lldir.h" +#include "llsdserialize.h" +#include "llsdutil_math.h" // ll_sd_from_color4 / ll_color4_from_sd +#include "llvoavatar.h" // LLVOAvatar::invalidateNameTags() + +static constexpr char GROUP_COLOR_FILE[] = "settings_group_colors.xml"; + +// --------------------------------------------------------------------------- +// Constructor +// --------------------------------------------------------------------------- + +LLGroupColorMap::LLGroupColorMap() +{ +} + +// --------------------------------------------------------------------------- +// Public API +// --------------------------------------------------------------------------- + +void LLGroupColorMap::setGroupColor(const LLUUID& group_id, const LLColor4& color) +{ + if (group_id.isNull()) + return; + + // Treat fully-transparent as "no color" (remove entry) + if (color.mV[VW] < 0.01f) + { + clearGroupColor(group_id); + return; + } + + mColors[group_id] = color; + saveToDisk(); + invalidateAllNameTags(); +} + +LLColor4 LLGroupColorMap::getGroupColor(const LLUUID& group_id) const +{ + if (group_id.isNull()) + return LLColor4::transparent; + + auto it = mColors.find(group_id); + if (it != mColors.end()) + return it->second; + + return LLColor4::transparent; +} + +bool LLGroupColorMap::hasGroupColor(const LLUUID& group_id) const +{ + if (group_id.isNull()) + return false; + return mColors.find(group_id) != mColors.end(); +} + +void LLGroupColorMap::clearGroupColor(const LLUUID& group_id) +{ + if (mColors.erase(group_id) > 0) + { + saveToDisk(); + invalidateAllNameTags(); + } +} + +// --------------------------------------------------------------------------- +// Persistence +// --------------------------------------------------------------------------- + +// static +std::string LLGroupColorMap::getFilePath() +{ + std::string path = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, ""); + if (!path.empty()) + return gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, GROUP_COLOR_FILE); + // Fall back to app_settings (before first login, path not yet set) + return gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, GROUP_COLOR_FILE); +} + +void LLGroupColorMap::loadFromDisk() +{ + mColors.clear(); + + const std::string filepath = getFilePath(); + if (!gDirUtilp->fileExists(filepath)) + return; + + llifstream stream(filepath.c_str()); + if (!stream.is_open()) + { + LL_WARNS("GroupColor") << "Cannot open " << filepath << LL_ENDL; + return; + } + + LLSD data; + if (LLSDSerialize::fromXMLDocument(data, stream) < 0) + { + LL_WARNS("GroupColor") << "Failed to parse " << filepath << LL_ENDL; + return; + } + + // Format: map of { group_uuid_string : [r, g, b, a] } + for (LLSD::map_const_iterator it = data.beginMap(); it != data.endMap(); ++it) + { + LLUUID group_id(it->first); + if (group_id.isNull()) continue; + LLColor4 color = ll_color4_from_sd(it->second); + if (color.mV[VW] >= 0.01f) + mColors[group_id] = color; + } + + LL_INFOS("GroupColor") << "Loaded " << mColors.size() + << " group color(s) from " << filepath << LL_ENDL; +} + +void LLGroupColorMap::saveToDisk() const +{ + const std::string filepath = getFilePath(); + + LLSD data = LLSD::emptyMap(); + for (const auto& [group_id, color] : mColors) + data[group_id.asString()] = ll_sd_from_color4(color); + + llofstream stream(filepath.c_str()); + if (!stream.is_open()) + { + LL_WARNS("GroupColor") << "Cannot write " << filepath << LL_ENDL; + return; + } + LLSDSerialize::toPrettyXML(data, stream); +} + +// --------------------------------------------------------------------------- +// Invalidation +// --------------------------------------------------------------------------- + +// static +void LLGroupColorMap::invalidateAllNameTags() +{ + LLVOAvatar::invalidateNameTags(); +} diff --git a/indra/newview/llgroupcolormap.h b/indra/newview/llgroupcolormap.h new file mode 100644 index 0000000000..d6f661c43a --- /dev/null +++ b/indra/newview/llgroupcolormap.h @@ -0,0 +1,64 @@ +/** + * @file llgroupcolormap.h + * @brief Per-group nameplate tint registry. + * + * Stores a client-side color for each group UUID. When the group color is + * set, every avatar whose *active* group tag matches that UUID gets their + * nameplate rendered in that color instead of the default. + * + * Data is persisted to <account_dir>/settings_group_colors.xml so colors + * survive relogs. The file is keyed by group UUID string → LLColor4 LLSD. + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 2.1 only. + * $/LicenseInfo$ + */ + +#ifndef LL_LLGROUPCOLORMAP_H +#define LL_LLGROUPCOLORMAP_H + +#include "llsingleton.h" +#include "lluuid.h" +#include "v4color.h" +#include <unordered_map> + +class LLGroupColorMap : public LLSingleton<LLGroupColorMap> +{ + LLSINGLETON(LLGroupColorMap); + ~LLGroupColorMap() = default; + LOG_CLASS(LLGroupColorMap); + +public: + // ---- Color CRUD --------------------------------------------------------- + + /** Set (or clear) the nameplate tint for a group. + * Pass LLColor4::transparent (alpha == 0) to remove the color entry. */ + void setGroupColor(const LLUUID& group_id, const LLColor4& color); + + /** Return the tint for @p group_id, or LLColor4::transparent if none. */ + LLColor4 getGroupColor(const LLUUID& group_id) const; + + /** True if a non-transparent color is stored for this group. */ + bool hasGroupColor(const LLUUID& group_id) const; + + /** Remove the color entry for this group. */ + void clearGroupColor(const LLUUID& group_id); + + // ---- Persistence -------------------------------------------------------- + void loadFromDisk(); + void saveToDisk() const; + + // ---- Cache invalidation ------------------------------------------------- + /** Called after a color is changed so nametags rebuild on next idle. */ + static void invalidateAllNameTags(); + +private: + static std::string getFilePath(); + + // group_uuid → RGBA color (std::hash<LLUUID> is specialised in lluuid.h) + std::unordered_map<LLUUID, LLColor4> mColors; +}; + +#endif // LL_LLGROUPCOLORMAP_H diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index 63ec43458b..a2a0731256 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -48,6 +48,8 @@ #include "llagentcamera.h" #include "llappviewer.h" // for gDisconnected #include "llavataractions.h" +#include "llgroupcolormap.h" // group-based dot tinting +#include "llvoavatar.h" // group-based dot tinting #include "llcallingcard.h" // LLAvatarTracker #include "llfloaterland.h" #include "llfloaterworldmap.h" @@ -432,6 +434,24 @@ void LLNetMap::draw() LLColor4 color = LLAvatarActions::isFriend(uuid) ? map_avatar_friend_color : map_avatar_color; + // Group-based dot tinting: override with group color if one is set. + // Look up the avatar's active group UUID from the LLVOAvatar object. + if (LLViewerObject* obj = gObjectList.findObject(uuid)) + { + if (LLVOAvatar* av = dynamic_cast<LLVOAvatar*>(obj)) + { + LLUUID active_group = av->getActiveGroupID(); + if (active_group.notNull()) + { + LLColor4 group_color = LLGroupColorMap::getInstance()->getGroupColor(active_group); + if (group_color.mV[VW] >= 0.01f) + { + color = group_color; + } + } + } + } + unknown_relative_z = sorted_positions[i].mdV[VZ] >= COARSEUPDATE_MAX_Z && camera_position.mV[VZ] >= COARSEUPDATE_MAX_Z; @@ -505,10 +525,23 @@ void LLNetMap::draw() LLUIImagePtr you = LLWorldMapView::sAvatarYouLargeImage; if (you) { + // Group-based dot tinting for self: use gAgent.getGroupID() directly + LLColor4 self_color = UI_VERTEX_COLOR; + LLUUID self_group = gAgent.getGroupID(); + if (self_group.notNull()) + { + LLColor4 group_color = LLGroupColorMap::getInstance()->getGroupColor(self_group); + if (group_color.mV[VW] >= 0.01f) + { + self_color = group_color; + } + } + you->draw(ll_round(pos_map.mV[VX] - mDotRadius), ll_round(pos_map.mV[VY] - mDotRadius), dot_width, - dot_width); + dot_width, + self_color); F32 dist_to_cursor_squared = dist_vec_squared(LLVector2(pos_map.mV[VX], pos_map.mV[VY]), LLVector2((F32)local_mouse_x, (F32)local_mouse_y)); diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index 38ae818910..aaa5b2ce9c 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -28,6 +28,8 @@ #include "llpanelgroupgeneral.h" +#include "llcolorswatch.h" // LLColorSwatchCtrl – group nameplate tinting +#include "llgroupcolormap.h" // per-group nameplate tinting #include "llavatarnamecache.h" #include "llagent.h" #include "llagentbenefits.h" @@ -188,6 +190,22 @@ bool LLPanelGroupGeneral::postBuild() mIncompleteMemberDataStr = getString("incomplete_member_data_str"); + // Group nameplate tinting: wire up the color swatch + mGroupColorSwatch = getChild<LLColorSwatchCtrl>("group_nametag_color", recurse); + if (mGroupColorSwatch) + { + mGroupColorSwatch->setCanApplyImmediately(false); + mGroupColorSwatch->setCommitCallback([this](LLUICtrl*, const LLSD&) { onGroupColorChanged(); }); + mGroupColorSwatch->setOnCancelCallback([this](LLUICtrl*, const LLSD&) { onGroupColorCancelled(); }); + mGroupColorSwatch->setOnSelectCallback([this](LLUICtrl*, const LLSD&) { onGroupColorChanged(); }); + refreshGroupColorSwatch(); + } + LLButton* clear_color_btn = getChild<LLButton>("group_nametag_color_clear", recurse); + if (clear_color_btn) + { + clear_color_btn->setCommitCallback([this](LLUICtrl*, const LLSD&) { onGroupColorCleared(); }); + } + // If the group_id is null, then we are creating a new group if (mGroupID.isNull()) { @@ -798,7 +816,65 @@ void LLPanelGroupGeneral::setGroupID(const LLUUID& id) mInsignia->setImageAssetID(LLUUID::null); + // Refresh the nameplate color swatch to show the stored color for this group + refreshGroupColorSwatch(); + resetDirty(); activate(); } + +// --------------------------------------------------------------------------- +// Group nameplate tinting callbacks +// --------------------------------------------------------------------------- + +void LLPanelGroupGeneral::onGroupColorChanged() +{ + if (!mGroupColorSwatch || mGroupID.isNull()) + return; + + LLColor4 color = mGroupColorSwatch->get(); + LLGroupColorMap::getInstance()->setGroupColor(mGroupID, color); +} + +void LLPanelGroupGeneral::onGroupColorCancelled() +{ + // Picker was cancelled — restore whatever is currently saved + refreshGroupColorSwatch(); +} + +void LLPanelGroupGeneral::onGroupColorCleared() +{ + if (mGroupID.isNull()) + return; + + LLGroupColorMap::getInstance()->clearGroupColor(mGroupID); + refreshGroupColorSwatch(); +} + +void LLPanelGroupGeneral::refreshGroupColorSwatch() +{ + if (!mGroupColorSwatch) + return; + + if (mGroupID.isNull()) + { + // No group selected — show a neutral white and disable + mGroupColorSwatch->set(LLColor4::white, false); + mGroupColorSwatch->setEnabled(false); + return; + } + + mGroupColorSwatch->setEnabled(true); + + LLColor4 stored = LLGroupColorMap::getInstance()->getGroupColor(mGroupID); + if (stored.mV[VW] >= 0.01f) + { + mGroupColorSwatch->set(stored, false); + } + else + { + // No color stored yet — show white as a neutral default + mGroupColorSwatch->set(LLColor4::white, false); + } +} diff --git a/indra/newview/llpanelgroupgeneral.h b/indra/newview/llpanelgroupgeneral.h index 37db2e96a0..613cc7b3a6 100644 --- a/indra/newview/llpanelgroupgeneral.h +++ b/indra/newview/llpanelgroupgeneral.h @@ -39,6 +39,7 @@ class LLCheckBoxCtrl; class LLComboBox; class LLSpinCtrl; class LLAvatarName; +class LLColorSwatchCtrl; // group nameplate tinting class LLPanelGroupGeneral : public LLPanelGroupTab { @@ -99,6 +100,13 @@ private: LLComboBox *mComboActiveTitle; LLComboBox *mComboMature; LLCheckBoxCtrl *mCtrlReceiveGroupChat; // <exodus/> + + // Group nameplate tinting (client-side, stored in LLGroupColorMap) + LLColorSwatchCtrl* mGroupColorSwatch { nullptr }; + void onGroupColorChanged(); + void onGroupColorCancelled(); + void onGroupColorCleared(); + void refreshGroupColorSwatch(); }; #endif diff --git a/indra/newview/llquickprefs.cpp b/indra/newview/llquickprefs.cpp new file mode 100644 index 0000000000..800aa7abac --- /dev/null +++ b/indra/newview/llquickprefs.cpp @@ -0,0 +1,205 @@ +/** + * @file llquickprefs.cpp + * @brief Quick Preferences floater: hover height and bandwidth sliders. + * + * Ported from Firestorm Viewer (quickprefs.cpp). + * Original authors: WoLf Loonie, Zi Ree, Ansariel Hiller @ Second Life. + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Phoenix Firestorm Viewer Source Code + * Copyright (C) 2011, WoLf Loonie @ Second Life + * + * 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. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llquickprefs.h" + +#include "llagent.h" +#include "llsliderctrl.h" +#include "lltextbox.h" +#include "llviewercontrol.h" +#include "llviewerregion.h" +#include "llvoavatar.h" // for MIN_HOVER_Z / MAX_HOVER_Z +#include "llvoavatarself.h" // for gAgentAvatarp, isAgentAvatarValid() + +// --------------------------------------------------------------------------- +// Constructor / destructor +// --------------------------------------------------------------------------- + +LLFloaterQuickPrefs::LLFloaterQuickPrefs(const LLSD& key) + : LLFloater(key) + , mAvatarZOffsetSlider(nullptr) +{ +} + +LLFloaterQuickPrefs::~LLFloaterQuickPrefs() +{ + if (mRegionChangedSlot.connected()) + { + mRegionChangedSlot.disconnect(); + } +} + +// --------------------------------------------------------------------------- +// postBuild – wire up all widgets +// --------------------------------------------------------------------------- + +bool LLFloaterQuickPrefs::postBuild() +{ + // ---- Hover height slider ------------------------------------------------ + mAvatarZOffsetSlider = getChild<LLSliderCtrl>("HoverHeightSlider"); + mAvatarZOffsetSlider->setMinValue(MIN_HOVER_Z); + mAvatarZOffsetSlider->setMaxValue(MAX_HOVER_Z); + + // Live preview while dragging + mAvatarZOffsetSlider->setCommitCallback( + boost::bind(&LLFloaterQuickPrefs::onAvatarZOffsetSliderMoved, this)); + + // Persist on release or typed entry + mAvatarZOffsetSlider->setSliderMouseUpCallback( + boost::bind(&LLFloaterQuickPrefs::onAvatarZOffsetFinalCommit, this)); + mAvatarZOffsetSlider->setSliderEditorCommitCallback( + boost::bind(&LLFloaterQuickPrefs::onAvatarZOffsetFinalCommit, this)); + + // Pull current value from settings + syncAvatarZOffsetFromPreferenceSetting(); + + // Keep slider in sync when something else changes the setting (e.g. RLVa, + // the Edit Shape floater, or the standalone Hover Height floater). + if (gSavedPerAccountSettings.getControl("AvatarHoverOffsetZ")) + { + gSavedPerAccountSettings.getControl("AvatarHoverOffsetZ") + ->getCommitSignal() + ->connect(boost::bind( + &LLFloaterQuickPrefs::syncAvatarZOffsetFromPreferenceSetting, this)); + } + else + { + LL_WARNS("QuickPrefs") << "Control 'AvatarHoverOffsetZ' not found" << LL_ENDL; + } + + // Watch for region changes so we can enable/disable the slider + if (!mRegionChangedSlot.connected()) + { + mRegionChangedSlot = gAgent.addRegionChangedCallback( + boost::bind(&LLFloaterQuickPrefs::onRegionChanged, this)); + } + onRegionChanged(); // evaluate current region immediately + + return true; +} + +// --------------------------------------------------------------------------- +// onClose +// --------------------------------------------------------------------------- + +void LLFloaterQuickPrefs::onClose(bool app_quitting) +{ + if (mRegionChangedSlot.connected()) + { + mRegionChangedSlot.disconnect(); + } + LLFloater::onClose(app_quitting); +} + +// --------------------------------------------------------------------------- +// Hover height callbacks +// --------------------------------------------------------------------------- + +void LLFloaterQuickPrefs::onAvatarZOffsetSliderMoved() +{ + F32 value = mAvatarZOffsetSlider->getValueF32(); + LLVector3 offset(0.0f, 0.0f, llclamp(value, MIN_HOVER_Z, MAX_HOVER_Z)); + + LL_INFOS("Avatar") << "QuickPrefs: setting hover from slider moved " << offset[VZ] << LL_ENDL; + + if (gAgent.getRegion() && gAgent.getRegion()->avatarHoverHeightEnabled()) + { + if (mAvatarZOffsetSlider->isMouseHeldDown()) + { + // Live preview: send to avatar but don't persist yet + if (isAgentAvatarValid()) + { + gAgentAvatarp->setHoverOffset(offset, false); + } + } + else + { + // Committed (e.g. arrow-key step): persist immediately + gSavedPerAccountSettings.setF32("AvatarHoverOffsetZ", value); + } + } + else if (isAgentAvatarValid()) + { + gSavedPerAccountSettings.setF32("AvatarHoverOffsetZ", value); + } +} + +void LLFloaterQuickPrefs::onAvatarZOffsetFinalCommit() +{ + F32 value = mAvatarZOffsetSlider->getValueF32(); + LL_INFOS("Avatar") << "QuickPrefs: setting hover from slider final commit " << value << LL_ENDL; + gSavedPerAccountSettings.setF32("AvatarHoverOffsetZ", + llclamp(value, MIN_HOVER_Z, MAX_HOVER_Z)); +} + +// --------------------------------------------------------------------------- +// Enable / disable based on region support +// --------------------------------------------------------------------------- + +void LLFloaterQuickPrefs::updateAvatarZOffsetEditEnabled() +{ + bool enabled = gAgent.getRegion() && gAgent.getRegion()->avatarHoverHeightEnabled(); + + if (!enabled && isAgentAvatarValid()) + { + enabled = true; + } + + mAvatarZOffsetSlider->setEnabled(enabled); + + if (enabled) + { + syncAvatarZOffsetFromPreferenceSetting(); + } +} + +void LLFloaterQuickPrefs::onRegionChanged() +{ + LLViewerRegion* region = gAgent.getRegion(); + if (region && region->simulatorFeaturesReceived()) + { + updateAvatarZOffsetEditEnabled(); + } + else if (region) + { + region->setSimulatorFeaturesReceivedCallback( + boost::bind(&LLFloaterQuickPrefs::onSimulatorFeaturesReceived, this, _1)); + } +} + +void LLFloaterQuickPrefs::onSimulatorFeaturesReceived(const LLUUID& region_id) +{ + LLViewerRegion* region = gAgent.getRegion(); + if (region && region->getRegionID() == region_id) + { + updateAvatarZOffsetEditEnabled(); + } +} + +void LLFloaterQuickPrefs::syncAvatarZOffsetFromPreferenceSetting() +{ + F32 value = gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ"); + mAvatarZOffsetSlider->setValue(value, false); // false = no commit signal +} diff --git a/indra/newview/llquickprefs.h b/indra/newview/llquickprefs.h new file mode 100644 index 0000000000..0ef56a4299 --- /dev/null +++ b/indra/newview/llquickprefs.h @@ -0,0 +1,81 @@ +/** + * @file llquickprefs.h + * @brief Quick Preferences floater: hover height and bandwidth sliders. + * + * Ported from Firestorm Viewer (quickprefs.h / quickprefs.cpp). + * Original authors: WoLf Loonie, Zi Ree, Ansariel Hiller @ Second Life. + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Phoenix Firestorm Viewer Source Code + * Copyright (C) 2011, WoLf Loonie @ Second Life + * + * 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. + * $/LicenseInfo$ + */ + +#ifndef LL_LLQUICKPREFS_H +#define LL_LLQUICKPREFS_H + +#include "llfloater.h" + +class LLSliderCtrl; +class LLTextBox; + +/** + * @class LLFloaterQuickPrefs + * + * A lightweight "Quick Preferences" panel that lets the user adjust commonly + * tweaked settings on the fly without opening the full Preferences dialog: + * - Avatar hover height + * - Maximum network bandwidth + * + * Hover-height logic is ported 1:1 from Firestorm's FloaterQuickPrefs so that + * live-preview while dragging, final commit on mouse-up, and region-feature + * gating all work exactly the same way. + */ +class LLFloaterQuickPrefs : public LLFloater +{ +public: + LLFloaterQuickPrefs(const LLSD& key); + virtual ~LLFloaterQuickPrefs(); + + bool postBuild() override; + void onClose(bool app_quitting) override; + +private: + // ---- Hover height ------------------------------------------------------- + LLSliderCtrl* mAvatarZOffsetSlider; + + /** Called every frame while the slider thumb is being dragged. + * Sends a live (non-persistent) hover offset to the avatar so the user + * gets immediate visual feedback. */ + void onAvatarZOffsetSliderMoved(); + + /** Called when the drag ends (mouse-up) or the user types a value. + * Persists the value to AvatarHoverOffsetZ. */ + void onAvatarZOffsetFinalCommit(); + + /** Enable/disable the slider based on whether the current region supports + * server-side hover height. */ + void updateAvatarZOffsetEditEnabled(); + + /** Called when the region changes so we can re-evaluate the above. */ + void onRegionChanged(); + void onSimulatorFeaturesReceived(const LLUUID& region_id); + + /** Pulls AvatarHoverOffsetZ from saved settings and pushes it to the slider + * without triggering a commit (avoids feedback loops). */ + void syncAvatarZOffsetFromPreferenceSetting(); + + boost::signals2::connection mRegionChangedSlot; +}; + +#endif // LL_LLQUICKPREFS_H diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 98a97b9457..e5c272a264 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -207,6 +207,7 @@ #include "llstartuplistener.h" #include "lltoolbarview.h" #include "llexperiencelog.h" +#include "llgroupcolormap.h" // group-based nameplate tinting #include "llcleanup.h" #include "llenvironment.h" @@ -3344,6 +3345,9 @@ void LLStartUp::initExperiences() boost::bind(&LLAgent::getRegionCapability, &gAgent, _1)); LLExperienceLog::instance().initialize(); + + // Load per-group nameplate colors for this account + LLGroupColorMap::getInstance()->loadFromDisk(); } void LLStartUp::cleanupNameCache() diff --git a/indra/newview/lltoolcomp.cpp b/indra/newview/lltoolcomp.cpp index c6e59a81c9..ff6c692699 100644 --- a/indra/newview/lltoolcomp.cpp +++ b/indra/newview/lltoolcomp.cpp @@ -52,6 +52,7 @@ #include "llagentcamera.h" #include "llfloatertools.h" #include "llviewercontrol.h" +#include "llviewercamera.h" // NaCl: mouselook right-click zoom extern LLControlGroup gSavedSettings; @@ -791,10 +792,42 @@ bool LLToolCompGun::handleRightMouseDown(S32 x, S32 y, MASK mask) return false; */ + // NaCl: Right-click + scroll wheel zoom in mouselook (ported from Firestorm). + // When right mouse is pressed without Alt, record the current FOV as the + // pre-zoom value (VX) and swap in the stored zoom target (VY). + // VZ == 1.0 means "currently zoomed", 0.0 means "not zoomed". + if (!(gKeyboard->currentMask(true) & MASK_ALT)) + { + LLVector3 mlFovValues = gSavedSettings.getVector3("_NACL_MLFovValues"); + F32 cameraAngle = gSavedSettings.getF32("CameraAngle"); + mlFovValues.mV[VX] = cameraAngle; // save normal FOV + mlFovValues.mV[VZ] = 1.0f; // mark as zoomed + gSavedSettings.setVector3("_NACL_MLFovValues", mlFovValues); + gSavedSettings.setF32("CameraAngle", mlFovValues.mV[VY]); // apply zoom FOV + } + // NaCl End + // Returning true will suppress the context menu return true; } +// NaCl: Right-click + scroll wheel zoom in mouselook (ported from Firestorm). +// Restore the pre-zoom FOV when the right mouse button is released. +bool LLToolCompGun::handleRightMouseUp(S32 x, S32 y, MASK mask) +{ + LLVector3 mlFovValues = gSavedSettings.getVector3("_NACL_MLFovValues"); + F32 cameraAngle = gSavedSettings.getF32("CameraAngle"); + if (mlFovValues.mV[VZ] == 1.0f) // only restore if we entered zoom + { + mlFovValues.mV[VY] = cameraAngle; // save last zoomed FOV for next right-click + mlFovValues.mV[VZ] = 0.0f; // mark as not zoomed + gSavedSettings.setVector3("_NACL_MLFovValues", mlFovValues); + gSavedSettings.setF32("CameraAngle", mlFovValues.mV[VX]); // restore normal FOV + } + return true; +} +// NaCl End + bool LLToolCompGun::handleMouseUp(S32 x, S32 y, MASK mask) { @@ -831,10 +864,28 @@ void LLToolCompGun::handleDeselect() bool LLToolCompGun::handleScrollWheel(S32 x, S32 y, S32 clicks) { - if (clicks > 0) + // NaCl: Right-click + scroll wheel zoom in mouselook (ported from Firestorm). + // If currently zoomed (right mouse held, VZ == 1.0), adjust the zoom level + // with the scroll wheel. Otherwise fall through to the original behaviour. + LLVector3 mlFovValues = gSavedSettings.getVector3("_NACL_MLFovValues"); + F32 cameraAngle = gSavedSettings.getF32("CameraAngle"); + mlFovValues.mV[VY] = cameraAngle; + if (mlFovValues.mV[VZ] > 0.0f) + { + // Scroll up (clicks > 0) = narrower FOV (zoom in); scroll down = wider (zoom out). + mlFovValues.mV[VY] = llclamp( + mlFovValues.mV[VY] + (F32)(clicks * 0.1f), + LLViewerCamera::getInstance()->getMinView(), + LLViewerCamera::getInstance()->getMaxView()); + gSavedSettings.setVector3("_NACL_MLFovValues", mlFovValues); + gSavedSettings.setF32("CameraAngle", mlFovValues.mV[VY]); + } + else if (clicks > 0 && gSavedSettings.getBOOL("FSScrollWheelExitsMouselook")) { + // Not zoomed: scroll up exits mouselook (original behaviour, now gated by setting). gAgentCamera.changeCameraToDefault(); - } + // NaCl End + return true; } diff --git a/indra/newview/lltoolcomp.h b/indra/newview/lltoolcomp.h index 4b945967d1..d868b2e9bf 100644 --- a/indra/newview/lltoolcomp.h +++ b/indra/newview/lltoolcomp.h @@ -53,6 +53,7 @@ public: virtual bool handleHover(S32 x, S32 y, MASK mask) { return mCur->handleHover( x, y, mask ); } virtual bool handleScrollWheel(S32 x, S32 y, S32 clicks) { return mCur->handleScrollWheel( x, y, clicks ); } virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask) { return mCur->handleRightMouseDown( x, y, mask ); } + virtual bool handleRightMouseUp(S32 x, S32 y, MASK mask) { return mCur->handleRightMouseUp( x, y, mask ); } // NaCl: mouselook zoom virtual LLViewerObject* getEditingObject() { return mCur->getEditingObject(); } virtual LLVector3d getEditingPointGlobal() { return mCur->getEditingPointGlobal(); } @@ -228,6 +229,7 @@ public: virtual bool handleMouseDown(S32 x, S32 y, MASK mask) override; virtual bool handleDoubleClick(S32 x, S32 y, MASK mask) override; virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask) override; + virtual bool handleRightMouseUp(S32 x, S32 y, MASK mask) override; // NaCl: mouselook zoom virtual bool handleMouseUp(S32 x, S32 y, MASK mask) override; virtual bool handleScrollWheel(S32 x, S32 y, S32 clicks) override; virtual void onMouseCaptureLost() override; diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index fe2d44a401..7d97151e9d 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -316,6 +316,24 @@ static void update_tp_display(bool minimized) { // Transition to REQUESTED. Viewer has sent some kind // of TeleportRequest to the source simulator + + // NaCl: Right-click + scroll wheel zoom in mouselook (ported from Firestorm). + // Reset the mouselook zoom state on teleport so we don't get stuck at + // the zoomed FOV after arriving at the destination. + if (gAgentCamera.cameraMouselook()) + { + LLVector3 mlFovValues = gSavedSettings.getVector3("_NACL_MLFovValues"); + bool wasZoomed = (mlFovValues.mV[VZ] > 0.0f); + mlFovValues.mV[VZ] = 0.0f; // clear "currently zoomed" flag + gSavedSettings.setVector3("_NACL_MLFovValues", mlFovValues); + if (wasZoomed) + { + // Restore the normal (pre-zoom) FOV + gSavedSettings.setF32("CameraAngle", mlFovValues.mV[VX]); + } + } + // NaCl End + gTeleportDisplayTimer.reset(); const std::string& msg = LLAgent::sTeleportProgressMessages["requesting"]; LL_INFOS("Teleport") << "A teleport request has been sent, setting state to TELEPORT_REQUESTED" << LL_ENDL; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 50cc2442cb..2a2542ff57 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -85,6 +85,7 @@ #include "llfloatergroups.h" #include "llfloaterhelpbrowser.h" #include "llfloaterhoverheight.h" +#include "llquickprefs.h" // Quick Preferences floater (Firestorm port) #include "mpfloatertuning.h" #include "llfloaterhowto.h" #include "llfloaterhud.h" @@ -393,6 +394,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("help_browser", "floater_help_browser.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHelpBrowser>); LLFloaterReg::add("edit_hover_height", "floater_edit_hover_height.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHoverHeight>); + LLFloaterReg::add("quick_prefs", "floater_quick_prefs.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterQuickPrefs>); // Firestorm port LLFloaterReg::add("hud", "floater_hud.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHUD>); LLFloaterReg::add("mpv_performance", "floater_mp_performance.xml", (LLFloaterBuildFunc)& diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 8dab1e42d0..4632800e2d 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -4784,6 +4784,19 @@ class LLViewMouselook : public view_listener_t } else { + // NaCl: Right-click + scroll wheel zoom in mouselook (ported from Firestorm). + // If we were zoomed when the user toggles out of mouselook, restore the + // normal (pre-zoom) FOV before switching back to the default camera. + LLVector3 mlFovValues = gSavedSettings.getVector3("_NACL_MLFovValues"); + F32 cameraAngle = gSavedSettings.getF32("CameraAngle"); + if (mlFovValues.mV[VZ] > 0.0f) + { + mlFovValues.mV[VY] = cameraAngle; // preserve last zoomed FOV + mlFovValues.mV[VZ] = 0.0f; + gSavedSettings.setVector3("_NACL_MLFovValues", mlFovValues); + gSavedSettings.setF32("CameraAngle", mlFovValues.mV[VX]); // restore normal FOV + } + // NaCl End gAgentCamera.changeCameraToDefault(); } return true; @@ -7154,6 +7167,13 @@ void handle_hover_height() LLFloaterReg::showInstance("edit_hover_height"); } +// Firestorm port: Quick Preferences floater (hover height, bandwidth) +void handle_quick_prefs() +{ + LLFloaterReg::toggleInstance("quick_prefs"); +} +// End Firestorm port + void handle_edit_physics() { LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "edit_physics")); @@ -9983,6 +10003,7 @@ void initialize_menus() commit.add("EditShape", boost::bind(&handle_edit_shape)); commit.add("HoverHeight", boost::bind(&handle_hover_height)); commit.add("EditPhysics", boost::bind(&handle_edit_physics)); + commit.add("QuickPrefs", boost::bind(&handle_quick_prefs)); // Firestorm port // View menu view_listener_t::addMenu(new LLViewMouselook(), "View.Mouselook"); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 384b45ca4c..16f2064d1b 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -44,6 +44,7 @@ #include "llanimationstates.h" #include "llavatarnamecache.h" #include "llavatarpropertiesprocessor.h" +#include "llgroupcolormap.h" // group-based nameplate tinting #include "llavatarrendernotifier.h" #include "llcontrolavatar.h" #include "llexperiencecache.h" @@ -674,6 +675,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mNameAlpha(0.f), mRenderGroupTitles(sRenderGroupTitles), mNameCloud(false), + mActiveGroupID(), + mGroupFetchPending(false), mFirstTEMessageReceived( false ), mFirstAppearanceMessageReceived( false ), mCulled( false ), @@ -3650,7 +3653,26 @@ void LLVOAvatar::idleUpdateNameTagText(bool new_name) mNameAppearance = is_appearance; mNameFriend = is_friend; mNameCloud = is_cloud; - mTitle = title ? title->getString() : ""; + + // Group-based nameplate tinting: when the title NameValue changes on a + // non-self avatar it means their active group (or role) changed. Fire + // a lightweight AvatarPropertiesRequest so the AvatarGroupsReply arrives + // and we can discover the active group UUID. We register ourselves as an + // observer for APT_GROUPS in processProperties() below. + const std::string new_title = title ? title->getString() : ""; + if (!isSelf() && new_title != mTitle) + { + // Reset cached group UUID - it will be repopulated by the reply. + mActiveGroupID.setNull(); + if (!mGroupFetchPending) + { + mGroupFetchPending = true; + LLAvatarPropertiesProcessor::getInstance()->addObserver(getID(), this); + LLAvatarPropertiesProcessor::getInstance()->sendAvatarLegacyPropertiesRequest(getID()); + } + } + + mTitle = new_title; LLStringFn::replace_ascii_controlchars(mTitle,LL_UNKNOWN_CHAR); new_name = true; } @@ -3883,7 +3905,80 @@ LLColor4 LLVOAvatar::getNameTagColor(bool is_friend) // ...not using display names color_name = "NameTagLegacy"; } - return LLUIColorTable::getInstance()->getColor( color_name ); + + LLColor4 base_color = LLUIColorTable::getInstance()->getColor(color_name); + + // Group-based nameplate tinting: override with the group color if one is set. + // For self, the active group UUID is always available via gAgent. + // For others, it is populated asynchronously via AvatarGroupsReply. + LLUUID active_group; + if (isSelf()) + { + active_group = gAgent.getGroupID(); + } + else + { + active_group = mActiveGroupID; + } + + if (active_group.notNull()) + { + LLColor4 group_color = LLGroupColorMap::getInstance()->getGroupColor(active_group); + if (group_color.mV[VW] >= 0.01f) // non-transparent = has a color set + { + return group_color; + } + } + + return base_color; +} + +// --------------------------------------------------------------------------- +// Group-based nameplate tinting: observer callback +// --------------------------------------------------------------------------- + +void LLVOAvatar::processProperties(void* data, EAvatarProcessorType type) +{ + if (type != APT_GROUPS) + return; + + LLAvatarGroups* groups = static_cast<LLAvatarGroups*>(data); + if (!groups || groups->avatar_id != getID()) + return; + + // Un-register ourselves — we only need this one reply per title change. + LLAvatarPropertiesProcessor::getInstance()->removeObserver(getID(), this); + mGroupFetchPending = false; + + // The active group is the one whose GroupTitle matches the avatar's + // current Title NameValue (mTitle). Each avatar can only have one + // selected role title displayed at a time, so this match is unambiguous. + for (const auto& gd : groups->group_list) + { + if (gd.group_title == mTitle) + { + mActiveGroupID = gd.group_id; + // Force nametag rebuild so new color is shown immediately. + clearNameTag(); + return; + } + } + + // No match found (e.g. avatar has no active group or title is blank). + mActiveGroupID.setNull(); + clearNameTag(); +} + +// Request the group list for a non-self avatar so we can resolve their +// active group UUID. Called from idleUpdateNameTagText on title change. +void LLVOAvatar::sendAvatarGroupsRequest() +{ + if (!isSelf() && !mGroupFetchPending) + { + mGroupFetchPending = true; + LLAvatarPropertiesProcessor::getInstance()->addObserver(getID(), this); + LLAvatarPropertiesProcessor::getInstance()->sendAvatarLegacyPropertiesRequest(getID()); + } } void LLVOAvatar::idleUpdateBelowWater() diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index fc3a97a25d..0c3b82c886 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -36,6 +36,7 @@ #include <boost/signals2/trackable.hpp> #include "llavatarappearance.h" +#include "llavatarpropertiesprocessor.h" // LLAvatarPropertiesObserver (group tinting) #include "llchat.h" #include "lldrawpoolalpha.h" #include "llviewerobject.h" @@ -87,6 +88,7 @@ extern U32 gFrameCount; class LLVOAvatar : public LLAvatarAppearance, public LLViewerObject, + public LLAvatarPropertiesObserver, // group-based nameplate tinting public boost::signals2::trackable { LL_ALIGN_NEW; @@ -292,6 +294,11 @@ public: void idleUpdateNameTagAlpha(bool new_name, F32 alpha); LLColor4 getNameTagColor(bool is_friend); void clearNameTag(); + + // LLAvatarPropertiesObserver: receives APT_GROUPS reply for group-tint lookup + /*virtual*/ void processProperties(void* data, EAvatarProcessorType type) override; + void sendAvatarGroupsRequest(); + const LLUUID& getActiveGroupID() const { return mActiveGroupID; } static void invalidateNameTag(const LLUUID& agent_id); // force all name tags to rebuild, useful when display names turned on/off static void invalidateNameTags(); @@ -1122,6 +1129,10 @@ private: F32 mNameAlpha; S32 mRenderGroupTitles; + // Group-based nameplate tinting + LLUUID mActiveGroupID; // active group UUID; null until known + bool mGroupFetchPending; // true while AvatarPropertiesRequest is in flight + //-------------------------------------------------------------------- // Display the name (then optionally fade it out) //-------------------------------------------------------------------- diff --git a/indra/newview/skins/default/xui/en/floater_quick_prefs.xml b/indra/newview/skins/default/xui/en/floater_quick_prefs.xml new file mode 100644 index 0000000000..8b20fda6f5 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_quick_prefs.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<!-- + floater_quick_prefs.xml + Quick Preferences panel – ported from Firestorm Viewer. + Provides on-the-fly access to avatar hover height and max bandwidth. +--> +<floater + positioning="cascading" + can_minimize="true" + can_close="true" + can_resize="false" + height="96" + width="320" + layout="topleft" + name="quick_prefs" + single_instance="true" + help_topic="quick_prefs" + save_rect="true" + save_visibility="false" + title="QUICK PREFERENCES"> + + <!-- ── Hover Height ─────────────────────────────────────────────────── --> + <text + type="string" + follows="left|top" + height="16" + layout="topleft" + left="10" + top="28" + width="100" + name="HoverHeightLabel" + tool_tip="Adjust your avatar's hover height above the ground"> + Hover Height: + </text> + + <slider + control_name="HoverHeightSlider" + decimal_digits="3" + enabled="false" + can_edit_text="true" + follows="left|right|top" + height="16" + increment="0.001" + initial_value="0.0" + label_width="0" + layout="topleft" + left="110" + right="-10" + top="30" + name="HoverHeightSlider" + tool_tip="Drag to set your avatar hover height (-3.0 to +3.0 m)" /> + + <!-- ── Max Bandwidth ────────────────────────────────────────────────── --> + <text + type="string" + follows="left|top" + height="16" + layout="topleft" + left="10" + top_pad="12" + width="100" + name="MaxBandwidthLabel" + tool_tip="Set the maximum network bandwidth in Kbps"> + Max Bandwidth: + </text> + + <slider + control_name="ThrottleBandwidthKBPS" + decimal_digits="0" + can_edit_text="true" + follows="left|right|top" + height="16" + increment="50" + initial_value="500" + max_val="3000" + min_val="50" + label_width="0" + layout="topleft" + left="110" + right="-10" + top_delta="-2" + name="max_bandwidth" + tool_tip="Network bandwidth in Kbps (50 – 3000)" /> + +</floater> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 5335e0f8bd..48ee13bf21 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -206,6 +206,14 @@ function="Edit.EnableHoverHeight" /> </menu_item_call> <menu_item_call + label="Quick Preferences..." + layout="topleft" + name="Quick Preferences" + shortcut="alt|control|Q"> + <menu_item_call.on_click + function="QuickPrefs" /> + </menu_item_call> + <menu_item_call label="Edit shape..." layout="topleft" name="Edit My Shape"> diff --git a/indra/newview/skins/default/xui/en/panel_group_general.xml b/indra/newview/skins/default/xui/en/panel_group_general.xml index d97411f5ab..e0fec0f5fd 100644 --- a/indra/newview/skins/default/xui/en/panel_group_general.xml +++ b/indra/newview/skins/default/xui/en/panel_group_general.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel label="General" - height="420" + height="580" width="304" class="panel_group_general" name="general_tab"> @@ -95,6 +95,53 @@ Hover your mouse over the options for more help. visible="true" width="120" /> </panel> + + <!-- Nameplate tinting: isolated panel, placed after group_info_top --> + <panel + name="nametag_color_panel" + follows="left|right|top" + layout="topleft" + left="0" + top="129" + width="304" + height="36" + background_visible="false"> + <text + type="string" + follows="left|top" + height="16" + layout="topleft" + left="5" + top="2" + width="160" + name="nametag_color_label" + text_color="EmphasisColor"> + Nameplate Color: + </text> + <color_swatch + can_apply_immediately="false" + follows="left|top" + height="22" + label="" + label_height="0" + layout="topleft" + left="170" + top="2" + name="group_nametag_color" + tool_tip="Click to set nameplate color for this group" + width="54" /> + <button + follows="left|top" + height="22" + label="Clear" + layout="topleft" + left="230" + top="2" + name="group_nametag_color_clear" + tool_tip="Remove the nameplate color for this group" + width="60" /> + </panel> + <text_editor type="string" follows="left|top|right" @@ -104,7 +151,7 @@ Hover your mouse over the options for more help. max_length="511" name="charter" parse_urls="true" - top="105" + top="170" right="-4" bg_readonly_color="DkGray2" text_readonly_color="White" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 6e48577064..418dd7ef45 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -4219,6 +4219,7 @@ name="Command_360_Capture_Label">360 snapshot</string> <string name="Command_Picks_Label">Picks</string> <string name="Command_Places_Label">Places</string> <string name="Command_Preferences_Label">Preferences</string> + <string name="Command_QuickPrefs_Label">Quick Prefs</string> <string name="Command_Profile_Label">Profile</string> <string name="Command_Report_Abuse_Label">Report Abuse</string> <string name="Command_Search_Label">Search</string> @@ -4254,6 +4255,7 @@ name="Command_360_Capture_Tooltip">Capture a 360 equirectangular image</string> <string name="Command_Picks_Tooltip">Places to show as favorites in your profile</string> <string name="Command_Places_Tooltip">Places you've saved</string> <string name="Command_Preferences_Tooltip">Preferences</string> + <string name="Command_QuickPrefs_Tooltip">Quickly adjust hover height and bandwidth</string> <string name="Command_Profile_Tooltip">Edit or view your profile</string> <string name="Command_Report_Abuse_Tooltip">Report Abuse</string> <string name="Command_Search_Tooltip">Find places, events, people</string> |
