diff options
Diffstat (limited to 'indra')
163 files changed, 4359 insertions, 3976 deletions
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 07deaa56b0..fb1a2bc4ac 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -15,6 +15,7 @@ include_guard() include(Variables) +include(Linker) # 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}") @@ -113,51 +114,90 @@ if (WINDOWS) add_compile_definitions(_CRT_SECURE_NO_WARNINGS) endif (WINDOWS) - if (LINUX) - set(CMAKE_SKIP_RPATH TRUE) - - # EXTERNAL_TOS - # force this platform to accept TOS via external browser + set( CMAKE_BUILD_WITH_INSTALL_RPATH TRUE ) + set( CMAKE_INSTALL_RPATH $ORIGIN $ORIGIN/../lib ) + set(CMAKE_EXE_LINKER_FLAGS "-Wl,--exclude-libs,ALL") + + find_program(CCACHE_EXE ccache) + if(CCACHE_EXE AND NOT DISABLE_CCACHE) + set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_EXE} ) + set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_EXE} ) + endif() - # LL_IGNORE_SIGCHLD - # don't catch SIGCHLD in our base application class for the viewer - some of - # our 3rd party libs may need their *own* SIGCHLD handler to work. Sigh! The - # viewer doesn't need to catch SIGCHLD anyway. + # LL_IGNORE_SIGCHLD + # don't catch SIGCHLD in our base application class for the viewer - some of + # our 3rd party libs may need their *own* SIGCHLD handler to work. Sigh! The + # viewer doesn't need to catch SIGCHLD anyway. add_compile_definitions( _REENTRANT - _FORTIFY_SOURCE=2 - EXTERNAL_TOS APPID=secondlife LL_IGNORE_SIGCHLD ) + + if( ENABLE_ASAN ) + add_compile_options(-U_FORTIFY_SOURCE + -fsanitize=address + --param asan-stack=0 + ) + add_link_options(-fsanitize=address) + else() + add_compile_definitions( _FORTIFY_SOURCE=2 ) + endif() + add_compile_options( - -fexceptions - -fno-math-errno - -fno-strict-aliasing - -fsigned-char - -msse2 - -mfpmath=sse - -pthread - -Wno-parentheses - -Wno-deprecated - -fvisibility=hidden + -fexceptions + -fno-math-errno + -fno-strict-aliasing + -fsigned-char + -msse2 + -mfpmath=sse + -pthread + -fvisibility=hidden + ) + + set(GCC_CLANG_COMPATIBLE_WARNINGS + -Wno-parentheses + -Wno-deprecated + -Wno-c++20-compat + -Wno-pessimizing-move + ) + + set(CLANG_WARNINGS + ${GCC_CLANG_COMPATIBLE_WARNINGS} + # Put clang specific warning configuration here + ) + + set(GCC_WARNINGS + ${GCC_CLANG_COMPATIBLE_WARNINGS} + -Wno-dangling-pointer ) - if (ADDRESS_SIZE EQUAL 32) - add_compile_options(-march=pentium4) - endif (ADDRESS_SIZE EQUAL 32) + add_link_options( + -Wl,--no-keep-memory + -Wl,--build-id + -Wl,--no-undefined + ) + if (NOT GCC_DISABLE_FATAL_WARNINGS) + add_compile_options( -Werror ) + endif (NOT GCC_DISABLE_FATAL_WARNINGS) # this stops us requiring a really recent glibc at runtime add_compile_options(-fno-stack-protector) - # linking can be very memory-hungry, especially the final viewer link - set(CMAKE_CXX_LINK_FLAGS "-Wl,--no-keep-memory") - set(CMAKE_CXX_FLAGS_DEBUG "-fno-inline ${CMAKE_CXX_FLAGS_DEBUG}") + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + # ND: clang is a bit more picky than GCC, the latter seems to auto include -lstdc++ and -lm. The former not so and thus fails to link + add_link_options( + -lstdc++ + -lm + ) + add_compile_options(${CLANG_WARNINGS}) + else() + add_compile_options(${GCC_WARNINGS}) + endif() endif (LINUX) - if (DARWIN) # Warnings should be fatal -- thanks, Nicky Perian, for spotting reversed default set(CLANG_DISABLE_FATAL_WARNINGS OFF) @@ -179,15 +219,9 @@ if (DARWIN) # required for clang-15/xcode-15 since our boost package still uses deprecated std::unary_function/binary_function # see https://developer.apple.com/documentation/xcode-release-notes/xcode-15-release-notes#C++-Standard-Library add_compile_definitions(_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION) -endif (DARWIN) -if (LINUX OR DARWIN) set(GCC_WARNINGS -Wall -Wno-sign-compare -Wno-trigraphs) - if (NOT GCC_DISABLE_FATAL_WARNINGS) - list(APPEND GCC_WARNINGS -Werror) - endif (NOT GCC_DISABLE_FATAL_WARNINGS) - list(APPEND GCC_WARNINGS -Wno-reorder -Wno-non-virtual-dtor ) if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 13) @@ -196,7 +230,4 @@ if (LINUX OR DARWIN) add_compile_options(${GCC_WARNINGS}) add_compile_options(-m${ADDRESS_SIZE}) -endif (LINUX OR DARWIN) - - - +endif () diff --git a/indra/cmake/Audio.cmake b/indra/cmake/Audio.cmake index 38547bb017..5efaff07c7 100644 --- a/indra/cmake/Audio.cmake +++ b/indra/cmake/Audio.cmake @@ -11,6 +11,6 @@ target_include_directories( ll::vorbis SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/inc if (WINDOWS) target_link_libraries(ll::vorbis INTERFACE ogg_static vorbis_static vorbisenc_static vorbisfile_static ) else (WINDOWS) - target_link_libraries(ll::vorbis INTERFACE ogg vorbis vorbisenc vorbisfile ) + target_link_libraries(ll::vorbis INTERFACE vorbisfile vorbis ogg vorbisenc ) endif (WINDOWS) diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake index 601a23a86d..293d8931cb 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -27,14 +27,14 @@ if (WINDOWS) libboost_thread-mt${addrsfx}) elseif (LINUX) target_link_libraries( ll::boost INTERFACE - boost_context-mt${addrsfx} boost_fiber-mt${addrsfx} + boost_context-mt${addrsfx} boost_filesystem-mt${addrsfx} boost_program_options-mt${addrsfx} boost_regex-mt${addrsfx} - boost_signals-mt${addrsfx} + boost_thread-mt${addrsfx} boost_system-mt${addrsfx} - boost_thread-mt${addrsfx}) + ) elseif (DARWIN) target_link_libraries( ll::boost INTERFACE boost_context-mt${addrsfx} diff --git a/indra/cmake/CEFPlugin.cmake b/indra/cmake/CEFPlugin.cmake index 9b77becf29..555d2aebbf 100644 --- a/indra/cmake/CEFPlugin.cmake +++ b/indra/cmake/CEFPlugin.cmake @@ -33,4 +33,9 @@ elseif (DARWIN) ) elseif (LINUX) + target_link_libraries( ll::cef INTERFACE + libdullahan.a + cef + cef_dll_wrapper.a + ) endif (WINDOWS) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index c067105f68..1692fa2415 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -15,7 +15,6 @@ set(cmake_SOURCE_FILES CEFPlugin.cmake CEFPlugin.cmake CMakeCopyIfDifferent.cmake - ConfigurePkgConfig.cmake CURL.cmake Copy3rdPartyLibs.cmake DBusGlib.cmake diff --git a/indra/cmake/ConfigurePkgConfig.cmake b/indra/cmake/ConfigurePkgConfig.cmake deleted file mode 100644 index 9e798d663b..0000000000 --- a/indra/cmake/ConfigurePkgConfig.cmake +++ /dev/null @@ -1,74 +0,0 @@ -# -*- cmake -*- - -SET(DEBUG_PKG_CONFIG "YES") - -# Don't change this if manually set by user. -IF("$ENV{PKG_CONFIG_LIBDIR}" STREQUAL "") - - # Guess at architecture-specific system library paths. - if (ADDRESS_SIZE EQUAL 32) - SET(PKG_CONFIG_NO_MULTI_GUESS /usr/lib32 /usr/lib) - SET(PKG_CONFIG_NO_MULTI_LOCAL_GUESS /usr/local/lib32 /usr/local/lib) - SET(PKG_CONFIG_MULTI_GUESS /usr/lib/i386-linux-gnu) - SET(PKG_CONFIG_MULTI_LOCAL_GUESS /usr/local/lib/i386-linux-gnu) - else (ADDRESS_SIZE EQUAL 32) - SET(PKG_CONFIG_NO_MULTI_GUESS /usr/lib64 /usr/lib) - SET(PKG_CONFIG_NO_MULTI_LOCAL_GUESS /usr/local/lib64 /usr/local/lib) - SET(PKG_CONFIG_MULTI_GUESS /usr/local/lib/x86_64-linux-gnu) - SET(PKG_CONFIG_MULTI_LOCAL_GUESS /usr/local/lib/x86_64-linux-gnu) - endif (ADDRESS_SIZE EQUAL 32) - - # Use DPKG architecture, if available. - IF (${DPKG_ARCH}) - SET(PKG_CONFIG_MULTI_GUESS /usr/lib/${DPKG_ARCH}) - SET(PKG_CONFIG_MULTI_LOCAL_GUESS /usrlocal/lib/${DPKG_ARCH}) - ENDIF (${DPKG_ARCH}) - - # Explicitly include anything listed in PKG_CONFIG_PATH - string(REPLACE ":" ";" PKG_CONFIG_PATH_LIST "$ENV{PKG_CONFIG_PATH}") - FOREACH(PKG_CONFIG_DIR ${PKG_CONFIG_PATH_LIST}) - SET(VALID_PKG_LIBDIRS "${VALID_PKG_LIBDIRS}:${PKG_CONFIG_DIR}/pkgconfig") - ENDFOREACH(PKG_CONFIG_DIR) - - # Look for valid pkgconfig directories. - FIND_PATH(PKG_CONFIG_ENV pkgconfig ENV LD_LIBRARY_PATH) - FIND_PATH(PKG_CONFIG_MULTI pkgconfig HINT ${PKG_CONFIG_MULTI_GUESS}) - FIND_PATH(PKG_CONFIG_MULTI_LOCAL pkgconfig HINT ${PKG_CONFIG_MULTI_LOCAL_GUESS}) - FIND_PATH(PKG_CONFIG_NO_MULTI pkgconfig HINT ${PKG_CONFIG_NO_MULTI_GUESS}) - FIND_PATH(PKG_CONFIG_NO_MULTI_LOCAL pkgconfig HINT ${PKG_CONFIG_NO_MULTI_LOCAL_GUESS}) - - # Add anything we found to our list. - IF(NOT PKG_CONFIG_ENV STREQUAL PKG_CONFIG_ENV-NOTFOUND) - SET(VALID_PKG_LIBDIRS "${VALID_PKG_LIBDIRS}:${PKG_CONFIG_ENV}/pkgconfig") - ENDIF(NOT PKG_CONFIG_ENV STREQUAL PKG_CONFIG_ENV-NOTFOUND) - - IF(NOT PKG_CONFIG_MULTI STREQUAL PKG_CONFIG_MULTI-NOTFOUND) - SET(VALID_PKG_LIBDIRS "${VALID_PKG_LIBDIRS}:${PKG_CONFIG_MULTI}/pkgconfig") - ENDIF(NOT PKG_CONFIG_MULTI STREQUAL PKG_CONFIG_MULTI-NOTFOUND) - - IF(NOT PKG_CONFIG_MULTI_LOCAL STREQUAL PKG_CONFIG_MULTI_LOCAL-NOTFOUND) - SET(VALID_PKG_LIBDIRS "${VALID_PKG_LIBDIRS}:${PKG_CONFIG_MULTI_LOCAL}/pkgconfig") - ENDIF(NOT PKG_CONFIG_MULTI_LOCAL STREQUAL PKG_CONFIG_MULTI_LOCAL-NOTFOUND) - - IF(NOT PKG_CONFIG_NO_MULTI STREQUAL PKG_CONFIG_NO_MULTI-NOTFOUND) - SET(VALID_PKG_LIBDIRS "${VALID_PKG_LIBDIRS}:${PKG_CONFIG_NO_MULTI}/pkgconfig") - ENDIF(NOT PKG_CONFIG_NO_MULTI STREQUAL PKG_CONFIG_NO_MULTI-NOTFOUND) - - IF(NOT PKG_CONFIG_NO_MULTI_LOCAL STREQUAL PKG_CONFIG_NO_MULTI_LOCAL-NOTFOUND) - SET(VALID_PKG_LIBDIRS "${VALID_PKG_LIBDIRS}:${PKG_CONFIG_NO_MULTI_LOCAL}/pkgconfig") - ENDIF(NOT PKG_CONFIG_NO_MULTI_LOCAL STREQUAL PKG_CONFIG_NO_MULTI_LOCAL-NOTFOUND) - - # Also add some non-architecture specific package locations. - SET(VALID_PKG_LIBDIRS "${VALID_PKG_LIBDIRS}:/usr/share/pkgconfig:/usr/local/share/pkgconfig") - - # Remove first unwanted ':' - string(SUBSTRING ${VALID_PKG_LIBDIRS} 1 -1 VALID_PKG_LIBDIRS) - - # Set PKG_CONFIG_LIBDIR environment. - SET(ENV{PKG_CONFIG_LIBDIR} ${VALID_PKG_LIBDIRS}) -ENDIF("$ENV{PKG_CONFIG_LIBDIR}" STREQUAL "") - -IF(DEBUG_PKG_CONFIG) - MESSAGE(STATUS "Using PKG_CONFIG_LIBDIR=$ENV{PKG_CONFIG_LIBDIR}") -ENDIF(DEBUG_PKG_CONFIG) - diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index 30dee3c6c1..d78f2b90b8 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -227,17 +227,7 @@ elseif(LINUX) list( APPEND release_files libapr-1.so.0 libaprutil-1.so.0 - libatk-1.0.so - libfreetype.so.6.6.2 - libfreetype.so.6 libhunspell-1.3.so.0.0.0 - libopenjp2.so - libuuid.so.16 - libuuid.so.16.0.22 - libfontconfig.so.1.8.0 - libfontconfig.so.1 - libgmodule-2.0.so - libgobject-2.0.so ) endif() diff --git a/indra/cmake/FindPipeWire.cmake b/indra/cmake/FindPipeWire.cmake new file mode 100644 index 0000000000..868acf5ec1 --- /dev/null +++ b/indra/cmake/FindPipeWire.cmake @@ -0,0 +1,100 @@ +# cmake-format: off +# .rst: FindPipeWire +# ------- +# +# Try to find PipeWire on a Unix system. +# +# This will define the following variables: +# +# ``PIPEWIRE_FOUND`` True if (the requested version of) PipeWire is available +# ``PIPEWIRE_VERSION`` The version of PipeWire ``PIPEWIRE_LIBRARIES`` This can +# be passed to target_link_libraries() instead of the ``PipeWire::PipeWire`` +# target ``PIPEWIRE_INCLUDE_DIRS`` This should be passed to +# target_include_directories() if the target is not used for linking +# ``PIPEWIRE_COMPILE_FLAGS`` This should be passed to target_compile_options() +# if the target is not used for linking +# +# If ``PIPEWIRE_FOUND`` is TRUE, it will also define the following imported +# target: +# +# ``PipeWire::PipeWire`` The PipeWire library +# +# In general we recommend using the imported target, as it is easier to use. +# Bear in mind, however, that if the target is in the link interface of an +# exported library, it must be made available by the package config file. + +# ============================================================================= +# Copyright 2014 Alex Merry <alex.merry@kde.org> Copyright 2014 Martin Gräßlin +# <mgraesslin@kde.org> Copyright 2018-2020 Jan Grulich <jgrulich@redhat.com> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the copyright notice, this list +# of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ============================================================================= +# cmake-format: on + +# Use pkg-config to get the directories and then use these values in the FIND_PATH() and FIND_LIBRARY() calls +find_package(PkgConfig QUIET) + +pkg_search_module(PKG_PIPEWIRE QUIET libpipewire-0.3) +pkg_search_module(PKG_SPA QUIET libspa-0.2) + +set(PIPEWIRE_COMPILE_FLAGS "${PKG_PIPEWIRE_CFLAGS}" "${PKG_SPA_CFLAGS}") +set(PIPEWIRE_VERSION "${PKG_PIPEWIRE_VERSION}") + +find_path( + PIPEWIRE_INCLUDE_DIRS + NAMES pipewire/pipewire.h + HINTS ${PKG_PIPEWIRE_INCLUDE_DIRS} ${PKG_PIPEWIRE_INCLUDE_DIRS}/pipewire-0.3) + +find_path( + SPA_INCLUDE_DIRS + NAMES spa/param/props.h + HINTS ${PKG_SPA_INCLUDE_DIRS} ${PKG_SPA_INCLUDE_DIRS}/spa-0.2) + +find_library( + PIPEWIRE_LIBRARIES + NAMES pipewire-0.3 + HINTS ${PKG_PIPEWIRE_LIBRARY_DIRS}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + PipeWire + FOUND_VAR PIPEWIRE_FOUND + REQUIRED_VARS PIPEWIRE_LIBRARIES PIPEWIRE_INCLUDE_DIRS SPA_INCLUDE_DIRS + VERSION_VAR PIPEWIRE_VERSION) + +if(PIPEWIRE_FOUND AND NOT TARGET PipeWire::PipeWire) + add_library(PipeWire::PipeWire UNKNOWN IMPORTED) + set_target_properties( + PipeWire::PipeWire + PROPERTIES IMPORTED_LOCATION "${PIPEWIRE_LIBRARIES}" + INTERFACE_COMPILE_OPTIONS "${PIPEWIRE_COMPILE_FLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${PIPEWIRE_INCLUDE_DIRS};${SPA_INCLUDE_DIRS}") +endif() + +mark_as_advanced(PIPEWIRE_LIBRARIES PIPEWIRE_INCLUDE_DIRS) + +include(FeatureSummary) +set_package_properties( + PipeWire PROPERTIES + URL "https://www.pipewire.org" + DESCRIPTION "PipeWire - multimedia processing") diff --git a/indra/cmake/FreeType.cmake b/indra/cmake/FreeType.cmake index 77140af641..e7e28a44d0 100644 --- a/indra/cmake/FreeType.cmake +++ b/indra/cmake/FreeType.cmake @@ -4,8 +4,11 @@ include(Prebuilt) include_guard() add_library( ll::freetype INTERFACE IMPORTED ) -use_system_binary(freetype) -use_prebuilt_binary(freetype) -target_include_directories( ll::freetype SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/freetype2/) -target_link_libraries( ll::freetype INTERFACE freetype ) - + use_system_binary(freetype) + use_prebuilt_binary(freetype) + target_include_directories( ll::freetype SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/freetype2/) +if( LINUX ) + target_link_libraries( ll::freetype INTERFACE ${LIBS_PREBUILT_DIR}/lib/release/libfreetype.a ${LIBS_PREBUILT_DIR}/lib/release/libpng16.a) +else() + target_link_libraries( ll::freetype INTERFACE freetype ) +endif() diff --git a/indra/cmake/GLIB.cmake b/indra/cmake/GLIB.cmake new file mode 100644 index 0000000000..f52cbb7f87 --- /dev/null +++ b/indra/cmake/GLIB.cmake @@ -0,0 +1,22 @@ +include_guard() + +include(Prebuilt) + +add_library( ll::glib INTERFACE IMPORTED ) +add_library( ll::glib_headers INTERFACE IMPORTED ) +add_library( ll::gio INTERFACE IMPORTED ) + +if( LINUX ) + find_package(PkgConfig REQUIRED) + pkg_search_module(GLIB REQUIRED glib-2.0) + pkg_search_module(GIO REQUIRED gio-2.0) + + target_include_directories( ll::glib SYSTEM INTERFACE ${GLIB_INCLUDE_DIRS} ) + target_link_libraries( ll::glib INTERFACE ${GLIB_LDFLAGS} ) + target_compile_definitions( ll::glib INTERFACE -DLL_GLIB=1) + + target_include_directories( ll::glib_headers SYSTEM INTERFACE ${GLIB_INCLUDE_DIRS} ) + target_compile_definitions( ll::glib_headers INTERFACE -DLL_GLIB=1) + + target_link_libraries( ll::gio INTERFACE ${GIO_LDFLAGS} ) +endif() diff --git a/indra/cmake/GStreamer10Plugin.cmake b/indra/cmake/GStreamer10Plugin.cmake new file mode 100644 index 0000000000..da2e33d04d --- /dev/null +++ b/indra/cmake/GStreamer10Plugin.cmake @@ -0,0 +1,27 @@ +# -*- cmake -*- + +include_guard() + +include(Prebuilt) +include(GLIB) + +add_library( ll::gstreamer10 INTERFACE IMPORTED ) + +if (LINUX) + include(FindPkgConfig) + + pkg_check_modules(GSTREAMER10 REQUIRED gstreamer-1.0) + pkg_check_modules(GSTREAMER10_PLUGINS_BASE REQUIRED gstreamer-plugins-base-1.0) + + target_include_directories( ll::gstreamer10 SYSTEM INTERFACE ${GSTREAMER10_INCLUDE_DIRS}) + target_link_libraries( ll::gstreamer10 INTERFACE ll::glib_headers) + +endif () + +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/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index 6408f1200c..5d96a4398f 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -1,4 +1,11 @@ # -*- cmake -*- + +include_guard() + +if( NOT LL_TESTS ) + return() +endif() + include(00-Common) include(LLTestCommand) include(bugsplat) diff --git a/indra/cmake/LLWindow.cmake b/indra/cmake/LLWindow.cmake index 2e1b601b79..23f4115aeb 100644 --- a/indra/cmake/LLWindow.cmake +++ b/indra/cmake/LLWindow.cmake @@ -10,13 +10,11 @@ add_library( ll::SDL INTERFACE IMPORTED ) if (LINUX) #Must come first as use_system_binary can exit this file early - target_compile_definitions( ll::SDL INTERFACE LL_SDL=1) + target_compile_definitions( ll::SDL INTERFACE LL_SDL_VERSION=2 LL_SDL) - use_system_binary(SDL) - use_prebuilt_binary(SDL) + #find_package(SDL2 REQUIRED) + #target_link_libraries( ll::SDL INTERFACE SDL2::SDL2 SDL2::SDL2main X11) - target_include_directories( ll::SDL SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include) - target_link_libraries( ll::SDL INTERFACE SDL directfb fusion direct X11) + use_prebuilt_binary(SDL2) + target_link_libraries( ll::SDL INTERFACE SDL2 X11) endif (LINUX) - - diff --git a/indra/cmake/Linker.cmake b/indra/cmake/Linker.cmake new file mode 100644 index 0000000000..8016842192 --- /dev/null +++ b/indra/cmake/Linker.cmake @@ -0,0 +1,11 @@ +include_guard(GLOBAL) + +if( LINK_WITH_MOLD ) + find_program(MOLD_BIN mold) + if(MOLD_BIN) + message(STATUS "Mold linker found: ${MOLD_BIN}. Enabling mold as active linker.") + add_link_options("-fuse-ld=${MOLD_BIN}") + else() + message(STATUS "Mold linker not found. Using default linker.") + endif() +endif() diff --git a/indra/cmake/Meshoptimizer.cmake b/indra/cmake/Meshoptimizer.cmake index fd144d2b97..6983a5895a 100644 --- a/indra/cmake/Meshoptimizer.cmake +++ b/indra/cmake/Meshoptimizer.cmake @@ -12,7 +12,7 @@ use_prebuilt_binary(meshoptimizer) if (WINDOWS) target_link_libraries( ll::meshoptimizer INTERFACE meshoptimizer.lib) elseif (LINUX) - target_link_libraries( ll::meshoptimizer INTERFACE meshoptimizer.o) + target_link_libraries( ll::meshoptimizer INTERFACE libmeshoptimizer.a) elseif (DARWIN) target_link_libraries( ll::meshoptimizer INTERFACE libmeshoptimizer.a) endif (WINDOWS) diff --git a/indra/cmake/NDOF.cmake b/indra/cmake/NDOF.cmake index b88fbccf2a..db9c8b1780 100644 --- a/indra/cmake/NDOF.cmake +++ b/indra/cmake/NDOF.cmake @@ -19,6 +19,6 @@ if (NDOF) target_link_libraries( ll::ndof INTERFACE ndofdev) endif (WINDOWS) target_compile_definitions( ll::ndof INTERFACE LIB_NDOF=1) +else() + add_compile_options(-ULIB_NDOF) endif (NDOF) - - diff --git a/indra/cmake/UI.cmake b/indra/cmake/UI.cmake index 8f135676d6..0df62808e7 100644 --- a/indra/cmake/UI.cmake +++ b/indra/cmake/UI.cmake @@ -1,35 +1,33 @@ # -*- cmake -*- include(Prebuilt) include(FreeType) +include(GLIB) add_library( ll::uilibraries INTERFACE IMPORTED ) if (LINUX) - target_compile_definitions(ll::uilibraries INTERFACE LL_GTK=1 LL_X11=1 ) + use_prebuilt_binary(fltk) + target_compile_definitions(ll::uilibraries INTERFACE LL_FLTK=1 LL_X11=1 ) if( USE_CONAN ) - target_link_libraries( ll::uilibraries INTERFACE CONAN_PKG::gtk ) return() endif() - use_prebuilt_binary(gtk-atk-pango-glib) target_link_libraries( ll::uilibraries INTERFACE - atk-1.0 - gdk-x11-2.0 - gdk_pixbuf-2.0 - Xinerama - 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 + fltk + Xrender + Xcursor + Xfixes + Xext + Xft Xinerama + ll::fontconfig ll::freetype - ) + ll::SDL + ll::glib + ll::gio + ) + endif (LINUX) if( WINDOWS ) target_link_libraries( ll::uilibraries INTERFACE @@ -51,4 +49,3 @@ endif() target_include_directories( ll::uilibraries SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include ) - diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index 5b3aeb8b7f..5dfad25577 100644 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -127,8 +127,6 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(CMAKE_SYSTEM_LIBRARY_PATH /usr/lib/${DPKG_ARCH} /usr/local/lib/${DPKG_ARCH} ${CMAKE_SYSTEM_LIBRARY_PATH}) endif (DPKG_RESULT EQUAL 0) - include(ConfigurePkgConfig) - if (INSTALL_PROPRIETARY) # Only turn on headless if we can find osmesa libraries. include(FindPkgConfig) diff --git a/indra/cmake/ViewerMiscLibs.cmake b/indra/cmake/ViewerMiscLibs.cmake index cae68fbc11..2cb11994fc 100644 --- a/indra/cmake/ViewerMiscLibs.cmake +++ b/indra/cmake/ViewerMiscLibs.cmake @@ -2,14 +2,10 @@ include(Prebuilt) if (LINUX) - #use_prebuilt_binary(libuuid) add_library( ll::fontconfig INTERFACE IMPORTED ) - if( NOT USE_CONAN ) - use_prebuilt_binary(fontconfig) - else() - target_link_libraries( ll::fontconfig INTERFACE CONAN_PKG::fontconfig ) - endif() + find_package(Fontconfig REQUIRED) + target_link_libraries( ll::fontconfig INTERFACE Fontconfig::Fontconfig ) endif (LINUX) if( NOT USE_CONAN ) diff --git a/indra/linux_crash_logger/llcrashloggerlinux.cpp b/indra/linux_crash_logger/llcrashloggerlinux.cpp index 9b40de741e..7992f59b36 100644 --- a/indra/linux_crash_logger/llcrashloggerlinux.cpp +++ b/indra/linux_crash_logger/llcrashloggerlinux.cpp @@ -38,9 +38,6 @@ #include "lldir.h" #include "llsdserialize.h" -#if LL_GTK -# include "gtk/gtk.h" -#endif // LL_GTK #define MAX_LOADSTRING 100 @@ -54,52 +51,9 @@ static const char dialog_text[] = static const char dialog_title[] = "Second Life Crash Logger"; -#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 + // Ask to send crash report. Yes/No dialog. } LLCrashLoggerLinux::LLCrashLoggerLinux(void) diff --git a/indra/llappearance/llpolymesh.cpp b/indra/llappearance/llpolymesh.cpp index 97f9ca68b6..719381b4fc 100644 --- a/indra/llappearance/llpolymesh.cpp +++ b/indra/llappearance/llpolymesh.cpp @@ -981,7 +981,7 @@ void LLPolyMesh::initializeForMorph() LLVector4a::memcpyNonAliased16((F32*) mScaledNormals, (F32*) mSharedData->mBaseNormals, sizeof(LLVector4a) * mSharedData->mNumVertices); LLVector4a::memcpyNonAliased16((F32*) mBinormals, (F32*) mSharedData->mBaseNormals, sizeof(LLVector4a) * mSharedData->mNumVertices); LLVector4a::memcpyNonAliased16((F32*) mScaledBinormals, (F32*) mSharedData->mBaseNormals, sizeof(LLVector4a) * mSharedData->mNumVertices); - LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) mSharedData->mTexCoords, sizeof(LLVector2) * (mSharedData->mNumVertices + mSharedData->mNumVertices%2)); + memcpy((F32*) mTexCoords, (F32*) mSharedData->mTexCoords, sizeof(LLVector2) * (mSharedData->mNumVertices)); // allocated in LLPolyMeshSharedData::allocateVertexData for (S32 i = 0; i < mSharedData->mNumVertices; ++i) { diff --git a/indra/llcommon/always_return.h b/indra/llcommon/always_return.h index a206471da5..b99eb49096 100644 --- a/indra/llcommon/always_return.h +++ b/indra/llcommon/always_return.h @@ -79,6 +79,22 @@ namespace LL DESIRED mDefault; }; + // specialize for AlwaysReturn<void> + template <> + struct AlwaysReturn<void> + { + public: + AlwaysReturn() {} + + // callable returns a type not convertible to DESIRED, return default + template <typename CALLABLE, typename... ARGS> + void operator()(CALLABLE&& callable, ARGS&&... args) + { + // discard whatever callable(args) returns + std::forward<CALLABLE>(callable)(std::forward<ARGS>(args)...); + } + }; + /** * always_return<T>(some_function, some_args...) calls * some_function(some_args...). It is guaranteed to return a value of type diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 1539b48bd3..3fe7e09e7e 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -140,7 +140,7 @@ LLCoros::LLCoros(): // Previously we used // boost::context::guarded_stack_allocator::default_stacksize(); // empirically this is insufficient. - mStackSize(1024*1024), + mStackSize(512*1024), // mCurrent does NOT own the current CoroData instance -- it simply // points to it. So initialize it with a no-op deleter. mCurrent{ [](CoroData*){} } @@ -172,7 +172,7 @@ void LLCoros::cleanupSingleton() // don't use llcoro::suspend() because that module depends // on this one // This will yield current(main) thread and will let active - // corutines run once + // coroutines run once boost::this_fiber::yield(); } printActiveCoroutines("after pumping"); @@ -303,55 +303,6 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl return name; } -namespace -{ - -#if LL_WINDOWS - -static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific - -U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) -{ - if (code == STATUS_MSC_EXCEPTION) - { - // C++ exception, go on - return EXCEPTION_CONTINUE_SEARCH; - } - else - { - // handle it - return EXCEPTION_EXECUTE_HANDLER; - } -} - -void sehandle(const LLCoros::callable_t& callable) -{ - __try - { - callable(); - } - __except (exception_filter(GetExceptionCode(), GetExceptionInformation())) - { - // convert to C++ styled exception - // Note: it might be better to use _se_set_translator - // if you want exception to inherit full callstack - char integer_string[512]; - sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode()); - throw std::exception(integer_string); - } -} - -#else // ! LL_WINDOWS - -inline void sehandle(const LLCoros::callable_t& callable) -{ - callable(); -} - -#endif // ! LL_WINDOWS - -} // anonymous namespace - // Top-level wrapper around caller's coroutine callable. // Normally we like to pass strings and such by const reference -- but in this // case, we WANT to copy both the name and the callable to our local stack! @@ -365,7 +316,7 @@ void LLCoros::toplevel(std::string name, callable_t callable) // run the code the caller actually wants in the coroutine try { - sehandle(callable); + LL::seh::catcher(callable); } catch (const Stop& exc) { diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 6176ce0d1d..7353a36c5f 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -95,6 +95,11 @@ const int LL_ERR_NOERR = 0; #define LL_STATIC_ASSERT(func, msg) static_assert(func, msg) #define LL_BAD_TEMPLATE_INSTANTIATION(type, msg) static_assert(false, msg) #else +#if LL_LINUX +// We need access to raise and SIGSEGV +#include <signal.h> +#endif + #define LL_STATIC_ASSERT(func, msg) BOOST_STATIC_ASSERT(func) #define LL_BAD_TEMPLATE_INSTANTIATION(type, msg) BOOST_STATIC_ASSERT(sizeof(type) != 0 && false); #endif @@ -408,10 +413,16 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; #define LL_NEWLINE '\n' // Use this only in LL_ERRS or in a place that LL_ERRS may not be used + +#ifndef LL_LINUX #define LLERROR_CRASH \ { \ crashdriver([](int* ptr){ *ptr = 0; exit(*ptr); }); \ } +#else +// For Linux we just call raise and be done with it. No fighting the compiler to create a crashing code snippet. +#define LLERROR_CRASH raise(SIGSEGV ); +#endif #define LL_ENDL \ LLError::End(); \ diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index c0154a569f..107fdc2b2d 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -15,7 +15,12 @@ #include "llexception.h" // STL headers // std headers +#include <iomanip> +#include <sstream> #include <typeinfo> +#if LL_WINDOWS +#include <excpt.h> +#endif // LL_WINDOWS // external library headers #include <boost/exception/diagnostic_information.hpp> #include <boost/exception/error_info.hpp> @@ -29,7 +34,6 @@ // On Windows, header-only implementation causes macro collisions -- use // prebuilt library #define BOOST_STACKTRACE_LINK -#include <excpt.h> #endif // LL_WINDOWS #include <boost/stacktrace.hpp> @@ -94,25 +98,47 @@ void annotate_exception_(boost::exception& exc) // For windows SEH exception handling we sometimes need a filter that will // separate C++ exceptions from C SEH exceptions -static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific +static constexpr U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific +static constexpr U32 STATUS_STACK_FULL = 0xC00000FD; -U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) +void LL::seh::fill_stacktrace(std::string& stacktrace, U32 code) { - const auto stack = to_string(boost::stacktrace::stacktrace()); - LL_WARNS() << "SEH Exception handled (that probably shouldn't be): Code " << code - << "\n Stack trace: \n" - << stack << LL_ENDL; + // Sadly, despite its diagnostic importance, trying to capture a + // stacktrace when the stack is already blown only terminates us faster. + if (code == STATUS_STACK_FULL) + { + stacktrace = "(stack overflow, no traceback)"; + } + else + { + stacktrace = to_string(boost::stacktrace::stacktrace()); + } +} +U32 LL::seh::common_filter(U32 code, struct _EXCEPTION_POINTERS*) +{ if (code == STATUS_MSC_EXCEPTION) { - // C++ exception, go on + // C++ exception, don't stop at this handler return EXCEPTION_CONTINUE_SEARCH; } else { - // handle it + // This is a non-C++ exception, e.g. hardware check. + // Pass control into the handler block. return EXCEPTION_EXECUTE_HANDLER; } } +void LL::seh::rethrow(U32 code, const std::string& stacktrace) +{ + std::ostringstream out; + out << "Windows exception 0x" << std::hex << code; + if (! stacktrace.empty()) + { + out << '\n' << stacktrace; + } + LLTHROW(Windows_SEH_exception(out.str())); +} + #endif //LL_WINDOWS diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index 68e609444e..f58a553eb3 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -12,6 +12,7 @@ #if ! defined(LL_LLEXCEPTION_H) #define LL_LLEXCEPTION_H +#include "always_return.h" #include <stdexcept> #include <boost/exception/exception.hpp> #include <boost/throw_exception.hpp> @@ -102,14 +103,115 @@ void crash_on_unhandled_exception_(const char*, int, const char*, const std::str log_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT) void log_unhandled_exception_(const char*, int, const char*, const std::string&); +/***************************************************************************** +* Structured Exception Handling +*****************************************************************************/ +// this is used in platform-generic code -- define outside #if LL_WINDOWS +struct Windows_SEH_exception: public LLException +{ + Windows_SEH_exception(const std::string& what): LLException(what) {} +}; + +namespace LL +{ +namespace seh +{ + +#if LL_WINDOWS //------------------------------------------------------------- + +void fill_stacktrace(std::string& stacktrace, U32 code); + +// wrapper around caller's U32 filter(U32 code, struct _EXCEPTION_POINTERS*) +// filter function: capture a stacktrace, if possible, before forwarding the +// call to the caller's filter() function +template <typename FILTER> +U32 filter_(std::string& stacktrace, FILTER&& filter, + U32 code, struct _EXCEPTION_POINTERS* exptrs) +{ + // By the time the handler gets control, the stack has been unwound, + // so report the stack trace now at filter() time. + fill_stacktrace(stacktrace, code); + return std::forward<FILTER>(filter)(code, exptrs); +} + +template <typename TRYCODE, typename FILTER, typename HANDLER> +auto catcher_inner(std::string& stacktrace, + TRYCODE&& trycode, FILTER&& filter, HANDLER&& handler) +{ + __try + { + return std::forward<TRYCODE>(trycode)(); + } + __except (filter_(stacktrace, + std::forward<FILTER>(filter), + GetExceptionCode(), GetExceptionInformation())) + { + return always_return<decltype(trycode())>( + std::forward<HANDLER>(handler), GetExceptionCode(), stacktrace); + } +} + +// triadic variant specifies try(), filter(U32, struct _EXCEPTION_POINTERS*), +// handler(U32, const std::string& stacktrace) +// stacktrace may or may not be available +template <typename TRYCODE, typename FILTER, typename HANDLER> +auto catcher(TRYCODE&& trycode, FILTER&& filter, HANDLER&& handler) +{ + // Construct and destroy this stacktrace string in the outer function + // because we can't do either in the function with __try/__except. + std::string stacktrace; + return catcher_inner(stacktrace, + std::forward<TRYCODE>(trycode), + std::forward<FILTER>(filter), + std::forward<HANDLER>(handler)); +} -#if LL_WINDOWS +// common_filter() handles the typical case in which we want our handler +// clause to handle only Structured Exceptions rather than explicitly-thrown +// C++ exceptions +U32 common_filter(U32 code, struct _EXCEPTION_POINTERS*); + +// dyadic variant specifies try(), handler(U32, stacktrace), assumes common_filter() +template <typename TRYCODE, typename HANDLER> +auto catcher(TRYCODE&& trycode, HANDLER&& handler) +{ + return catcher(std::forward<TRYCODE>(trycode), + common_filter, + std::forward<HANDLER>(handler)); +} + +// monadic variant specifies try(), assumes default filter and handler +template <typename TRYCODE> +auto catcher(TRYCODE&& trycode) +{ + return catcher(std::forward<TRYCODE>(trycode), rethrow); +} + +[[noreturn]] void rethrow(U32 code, const std::string& stacktrace); + +#else // not LL_WINDOWS ----------------------------------------------------- + +template <typename TRYCODE, typename FILTER, typename HANDLER> +auto catcher(TRYCODE&& trycode, FILTER&&, HANDLER&&) +{ + return std::forward<TRYCODE>(trycode)(); +} + +template <typename TRYCODE, typename HANDLER> +auto catcher(TRYCODE&& trycode, HANDLER&&) +{ + return std::forward<TRYCODE>(trycode)(); +} + +template <typename TRYCODE> +auto catcher(TRYCODE&& trycode) +{ + return std::forward<TRYCODE>(trycode)(); +} -// SEH exception filtering for use in __try __except -// Separates C++ exceptions from C SEH exceptions -// Todo: might be good idea to do some kind of seh_to_msc_wrapper(function, ARGS&&); -U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop); +#endif // not LL_WINDOWS ----------------------------------------------------- -#endif //LL_WINDOWS +} // namespace LL::seh +} // namespace LL #endif /* ! defined(LL_LLEXCEPTION_H) */ diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index 9d53b9be35..3a05407dd0 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -793,19 +793,55 @@ private: }; #elif LL_LINUX + +// *NOTE:Mani - eww, macros! srry. +#define LLPI_SET_INFO_STRING(llpi_id, cpuinfo_id) \ + if (!cpuinfo[cpuinfo_id].empty()) \ + { setInfo(llpi_id, cpuinfo[cpuinfo_id]);} + +#define LLPI_SET_INFO_INT(llpi_id, cpuinfo_id) \ + {\ + S32 result; \ + if (!cpuinfo[cpuinfo_id].empty() \ + && LLStringUtil::convertToS32(cpuinfo[cpuinfo_id], result)) \ + { setInfo(llpi_id, result);} \ + } + const char CPUINFO_FILE[] = "/proc/cpuinfo"; -class LLProcessorInfoLinuxImpl : public LLProcessorInfoImpl -{ +class LLProcessorInfoLinuxImpl : public LLProcessorInfoImpl { public: - LLProcessorInfoLinuxImpl() - { + LLProcessorInfoLinuxImpl() { get_proc_cpuinfo(); } virtual ~LLProcessorInfoLinuxImpl() {} + private: + F64 getCPUMaxMHZ() + { + // Nicky: We just look into cpu0. In theory we could iterate over all cores + // "/sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_max_freq" + // But those should not fluctuate that much? + std::ifstream fIn { "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq" }; + + if( !fIn.is_open() ) + return 0.0; + + std::string strLine; + fIn >> strLine; + if( strLine.empty() ) + return 0.0l; + + F64 mhz {}; + if( !LLStringUtil::convertToF64(strLine, mhz ) ) + return 0.0; + + mhz = mhz / 1000.0; + return mhz; + } + void get_proc_cpuinfo() { std::map< std::string, std::string > cpuinfo; @@ -834,30 +870,23 @@ private: std::string llinename(linename); LLStringUtil::toLower(llinename); std::string lineval( spacespot + 1, nlspot ); - cpuinfo[ llinename ] = lineval; + cpuinfo[ llinename ] = lineval; } fclose(cpuinfo_fp); } # if LL_X86 -// *NOTE:Mani - eww, macros! srry. -#define LLPI_SET_INFO_STRING(llpi_id, cpuinfo_id) \ - if (!cpuinfo[cpuinfo_id].empty()) \ - { setInfo(llpi_id, cpuinfo[cpuinfo_id]);} - -#define LLPI_SET_INFO_INT(llpi_id, cpuinfo_id) \ - {\ - S32 result; \ - if (!cpuinfo[cpuinfo_id].empty() \ - && LLStringUtil::convertToS32(cpuinfo[cpuinfo_id], result)) \ - { setInfo(llpi_id, result);} \ + F64 mhzFromSys = getCPUMaxMHZ(); + F64 mhzFromProc {}; + if( !LLStringUtil::convertToF64(cpuinfo["cpu mhz"], mhzFromProc ) ) + mhzFromProc = 0.0; + if (mhzFromSys > 1.0 && mhzFromSys > mhzFromProc ) + { + setInfo( eFrequency, mhzFromSys ); } - - F64 mhz; - if (LLStringUtil::convertToF64(cpuinfo["cpu mhz"], mhz) - && 200.0 < mhz && mhz < 10000.0) + else if ( 200.0 < mhzFromProc && mhzFromProc < 10000.0) { - setInfo(eFrequency,(F64)(mhz)); + setInfo(eFrequency,(F64)(mhzFromProc)); } LLPI_SET_INFO_STRING(eBrandName, "model name"); @@ -867,7 +896,7 @@ private: LLPI_SET_INFO_INT(eModel, "model"); - S32 family; + S32 family{}; if (!cpuinfo["cpu family"].empty() && LLStringUtil::convertToS32(cpuinfo["cpu family"], family)) { diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp index 12f67208c1..353a924e52 100644 --- a/indra/llcommon/llsdutil.cpp +++ b/indra/llcommon/llsdutil.cpp @@ -161,7 +161,7 @@ LLSD ll_binary_from_string(const LLSD& sd) char* ll_print_sd(const LLSD& sd) { const U32 bufferSize = 10 * 1024; - static char buffer[bufferSize]; + static char buffer[bufferSize + 1]; std::ostringstream stream; //stream.rdbuf()->pubsetbuf(buffer, bufferSize); stream << LLSDOStreamer<LLSDXMLFormatter>(sd); @@ -183,7 +183,7 @@ char* ll_pretty_print_sd_ptr(const LLSD* sd) char* ll_pretty_print_sd(const LLSD& sd) { const U32 bufferSize = 100 * 1024; - static char buffer[bufferSize]; + static char buffer[bufferSize + 1]; std::ostringstream stream; //stream.rdbuf()->pubsetbuf(buffer, bufferSize); stream << LLSDOStreamer<LLSDXMLFormatter>(sd, LLSDFormatter::OPTIONS_PRETTY); diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 79625ad9f8..06b1855785 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -504,57 +504,46 @@ const S32 LLOSInfo::getOSBitness() const return mOSBitness; } -//static -U32 LLOSInfo::getProcessVirtualSizeKB() -{ - U32 virtual_size = 0; -#if LL_LINUX -# define STATUS_SIZE 2048 - LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb"); - if (status_filep) - { - S32 numRead = 0; - char buff[STATUS_SIZE]; /* Flawfinder: ignore */ +namespace { - size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep); - buff[nbytes] = '\0'; + U32 readFromProcStat( std::string entryName ) + { + U32 val{}; +#if LL_LINUX + constexpr U32 STATUS_SIZE = 2048; - // All these guys return numbers in KB - char *memp = strstr(buff, "VmSize:"); - if (memp) + LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb"); + if (status_filep) { - numRead += sscanf(memp, "%*s %u", &virtual_size); + char buff[STATUS_SIZE]; /* Flawfinder: ignore */ + + size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep); + buff[nbytes] = '\0'; + + // All these guys return numbers in KB + char *memp = strstr(buff, entryName.c_str()); + if (memp) + { + (void) sscanf(memp, "%*s %u", &val); + } + fclose(status_filep); } - fclose(status_filep); - } #endif - return virtual_size; + return val; + } + } //static -U32 LLOSInfo::getProcessResidentSizeKB() +U32 LLOSInfo::getProcessVirtualSizeKB() { - U32 resident_size = 0; -#if LL_LINUX - LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb"); - if (status_filep != NULL) - { - S32 numRead = 0; - char buff[STATUS_SIZE]; /* Flawfinder: ignore */ - - size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep); - buff[nbytes] = '\0'; + return readFromProcStat( "VmSize:" ); +} - // All these guys return numbers in KB - char *memp = strstr(buff, "VmRSS:"); - if (memp) - { - numRead += sscanf(memp, "%*s %u", &resident_size); - } - fclose(status_filep); - } -#endif - return resident_size; +//static +U32 LLOSInfo::getProcessResidentSizeKB() +{ + return readFromProcStat( "VmRSS:" ); } //static diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp index fa48bcdefd..3fb25b4cef 100644 --- a/indra/llcommon/tests/llleap_test.cpp +++ b/indra/llcommon/tests/llleap_test.cpp @@ -35,7 +35,7 @@ // causes Windows abdominal pain such that it later fails code-signing in some // mysterious way. Entirely suppressing these LLLeap tests pushes the failure // rate MUCH lower. Can we re-enable them with a smaller data size on Windows? -const size_t BUFFERED_LENGTH = 100*1024; +const size_t BUFFERED_LENGTH = 1023*1024; #else // not Windows const size_t BUFFERED_LENGTH = 1023*1024; // try wrangling just under a megabyte of data diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp index b18712b8e9..b68c63a15f 100644 --- a/indra/llcommon/tests/llstring_test.cpp +++ b/indra/llcommon/tests/llstring_test.cpp @@ -377,7 +377,7 @@ namespace tut { F32 value; std::string str_val("2147483647"); //0x7FFFFFFF - ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647); + ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647.f); str_val = "0"; ensure("2: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 0); @@ -399,7 +399,7 @@ namespace tut { F64 value; std::string str_val("9223372036854775807"); //0x7FFFFFFFFFFFFFFF - ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807LL); + ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807.); str_val = "0"; ensure("2: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 0.0F); diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 5650c4c8ba..8b0f8c2e4c 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -10,7 +10,6 @@ include(ZLIBNG) include(LLCoreHttp) include(LLAddBuildTest) include(LLCommon) -include(Tut) include(bugsplat) set(llcorehttp_SOURCE_FILES diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp index b2bd3186a0..4c1594ec98 100644 --- a/indra/llimagej2coj/llimagej2coj.cpp +++ b/indra/llimagej2coj/llimagej2coj.cpp @@ -576,7 +576,6 @@ public: for (S32 c = 0; c < numcomps; c++) { cmptparm[c].prec = 8; - cmptparm[c].bpp = 8; cmptparm[c].sgnd = 0; cmptparm[c].dx = parameters.subsampling_dx; cmptparm[c].dy = parameters.subsampling_dy; diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h index ea71752ebc..ec7f6a2cb6 100644 --- a/indra/llmath/llcalcparser.h +++ b/indra/llmath/llcalcparser.h @@ -131,14 +131,14 @@ struct LLCalcParser : grammar<LLCalcParser> power = unary_expr[power.value = arg1] >> - *('^' >> assert_syntax(unary_expr[power.value = phoenix::bind(&powf)(power.value, arg1)])) + *('^' >> assert_syntax(unary_expr[power.value = phoenix::bind(&LLCalcParser::_pow)(self, power.value, arg1)])) ; term = power[term.value = arg1] >> *(('*' >> assert_syntax(power[term.value *= arg1])) | ('/' >> assert_syntax(power[term.value /= arg1])) | - ('%' >> assert_syntax(power[term.value = phoenix::bind(&fmodf)(term.value, arg1)])) + ('%' >> assert_syntax(power[term.value = phoenix::bind(&LLCalcParser::_fmod)(self, term.value, arg1)])) ) ; @@ -177,6 +177,8 @@ private: F32 _floor(const F32& a) const { return (F32)llfloor(a); } F32 _ceil(const F32& a) const { return (F32)llceil(a); } F32 _atan2(const F32& a,const F32& b) const { return atan2(a,b); } + F32 _pow(const F32& a, const F32& b) const { return powf(a, b); } + F32 _fmod(const F32&a, const F32& b) const { return fmodf(a, b); } LLCalc::calc_map_t* mConstants; LLCalc::calc_map_t* mVariables; diff --git a/indra/llmessage/llnamevalue.cpp b/indra/llmessage/llnamevalue.cpp index 853ae7df82..05ea3f26a1 100644 --- a/indra/llmessage/llnamevalue.cpp +++ b/indra/llmessage/llnamevalue.cpp @@ -967,4 +967,3 @@ std::ostream& operator<<(std::ostream& s, const LLNameValue &a) } return s; } - diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp index 864e68998c..d713cb20d9 100644 --- a/indra/llmessage/llproxy.cpp +++ b/indra/llmessage/llproxy.cpp @@ -465,7 +465,7 @@ void LLProxy::applyProxySettings(CURL* handle) /** * @brief Send one TCP packet and receive one in return. * - * This operation is done synchronously with a 1000ms timeout. Therefore, it should not be used when a blocking + * This operation is done synchronously with a 100ms timeout. Therefore, it should not be used when a blocking * operation would impact the operation of the viewer. * * @param handle_ptr Pointer to a connected LLSocket of type STREAM_TCP. @@ -482,7 +482,7 @@ static apr_status_t tcp_blocking_handshake(LLSocket::ptr_t handle, char * dataou apr_size_t expected_len = outlen; - handle->setBlocking(1000); + handle->setBlocking(100000); // 100ms, 100000us. Should be sufficient for localhost, nearby network rv = apr_socket_send(apr_socket, dataout, &outlen); if (APR_SUCCESS != rv) @@ -498,8 +498,6 @@ static apr_status_t tcp_blocking_handshake(LLSocket::ptr_t handle, char * dataou rv = -1; } - ms_sleep(1); - if (APR_SUCCESS == rv) { expected_len = maxinlen; diff --git a/indra/llplugin/CMakeLists.txt b/indra/llplugin/CMakeLists.txt index 14a69afe6e..005426acde 100644 --- a/indra/llplugin/CMakeLists.txt +++ b/indra/llplugin/CMakeLists.txt @@ -31,14 +31,6 @@ set(llplugin_HEADER_FILES llpluginsharedmemory.h ) -if(NOT ADDRESS_SIZE EQUAL 32) - if(WINDOWS) - ##add_definitions(/FIXED:NO) - else(WINDOWS) # not windows therefore gcc LINUX and DARWIN - add_definitions(-fPIC) - endif(WINDOWS) -endif(NOT ADDRESS_SIZE EQUAL 32) - list(APPEND llplugin_SOURCE_FILES ${llplugin_HEADER_FILES}) add_library (llplugin ${llplugin_SOURCE_FILES}) diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index 8a356da93a..0527c76a2a 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -981,6 +981,15 @@ void LLPluginClassMedia::enableMediaPluginDebugging( bool enable ) sendMessage( message ); } +#if LL_LINUX +void LLPluginClassMedia::enablePipeWireVolumeCatcher( bool enable ) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "enable_pipewire_volume_catcher"); + message.setValueBoolean( "enable", enable ); + sendMessage( message ); +} +#endif + void LLPluginClassMedia::setTarget(const std::string &target) { mTarget = target; diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index d74b790d8f..5d2f3bbb79 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -135,6 +135,10 @@ public: // Text may be unicode (utf8 encoded) bool textInput(const std::string &text, MASK modifiers, LLSD native_key_data); +#if LL_LINUX + void enablePipeWireVolumeCatcher( bool enable ); +#endif + static std::string sOIDcookieUrl; static std::string sOIDcookieName; static std::string sOIDcookieValue; diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index 3e84eeffc2..ac17a43217 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -1120,19 +1120,17 @@ bool LLDAELoader::OpenFile(const std::string& filename) if (skin) { - domGeometry* geom = daeSafeCast<domGeometry>(skin->getSource().getElement()); - - if (geom) + if (domGeometry* geom = daeSafeCast<domGeometry>(skin->getSource().getElement())) { - domMesh* mesh = geom->getMesh(); - if (mesh) + if (domMesh* mesh = geom->getMesh()) { - std::vector< LLPointer< LLModel > >::iterator i = mModelsMap[mesh].begin(); - while (i != mModelsMap[mesh].end()) + dae_model_map::const_iterator it = mModelsMap.find(mesh); + if (it != mModelsMap.end()) { - LLPointer<LLModel> mdl = *i; - LLDAELoader::processDomModel(mdl, &dae, root, mesh, skin); - i++; + for (const LLPointer<LLModel>& model : it->second) + { + LLDAELoader::processDomModel(model, &dae, root, mesh, skin); + } } } } @@ -1302,6 +1300,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do } } else + { //Has one or more skeletons for (std::vector<domInstance_controller::domSkeleton*>::iterator skel_it = skeletons.begin(); skel_it != skeletons.end(); ++skel_it) @@ -1386,6 +1385,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do } }//got skeleton? } + } domSkin::domJoints* joints = skin->getJoints(); @@ -1686,7 +1686,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do materials[model->mMaterialList[i]] = LLImportMaterial(); } mScene[transformation].push_back(LLModelInstance(model, model->mLabel, transformation, materials)); - stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); + stretch_extents(model, transformation); } } @@ -2079,21 +2079,14 @@ void LLDAELoader::processElement( daeElement* element, bool& badElement, DAE* da mTransform.condition(); } - domInstance_geometry* instance_geo = daeSafeCast<domInstance_geometry>(element); - if (instance_geo) + if (domInstance_geometry* instance_geo = daeSafeCast<domInstance_geometry>(element)) { - domGeometry* geo = daeSafeCast<domGeometry>(instance_geo->getUrl().getElement()); - if (geo) + if (domGeometry* geo = daeSafeCast<domGeometry>(instance_geo->getUrl().getElement())) { - domMesh* mesh = daeSafeCast<domMesh>(geo->getDescendant(daeElement::matchType(domMesh::ID()))); - if (mesh) + if (domMesh* mesh = daeSafeCast<domMesh>(geo->getDescendant(daeElement::matchType(domMesh::ID())))) { - - std::vector< LLPointer< LLModel > >::iterator i = mModelsMap[mesh].begin(); - while (i != mModelsMap[mesh].end()) + for (LLModel* model : mModelsMap.find(mesh)->second) { - LLModel* model = *i; - LLMatrix4 transformation = mTransform; if (mTransform.determinant() < 0) @@ -2164,8 +2157,7 @@ void LLDAELoader::processElement( daeElement* element, bool& badElement, DAE* da } mScene[transformation].push_back(LLModelInstance(model, label, transformation, materials)); - stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); - i++; + stretch_extents(model, transformation); } } } diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 15087a7255..77992d93a4 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -91,19 +91,15 @@ std::string LLModel::getStatusString(U32 status) } -void LLModel::offsetMesh( const LLVector3& pivotPoint ) +void LLModel::offsetMesh(const LLVector3& pivotPoint) { - LLVector4a pivot( pivotPoint[VX], pivotPoint[VY], pivotPoint[VZ] ); + LLVector4a pivot(pivotPoint[VX], pivotPoint[VY], pivotPoint[VZ]); - for (std::vector<LLVolumeFace>::iterator faceIt = mVolumeFaces.begin(); faceIt != mVolumeFaces.end(); ) + for (LLVolumeFace& face : mVolumeFaces) { - std::vector<LLVolumeFace>:: iterator currentFaceIt = faceIt++; - LLVolumeFace& face = *currentFaceIt; - LLVector4a *pos = (LLVector4a*) face.mPositions; - - for (S32 i=0; i<face.mNumVertices; ++i ) + for (S32 i = 0; i < face.mNumVertices; ++i) { - pos[i].add( pivot ); + face.mPositions[i].add(pivot); } } } @@ -338,7 +334,7 @@ void LLModel::normalizeVolumeFaces() } } -void LLModel::getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out) +void LLModel::getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out) const { scale_out = mNormalizedScale; translation_out = mNormalizedTranslation; diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index b0cba2d655..bd2ac68bed 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -202,8 +202,8 @@ public: void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL); void remapVolumeFaces(); void optimizeVolumeFaces(); - void offsetMesh( const LLVector3& pivotPoint ); - void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out); + void offsetMesh(const LLVector3& pivotPoint); + void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out) const; LLVector3 getTransformedCenter(const LLMatrix4& mat); //reorder face list based on mMaterialList in this and reference so diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp index 9a194567d2..1a64e6227f 100644 --- a/indra/llprimitive/llmodelloader.cpp +++ b/indra/llprimitive/llmodelloader.cpp @@ -37,7 +37,7 @@ std::list<LLModelLoader*> LLModelLoader::sActiveLoaderList; -void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, bool& first_transform) +static void stretch_extents(const LLModel* model, const LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, bool& first_transform) { LLVector4a box[] = { @@ -59,7 +59,7 @@ void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4 center.setAdd(face.mExtents[0], face.mExtents[1]); center.mul(0.5f); LLVector4a size; - size.setSub(face.mExtents[1],face.mExtents[0]); + size.setSub(face.mExtents[1], face.mExtents[0]); size.mul(0.5f); for (U32 i = 0; i < 8; i++) @@ -85,19 +85,19 @@ void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4 } } -void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, bool& first_transform) +void LLModelLoader::stretch_extents(const LLModel* model, const LLMatrix4& mat) { LLVector4a mina, maxa; LLMatrix4a mata; mata.loadu(mat); - mina.load3(min.mV); - maxa.load3(max.mV); + mina.load3(mExtents[0].mV); + maxa.load3(mExtents[1].mV); - stretch_extents(model, mata, mina, maxa, first_transform); + ::stretch_extents(model, mata, mina, maxa, mFirstTransform); - min.set(mina.getF32ptr()); - max.set(maxa.getF32ptr()); + mExtents[0].set(mina.getF32ptr()); + mExtents[1].set(maxa.getF32ptr()); } //----------------------------------------------------------------------------- @@ -292,14 +292,7 @@ bool LLModelLoader::loadFromSLM(const std::string& filename) { if (idx >= model[lod].size()) { - if (model[lod].size()) - { - instance_list[i].mLOD[lod] = model[lod][0]; - } - else - { - instance_list[i].mLOD[lod] = NULL; - } + instance_list[i].mLOD[lod] = model[lod].front(); continue; } @@ -342,7 +335,7 @@ bool LLModelLoader::loadFromSLM(const std::string& filename) { LLModelInstance& cur_instance = instance_list[i]; mScene[cur_instance.mTransform].push_back(cur_instance); - stretch_extents(cur_instance.mModel, cur_instance.mTransform, mExtents[0], mExtents[1], mFirstTransform); + stretch_extents(cur_instance.mModel, cur_instance.mTransform); } setLoadState( DONE ); diff --git a/indra/llprimitive/llmodelloader.h b/indra/llprimitive/llmodelloader.h index 83bd2f5bbd..637dabe08a 100644 --- a/indra/llprimitive/llmodelloader.h +++ b/indra/llprimitive/llmodelloader.h @@ -34,13 +34,13 @@ class LLJoint; -typedef std::map<std::string, LLMatrix4> JointTransformMap; -typedef std::map<std::string, LLMatrix4>::iterator JointTransformMapIt; -typedef std::map<std::string, std::string> JointMap; -typedef std::deque<std::string> JointNameSet; +typedef std::map<std::string, LLMatrix4> JointTransformMap; +typedef std::map<std::string, LLMatrix4>::iterator JointTransformMapIt; +typedef std::map<std::string, std::string> JointMap; +typedef std::deque<std::string> JointNameSet; const S32 SLM_SUPPORTED_VERSION = 3; -const S32 NUM_LOD = 4; +const S32 NUM_LOD = 4; const U32 LEGACY_RIG_OK = 0; const U32 LEGACY_RIG_FLAG_TOO_MANY_JOINTS = 1; @@ -50,32 +50,32 @@ class LLModelLoader : public LLThread { public: - typedef std::map<std::string, LLImportMaterial> material_map; - typedef std::vector<LLPointer<LLModel > > model_list; - typedef std::vector<LLModelInstance> model_instance_list; - typedef std::map<LLMatrix4, model_instance_list > scene; + typedef std::map<std::string, LLImportMaterial> material_map; + typedef std::vector<LLPointer<LLModel>> model_list; + typedef std::vector<LLModelInstance> model_instance_list; + typedef std::map<LLMatrix4, model_instance_list> scene; // Callback with loaded model data and loaded LoD // - typedef boost::function<void (scene&,model_list&,S32,void*) > load_callback_t; + typedef boost::function<void (scene&, model_list&, S32, void*)> load_callback_t; // Function to provide joint lookup by name // (within preview avi skeleton, for example) // - typedef boost::function<LLJoint* (const std::string&,void*) > joint_lookup_func_t; + typedef boost::function<LLJoint* (const std::string&, void*)> joint_lookup_func_t; // Func to load and associate material with all it's textures, // returned value is the number of textures loaded // intentionally non-const so func can modify material to // store platform-specific data // - typedef boost::function<U32 (LLImportMaterial&,void*) > texture_load_func_t; + typedef boost::function<U32 (LLImportMaterial&, void*)> texture_load_func_t; // Callback to inform client of state changes // during loading process (errors will be reported // as state changes here as well) // - typedef boost::function<void (U32,void*) > state_callback_t; + typedef boost::function<void (U32, void*)> state_callback_t; typedef enum { @@ -136,7 +136,7 @@ public: JointNameSet& jointsFromNodes, JointMap& legalJointNamesMap, U32 maxJointsPerMesh); - virtual ~LLModelLoader() ; + virtual ~LLModelLoader(); virtual void setNoNormalize() { mNoNormalize = true; } virtual void setNoOptimize() { mNoOptimize = true; } @@ -156,13 +156,13 @@ public: bool loadFromSLM(const std::string& filename); void loadModelCallback(); - void loadTextures() ; //called in the main thread. + void loadTextures(); // called in the main thread. void setLoadState(U32 state); + void stretch_extents(const LLModel* model, const LLMatrix4& mat); - - S32 mNumOfFetchingTextures ; //updated in the main thread - bool areTexturesReady() { return !mNumOfFetchingTextures; } //called in the main thread. + S32 mNumOfFetchingTextures; // updated in the main thread + bool areTexturesReady() { return !mNumOfFetchingTextures; } // called in the main thread. bool verifyCount( int expected, int result ); @@ -212,10 +212,7 @@ protected: LLSD mWarningsArray; // preview floater will pull logs from here static std::list<LLModelLoader*> sActiveLoaderList; - static bool isAlive(LLModelLoader* loader) ; + static bool isAlive(LLModelLoader* loader); }; -class LLMatrix4a; -void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, bool& first_transform); -void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, bool& first_transform); #endif // LL_LLMODELLOADER_H diff --git a/indra/llprimitive/llprimtexturelist.cpp b/indra/llprimitive/llprimtexturelist.cpp index 68f3f5ffac..76322a9d29 100644 --- a/indra/llprimitive/llprimtexturelist.cpp +++ b/indra/llprimitive/llprimtexturelist.cpp @@ -137,14 +137,7 @@ S32 LLPrimTextureList::copyTexture(const U8 index, const LLTextureEntry& te) // we're changing an existing entry llassert(mEntryList[index]); delete (mEntryList[index]); - if (&te) - { - mEntryList[index] = te.newCopy(); - } - else - { - mEntryList[index] = LLPrimTextureList::newTextureEntry(); - } + mEntryList[index] = te.newCopy(); return TEM_CHANGE_TEXTURE; } diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp index 6128e03fa7..fa76669258 100644 --- a/indra/llrender/llfontfreetype.cpp +++ b/indra/llrender/llfontfreetype.cpp @@ -52,7 +52,9 @@ #include "llfontbitmapcache.h" #include "llgl.h" -#define ENABLE_OT_SVG_SUPPORT +#if !defined(LL_NO_OTSVG) + #define ENABLE_OT_SVG_SUPPORT +#endif FT_Render_Mode gFontRenderMode = FT_RENDER_MODE_NORMAL; @@ -87,7 +89,7 @@ LLFontManager::LLFontManager() FT_Done_FreeType(gFTLibrary); } -#ifdef ENABLE_OT_SVG_SUPPORT +#if defined(ENABLE_OT_SVG_SUPPORT) SVG_RendererHooks hooks = { LLFontFreeTypeSvgRenderer::OnInit, LLFontFreeTypeSvgRenderer::OnFree, @@ -483,8 +485,8 @@ LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch, EFontGlyphType glyph_type continue; } glyph_index = FT_Get_Char_Index(pair.first->mFTFace, wch); - if (glyph_index) - { + if (glyph_index) + { return addGlyphFromFont(pair.first, wch, glyph_index, glyph_type); } diff --git a/indra/llrender/llfontfreetypesvg.cpp b/indra/llrender/llfontfreetypesvg.cpp index 71f751329e..15251fe1b1 100644 --- a/indra/llrender/llfontfreetypesvg.cpp +++ b/indra/llrender/llfontfreetypesvg.cpp @@ -80,6 +80,7 @@ void LLFontFreeTypeSvgRenderer::OnDataFinalizer(void* objectp) //static FT_Error LLFontFreeTypeSvgRenderer::OnPresetGlypthSlot(FT_GlyphSlot glyph_slot, FT_Bool cache, FT_Pointer*) { +#ifndef LL_NO_OTSVG FT_SVG_Document document = static_cast<FT_SVG_Document>(glyph_slot->other); llassert(!glyph_slot->generic.data || !cache || glyph_slot->glyph_index == ((LLSvgRenderData*)glyph_slot->generic.data)->GlyphIndex); @@ -166,6 +167,9 @@ FT_Error LLFontFreeTypeSvgRenderer::OnPresetGlypthSlot(FT_GlyphSlot glyph_slot, } return FT_Err_Ok; +#else + return FT_Err_Unimplemented_Feature; +#endif } // static diff --git a/indra/llrender/llfontfreetypesvg.h b/indra/llrender/llfontfreetypesvg.h index 4170cab273..94b83d5fff 100644 --- a/indra/llrender/llfontfreetypesvg.h +++ b/indra/llrender/llfontfreetypesvg.h @@ -29,7 +29,11 @@ #include <ft2build.h> #include FT_TYPES_H #include FT_MODULE_H -#include FT_OTSVG_H +#ifdef FT_OTSVG_H + #include FT_OTSVG_H +#else + #define LL_NO_OTSVG +#endif // See https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html class LLFontFreeTypeSvgRenderer diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index 7959b3bb57..556b7b0e35 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -211,8 +211,6 @@ LLMatrix4 gGLObliqueProjectionInverse; std::list<LLGLUpdate*> LLGLUpdate::sGLQ; -#if (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS - #if LL_WINDOWS // WGL_ARB_create_context PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr; @@ -232,8 +230,6 @@ PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC wglBlitContextFramebufferAMD = n PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr; PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = nullptr; -#endif - // GL_VERSION_1_2 //PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements = nullptr; //PFNGLTEXIMAGE3DPROC glTexImage3D = nullptr; @@ -1386,6 +1382,9 @@ void LLGLManager::shutdownGL() void LLGLManager::initExtensions() { +#if LL_LINUX + glh_init_extensions(""); +#endif #if LL_DARWIN GLint num_extensions = 0; std::string all_extensions{""}; @@ -1416,10 +1415,9 @@ void LLGLManager::initExtensions() mInited = true; -#if (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS +#if LL_WINDOWS LL_DEBUGS("RenderInit") << "GL Probe: Getting symbols" << LL_ENDL; -#if LL_WINDOWS // WGL_AMD_gpu_association wglGetGPUIDsAMD = (PFNWGLGETGPUIDSAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetGPUIDsAMD"); wglGetGPUInfoAMD = (PFNWGLGETGPUINFOAMDPROC)GLH_EXT_GET_PROC_ADDRESS("wglGetGPUInfoAMD"); @@ -1437,8 +1435,6 @@ void LLGLManager::initExtensions() // WGL_ARB_create_context wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)GLH_EXT_GET_PROC_ADDRESS("wglCreateContextAttribsARB"); -#endif - // Load entire OpenGL API through GetProcAddress, leaving sections beyond mGLVersion unloaded @@ -2566,6 +2562,7 @@ void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor { return; } + LL_INFOS() << "GL: " << version << LL_ENDL; version_string->assign(version); @@ -2945,5 +2942,3 @@ extern "C" __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; } #endif - - diff --git a/indra/llrender/llglheaders.h b/indra/llrender/llglheaders.h index c5e1ff3e23..df23a9da1d 100644 --- a/indra/llrender/llglheaders.h +++ b/indra/llrender/llglheaders.h @@ -41,6 +41,22 @@ # include "GL/glh_extensions.h" # undef __APPLE__ +#elif LL_LINUX +#define GL_GLEXT_PROTOTYPES +#define GLX_GLEXT_PROTOTYPES + +#include "GL/gl.h" +#include "GL/glext.h" +#include "GL/glu.h" + +// The __APPLE__ kludge is to make glh_extensions.h not symbol-clash horribly +# define __APPLE__ +# include "GL/glh_extensions.h" +# undef __APPLE__ + +# include "GL/glx.h" +# include "GL/glxext.h" + #elif LL_WINDOWS //---------------------------------------------------------------------------- // LL_WINDOWS @@ -1029,6 +1045,25 @@ extern void glGetBufferPointervARB (GLenum, GLenum, GLvoid* *); #include <OpenGL/gl.h> +#elif LL_LINUX + +#define GL_GLEXT_PROTOTYPES +#define GLX_GLEXT_PROTOTYPES + +#include "GL/gl.h" +#include "GL/glu.h" +#include "GL/glext.h" +#include "GL/glx.h" + +// The __APPLE__ kludge is to make glh_extensions.h not symbol-clash horribly +# define __APPLE__ +# include "GL/glh_extensions.h" +# undef __APPLE__ + +// #include <X11/Xlib.h> +// #include <X11/Xutil.h> +#include "GL/glh_extensions.h" + #endif // LL_MESA / LL_WINDOWS / LL_DARWIN // Even when GL_ARB_depth_clamp is available in the driver, the (correct) diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index 7b1430c67c..9ebd6ef0b0 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -59,11 +59,12 @@ set(llwindow_LINK_LIBRARIES ll::uilibraries ll::SDL ) + # Libraries on which this library depends, needed for Linux builds # Sort by high-level to low-level if (LINUX) - list(APPEND viewer_SOURCE_FILES - llkeyboardsdl.cpp + list(APPEND viewer_SOURCE_FILES + llkeyboardsdl.cpp llwindowsdl.cpp ) list(APPEND viewer_HEADER_FILES @@ -83,7 +84,6 @@ if (LINUX) fontconfig # For FCInit and other FC* functions. ) endif (BUILD_HEADLESS) - endif (LINUX) if (DARWIN) @@ -179,7 +179,7 @@ endif (SDL_FOUND) target_link_libraries (llwindow ${llwindow_LINK_LIBRARIES}) target_include_directories(llwindow INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) - + if (DARWIN) include(CMakeFindFrameworks) find_library(CARBON_LIBRARY Carbon) diff --git a/indra/llwindow/llkeyboard.cpp b/indra/llwindow/llkeyboard.cpp index 33eebdadd1..a16c0a318a 100644 --- a/indra/llwindow/llkeyboard.cpp +++ b/indra/llwindow/llkeyboard.cpp @@ -41,7 +41,6 @@ std::map<KEY,std::string> LLKeyboard::sKeysToNames; std::map<std::string,KEY> LLKeyboard::sNamesToKeys; LLKeyStringTranslatorFunc* LLKeyboard::mStringTranslator = NULL; // Used for l10n + PC/Mac/Linux accelerator labeling - // // Class Implementation // @@ -195,12 +194,11 @@ void LLKeyboard::resetKeys() } -bool LLKeyboard::translateKey(const U16 os_key, KEY *out_key) +bool LLKeyboard::translateKey(const NATIVE_KEY_TYPE os_key, KEY *out_key) { - std::map<U16, KEY>::iterator iter; // Only translate keys in the map, ignore all other keys for now - iter = mTranslateKeyMap.find(os_key); + auto iter = mTranslateKeyMap.find(os_key); if (iter == mTranslateKeyMap.end()) { //LL_WARNS() << "Unknown virtual key " << os_key << LL_ENDL; @@ -214,11 +212,9 @@ bool LLKeyboard::translateKey(const U16 os_key, KEY *out_key) } } - -U16 LLKeyboard::inverseTranslateKey(const KEY translated_key) +LLKeyboard::NATIVE_KEY_TYPE LLKeyboard::inverseTranslateKey(const KEY translated_key) { - std::map<KEY, U16>::iterator iter; - iter = mInvTranslateKeyMap.find(translated_key); + auto iter = mInvTranslateKeyMap.find(translated_key); if (iter == mInvTranslateKeyMap.end()) { return 0; diff --git a/indra/llwindow/llkeyboard.h b/indra/llwindow/llkeyboard.h index 713eb7aec2..d3c35b1ed4 100644 --- a/indra/llwindow/llkeyboard.h +++ b/indra/llwindow/llkeyboard.h @@ -55,6 +55,11 @@ class LLWindowCallbacks; class LLKeyboard { public: +#ifndef LL_SDL + typedef U16 NATIVE_KEY_TYPE; +#else + typedef U32 NATIVE_KEY_TYPE; +#endif LLKeyboard(); virtual ~LLKeyboard(); @@ -67,14 +72,13 @@ public: bool getKeyDown(const KEY key) { return mKeyLevel[key]; } bool getKeyRepeated(const KEY key) { return mKeyRepeated[key]; } - bool translateKey(const U16 os_key, KEY *translated_key); - U16 inverseTranslateKey(const KEY translated_key); + bool translateKey(const NATIVE_KEY_TYPE os_key, KEY *translated_key); + NATIVE_KEY_TYPE inverseTranslateKey(const KEY translated_key); bool handleTranslatedKeyUp(KEY translated_key, U32 translated_mask); // Translated into "Linden" keycodes bool handleTranslatedKeyDown(KEY translated_key, U32 translated_mask); // Translated into "Linden" keycodes - - virtual bool handleKeyUp(const U16 key, MASK mask) = 0; - virtual bool handleKeyDown(const U16 key, MASK mask) = 0; + virtual bool handleKeyUp(const NATIVE_KEY_TYPE key, MASK mask) = 0; + virtual bool handleKeyDown(const NATIVE_KEY_TYPE key, MASK mask) = 0; #ifdef LL_DARWIN // We only actually use this for macOS. @@ -111,8 +115,8 @@ protected: void addKeyName(KEY key, const std::string& name); protected: - std::map<U16, KEY> mTranslateKeyMap; // Map of translations from OS keys to Linden KEYs - std::map<KEY, U16> mInvTranslateKeyMap; // Map of translations from Linden KEYs to OS keys + std::map<NATIVE_KEY_TYPE, KEY> mTranslateKeyMap; // Map of translations from OS keys to Linden KEYs + std::map<KEY, NATIVE_KEY_TYPE> mInvTranslateKeyMap; // Map of translations from Linden KEYs to OS keys LLWindowCallbacks *mCallbacks; LLTimer mKeyLevelTimer[KEY_COUNT]; // Time since level was set diff --git a/indra/llwindow/llkeyboardheadless.cpp b/indra/llwindow/llkeyboardheadless.cpp index 8669a5b41a..ad8e42a412 100644 --- a/indra/llwindow/llkeyboardheadless.cpp +++ b/indra/llwindow/llkeyboardheadless.cpp @@ -34,14 +34,6 @@ LLKeyboardHeadless::LLKeyboardHeadless() void LLKeyboardHeadless::resetMaskKeys() { } - -bool LLKeyboardHeadless::handleKeyDown(const U16 key, const U32 mask) -{ return false; } - - -bool LLKeyboardHeadless::handleKeyUp(const U16 key, const U32 mask) -{ return false; } - MASK LLKeyboardHeadless::currentMask(bool for_mouse_event) { return MASK_NONE; } diff --git a/indra/llwindow/llkeyboardheadless.h b/indra/llwindow/llkeyboardheadless.h index 2528f0e3f1..439abaf25b 100644 --- a/indra/llwindow/llkeyboardheadless.h +++ b/indra/llwindow/llkeyboardheadless.h @@ -35,8 +35,13 @@ public: LLKeyboardHeadless(); /*virtual*/ ~LLKeyboardHeadless() {}; - /*virtual*/ bool handleKeyUp(const U16 key, MASK mask); - /*virtual*/ bool handleKeyDown(const U16 key, MASK mask); +#ifndef LL_SDL + /*virtual*/ bool handleKeyUp(const U16 key, MASK mask) { return false; } + /*virtual*/ bool handleKeyDown(const U16 key, MASK mask) { return false; } +#else + /*virtual*/ bool handleKeyUp(const U32 key, MASK mask) { return false; } + /*virtual*/ bool handleKeyDown(const U32 key, MASK mask) { return false; } +#endif /*virtual*/ void resetMaskKeys(); /*virtual*/ MASK currentMask(bool for_mouse_event); /*virtual*/ void scanKeyboard(); diff --git a/indra/llwindow/llkeyboardsdl.cpp b/indra/llwindow/llkeyboardsdl.cpp index 97198f0cc0..543882fc8f 100644 --- a/indra/llwindow/llkeyboardsdl.cpp +++ b/indra/llwindow/llkeyboardsdl.cpp @@ -1,6 +1,5 @@ /** - * @file llkeyboardsdl.cpp - * @brief Handler for assignable key bindings + * @author This module has many fathers, and it shows. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code @@ -24,12 +23,11 @@ * $/LicenseInfo$ */ -#if LL_SDL - #include "linden_common.h" #include "llkeyboardsdl.h" #include "llwindowcallbacks.h" -#include "SDL/SDL.h" +#include "SDL2/SDL.h" +#include "SDL2/SDL_keycode.h" LLKeyboardSDL::LLKeyboardSDL() { @@ -40,6 +38,10 @@ LLKeyboardSDL::LLKeyboardSDL() // Virtual key mappings from SDL_keysym.h ... // SDL maps the letter keys to the ASCII you'd expect, but it's lowercase... + + // <FS:ND> Looks like we need to map those despite of SDL_TEXTINPUT handling most of this, but without + // the translation lower->upper here accelerators will not work. + U16 cur_char; for (cur_char = 'A'; cur_char <= 'Z'; cur_char++) { @@ -68,13 +70,12 @@ LLKeyboardSDL::LLKeyboardSDL() //mTranslateKeyMap[SDLK_KP3] = KEY_PAGE_DOWN; //mTranslateKeyMap[SDLK_KP0] = KEY_INSERT; - mTranslateKeyMap[SDLK_SPACE] = ' '; + mTranslateKeyMap[SDLK_SPACE] = ' '; // <FS:ND/> Those are handled by SDL2 via text input, do not map them mTranslateKeyMap[SDLK_RETURN] = KEY_RETURN; mTranslateKeyMap[SDLK_LEFT] = KEY_LEFT; mTranslateKeyMap[SDLK_RIGHT] = KEY_RIGHT; mTranslateKeyMap[SDLK_UP] = KEY_UP; mTranslateKeyMap[SDLK_DOWN] = KEY_DOWN; - mTranslateKeyMap[SDLK_ESCAPE] = KEY_ESCAPE; mTranslateKeyMap[SDLK_KP_ENTER] = KEY_RETURN; mTranslateKeyMap[SDLK_ESCAPE] = KEY_ESCAPE; mTranslateKeyMap[SDLK_BACKSPACE] = KEY_BACKSPACE; @@ -111,40 +112,39 @@ LLKeyboardSDL::LLKeyboardSDL() mTranslateKeyMap[SDLK_F10] = KEY_F10; mTranslateKeyMap[SDLK_F11] = KEY_F11; mTranslateKeyMap[SDLK_F12] = KEY_F12; - mTranslateKeyMap[SDLK_PLUS] = '='; - mTranslateKeyMap[SDLK_COMMA] = ','; - mTranslateKeyMap[SDLK_MINUS] = '-'; - mTranslateKeyMap[SDLK_PERIOD] = '.'; - mTranslateKeyMap[SDLK_BACKQUOTE] = '`'; - mTranslateKeyMap[SDLK_SLASH] = KEY_DIVIDE; - mTranslateKeyMap[SDLK_SEMICOLON] = ';'; - mTranslateKeyMap[SDLK_LEFTBRACKET] = '['; - mTranslateKeyMap[SDLK_BACKSLASH] = '\\'; - mTranslateKeyMap[SDLK_RIGHTBRACKET] = ']'; - mTranslateKeyMap[SDLK_QUOTE] = '\''; + mTranslateKeyMap[SDLK_PLUS] = '='; // <FS:ND/> Those are handled by SDL2 via text input, do not map them + mTranslateKeyMap[SDLK_COMMA] = ','; // <FS:ND/> Those are handled by SDL2 via text input, do not map them + mTranslateKeyMap[SDLK_MINUS] = '-'; // <FS:ND/> Those are handled by SDL2 via text input, do not map them + mTranslateKeyMap[SDLK_PERIOD] = '.'; // <FS:ND/> Those are handled by SDL2 via text input, do not map them + mTranslateKeyMap[SDLK_BACKQUOTE] = '`'; // <FS:ND/> Those are handled by SDL2 via text input, do not map them + mTranslateKeyMap[SDLK_SLASH] = KEY_DIVIDE; // <FS:ND/> Those are handled by SDL2 via text input, do not map them + mTranslateKeyMap[SDLK_SEMICOLON] = ';'; // <FS:ND/> Those are handled by SDL2 via text input, do not map them + mTranslateKeyMap[SDLK_LEFTBRACKET] = '['; // <FS:ND/> Those are handled by SDL2 via text input, do not map them + mTranslateKeyMap[SDLK_BACKSLASH] = '\\'; // <FS:ND/> Those are handled by SDL2 via text input, do not map them + mTranslateKeyMap[SDLK_RIGHTBRACKET] = ']'; // <FS:ND/> Those are handled by SDL2 via text input, do not map them + mTranslateKeyMap[SDLK_QUOTE] = '\''; // <FS:ND/> Those are handled by SDL2 via text input, do not map them // Build inverse map - std::map<U16, KEY>::iterator iter; - for (iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++) + for (auto iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++) { mInvTranslateKeyMap[iter->second] = iter->first; } // numpad map - mTranslateNumpadMap[SDLK_KP0] = KEY_PAD_INS; - mTranslateNumpadMap[SDLK_KP1] = KEY_PAD_END; - mTranslateNumpadMap[SDLK_KP2] = KEY_PAD_DOWN; - mTranslateNumpadMap[SDLK_KP3] = KEY_PAD_PGDN; - mTranslateNumpadMap[SDLK_KP4] = KEY_PAD_LEFT; - mTranslateNumpadMap[SDLK_KP5] = KEY_PAD_CENTER; - mTranslateNumpadMap[SDLK_KP6] = KEY_PAD_RIGHT; - mTranslateNumpadMap[SDLK_KP7] = KEY_PAD_HOME; - mTranslateNumpadMap[SDLK_KP8] = KEY_PAD_UP; - mTranslateNumpadMap[SDLK_KP9] = KEY_PAD_PGUP; + mTranslateNumpadMap[SDLK_KP_0] = KEY_PAD_INS; + mTranslateNumpadMap[SDLK_KP_1] = KEY_PAD_END; + mTranslateNumpadMap[SDLK_KP_2] = KEY_PAD_DOWN; + mTranslateNumpadMap[SDLK_KP_3] = KEY_PAD_PGDN; + mTranslateNumpadMap[SDLK_KP_4] = KEY_PAD_LEFT; + mTranslateNumpadMap[SDLK_KP_5] = KEY_PAD_CENTER; + mTranslateNumpadMap[SDLK_KP_6] = KEY_PAD_RIGHT; + mTranslateNumpadMap[SDLK_KP_7] = KEY_PAD_HOME; + mTranslateNumpadMap[SDLK_KP_8] = KEY_PAD_UP; + mTranslateNumpadMap[SDLK_KP_9] = KEY_PAD_PGUP; mTranslateNumpadMap[SDLK_KP_PERIOD] = KEY_PAD_DEL; // build inverse numpad map - for (iter = mTranslateNumpadMap.begin(); + for (auto iter = mTranslateNumpadMap.begin(); iter != mTranslateNumpadMap.end(); iter++) { @@ -154,7 +154,7 @@ LLKeyboardSDL::LLKeyboardSDL() void LLKeyboardSDL::resetMaskKeys() { - SDLMod mask = SDL_GetModState(); + SDL_Keymod mask = SDL_GetModState(); // MBW -- XXX -- This mirrors the operation of the Windows version of resetMaskKeys(). // It looks a bit suspicious, as it won't correct for keys that have been released. @@ -201,37 +201,37 @@ MASK LLKeyboardSDL::updateModifiers(const U32 mask) } -static U16 adjustNativekeyFromUnhandledMask(const U16 key, const U32 mask) +static U32 adjustNativekeyFromUnhandledMask(const U32 key, const U32 mask) { // SDL doesn't automatically adjust the keysym according to // whether NUMLOCK is engaged, so we massage the keysym manually. - U16 rtn = key; + U32 rtn = key; if (!(mask & KMOD_NUM)) { switch (key) { - case SDLK_KP_PERIOD: rtn = SDLK_DELETE; break; - case SDLK_KP0: rtn = SDLK_INSERT; break; - case SDLK_KP1: rtn = SDLK_END; break; - case SDLK_KP2: rtn = SDLK_DOWN; break; - case SDLK_KP3: rtn = SDLK_PAGEDOWN; break; - case SDLK_KP4: rtn = SDLK_LEFT; break; - case SDLK_KP6: rtn = SDLK_RIGHT; break; - case SDLK_KP7: rtn = SDLK_HOME; break; - case SDLK_KP8: rtn = SDLK_UP; break; - case SDLK_KP9: rtn = SDLK_PAGEUP; break; + case SDLK_KP_PERIOD: rtn = SDLK_DELETE; break; + case SDLK_KP_0: rtn = SDLK_INSERT; break; + case SDLK_KP_1: rtn = SDLK_END; break; + case SDLK_KP_2: rtn = SDLK_DOWN; break; + case SDLK_KP_3: rtn = SDLK_PAGEDOWN; break; + case SDLK_KP_4: rtn = SDLK_LEFT; break; + case SDLK_KP_6: rtn = SDLK_RIGHT; break; + case SDLK_KP_7: rtn = SDLK_HOME; break; + case SDLK_KP_8: rtn = SDLK_UP; break; + case SDLK_KP_9: rtn = SDLK_PAGEUP; break; } } return rtn; } -bool LLKeyboardSDL::handleKeyDown(const U16 key, const U32 mask) +bool LLKeyboardSDL::handleKeyDown(const U32 key, const U32 mask) { - U16 adjusted_nativekey; + U32 adjusted_nativekey; KEY translated_key = 0; U32 translated_mask = MASK_NONE; - bool handled = false; + bool handled = false; adjusted_nativekey = adjustNativekeyFromUnhandledMask(key, mask); @@ -246,12 +246,12 @@ bool LLKeyboardSDL::handleKeyDown(const U16 key, const U32 mask) } -bool LLKeyboardSDL::handleKeyUp(const U16 key, const U32 mask) +bool LLKeyboardSDL::handleKeyUp(const U32 key, const U32 mask) { - U16 adjusted_nativekey; + U32 adjusted_nativekey; KEY translated_key = 0; U32 translated_mask = MASK_NONE; - bool handled = false; + bool handled = false; adjusted_nativekey = adjustNativekeyFromUnhandledMask(key, mask); @@ -268,16 +268,20 @@ bool LLKeyboardSDL::handleKeyUp(const U16 key, const U32 mask) MASK LLKeyboardSDL::currentMask(bool for_mouse_event) { MASK result = MASK_NONE; - SDLMod mask = SDL_GetModState(); + SDL_Keymod mask = SDL_GetModState(); - if (mask & KMOD_SHIFT) result |= MASK_SHIFT; - if (mask & KMOD_CTRL) result |= MASK_CONTROL; - if (mask & KMOD_ALT) result |= MASK_ALT; + if (mask & KMOD_SHIFT) + result |= MASK_SHIFT; + if (mask & KMOD_CTRL) + result |= MASK_CONTROL; + if (mask & KMOD_ALT) + result |= MASK_ALT; // For keyboard events, consider Meta keys equivalent to Control if (!for_mouse_event) { - if (mask & KMOD_META) result |= MASK_CONTROL; + if (mask & KMOD_GUI) + result |= MASK_CONTROL; } return result; @@ -310,7 +314,7 @@ void LLKeyboardSDL::scanKeyboard() } -bool LLKeyboardSDL::translateNumpadKey( const U16 os_key, KEY *translated_key) +bool LLKeyboardSDL::translateNumpadKey( const U32 os_key, KEY *translated_key) { return translateKey(os_key, translated_key); } @@ -320,5 +324,338 @@ U16 LLKeyboardSDL::inverseTranslateNumpadKey(const KEY translated_key) return inverseTranslateKey(translated_key); } -#endif +enum class WindowsVK : U32 +{ + VK_UNKNOWN = 0, + VK_CANCEL = 0x03, + VK_BACK = 0x08, + VK_TAB = 0x09, + VK_CLEAR = 0x0C, + VK_RETURN = 0x0D, + VK_SHIFT = 0x10, + VK_CONTROL = 0x11, + VK_MENU = 0x12, + VK_PAUSE = 0x13, + VK_CAPITAL = 0x14, + VK_KANA = 0x15, + VK_HANGUL = 0x15, + VK_JUNJA = 0x17, + VK_FINAL = 0x18, + VK_HANJA = 0x19, + VK_KANJI = 0x19, + VK_ESCAPE = 0x1B, + VK_CONVERT = 0x1C, + VK_NONCONVERT = 0x1D, + VK_ACCEPT = 0x1E, + VK_MODECHANGE = 0x1F, + VK_SPACE = 0x20, + VK_PRIOR = 0x21, + VK_NEXT = 0x22, + VK_END = 0x23, + VK_HOME = 0x24, + VK_LEFT = 0x25, + VK_UP = 0x26, + VK_RIGHT = 0x27, + VK_DOWN = 0x28, + VK_SELECT = 0x29, + VK_PRINT = 0x2A, + VK_EXECUTE = 0x2B, + VK_SNAPSHOT = 0x2C, + VK_INSERT = 0x2D, + VK_DELETE = 0x2E, + VK_HELP = 0x2F, + VK_0 = 0x30, + VK_1 = 0x31, + VK_2 = 0x32, + VK_3 = 0x33, + VK_4 = 0x34, + VK_5 = 0x35, + VK_6 = 0x36, + VK_7 = 0x37, + VK_8 = 0x38, + VK_9 = 0x39, + VK_A = 0x41, + VK_B = 0x42, + VK_C = 0x43, + VK_D = 0x44, + VK_E = 0x45, + VK_F = 0x46, + VK_G = 0x47, + VK_H = 0x48, + VK_I = 0x49, + VK_J = 0x4A, + VK_K = 0x4B, + VK_L = 0x4C, + VK_M = 0x4D, + VK_N = 0x4E, + VK_O = 0x4F, + VK_P = 0x50, + VK_Q = 0x51, + VK_R = 0x52, + VK_S = 0x53, + VK_T = 0x54, + VK_U = 0x55, + VK_V = 0x56, + VK_W = 0x57, + VK_X = 0x58, + VK_Y = 0x59, + VK_Z = 0x5A, + VK_LWIN = 0x5B, + VK_RWIN = 0x5C, + VK_APPS = 0x5D, + VK_SLEEP = 0x5F, + VK_NUMPAD0 = 0x60, + VK_NUMPAD1 = 0x61, + VK_NUMPAD2 = 0x62, + VK_NUMPAD3 = 0x63, + VK_NUMPAD4 = 0x64, + VK_NUMPAD5 = 0x65, + VK_NUMPAD6 = 0x66, + VK_NUMPAD7 = 0x67, + VK_NUMPAD8 = 0x68, + VK_NUMPAD9 = 0x69, + VK_MULTIPLY = 0x6A, + VK_ADD = 0x6B, + VK_SEPARATOR = 0x6C, + VK_SUBTRACT = 0x6D, + VK_DECIMAL = 0x6E, + VK_DIVIDE = 0x6F, + VK_F1 = 0x70, + VK_F2 = 0x71, + VK_F3 = 0x72, + VK_F4 = 0x73, + VK_F5 = 0x74, + VK_F6 = 0x75, + VK_F7 = 0x76, + VK_F8 = 0x77, + VK_F9 = 0x78, + VK_F10 = 0x79, + VK_F11 = 0x7A, + VK_F12 = 0x7B, + VK_F13 = 0x7C, + VK_F14 = 0x7D, + VK_F15 = 0x7E, + VK_F16 = 0x7F, + VK_F17 = 0x80, + VK_F18 = 0x81, + VK_F19 = 0x82, + VK_F20 = 0x83, + VK_F21 = 0x84, + VK_F22 = 0x85, + VK_F23 = 0x86, + VK_F24 = 0x87, + VK_NUMLOCK = 0x90, + VK_SCROLL = 0x91, + VK_LSHIFT = 0xA0, + VK_RSHIFT = 0xA1, + VK_LCONTROL = 0xA2, + VK_RCONTROL = 0xA3, + VK_LMENU = 0xA4, + VK_RMENU = 0xA5, + VK_BROWSER_BACK = 0xA6, + VK_BROWSER_FORWARD = 0xA7, + VK_BROWSER_REFRESH = 0xA8, + VK_BROWSER_STOP = 0xA9, + VK_BROWSER_SEARCH = 0xAA, + VK_BROWSER_FAVORITES = 0xAB, + VK_BROWSER_HOME = 0xAC, + VK_VOLUME_MUTE = 0xAD, + VK_VOLUME_DOWN = 0xAE, + VK_VOLUME_UP = 0xAF, + VK_MEDIA_NEXT_TRACK = 0xB0, + VK_MEDIA_PREV_TRACK = 0xB1, + VK_MEDIA_STOP = 0xB2, + VK_MEDIA_PLAY_PAUSE = 0xB3, + VK_MEDIA_LAUNCH_MAIL = 0xB4, + VK_MEDIA_LAUNCH_MEDIA_SELECT = 0xB5, + VK_MEDIA_LAUNCH_APP1 = 0xB6, + VK_MEDIA_LAUNCH_APP2 = 0xB7, + VK_OEM_1 = 0xBA, + VK_OEM_PLUS = 0xBB, + VK_OEM_COMMA = 0xBC, + VK_OEM_MINUS = 0xBD, + VK_OEM_PERIOD = 0xBE, + VK_OEM_2 = 0xBF, + VK_OEM_3 = 0xC0, + VK_OEM_4 = 0xDB, + VK_OEM_5 = 0xDC, + VK_OEM_6 = 0xDD, + VK_OEM_7 = 0xDE, + VK_OEM_8 = 0xDF, + VK_OEM_102 = 0xE2, + VK_PROCESSKEY = 0xE5, + VK_PACKET = 0xE7, + VK_ATTN = 0xF6, + VK_CRSEL = 0xF7, + VK_EXSEL = 0xF8, + VK_EREOF = 0xF9, + VK_PLAY = 0xFA, + VK_ZOOM = 0xFB, + VK_NONAME = 0xFC, + VK_PA1 = 0xFD, + VK_OEM_CLEAR = 0xFE, +}; + +std::map< U32, U32 > mSDL2_to_Win; +std::set< U32 > mIgnoreSDL2Keys; + +U32 LLKeyboardSDL::mapSDL2toWin( U32 aSymbol ) +{ + // <FS:ND> Map SDLK_ virtual keys to Windows VK_ virtual keys. + // Text is handled via unicode input (SDL_TEXTINPUT event) and does not need to be translated into VK_ values as those match already. + if( mSDL2_to_Win.empty() ) + { + + mSDL2_to_Win[ SDLK_BACKSPACE ] = (U32)WindowsVK::VK_BACK; + mSDL2_to_Win[ SDLK_TAB ] = (U32)WindowsVK::VK_TAB; + mSDL2_to_Win[ 12 ] = (U32)WindowsVK::VK_CLEAR; + mSDL2_to_Win[ SDLK_RETURN ] = (U32)WindowsVK::VK_RETURN; + mSDL2_to_Win[ 19 ] = (U32)WindowsVK::VK_PAUSE; + mSDL2_to_Win[ SDLK_ESCAPE ] = (U32)WindowsVK::VK_ESCAPE; + mSDL2_to_Win[ SDLK_SPACE ] = (U32)WindowsVK::VK_SPACE; + mSDL2_to_Win[ SDLK_QUOTE ] = (U32)WindowsVK::VK_OEM_7; + mSDL2_to_Win[ SDLK_COMMA ] = (U32)WindowsVK::VK_OEM_COMMA; + mSDL2_to_Win[ SDLK_MINUS ] = (U32)WindowsVK::VK_OEM_MINUS; + mSDL2_to_Win[ SDLK_PERIOD ] = (U32)WindowsVK::VK_OEM_PERIOD; + mSDL2_to_Win[ SDLK_SLASH ] = (U32)WindowsVK::VK_OEM_2; + + mSDL2_to_Win[ SDLK_0 ] = (U32)WindowsVK::VK_0; + mSDL2_to_Win[ SDLK_1 ] = (U32)WindowsVK::VK_1; + mSDL2_to_Win[ SDLK_2 ] = (U32)WindowsVK::VK_2; + mSDL2_to_Win[ SDLK_3 ] = (U32)WindowsVK::VK_3; + mSDL2_to_Win[ SDLK_4 ] = (U32)WindowsVK::VK_4; + mSDL2_to_Win[ SDLK_5 ] = (U32)WindowsVK::VK_5; + mSDL2_to_Win[ SDLK_6 ] = (U32)WindowsVK::VK_6; + mSDL2_to_Win[ SDLK_7 ] = (U32)WindowsVK::VK_7; + mSDL2_to_Win[ SDLK_8 ] = (U32)WindowsVK::VK_8; + mSDL2_to_Win[ SDLK_9 ] = (U32)WindowsVK::VK_9; + + mSDL2_to_Win[ SDLK_SEMICOLON ] = (U32)WindowsVK::VK_OEM_1; + mSDL2_to_Win[ SDLK_LESS ] = (U32)WindowsVK::VK_OEM_102; + mSDL2_to_Win[ SDLK_EQUALS ] = (U32)WindowsVK::VK_OEM_PLUS; + mSDL2_to_Win[ SDLK_KP_EQUALS ] = (U32)WindowsVK::VK_OEM_PLUS; + + mSDL2_to_Win[ SDLK_LEFTBRACKET ] = (U32)WindowsVK::VK_OEM_4; + mSDL2_to_Win[ SDLK_BACKSLASH ] = (U32)WindowsVK::VK_OEM_5; + mSDL2_to_Win[ SDLK_RIGHTBRACKET ] = (U32)WindowsVK::VK_OEM_6; + mSDL2_to_Win[ SDLK_BACKQUOTE ] = (U32)WindowsVK::VK_OEM_8; + + mSDL2_to_Win[ SDLK_a ] = (U32)WindowsVK::VK_A; + mSDL2_to_Win[ SDLK_b ] = (U32)WindowsVK::VK_B; + mSDL2_to_Win[ SDLK_c ] = (U32)WindowsVK::VK_C; + mSDL2_to_Win[ SDLK_d ] = (U32)WindowsVK::VK_D; + mSDL2_to_Win[ SDLK_e ] = (U32)WindowsVK::VK_E; + mSDL2_to_Win[ SDLK_f ] = (U32)WindowsVK::VK_F; + mSDL2_to_Win[ SDLK_g ] = (U32)WindowsVK::VK_G; + mSDL2_to_Win[ SDLK_h ] = (U32)WindowsVK::VK_H; + mSDL2_to_Win[ SDLK_i ] = (U32)WindowsVK::VK_I; + mSDL2_to_Win[ SDLK_j ] = (U32)WindowsVK::VK_J; + mSDL2_to_Win[ SDLK_k ] = (U32)WindowsVK::VK_K; + mSDL2_to_Win[ SDLK_l ] = (U32)WindowsVK::VK_L; + mSDL2_to_Win[ SDLK_m ] = (U32)WindowsVK::VK_M; + mSDL2_to_Win[ SDLK_n ] = (U32)WindowsVK::VK_N; + mSDL2_to_Win[ SDLK_o ] = (U32)WindowsVK::VK_O; + mSDL2_to_Win[ SDLK_p ] = (U32)WindowsVK::VK_P; + mSDL2_to_Win[ SDLK_q ] = (U32)WindowsVK::VK_Q; + mSDL2_to_Win[ SDLK_r ] = (U32)WindowsVK::VK_R; + mSDL2_to_Win[ SDLK_s ] = (U32)WindowsVK::VK_S; + mSDL2_to_Win[ SDLK_t ] = (U32)WindowsVK::VK_T; + mSDL2_to_Win[ SDLK_u ] = (U32)WindowsVK::VK_U; + mSDL2_to_Win[ SDLK_v ] = (U32)WindowsVK::VK_V; + mSDL2_to_Win[ SDLK_w ] = (U32)WindowsVK::VK_W; + mSDL2_to_Win[ SDLK_x ] = (U32)WindowsVK::VK_X; + mSDL2_to_Win[ SDLK_y ] = (U32)WindowsVK::VK_Y; + mSDL2_to_Win[ SDLK_z ] = (U32)WindowsVK::VK_Z; + + mSDL2_to_Win[ SDLK_DELETE ] = (U32)WindowsVK::VK_DELETE; + + + mSDL2_to_Win[ SDLK_NUMLOCKCLEAR ] = (U32)WindowsVK::VK_NUMLOCK; + mSDL2_to_Win[ SDLK_SCROLLLOCK ] = (U32)WindowsVK::VK_SCROLL; + + mSDL2_to_Win[ SDLK_HELP ] = (U32)WindowsVK::VK_HELP; + mSDL2_to_Win[ SDLK_PRINTSCREEN ] = (U32)WindowsVK::VK_SNAPSHOT; + mSDL2_to_Win[ SDLK_CANCEL ] = (U32)WindowsVK::VK_CANCEL; + mSDL2_to_Win[ SDLK_APPLICATION ] = (U32)WindowsVK::VK_APPS; + + mSDL2_to_Win[ SDLK_UNKNOWN ] = (U32)WindowsVK::VK_UNKNOWN; + mSDL2_to_Win[ SDLK_BACKSPACE ] = (U32)WindowsVK::VK_BACK; + mSDL2_to_Win[ SDLK_TAB ] = (U32)WindowsVK::VK_TAB; + mSDL2_to_Win[ SDLK_CLEAR ] = (U32)WindowsVK::VK_CLEAR; + mSDL2_to_Win[ SDLK_RETURN ] = (U32)WindowsVK::VK_RETURN; + mSDL2_to_Win[ SDLK_PAUSE ] = (U32)WindowsVK::VK_PAUSE; + mSDL2_to_Win[ SDLK_ESCAPE ] = (U32)WindowsVK::VK_ESCAPE; + mSDL2_to_Win[ SDLK_DELETE ] = (U32)WindowsVK::VK_DELETE; + + mSDL2_to_Win[ SDLK_KP_PERIOD ] = (U32)WindowsVK::VK_OEM_PERIOD; // VK_DECIMAL? + mSDL2_to_Win[ SDLK_KP_DIVIDE ] = (U32)WindowsVK::VK_DIVIDE; + mSDL2_to_Win[ SDLK_KP_MULTIPLY] = (U32)WindowsVK::VK_MULTIPLY; + mSDL2_to_Win[ SDLK_KP_MINUS ] = (U32)WindowsVK::VK_OEM_MINUS; // VK_SUBSTRACT? + mSDL2_to_Win[ SDLK_KP_PLUS ] = (U32)WindowsVK::VK_OEM_PLUS; // VK_ADD? + mSDL2_to_Win[ SDLK_KP_ENTER ] = (U32)WindowsVK::VK_RETURN; + mSDL2_to_Win[ SDLK_KP_0 ] = (U32)WindowsVK::VK_NUMPAD0; + mSDL2_to_Win[ SDLK_KP_1 ] = (U32)WindowsVK::VK_NUMPAD1; + mSDL2_to_Win[ SDLK_KP_2 ] = (U32)WindowsVK::VK_NUMPAD2; + mSDL2_to_Win[ SDLK_KP_3 ] = (U32)WindowsVK::VK_NUMPAD3; + mSDL2_to_Win[ SDLK_KP_4 ] = (U32)WindowsVK::VK_NUMPAD4; + mSDL2_to_Win[ SDLK_KP_5 ] = (U32)WindowsVK::VK_NUMPAD5; + mSDL2_to_Win[ SDLK_KP_6 ] = (U32)WindowsVK::VK_NUMPAD6; + mSDL2_to_Win[ SDLK_KP_7 ] = (U32)WindowsVK::VK_NUMPAD7; + mSDL2_to_Win[ SDLK_KP_8 ] = (U32)WindowsVK::VK_NUMPAD8; + mSDL2_to_Win[ SDLK_KP_9 ] = (U32)WindowsVK::VK_NUMPAD9; + + // ? + + mSDL2_to_Win[ SDLK_UP ] = (U32)WindowsVK::VK_UP; + mSDL2_to_Win[ SDLK_DOWN ] = (U32)WindowsVK::VK_DOWN; + mSDL2_to_Win[ SDLK_RIGHT ] = (U32)WindowsVK::VK_RIGHT; + mSDL2_to_Win[ SDLK_LEFT ] = (U32)WindowsVK::VK_LEFT; + mSDL2_to_Win[ SDLK_INSERT ] = (U32)WindowsVK::VK_INSERT; + mSDL2_to_Win[ SDLK_HOME ] = (U32)WindowsVK::VK_HOME; + mSDL2_to_Win[ SDLK_END ] = (U32)WindowsVK::VK_END; + mSDL2_to_Win[ SDLK_PAGEUP ] = (U32)WindowsVK::VK_PRIOR; + mSDL2_to_Win[ SDLK_PAGEDOWN ] = (U32)WindowsVK::VK_NEXT; + mSDL2_to_Win[ SDLK_F1 ] = (U32)WindowsVK::VK_F1; + mSDL2_to_Win[ SDLK_F2 ] = (U32)WindowsVK::VK_F2; + mSDL2_to_Win[ SDLK_F3 ] = (U32)WindowsVK::VK_F3; + mSDL2_to_Win[ SDLK_F4 ] = (U32)WindowsVK::VK_F4; + mSDL2_to_Win[ SDLK_F5 ] = (U32)WindowsVK::VK_F5; + mSDL2_to_Win[ SDLK_F6 ] = (U32)WindowsVK::VK_F6; + mSDL2_to_Win[ SDLK_F7 ] = (U32)WindowsVK::VK_F7; + mSDL2_to_Win[ SDLK_F8 ] = (U32)WindowsVK::VK_F8; + mSDL2_to_Win[ SDLK_F9 ] = (U32)WindowsVK::VK_F9; + mSDL2_to_Win[ SDLK_F10 ] = (U32)WindowsVK::VK_F10; + mSDL2_to_Win[ SDLK_F11 ] = (U32)WindowsVK::VK_F11; + mSDL2_to_Win[ SDLK_F12 ] = (U32)WindowsVK::VK_F12; + mSDL2_to_Win[ SDLK_F13 ] = (U32)WindowsVK::VK_F13; + mSDL2_to_Win[ SDLK_F14 ] = (U32)WindowsVK::VK_F14; + mSDL2_to_Win[ SDLK_F15 ] = (U32)WindowsVK::VK_F15; + mSDL2_to_Win[ SDLK_CAPSLOCK ] = (U32)WindowsVK::VK_CAPITAL; + mSDL2_to_Win[ SDLK_RSHIFT ] = (U32)WindowsVK::VK_SHIFT; + mSDL2_to_Win[ SDLK_LSHIFT ] = (U32)WindowsVK::VK_SHIFT; + mSDL2_to_Win[ SDLK_RCTRL ] = (U32)WindowsVK::VK_CONTROL; + mSDL2_to_Win[ SDLK_LCTRL ] = (U32)WindowsVK::VK_CONTROL; + mSDL2_to_Win[ SDLK_RALT ] = (U32)WindowsVK::VK_MENU; + mSDL2_to_Win[ SDLK_LALT ] = (U32)WindowsVK::VK_MENU; + + mSDL2_to_Win[ SDLK_MENU ] = (U32)WindowsVK::VK_MENU; + + // VK_MODECHANGE ? + // mSDL2_to_Win[ SDLK_MODE ] = (U32)WindowsVK::VK_MODE; + + // ? + // mSDL2_to_Win[ SDLK_SYSREQ ] = (U32)WindowsVK::VK_SYSREQ; + // mSDL2_to_Win[ SDLK_POWER ] = (U32)WindowsVK::VK_POWER; + // mSDL2_to_Win[ SDLK_UNDO ] = (U32)WindowsVK::VK_UNDO; + // mSDL2_to_Win[ SDLK_KP_EQUALS ] = (U32)WindowsVK::VK_EQUALS; + // mSDL2_to_Win[ 311 ] = (U32)WindowsVK::VK_LWIN; + // mSDL2_to_Win[ 312 ] = (U32)WindowsVK::VK_RWIN; + // mSDL2_to_Win[ SDLK_COLON ] = ? + } + auto itr = mSDL2_to_Win.find( aSymbol ); + if( itr != mSDL2_to_Win.end() ) + return itr->second; + + return aSymbol; +} diff --git a/indra/llwindow/llkeyboardsdl.h b/indra/llwindow/llkeyboardsdl.h index fd348b28f2..9ebff66865 100644 --- a/indra/llwindow/llkeyboardsdl.h +++ b/indra/llwindow/llkeyboardsdl.h @@ -1,8 +1,7 @@ /** - * @file llkeyboardsdl.h - * @brief Handler for assignable key bindings + * @author This module has many fathers, and it shows. * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * @@ -24,11 +23,11 @@ * $/LicenseInfo$ */ -#ifndef LL_LLKEYBOARDSDL_H -#define LL_LLKEYBOARDSDL_H +#ifndef LL_LLKEYBOARDSDL2_H +#define LL_LLKEYBOARDSDL2_H #include "llkeyboard.h" -#include "SDL/SDL.h" +#include "SDL2/SDL.h" class LLKeyboardSDL : public LLKeyboard { @@ -36,8 +35,8 @@ public: LLKeyboardSDL(); /*virtual*/ ~LLKeyboardSDL() {}; - /*virtual*/ bool handleKeyUp(const U16 key, MASK mask); - /*virtual*/ bool handleKeyDown(const U16 key, MASK mask); + /*virtual*/ bool handleKeyUp(const U32 key, MASK mask); + /*virtual*/ bool handleKeyDown(const U32 key, MASK mask); /*virtual*/ void resetMaskKeys(); /*virtual*/ MASK currentMask(bool for_mouse_event); /*virtual*/ void scanKeyboard(); @@ -45,11 +44,14 @@ public: protected: MASK updateModifiers(const U32 mask); void setModifierKeyLevel( KEY key, bool new_state ); - bool translateNumpadKey( const U16 os_key, KEY *translated_key ); + bool translateNumpadKey( const U32 os_key, KEY *translated_key ); U16 inverseTranslateNumpadKey(const KEY translated_key); private: - std::map<U16, KEY> mTranslateNumpadMap; // special map for translating OS keys to numpad keys - std::map<KEY, U16> mInvTranslateNumpadMap; // inverse of the above + std::map<U32, KEY> mTranslateNumpadMap; // special map for translating OS keys to numpad keys + std::map<KEY, U32> mInvTranslateNumpadMap; // inverse of the above + +public: + static U32 mapSDL2toWin( U32 ); }; #endif diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 70316ec36a..a2d812ae0d 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -163,18 +163,7 @@ HGLRC SafeCreateContext(HDC &hdc) GLuint SafeChoosePixelFormat(HDC &hdc, const PIXELFORMATDESCRIPTOR *ppfd) { - __try - { - return ChoosePixelFormat(hdc, ppfd); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - // convert to C++ styled exception - // C exception don't allow classes, so it's a regular char array - char integer_string[32]; - sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode()); - throw std::exception(integer_string); - } + return LL::seh::catcher([hdc, ppfd]{ return ChoosePixelFormat(hdc, ppfd); }); } //static diff --git a/indra/llxml/llxmltree.cpp b/indra/llxml/llxmltree.cpp index 0ace1baf2a..d66544d0f8 100644 --- a/indra/llxml/llxmltree.cpp +++ b/indra/llxml/llxmltree.cpp @@ -108,14 +108,17 @@ LLXmlTreeNode::LLXmlTreeNode( const std::string& name, LLXmlTreeNode* parent, LL LLXmlTreeNode::~LLXmlTreeNode() { - attribute_map_t::iterator iter; - for (iter=mAttributes.begin(); iter != mAttributes.end(); iter++) - delete iter->second; - for(LLXmlTreeNode* node : mChildren) - { - delete node; - } - mChildren.clear(); + for (auto& attr : mAttributes) + { + delete attr.second; + } + mAttributes.clear(); + + for (auto& child : mChildren) + { + delete child; + } + mChildren.clear(); } void LLXmlTreeNode::dump( const std::string& prefix ) diff --git a/indra/media_plugins/CMakeLists.txt b/indra/media_plugins/CMakeLists.txt index 972bb7dd2d..86c46cb476 100644 --- a/indra/media_plugins/CMakeLists.txt +++ b/indra/media_plugins/CMakeLists.txt @@ -3,8 +3,9 @@ add_subdirectory(base) if (LINUX) - #add_subdirectory(gstreamer010) + add_subdirectory(cef) add_subdirectory(example) + add_subdirectory(gstreamer10) endif (LINUX) if (DARWIN) diff --git a/indra/media_plugins/base/CMakeLists.txt b/indra/media_plugins/base/CMakeLists.txt index 64b6a4228d..5e635c6ca3 100644 --- a/indra/media_plugins/base/CMakeLists.txt +++ b/indra/media_plugins/base/CMakeLists.txt @@ -12,13 +12,6 @@ include(PluginAPI) ### media_plugin_base -if(NOT ADDRESS_SIZE EQUAL 32) - if(WINDOWS) - ##add_definitions(/FIXED:NO) - else(WINDOWS) # not windows therefore gcc LINUX and DARWIN - add_definitions(-fPIC) - endif(WINDOWS) -endif(NOT ADDRESS_SIZE EQUAL 32) set(media_plugin_base_SOURCE_FILES media_plugin_base.cpp diff --git a/indra/media_plugins/base/media_plugin_base.cpp b/indra/media_plugins/base/media_plugin_base.cpp index 545eee25a9..b21f29ac32 100644 --- a/indra/media_plugins/base/media_plugin_base.cpp +++ b/indra/media_plugins/base/media_plugin_base.cpp @@ -167,6 +167,56 @@ void MediaPluginBase::sendStatus() sendMessage(message); } +#if LL_LINUX + +size_t SymbolGrabber::registerSymbol( SymbolToGrab aSymbol ) +{ + gSymbolsToGrab.emplace_back(aSymbol); + return gSymbolsToGrab.size(); +} + +bool SymbolGrabber::grabSymbols(std::vector< std::string > const &aDSONames) +{ + std::cerr << "SYMBOLS: " << gSymbolsToGrab.size() << std::endl; + + if (sSymsGrabbed) + return true; + + //attempt to load the shared libraries + apr_pool_create(&sSymDSOMemoryPool, nullptr); + + for( std::vector< std::string >::const_iterator itr = aDSONames.begin(); itr != aDSONames.end(); ++itr ) + { + apr_dso_handle_t *pDSO(NULL); + std::string strDSO{ *itr }; + if( APR_SUCCESS == apr_dso_load( &pDSO, strDSO.c_str(), sSymDSOMemoryPool )) + sLoadedLibraries.push_back( pDSO ); + + for( auto i = 0; i < gSymbolsToGrab.size(); ++i ) + { + if( !*gSymbolsToGrab[i].mPPFunc ) + apr_dso_sym( gSymbolsToGrab[i].mPPFunc, pDSO, gSymbolsToGrab[i].mName ); + } + } + + bool sym_error = false; + + for( auto i = 0; i < gSymbolsToGrab.size(); ++i ) + { + if( gSymbolsToGrab[ i ].mRequired && ! *gSymbolsToGrab[ i ].mPPFunc ) + sym_error = true; + } + + sSymsGrabbed = !sym_error; + return sSymsGrabbed; +} + +void SymbolGrabber::ungrabSymbols() +{ + +} +#endif + #if LL_WINDOWS # define LLSYMEXPORT __declspec(dllexport) @@ -204,3 +254,50 @@ int WINAPI DllEntryPoint( HINSTANCE hInstance, unsigned long reason, void* param return 1; } #endif + +#if LL_LINUX +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; +} +#endif diff --git a/indra/media_plugins/base/media_plugin_base.h b/indra/media_plugins/base/media_plugin_base.h index f65c712a66..a084fc9834 100644 --- a/indra/media_plugins/base/media_plugin_base.h +++ b/indra/media_plugins/base/media_plugin_base.h @@ -32,6 +32,41 @@ #include "llpluginmessage.h" #include "llpluginmessageclasses.h" +#if LL_LINUX + +struct SymbolToGrab +{ + bool mRequired; + char const *mName; + apr_dso_handle_sym_t *mPPFunc; +}; + +class SymbolGrabber +{ +public: + size_t registerSymbol( SymbolToGrab aSymbol ); + bool grabSymbols(std::vector< std::string > const &aDSONames); + void ungrabSymbols(); + +private: + std::vector< SymbolToGrab > gSymbolsToGrab; + + bool sSymsGrabbed = false; + apr_pool_t *sSymDSOMemoryPool = nullptr; + std::vector<apr_dso_handle_t *> sLoadedLibraries; +}; + +extern SymbolGrabber gSymbolGrabber; + +// extern SymbolGrabber gSymbolGrabber; + +#define LL_GRAB_SYM(SYMBOL_GRABBER, REQUIRED, SYMBOL_NAME, RETURN, ...) \ + RETURN (*ll##SYMBOL_NAME)(__VA_ARGS__) = nullptr; \ + size_t gRegistered##SYMBOL_NAME = SYMBOL_GRABBER.registerSymbol( \ + { REQUIRED, #SYMBOL_NAME , (apr_dso_handle_sym_t*)&ll##SYMBOL_NAME} \ + ); + +#endif class MediaPluginBase { @@ -46,7 +81,6 @@ public: static void staticReceiveMessage(const char *message_string, void **user_data); protected: - /** Plugin status. */ typedef enum { @@ -126,4 +160,7 @@ int init_media_plugin( LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data); - +#if LL_LINUX +pid_t getParentPid(pid_t aPid); +bool isPluginPid(pid_t aPid); +#endif diff --git a/indra/media_plugins/cef/CMakeLists.txt b/indra/media_plugins/cef/CMakeLists.txt index 410778114d..2c4ccd46d7 100644 --- a/indra/media_plugins/cef/CMakeLists.txt +++ b/indra/media_plugins/cef/CMakeLists.txt @@ -10,18 +10,10 @@ include(Linking) include(PluginAPI) include(CEFPlugin) - +include(GLIB) ### media_plugin_cef -if(NOT ADDRESS_SIZE EQUAL 32) - if(WINDOWS) - ##add_definitions(/FIXED:NO) - else(WINDOWS) # not windows therefore gcc LINUX and DARWIN - add_definitions(-fPIC) - endif(WINDOWS) -endif(NOT ADDRESS_SIZE EQUAL 32) - set(media_plugin_cef_SOURCE_FILES media_plugin_cef.cpp ) @@ -32,10 +24,36 @@ set(media_plugin_cef_HEADER_FILES # Select which VolumeCatcher implementation to use if (LINUX) - message(FATAL_ERROR "CEF plugin has been enabled for a Linux compile.\n" - " Please create a volume_catcher implementation for this platform.") + 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() + + include(FindPipeWire) + include_directories(SYSTEM ${PIPEWIRE_INCLUDE_DIRS} ${SPA_INCLUDE_DIRS}) + + message( "Building with Linux volume catcher for PipeWire and PulseAudio" ) + + list(APPEND media_plugin_cef_HEADER_FILES + linux/volume_catcher_linux.h + ) + + set(LINUX_VOLUME_CATCHER + linux/volume_catcher_linux.cpp + linux/volume_catcher_pulseaudio.cpp + linux/volume_catcher_pipewire.cpp + ) + + list(APPEND media_plugin_cef_SOURCE_FILES ${LINUX_VOLUME_CATCHER}) + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--build-id -Wl,-rpath,'$ORIGIN:$ORIGIN/../../lib'") + list(APPEND media_plugin_cef_LINK_LIBRARIES llwindow ) elseif (DARWIN) - list(APPEND media_plugin_cef_SOURCE_FILES mac_volume_catcher_null.cpp) + list(APPEND media_plugin_cef_SOURCE_FILES volume_catcher_null.cpp) find_library(CORESERVICES_LIBRARY CoreServices) find_library(AUDIOUNIT_LIBRARY AudioUnit) set( media_plugin_cef_LINK_LIBRARIES @@ -60,6 +78,7 @@ add_library(media_plugin_cef target_link_libraries(media_plugin_cef media_plugin_base ll::cef + ll::glib_headers ) if (WINDOWS) diff --git a/indra/media_plugins/cef/linux/volume_catcher_linux.cpp b/indra/media_plugins/cef/linux/volume_catcher_linux.cpp new file mode 100644 index 0000000000..7d33242063 --- /dev/null +++ b/indra/media_plugins/cef/linux/volume_catcher_linux.cpp @@ -0,0 +1,78 @@ +/** + * @file volume_catcher.cpp + * @brief Linux volume catcher which will pick an implementation to use + * + * @cond + * $LicenseInfo:firstyear=2010&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 "volume_catcher_linux.h" + +VolumeCatcher::VolumeCatcher() +{ +} + +void VolumeCatcher::onEnablePipeWireVolumeCatcher(bool enable) +{ + if (pimpl != nullptr) + return; + + if (enable) + { + LL_DEBUGS() << "volume catcher using pipewire" << LL_ENDL; + pimpl = new VolumeCatcherPipeWire(); + } + else + { + LL_DEBUGS() << "volume catcher using pulseaudio" << LL_ENDL; + pimpl = new VolumeCatcherPulseAudio(); + } +} + +VolumeCatcher::~VolumeCatcher() +{ + if (pimpl != nullptr) + { + delete pimpl; + pimpl = nullptr; + } +} + +void VolumeCatcher::setVolume(F32 volume) +{ + if (pimpl != nullptr) { + pimpl->setVolume(volume); + } +} + +void VolumeCatcher::setPan(F32 pan) +{ + if (pimpl != nullptr) + pimpl->setPan(pan); +} + +void VolumeCatcher::pump() +{ + if (pimpl != nullptr) + pimpl->pump(); +} diff --git a/indra/media_plugins/cef/linux/volume_catcher_linux.h b/indra/media_plugins/cef/linux/volume_catcher_linux.h new file mode 100644 index 0000000000..505f9ffb31 --- /dev/null +++ b/indra/media_plugins/cef/linux/volume_catcher_linux.h @@ -0,0 +1,149 @@ +/** + * @file volume_catcher_impl.h + * @brief + * + * @cond + * $LicenseInfo:firstyear=2010&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 + */ + +#ifndef VOLUME_CATCHER_LINUX_H +#define VOLUME_CATCHER_LINUX_H + +#include "linden_common.h" + +#include "../volume_catcher.h" + +#include <unordered_set> +#include <mutex> + +extern "C" { +// There's no special reason why we want the *glib* PA mainloop, but the generic polling implementation seems broken. +#include <pulse/glib-mainloop.h> +#include <pulse/context.h> + +#include <pipewire/pipewire.h> + +#include "apr_pools.h" +#include "apr_dso.h" +} + +#include "media_plugin_base.h" + +class VolumeCatcherImpl +{ +public: + virtual ~VolumeCatcherImpl() = default; + + virtual void setVolume(F32 volume) = 0; // 0.0 - 1.0 + + // Set the left-right pan of audio sources + // where -1.0 = left, 0 = center, and 1.0 = right + virtual void setPan(F32 pan) = 0; + + virtual void pump() = 0; // call this at least a few times a second if you can - it affects how quickly we can 'catch' a new audio source and adjust its volume +}; + +class VolumeCatcherPulseAudio : public VolumeCatcherImpl +{ +public: + VolumeCatcherPulseAudio(); + ~VolumeCatcherPulseAudio(); + + void setVolume(F32 volume); + void setPan(F32 pan); + void pump(); + + // for internal use - can't be private because used from our C callbacks + + bool loadsyms(std::string pa_dso_name); + void init(); + void cleanup(); + + void update_all_volumes(F32 volume); + void update_index_volume(U32 index, F32 volume); + void connected_okay(); + + std::set<U32> mSinkInputIndices; + std::map<U32,U32> mSinkInputNumChannels; + F32 mDesiredVolume; + pa_glib_mainloop *mMainloop; + pa_context *mPAContext; + bool mConnected; + bool mGotSyms; +}; + +class VolumeCatcherPipeWire : public VolumeCatcherImpl +{ +public: + VolumeCatcherPipeWire(); + ~VolumeCatcherPipeWire(); + + bool loadsyms(std::string pw_dso_name); + void init(); + void cleanup(); + + // some of these should be private + + void lock(); + void unlock(); + + void setVolume(F32 volume); + void setPan(F32 pan); + void pump(); + + void handleRegistryEventGlobal( + uint32_t id, uint32_t permissions, const char* type, + uint32_t version, const struct spa_dict* props + ); + + class ChildNode + { + public: + bool mActive = false; + + pw_proxy* mProxy = nullptr; + spa_hook mNodeListener {}; + spa_hook mProxyListener {}; + VolumeCatcherPipeWire* mImpl = nullptr; + + void updateVolume(); + void destroy(); + }; + + bool mGotSyms = false; + + F32 mVolume = 1.0f; // max by default + // F32 mPan = 0.0f; // center + + pw_thread_loop* mThreadLoop = nullptr; + pw_context* mContext = nullptr; + pw_core* mCore = nullptr; + pw_registry* mRegistry = nullptr; + spa_hook mRegistryListener; + + std::unordered_set<ChildNode*> mChildNodes; + std::mutex mChildNodesMutex; + std::mutex mCleanupMutex; +}; + +#endif // VOLUME_CATCHER_LINUX_H diff --git a/indra/media_plugins/cef/linux/volume_catcher_pipewire.cpp b/indra/media_plugins/cef/linux/volume_catcher_pipewire.cpp new file mode 100755 index 0000000000..27fea547c9 --- /dev/null +++ b/indra/media_plugins/cef/linux/volume_catcher_pipewire.cpp @@ -0,0 +1,333 @@ +/** + * @file volume_catcher_pipewire.cpp + * @brief A Linux-specific, PipeWire-specific hack to detect and volume-adjust new audio sources + * + * @cond + * $LicenseInfo:firstyear=2010&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 + */ + +/* + The high-level design is as follows: + 1) Connect to the PipeWire daemon + 2) Find all existing and new audio nodes + 3) Examine PID and parent PID's to see if it belongs to our process + 4) If so, tell PipeWire to adjust the volume of that node + 5) Keep a list of all audio nodes and adjust when we setVolume() + */ + +#include "linden_common.h" + +#include "volume_catcher_linux.h" + +extern "C" { +#include <spa/pod/builder.h> +#include <spa/param/props.h> +} + +SymbolGrabber pwSymbolGrabber; + +#include "volume_catcher_pipewire_syms.inc" + +//////////////////////////////////////////////////// + +VolumeCatcherPipeWire::VolumeCatcherPipeWire() +{ + init(); +} + +VolumeCatcherPipeWire::~VolumeCatcherPipeWire() +{ + cleanup(); +} + +static void registryEventGlobal( + void *data, uint32_t id, uint32_t permissions, const char *type, + uint32_t version, const struct spa_dict *props) +{ + static_cast<VolumeCatcherPipeWire*>(data)->handleRegistryEventGlobal( + id, permissions, type, version, props + ); +} + +static const struct pw_registry_events REGISTRY_EVENTS = { + .version = PW_VERSION_REGISTRY_EVENTS, + .global = registryEventGlobal, +}; + +bool VolumeCatcherPipeWire::loadsyms(std::string pw_dso_name) +{ + return pwSymbolGrabber.grabSymbols({ pw_dso_name }); +} + +void VolumeCatcherPipeWire::init() +{ + LL_DEBUGS() << "init" << LL_ENDL; + + mGotSyms = loadsyms("libpipewire-0.3.so.0"); + + if (!mGotSyms) + return; + + LL_DEBUGS() << "successfully got symbols" << LL_ENDL; + + llpw_init(nullptr, nullptr); + + mThreadLoop = llpw_thread_loop_new("SL Plugin Volume Adjuster", nullptr); + + if (!mThreadLoop) + return; + + // i dont think we need to lock this early + // std::lock_guard pwLock(*this); + + mContext = llpw_context_new( + llpw_thread_loop_get_loop(mThreadLoop), nullptr, 0 + ); + + if (!mContext) + return; + + mCore = llpw_context_connect(mContext, nullptr, 0); + + if (!mCore) + return; + + mRegistry = pw_core_get_registry(mCore, PW_VERSION_REGISTRY, 0); + + LL_DEBUGS() << "pw_core_get_registry: " << (mRegistry?"success":"nullptr") << LL_ENDL; + + spa_zero(mRegistryListener); + + pw_registry_add_listener( + mRegistry, &mRegistryListener, ®ISTRY_EVENTS, this + ); + + llpw_thread_loop_start(mThreadLoop); + + LL_DEBUGS() << "thread loop started" << LL_ENDL; +} + +void VolumeCatcherPipeWire::cleanup() +{ + { + std::unique_lock childNodesLock(mChildNodesMutex); + for (auto *childNode: mChildNodes) + childNode->destroy(); + + mChildNodes.clear(); + } + + { + std::unique_lock pwLock(mCleanupMutex); + if (mRegistry) + llpw_proxy_destroy((struct pw_proxy *) mRegistry); + + spa_zero(mRegistryListener); + + if (mCore) + llpw_core_disconnect(mCore); + if (mContext) + llpw_context_destroy(mContext); + } + + if (!mThreadLoop) + return; + + llpw_thread_loop_stop(mThreadLoop); + llpw_thread_loop_destroy(mThreadLoop); + + LL_DEBUGS() << "cleanup done" << LL_ENDL; +} + +void VolumeCatcherPipeWire::lock() +{ + if (!mThreadLoop) + return; + + llpw_thread_loop_lock(mThreadLoop); +} + +void VolumeCatcherPipeWire::unlock() +{ + if (!mThreadLoop) + return; + + llpw_thread_loop_unlock(mThreadLoop); +} + +const uint32_t channels = 1; +const float resetVolumes[channels] = { 1.0f }; + +void VolumeCatcherPipeWire::ChildNode::updateVolume() +{ + if (!mActive) + return; + + F32 volume = std::clamp(mImpl->mVolume, 0.0f, 1.0f); + + const float volumes[channels] = { volume }; + + uint8_t buffer[512]; + + spa_pod_builder builder; + spa_pod_builder_init(&builder, buffer, sizeof(buffer)); + + spa_pod_frame frame; + spa_pod_builder_push_object(&builder, &frame, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props); + + // resets system-wide memorized volume for chromium (not google chrome) to 100% + spa_pod_builder_prop(&builder, SPA_PROP_channelVolumes, 0); + spa_pod_builder_array(&builder, sizeof(float), SPA_TYPE_Float, channels, resetVolumes); + + // sets temporary volume + spa_pod_builder_prop(&builder, SPA_PROP_softVolumes, 0); + spa_pod_builder_array(&builder, sizeof(float), SPA_TYPE_Float, channels, volumes); + + spa_pod* pod = static_cast<spa_pod*>(spa_pod_builder_pop(&builder, &frame)); + + { + std::lock_guard pwLock(*mImpl); + pw_node_set_param(mProxy, SPA_PARAM_Props, 0, pod); + } +} + +void VolumeCatcherPipeWire::ChildNode::destroy() +{ + if (!mActive) + return; + + mActive = false; + + { + std::unique_lock childNodesLock(mImpl->mChildNodesMutex); + mImpl->mChildNodes.erase(this); + } + + spa_hook_remove(&mNodeListener); + spa_hook_remove(&mProxyListener); + + { + std::lock_guard pwLock(*mImpl); + llpw_proxy_destroy(mProxy); + } +} + +static void nodeEventInfo(void* data, const struct pw_node_info* info) +{ + const char* processId = spa_dict_lookup(info->props, PW_KEY_APP_PROCESS_ID); + + if (processId == nullptr) + return; + + pid_t pid = atoll(processId); + + if (!isPluginPid(pid)) + return; + + const char* appName = spa_dict_lookup(info->props, PW_KEY_APP_NAME); + LL_DEBUGS() << "got app: " << appName << LL_ENDL; + + auto* const childNode = static_cast<VolumeCatcherPipeWire::ChildNode*>(data); + LL_DEBUGS() << "init volume: " << childNode->mImpl->mVolume << LL_ENDL; + + childNode->updateVolume(); + + { + std::lock_guard childNodesLock(childNode->mImpl->mChildNodesMutex); + childNode->mImpl->mChildNodes.insert(childNode); + } +} + +static const struct pw_node_events NODE_EVENTS = { + .version = PW_VERSION_CLIENT_EVENTS, + .info = nodeEventInfo, +}; + +static void proxyEventDestroy(void* data) +{ + auto* const childNode = static_cast<VolumeCatcherPipeWire::ChildNode*>(data); + childNode->destroy(); +} + +static void proxyEventRemoved(void* data) +{ + auto* const childNode = static_cast<VolumeCatcherPipeWire::ChildNode*>(data); + childNode->destroy(); +} + +static const struct pw_proxy_events PROXY_EVENTS = { + .version = PW_VERSION_PROXY_EVENTS, + .destroy = proxyEventDestroy, + .removed = proxyEventRemoved, +}; + +void VolumeCatcherPipeWire::handleRegistryEventGlobal( + uint32_t id, uint32_t permissions, const char *type, uint32_t version, + const struct spa_dict *props) +{ + if (props == nullptr || type == nullptr || strcmp(type, PW_TYPE_INTERFACE_Node) != 0) + return; + + const char* mediaClass = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS); + + if (mediaClass == nullptr || strcmp(mediaClass, "Stream/Output/Audio") != 0) + return; + + pw_proxy* proxy = static_cast<pw_proxy*>( + pw_registry_bind(mRegistry, id, type, PW_VERSION_CLIENT, sizeof(ChildNode)) + ); + + auto* const childNode = static_cast<ChildNode*>(llpw_proxy_get_user_data(proxy)); + + childNode->mActive = true; + childNode->mProxy = proxy; + childNode->mImpl = this; + + pw_node_add_listener(proxy, &childNode->mNodeListener, &NODE_EVENTS, childNode); + llpw_proxy_add_listener(proxy, &childNode->mProxyListener, &PROXY_EVENTS, childNode); +} + +void VolumeCatcherPipeWire::setVolume(F32 volume) +{ + LL_DEBUGS() << "setting volume to: " << volume << LL_ENDL; + + mVolume = volume; + + { + std::unique_lock childNodeslock(mChildNodesMutex); + std::unordered_set<ChildNode *> copyOfChildNodes(mChildNodes); + + LL_DEBUGS() << "found " << copyOfChildNodes.size() << " child nodes" << LL_ENDL; + + for (auto* childNode : copyOfChildNodes) + childNode->updateVolume(); + } +} + +void VolumeCatcherPipeWire::setPan(F32 pan) +{ +} + +void VolumeCatcherPipeWire::pump() +{ +} diff --git a/indra/media_plugins/cef/linux/volume_catcher_pipewire_syms.inc b/indra/media_plugins/cef/linux/volume_catcher_pipewire_syms.inc new file mode 100644 index 0000000000..dbc0f5f169 --- /dev/null +++ b/indra/media_plugins/cef/linux/volume_catcher_pipewire_syms.inc @@ -0,0 +1,26 @@ +#define G pwSymbolGrabber + +// required symbols to grab +LL_GRAB_SYM(G, true, pw_init, void, int *argc, char **argv[]); +// LL_GRAB_SYM(G, true, pw_main_loop_new, struct pw_main_loop *, const struct spa_dict *props); +// LL_GRAB_SYM(G, true, pw_main_loop_get_loop, struct pw_loop *, struct pw_main_loop *loop); +// LL_GRAB_SYM(G, true, pw_main_loop_destroy, void, struct pw_main_loop *loop); +// LL_GRAB_SYM(G, true, pw_main_loop_run, void, struct pw_main_loop *loop); +LL_GRAB_SYM(G, true, pw_context_new, struct pw_context *, struct pw_loop *main_loop, struct pw_properties *props, size_t user_data_size); +LL_GRAB_SYM(G, true, pw_context_destroy, void, struct pw_context *context); +LL_GRAB_SYM(G, true, pw_context_connect, struct pw_core *, struct pw_context *context, struct pw_properties *properties, size_t user_data_size); +LL_GRAB_SYM(G, true, pw_thread_loop_new, struct pw_thread_loop *, const char *name, const struct spa_dict *props); +LL_GRAB_SYM(G, true, pw_thread_loop_destroy, void, struct pw_thread_loop *loop); +LL_GRAB_SYM(G, true, pw_thread_loop_get_loop, struct pw_loop *, struct pw_thread_loop *loop); +LL_GRAB_SYM(G, true, pw_thread_loop_start, int, struct pw_thread_loop *loop); +LL_GRAB_SYM(G, true, pw_thread_loop_stop, void, struct pw_thread_loop *loop); +LL_GRAB_SYM(G, true, pw_thread_loop_lock, void, struct pw_thread_loop *loop); +LL_GRAB_SYM(G, true, pw_thread_loop_unlock, void, struct pw_thread_loop *loop); +LL_GRAB_SYM(G, true, pw_proxy_add_listener, void, struct pw_proxy *proxy, struct spa_hook *listener, const struct pw_proxy_events *events, void *data); +LL_GRAB_SYM(G, true, pw_proxy_destroy, void, struct pw_proxy *proxy); +LL_GRAB_SYM(G, true, pw_proxy_get_user_data, void *, struct pw_proxy *proxy); +LL_GRAB_SYM(G, true, pw_core_disconnect, int, struct pw_core *core); + +// optional symbols to grab + +#undef G diff --git a/indra/media_plugins/cef/linux/volume_catcher_pulseaudio.cpp b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio.cpp new file mode 100755 index 0000000000..9417c49d38 --- /dev/null +++ b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio.cpp @@ -0,0 +1,322 @@ +/** + * @file volume_catcher_pulseaudio.cpp + * @brief A Linux-specific, PulseAudio-specific hack to detect and volume-adjust new audio sources + * + * @cond + * $LicenseInfo:firstyear=2010&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 + */ + +/* + The high-level design is as follows: + 1) Connect to the PulseAudio daemon + 2) Watch for the creation of new audio players connecting to the daemon (this includes ALSA clients running on the PulseAudio emulation layer, such as Flash plugins) + 3) Examine any new audio player's PID to see if it belongs to our own process + 4) If so, tell PA to adjust the volume of that audio player ('sink input' in PA parlance) + 5) Keep a list of all living audio players that we care about, adjust the volumes of all of them when we get a new setVolume() call + */ + +#include "linden_common.h" + +#include "volume_catcher_linux.h" + +extern "C" { +#include <glib.h> +#include <glib-object.h> + +#include <pulse/introspect.h> + +#include <pulse/subscribe.h> +} + +SymbolGrabber paSymbolGrabber; + +#include "volume_catcher_pulseaudio_syms.inc" +#include "volume_catcher_pulseaudio_glib_syms.inc" + +//////////////////////////////////////////////////// + +// PulseAudio requires a chain of callbacks with C linkage +extern "C" { + void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *i, int eol, void *userdata); + void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata); + void callback_context_state(pa_context *context, void *userdata); +} + +VolumeCatcherPulseAudio::VolumeCatcherPulseAudio() + : mDesiredVolume(0.0f), + mMainloop(nullptr), + mPAContext(nullptr), + mConnected(false), + mGotSyms(false) +{ + init(); +} + +VolumeCatcherPulseAudio::~VolumeCatcherPulseAudio() +{ + cleanup(); +} + +bool VolumeCatcherPulseAudio::loadsyms(std::string pulse_dso_name) +{ + return paSymbolGrabber.grabSymbols({ pulse_dso_name }); +} + +void VolumeCatcherPulseAudio::init() +{ + // try to be as defensive as possible because PA's interface is a + // bit fragile and (for our purposes) we'd rather simply not function + // than crash + + // we cheat and rely upon libpulse-mainloop-glib.so.0 to pull-in + // libpulse.so.0 - this isn't a great assumption, and the two DSOs should + // probably be loaded separately. Our Linux DSO framework needs refactoring, + // we do this sort of thing a lot with practically identical logic... + mGotSyms = loadsyms("libpulse-mainloop-glib.so.0"); + + if (!mGotSyms) + mGotSyms = loadsyms("libpulse.so.0"); + + if (!mGotSyms) + return; + + 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"); + llpa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "com.secondlife.viewer.mediaplugvoladjust"); + llpa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "SL Plugin Volume Adjuster"); + llpa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, "1"); + + // plain old pa_context_new() is broken! + mPAContext = llpa_context_new_with_proplist(api, nullptr, proplist); + + llpa_proplist_free(proplist); + } + } + } + + // Now we've set up a PA context and mainloop, try connecting the + // PA context to a PA daemon. + if (mPAContext) + { + llpa_context_set_state_callback(mPAContext, callback_context_state, this); + pa_context_flags_t cflags = (pa_context_flags)0; // maybe add PA_CONTEXT_NOAUTOSPAWN? + if (llpa_context_connect(mPAContext, nullptr, cflags, nullptr) >= 0) + { + // Okay! We haven't definitely connected, but we + // haven't definitely failed yet. + } + else + { + // Failed to connect to PA manager... we'll leave + // things like that. Perhaps we should try again later. + } + } +} + +void VolumeCatcherPulseAudio::cleanup() +{ + mConnected = false; + + if (mGotSyms && mPAContext) + { + llpa_context_disconnect(mPAContext); + llpa_context_unref(mPAContext); + } + + mPAContext = nullptr; + + if (mGotSyms && mMainloop) + llpa_glib_mainloop_free(mMainloop); + + mMainloop = nullptr; +} + +void VolumeCatcherPulseAudio::setVolume(F32 volume) +{ + mDesiredVolume = volume; + + if (!mGotSyms) + return; + + if (mConnected && mPAContext) + { + update_all_volumes(mDesiredVolume); + } + + pump(); +} + +void VolumeCatcherPulseAudio::setPan(F32 pan) +{ +} + +void VolumeCatcherPulseAudio::pump() +{ + gboolean may_block = FALSE; + g_main_context_iteration(g_main_context_default(), may_block); +} + +void VolumeCatcherPulseAudio::connected_okay() +{ + pa_operation *op; + + // fetch global list of existing sinkinputs + if ((op = llpa_context_get_sink_input_info_list(mPAContext, + callback_discovered_sinkinput, + this))) + { + llpa_operation_unref(op); + } + + // subscribe to future global sinkinput changes + llpa_context_set_subscribe_callback(mPAContext, + callback_subscription_alert, + this); + if ((op = llpa_context_subscribe(mPAContext, (pa_subscription_mask_t) + (PA_SUBSCRIPTION_MASK_SINK_INPUT), + nullptr, nullptr))) + { + llpa_operation_unref(op); + } +} + +void VolumeCatcherPulseAudio::update_all_volumes(F32 volume) +{ + for (std::set<U32>::iterator it = mSinkInputIndices.begin(); + it != mSinkInputIndices.end(); ++it) + { + update_index_volume(*it, volume); + } +} + +void VolumeCatcherPulseAudio::update_index_volume(U32 index, F32 volume) +{ + static pa_cvolume cvol; + llpa_cvolume_set(&cvol, mSinkInputNumChannels[index], + llpa_sw_volume_from_linear(volume)); + + pa_context *c = mPAContext; + uint32_t idx = index; + const pa_cvolume *cvolumep = &cvol; + pa_context_success_cb_t cb = nullptr; // okay as null + void *userdata = nullptr; // okay as null + + pa_operation *op; + if ((op = llpa_context_set_sink_input_volume(c, idx, cvolumep, cb, userdata))) + llpa_operation_unref(op); +} + +void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *sii, int eol, void *userdata) +{ + VolumeCatcherPulseAudio *impl = dynamic_cast<VolumeCatcherPulseAudio*>((VolumeCatcherPulseAudio*)userdata); + llassert(impl); + + if (0 == eol) + { + pa_proplist *proplist = sii->proplist; + pid_t sinkpid = atoll(llpa_proplist_gets(proplist, PA_PROP_APPLICATION_PROCESS_ID)); + + if (isPluginPid( sinkpid )) // does the discovered sinkinput belong to this process? + { + bool is_new = (impl->mSinkInputIndices.find(sii->index) == impl->mSinkInputIndices.end()); + + impl->mSinkInputIndices.insert(sii->index); + impl->mSinkInputNumChannels[sii->index] = sii->channel_map.channels; + + if (is_new) + { + // new! + impl->update_index_volume(sii->index, impl->mDesiredVolume); + } + else + { + // seen it already, do nothing. + } + } + } +} + +void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata) +{ + VolumeCatcherPulseAudio *impl = dynamic_cast<VolumeCatcherPulseAudio*>((VolumeCatcherPulseAudio*)userdata); + llassert(impl); + + 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))) + { + llpa_operation_unref(op); + } + } + else + { + // property change on this sinkinput - we don't care. + } + break; + + default:; + } +} + +void callback_context_state(pa_context *context, void *userdata) +{ + VolumeCatcherPulseAudio *impl = dynamic_cast<VolumeCatcherPulseAudio*>((VolumeCatcherPulseAudio*)userdata); + llassert(impl); + + switch (llpa_context_get_state(context)) + { + case PA_CONTEXT_READY: + impl->mConnected = true; + impl->connected_okay(); + break; + case PA_CONTEXT_TERMINATED: + impl->mConnected = false; + break; + case PA_CONTEXT_FAILED: + impl->mConnected = false; + break; + default:; + } +} diff --git a/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_glib_syms.inc b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_glib_syms.inc new file mode 100755 index 0000000000..e9b7196e51 --- /dev/null +++ b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_glib_syms.inc @@ -0,0 +1,10 @@ +#define G paSymbolGrabber + +// required symbols to grab +LL_GRAB_SYM(G, true, pa_glib_mainloop_free, void, pa_glib_mainloop* g) +LL_GRAB_SYM(G, true, pa_glib_mainloop_get_api, pa_mainloop_api*, pa_glib_mainloop* g) +LL_GRAB_SYM(G, true, pa_glib_mainloop_new, pa_glib_mainloop *, GMainContext *c) + +// optional symbols to grab + +#undef G diff --git a/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_syms.inc b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_syms.inc new file mode 100755 index 0000000000..4859a34405 --- /dev/null +++ b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_syms.inc @@ -0,0 +1,29 @@ +#define G paSymbolGrabber + +// required symbols to grab +LL_GRAB_SYM(G, true, pa_context_connect, int, pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api) +LL_GRAB_SYM(G, true, pa_context_disconnect, void, pa_context *c) +LL_GRAB_SYM(G, true, pa_context_get_sink_input_info, pa_operation*, pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata) +LL_GRAB_SYM(G, true, pa_context_get_sink_input_info_list, pa_operation*, pa_context *c, pa_sink_input_info_cb_t cb, void *userdata) +LL_GRAB_SYM(G, true, pa_context_get_state, pa_context_state_t, pa_context *c) +LL_GRAB_SYM(G, true, pa_context_new_with_proplist, pa_context*, pa_mainloop_api *mainloop, const char *name, pa_proplist *proplist) +LL_GRAB_SYM(G, true, pa_context_set_sink_input_volume, pa_operation*, pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) +LL_GRAB_SYM(G, true, pa_context_set_state_callback, void, pa_context *c, pa_context_notify_cb_t cb, void *userdata) +LL_GRAB_SYM(G, true, pa_context_set_subscribe_callback, void, pa_context *c, pa_context_subscribe_cb_t cb, void *userdata) +LL_GRAB_SYM(G, true, pa_context_subscribe, pa_operation*, pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata) +LL_GRAB_SYM(G, true, pa_context_unref, void, pa_context *c) +LL_GRAB_SYM(G, true, pa_cvolume_set, pa_cvolume*, pa_cvolume *a, unsigned channels, pa_volume_t v) +LL_GRAB_SYM(G, true, pa_operation_unref, void, pa_operation *o) +LL_GRAB_SYM(G, true, pa_proplist_free, void, pa_proplist* p) +LL_GRAB_SYM(G, true, pa_proplist_gets, const char*, pa_proplist *p, const char *key) +LL_GRAB_SYM(G, true, pa_proplist_new, pa_proplist*, void) +LL_GRAB_SYM(G, true, pa_proplist_sets, int, pa_proplist *p, const char *key, const char *value) +LL_GRAB_SYM(G, true, pa_sw_volume_from_linear, pa_volume_t, double v) +// LL_GRAB_SYM(G, true, pa_mainloop_free, void, pa_mainloop *m) +// LL_GRAB_SYM(G, true, pa_mainloop_get_api, pa_mainloop_api *, pa_mainloop *m) +// LL_GRAB_SYM(G, true, pa_mainloop_iterate, int, pa_mainloop *m, int block, int *retval) +// LL_GRAB_SYM(G, true, pa_mainloop_new, pa_mainloop *, void) + +// optional symbols to grab + +#undef G diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp index 64fc7e452b..9c205b558c 100644 --- a/indra/media_plugins/cef/media_plugin_cef.cpp +++ b/indra/media_plugins/cef/media_plugin_cef.cpp @@ -886,7 +886,7 @@ void MediaPluginCEF::receiveMessage(const char* message_string) keyEvent(key_event, native_key_data); -#elif LL_WINDOWS +#else std::string event = message_in.getValue("event"); LLSD native_key_data = message_in.getValueLLSD("native_key_data"); @@ -908,6 +908,13 @@ void MediaPluginCEF::receiveMessage(const char* message_string) { mEnableMediaPluginDebugging = message_in.getValueBoolean("enable"); } +#if LL_LINUX + else if (message_name == "enable_pipewire_volume_catcher") + { + bool enable = message_in.getValueBoolean("enable"); + mVolumeCatcher.onEnablePipeWireVolumeCatcher(enable); + } +#endif if (message_name == "pick_file_response") { LLSD file_list_llsd = message_in.getValueLLSD("file_list"); @@ -1050,6 +1057,28 @@ void MediaPluginCEF::keyEvent(dullahan::EKeyEvent key_event, LLSD native_key_dat mCEFLib->nativeKeyboardEventWin(msg, wparam, lparam); #endif + +#if LL_LINUX + + uint32_t native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger()); // this is actually the SDL event.key.keysym.sym; + uint32_t native_virtual_key_win = (uint32_t)(native_key_data["virtual_key_win"].asInteger()); + uint32_t native_modifiers = (uint32_t)(native_key_data["modifiers"].asInteger()); + + // only for non-printable keysyms, the actual text input is done in unicodeInput() below + if (native_virtual_key <= 0x1b || native_virtual_key >= 0x7f) + { + // set keypad flag, not sure if this even does anything + bool keypad = false; + if (native_virtual_key_win >= 0x60 && native_virtual_key_win <= 0x6f) + { + keypad = true; + } + + // yes, we send native_virtual_key_win twice because native_virtual_key breaks it + mCEFLib->nativeKeyboardEventSDL2(key_event, native_virtual_key, native_modifiers, keypad); + } + +#endif // LL_LINUX }; void MediaPluginCEF::unicodeInput(std::string event, LLSD native_key_data = LLSD::emptyMap()) @@ -1080,6 +1109,16 @@ void MediaPluginCEF::unicodeInput(std::string event, LLSD native_key_data = LLSD U64 lparam = ll_U32_from_sd(native_key_data["l_param"]); mCEFLib->nativeKeyboardEventWin(msg, wparam, lparam); #endif + +#if LL_LINUX + + uint32_t native_scan_code = (uint32_t)(native_key_data["sdl_sym"].asInteger()); + uint32_t native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger()); + uint32_t native_modifiers = (uint32_t)(native_key_data["modifiers"].asInteger()); + + mCEFLib->nativeKeyboardEvent(dullahan::KE_KEY_DOWN, native_scan_code, native_virtual_key, native_modifiers); + +#endif // LL_LINUX }; //////////////////////////////////////////////////////////////////////////////// diff --git a/indra/media_plugins/cef/volume_catcher.h b/indra/media_plugins/cef/volume_catcher.h index ea97a24947..6933854e8e 100644 --- a/indra/media_plugins/cef/volume_catcher.h +++ b/indra/media_plugins/cef/volume_catcher.h @@ -35,19 +35,20 @@ class VolumeCatcherImpl; class VolumeCatcher { - public: +public: VolumeCatcher(); ~VolumeCatcher(); - void setVolume(F32 volume); // 0.0 - 1.0 - - // Set the left-right pan of audio sources - // where -1.0 = left, 0 = center, and 1.0 = right + void setVolume(F32 volume); void setPan(F32 pan); - void pump(); // call this at least a few times a second if you can - it affects how quickly we can 'catch' a new audio source and adjust its volume + void pump(); + +#if LL_LINUX + void onEnablePipeWireVolumeCatcher(bool enable); +#endif - private: +private: VolumeCatcherImpl *pimpl; }; diff --git a/indra/media_plugins/cef/mac_volume_catcher_null.cpp b/indra/media_plugins/cef/volume_catcher_null.cpp index c479e24a95..c6028da45b 100644 --- a/indra/media_plugins/cef/mac_volume_catcher_null.cpp +++ b/indra/media_plugins/cef/volume_catcher_null.cpp @@ -1,5 +1,5 @@ -/** - * @file windows_volume_catcher.cpp +/** + * @file volume_catcher_null.cpp * @brief A null implementation of volume level control of all audio channels opened by a process. * We are using this for the macOS version for now until we can understand how to make the * exitising mac_volume_catcher.cpp work without the (now, non-existant) QuickTime dependency @@ -29,67 +29,25 @@ */ #include "volume_catcher.h" -#include "llsingleton.h" -class VolumeCatcherImpl : public LLSingleton<VolumeCatcherImpl> -{ - LLSINGLETON(VolumeCatcherImpl); - // This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance. - ~VolumeCatcherImpl(); - -public: - - void setVolume(F32 volume); - void setPan(F32 pan); - -private: - F32 mVolume; - F32 mPan; - bool mSystemIsVistaOrHigher; -}; - -VolumeCatcherImpl::VolumeCatcherImpl() -: mVolume(1.0f), // default volume is max - mPan(0.f) // default pan is centered -{ -} - -VolumeCatcherImpl::~VolumeCatcherImpl() -{ -} - -void VolumeCatcherImpl::setVolume(F32 volume) -{ - mVolume = volume; -} - -void VolumeCatcherImpl::setPan(F32 pan) -{ // remember pan for calculating individual channel levels later - mPan = pan; -} ///////////////////////////////////////////////////// VolumeCatcher::VolumeCatcher() { - pimpl = VolumeCatcherImpl::getInstance(); } VolumeCatcher::~VolumeCatcher() { - // Let the instance persist until exit. } void VolumeCatcher::setVolume(F32 volume) { - pimpl->setVolume(volume); } void VolumeCatcher::setPan(F32 pan) { - pimpl->setPan(pan); } void VolumeCatcher::pump() { - // No periodic tasks are necessary for this implementation. } diff --git a/indra/media_plugins/cef/windows_volume_catcher.cpp b/indra/media_plugins/cef/windows_volume_catcher.cpp index e7daeb5f74..1e52fee9de 100644 --- a/indra/media_plugins/cef/windows_volume_catcher.cpp +++ b/indra/media_plugins/cef/windows_volume_catcher.cpp @@ -44,7 +44,6 @@ public: private: F32 mVolume; F32 mPan; - bool mSystemIsVistaOrHigher; }; VolumeCatcherImpl::VolumeCatcherImpl() diff --git a/indra/media_plugins/example/CMakeLists.txt b/indra/media_plugins/example/CMakeLists.txt index 7d3e7f663b..86e982fbb4 100644 --- a/indra/media_plugins/example/CMakeLists.txt +++ b/indra/media_plugins/example/CMakeLists.txt @@ -13,14 +13,6 @@ include(ExamplePlugin) ### media_plugin_example -if(NOT ADDRESS_SIZE EQUAL 32) - if(WINDOWS) - ##add_definitions(/FIXED:NO) - else(WINDOWS) # not windows therefore gcc LINUX and DARWIN - add_definitions(-fPIC) - endif(WINDOWS) -endif(NOT ADDRESS_SIZE EQUAL 32) - set(media_plugin_example_SOURCE_FILES media_plugin_example.cpp ) diff --git a/indra/media_plugins/gstreamer010/CMakeLists.txt b/indra/media_plugins/gstreamer010/CMakeLists.txt deleted file mode 100644 index 38fc8201bf..0000000000 --- a/indra/media_plugins/gstreamer010/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -# -*- cmake -*- - -project(media_plugin_gstreamer010) - -include(00-Common) -include(LLCommon) -include(LLImage) -include(LLMath) -include(LLWindow) -include(Linking) -include(PluginAPI) -include(OpenGL) - -include(GStreamer010Plugin) - -### media_plugin_gstreamer010 - -if(NOT ADDRESS_SIZE EQUAL 32) - if(WINDOWS) - ##add_definitions(/FIXED:NO) - else(WINDOWS) # not windows therefore gcc LINUX and DARWIN - add_definitions(-fPIC) - endif(WINDOWS) -endif(NOT ADDRESS_SIZE EQUAL 32) - -set(media_plugin_gstreamer010_SOURCE_FILES - media_plugin_gstreamer010.cpp - llmediaimplgstreamer_syms.cpp - llmediaimplgstreamervidplug.cpp - ) - -set(media_plugin_gstreamer010_HEADER_FILES - llmediaimplgstreamervidplug.h - llmediaimplgstreamer_syms.h - llmediaimplgstreamertriviallogging.h - ) - -add_library(media_plugin_gstreamer010 - SHARED - ${media_plugin_gstreamer010_SOURCE_FILES} - ) - -target_link_libraries(media_plugin_gstreamer010 - media_plugin_base - ll::gstreamer - ) diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp deleted file mode 100644 index dcc04b37e4..0000000000 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/** - * @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 - */ - -#if LL_GSTREAMER010_ENABLED - -#include <string> - -extern "C" { -#include <gst/gst.h> - -#include "apr_pools.h" -#include "apr_dso.h" -} - -#include "llmediaimplgstreamertriviallogging.h" - -#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) RTN (*ll##GSTSYM)(__VA_ARGS__) = NULL -#include "llmediaimplgstreamer_syms_raw.inc" -#include "llmediaimplgstreamer_syms_rawv.inc" -#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; -static apr_dso_handle_t *sSymGSTDSOHandleG = NULL; -static apr_dso_handle_t *sSymGSTDSOHandleV = NULL; - - -bool grab_gst_syms(std::string gst_dso_name, - std::string gst_dso_name_vid) -{ - if (sSymsGrabbed) - { - // already have grabbed good syms - return TRUE; - } - - bool sym_error = false; - bool rtn = false; - apr_status_t rv; - apr_dso_handle_t *sSymGSTDSOHandle = NULL; - -#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) do{rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll##GSTSYM, sSymGSTDSOHandle, #GSTSYM); if (rv != APR_SUCCESS) {INFOMSG("Failed to grab symbol: %s", #GSTSYM); if (REQ) sym_error = true;} else DEBUGMSG("grabbed symbol: %s from %p", #GSTSYM, (void*)ll##GSTSYM);}while(0) - - //attempt to load the shared libraries - apr_pool_create(&sSymGSTDSOMemoryPool, NULL); - - if ( APR_SUCCESS == (rv = apr_dso_load(&sSymGSTDSOHandle, - gst_dso_name.c_str(), - sSymGSTDSOMemoryPool) )) - { - INFOMSG("Found DSO: %s", gst_dso_name.c_str()); -#include "llmediaimplgstreamer_syms_raw.inc" - - if ( sSymGSTDSOHandle ) - { - sSymGSTDSOHandleG = sSymGSTDSOHandle; - sSymGSTDSOHandle = NULL; - } - - if ( APR_SUCCESS == - (rv = apr_dso_load(&sSymGSTDSOHandle, - gst_dso_name_vid.c_str(), - sSymGSTDSOMemoryPool) )) - { - INFOMSG("Found DSO: %s", gst_dso_name_vid.c_str()); -#include "llmediaimplgstreamer_syms_rawv.inc" - rtn = !sym_error; - } - else - { - INFOMSG("Couldn't load DSO: %s", gst_dso_name_vid.c_str()); - rtn = false; // failure - } - } - else - { - INFOMSG("Couldn't load DSO: %s", gst_dso_name.c_str()); - rtn = false; // failure - } - - if (sym_error) - { - WARNMSG("Failed to find necessary symbols in GStreamer libraries."); - } - - if ( sSymGSTDSOHandle ) - { - sSymGSTDSOHandleV = sSymGSTDSOHandle; - sSymGSTDSOHandle = NULL; - } -#undef LL_GST_SYM - - sSymsGrabbed = !!rtn; - return rtn; -} - - -void ungrab_gst_syms() -{ - // should be safe to call regardless of whether we've - // actually grabbed syms. - - if ( sSymGSTDSOHandleG ) - { - apr_dso_unload(sSymGSTDSOHandleG); - sSymGSTDSOHandleG = NULL; - } - - if ( sSymGSTDSOHandleV ) - { - apr_dso_unload(sSymGSTDSOHandleV); - sSymGSTDSOHandleV = NULL; - } - - if ( sSymGSTDSOMemoryPool ) - { - apr_pool_destroy(sSymGSTDSOMemoryPool); - sSymGSTDSOMemoryPool = NULL; - } - - // NULL-out all of the symbols we'd grabbed -#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) do{ll##GSTSYM = NULL;}while(0) -#include "llmediaimplgstreamer_syms_raw.inc" -#include "llmediaimplgstreamer_syms_rawv.inc" -#undef LL_GST_SYM - - sSymsGrabbed = false; -} - - -#endif // LL_GSTREAMER010_ENABLED diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h deleted file mode 100644 index 57d446c7df..0000000000 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h +++ /dev/null @@ -1,74 +0,0 @@ -/** - * @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" - -#if LL_GSTREAMER010_ENABLED - -extern "C" { -#include <gst/gst.h> -} - -bool grab_gst_syms(std::string gst_dso_name, - std::string gst_dso_name_vid); -void ungrab_gst_syms(); - -#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) extern RTN (*ll##GSTSYM)(__VA_ARGS__) -#include "llmediaimplgstreamer_syms_raw.inc" -#include "llmediaimplgstreamer_syms_rawv.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))) - -#endif // LL_GSTREAMER010_ENABLED diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc deleted file mode 100644 index b33e59363d..0000000000 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc +++ /dev/null @@ -1,51 +0,0 @@ - -// required symbols to grab -LL_GST_SYM(true, gst_pad_peer_accept_caps, gboolean, GstPad *pad, GstCaps *caps); -LL_GST_SYM(true, gst_buffer_new, GstBuffer*, void); -LL_GST_SYM(true, gst_buffer_set_caps, void, GstBuffer*, GstCaps *); -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_element_class_set_details, void, GstElementClass *klass, const GstElementDetails *details); -LL_GST_SYM(true, gst_caps_unref, void, GstCaps* caps); -LL_GST_SYM(true, gst_caps_ref, GstCaps *, GstCaps* caps); -//LL_GST_SYM(true, gst_caps_is_empty, gboolean, const GstCaps *caps); -LL_GST_SYM(true, gst_caps_from_string, GstCaps *, const gchar *string); -LL_GST_SYM(true, gst_caps_replace, void, GstCaps **caps, GstCaps *newcaps); -LL_GST_SYM(true, gst_caps_get_structure, GstStructure *, const GstCaps *caps, guint index); -LL_GST_SYM(true, gst_caps_copy, GstCaps *, const GstCaps * caps); -//LL_GST_SYM(true, gst_caps_intersect, GstCaps *, const GstCaps *caps1, const GstCaps *caps2); -LL_GST_SYM(true, gst_element_register, gboolean, GstPlugin *plugin, const gchar *name, guint rank, GType type); -LL_GST_SYM(true, _gst_plugin_register_static, void, GstPluginDesc *desc); -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); - -// optional symbols to grab -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); - -// GStreamer 'internal' symbols which may not be visible in some runtimes but are still used in expanded GStreamer header macros - yuck! We'll substitute our own stubs for these. -//LL_GST_SYM(true, _gst_debug_register_funcptr, void, GstDebugFuncPtr func, gchar* ptrname); -//LL_GST_SYM(true, _gst_debug_category_new, GstDebugCategory *, gchar *name, guint color, gchar *description); diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc deleted file mode 100644 index 14fbcb48b9..0000000000 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc +++ /dev/null @@ -1,5 +0,0 @@ - -// required symbols to grab -LL_GST_SYM(true, gst_video_sink_get_type, GType, void); - -// optional symbols to grab diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h b/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h deleted file mode 100644 index 43ebad6744..0000000000 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @file llmediaimplgstreamertriviallogging.h - * @brief minimal logging utilities. - * - * @cond - * $LicenseInfo:firstyear=2009&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 - */ - -#ifndef __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ -#define __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ - -#include <cstdio> - -extern "C" { -#include <sys/types.h> -#include <unistd.h> -} - -///////////////////////////////////////////////////////////////////////// -// Debug/Info/Warning macros. -#define MSGMODULEFOO "(media plugin)" -#define STDERRMSG(...) do{\ - fprintf(stderr, " pid:%d: ", (int)getpid());\ - fprintf(stderr, MSGMODULEFOO " %s:%d: ", __FUNCTION__, __LINE__);\ - fprintf(stderr, __VA_ARGS__);\ - fputc('\n',stderr);\ - }while(0) -#define NULLMSG(...) do{}while(0) - -#define DEBUGMSG NULLMSG -#define INFOMSG STDERRMSG -#define WARNMSG STDERRMSG -///////////////////////////////////////////////////////////////////////// - -#endif /* __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ */ diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp b/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp deleted file mode 100644 index acec0f2399..0000000000 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp +++ /dev/null @@ -1,526 +0,0 @@ -/** - * @file llmediaimplgstreamervidplug.h - * @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl - * - * @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 - */ - -#if LL_GSTREAMER010_ENABLED - -#include "linden_common.h" - -#include <gst/gst.h> -#include <gst/video/video.h> -#include <gst/video/gstvideosink.h> - -#include "llmediaimplgstreamer_syms.h" -#include "llmediaimplgstreamertriviallogging.h" - -#include "llmediaimplgstreamervidplug.h" - - -GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug); -#define GST_CAT_DEFAULT gst_slvideo_debug - - -#define SLV_SIZECAPS ", width=(int)[1,2048], height=(int)[1,2048] " -#define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS - -static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ( - (gchar*)"sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (SLV_ALLCAPS) - ); - -GST_BOILERPLATE (GstSLVideo, gst_slvideo, GstVideoSink, - GST_TYPE_VIDEO_SINK); - -static void gst_slvideo_set_property (GObject * object, guint prop_id, - const GValue * value, - GParamSpec * pspec); -static void gst_slvideo_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static void -gst_slvideo_base_init (gpointer gclass) -{ - static GstElementDetails element_details = { - (gchar*)"PluginTemplate", - (gchar*)"Generic/PluginTemplate", - (gchar*)"Generic Template Element", - (gchar*)"Linden Lab" - }; - GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); - - llgst_element_class_add_pad_template (element_class, - llgst_static_pad_template_get (&sink_factory)); - llgst_element_class_set_details (element_class, &element_details); -} - - -static void -gst_slvideo_finalize (GObject * object) -{ - GstSLVideo *slvideo; - slvideo = GST_SLVIDEO (object); - if (slvideo->caps) - { - llgst_caps_unref(slvideo->caps); - } - - G_OBJECT_CLASS(parent_class)->finalize (object); -} - - -static GstFlowReturn -gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf) -{ - GstSLVideo *slvideo; - llg_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); - - slvideo = GST_SLVIDEO(bsink); - - DEBUGMSG("transferring a frame of %dx%d <- %p (%d)", - slvideo->width, slvideo->height, GST_BUFFER_DATA(buf), - slvideo->format); - - if (GST_BUFFER_DATA(buf)) - { - // copy frame and frame info into neutral territory - GST_OBJECT_LOCK(slvideo); - slvideo->retained_frame_ready = TRUE; - slvideo->retained_frame_width = slvideo->width; - slvideo->retained_frame_height = slvideo->height; - slvideo->retained_frame_format = slvideo->format; - int rowbytes = - SLVPixelFormatBytes[slvideo->retained_frame_format] * - slvideo->retained_frame_width; - int needbytes = rowbytes * slvideo->retained_frame_width; - // resize retained frame hunk only if necessary - if (needbytes != slvideo->retained_frame_allocbytes) - { - delete[] slvideo->retained_frame_data; - slvideo->retained_frame_data = new unsigned char[needbytes]; - slvideo->retained_frame_allocbytes = needbytes; - - } - // copy the actual frame data to neutral territory - - // flipped, for GL reasons - for (int ypos=0; ypos<slvideo->height; ++ypos) - { - memcpy(&slvideo->retained_frame_data[(slvideo->height-1-ypos)*rowbytes], - &(((unsigned char*)GST_BUFFER_DATA(buf))[ypos*rowbytes]), - rowbytes); - } - // done with the shared data - GST_OBJECT_UNLOCK(slvideo); - } - - return GST_FLOW_OK; -} - - -static GstStateChangeReturn -gst_slvideo_change_state(GstElement * element, GstStateChange transition) -{ - GstSLVideo *slvideo; - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - - slvideo = GST_SLVIDEO (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - slvideo->fps_n = 0; - slvideo->fps_d = 1; - GST_VIDEO_SINK_WIDTH(slvideo) = 0; - GST_VIDEO_SINK_HEIGHT(slvideo) = 0; - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - return ret; -} - - -static GstCaps * -gst_slvideo_get_caps (GstBaseSink * bsink) -{ - GstSLVideo *slvideo; - slvideo = GST_SLVIDEO(bsink); - - return llgst_caps_ref (slvideo->caps); -} - - -/* this function handles the link with other elements */ -static gboolean -gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps) -{ - GstSLVideo *filter; - GstStructure *structure; - - GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps); - - filter = GST_SLVIDEO(bsink); - - int width, height; - gboolean ret; - const GValue *fps; - const GValue *par; - structure = llgst_caps_get_structure (caps, 0); - ret = llgst_structure_get_int (structure, "width", &width); - ret = ret && llgst_structure_get_int (structure, "height", &height); - fps = llgst_structure_get_value (structure, "framerate"); - ret = ret && (fps != NULL); - par = llgst_structure_get_value (structure, "pixel-aspect-ratio"); - if (!ret) - return FALSE; - - INFOMSG("** filter caps set with width=%d, height=%d", width, height); - - GST_OBJECT_LOCK(filter); - - filter->width = width; - filter->height = height; - - filter->fps_n = llgst_value_get_fraction_numerator(fps); - filter->fps_d = llgst_value_get_fraction_denominator(fps); - if (par) - { - filter->par_n = llgst_value_get_fraction_numerator(par); - filter->par_d = llgst_value_get_fraction_denominator(par); - } - else - { - filter->par_n = 1; - filter->par_d = 1; - } - GST_VIDEO_SINK_WIDTH(filter) = width; - GST_VIDEO_SINK_HEIGHT(filter) = height; - - // crufty lump - we *always* accept *only* RGBX now. - /* - filter->format = SLV_PF_UNKNOWN; - if (0 == strcmp(llgst_structure_get_name(structure), - "video/x-raw-rgb")) - { - int red_mask; - int green_mask; - int blue_mask; - llgst_structure_get_int(structure, "red_mask", &red_mask); - llgst_structure_get_int(structure, "green_mask", &green_mask); - llgst_structure_get_int(structure, "blue_mask", &blue_mask); - if ((unsigned int)red_mask == 0xFF000000 && - (unsigned int)green_mask == 0x00FF0000 && - (unsigned int)blue_mask == 0x0000FF00) - { - filter->format = SLV_PF_RGBX; - //fprintf(stderr, "\n\nPIXEL FORMAT RGB\n\n"); - } else if ((unsigned int)red_mask == 0x0000FF00 && - (unsigned int)green_mask == 0x00FF0000 && - (unsigned int)blue_mask == 0xFF000000) - { - filter->format = SLV_PF_BGRX; - //fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n"); - } - }*/ - - filter->format = SLV_PF_RGBX; - - GST_OBJECT_UNLOCK(filter); - - return TRUE; -} - - -static gboolean -gst_slvideo_start (GstBaseSink * bsink) -{ - gboolean ret = TRUE; - - GST_SLVIDEO(bsink); - - return ret; -} - -static gboolean -gst_slvideo_stop (GstBaseSink * bsink) -{ - GstSLVideo *slvideo; - slvideo = GST_SLVIDEO(bsink); - - // free-up retained frame buffer - GST_OBJECT_LOCK(slvideo); - slvideo->retained_frame_ready = FALSE; - delete[] slvideo->retained_frame_data; - slvideo->retained_frame_data = NULL; - slvideo->retained_frame_allocbytes = 0; - GST_OBJECT_UNLOCK(slvideo); - - return TRUE; -} - - -static GstFlowReturn -gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, - GstCaps * caps, GstBuffer ** buf) -{ - gint width, height; - GstStructure *structure = NULL; - GstSLVideo *slvideo; - slvideo = GST_SLVIDEO(bsink); - - // caps == requested caps - // we can ignore these and reverse-negotiate our preferred dimensions with - // the peer if we like - we need to do this to obey dynamic resize requests - // flowing in from the app. - structure = llgst_caps_get_structure (caps, 0); - if (!llgst_structure_get_int(structure, "width", &width) || - !llgst_structure_get_int(structure, "height", &height)) - { - GST_WARNING_OBJECT (slvideo, "no width/height in caps %" GST_PTR_FORMAT, caps); - return GST_FLOW_NOT_NEGOTIATED; - } - - GstBuffer *newbuf = llgst_buffer_new(); - bool made_bufferdata_ptr = false; -#define MAXDEPTHHACK 4 - - GST_OBJECT_LOCK(slvideo); - if (slvideo->resize_forced_always) // app is giving us a fixed size to work with - { - gint slwantwidth, slwantheight; - slwantwidth = slvideo->resize_try_width; - slwantheight = slvideo->resize_try_height; - - if (slwantwidth != width || - slwantheight != height) - { - // don't like requested caps, we will issue our own suggestion - copy - // the requested caps but substitute our own width and height and see - // if our peer is happy with that. - - GstCaps *desired_caps; - GstStructure *desired_struct; - desired_caps = llgst_caps_copy (caps); - desired_struct = llgst_caps_get_structure (desired_caps, 0); - - GValue value = {0}; - g_value_init(&value, G_TYPE_INT); - g_value_set_int(&value, slwantwidth); - llgst_structure_set_value (desired_struct, "width", &value); - g_value_unset(&value); - g_value_init(&value, G_TYPE_INT); - g_value_set_int(&value, slwantheight); - llgst_structure_set_value (desired_struct, "height", &value); - - if (llgst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo), - desired_caps)) - { - // todo: re-use buffers from a pool? - // todo: set MALLOCDATA to null, set DATA to point straight to shm? - - // peer likes our cap suggestion - DEBUGMSG("peer loves us :)"); - GST_BUFFER_SIZE(newbuf) = slwantwidth * slwantheight * MAXDEPTHHACK; - GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf)); - GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf); - llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), desired_caps); - - made_bufferdata_ptr = true; - } else { - // peer hates our cap suggestion - INFOMSG("peer hates us :("); - llgst_caps_unref(desired_caps); - } - } - } - - GST_OBJECT_UNLOCK(slvideo); - - if (!made_bufferdata_ptr) // need to fallback to malloc at original size - { - GST_BUFFER_SIZE(newbuf) = width * height * MAXDEPTHHACK; - GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf)); - GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf); - llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps); - } - - *buf = GST_BUFFER_CAST(newbuf); - - return GST_FLOW_OK; -} - - -/* initialize the plugin's class */ -static void -gst_slvideo_class_init (GstSLVideoClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - GstBaseSinkClass *gstbasesink_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - gstbasesink_class = (GstBaseSinkClass *) klass; - - gobject_class->finalize = gst_slvideo_finalize; - gobject_class->set_property = gst_slvideo_set_property; - gobject_class->get_property = gst_slvideo_get_property; - - gstelement_class->change_state = gst_slvideo_change_state; - -#define LLGST_DEBUG_FUNCPTR(p) (p) - gstbasesink_class->get_caps = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_caps); - gstbasesink_class->set_caps = LLGST_DEBUG_FUNCPTR( gst_slvideo_set_caps); - gstbasesink_class->buffer_alloc=LLGST_DEBUG_FUNCPTR(gst_slvideo_buffer_alloc); - //gstbasesink_class->get_times = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_times); - gstbasesink_class->preroll = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame); - gstbasesink_class->render = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame); - - gstbasesink_class->start = LLGST_DEBUG_FUNCPTR (gst_slvideo_start); - gstbasesink_class->stop = LLGST_DEBUG_FUNCPTR (gst_slvideo_stop); - - // gstbasesink_class->unlock = LLGST_DEBUG_FUNCPTR (gst_slvideo_unlock); -#undef LLGST_DEBUG_FUNCPTR -} - - -/* initialize the new element - * instantiate pads and add them to element - * set functions - * initialize structure - */ -static void -gst_slvideo_init (GstSLVideo * filter, - GstSLVideoClass * gclass) -{ - filter->caps = NULL; - filter->width = -1; - filter->height = -1; - - // this is the info we share with the client app - GST_OBJECT_LOCK(filter); - filter->retained_frame_ready = FALSE; - filter->retained_frame_data = NULL; - filter->retained_frame_allocbytes = 0; - filter->retained_frame_width = filter->width; - filter->retained_frame_height = filter->height; - filter->retained_frame_format = SLV_PF_UNKNOWN; - GstCaps *caps = llgst_caps_from_string (SLV_ALLCAPS); - llgst_caps_replace (&filter->caps, caps); - filter->resize_forced_always = false; - filter->resize_try_width = -1; - filter->resize_try_height = -1; - GST_OBJECT_UNLOCK(filter); -} - -static void -gst_slvideo_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - llg_return_if_fail (GST_IS_SLVIDEO (object)); - - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_slvideo_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - llg_return_if_fail (GST_IS_SLVIDEO (object)); - - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - -/* entry point to initialize the plug-in - * initialize the plug-in itself - * register the element factories and pad templates - * register the features - */ -static gboolean -plugin_init (GstPlugin * plugin) -{ - DEBUGMSG("PLUGIN INIT"); - - GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, (gchar*)"private-slvideo-plugin", - 0, (gchar*)"Second Life Video Sink"); - - return llgst_element_register (plugin, "private-slvideo", - GST_RANK_NONE, GST_TYPE_SLVIDEO); -} - -/* this is the structure that gstreamer looks for to register plugins - */ -/* NOTE: Can't rely upon GST_PLUGIN_DEFINE_STATIC to self-register, since - some g++ versions buggily avoid __attribute__((constructor)) functions - - so we provide an explicit plugin init function. - */ -#define PACKAGE (gchar*)"packagehack" -// this macro quietly refers to PACKAGE internally -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - (gchar*)"private-slvideoplugin", - (gchar*)"SL Video sink plugin", - plugin_init, (gchar*)"1.0", (gchar*)"LGPL", - (gchar*)"Second Life", - (gchar*)"http://www.secondlife.com/"); -#undef PACKAGE -void gst_slvideo_init_class (void) -{ - ll_gst_plugin_register_static (&gst_plugin_desc); - DEBUGMSG("CLASS INIT"); -} - -#endif // LL_GSTREAMER010_ENABLED diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h b/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h deleted file mode 100644 index d4e07daf4f..0000000000 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h +++ /dev/null @@ -1,105 +0,0 @@ -/** - * @file llmediaimplgstreamervidplug.h - * @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl - * - * @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 - */ - -#ifndef __GST_SLVIDEO_H__ -#define __GST_SLVIDEO_H__ - -#if LL_GSTREAMER010_ENABLED - -extern "C" { -#include <gst/gst.h> -#include <gst/video/video.h> -#include <gst/video/gstvideosink.h> -} - -G_BEGIN_DECLS - -/* #defines don't like whitespacey bits */ -#define GST_TYPE_SLVIDEO \ - (gst_slvideo_get_type()) -#define GST_SLVIDEO(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SLVIDEO,GstSLVideo)) -#define GST_SLVIDEO_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SLVIDEO,GstSLVideoClass)) -#define GST_IS_SLVIDEO(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SLVIDEO)) -#define GST_IS_SLVIDEO_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SLVIDEO)) - -typedef struct _GstSLVideo GstSLVideo; -typedef struct _GstSLVideoClass GstSLVideoClass; - -typedef enum { - SLV_PF_UNKNOWN = 0, - SLV_PF_RGBX = 1, - SLV_PF_BGRX = 2, - SLV__END = 3 -} SLVPixelFormat; -const int SLVPixelFormatBytes[SLV__END] = {1, 4, 4}; - -struct _GstSLVideo -{ - GstVideoSink video_sink; - - GstCaps *caps; - - int fps_n, fps_d; - int par_n, par_d; - int height, width; - SLVPixelFormat format; - - // SHARED WITH APPLICATION: - // Access to the following should be protected by GST_OBJECT_LOCK() on - // the GstSLVideo object, and should be totally consistent upon UNLOCK - // (i.e. all written at once to reflect the current retained frame info - // when the retained frame is updated.) - bool retained_frame_ready; // new frame ready since flag last reset. (*TODO: could get the writer to wait on a semaphore instead of having the reader poll, potentially making dropped frames somewhat cheaper.) - unsigned char* retained_frame_data; - int retained_frame_allocbytes; - int retained_frame_width, retained_frame_height; - SLVPixelFormat retained_frame_format; - // sticky resize info - bool resize_forced_always; - int resize_try_width; - int resize_try_height; -}; - -struct _GstSLVideoClass -{ - GstVideoSinkClass parent_class; -}; - -GType gst_slvideo_get_type (void); - -void gst_slvideo_init_class (void); - -G_END_DECLS - -#endif // LL_GSTREAMER010_ENABLED - -#endif /* __GST_SLVIDEO_H__ */ diff --git a/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp b/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp deleted file mode 100644 index 97d1d7d7b5..0000000000 --- a/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp +++ /dev/null @@ -1,1266 +0,0 @@ -/** - * @file media_plugin_gstreamer010.cpp - * @brief GStreamer-0.10 plugin for LLMedia API plugin system - * - * @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 "llgl.h" - -#include "llplugininstance.h" -#include "llpluginmessage.h" -#include "llpluginmessageclasses.h" -#include "media_plugin_base.h" - -#if LL_GSTREAMER010_ENABLED - -extern "C" { -#include <gst/gst.h> -} - -#include "llmediaimplgstreamer.h" -#include "llmediaimplgstreamertriviallogging.h" - -#include "llmediaimplgstreamervidplug.h" - -#include "llmediaimplgstreamer_syms.h" - -////////////////////////////////////////////////////////////////////////////// -// -class MediaPluginGStreamer010 : public MediaPluginBase -{ -public: - MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); - ~MediaPluginGStreamer010(); - - /* 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); - - static const double MIN_LOOP_SEC = 1.0F; - - 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 ); - - void sizeChanged(); - - static bool mDoneInit; - - guint mBusWatchID; - - float mVolume; - - int mDepth; - - // media NATURAL size - int mNaturalWidth; - int mNaturalHeight; - // media current size - int mCurrentWidth; - int mCurrentHeight; - int mCurrentRowbytes; - // previous media size so we can detect changes - int mPreviousWidth; - int mPreviousHeight; - // desired render size from host - int mWidth; - int mHeight; - // padded texture size we need to write into - int mTextureWidth; - int mTextureHeight; - - int mTextureFormatPrimary; - int mTextureFormatType; - - bool mSeekWanted; - double mSeekDestination; - - // Very GStreamer-specific - GMainLoop *mPump; // event pump for this media - GstElement *mPlaybin; - GstElement *mVisualizer; - GstSLVideo *mVideoSink; -}; - -//static -bool MediaPluginGStreamer010::mDoneInit = false; - -MediaPluginGStreamer010::MediaPluginGStreamer010( - LLPluginInstance::sendMessageFunction host_send_func, - void *host_user_data ) : - MediaPluginBase(host_send_func, host_user_data), - mBusWatchID ( 0 ), - mCurrentRowbytes ( 4 ), - mTextureFormatPrimary ( GL_RGBA ), - mTextureFormatType ( GL_UNSIGNED_INT_8_8_8_8_REV ), - mSeekWanted(false), - mSeekDestination(0.0), - mPump ( NULL ), - mPlaybin ( NULL ), - mVisualizer ( NULL ), - mVideoSink ( NULL ), - mCommand ( COMMAND_NONE ) -{ - std::ostringstream str; - INFOMSG("MediaPluginGStreamer010 constructor - my PID=%u", U32(getpid())); -} - -/////////////////////////////////////////////////////////////////////////////// -// -//#define LL_GST_REPORT_STATE_CHANGES -#ifdef LL_GST_REPORT_STATE_CHANGES -static char* get_gst_state_name(GstState state) -{ - switch (state) { - case GST_STATE_VOID_PENDING: return "VOID_PENDING"; - case GST_STATE_NULL: return "NULL"; - case GST_STATE_READY: return "READY"; - case GST_STATE_PAUSED: return "PAUSED"; - case GST_STATE_PLAYING: return "PLAYING"; - } - return "(unknown)"; -} -#endif // LL_GST_REPORT_STATE_CHANGES - -gboolean -MediaPluginGStreamer010::processGSTEvents(GstBus *bus, - GstMessage *message) -{ - if (!message) - return TRUE; // shield against GStreamer bug - - if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_STATE_CHANGED && - GST_MESSAGE_TYPE(message) != GST_MESSAGE_BUFFERING) - { - DEBUGMSG("Got GST message type: %s", - LLGST_MESSAGE_TYPE_NAME (message)); - } - else - { - // TODO: grok 'duration' message type - DEBUGMSG("Got GST message type: %s", - LLGST_MESSAGE_TYPE_NAME (message)); - } - - 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); - DEBUGMSG("GST buffering: %d%%", 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); -#ifdef LL_GST_REPORT_STATE_CHANGES - // not generally very useful, and rather spammy. - DEBUGMSG("state change (old,<new>,pending): %s,<%s>,%s", - get_gst_state_name(old_state), - get_gst_state_name(new_state), - get_gst_state_name(pending_state)); -#endif // LL_GST_REPORT_STATE_CHANGES - - 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); - WARNMSG("GST error: %s", err?err->message:"(unknown)"); - if (err) - g_error_free (err); - g_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); - INFOMSG("GST info: %s", err?err->message:"(unknown)"); - if (err) - g_error_free (err); - g_free (debug); - } - break; - } - case GST_MESSAGE_WARNING: { - GError *err = NULL; - gchar *debug = NULL; - - llgst_message_parse_warning (message, &err, &debug); - WARNMSG("GST warning: %s", err?err->message:"(unknown)"); - if (err) - g_error_free (err); - g_free (debug); - - break; - } - case GST_MESSAGE_EOS: - /* end-of-stream */ - DEBUGMSG("GST end-of-stream."); - if (mIsLooping) - { - DEBUGMSG("looping media..."); - 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 - DEBUGMSG("really short movie (%0.3fsec) - not gonna loop this, pausing instead.", eos_pos_sec); - // inject a COMMAND_PAUSE - mCommand = COMMAND_PAUSE; - } - else - { -#undef LLGST_LOOP_BY_SEEKING -// loop with a stop-start instead of a seek, because it actually seems rather -// faster than seeking on remote streams. -#ifdef LLGST_LOOP_BY_SEEKING - // first, try looping by an explicit rewind - bool seeksuccess = seek(0.0); - if (seeksuccess) - { - play(1.0); - } - else -#endif // LLGST_LOOP_BY_SEEKING - { // use clumsy stop-start to loop - DEBUGMSG("didn't loop by rewinding - stopping and starting instead..."); - 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) -{ - MediaPluginGStreamer010 *impl = (MediaPluginGStreamer010*)data; - return impl->processGSTEvents(bus, message); -} -} // extern "C" - - - -bool -MediaPluginGStreamer010::navigateTo ( const std::string urlIn ) -{ - if (!mDoneInit) - return false; // error - - setStatus(STATUS_LOADING); - - DEBUGMSG("Setting media URI: %s", urlIn.c_str()); - - mSeekWanted = false; - - if (NULL == mPump || - NULL == mPlaybin) - { - setStatus(STATUS_ERROR); - return false; // error - } - - // set URI - g_object_set (G_OBJECT (mPlaybin), "uri", urlIn.c_str(), NULL); - //g_object_set (G_OBJECT (mPlaybin), "uri", "file:///tmp/movie", NULL); - - // navigateTo implicitly plays, too. - play(1.0); - - return true; -} - - -bool -MediaPluginGStreamer010::update(int milliseconds) -{ - if (!mDoneInit) - return false; // error - - DEBUGMSG("updating media..."); - - // sanity check - if (NULL == mPump || - NULL == mPlaybin) - { - DEBUGMSG("dead media..."); - 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 (g_main_context_pending(g_main_loop_get_context(mPump))) - { - g_main_context_iteration(g_main_loop_get_context(mPump), FALSE); - } - - // check for availability of a new frame - - if (mVideoSink) - { - GST_OBJECT_LOCK(mVideoSink); - if (mVideoSink->retained_frame_ready) - { - DEBUGMSG("NEW FRAME READY"); - - if (mVideoSink->retained_frame_width != mCurrentWidth || - mVideoSink->retained_frame_height != mCurrentHeight) - // *TODO: also check for change in format - { - // just resize container, don't consume frame - int neww = mVideoSink->retained_frame_width; - int newh = mVideoSink->retained_frame_height; - - int newd = 4; - mTextureFormatPrimary = GL_RGBA; - mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV; - - /* - int newd = SLVPixelFormatBytes[mVideoSink->retained_frame_format]; - if (SLV_PF_BGRX == mVideoSink->retained_frame_format) - { - mTextureFormatPrimary = GL_BGRA; - mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV; - } - else - { - mTextureFormatPrimary = GL_RGBA; - mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV; - } - */ - - GST_OBJECT_UNLOCK(mVideoSink); - - mCurrentRowbytes = neww * newd; - DEBUGMSG("video container resized to %dx%d", - neww, newh); - - mDepth = newd; - mCurrentWidth = neww; - mCurrentHeight = newh; - sizeChanged(); - return true; - } - - if (mPixels && - mCurrentHeight <= mHeight && - mCurrentWidth <= mWidth && - !mTextureSegmentName.empty()) - { - // we're gonna totally consume this frame - reset 'ready' flag - mVideoSink->retained_frame_ready = FALSE; - int destination_rowbytes = mWidth * mDepth; - for (int row=0; row<mCurrentHeight; ++row) - { - memcpy(&mPixels - [destination_rowbytes * row], - &mVideoSink->retained_frame_data - [mCurrentRowbytes * row], - mCurrentRowbytes); - } - - GST_OBJECT_UNLOCK(mVideoSink); - DEBUGMSG("NEW FRAME REALLY TRULY CONSUMED, TELLING HOST"); - - setDirty(0,0,mCurrentWidth,mCurrentHeight); - } - else - { - // new frame ready, but we're not ready to - // consume it. - - GST_OBJECT_UNLOCK(mVideoSink); - - DEBUGMSG("NEW FRAME not consumed, still waiting for a shm segment and/or shm resize"); - } - - return true; - } - else - { - // nothing to do yet. - GST_OBJECT_UNLOCK(mVideoSink); - return true; - } - } - - return true; -} - - -void -MediaPluginGStreamer010::mouseDown( int x, int y ) -{ - // do nothing -} - -void -MediaPluginGStreamer010::mouseUp( int x, int y ) -{ - // do nothing -} - -void -MediaPluginGStreamer010::mouseMove( int x, int y ) -{ - // do nothing -} - - -bool -MediaPluginGStreamer010::pause() -{ - DEBUGMSG("pausing media..."); - // todo: error-check this? - if (mDoneInit && mPlaybin) - { - llgst_element_set_state(mPlaybin, GST_STATE_PAUSED); - return true; - } - return false; -} - -bool -MediaPluginGStreamer010::stop() -{ - DEBUGMSG("stopping media..."); - // todo: error-check this? - if (mDoneInit && mPlaybin) - { - llgst_element_set_state(mPlaybin, GST_STATE_READY); - return true; - } - return false; -} - -bool -MediaPluginGStreamer010::play(double rate) -{ - // NOTE: we don't actually support non-natural rate. - - DEBUGMSG("playing media... rate=%f", rate); - // todo: error-check this? - if (mDoneInit && mPlaybin) - { - llgst_element_set_state(mPlaybin, GST_STATE_PLAYING); - return true; - } - return false; -} - -bool -MediaPluginGStreamer010::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) - { - g_object_set(mPlaybin, "volume", mVolume, NULL); - return true; - } - - return false; -} - -bool -MediaPluginGStreamer010::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); - } - DEBUGMSG("MEDIA SEEK REQUEST to %fsec result was %d", - float(time_sec), int(success)); - return success; -} - -bool -MediaPluginGStreamer010::getTimePos(double &sec_out) -{ - bool got_position = false; - if (mDoneInit && mPlaybin) - { - gint64 pos; - 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 -MediaPluginGStreamer010::load() -{ - if (!mDoneInit) - return false; // error - - setStatus(STATUS_LOADING); - - DEBUGMSG("setting up media..."); - - mIsLooping = false; - mVolume = 0.1234567; // minor hack to force an initial volume update - - // Create a pumpable main-loop for this media - mPump = g_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", "play"); - 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); - -#if 0 // not quite stable/correct yet - // get a visualizer element (bonus feature!) - char* vis_name = getenv("LL_GST_VIS_NAME"); - if (!vis_name || - (vis_name && std::string(vis_name)!="none")) - { - if (vis_name) - { - mVisualizer = llgst_element_factory_make (vis_name, "vis"); - } - if (!mVisualizer) - { - mVisualizer = llgst_element_factory_make ("libvisual_jess", "vis"); - if (!mVisualizer) - { - mVisualizer = llgst_element_factory_make ("goom", "vis"); - if (!mVisualizer) - { - mVisualizer = llgst_element_factory_make ("libvisual_lv_scope", "vis"); - if (!mVisualizer) - { - // That's okay, we don't NEED this. - } - } - } - } - } -#endif - - if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) { - // instantiate a custom video sink - mVideoSink = - GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo")); - if (!mVideoSink) - { - WARNMSG("Could not instantiate private-slvideo element."); - // todo: cleanup. - setStatus(STATUS_ERROR); - return false; // error - } - - // connect the pieces - g_object_set(mPlaybin, "video-sink", mVideoSink, NULL); - } - - if (mVisualizer) - { - g_object_set(mPlaybin, "vis-plugin", mVisualizer, NULL); - } - - return true; -} - -bool -MediaPluginGStreamer010::unload () -{ - if (!mDoneInit) - return false; // error - - DEBUGMSG("unloading media..."); - - // stop getting callbacks for this bus - g_source_remove(mBusWatchID); - mBusWatchID = 0; - - if (mPlaybin) - { - llgst_element_set_state (mPlaybin, GST_STATE_NULL); - llgst_object_unref (GST_OBJECT (mPlaybin)); - mPlaybin = NULL; - } - - if (mVisualizer) - { - llgst_object_unref (GST_OBJECT (mVisualizer)); - mVisualizer = NULL; - } - - if (mPump) - { - g_main_loop_quit(mPump); - mPump = NULL; - } - - mVideoSink = NULL; - - setStatus(STATUS_NONE); - - return true; -} - - -//static -bool -MediaPluginGStreamer010::startup() -{ - // first - check if GStreamer is explicitly disabled - if (NULL != getenv("LL_DISABLE_GSTREAMER")) - return false; - - // only do global GStreamer initialization once. - if (!mDoneInit) - { - g_thread_init(NULL); - - // Init the glib type system - we need it. - g_type_init(); - - // Get symbols! -#if LL_DARWIN - if (! grab_gst_syms("libgstreamer-0.10.dylib", - "libgstvideo-0.10.dylib") ) -#elseif LL_WINDOWS - if (! grab_gst_syms("libgstreamer-0.10.dll", - "libgstvideo-0.10.dll") ) -#else // linux or other ELFy unixoid - if (! grab_gst_syms("libgstreamer-0.10.so.0", - "libgstvideo-0.10.so.0") ) -#endif - { - WARNMSG("Couldn't find suitable GStreamer 0.10 support on this system - video playback disabled."); - return false; - } - - if (llgst_segtrap_set_enabled) - { - llgst_segtrap_set_enabled(FALSE); - } - else - { - WARNMSG("gst_segtrap_set_enabled() is not available; plugin crashes won't be caught."); - } - -#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); - - // 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) - { - WARNMSG("GST init failed: %s", err->message); - g_error_free(err); - } - else - { - WARNMSG("GST init failed for unspecified reason."); - } - return false; - } - - // Init our custom plugins - only really need do this once. - gst_slvideo_init_class(); - - mDoneInit = true; - } - - return true; -} - - -void -MediaPluginGStreamer010::sizeChanged() -{ - // the shared writing space has possibly changed size/location/whatever - - // Check to see whether the movie's NATURAL size has been set yet - if (1 == mNaturalWidth && - 1 == mNaturalHeight) - { - mNaturalWidth = mCurrentWidth; - mNaturalHeight = mCurrentHeight; - DEBUGMSG("Media NATURAL size better detected as %dx%d", - mNaturalWidth, mNaturalHeight); - } - - // if the size has changed then the shm has changed and the app needs telling - if (mCurrentWidth != mPreviousWidth || - mCurrentHeight != mPreviousHeight) - { - mPreviousWidth = mCurrentWidth; - mPreviousHeight = mCurrentHeight; - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request"); - message.setValue("name", mTextureSegmentName); - message.setValueS32("width", mNaturalWidth); - message.setValueS32("height", mNaturalHeight); - DEBUGMSG("<--- Sending size change request to application with name: '%s' - natural size is %d x %d", mTextureSegmentName.c_str(), mNaturalWidth, mNaturalHeight); - sendMessage(message); - } -} - - - -//static -bool -MediaPluginGStreamer010::closedown() -{ - if (!mDoneInit) - return false; // error - - ungrab_gst_syms(); - - mDoneInit = false; - - return true; -} - -MediaPluginGStreamer010::~MediaPluginGStreamer010() -{ - DEBUGMSG("MediaPluginGStreamer010 destructor"); - - closedown(); - - DEBUGMSG("GStreamer010 closing down"); -} - - -std::string -MediaPluginGStreamer010::getVersion() -{ - std::string plugin_version = "GStreamer010 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 MediaPluginGStreamer010::receiveMessage(const char *message_string) -{ - //std::cerr << "MediaPluginGStreamer010::receiveMessage: received message: \"" << message_string << "\"" << std::endl; - - LLPluginMessage message_in; - - if(message_in.parse(message_string) >= 0) - { - std::string message_class = message_in.getClass(); - std::string message_name = message_in.getName(); - if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE) - { - if(message_name == "init") - { - LLPluginMessage message("base", "init_response"); - LLSD versions = LLSD::emptyMap(); - versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; - versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; - versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION; - message.setValueLLSD("versions", versions); - - if ( load() ) - { - DEBUGMSG("GStreamer010 media instance set up"); - } - else - { - WARNMSG("GStreamer010 media instance failed to set up"); - } - - 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"); - - std::ostringstream str; - INFOMSG("MediaPluginGStreamer010::receiveMessage: shared memory added, name: %s, size: %d, address: %p", name.c_str(), int(info.mSize), info.mAddress); - - mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); - } - else if(message_name == "shm_remove") - { - std::string name = message_in.getValue("name"); - - DEBUGMSG("MediaPluginGStreamer010::receiveMessage: shared memory remove, name = %s", name.c_str()); - - SharedSegmentMap::iterator iter = mSharedSegments.find(name); - if(iter != mSharedSegments.end()) - { - if(mPixels == iter->second.mAddress) - { - // This is the currently active pixel buffer. Make sure we stop drawing to it. - mPixels = NULL; - mTextureSegmentName.clear(); - - // Make sure the movie decoder is no longer pointed at the shared segment. - sizeChanged(); - } - mSharedSegments.erase(iter); - } - else - { - WARNMSG("MediaPluginGStreamer010::receiveMessage: unknown shared memory region!"); - } - - // Send the response so it can be cleaned up. - LLPluginMessage message("base", "shm_remove_response"); - message.setValue("name", name); - sendMessage(message); - } - else - { - std::ostringstream str; - INFOMSG("MediaPluginGStreamer010::receiveMessage: unknown base message: %s", message_name.c_str()); - } - } - 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; - - mCurrentWidth = 1; - mCurrentHeight = 1; - mPreviousWidth = 1; - mPreviousHeight = 1; - mNaturalWidth = 1; - mNaturalHeight = 1; - mWidth = 1; - mHeight = 1; - 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", mWidth); - message.setValueS32("default_height", mHeight); - 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"); - - std::ostringstream str; - INFOMSG("---->Got size change instruction from application with shm name: %s - size is %d x %d", name.c_str(), width, 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()) - { - INFOMSG("*** Got size change with matching shm, new size is %d x %d", width, height); - INFOMSG("*** Got size change with matching shm, texture size size is %d x %d", texture_width, texture_height); - - mPixels = (unsigned char*)iter->second.mAddress; - mTextureSegmentName = name; - mWidth = width; - mHeight = height; - - if (texture_width > 1 || - texture_height > 1) // not a dummy size from the app, a real explicit forced size - { - INFOMSG("**** = REAL RESIZE REQUEST FROM APP"); - - GST_OBJECT_LOCK(mVideoSink); - mVideoSink->resize_forced_always = true; - mVideoSink->resize_try_width = texture_width; - mVideoSink->resize_try_height = texture_height; - GST_OBJECT_UNLOCK(mVideoSink); - } - - mTextureWidth = texture_width; - mTextureHeight = texture_height; - } - } - } - 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); - } - } - else - { - INFOMSG("MediaPluginGStreamer010::receiveMessage: unknown message class: %s", message_class.c_str()); - } - } -} - -int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) -{ - if (MediaPluginGStreamer010::startup()) - { - MediaPluginGStreamer010 *self = new MediaPluginGStreamer010(host_send_func, host_user_data); - *plugin_send_func = MediaPluginGStreamer010::staticReceiveMessage; - *plugin_user_data = (void*)self; - - return 0; // okay - } - else - { - return -1; // failed to init - } -} - -#else // LL_GSTREAMER010_ENABLED - -// Stubbed-out class with constructor/destructor (necessary or windows linker -// will just think its dead code and optimize it all out) -class MediaPluginGStreamer010 : public MediaPluginBase -{ -public: - MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); - ~MediaPluginGStreamer010(); - /* virtual */ void receiveMessage(const char *message_string); -}; - -MediaPluginGStreamer010::MediaPluginGStreamer010( - LLPluginInstance::sendMessageFunction host_send_func, - void *host_user_data ) : - MediaPluginBase(host_send_func, host_user_data) -{ - // no-op -} - -MediaPluginGStreamer010::~MediaPluginGStreamer010() -{ - // no-op -} - -void MediaPluginGStreamer010::receiveMessage(const char *message_string) -{ - // no-op -} - -// We're building without GStreamer enabled. Just refuse to initialize. -int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) -{ - return -1; -} - -#endif // LL_GSTREAMER010_ENABLED diff --git a/indra/media_plugins/gstreamer10/CMakeLists.txt b/indra/media_plugins/gstreamer10/CMakeLists.txt new file mode 100644 index 0000000000..14ce5bfaa1 --- /dev/null +++ b/indra/media_plugins/gstreamer10/CMakeLists.txt @@ -0,0 +1,41 @@ +# -*- cmake -*- + +project(media_plugin_gstreamer10) + +include(00-Common) +include(LLCommon) +include(LLImage) +include(LLMath) +include(LLWindow) +include(Linking) +include(PluginAPI) +include(OpenGL) +include(GLIB) + +include(GStreamer10Plugin) + +### media_plugin_gstreamer10 + +set(media_plugin_gstreamer10_SOURCE_FILES + media_plugin_gstreamer10.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 media_plugin_base ll::gstreamer10 ) + +if (WINDOWS) + set_target_properties( + media_plugin_gstreamer10 + PROPERTIES + LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /NODEFAULTLIB:LIBCMT" + ) +endif (WINDOWS) diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h b/indra/media_plugins/gstreamer10/llmediaimplgstreamer.h index cae11a5cb3..cae11a5cb3 100644 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h +++ b/indra/media_plugins/gstreamer10/llmediaimplgstreamer.h 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..6f5bb04bdf --- /dev/null +++ b/indra/media_plugins/gstreamer10/llmediaimplgstreamer_syms_raw.inc @@ -0,0 +1,71 @@ +#define G gstSymbolGrabber + +LL_GRAB_SYM(G, true, gst_buffer_new, GstBuffer*, void) +LL_GRAB_SYM(G, true, gst_structure_set_value, void, GstStructure *, const gchar *, const GValue*) +LL_GRAB_SYM(G, true, gst_init_check, gboolean, int *argc, char **argv[], GError ** err) +LL_GRAB_SYM(G, true, gst_message_get_type, GType, void) +LL_GRAB_SYM(G, true, gst_message_type_get_name, const gchar*, GstMessageType type) +LL_GRAB_SYM(G, true, gst_message_parse_error, void, GstMessage *message, GError **gerror, gchar **debug) +LL_GRAB_SYM(G, true, gst_message_parse_warning, void, GstMessage *message, GError **gerror, gchar **debug) +LL_GRAB_SYM(G, true, gst_message_parse_state_changed, void, GstMessage *message, GstState *oldstate, GstState *newstate, GstState *pending) +LL_GRAB_SYM(G, true, gst_element_set_state, GstStateChangeReturn, GstElement *element, GstState state) +LL_GRAB_SYM(G, true, gst_object_unref, void, gpointer object) +LL_GRAB_SYM(G, true, gst_object_get_type, GType, void) +LL_GRAB_SYM(G, true, gst_pipeline_get_type, GType, void) +LL_GRAB_SYM(G, true, gst_pipeline_get_bus, GstBus*, GstPipeline *pipeline) +LL_GRAB_SYM(G, true, gst_bus_add_watch, guint, GstBus * bus, GstBusFunc func, gpointer user_data) +LL_GRAB_SYM(G, true, gst_element_factory_make, GstElement*, const gchar *factoryname, const gchar *name) +LL_GRAB_SYM(G, true, gst_element_get_type, GType, void) +LL_GRAB_SYM(G, true, gst_static_pad_template_get, GstPadTemplate*, GstStaticPadTemplate *pad_template) +LL_GRAB_SYM(G, true, gst_element_class_add_pad_template, void, GstElementClass *klass, GstPadTemplate *temp) +LL_GRAB_SYM(G, true, gst_caps_from_string, GstCaps *, const gchar *string) +LL_GRAB_SYM(G, true, gst_caps_get_structure, GstStructure *, const GstCaps *caps, guint index) +LL_GRAB_SYM(G, true, gst_element_register, gboolean, GstPlugin *plugin, const gchar *name, guint rank, GType type) +LL_GRAB_SYM(G, true, gst_structure_get_int, gboolean, const GstStructure *structure, const gchar *fieldname, gint *value) +LL_GRAB_SYM(G, true, gst_structure_get_value, const GValue *, const GstStructure *structure, const gchar *fieldname) +LL_GRAB_SYM(G, true, gst_value_get_fraction_numerator, gint, const GValue *value) +LL_GRAB_SYM(G, true, gst_value_get_fraction_denominator, gint, const GValue *value) +LL_GRAB_SYM(G, true, gst_structure_get_name, const gchar *, const GstStructure *structure) +LL_GRAB_SYM(G, true, gst_element_seek, bool, GstElement *, gdouble, GstFormat, GstSeekFlags, GstSeekType, gint64, GstSeekType, gint64) + +LL_GRAB_SYM(G, false, gst_registry_fork_set_enabled, void, gboolean enabled) +LL_GRAB_SYM(G, false, gst_segtrap_set_enabled, void, gboolean enabled) +LL_GRAB_SYM(G, false, gst_message_parse_buffering, void, GstMessage *message, gint *percent) +LL_GRAB_SYM(G, false, gst_message_parse_info, void, GstMessage *message, GError **gerror, gchar **debug) +LL_GRAB_SYM(G, false, gst_element_query_position, gboolean, GstElement *element, GstFormat *format, gint64 *cur) +LL_GRAB_SYM(G, false, gst_version, void, guint *major, guint *minor, guint *micro, guint *nano) + +LL_GRAB_SYM(G, true, gst_message_parse_tag, void, GstMessage *, GstTagList **) +LL_GRAB_SYM(G, true, gst_tag_list_foreach, void, const GstTagList *, GstTagForeachFunc, gpointer) +LL_GRAB_SYM(G, true, gst_tag_list_get_tag_size, guint, const GstTagList *, const gchar *) +LL_GRAB_SYM(G, true, gst_tag_list_get_value_index, const GValue *, const GstTagList *, const gchar *, guint) + +LL_GRAB_SYM(G, true, gst_caps_new_simple, GstCaps*, const char *, const char*, ... ) + +LL_GRAB_SYM(G, true, gst_sample_get_caps, GstCaps*, GstSample* ) +LL_GRAB_SYM(G, true, gst_sample_get_buffer, GstBuffer*, GstSample* ) +LL_GRAB_SYM(G, true, gst_buffer_map, gboolean, GstBuffer*, GstMapInfo*, GstMapFlags ) +LL_GRAB_SYM(G, true, gst_buffer_unmap, void, GstBuffer*, GstMapInfo* ) + +LL_GRAB_SYM(G, true, gst_app_sink_set_caps, void, GstAppSink*, GstCaps const* ) +LL_GRAB_SYM(G, true, gst_app_sink_pull_sample, GstSample*, GstAppSink* ) + +LL_GRAB_SYM(G, true, g_free, void, gpointer ) +LL_GRAB_SYM(G, true, g_error_free, void, GError* ) + +LL_GRAB_SYM(G, true, g_main_context_pending, gboolean, GMainContext* ) +LL_GRAB_SYM(G, true, g_main_loop_get_context, GMainContext*, GMainLoop* ) +LL_GRAB_SYM(G, true, g_main_context_iteration, gboolean, GMainContext*, gboolean ) +LL_GRAB_SYM(G, true, g_main_loop_new, GMainLoop*, GMainContext*, gboolean ) +LL_GRAB_SYM(G, true, g_main_loop_quit, void, GMainLoop* ) +LL_GRAB_SYM(G, true, gst_mini_object_unref, void, GstMiniObject* ) +LL_GRAB_SYM(G, true, g_object_set, void, gpointer, gchar const*, ... ) +LL_GRAB_SYM(G, true, g_source_remove, gboolean, guint ) +LL_GRAB_SYM(G, true, g_value_get_string, gchar const*, GValue const* ) + +LL_GRAB_SYM(G, true, gst_debug_set_active, void, gboolean ) +LL_GRAB_SYM(G, true, gst_debug_add_log_function, void, GstLogFunction, gpointer, GDestroyNotify ) +LL_GRAB_SYM(G, true, gst_debug_set_default_threshold, void, GstDebugLevel ) +LL_GRAB_SYM(G, true, gst_debug_message_get , gchar const*, GstDebugMessage * ) + +#undef G 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..3f636915ea --- /dev/null +++ b/indra/media_plugins/gstreamer10/media_plugin_gstreamer10.cpp @@ -0,0 +1,958 @@ +/** + * @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 <gst/gst.h> +#include <gst/app/gstappsink.h> +} + +SymbolGrabber gstSymbolGrabber; + +#include "llmediaimplgstreamer_syms_raw.inc" + +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 ( nullptr ) + , mPlaybin ( nullptr ) + , mAppSink ( nullptr ) + , 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 = nullptr; + gchar *debug = nullptr; + + 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 = nullptr; + gchar *debug = nullptr; + + llgst_message_parse_info (message, &err, &debug); + if (err) + llg_error_free (err); + llg_free (debug); + } + break; + } + case GST_MESSAGE_WARNING: + { + GError *err = nullptr; + gchar *debug = nullptr; + + 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 (nullptr == mPump || nullptr == mPlaybin) + { + setStatus(STATUS_ERROR); + return false; // error + } + + llg_object_set (G_OBJECT (mPlaybin), "uri", urlIn.c_str(), nullptr); + + // 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 (nullptr == mPump || nullptr == 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 = 0, height = 0; + GstStructure *pStruct = llgst_caps_get_structure ( pCaps, 0); + + if(!llgst_structure_get_int ( pStruct, "width", &width) ) + width = 0; + if(!llgst_structure_get_int ( pStruct, "height", &height) ) + height = 0; + + if( !mPixels || width == 0 || height == 0) + 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, nullptr); + 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 (nullptr, 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, + nullptr ); + + 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, nullptr); + + 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 = nullptr; + } + + if (mPump) + { + llg_main_loop_quit(mPump); + mPump = nullptr; + } + + mAppSink = nullptr; + + 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 ) +{ + std::cerr << file << ":" << line << "(" << function << "): " << llgst_debug_message_get( message ) << std::endl; +} + +//static +bool MediaPluginGStreamer10::startup() +{ + // first - check if GStreamer is explicitly disabled + if (nullptr != getenv("LL_DISABLE_GSTREAMER")) + return false; + + // only do global GStreamer initialization once. + if (!mDoneInit) + { + ll_init_apr(); + + // Get symbols! + std::vector< std::string > vctDSONames; + 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" ); + if( !gstSymbolGrabber.grabSymbols( vctDSONames ) ) + return false; + + if (llgst_segtrap_set_enabled) + { + llgst_segtrap_set_enabled(FALSE); + } + + // 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); + } + // Protect against GStreamer resetting the locale, yuck. + static std::string saved_locale; + saved_locale = setlocale(LC_ALL, nullptr); + + llgst_debug_set_default_threshold( GST_LEVEL_WARNING ); + llgst_debug_add_log_function( LogFunction, nullptr, nullptr ); + llgst_debug_set_active( false ); + + // finally, try to initialize GStreamer! + GError *err = nullptr; + gboolean init_gst_success = llgst_init_check(nullptr, nullptr, &err); + + // restore old locale + setlocale(LC_ALL, saved_locale.c_str() ); + + // restore old SIGCHLD handler + if (!llgst_registry_fork_set_enabled) + sigaction(SIGCHLD, &oldact, nullptr); + + 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 + + gstSymbolGrabber.ungrabSymbols(); + 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 = nullptr; + 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/media_plugins/libvlc/CMakeLists.txt b/indra/media_plugins/libvlc/CMakeLists.txt index a3c1c4ef99..eaf9dde981 100644 --- a/indra/media_plugins/libvlc/CMakeLists.txt +++ b/indra/media_plugins/libvlc/CMakeLists.txt @@ -13,13 +13,6 @@ include(LibVLCPlugin) ### media_plugin_libvlc -if(NOT ADDRESS_SIZE EQUAL 32) - if(WINDOWS) - ##add_definitions(/FIXED:NO) - else(WINDOWS) # not windows therefore gcc LINUX and DARWIN - add_definitions(-fPIC) - endif(WINDOWS) -endif(NOT ADDRESS_SIZE EQUAL 32) set(media_plugin_libvlc_SOURCE_FILES media_plugin_libvlc.cpp diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index d4c2875c6c..cc5c7f5d12 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1448,7 +1448,6 @@ if (LINUX) PROPERTIES COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}" ) - LIST(APPEND viewer_SOURCE_FILES llappviewerlinux_api_dbus.cpp) SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed") endif (LINUX) @@ -2375,4 +2374,3 @@ if (LL_TESTS) endif (LL_TESTS) check_message_template(${VIEWER_BINARY_NAME}) - diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 235d294849..0f0b8d4c2a 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4931,6 +4931,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>MediaPluginPipeWireVolumeCatcher</key> + <map> + <key>Comment</key> + <string>Use PipeWire instead of PulseAudio for controlling web media volume.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>MediaControlFadeTime</key> <map> <key>Comment</key> diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl index 210ecce8db..e9f849a8c0 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl @@ -79,7 +79,7 @@ float getAmbientClamp(); void mirrorClip(vec3 pos); -void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, +void sampleReflectionProbesLegacy(out vec3 ambenv, out vec3 glossenv, out vec3 legacyenv, vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit_linear); vec3 calcPointLightOrSpotLight(vec3 light_col, vec3 diffuse, vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float fa, float is_pointlight, float ambiance) diff --git a/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl index d178bf22b6..35848ff4cd 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl @@ -37,7 +37,7 @@ void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent, vec3 amblit_linear) { ambenv = vec3(reflection_probe_ambiance * 0.25); - + vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); vec3 env_vec = env_mat * refnormpersp; glossenv = srgb_to_linear(texture(environmentMap, env_vec).rgb); @@ -55,11 +55,11 @@ vec4 sampleReflectionProbesDebug(vec3 pos) return vec4(0, 0, 0, 0); } -void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, +void sampleReflectionProbesLegacy(out vec3 ambenv, out vec3 glossenv, out vec3 legacyenv, vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit_linear) { ambenv = vec3(reflection_probe_ambiance * 0.25); - + vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); vec3 env_vec = env_mat * refnormpersp; @@ -70,7 +70,7 @@ void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm) { - + } void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity) diff --git a/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl b/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl index 03dc3d7113..5e38864d38 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl @@ -48,7 +48,7 @@ vec3 linear_to_srgb(vec3 c); vec3 srgb_to_linear(vec3 c); // reflection probe interface -void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, +void sampleReflectionProbesLegacy(out vec3 ambenv, out vec3 glossenv, out vec3 legacyenv, vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit_linear); void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity); diff --git a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl index 26ab0406f6..5ee9aea09d 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl @@ -60,7 +60,7 @@ out vec4 frag_color; float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen); #endif -void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, +void sampleReflectionProbesLegacy(out vec3 ambenv, out vec3 glossenv, out vec3 legacyenv, vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit_linear); void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm); void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity); @@ -280,10 +280,10 @@ float getShadow(vec3 pos, vec3 norm) #if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND) return sampleDirectionalShadow(pos, norm, vary_texcoord0.xy); #else - return 1; + return 1.; #endif #else - return 1; + return 1.; #endif } diff --git a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl index 5dfa196cf6..4bae7b6deb 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl @@ -837,7 +837,7 @@ vec4 sampleReflectionProbesDebug(vec3 pos) return col; } -void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, +void sampleReflectionProbesLegacy(out vec3 ambenv, out vec3 glossenv, out vec3 legacyenv, vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit) { float reflection_lods = max_probe_lod; diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index ca88fe7482..4231d8580e 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -69,7 +69,7 @@ vec3 scaleSoftClipFragLinear(vec3 l); // reflection probe interface void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent, vec3 amblit_linear); -void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, +void sampleReflectionProbesLegacy(out vec3 ambenv, out vec3 glossenv, out vec3 legacyenv, vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit_linear); void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm); void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity); diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh index eb3ead433b..a027aaf6d1 100755 --- a/indra/newview/linux_tools/wrapper.sh +++ b/indra/newview/linux_tools/wrapper.sh @@ -58,21 +58,14 @@ fi ## - Avoids an often-buggy X feature that doesn't really benefit us anyway. export SDL_VIDEO_X11_DGAMOUSE=0 -## - Works around a problem with misconfigured 64-bit systems not finding GL -I386_MULTIARCH="$(dpkg-architecture -ai386 -qDEB_HOST_MULTIARCH 2>/dev/null)" -MULTIARCH_ERR=$? -if [ $MULTIARCH_ERR -eq 0 ]; then - echo 'Multi-arch support detected.' - MULTIARCH_GL_DRIVERS="/usr/lib/${I386_MULTIARCH}/dri" - export LIBGL_DRIVERS_PATH="${LIBGL_DRIVERS_PATH}:${MULTIARCH_GL_DRIVERS}:/usr/lib64/dri:/usr/lib32/dri:/usr/lib/dri" -else - export LIBGL_DRIVERS_PATH="${LIBGL_DRIVERS_PATH}:/usr/lib64/dri:/usr/lib32/dri:/usr/lib/dri" -fi - ## - The 'scim' GTK IM module widely crashes the viewer. Avoid it. if [ "$GTK_IM_MODULE" = "scim" ]; then export GTK_IM_MODULE=xim fi +if [ "$XMODIFIERS" = "" ]; then + ## IME is valid only for fcitx, not when using ibus + export XMODIFIERS="@im=fcitx" +fi ## - Automatically work around the ATI mouse cursor crash bug: ## (this workaround is disabled as most fglrx users do not see the bug) @@ -98,25 +91,6 @@ cd "${RUN_PATH}" ## Before we mess with LD_LIBRARY_PATH, save the old one to restore for ## subprocesses that care. export SAVED_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}" - -# if [ -n "$LL_TCMALLOC" ]; then -# tcmalloc_libs='/usr/lib/libtcmalloc.so.0 /usr/lib/libstacktrace.so.0 /lib/libpthread.so.0' -# all=1 -# for f in $tcmalloc_libs; do -# if [ ! -f $f ]; then -# all=0 -# fi -# done -# if [ $all != 1 ]; then -# echo 'Cannot use tcmalloc libraries: components missing' 1>&2 -# else -# export LD_PRELOAD=$(echo $tcmalloc_libs | tr ' ' :) -# if [ -z "$HEAPCHECK" -a -z "$HEAPPROFILE" ]; then -# export HEAPCHECK=${HEAPCHECK:-normal} -# fi -# fi -#fi - export LD_LIBRARY_PATH="$PWD/lib:${LD_LIBRARY_PATH}" # Copy "$@" to ARGS array specifically to delete the --skip-gridargs switch. @@ -140,18 +114,6 @@ LL_RUN_ERR=$? if [ $LL_RUN_ERR -ne 0 ]; then # generic error running the binary echo '*** Bad shutdown ($LL_RUN_ERR). ***' - if [ "$(uname -m)" = "x86_64" ]; then - echo - cat << EOFMARKER -You are running the Second Life Viewer on a x86_64 platform. The -most common problems when launching the Viewer (particularly -'bin/do-not-directly-run-secondlife-bin: not found' and 'error while -loading shared libraries') may be solved by installing your Linux -distribution's 32-bit compatibility packages. -For example, on Ubuntu and other Debian-based Linuxes you might run: -$ sudo apt-get install ia32-libs ia32-libs-gtk ia32-libs-kde ia32-libs-sdl -EOFMARKER - fi fi echo diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp index ce4e8e9392..11c5ffecb6 100644 --- a/indra/newview/llaisapi.cpp +++ b/indra/newview/llaisapi.cpp @@ -1385,8 +1385,6 @@ void AISUpdate::parseCategory(const LLSD& category_map, S32 depth) && curr_cat->getVersion() > LLViewerInventoryCategory::VERSION_UNKNOWN && version > curr_cat->getVersion()) { - // Potentially should new_cat->setVersion(unknown) here, - // but might be waiting for a callback that would increment LL_DEBUGS("Inventory") << "Category " << category_id << " is stale. Known version: " << curr_cat->getVersion() << " server version: " << version << LL_ENDL; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index a10ef451e1..a0d5ba8de9 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -265,10 +265,6 @@ using namespace LL; // define a self-registering event API object #include "llappviewerlistener.h" -#if LL_LINUX && LL_GTK -#include "glib.h" -#endif // (LL_LINUX) && LL_GTK - #if LL_MSVC // disable boost::lexical_cast warning #pragma warning (disable:4702) @@ -1122,7 +1118,7 @@ bool LLAppViewer::init() gGLActive = false; -#if LL_RELEASE_FOR_DOWNLOAD +#if LL_RELEASE_FOR_DOWNLOAD && !LL_LINUX // Skip updater if this is a non-interactive instance if (!gSavedSettings.getBOOL("CmdLineSkipUpdater") && !gNonInteractive) { @@ -2352,6 +2348,14 @@ void LLAppViewer::initLoggingAndGetLastDuration() { LL_WARNS("MarkerFile") << duration_log_msg << LL_ENDL; } + + std::string user_data_path_cef_log = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef.log"); + if (gDirUtilp->fileExists(user_data_path_cef_log)) + { + std::string user_data_path_cef_old = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef.old"); + LLFile::remove(user_data_path_cef_old, ENOENT); + LLFile::rename(user_data_path_cef_log, user_data_path_cef_old); + } } } @@ -2991,9 +2995,10 @@ void LLAppViewer::initStrings() std::string strings_path_full = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, strings_file); if (strings_path_full.empty() || !LLFile::isfile(strings_path_full)) { + std::string crash_reason; if (strings_path_full.empty()) { - LL_WARNS() << "The file '" << strings_file << "' is not found" << LL_ENDL; + crash_reason = "The file '" + strings_file + "' is not found"; } else { @@ -3001,24 +3006,23 @@ void LLAppViewer::initStrings() int rc = LLFile::stat(strings_path_full, &st); if (rc != 0) { - LL_WARNS() << "The file '" << strings_path_full << "' failed to get status. Error code: " << rc << LL_ENDL; + crash_reason = "The file '" + strings_path_full + "' failed to get status. Error code: " + std::to_string(rc); } else if (S_ISDIR(st.st_mode)) { - LL_WARNS() << "The filename '" << strings_path_full << "' is a directory name" << LL_ENDL; + crash_reason = "The filename '" + strings_path_full + "' is a directory name"; } else { - LL_WARNS() << "The filename '" << strings_path_full << "' doesn't seem to be a regular file name" << LL_ENDL; + crash_reason = "The filename '" + strings_path_full + "' doesn't seem to be a regular file name"; } } // initial check to make sure files are there failed gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN); LLError::LLUserWarningMsg::showMissingFiles(); - LL_ERRS() << "Viewer failed to find localization and UI files." - << " Please reinstall viewer from https://secondlife.com/support/downloads" - << " and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL; + LL_ERRS() << "Viewer failed to open some of localization and UI files." + << " " << crash_reason << "." << LL_ENDL; } LLTransUtil::parseStrings(strings_file, default_trans_args); LLTransUtil::parseLanguageStrings("language_settings.xml"); @@ -5696,4 +5700,3 @@ void LLAppViewer::metricsSend(bool enable_reporting) // resolution in time. gViewerAssetStats->restart(); } - diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index 1709970156..2f366f0538 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -40,17 +40,57 @@ #include <exception> -#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 <gio/gio.h> +#include <resolv.h> + +#if (__GLIBC__*1000 + __GLIBC_MINOR__) >= 2034 +extern "C" +{ + int __res_nquery(res_state statep, + const char *dname, int qclass, int type, + unsigned char *answer, int anslen) + { + return res_nquery( statep, dname, qclass, type, answer, anslen ); + } + + int __dn_expand(const unsigned char *msg, + const unsigned char *eomorig, + const unsigned char *comp_dn, char *exp_dn, + int length) + { + return dn_expand( msg,eomorig,comp_dn,exp_dn,length); + } +} +#endif + +#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" +#include "jsoncpp/reader.h" // JSON + #endif +#define VIEWERAPI_SERVICE "com.secondlife.ViewerAppAPIService" +#define VIEWERAPI_PATH "/com/secondlife/ViewerAppAPI" +#define VIEWERAPI_INTERFACE "com.secondlife.ViewerAppAPI" + +static const char * DBUS_SERVER = "<node name=\"/com/secondlife/ViewerAppAPI\">\n" + " <interface name=\"com.secondlife.ViewerAppAPI\">\n" + " <annotation name=\"org.freedesktop.DBus.GLib.CSymbol\" value=\"viewer_app_api\"/>\n" + " <method name=\"GoSLURL\">\n" + " <annotation name=\"org.freedesktop.DBus.GLib.CSymbol\" value=\"dispatchSLURL\"/>\n" + " <arg type=\"s\" name=\"slurl\" direction=\"in\" />\n" + " </method>\n" + " </interface>\n" + "</node>"; + +typedef struct +{ + GObject parent; +} ViewerAppAPI; + namespace { int gArgC = 0; @@ -81,6 +121,8 @@ int main( int argc, char **argv ) // install unexpected exception handler gOldTerminateHandler = std::set_terminate(exceptionTerminateHandler); + unsetenv( "LD_PRELOAD" ); // <FS:ND/> Get rid of any preloading, we do not want this to happen during startup of plugins. + bool ok = viewer_app_ptr->init(); if(!ok) { @@ -114,21 +156,74 @@ LLAppViewerLinux::~LLAppViewerLinux() { } -bool LLAppViewerLinux::init() +#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() { - // g_thread_init() must be called before *any* use of glib, *and* - // before any mutexes are held, *and* some of our third-party - // libraries likes to use glib functions; in short, do this here - // really early in app startup! - if (!g_thread_supported ()) g_thread_init (NULL); + 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() +{ bool success = LLAppViewer::init(); #if LL_SEND_CRASH_REPORTS - if (success) + 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 ) { - LLAppViewer* pApp = LLAppViewer::instance(); - pApp->initCrashReporting(); + if( nCrashSubmitBehavior == CRASH_BEHAVIOR_ASK ) + gCrashBehavior = "ask"; + else + gCrashBehavior = "send"; + setupBreadpad(); } #endif @@ -143,7 +238,7 @@ bool LLAppViewerLinux::restoreErrorTrap() } ///////////////////////////////////////// -#if LL_DBUS_ENABLED +#if LL_GLIB typedef struct { @@ -153,101 +248,77 @@ typedef struct 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) - G_DEFINE_TYPE(ViewerAppAPI, viewerappapi, G_TYPE_OBJECT); 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. - - 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); + LL_INFOS() << "Was asked to go to slurl: " << slurl << LL_ENDL; - lldbus_g_connection_register_g_object(server->connection, VIEWERAPI_PATH, G_OBJECT(server)); + std::string url = slurl; + LLMediaCtrl* web = NULL; + const bool trusted_browser = false; + LLURLDispatcher::dispatch(url, "", web, trusted_browser); +} - DBusGProxy *serverproxy = lldbus_g_proxy_new_for_name(server->connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); +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) +{ + LL_INFOS() << "DBUS message " << method_name << " from: " << sender << " interface: " << interface_name << LL_ENDL; + const gchar *slurl; - 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_variant_get (parameters, "(&s)", &slurl); + dispatchSLURL(slurl); +} - g_object_unref(serverproxy); - } - else +GDBusNodeInfo *gBusNodeInfo = nullptr; +static const GDBusInterfaceVTable interface_vtable = { - g_warning("Unable to connect to dbus: %s", error->message); - } - - if (error) - g_error_free(error); - } + 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); } -gboolean viewer_app_api_GoSLURL(ViewerAppAPI *obj, gchar *slurl, gboolean **success_rtn, GError **error) +static void nameAcquired(GDBusConnection *connection, const gchar *name, gpointer user_data) { - bool success = false; - - LL_INFOS() << "Was asked to go to slurl: " << slurl << LL_ENDL; +} - 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; - } +static void nameLost(GDBusConnection *connection, const gchar *name, gpointer user_data) +{ - *success_rtn = g_new (gboolean, 1); - (*success_rtn)[0] = (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. } /// @@ -255,13 +326,6 @@ gboolean viewer_app_api_GoSLURL(ViewerAppAPI *obj, gchar *slurl, gboolean **succ //virtual bool LLAppViewerLinux::initSLURLHandler() { - if (!grab_dbus_syms(DBUSGLIB_DYLIB_DEFAULT_NAME)) - { - return false; // failed - } - - g_type_init(); - //ViewerAppAPI *api_server = (ViewerAppAPI*) g_object_new(viewerappapi_get_type(), NULL); @@ -271,49 +335,49 @@ bool LLAppViewerLinux::initSLURLHandler() //virtual bool LLAppViewerLinux::sendURLToOtherInstance(const std::string& url) { - if (!grab_dbus_syms(DBUSGLIB_DYLIB_DEFAULT_NAME)) + auto *pBus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, nullptr); + + if( !pBus ) { - return false; // failed + LL_WARNS() << "Getting dbus failed." << LL_ENDL; + return false; } - bool success = false; - DBusGConnection *bus; - GError *error = NULL; - - g_type_init(); + auto pProxy = g_dbus_proxy_new_sync(pBus, G_DBUS_PROXY_FLAGS_NONE, nullptr, + VIEWERAPI_SERVICE, VIEWERAPI_PATH, + VIEWERAPI_INTERFACE, nullptr, nullptr); - bus = lldbus_g_bus_get (DBUS_BUS_SESSION, &error); - if (bus) + if( !pProxy ) { - gboolean rtn = FALSE; - DBusGProxy *remote_object = - lldbus_g_proxy_new_for_name(bus, VIEWERAPI_SERVICE, VIEWERAPI_PATH, VIEWERAPI_INTERFACE); - - 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; - } - - g_object_unref(G_OBJECT(remote_object)); + LL_WARNS() << "Cannot create new dbus proxy." << LL_ENDL; + g_object_unref( pBus ); + return false; } - else + + auto *pArgs = g_variant_new( "(s)", url.c_str() ); + if( !pArgs ) { - LL_WARNS() << "Couldn't connect to session bus: " << error->message << LL_ENDL; + LL_WARNS() << "Cannot create new variant." << LL_ENDL; + g_object_unref( pBus ); + return false; } - if (error) - g_error_free(error); + auto pRes = g_dbus_proxy_call_sync(pProxy, + "GoSLURL", + pArgs, + G_DBUS_CALL_FLAGS_NONE, + -1, nullptr, nullptr); - return success; + + + 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 @@ -322,7 +386,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) { @@ -338,15 +402,18 @@ void LLAppViewerLinux::initCrashReporting(bool reportFreeze) pid_str << LLApp::getPid(); std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, ""); std::string appname = gDirUtilp->getExecutableFilename(); + std::string grid{ LLGridManager::getInstance()->getGridId() }; + std::string title{ LLAppViewer::instance()->getSecondLifeTitle() }; + std::string pidstr{ pid_str.str() }; // launch the actual crash logger const char * cmdargv[] = {cmd.c_str(), "-user", - (char*)LLGridManager::getInstance()->getGridId().c_str(), + grid.c_str(), "-name", - LLAppViewer::instance()->getSecondLifeTitle().c_str(), + title.c_str(), "-pid", - pid_str.str().c_str(), + pidstr.c_str(), "-dumpdir", logdir.c_str(), "-procname", diff --git a/indra/newview/llappviewerlinux.h b/indra/newview/llappviewerlinux.h index dde223878d..460ca721f1 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 <glib.h> -} - -#if LL_DBUS_ENABLED -extern "C" { -# include <glib-object.h> -# include <dbus/dbus-glib.h> -} -#endif - #ifndef LL_LLAPPVIEWER_H #include "llappviewer.h" #endif @@ -70,21 +59,4 @@ protected: virtual bool sendURLToOtherInstance(const std::string& url); }; -#if LL_DBUS_ENABLED -typedef struct -{ - GObject parent; - DBusGConnection *connection; -} ViewerAppAPI; - -extern "C" { - 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 3d1324dd19..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 <glib-object.h> - -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 <dbus/dbus-glib.h> -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 @@ -<?xml version="1.0" encoding="UTF-8" ?> - -<!-- dbus-binding-tool -mode=glib-server llappviewerlinux_api.xml -prefix=viewerapp -output=llappviewerlinux_api.h --> - -<node name="/com/secondlife/ViewerAppAPI"> - <interface name="com.secondlife.ViewerAppAPI"> - <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="viewer_app_api"/> - <method name="GoSLURL"> - <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="viewer_app_api_GoSLURL"/> - <arg type="s" name="slurl" direction="in" /> - <arg type="b" name="success_ret" direction="out" /> - </method> - </interface> -</node> diff --git a/indra/newview/llappviewerlinux_api_dbus.cpp b/indra/newview/llappviewerlinux_api_dbus.cpp deleted file mode 100644 index 9aed8a98d4..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 <dbus/dbus-glib.h> - -#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.h b/indra/newview/llappviewerlinux_api_dbus.h deleted file mode 100644 index 2f4492bd7a..0000000000 --- a/indra/newview/llappviewerlinux_api_dbus.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @file llappviewerlinux_api_dbus.h - * @brief DBus-glib symbol handling - * - * $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$ - */ - -#include "linden_common.h" - -#if LL_DBUS_ENABLED - -extern "C" { -#include <dbus/dbus-glib.h> -} - -#define DBUSGLIB_DYLIB_DEFAULT_NAME "libdbus-glib-1.so.2" - -bool grab_dbus_syms(std::string dbus_dso_name); -void ungrab_dbus_syms(); - -#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_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/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index c4c6dd3327..625a7b7fba 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -389,17 +389,10 @@ void ll_nvapi_init(NvDRSSessionHandle hSession) } } -//#define DEBUGGING_SEH_FILTER 1 -#if DEBUGGING_SEH_FILTER -# define WINMAIN DebuggingWinMain -#else -# define WINMAIN wWinMain -#endif - -int APIENTRY WINMAIN(HINSTANCE hInstance, - HINSTANCE hPrevInstance, - PWSTR pCmdLine, - int nCmdShow) +int APIENTRY wWinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + PWSTR pCmdLine, + int nCmdShow) { // Call Tracy first thing to have it allocate memory // https://github.com/wolfpld/tracy/issues/196 @@ -548,27 +541,6 @@ int APIENTRY WINMAIN(HINSTANCE hInstance, return 0; } -#if DEBUGGING_SEH_FILTER -// The compiler doesn't like it when you use __try/__except blocks -// in a method that uses object destructors. Go figure. -// This winmain just calls the real winmain inside __try. -// The __except calls our exception filter function. For debugging purposes. -int APIENTRY wWinMain(HINSTANCE hInstance, - HINSTANCE hPrevInstance, - PWSTR lpCmdLine, - int nCmdShow) -{ - __try - { - WINMAIN(hInstance, hPrevInstance, lpCmdLine, nCmdShow); - } - __except( viewer_windows_exception_handler( GetExceptionInformation() ) ) - { - _tprintf( _T("Exception handled.\n") ); - } -} -#endif - void LLAppViewerWin32::disableWinErrorReporting() { std::string executable_name = gDirUtilp->getExecutableFilename(); diff --git a/indra/newview/llconversationloglist.cpp b/indra/newview/llconversationloglist.cpp index 65863f0a5e..3e08c6ee60 100644 --- a/indra/newview/llconversationloglist.cpp +++ b/indra/newview/llconversationloglist.cpp @@ -94,7 +94,9 @@ bool LLConversationLogList::handleRightMouseDown(S32 x, S32 y, MASK mask) if (context_menu && size()) { context_menu->buildDrawLabels(); - context_menu->updateParent(LLMenuGL::sMenuContainer); + if (context_menu && size()) + context_menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, context_menu, x, y); } diff --git a/indra/newview/lldirpicker.cpp b/indra/newview/lldirpicker.cpp index 1423ca1b9b..5df3ed1ba4 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 + #if LL_WINDOWS #include <shlobj.h> #endif @@ -214,20 +219,28 @@ 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(); +#else + mDir = ""; +#endif } bool LLDirPicker::getDir(std::string* filename, bool blocking) @@ -240,6 +253,25 @@ bool LLDirPicker::getDir(std::string* filename, bool blocking) return false; } +#ifdef LL_FLTK + 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 #if !LL_MESA_HEADLESS if (mFilePicker) @@ -262,11 +294,15 @@ bool LLDirPicker::getDir(std::string* filename, bool blocking) 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 dc740caab2..2ac3db7c2e 100644 --- a/indra/newview/lldirpicker.h +++ b/indra/newview/lldirpicker.h @@ -77,8 +77,10 @@ private: #if LL_LINUX || LL_DARWIN // On Linux we just implement LLDirPicker on top of LLFilePicker +#ifndef LL_FLTK LLFilePicker *mFilePicker; #endif +#endif std::string* mFileName; diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index f73c530ff9..e45549136e 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -57,12 +57,6 @@ #include "llsculptidsize.h" #include "llmeshrepository.h" -#if LL_LINUX -// Work-around spurious used before init warning on Vector4a -// -#pragma GCC diagnostic ignored "-Wuninitialized" -#endif - #define LL_MAX_INDICES_COUNT 1000000 static LLStaticHashedString sTextureIndexIn("texture_index_in"); @@ -834,7 +828,6 @@ bool LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f, //VECTORIZE THIS LLMatrix4a mat_vert; mat_vert.loadu(mat_vert_in); - LLVector4a new_extents[2]; llassert(less_than_max_mag(face.mExtents[0])); llassert(less_than_max_mag(face.mExtents[1])); diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index aa04221f4b..bf046e200f 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -40,6 +40,7 @@ #include "llappviewer.h" #include "llbufferstream.h" +#include "llexception.h" #include "llnotificationsutil.h" #include "llviewercontrol.h" #include "llworld.h" @@ -377,33 +378,6 @@ bool LLFeatureManager::parseFeatureTable(std::string filename) F32 gpu_benchmark(); -#if LL_WINDOWS - -F32 logExceptionBenchmark() -{ - // FIXME: gpu_benchmark uses many C++ classes on the stack to control state. - // SEH exceptions with our current exception handling options do not call - // destructors for these classes, resulting in an undefined state should - // this handler be invoked. - F32 gbps = -1; - __try - { - gbps = gpu_benchmark(); - } - __except (msc_exception_filter(GetExceptionCode(), GetExceptionInformation())) - { - // HACK - ensure that profiling is disabled - LLGLSLShader::finishProfile(false); - - // convert to C++ styled exception - char integer_string[32]; - sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode()); - throw std::exception(integer_string); - } - return gbps; -} -#endif - bool LLFeatureManager::loadGPUClass() { if (!gSavedSettings.getBOOL("SkipBenchmark")) @@ -413,14 +387,12 @@ bool LLFeatureManager::loadGPUClass() F32 gbps; try { -#if LL_WINDOWS - gbps = logExceptionBenchmark(); -#else - gbps = gpu_benchmark(); -#endif + gbps = LL::seh::catcher(gpu_benchmark); } catch (const std::exception& e) { + // HACK - ensure that profiling is disabled + LLGLSLShader::finishProfile(false); gbps = -1.f; LL_WARNS("RenderInit") << "GPU benchmark failed: " << e.what() << LL_ENDL; } diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h index 75ff14f4cf..b99156c61e 100644 --- a/indra/newview/llfilepicker.h +++ b/indra/newview/llfilepicker.h @@ -54,19 +54,8 @@ #include <commdlg.h> #endif -extern "C" { -// mostly for Linux, possible on others -#if LL_GTK -# include "gtk/gtk.h" -#endif // LL_GTK -} - class LLFilePicker { -#ifdef LL_GTK - friend class LLDirPicker; - friend void chooser_responder(GtkWidget *, gint, gpointer); -#endif // LL_GTK public: // calling this before main() is undefined static LLFilePicker& instance( void ) { return sInstance; } @@ -184,14 +173,12 @@ private: void *userdata); #endif -#if LL_GTK - static void add_to_selectedfiles(gpointer data, gpointer user_data); - static void chooser_responder(GtkWidget *widget, gint response, gpointer user_data); - // we remember the last path that was accessed for a particular usage - std::map <std::string, std::string> mContextToPathMap; - std::string mCurContextName; - // we also remember the extension of the last added file. - std::string mCurrentExtension; +#if LL_FLTK + enum EType + { + eSaveFile, eOpenFile, eOpenMultiple + }; + bool openFileDialog( int32_t filter, bool blocking, EType aType ); #endif std::vector<std::string> mFiles; @@ -200,12 +187,6 @@ private: static LLFilePicker sInstance; -protected: -#if LL_GTK - GtkWindow* buildFilePicker(bool is_save, bool is_folder, - std::string context = "generic"); -#endif - public: // don't call these directly please. LLFilePicker(); diff --git a/indra/newview/llfloatercreatelandmark.cpp b/indra/newview/llfloatercreatelandmark.cpp index 2ce8a7a212..77ea544c5d 100644 --- a/indra/newview/llfloatercreatelandmark.cpp +++ b/indra/newview/llfloatercreatelandmark.cpp @@ -31,6 +31,7 @@ #include "llagent.h" #include "llagentui.h" #include "llcombobox.h" +#include "llfloaterreg.h" #include "llinventoryfunctions.h" #include "llinventoryobserver.h" #include "lllandmarkactions.h" @@ -286,8 +287,7 @@ void LLFloaterCreateLandmark::onCreateFolderClicked() std::string folder_name = resp["message"].asString(); if (!folder_name.empty()) { - inventory_func_type func = boost::bind(&LLFloaterCreateLandmark::folderCreatedCallback, this, _1); - gInventory.createNewCategory(mLandmarksID, LLFolderType::FT_NONE, folder_name, func); + gInventory.createNewCategory(mLandmarksID, LLFolderType::FT_NONE, folder_name, folderCreatedCallback); gInventory.notifyObservers(); } } @@ -296,7 +296,11 @@ void LLFloaterCreateLandmark::onCreateFolderClicked() void LLFloaterCreateLandmark::folderCreatedCallback(LLUUID folder_id) { - populateFoldersList(folder_id); + LLFloaterCreateLandmark* floater = LLFloaterReg::findTypedInstance<LLFloaterCreateLandmark>("add_landmark"); + if (floater && !floater->isDead()) + { + floater->populateFoldersList(folder_id); + } } void LLFloaterCreateLandmark::onSaveClicked() @@ -389,6 +393,7 @@ void LLFloaterCreateLandmark::setItem(const uuid_set_t& items) { mItem = item; mAssetID = mItem->getAssetUUID(); + mParentID = mItem->getParentUUID(); setVisibleAndFrontmost(true); break; } @@ -418,8 +423,7 @@ void LLFloaterCreateLandmark::updateItem(const uuid_set_t& items, U32 mask) closeFloater(); } - LLUUID folder_id = mFolderCombo->getValue().asUUID(); - if (folder_id != mItem->getParentUUID()) + if (mParentID != mItem->getParentUUID()) { // user moved landmark in inventory, // assume that we are done all other changes should already be commited diff --git a/indra/newview/llfloatercreatelandmark.h b/indra/newview/llfloatercreatelandmark.h index fa6d001b8e..bcf9d8578c 100644 --- a/indra/newview/llfloatercreatelandmark.h +++ b/indra/newview/llfloatercreatelandmark.h @@ -62,13 +62,14 @@ private: void onSaveClicked(); void onCancelClicked(); - void folderCreatedCallback(LLUUID folder_id); + static void folderCreatedCallback(LLUUID folder_id); LLComboBox* mFolderCombo; LLLineEditor* mLandmarkTitleEditor; LLTextEditor* mNotesEditor; LLUUID mLandmarksID; LLUUID mAssetID; + LLUUID mParentID; LLLandmarksInventoryObserver* mInventoryObserver; LLPointer<LLInventoryItem> mItem; diff --git a/indra/newview/llfloateremojipicker.cpp b/indra/newview/llfloateremojipicker.cpp index 30f58aaeec..9e69e3d6f9 100644 --- a/indra/newview/llfloateremojipicker.cpp +++ b/indra/newview/llfloateremojipicker.cpp @@ -57,8 +57,7 @@ static const S32 USED_EMOJIS_IMAGE_INDEX = 0x23F2; // https://www.compart.com/en/unicode/U+1F6D1 static const S32 EMPTY_LIST_IMAGE_INDEX = 0x1F6D1; // The following categories should follow the required alphabetic order -static const std::string RECENTLY_USED_CATEGORY = "1 recently used"; -static const std::string FREQUENTLY_USED_CATEGORY = "2 frequently used"; +static const std::string FREQUENTLY_USED_CATEGORY = "frequently used"; // Floater state related variables static std::list<llwchar> sRecentlyUsed; @@ -436,6 +435,7 @@ void LLFloaterEmojiPicker::fillGroups() LLButton::Params params; params.font = LLFontGL::getFontEmojiLarge(); + params.name = "all_categories"; LLRect rect; rect.mTop = mGroups->getRect().getHeight(); @@ -444,11 +444,10 @@ void LLFloaterEmojiPicker::fillGroups() // Create button for "All categories" createGroupButton(params, rect, ALL_EMOJIS_IMAGE_INDEX); - // Create group and button for "Recently used" and/or "Frequently used" - if (!sRecentlyUsed.empty() || !sFrequentlyUsed.empty()) + // Create group and button for "Frequently used" + if (!sFrequentlyUsed.empty()) { std::map<std::string, std::vector<LLEmojiSearchResult>> cats; - fillCategoryRecentlyUsed(cats); fillCategoryFrequentlyUsed(cats); if (!cats.empty()) @@ -479,40 +478,6 @@ void LLFloaterEmojiPicker::fillGroups() resizeGroupButtons(); } -void LLFloaterEmojiPicker::fillCategoryRecentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats) -{ - if (sRecentlyUsed.empty()) - return; - - std::vector<LLEmojiSearchResult> emojis; - - // In case of empty mFilterPattern we'd use sRecentlyUsed directly - if (!mFilterPattern.empty()) - { - // List all emojis in "Recently used" - const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr(); - std::size_t begin, end; - for (llwchar emoji : sRecentlyUsed) - { - auto e2d = emoji2descr.find(emoji); - if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty()) - { - for (const std::string& shortcode : e2d->second->ShortCodes) - { - if (LLEmojiDictionary::searchInShortCode(begin, end, shortcode, mFilterPattern)) - { - emojis.emplace_back(emoji, shortcode, begin, end); - } - } - } - } - if (emojis.empty()) - return; - } - - cats.emplace(std::make_pair(RECENTLY_USED_CATEGORY, emojis)); -} - void LLFloaterEmojiPicker::fillCategoryFrequentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats) { if (sFrequentlyUsed.empty()) @@ -754,7 +719,6 @@ void LLFloaterEmojiPicker::fillEmojisCategory(const std::vector<LLEmojiSearchRes { // Place the category title std::string title = - category == RECENTLY_USED_CATEGORY ? getString("title_for_recently_used") : category == FREQUENTLY_USED_CATEGORY ? getString("title_for_frequently_used") : isupper(category.front()) ? category : LLStringUtil::capitalize(category); LLEmojiGridDivider* div = new LLEmojiGridDivider(row_panel_params, title); @@ -767,21 +731,7 @@ void LLFloaterEmojiPicker::fillEmojisCategory(const std::vector<LLEmojiSearchRes { const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr(); LLEmojiSearchResult emoji { 0, "", 0, 0 }; - if (category == RECENTLY_USED_CATEGORY) - { - for (llwchar code : sRecentlyUsed) - { - const LLEmojiDictionary::emoji2descr_map_t::const_iterator& e2d = emoji2descr.find(code); - if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty()) - { - emoji.Character = code; - emoji.String = e2d->second->ShortCodes.front(); - createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params, - icon_rect, max_icons, bg, row, icon_index); - } - } - } - else if (category == FREQUENTLY_USED_CATEGORY) + if (category == FREQUENTLY_USED_CATEGORY) { for (const auto& code : sFrequentlyUsed) { diff --git a/indra/newview/llfloateremojipicker.h b/indra/newview/llfloateremojipicker.h index 669683eb9e..b807adb67d 100644 --- a/indra/newview/llfloateremojipicker.h +++ b/indra/newview/llfloateremojipicker.h @@ -60,7 +60,6 @@ public: private: void initialize(); void fillGroups(); - void fillCategoryRecentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats); void fillCategoryFrequentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats); void fillGroupEmojis(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats, U32 index); void createGroupButton(LLButton::Params& params, const LLRect& rect, llwchar emoji); diff --git a/indra/newview/llfloaterperformance.cpp b/indra/newview/llfloaterperformance.cpp index 3a633a7ff8..01579ac165 100644 --- a/indra/newview/llfloaterperformance.cpp +++ b/indra/newview/llfloaterperformance.cpp @@ -115,12 +115,12 @@ bool LLFloaterPerformance::postBuild() mHUDList = mHUDsPanel->getChild<LLNameListCtrl>("hud_list"); mHUDList->setNameListType(LLNameListCtrl::SPECIAL); mHUDList->setHoverIconName("StopReload_Off"); - mHUDList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachItem, this, _1)); + mHUDList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachObject, this, _1)); mObjectList = mComplexityPanel->getChild<LLNameListCtrl>("obj_list"); mObjectList->setNameListType(LLNameListCtrl::SPECIAL); mObjectList->setHoverIconName("StopReload_Off"); - mObjectList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachItem, this, _1)); + mObjectList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachObject, this, _1)); mSettingsPanel->getChild<LLButton>("advanced_btn")->setCommitCallback(boost::bind(&LLFloaterPerformance::onClickAdvanced, this)); mSettingsPanel->getChild<LLButton>("defaults_btn")->setCommitCallback(boost::bind(&LLFloaterPerformance::onClickDefaults, this)); @@ -524,9 +524,13 @@ void LLFloaterPerformance::setFPSText() getChild<LLTextBox>("fps_lbl")->setValue(fps_text); } -void LLFloaterPerformance::detachItem(const LLUUID& item_id) +void LLFloaterPerformance::detachObject(const LLUUID& obj_id) { - LLAppearanceMgr::instance().removeItemFromAvatar(item_id); + LLViewerObject* obj = gObjectList.findObject(obj_id); + if (obj) + { + LLAppearanceMgr::instance().removeItemFromAvatar(obj->getAttachmentItemID()); + } } void LLFloaterPerformance::onClickAdvanced() diff --git a/indra/newview/llfloaterperformance.h b/indra/newview/llfloaterperformance.h index 089a508455..797b800b62 100644 --- a/indra/newview/llfloaterperformance.h +++ b/indra/newview/llfloaterperformance.h @@ -46,7 +46,7 @@ public: void hidePanels(); void showAutoadjustmentsPanel(); - void detachItem(const LLUUID& item_id); + void detachObject(const LLUUID& obj_id); void onAvatarListRightClick(LLUICtrl* ctrl, S32 x, S32 y); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 5e5cfcecbf..a1e15da56d 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -329,6 +329,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mCommitCallbackRegistrar.add("Pref.AutoAdjustments", boost::bind(&LLFloaterPreference::onClickAutoAdjustments, this)); mCommitCallbackRegistrar.add("Pref.HardwareDefaults", boost::bind(&LLFloaterPreference::setHardwareDefaults, this)); mCommitCallbackRegistrar.add("Pref.AvatarImpostorsEnable", boost::bind(&LLFloaterPreference::onAvatarImpostorsEnable, this)); + mCommitCallbackRegistrar.add("Pref.UpdateIndirectMaxNonImpostors", boost::bind(&LLFloaterPreference::updateMaxNonImpostors, this)); mCommitCallbackRegistrar.add("Pref.UpdateIndirectMaxComplexity", boost::bind(&LLFloaterPreference::updateMaxComplexity, this)); mCommitCallbackRegistrar.add("Pref.RenderOptionUpdate", boost::bind(&LLFloaterPreference::onRenderOptionEnable, this)); mCommitCallbackRegistrar.add("Pref.WindowedMod", boost::bind(&LLFloaterPreference::onCommitWindowedMode, this)); @@ -360,6 +361,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) LLAvatarPropertiesProcessor::getInstance()->addObserver( gAgent.getID(), this ); mComplexityChangedSignal = gSavedSettings.getControl("RenderAvatarMaxComplexity")->getCommitSignal()->connect(boost::bind(&LLFloaterPreference::updateComplexityText, this)); + mImpostorsChangedSignal = gSavedSettings.getControl("RenderAvatarMaxNonImpostors")->getSignal()->connect(boost::bind(&LLFloaterPreference::updateIndirectMaxNonImpostors, this, _2)); mCommitCallbackRegistrar.add("Pref.ClearLog", boost::bind(&LLConversationLog::onClearLog, &LLConversationLog::instance())); mCommitCallbackRegistrar.add("Pref.DeleteTranscripts", boost::bind(&LLFloaterPreference::onDeleteTranscripts, this)); @@ -536,6 +538,7 @@ LLFloaterPreference::~LLFloaterPreference() { LLConversationLog::instance().removeObserver(this); mComplexityChangedSignal.disconnect(); + mImpostorsChangedSignal.disconnect(); } void LLFloaterPreference::draw() @@ -1290,6 +1293,9 @@ void LLAvatarComplexityControls::setIndirectMaxArc() void LLFloaterPreference::refresh() { LLPanel::refresh(); + setMaxNonImpostorsText( + gSavedSettings.getU32("RenderAvatarMaxNonImpostors"), + getChild<LLTextBox>("IndirectMaxNonImpostorsText", true)); LLAvatarComplexityControls::setText( gSavedSettings.getU32("RenderAvatarMaxComplexity"), getChild<LLTextBox>("IndirectMaxComplexityText", true)); @@ -1568,6 +1574,44 @@ void LLAvatarComplexityControls::setRenderTimeText(F32 value, LLTextBox* text_bo } } +void LLFloaterPreference::updateMaxNonImpostors() +{ + // Called when the IndirectMaxNonImpostors control changes + // Responsible for fixing the slider label (IndirectMaxNonImpostorsText) and setting RenderAvatarMaxNonImpostors + LLSliderCtrl* ctrl = getChild<LLSliderCtrl>("IndirectMaxNonImpostors", true); + U32 value = ctrl->getValue().asInteger(); + + if (0 == value || LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER <= value) + { + value = 0; + } + gSavedSettings.setU32("RenderAvatarMaxNonImpostors", value); + LLVOAvatar::updateImpostorRendering(value); // make it effective immediately + setMaxNonImpostorsText(value, getChild<LLTextBox>("IndirectMaxNonImpostorsText")); +} + +void LLFloaterPreference::updateIndirectMaxNonImpostors(const LLSD& newvalue) +{ + U32 value = newvalue.asInteger(); + if ((value != 0) && (value != gSavedSettings.getU32("IndirectMaxNonImpostors"))) + { + gSavedSettings.setU32("IndirectMaxNonImpostors", value); + } + setMaxNonImpostorsText(value, getChild<LLTextBox>("IndirectMaxNonImpostorsText")); +} + +void LLFloaterPreference::setMaxNonImpostorsText(U32 value, LLTextBox* text_box) +{ + if (0 == value) + { + text_box->setText(LLTrans::getString("no_limit")); + } + else + { + text_box->setText(llformat("%d", value)); + } +} + void LLFloaterPreference::updateMaxComplexity() { // Called when the IndirectMaxComplexity control changes diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index c8b98d8e1b..c26569f17c 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -206,6 +206,9 @@ private: void onDeleteTranscripts(); void onDeleteTranscriptsResponse(const LLSD& notification, const LLSD& response); void updateDeleteTranscriptsButton(); + void updateMaxNonImpostors(); + void updateIndirectMaxNonImpostors(const LLSD& newvalue); + void setMaxNonImpostorsText(U32 value, LLTextBox* text_box); void updateMaxComplexity(); void updateComplexityText(); static bool loadFromFilename(const std::string& filename, std::map<std::string, std::string> &label_map); @@ -229,6 +232,7 @@ private: std::unique_ptr< ll::prefs::SearchData > mSearchData; bool mSearchDataDirty; + boost::signals2::connection mImpostorsChangedSignal; boost::signals2::connection mComplexityChangedSignal; void onUpdateFilterTerm( bool force = false ); diff --git a/indra/newview/llfloaterpreferencesgraphicsadvanced.cpp b/indra/newview/llfloaterpreferencesgraphicsadvanced.cpp index d66c303c65..f840a6124f 100644 --- a/indra/newview/llfloaterpreferencesgraphicsadvanced.cpp +++ b/indra/newview/llfloaterpreferencesgraphicsadvanced.cpp @@ -250,8 +250,8 @@ void LLFloaterPreferenceGraphicsAdvanced::updateIndirectMaxNonImpostors(const LL if ((value != 0) && (value != gSavedSettings.getU32("IndirectMaxNonImpostors"))) { gSavedSettings.setU32("IndirectMaxNonImpostors", value); - setMaxNonImpostorsText(value, getChild<LLTextBox>("IndirectMaxNonImpostorsText")); } + setMaxNonImpostorsText(value, getChild<LLTextBox>("IndirectMaxNonImpostorsText")); } void LLFloaterPreferenceGraphicsAdvanced::setMaxNonImpostorsText(U32 value, LLTextBox* text_box) diff --git a/indra/newview/llfloaterpreferencesgraphicsadvanced.h b/indra/newview/llfloaterpreferencesgraphicsadvanced.h index 61203be068..6f793c1379 100644 --- a/indra/newview/llfloaterpreferencesgraphicsadvanced.h +++ b/indra/newview/llfloaterpreferencesgraphicsadvanced.h @@ -61,6 +61,7 @@ protected: void onBtnOK(const LLSD& userdata); void onBtnCancel(const LLSD& userdata); + boost::signals2::connection mImpostorsChangedSignal; boost::signals2::connection mComplexityChangedSignal; boost::signals2::connection mComplexityModeChangedSignal; boost::signals2::connection mLODFactorChangedSignal; diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 52eddcfc67..fe867ffd14 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -4307,7 +4307,6 @@ void LLPanelRegionEnvironment::onChkAllowOverride(bool value) mAllowOverrideRestore = mAllowOverride; mAllowOverride = value; - std::string notification("EstateParcelEnvironmentOverride"); if (LLPanelEstateInfo::isLindenEstate()) notification = "ChangeLindenEstate"; diff --git a/indra/newview/llhttpretrypolicy.cpp b/indra/newview/llhttpretrypolicy.cpp index 3e55030610..c5692554c6 100644 --- a/indra/newview/llhttpretrypolicy.cpp +++ b/indra/newview/llhttpretrypolicy.cpp @@ -91,14 +91,14 @@ void LLAdaptiveRetryPolicy::onSuccess() void LLAdaptiveRetryPolicy::onFailure(S32 status, const LLSD& headers) { - F32 retry_header_time; + F32 retry_header_time{}; bool has_retry_header_time = getRetryAfter(headers,retry_header_time); onFailureCommon(status, has_retry_header_time, retry_header_time); } void LLAdaptiveRetryPolicy::onFailure(const LLCore::HttpResponse *response) { - F32 retry_header_time; + F32 retry_header_time{}; const LLCore::HttpHeaders::ptr_t headers = response->getHeaders(); bool has_retry_header_time = getRetryAfter(headers,retry_header_time); onFailureCommon(response->getStatus().getType(), has_retry_header_time, retry_header_time); @@ -184,4 +184,3 @@ bool LLAdaptiveRetryPolicy::getSecondsUntilRetryAfter(const std::string& retry_a return true; } - diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 0f2f0ec942..5a82d3b6f8 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -846,7 +846,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, disabled_items.push_back(std::string("Copy")); } - if (isAgentInventory() && !single_folder_root) + if (isAgentInventory() && !single_folder_root && !isMarketplaceListingsFolder()) { items.push_back(std::string("New folder from selected")); items.push_back(std::string("Subfolder Separator")); @@ -5305,7 +5305,7 @@ void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLI // Note: creation will take time, so passing folder id to callback is slightly unreliable, // but so is collecting and passing descendants' ids - inventory_func_type func = boost::bind(&LLFolderBridge::outfitFolderCreatedCallback, this, inv_cat->getUUID(), _1, cb); + inventory_func_type func = boost::bind(outfitFolderCreatedCallback, inv_cat->getUUID(), _1, cb, mInventoryPanel); gInventory.createNewCategory(dest_id, LLFolderType::FT_OUTFIT, inv_cat->getName(), @@ -5313,11 +5313,25 @@ void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLI inv_cat->getThumbnailUUID()); } -void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer<LLInventoryCallback> cb) +void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, + LLUUID cat_dest_id, + LLPointer<LLInventoryCallback> cb, + LLHandle<LLInventoryPanel> inventory_panel) { LLInventoryModel::cat_array_t* categories; LLInventoryModel::item_array_t* items; - getInventoryModel()->getDirectDescendentsOf(cat_source_id, categories, items); + + LLInventoryPanel* panel = inventory_panel.get(); + if (!panel) + { + return; + } + LLInventoryModel* model = panel->getModel(); + if (!model) + { + return; + } + model->getDirectDescendentsOf(cat_source_id, categories, items); LLInventoryObject::const_object_list_t link_array; diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 746b79ce87..3e7f74384b 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -378,7 +378,10 @@ public: static void staticFolderOptionsMenu(); protected: - void outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer<LLInventoryCallback> cb); + static void outfitFolderCreatedCallback(LLUUID cat_source_id, + LLUUID cat_dest_id, + LLPointer<LLInventoryCallback> cb, + LLHandle<LLInventoryPanel> inventory_panel); void callback_pasteFromClipboard(const LLSD& notification, const LLSD& response); void perform_pasteFromClipboard(); void gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level); diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index 8e56ccc01d..dbf4821ca1 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -810,7 +810,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men items.push_back(std::string("Copy Asset UUID")); items.push_back(std::string("Copy Separator")); - bool is_asset_knowable = is_asset_knowable = LLAssetType::lookupIsAssetIDKnowable(obj->getType()); + bool is_asset_knowable = LLAssetType::lookupIsAssetIDKnowable(obj->getType()); if ( !is_asset_knowable // disable menu item for Inventory items with unknown asset. EXT-5308 || (! ( is_full_perm_item || gAgent.isGodlike()))) { diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index d57cb13362..53ea02983a 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -2037,8 +2037,8 @@ void LLInventoryModel::deleteObject(const LLUUID& id, bool fix_broken_links, boo { LL_WARNS(LOG_INV) << "Deleting cat " << id << " while it still has child cats" << LL_ENDL; } - delete cat_list; mParentChildCategoryTree.erase(id); + delete cat_list; } addChangedMask(LLInventoryObserver::REMOVE, id); @@ -5035,4 +5035,3 @@ void LLInventoryModel::FetchItemHttpHandler::processFailure(const char * const r << LLCoreHttpUtil::responseToString(response) << "]" << LL_ENDL; gInventory.notifyObservers(); } - diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index 9cb94b313e..d9ace1542b 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -30,6 +30,7 @@ #include "llaisapi.h" #include "llagent.h" #include "llappviewer.h" +#include "llappearancemgr.h" #include "llcallbacklist.h" #include "llinventorymodel.h" #include "llinventorypanel.h" @@ -470,6 +471,22 @@ void LLInventoryModelBackgroundFetch::fetchCOF(nullary_func_t callback) callback(); LLUUID cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); LLInventoryModelBackgroundFetch::getInstance()->onAISFolderCalback(cat_id, id, FT_DEFAULT); + + if (id.notNull()) + { + // COF might have fetched base outfit folder through a link, but it hasn't + // fetched base outfit's content, which doesn't nessesary match COF, + // so make sure it's up to date + LLUUID baseoutfit_id = LLAppearanceMgr::getInstance()->getBaseOutfitUUID(); + if (baseoutfit_id.notNull()) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(baseoutfit_id); + if (!cat || cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) + { + LLInventoryModelBackgroundFetch::getInstance()->fetchFolderAndLinks(baseoutfit_id, no_op); + } + } + } }); // start idle loop to track completion diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 6c58eb2ca4..e0f35aec70 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -578,8 +578,8 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve if (model_item && view_item && viewmodel_item) { const LLUUID& idp = viewmodel_item->getUUID(); - view_item->destroyView(); removeItemID(idp); + view_item->destroyView(); } LLInventoryObject const* objectp = mInventory->getObject(item_id); diff --git a/indra/newview/llmanip.cpp b/indra/newview/llmanip.cpp index 0c82db1011..0de8f00418 100644 --- a/indra/newview/llmanip.cpp +++ b/indra/newview/llmanip.cpp @@ -574,9 +574,6 @@ 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; - if (fractional_portion != 0) { fraction_string = llformat("%c%02d%s", LLResMgr::getInstance()->getDecimalPoint(), fractional_portion, suffix.c_str()); diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index df573bd785..e2c29bf241 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -134,25 +134,17 @@ std::string getLodSuffix(S32 lod) void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LLModel*& baseModelOut, LLMatrix4& matOut) { - LLModelLoader::scene::iterator base_iter = scene.begin(); - bool found = false; - while (!found && (base_iter != scene.end())) + for (auto scene_iter = scene.begin(); scene_iter != scene.end(); scene_iter++) { - matOut = base_iter->first; - - LLModelLoader::model_instance_list::iterator base_instance_iter = base_iter->second.begin(); - while (!found && (base_instance_iter != base_iter->second.end())) + for (auto model_iter = scene_iter->second.begin(); model_iter != scene_iter->second.end(); model_iter++) { - LLModelInstance& base_instance = *base_instance_iter++; - LLModel* base_model = base_instance.mModel; - - if (base_model && (base_model->mLabel == name_to_match)) + if (model_iter->mModel && (model_iter->mModel->mLabel == name_to_match)) { - baseModelOut = base_model; + baseModelOut = model_iter->mModel; + matOut = scene_iter->first; return; } } - base_iter++; } } @@ -212,9 +204,12 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) LLModelPreview::~LLModelPreview() { + LLMutexLock lock(this); + if (mModelLoader) { mModelLoader->shutdown(); + mModelLoader = NULL; } if (mPreviewAvatar) @@ -262,7 +257,7 @@ void LLModelPreview::updateDimentionsAndOffsets() accounted.insert(instance.mModel); // update instance skin info for each lods pelvisZoffset - for (int j = 0; j<LLModel::NUM_LODS; ++j) + for (int j = 0; j < LLModel::NUM_LODS; ++j) { if (instance.mLOD[j]) { @@ -303,7 +298,7 @@ void LLModelPreview::rebuildUploadData() bool legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); U32 load_state = 0; - for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter) + for (auto iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter) { //for each transform in scene LLMatrix4 mat = iter->first; @@ -322,9 +317,9 @@ void LLModelPreview::rebuildUploadData() mat *= scale_mat; - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end();) + for (auto model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) { // for each instance with said transform applied - LLModelInstance instance = *model_iter++; + LLModelInstance instance = *model_iter; LLModel* base_model = instance.mModel; @@ -910,7 +905,7 @@ void LLModelPreview::clearIncompatible(S32 lod) { mBaseModel = mModel[lod]; mBaseScene = mScene[lod]; - mVertexBuffer[5].clear(); + mVertexBuffer[LLModel::NUM_LODS].clear(); replaced_base_model = true; } } @@ -1132,7 +1127,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod) mBaseModel = mModel[loaded_lod]; mBaseScene = mScene[loaded_lod]; - mVertexBuffer[5].clear(); + mVertexBuffer[LLModel::NUM_LODS].clear(); } else { @@ -1248,7 +1243,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod) { if (!mBaseModel.empty()) { - const std::string& model_name = mBaseModel[0]->getName(); + std::string model_name = mBaseModel.front()->getName(); LLLineEditor* description_form = mFMP->getChild<LLLineEditor>("description_form"); if (description_form->getText().empty()) { @@ -1269,6 +1264,8 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod) void LLModelPreview::resetPreviewTarget() { + LLMutexLock lock(this); + if (mModelLoader) { mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f; @@ -1314,7 +1311,7 @@ void LLModelPreview::generateNormals() (*it)->generateNormals(angle_cutoff); } - mVertexBuffer[5].clear(); + mVertexBuffer[LLModel::NUM_LODS].clear(); } bool perform_copy = mModelFacesCopy[which_lod].empty(); @@ -2156,7 +2153,7 @@ void LLModelPreview::updateStatusMessages() S32 total_verts[LLModel::NUM_LODS]; S32 total_submeshes[LLModel::NUM_LODS]; - for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) + for (U32 i = 0; i < LLModel::NUM_LODS; i++) { total_tris[i] = 0; total_verts[i] = 0; @@ -2460,12 +2457,16 @@ void LLModelPreview::updateStatusMessages() } } - if (mModelNoErrors && mModelLoader) + if (mModelNoErrors) { - if (!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean()) + LLMutexLock lock(this); + if (mModelLoader) { - // Some textures are still loading, prevent upload until they are done - mModelNoErrors = false; + if (!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean()) + { + // Some textures are still loading, prevent upload until they are done + mModelNoErrors = false; + } } } @@ -2794,10 +2795,10 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights) { LLModelLoader::model_list* model = NULL; - if (lod < 0 || lod > 4) + if (lod < 0 || lod >= LLModel::NUM_LODS) { model = &mBaseModel; - lod = 5; + lod = LLModel::NUM_LODS; } else { @@ -3034,8 +3035,9 @@ void LLModelPreview::loadedCallback( S32 lod, void* opaque) { - LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); - if (pPreview && !LLModelPreview::sIgnoreLoadedCallback) + LLModelPreview* pPreview = static_cast<LLModelPreview*>(opaque); + LLMutexLock lock(pPreview); + if (pPreview && pPreview->mModelLoader && !LLModelPreview::sIgnoreLoadedCallback) { // Load loader's warnings into floater's log tab const LLSD out = pPreview->mModelLoader->logOut(); @@ -3056,7 +3058,9 @@ void LLModelPreview::loadedCallback( } const LLVOAvatar* avatarp = pPreview->getPreviewAvatar(); - if (avatarp) { // set up ground plane for possible rendering + if (avatarp && avatarp->mRoot && avatarp->mDrawable) + { + // set up ground plane for possible rendering const LLVector3 root_pos = avatarp->mRoot->getPosition(); const LLVector4a* ext = avatarp->mDrawable->getSpatialExtents(); const LLVector4a min = ext[0], max = ext[1]; @@ -3200,12 +3204,12 @@ bool LLModelPreview::render() LLMutexLock lock(this); mNeedsUpdate = false; - bool edges = mViewOption["show_edges"]; - bool joint_overrides = mViewOption["show_joint_overrides"]; - bool joint_positions = mViewOption["show_joint_positions"]; - bool skin_weight = mViewOption["show_skin_weight"]; - bool textures = mViewOption["show_textures"]; - bool physics = mViewOption["show_physics"]; + bool show_edges = mViewOption["show_edges"]; + bool show_joint_overrides = mViewOption["show_joint_overrides"]; + bool show_joint_positions = mViewOption["show_joint_positions"]; + bool show_skin_weight = mViewOption["show_skin_weight"]; + bool show_textures = mViewOption["show_textures"]; + bool show_physics = mViewOption["show_physics"]; S32 width = getWidth(); S32 height = getHeight(); @@ -3282,15 +3286,15 @@ bool LLModelPreview::render() fmp->childSetValue("upload_skin", true); mFirstSkinUpdate = false; upload_skin = true; - skin_weight = true; + show_skin_weight = true; mViewOption["show_skin_weight"] = true; } fmp->enableViewOption("show_skin_weight"); - fmp->setViewOptionEnabled("show_joint_overrides", skin_weight); - fmp->setViewOptionEnabled("show_joint_positions", skin_weight); + fmp->setViewOptionEnabled("show_joint_overrides", show_skin_weight); + fmp->setViewOptionEnabled("show_joint_positions", show_skin_weight); mFMP->childEnable("upload_skin"); - mFMP->childSetValue("show_skin_weight", skin_weight); + mFMP->childSetValue("show_skin_weight", show_skin_weight); } else if ((flags & LEGACY_RIG_FLAG_TOO_MANY_JOINTS) > 0) @@ -3313,11 +3317,12 @@ bool LLModelPreview::render() fmp->disableViewOption("show_joint_overrides"); fmp->disableViewOption("show_joint_positions"); - skin_weight = false; + show_skin_weight = false; mFMP->childSetValue("show_skin_weight", false); - fmp->setViewOptionEnabled("show_skin_weight", skin_weight); + fmp->setViewOptionEnabled("show_skin_weight", show_skin_weight); } } + //if (this) return TRUE; if (upload_skin && !has_skin_weights) { //can't upload skin weights if model has no skin weights @@ -3380,7 +3385,7 @@ bool LLModelPreview::render() F32 z_near = 0.001f; F32 z_far = mCameraDistance*10.0f + mPreviewScale.magVec() + mCameraOffset.magVec(); - if (skin_weight) + if (show_skin_weight) { target_pos = getPreviewAvatar()->getPositionAgent() + offset; z_near = 0.01f; @@ -3390,7 +3395,7 @@ bool LLModelPreview::render() refresh(); } - gObjectPreviewProgram.bind(skin_weight); + gObjectPreviewProgram.bind(show_skin_weight); gGL.loadIdentity(); gPipeline.enableLightsPreview(); @@ -3399,7 +3404,7 @@ bool LLModelPreview::render() LLQuaternion(mCameraYaw, LLVector3::z_axis); LLQuaternion av_rot = camera_rot; - F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; + F32 camera_distance = show_skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; LLViewerCamera::getInstance()->setOriginAndLookAt( target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot), // camera LLVector3::z_axis, // up @@ -3415,9 +3420,9 @@ bool LLModelPreview::render() gGL.pushMatrix(); gGL.color4fv(PREVIEW_EDGE_COL.mV); - if (!mBaseModel.empty() && mVertexBuffer[5].empty()) + if (!mBaseModel.empty() && mVertexBuffer[LLModel::NUM_LODS].empty()) { - genBuffers(-1, skin_weight); + genBuffers(-1, show_skin_weight); //genBuffers(3); } @@ -3432,7 +3437,7 @@ bool LLModelPreview::render() if (!vb_vec.empty()) { const LLVertexBuffer* buff = vb_vec[0]; - regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != skin_weight; + regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != show_skin_weight; } else { @@ -3443,15 +3448,15 @@ bool LLModelPreview::render() if (regen) { - genBuffers(mPreviewLOD, skin_weight); + genBuffers(mPreviewLOD, show_skin_weight); } - if (physics && mVertexBuffer[LLModel::LOD_PHYSICS].empty()) + if (show_physics && mVertexBuffer[LLModel::LOD_PHYSICS].empty()) { genBuffers(LLModel::LOD_PHYSICS, false); } - if (!skin_weight) + if (!show_skin_weight) { for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) { @@ -3473,11 +3478,7 @@ bool LLModelPreview::render() auto num_models = mVertexBuffer[mPreviewLOD][model].size(); for (size_t i = 0; i < num_models; ++i) { - LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; - - buffer->setBuffer(); - - if (textures) + if (show_textures) { auto materialCnt = instance.mModel->mMaterialList.size(); if (i < materialCnt) @@ -3501,10 +3502,16 @@ bool LLModelPreview::render() gGL.diffuseColor4fv(PREVIEW_BASE_COL.mV); } + // Zero this variable for an obligatory buffer initialization + // See https://github.com/secondlife/viewer/issues/912 + LLVertexBuffer::sGLRenderBuffer = 0; + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; + buffer->setBuffer(); buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.diffuseColor4fv(PREVIEW_EDGE_COL.mV); - if (edges) + if (show_edges) { glLineWidth(PREVIEW_EDGE_WIDTH); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); @@ -3517,7 +3524,7 @@ bool LLModelPreview::render() gGL.popMatrix(); } - if (physics) + if (show_physics) { glClear(GL_DEPTH_BUFFER_BIT); @@ -3621,11 +3628,13 @@ bool LLModelPreview::render() if (pass > 0){ for (size_t i = 0; i < num_models; ++i) { - LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.diffuseColor4fv(PREVIEW_PSYH_FILL_COL.mV); + // Zero this variable for an obligatory buffer initialization + // See https://github.com/secondlife/viewer/issues/912 + LLVertexBuffer::sGLRenderBuffer = 0; + LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; buffer->setBuffer(); buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); @@ -3685,10 +3694,11 @@ bool LLModelPreview::render() auto num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); for (size_t v = 0; v < num_models; ++v) { + // Zero this variable for an obligatory buffer initialization + // See https://github.com/secondlife/viewer/issues/912 + LLVertexBuffer::sGLRenderBuffer = 0; LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][v]; - buffer->setBuffer(); - LLStrider<LLVector3> pos_strider; buffer->getVertexStrider(pos_strider, 0); LLVector4a* pos = (LLVector4a*)pos_strider.get(); @@ -3752,7 +3762,7 @@ bool LLModelPreview::render() U32 joint_count = LLSkinningUtil::getMeshJointCount(skin); auto bind_count = skin->mAlternateBindMatrix.size(); - if (joint_overrides + if (show_joint_overrides && bind_count > 0 && joint_count == bind_count) { @@ -3797,14 +3807,12 @@ bool LLModelPreview::render() for (U32 i = 0, e = static_cast<U32>(mVertexBuffer[mPreviewLOD][model].size()); i < e; ++i) { - LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; - model->mSkinInfo.updateHash(); LLRenderPass::uploadMatrixPalette(mPreviewAvatar, &model->mSkinInfo); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - if (textures) + if (show_textures) { auto materialCnt = instance.mModel->mMaterialList.size(); if (i < materialCnt) @@ -3828,10 +3836,14 @@ bool LLModelPreview::render() gGL.diffuseColor4fv(PREVIEW_BASE_COL.mV); } + // Zero this variable for an obligatory buffer initialization + // See https://github.com/secondlife/viewer/issues/912 + LLVertexBuffer::sGLRenderBuffer = 0; + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; buffer->setBuffer(); buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); - if (edges) + if (show_edges) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.diffuseColor4fv(PREVIEW_EDGE_COL.mV); @@ -3846,7 +3858,7 @@ bool LLModelPreview::render() } } - if (joint_positions) + if (show_joint_positions) { LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; if (shader) diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 0f5f7aebf8..6e666b8a4b 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -924,8 +924,8 @@ void LLOutfitListBase::onIdleRefreshList() if (cat) { std::string name = cat->getName(); - updateChangedCategoryName(cat, name); - } + updateChangedCategoryName(cat, name); + } curent_time = LLTimer::getTotalSeconds(); if (curent_time >= end_time) diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index cb89a5910e..dd4203745e 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -68,6 +68,9 @@ LLPanelEmojiComplete::LLPanelEmojiComplete(const LLPanelEmojiComplete::Params& p { LLScrollbar::Params sbparams; sbparams.orientation(LLScrollbar::VERTICAL); + sbparams.doc_size((S32)mTotalEmojis); + sbparams.doc_pos(0); + sbparams.page_size((S32)mVisibleEmojis); sbparams.change_callback([this](S32 index, LLScrollbar*) { onScrollbarChange(index); }); mScrollbar = LLUICtrlFactory::create<LLScrollbar>(sbparams); addChild(mScrollbar); diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 00338d3125..51095fd1db 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -5540,4 +5540,3 @@ void LLPanelFace::LLSelectedTE::getMaxDiffuseRepeats(F32& repeats, bool& identic } max_diff_repeats_func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &max_diff_repeats_func, repeats ); } - diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index 7e1553c80a..602b05d240 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -396,7 +396,7 @@ private: ReturnType (LLMaterial::* const MaterialGetFunc)() const > static void getTEMaterialValue(DataType& data_to_return, bool& identical,DataType default_value, bool has_tolerance = false, DataType tolerance = DataType()) { - DataType data_value; + DataType data_value{}; struct GetTEMaterialVal : public LLSelectedTEGetFunctor<DataType> { GetTEMaterialVal(DataType default_value) : _default(default_value) {} @@ -429,7 +429,7 @@ private: ReturnType (LLTextureEntry::* const TEGetFunc)() const > static void getTEValue(DataType& data_to_return, bool& identical, DataType default_value, bool has_tolerance = false, DataType tolerance = DataType()) { - DataType data_value; + DataType data_value {}; struct GetTEVal : public LLSelectedTEGetFunctor<DataType> { GetTEVal(DataType default_value) : _default(default_value) {} @@ -622,4 +622,3 @@ public: }; #endif - diff --git a/indra/newview/llphysicsshapebuilderutil.cpp b/indra/newview/llphysicsshapebuilderutil.cpp index 37534feadc..eb0df1194e 100644 --- a/indra/newview/llphysicsshapebuilderutil.cpp +++ b/indra/newview/llphysicsshapebuilderutil.cpp @@ -28,6 +28,26 @@ #include "llphysicsshapebuilderutil.h" +#include "llmeshrepository.h" + +bool LLPhysicsVolumeParams::hasDecomposition() const + { + if (!isMeshSculpt()) + { + return false; + } + + LLUUID mesh_id = getSculptID(); + if (mesh_id.isNull()) + { + return false; + } + + LLModel::Decomposition* decomp = gMeshRepo.getDecomposition(mesh_id); + + return decomp != NULL; +} + /* static */ void LLPhysicsShapeBuilderUtil::determinePhysicsShape( const LLPhysicsVolumeParams& volume_params, const LLVector3& scale, PhysicsShapeSpecification& specOut) { @@ -200,19 +220,32 @@ void LLPhysicsShapeBuilderUtil::determinePhysicsShape( const LLPhysicsVolumePara { specOut.mType = PhysicsShapeSpecification::PRIM_CONVEX; } - else if (volume_params.isMeshSculpt() && - // Check overall dimensions, not individual triangles. - (scale.mV[0] < SHAPE_BUILDER_USER_MESH_CONVEXIFICATION_SIZE || - scale.mV[1] < SHAPE_BUILDER_USER_MESH_CONVEXIFICATION_SIZE || - scale.mV[2] < SHAPE_BUILDER_USER_MESH_CONVEXIFICATION_SIZE - ) ) + else if (volume_params.isMeshSculpt()) { - // Server distinguishes between user-specified or default convex mesh, vs server's thin-triangle override, but we don't. - specOut.mType = PhysicsShapeSpecification::PRIM_CONVEX; + // Check overall dimensions, not individual triangles. + if (scale.mV[0] < SHAPE_BUILDER_USER_MESH_CONVEXIFICATION_SIZE + || scale.mV[1] < SHAPE_BUILDER_USER_MESH_CONVEXIFICATION_SIZE + || scale.mV[2] < SHAPE_BUILDER_USER_MESH_CONVEXIFICATION_SIZE + ) + { + if (volume_params.hasDecomposition()) + { + specOut.mType = PhysicsShapeSpecification::USER_MESH; + } + else + { + // Server distinguishes between user-specified or default convex mesh, vs server's thin-triangle override, but we don't. + specOut.mType = PhysicsShapeSpecification::PRIM_CONVEX; + } + } + else + { + specOut.mType = PhysicsShapeSpecification::USER_MESH; + } } - else if ( volume_params.isSculpt() ) // Is a sculpt of any kind (mesh or legacy) + else if ( volume_params.isSculpt() ) { - specOut.mType = volume_params.isMeshSculpt() ? PhysicsShapeSpecification::USER_MESH : PhysicsShapeSpecification::SCULPT; + specOut.mType = PhysicsShapeSpecification::SCULPT; } else // Resort to mesh { diff --git a/indra/newview/llphysicsshapebuilderutil.h b/indra/newview/llphysicsshapebuilderutil.h index 33c2d0a8b6..01c173523b 100644 --- a/indra/newview/llphysicsshapebuilderutil.h +++ b/indra/newview/llphysicsshapebuilderutil.h @@ -79,6 +79,8 @@ public: bool shouldForceConvex() const { return mForceConvex; } + bool hasDecomposition() const; + private: bool mForceConvex; }; diff --git a/indra/newview/lltoastalertpanel.h b/indra/newview/lltoastalertpanel.h index 167a7b2363..c2cdd775f1 100644 --- a/indra/newview/lltoastalertpanel.h +++ b/indra/newview/lltoastalertpanel.h @@ -82,14 +82,10 @@ private: struct ButtonData { - ButtonData() - : mWidth(0) - {} - - LLButton* mButton; + LLButton* mButton = nullptr; std::string mURL; - U32 mURLExternal; - S32 mWidth; + U32 mURLExternal = 0; + S32 mWidth = 0; }; std::vector<ButtonData> mButtonData; diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp index f0567b18c4..da7ad71336 100644 --- a/indra/newview/llviewerjointmesh.cpp +++ b/indra/newview/llviewerjointmesh.cpp @@ -407,10 +407,16 @@ void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, bool damp_w F32* vw = (F32*) vertex_weightsp.get(); F32* cw = (F32*) clothing_weightsp.get(); - S32 tc_size = (num_verts*2*sizeof(F32)+0xF) & ~0xF; - LLVector4a::memcpyNonAliased16(tc, (F32*) mMesh->getTexCoords(), tc_size); - S32 vw_size = (num_verts*sizeof(F32)+0xF) & ~0xF; - LLVector4a::memcpyNonAliased16(vw, (F32*) mMesh->getWeights(), vw_size); + //S32 tc_size = (num_verts*2*sizeof(F32)+0xF) & ~0xF; + //LLVector4a::memcpyNonAliased16(tc, (F32*) mMesh->getTexCoords(), tc_size); + //S32 vw_size = (num_verts*sizeof(F32)+0xF) & ~0xF; + //LLVector4a::memcpyNonAliased16(vw, (F32*) mMesh->getWeights(), vw_size); + + // Both allocated in LLPolyMeshSharedData::allocateVertexData(unsigned int) + + memcpy(tc, mMesh->getTexCoords(), num_verts*2*sizeof(F32) ); + memcpy(vw, mMesh->getWeights(), num_verts*sizeof(F32) ); + LLVector4a::memcpyNonAliased16(cw, (F32*) mMesh->getClothingWeights(), num_verts*4*sizeof(F32)); } diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 6fc9f2a6f0..50171ffcb0 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1728,8 +1728,6 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ std::string user_data_path_cache = gDirUtilp->getCacheDir(false); user_data_path_cache += gDirUtilp->getDirDelimiter(); - std::string user_data_path_cef_log = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef_log.txt"); - // See if the plugin executable exists llstat s; if(LLFile::stat(launcher_name, &s)) @@ -1738,14 +1736,13 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ } else if(LLFile::stat(plugin_name, &s)) { -#if !LL_LINUX LL_WARNS_ONCE("Media") << "Couldn't find plugin at " << plugin_name << LL_ENDL; -#endif } else { media_source = new LLPluginClassMedia(owner); media_source->setSize(default_width, default_height); + std::string user_data_path_cef_log = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef.log"); media_source->setUserDataPath(user_data_path_cache, gDirUtilp->getUserName(), user_data_path_cef_log); media_source->setLanguageCode(LLUI::getLanguage()); media_source->setZoomFactor(zoom_factor); @@ -1773,6 +1770,11 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ bool media_plugin_debugging_enabled = gSavedSettings.getBOOL("MediaPluginDebugging"); media_source->enableMediaPluginDebugging( media_plugin_debugging_enabled || clean_browser); +#if LL_LINUX + bool media_plugin_pipewire_volume_catcher = gSavedSettings.getBOOL("MediaPluginPipeWireVolumeCatcher"); + media_source->enablePipeWireVolumeCatcher( media_plugin_pipewire_volume_catcher ); +#endif + // need to set agent string here before instance created media_source->setBrowserUserAgent(LLViewerMedia::getInstance()->getCurrentUserAgent()); @@ -1794,9 +1796,7 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ } } } -#if !LL_LINUX LL_WARNS_ONCE("Plugin") << "plugin initialization failed for mime type: " << media_type << LL_ENDL; -#endif if(gAgent.isInitialized()) { diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 8738151930..bc4ae9d9bd 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -2915,24 +2915,33 @@ void LLViewerObject::fetchInventoryFromServer() delete mInventory; mInventory = NULL; - // Results in processTaskInv - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_RequestTaskInventory); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addU32Fast(_PREHASH_LocalID, mLocalID); - msg->sendReliable(mRegionp->getHost()); - // This will get reset by doInventoryCallback or processTaskInv mInvRequestState = INVENTORY_REQUEST_PENDING; + + if (mRegionp && !mRegionp->getCapability("RequestTaskInventory").empty()) + { + LLCoros::instance().launch("LLViewerObject::fetchInventoryFromCapCoro()", + boost::bind(&LLViewerObject::fetchInventoryFromCapCoro, mID)); + } + else + { + LL_WARNS() << "Using old task inventory path!" << LL_ENDL; + // Results in processTaskInv + LLMessageSystem *msg = gMessageSystem; + msg->newMessageFast(_PREHASH_RequestTaskInventory); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addU32Fast(_PREHASH_LocalID, mLocalID); + msg->sendReliable(mRegionp->getHost()); + } } } void LLViewerObject::fetchInventoryDelayed(const F64 &time_seconds) { - // unless already waiting, drop previous request and shedule an update + // unless already waiting, drop previous request and schedule an update if (mInvRequestState != INVENTORY_REQUEST_WAIT) { if (mInvRequestXFerId != 0) @@ -2963,6 +2972,80 @@ void LLViewerObject::fetchInventoryDelayedCoro(const LLUUID task_inv, const F64 } } +//static +void LLViewerObject::fetchInventoryFromCapCoro(const LLUUID task_inv) +{ + LLViewerObject *obj = gObjectList.findObject(task_inv); + if (obj) + { + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("TaskInventoryRequest", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + std::string url = obj->mRegionp->getCapability("RequestTaskInventory") + "?task_id=" + obj->mID.asString(); + // If we already have a copy of the inventory then add it so the server won't re-send something we already have. + // We expect this case to crop up in the case of failed inventory mutations, but it might happen otherwise as well. + if (obj->mInventorySerialNum && obj->mInventory) + url += "&inventory_serial=" + std::to_string(obj->mInventorySerialNum); + + obj->mInvRequestState = INVENTORY_XFER; + LLSD result = httpAdapter->getAndSuspend(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + // Object may have gone away while we were suspended, double-check that it still exists + obj = gObjectList.findObject(task_inv); + if (!obj) + { + LL_WARNS() << "Object " << task_inv << " went away while fetching inventory, dropping result" << LL_ENDL; + return; + } + + bool potentially_stale = false; + if (status) + { + // Dealing with inventory serials is kind of funky. They're monotonically increasing and 16 bits, + // so we expect them to overflow, but we can use inv serial < expected serial as a signal that we may + // have mutated the task inventory since we kicked off the request, and those mutations may have not + // been taken into account yet. Of course, those mutations may have actually failed which would result + // in the inv serial never increasing. + // + // When we detect this case, set the expected inv serial to the inventory serial we actually received + // and kick off a re-request after a slight delay. + S16 serial = (S16)result["inventory_serial"].asInteger(); + potentially_stale = serial < obj->mExpectedInventorySerialNum; + LL_INFOS() << "Inventory loaded for " << task_inv << LL_ENDL; + obj->mInventorySerialNum = serial; + obj->mExpectedInventorySerialNum = serial; + obj->loadTaskInvLLSD(result); + } + else if (status.getType() == 304) + { + LL_INFOS() << "Inventory wasn't changed on server!" << LL_ENDL; + obj->mInvRequestState = INVENTORY_REQUEST_STOPPED; + // Even though it wasn't necessary to send a response, we still may have mutated + // the inventory since we kicked off the request, check for that case. + potentially_stale = obj->mInventorySerialNum < obj->mExpectedInventorySerialNum; + // Set this to what we already have so that we don't re-request a second time. + obj->mExpectedInventorySerialNum = obj->mInventorySerialNum; + } + else + { + // Not sure that there's anything sensible we can do to recover here, retrying in a loop would be bad. + LL_WARNS() << "Error status while requesting task inventory: " << status.toString() << LL_ENDL; + obj->mInvRequestState = INVENTORY_REQUEST_STOPPED; + } + + if (potentially_stale) + { + // Stale? I guess we can use what we got for now, but we'll have to re-request + LL_WARNS() << "Stale inv_serial? Re-requesting." << LL_ENDL; + obj->fetchInventoryDelayed(INVENTORY_UPDATE_WAIT_TIME_OUTDATED); + } + } +} + LLControlAvatar *LLViewerObject::getControlAvatar() { return getRootEdit()->mControlAvatar.get(); @@ -3138,6 +3221,20 @@ void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data) S16 serial = 0; msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, serial); + if (object->mRegionp && !object->mRegionp->getCapability("RequestTaskInventory").empty()) + { + // It seems that simulator may ask us to re-download the task inventory if an update to the inventory + // happened out-of-band while we had the object selected (like if a script is saved.) + // + // If we're meant to use the HTTP capability, ignore the contents of the UDP message and fetch the + // inventory via the CAP so that we don't flow down the UDP inventory request path unconditionally here. + // We shouldn't need to wait, as any updates should already be ready to fetch by this point. + LL_INFOS() << "Handling unsolicited ReplyTaskInventory for " << task_id << LL_ENDL; + object->mExpectedInventorySerialNum = serial; + object->fetchInventoryFromServer(); + return; + } + if (serial == object->mInventorySerialNum && serial < object->mExpectedInventorySerialNum) { @@ -3345,6 +3442,47 @@ bool LLViewerObject::loadTaskInvFile(const std::string& filename) return true; } +void LLViewerObject::loadTaskInvLLSD(const LLSD& inv_result) +{ + if (inv_result.has("contents")) + { + if(mInventory) + { + mInventory->clear(); // will deref and delete it + } + else + { + mInventory = new LLInventoryObject::object_list_t; + } + + // Synthesize the "Contents" category, the viewer expects it, but it isn't sent. + LLPointer<LLInventoryObject> inv = new LLInventoryObject(mID, LLUUID::null, LLAssetType::AT_CATEGORY, "Contents"); + mInventory->push_front(inv); + + const LLSD& inventory = inv_result["contents"]; + for (const auto& inv_entry : llsd::inArray(inventory)) + { + if (inv_entry.has("item_id")) + { + LLPointer<LLViewerInventoryItem> inv = new LLViewerInventoryItem; + inv->unpackMessage(inv_entry); + mInventory->push_front(inv); + } + else + { + LL_WARNS_ONCE() << "Unknown inventory entry while reading from inventory file. Entry: '" + << inv_entry << "'" << LL_ENDL; + } + } + } + else + { + LL_WARNS() << "unable to load task inventory: " << inv_result << LL_ENDL; + return; + } + doInventoryCallback(); +} + void LLViewerObject::doInventoryCallback() { for (callback_list_t::iterator iter = mInventoryCallbacks.begin(); diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 09584d22a8..08c81c4b6c 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -684,6 +684,7 @@ private: // forms task inventory request after some time passed, marks request as pending void fetchInventoryDelayed(const F64 &time_seconds); static void fetchInventoryDelayedCoro(const LLUUID task_inv, const F64 time_seconds); + static void fetchInventoryFromCapCoro(const LLUUID task_inv); public: // @@ -818,6 +819,7 @@ protected: static void processTaskInvFile(void** user_data, S32 error_code, LLExtStat ext_status); bool loadTaskInvFile(const std::string& filename); + void loadTaskInvLLSD(const LLSD &inv_result); void doInventoryCallback(); bool isOnMap(); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 7623ab56a5..cb0635d103 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3230,6 +3230,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("FetchInventory2"); capabilityNames.append("FetchInventoryDescendents2"); capabilityNames.append("IncrementCOFVersion"); + capabilityNames.append("RequestTaskInventory"); AISAPI::getCapNames(capabilityNames); capabilityNames.append("InterestList"); diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index d1ee9ea17c..b351f3e12f 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -827,6 +827,8 @@ void send_viewer_stats(bool include_preferences) LL_INFOS("LogViewerStatsPacket") << "Sending viewer statistics: " << body << LL_ENDL; + + // <ND> Do those lines even do anything sane in regard of debug logging? LL_DEBUGS("LogViewerStatsPacket"); std::string filename("viewer_stats_packet.xml"); llofstream of(filename.c_str()); diff --git a/indra/newview/llviewerstatsrecorder.cpp b/indra/newview/llviewerstatsrecorder.cpp index 88edb96fbb..2e4002479d 100644 --- a/indra/newview/llviewerstatsrecorder.cpp +++ b/indra/newview/llviewerstatsrecorder.cpp @@ -317,5 +317,3 @@ F32 LLViewerStatsRecorder::getTimeSinceStart() { return (F32) (LLFrameTimer::getTotalSeconds() - mFileOpenTime); } - - diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 060e1243ab..6e301740a0 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -1015,6 +1015,7 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() std::string old_log = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SLVoice.old"); if (gDirUtilp->fileExists(new_log)) { + LLFile::remove(old_log, ENOENT); LLFile::rename(new_log, old_log); } diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 2630aaf43e..e7919e8970 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -5832,8 +5832,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) { type = LLDrawPool::POOL_GLTF_PBR; } - else - if (type != LLDrawPool::POOL_ALPHA && force_simple) + else if (type != LLDrawPool::POOL_ALPHA && force_simple) { type = LLDrawPool::POOL_SIMPLE; } diff --git a/indra/newview/skins/default/xui/en/floater_emoji_picker.xml b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml index e4b8f13df7..f642ca93b7 100644 --- a/indra/newview/skins/default/xui/en/floater_emoji_picker.xml +++ b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml @@ -13,7 +13,6 @@ chrome="true" height="350" width="304"> - <floater.string name="title_for_recently_used" value="Recently used"/> <floater.string name="title_for_frequently_used" value="Frequently used"/> <floater.string name="text_no_emoji_for_filter" value="No emoji found for '[FILTER]'"/> <scroll_container diff --git a/indra/newview/skins/default/xui/en/floater_fast_timers.xml b/indra/newview/skins/default/xui/en/floater_fast_timers.xml index f5852fdfaf..4dc9876ac8 100644 --- a/indra/newview/skins/default/xui/en/floater_fast_timers.xml +++ b/indra/newview/skins/default/xui/en/floater_fast_timers.xml @@ -74,6 +74,8 @@ orientation="vertical" step_size="16" doc_size="3000" + page_size="50" + doc_pos="0" /> </layout_panel> <layout_panel name="timers_panel" diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml index 7bc81a1f79..8c4019a668 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml @@ -222,7 +222,7 @@ height="16" increment="1" initial_value="12" - label="Max. # of non-impostors:" + label="Max. # animated avatars:" label_width="185" layout="topleft" left="30" diff --git a/indra/newview/skins/default/xui/en/floater_world_map.xml b/indra/newview/skins/default/xui/en/floater_world_map.xml index 7efa81d263..19d61eb43b 100644 --- a/indra/newview/skins/default/xui/en/floater_world_map.xml +++ b/indra/newview/skins/default/xui/en/floater_world_map.xml @@ -34,7 +34,8 @@ top="16" left="0" right="-1" - bottom="-1"> + bottom="-1" + orientation="horizontal"> <layout_panel name="map_lp" width="385" diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 347638249b..e17dbbeb2f 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -4157,16 +4157,6 @@ function="World.EnvPreset" <menu_item_separator/> - <menu_item_check - label="HTTP Textures" - name="HTTP Textures"> - <menu_item_check.on_check - function="CheckControl" - parameter="ImagePipelineUseHTTP" /> - <menu_item_check.on_click - function="ToggleControl" - parameter="ImagePipelineUseHTTP" /> - </menu_item_check> <menu_item_call label="Compress Images" name="Compress Images"> diff --git a/indra/newview/skins/default/xui/en/mime_types_linux.xml b/indra/newview/skins/default/xui/en/mime_types_linux.xml index d0ecd0a11c..87284f564b 100644 --- a/indra/newview/skins/default/xui/en/mime_types_linux.xml +++ b/indra/newview/skins/default/xui/en/mime_types_linux.xml @@ -7,7 +7,7 @@ none </defaultwidget> <defaultimpl> - media_plugin_webkit + media_plugin_cef </defaultimpl> <widgetset name="web"> <label name="web_label"> @@ -130,7 +130,7 @@ movie </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </scheme> <mimetype name="blank"> @@ -141,7 +141,7 @@ none </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype name="none/none"> @@ -152,7 +152,7 @@ none </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype name="audio/*"> @@ -163,7 +163,7 @@ audio </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </mimetype> <mimetype name="video/*"> @@ -174,7 +174,7 @@ movie </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </mimetype> <mimetype name="image/*"> @@ -185,7 +185,7 @@ image </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype menu="1" name="video/vnd.secondlife.qt.legacy"> @@ -196,7 +196,7 @@ movie </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </mimetype> <mimetype name="application/javascript"> @@ -207,7 +207,7 @@ web </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype name="application/ogg"> @@ -218,7 +218,7 @@ audio </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </mimetype> <mimetype name="application/pdf"> @@ -229,7 +229,7 @@ image </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype name="application/postscript"> @@ -240,7 +240,7 @@ image </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype name="application/rtf"> @@ -251,7 +251,7 @@ image </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype name="application/smil"> @@ -262,7 +262,7 @@ movie </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype name="application/xhtml+xml"> @@ -273,7 +273,7 @@ web </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype name="application/x-director"> @@ -284,7 +284,7 @@ image </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype name="audio/mid"> @@ -295,7 +295,7 @@ audio </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </mimetype> <mimetype name="audio/mpeg"> @@ -306,7 +306,7 @@ audio </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </mimetype> <mimetype name="audio/x-aiff"> @@ -317,7 +317,7 @@ audio </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </mimetype> <mimetype name="audio/x-wav"> @@ -328,7 +328,7 @@ audio </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </mimetype> <mimetype menu="1" name="image/bmp"> @@ -339,7 +339,7 @@ image </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype menu="1" name="image/gif"> @@ -350,7 +350,7 @@ image </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype menu="1" name="image/jpeg"> @@ -361,7 +361,7 @@ image </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype menu="1" name="image/png"> @@ -372,7 +372,7 @@ image </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype name="image/svg+xml"> @@ -383,7 +383,7 @@ image </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype menu="1" name="image/tiff"> @@ -394,7 +394,7 @@ image </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype menu="1" name="text/html"> @@ -405,7 +405,7 @@ web </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype menu="1" name="text/plain"> @@ -416,7 +416,7 @@ text </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype name="text/xml"> @@ -427,7 +427,7 @@ text </widgettype> <impl> - media_plugin_webkit + media_plugin_cef </impl> </mimetype> <mimetype menu="1" name="video/mpeg"> @@ -438,7 +438,7 @@ movie </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </mimetype> <mimetype name="video/mp4"> @@ -449,7 +449,7 @@ movie </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </mimetype> <mimetype menu="1" name="video/quicktime"> @@ -460,7 +460,7 @@ movie </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </mimetype> <mimetype name="video/x-ms-asf"> @@ -471,7 +471,7 @@ movie </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </mimetype> <mimetype name="video/x-ms-wmv"> @@ -482,7 +482,7 @@ movie </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </mimetype> <mimetype menu="1" name="video/x-msvideo"> @@ -493,7 +493,7 @@ movie </widgettype> <impl> - media_plugin_libvlc + media_plugin_gstreamer </impl> </mimetype> </mimetypes> diff --git a/indra/newview/skins/default/xui/en/panel_login.xml b/indra/newview/skins/default/xui/en/panel_login.xml index 1801ae1f49..27b74a46ce 100644 --- a/indra/newview/skins/default/xui/en/panel_login.xml +++ b/indra/newview/skins/default/xui/en/panel_login.xml @@ -145,7 +145,6 @@ control_name="RememberPassword" follows="left|top" font="SansSerifMedium" - text_color="EmphasisColor" height="24" left="408" bottom_delta="0" diff --git a/indra/newview/skins/default/xui/en/panel_login_first.xml b/indra/newview/skins/default/xui/en/panel_login_first.xml index c906e2f96c..d6ac71db94 100644 --- a/indra/newview/skins/default/xui/en/panel_login_first.xml +++ b/indra/newview/skins/default/xui/en/panel_login_first.xml @@ -179,7 +179,6 @@ control_name="RememberPassword" follows="left|top" font="SansSerifLarge" - text_color="EmphasisColor" height="24" left="262" bottom_delta="0" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml index 5d347397bf..cb3e0e4b6a 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml @@ -210,26 +210,60 @@ increment="8" initial_value="160" label="Draw distance:" - label_width="90" + label_width="187" layout="topleft" left="30" min_val="64" max_val="512" name="DrawDistance" top_delta="40" - width="330" /> + width="427" /> <text type="string" length="1" follows="left|top" height="12" layout="topleft" - left_delta="330" + left_delta="427" name="DrawDistanceMeterText2" top_delta="0" width="128"> m </text> + <slider + control_name="IndirectMaxNonImpostors" + name="IndirectMaxNonImpostors" + decimal_digits="0" + increment="1" + initial_value="12" + show_text="false" + min_val="1" + max_val="66" + label="Maximum number of animated avatars:" + follows="left|top" + layout="topleft" + height="16" + label_width="240" + left="30" + top_delta="40" + width="393"> + <slider.commit_callback + function="Pref.UpdateIndirectMaxNonImpostors" + parameter="IndirectNonImpostorsText" /> + </slider> + <text + type="string" + length="1" + follows="left|top" + height="16" + layout="topleft" + top_delta="0" + left_delta="397" + text_readonly_color="LabelDisabledColor" + name="IndirectMaxNonImpostorsText" + width="65"> + 0 + </text> <button height="23" diff --git a/indra/newview/skins/default/xui/en/panel_region_environment.xml b/indra/newview/skins/default/xui/en/panel_region_environment.xml index 6d23592948..6531233696 100644 --- a/indra/newview/skins/default/xui/en/panel_region_environment.xml +++ b/indra/newview/skins/default/xui/en/panel_region_environment.xml @@ -264,8 +264,7 @@ top_pad="1" halign="right" name="txt_alt1"> - Sky [INDEX] - [ALTITUDE]m + Sky [INDEX]
[ALTITUDE]m </text> <line_editor follows="left|top" @@ -310,8 +309,7 @@ top_pad="1" halign="right" name="txt_alt2"> - Sky [INDEX] - [ALTITUDE]m + Sky [INDEX]
[ALTITUDE]m </text> <line_editor follows="left|top" @@ -356,8 +354,7 @@ top_pad="1" halign="right" name="txt_alt3"> - Sky [INDEX] - [ALTITUDE]m + Sky [INDEX]
[ALTITUDE]m </text> <line_editor follows="left|top" diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 470c8bc8a8..5bb1a8a44a 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -65,6 +65,8 @@ class ViewerManifest(LLManifest): self.path(src="../../scripts/messages/message_template.msg", dst="app_settings/message_template.msg") self.path(src="../../etc/message.xml", dst="app_settings/message.xml") + os.environ["XZ_DEFAULTS"] = "-T0" + if self.is_packaging_viewer(): with self.prefix(src_dst="app_settings"): self.exclude("logcontrol.xml") @@ -1206,6 +1208,9 @@ class LinuxManifest(ViewerManifest): super(LinuxManifest, self).construct() pkgdir = os.path.join(self.args['build'], os.pardir, 'packages') + if "package_dir" in self.args: + pkgdir = self.args['package_dir'] + relpkgdir = os.path.join(pkgdir, "lib", "release") debpkgdir = os.path.join(pkgdir, "lib", "debug") @@ -1224,49 +1229,123 @@ class LinuxManifest(ViewerManifest): with self.prefix(dst="bin"): self.path("secondlife-bin","do-not-directly-run-secondlife-bin") - self.path("../linux_crash_logger/linux-crash-logger","linux-crash-logger.bin") + #self.path("../linux_crash_logger/linux-crash-logger","linux-crash-logger.bin") self.path2basename("../llplugin/slplugin", "SLPlugin") #this copies over the python wrapper script, associated utilities and required libraries, see SL-321, SL-322 and SL-323 - with self.prefix(src="../viewer_components/manager", dst=""): - self.path("*.py") + #with self.prefix(src="../viewer_components/manager", dst=""): + # self.path("*.py") # recurses, packaged again self.path("res-sdl") + # We copy ll_icon.BMP in CMakeLists.txt to newview/res-sdl and this will let the above self.path step take care of copying + # the correct branded icon # Get the icons based on the channel type icon_path = self.icon_path() - print("DEBUG: icon_path '%s'" % icon_path) + #print("DEBUG: icon_path '%s'" % icon_path) with self.prefix(src=icon_path) : self.path("secondlife_256.png","secondlife_icon.png") with self.prefix(dst="res-sdl") : self.path("secondlife_256.BMP","ll_icon.BMP") # plugins - with self.prefix(src="../media_plugins", dst="bin/llplugin"): - self.path("gstreamer010/libmedia_plugin_gstreamer010.so", - "libmedia_plugin_gstreamer.so") - self.path2basename("libvlc", "libmedia_plugin_libvlc.so") - - with self.prefix(src=os.path.join(pkgdir, 'lib', 'vlc', 'plugins'), dst="bin/llplugin/vlc/plugins"): - self.path( "plugins.dat" ) - self.path( "*/*.so" ) - - with self.prefix(src=os.path.join(pkgdir, 'lib' ), dst="lib"): - self.path( "libvlc*.so*" ) - - # llcommon - if not self.path("../llcommon/libllcommon.so", "lib/libllcommon.so"): - print("Skipping llcommon.so (assuming llcommon was linked statically)") + with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'media_plugins'), dst="bin/llplugin"): + self.path("gstreamer10/libmedia_plugin_gstreamer10.so", "libmedia_plugin_gstreamer.so") + + + with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'media_plugins'), dst="bin/llplugin"): + self.path("cef/libmedia_plugin_cef.so", "libmedia_plugin_cef.so" ) + with self.prefix(src=os.path.join(pkgdir, 'lib', 'release'), dst="lib"): + self.path( "libcef.so" ) + + self.path( "libEGL*" ) + self.path( "libvulkan*" ) + self.path( "libvk_swiftshader*" ) + self.path( "libGLESv2*" ) + self.path( "vk_swiftshader_icd.json") + + with self.prefix(src=os.path.join(pkgdir, 'bin', 'release'), dst="bin"): + self.path( "chrome-sandbox" ) + self.path( "dullahan_host" ) + self.path( "snapshot_blob.bin" ) + self.path( "v8_context_snapshot.bin" ) + with self.prefix(src=os.path.join(pkgdir, 'bin', 'release'), dst="lib"): + self.path( "snapshot_blob.bin" ) + self.path( "v8_context_snapshot.bin" ) + + with self.prefix(src=os.path.join(pkgdir, 'resources'), dst="lib"): + self.path( "chrome_100_percent.pak" ) + self.path( "chrome_200_percent.pak" ) + self.path( "resources.pak" ) + self.path( "icudtl.dat" ) + + with self.prefix(src=os.path.join(pkgdir, 'resources', 'locales'), dst=os.path.join('lib', 'locales')): + self.path("am.pak") + self.path("ar.pak") + self.path("bg.pak") + self.path("bn.pak") + self.path("ca.pak") + self.path("cs.pak") + self.path("da.pak") + self.path("de.pak") + self.path("el.pak") + self.path("en-GB.pak") + self.path("en-US.pak") + self.path("es-419.pak") + self.path("es.pak") + self.path("et.pak") + self.path("fa.pak") + self.path("fi.pak") + self.path("fil.pak") + self.path("fr.pak") + self.path("gu.pak") + self.path("he.pak") + self.path("hi.pak") + self.path("hr.pak") + self.path("hu.pak") + self.path("id.pak") + self.path("it.pak") + self.path("ja.pak") + self.path("kn.pak") + self.path("ko.pak") + self.path("lt.pak") + self.path("lv.pak") + self.path("ml.pak") + self.path("mr.pak") + self.path("ms.pak") + self.path("nb.pak") + self.path("nl.pak") + self.path("pl.pak") + self.path("pt-BR.pak") + self.path("pt-PT.pak") + self.path("ro.pak") + self.path("ru.pak") + self.path("sk.pak") + self.path("sl.pak") + self.path("sr.pak") + self.path("sv.pak") + self.path("sw.pak") + self.path("ta.pak") + self.path("te.pak") + self.path("th.pak") + self.path("tr.pak") + self.path("uk.pak") + self.path("vi.pak") + self.path("zh-CN.pak") + self.path("zh-TW.pak") self.path("featuretable_linux.txt") self.path("cube.dae") - with self.prefix(src=pkgdir): + with self.prefix(src=pkgdir, dst="bin"): self.path("ca-bundle.crt") def package_finish(self): installer_name = self.installer_base_name() + # When running as a GitHub Action job, RUNNER_TEMP is defined as the tmp dir + RUNNER_TEMP = os.getenv('RUNNER_TEMP') + self.strip_binaries() # Fix access permissions @@ -1281,64 +1360,88 @@ class LinuxManifest(ViewerManifest): # temporarily move directory tree so that it has the right # name in the tarfile realname = self.get_dst_prefix() - tempname = self.build_path_of(installer_name) - self.run_command(["mv", realname, tempname]) + versionedName = self.build_path_of(installer_name) + + tarName = versionedName + ".tar.xz" + + # If using a github runner we divert packaging a little. Considering this wil be a VM/docker image + # we can just pack the final installer into RUNNER_TEMP and not into the usual stop we'd pick when + # not building a GHA release + if RUNNER_TEMP: + tarName = os.path.join(RUNNER_TEMP, self.package_file) + + self.run_command(["mv", realname, versionedName]) + try: # only create tarball if it's a release build. if self.args['buildtype'].lower() == 'release': - # --numeric-owner hides the username of the builder for - # security etc. self.run_command(['tar', '-C', self.get_build_prefix(), '--numeric-owner', '-cJf', - tempname + '.tar.xz', installer_name]) + tarName, installer_name]) + self.set_github_output_path('viewer_app', tarName) else: print("Skipping %s.tar.xz for non-Release build (%s)" % \ (installer_name, self.args['buildtype'])) finally: - self.run_command(["mv", tempname, realname]) + self.run_command(["mv", versionedName, realname]) def strip_binaries(self): + doStrip = False if self.args['buildtype'].lower() == 'release' and self.is_packaging_viewer(): - print("* Going strip-crazy on the packaged binaries, since this is a RELEASE build") + doStrip = True + # In case of flatpak flatpak-build will call strip, disable doStrip here to get a flatpak symbol package. Increases flatpak size by about 1G + if "FLATPAK_DEST" in os.environ: + doStrip = True + + if doStrip: + print("* Going strip-crazy on the packaged binaries, since this is a Release build") # makes some small assumptions about our packaged dir structure self.run_command( ["find"] + [os.path.join(self.get_dst_prefix(), dir) for dir in ('bin', 'lib')] + ['-type', 'f', '!', '-name', '*.py', + '!', '-name', '*.pak', + '!', '-name', '*.bin', + '!', '-name', '*.dat', + '!', '-name', '*.crt', + '!', '-name', '*.dll', + '!', '-name', '*.lib', '!', '-name', 'update_install', '-exec', 'strip', '-S', '{}', ';']) -class Linux_i686_Manifest(LinuxManifest): - address_size = 32 +class Linux_x86_64_Manifest(LinuxManifest): + address_size = 64 def construct(self): - super(Linux_i686_Manifest, self).construct() + super(Linux_x86_64_Manifest, self).construct() pkgdir = os.path.join(self.args['build'], os.pardir, 'packages') + if "package_dir" in self.args: + pkgdir = self.args['package_dir'] + relpkgdir = os.path.join(pkgdir, "lib", "release") debpkgdir = os.path.join(pkgdir, "lib", "debug") with self.prefix(src=relpkgdir, dst="lib"): - self.path("libapr-1.so") - self.path("libapr-1.so.0") - self.path("libapr-1.so.0.4.5") - self.path("libaprutil-1.so") - self.path("libaprutil-1.so.0") - self.path("libaprutil-1.so.0.4.1") - self.path("libdb*.so") + self.path("libapr-1.so*") + self.path("libaprutil-1.so*") self.path("libexpat.so.*") - self.path("libuuid.so*") - self.path("libSDL-1.2.so.*") - self.path("libdirectfb-1.*.so.*") - self.path("libfusion-1.*.so.*") - self.path("libdirect-1.*.so.*") - self.path("libopenjp2.so*") - self.path("libdirectfb-1.4.so.5") - self.path("libfusion-1.4.so.5") - self.path("libdirect-1.4.so.5*") + self.path_optional("libSDL*.so.*") + + self.path_optional("libjemalloc*.so") + self.path("libhunspell-1.3.so*") self.path("libalut.so*") self.path("libopenal.so*") self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname + if self.args['fmodstudio'] == 'ON': + try: + self.path("libfmod.so.11.7") + self.path("libfmod.so.11") + self.path("libfmod.so") + pass + except: + print("Skipping libfmod.so - not found") + # KLUDGE: As of 2012-04-11, the 'fontconfig' package installs # libfontconfig.so.1.4.4, along with symlinks libfontconfig.so.1 # and libfontconfig.so. Before we added support for library-file @@ -1378,17 +1481,6 @@ class Linux_i686_Manifest(LinuxManifest): self.path("libvivoxsdk.so") self.strip_binaries() - - -class Linux_x86_64_Manifest(LinuxManifest): - address_size = 64 - - def construct(self): - super(Linux_x86_64_Manifest, self).construct() - - # support file for valgrind debug tool - self.path("secondlife-i686.supp") - ################################################################ if __name__ == "__main__": diff --git a/indra/test/test.cpp b/indra/test/test.cpp index 6c4a1f43b4..1a31beac31 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -36,6 +36,7 @@ #include "linden_common.h" #include "llerrorcontrol.h" +#include "llexception.h" #include "lltut.h" #include "chained_callback.h" #include "stringize.h" @@ -63,11 +64,6 @@ #pragma warning (pop) #endif -#include <boost/scoped_ptr.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/make_shared.hpp> -#include <boost/foreach.hpp> - #include <fstream> void wouldHaveCrashed(const std::string& message); @@ -176,10 +172,6 @@ public: LLTestCallback(bool verbose_mode, std::ostream *stream, std::shared_ptr<LLReplayLog> replayer) : mVerboseMode(verbose_mode), - mTotalTests(0), - mPassedTests(0), - mFailedTests(0), - mSkippedTests(0), // By default, capture a shared_ptr to std::cout, with a no-op "deleter" // so that destroying the shared_ptr makes no attempt to delete std::cout. mStream(std::shared_ptr<std::ostream>(&std::cout, [](std::ostream*){})), @@ -215,6 +207,8 @@ public: virtual void group_started(const std::string& name) { LL_INFOS("TestRunner")<<"Unit test group_started name=" << name << LL_ENDL; *mStream << "Unit test group_started name=" << name << std::endl; + mGroup = name; + mGroupTests = 0; super::group_started(name); } @@ -227,6 +221,7 @@ public: virtual void test_completed(const tut::test_result& tr) { ++mTotalTests; + ++mGroupTests; // If this test failed, dump requested log messages BEFORE stating the // test result. @@ -314,12 +309,15 @@ public: super::run_completed(); } + std::string mGroup; + int mGroupTests{ 0 }; + protected: - bool mVerboseMode; - int mTotalTests; - int mPassedTests; - int mFailedTests; - int mSkippedTests; + bool mVerboseMode{ false }; + int mTotalTests{ 0 }; + int mPassedTests{ 0 }; + int mFailedTests{ 0 }; + int mSkippedTests{ 0 }; std::shared_ptr<std::ostream> mStream; std::shared_ptr<LLReplayLog> mReplayer; }; @@ -647,14 +645,47 @@ int main(int argc, char **argv) // a chained_callback subclass must be linked with previous mycallback->link(); - if(test_group.empty()) - { - tut::runner.get().run_tests(); - } - else - { - tut::runner.get().run_tests(test_group); - } + LL::seh::catcher( + // __try + [test_group] + { + if(test_group.empty()) + { + tut::runner.get().run_tests(); + } + else + { + tut::runner.get().run_tests(test_group); + } + }, + // __except + [mycallback](U32 code, const std::string& /*stacktrace*/) + { + static std::map<U32, const char*> codes = { + { 0xC0000005, "Access Violation" }, + { 0xC00000FD, "Stack Overflow" }, + // ... continue filling in as desired + }; + + auto found{ codes.find(code) }; + const char* name = ((found == codes.end())? "unknown" : found->second); + auto msg{ stringize("test threw ", std::hex, code, " (", name, ")") }; + + // Instead of bombing the whole test run, report this as a test + // failure. Arguably, catching structured exceptions should be + // hacked into TUT itself. + mycallback->test_completed(tut::test_result( + mycallback->mGroup, + mycallback->mGroupTests+1, // test within group + "unknown", // test name + tut::test_result::ex, // result: exception + // we don't have to throw this exception subclass to use it to + // populate the test_result struct + Windows_SEH_exception(msg))); + // we've left the TUT framework -- finish up by hand + mycallback->group_completed(mycallback->mGroup); + mycallback->run_completed(); + }); bool success = (mycallback->getFailedTests() == 0); |