Merge branch 'main' into DRTVWR-489
# Conflicts: # indra/newview/fonts/DejaVu-license.txt # indra/newview/fonts/DejaVuSans-Bold.ttf # indra/newview/fonts/DejaVuSans-BoldOblique.ttf # indra/newview/fonts/DejaVuSans-Oblique.ttf # indra/newview/fonts/DejaVuSans.ttf # indra/newview/fonts/DejaVuSansMono.ttfmaster
commit
793bed7d06
|
|
@ -254,7 +254,7 @@ jobs:
|
||||||
runs-on: windows
|
runs-on: windows
|
||||||
steps:
|
steps:
|
||||||
- name: Sign and package Windows viewer
|
- name: Sign and package Windows viewer
|
||||||
uses: secondlife/viewer-build-util/sign-pkg-windows@main
|
uses: secondlife/viewer-build-util/sign-pkg-windows@v1
|
||||||
with:
|
with:
|
||||||
vault_uri: "${{ secrets.AZURE_KEY_VAULT_URI }}"
|
vault_uri: "${{ secrets.AZURE_KEY_VAULT_URI }}"
|
||||||
cert_name: "${{ secrets.AZURE_CERT_NAME }}"
|
cert_name: "${{ secrets.AZURE_CERT_NAME }}"
|
||||||
|
|
@ -286,7 +286,7 @@ jobs:
|
||||||
[[ -n "$USERNAME" && -n "$PASSWORD" && -n "$TEAM_ID" ]]
|
[[ -n "$USERNAME" && -n "$PASSWORD" && -n "$TEAM_ID" ]]
|
||||||
|
|
||||||
- name: Sign and package Mac viewer
|
- name: Sign and package Mac viewer
|
||||||
uses: secondlife/viewer-build-util/sign-pkg-mac@main
|
uses: secondlife/viewer-build-util/sign-pkg-mac@v1
|
||||||
with:
|
with:
|
||||||
channel: ${{ needs.build.outputs.viewer_channel }}
|
channel: ${{ needs.build.outputs.viewer_channel }}
|
||||||
imagename: ${{ needs.build.outputs.imagename }}
|
imagename: ${{ needs.build.outputs.imagename }}
|
||||||
|
|
@ -302,7 +302,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Post Windows symbols
|
- name: Post Windows symbols
|
||||||
uses: secondlife/viewer-build-util/post-bugsplat-windows@main
|
uses: secondlife/viewer-build-util/post-bugsplat-windows@v1
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.BUGSPLAT_USER }}
|
username: ${{ secrets.BUGSPLAT_USER }}
|
||||||
password: ${{ secrets.BUGSPLAT_PASS }}
|
password: ${{ secrets.BUGSPLAT_PASS }}
|
||||||
|
|
@ -315,7 +315,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Post Mac symbols
|
- name: Post Mac symbols
|
||||||
uses: secondlife/viewer-build-util/post-bugsplat-mac@main
|
uses: secondlife/viewer-build-util/post-bugsplat-mac@v1
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.BUGSPLAT_USER }}
|
username: ${{ secrets.BUGSPLAT_USER }}
|
||||||
password: ${{ secrets.BUGSPLAT_PASS }}
|
password: ${{ secrets.BUGSPLAT_PASS }}
|
||||||
|
|
@ -330,29 +330,29 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
path: artifacts
|
name: Windows-installer
|
||||||
|
|
||||||
- name: Reshuffle artifact files
|
- uses: actions/download-artifact@v3
|
||||||
uses: secondlife/viewer-build-util/release-artifacts@main
|
|
||||||
with:
|
with:
|
||||||
input-path: artifacts
|
name: macOS-installer
|
||||||
output-path: assets
|
|
||||||
# The *-app artifacts are for use only by the signing and
|
- uses: actions/download-artifact@v3
|
||||||
# packaging steps. Once we've generated signed installers, we no
|
with:
|
||||||
# longer need them, and we CERTAINLY don't want to publish
|
name: Windows-metadata
|
||||||
# thousands of individual files as separate URLs.
|
|
||||||
exclude: |-
|
- name: Rename windows metadata
|
||||||
Windows-app
|
run: |
|
||||||
macOS-app
|
mv autobuild-package.xml Windows-autobuild-package.xml
|
||||||
# Use just "Windows" or "macOS" prefix because these are the only
|
mv newview/viewer_version.txt Windows-viewer_version.txt
|
||||||
# artifacts in which we expect files from both platforms with
|
|
||||||
# colliding names (e.g. autobuild-package.xml). release-artifacts
|
- uses: actions/download-artifact@v3
|
||||||
# normally resolves collisions by prepending the artifact name, so
|
with:
|
||||||
# when we anticipate collisions, it's good to keep the prefix
|
name: macOS-metadata
|
||||||
# short and sweet.
|
|
||||||
prefix: |-
|
- name: Rename macOS metadata
|
||||||
Windows-metadata=Windows
|
run: |
|
||||||
macOS-metadata=macOS
|
mv autobuild-package.xml macOS-autobuild-package.xml
|
||||||
|
mv newview/viewer_version.txt macOS-viewer_version.txt
|
||||||
|
|
||||||
# forked from softprops/action-gh-release
|
# forked from softprops/action-gh-release
|
||||||
- uses: secondlife-3p/action-gh-release@v1
|
- uses: secondlife-3p/action-gh-release@v1
|
||||||
|
|
@ -364,4 +364,8 @@ jobs:
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
# the only reason we generate a GH release is to post build products
|
# the only reason we generate a GH release is to post build products
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
files: "assets/*"
|
files: |
|
||||||
|
*.dmg
|
||||||
|
*.exe
|
||||||
|
*-autobuild-package.xml
|
||||||
|
*-viewer_version.txt
|
||||||
|
|
|
||||||
|
|
@ -91,3 +91,4 @@ web/locale.*
|
||||||
web/secondlife.com.*
|
web/secondlife.com.*
|
||||||
|
|
||||||
.env
|
.env
|
||||||
|
.vscode
|
||||||
|
|
|
||||||
|
|
@ -2731,11 +2731,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>d8bc8720846cfa31e23e7e1008e32ba6ad4a8322</string>
|
<string>eb1316584188dafb591f80b46b357c737f90d1a7</string>
|
||||||
<key>hash_algorithm</key>
|
<key>hash_algorithm</key>
|
||||||
<string>sha1</string>
|
<string>sha1</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://github.com/secondlife/viewer-manager/releases/download/v3.0.cc7ea1e/viewer_manager-3.0.cc7ea1e-darwin64-cc7ea1e.tar.zst</string>
|
<string>https://github.com/secondlife/viewer-manager/releases/download/v3.0-08bf5ee/viewer_manager-3.0-08bf5ee-darwin64-08bf5ee.tar.zst</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>darwin64</string>
|
<string>darwin64</string>
|
||||||
|
|
@ -2745,11 +2745,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>228fae4ee0ce12b9d1d1b0a8ebb0bdf58ee521eb</string>
|
<string>f4677b0ebd9880f29c118af51ada50883dd0a1e4</string>
|
||||||
<key>hash_algorithm</key>
|
<key>hash_algorithm</key>
|
||||||
<string>sha1</string>
|
<string>sha1</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://github.com/secondlife/viewer-manager/releases/download/v3.0.cc7ea1e/viewer_manager-3.0.cc7ea1e-linux64-cc7ea1e.tar.zst</string>
|
<string>https://github.com/secondlife/viewer-manager/releases/download/v3.0-08bf5ee/viewer_manager-3.0-08bf5ee-linux64-08bf5ee.tar.zst</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>linux64</string>
|
<string>linux64</string>
|
||||||
|
|
@ -2759,11 +2759,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>ca6999b64d96d45952fe872b381db9b2abc0248c</string>
|
<string>7426c5a1d7eb231b476625637a1f2daba0a6bc55</string>
|
||||||
<key>hash_algorithm</key>
|
<key>hash_algorithm</key>
|
||||||
<string>sha1</string>
|
<string>sha1</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://github.com/secondlife/viewer-manager/releases/download/v3.0.cc7ea1e/viewer_manager-3.0.cc7ea1e-windows64-cc7ea1e.tar.zst</string>
|
<string>https://github.com/secondlife/viewer-manager/releases/download/v3.0-08bf5ee/viewer_manager-3.0-08bf5ee-windows64-08bf5ee.tar.zst</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>windows64</string>
|
<string>windows64</string>
|
||||||
|
|
@ -2776,7 +2776,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
||||||
<key>copyright</key>
|
<key>copyright</key>
|
||||||
<string>Copyright (c) 2000-2012, Linden Research, Inc.</string>
|
<string>Copyright (c) 2000-2012, Linden Research, Inc.</string>
|
||||||
<key>version</key>
|
<key>version</key>
|
||||||
<string>3.0.cc7ea1e</string>
|
<string>3.0-08bf5ee</string>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>viewer-manager</string>
|
<string>viewer-manager</string>
|
||||||
<key>description</key>
|
<key>description</key>
|
||||||
|
|
|
||||||
|
|
@ -242,6 +242,7 @@ Ansariel Hiller
|
||||||
SL-19575
|
SL-19575
|
||||||
SL-19623
|
SL-19623
|
||||||
SL-4126
|
SL-4126
|
||||||
|
SL-20224
|
||||||
Aralara Rajal
|
Aralara Rajal
|
||||||
Arare Chantilly
|
Arare Chantilly
|
||||||
CHUIBUG-191
|
CHUIBUG-191
|
||||||
|
|
@ -931,6 +932,8 @@ LSL Scientist
|
||||||
Lamorna Proctor
|
Lamorna Proctor
|
||||||
Lares Carter
|
Lares Carter
|
||||||
Larry Pixel
|
Larry Pixel
|
||||||
|
Lars Næsbye Christensen
|
||||||
|
SL-20054
|
||||||
Laurent Bechir
|
Laurent Bechir
|
||||||
Leal Choche
|
Leal Choche
|
||||||
Lenae Munz
|
Lenae Munz
|
||||||
|
|
|
||||||
|
|
@ -29,15 +29,6 @@ else()
|
||||||
set( USE_AUTOBUILD_3P ON )
|
set( USE_AUTOBUILD_3P ON )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# The viewer code base can now be successfully compiled with -std=c++14. But
|
|
||||||
# turning that on in the generic viewer-build-variables/variables file would
|
|
||||||
# potentially require tweaking each of our ~50 third-party library builds.
|
|
||||||
# Until we decide to set -std=c++14 in viewer-build-variables/variables, set
|
|
||||||
# it locally here: we want to at least prevent inadvertently reintroducing
|
|
||||||
# viewer code that would fail with C++14.
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
|
|
||||||
include(Variables)
|
include(Variables)
|
||||||
include(BuildVersion)
|
include(BuildVersion)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,8 @@ if(WINDOWS)
|
||||||
endif (USE_BUGSPLAT)
|
endif (USE_BUGSPLAT)
|
||||||
|
|
||||||
if (TARGET ll::fmodstudio)
|
if (TARGET ll::fmodstudio)
|
||||||
set(debug_files ${debug_files} fmodL.dll)
|
# fmodL is included for logging, only one should be picked by manifest
|
||||||
|
set(release_files ${release_files} fmodL.dll)
|
||||||
set(release_files ${release_files} fmod.dll)
|
set(release_files ${release_files} fmod.dll)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ elseif (WINDOWS)
|
||||||
foreach(hive HKEY_CURRENT_USER HKEY_LOCAL_MACHINE)
|
foreach(hive HKEY_CURRENT_USER HKEY_LOCAL_MACHINE)
|
||||||
# prefer more recent Python versions to older ones, if multiple versions
|
# prefer more recent Python versions to older ones, if multiple versions
|
||||||
# are installed
|
# are installed
|
||||||
foreach(pyver 3.11 3.10 3.9 3.8 3.7)
|
foreach(pyver 3.12 3.11 3.10 3.9 3.8 3.7)
|
||||||
list(APPEND regpaths "[${hive}\\SOFTWARE\\Python\\PythonCore\\${pyver}\\InstallPath]")
|
list(APPEND regpaths "[${hive}\\SOFTWARE\\Python\\PythonCore\\${pyver}\\InstallPath]")
|
||||||
endforeach()
|
endforeach()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
@ -40,7 +40,7 @@ elseif (WINDOWS)
|
||||||
${regpaths}
|
${regpaths}
|
||||||
${pymaybe}
|
${pymaybe}
|
||||||
)
|
)
|
||||||
include(FindPythonInterp)
|
find_package(Python3 COMPONENTS Interpreter)
|
||||||
else()
|
else()
|
||||||
find_program(python python3)
|
find_program(python python3)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,10 @@ include(Tracy)
|
||||||
|
|
||||||
|
|
||||||
set(llcommon_SOURCE_FILES
|
set(llcommon_SOURCE_FILES
|
||||||
|
apply.cpp
|
||||||
commoncontrol.cpp
|
commoncontrol.cpp
|
||||||
indra_constants.cpp
|
indra_constants.cpp
|
||||||
|
lazyeventapi.cpp
|
||||||
llallocator.cpp
|
llallocator.cpp
|
||||||
llallocator_heap_profile.cpp
|
llallocator_heap_profile.cpp
|
||||||
llapp.cpp
|
llapp.cpp
|
||||||
|
|
@ -116,12 +118,16 @@ set(llcommon_SOURCE_FILES
|
||||||
set(llcommon_HEADER_FILES
|
set(llcommon_HEADER_FILES
|
||||||
CMakeLists.txt
|
CMakeLists.txt
|
||||||
|
|
||||||
|
always_return.h
|
||||||
|
apply.h
|
||||||
chrono.h
|
chrono.h
|
||||||
classic_callback.h
|
classic_callback.h
|
||||||
commoncontrol.h
|
commoncontrol.h
|
||||||
ctype_workaround.h
|
ctype_workaround.h
|
||||||
fix_macros.h
|
fix_macros.h
|
||||||
|
function_types.h
|
||||||
indra_constants.h
|
indra_constants.h
|
||||||
|
lazyeventapi.h
|
||||||
linden_common.h
|
linden_common.h
|
||||||
llalignedarray.h
|
llalignedarray.h
|
||||||
llallocator.h
|
llallocator.h
|
||||||
|
|
@ -294,9 +300,11 @@ if (LL_TESTS)
|
||||||
|
|
||||||
#set(TEST_DEBUG on)
|
#set(TEST_DEBUG on)
|
||||||
set(test_libs llcommon)
|
set(test_libs llcommon)
|
||||||
|
LL_ADD_INTEGRATION_TEST(apply "" "${test_libs}")
|
||||||
LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}")
|
LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}")
|
||||||
LL_ADD_INTEGRATION_TEST(classic_callback "" "${test_libs}")
|
LL_ADD_INTEGRATION_TEST(classic_callback "" "${test_libs}")
|
||||||
LL_ADD_INTEGRATION_TEST(commonmisc "" "${test_libs}")
|
LL_ADD_INTEGRATION_TEST(commonmisc "" "${test_libs}")
|
||||||
|
LL_ADD_INTEGRATION_TEST(lazyeventapi "" "${test_libs}")
|
||||||
LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}")
|
LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}")
|
||||||
LL_ADD_INTEGRATION_TEST(llcond "" "${test_libs}")
|
LL_ADD_INTEGRATION_TEST(llcond "" "${test_libs}")
|
||||||
LL_ADD_INTEGRATION_TEST(lldate "" "${test_libs}")
|
LL_ADD_INTEGRATION_TEST(lldate "" "${test_libs}")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
/**
|
||||||
|
* @file always_return.h
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2023-01-20
|
||||||
|
* @brief Call specified callable with arbitrary arguments, but always return
|
||||||
|
* specified type.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
|
||||||
|
* Copyright (c) 2023, Linden Research, Inc.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if ! defined(LL_ALWAYS_RETURN_H)
|
||||||
|
#define LL_ALWAYS_RETURN_H
|
||||||
|
|
||||||
|
#include <type_traits> // std::enable_if, std::is_convertible
|
||||||
|
|
||||||
|
namespace LL
|
||||||
|
{
|
||||||
|
|
||||||
|
#if __cpp_lib_is_invocable >= 201703L // C++17
|
||||||
|
template <typename CALLABLE, typename... ARGS>
|
||||||
|
using invoke_result = std::invoke_result<CALLABLE, ARGS...>;
|
||||||
|
#else // C++14
|
||||||
|
template <typename CALLABLE, typename... ARGS>
|
||||||
|
using invoke_result = std::result_of<CALLABLE(ARGS...)>;
|
||||||
|
#endif // C++14
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AlwaysReturn<T>()(some_function, some_args...) calls
|
||||||
|
* some_function(some_args...). It is guaranteed to return a value of type
|
||||||
|
* T, regardless of the return type of some_function(). If some_function()
|
||||||
|
* returns a type convertible to T, it will convert and return that value.
|
||||||
|
* Otherwise (notably if some_function() is void), AlwaysReturn returns
|
||||||
|
* T().
|
||||||
|
*
|
||||||
|
* When some_function() returns a type not convertible to T, if
|
||||||
|
* you want AlwaysReturn to return some T value other than
|
||||||
|
* default-constructed T(), pass that value to AlwaysReturn's constructor.
|
||||||
|
*/
|
||||||
|
template <typename DESIRED>
|
||||||
|
class AlwaysReturn
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// pass explicit default value if other than default-constructed type
|
||||||
|
AlwaysReturn(const DESIRED& dft=DESIRED()): mDefault(dft) {}
|
||||||
|
|
||||||
|
// callable returns a type not convertible to DESIRED, return default
|
||||||
|
template <typename CALLABLE, typename... ARGS,
|
||||||
|
typename std::enable_if<
|
||||||
|
! std::is_convertible<
|
||||||
|
typename invoke_result<CALLABLE, ARGS...>::type,
|
||||||
|
DESIRED
|
||||||
|
>::value,
|
||||||
|
bool
|
||||||
|
>::type=true>
|
||||||
|
DESIRED operator()(CALLABLE&& callable, ARGS&&... args)
|
||||||
|
{
|
||||||
|
// discard whatever callable(args) returns
|
||||||
|
std::forward<CALLABLE>(callable)(std::forward<ARGS>(args)...);
|
||||||
|
return mDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
// callable returns a type convertible to DESIRED
|
||||||
|
template <typename CALLABLE, typename... ARGS,
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_convertible<
|
||||||
|
typename invoke_result<CALLABLE, ARGS...>::type,
|
||||||
|
DESIRED
|
||||||
|
>::value,
|
||||||
|
bool
|
||||||
|
>::type=true>
|
||||||
|
DESIRED operator()(CALLABLE&& callable, ARGS&&... args)
|
||||||
|
{
|
||||||
|
return { std::forward<CALLABLE>(callable)(std::forward<ARGS>(args)...) };
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DESIRED mDefault;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* always_return<T>(some_function, some_args...) calls
|
||||||
|
* some_function(some_args...). It is guaranteed to return a value of type
|
||||||
|
* T, regardless of the return type of some_function(). If some_function()
|
||||||
|
* returns a type convertible to T, it will convert and return that value.
|
||||||
|
* Otherwise (notably if some_function() is void), always_return() returns
|
||||||
|
* T().
|
||||||
|
*/
|
||||||
|
template <typename DESIRED, typename CALLABLE, typename... ARGS>
|
||||||
|
DESIRED always_return(CALLABLE&& callable, ARGS&&... args)
|
||||||
|
{
|
||||||
|
return AlwaysReturn<DESIRED>()(std::forward<CALLABLE>(callable),
|
||||||
|
std::forward<ARGS>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make_always_return<T>(some_function) returns a callable which, when
|
||||||
|
* called with appropriate some_function() arguments, always returns a
|
||||||
|
* value of type T, regardless of the return type of some_function(). If
|
||||||
|
* some_function() returns a type convertible to T, the returned callable
|
||||||
|
* will convert and return that value. Otherwise (notably if
|
||||||
|
* some_function() is void), the returned callable returns T().
|
||||||
|
*
|
||||||
|
* When some_function() returns a type not convertible to T, if
|
||||||
|
* you want the returned callable to return some T value other than
|
||||||
|
* default-constructed T(), pass that value to make_always_return() as its
|
||||||
|
* optional second argument.
|
||||||
|
*/
|
||||||
|
template <typename DESIRED, typename CALLABLE>
|
||||||
|
auto make_always_return(CALLABLE&& callable, const DESIRED& dft=DESIRED())
|
||||||
|
{
|
||||||
|
return
|
||||||
|
[dft, callable = std::forward<CALLABLE>(callable)]
|
||||||
|
(auto&&... args)
|
||||||
|
{
|
||||||
|
return AlwaysReturn<DESIRED>(dft)(callable,
|
||||||
|
std::forward<decltype(args)>(args)...);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace LL
|
||||||
|
|
||||||
|
#endif /* ! defined(LL_ALWAYS_RETURN_H) */
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* @file apply.cpp
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2022-12-21
|
||||||
|
* @brief Implementation for apply.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
||||||
|
* Copyright (c) 2022, Linden Research, Inc.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Precompiled header
|
||||||
|
#include "linden_common.h"
|
||||||
|
// associated header
|
||||||
|
#include "apply.h"
|
||||||
|
// STL headers
|
||||||
|
// std headers
|
||||||
|
// external library headers
|
||||||
|
// other Linden headers
|
||||||
|
#include "stringize.h"
|
||||||
|
|
||||||
|
void LL::apply_validate_size(size_t size, size_t arity)
|
||||||
|
{
|
||||||
|
if (size != arity)
|
||||||
|
{
|
||||||
|
LLTHROW(apply_error(stringize("LL::apply(func(", arity, " args), "
|
||||||
|
"std::vector(", size, " elements))")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,8 +12,11 @@
|
||||||
#if ! defined(LL_APPLY_H)
|
#if ! defined(LL_APPLY_H)
|
||||||
#define LL_APPLY_H
|
#define LL_APPLY_H
|
||||||
|
|
||||||
|
#include "llexception.h"
|
||||||
#include <boost/type_traits/function_traits.hpp>
|
#include <boost/type_traits/function_traits.hpp>
|
||||||
|
#include <functional> // std::mem_fn()
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <type_traits> // std::is_member_pointer
|
||||||
|
|
||||||
namespace LL
|
namespace LL
|
||||||
{
|
{
|
||||||
|
|
@ -54,20 +57,67 @@ namespace LL
|
||||||
}, \
|
}, \
|
||||||
(ARGS))
|
(ARGS))
|
||||||
|
|
||||||
#if __cplusplus >= 201703L
|
/*****************************************************************************
|
||||||
|
* invoke()
|
||||||
|
*****************************************************************************/
|
||||||
|
#if __cpp_lib_invoke >= 201411L
|
||||||
|
|
||||||
// C++17 implementation
|
// C++17 implementation
|
||||||
using std::apply;
|
using std::invoke;
|
||||||
|
|
||||||
|
#else // no std::invoke
|
||||||
|
|
||||||
|
// Use invoke() to handle pointer-to-method:
|
||||||
|
// derived from https://stackoverflow.com/a/38288251
|
||||||
|
template<typename Fn, typename... Args,
|
||||||
|
typename std::enable_if<std::is_member_pointer<typename std::decay<Fn>::type>::value,
|
||||||
|
int>::type = 0 >
|
||||||
|
auto invoke(Fn&& f, Args&&... args)
|
||||||
|
{
|
||||||
|
return std::mem_fn(std::forward<Fn>(f))(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Fn, typename... Args,
|
||||||
|
typename std::enable_if<!std::is_member_pointer<typename std::decay<Fn>::type>::value,
|
||||||
|
int>::type = 0 >
|
||||||
|
auto invoke(Fn&& f, Args&&... args)
|
||||||
|
{
|
||||||
|
return std::forward<Fn>(f)(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // no std::invoke
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* apply(function, tuple); apply(function, array)
|
||||||
|
*****************************************************************************/
|
||||||
|
#if __cpp_lib_apply >= 201603L
|
||||||
|
|
||||||
|
// C++17 implementation
|
||||||
|
// We don't just say 'using std::apply;' because that template is too general:
|
||||||
|
// it also picks up the apply(function, vector) case, which we want to handle
|
||||||
|
// below.
|
||||||
|
template <typename CALLABLE, typename... ARGS>
|
||||||
|
auto apply(CALLABLE&& func, const std::tuple<ARGS...>& args)
|
||||||
|
{
|
||||||
|
return std::apply(std::forward<CALLABLE>(func), args);
|
||||||
|
}
|
||||||
|
|
||||||
#else // C++14
|
#else // C++14
|
||||||
|
|
||||||
// Derived from https://stackoverflow.com/a/20441189
|
// Derived from https://stackoverflow.com/a/20441189
|
||||||
// and https://en.cppreference.com/w/cpp/utility/apply
|
// and https://en.cppreference.com/w/cpp/utility/apply
|
||||||
template <typename CALLABLE, typename TUPLE, std::size_t... I>
|
template <typename CALLABLE, typename... ARGS, std::size_t... I>
|
||||||
auto apply_impl(CALLABLE&& func, TUPLE&& args, std::index_sequence<I...>)
|
auto apply_impl(CALLABLE&& func, const std::tuple<ARGS...>& args, std::index_sequence<I...>)
|
||||||
{
|
{
|
||||||
|
// We accept const std::tuple& so a caller can construct an tuple on the
|
||||||
|
// fly. But std::get<I>(const tuple) adds a const qualifier to everything
|
||||||
|
// it extracts. Get a non-const ref to this tuple so we can extract
|
||||||
|
// without the extraneous const.
|
||||||
|
auto& non_const_args{ const_cast<std::tuple<ARGS...>&>(args) };
|
||||||
|
|
||||||
// call func(unpacked args)
|
// call func(unpacked args)
|
||||||
return std::forward<CALLABLE>(func)(std::move(std::get<I>(args))...);
|
return invoke(std::forward<CALLABLE>(func),
|
||||||
|
std::forward<ARGS>(std::get<I>(non_const_args))...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CALLABLE, typename... ARGS>
|
template <typename CALLABLE, typename... ARGS>
|
||||||
|
|
@ -81,6 +131,8 @@ auto apply(CALLABLE&& func, const std::tuple<ARGS...>& args)
|
||||||
std::index_sequence_for<ARGS...>{});
|
std::index_sequence_for<ARGS...>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // C++14
|
||||||
|
|
||||||
// per https://stackoverflow.com/a/57510428/5533635
|
// per https://stackoverflow.com/a/57510428/5533635
|
||||||
template <typename CALLABLE, typename T, size_t SIZE>
|
template <typename CALLABLE, typename T, size_t SIZE>
|
||||||
auto apply(CALLABLE&& func, const std::array<T, SIZE>& args)
|
auto apply(CALLABLE&& func, const std::array<T, SIZE>& args)
|
||||||
|
|
@ -88,28 +140,92 @@ auto apply(CALLABLE&& func, const std::array<T, SIZE>& args)
|
||||||
return apply(std::forward<CALLABLE>(func), std::tuple_cat(args));
|
return apply(std::forward<CALLABLE>(func), std::tuple_cat(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* bind_front()
|
||||||
|
*****************************************************************************/
|
||||||
|
// To invoke a non-static member function with a tuple, you need a callable
|
||||||
|
// that binds your member function with an instance pointer or reference.
|
||||||
|
// std::bind_front() is perfect: std::bind_front(&cls::method, instance).
|
||||||
|
// Unfortunately bind_front() only enters the standard library in C++20.
|
||||||
|
#if __cpp_lib_bind_front >= 201907L
|
||||||
|
|
||||||
|
// C++20 implementation
|
||||||
|
using std::bind_front;
|
||||||
|
|
||||||
|
#else // no std::bind_front()
|
||||||
|
|
||||||
|
template<typename Fn, typename... Args,
|
||||||
|
typename std::enable_if<!std::is_member_pointer<typename std::decay<Fn>::type>::value,
|
||||||
|
int>::type = 0 >
|
||||||
|
auto bind_front(Fn&& f, Args&&... args)
|
||||||
|
{
|
||||||
|
// Don't use perfect forwarding for f or args: we must bind them for later.
|
||||||
|
return [f, pfx_args=std::make_tuple(args...)]
|
||||||
|
(auto&&... sfx_args)
|
||||||
|
{
|
||||||
|
// Use perfect forwarding for sfx_args because we use them as soon as
|
||||||
|
// we receive them.
|
||||||
|
return apply(
|
||||||
|
f,
|
||||||
|
std::tuple_cat(pfx_args,
|
||||||
|
std::make_tuple(std::forward<decltype(sfx_args)>(sfx_args)...)));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Fn, typename... Args,
|
||||||
|
typename std::enable_if<std::is_member_pointer<typename std::decay<Fn>::type>::value,
|
||||||
|
int>::type = 0 >
|
||||||
|
auto bind_front(Fn&& f, Args&&... args)
|
||||||
|
{
|
||||||
|
return bind_front(std::mem_fn(std::forward<Fn>(f)), std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // C++20 with std::bind_front()
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* apply(function, std::vector)
|
||||||
|
*****************************************************************************/
|
||||||
// per https://stackoverflow.com/a/28411055/5533635
|
// per https://stackoverflow.com/a/28411055/5533635
|
||||||
template <typename CALLABLE, typename T, std::size_t... I>
|
template <typename CALLABLE, typename T, std::size_t... I>
|
||||||
auto apply_impl(CALLABLE&& func, const std::vector<T>& args, std::index_sequence<I...>)
|
auto apply_impl(CALLABLE&& func, const std::vector<T>& args, std::index_sequence<I...>)
|
||||||
{
|
{
|
||||||
return apply_impl(std::forward<CALLABLE>(func),
|
return apply(std::forward<CALLABLE>(func),
|
||||||
std::make_tuple(std::forward<T>(args[I])...),
|
std::make_tuple(args[I]...));
|
||||||
I...);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this goes beyond C++17 std::apply()
|
// produce suitable error if apply(func, vector) is the wrong size for func()
|
||||||
|
void apply_validate_size(size_t size, size_t arity);
|
||||||
|
|
||||||
|
/// possible exception from apply() validation
|
||||||
|
struct apply_error: public LLException
|
||||||
|
{
|
||||||
|
apply_error(const std::string& what): LLException(what) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t ARITY, typename CALLABLE, typename T>
|
||||||
|
auto apply_n(CALLABLE&& func, const std::vector<T>& args)
|
||||||
|
{
|
||||||
|
apply_validate_size(args.size(), ARITY);
|
||||||
|
return apply_impl(std::forward<CALLABLE>(func),
|
||||||
|
args,
|
||||||
|
std::make_index_sequence<ARITY>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* apply(function, std::vector) goes beyond C++17 std::apply(). For this case
|
||||||
|
* @a function @emph cannot be variadic: the compiler must know at compile
|
||||||
|
* time how many arguments to pass. This isn't Python. (But see apply_n() to
|
||||||
|
* pass a specific number of args to a variadic function.)
|
||||||
|
*/
|
||||||
template <typename CALLABLE, typename T>
|
template <typename CALLABLE, typename T>
|
||||||
auto apply(CALLABLE&& func, const std::vector<T>& args)
|
auto apply(CALLABLE&& func, const std::vector<T>& args)
|
||||||
{
|
{
|
||||||
|
// infer arity from the definition of func
|
||||||
constexpr auto arity = boost::function_traits<CALLABLE>::arity;
|
constexpr auto arity = boost::function_traits<CALLABLE>::arity;
|
||||||
assert(args.size() == arity);
|
// now that we have a compile-time arity, apply_n() works
|
||||||
return apply_impl(std::forward<CALLABLE>(func),
|
return apply_n<arity>(std::forward<CALLABLE>(func), args);
|
||||||
args,
|
|
||||||
std::make_index_sequence<arity>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // C++14
|
|
||||||
|
|
||||||
} // namespace LL
|
} // namespace LL
|
||||||
|
|
||||||
#endif /* ! defined(LL_APPLY_H) */
|
#endif /* ! defined(LL_APPLY_H) */
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
* @file function_types.h
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2023-01-20
|
||||||
|
* @brief Extend boost::function_types to examine boost::function and
|
||||||
|
* std::function
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
|
||||||
|
* Copyright (c) 2023, Linden Research, Inc.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if ! defined(LL_FUNCTION_TYPES_H)
|
||||||
|
#define LL_FUNCTION_TYPES_H
|
||||||
|
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
#include <boost/function_types/function_arity.hpp>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace LL
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
struct function_arity_impl
|
||||||
|
{
|
||||||
|
static constexpr auto value = boost::function_types::function_arity<F>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
struct function_arity_impl<std::function<F>>
|
||||||
|
{
|
||||||
|
static constexpr auto value = function_arity_impl<F>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
struct function_arity_impl<boost::function<F>>
|
||||||
|
{
|
||||||
|
static constexpr auto value = function_arity_impl<F>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
struct function_arity
|
||||||
|
{
|
||||||
|
static constexpr auto value = function_arity_impl<typename std::decay<F>::type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace LL
|
||||||
|
|
||||||
|
#endif /* ! defined(LL_FUNCTION_TYPES_H) */
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
* @file lazyeventapi.cpp
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2022-06-17
|
||||||
|
* @brief Implementation for lazyeventapi.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
||||||
|
* Copyright (c) 2022, Linden Research, Inc.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Precompiled header
|
||||||
|
#include "linden_common.h"
|
||||||
|
// associated header
|
||||||
|
#include "lazyeventapi.h"
|
||||||
|
// STL headers
|
||||||
|
// std headers
|
||||||
|
#include <algorithm> // std::find_if
|
||||||
|
// external library headers
|
||||||
|
// other Linden headers
|
||||||
|
#include "llevents.h"
|
||||||
|
#include "llsdutil.h"
|
||||||
|
|
||||||
|
LL::LazyEventAPIBase::LazyEventAPIBase(
|
||||||
|
const std::string& name, const std::string& desc, const std::string& field)
|
||||||
|
{
|
||||||
|
// populate embedded LazyEventAPIParams instance
|
||||||
|
mParams.name = name;
|
||||||
|
mParams.desc = desc;
|
||||||
|
mParams.field = field;
|
||||||
|
// mParams.init and mOperations are populated by subsequent add() calls.
|
||||||
|
|
||||||
|
// Our raison d'etre: register as an LLEventPumps::PumpFactory
|
||||||
|
// so obtain() will notice any request for this name and call us.
|
||||||
|
// Of course, our subclass constructor must finish running (making add()
|
||||||
|
// calls) before mParams will be fully populated, but we expect that to
|
||||||
|
// happen well before the first LLEventPumps::obtain(name) call.
|
||||||
|
mRegistered = LLEventPumps::instance().registerPumpFactory(
|
||||||
|
name,
|
||||||
|
[this](const std::string& name){ return construct(name); });
|
||||||
|
}
|
||||||
|
|
||||||
|
LL::LazyEventAPIBase::~LazyEventAPIBase()
|
||||||
|
{
|
||||||
|
// If our constructor's registerPumpFactory() call was unsuccessful, that
|
||||||
|
// probably means somebody else claimed the name first. If that's the
|
||||||
|
// case, do NOT unregister their name out from under them!
|
||||||
|
// If this is a static instance being destroyed at process shutdown,
|
||||||
|
// LLEventPumps will probably have been cleaned up already.
|
||||||
|
if (mRegistered && ! LLEventPumps::wasDeleted())
|
||||||
|
{
|
||||||
|
// unregister the callback to this doomed instance
|
||||||
|
LLEventPumps::instance().unregisterPumpFactory(mParams.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LLSD LL::LazyEventAPIBase::getMetadata(const std::string& name) const
|
||||||
|
{
|
||||||
|
// Since mOperations is a vector rather than a map, just search.
|
||||||
|
auto found = std::find_if(mOperations.begin(), mOperations.end(),
|
||||||
|
[&name](const auto& namedesc)
|
||||||
|
{ return (namedesc.first == name); });
|
||||||
|
if (found == mOperations.end())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// LLEventDispatcher() supplements the returned metadata in different
|
||||||
|
// ways, depending on metadata provided to the specific add() method.
|
||||||
|
// Don't try to emulate all that. At some point we might consider more
|
||||||
|
// closely unifying LLEventDispatcher machinery with LazyEventAPI, but for
|
||||||
|
// now this will have to do.
|
||||||
|
return llsd::map("name", found->first, "desc", found->second);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,205 @@
|
||||||
|
/**
|
||||||
|
* @file lazyeventapi.h
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2022-06-16
|
||||||
|
* @brief Declaring a static module-scope LazyEventAPI registers a specific
|
||||||
|
* LLEventAPI for future on-demand instantiation.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
||||||
|
* Copyright (c) 2022, Linden Research, Inc.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if ! defined(LL_LAZYEVENTAPI_H)
|
||||||
|
#define LL_LAZYEVENTAPI_H
|
||||||
|
|
||||||
|
#include "apply.h"
|
||||||
|
#include "lleventapi.h"
|
||||||
|
#include "llinstancetracker.h"
|
||||||
|
#include <boost/signals2/signal.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <utility> // std::pair
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace LL
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Bundle params we want to pass to LLEventAPI's protected constructor. We
|
||||||
|
* package them this way so a subclass constructor can simply forward an
|
||||||
|
* opaque reference to the LLEventAPI constructor.
|
||||||
|
*/
|
||||||
|
// This is a class instead of a plain struct mostly so when we forward-
|
||||||
|
// declare it we don't have to remember the distinction.
|
||||||
|
class LazyEventAPIParams
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// package the parameters used by the normal LLEventAPI constructor
|
||||||
|
std::string name, desc, field;
|
||||||
|
// bundle LLEventAPI::add() calls collected by LazyEventAPI::add(), so
|
||||||
|
// the special LLEventAPI constructor we engage can "play back" those
|
||||||
|
// add() calls
|
||||||
|
boost::signals2::signal<void(LLEventAPI*)> init;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LazyEventAPIBase implements most of the functionality of LazyEventAPI
|
||||||
|
* (q.v.), but we need the LazyEventAPI template subclass so we can accept
|
||||||
|
* the specific LLEventAPI subclass type.
|
||||||
|
*/
|
||||||
|
// No LLInstanceTracker key: we don't need to find a specific instance,
|
||||||
|
// LLLeapListener just needs to be able to enumerate all instances.
|
||||||
|
class LazyEventAPIBase: public LLInstanceTracker<LazyEventAPIBase>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LazyEventAPIBase(const std::string& name, const std::string& desc,
|
||||||
|
const std::string& field);
|
||||||
|
virtual ~LazyEventAPIBase();
|
||||||
|
|
||||||
|
// Do not copy or move: once constructed, LazyEventAPIBase must stay
|
||||||
|
// put: we bind its instance pointer into a callback.
|
||||||
|
LazyEventAPIBase(const LazyEventAPIBase&) = delete;
|
||||||
|
LazyEventAPIBase(LazyEventAPIBase&&) = delete;
|
||||||
|
LazyEventAPIBase& operator=(const LazyEventAPIBase&) = delete;
|
||||||
|
LazyEventAPIBase& operator=(LazyEventAPIBase&&) = delete;
|
||||||
|
|
||||||
|
// capture add() calls we want to play back on LLEventAPI construction
|
||||||
|
template <typename... ARGS>
|
||||||
|
void add(const std::string& name, const std::string& desc, ARGS&&... rest)
|
||||||
|
{
|
||||||
|
// capture the metadata separately
|
||||||
|
mOperations.push_back(std::make_pair(name, desc));
|
||||||
|
// Use connect_extended() so the lambda is passed its own
|
||||||
|
// connection.
|
||||||
|
|
||||||
|
// apply() can't accept a template per se; it needs a particular
|
||||||
|
// specialization. Specialize out here to work around a clang bug:
|
||||||
|
// https://github.com/llvm/llvm-project/issues/41999
|
||||||
|
auto func{ &LazyEventAPIBase::add_trampoline
|
||||||
|
<const std::string&, const std::string&, ARGS...> };
|
||||||
|
// We can't bind an unexpanded parameter pack into a lambda --
|
||||||
|
// shame really. Instead, capture all our args as a std::tuple and
|
||||||
|
// then, in the lambda, use apply() to pass to add_trampoline().
|
||||||
|
auto args{ std::make_tuple(name, desc, std::forward<ARGS>(rest)...) };
|
||||||
|
|
||||||
|
mParams.init.connect_extended(
|
||||||
|
[func, args]
|
||||||
|
(const boost::signals2::connection& conn, LLEventAPI* instance)
|
||||||
|
{
|
||||||
|
// we only need this connection once
|
||||||
|
conn.disconnect();
|
||||||
|
// apply() expects a tuple specifying ALL the arguments,
|
||||||
|
// so prepend instance.
|
||||||
|
apply(func, std::tuple_cat(std::make_tuple(instance), args));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following queries mimic the LLEventAPI / LLEventDispatcher
|
||||||
|
// query API.
|
||||||
|
|
||||||
|
// Get the string name of the subject LLEventAPI
|
||||||
|
std::string getName() const { return mParams.name; }
|
||||||
|
// Get the documentation string
|
||||||
|
std::string getDesc() const { return mParams.desc; }
|
||||||
|
// Retrieve the LLSD key we use for dispatching
|
||||||
|
std::string getDispatchKey() const { return mParams.field; }
|
||||||
|
|
||||||
|
// operations
|
||||||
|
using NameDesc = std::pair<std::string, std::string>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// metadata that might be queried by LLLeapListener
|
||||||
|
std::vector<NameDesc> mOperations;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using const_iterator = decltype(mOperations)::const_iterator;
|
||||||
|
const_iterator begin() const { return mOperations.begin(); }
|
||||||
|
const_iterator end() const { return mOperations.end(); }
|
||||||
|
LLSD getMetadata(const std::string& name) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Params with which to instantiate the companion LLEventAPI subclass
|
||||||
|
LazyEventAPIParams mParams;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// true if we successfully registered our LLEventAPI on construction
|
||||||
|
bool mRegistered;
|
||||||
|
|
||||||
|
// actually instantiate the companion LLEventAPI subclass
|
||||||
|
virtual LLEventPump* construct(const std::string& name) = 0;
|
||||||
|
|
||||||
|
// Passing an overloaded function to any function that accepts an
|
||||||
|
// arbitrary callable is a PITB because you have to specify the
|
||||||
|
// correct overload. What we want is for the compiler to select the
|
||||||
|
// correct overload, based on the carefully-wrought enable_ifs in
|
||||||
|
// LLEventDispatcher. This (one and only) add_trampoline() method
|
||||||
|
// exists solely to pass to LL::apply(). Once add_trampoline() is
|
||||||
|
// called with the expanded arguments, we hope the compiler will Do
|
||||||
|
// The Right Thing in selecting the correct LLEventAPI::add()
|
||||||
|
// overload.
|
||||||
|
template <typename... ARGS>
|
||||||
|
static
|
||||||
|
void add_trampoline(LLEventAPI* instance, ARGS&&... args)
|
||||||
|
{
|
||||||
|
instance->add(std::forward<ARGS>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LazyEventAPI provides a way to register a particular LLEventAPI to be
|
||||||
|
* instantiated on demand, that is, when its name is passed to
|
||||||
|
* LLEventPumps::obtain().
|
||||||
|
*
|
||||||
|
* Derive your listener from LLEventAPI as usual, with its various
|
||||||
|
* operation methods, but code your constructor to accept
|
||||||
|
* <tt>(const LL::LazyEventAPIParams& params)</tt>
|
||||||
|
* and forward that reference to (the protected)
|
||||||
|
* <tt>LLEventAPI(const LL::LazyEventAPIParams&)</tt> constructor.
|
||||||
|
*
|
||||||
|
* Then derive your listener registrar from
|
||||||
|
* <tt>LazyEventAPI<your LLEventAPI subclass></tt>. The constructor should
|
||||||
|
* look very like a traditional LLEventAPI constructor:
|
||||||
|
*
|
||||||
|
* * pass (name, desc [, field]) to LazyEventAPI's constructor
|
||||||
|
* * in the body, make a series of add() calls referencing your LLEventAPI
|
||||||
|
* subclass methods.
|
||||||
|
*
|
||||||
|
* You may use any LLEventAPI::add() methods, that is, any
|
||||||
|
* LLEventDispatcher::add() methods. But the target methods you pass to
|
||||||
|
* add() must belong to your LLEventAPI subclass, not the LazyEventAPI
|
||||||
|
* subclass.
|
||||||
|
*
|
||||||
|
* Declare a static instance of your LazyEventAPI listener registrar
|
||||||
|
* class. When it's constructed at static initialization time, it will
|
||||||
|
* register your LLEventAPI subclass with LLEventPumps. It will also
|
||||||
|
* collect metadata for the LLEventAPI and its operations to provide to
|
||||||
|
* LLLeapListener's introspection queries.
|
||||||
|
*
|
||||||
|
* When someone later calls LLEventPumps::obtain() to post an event to
|
||||||
|
* your LLEventAPI subclass, obtain() will instantiate it using
|
||||||
|
* LazyEventAPI's name, desc, field and add() calls.
|
||||||
|
*/
|
||||||
|
template <class EVENTAPI>
|
||||||
|
class LazyEventAPI: public LazyEventAPIBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// for subclass constructor to reference handler methods
|
||||||
|
using listener = EVENTAPI;
|
||||||
|
|
||||||
|
LazyEventAPI(const std::string& name, const std::string& desc,
|
||||||
|
const std::string& field="op"):
|
||||||
|
// Forward ctor params to LazyEventAPIBase
|
||||||
|
LazyEventAPIBase(name, desc, field)
|
||||||
|
{}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LLEventPump* construct(const std::string& /*name*/) override
|
||||||
|
{
|
||||||
|
// base class has carefully assembled LazyEventAPIParams embedded
|
||||||
|
// in this instance, just pass to LLEventAPI subclass constructor
|
||||||
|
return new EVENTAPI(mParams);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace LL
|
||||||
|
|
||||||
|
#endif /* ! defined(LL_LAZYEVENTAPI_H) */
|
||||||
|
|
@ -38,6 +38,12 @@ const S32 FULL_VOLATILE_APR_POOL = 1024 ; //number of references to LLVolatileAP
|
||||||
|
|
||||||
bool gAPRInitialized = false;
|
bool gAPRInitialized = false;
|
||||||
|
|
||||||
|
int abortfunc(int retcode)
|
||||||
|
{
|
||||||
|
LL_WARNS("APR") << "Allocation failure in apr pool with code " << (S32)retcode << LL_ENDL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void ll_init_apr()
|
void ll_init_apr()
|
||||||
{
|
{
|
||||||
// Initialize APR and create the global pool
|
// Initialize APR and create the global pool
|
||||||
|
|
@ -45,7 +51,7 @@ void ll_init_apr()
|
||||||
|
|
||||||
if (!gAPRPoolp)
|
if (!gAPRPoolp)
|
||||||
{
|
{
|
||||||
apr_pool_create(&gAPRPoolp, NULL);
|
apr_pool_create_ex(&gAPRPoolp, NULL, abortfunc, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!LLAPRFile::sAPRFilePoolp)
|
if(!LLAPRFile::sAPRFilePoolp)
|
||||||
|
|
|
||||||
|
|
@ -278,6 +278,7 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl
|
||||||
catch (std::bad_alloc&)
|
catch (std::bad_alloc&)
|
||||||
{
|
{
|
||||||
// Out of memory on stack allocation?
|
// Out of memory on stack allocation?
|
||||||
|
printActiveCoroutines();
|
||||||
LL_ERRS("LLCoros") << "Bad memory allocation in LLCoros::launch(" << prefix << ")!" << LL_ENDL;
|
LL_ERRS("LLCoros") << "Bad memory allocation in LLCoros::launch(" << prefix << ")!" << LL_ENDL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@
|
||||||
// external library headers
|
// external library headers
|
||||||
// other Linden headers
|
// other Linden headers
|
||||||
#include "llerror.h"
|
#include "llerror.h"
|
||||||
|
#include "lazyeventapi.h"
|
||||||
|
|
||||||
LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const std::string& field):
|
LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const std::string& field):
|
||||||
lbase(name, field),
|
lbase(name, field),
|
||||||
|
|
@ -43,6 +44,13 @@ LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const s
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LLEventAPI::LLEventAPI(const LL::LazyEventAPIParams& params):
|
||||||
|
LLEventAPI(params.name, params.desc, params.field)
|
||||||
|
{
|
||||||
|
// call initialization functions with our brand-new instance pointer
|
||||||
|
params.init(this);
|
||||||
|
}
|
||||||
|
|
||||||
LLEventAPI::~LLEventAPI()
|
LLEventAPI::~LLEventAPI()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,11 @@
|
||||||
#include "llinstancetracker.h"
|
#include "llinstancetracker.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
namespace LL
|
||||||
|
{
|
||||||
|
class LazyEventAPIParams;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LLEventAPI not only provides operation dispatch functionality, inherited
|
* LLEventAPI not only provides operation dispatch functionality, inherited
|
||||||
* from LLDispatchListener -- it also gives us event API introspection.
|
* from LLDispatchListener -- it also gives us event API introspection.
|
||||||
|
|
@ -64,19 +69,6 @@ public:
|
||||||
/// Get the documentation string
|
/// Get the documentation string
|
||||||
std::string getDesc() const { return mDesc; }
|
std::string getDesc() const { return mDesc; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Publish only selected add() methods from LLEventDispatcher.
|
|
||||||
* Every LLEventAPI add() @em must have a description string.
|
|
||||||
*/
|
|
||||||
template <typename CALLABLE>
|
|
||||||
void add(const std::string& name,
|
|
||||||
const std::string& desc,
|
|
||||||
CALLABLE callable,
|
|
||||||
const LLSD& required=LLSD())
|
|
||||||
{
|
|
||||||
LLEventDispatcher::add(name, desc, callable, required);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiate a Response object in any LLEventAPI subclass method that
|
* Instantiate a Response object in any LLEventAPI subclass method that
|
||||||
* wants to guarantee a reply (if requested) will be sent on exit from the
|
* wants to guarantee a reply (if requested) will be sent on exit from the
|
||||||
|
|
@ -160,6 +152,10 @@ public:
|
||||||
LLSD::String mKey;
|
LLSD::String mKey;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// constructor used only by subclasses registered by LazyEventAPI
|
||||||
|
LLEventAPI(const LL::LazyEventAPIParams&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mDesc;
|
std::string mDesc;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -40,70 +40,12 @@
|
||||||
// other Linden headers
|
// other Linden headers
|
||||||
#include "llevents.h"
|
#include "llevents.h"
|
||||||
#include "llerror.h"
|
#include "llerror.h"
|
||||||
|
#include "llexception.h"
|
||||||
#include "llsdutil.h"
|
#include "llsdutil.h"
|
||||||
#include "stringize.h"
|
#include "stringize.h"
|
||||||
|
#include <iomanip> // std::quoted()
|
||||||
#include <memory> // std::auto_ptr
|
#include <memory> // std::auto_ptr
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* LLSDArgsSource
|
|
||||||
*****************************************************************************/
|
|
||||||
/**
|
|
||||||
* Store an LLSD array, producing its elements one at a time. Die with LL_ERRS
|
|
||||||
* if the consumer requests more elements than the array contains.
|
|
||||||
*/
|
|
||||||
class LL_COMMON_API LLSDArgsSource
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LLSDArgsSource(const std::string function, const LLSD& args);
|
|
||||||
~LLSDArgsSource();
|
|
||||||
|
|
||||||
LLSD next();
|
|
||||||
|
|
||||||
void done() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string _function;
|
|
||||||
LLSD _args;
|
|
||||||
LLSD::Integer _index;
|
|
||||||
};
|
|
||||||
|
|
||||||
LLSDArgsSource::LLSDArgsSource(const std::string function, const LLSD& args):
|
|
||||||
_function(function),
|
|
||||||
_args(args),
|
|
||||||
_index(0)
|
|
||||||
{
|
|
||||||
if (! (_args.isUndefined() || _args.isArray()))
|
|
||||||
{
|
|
||||||
LL_ERRS("LLSDArgsSource") << _function << " needs an args array instead of "
|
|
||||||
<< _args << LL_ENDL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LLSDArgsSource::~LLSDArgsSource()
|
|
||||||
{
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
|
|
||||||
LLSD LLSDArgsSource::next()
|
|
||||||
{
|
|
||||||
if (_index >= _args.size())
|
|
||||||
{
|
|
||||||
LL_ERRS("LLSDArgsSource") << _function << " requires more arguments than the "
|
|
||||||
<< _args.size() << " provided: " << _args << LL_ENDL;
|
|
||||||
}
|
|
||||||
return _args[_index++];
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLSDArgsSource::done() const
|
|
||||||
{
|
|
||||||
if (_index < _args.size())
|
|
||||||
{
|
|
||||||
LL_WARNS("LLSDArgsSource") << _function << " only consumed " << _index
|
|
||||||
<< " of the " << _args.size() << " arguments provided: "
|
|
||||||
<< _args << LL_ENDL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* LLSDArgsMapper
|
* LLSDArgsMapper
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
@ -156,19 +98,26 @@ void LLSDArgsSource::done() const
|
||||||
* - Holes are filled with the default values.
|
* - Holes are filled with the default values.
|
||||||
* - Any remaining holes constitute an error.
|
* - Any remaining holes constitute an error.
|
||||||
*/
|
*/
|
||||||
class LL_COMMON_API LLSDArgsMapper
|
class LL_COMMON_API LLEventDispatcher::LLSDArgsMapper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Accept description of function: function name, param names, param
|
/// Accept description of function: function name, param names, param
|
||||||
/// default values
|
/// default values
|
||||||
LLSDArgsMapper(const std::string& function, const LLSD& names, const LLSD& defaults);
|
LLSDArgsMapper(LLEventDispatcher* parent, const std::string& function,
|
||||||
|
const LLSD& names, const LLSD& defaults);
|
||||||
|
|
||||||
/// Given arguments map, return LLSD::Array of parameter values, or LL_ERRS.
|
/// Given arguments map, return LLSD::Array of parameter values, or
|
||||||
|
/// trigger error.
|
||||||
LLSD map(const LLSD& argsmap) const;
|
LLSD map(const LLSD& argsmap) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::string formatlist(const LLSD&);
|
static std::string formatlist(const LLSD&);
|
||||||
|
template <typename... ARGS>
|
||||||
|
[[noreturn]] void callFail(ARGS&&... args) const;
|
||||||
|
|
||||||
|
// store a plain dumb back-pointer because we don't have to manage the
|
||||||
|
// parent LLEventDispatcher's lifespan
|
||||||
|
LLEventDispatcher* _parent;
|
||||||
// The function-name string is purely descriptive. We want error messages
|
// The function-name string is purely descriptive. We want error messages
|
||||||
// to be able to indicate which function's LLSDArgsMapper has the problem.
|
// to be able to indicate which function's LLSDArgsMapper has the problem.
|
||||||
std::string _function;
|
std::string _function;
|
||||||
|
|
@ -187,15 +136,18 @@ private:
|
||||||
FilledVector _has_dft;
|
FilledVector _has_dft;
|
||||||
};
|
};
|
||||||
|
|
||||||
LLSDArgsMapper::LLSDArgsMapper(const std::string& function,
|
LLEventDispatcher::LLSDArgsMapper::LLSDArgsMapper(LLEventDispatcher* parent,
|
||||||
const LLSD& names, const LLSD& defaults):
|
const std::string& function,
|
||||||
|
const LLSD& names,
|
||||||
|
const LLSD& defaults):
|
||||||
|
_parent(parent),
|
||||||
_function(function),
|
_function(function),
|
||||||
_names(names),
|
_names(names),
|
||||||
_has_dft(names.size())
|
_has_dft(names.size())
|
||||||
{
|
{
|
||||||
if (! (_names.isUndefined() || _names.isArray()))
|
if (! (_names.isUndefined() || _names.isArray()))
|
||||||
{
|
{
|
||||||
LL_ERRS("LLSDArgsMapper") << function << " names must be an array, not " << names << LL_ENDL;
|
callFail(" names must be an array, not ", names);
|
||||||
}
|
}
|
||||||
auto nparams(_names.size());
|
auto nparams(_names.size());
|
||||||
// From _names generate _indexes.
|
// From _names generate _indexes.
|
||||||
|
|
@ -218,8 +170,7 @@ LLSDArgsMapper::LLSDArgsMapper(const std::string& function,
|
||||||
// defaults is a (possibly empty) array. Right-align it with names.
|
// defaults is a (possibly empty) array. Right-align it with names.
|
||||||
if (ndefaults > nparams)
|
if (ndefaults > nparams)
|
||||||
{
|
{
|
||||||
LL_ERRS("LLSDArgsMapper") << function << " names array " << names
|
callFail(" names array ", names, " shorter than defaults array ", defaults);
|
||||||
<< " shorter than defaults array " << defaults << LL_ENDL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Offset by which we slide defaults array right to right-align with
|
// Offset by which we slide defaults array right to right-align with
|
||||||
|
|
@ -256,23 +207,20 @@ LLSDArgsMapper::LLSDArgsMapper(const std::string& function,
|
||||||
}
|
}
|
||||||
if (bogus.size())
|
if (bogus.size())
|
||||||
{
|
{
|
||||||
LL_ERRS("LLSDArgsMapper") << function << " defaults specified for nonexistent params "
|
callFail(" defaults specified for nonexistent params ", formatlist(bogus));
|
||||||
<< formatlist(bogus) << LL_ENDL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LL_ERRS("LLSDArgsMapper") << function << " defaults must be a map or an array, not "
|
callFail(" defaults must be a map or an array, not ", defaults);
|
||||||
<< defaults << LL_ENDL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LLSD LLSDArgsMapper::map(const LLSD& argsmap) const
|
LLSD LLEventDispatcher::LLSDArgsMapper::map(const LLSD& argsmap) const
|
||||||
{
|
{
|
||||||
if (! (argsmap.isUndefined() || argsmap.isMap() || argsmap.isArray()))
|
if (! (argsmap.isUndefined() || argsmap.isMap() || argsmap.isArray()))
|
||||||
{
|
{
|
||||||
LL_ERRS("LLSDArgsMapper") << _function << " map() needs a map or array, not "
|
callFail(" map() needs a map or array, not ", argsmap);
|
||||||
<< argsmap << LL_ENDL;
|
|
||||||
}
|
}
|
||||||
// Initialize the args array. Indexing a non-const LLSD array grows it
|
// Initialize the args array. Indexing a non-const LLSD array grows it
|
||||||
// to appropriate size, but we don't want to resize this one on each
|
// to appropriate size, but we don't want to resize this one on each
|
||||||
|
|
@ -369,15 +317,14 @@ LLSD LLSDArgsMapper::map(const LLSD& argsmap) const
|
||||||
// by argsmap, that's a problem.
|
// by argsmap, that's a problem.
|
||||||
if (unfilled.size())
|
if (unfilled.size())
|
||||||
{
|
{
|
||||||
LL_ERRS("LLSDArgsMapper") << _function << " missing required arguments "
|
callFail(" missing required arguments ", formatlist(unfilled), " from ", argsmap);
|
||||||
<< formatlist(unfilled) << " from " << argsmap << LL_ENDL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// done
|
// done
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string LLSDArgsMapper::formatlist(const LLSD& list)
|
std::string LLEventDispatcher::LLSDArgsMapper::formatlist(const LLSD& list)
|
||||||
{
|
{
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
const char* delim = "";
|
const char* delim = "";
|
||||||
|
|
@ -390,23 +337,44 @@ std::string LLSDArgsMapper::formatlist(const LLSD& list)
|
||||||
return out.str();
|
return out.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key):
|
template <typename... ARGS>
|
||||||
mDesc(desc),
|
[[noreturn]] void LLEventDispatcher::LLSDArgsMapper::callFail(ARGS&&... args) const
|
||||||
mKey(key)
|
|
||||||
{
|
{
|
||||||
|
_parent->callFail<LLEventDispatcher::DispatchError>
|
||||||
|
(_function, std::forward<ARGS>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* LLEventDispatcher
|
||||||
|
*****************************************************************************/
|
||||||
|
LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key):
|
||||||
|
LLEventDispatcher(desc, key, "args")
|
||||||
|
{}
|
||||||
|
|
||||||
|
LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key,
|
||||||
|
const std::string& argskey):
|
||||||
|
mDesc(desc),
|
||||||
|
mKey(key),
|
||||||
|
mArgskey(argskey)
|
||||||
|
{}
|
||||||
|
|
||||||
LLEventDispatcher::~LLEventDispatcher()
|
LLEventDispatcher::~LLEventDispatcher()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LLEventDispatcher::DispatchEntry::DispatchEntry(LLEventDispatcher* parent, const std::string& desc):
|
||||||
|
mParent(parent),
|
||||||
|
mDesc(desc)
|
||||||
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DispatchEntry subclass used for callables accepting(const LLSD&)
|
* DispatchEntry subclass used for callables accepting(const LLSD&)
|
||||||
*/
|
*/
|
||||||
struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchEntry
|
struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchEntry
|
||||||
{
|
{
|
||||||
LLSDDispatchEntry(const std::string& desc, const Callable& func, const LLSD& required):
|
LLSDDispatchEntry(LLEventDispatcher* parent, const std::string& desc,
|
||||||
DispatchEntry(desc),
|
const Callable& func, const LLSD& required):
|
||||||
|
DispatchEntry(parent, desc),
|
||||||
mFunc(func),
|
mFunc(func),
|
||||||
mRequired(required)
|
mRequired(required)
|
||||||
{}
|
{}
|
||||||
|
|
@ -414,22 +382,21 @@ struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchE
|
||||||
Callable mFunc;
|
Callable mFunc;
|
||||||
LLSD mRequired;
|
LLSD mRequired;
|
||||||
|
|
||||||
virtual void call(const std::string& desc, const LLSD& event) const
|
LLSD call(const std::string& desc, const LLSD& event, bool, const std::string&) const override
|
||||||
{
|
{
|
||||||
// Validate the syntax of the event itself.
|
// Validate the syntax of the event itself.
|
||||||
std::string mismatch(llsd_matches(mRequired, event));
|
std::string mismatch(llsd_matches(mRequired, event));
|
||||||
if (! mismatch.empty())
|
if (! mismatch.empty())
|
||||||
{
|
{
|
||||||
LL_ERRS("LLEventDispatcher") << desc << ": bad request: " << mismatch << LL_ENDL;
|
callFail(desc, ": bad request: ", mismatch);
|
||||||
}
|
}
|
||||||
// Event syntax looks good, go for it!
|
// Event syntax looks good, go for it!
|
||||||
mFunc(event);
|
return mFunc(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual LLSD addMetadata(LLSD meta) const
|
LLSD getMetadata() const override
|
||||||
{
|
{
|
||||||
meta["required"] = mRequired;
|
return llsd::map("required", mRequired);
|
||||||
return meta;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -439,17 +406,27 @@ struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchE
|
||||||
*/
|
*/
|
||||||
struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::DispatchEntry
|
struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::DispatchEntry
|
||||||
{
|
{
|
||||||
ParamsDispatchEntry(const std::string& desc, const invoker_function& func):
|
ParamsDispatchEntry(LLEventDispatcher* parent, const std::string& name,
|
||||||
DispatchEntry(desc),
|
const std::string& desc, const invoker_function& func):
|
||||||
|
DispatchEntry(parent, desc),
|
||||||
|
mName(name),
|
||||||
mInvoker(func)
|
mInvoker(func)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
std::string mName;
|
||||||
invoker_function mInvoker;
|
invoker_function mInvoker;
|
||||||
|
|
||||||
virtual void call(const std::string& desc, const LLSD& event) const
|
LLSD call(const std::string&, const LLSD& event, bool, const std::string&) const override
|
||||||
{
|
{
|
||||||
LLSDArgsSource src(desc, event);
|
try
|
||||||
mInvoker(boost::bind(&LLSDArgsSource::next, boost::ref(src)));
|
{
|
||||||
|
return mInvoker(event);
|
||||||
|
}
|
||||||
|
catch (const LL::apply_error& err)
|
||||||
|
{
|
||||||
|
// could hit runtime errors with LL::apply()
|
||||||
|
callFail(err.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -459,23 +436,62 @@ struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::Dispatc
|
||||||
*/
|
*/
|
||||||
struct LLEventDispatcher::ArrayParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
|
struct LLEventDispatcher::ArrayParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
|
||||||
{
|
{
|
||||||
ArrayParamsDispatchEntry(const std::string& desc, const invoker_function& func,
|
ArrayParamsDispatchEntry(LLEventDispatcher* parent, const std::string& name,
|
||||||
|
const std::string& desc, const invoker_function& func,
|
||||||
LLSD::Integer arity):
|
LLSD::Integer arity):
|
||||||
ParamsDispatchEntry(desc, func),
|
ParamsDispatchEntry(parent, name, desc, func),
|
||||||
mArity(arity)
|
mArity(arity)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
LLSD::Integer mArity;
|
LLSD::Integer mArity;
|
||||||
|
|
||||||
virtual LLSD addMetadata(LLSD meta) const
|
LLSD call(const std::string& desc, const LLSD& event, bool fromMap, const std::string& argskey) const override
|
||||||
|
{
|
||||||
|
// std::string context { stringize(desc, "(", event, ") with argskey ", std::quoted(argskey), ": ") };
|
||||||
|
// Whether we try to extract arguments from 'event' depends on whether
|
||||||
|
// the LLEventDispatcher consumer called one of the (name, event)
|
||||||
|
// methods (! fromMap) or one of the (event) methods (fromMap). If we
|
||||||
|
// were called with (name, event), the passed event must itself be
|
||||||
|
// suitable to pass to the registered callable, no args extraction
|
||||||
|
// required or even attempted. Only if called with plain (event) do we
|
||||||
|
// consider extracting args from that event. Initially assume 'event'
|
||||||
|
// itself contains the arguments.
|
||||||
|
LLSD args{ event };
|
||||||
|
if (fromMap)
|
||||||
|
{
|
||||||
|
if (! mArity)
|
||||||
|
{
|
||||||
|
// When the target function is nullary, and we're called from
|
||||||
|
// an (event) method, just ignore the rest of the map entries.
|
||||||
|
args.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We only require/retrieve argskey if the target function
|
||||||
|
// isn't nullary. For all others, since we require an LLSD
|
||||||
|
// array, we must have an argskey.
|
||||||
|
if (argskey.empty())
|
||||||
|
{
|
||||||
|
callFail("LLEventDispatcher has no args key");
|
||||||
|
}
|
||||||
|
if ((! event.has(argskey)))
|
||||||
|
{
|
||||||
|
callFail("missing required key ", std::quoted(argskey));
|
||||||
|
}
|
||||||
|
args = event[argskey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ParamsDispatchEntry::call(desc, args, fromMap, argskey);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLSD getMetadata() const override
|
||||||
{
|
{
|
||||||
LLSD array(LLSD::emptyArray());
|
LLSD array(LLSD::emptyArray());
|
||||||
// Resize to number of arguments required
|
// Resize to number of arguments required
|
||||||
if (mArity)
|
if (mArity)
|
||||||
array[mArity - 1] = LLSD();
|
array[mArity - 1] = LLSD();
|
||||||
llassert_always(array.size() == mArity);
|
llassert_always(array.size() == mArity);
|
||||||
meta["required"] = array;
|
return llsd::map("required", array);
|
||||||
return meta;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -485,11 +501,11 @@ struct LLEventDispatcher::ArrayParamsDispatchEntry: public LLEventDispatcher::Pa
|
||||||
*/
|
*/
|
||||||
struct LLEventDispatcher::MapParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
|
struct LLEventDispatcher::MapParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
|
||||||
{
|
{
|
||||||
MapParamsDispatchEntry(const std::string& name, const std::string& desc,
|
MapParamsDispatchEntry(LLEventDispatcher* parent, const std::string& name,
|
||||||
const invoker_function& func,
|
const std::string& desc, const invoker_function& func,
|
||||||
const LLSD& params, const LLSD& defaults):
|
const LLSD& params, const LLSD& defaults):
|
||||||
ParamsDispatchEntry(desc, func),
|
ParamsDispatchEntry(parent, name, desc, func),
|
||||||
mMapper(name, params, defaults),
|
mMapper(parent, name, params, defaults),
|
||||||
mRequired(LLSD::emptyMap())
|
mRequired(LLSD::emptyMap())
|
||||||
{
|
{
|
||||||
// Build the set of all param keys, then delete the ones that are
|
// Build the set of all param keys, then delete the ones that are
|
||||||
|
|
@ -532,18 +548,27 @@ struct LLEventDispatcher::MapParamsDispatchEntry: public LLEventDispatcher::Para
|
||||||
LLSD mRequired;
|
LLSD mRequired;
|
||||||
LLSD mOptional;
|
LLSD mOptional;
|
||||||
|
|
||||||
virtual void call(const std::string& desc, const LLSD& event) const
|
LLSD call(const std::string& desc, const LLSD& event, bool fromMap, const std::string& argskey) const override
|
||||||
{
|
{
|
||||||
// Just convert from LLSD::Map to LLSD::Array using mMapper, then pass
|
// by default, pass the whole event as the arguments map
|
||||||
// to base-class call() method.
|
LLSD args{ event };
|
||||||
ParamsDispatchEntry::call(desc, mMapper.map(event));
|
// Were we called by one of the (event) methods (instead of the (name,
|
||||||
|
// event) methods), do we have an argskey, and does the incoming event
|
||||||
|
// have that key?
|
||||||
|
if (fromMap && (! argskey.empty()) && event.has(argskey))
|
||||||
|
{
|
||||||
|
// if so, extract the value of argskey from the incoming event,
|
||||||
|
// and use that as the arguments map
|
||||||
|
args = event[argskey];
|
||||||
|
}
|
||||||
|
// Now convert args from LLSD map to LLSD array using mMapper, then
|
||||||
|
// pass to base-class call() method.
|
||||||
|
return ParamsDispatchEntry::call(desc, mMapper.map(args), fromMap, argskey);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual LLSD addMetadata(LLSD meta) const
|
LLSD getMetadata() const override
|
||||||
{
|
{
|
||||||
meta["required"] = mRequired;
|
return llsd::map("required", mRequired, "optional", mOptional);
|
||||||
meta["optional"] = mOptional;
|
|
||||||
return meta;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -552,9 +577,9 @@ void LLEventDispatcher::addArrayParamsDispatchEntry(const std::string& name,
|
||||||
const invoker_function& invoker,
|
const invoker_function& invoker,
|
||||||
LLSD::Integer arity)
|
LLSD::Integer arity)
|
||||||
{
|
{
|
||||||
mDispatch.insert(
|
mDispatch.emplace(
|
||||||
DispatchMap::value_type(name, DispatchMap::mapped_type(
|
name,
|
||||||
new ArrayParamsDispatchEntry(desc, invoker, arity))));
|
new ArrayParamsDispatchEntry(this, "", desc, invoker, arity));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name,
|
void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name,
|
||||||
|
|
@ -563,25 +588,25 @@ void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name,
|
||||||
const LLSD& params,
|
const LLSD& params,
|
||||||
const LLSD& defaults)
|
const LLSD& defaults)
|
||||||
{
|
{
|
||||||
mDispatch.insert(
|
// Pass instance info as well as this entry name for error messages.
|
||||||
DispatchMap::value_type(name, DispatchMap::mapped_type(
|
mDispatch.emplace(
|
||||||
new MapParamsDispatchEntry(name, desc, invoker, params, defaults))));
|
name,
|
||||||
|
new MapParamsDispatchEntry(this, "", desc, invoker, params, defaults));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a callable by name
|
/// Register a callable by name
|
||||||
void LLEventDispatcher::add(const std::string& name, const std::string& desc,
|
void LLEventDispatcher::addLLSD(const std::string& name, const std::string& desc,
|
||||||
const Callable& callable, const LLSD& required)
|
const Callable& callable, const LLSD& required)
|
||||||
{
|
{
|
||||||
mDispatch.insert(
|
mDispatch.emplace(name, new LLSDDispatchEntry(this, desc, callable, required));
|
||||||
DispatchMap::value_type(name, DispatchMap::mapped_type(
|
|
||||||
new LLSDDispatchEntry(desc, callable, required))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLEventDispatcher::addFail(const std::string& name, const std::string& classname) const
|
void LLEventDispatcher::addFail(const std::string& name, const char* classname) const
|
||||||
{
|
{
|
||||||
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ")::add(" << name
|
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ")::add(" << name
|
||||||
<< "): " << classname << " is not a subclass "
|
<< "): " << LLError::Log::demangle(classname)
|
||||||
<< "of LLEventDispatcher" << LL_ENDL;
|
<< " is not a subclass of LLEventDispatcher"
|
||||||
|
<< LL_ENDL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unregister a callable
|
/// Unregister a callable
|
||||||
|
|
@ -596,48 +621,105 @@ bool LLEventDispatcher::remove(const std::string& name)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a registered callable with an explicitly-specified name. If no
|
/// Call a registered callable with an explicitly-specified name. It is an
|
||||||
/// such callable exists, die with LL_ERRS.
|
/// error if no such callable exists.
|
||||||
void LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const
|
LLSD LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const
|
||||||
{
|
{
|
||||||
if (! try_call(name, event))
|
return try_call(std::string(), name, event);
|
||||||
{
|
|
||||||
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): '" << name
|
|
||||||
<< "' not found" << LL_ENDL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract the @a key value from the incoming @a event, and call the
|
|
||||||
/// callable whose name is specified by that map @a key. If no such
|
|
||||||
/// callable exists, die with LL_ERRS.
|
|
||||||
void LLEventDispatcher::operator()(const LLSD& event) const
|
|
||||||
{
|
|
||||||
// This could/should be implemented in terms of the two-arg overload.
|
|
||||||
// However -- we can produce a more informative error message.
|
|
||||||
std::string name(event[mKey]);
|
|
||||||
if (! try_call(name, event))
|
|
||||||
{
|
|
||||||
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey
|
|
||||||
<< " value '" << name << "'" << LL_ENDL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LLEventDispatcher::try_call(const LLSD& event) const
|
|
||||||
{
|
|
||||||
return try_call(event[mKey], event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLEventDispatcher::try_call(const std::string& name, const LLSD& event) const
|
bool LLEventDispatcher::try_call(const std::string& name, const LLSD& event) const
|
||||||
{
|
{
|
||||||
DispatchMap::const_iterator found = mDispatch.find(name);
|
try
|
||||||
if (found == mDispatch.end())
|
{
|
||||||
|
try_call(std::string(), name, event);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Note that we don't catch the generic DispatchError, only the specific
|
||||||
|
// DispatchMissing. try_call() only promises to return false if the
|
||||||
|
// specified callable name isn't found -- not for general errors.
|
||||||
|
catch (const DispatchMissing&)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract the @a key value from the incoming @a event, and call the callable
|
||||||
|
/// whose name is specified by that map @a key. It is an error if no such
|
||||||
|
/// callable exists.
|
||||||
|
LLSD LLEventDispatcher::operator()(const LLSD& event) const
|
||||||
|
{
|
||||||
|
return try_call(mKey, event[mKey], event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLEventDispatcher::try_call(const LLSD& event) const
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
try_call(mKey, event[mKey], event);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (const DispatchMissing&)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LLSD LLEventDispatcher::try_call(const std::string& key, const std::string& name,
|
||||||
|
const LLSD& event) const
|
||||||
|
{
|
||||||
|
if (name.empty())
|
||||||
|
{
|
||||||
|
if (key.empty())
|
||||||
|
{
|
||||||
|
callFail<DispatchError>("attempting to call with no name");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callFail<DispatchError>("no ", key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchMap::const_iterator found = mDispatch.find(name);
|
||||||
|
if (found == mDispatch.end())
|
||||||
|
{
|
||||||
|
// Here we were passed a non-empty name, but there's no registered
|
||||||
|
// callable with that name. This is the one case in which we throw
|
||||||
|
// DispatchMissing instead of the generic DispatchError.
|
||||||
|
// Distinguish the public method by which our caller reached here:
|
||||||
|
// key.empty() means the name was passed explicitly, non-empty means
|
||||||
|
// we extracted the name from the incoming event using that key.
|
||||||
|
if (key.empty())
|
||||||
|
{
|
||||||
|
callFail<DispatchMissing>(std::quoted(name), " not found");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callFail<DispatchMissing>("bad ", key, " value ", std::quoted(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Found the name, so it's plausible to even attempt the call.
|
// Found the name, so it's plausible to even attempt the call.
|
||||||
found->second->call(STRINGIZE("LLEventDispatcher(" << mDesc << ") calling '" << name << "'"),
|
const char* delim = (key.empty()? "" : "=");
|
||||||
event);
|
// append either "[key=name]" or just "[name]"
|
||||||
return true; // tell caller we were able to call
|
SetState transient(this, '[', key, delim, name, ']');
|
||||||
|
return found->second->call("", event, (! key.empty()), mArgskey);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename EXCEPTION, typename... ARGS>
|
||||||
|
//static
|
||||||
|
[[noreturn]] void LLEventDispatcher::sCallFail(ARGS&&... args)
|
||||||
|
{
|
||||||
|
auto error = stringize(std::forward<ARGS>(args)...);
|
||||||
|
LL_WARNS("LLEventDispatcher") << error << LL_ENDL;
|
||||||
|
LLTHROW(EXCEPTION(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename EXCEPTION, typename... ARGS>
|
||||||
|
[[noreturn]] void LLEventDispatcher::callFail(ARGS&&... args) const
|
||||||
|
{
|
||||||
|
// Describe this instance in addition to the error itself.
|
||||||
|
sCallFail<EXCEPTION>(*this, ": ", std::forward<ARGS>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
LLSD LLEventDispatcher::getMetadata(const std::string& name) const
|
LLSD LLEventDispatcher::getMetadata(const std::string& name) const
|
||||||
|
|
@ -647,26 +729,243 @@ LLSD LLEventDispatcher::getMetadata(const std::string& name) const
|
||||||
{
|
{
|
||||||
return LLSD();
|
return LLSD();
|
||||||
}
|
}
|
||||||
LLSD meta;
|
LLSD meta{ found->second->getMetadata() };
|
||||||
meta["name"] = name;
|
meta["name"] = name;
|
||||||
meta["desc"] = found->second->mDesc;
|
meta["desc"] = found->second->mDesc;
|
||||||
return found->second->addMetadata(meta);
|
return meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key):
|
std::ostream& operator<<(std::ostream& out, const LLEventDispatcher& self)
|
||||||
LLEventDispatcher(pumpname, key),
|
|
||||||
mPump(pumpname, true), // allow tweaking for uniqueness
|
|
||||||
mBoundListener(mPump.listen("self", boost::bind(&LLDispatchListener::process, this, _1)))
|
|
||||||
{
|
{
|
||||||
|
// If we're a subclass of LLEventDispatcher, e.g. LLEventAPI, report that.
|
||||||
|
// Also report whatever transient state is active.
|
||||||
|
return out << LLError::Log::classname(self) << '(' << self.mDesc << ')'
|
||||||
|
<< self.getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLDispatchListener::process(const LLSD& event)
|
std::string LLEventDispatcher::getState() const
|
||||||
{
|
{
|
||||||
(*this)(event);
|
// default value of fiber_specific_ptr is nullptr, and ~SetState() reverts
|
||||||
|
// to that; infer empty string
|
||||||
|
if (! mState.get())
|
||||||
|
return {};
|
||||||
|
else
|
||||||
|
return *mState;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLEventDispatcher::setState(SetState&, const std::string& state) const
|
||||||
|
{
|
||||||
|
// If SetState is instantiated at multiple levels of function call, ignore
|
||||||
|
// the lower-level call because the outer call presumably provides more
|
||||||
|
// context.
|
||||||
|
if (mState.get())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Pass us empty string (a la ~SetState()) to reset to nullptr, else take
|
||||||
|
// a heap copy of the passed state string so we can delete it on
|
||||||
|
// subsequent reset().
|
||||||
|
mState.reset(state.empty()? nullptr : new std::string(state));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* LLDispatchListener
|
||||||
|
*****************************************************************************/
|
||||||
|
std::string LLDispatchListener::mReplyKey{ "reply" };
|
||||||
|
|
||||||
|
bool LLDispatchListener::process(const LLSD& event) const
|
||||||
|
{
|
||||||
|
// Decide what to do based on the incoming value of the specified dispatch
|
||||||
|
// key.
|
||||||
|
LLSD name{ event[getDispatchKey()] };
|
||||||
|
if (name.isMap())
|
||||||
|
{
|
||||||
|
call_map(name, event);
|
||||||
|
}
|
||||||
|
else if (name.isArray())
|
||||||
|
{
|
||||||
|
call_array(name, event);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
call_one(name, event);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LLEventDispatcher::DispatchEntry::DispatchEntry(const std::string& desc):
|
void LLDispatchListener::call_one(const LLSD& name, const LLSD& event) const
|
||||||
mDesc(desc)
|
{
|
||||||
{}
|
LLSD result;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = (*this)(event);
|
||||||
|
}
|
||||||
|
catch (const DispatchError& err)
|
||||||
|
{
|
||||||
|
if (! event.has(mReplyKey))
|
||||||
|
{
|
||||||
|
// Without a reply key, let the exception propagate.
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here there was an error and the incoming event has mReplyKey. Reply
|
||||||
|
// with a map containing an "error" key explaining the problem.
|
||||||
|
return reply(llsd::map("error", err.what()), event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We seem to have gotten a valid result. But we don't know whether the
|
||||||
|
// registered callable is void or non-void. If it's void,
|
||||||
|
// LLEventDispatcher returned isUndefined(). Otherwise, try to send it
|
||||||
|
// back to our invoker.
|
||||||
|
if (result.isDefined())
|
||||||
|
{
|
||||||
|
if (! result.isMap())
|
||||||
|
{
|
||||||
|
// wrap the result in a map as the "data" key
|
||||||
|
result = llsd::map("data", result);
|
||||||
|
}
|
||||||
|
reply(result, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLDispatchListener::call_map(const LLSD& reqmap, const LLSD& event) const
|
||||||
|
{
|
||||||
|
// LLSD map containing returned values
|
||||||
|
LLSD result;
|
||||||
|
// cache dispatch key
|
||||||
|
std::string key{ getDispatchKey() };
|
||||||
|
// collect any error messages here
|
||||||
|
std::ostringstream errors;
|
||||||
|
const char* delim = "";
|
||||||
|
|
||||||
|
for (const auto& pair : llsd::inMap(reqmap))
|
||||||
|
{
|
||||||
|
const LLSD::String& name{ pair.first };
|
||||||
|
const LLSD& args{ pair.second };
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// in case of errors, tell user the dispatch key, the fact that
|
||||||
|
// we're processing a request map and the current key in that map
|
||||||
|
SetState(this, '[', key, '[', name, "]]");
|
||||||
|
// With this form, capture return value even if undefined:
|
||||||
|
// presence of the key in the response map can be used to detect
|
||||||
|
// which request keys succeeded.
|
||||||
|
result[name] = (*this)(name, args);
|
||||||
|
}
|
||||||
|
catch (const std::exception& err)
|
||||||
|
{
|
||||||
|
// Catch not only DispatchError, but any C++ exception thrown by
|
||||||
|
// the target callable. Collect exception name and message in
|
||||||
|
// 'errors'.
|
||||||
|
errors << delim << LLError::Log::classname(err) << ": " << err.what();
|
||||||
|
delim = "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// so, were there any errors?
|
||||||
|
std::string error = errors.str();
|
||||||
|
if (! error.empty())
|
||||||
|
{
|
||||||
|
if (! event.has(mReplyKey))
|
||||||
|
{
|
||||||
|
// can't send reply, throw
|
||||||
|
sCallFail<DispatchError>(error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// reply key present
|
||||||
|
result["error"] = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reply(result, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLDispatchListener::call_array(const LLSD& reqarray, const LLSD& event) const
|
||||||
|
{
|
||||||
|
// LLSD array containing returned values
|
||||||
|
LLSD results;
|
||||||
|
// cache the dispatch key
|
||||||
|
std::string key{ getDispatchKey() };
|
||||||
|
// arguments array, if present -- const because, if it's shorter than
|
||||||
|
// reqarray, we don't want to grow it
|
||||||
|
const LLSD argsarray{ event[getArgsKey()] };
|
||||||
|
// error message, if any
|
||||||
|
std::string error;
|
||||||
|
|
||||||
|
// classic index loop because we need the index
|
||||||
|
for (size_t i = 0, size = reqarray.size(); i < size; ++i)
|
||||||
|
{
|
||||||
|
const auto& reqentry{ reqarray[i] };
|
||||||
|
std::string name;
|
||||||
|
LLSD args;
|
||||||
|
if (reqentry.isString())
|
||||||
|
{
|
||||||
|
name = reqentry.asString();
|
||||||
|
args = argsarray[i];
|
||||||
|
}
|
||||||
|
else if (reqentry.isArray() && reqentry.size() == 2 && reqentry[0].isString())
|
||||||
|
{
|
||||||
|
name = reqentry[0].asString();
|
||||||
|
args = reqentry[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// reqentry isn't in either of the documented forms
|
||||||
|
error = stringize(*this, ": ", getDispatchKey(), '[', i, "] ",
|
||||||
|
reqentry, " unsupported");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reqentry is one of the valid forms, got name and args
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// in case of errors, tell user the dispatch key, the fact that
|
||||||
|
// we're processing a request array, the current entry in that
|
||||||
|
// array and the corresponding callable name
|
||||||
|
SetState(this, '[', key, '[', i, "]=", name, ']');
|
||||||
|
// With this form, capture return value even if undefined
|
||||||
|
results.append((*this)(name, args));
|
||||||
|
}
|
||||||
|
catch (const std::exception& err)
|
||||||
|
{
|
||||||
|
// Catch not only DispatchError, but any C++ exception thrown by
|
||||||
|
// the target callable. Report the exception class as well as the
|
||||||
|
// error string.
|
||||||
|
error = stringize(LLError::Log::classname(err), ": ", err.what());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LLSD result;
|
||||||
|
// was there an error?
|
||||||
|
if (! error.empty())
|
||||||
|
{
|
||||||
|
if (! event.has(mReplyKey))
|
||||||
|
{
|
||||||
|
// can't send reply, throw
|
||||||
|
sCallFail<DispatchError>(error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// reply key present
|
||||||
|
result["error"] = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrap the results array as response map "data" key, as promised
|
||||||
|
if (results.isDefined())
|
||||||
|
{
|
||||||
|
result["data"] = results;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply(result, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLDispatchListener::reply(const LLSD& reply, const LLSD& request) const
|
||||||
|
{
|
||||||
|
// Call sendReply() unconditionally: sendReply() itself tests whether the
|
||||||
|
// specified reply key is present in the incoming request, and does
|
||||||
|
// nothing if there's no such key.
|
||||||
|
sendReply(reply, request, mReplyKey);
|
||||||
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -435,16 +435,61 @@ public:
|
||||||
// generic type-appropriate store through mTarget, construct an
|
// generic type-appropriate store through mTarget, construct an
|
||||||
// LLSDParam<T> and store that, thus engaging LLSDParam's custom
|
// LLSDParam<T> and store that, thus engaging LLSDParam's custom
|
||||||
// conversions.
|
// conversions.
|
||||||
mTarget = LLSDParam<T>(llsd::drill(event, mPath));
|
storeTarget(LLSDParam<T>(llsd::drill(event, mPath)));
|
||||||
return mConsume;
|
return mConsume;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// This method disambiguates LLStoreListener<LLSD>. Directly assigning
|
||||||
|
// some_LLSD_var = LLSDParam<LLSD>(some_LLSD_value);
|
||||||
|
// is problematic because the compiler has too many choices: LLSD has
|
||||||
|
// multiple assignment operator overloads, and LLSDParam<LLSD> has a
|
||||||
|
// templated conversion operator. But LLSDParam<LLSD> can convert to a
|
||||||
|
// (const LLSD&) parameter, and LLSD::operator=(const LLSD&) works.
|
||||||
|
void storeTarget(const T& value)
|
||||||
|
{
|
||||||
|
mTarget = value;
|
||||||
|
}
|
||||||
|
|
||||||
T& mTarget;
|
T& mTarget;
|
||||||
const LLSD mPath;
|
const LLSD mPath;
|
||||||
const bool mConsume;
|
const bool mConsume;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LLVarHolder bundles a target variable of the specified type. We use it as a
|
||||||
|
* base class so the target variable will be fully constructed by the time a
|
||||||
|
* subclass constructor tries to pass a reference to some other base class.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
struct LLVarHolder
|
||||||
|
{
|
||||||
|
T mVar;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LLCaptureListener isa LLStoreListener that bundles the target variable of
|
||||||
|
* interest.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class LLCaptureListener: public LLVarHolder<T>,
|
||||||
|
public LLStoreListener<T>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using holder = LLVarHolder<T>;
|
||||||
|
using super = LLStoreListener<T>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LLCaptureListener(const LLSD& path=LLSD(), bool consume=false):
|
||||||
|
super(*this, holder::mVar, path, consume)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void set(T&& newval=T()) { holder::mVar = std::forward<T>(newval); }
|
||||||
|
|
||||||
|
const T& get() const { return holder::mVar; }
|
||||||
|
operator const T&() { return holder::mVar; }
|
||||||
|
};
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* LLEventLogProxy
|
* LLEventLogProxy
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
|
||||||
|
|
@ -68,19 +68,78 @@
|
||||||
LLEventPumps::LLEventPumps():
|
LLEventPumps::LLEventPumps():
|
||||||
mFactories
|
mFactories
|
||||||
{
|
{
|
||||||
{ "LLEventStream", [](const std::string& name, bool tweak)
|
{ "LLEventStream", [](const std::string& name, bool tweak, const std::string& /*type*/)
|
||||||
{ return new LLEventStream(name, tweak); } },
|
{ return new LLEventStream(name, tweak); } },
|
||||||
{ "LLEventMailDrop", [](const std::string& name, bool tweak)
|
{ "LLEventMailDrop", [](const std::string& name, bool tweak, const std::string& /*type*/)
|
||||||
{ return new LLEventMailDrop(name, tweak); } }
|
{ return new LLEventMailDrop(name, tweak); } }
|
||||||
},
|
},
|
||||||
mTypes
|
mTypes
|
||||||
{
|
{
|
||||||
// LLEventStream is the default for obtain(), so even if somebody DOES
|
// { "placeholder", "LLEventStream" }
|
||||||
// call obtain("placeholder"), this sample entry won't break anything.
|
|
||||||
{ "placeholder", "LLEventStream" }
|
|
||||||
}
|
}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
bool LLEventPumps::registerTypeFactory(const std::string& type, const TypeFactory& factory)
|
||||||
|
{
|
||||||
|
auto found = mFactories.find(type);
|
||||||
|
// can't re-register a TypeFactory for a type name that's already registered
|
||||||
|
if (found != mFactories.end())
|
||||||
|
return false;
|
||||||
|
// doesn't already exist, go ahead and register
|
||||||
|
mFactories[type] = factory;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventPumps::unregisterTypeFactory(const std::string& type)
|
||||||
|
{
|
||||||
|
auto found = mFactories.find(type);
|
||||||
|
if (found != mFactories.end())
|
||||||
|
mFactories.erase(found);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLEventPumps::registerPumpFactory(const std::string& name, const PumpFactory& factory)
|
||||||
|
{
|
||||||
|
// Do we already have a pump by this name?
|
||||||
|
if (mPumpMap.find(name) != mPumpMap.end())
|
||||||
|
return false;
|
||||||
|
// Do we already have an override for this pump name?
|
||||||
|
if (mTypes.find(name) != mTypes.end())
|
||||||
|
return false;
|
||||||
|
// Leverage the two-level lookup implemented by mTypes (pump name -> type
|
||||||
|
// name) and mFactories (type name -> factory). We could instead create a
|
||||||
|
// whole separate (pump name -> factory) map, and look in both; or we
|
||||||
|
// could change mTypes to (pump name -> factory) and, for typical type-
|
||||||
|
// based lookups, use a "factory" that looks up the real factory in
|
||||||
|
// mFactories. But this works, and we don't expect many calls to make() -
|
||||||
|
// either explicit or implicit via obtain().
|
||||||
|
// Create a bogus type name extremely unlikely to collide with an actual type.
|
||||||
|
static std::string nul(1, '\0');
|
||||||
|
std::string type_name{ nul + name };
|
||||||
|
mTypes[name] = type_name;
|
||||||
|
// TypeFactory is called with (name, tweak, type), whereas PumpFactory
|
||||||
|
// accepts only name. We could adapt with std::bind(), but this lambda
|
||||||
|
// does the trick.
|
||||||
|
mFactories[type_name] =
|
||||||
|
[factory]
|
||||||
|
(const std::string& name, bool /*tweak*/, const std::string& /*type*/)
|
||||||
|
{ return factory(name); };
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventPumps::unregisterPumpFactory(const std::string& name)
|
||||||
|
{
|
||||||
|
auto tfound = mTypes.find(name);
|
||||||
|
if (tfound != mTypes.end())
|
||||||
|
{
|
||||||
|
auto ffound = mFactories.find(tfound->second);
|
||||||
|
if (ffound != mFactories.end())
|
||||||
|
{
|
||||||
|
mFactories.erase(ffound);
|
||||||
|
}
|
||||||
|
mTypes.erase(tfound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LLEventPump& LLEventPumps::obtain(const std::string& name)
|
LLEventPump& LLEventPumps::obtain(const std::string& name)
|
||||||
{
|
{
|
||||||
PumpMap::iterator found = mPumpMap.find(name);
|
PumpMap::iterator found = mPumpMap.find(name);
|
||||||
|
|
@ -114,7 +173,7 @@ LLEventPump& LLEventPumps::make(const std::string& name, bool tweak,
|
||||||
// Passing an unrecognized type name is a no-no
|
// Passing an unrecognized type name is a no-no
|
||||||
LLTHROW(BadType(type));
|
LLTHROW(BadType(type));
|
||||||
}
|
}
|
||||||
auto newInstance = (found->second)(name, tweak);
|
auto newInstance = (found->second)(name, tweak, type);
|
||||||
// LLEventPump's constructor implicitly registers each new instance in
|
// LLEventPump's constructor implicitly registers each new instance in
|
||||||
// mPumpMap. But remember that we instantiated it (in mOurPumps) so we'll
|
// mPumpMap. But remember that we instantiated it (in mOurPumps) so we'll
|
||||||
// delete it later.
|
// delete it later.
|
||||||
|
|
|
||||||
|
|
@ -268,6 +268,45 @@ public:
|
||||||
LLEventPump& make(const std::string& name, bool tweak=false,
|
LLEventPump& make(const std::string& name, bool tweak=false,
|
||||||
const std::string& type=std::string());
|
const std::string& type=std::string());
|
||||||
|
|
||||||
|
/// function passed to registerTypeFactory()
|
||||||
|
typedef std::function<LLEventPump*(const std::string& name, bool tweak, const std::string& type)> TypeFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a TypeFactory for use with make(). When make() is called with
|
||||||
|
* the specified @a type string, call @a factory(name, tweak, type) to
|
||||||
|
* instantiate it.
|
||||||
|
*
|
||||||
|
* Returns true if successfully registered, false if there already exists
|
||||||
|
* a TypeFactory for the specified @a type name.
|
||||||
|
*/
|
||||||
|
bool registerTypeFactory(const std::string& type, const TypeFactory& factory);
|
||||||
|
void unregisterTypeFactory(const std::string& type);
|
||||||
|
|
||||||
|
/// function passed to registerPumpFactory()
|
||||||
|
typedef std::function<LLEventPump*(const std::string&)> PumpFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a PumpFactory for use with obtain(). When obtain() is called
|
||||||
|
* with the specified @a name string, if an LLEventPump with the specified
|
||||||
|
* @a name doesn't already exist, call @a factory(name) to instantiate it.
|
||||||
|
*
|
||||||
|
* Returns true if successfully registered, false if there already exists
|
||||||
|
* a factory override for the specified @a name.
|
||||||
|
*
|
||||||
|
* PumpFactory does not support @a tweak because it's only called when
|
||||||
|
* <i>that particular</i> @a name is passed to obtain(). Bear in mind that
|
||||||
|
* <tt>obtain(name)</tt> might still bypass the caller's PumpFactory for a
|
||||||
|
* couple different reasons:
|
||||||
|
*
|
||||||
|
* * registerPumpFactory() returns false because there's already a factory
|
||||||
|
* override for the specified @name
|
||||||
|
* * between a successful <tt>registerPumpFactory(name)</tt> call (returns
|
||||||
|
* true) and a call to <tt>obtain(name)</tt>, someone explicitly
|
||||||
|
* instantiated an LLEventPump(name), so obtain(name) returned that.
|
||||||
|
*/
|
||||||
|
bool registerPumpFactory(const std::string& name, const PumpFactory& factory);
|
||||||
|
void unregisterPumpFactory(const std::string& name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the named LLEventPump instance. If it exists post the message to it.
|
* Find the named LLEventPump instance. If it exists post the message to it.
|
||||||
* If the pump does not exist, do nothing.
|
* If the pump does not exist, do nothing.
|
||||||
|
|
@ -325,13 +364,13 @@ testable:
|
||||||
typedef std::set<LLEventPump*> PumpSet;
|
typedef std::set<LLEventPump*> PumpSet;
|
||||||
PumpSet mOurPumps;
|
PumpSet mOurPumps;
|
||||||
// for make(), map string type name to LLEventPump subclass factory function
|
// for make(), map string type name to LLEventPump subclass factory function
|
||||||
typedef std::map<std::string, std::function<LLEventPump*(const std::string&, bool)>> PumpFactories;
|
typedef std::map<std::string, TypeFactory> TypeFactories;
|
||||||
// Data used by make().
|
// Data used by make().
|
||||||
// One might think mFactories and mTypes could reasonably be static. So
|
// One might think mFactories and mTypes could reasonably be static. So
|
||||||
// they could -- if not for the fact that make() or obtain() might be
|
// they could -- if not for the fact that make() or obtain() might be
|
||||||
// called before this module's static variables have been initialized.
|
// called before this module's static variables have been initialized.
|
||||||
// This is why we use singletons in the first place.
|
// This is why we use singletons in the first place.
|
||||||
PumpFactories mFactories;
|
TypeFactories mFactories;
|
||||||
|
|
||||||
// for obtain(), map desired string instance name to string type when
|
// for obtain(), map desired string instance name to string type when
|
||||||
// obtain() must create the instance
|
// obtain() must create the instance
|
||||||
|
|
|
||||||
|
|
@ -340,11 +340,28 @@ public:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// The LLSD object we got from our stream contains the keys we
|
try
|
||||||
// need.
|
{
|
||||||
|
// The LLSD object we got from our stream contains the
|
||||||
|
// keys we need.
|
||||||
LLEventPumps::instance().obtain(data["pump"]).post(data["data"]);
|
LLEventPumps::instance().obtain(data["pump"]).post(data["data"]);
|
||||||
// Block calls to this method; resetting mBlocker unblocks calls
|
}
|
||||||
// to the other method.
|
catch (const std::exception& err)
|
||||||
|
{
|
||||||
|
// No plugin should be allowed to crash the viewer by
|
||||||
|
// driving an exception -- intentionally or not.
|
||||||
|
LOG_UNHANDLED_EXCEPTION(stringize("handling request ", data));
|
||||||
|
// Whether or not the plugin added a "reply" key to the
|
||||||
|
// request, send a reply. We happen to know who originated
|
||||||
|
// this request, and the reply LLEventPump of interest.
|
||||||
|
// Not our problem if the plugin ignores the reply event.
|
||||||
|
data["reply"] = mReplyPump.getName();
|
||||||
|
sendReply(llsd::map("error",
|
||||||
|
stringize(LLError::Log::classname(err), ": ", err.what())),
|
||||||
|
data);
|
||||||
|
}
|
||||||
|
// Block calls to this method; resetting mBlocker unblocks
|
||||||
|
// calls to the other method.
|
||||||
mBlocker.reset(new LLEventPump::Blocker(mStdoutDataConnection));
|
mBlocker.reset(new LLEventPump::Blocker(mStdoutDataConnection));
|
||||||
// Go check for any more pending events in the buffer.
|
// Go check for any more pending events in the buffer.
|
||||||
if (childout.size())
|
if (childout.size())
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,16 @@
|
||||||
// associated header
|
// associated header
|
||||||
#include "llleaplistener.h"
|
#include "llleaplistener.h"
|
||||||
// STL headers
|
// STL headers
|
||||||
#include <map>
|
#include <algorithm> // std::find_if
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
// std headers
|
// std headers
|
||||||
// external library headers
|
// external library headers
|
||||||
#include <boost/foreach.hpp>
|
|
||||||
// other Linden headers
|
// other Linden headers
|
||||||
#include "lluuid.h"
|
#include "lazyeventapi.h"
|
||||||
#include "llsdutil.h"
|
#include "llsdutil.h"
|
||||||
|
#include "lluuid.h"
|
||||||
#include "stringize.h"
|
#include "stringize.h"
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
|
|
@ -110,7 +112,7 @@ LLLeapListener::~LLLeapListener()
|
||||||
// value_type, and Bad Things would happen if you copied an
|
// value_type, and Bad Things would happen if you copied an
|
||||||
// LLTempBoundListener. (Destruction of the original would disconnect the
|
// LLTempBoundListener. (Destruction of the original would disconnect the
|
||||||
// listener, invalidating every stored connection.)
|
// listener, invalidating every stored connection.)
|
||||||
BOOST_FOREACH(ListenersMap::value_type& pair, mListeners)
|
for (ListenersMap::value_type& pair : mListeners)
|
||||||
{
|
{
|
||||||
pair.second.disconnect();
|
pair.second.disconnect();
|
||||||
}
|
}
|
||||||
|
|
@ -208,31 +210,65 @@ void LLLeapListener::getAPIs(const LLSD& request) const
|
||||||
{
|
{
|
||||||
Response reply(LLSD(), request);
|
Response reply(LLSD(), request);
|
||||||
|
|
||||||
|
// first, traverse existing LLEventAPI instances
|
||||||
|
std::set<std::string> instances;
|
||||||
for (auto& ea : LLEventAPI::instance_snapshot())
|
for (auto& ea : LLEventAPI::instance_snapshot())
|
||||||
{
|
{
|
||||||
LLSD info;
|
// remember which APIs are actually instantiated
|
||||||
info["desc"] = ea.getDesc();
|
instances.insert(ea.getName());
|
||||||
reply[ea.getName()] = info;
|
reply[ea.getName()] = llsd::map("desc", ea.getDesc());
|
||||||
}
|
}
|
||||||
|
// supplement that with *potential* instances: that is, instances of
|
||||||
|
// LazyEventAPI that can each instantiate an LLEventAPI on demand
|
||||||
|
for (const auto& lea : LL::LazyEventAPIBase::instance_snapshot())
|
||||||
|
{
|
||||||
|
// skip any LazyEventAPI that's already instantiated its LLEventAPI
|
||||||
|
if (instances.find(lea.getName()) == instances.end())
|
||||||
|
{
|
||||||
|
reply[lea.getName()] = llsd::map("desc", lea.getDesc());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because LazyEventAPI deliberately mimics LLEventAPI's query API, this
|
||||||
|
// function can be passed either -- even though they're unrelated types.
|
||||||
|
template <typename API>
|
||||||
|
void reportAPI(LLEventAPI::Response& reply, const API& api)
|
||||||
|
{
|
||||||
|
reply["name"] = api.getName();
|
||||||
|
reply["desc"] = api.getDesc();
|
||||||
|
reply["key"] = api.getDispatchKey();
|
||||||
|
LLSD ops;
|
||||||
|
for (const auto& namedesc : api)
|
||||||
|
{
|
||||||
|
ops.append(api.getMetadata(namedesc.first));
|
||||||
|
}
|
||||||
|
reply["ops"] = ops;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLLeapListener::getAPI(const LLSD& request) const
|
void LLLeapListener::getAPI(const LLSD& request) const
|
||||||
{
|
{
|
||||||
Response reply(LLSD(), request);
|
Response reply(LLSD(), request);
|
||||||
|
|
||||||
auto found = LLEventAPI::getInstance(request["api"]);
|
// check first among existing LLEventAPI instances
|
||||||
if (found)
|
auto foundea = LLEventAPI::getInstance(request["api"]);
|
||||||
|
if (foundea)
|
||||||
{
|
{
|
||||||
reply["name"] = found->getName();
|
reportAPI(reply, *foundea);
|
||||||
reply["desc"] = found->getDesc();
|
}
|
||||||
reply["key"] = found->getDispatchKey();
|
else
|
||||||
LLSD ops;
|
{
|
||||||
for (LLEventAPI::const_iterator oi(found->begin()), oend(found->end());
|
// Here the requested LLEventAPI doesn't yet exist, but do we have a
|
||||||
oi != oend; ++oi)
|
// registered LazyEventAPI for it?
|
||||||
{
|
LL::LazyEventAPIBase::instance_snapshot snap;
|
||||||
ops.append(found->getMetadata(oi->first));
|
auto foundlea = std::find_if(snap.begin(), snap.end(),
|
||||||
|
[api = request["api"].asString()]
|
||||||
|
(const auto& lea)
|
||||||
|
{ return (lea.getName() == api); });
|
||||||
|
if (foundlea != snap.end())
|
||||||
|
{
|
||||||
|
reportAPI(reply, *foundlea);
|
||||||
}
|
}
|
||||||
reply["ops"] = ops;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -529,6 +529,7 @@ LLProcess::LLProcess(const LLSDOrParams& params):
|
||||||
// preserve existing semantics, we promise that mAttached defaults to the
|
// preserve existing semantics, we promise that mAttached defaults to the
|
||||||
// same setting as mAutokill.
|
// same setting as mAutokill.
|
||||||
mAttached(params.attached.isProvided()? params.attached : params.autokill),
|
mAttached(params.attached.isProvided()? params.attached : params.autokill),
|
||||||
|
mPool(NULL),
|
||||||
mPipes(NSLOTS)
|
mPipes(NSLOTS)
|
||||||
{
|
{
|
||||||
// Hmm, when you construct a ptr_vector with a size, it merely reserves
|
// Hmm, when you construct a ptr_vector with a size, it merely reserves
|
||||||
|
|
@ -549,8 +550,14 @@ LLProcess::LLProcess(const LLSDOrParams& params):
|
||||||
|
|
||||||
mPostend = params.postend;
|
mPostend = params.postend;
|
||||||
|
|
||||||
|
apr_pool_create(&mPool, gAPRPoolp);
|
||||||
|
if (!mPool)
|
||||||
|
{
|
||||||
|
LLTHROW(LLProcessError(STRINGIZE("failed to create apr pool")));
|
||||||
|
}
|
||||||
|
|
||||||
apr_procattr_t *procattr = NULL;
|
apr_procattr_t *procattr = NULL;
|
||||||
chkapr(apr_procattr_create(&procattr, gAPRPoolp));
|
chkapr(apr_procattr_create(&procattr, mPool));
|
||||||
|
|
||||||
// IQA-490, CHOP-900: On Windows, ask APR to jump through hoops to
|
// IQA-490, CHOP-900: On Windows, ask APR to jump through hoops to
|
||||||
// constrain the set of handles passed to the child process. Before we
|
// constrain the set of handles passed to the child process. Before we
|
||||||
|
|
@ -689,14 +696,14 @@ LLProcess::LLProcess(const LLSDOrParams& params):
|
||||||
// one. Hand-expand chkapr() macro so we can fill in the actual command
|
// one. Hand-expand chkapr() macro so we can fill in the actual command
|
||||||
// string instead of the variable names.
|
// string instead of the variable names.
|
||||||
if (ll_apr_warn_status(apr_proc_create(&mProcess, argv[0], &argv[0], NULL, procattr,
|
if (ll_apr_warn_status(apr_proc_create(&mProcess, argv[0], &argv[0], NULL, procattr,
|
||||||
gAPRPoolp)))
|
mPool)))
|
||||||
{
|
{
|
||||||
LLTHROW(LLProcessError(STRINGIZE(params << " failed")));
|
LLTHROW(LLProcessError(STRINGIZE(params << " failed")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// arrange to call status_callback()
|
// arrange to call status_callback()
|
||||||
apr_proc_other_child_register(&mProcess, &LLProcess::status_callback, this, mProcess.in,
|
apr_proc_other_child_register(&mProcess, &LLProcess::status_callback, this, mProcess.in,
|
||||||
gAPRPoolp);
|
mPool);
|
||||||
// and make sure we poll it once per "mainloop" tick
|
// and make sure we poll it once per "mainloop" tick
|
||||||
sProcessListener.addPoll(*this);
|
sProcessListener.addPoll(*this);
|
||||||
mStatus.mState = RUNNING;
|
mStatus.mState = RUNNING;
|
||||||
|
|
@ -815,6 +822,12 @@ LLProcess::~LLProcess()
|
||||||
{
|
{
|
||||||
kill("destructor");
|
kill("destructor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mPool)
|
||||||
|
{
|
||||||
|
apr_pool_destroy(mPool);
|
||||||
|
mPool = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLProcess::kill(const std::string& who)
|
bool LLProcess::kill(const std::string& who)
|
||||||
|
|
|
||||||
|
|
@ -568,6 +568,7 @@ private:
|
||||||
// explicitly want this ptr_vector to be able to store NULLs
|
// explicitly want this ptr_vector to be able to store NULLs
|
||||||
typedef boost::ptr_vector< boost::nullable<BasePipe> > PipeVector;
|
typedef boost::ptr_vector< boost::nullable<BasePipe> > PipeVector;
|
||||||
PipeVector mPipes;
|
PipeVector mPipes;
|
||||||
|
apr_pool_t* mPool;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// for logging
|
/// for logging
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,12 @@
|
||||||
|
|
||||||
#include "llpointer.h"
|
#include "llpointer.h"
|
||||||
#include "llrefcount.h" // LLRefCount
|
#include "llrefcount.h" // LLRefCount
|
||||||
|
#include <boost/intrusive_ptr.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
#include <boost/type_traits/is_base_of.hpp>
|
#include <boost/type_traits/is_base_of.hpp>
|
||||||
#include <boost/type_traits/remove_pointer.hpp>
|
#include <boost/type_traits/remove_pointer.hpp>
|
||||||
#include <boost/utility/enable_if.hpp>
|
#include <memory> // std::shared_ptr, std::unique_ptr
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LLPtrTo<TARGET>::type is either of two things:
|
* LLPtrTo<TARGET>::type is either of two things:
|
||||||
|
|
@ -55,14 +58,14 @@ struct LLPtrTo
|
||||||
|
|
||||||
/// specialize for subclasses of LLRefCount
|
/// specialize for subclasses of LLRefCount
|
||||||
template <class T>
|
template <class T>
|
||||||
struct LLPtrTo<T, typename boost::enable_if< boost::is_base_of<LLRefCount, T> >::type>
|
struct LLPtrTo<T, typename std::enable_if< boost::is_base_of<LLRefCount, T>::value >::type>
|
||||||
{
|
{
|
||||||
typedef LLPointer<T> type;
|
typedef LLPointer<T> type;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// specialize for subclasses of LLThreadSafeRefCount
|
/// specialize for subclasses of LLThreadSafeRefCount
|
||||||
template <class T>
|
template <class T>
|
||||||
struct LLPtrTo<T, typename boost::enable_if< boost::is_base_of<LLThreadSafeRefCount, T> >::type>
|
struct LLPtrTo<T, typename std::enable_if< boost::is_base_of<LLThreadSafeRefCount, T>::value >::type>
|
||||||
{
|
{
|
||||||
typedef LLPointer<T> type;
|
typedef LLPointer<T> type;
|
||||||
};
|
};
|
||||||
|
|
@ -83,4 +86,83 @@ struct LLRemovePointer< LLPointer<SOMECLASS> >
|
||||||
typedef SOMECLASS type;
|
typedef SOMECLASS type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace LL
|
||||||
|
{
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* get_ref()
|
||||||
|
*****************************************************************************/
|
||||||
|
template <typename T>
|
||||||
|
struct GetRef
|
||||||
|
{
|
||||||
|
// return const ref or non-const ref, depending on whether we can bind
|
||||||
|
// a non-const lvalue ref to the argument
|
||||||
|
const auto& operator()(const T& obj) const { return obj; }
|
||||||
|
auto& operator()(T& obj) const { return obj; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct GetRef<const T*>
|
||||||
|
{
|
||||||
|
const auto& operator()(const T* ptr) const { return *ptr; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct GetRef<T*>
|
||||||
|
{
|
||||||
|
auto& operator()(T* ptr) const { return *ptr; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct GetRef< LLPointer<T> >
|
||||||
|
{
|
||||||
|
auto& operator()(LLPointer<T> ptr) const { return *ptr; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// whether we're passed a pointer or a reference, return a reference
|
||||||
|
template <typename T>
|
||||||
|
auto& get_ref(T& ptr_or_ref)
|
||||||
|
{
|
||||||
|
return GetRef<typename std::decay<T>::type>()(ptr_or_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const auto& get_ref(const T& ptr_or_ref)
|
||||||
|
{
|
||||||
|
return GetRef<typename std::decay<T>::type>()(ptr_or_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* get_ptr()
|
||||||
|
*****************************************************************************/
|
||||||
|
// if T is any pointer type we recognize, return it unchanged
|
||||||
|
template <typename T>
|
||||||
|
const T* get_ptr(const T* ptr) { return ptr; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* get_ptr(T* ptr) { return ptr; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const std::shared_ptr<T>& get_ptr(const std::shared_ptr<T>& ptr) { return ptr; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const std::unique_ptr<T>& get_ptr(const std::unique_ptr<T>& ptr) { return ptr; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const boost::shared_ptr<T>& get_ptr(const boost::shared_ptr<T>& ptr) { return ptr; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const boost::intrusive_ptr<T>& get_ptr(const boost::intrusive_ptr<T>& ptr) { return ptr; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const LLPointer<T>& get_ptr(const LLPointer<T>& ptr) { return ptr; }
|
||||||
|
|
||||||
|
// T is not any pointer type we recognize, take a pointer to the parameter
|
||||||
|
template <typename T>
|
||||||
|
const T* get_ptr(const T& obj) { return &obj; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* get_ptr(T& obj) { return &obj; }
|
||||||
|
} // namespace LL
|
||||||
|
|
||||||
#endif /* ! defined(LL_LLPTRTO_H) */
|
#endif /* ! defined(LL_LLPTRTO_H) */
|
||||||
|
|
|
||||||
|
|
@ -1046,3 +1046,38 @@ LLSD llsd_shallow(LLSD value, LLSD filter)
|
||||||
|
|
||||||
return shallow;
|
return shallow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LLSD LL::apply_llsd_fix(size_t arity, const LLSD& args)
|
||||||
|
{
|
||||||
|
// LLSD supports a number of types, two of which are aggregates: Map and
|
||||||
|
// Array. We don't try to support Map: supporting Map would seem to
|
||||||
|
// promise that we could somehow match the string key to 'func's parameter
|
||||||
|
// names. Uh sorry, maybe in some future version of C++ with reflection.
|
||||||
|
if (args.isMap())
|
||||||
|
{
|
||||||
|
LLTHROW(LL::apply_error("LL::apply(function, Map LLSD) unsupported"));
|
||||||
|
}
|
||||||
|
// We expect an LLSD array, but what the heck, treat isUndefined() as a
|
||||||
|
// zero-length array for calling a nullary 'func'.
|
||||||
|
if (args.isUndefined() || args.isArray())
|
||||||
|
{
|
||||||
|
// this works because LLSD().size() == 0
|
||||||
|
if (args.size() != arity)
|
||||||
|
{
|
||||||
|
LLTHROW(LL::apply_error(stringize("LL::apply(function(", arity, " args), ",
|
||||||
|
args.size(), "-entry LLSD array)")));
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
// args is one of the scalar types
|
||||||
|
// scalar_LLSD.size() == 0, so don't test that here.
|
||||||
|
// You can pass a scalar LLSD only to a unary 'func'.
|
||||||
|
if (arity != 1)
|
||||||
|
{
|
||||||
|
LLTHROW(LL::apply_error(stringize("LL::apply(function(", arity, " args), "
|
||||||
|
"LLSD ", LLSD::typeString(args.type()), ")")));
|
||||||
|
}
|
||||||
|
// make an array of it
|
||||||
|
return llsd::array(args);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,14 @@
|
||||||
#ifndef LL_LLSDUTIL_H
|
#ifndef LL_LLSDUTIL_H
|
||||||
#define LL_LLSDUTIL_H
|
#define LL_LLSDUTIL_H
|
||||||
|
|
||||||
|
#include "apply.h" // LL::invoke()
|
||||||
|
#include "function_types.h" // LL::function_arity
|
||||||
#include "llsd.h"
|
#include "llsd.h"
|
||||||
#include <boost/functional/hash.hpp>
|
#include <boost/functional/hash.hpp>
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory> // std::shared_ptr
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
// U32
|
// U32
|
||||||
LL_COMMON_API LLSD ll_sd_from_U32(const U32);
|
LL_COMMON_API LLSD ll_sd_from_U32(const U32);
|
||||||
|
|
@ -298,6 +304,11 @@ LLSD map(Ts&&... vs)
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* LLSDParam
|
* LLSDParam
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
struct LLSDParamBase
|
||||||
|
{
|
||||||
|
virtual ~LLSDParamBase() {}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LLSDParam is a customization point for passing LLSD values to function
|
* LLSDParam is a customization point for passing LLSD values to function
|
||||||
* parameters of more or less arbitrary type. LLSD provides a small set of
|
* parameters of more or less arbitrary type. LLSD provides a small set of
|
||||||
|
|
@ -315,7 +326,7 @@ LLSD map(Ts&&... vs)
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class LLSDParam
|
class LLSDParam: public LLSDParamBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
|
@ -323,13 +334,66 @@ public:
|
||||||
* value for later retrieval
|
* value for later retrieval
|
||||||
*/
|
*/
|
||||||
LLSDParam(const LLSD& value):
|
LLSDParam(const LLSD& value):
|
||||||
_value(value)
|
value_(value)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
operator T() const { return _value; }
|
operator T() const { return value_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T _value;
|
T value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LLSDParam<LLSD> is for when you don't already have the target parameter
|
||||||
|
* type in hand. Instantiate LLSDParam<LLSD>(your LLSD object), and the
|
||||||
|
* templated conversion operator will try to select a more specific LLSDParam
|
||||||
|
* specialization.
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
class LLSDParam<LLSD>: public LLSDParamBase
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
LLSD value_;
|
||||||
|
// LLSDParam<LLSD>::operator T() works by instantiating an LLSDParam<T> on
|
||||||
|
// demand. Returning that engages LLSDParam<T>::operator T(), producing
|
||||||
|
// the desired result. But LLSDParam<const char*> owns a std::string whose
|
||||||
|
// c_str() is returned by its operator const char*(). If we return a temp
|
||||||
|
// LLSDParam<const char*>, the compiler can destroy it right away, as soon
|
||||||
|
// as we've called operator const char*(). That's a problem! That
|
||||||
|
// invalidates the const char* we've just passed to the subject function.
|
||||||
|
// This LLSDParam<LLSD> is presumably guaranteed to survive until the
|
||||||
|
// subject function has returned, so we must ensure that any constructed
|
||||||
|
// LLSDParam<T> lives just as long as this LLSDParam<LLSD> does. Putting
|
||||||
|
// each LLSDParam<T> on the heap and capturing a smart pointer in a vector
|
||||||
|
// works. We would have liked to use std::unique_ptr, but vector entries
|
||||||
|
// must be copyable.
|
||||||
|
// (Alternatively we could assume that every instance of LLSDParam<LLSD>
|
||||||
|
// will be asked for at most ONE conversion. We could store a scalar
|
||||||
|
// std::unique_ptr and, when constructing an new LLSDParam<T>, assert that
|
||||||
|
// the unique_ptr is empty. But some future change in usage patterns, and
|
||||||
|
// consequent failure of that assertion, would be very mysterious. Instead
|
||||||
|
// of explaining how to fix it, just fix it now.)
|
||||||
|
mutable std::vector<std::shared_ptr<LLSDParamBase>> converters_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LLSDParam(const LLSD& value): value_(value) {}
|
||||||
|
|
||||||
|
/// if we're literally being asked for an LLSD parameter, avoid infinite
|
||||||
|
/// recursion
|
||||||
|
operator LLSD() const { return value_; }
|
||||||
|
|
||||||
|
/// otherwise, instantiate a more specific LLSDParam<T> to convert; that
|
||||||
|
/// preserves the existing customization mechanism
|
||||||
|
template <typename T>
|
||||||
|
operator T() const
|
||||||
|
{
|
||||||
|
// capture 'ptr' with the specific subclass type because converters_
|
||||||
|
// only stores LLSDParamBase pointers
|
||||||
|
auto ptr{ std::make_shared<LLSDParam<std::decay_t<T>>>(value_) };
|
||||||
|
// keep the new converter alive until we ourselves are destroyed
|
||||||
|
converters_.push_back(ptr);
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -346,17 +410,17 @@ private:
|
||||||
*/
|
*/
|
||||||
#define LLSDParam_for(T, AS) \
|
#define LLSDParam_for(T, AS) \
|
||||||
template <> \
|
template <> \
|
||||||
class LLSDParam<T> \
|
class LLSDParam<T>: public LLSDParamBase \
|
||||||
{ \
|
{ \
|
||||||
public: \
|
public: \
|
||||||
LLSDParam(const LLSD& value): \
|
LLSDParam(const LLSD& value): \
|
||||||
_value((T)value.AS()) \
|
value_((T)value.AS()) \
|
||||||
{} \
|
{} \
|
||||||
\
|
\
|
||||||
operator T() const { return _value; } \
|
operator T() const { return value_; } \
|
||||||
\
|
\
|
||||||
private: \
|
private: \
|
||||||
T _value; \
|
T value_; \
|
||||||
}
|
}
|
||||||
|
|
||||||
LLSDParam_for(float, asReal);
|
LLSDParam_for(float, asReal);
|
||||||
|
|
@ -372,31 +436,31 @@ LLSDParam_for(LLSD::Binary, asBinary);
|
||||||
* safely pass an LLSDParam<const char*>(yourLLSD).
|
* safely pass an LLSDParam<const char*>(yourLLSD).
|
||||||
*/
|
*/
|
||||||
template <>
|
template <>
|
||||||
class LLSDParam<const char*>
|
class LLSDParam<const char*>: public LLSDParamBase
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
// The difference here is that we store a std::string rather than a const
|
// The difference here is that we store a std::string rather than a const
|
||||||
// char*. It's important that the LLSDParam object own the std::string.
|
// char*. It's important that the LLSDParam object own the std::string.
|
||||||
std::string _value;
|
std::string value_;
|
||||||
// We don't bother storing the incoming LLSD object, but we do have to
|
// We don't bother storing the incoming LLSD object, but we do have to
|
||||||
// distinguish whether _value is an empty string because the LLSD object
|
// distinguish whether value_ is an empty string because the LLSD object
|
||||||
// contains an empty string or because it's isUndefined().
|
// contains an empty string or because it's isUndefined().
|
||||||
bool _undefined;
|
bool undefined_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LLSDParam(const LLSD& value):
|
LLSDParam(const LLSD& value):
|
||||||
_value(value),
|
value_(value),
|
||||||
_undefined(value.isUndefined())
|
undefined_(value.isUndefined())
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// The const char* we retrieve is for storage owned by our _value member.
|
// The const char* we retrieve is for storage owned by our value_ member.
|
||||||
// That's how we guarantee that the const char* is valid for the lifetime
|
// That's how we guarantee that the const char* is valid for the lifetime
|
||||||
// of this LLSDParam object. Constructing your LLSDParam in the argument
|
// of this LLSDParam object. Constructing your LLSDParam in the argument
|
||||||
// list should ensure that the LLSDParam object will persist for the
|
// list should ensure that the LLSDParam object will persist for the
|
||||||
// duration of the function call.
|
// duration of the function call.
|
||||||
operator const char*() const
|
operator const char*() const
|
||||||
{
|
{
|
||||||
if (_undefined)
|
if (undefined_)
|
||||||
{
|
{
|
||||||
// By default, an isUndefined() LLSD object's asString() method
|
// By default, an isUndefined() LLSD object's asString() method
|
||||||
// will produce an empty string. But for a function accepting
|
// will produce an empty string. But for a function accepting
|
||||||
|
|
@ -406,7 +470,7 @@ public:
|
||||||
// case, though, no LLSD value could pass NULL.
|
// case, though, no LLSD value could pass NULL.
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return _value.c_str();
|
return value_.c_str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -555,4 +619,56 @@ struct hash<LLSD>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace LL
|
||||||
|
{
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* apply(function, LLSD array)
|
||||||
|
*****************************************************************************/
|
||||||
|
// validate incoming LLSD blob, and return an LLSD array suitable to pass to
|
||||||
|
// the function of interest
|
||||||
|
LLSD apply_llsd_fix(size_t arity, const LLSD& args);
|
||||||
|
|
||||||
|
// Derived from https://stackoverflow.com/a/20441189
|
||||||
|
// and https://en.cppreference.com/w/cpp/utility/apply .
|
||||||
|
// We can't simply make a tuple from the LLSD array and then apply() that
|
||||||
|
// tuple to the function -- how would make_tuple() deduce the correct
|
||||||
|
// parameter type for each entry? We must go directly to the target function.
|
||||||
|
template <typename CALLABLE, std::size_t... I>
|
||||||
|
auto apply_impl(CALLABLE&& func, const LLSD& array, std::index_sequence<I...>)
|
||||||
|
{
|
||||||
|
// call func(unpacked args), using generic LLSDParam<LLSD> to convert each
|
||||||
|
// entry in 'array' to the target parameter type
|
||||||
|
return std::forward<CALLABLE>(func)(LLSDParam<LLSD>(array[I])...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// use apply_n<ARITY>(function, LLSD) to call a specific arity of a variadic
|
||||||
|
// function with (that many) items from the passed LLSD array
|
||||||
|
template <size_t ARITY, typename CALLABLE>
|
||||||
|
auto apply_n(CALLABLE&& func, const LLSD& args)
|
||||||
|
{
|
||||||
|
return apply_impl(std::forward<CALLABLE>(func),
|
||||||
|
apply_llsd_fix(ARITY, args),
|
||||||
|
std::make_index_sequence<ARITY>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* apply(function, LLSD) goes beyond C++17 std::apply(). For this case
|
||||||
|
* @a function @emph cannot be variadic: the compiler must know at compile
|
||||||
|
* time how many arguments to pass. This isn't Python. (But see apply_n() to
|
||||||
|
* pass a specific number of args to a variadic function.)
|
||||||
|
*/
|
||||||
|
template <typename CALLABLE>
|
||||||
|
auto apply(CALLABLE&& func, const LLSD& args)
|
||||||
|
{
|
||||||
|
// infer arity from the definition of func
|
||||||
|
constexpr auto arity = function_arity<
|
||||||
|
typename std::remove_reference<CALLABLE>::type>::value;
|
||||||
|
// now that we have a compile-time arity, apply_n() works
|
||||||
|
return apply_n<arity>(std::forward<CALLABLE>(func), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace LL
|
||||||
|
|
||||||
#endif // LL_LLSDUTIL_H
|
#endif // LL_LLSDUTIL_H
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,240 @@
|
||||||
|
/**
|
||||||
|
* @file apply_test.cpp
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2022-12-19
|
||||||
|
* @brief Test for apply.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
||||||
|
* Copyright (c) 2022, Linden Research, Inc.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Precompiled header
|
||||||
|
#include "linden_common.h"
|
||||||
|
// associated header
|
||||||
|
#include "apply.h"
|
||||||
|
// STL headers
|
||||||
|
// std headers
|
||||||
|
#include <iomanip>
|
||||||
|
// external library headers
|
||||||
|
// other Linden headers
|
||||||
|
#include "llsd.h"
|
||||||
|
#include "llsdutil.h"
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// for ensure_equals
|
||||||
|
std::ostream& operator<<(std::ostream& out, const std::vector<std::string>& stringvec)
|
||||||
|
{
|
||||||
|
const char* delim = "[";
|
||||||
|
for (const auto& str : stringvec)
|
||||||
|
{
|
||||||
|
out << delim << std::quoted(str);
|
||||||
|
delim = ", ";
|
||||||
|
}
|
||||||
|
return out << ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
// the above must be declared BEFORE ensure_equals(std::vector<std::string>)
|
||||||
|
#include "../test/lltut.h"
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* TUT
|
||||||
|
*****************************************************************************/
|
||||||
|
namespace tut
|
||||||
|
{
|
||||||
|
namespace statics
|
||||||
|
{
|
||||||
|
/*------------------------------ data ------------------------------*/
|
||||||
|
// Although we're using types from the LLSD namespace, we're not
|
||||||
|
// constructing LLSD values, but rather instances of the C++ types
|
||||||
|
// supported by LLSD.
|
||||||
|
static LLSD::Boolean b{true};
|
||||||
|
static LLSD::Integer i{17};
|
||||||
|
static LLSD::Real f{3.14};
|
||||||
|
static LLSD::String s{ "hello" };
|
||||||
|
static LLSD::UUID uu{ "baadf00d-dead-beef-baad-feedb0ef" };
|
||||||
|
static LLSD::Date dt{ "2022-12-19" };
|
||||||
|
static LLSD::URI uri{ "http://secondlife.com" };
|
||||||
|
static LLSD::Binary bin{ 0x01, 0x02, 0x03, 0x04, 0x05 };
|
||||||
|
|
||||||
|
static std::vector<LLSD::String> quick
|
||||||
|
{
|
||||||
|
"The", "quick", "brown", "fox", "etc."
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::array<int, 5> fibs
|
||||||
|
{
|
||||||
|
0, 1, 1, 2, 3
|
||||||
|
};
|
||||||
|
|
||||||
|
// ensure that apply() actually reaches the target method --
|
||||||
|
// lack of ensure_equals() failure could be due to no-op apply()
|
||||||
|
bool called{ false };
|
||||||
|
// capture calls from collect()
|
||||||
|
std::vector<std::string> collected;
|
||||||
|
|
||||||
|
/*------------------------- test functions -------------------------*/
|
||||||
|
void various(LLSD::Boolean b, LLSD::Integer i, LLSD::Real f, const LLSD::String& s,
|
||||||
|
const LLSD::UUID& uu, const LLSD::Date& dt,
|
||||||
|
const LLSD::URI& uri, const LLSD::Binary& bin)
|
||||||
|
{
|
||||||
|
called = true;
|
||||||
|
ensure_equals( "b mismatch", b, statics::b);
|
||||||
|
ensure_equals( "i mismatch", i, statics::i);
|
||||||
|
ensure_equals( "f mismatch", f, statics::f);
|
||||||
|
ensure_equals( "s mismatch", s, statics::s);
|
||||||
|
ensure_equals( "uu mismatch", uu, statics::uu);
|
||||||
|
ensure_equals( "dt mismatch", dt, statics::dt);
|
||||||
|
ensure_equals("uri mismatch", uri, statics::uri);
|
||||||
|
ensure_equals("bin mismatch", bin, statics::bin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void strings(std::string s0, std::string s1, std::string s2, std::string s3, std::string s4)
|
||||||
|
{
|
||||||
|
called = true;
|
||||||
|
ensure_equals("s0 mismatch", s0, statics::quick[0]);
|
||||||
|
ensure_equals("s1 mismatch", s1, statics::quick[1]);
|
||||||
|
ensure_equals("s2 mismatch", s2, statics::quick[2]);
|
||||||
|
ensure_equals("s3 mismatch", s3, statics::quick[3]);
|
||||||
|
ensure_equals("s4 mismatch", s4, statics::quick[4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ints(int i0, int i1, int i2, int i3, int i4)
|
||||||
|
{
|
||||||
|
called = true;
|
||||||
|
ensure_equals("i0 mismatch", i0, statics::fibs[0]);
|
||||||
|
ensure_equals("i1 mismatch", i1, statics::fibs[1]);
|
||||||
|
ensure_equals("i2 mismatch", i2, statics::fibs[2]);
|
||||||
|
ensure_equals("i3 mismatch", i3, statics::fibs[3]);
|
||||||
|
ensure_equals("i4 mismatch", i4, statics::fibs[4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdfunc(const LLSD& sd)
|
||||||
|
{
|
||||||
|
called = true;
|
||||||
|
ensure_equals("sd mismatch", sd.asInteger(), statics::i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void intfunc(int i)
|
||||||
|
{
|
||||||
|
called = true;
|
||||||
|
ensure_equals("i mismatch", i, statics::i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void voidfunc()
|
||||||
|
{
|
||||||
|
called = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recursion tail
|
||||||
|
void collect()
|
||||||
|
{
|
||||||
|
called = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect(arbitrary)
|
||||||
|
template <typename... ARGS>
|
||||||
|
void collect(const std::string& first, ARGS&&... rest)
|
||||||
|
{
|
||||||
|
statics::collected.push_back(first);
|
||||||
|
collect(std::forward<ARGS>(rest)...);
|
||||||
|
}
|
||||||
|
} // namespace statics
|
||||||
|
|
||||||
|
struct apply_data
|
||||||
|
{
|
||||||
|
apply_data()
|
||||||
|
{
|
||||||
|
// reset called before each test
|
||||||
|
statics::called = false;
|
||||||
|
statics::collected.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
typedef test_group<apply_data> apply_group;
|
||||||
|
typedef apply_group::object object;
|
||||||
|
apply_group applygrp("apply");
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<1>()
|
||||||
|
{
|
||||||
|
set_test_name("apply(tuple)");
|
||||||
|
LL::apply(statics::various,
|
||||||
|
std::make_tuple(statics::b, statics::i, statics::f, statics::s,
|
||||||
|
statics::uu, statics::dt, statics::uri, statics::bin));
|
||||||
|
ensure("apply(tuple) failed", statics::called);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<2>()
|
||||||
|
{
|
||||||
|
set_test_name("apply(array)");
|
||||||
|
LL::apply(statics::ints, statics::fibs);
|
||||||
|
ensure("apply(array) failed", statics::called);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<3>()
|
||||||
|
{
|
||||||
|
set_test_name("apply(vector)");
|
||||||
|
LL::apply(statics::strings, statics::quick);
|
||||||
|
ensure("apply(vector) failed", statics::called);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The various apply(LLSD) tests exercise only the success cases because
|
||||||
|
// the failure cases trigger assert() fail, which is hard to catch.
|
||||||
|
template<> template<>
|
||||||
|
void object::test<4>()
|
||||||
|
{
|
||||||
|
set_test_name("apply(LLSD())");
|
||||||
|
LL::apply(statics::voidfunc, LLSD());
|
||||||
|
ensure("apply(LLSD()) failed", statics::called);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<5>()
|
||||||
|
{
|
||||||
|
set_test_name("apply(fn(int), LLSD scalar)");
|
||||||
|
LL::apply(statics::intfunc, LLSD(statics::i));
|
||||||
|
ensure("apply(fn(int), LLSD scalar) failed", statics::called);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<6>()
|
||||||
|
{
|
||||||
|
set_test_name("apply(fn(LLSD), LLSD scalar)");
|
||||||
|
// This test verifies that LLSDParam<LLSD> doesn't send the compiler
|
||||||
|
// into infinite recursion when the target is itself LLSD.
|
||||||
|
LL::apply(statics::sdfunc, LLSD(statics::i));
|
||||||
|
ensure("apply(fn(LLSD), LLSD scalar) failed", statics::called);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<7>()
|
||||||
|
{
|
||||||
|
set_test_name("apply(LLSD array)");
|
||||||
|
LL::apply(statics::various,
|
||||||
|
llsd::array(statics::b, statics::i, statics::f, statics::s,
|
||||||
|
statics::uu, statics::dt, statics::uri, statics::bin));
|
||||||
|
ensure("apply(LLSD array) failed", statics::called);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<8>()
|
||||||
|
{
|
||||||
|
set_test_name("VAPPLY()");
|
||||||
|
// Make a std::array<std::string> from statics::quick. We can't call a
|
||||||
|
// variadic function with a data structure of dynamic length.
|
||||||
|
std::array<std::string, 5> strray;
|
||||||
|
for (size_t i = 0; i < strray.size(); ++i)
|
||||||
|
strray[i] = statics::quick[i];
|
||||||
|
// This doesn't work: the compiler doesn't know which overload of
|
||||||
|
// collect() to pass to LL::apply().
|
||||||
|
// LL::apply(statics::collect, strray);
|
||||||
|
// That's what VAPPLY() is for.
|
||||||
|
VAPPLY(statics::collect, strray);
|
||||||
|
ensure("VAPPLY() failed", statics::called);
|
||||||
|
ensure_equals("collected mismatch", statics::collected, statics::quick);
|
||||||
|
}
|
||||||
|
} // namespace tut
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
/**
|
||||||
|
* @file lazyeventapi_test.cpp
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2022-06-18
|
||||||
|
* @brief Test for lazyeventapi.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
||||||
|
* Copyright (c) 2022, Linden Research, Inc.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Precompiled header
|
||||||
|
#include "linden_common.h"
|
||||||
|
// associated header
|
||||||
|
#include "lazyeventapi.h"
|
||||||
|
// STL headers
|
||||||
|
// std headers
|
||||||
|
// external library headers
|
||||||
|
// other Linden headers
|
||||||
|
#include "../test/lltut.h"
|
||||||
|
#include "llevents.h"
|
||||||
|
#include "llsdutil.h"
|
||||||
|
|
||||||
|
// observable side effect, solely for testing
|
||||||
|
static LLSD data;
|
||||||
|
|
||||||
|
// LLEventAPI listener subclass
|
||||||
|
class MyListener: public LLEventAPI
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// need this trivial forwarding constructor
|
||||||
|
// (of course do any other initialization your subclass requires)
|
||||||
|
MyListener(const LL::LazyEventAPIParams& params):
|
||||||
|
LLEventAPI(params)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// example operation, registered by LazyEventAPI subclass below
|
||||||
|
void set_data(const LLSD& event)
|
||||||
|
{
|
||||||
|
data = event["data"];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// LazyEventAPI registrar subclass
|
||||||
|
class MyRegistrar: public LL::LazyEventAPI<MyListener>
|
||||||
|
{
|
||||||
|
using super = LL::LazyEventAPI<MyListener>;
|
||||||
|
using super::listener;
|
||||||
|
public:
|
||||||
|
// LazyEventAPI subclass initializes like a classic LLEventAPI subclass
|
||||||
|
// constructor, with API name and desc plus add() calls for the defined
|
||||||
|
// operations
|
||||||
|
MyRegistrar():
|
||||||
|
super("Test", "This is a test LLEventAPI")
|
||||||
|
{
|
||||||
|
add("set", "This is a set operation", &listener::set_data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Normally we'd declare a static instance of MyRegistrar -- but because we
|
||||||
|
// want to test both with and without, defer declaration to individual test
|
||||||
|
// methods.
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* TUT
|
||||||
|
*****************************************************************************/
|
||||||
|
namespace tut
|
||||||
|
{
|
||||||
|
struct lazyeventapi_data
|
||||||
|
{
|
||||||
|
lazyeventapi_data()
|
||||||
|
{
|
||||||
|
// before every test, reset 'data'
|
||||||
|
data.clear();
|
||||||
|
}
|
||||||
|
~lazyeventapi_data()
|
||||||
|
{
|
||||||
|
// after every test, reset LLEventPumps
|
||||||
|
LLEventPumps::deleteSingleton();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
typedef test_group<lazyeventapi_data> lazyeventapi_group;
|
||||||
|
typedef lazyeventapi_group::object object;
|
||||||
|
lazyeventapi_group lazyeventapigrp("lazyeventapi");
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<1>()
|
||||||
|
{
|
||||||
|
set_test_name("LazyEventAPI");
|
||||||
|
// this is where the magic (should) happen
|
||||||
|
// 'register' still a keyword until C++17
|
||||||
|
MyRegistrar regster;
|
||||||
|
LLEventPumps::instance().obtain("Test").post(llsd::map("op", "set", "data", "hey"));
|
||||||
|
ensure_equals("failed to set data", data.asString(), "hey");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<2>()
|
||||||
|
{
|
||||||
|
set_test_name("No LazyEventAPI");
|
||||||
|
// Because the MyRegistrar declaration in test<1>() is local, because
|
||||||
|
// it has been destroyed, we fully expect NOT to reach a MyListener
|
||||||
|
// instance with this post.
|
||||||
|
LLEventPumps::instance().obtain("Test").post(llsd::map("op", "set", "data", "moot"));
|
||||||
|
ensure("accidentally set data", ! data.isDefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<3>()
|
||||||
|
{
|
||||||
|
set_test_name("LazyEventAPI metadata");
|
||||||
|
MyRegistrar regster;
|
||||||
|
// Of course we have 'regster' in hand; we don't need to search for
|
||||||
|
// it. But this next test verifies that we can find (all) LazyEventAPI
|
||||||
|
// instances using LazyEventAPIBase::instance_snapshot. Normally we
|
||||||
|
// wouldn't search; normally we'd just look at each instance in the
|
||||||
|
// loop body.
|
||||||
|
const MyRegistrar* found = nullptr;
|
||||||
|
for (const auto& registrar : LL::LazyEventAPIBase::instance_snapshot())
|
||||||
|
if ((found = dynamic_cast<const MyRegistrar*>(®istrar)))
|
||||||
|
break;
|
||||||
|
ensure("Failed to find MyRegistrar via LLInstanceTracker", found);
|
||||||
|
|
||||||
|
ensure_equals("wrong API name", found->getName(), "Test");
|
||||||
|
ensure_contains("wrong API desc", found->getDesc(), "test LLEventAPI");
|
||||||
|
ensure_equals("wrong API field", found->getDispatchKey(), "op");
|
||||||
|
// Normally we'd just iterate over *found. But for test purposes,
|
||||||
|
// actually capture the range of NameDesc pairs in a vector.
|
||||||
|
std::vector<LL::LazyEventAPIBase::NameDesc> ops{ found->begin(), found->end() };
|
||||||
|
ensure_equals("failed to find operations", ops.size(), 1);
|
||||||
|
ensure_equals("wrong operation name", ops[0].first, "set");
|
||||||
|
ensure_contains("wrong operation desc", ops[0].second, "set operation");
|
||||||
|
LLSD metadata{ found->getMetadata(ops[0].first) };
|
||||||
|
ensure_equals("bad metadata name", metadata["name"].asString(), ops[0].first);
|
||||||
|
ensure_equals("bad metadata desc", metadata["desc"].asString(), ops[0].second);
|
||||||
|
}
|
||||||
|
} // namespace tut
|
||||||
|
|
@ -18,9 +18,12 @@
|
||||||
// external library headers
|
// external library headers
|
||||||
// other Linden headers
|
// other Linden headers
|
||||||
#include "../test/lltut.h"
|
#include "../test/lltut.h"
|
||||||
|
#include "lleventfilter.h"
|
||||||
#include "llsd.h"
|
#include "llsd.h"
|
||||||
#include "llsdutil.h"
|
#include "llsdutil.h"
|
||||||
|
#include "llevents.h"
|
||||||
#include "stringize.h"
|
#include "stringize.h"
|
||||||
|
#include "StringVec.h"
|
||||||
#include "tests/wrapllerrs.h"
|
#include "tests/wrapllerrs.h"
|
||||||
#include "../test/catch_and_store_what_in.h"
|
#include "../test/catch_and_store_what_in.h"
|
||||||
#include "../test/debug.h"
|
#include "../test/debug.h"
|
||||||
|
|
@ -32,8 +35,6 @@
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
#include <boost/function.hpp>
|
#include <boost/function.hpp>
|
||||||
#include <boost/range.hpp>
|
#include <boost/range.hpp>
|
||||||
#include <boost/foreach.hpp>
|
|
||||||
#define foreach BOOST_FOREACH
|
|
||||||
|
|
||||||
#include <boost/lambda/lambda.hpp>
|
#include <boost/lambda/lambda.hpp>
|
||||||
|
|
||||||
|
|
@ -177,6 +178,7 @@ struct Vars
|
||||||
/*-------- Arbitrary-params (non-const, const, static) methods ---------*/
|
/*-------- Arbitrary-params (non-const, const, static) methods ---------*/
|
||||||
void methodna(NPARAMSa)
|
void methodna(NPARAMSa)
|
||||||
{
|
{
|
||||||
|
DEBUG;
|
||||||
// Because our const char* param cp might be NULL, and because we
|
// Because our const char* param cp might be NULL, and because we
|
||||||
// intend to capture the value in a std::string, have to distinguish
|
// intend to capture the value in a std::string, have to distinguish
|
||||||
// between the NULL value and any non-NULL value. Use a convention
|
// between the NULL value and any non-NULL value. Use a convention
|
||||||
|
|
@ -188,7 +190,7 @@ struct Vars
|
||||||
else
|
else
|
||||||
vcp = std::string("'") + cp + "'";
|
vcp = std::string("'") + cp + "'";
|
||||||
|
|
||||||
debug()("methodna(", b,
|
this->debug()("methodna(", b,
|
||||||
", ", i,
|
", ", i,
|
||||||
", ", f,
|
", ", f,
|
||||||
", ", d,
|
", ", d,
|
||||||
|
|
@ -205,7 +207,7 @@ struct Vars
|
||||||
void methodnb(NPARAMSb)
|
void methodnb(NPARAMSb)
|
||||||
{
|
{
|
||||||
std::ostringstream vbin;
|
std::ostringstream vbin;
|
||||||
foreach(U8 byte, bin)
|
for (U8 byte: bin)
|
||||||
{
|
{
|
||||||
vbin << std::hex << std::setfill('0') << std::setw(2) << unsigned(byte);
|
vbin << std::hex << std::setfill('0') << std::setw(2) << unsigned(byte);
|
||||||
}
|
}
|
||||||
|
|
@ -226,7 +228,8 @@ struct Vars
|
||||||
|
|
||||||
void cmethodna(NPARAMSa) const
|
void cmethodna(NPARAMSa) const
|
||||||
{
|
{
|
||||||
debug()('c', NONL);
|
DEBUG;
|
||||||
|
this->debug()('c', NONL);
|
||||||
const_cast<Vars*>(this)->methodna(NARGSa);
|
const_cast<Vars*>(this)->methodna(NARGSa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,6 +318,31 @@ void freenb(NPARAMSb)
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
namespace tut
|
namespace tut
|
||||||
{
|
{
|
||||||
|
void ensure_has(const std::string& outer, const std::string& inner)
|
||||||
|
{
|
||||||
|
ensure(stringize("'", outer, "' does not contain '", inner, "'"),
|
||||||
|
outer.find(inner) != std::string::npos);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CALLABLE>
|
||||||
|
std::string call_exc(CALLABLE&& func, const std::string& exc_frag)
|
||||||
|
{
|
||||||
|
std::string what =
|
||||||
|
catch_what<LLEventDispatcher::DispatchError>(std::forward<CALLABLE>(func));
|
||||||
|
ensure_has(what, exc_frag);
|
||||||
|
return what;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CALLABLE>
|
||||||
|
void call_logerr(CALLABLE&& func, const std::string& frag)
|
||||||
|
{
|
||||||
|
CaptureLog capture;
|
||||||
|
// the error should be logged; we just need to stop the exception
|
||||||
|
// propagating
|
||||||
|
catch_what<LLEventDispatcher::DispatchError>(std::forward<CALLABLE>(func));
|
||||||
|
capture.messageWith(frag);
|
||||||
|
}
|
||||||
|
|
||||||
struct lleventdispatcher_data
|
struct lleventdispatcher_data
|
||||||
{
|
{
|
||||||
Debug debug{"test"};
|
Debug debug{"test"};
|
||||||
|
|
@ -397,9 +425,9 @@ namespace tut
|
||||||
work.add(name, desc, &Dispatcher::cmethod1, required);
|
work.add(name, desc, &Dispatcher::cmethod1, required);
|
||||||
// Non-subclass method with/out required params
|
// Non-subclass method with/out required params
|
||||||
addf("method1", "method1", &v);
|
addf("method1", "method1", &v);
|
||||||
work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1));
|
work.add(name, desc, [this](const LLSD& args){ return v.method1(args); });
|
||||||
addf("method1_req", "method1", &v);
|
addf("method1_req", "method1", &v);
|
||||||
work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1), required);
|
work.add(name, desc, [this](const LLSD& args){ return v.method1(args); }, required);
|
||||||
|
|
||||||
/*--------------- Arbitrary params, array style ----------------*/
|
/*--------------- Arbitrary params, array style ----------------*/
|
||||||
|
|
||||||
|
|
@ -461,7 +489,7 @@ namespace tut
|
||||||
debug("dft_array_full:\n",
|
debug("dft_array_full:\n",
|
||||||
dft_array_full);
|
dft_array_full);
|
||||||
// Partial defaults arrays.
|
// Partial defaults arrays.
|
||||||
foreach(LLSD::String a, ab)
|
for (LLSD::String a: ab)
|
||||||
{
|
{
|
||||||
LLSD::Integer partition(std::min(partial_offset, dft_array_full[a].size()));
|
LLSD::Integer partition(std::min(partial_offset, dft_array_full[a].size()));
|
||||||
dft_array_partial[a] =
|
dft_array_partial[a] =
|
||||||
|
|
@ -471,7 +499,7 @@ namespace tut
|
||||||
debug("dft_array_partial:\n",
|
debug("dft_array_partial:\n",
|
||||||
dft_array_partial);
|
dft_array_partial);
|
||||||
|
|
||||||
foreach(LLSD::String a, ab)
|
for(LLSD::String a: ab)
|
||||||
{
|
{
|
||||||
// Generate full defaults maps by zipping (params, dft_array_full).
|
// Generate full defaults maps by zipping (params, dft_array_full).
|
||||||
dft_map_full[a] = zipmap(params[a], dft_array_full[a]);
|
dft_map_full[a] = zipmap(params[a], dft_array_full[a]);
|
||||||
|
|
@ -583,6 +611,7 @@ namespace tut
|
||||||
|
|
||||||
void addf(const std::string& n, const std::string& d, Vars* v)
|
void addf(const std::string& n, const std::string& d, Vars* v)
|
||||||
{
|
{
|
||||||
|
debug("addf('", n, "', '", d, "')");
|
||||||
// This method is to capture in our own DescMap the name and
|
// This method is to capture in our own DescMap the name and
|
||||||
// description of every registered function, for metadata query
|
// description of every registered function, for metadata query
|
||||||
// testing.
|
// testing.
|
||||||
|
|
@ -598,19 +627,14 @@ namespace tut
|
||||||
{
|
{
|
||||||
// Copy descs to a temp map of same type.
|
// Copy descs to a temp map of same type.
|
||||||
DescMap forgotten(descs.begin(), descs.end());
|
DescMap forgotten(descs.begin(), descs.end());
|
||||||
// LLEventDispatcher intentionally provides only const_iterator:
|
for (LLEventDispatcher::NameDesc nd: work)
|
||||||
// since dereferencing that iterator generates values on the fly,
|
|
||||||
// it's meaningless to have a modifiable iterator. But since our
|
|
||||||
// 'work' object isn't const, by default BOOST_FOREACH() wants to
|
|
||||||
// use non-const iterators. Persuade it to use the const_iterator.
|
|
||||||
foreach(LLEventDispatcher::NameDesc nd, const_cast<const Dispatcher&>(work))
|
|
||||||
{
|
{
|
||||||
DescMap::iterator found = forgotten.find(nd.first);
|
DescMap::iterator found = forgotten.find(nd.first);
|
||||||
ensure(STRINGIZE("LLEventDispatcher records function '" << nd.first
|
ensure(stringize("LLEventDispatcher records function '", nd.first,
|
||||||
<< "' we didn't enter"),
|
"' we didn't enter"),
|
||||||
found != forgotten.end());
|
found != forgotten.end());
|
||||||
ensure_equals(STRINGIZE("LLEventDispatcher desc '" << nd.second <<
|
ensure_equals(stringize("LLEventDispatcher desc '", nd.second,
|
||||||
"' doesn't match what we entered: '" << found->second << "'"),
|
"' doesn't match what we entered: '", found->second, "'"),
|
||||||
nd.second, found->second);
|
nd.second, found->second);
|
||||||
// found in our map the name from LLEventDispatcher, good, erase
|
// found in our map the name from LLEventDispatcher, good, erase
|
||||||
// our map entry
|
// our map entry
|
||||||
|
|
@ -621,41 +645,49 @@ namespace tut
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << "LLEventDispatcher failed to report";
|
out << "LLEventDispatcher failed to report";
|
||||||
const char* delim = ": ";
|
const char* delim = ": ";
|
||||||
foreach(const DescMap::value_type& fme, forgotten)
|
for (const DescMap::value_type& fme: forgotten)
|
||||||
{
|
{
|
||||||
out << delim << fme.first;
|
out << delim << fme.first;
|
||||||
delim = ", ";
|
delim = ", ";
|
||||||
}
|
}
|
||||||
ensure(out.str(), false);
|
throw failure(out.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vars* varsfor(const std::string& name)
|
Vars* varsfor(const std::string& name)
|
||||||
{
|
{
|
||||||
VarsMap::const_iterator found = funcvars.find(name);
|
VarsMap::const_iterator found = funcvars.find(name);
|
||||||
ensure(STRINGIZE("No Vars* for " << name), found != funcvars.end());
|
ensure(stringize("No Vars* for ", name), found != funcvars.end());
|
||||||
ensure(STRINGIZE("NULL Vars* for " << name), found->second);
|
ensure(stringize("NULL Vars* for ", name), found->second);
|
||||||
return found->second;
|
return found->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ensure_has(const std::string& outer, const std::string& inner)
|
std::string call_exc(const std::string& func, const LLSD& args, const std::string& exc_frag)
|
||||||
{
|
{
|
||||||
ensure(STRINGIZE("'" << outer << "' does not contain '" << inner << "'").c_str(),
|
return tut::call_exc(
|
||||||
outer.find(inner) != std::string::npos);
|
[this, func, args]()
|
||||||
|
{
|
||||||
|
if (func.empty())
|
||||||
|
{
|
||||||
|
work(args);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
work(func, args);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
exc_frag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_exc(const std::string& func, const LLSD& args, const std::string& exc_frag)
|
void call_logerr(const std::string& func, const LLSD& args, const std::string& frag)
|
||||||
{
|
{
|
||||||
std::string threw = catch_what<std::runtime_error>([this, &func, &args](){
|
tut::call_logerr([this, func, args](){ work(func, args); }, frag);
|
||||||
work(func, args);
|
|
||||||
});
|
|
||||||
ensure_has(threw, exc_frag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LLSD getMetadata(const std::string& name)
|
LLSD getMetadata(const std::string& name)
|
||||||
{
|
{
|
||||||
LLSD meta(work.getMetadata(name));
|
LLSD meta(work.getMetadata(name));
|
||||||
ensure(STRINGIZE("No metadata for " << name), meta.isDefined());
|
ensure(stringize("No metadata for ", name), meta.isDefined());
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -724,7 +756,7 @@ namespace tut
|
||||||
set_test_name("map-style registration with non-array params");
|
set_test_name("map-style registration with non-array params");
|
||||||
// Pass "param names" as scalar or as map
|
// Pass "param names" as scalar or as map
|
||||||
LLSD attempts(llsd::array(17, LLSDMap("pi", 3.14)("two", 2)));
|
LLSD attempts(llsd::array(17, LLSDMap("pi", 3.14)("two", 2)));
|
||||||
foreach(LLSD ae, inArray(attempts))
|
for (LLSD ae: inArray(attempts))
|
||||||
{
|
{
|
||||||
std::string threw = catch_what<std::exception>([this, &ae](){
|
std::string threw = catch_what<std::exception>([this, &ae](){
|
||||||
work.add("freena_err", "freena", freena, ae);
|
work.add("freena_err", "freena", freena, ae);
|
||||||
|
|
@ -799,7 +831,7 @@ namespace tut
|
||||||
{
|
{
|
||||||
set_test_name("query Callables with/out required params");
|
set_test_name("query Callables with/out required params");
|
||||||
LLSD names(llsd::array("free1", "Dmethod1", "Dcmethod1", "method1"));
|
LLSD names(llsd::array("free1", "Dmethod1", "Dcmethod1", "method1"));
|
||||||
foreach(LLSD nm, inArray(names))
|
for (LLSD nm: inArray(names))
|
||||||
{
|
{
|
||||||
LLSD metadata(getMetadata(nm));
|
LLSD metadata(getMetadata(nm));
|
||||||
ensure_equals("name mismatch", metadata["name"], nm);
|
ensure_equals("name mismatch", metadata["name"], nm);
|
||||||
|
|
@ -828,19 +860,19 @@ namespace tut
|
||||||
(5, llsd::array("freena_array", "smethodna_array", "methodna_array")),
|
(5, llsd::array("freena_array", "smethodna_array", "methodna_array")),
|
||||||
llsd::array
|
llsd::array
|
||||||
(5, llsd::array("freenb_array", "smethodnb_array", "methodnb_array"))));
|
(5, llsd::array("freenb_array", "smethodnb_array", "methodnb_array"))));
|
||||||
foreach(LLSD ae, inArray(expected))
|
for (LLSD ae: inArray(expected))
|
||||||
{
|
{
|
||||||
LLSD::Integer arity(ae[0].asInteger());
|
LLSD::Integer arity(ae[0].asInteger());
|
||||||
LLSD names(ae[1]);
|
LLSD names(ae[1]);
|
||||||
LLSD req(LLSD::emptyArray());
|
LLSD req(LLSD::emptyArray());
|
||||||
if (arity)
|
if (arity)
|
||||||
req[arity - 1] = LLSD();
|
req[arity - 1] = LLSD();
|
||||||
foreach(LLSD nm, inArray(names))
|
for (LLSD nm: inArray(names))
|
||||||
{
|
{
|
||||||
LLSD metadata(getMetadata(nm));
|
LLSD metadata(getMetadata(nm));
|
||||||
ensure_equals("name mismatch", metadata["name"], nm);
|
ensure_equals("name mismatch", metadata["name"], nm);
|
||||||
ensure_equals(metadata["desc"].asString(), descs[nm]);
|
ensure_equals(metadata["desc"].asString(), descs[nm]);
|
||||||
ensure_equals(STRINGIZE("mismatched required for " << nm.asString()),
|
ensure_equals(stringize("mismatched required for ", nm.asString()),
|
||||||
metadata["required"], req);
|
metadata["required"], req);
|
||||||
ensure("should not have optional", metadata["optional"].isUndefined());
|
ensure("should not have optional", metadata["optional"].isUndefined());
|
||||||
}
|
}
|
||||||
|
|
@ -854,7 +886,7 @@ namespace tut
|
||||||
// - (Free function | non-static method), map style, no params (ergo
|
// - (Free function | non-static method), map style, no params (ergo
|
||||||
// no defaults)
|
// no defaults)
|
||||||
LLSD names(llsd::array("free0_map", "smethod0_map", "method0_map"));
|
LLSD names(llsd::array("free0_map", "smethod0_map", "method0_map"));
|
||||||
foreach(LLSD nm, inArray(names))
|
for (LLSD nm: inArray(names))
|
||||||
{
|
{
|
||||||
LLSD metadata(getMetadata(nm));
|
LLSD metadata(getMetadata(nm));
|
||||||
ensure_equals("name mismatch", metadata["name"], nm);
|
ensure_equals("name mismatch", metadata["name"], nm);
|
||||||
|
|
@ -884,7 +916,7 @@ namespace tut
|
||||||
llsd::array("smethodnb_map_adft", "smethodnb_map_mdft"),
|
llsd::array("smethodnb_map_adft", "smethodnb_map_mdft"),
|
||||||
llsd::array("methodna_map_adft", "methodna_map_mdft"),
|
llsd::array("methodna_map_adft", "methodna_map_mdft"),
|
||||||
llsd::array("methodnb_map_adft", "methodnb_map_mdft")));
|
llsd::array("methodnb_map_adft", "methodnb_map_mdft")));
|
||||||
foreach(LLSD eq, inArray(equivalences))
|
for (LLSD eq: inArray(equivalences))
|
||||||
{
|
{
|
||||||
LLSD adft(eq[0]);
|
LLSD adft(eq[0]);
|
||||||
LLSD mdft(eq[1]);
|
LLSD mdft(eq[1]);
|
||||||
|
|
@ -898,8 +930,8 @@ namespace tut
|
||||||
ensure_equals("mdft name", mdft, mmeta["name"]);
|
ensure_equals("mdft name", mdft, mmeta["name"]);
|
||||||
ameta.erase("name");
|
ameta.erase("name");
|
||||||
mmeta.erase("name");
|
mmeta.erase("name");
|
||||||
ensure_equals(STRINGIZE("metadata for " << adft.asString()
|
ensure_equals(stringize("metadata for ", adft.asString(),
|
||||||
<< " vs. " << mdft.asString()),
|
" vs. ", mdft.asString()),
|
||||||
ameta, mmeta);
|
ameta, mmeta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -915,7 +947,7 @@ namespace tut
|
||||||
// params are required. Also maps containing left requirements for
|
// params are required. Also maps containing left requirements for
|
||||||
// partial defaults arrays. Also defaults maps from defaults arrays.
|
// partial defaults arrays. Also defaults maps from defaults arrays.
|
||||||
LLSD allreq, leftreq, rightdft;
|
LLSD allreq, leftreq, rightdft;
|
||||||
foreach(LLSD::String a, ab)
|
for (LLSD::String a: ab)
|
||||||
{
|
{
|
||||||
// The map in which all params are required uses params[a] as
|
// The map in which all params are required uses params[a] as
|
||||||
// keys, with all isUndefined() as values. We can accomplish that
|
// keys, with all isUndefined() as values. We can accomplish that
|
||||||
|
|
@ -943,9 +975,9 @@ namespace tut
|
||||||
// Generate maps containing parameter names not provided by the
|
// Generate maps containing parameter names not provided by the
|
||||||
// dft_map_partial maps.
|
// dft_map_partial maps.
|
||||||
LLSD skipreq(allreq);
|
LLSD skipreq(allreq);
|
||||||
foreach(LLSD::String a, ab)
|
for (LLSD::String a: ab)
|
||||||
{
|
{
|
||||||
foreach(const MapEntry& me, inMap(dft_map_partial[a]))
|
for (const MapEntry& me: inMap(dft_map_partial[a]))
|
||||||
{
|
{
|
||||||
skipreq[a].erase(me.first);
|
skipreq[a].erase(me.first);
|
||||||
}
|
}
|
||||||
|
|
@ -990,7 +1022,7 @@ namespace tut
|
||||||
(llsd::array("freenb_map_mdft", "smethodnb_map_mdft", "methodnb_map_mdft"),
|
(llsd::array("freenb_map_mdft", "smethodnb_map_mdft", "methodnb_map_mdft"),
|
||||||
llsd::array(LLSD::emptyMap(), dft_map_full["b"])))); // required, optional
|
llsd::array(LLSD::emptyMap(), dft_map_full["b"])))); // required, optional
|
||||||
|
|
||||||
foreach(LLSD grp, inArray(groups))
|
for (LLSD grp: inArray(groups))
|
||||||
{
|
{
|
||||||
// Internal structure of each group in 'groups':
|
// Internal structure of each group in 'groups':
|
||||||
LLSD names(grp[0]);
|
LLSD names(grp[0]);
|
||||||
|
|
@ -1003,14 +1035,14 @@ namespace tut
|
||||||
optional);
|
optional);
|
||||||
|
|
||||||
// Loop through 'names'
|
// Loop through 'names'
|
||||||
foreach(LLSD nm, inArray(names))
|
for (LLSD nm: inArray(names))
|
||||||
{
|
{
|
||||||
LLSD metadata(getMetadata(nm));
|
LLSD metadata(getMetadata(nm));
|
||||||
ensure_equals("name mismatch", metadata["name"], nm);
|
ensure_equals("name mismatch", metadata["name"], nm);
|
||||||
ensure_equals(nm.asString(), metadata["desc"].asString(), descs[nm]);
|
ensure_equals(nm.asString(), metadata["desc"].asString(), descs[nm]);
|
||||||
ensure_equals(STRINGIZE(nm << " required mismatch"),
|
ensure_equals(stringize(nm, " required mismatch"),
|
||||||
metadata["required"], required);
|
metadata["required"], required);
|
||||||
ensure_equals(STRINGIZE(nm << " optional mismatch"),
|
ensure_equals(stringize(nm, " optional mismatch"),
|
||||||
metadata["optional"], optional);
|
metadata["optional"], optional);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1031,13 +1063,7 @@ namespace tut
|
||||||
{
|
{
|
||||||
set_test_name("call with bad name");
|
set_test_name("call with bad name");
|
||||||
call_exc("freek", LLSD(), "not found");
|
call_exc("freek", LLSD(), "not found");
|
||||||
// We don't have a comparable helper function for the one-arg
|
std::string threw = call_exc("", LLSDMap("op", "freek"), "bad");
|
||||||
// operator() method, and it's not worth building one just for this
|
|
||||||
// case. Write it out.
|
|
||||||
std::string threw = catch_what<std::runtime_error>([this](){
|
|
||||||
work(LLSDMap("op", "freek"));
|
|
||||||
});
|
|
||||||
ensure_has(threw, "bad");
|
|
||||||
ensure_has(threw, "op");
|
ensure_has(threw, "op");
|
||||||
ensure_has(threw, "freek");
|
ensure_has(threw, "freek");
|
||||||
}
|
}
|
||||||
|
|
@ -1079,7 +1105,7 @@ namespace tut
|
||||||
// LLSD value matching 'required' according to llsd_matches() rules.
|
// LLSD value matching 'required' according to llsd_matches() rules.
|
||||||
LLSD matching(LLSDMap("d", 3.14)("array", llsd::array("answer", true, answer)));
|
LLSD matching(LLSDMap("d", 3.14)("array", llsd::array("answer", true, answer)));
|
||||||
// Okay, walk through 'tests'.
|
// Okay, walk through 'tests'.
|
||||||
foreach(const CallablesTriple& tr, tests)
|
for (const CallablesTriple& tr: tests)
|
||||||
{
|
{
|
||||||
// Should be able to pass 'answer' to Callables registered
|
// Should be able to pass 'answer' to Callables registered
|
||||||
// without 'required'.
|
// without 'required'.
|
||||||
|
|
@ -1087,7 +1113,7 @@ namespace tut
|
||||||
ensure_equals("answer mismatch", tr.llsd, answer);
|
ensure_equals("answer mismatch", tr.llsd, answer);
|
||||||
// Should NOT be able to pass 'answer' to Callables registered
|
// Should NOT be able to pass 'answer' to Callables registered
|
||||||
// with 'required'.
|
// with 'required'.
|
||||||
call_exc(tr.name_req, answer, "bad request");
|
call_logerr(tr.name_req, answer, "bad request");
|
||||||
// But SHOULD be able to pass 'matching' to Callables registered
|
// But SHOULD be able to pass 'matching' to Callables registered
|
||||||
// with 'required'.
|
// with 'required'.
|
||||||
work(tr.name_req, matching);
|
work(tr.name_req, matching);
|
||||||
|
|
@ -1101,17 +1127,20 @@ namespace tut
|
||||||
set_test_name("passing wrong args to (map | array)-style registrations");
|
set_test_name("passing wrong args to (map | array)-style registrations");
|
||||||
|
|
||||||
// Pass scalar/map to array-style functions, scalar/array to map-style
|
// Pass scalar/map to array-style functions, scalar/array to map-style
|
||||||
// functions. As that validation happens well before we engage the
|
// functions. It seems pointless to repeat this with every variation:
|
||||||
// argument magic, it seems pointless to repeat this with every
|
// (free function | non-static method), (no | arbitrary) args. We
|
||||||
// variation: (free function | non-static method), (no | arbitrary)
|
// should only need to engage it for one map-style registration and
|
||||||
// args. We should only need to engage it for one map-style
|
// one array-style registration.
|
||||||
// registration and one array-style registration.
|
// Now that LLEventDispatcher has been extended to treat an LLSD
|
||||||
std::string array_exc("needs an args array");
|
// scalar as a single-entry array, the error we expect in this case is
|
||||||
call_exc("free0_array", 17, array_exc);
|
// that apply() is trying to pass that non-empty array to a nullary
|
||||||
call_exc("free0_array", LLSDMap("pi", 3.14), array_exc);
|
// function.
|
||||||
|
call_logerr("free0_array", 17, "LL::apply");
|
||||||
|
// similarly, apply() doesn't accept an LLSD Map
|
||||||
|
call_logerr("free0_array", LLSDMap("pi", 3.14), "unsupported");
|
||||||
|
|
||||||
std::string map_exc("needs a map");
|
std::string map_exc("needs a map");
|
||||||
call_exc("free0_map", 17, map_exc);
|
call_logerr("free0_map", 17, map_exc);
|
||||||
// Passing an array to a map-style function works now! No longer an
|
// Passing an array to a map-style function works now! No longer an
|
||||||
// error case!
|
// error case!
|
||||||
// call_exc("free0_map", llsd::array("a", "b"), map_exc);
|
// call_exc("free0_map", llsd::array("a", "b"), map_exc);
|
||||||
|
|
@ -1125,7 +1154,7 @@ namespace tut
|
||||||
("free0_array", "free0_map",
|
("free0_array", "free0_map",
|
||||||
"smethod0_array", "smethod0_map",
|
"smethod0_array", "smethod0_map",
|
||||||
"method0_array", "method0_map"));
|
"method0_array", "method0_map"));
|
||||||
foreach(LLSD name, inArray(names))
|
for (LLSD name: inArray(names))
|
||||||
{
|
{
|
||||||
// Look up the Vars instance for this function.
|
// Look up the Vars instance for this function.
|
||||||
Vars* vars(varsfor(name));
|
Vars* vars(varsfor(name));
|
||||||
|
|
@ -1150,15 +1179,21 @@ namespace tut
|
||||||
template<> template<>
|
template<> template<>
|
||||||
void object::test<19>()
|
void object::test<19>()
|
||||||
{
|
{
|
||||||
set_test_name("call array-style functions with too-short arrays");
|
set_test_name("call array-style functions with wrong-length arrays");
|
||||||
// Could have two different too-short arrays, one for *na and one for
|
// Could have different wrong-length arrays for *na and for *nb, but
|
||||||
// *nb, but since they both take 5 params...
|
// since they both take 5 params...
|
||||||
LLSD tooshort(llsd::array("this", "array", "too", "short"));
|
LLSD tooshort(llsd::array("this", "array", "too", "short"));
|
||||||
foreach(const LLSD& funcsab, inArray(array_funcs))
|
LLSD toolong (llsd::array("this", "array", "is", "one", "too", "long"));
|
||||||
|
LLSD badargs (llsd::array(tooshort, toolong));
|
||||||
|
for (const LLSD& toosomething: inArray(badargs))
|
||||||
{
|
{
|
||||||
foreach(const llsd::MapEntry& e, inMap(funcsab))
|
for (const LLSD& funcsab: inArray(array_funcs))
|
||||||
{
|
{
|
||||||
call_exc(e.second, tooshort, "requires more arguments");
|
for (const llsd::MapEntry& e: inMap(funcsab))
|
||||||
|
{
|
||||||
|
// apply() complains about wrong number of array entries
|
||||||
|
call_logerr(e.second, toosomething, "LL::apply");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1166,7 +1201,7 @@ namespace tut
|
||||||
template<> template<>
|
template<> template<>
|
||||||
void object::test<20>()
|
void object::test<20>()
|
||||||
{
|
{
|
||||||
set_test_name("call array-style functions with (just right | too long) arrays");
|
set_test_name("call array-style functions with right-size arrays");
|
||||||
std::vector<U8> binary;
|
std::vector<U8> binary;
|
||||||
for (size_t h(0x01), i(0); i < 5; h+= 0x22, ++i)
|
for (size_t h(0x01), i(0); i < 5; h+= 0x22, ++i)
|
||||||
{
|
{
|
||||||
|
|
@ -1178,40 +1213,25 @@ namespace tut
|
||||||
LLDate("2011-02-03T15:07:00Z"),
|
LLDate("2011-02-03T15:07:00Z"),
|
||||||
LLURI("http://secondlife.com"),
|
LLURI("http://secondlife.com"),
|
||||||
binary)));
|
binary)));
|
||||||
LLSD argsplus(args);
|
|
||||||
argsplus["a"].append("bogus");
|
|
||||||
argsplus["b"].append("bogus");
|
|
||||||
LLSD expect;
|
LLSD expect;
|
||||||
foreach(LLSD::String a, ab)
|
for (LLSD::String a: ab)
|
||||||
{
|
{
|
||||||
expect[a] = zipmap(params[a], args[a]);
|
expect[a] = zipmap(params[a], args[a]);
|
||||||
}
|
}
|
||||||
// Adjust expect["a"]["cp"] for special Vars::cp treatment.
|
// Adjust expect["a"]["cp"] for special Vars::cp treatment.
|
||||||
expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'";
|
expect["a"]["cp"] = stringize("'", expect["a"]["cp"].asString(), "'");
|
||||||
debug("expect: ", expect);
|
debug("expect: ", expect);
|
||||||
|
|
||||||
// Use substantially the same logic for args and argsplus
|
for (const LLSD& funcsab: inArray(array_funcs))
|
||||||
LLSD argsarrays(llsd::array(args, argsplus));
|
|
||||||
// So i==0 selects 'args', i==1 selects argsplus
|
|
||||||
for (LLSD::Integer i(0), iend(argsarrays.size()); i < iend; ++i)
|
|
||||||
{
|
{
|
||||||
foreach(const LLSD& funcsab, inArray(array_funcs))
|
for (LLSD::String a: ab)
|
||||||
{
|
|
||||||
foreach(LLSD::String a, ab)
|
|
||||||
{
|
{
|
||||||
// Reset the Vars instance before each call
|
// Reset the Vars instance before each call
|
||||||
Vars* vars(varsfor(funcsab[a]));
|
Vars* vars(varsfor(funcsab[a]));
|
||||||
*vars = Vars();
|
*vars = Vars();
|
||||||
work(funcsab[a], argsarrays[i][a]);
|
work(funcsab[a], args[a]);
|
||||||
ensure_llsd(STRINGIZE(funcsab[a].asString() <<
|
ensure_llsd(stringize(funcsab[a].asString(), ": expect[\"", a, "\"] mismatch"),
|
||||||
": expect[\"" << a << "\"] mismatch"),
|
|
||||||
vars->inspect(), expect[a], 7); // 7 bits ~= 2 decimal digits
|
vars->inspect(), expect[a], 7); // 7 bits ~= 2 decimal digits
|
||||||
|
|
||||||
// TODO: in the i==1 or argsplus case, intercept LL_WARNS
|
|
||||||
// output? Even without that, using argsplus verifies that
|
|
||||||
// passing too many args isn't fatal; it works -- but
|
|
||||||
// would be nice to notice the warning too.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1239,7 +1259,7 @@ namespace tut
|
||||||
("a", llsd::array(false, 255, 98.6, 1024.5, "pointer"))
|
("a", llsd::array(false, 255, 98.6, 1024.5, "pointer"))
|
||||||
("b", llsd::array("object", LLUUID::generateNewID(), LLDate::now(), LLURI("http://wiki.lindenlab.com/wiki"), LLSD::Binary(boost::begin(binary), boost::end(binary)))));
|
("b", llsd::array("object", LLUUID::generateNewID(), LLDate::now(), LLURI("http://wiki.lindenlab.com/wiki"), LLSD::Binary(boost::begin(binary), boost::end(binary)))));
|
||||||
LLSD array_overfull(array_full);
|
LLSD array_overfull(array_full);
|
||||||
foreach(LLSD::String a, ab)
|
for (LLSD::String a: ab)
|
||||||
{
|
{
|
||||||
array_overfull[a].append("bogus");
|
array_overfull[a].append("bogus");
|
||||||
}
|
}
|
||||||
|
|
@ -1253,7 +1273,7 @@ namespace tut
|
||||||
ensure_not_equals("UUID collision",
|
ensure_not_equals("UUID collision",
|
||||||
array_full["b"][1].asUUID(), dft_array_full["b"][1].asUUID());
|
array_full["b"][1].asUUID(), dft_array_full["b"][1].asUUID());
|
||||||
LLSD map_full, map_overfull;
|
LLSD map_full, map_overfull;
|
||||||
foreach(LLSD::String a, ab)
|
for (LLSD::String a: ab)
|
||||||
{
|
{
|
||||||
map_full[a] = zipmap(params[a], array_full[a]);
|
map_full[a] = zipmap(params[a], array_full[a]);
|
||||||
map_overfull[a] = map_full[a];
|
map_overfull[a] = map_full[a];
|
||||||
|
|
@ -1294,21 +1314,360 @@ namespace tut
|
||||||
"freenb_map_mdft", "smethodnb_map_mdft", "methodnb_map_mdft")));
|
"freenb_map_mdft", "smethodnb_map_mdft", "methodnb_map_mdft")));
|
||||||
// Treat (full | overfull) (array | map) the same.
|
// Treat (full | overfull) (array | map) the same.
|
||||||
LLSD argssets(llsd::array(array_full, array_overfull, map_full, map_overfull));
|
LLSD argssets(llsd::array(array_full, array_overfull, map_full, map_overfull));
|
||||||
foreach(const LLSD& args, inArray(argssets))
|
for (const LLSD& args: inArray(argssets))
|
||||||
{
|
{
|
||||||
foreach(LLSD::String a, ab)
|
for (LLSD::String a: ab)
|
||||||
{
|
{
|
||||||
foreach(LLSD::String name, inArray(names[a]))
|
for (LLSD::String name: inArray(names[a]))
|
||||||
{
|
{
|
||||||
// Reset the Vars instance
|
// Reset the Vars instance
|
||||||
Vars* vars(varsfor(name));
|
Vars* vars(varsfor(name));
|
||||||
*vars = Vars();
|
*vars = Vars();
|
||||||
work(name, args[a]);
|
work(name, args[a]);
|
||||||
ensure_llsd(STRINGIZE(name << ": expect[\"" << a << "\"] mismatch"),
|
ensure_llsd(stringize(name, ": expect[\"", a, "\"] mismatch"),
|
||||||
vars->inspect(), expect[a], 7); // 7 bits, 2 decimal digits
|
vars->inspect(), expect[a], 7); // 7 bits, 2 decimal digits
|
||||||
// intercept LL_WARNS for the two overfull cases?
|
// intercept LL_WARNS for the two overfull cases?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DispatchResult: public LLDispatchListener
|
||||||
|
{
|
||||||
|
using DR = DispatchResult;
|
||||||
|
|
||||||
|
DispatchResult(): LLDispatchListener("results", "op")
|
||||||
|
{
|
||||||
|
add("strfunc", "return string", &DR::strfunc);
|
||||||
|
add("voidfunc", "void function", &DR::voidfunc);
|
||||||
|
add("emptyfunc", "return empty LLSD", &DR::emptyfunc);
|
||||||
|
add("intfunc", "return Integer LLSD", &DR::intfunc);
|
||||||
|
add("llsdfunc", "return passed LLSD", &DR::llsdfunc);
|
||||||
|
add("mapfunc", "return map LLSD", &DR::mapfunc);
|
||||||
|
add("arrayfunc", "return array LLSD", &DR::arrayfunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string strfunc(const std::string& str) const { return "got " + str; }
|
||||||
|
void voidfunc() const {}
|
||||||
|
LLSD emptyfunc() const { return {}; }
|
||||||
|
int intfunc(int i) const { return -i; }
|
||||||
|
LLSD llsdfunc(const LLSD& event) const
|
||||||
|
{
|
||||||
|
LLSD result{ event };
|
||||||
|
result["with"] = "string";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
LLSD mapfunc(int i, const std::string& str) const
|
||||||
|
{
|
||||||
|
return llsd::map("i", intfunc(i), "str", strfunc(str));
|
||||||
|
}
|
||||||
|
LLSD arrayfunc(int i, const std::string& str) const
|
||||||
|
{
|
||||||
|
return llsd::array(intfunc(i), strfunc(str));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<23>()
|
||||||
|
{
|
||||||
|
set_test_name("string result");
|
||||||
|
DispatchResult service;
|
||||||
|
LLSD result{ service("strfunc", "a string") };
|
||||||
|
ensure_equals("strfunc() mismatch", result.asString(), "got a string");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<24>()
|
||||||
|
{
|
||||||
|
set_test_name("void result");
|
||||||
|
DispatchResult service;
|
||||||
|
LLSD result{ service("voidfunc", LLSD()) };
|
||||||
|
ensure("voidfunc() returned defined", result.isUndefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<25>()
|
||||||
|
{
|
||||||
|
set_test_name("Integer result");
|
||||||
|
DispatchResult service;
|
||||||
|
LLSD result{ service("intfunc", -17) };
|
||||||
|
ensure_equals("intfunc() mismatch", result.asInteger(), 17);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<26>()
|
||||||
|
{
|
||||||
|
set_test_name("LLSD echo");
|
||||||
|
DispatchResult service;
|
||||||
|
LLSD result{ service("llsdfunc", llsd::map("op", "llsdfunc", "reqid", 17)) };
|
||||||
|
ensure_equals("llsdfunc() mismatch", result,
|
||||||
|
llsd::map("op", "llsdfunc", "reqid", 17, "with", "string"));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<27>()
|
||||||
|
{
|
||||||
|
set_test_name("map LLSD result");
|
||||||
|
DispatchResult service;
|
||||||
|
LLSD result{ service("mapfunc", llsd::array(-12, "value")) };
|
||||||
|
ensure_equals("mapfunc() mismatch", result, llsd::map("i", 12, "str", "got value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<28>()
|
||||||
|
{
|
||||||
|
set_test_name("array LLSD result");
|
||||||
|
DispatchResult service;
|
||||||
|
LLSD result{ service("arrayfunc", llsd::array(-8, "word")) };
|
||||||
|
ensure_equals("arrayfunc() mismatch", result, llsd::array(8, "got word"));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<29>()
|
||||||
|
{
|
||||||
|
set_test_name("listener error, no reply");
|
||||||
|
DispatchResult service;
|
||||||
|
tut::call_exc(
|
||||||
|
[&service]()
|
||||||
|
{ service.post(llsd::map("op", "nosuchfunc", "reqid", 17)); },
|
||||||
|
"nosuchfunc");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<30>()
|
||||||
|
{
|
||||||
|
set_test_name("listener error with reply");
|
||||||
|
DispatchResult service;
|
||||||
|
LLCaptureListener<LLSD> result;
|
||||||
|
service.post(llsd::map("op", "nosuchfunc", "reqid", 17, "reply", result.getName()));
|
||||||
|
LLSD reply{ result.get() };
|
||||||
|
ensure("no reply", reply.isDefined());
|
||||||
|
ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17);
|
||||||
|
ensure_has(reply["error"].asString(), "nosuchfunc");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<31>()
|
||||||
|
{
|
||||||
|
set_test_name("listener call to void function");
|
||||||
|
DispatchResult service;
|
||||||
|
LLCaptureListener<LLSD> result;
|
||||||
|
result.set("non-empty");
|
||||||
|
for (const auto& func: StringVec{ "voidfunc", "emptyfunc" })
|
||||||
|
{
|
||||||
|
service.post(llsd::map(
|
||||||
|
"op", func,
|
||||||
|
"reqid", 17,
|
||||||
|
"reply", result.getName()));
|
||||||
|
ensure_equals("reply from " + func, result.get().asString(), "non-empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<32>()
|
||||||
|
{
|
||||||
|
set_test_name("listener call to string function");
|
||||||
|
DispatchResult service;
|
||||||
|
LLCaptureListener<LLSD> result;
|
||||||
|
service.post(llsd::map(
|
||||||
|
"op", "strfunc",
|
||||||
|
"args", llsd::array("a string"),
|
||||||
|
"reqid", 17,
|
||||||
|
"reply", result.getName()));
|
||||||
|
LLSD reply{ result.get() };
|
||||||
|
ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17);
|
||||||
|
ensure_equals("bad reply from strfunc", reply["data"].asString(), "got a string");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<33>()
|
||||||
|
{
|
||||||
|
set_test_name("listener call to map function");
|
||||||
|
DispatchResult service;
|
||||||
|
LLCaptureListener<LLSD> result;
|
||||||
|
service.post(llsd::map(
|
||||||
|
"op", "mapfunc",
|
||||||
|
"args", llsd::array(-7, "value"),
|
||||||
|
"reqid", 17,
|
||||||
|
"reply", result.getName()));
|
||||||
|
LLSD reply{ result.get() };
|
||||||
|
ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17);
|
||||||
|
ensure_equals("bad i from mapfunc", reply["i"].asInteger(), 7);
|
||||||
|
ensure_equals("bad str from mapfunc", reply["str"], "got value");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<34>()
|
||||||
|
{
|
||||||
|
set_test_name("batched map success");
|
||||||
|
DispatchResult service;
|
||||||
|
LLCaptureListener<LLSD> result;
|
||||||
|
service.post(llsd::map(
|
||||||
|
"op", llsd::map(
|
||||||
|
"strfunc", "some string",
|
||||||
|
"intfunc", 2,
|
||||||
|
"voidfunc", LLSD(),
|
||||||
|
"arrayfunc", llsd::array(-5, "other string")),
|
||||||
|
"reqid", 17,
|
||||||
|
"reply", result.getName()));
|
||||||
|
LLSD reply{ result.get() };
|
||||||
|
ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17);
|
||||||
|
reply.erase("reqid");
|
||||||
|
ensure_equals(
|
||||||
|
"bad map batch",
|
||||||
|
reply,
|
||||||
|
llsd::map(
|
||||||
|
"strfunc", "got some string",
|
||||||
|
"intfunc", -2,
|
||||||
|
"voidfunc", LLSD(),
|
||||||
|
"arrayfunc", llsd::array(5, "got other string")));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<35>()
|
||||||
|
{
|
||||||
|
set_test_name("batched map error");
|
||||||
|
DispatchResult service;
|
||||||
|
LLCaptureListener<LLSD> result;
|
||||||
|
service.post(llsd::map(
|
||||||
|
"op", llsd::map(
|
||||||
|
"badfunc", 34, // !
|
||||||
|
"strfunc", "some string",
|
||||||
|
"intfunc", 2,
|
||||||
|
"missing", LLSD(), // !
|
||||||
|
"voidfunc", LLSD(),
|
||||||
|
"arrayfunc", llsd::array(-5, "other string")),
|
||||||
|
"reqid", 17,
|
||||||
|
"reply", result.getName()));
|
||||||
|
LLSD reply{ result.get() };
|
||||||
|
ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17);
|
||||||
|
reply.erase("reqid");
|
||||||
|
auto error{ reply["error"].asString() };
|
||||||
|
reply.erase("error");
|
||||||
|
ensure_has(error, "badfunc");
|
||||||
|
ensure_has(error, "missing");
|
||||||
|
ensure_equals(
|
||||||
|
"bad partial batch",
|
||||||
|
reply,
|
||||||
|
llsd::map(
|
||||||
|
"strfunc", "got some string",
|
||||||
|
"intfunc", -2,
|
||||||
|
"voidfunc", LLSD(),
|
||||||
|
"arrayfunc", llsd::array(5, "got other string")));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<36>()
|
||||||
|
{
|
||||||
|
set_test_name("batched map exception");
|
||||||
|
DispatchResult service;
|
||||||
|
auto error = tut::call_exc(
|
||||||
|
[&service]()
|
||||||
|
{
|
||||||
|
service.post(llsd::map(
|
||||||
|
"op", llsd::map(
|
||||||
|
"badfunc", 34, // !
|
||||||
|
"strfunc", "some string",
|
||||||
|
"intfunc", 2,
|
||||||
|
"missing", LLSD(), // !
|
||||||
|
"voidfunc", LLSD(),
|
||||||
|
"arrayfunc", llsd::array(-5, "other string")),
|
||||||
|
"reqid", 17));
|
||||||
|
// no "reply"
|
||||||
|
},
|
||||||
|
"badfunc");
|
||||||
|
ensure_has(error, "missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<37>()
|
||||||
|
{
|
||||||
|
set_test_name("batched array success");
|
||||||
|
DispatchResult service;
|
||||||
|
LLCaptureListener<LLSD> result;
|
||||||
|
service.post(llsd::map(
|
||||||
|
"op", llsd::array(
|
||||||
|
llsd::array("strfunc", "some string"),
|
||||||
|
llsd::array("intfunc", 2),
|
||||||
|
"arrayfunc",
|
||||||
|
"voidfunc"),
|
||||||
|
"args", llsd::array(
|
||||||
|
LLSD(),
|
||||||
|
LLSD(),
|
||||||
|
llsd::array(-5, "other string")),
|
||||||
|
// args array deliberately short, since the default
|
||||||
|
// [3] is undefined, which should work for voidfunc
|
||||||
|
"reqid", 17,
|
||||||
|
"reply", result.getName()));
|
||||||
|
LLSD reply{ result.get() };
|
||||||
|
ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17);
|
||||||
|
reply.erase("reqid");
|
||||||
|
ensure_equals(
|
||||||
|
"bad array batch",
|
||||||
|
reply,
|
||||||
|
llsd::map(
|
||||||
|
"data", llsd::array(
|
||||||
|
"got some string",
|
||||||
|
-2,
|
||||||
|
llsd::array(5, "got other string"),
|
||||||
|
LLSD())));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<38>()
|
||||||
|
{
|
||||||
|
set_test_name("batched array error");
|
||||||
|
DispatchResult service;
|
||||||
|
LLCaptureListener<LLSD> result;
|
||||||
|
service.post(llsd::map(
|
||||||
|
"op", llsd::array(
|
||||||
|
llsd::array("strfunc", "some string"),
|
||||||
|
llsd::array("intfunc", 2, "whoops"), // bad form
|
||||||
|
"arrayfunc",
|
||||||
|
"voidfunc"),
|
||||||
|
"args", llsd::array(
|
||||||
|
LLSD(),
|
||||||
|
LLSD(),
|
||||||
|
llsd::array(-5, "other string")),
|
||||||
|
// args array deliberately short, since the default
|
||||||
|
// [3] is undefined, which should work for voidfunc
|
||||||
|
"reqid", 17,
|
||||||
|
"reply", result.getName()));
|
||||||
|
LLSD reply{ result.get() };
|
||||||
|
ensure_equals("reqid not echoed", reply["reqid"].asInteger(), 17);
|
||||||
|
reply.erase("reqid");
|
||||||
|
auto error{ reply["error"] };
|
||||||
|
reply.erase("error");
|
||||||
|
ensure_has(error, "[1]");
|
||||||
|
ensure_has(error, "unsupported");
|
||||||
|
ensure_equals("bad array batch", reply,
|
||||||
|
llsd::map("data", llsd::array("got some string")));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void object::test<39>()
|
||||||
|
{
|
||||||
|
set_test_name("batched array exception");
|
||||||
|
DispatchResult service;
|
||||||
|
auto error = tut::call_exc(
|
||||||
|
[&service]()
|
||||||
|
{
|
||||||
|
service.post(llsd::map(
|
||||||
|
"op", llsd::array(
|
||||||
|
llsd::array("strfunc", "some string"),
|
||||||
|
llsd::array("intfunc", 2, "whoops"), // bad form
|
||||||
|
"arrayfunc",
|
||||||
|
"voidfunc"),
|
||||||
|
"args", llsd::array(
|
||||||
|
LLSD(),
|
||||||
|
LLSD(),
|
||||||
|
llsd::array(-5, "other string")),
|
||||||
|
// args array deliberately short, since the default
|
||||||
|
// [3] is undefined, which should work for voidfunc
|
||||||
|
"reqid", 17));
|
||||||
|
// no "reply"
|
||||||
|
},
|
||||||
|
"[1]");
|
||||||
|
ensure_has(error, "unsupported");
|
||||||
|
}
|
||||||
} // namespace tut
|
} // namespace tut
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,11 @@ public:
|
||||||
return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->streamto(out);
|
return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->streamto(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
friend inline std::ostream& operator<<(std::ostream& out, const CaptureLog& self)
|
||||||
|
{
|
||||||
|
return self.streamto(out);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LLError::FatalFunction mFatalFunction;
|
LLError::FatalFunction mFatalFunction;
|
||||||
LLError::SettingsStoragePtr mOldSettings;
|
LLError::SettingsStoragePtr mOldSettings;
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ void LLCocoaPlugin::processEvents()
|
||||||
{
|
{
|
||||||
// Some plugins (webkit at least) will want an event loop. This qualifies.
|
// Some plugins (webkit at least) will want an event loop. This qualifies.
|
||||||
NSEvent * event;
|
NSEvent * event;
|
||||||
event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
|
event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
|
||||||
[NSApp sendEvent: event];
|
[NSApp sendEvent: event];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,8 @@ void LLGLTexture::setBoostLevel(S32 level)
|
||||||
{
|
{
|
||||||
mBoostLevel = level ;
|
mBoostLevel = level ;
|
||||||
if(mBoostLevel != LLGLTexture::BOOST_NONE
|
if(mBoostLevel != LLGLTexture::BOOST_NONE
|
||||||
&& mBoostLevel != LLGLTexture::BOOST_ICON)
|
&& mBoostLevel != LLGLTexture::BOOST_ICON
|
||||||
|
&& mBoostLevel != LLGLTexture::BOOST_THUMBNAIL)
|
||||||
{
|
{
|
||||||
setNoDelete() ;
|
setNoDelete() ;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ public:
|
||||||
BOOST_SUPER_HIGH , //textures higher than this need to be downloaded at the required resolution without delay.
|
BOOST_SUPER_HIGH , //textures higher than this need to be downloaded at the required resolution without delay.
|
||||||
BOOST_HUD ,
|
BOOST_HUD ,
|
||||||
BOOST_ICON ,
|
BOOST_ICON ,
|
||||||
|
BOOST_THUMBNAIL ,
|
||||||
BOOST_UI ,
|
BOOST_UI ,
|
||||||
BOOST_PREVIEW ,
|
BOOST_PREVIEW ,
|
||||||
BOOST_MAP ,
|
BOOST_MAP ,
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,8 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
|
||||||
mHighlightColor(p.highlight_color()),
|
mHighlightColor(p.highlight_color()),
|
||||||
mPreeditBgColor(p.preedit_bg_color()),
|
mPreeditBgColor(p.preedit_bg_color()),
|
||||||
mGLFont(p.font),
|
mGLFont(p.font),
|
||||||
mContextMenuHandle()
|
mContextMenuHandle(),
|
||||||
|
mShowContextMenu(true)
|
||||||
{
|
{
|
||||||
llassert( mMaxLengthBytes > 0 );
|
llassert( mMaxLengthBytes > 0 );
|
||||||
|
|
||||||
|
|
@ -827,7 +828,7 @@ BOOL LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
|
||||||
BOOL LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask)
|
BOOL LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask)
|
||||||
{
|
{
|
||||||
setFocus(TRUE);
|
setFocus(TRUE);
|
||||||
if (!LLUICtrl::handleRightMouseDown(x, y, mask))
|
if (!LLUICtrl::handleRightMouseDown(x, y, mask) && getShowContextMenu())
|
||||||
{
|
{
|
||||||
showContextMenu(x, y);
|
showContextMenu(x, y);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -287,6 +287,9 @@ public:
|
||||||
void setBgImage(LLPointer<LLUIImage> image) { mBgImage = image; }
|
void setBgImage(LLPointer<LLUIImage> image) { mBgImage = image; }
|
||||||
void setBgImageFocused(LLPointer<LLUIImage> image) { mBgImageFocused = image; }
|
void setBgImageFocused(LLPointer<LLUIImage> image) { mBgImageFocused = image; }
|
||||||
|
|
||||||
|
void setShowContextMenu(bool show) { mShowContextMenu = show; }
|
||||||
|
bool getShowContextMenu() const { return mShowContextMenu; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// private helper methods
|
// private helper methods
|
||||||
|
|
||||||
|
|
@ -407,6 +410,8 @@ protected:
|
||||||
|
|
||||||
LLHandle<LLContextMenu> mContextMenuHandle;
|
LLHandle<LLContextMenu> mContextMenuHandle;
|
||||||
|
|
||||||
|
bool mShowContextMenu;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Instances that by default point to the statics but can be overidden in XML.
|
// Instances that by default point to the statics but can be overidden in XML.
|
||||||
LLPointer<LLUIImage> mBgImage;
|
LLPointer<LLUIImage> mBgImage;
|
||||||
|
|
|
||||||
|
|
@ -2151,12 +2151,17 @@ void LLTabContainer::commitHoveredButton(S32 x, S32 y)
|
||||||
{
|
{
|
||||||
for (tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
for (tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
||||||
{
|
{
|
||||||
LLTabTuple* tuple = *iter;
|
LLButton* button = (*iter)->mButton;
|
||||||
S32 local_x = x - tuple->mButton->getRect().mLeft;
|
LLPanel* panel = (*iter)->mTabPanel;
|
||||||
S32 local_y = y - tuple->mButton->getRect().mBottom;
|
if (button->getEnabled() && button->getVisible() && !panel->getVisible())
|
||||||
if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
|
|
||||||
{
|
{
|
||||||
tuple->mButton->onCommit();
|
S32 local_x = x - button->getRect().mLeft;
|
||||||
|
S32 local_y = y - button->getRect().mBottom;
|
||||||
|
if (button->pointInView(local_x, local_y))
|
||||||
|
{
|
||||||
|
button->onCommit();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1578,7 +1578,13 @@ S32 LLTextBase::getLeftOffset(S32 width)
|
||||||
case LLFontGL::HCENTER:
|
case LLFontGL::HCENTER:
|
||||||
return mHPad + llmax(0, (mVisibleTextRect.getWidth() - width - mHPad) / 2);
|
return mHPad + llmax(0, (mVisibleTextRect.getWidth() - width - mHPad) / 2);
|
||||||
case LLFontGL::RIGHT:
|
case LLFontGL::RIGHT:
|
||||||
return mVisibleTextRect.getWidth() - width;
|
{
|
||||||
|
// Font's rendering rounds string size, if value gets rounded
|
||||||
|
// down last symbol might not have enough space to render,
|
||||||
|
// compensate by adding an extra pixel as padding
|
||||||
|
const S32 right_padding = 1;
|
||||||
|
return llmax(mHPad, mVisibleTextRect.getWidth() - width - right_padding);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return mHPad;
|
return mHPad;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,9 @@
|
||||||
|
|
||||||
#include "llavatarnamecache.h"
|
#include "llavatarnamecache.h"
|
||||||
#include "llcachename.h"
|
#include "llcachename.h"
|
||||||
|
#include "llkeyboard.h"
|
||||||
#include "llregex.h"
|
#include "llregex.h"
|
||||||
|
#include "llscrolllistctrl.h" // for LLUrlEntryKeybinding file parsing
|
||||||
#include "lltrans.h"
|
#include "lltrans.h"
|
||||||
#include "lluicolortable.h"
|
#include "lluicolortable.h"
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
|
|
@ -1609,3 +1611,122 @@ std::string LLUrlEntryIPv6::getUrl(const std::string &string) const
|
||||||
{
|
{
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// LLUrlEntryKeybinding Displays currently assigned key
|
||||||
|
//
|
||||||
|
LLUrlEntryKeybinding::LLUrlEntryKeybinding()
|
||||||
|
: LLUrlEntryBase()
|
||||||
|
, pHandler(NULL)
|
||||||
|
{
|
||||||
|
mPattern = boost::regex(APP_HEADER_REGEX "/keybinding/\\w+(\\?mode=\\w+)?$",
|
||||||
|
boost::regex::perl | boost::regex::icase);
|
||||||
|
mMenuName = "menu_url_experience.xml";
|
||||||
|
|
||||||
|
initLocalization();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LLUrlEntryKeybinding::getLabel(const std::string& url, const LLUrlLabelCallback& cb)
|
||||||
|
{
|
||||||
|
std::string control = getControlName(url);
|
||||||
|
|
||||||
|
std::map<std::string, LLLocalizationData>::iterator iter = mLocalizations.find(control);
|
||||||
|
|
||||||
|
std::string keybind;
|
||||||
|
if (pHandler)
|
||||||
|
{
|
||||||
|
keybind = pHandler->getKeyBindingAsString(getMode(url), control);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iter != mLocalizations.end())
|
||||||
|
{
|
||||||
|
return iter->second.mLocalization + ": " + keybind;
|
||||||
|
}
|
||||||
|
|
||||||
|
return control + ": " + keybind;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LLUrlEntryKeybinding::getTooltip(const std::string& url) const
|
||||||
|
{
|
||||||
|
std::string control = getControlName(url);
|
||||||
|
|
||||||
|
std::map<std::string, LLLocalizationData>::const_iterator iter = mLocalizations.find(control);
|
||||||
|
if (iter != mLocalizations.end())
|
||||||
|
{
|
||||||
|
return iter->second.mTooltip;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LLUrlEntryKeybinding::getControlName(const std::string& url) const
|
||||||
|
{
|
||||||
|
std::string search = "/keybinding/";
|
||||||
|
size_t pos_start = url.find(search);
|
||||||
|
if (pos_start == std::string::npos)
|
||||||
|
{
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
pos_start += search.size();
|
||||||
|
|
||||||
|
size_t pos_end = url.find("?mode=");
|
||||||
|
if (pos_end == std::string::npos)
|
||||||
|
{
|
||||||
|
pos_end = url.size();
|
||||||
|
}
|
||||||
|
return url.substr(pos_start, pos_end - pos_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LLUrlEntryKeybinding::getMode(const std::string& url) const
|
||||||
|
{
|
||||||
|
std::string search = "?mode=";
|
||||||
|
size_t pos_start = url.find(search);
|
||||||
|
if (pos_start == std::string::npos)
|
||||||
|
{
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
pos_start += search.size();
|
||||||
|
return url.substr(pos_start, url.size() - pos_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLUrlEntryKeybinding::initLocalization()
|
||||||
|
{
|
||||||
|
initLocalizationFromFile("control_table_contents_movement.xml");
|
||||||
|
initLocalizationFromFile("control_table_contents_camera.xml");
|
||||||
|
initLocalizationFromFile("control_table_contents_editing.xml");
|
||||||
|
initLocalizationFromFile("control_table_contents_media.xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLUrlEntryKeybinding::initLocalizationFromFile(const std::string& filename)
|
||||||
|
{
|
||||||
|
LLXMLNodePtr xmlNode;
|
||||||
|
LLScrollListCtrl::Contents contents;
|
||||||
|
if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode))
|
||||||
|
{
|
||||||
|
LL_WARNS() << "Failed to load " << filename << LL_ENDL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LLXUIParser parser;
|
||||||
|
parser.readXUI(xmlNode, contents, filename);
|
||||||
|
|
||||||
|
if (!contents.validateBlock())
|
||||||
|
{
|
||||||
|
LL_WARNS() << "Failed to validate " << filename << LL_ENDL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (LLInitParam::ParamIterator<LLScrollListItem::Params>::const_iterator row_it = contents.rows.begin();
|
||||||
|
row_it != contents.rows.end();
|
||||||
|
++row_it)
|
||||||
|
{
|
||||||
|
std::string control = row_it->value.getValue().asString();
|
||||||
|
if (!control.empty() && control != "menu_separator")
|
||||||
|
{
|
||||||
|
mLocalizations[control] =
|
||||||
|
LLLocalizationData(
|
||||||
|
row_it->columns.begin()->value.getValue().asString(),
|
||||||
|
row_it->columns.begin()->tool_tip.getValue()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -550,4 +550,37 @@ public:
|
||||||
std::string mHostPath;
|
std::string mHostPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LLKeyBindingToStringHandler;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// LLUrlEntryKeybinding A way to access keybindings and show currently used one in text.
|
||||||
|
/// secondlife:///app/keybinding/control_name
|
||||||
|
class LLUrlEntryKeybinding: public LLUrlEntryBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLUrlEntryKeybinding();
|
||||||
|
/*virtual*/ std::string getLabel(const std::string& url, const LLUrlLabelCallback& cb);
|
||||||
|
/*virtual*/ std::string getTooltip(const std::string& url) const;
|
||||||
|
void setHandler(LLKeyBindingToStringHandler* handler) {pHandler = handler;}
|
||||||
|
private:
|
||||||
|
std::string getControlName(const std::string& url) const;
|
||||||
|
std::string getMode(const std::string& url) const;
|
||||||
|
void initLocalization();
|
||||||
|
void initLocalizationFromFile(const std::string& filename);
|
||||||
|
|
||||||
|
struct LLLocalizationData
|
||||||
|
{
|
||||||
|
LLLocalizationData() {}
|
||||||
|
LLLocalizationData(const std::string& localization, const std::string& tooltip)
|
||||||
|
: mLocalization(localization)
|
||||||
|
, mTooltip(tooltip)
|
||||||
|
{}
|
||||||
|
std::string mLocalization;
|
||||||
|
std::string mTooltip;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<std::string, LLLocalizationData> mLocalizations;
|
||||||
|
LLKeyBindingToStringHandler* pHandler;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,8 @@ LLUrlRegistry::LLUrlRegistry()
|
||||||
registerUrl(new LLUrlEntryPlace());
|
registerUrl(new LLUrlEntryPlace());
|
||||||
registerUrl(new LLUrlEntryInventory());
|
registerUrl(new LLUrlEntryInventory());
|
||||||
registerUrl(new LLUrlEntryExperienceProfile());
|
registerUrl(new LLUrlEntryExperienceProfile());
|
||||||
|
mUrlEntryKeybinding = new LLUrlEntryKeybinding();
|
||||||
|
registerUrl(mUrlEntryKeybinding);
|
||||||
//LLUrlEntrySL and LLUrlEntrySLLabel have more common pattern,
|
//LLUrlEntrySL and LLUrlEntrySLLabel have more common pattern,
|
||||||
//so it should be registered in the end of list
|
//so it should be registered in the end of list
|
||||||
registerUrl(new LLUrlEntrySL());
|
registerUrl(new LLUrlEntrySL());
|
||||||
|
|
@ -307,3 +309,9 @@ bool LLUrlRegistry::isUrl(const LLWString &text)
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LLUrlRegistry::setKeybindingHandler(LLKeyBindingToStringHandler* handler)
|
||||||
|
{
|
||||||
|
LLUrlEntryKeybinding *entry = (LLUrlEntryKeybinding*)mUrlEntryKeybinding;
|
||||||
|
entry->setHandler(handler);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class LLKeyBindingToStringHandler;
|
||||||
|
|
||||||
/// This default callback for findUrl() simply ignores any label updates
|
/// This default callback for findUrl() simply ignores any label updates
|
||||||
void LLUrlRegistryNullCallback(const std::string &url,
|
void LLUrlRegistryNullCallback(const std::string &url,
|
||||||
const std::string &label,
|
const std::string &label,
|
||||||
|
|
@ -88,6 +90,9 @@ public:
|
||||||
bool isUrl(const std::string &text);
|
bool isUrl(const std::string &text);
|
||||||
bool isUrl(const LLWString &text);
|
bool isUrl(const LLWString &text);
|
||||||
|
|
||||||
|
// Set handler for url registry to be capable of parsing and populating keybindings
|
||||||
|
void setKeybindingHandler(LLKeyBindingToStringHandler* handler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<LLUrlEntryBase *> mUrlEntry;
|
std::vector<LLUrlEntryBase *> mUrlEntry;
|
||||||
LLUrlEntryBase* mUrlEntryTrusted;
|
LLUrlEntryBase* mUrlEntryTrusted;
|
||||||
|
|
@ -96,6 +101,7 @@ private:
|
||||||
LLUrlEntryBase* mUrlEntryHTTPLabel;
|
LLUrlEntryBase* mUrlEntryHTTPLabel;
|
||||||
LLUrlEntryBase* mUrlEntrySLLabel;
|
LLUrlEntryBase* mUrlEntrySLLabel;
|
||||||
LLUrlEntryBase* mUrlEntryNoLink;
|
LLUrlEntryBase* mUrlEntryNoLink;
|
||||||
|
LLUrlEntryBase* mUrlEntryKeybinding;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -366,6 +366,45 @@ std::string LLKeyboard::stringFromKey(KEY key, bool translate)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
std::string LLKeyboard::stringFromMouse(EMouseClickType click, bool translate)
|
||||||
|
{
|
||||||
|
std::string res;
|
||||||
|
switch (click)
|
||||||
|
{
|
||||||
|
case CLICK_LEFT:
|
||||||
|
res = "LMB";
|
||||||
|
break;
|
||||||
|
case CLICK_MIDDLE:
|
||||||
|
res = "MMB";
|
||||||
|
break;
|
||||||
|
case CLICK_RIGHT:
|
||||||
|
res = "RMB";
|
||||||
|
break;
|
||||||
|
case CLICK_BUTTON4:
|
||||||
|
res = "MB4";
|
||||||
|
break;
|
||||||
|
case CLICK_BUTTON5:
|
||||||
|
res = "MB5";
|
||||||
|
break;
|
||||||
|
case CLICK_DOUBLELEFT:
|
||||||
|
res = "Double LMB";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (translate && !res.empty())
|
||||||
|
{
|
||||||
|
LLKeyStringTranslatorFunc* trans = gKeyboard->mStringTranslator;
|
||||||
|
if (trans != NULL)
|
||||||
|
{
|
||||||
|
res = trans(res.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
//static
|
//static
|
||||||
std::string LLKeyboard::stringFromAccelerator(MASK accel_mask)
|
std::string LLKeyboard::stringFromAccelerator(MASK accel_mask)
|
||||||
{
|
{
|
||||||
|
|
@ -433,6 +472,18 @@ std::string LLKeyboard::stringFromAccelerator( MASK accel_mask, KEY key )
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
std::string LLKeyboard::stringFromAccelerator(MASK accel_mask, EMouseClickType click)
|
||||||
|
{
|
||||||
|
std::string res;
|
||||||
|
if (CLICK_NONE == click)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
res.append(stringFromAccelerator(accel_mask));
|
||||||
|
res.append(stringFromMouse(click));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
//static
|
//static
|
||||||
BOOL LLKeyboard::maskFromString(const std::string& str, MASK *mask)
|
BOOL LLKeyboard::maskFromString(const std::string& str, MASK *mask)
|
||||||
|
|
|
||||||
|
|
@ -96,8 +96,10 @@ public:
|
||||||
static BOOL maskFromString(const std::string& str, MASK *mask); // False on failure
|
static BOOL maskFromString(const std::string& str, MASK *mask); // False on failure
|
||||||
static BOOL keyFromString(const std::string& str, KEY *key); // False on failure
|
static BOOL keyFromString(const std::string& str, KEY *key); // False on failure
|
||||||
static std::string stringFromKey(KEY key, bool translate = true);
|
static std::string stringFromKey(KEY key, bool translate = true);
|
||||||
|
static std::string stringFromMouse(EMouseClickType click, bool translate = true);
|
||||||
static std::string stringFromAccelerator( MASK accel_mask ); // separated for convinience, returns with "+": "Shift+" or "Shift+Alt+"...
|
static std::string stringFromAccelerator( MASK accel_mask ); // separated for convinience, returns with "+": "Shift+" or "Shift+Alt+"...
|
||||||
static std::string stringFromAccelerator( MASK accel_mask, KEY key );
|
static std::string stringFromAccelerator( MASK accel_mask, KEY key );
|
||||||
|
static std::string stringFromAccelerator(MASK accel_mask, EMouseClickType click);
|
||||||
|
|
||||||
void setCallbacks(LLWindowCallbacks *cbs) { mCallbacks = cbs; }
|
void setCallbacks(LLWindowCallbacks *cbs) { mCallbacks = cbs; }
|
||||||
F32 getKeyElapsedTime( KEY key ); // Returns time in seconds since key was pressed.
|
F32 getKeyElapsedTime( KEY key ); // Returns time in seconds since key was pressed.
|
||||||
|
|
@ -130,6 +132,13 @@ protected:
|
||||||
static std::map<std::string,KEY> sNamesToKeys;
|
static std::map<std::string,KEY> sNamesToKeys;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Interface to get key from assigned command
|
||||||
|
class LLKeyBindingToStringHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual std::string getKeyBindingAsString(const std::string& mode, const std::string& control) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
extern LLKeyboard *gKeyboard;
|
extern LLKeyboard *gKeyboard;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1761,7 +1761,7 @@ if (WINDOWS)
|
||||||
if (TARGET ll::fmodstudio)
|
if (TARGET ll::fmodstudio)
|
||||||
list(APPEND COPY_INPUT_DEPENDENCIES
|
list(APPEND COPY_INPUT_DEPENDENCIES
|
||||||
${SHARED_LIB_STAGING_DIR}/fmod.dll
|
${SHARED_LIB_STAGING_DIR}/fmod.dll
|
||||||
${SHARED_LIB_STAGING_DIR}/Debug/fmodL.dll
|
${SHARED_LIB_STAGING_DIR}/fmodL.dll
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
7.1.1
|
7.1.2
|
||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 61 KiB |
|
|
@ -486,7 +486,11 @@ void LLAgent::init()
|
||||||
|
|
||||||
// *Note: this is where LLViewerCamera::getInstance() used to be constructed.
|
// *Note: this is where LLViewerCamera::getInstance() used to be constructed.
|
||||||
|
|
||||||
setFlying( gSavedSettings.getBOOL("FlyingAtExit") );
|
bool is_flying = gSavedSettings.getBOOL("FlyingAtExit");
|
||||||
|
if(is_flying)
|
||||||
|
{
|
||||||
|
setFlying(is_flying);
|
||||||
|
}
|
||||||
|
|
||||||
*mEffectColor = LLUIColorTable::instance().getColor("EffectColor");
|
*mEffectColor = LLUIColorTable::instance().getColor("EffectColor");
|
||||||
|
|
||||||
|
|
@ -2628,12 +2632,6 @@ void LLAgent::setStartPosition( U32 location_id )
|
||||||
if (!requestPostCapability("HomeLocation", body,
|
if (!requestPostCapability("HomeLocation", body,
|
||||||
boost::bind(&LLAgent::setStartPositionSuccess, this, _1)))
|
boost::bind(&LLAgent::setStartPositionSuccess, this, _1)))
|
||||||
LL_WARNS() << "Unable to post to HomeLocation capability." << LL_ENDL;
|
LL_WARNS() << "Unable to post to HomeLocation capability." << LL_ENDL;
|
||||||
|
|
||||||
const U32 HOME_INDEX = 1;
|
|
||||||
if( HOME_INDEX == location_id )
|
|
||||||
{
|
|
||||||
setHomePosRegion( mRegionp->getHandle(), getPositionAgent() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLAgent::setStartPositionSuccess(const LLSD &result)
|
void LLAgent::setStartPositionSuccess(const LLSD &result)
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,12 @@ const F32 GROUND_TO_AIR_CAMERA_TRANSITION_START_TIME = 0.5f;
|
||||||
|
|
||||||
const F32 OBJECT_EXTENTS_PADDING = 0.5f;
|
const F32 OBJECT_EXTENTS_PADDING = 0.5f;
|
||||||
|
|
||||||
|
static bool isDisableCameraConstraints()
|
||||||
|
{
|
||||||
|
static LLCachedControl<bool> sDisableCameraConstraints(gSavedSettings, "DisableCameraConstraints", false);
|
||||||
|
return sDisableCameraConstraints;
|
||||||
|
}
|
||||||
|
|
||||||
// The agent instance.
|
// The agent instance.
|
||||||
LLAgentCamera gAgentCamera;
|
LLAgentCamera gAgentCamera;
|
||||||
|
|
||||||
|
|
@ -572,7 +578,7 @@ BOOL LLAgentCamera::calcCameraMinDistance(F32 &obj_min_distance)
|
||||||
|
|
||||||
if (!mFocusObject || mFocusObject->isDead() ||
|
if (!mFocusObject || mFocusObject->isDead() ||
|
||||||
mFocusObject->isMesh() ||
|
mFocusObject->isMesh() ||
|
||||||
gSavedSettings.getBOOL("DisableCameraConstraints"))
|
isDisableCameraConstraints())
|
||||||
{
|
{
|
||||||
obj_min_distance = 0.f;
|
obj_min_distance = 0.f;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
@ -742,17 +748,23 @@ F32 LLAgentCamera::getCameraZoomFraction(bool get_third_person)
|
||||||
// already [0,1]
|
// already [0,1]
|
||||||
return mHUDTargetZoom;
|
return mHUDTargetZoom;
|
||||||
}
|
}
|
||||||
else if (get_third_person || (mFocusOnAvatar && cameraThirdPerson()))
|
|
||||||
|
if (isDisableCameraConstraints())
|
||||||
|
{
|
||||||
|
return mCameraZoomFraction;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_third_person || (mFocusOnAvatar && cameraThirdPerson()))
|
||||||
{
|
{
|
||||||
return clamp_rescale(mCameraZoomFraction, MIN_ZOOM_FRACTION, MAX_ZOOM_FRACTION, 1.f, 0.f);
|
return clamp_rescale(mCameraZoomFraction, MIN_ZOOM_FRACTION, MAX_ZOOM_FRACTION, 1.f, 0.f);
|
||||||
}
|
}
|
||||||
else if (cameraCustomizeAvatar())
|
|
||||||
|
if (cameraCustomizeAvatar())
|
||||||
{
|
{
|
||||||
F32 distance = (F32)mCameraFocusOffsetTarget.magVec();
|
F32 distance = (F32)mCameraFocusOffsetTarget.magVec();
|
||||||
return clamp_rescale(distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM, 1.f, 0.f );
|
return clamp_rescale(distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM, 1.f, 0.f );
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
F32 min_zoom;
|
F32 min_zoom;
|
||||||
F32 max_zoom = getCameraMaxZoomDistance();
|
F32 max_zoom = getCameraMaxZoomDistance();
|
||||||
|
|
||||||
|
|
@ -775,7 +787,6 @@ F32 LLAgentCamera::getCameraZoomFraction(bool get_third_person)
|
||||||
|
|
||||||
return clamp_rescale(distance, min_zoom, max_zoom, 1.f, 0.f);
|
return clamp_rescale(distance, min_zoom, max_zoom, 1.f, 0.f);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void LLAgentCamera::setCameraZoomFraction(F32 fraction)
|
void LLAgentCamera::setCameraZoomFraction(F32 fraction)
|
||||||
{
|
{
|
||||||
|
|
@ -787,6 +798,10 @@ void LLAgentCamera::setCameraZoomFraction(F32 fraction)
|
||||||
{
|
{
|
||||||
mHUDTargetZoom = fraction;
|
mHUDTargetZoom = fraction;
|
||||||
}
|
}
|
||||||
|
else if (isDisableCameraConstraints())
|
||||||
|
{
|
||||||
|
mCameraZoomFraction = fraction;
|
||||||
|
}
|
||||||
else if (mFocusOnAvatar && cameraThirdPerson())
|
else if (mFocusOnAvatar && cameraThirdPerson())
|
||||||
{
|
{
|
||||||
mCameraZoomFraction = rescale(fraction, 0.f, 1.f, MAX_ZOOM_FRACTION, MIN_ZOOM_FRACTION);
|
mCameraZoomFraction = rescale(fraction, 0.f, 1.f, MAX_ZOOM_FRACTION, MIN_ZOOM_FRACTION);
|
||||||
|
|
@ -821,6 +836,7 @@ void LLAgentCamera::setCameraZoomFraction(F32 fraction)
|
||||||
camera_offset_dir.normalize();
|
camera_offset_dir.normalize();
|
||||||
mCameraFocusOffsetTarget = camera_offset_dir * rescale(fraction, 0.f, 1.f, max_zoom, min_zoom);
|
mCameraFocusOffsetTarget = camera_offset_dir * rescale(fraction, 0.f, 1.f, max_zoom, min_zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
startCameraAnimation();
|
startCameraAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -925,12 +941,15 @@ void LLAgentCamera::cameraZoomIn(const F32 fraction)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LLVector3d camera_offset_unit(mCameraFocusOffsetTarget);
|
LLVector3d camera_offset_unit(mCameraFocusOffsetTarget);
|
||||||
F32 min_zoom = LAND_MIN_ZOOM;
|
|
||||||
F32 current_distance = (F32)camera_offset_unit.normalize();
|
F32 current_distance = (F32)camera_offset_unit.normalize();
|
||||||
F32 new_distance = current_distance * fraction;
|
F32 new_distance = current_distance * fraction;
|
||||||
|
|
||||||
|
// Unless camera is unlocked
|
||||||
|
if (!isDisableCameraConstraints())
|
||||||
|
{
|
||||||
|
F32 min_zoom = LAND_MIN_ZOOM;
|
||||||
|
|
||||||
// Don't move through focus point
|
// Don't move through focus point
|
||||||
if (mFocusObject)
|
if (mFocusObject)
|
||||||
{
|
{
|
||||||
|
|
@ -949,26 +968,14 @@ void LLAgentCamera::cameraZoomIn(const F32 fraction)
|
||||||
new_distance = llmax(new_distance, min_zoom);
|
new_distance = llmax(new_distance, min_zoom);
|
||||||
|
|
||||||
F32 max_distance = getCameraMaxZoomDistance();
|
F32 max_distance = getCameraMaxZoomDistance();
|
||||||
|
|
||||||
max_distance = llmin(max_distance, current_distance * 4.f); //Scaled max relative to current distance. MAINT-3154
|
max_distance = llmin(max_distance, current_distance * 4.f); //Scaled max relative to current distance. MAINT-3154
|
||||||
|
new_distance = llmin(new_distance, max_distance);
|
||||||
if (new_distance > max_distance)
|
|
||||||
{
|
|
||||||
new_distance = max_distance;
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Unless camera is unlocked
|
|
||||||
if (!LLViewerCamera::sDisableCameraConstraints)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cameraCustomizeAvatar())
|
if (cameraCustomizeAvatar())
|
||||||
{
|
{
|
||||||
new_distance = llclamp(new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM);
|
new_distance = llclamp(new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mCameraFocusOffsetTarget = new_distance * camera_offset_unit;
|
mCameraFocusOffsetTarget = new_distance * camera_offset_unit;
|
||||||
}
|
}
|
||||||
|
|
@ -990,13 +997,20 @@ void LLAgentCamera::cameraOrbitIn(const F32 meters)
|
||||||
changeCameraToMouselook(FALSE);
|
changeCameraToMouselook(FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isDisableCameraConstraints())
|
||||||
|
{
|
||||||
mCameraZoomFraction = llclamp(mCameraZoomFraction, MIN_ZOOM_FRACTION, MAX_ZOOM_FRACTION);
|
mCameraZoomFraction = llclamp(mCameraZoomFraction, MIN_ZOOM_FRACTION, MAX_ZOOM_FRACTION);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LLVector3d camera_offset_unit(mCameraFocusOffsetTarget);
|
LLVector3d camera_offset_unit(mCameraFocusOffsetTarget);
|
||||||
F32 current_distance = (F32)camera_offset_unit.normalize();
|
F32 current_distance = (F32)camera_offset_unit.normalize();
|
||||||
F32 new_distance = current_distance - meters;
|
F32 new_distance = current_distance - meters;
|
||||||
|
|
||||||
|
// Unless camera is unlocked
|
||||||
|
if (!isDisableCameraConstraints())
|
||||||
|
{
|
||||||
F32 min_zoom = LAND_MIN_ZOOM;
|
F32 min_zoom = LAND_MIN_ZOOM;
|
||||||
|
|
||||||
// Don't move through focus point
|
// Don't move through focus point
|
||||||
|
|
@ -1015,20 +1029,13 @@ void LLAgentCamera::cameraOrbitIn(const F32 meters)
|
||||||
new_distance = llmax(new_distance, min_zoom);
|
new_distance = llmax(new_distance, min_zoom);
|
||||||
|
|
||||||
F32 max_distance = getCameraMaxZoomDistance();
|
F32 max_distance = getCameraMaxZoomDistance();
|
||||||
|
new_distance = llmin(new_distance, max_distance);
|
||||||
if (new_distance > max_distance)
|
|
||||||
{
|
|
||||||
// Unless camera is unlocked
|
|
||||||
if (!gSavedSettings.getBOOL("DisableCameraConstraints"))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CAMERA_MODE_CUSTOMIZE_AVATAR == getCameraMode())
|
if (CAMERA_MODE_CUSTOMIZE_AVATAR == getCameraMode())
|
||||||
{
|
{
|
||||||
new_distance = llclamp(new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM);
|
new_distance = llclamp(new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Compute new camera offset
|
// Compute new camera offset
|
||||||
mCameraFocusOffsetTarget = new_distance * camera_offset_unit;
|
mCameraFocusOffsetTarget = new_distance * camera_offset_unit;
|
||||||
|
|
@ -1036,7 +1043,6 @@ void LLAgentCamera::cameraOrbitIn(const F32 meters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// cameraPanIn()
|
// cameraPanIn()
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
@ -1841,7 +1847,8 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit)
|
||||||
local_camera_offset = gAgent.getFrameAgent().rotateToAbsolute( local_camera_offset );
|
local_camera_offset = gAgent.getFrameAgent().rotateToAbsolute( local_camera_offset );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mCameraCollidePlane.isExactlyZero() && (!isAgentAvatarValid() || !gAgentAvatarp->isSitting()))
|
if (!isDisableCameraConstraints() && !mCameraCollidePlane.isExactlyZero() &&
|
||||||
|
(!isAgentAvatarValid() || !gAgentAvatarp->isSitting()))
|
||||||
{
|
{
|
||||||
LLVector3 plane_normal;
|
LLVector3 plane_normal;
|
||||||
plane_normal.setVec(mCameraCollidePlane.mV);
|
plane_normal.setVec(mCameraCollidePlane.mV);
|
||||||
|
|
@ -1960,7 +1967,7 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit)
|
||||||
camera_position_global = focusPosGlobal + mCameraFocusOffset;
|
camera_position_global = focusPosGlobal + mCameraFocusOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gSavedSettings.getBOOL("DisableCameraConstraints") && !gAgent.isGodlike())
|
if (!isDisableCameraConstraints() && !gAgent.isGodlike())
|
||||||
{
|
{
|
||||||
LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromPosGlobal(camera_position_global);
|
LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromPosGlobal(camera_position_global);
|
||||||
bool constrain = true;
|
bool constrain = true;
|
||||||
|
|
@ -1995,16 +2002,14 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit)
|
||||||
|
|
||||||
// Don't let camera go underground
|
// Don't let camera go underground
|
||||||
F32 camera_min_off_ground = getCameraMinOffGround();
|
F32 camera_min_off_ground = getCameraMinOffGround();
|
||||||
|
|
||||||
camera_land_height = LLWorld::getInstance()->resolveLandHeightGlobal(camera_position_global);
|
camera_land_height = LLWorld::getInstance()->resolveLandHeightGlobal(camera_position_global);
|
||||||
|
F32 minZ = llmax(F_ALMOST_ZERO, camera_land_height + camera_min_off_ground);
|
||||||
if (camera_position_global.mdV[VZ] < camera_land_height + camera_min_off_ground)
|
if (camera_position_global.mdV[VZ] < minZ)
|
||||||
{
|
{
|
||||||
camera_position_global.mdV[VZ] = camera_land_height + camera_min_off_ground;
|
camera_position_global.mdV[VZ] = minZ;
|
||||||
isConstrained = TRUE;
|
isConstrained = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (hit_limit)
|
if (hit_limit)
|
||||||
{
|
{
|
||||||
*hit_limit = isConstrained;
|
*hit_limit = isConstrained;
|
||||||
|
|
@ -2131,18 +2136,14 @@ F32 LLAgentCamera::getCameraMinOffGround()
|
||||||
{
|
{
|
||||||
return 0.f;
|
return 0.f;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
if (isDisableCameraConstraints())
|
||||||
if (gSavedSettings.getBOOL("DisableCameraConstraints"))
|
|
||||||
{
|
{
|
||||||
return -1000.f;
|
return -1000.f;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0.5f;
|
return 0.5f;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -2776,18 +2776,32 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
selfStartPhase("wear_inventory_category_fetch");
|
selfStartPhase("wear_inventory_category_fetch");
|
||||||
|
if (AISAPI::isAvailable() && category->getPreferredType() == LLFolderType::FT_OUTFIT)
|
||||||
|
{
|
||||||
|
// for reliability just fetch it whole, linked items included
|
||||||
|
LLUUID cat_id = category->getUUID();
|
||||||
|
LLInventoryModelBackgroundFetch::getInstance()->fetchFolderAndLinks(
|
||||||
|
cat_id,
|
||||||
|
[cat_id, copy, append]
|
||||||
|
{
|
||||||
|
LLAppearanceMgr::instance().wearCategoryFinal(cat_id, copy, append);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
callAfterCategoryFetch(category->getUUID(), boost::bind(&LLAppearanceMgr::wearCategoryFinal,
|
callAfterCategoryFetch(category->getUUID(), boost::bind(&LLAppearanceMgr::wearCategoryFinal,
|
||||||
&LLAppearanceMgr::instance(),
|
&LLAppearanceMgr::instance(),
|
||||||
category->getUUID(), copy, append));
|
category->getUUID(), copy, append));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
S32 LLAppearanceMgr::getActiveCopyOperations() const
|
S32 LLAppearanceMgr::getActiveCopyOperations() const
|
||||||
{
|
{
|
||||||
return LLCallAfterInventoryCopyMgr::getInstanceCount();
|
return LLCallAfterInventoryCopyMgr::getInstanceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool append)
|
void LLAppearanceMgr::wearCategoryFinal(const LLUUID& cat_id, bool copy_items, bool append)
|
||||||
{
|
{
|
||||||
LL_INFOS("Avatar") << self_av_string() << "starting" << LL_ENDL;
|
LL_INFOS("Avatar") << self_av_string() << "starting" << LL_ENDL;
|
||||||
|
|
||||||
|
|
@ -4571,30 +4585,20 @@ protected:
|
||||||
|
|
||||||
void callAfterCOFFetch(nullary_func_t cb)
|
void callAfterCOFFetch(nullary_func_t cb)
|
||||||
{
|
{
|
||||||
LLUUID cat_id = LLAppearanceMgr::instance().getCOF();
|
|
||||||
LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
|
|
||||||
|
|
||||||
if (AISAPI::isAvailable())
|
if (AISAPI::isAvailable())
|
||||||
{
|
{
|
||||||
// Mark cof (update timer) so that background fetch won't request it
|
|
||||||
cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE);
|
|
||||||
// For reliability assume that we have no relevant cache, so
|
// For reliability assume that we have no relevant cache, so
|
||||||
// fetch cof along with items cof's links point to.
|
// fetch cof along with items cof's links point to.
|
||||||
AISAPI::FetchCOF([cb](const LLUUID& id)
|
LLInventoryModelBackgroundFetch::getInstance()->fetchCOF(cb);
|
||||||
{
|
|
||||||
cb();
|
|
||||||
LLUUID cat_id = LLAppearanceMgr::instance().getCOF();
|
|
||||||
LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
|
|
||||||
if (cat)
|
|
||||||
{
|
|
||||||
cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LL_INFOS() << "AIS API v3 not available, using callAfterCategoryFetch" << LL_ENDL;
|
LL_INFOS() << "AIS API v3 not available, using callAfterCategoryFetch" << LL_ENDL;
|
||||||
// startup should have marked folder as fetching, remove that
|
LLUUID cat_id = LLAppearanceMgr::instance().getCOF();
|
||||||
|
LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
|
||||||
|
|
||||||
|
// Special case, startup should have marked cof as FETCH_RECURSIVE
|
||||||
|
// to prevent dupplicate request, remove that
|
||||||
cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
|
cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
|
||||||
callAfterCategoryFetch(cat_id, cb);
|
callAfterCategoryFetch(cat_id, cb);
|
||||||
}
|
}
|
||||||
|
|
@ -4616,28 +4620,14 @@ void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb)
|
||||||
|
|
||||||
void callAfterCategoryLinksFetch(const LLUUID &cat_id, nullary_func_t cb)
|
void callAfterCategoryLinksFetch(const LLUUID &cat_id, nullary_func_t cb)
|
||||||
{
|
{
|
||||||
LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
|
|
||||||
if (AISAPI::isAvailable())
|
if (AISAPI::isAvailable())
|
||||||
{
|
{
|
||||||
// Mark folder (update timer) so that background fetch won't request it
|
|
||||||
cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE);
|
|
||||||
// Assume that we have no relevant cache. Fetch folder, and items folder's links point to.
|
// Assume that we have no relevant cache. Fetch folder, and items folder's links point to.
|
||||||
AISAPI::FetchCategoryLinks(cat_id,
|
LLInventoryModelBackgroundFetch::getInstance()->fetchFolderAndLinks(cat_id, cb);
|
||||||
[cb, cat_id](const LLUUID &id)
|
|
||||||
{
|
|
||||||
cb();
|
|
||||||
LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
|
|
||||||
if (cat)
|
|
||||||
{
|
|
||||||
cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LL_WARNS() << "AIS API v3 not available, can't use AISAPI::FetchCOF" << LL_ENDL;
|
LL_WARNS() << "AIS API v3 not available, can't use AISAPI::FetchCOF" << LL_ENDL;
|
||||||
// startup should have marked folder as fetching, remove that
|
|
||||||
cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
|
|
||||||
callAfterCategoryFetch(cat_id, cb);
|
callAfterCategoryFetch(cat_id, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ public:
|
||||||
void updateCOF(const LLUUID& category, bool append = false);
|
void updateCOF(const LLUUID& category, bool append = false);
|
||||||
void wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append);
|
void wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append);
|
||||||
void wearInventoryCategoryOnAvatar(LLInventoryCategory* category, bool append);
|
void wearInventoryCategoryOnAvatar(LLInventoryCategory* category, bool append);
|
||||||
void wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool append);
|
void wearCategoryFinal(const LLUUID& cat_id, bool copy_items, bool append);
|
||||||
void wearOutfitByName(const std::string& name);
|
void wearOutfitByName(const std::string& name);
|
||||||
void changeOutfit(bool proceed, const LLUUID& category, bool append);
|
void changeOutfit(bool proceed, const LLUUID& category, bool append);
|
||||||
void replaceCurrentOutfit(const LLUUID& new_outfit);
|
void replaceCurrentOutfit(const LLUUID& new_outfit);
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,7 @@
|
||||||
#include "llhudeffecttrail.h"
|
#include "llhudeffecttrail.h"
|
||||||
#include "llvectorperfoptions.h"
|
#include "llvectorperfoptions.h"
|
||||||
#include "llslurl.h"
|
#include "llslurl.h"
|
||||||
|
#include "llurlregistry.h"
|
||||||
#include "llwatchdog.h"
|
#include "llwatchdog.h"
|
||||||
|
|
||||||
// Included so that constants/settings might be initialized
|
// Included so that constants/settings might be initialized
|
||||||
|
|
@ -4312,6 +4313,7 @@ void LLAppViewer::loadKeyBindings()
|
||||||
LL_ERRS("InitInfo") << "Unable to open default key bindings from " << key_bindings_file << LL_ENDL;
|
LL_ERRS("InitInfo") << "Unable to open default key bindings from " << key_bindings_file << LL_ENDL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LLUrlRegistry::instance().setKeybindingHandler(&gViewerInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLAppViewer::purgeCache()
|
void LLAppViewer::purgeCache()
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,11 @@ void LLAvatarIconCtrl::setValue(const LLSD& value)
|
||||||
app->addObserver(mAvatarId, this);
|
app->addObserver(mAvatarId, this);
|
||||||
app->sendAvatarPropertiesRequest(mAvatarId);
|
app->sendAvatarPropertiesRequest(mAvatarId);
|
||||||
}
|
}
|
||||||
|
else if (gAgentID == mAvatarId)
|
||||||
|
{
|
||||||
|
// Always track any changes to our own icon id
|
||||||
|
app->addObserver(mAvatarId, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,14 @@ void LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro(std::string url, U64
|
||||||
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
|
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
|
||||||
httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("AvatarRenderInfoAccountant", httpPolicy));
|
httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("AvatarRenderInfoAccountant", httpPolicy));
|
||||||
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
|
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
|
||||||
|
LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
|
||||||
|
|
||||||
LLSD result = httpAdapter->getAndSuspend(httpRequest, url);
|
// Going to request each 15 seconds either way, so don't wait
|
||||||
|
// too long and don't repeat
|
||||||
|
httpOpts->setRetries(0);
|
||||||
|
httpOpts->setTimeout(SECS_BETWEEN_REGION_REQUEST);
|
||||||
|
|
||||||
|
LLSD result = httpAdapter->getAndSuspend(httpRequest, url, httpOpts);
|
||||||
|
|
||||||
LLWorld *world_inst = LLWorld::getInstance();
|
LLWorld *world_inst = LLWorld::getInstance();
|
||||||
if (!world_inst)
|
if (!world_inst)
|
||||||
|
|
@ -190,6 +196,11 @@ void LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro(std::string url, U
|
||||||
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
|
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
|
||||||
httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("AvatarRenderInfoAccountant", httpPolicy));
|
httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("AvatarRenderInfoAccountant", httpPolicy));
|
||||||
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
|
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
|
||||||
|
LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
|
||||||
|
|
||||||
|
// Going to request each 60+ seconds, timeout is 30s.
|
||||||
|
// Don't repeat too often, will be sending newer data soon
|
||||||
|
httpOpts->setRetries(1);
|
||||||
|
|
||||||
LLWorld *world_inst = LLWorld::getInstance();
|
LLWorld *world_inst = LLWorld::getInstance();
|
||||||
if (!world_inst)
|
if (!world_inst)
|
||||||
|
|
@ -256,7 +267,7 @@ void LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro(std::string url, U
|
||||||
|
|
||||||
regionp = NULL;
|
regionp = NULL;
|
||||||
world_inst = NULL;
|
world_inst = NULL;
|
||||||
LLSD result = httpAdapter->postAndSuspend(httpRequest, url, report);
|
LLSD result = httpAdapter->postAndSuspend(httpRequest, url, report, httpOpts);
|
||||||
|
|
||||||
world_inst = LLWorld::getInstance();
|
world_inst = LLWorld::getInstance();
|
||||||
if (!world_inst)
|
if (!world_inst)
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ class LLConversationItem;
|
||||||
class LLConversationItemSession;
|
class LLConversationItemSession;
|
||||||
class LLConversationItemParticipant;
|
class LLConversationItemParticipant;
|
||||||
|
|
||||||
typedef std::map<LLUUID, LLConversationItem*> conversations_items_map;
|
typedef std::map<LLUUID, LLPointer<LLConversationItem> > conversations_items_map;
|
||||||
typedef std::map<LLUUID, LLFolderViewItem*> conversations_widgets_map;
|
typedef std::map<LLUUID, LLFolderViewItem*> conversations_widgets_map;
|
||||||
|
|
||||||
typedef std::vector<std::string> menuentry_vec_t;
|
typedef std::vector<std::string> menuentry_vec_t;
|
||||||
|
|
|
||||||
|
|
@ -760,19 +760,6 @@ void LLDrawable::movePartition()
|
||||||
if (part)
|
if (part)
|
||||||
{
|
{
|
||||||
part->move(this, getSpatialGroup());
|
part->move(this, getSpatialGroup());
|
||||||
|
|
||||||
// SL-18251 "On-screen animesh characters using pelvis offset animations
|
|
||||||
// disappear when root goes off-screen"
|
|
||||||
//
|
|
||||||
// Update extents of the root node when Control Avatar changes it's bounds
|
|
||||||
if (mRenderType == LLPipeline::RENDER_TYPE_CONTROL_AV && isRoot())
|
|
||||||
{
|
|
||||||
LLControlAvatar* controlAvatar = dynamic_cast<LLControlAvatar*>(getVObj().get());
|
|
||||||
if (controlAvatar && controlAvatar->mControlAVBridge)
|
|
||||||
{
|
|
||||||
((LLSpatialGroup*)controlAvatar->mControlAVBridge->mOctree->getListener(0))->setState(LLViewerOctreeGroup::DIRTY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,20 @@ void LLFloaterIMContainer::sessionIDUpdated(const LLUUID& old_session_id, const
|
||||||
LLFloaterIMSessionTab::addToHost(new_session_id);
|
LLFloaterIMSessionTab::addToHost(new_session_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LLConversationItem* LLFloaterIMContainer::getSessionModel(const LLUUID& session_id)
|
||||||
|
{
|
||||||
|
conversations_items_map::iterator iter = mConversationsItems.find(session_id);
|
||||||
|
if (iter == mConversationsItems.end())
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return iter->second.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LLFloaterIMContainer::sessionRemoved(const LLUUID& session_id)
|
void LLFloaterIMContainer::sessionRemoved(const LLUUID& session_id)
|
||||||
{
|
{
|
||||||
removeConversationListItem(session_id);
|
removeConversationListItem(session_id);
|
||||||
|
|
@ -611,7 +625,8 @@ void LLFloaterIMContainer::handleConversationModelEvent(const LLSD& event)
|
||||||
}
|
}
|
||||||
else if (type == "add_participant")
|
else if (type == "add_participant")
|
||||||
{
|
{
|
||||||
LLConversationItemSession* session_model = dynamic_cast<LLConversationItemSession*>(mConversationsItems[session_id]);
|
LLConversationItem* item = getSessionModel(session_id);
|
||||||
|
LLConversationItemSession* session_model = dynamic_cast<LLConversationItemSession*>(item);
|
||||||
LLConversationItemParticipant* participant_model = (session_model ? session_model->findParticipant(participant_id) : NULL);
|
LLConversationItemParticipant* participant_model = (session_model ? session_model->findParticipant(participant_id) : NULL);
|
||||||
LLIMModel::LLIMSession * im_sessionp = LLIMModel::getInstance()->findIMSession(session_id);
|
LLIMModel::LLIMSession * im_sessionp = LLIMModel::getInstance()->findIMSession(session_id);
|
||||||
if (!participant_view && session_model && participant_model)
|
if (!participant_view && session_model && participant_model)
|
||||||
|
|
@ -1752,10 +1767,9 @@ BOOL LLFloaterIMContainer::selectConversationPair(const LLUUID& session_id, bool
|
||||||
|
|
||||||
void LLFloaterIMContainer::setTimeNow(const LLUUID& session_id, const LLUUID& participant_id)
|
void LLFloaterIMContainer::setTimeNow(const LLUUID& session_id, const LLUUID& participant_id)
|
||||||
{
|
{
|
||||||
LLConversationItemSession* item = dynamic_cast<LLConversationItemSession*>(get_ptr_in_map(mConversationsItems,session_id));
|
LLConversationItemSession* item = dynamic_cast<LLConversationItemSession*>(getSessionModel(session_id));
|
||||||
if (item)
|
if (item)
|
||||||
{
|
{
|
||||||
item->setTimeNow(participant_id);
|
|
||||||
mConversationViewModel.requestSortAll();
|
mConversationViewModel.requestSortAll();
|
||||||
mConversationsRoot->arrangeAll();
|
mConversationsRoot->arrangeAll();
|
||||||
}
|
}
|
||||||
|
|
@ -1764,7 +1778,7 @@ void LLFloaterIMContainer::setTimeNow(const LLUUID& session_id, const LLUUID& pa
|
||||||
void LLFloaterIMContainer::setNearbyDistances()
|
void LLFloaterIMContainer::setNearbyDistances()
|
||||||
{
|
{
|
||||||
// Get the nearby chat session: that's the one with uuid nul
|
// Get the nearby chat session: that's the one with uuid nul
|
||||||
LLConversationItemSession* item = dynamic_cast<LLConversationItemSession*>(get_ptr_in_map(mConversationsItems,LLUUID()));
|
LLConversationItemSession* item = dynamic_cast<LLConversationItemSession*>(getSessionModel(LLUUID()));
|
||||||
if (item)
|
if (item)
|
||||||
{
|
{
|
||||||
// Get the positions of the nearby avatars and their ids
|
// Get the positions of the nearby avatars and their ids
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ public:
|
||||||
LLConversationViewModel& getRootViewModel() { return mConversationViewModel; }
|
LLConversationViewModel& getRootViewModel() { return mConversationViewModel; }
|
||||||
LLUUID getSelectedSession() { return mSelectedSession; }
|
LLUUID getSelectedSession() { return mSelectedSession; }
|
||||||
void setSelectedSession(LLUUID sessionID) { mSelectedSession = sessionID; }
|
void setSelectedSession(LLUUID sessionID) { mSelectedSession = sessionID; }
|
||||||
LLConversationItem* getSessionModel(const LLUUID& session_id) { return get_ptr_in_map(mConversationsItems,session_id); }
|
LLConversationItem* getSessionModel(const LLUUID& session_id);
|
||||||
LLConversationSort& getSortOrder() { return mConversationViewModel.getSorter(); }
|
LLConversationSort& getSortOrder() { return mConversationViewModel.getSorter(); }
|
||||||
|
|
||||||
// Handling of lists of participants is public so to be common with llfloatersessiontab
|
// Handling of lists of participants is public so to be common with llfloatersessiontab
|
||||||
|
|
|
||||||
|
|
@ -250,11 +250,49 @@ void fractionFromDecimal(F32 decimal_val, S32& numerator, S32& denominator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// static
|
|
||||||
std::string LLFloaterPreference::sSkin = "";
|
// handle secondlife:///app/worldmap/{NAME}/{COORDS} URLs
|
||||||
|
// Also see LLUrlEntryKeybinding, the value of this command type
|
||||||
|
// is ability to show up to date value in chat
|
||||||
|
class LLKeybindingHandler: public LLCommandHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// requires trusted browser to trigger
|
||||||
|
LLKeybindingHandler(): LLCommandHandler("keybinding", UNTRUSTED_CLICK_ONLY)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handle(const LLSD& params, const LLSD& query_map,
|
||||||
|
const std::string& grid, LLMediaCtrl* web)
|
||||||
|
{
|
||||||
|
if (params.size() < 1) return false;
|
||||||
|
|
||||||
|
LLFloaterPreference* prefsfloater = dynamic_cast<LLFloaterPreference*>
|
||||||
|
(LLFloaterReg::showInstance("preferences"));
|
||||||
|
|
||||||
|
if (prefsfloater)
|
||||||
|
{
|
||||||
|
// find 'controls' panel and bring it the front
|
||||||
|
LLTabContainer* tabcontainer = prefsfloater->getChild<LLTabContainer>("pref core");
|
||||||
|
LLPanel* panel = prefsfloater->getChild<LLPanel>("controls");
|
||||||
|
if (tabcontainer && panel)
|
||||||
|
{
|
||||||
|
tabcontainer->selectTabPanel(panel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
LLKeybindingHandler gKeybindHandler;
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////
|
//////////////////////////////////////////////
|
||||||
// LLFloaterPreference
|
// LLFloaterPreference
|
||||||
|
|
||||||
|
// static
|
||||||
|
std::string LLFloaterPreference::sSkin = "";
|
||||||
|
|
||||||
LLFloaterPreference::LLFloaterPreference(const LLSD& key)
|
LLFloaterPreference::LLFloaterPreference(const LLSD& key)
|
||||||
: LLFloater(key),
|
: LLFloater(key),
|
||||||
mGotPersonalInfo(false),
|
mGotPersonalInfo(false),
|
||||||
|
|
|
||||||
|
|
@ -175,10 +175,9 @@ void LLFloaterURLEntry::onBtnOK( void* userdata )
|
||||||
// We assume that an empty scheme is an http url, as this is how we will treat it.
|
// We assume that an empty scheme is an http url, as this is how we will treat it.
|
||||||
if(scheme == "")
|
if(scheme == "")
|
||||||
{
|
{
|
||||||
scheme = "http";
|
scheme = "https";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discover the MIME type only for "http" scheme.
|
|
||||||
if(!media_url.empty() &&
|
if(!media_url.empty() &&
|
||||||
(scheme == "http" || scheme == "https"))
|
(scheme == "http" || scheme == "https"))
|
||||||
{
|
{
|
||||||
|
|
@ -204,13 +203,18 @@ void LLFloaterURLEntry::getMediaTypeCoro(std::string url, LLHandle<LLFloater> pa
|
||||||
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
|
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
|
||||||
httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getMediaTypeCoro", httpPolicy));
|
httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getMediaTypeCoro", httpPolicy));
|
||||||
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
|
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
|
||||||
|
LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders);
|
||||||
LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
|
LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
|
||||||
|
|
||||||
|
httpOpts->setFollowRedirects(true);
|
||||||
httpOpts->setHeadersOnly(true);
|
httpOpts->setHeadersOnly(true);
|
||||||
|
|
||||||
|
httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, "*/*");
|
||||||
|
httpHeaders->append(HTTP_OUT_HEADER_COOKIE, "");
|
||||||
|
|
||||||
LL_INFOS("HttpCoroutineAdapter", "genericPostCoro") << "Generic POST for " << url << LL_ENDL;
|
LL_INFOS("HttpCoroutineAdapter", "genericPostCoro") << "Generic POST for " << url << LL_ENDL;
|
||||||
|
|
||||||
LLSD result = httpAdapter->getAndSuspend(httpRequest, url, httpOpts);
|
LLSD result = httpAdapter->getRawAndSuspend(httpRequest, url, httpOpts, httpHeaders);
|
||||||
|
|
||||||
LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
|
LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
|
||||||
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
|
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
|
||||||
|
|
@ -226,12 +230,6 @@ void LLFloaterURLEntry::getMediaTypeCoro(std::string url, LLHandle<LLFloater> pa
|
||||||
// which have no mime type set.
|
// which have no mime type set.
|
||||||
std::string resolvedMimeType = LLMIMETypes::getDefaultMimeType();
|
std::string resolvedMimeType = LLMIMETypes::getDefaultMimeType();
|
||||||
|
|
||||||
if (!status)
|
|
||||||
{
|
|
||||||
floaterUrlEntry->headerFetchComplete(status.getType(), resolvedMimeType);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LLSD resultHeaders = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS];
|
LLSD resultHeaders = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS];
|
||||||
|
|
||||||
if (resultHeaders.has(HTTP_IN_HEADER_CONTENT_TYPE))
|
if (resultHeaders.has(HTTP_IN_HEADER_CONTENT_TYPE))
|
||||||
|
|
|
||||||
|
|
@ -121,8 +121,25 @@ static const F32 ZOOM_MAX = 128.f;
|
||||||
class LLWorldMapHandler : public LLCommandHandler
|
class LLWorldMapHandler : public LLCommandHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// requires trusted browser to trigger
|
LLWorldMapHandler() : LLCommandHandler("worldmap", UNTRUSTED_THROTTLE)
|
||||||
LLWorldMapHandler() : LLCommandHandler("worldmap", UNTRUSTED_CLICK_ONLY ) { }
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool canHandleUntrusted(
|
||||||
|
const LLSD& params,
|
||||||
|
const LLSD& query_map,
|
||||||
|
LLMediaCtrl* web,
|
||||||
|
const std::string& nav_type)
|
||||||
|
{
|
||||||
|
if (nav_type == NAV_TYPE_CLICKED
|
||||||
|
|| nav_type == NAV_TYPE_EXTERNAL)
|
||||||
|
{
|
||||||
|
// NAV_TYPE_EXTERNAL will be throttled
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool handle(const LLSD& params,
|
bool handle(const LLSD& params,
|
||||||
const LLSD& query_map,
|
const LLSD& query_map,
|
||||||
|
|
@ -160,11 +177,31 @@ LLWorldMapHandler gWorldMapHandler;
|
||||||
class LLMapTrackAvatarHandler : public LLCommandHandler
|
class LLMapTrackAvatarHandler : public LLCommandHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// requires trusted browser to trigger
|
LLMapTrackAvatarHandler() : LLCommandHandler("maptrackavatar", UNTRUSTED_THROTTLE)
|
||||||
LLMapTrackAvatarHandler() : LLCommandHandler("maptrackavatar", UNTRUSTED_CLICK_ONLY)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool canHandleUntrusted(
|
||||||
|
const LLSD& params,
|
||||||
|
const LLSD& query_map,
|
||||||
|
LLMediaCtrl* web,
|
||||||
|
const std::string& nav_type)
|
||||||
|
{
|
||||||
|
if (params.size() < 1)
|
||||||
|
{
|
||||||
|
return true; // don't block, will fail later
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nav_type == NAV_TYPE_CLICKED
|
||||||
|
|| nav_type == NAV_TYPE_EXTERNAL)
|
||||||
|
{
|
||||||
|
// NAV_TYPE_EXTERNAL will be throttled
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool handle(const LLSD& params,
|
bool handle(const LLSD& params,
|
||||||
const LLSD& query_map,
|
const LLSD& query_map,
|
||||||
const std::string& grid,
|
const std::string& grid,
|
||||||
|
|
|
||||||
|
|
@ -549,20 +549,7 @@ void LLFriendCardsManager::syncFriendsFolder()
|
||||||
// Create own calling card if it was not found in Friends/All folder
|
// Create own calling card if it was not found in Friends/All folder
|
||||||
if (!collector.isAgentCallingCardFound())
|
if (!collector.isAgentCallingCardFound())
|
||||||
{
|
{
|
||||||
LLAvatarName av_name;
|
create_inventory_callingcard(gAgentID, calling_cards_folder_id);
|
||||||
LLAvatarNameCache::get( gAgentID, &av_name );
|
|
||||||
|
|
||||||
create_inventory_item(gAgentID,
|
|
||||||
gAgent.getSessionID(),
|
|
||||||
calling_cards_folder_id,
|
|
||||||
LLTransactionID::tnull,
|
|
||||||
av_name.getCompleteName(),
|
|
||||||
gAgentID.asString(),
|
|
||||||
LLAssetType::AT_CALLINGCARD,
|
|
||||||
LLInventoryType::IT_CALLINGCARD,
|
|
||||||
NO_INV_SUBTYPE,
|
|
||||||
PERM_MOVE | PERM_TRANSFER,
|
|
||||||
NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// All folders created and updated.
|
// All folders created and updated.
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,8 @@ public:
|
||||||
return true; // don't block, will fail later
|
return true; // don't block, will fail later
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nav_type == NAV_TYPE_CLICKED)
|
if (nav_type == NAV_TYPE_CLICKED
|
||||||
|
|| nav_type == NAV_TYPE_EXTERNAL)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4336,7 +4336,8 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
|
||||||
|| is_recent_panel
|
|| is_recent_panel
|
||||||
|| !trash
|
|| !trash
|
||||||
|| trash->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN
|
|| trash->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN
|
||||||
|| trash->getDescendentCount() == LLViewerInventoryCategory::VERSION_UNKNOWN)
|
|| trash->getDescendentCount() == LLViewerInventoryCategory::VERSION_UNKNOWN
|
||||||
|
|| gAgentAvatarp->hasAttachmentsInTrash())
|
||||||
{
|
{
|
||||||
disabled_items.push_back(std::string("Empty Trash"));
|
disabled_items.push_back(std::string("Empty Trash"));
|
||||||
}
|
}
|
||||||
|
|
@ -6510,6 +6511,7 @@ void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
|
||||||
{
|
{
|
||||||
items.push_back(std::string("Activate"));
|
items.push_back(std::string("Activate"));
|
||||||
}
|
}
|
||||||
|
items.push_back(std::string("PlayGesture"));
|
||||||
}
|
}
|
||||||
addLinkReplaceMenuOption(items, disabled_items);
|
addLinkReplaceMenuOption(items, disabled_items);
|
||||||
hide_context_entries(menu, items, disabled_items);
|
hide_context_entries(menu, items, disabled_items);
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@
|
||||||
static LLPanelInjector<LLInventoryGallery> t_inventory_gallery("inventory_gallery");
|
static LLPanelInjector<LLInventoryGallery> t_inventory_gallery("inventory_gallery");
|
||||||
|
|
||||||
const S32 GALLERY_ITEMS_PER_ROW_MIN = 2;
|
const S32 GALLERY_ITEMS_PER_ROW_MIN = 2;
|
||||||
|
const S32 FAST_LOAD_THUMBNAIL_TRSHOLD = 50; // load folders below this value immediately
|
||||||
|
|
||||||
// Helper dnd functions
|
// Helper dnd functions
|
||||||
BOOL dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat, BOOL drop, std::string& tooltip_msg, BOOL is_link);
|
BOOL dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat, BOOL drop, std::string& tooltip_msg, BOOL is_link);
|
||||||
|
|
@ -106,6 +107,7 @@ LLInventoryGallery::LLInventoryGallery(const LLInventoryGallery::Params& p)
|
||||||
mGalleryWidthFactor(p.gallery_width_factor),
|
mGalleryWidthFactor(p.gallery_width_factor),
|
||||||
mIsInitialized(false),
|
mIsInitialized(false),
|
||||||
mRootDirty(false),
|
mRootDirty(false),
|
||||||
|
mLoadThumbnailsImmediately(true),
|
||||||
mNeedsArrange(false),
|
mNeedsArrange(false),
|
||||||
mSearchType(LLInventoryFilter::SEARCHTYPE_NAME),
|
mSearchType(LLInventoryFilter::SEARCHTYPE_NAME),
|
||||||
mSortOrder(LLInventoryFilter::SO_DATE)
|
mSortOrder(LLInventoryFilter::SO_DATE)
|
||||||
|
|
@ -540,6 +542,12 @@ void LLInventoryGallery::addToGallery(LLInventoryGalleryItem* item)
|
||||||
int n_prev = n - 1;
|
int n_prev = n - 1;
|
||||||
int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1;
|
int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1;
|
||||||
|
|
||||||
|
// Avoid loading too many items.
|
||||||
|
// Intent is for small folders to display all content fast
|
||||||
|
// and for large folders to load content mostly as needed
|
||||||
|
// Todo: ideally needs to unload images outside visible area
|
||||||
|
mLoadThumbnailsImmediately = mItemsAddedCount < FAST_LOAD_THUMBNAIL_TRSHOLD;
|
||||||
|
|
||||||
bool add_row = row_count != row_count_prev;
|
bool add_row = row_count != row_count_prev;
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
if (add_row)
|
if (add_row)
|
||||||
|
|
@ -573,6 +581,8 @@ void LLInventoryGallery::removeFromGalleryLast(LLInventoryGalleryItem* item, boo
|
||||||
mItemsAddedCount--;
|
mItemsAddedCount--;
|
||||||
mIndexToItemMap.erase(mItemsAddedCount);
|
mIndexToItemMap.erase(mItemsAddedCount);
|
||||||
|
|
||||||
|
mLoadThumbnailsImmediately = mItemsAddedCount < FAST_LOAD_THUMBNAIL_TRSHOLD;
|
||||||
|
|
||||||
bool remove_row = row_count != row_count_prev;
|
bool remove_row = row_count != row_count_prev;
|
||||||
removeFromLastRow(mItems[mItemsAddedCount]);
|
removeFromLastRow(mItems[mItemsAddedCount]);
|
||||||
mItems.pop_back();
|
mItems.pop_back();
|
||||||
|
|
@ -636,6 +646,7 @@ LLInventoryGalleryItem* LLInventoryGallery::buildGalleryItem(std::string name, L
|
||||||
gitem->setUUID(item_id);
|
gitem->setUUID(item_id);
|
||||||
gitem->setGallery(this);
|
gitem->setGallery(this);
|
||||||
gitem->setType(type, inventory_type, flags, is_link);
|
gitem->setType(type, inventory_type, flags, is_link);
|
||||||
|
gitem->setLoadImmediately(mLoadThumbnailsImmediately);
|
||||||
gitem->setThumbnail(thumbnail_id);
|
gitem->setThumbnail(thumbnail_id);
|
||||||
gitem->setWorn(is_worn);
|
gitem->setWorn(is_worn);
|
||||||
gitem->setCreatorName(get_searchable_creator_name(&gInventory, item_id));
|
gitem->setCreatorName(get_searchable_creator_name(&gInventory, item_id));
|
||||||
|
|
@ -997,6 +1008,7 @@ void LLInventoryGallery::updateItemThumbnail(LLUUID item_id)
|
||||||
|
|
||||||
if (mItemMap[item_id])
|
if (mItemMap[item_id])
|
||||||
{
|
{
|
||||||
|
mItemMap[item_id]->setLoadImmediately(mLoadThumbnailsImmediately);
|
||||||
mItemMap[item_id]->setThumbnail(thumbnail_id);
|
mItemMap[item_id]->setThumbnail(thumbnail_id);
|
||||||
|
|
||||||
bool passes_filter = checkAgainstFilters(mItemMap[item_id], mFilterSubString);
|
bool passes_filter = checkAgainstFilters(mItemMap[item_id], mFilterSubString);
|
||||||
|
|
@ -2570,6 +2582,7 @@ BOOL LLInventoryGalleryItem::postBuild()
|
||||||
{
|
{
|
||||||
mNameText = getChild<LLTextBox>("item_name");
|
mNameText = getChild<LLTextBox>("item_name");
|
||||||
mTextBgPanel = getChild<LLPanel>("text_bg_panel");
|
mTextBgPanel = getChild<LLPanel>("text_bg_panel");
|
||||||
|
mThumbnailCtrl = getChild<LLThumbnailCtrl>("preview_thumbnail");
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
@ -2645,14 +2658,19 @@ void LLInventoryGalleryItem::setThumbnail(LLUUID id)
|
||||||
mDefaultImage = id.isNull();
|
mDefaultImage = id.isNull();
|
||||||
if(mDefaultImage)
|
if(mDefaultImage)
|
||||||
{
|
{
|
||||||
getChild<LLThumbnailCtrl>("preview_thumbnail")->clearTexture();
|
mThumbnailCtrl->clearTexture();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
getChild<LLThumbnailCtrl>("preview_thumbnail")->setValue(id);
|
mThumbnailCtrl->setValue(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LLInventoryGalleryItem::setLoadImmediately(bool val)
|
||||||
|
{
|
||||||
|
mThumbnailCtrl->setInitImmediately(val);
|
||||||
|
}
|
||||||
|
|
||||||
void LLInventoryGalleryItem::draw()
|
void LLInventoryGalleryItem::draw()
|
||||||
{
|
{
|
||||||
if (isFadeItem())
|
if (isFadeItem())
|
||||||
|
|
@ -2667,7 +2685,7 @@ void LLInventoryGalleryItem::draw()
|
||||||
|
|
||||||
// Draw border
|
// Draw border
|
||||||
LLUIColor border_color = LLUIColorTable::instance().getColor(mSelected ? "MenuItemHighlightBgColor" : "TextFgTentativeColor", LLColor4::white);
|
LLUIColor border_color = LLUIColorTable::instance().getColor(mSelected ? "MenuItemHighlightBgColor" : "TextFgTentativeColor", LLColor4::white);
|
||||||
LLRect border = getChildView("preview_thumbnail")->getRect();
|
LLRect border = mThumbnailCtrl->getRect();
|
||||||
border.mRight = border.mRight + 1;
|
border.mRight = border.mRight + 1;
|
||||||
border.mTop = border.mTop + 1;
|
border.mTop = border.mTop + 1;
|
||||||
gl_rect_2d(border, border_color.get(), FALSE);
|
gl_rect_2d(border, border_color.get(), FALSE);
|
||||||
|
|
@ -2889,7 +2907,7 @@ void LLInventoryGalleryItem::updateNameText()
|
||||||
mNameText->setFont(getTextFont());
|
mNameText->setFont(getTextFont());
|
||||||
mNameText->setText(mItemName + mPermSuffix + mWornSuffix);
|
mNameText->setText(mItemName + mPermSuffix + mWornSuffix);
|
||||||
mNameText->setToolTip(mItemName + mPermSuffix + mWornSuffix);
|
mNameText->setToolTip(mItemName + mPermSuffix + mWornSuffix);
|
||||||
getChild<LLThumbnailCtrl>("preview_thumbnail")->setToolTip(mItemName + mPermSuffix + mWornSuffix);
|
mThumbnailCtrl->setToolTip(mItemName + mPermSuffix + mWornSuffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLInventoryGalleryItem::isFadeItem()
|
bool LLInventoryGalleryItem::isFadeItem()
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ class LLInventoryGalleryItem;
|
||||||
class LLScrollContainer;
|
class LLScrollContainer;
|
||||||
class LLTextBox;
|
class LLTextBox;
|
||||||
class LLThumbnailsObserver;
|
class LLThumbnailsObserver;
|
||||||
|
class LLThumbnailCtrl;
|
||||||
class LLGalleryGestureObserver;
|
class LLGalleryGestureObserver;
|
||||||
|
|
||||||
class LLInventoryGalleryContextMenu;
|
class LLInventoryGalleryContextMenu;
|
||||||
|
|
@ -246,6 +247,7 @@ private:
|
||||||
int mRowCount;
|
int mRowCount;
|
||||||
int mItemsAddedCount;
|
int mItemsAddedCount;
|
||||||
bool mGalleryCreated;
|
bool mGalleryCreated;
|
||||||
|
bool mLoadThumbnailsImmediately;
|
||||||
bool mNeedsArrange;
|
bool mNeedsArrange;
|
||||||
|
|
||||||
/* Params */
|
/* Params */
|
||||||
|
|
@ -342,6 +344,7 @@ public:
|
||||||
LLAssetType::EType getAssetType() { return mType; }
|
LLAssetType::EType getAssetType() { return mType; }
|
||||||
void setThumbnail(LLUUID id);
|
void setThumbnail(LLUUID id);
|
||||||
void setGallery(LLInventoryGallery* gallery) { mGallery = gallery; }
|
void setGallery(LLInventoryGallery* gallery) { mGallery = gallery; }
|
||||||
|
void setLoadImmediately(bool val);
|
||||||
bool isFolder() { return mIsFolder; }
|
bool isFolder() { return mIsFolder; }
|
||||||
bool isLink() { return mIsLink; }
|
bool isLink() { return mIsLink; }
|
||||||
EInventorySortGroup getSortGroup() { return mSortGroup; }
|
EInventorySortGroup getSortGroup() { return mSortGroup; }
|
||||||
|
|
@ -354,6 +357,7 @@ private:
|
||||||
LLUUID mUUID;
|
LLUUID mUUID;
|
||||||
LLTextBox* mNameText;
|
LLTextBox* mNameText;
|
||||||
LLPanel* mTextBgPanel;
|
LLPanel* mTextBgPanel;
|
||||||
|
LLThumbnailCtrl* mThumbnailCtrl;
|
||||||
bool mSelected;
|
bool mSelected;
|
||||||
bool mWorn;
|
bool mWorn;
|
||||||
bool mDefaultImage;
|
bool mDefaultImage;
|
||||||
|
|
|
||||||
|
|
@ -357,6 +357,7 @@ void LLInventoryModelBackgroundFetch::scheduleFolderFetch(const LLUUID& cat_id,
|
||||||
if (mFetchFolderQueue.empty() || mFetchFolderQueue.front().mUUID != cat_id)
|
if (mFetchFolderQueue.empty() || mFetchFolderQueue.front().mUUID != cat_id)
|
||||||
{
|
{
|
||||||
mBackgroundFetchActive = true;
|
mBackgroundFetchActive = true;
|
||||||
|
mFolderFetchActive = true;
|
||||||
|
|
||||||
// Specific folder requests go to front of queue.
|
// Specific folder requests go to front of queue.
|
||||||
mFetchFolderQueue.push_front(FetchQueueInfo(cat_id, forced ? FT_FORCED : FT_DEFAULT));
|
mFetchFolderQueue.push_front(FetchQueueInfo(cat_id, forced ? FT_FORCED : FT_DEFAULT));
|
||||||
|
|
@ -375,6 +376,61 @@ void LLInventoryModelBackgroundFetch::scheduleItemFetch(const LLUUID& item_id, b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LLInventoryModelBackgroundFetch::fetchFolderAndLinks(const LLUUID& cat_id, nullary_func_t callback)
|
||||||
|
{
|
||||||
|
LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
|
||||||
|
if (cat)
|
||||||
|
{
|
||||||
|
// Mark folder (update timer) so that background fetch won't request it
|
||||||
|
cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE);
|
||||||
|
}
|
||||||
|
incrFetchFolderCount(1);
|
||||||
|
mExpectedFolderIds.push_back(cat_id);
|
||||||
|
|
||||||
|
// Assume that we have no relevant cache. Fetch folder, and items folder's links point to.
|
||||||
|
AISAPI::FetchCategoryLinks(cat_id,
|
||||||
|
[callback, cat_id](const LLUUID& id)
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
if (id.isNull())
|
||||||
|
{
|
||||||
|
LL_WARNS() << "Failed to fetch category links " << cat_id << LL_ENDL;
|
||||||
|
}
|
||||||
|
LLInventoryModelBackgroundFetch::getInstance()->onAISFolderCalback(cat_id, id, FT_DEFAULT);
|
||||||
|
});
|
||||||
|
|
||||||
|
// start idle loop to track completion
|
||||||
|
mBackgroundFetchActive = true;
|
||||||
|
mFolderFetchActive = true;
|
||||||
|
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLInventoryModelBackgroundFetch::fetchCOF(nullary_func_t callback)
|
||||||
|
{
|
||||||
|
LLUUID cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
|
||||||
|
LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
|
||||||
|
if (cat)
|
||||||
|
{
|
||||||
|
// Mark cof (update timer) so that background fetch won't request it
|
||||||
|
cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE);
|
||||||
|
}
|
||||||
|
incrFetchFolderCount(1);
|
||||||
|
mExpectedFolderIds.push_back(cat_id);
|
||||||
|
// For reliability assume that we have no relevant cache, so
|
||||||
|
// fetch cof along with items cof's links point to.
|
||||||
|
AISAPI::FetchCOF([callback](const LLUUID& id)
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
LLUUID cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
|
||||||
|
LLInventoryModelBackgroundFetch::getInstance()->onAISFolderCalback(cat_id, id, FT_DEFAULT);
|
||||||
|
});
|
||||||
|
|
||||||
|
// start idle loop to track completion
|
||||||
|
mBackgroundFetchActive = true;
|
||||||
|
mFolderFetchActive = true;
|
||||||
|
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
void LLInventoryModelBackgroundFetch::findLostItems()
|
void LLInventoryModelBackgroundFetch::findLostItems()
|
||||||
{
|
{
|
||||||
mBackgroundFetchActive = true;
|
mBackgroundFetchActive = true;
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,13 @@ public:
|
||||||
void scheduleFolderFetch(const LLUUID& cat_id, bool forced = false);
|
void scheduleFolderFetch(const LLUUID& cat_id, bool forced = false);
|
||||||
void scheduleItemFetch(const LLUUID& item_id, bool forced = false);
|
void scheduleItemFetch(const LLUUID& item_id, bool forced = false);
|
||||||
|
|
||||||
|
typedef boost::function<void()> nullary_func_t;
|
||||||
|
// AIS3 only, Fetches folder and everithing links inside the folder point to
|
||||||
|
// Intended for outfits
|
||||||
|
void fetchFolderAndLinks(const LLUUID& cat_id, nullary_func_t callback);
|
||||||
|
// AIS3 only
|
||||||
|
void fetchCOF(nullary_func_t callback);
|
||||||
|
|
||||||
BOOL folderFetchActive() const;
|
BOOL folderFetchActive() const;
|
||||||
bool isEverythingFetched() const; // completing the fetch once per session should be sufficient
|
bool isEverythingFetched() const; // completing the fetch once per session should be sufficient
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -336,14 +336,6 @@ void LLInventoryFetchItemsObserver::startFetch()
|
||||||
if (aisv3)
|
if (aisv3)
|
||||||
{
|
{
|
||||||
for (requests_by_folders_t::value_type &folder : requests)
|
for (requests_by_folders_t::value_type &folder : requests)
|
||||||
{
|
|
||||||
if (folder.second.size() > MAX_INDIVIDUAL_ITEM_REQUESTS)
|
|
||||||
{
|
|
||||||
// requesting one by one will take a while
|
|
||||||
// do whole folder
|
|
||||||
LLInventoryModelBackgroundFetch::getInstance()->scheduleFolderFetch(folder.first, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
LLViewerInventoryCategory* cat = gInventory.getCategory(folder.first);
|
LLViewerInventoryCategory* cat = gInventory.getCategory(folder.first);
|
||||||
if (cat)
|
if (cat)
|
||||||
|
|
@ -353,6 +345,12 @@ void LLInventoryFetchItemsObserver::startFetch()
|
||||||
// start fetching whole folder since it's not ready either way
|
// start fetching whole folder since it's not ready either way
|
||||||
cat->fetch();
|
cat->fetch();
|
||||||
}
|
}
|
||||||
|
else if (folder.second.size() > MAX_INDIVIDUAL_ITEM_REQUESTS)
|
||||||
|
{
|
||||||
|
// requesting one by one will take a while
|
||||||
|
// do whole folder
|
||||||
|
LLInventoryModelBackgroundFetch::getInstance()->scheduleFolderFetch(folder.first, true);
|
||||||
|
}
|
||||||
else if (cat->getViewerDescendentCount() <= folder.second.size()
|
else if (cat->getViewerDescendentCount() <= folder.second.size()
|
||||||
|| cat->getDescendentCount() <= folder.second.size())
|
|| cat->getDescendentCount() <= folder.second.size())
|
||||||
{
|
{
|
||||||
|
|
@ -384,7 +382,6 @@ void LLInventoryFetchItemsObserver::startFetch()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fetch_items_from_llsd(items_llsd);
|
fetch_items_from_llsd(items_llsd);
|
||||||
|
|
@ -421,10 +418,22 @@ void LLInventoryFetchDescendentsObserver::changed(U32 mask)
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mIncomplete.empty())
|
if (mIncomplete.empty())
|
||||||
{
|
{
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LLInventoryModelBackgroundFetch* fetcher = LLInventoryModelBackgroundFetch::getInstance();
|
||||||
|
if (fetcher->isEverythingFetched()
|
||||||
|
&& !fetcher->folderFetchActive())
|
||||||
|
{
|
||||||
|
// If fetcher is done with folders yet we are waiting, fetch either
|
||||||
|
// failed or version is somehow stuck at -1
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLInventoryFetchDescendentsObserver::startFetch()
|
void LLInventoryFetchDescendentsObserver::startFetch()
|
||||||
|
|
@ -435,12 +444,8 @@ void LLInventoryFetchDescendentsObserver::startFetch()
|
||||||
if (!cat) continue;
|
if (!cat) continue;
|
||||||
if (!isCategoryComplete(cat))
|
if (!isCategoryComplete(cat))
|
||||||
{
|
{
|
||||||
// CHECK IT: isCategoryComplete() checks both version and descendant count but
|
//blindly fetch it without seeing if anything else is fetching it.
|
||||||
// fetch() only works for Unknown version and doesn't care about descentants,
|
LLInventoryModelBackgroundFetch::getInstance()->scheduleFolderFetch(*it, true);
|
||||||
// as result fetch won't start and folder will potentially get stuck as
|
|
||||||
// incomplete in observer.
|
|
||||||
// Likely either both should use only version or both should check descendants.
|
|
||||||
cat->fetch(); //blindly fetch it without seeing if anything else is fetching it.
|
|
||||||
mIncomplete.push_back(*it); //Add to list of things being downloaded for this observer.
|
mIncomplete.push_back(*it); //Add to list of things being downloaded for this observer.
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -74,40 +74,6 @@ std::string string_from_mask(MASK mask)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string string_from_mouse(EMouseClickType click, bool translate)
|
|
||||||
{
|
|
||||||
std::string res;
|
|
||||||
switch (click)
|
|
||||||
{
|
|
||||||
case CLICK_LEFT:
|
|
||||||
res = "LMB";
|
|
||||||
break;
|
|
||||||
case CLICK_MIDDLE:
|
|
||||||
res = "MMB";
|
|
||||||
break;
|
|
||||||
case CLICK_RIGHT:
|
|
||||||
res = "RMB";
|
|
||||||
break;
|
|
||||||
case CLICK_BUTTON4:
|
|
||||||
res = "MB4";
|
|
||||||
break;
|
|
||||||
case CLICK_BUTTON5:
|
|
||||||
res = "MB5";
|
|
||||||
break;
|
|
||||||
case CLICK_DOUBLELEFT:
|
|
||||||
res = "Double LMB";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (translate && !res.empty())
|
|
||||||
{
|
|
||||||
res = LLTrans::getString(res);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// LLKeyConflictHandler
|
// LLKeyConflictHandler
|
||||||
|
|
||||||
S32 LLKeyConflictHandler::sTemporaryFileUseCount = 0;
|
S32 LLKeyConflictHandler::sTemporaryFileUseCount = 0;
|
||||||
|
|
@ -270,7 +236,7 @@ std::string LLKeyConflictHandler::getStringFromKeyData(const LLKeyData& keydata)
|
||||||
result = LLKeyboard::stringFromAccelerator(keydata.mMask);
|
result = LLKeyboard::stringFromAccelerator(keydata.mMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
result += string_from_mouse(keydata.mMouse, true);
|
result += LLKeyboard::stringFromMouse(keydata.mMouse);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -545,7 +511,7 @@ void LLKeyConflictHandler::saveToSettings(bool temporary)
|
||||||
{
|
{
|
||||||
// set() because 'optional', for compatibility purposes
|
// set() because 'optional', for compatibility purposes
|
||||||
// just copy old keys.xml and rename to key_bindings.xml, it should work
|
// just copy old keys.xml and rename to key_bindings.xml, it should work
|
||||||
binding.mouse.set(string_from_mouse(data.mMouse, false), true);
|
binding.mouse.set(LLKeyboard::stringFromMouse(data.mMouse, false), true);
|
||||||
}
|
}
|
||||||
binding.command = iter->first;
|
binding.command = iter->first;
|
||||||
mode.bindings.add(binding);
|
mode.bindings.add(binding);
|
||||||
|
|
|
||||||
|
|
@ -391,7 +391,9 @@ LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p)
|
||||||
LL_WARNS() << "Error loading navigation bar context menu" << LL_ENDL;
|
LL_WARNS() << "Error loading navigation bar context menu" << LL_ENDL;
|
||||||
|
|
||||||
}
|
}
|
||||||
getTextEntry()->setRightMouseUpCallback(boost::bind(&LLLocationInputCtrl::onTextEditorRightClicked,this,_2,_3,_4));
|
//don't show default context menu
|
||||||
|
getTextEntry()->setShowContextMenu(false);
|
||||||
|
getTextEntry()->setRightMouseDownCallback(boost::bind(&LLLocationInputCtrl::onTextEditorRightClicked, this, _2, _3, _4));
|
||||||
updateWidgetlayout();
|
updateWidgetlayout();
|
||||||
|
|
||||||
// Connecting signal for updating location on "Show Coordinates" setting change.
|
// Connecting signal for updating location on "Show Coordinates" setting change.
|
||||||
|
|
|
||||||
|
|
@ -1077,7 +1077,7 @@ void LLGroupMoneyDetailsTabEventHandler::processReply(LLMessageSystem* msg,
|
||||||
msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_CurrentInterval, current_interval );
|
msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_CurrentInterval, current_interval );
|
||||||
msg->getStringFast(_PREHASH_MoneyData, _PREHASH_StartDate, start_date);
|
msg->getStringFast(_PREHASH_MoneyData, _PREHASH_StartDate, start_date);
|
||||||
|
|
||||||
std::string time_str = LLTrans::getString("GroupMoneyDate");
|
std::string time_str = LLTrans::getString("GroupMoneyStartDate");
|
||||||
LLSD substitution;
|
LLSD substitution;
|
||||||
|
|
||||||
// We don't do time zone corrections of the calculated number of seconds
|
// We don't do time zone corrections of the calculated number of seconds
|
||||||
|
|
@ -1232,7 +1232,7 @@ void LLGroupMoneySalesTabEventHandler::processReply(LLMessageSystem* msg,
|
||||||
// Start with the date.
|
// Start with the date.
|
||||||
if (text == mImplementationp->mLoadingText)
|
if (text == mImplementationp->mLoadingText)
|
||||||
{
|
{
|
||||||
std::string time_str = LLTrans::getString("GroupMoneyDate");
|
std::string time_str = LLTrans::getString("GroupMoneyStartDate");
|
||||||
LLSD substitution;
|
LLSD substitution;
|
||||||
|
|
||||||
// We don't do time zone corrections of the calculated number of seconds
|
// We don't do time zone corrections of the calculated number of seconds
|
||||||
|
|
|
||||||
|
|
@ -277,9 +277,9 @@ void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id)
|
||||||
//TODO: changes take two minutes to propagate!
|
//TODO: changes take two minutes to propagate!
|
||||||
// Add some storage that holds updated data for two minutes
|
// Add some storage that holds updated data for two minutes
|
||||||
// for new instances to reuse the data
|
// for new instances to reuse the data
|
||||||
// Profile data is only relevant to won avatar, but notes
|
// Profile data is only relevant to own avatar, but notes
|
||||||
// are for everybody
|
// are for everybody (no onger an issue?)
|
||||||
void put_avatar_properties_coro(std::string cap_url, LLUUID agent_id, LLSD data)
|
void put_avatar_properties_coro(std::string cap_url, LLUUID agent_id, LLSD data, std::function<void(bool)> callback)
|
||||||
{
|
{
|
||||||
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
|
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
|
||||||
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
|
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
|
||||||
|
|
@ -300,10 +300,16 @@ void put_avatar_properties_coro(std::string cap_url, LLUUID agent_id, LLSD data)
|
||||||
if (!status)
|
if (!status)
|
||||||
{
|
{
|
||||||
LL_WARNS("AvatarProperties") << "Failed to put agent information " << data << " for id " << agent_id << LL_ENDL;
|
LL_WARNS("AvatarProperties") << "Failed to put agent information " << data << " for id " << agent_id << LL_ENDL;
|
||||||
return;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LL_DEBUGS("AvatarProperties") << "Agent id: " << agent_id << " Data: " << data << " Result: " << httpResults << LL_ENDL;
|
||||||
}
|
}
|
||||||
|
|
||||||
LL_DEBUGS("AvatarProperties") << "Agent id: " << agent_id << " Data: " << data << " Result: " << httpResults << LL_ENDL;
|
if (callback)
|
||||||
|
{
|
||||||
|
callback(status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LLUUID post_profile_image(std::string cap_url, const LLSD &first_data, std::string path_to_image, LLHandle<LLPanel> *handle)
|
LLUUID post_profile_image(std::string cap_url, const LLSD &first_data, std::string path_to_image, LLHandle<LLPanel> *handle)
|
||||||
|
|
@ -448,6 +454,13 @@ void post_profile_image_coro(std::string cap_url, EProfileImageType type, std::s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == PROFILE_IMAGE_SL && result.notNull())
|
||||||
|
{
|
||||||
|
LLAvatarIconIDCache::getInstance()->add(gAgentID, result);
|
||||||
|
// Should trigger callbacks in icon controls
|
||||||
|
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(gAgentID);
|
||||||
|
}
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
LLFile::remove(path_to_image);
|
LLFile::remove(path_to_image);
|
||||||
delete handle;
|
delete handle;
|
||||||
|
|
@ -499,7 +512,8 @@ public:
|
||||||
return true; // don't block, will fail later
|
return true; // don't block, will fail later
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nav_type == NAV_TYPE_CLICKED)
|
if (nav_type == NAV_TYPE_CLICKED
|
||||||
|
|| nav_type == NAV_TYPE_EXTERNAL)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -1832,7 +1846,7 @@ void LLPanelProfileSecondLife::onShowInSearchCallback()
|
||||||
LLSD data;
|
LLSD data;
|
||||||
data["allow_publish"] = mAllowPublish;
|
data["allow_publish"] = mAllowPublish;
|
||||||
LLCoros::instance().launch("putAgentUserInfoCoro",
|
LLCoros::instance().launch("putAgentUserInfoCoro",
|
||||||
boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), data));
|
boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), data, nullptr));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -1847,7 +1861,7 @@ void LLPanelProfileSecondLife::onSaveDescriptionChanges()
|
||||||
if (!cap_url.empty())
|
if (!cap_url.empty())
|
||||||
{
|
{
|
||||||
LLCoros::instance().launch("putAgentUserInfoCoro",
|
LLCoros::instance().launch("putAgentUserInfoCoro",
|
||||||
boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("sl_about_text", mDescriptionText)));
|
boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("sl_about_text", mDescriptionText), nullptr));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -1993,10 +2007,19 @@ void LLPanelProfileSecondLife::onCommitProfileImage(const LLUUID& id)
|
||||||
std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
|
std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
|
||||||
if (!cap_url.empty())
|
if (!cap_url.empty())
|
||||||
{
|
{
|
||||||
|
std::function<void(bool)> callback = [id](bool result)
|
||||||
|
{
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
LLAvatarIconIDCache::getInstance()->add(gAgentID, id);
|
||||||
|
// Should trigger callbacks in icon controls
|
||||||
|
LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(gAgentID);
|
||||||
|
}
|
||||||
|
};
|
||||||
LLSD params;
|
LLSD params;
|
||||||
params["sl_image_id"] = id;
|
params["sl_image_id"] = id;
|
||||||
LLCoros::instance().launch("putAgentUserInfoCoro",
|
LLCoros::instance().launch("putAgentUserInfoCoro",
|
||||||
boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params));
|
boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params, callback));
|
||||||
|
|
||||||
mImageId = id;
|
mImageId = id;
|
||||||
if (mImageId == LLUUID::null)
|
if (mImageId == LLUUID::null)
|
||||||
|
|
@ -2332,7 +2355,7 @@ void LLPanelProfileFirstLife::onCommitPhoto(const LLUUID& id)
|
||||||
LLSD params;
|
LLSD params;
|
||||||
params["fl_image_id"] = id;
|
params["fl_image_id"] = id;
|
||||||
LLCoros::instance().launch("putAgentUserInfoCoro",
|
LLCoros::instance().launch("putAgentUserInfoCoro",
|
||||||
boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params));
|
boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params, nullptr));
|
||||||
|
|
||||||
mImageId = id;
|
mImageId = id;
|
||||||
if (mImageId.notNull())
|
if (mImageId.notNull())
|
||||||
|
|
@ -2376,7 +2399,7 @@ void LLPanelProfileFirstLife::onSaveDescriptionChanges()
|
||||||
if (!cap_url.empty())
|
if (!cap_url.empty())
|
||||||
{
|
{
|
||||||
LLCoros::instance().launch("putAgentUserInfoCoro",
|
LLCoros::instance().launch("putAgentUserInfoCoro",
|
||||||
boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("fl_about_text", mCurrentDescription)));
|
boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("fl_about_text", mCurrentDescription), nullptr));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -2519,7 +2542,7 @@ void LLPanelProfileNotes::onSaveNotesChanges()
|
||||||
if (!cap_url.empty())
|
if (!cap_url.empty())
|
||||||
{
|
{
|
||||||
LLCoros::instance().launch("putAgentUserInfoCoro",
|
LLCoros::instance().launch("putAgentUserInfoCoro",
|
||||||
boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("notes", mCurrentNotes)));
|
boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("notes", mCurrentNotes), nullptr));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,8 @@ public:
|
||||||
return true; // don't block, will fail later
|
return true; // don't block, will fail later
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nav_type == NAV_TYPE_CLICKED)
|
if (nav_type == NAV_TYPE_CLICKED
|
||||||
|
|| nav_type == NAV_TYPE_EXTERNAL)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,8 @@ public:
|
||||||
return true; // don't block, will fail later
|
return true; // don't block, will fail later
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nav_type == NAV_TYPE_CLICKED)
|
if (nav_type == NAV_TYPE_CLICKED
|
||||||
|
|| nav_type == NAV_TYPE_EXTERNAL)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -830,6 +830,42 @@ void LLSpatialGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* c
|
||||||
assert_states_valid(this);
|
assert_states_valid(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//virtual
|
||||||
|
void LLSpatialGroup::rebound()
|
||||||
|
{
|
||||||
|
if (!isDirty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
super::rebound();
|
||||||
|
|
||||||
|
if (mSpatialPartition->mDrawableType == LLPipeline::RENDER_TYPE_CONTROL_AV)
|
||||||
|
{
|
||||||
|
llassert(mSpatialPartition->mPartitionType == LLViewerRegion::PARTITION_CONTROL_AV);
|
||||||
|
|
||||||
|
LLSpatialBridge* bridge = getSpatialPartition()->asBridge();
|
||||||
|
if (bridge &&
|
||||||
|
bridge->mDrawable &&
|
||||||
|
bridge->mDrawable->getVObj() &&
|
||||||
|
bridge->mDrawable->getVObj()->isRoot())
|
||||||
|
{
|
||||||
|
LLControlAvatar* controlAvatar = bridge->mDrawable->getVObj()->getControlAvatar();
|
||||||
|
if (controlAvatar &&
|
||||||
|
controlAvatar->mDrawable &&
|
||||||
|
controlAvatar->mControlAVBridge)
|
||||||
|
{
|
||||||
|
llassert(controlAvatar->mControlAVBridge->mOctree);
|
||||||
|
|
||||||
|
LLSpatialGroup* root = (LLSpatialGroup*)controlAvatar->mControlAVBridge->mOctree->getListener(0);
|
||||||
|
if (this == root)
|
||||||
|
{
|
||||||
|
const LLVector4a* addingExtents = controlAvatar->mDrawable->getSpatialExtents();
|
||||||
|
const LLXformMatrix* currentTransform = bridge->mDrawable->getXform();
|
||||||
|
expandExtents(addingExtents, *currentTransform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LLSpatialGroup::destroyGLState(bool keep_occlusion)
|
void LLSpatialGroup::destroyGLState(bool keep_occlusion)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,7 @@ public:
|
||||||
LL_ALIGN_PREFIX(16)
|
LL_ALIGN_PREFIX(16)
|
||||||
class LLSpatialGroup : public LLOcclusionCullingGroup
|
class LLSpatialGroup : public LLOcclusionCullingGroup
|
||||||
{
|
{
|
||||||
|
using super = LLOcclusionCullingGroup;
|
||||||
friend class LLSpatialPartition;
|
friend class LLSpatialPartition;
|
||||||
friend class LLOctreeStateCheck;
|
friend class LLOctreeStateCheck;
|
||||||
public:
|
public:
|
||||||
|
|
@ -333,6 +334,9 @@ public:
|
||||||
virtual void handleDestruction(const TreeNode* node);
|
virtual void handleDestruction(const TreeNode* node);
|
||||||
virtual void handleChildAddition(const OctreeNode* parent, OctreeNode* child);
|
virtual void handleChildAddition(const OctreeNode* parent, OctreeNode* child);
|
||||||
|
|
||||||
|
// LLViewerOctreeGroup
|
||||||
|
virtual void rebound();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LL_ALIGN_16(LLVector4a mViewAngle);
|
LL_ALIGN_16(LLVector4a mViewAngle);
|
||||||
LL_ALIGN_16(LLVector4a mLastUpdateViewAngle);
|
LL_ALIGN_16(LLVector4a mLastUpdateViewAngle);
|
||||||
|
|
|
||||||
|
|
@ -2904,8 +2904,7 @@ void LLStartUp::loadInitialOutfit( const std::string& outfit_folder_name,
|
||||||
// Need to fetch cof contents before we can wear.
|
// Need to fetch cof contents before we can wear.
|
||||||
if (do_copy)
|
if (do_copy)
|
||||||
{
|
{
|
||||||
callAfterCategoryFetch(LLAppearanceMgr::instance().getCOF(),
|
callAfterCOFFetch(boost::bind(&LLAppearanceMgr::wearInventoryCategory, LLAppearanceMgr::getInstance(), cat, do_copy, do_append));
|
||||||
boost::bind(&LLAppearanceMgr::wearInventoryCategory, LLAppearanceMgr::getInstance(), cat, do_copy, do_append));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,8 @@ LLThumbnailCtrl::LLThumbnailCtrl(const LLThumbnailCtrl::Params& p)
|
||||||
, mFallbackImagep(p.fallback_image)
|
, mFallbackImagep(p.fallback_image)
|
||||||
, mInteractable(p.interactable())
|
, mInteractable(p.interactable())
|
||||||
, mShowLoadingPlaceholder(p.show_loading())
|
, mShowLoadingPlaceholder(p.show_loading())
|
||||||
, mPriority(LLGLTexture::BOOST_PREVIEW)
|
, mInited(false)
|
||||||
|
, mInitImmediately(true)
|
||||||
{
|
{
|
||||||
mLoadingPlaceholderString = LLTrans::getString("texture_loading");
|
mLoadingPlaceholderString = LLTrans::getString("texture_loading");
|
||||||
|
|
||||||
|
|
@ -84,6 +85,10 @@ LLThumbnailCtrl::~LLThumbnailCtrl()
|
||||||
|
|
||||||
void LLThumbnailCtrl::draw()
|
void LLThumbnailCtrl::draw()
|
||||||
{
|
{
|
||||||
|
if (!mInited)
|
||||||
|
{
|
||||||
|
initImage();
|
||||||
|
}
|
||||||
LLRect draw_rect = getLocalRect();
|
LLRect draw_rect = getLocalRect();
|
||||||
|
|
||||||
if (mBorderVisible)
|
if (mBorderVisible)
|
||||||
|
|
@ -171,11 +176,19 @@ void LLThumbnailCtrl::draw()
|
||||||
LLUICtrl::draw();
|
LLUICtrl::draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LLThumbnailCtrl::setVisible(BOOL visible)
|
||||||
|
{
|
||||||
|
if (!visible && mInited)
|
||||||
|
{
|
||||||
|
unloadImage();
|
||||||
|
}
|
||||||
|
LLUICtrl::setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
void LLThumbnailCtrl::clearTexture()
|
void LLThumbnailCtrl::clearTexture()
|
||||||
{
|
{
|
||||||
mImageAssetID = LLUUID::null;
|
setValue(LLSD());
|
||||||
mTexturep = nullptr;
|
mInited = true; // nothing to do
|
||||||
mImagep = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
|
|
@ -191,9 +204,32 @@ void LLThumbnailCtrl::setValue(const LLSD& value)
|
||||||
|
|
||||||
LLUICtrl::setValue(tvalue);
|
LLUICtrl::setValue(tvalue);
|
||||||
|
|
||||||
mImageAssetID = LLUUID::null;
|
unloadImage();
|
||||||
mTexturep = nullptr;
|
|
||||||
mImagep = nullptr;
|
if (mInitImmediately)
|
||||||
|
{
|
||||||
|
initImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL LLThumbnailCtrl::handleHover(S32 x, S32 y, MASK mask)
|
||||||
|
{
|
||||||
|
if (mInteractable && getEnabled())
|
||||||
|
{
|
||||||
|
getWindow()->setCursor(UI_CURSOR_HAND);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return LLUICtrl::handleHover(x, y, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLThumbnailCtrl::initImage()
|
||||||
|
{
|
||||||
|
if (mInited)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mInited = true;
|
||||||
|
LLSD tvalue = getValue();
|
||||||
|
|
||||||
if (tvalue.isUUID())
|
if (tvalue.isUUID())
|
||||||
{
|
{
|
||||||
|
|
@ -201,14 +237,12 @@ void LLThumbnailCtrl::setValue(const LLSD& value)
|
||||||
if (mImageAssetID.notNull())
|
if (mImageAssetID.notNull())
|
||||||
{
|
{
|
||||||
// Should it support baked textures?
|
// Should it support baked textures?
|
||||||
mTexturep = LLViewerTextureManager::getFetchedTexture(mImageAssetID, FTT_DEFAULT, MIPMAP_YES, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
|
mTexturep = LLViewerTextureManager::getFetchedTexture(mImageAssetID, FTT_DEFAULT, MIPMAP_YES, LLGLTexture::BOOST_THUMBNAIL);
|
||||||
|
|
||||||
mTexturep->setBoostLevel(mPriority);
|
|
||||||
mTexturep->forceToSaveRawImage(0);
|
mTexturep->forceToSaveRawImage(0);
|
||||||
|
|
||||||
S32 desired_draw_width = mTexturep->getWidth();
|
S32 desired_draw_width = MAX_IMAGE_SIZE;
|
||||||
S32 desired_draw_height = mTexturep->getHeight();
|
S32 desired_draw_height = MAX_IMAGE_SIZE;
|
||||||
|
|
||||||
mTexturep->setKnownDrawSize(desired_draw_width, desired_draw_height);
|
mTexturep->setKnownDrawSize(desired_draw_width, desired_draw_height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -226,14 +260,12 @@ void LLThumbnailCtrl::setValue(const LLSD& value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL LLThumbnailCtrl::handleHover(S32 x, S32 y, MASK mask)
|
void LLThumbnailCtrl::unloadImage()
|
||||||
{
|
{
|
||||||
if (mInteractable && getEnabled())
|
mImageAssetID = LLUUID::null;
|
||||||
{
|
mTexturep = nullptr;
|
||||||
getWindow()->setCursor(UI_CURSOR_HAND);
|
mImagep = nullptr;
|
||||||
return TRUE;
|
mInited = false;
|
||||||
}
|
|
||||||
return LLUICtrl::handleHover(x, y, mask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,17 +64,24 @@ public:
|
||||||
virtual ~LLThumbnailCtrl();
|
virtual ~LLThumbnailCtrl();
|
||||||
|
|
||||||
virtual void draw() override;
|
virtual void draw() override;
|
||||||
|
void setVisible(BOOL visible) override;
|
||||||
|
|
||||||
virtual void setValue(const LLSD& value ) override;
|
virtual void setValue(const LLSD& value ) override;
|
||||||
|
void setInitImmediately(bool val) { mInitImmediately = val; }
|
||||||
void clearTexture();
|
void clearTexture();
|
||||||
|
|
||||||
virtual BOOL handleHover(S32 x, S32 y, MASK mask) override;
|
virtual BOOL handleHover(S32 x, S32 y, MASK mask) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void initImage();
|
||||||
|
void unloadImage();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
S32 mPriority;
|
|
||||||
bool mBorderVisible;
|
bool mBorderVisible;
|
||||||
bool mInteractable;
|
bool mInteractable;
|
||||||
bool mShowLoadingPlaceholder;
|
bool mShowLoadingPlaceholder;
|
||||||
|
bool mInited;
|
||||||
|
bool mInitImmediately;
|
||||||
std::string mLoadingPlaceholderString;
|
std::string mLoadingPlaceholderString;
|
||||||
LLUUID mImageAssetID;
|
LLUUID mImageAssetID;
|
||||||
LLViewBorder* mBorder;
|
LLViewBorder* mBorder;
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,11 @@ public:
|
||||||
|
|
||||||
std::string fl_name = params[0].asString();
|
std::string fl_name = params[0].asString();
|
||||||
|
|
||||||
if (nav_type == NAV_TYPE_CLICKED)
|
// External browsers explicitly ask user about opening links
|
||||||
|
// so treat "external" same as "clicked" in this case,
|
||||||
|
// despite it being treated as untrusted.
|
||||||
|
if (nav_type == NAV_TYPE_CLICKED
|
||||||
|
|| nav_type == NAV_TYPE_EXTERNAL)
|
||||||
{
|
{
|
||||||
const std::list<std::string> blacklist_clicked = {
|
const std::list<std::string> blacklist_clicked = {
|
||||||
"camera_presets",
|
"camera_presets",
|
||||||
|
|
|
||||||
|
|
@ -991,34 +991,50 @@ LLViewerInput::LLViewerInput()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
LLViewerInput::~LLViewerInput()
|
||||||
BOOL LLViewerInput::modeFromString(const std::string& string, S32 *mode)
|
|
||||||
{
|
{
|
||||||
if (string == "FIRST_PERSON")
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool LLViewerInput::modeFromString(const std::string& string, S32 *mode)
|
||||||
|
{
|
||||||
|
if (string.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cmp_string = string;
|
||||||
|
LLStringUtil::toLower(cmp_string);
|
||||||
|
if (cmp_string == "first_person")
|
||||||
{
|
{
|
||||||
*mode = MODE_FIRST_PERSON;
|
*mode = MODE_FIRST_PERSON;
|
||||||
return TRUE;
|
return true;
|
||||||
}
|
}
|
||||||
else if (string == "THIRD_PERSON")
|
else if (cmp_string == "third_person")
|
||||||
{
|
{
|
||||||
*mode = MODE_THIRD_PERSON;
|
*mode = MODE_THIRD_PERSON;
|
||||||
return TRUE;
|
return true;
|
||||||
}
|
}
|
||||||
else if (string == "EDIT_AVATAR")
|
else if (cmp_string == "edit_avatar")
|
||||||
{
|
{
|
||||||
*mode = MODE_EDIT_AVATAR;
|
*mode = MODE_EDIT_AVATAR;
|
||||||
return TRUE;
|
return true;
|
||||||
}
|
}
|
||||||
else if (string == "SITTING")
|
else if (cmp_string == "sitting")
|
||||||
{
|
{
|
||||||
*mode = MODE_SITTING;
|
*mode = MODE_SITTING;
|
||||||
return TRUE;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
S32 val = atoi(string.c_str());
|
||||||
|
if (val >= 0 && val < MODE_COUNT)
|
||||||
{
|
{
|
||||||
*mode = MODE_THIRD_PERSON;
|
*mode = val;
|
||||||
return FALSE;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|
@ -1222,6 +1238,7 @@ BOOL LLViewerInput::bindKey(const S32 mode, const KEY key, const MASK mask, cons
|
||||||
bind.mKey = key;
|
bind.mKey = key;
|
||||||
bind.mMask = mask;
|
bind.mMask = mask;
|
||||||
bind.mFunction = function;
|
bind.mFunction = function;
|
||||||
|
bind.mFunctionName = function_name;
|
||||||
|
|
||||||
if (result->mIsGlobal)
|
if (result->mIsGlobal)
|
||||||
{
|
{
|
||||||
|
|
@ -1303,6 +1320,7 @@ BOOL LLViewerInput::bindMouse(const S32 mode, const EMouseClickType mouse, const
|
||||||
bind.mMouse = mouse;
|
bind.mMouse = mouse;
|
||||||
bind.mMask = mask;
|
bind.mMask = mask;
|
||||||
bind.mFunction = function;
|
bind.mFunction = function;
|
||||||
|
bind.mFunctionName = function_name;
|
||||||
|
|
||||||
if (result->mIsGlobal)
|
if (result->mIsGlobal)
|
||||||
{
|
{
|
||||||
|
|
@ -1801,3 +1819,49 @@ bool LLViewerInput::isMouseBindUsed(const EMouseClickType mouse, const MASK mask
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string LLViewerInput::getKeyBindingAsString(const std::string& mode, const std::string& control) const
|
||||||
|
{
|
||||||
|
S32 keyboard_mode;
|
||||||
|
if (!modeFromString(mode, &keyboard_mode))
|
||||||
|
{
|
||||||
|
keyboard_mode = getMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string res;
|
||||||
|
bool needs_separator = false;
|
||||||
|
|
||||||
|
// keybindings are sorted from having most mask to no mask (from restrictive to less restrictive),
|
||||||
|
// but it's visually better to present this data in reverse
|
||||||
|
std::vector<LLKeyboardBinding>::const_reverse_iterator iter_key = mKeyBindings[keyboard_mode].rbegin();
|
||||||
|
while (iter_key != mKeyBindings[keyboard_mode].rend())
|
||||||
|
{
|
||||||
|
if (iter_key->mFunctionName == control)
|
||||||
|
{
|
||||||
|
if (needs_separator)
|
||||||
|
{
|
||||||
|
res.append(" | ");
|
||||||
|
}
|
||||||
|
res.append(LLKeyboard::stringFromAccelerator(iter_key->mMask, iter_key->mKey));
|
||||||
|
needs_separator = true;
|
||||||
|
}
|
||||||
|
iter_key++;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<LLMouseBinding>::const_reverse_iterator iter_mouse = mMouseBindings[keyboard_mode].rbegin();
|
||||||
|
while (iter_mouse != mMouseBindings[keyboard_mode].rend())
|
||||||
|
{
|
||||||
|
if (iter_mouse->mFunctionName == control)
|
||||||
|
{
|
||||||
|
if (needs_separator)
|
||||||
|
{
|
||||||
|
res.append(" | ");
|
||||||
|
}
|
||||||
|
res.append(LLKeyboard::stringFromAccelerator(iter_mouse->mMask, iter_mouse->mMouse));
|
||||||
|
needs_separator = true;
|
||||||
|
}
|
||||||
|
iter_mouse++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,13 @@
|
||||||
#define LL_LLVIEWERINPUT_H
|
#define LL_LLVIEWERINPUT_H
|
||||||
|
|
||||||
#include "llkeyboard.h" // For EKeystate
|
#include "llkeyboard.h" // For EKeystate
|
||||||
#include "llinitparam.h"
|
|
||||||
|
|
||||||
const S32 MAX_KEY_BINDINGS = 128; // was 60
|
const S32 MAX_KEY_BINDINGS = 128; // was 60
|
||||||
const S32 keybindings_xml_version = 1;
|
const S32 keybindings_xml_version = 1;
|
||||||
const std::string script_mouse_handler_name = "script_trigger_lbutton";
|
const std::string script_mouse_handler_name = "script_trigger_lbutton";
|
||||||
|
|
||||||
|
class LLWindow;
|
||||||
|
|
||||||
class LLNamedFunction
|
class LLNamedFunction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -51,6 +52,7 @@ public:
|
||||||
MASK mMask;
|
MASK mMask;
|
||||||
|
|
||||||
LLKeyFunc mFunction;
|
LLKeyFunc mFunction;
|
||||||
|
std::string mFunctionName;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LLMouseBinding
|
class LLMouseBinding
|
||||||
|
|
@ -60,6 +62,7 @@ public:
|
||||||
MASK mMask;
|
MASK mMask;
|
||||||
|
|
||||||
LLKeyFunc mFunction;
|
LLKeyFunc mFunction;
|
||||||
|
std::string mFunctionName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -72,11 +75,7 @@ typedef enum e_keyboard_mode
|
||||||
MODE_COUNT
|
MODE_COUNT
|
||||||
} EKeyboardMode;
|
} EKeyboardMode;
|
||||||
|
|
||||||
class LLWindow;
|
class LLViewerInput : public LLKeyBindingToStringHandler
|
||||||
|
|
||||||
void bind_keyboard_functions();
|
|
||||||
|
|
||||||
class LLViewerInput
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct KeyBinding : public LLInitParam::Block<KeyBinding>
|
struct KeyBinding : public LLInitParam::Block<KeyBinding>
|
||||||
|
|
@ -107,6 +106,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
LLViewerInput();
|
LLViewerInput();
|
||||||
|
virtual ~LLViewerInput();
|
||||||
|
|
||||||
BOOL handleKey(KEY key, MASK mask, BOOL repeated);
|
BOOL handleKey(KEY key, MASK mask, BOOL repeated);
|
||||||
BOOL handleKeyUp(KEY key, MASK mask);
|
BOOL handleKeyUp(KEY key, MASK mask);
|
||||||
|
|
@ -121,7 +121,7 @@ public:
|
||||||
S32 loadBindingsXML(const std::string& filename); // returns number bound, 0 on error
|
S32 loadBindingsXML(const std::string& filename); // returns number bound, 0 on error
|
||||||
EKeyboardMode getMode() const;
|
EKeyboardMode getMode() const;
|
||||||
|
|
||||||
static BOOL modeFromString(const std::string& string, S32 *mode); // False on failure
|
static bool modeFromString(const std::string& string, S32 *mode); // False on failure
|
||||||
static BOOL mouseFromString(const std::string& string, EMouseClickType *mode);// False on failure
|
static BOOL mouseFromString(const std::string& string, EMouseClickType *mode);// False on failure
|
||||||
|
|
||||||
bool scanKey(KEY key,
|
bool scanKey(KEY key,
|
||||||
|
|
@ -136,6 +136,9 @@ public:
|
||||||
bool isMouseBindUsed(const EMouseClickType mouse, const MASK mask, const S32 mode) const;
|
bool isMouseBindUsed(const EMouseClickType mouse, const MASK mask, const S32 mode) const;
|
||||||
bool isLMouseHandlingDefault(const S32 mode) const { return mLMouseDefaultHandling[mode]; }
|
bool isLMouseHandlingDefault(const S32 mode) const { return mLMouseDefaultHandling[mode]; }
|
||||||
|
|
||||||
|
// inherited from LLKeyBindingToStringHandler
|
||||||
|
virtual std::string getKeyBindingAsString(const std::string& mode, const std::string& control) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool scanKey(const std::vector<LLKeyboardBinding> &binding,
|
bool scanKey(const std::vector<LLKeyboardBinding> &binding,
|
||||||
S32 binding_count,
|
S32 binding_count,
|
||||||
|
|
|
||||||
|
|
@ -232,8 +232,28 @@ LLLocalizedInventoryItemsDictionary::LLLocalizedInventoryItemsDictionary()
|
||||||
class LLInventoryHandler : public LLCommandHandler
|
class LLInventoryHandler : public LLCommandHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// requires trusted browser to trigger
|
LLInventoryHandler() : LLCommandHandler("inventory", UNTRUSTED_THROTTLE) { }
|
||||||
LLInventoryHandler() : LLCommandHandler("inventory", UNTRUSTED_CLICK_ONLY) { }
|
|
||||||
|
virtual bool canHandleUntrusted(
|
||||||
|
const LLSD& params,
|
||||||
|
const LLSD& query_map,
|
||||||
|
LLMediaCtrl* web,
|
||||||
|
const std::string& nav_type)
|
||||||
|
{
|
||||||
|
if (params.size() < 1)
|
||||||
|
{
|
||||||
|
return true; // don't block, will fail later
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nav_type == NAV_TYPE_CLICKED
|
||||||
|
|| nav_type == NAV_TYPE_EXTERNAL)
|
||||||
|
{
|
||||||
|
// NAV_TYPE_EXTERNAL will be throttled
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool handle(const LLSD& params,
|
bool handle(const LLSD& params,
|
||||||
const LLSD& query_map,
|
const LLSD& query_map,
|
||||||
|
|
@ -1151,14 +1171,29 @@ void create_inventory_item(
|
||||||
gAgent.sendReliableMessage();
|
gAgent.sendReliableMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void create_inventory_callingcard(const LLUUID& avatar_id, const LLUUID& parent /*= LLUUID::null*/, LLPointer<LLInventoryCallback> cb/*=NULL*/)
|
void create_inventory_callingcard_callback(LLPointer<LLInventoryCallback> cb,
|
||||||
|
const LLUUID &parent,
|
||||||
|
const LLUUID &avatar_id,
|
||||||
|
const LLAvatarName &av_name)
|
||||||
{
|
{
|
||||||
std::string item_desc = avatar_id.asString();
|
std::string item_desc = avatar_id.asString();
|
||||||
|
create_inventory_item(gAgent.getID(),
|
||||||
|
gAgent.getSessionID(),
|
||||||
|
parent,
|
||||||
|
LLTransactionID::tnull,
|
||||||
|
av_name.getUserName(),
|
||||||
|
item_desc,
|
||||||
|
LLAssetType::AT_CALLINGCARD,
|
||||||
|
LLInventoryType::IT_CALLINGCARD,
|
||||||
|
NO_INV_SUBTYPE,
|
||||||
|
PERM_MOVE | PERM_TRANSFER,
|
||||||
|
cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_inventory_callingcard(const LLUUID& avatar_id, const LLUUID& parent /*= LLUUID::null*/, LLPointer<LLInventoryCallback> cb/*=NULL*/)
|
||||||
|
{
|
||||||
LLAvatarName av_name;
|
LLAvatarName av_name;
|
||||||
LLAvatarNameCache::get(avatar_id, &av_name);
|
LLAvatarNameCache::get(avatar_id, boost::bind(&create_inventory_callingcard_callback, cb, parent, _1, _2));
|
||||||
create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
|
|
||||||
parent, LLTransactionID::tnull, av_name.getUserName(), item_desc, LLAssetType::AT_CALLINGCARD,
|
|
||||||
LLInventoryType::IT_CALLINGCARD, NO_INV_SUBTYPE, PERM_MOVE | PERM_TRANSFER, cb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void create_inventory_wearable(const LLUUID& agent_id, const LLUUID& session_id,
|
void create_inventory_wearable(const LLUUID& agent_id, const LLUUID& session_id,
|
||||||
|
|
|
||||||
|
|
@ -3485,6 +3485,12 @@ void LLViewerObject::doInventoryCallback()
|
||||||
|
|
||||||
void LLViewerObject::removeInventory(const LLUUID& item_id)
|
void LLViewerObject::removeInventory(const LLUUID& item_id)
|
||||||
{
|
{
|
||||||
|
// close associated floater properties
|
||||||
|
LLSD params;
|
||||||
|
params["id"] = item_id;
|
||||||
|
params["object"] = mID;
|
||||||
|
LLFloaterReg::hideInstance("item_properties", params);
|
||||||
|
|
||||||
LLMessageSystem* msg = gMessageSystem;
|
LLMessageSystem* msg = gMessageSystem;
|
||||||
msg->newMessageFast(_PREHASH_RemoveTaskInventory);
|
msg->newMessageFast(_PREHASH_RemoveTaskInventory);
|
||||||
msg->nextBlockFast(_PREHASH_AgentData);
|
msg->nextBlockFast(_PREHASH_AgentData);
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,8 @@ S32 LLViewerTexture::sMaxSculptRez = 128; //max sculpt image size
|
||||||
const S32 MAX_CACHED_RAW_IMAGE_AREA = 64 * 64;
|
const S32 MAX_CACHED_RAW_IMAGE_AREA = 64 * 64;
|
||||||
const S32 MAX_CACHED_RAW_SCULPT_IMAGE_AREA = LLViewerTexture::sMaxSculptRez * LLViewerTexture::sMaxSculptRez;
|
const S32 MAX_CACHED_RAW_SCULPT_IMAGE_AREA = LLViewerTexture::sMaxSculptRez * LLViewerTexture::sMaxSculptRez;
|
||||||
const S32 MAX_CACHED_RAW_TERRAIN_IMAGE_AREA = 128 * 128;
|
const S32 MAX_CACHED_RAW_TERRAIN_IMAGE_AREA = 128 * 128;
|
||||||
const S32 DEFAULT_ICON_DIMENTIONS = 32;
|
const S32 DEFAULT_ICON_DIMENSIONS = 32;
|
||||||
|
const S32 DEFAULT_THUMBNAIL_DIMENSIONS = 256;
|
||||||
U32 LLViewerTexture::sMinLargeImageSize = 65536; //256 * 256.
|
U32 LLViewerTexture::sMinLargeImageSize = 65536; //256 * 256.
|
||||||
U32 LLViewerTexture::sMaxSmallImageSize = MAX_CACHED_RAW_IMAGE_AREA;
|
U32 LLViewerTexture::sMaxSmallImageSize = MAX_CACHED_RAW_IMAGE_AREA;
|
||||||
bool LLViewerTexture::sFreezeImageUpdates = false;
|
bool LLViewerTexture::sFreezeImageUpdates = false;
|
||||||
|
|
@ -665,7 +666,8 @@ void LLViewerTexture::setBoostLevel(S32 level)
|
||||||
mBoostLevel = level;
|
mBoostLevel = level;
|
||||||
if(mBoostLevel != LLViewerTexture::BOOST_NONE &&
|
if(mBoostLevel != LLViewerTexture::BOOST_NONE &&
|
||||||
mBoostLevel != LLViewerTexture::BOOST_SELECTED &&
|
mBoostLevel != LLViewerTexture::BOOST_SELECTED &&
|
||||||
mBoostLevel != LLViewerTexture::BOOST_ICON)
|
mBoostLevel != LLViewerTexture::BOOST_ICON &&
|
||||||
|
mBoostLevel != LLViewerTexture::BOOST_THUMBNAIL)
|
||||||
{
|
{
|
||||||
setNoDelete();
|
setNoDelete();
|
||||||
}
|
}
|
||||||
|
|
@ -1180,8 +1182,19 @@ void LLViewerFetchedTexture::loadFromFastCache()
|
||||||
{
|
{
|
||||||
// Shouldn't do anything usefull since texures in fast cache are 16x16,
|
// Shouldn't do anything usefull since texures in fast cache are 16x16,
|
||||||
// it is here in case fast cache changes.
|
// it is here in case fast cache changes.
|
||||||
S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENTIONS;
|
S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENSIONS;
|
||||||
S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENTIONS;
|
S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENSIONS;
|
||||||
|
if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height))
|
||||||
|
{
|
||||||
|
// scale oversized icon, no need to give more work to gl
|
||||||
|
mRawImage->scale(expected_width, expected_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mBoostLevel == LLGLTexture::BOOST_THUMBNAIL)
|
||||||
|
{
|
||||||
|
S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_THUMBNAIL_DIMENSIONS;
|
||||||
|
S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_THUMBNAIL_DIMENSIONS;
|
||||||
if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height))
|
if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height))
|
||||||
{
|
{
|
||||||
// scale oversized icon, no need to give more work to gl
|
// scale oversized icon, no need to give more work to gl
|
||||||
|
|
@ -1682,7 +1695,7 @@ void LLViewerFetchedTexture::processTextureStats()
|
||||||
{
|
{
|
||||||
mDesiredDiscardLevel = 0;
|
mDesiredDiscardLevel = 0;
|
||||||
}
|
}
|
||||||
else if (mDontDiscard && mBoostLevel == LLGLTexture::BOOST_ICON)
|
else if (mDontDiscard && (mBoostLevel == LLGLTexture::BOOST_ICON || mBoostLevel == LLGLTexture::BOOST_THUMBNAIL))
|
||||||
{
|
{
|
||||||
if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT)
|
if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT)
|
||||||
{
|
{
|
||||||
|
|
@ -1916,8 +1929,20 @@ bool LLViewerFetchedTexture::updateFetch()
|
||||||
|
|
||||||
if (mBoostLevel == LLGLTexture::BOOST_ICON)
|
if (mBoostLevel == LLGLTexture::BOOST_ICON)
|
||||||
{
|
{
|
||||||
S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENTIONS;
|
S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENSIONS;
|
||||||
S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENTIONS;
|
S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENSIONS;
|
||||||
|
if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height))
|
||||||
|
{
|
||||||
|
// scale oversized icon, no need to give more work to gl
|
||||||
|
// since we got mRawImage from thread worker and image may be in use (ex: writing cache), make a copy
|
||||||
|
mRawImage = mRawImage->scaled(expected_width, expected_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mBoostLevel == LLGLTexture::BOOST_THUMBNAIL)
|
||||||
|
{
|
||||||
|
S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_THUMBNAIL_DIMENSIONS;
|
||||||
|
S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_THUMBNAIL_DIMENSIONS;
|
||||||
if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height))
|
if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height))
|
||||||
{
|
{
|
||||||
// scale oversized icon, no need to give more work to gl
|
// scale oversized icon, no need to give more work to gl
|
||||||
|
|
@ -2652,7 +2677,9 @@ LLImageRaw* LLViewerFetchedTexture::reloadRawImage(S8 discard_level)
|
||||||
|
|
||||||
if(mSavedRawDiscardLevel >= 0 && mSavedRawDiscardLevel <= discard_level)
|
if(mSavedRawDiscardLevel >= 0 && mSavedRawDiscardLevel <= discard_level)
|
||||||
{
|
{
|
||||||
if (mSavedRawDiscardLevel != discard_level && mBoostLevel != BOOST_ICON)
|
if (mSavedRawDiscardLevel != discard_level
|
||||||
|
&& mBoostLevel != BOOST_ICON
|
||||||
|
&& mBoostLevel != BOOST_THUMBNAIL)
|
||||||
{
|
{
|
||||||
mRawImage = new LLImageRaw(getWidth(discard_level), getHeight(discard_level), getComponents());
|
mRawImage = new LLImageRaw(getWidth(discard_level), getHeight(discard_level), getComponents());
|
||||||
mRawImage->copy(getSavedRawImage());
|
mRawImage->copy(getSavedRawImage());
|
||||||
|
|
@ -2759,8 +2786,22 @@ void LLViewerFetchedTexture::setCachedRawImage(S32 discard_level, LLImageRaw* im
|
||||||
{
|
{
|
||||||
if (mBoostLevel == LLGLTexture::BOOST_ICON)
|
if (mBoostLevel == LLGLTexture::BOOST_ICON)
|
||||||
{
|
{
|
||||||
S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENTIONS;
|
S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENSIONS;
|
||||||
S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENTIONS;
|
S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENSIONS;
|
||||||
|
if (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)
|
||||||
|
{
|
||||||
|
mCachedRawImage = new LLImageRaw(expected_width, expected_height, imageraw->getComponents());
|
||||||
|
mCachedRawImage->copyScaled(imageraw);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mCachedRawImage = imageraw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mBoostLevel == LLGLTexture::BOOST_THUMBNAIL)
|
||||||
|
{
|
||||||
|
S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_THUMBNAIL_DIMENSIONS;
|
||||||
|
S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_THUMBNAIL_DIMENSIONS;
|
||||||
if (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)
|
if (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)
|
||||||
{
|
{
|
||||||
mCachedRawImage = new LLImageRaw(expected_width, expected_height, imageraw->getComponents());
|
mCachedRawImage = new LLImageRaw(expected_width, expected_height, imageraw->getComponents());
|
||||||
|
|
@ -2867,8 +2908,22 @@ void LLViewerFetchedTexture::saveRawImage()
|
||||||
mSavedRawDiscardLevel = mRawDiscardLevel;
|
mSavedRawDiscardLevel = mRawDiscardLevel;
|
||||||
if (mBoostLevel == LLGLTexture::BOOST_ICON)
|
if (mBoostLevel == LLGLTexture::BOOST_ICON)
|
||||||
{
|
{
|
||||||
S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENTIONS;
|
S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENSIONS;
|
||||||
S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENTIONS;
|
S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENSIONS;
|
||||||
|
if (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)
|
||||||
|
{
|
||||||
|
mSavedRawImage = new LLImageRaw(expected_width, expected_height, mRawImage->getComponents());
|
||||||
|
mSavedRawImage->copyScaled(mRawImage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mSavedRawImage = new LLImageRaw(mRawImage->getData(), mRawImage->getWidth(), mRawImage->getHeight(), mRawImage->getComponents());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mBoostLevel == LLGLTexture::BOOST_THUMBNAIL)
|
||||||
|
{
|
||||||
|
S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_THUMBNAIL_DIMENSIONS;
|
||||||
|
S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_THUMBNAIL_DIMENSIONS;
|
||||||
if (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)
|
if (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)
|
||||||
{
|
{
|
||||||
mSavedRawImage = new LLImageRaw(expected_width, expected_height, mRawImage->getComponents());
|
mSavedRawImage = new LLImageRaw(expected_width, expected_height, mRawImage->getComponents());
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ LLViewerTextureList gTextureList;
|
||||||
|
|
||||||
ETexListType get_element_type(S32 priority)
|
ETexListType get_element_type(S32 priority)
|
||||||
{
|
{
|
||||||
return (priority == LLViewerFetchedTexture::BOOST_ICON) ? TEX_LIST_SCALE : TEX_LIST_STANDARD;
|
return (priority == LLViewerFetchedTexture::BOOST_ICON || priority == LLViewerFetchedTexture::BOOST_THUMBNAIL) ? TEX_LIST_SCALE : TEX_LIST_STANDARD;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
@ -492,7 +492,8 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string&
|
||||||
{
|
{
|
||||||
imagep->dontDiscard();
|
imagep->dontDiscard();
|
||||||
}
|
}
|
||||||
if (boost_priority == LLViewerFetchedTexture::BOOST_ICON)
|
if (boost_priority == LLViewerFetchedTexture::BOOST_ICON
|
||||||
|
|| boost_priority == LLViewerFetchedTexture::BOOST_THUMBNAIL)
|
||||||
{
|
{
|
||||||
// Agent and group Icons are downloadable content, nothing manages
|
// Agent and group Icons are downloadable content, nothing manages
|
||||||
// icon deletion yet, so they should not persist
|
// icon deletion yet, so they should not persist
|
||||||
|
|
@ -604,7 +605,8 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id,
|
||||||
{
|
{
|
||||||
imagep->dontDiscard();
|
imagep->dontDiscard();
|
||||||
}
|
}
|
||||||
if (boost_priority == LLViewerFetchedTexture::BOOST_ICON)
|
if (boost_priority == LLViewerFetchedTexture::BOOST_ICON
|
||||||
|
|| boost_priority == LLViewerFetchedTexture::BOOST_THUMBNAIL)
|
||||||
{
|
{
|
||||||
// Agent and group Icons are downloadable content, nothing manages
|
// Agent and group Icons are downloadable content, nothing manages
|
||||||
// icon deletion yet, so they should not persist.
|
// icon deletion yet, so they should not persist.
|
||||||
|
|
@ -1510,8 +1512,9 @@ LLUIImagePtr LLUIImageList::loadUIImage(LLViewerFetchedTexture* imagep, const st
|
||||||
LLUIImagePtr new_imagep = new LLUIImage(name, imagep);
|
LLUIImagePtr new_imagep = new LLUIImage(name, imagep);
|
||||||
new_imagep->setScaleStyle(scale_style);
|
new_imagep->setScaleStyle(scale_style);
|
||||||
|
|
||||||
if (imagep->getBoostLevel() != LLGLTexture::BOOST_ICON &&
|
if (imagep->getBoostLevel() != LLGLTexture::BOOST_ICON
|
||||||
imagep->getBoostLevel() != LLGLTexture::BOOST_PREVIEW)
|
&& imagep->getBoostLevel() != LLGLTexture::BOOST_THUMBNAIL
|
||||||
|
&& imagep->getBoostLevel() != LLGLTexture::BOOST_PREVIEW)
|
||||||
{
|
{
|
||||||
// Don't add downloadable content into this list
|
// Don't add downloadable content into this list
|
||||||
// all UI images are non-deletable and list does not support deletion
|
// all UI images are non-deletable and list does not support deletion
|
||||||
|
|
|
||||||
|
|
@ -5233,6 +5233,9 @@ U32 LLVOAvatar::renderRigid()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool should_alpha_mask = shouldAlphaMask();
|
||||||
|
LLGLState test(GL_ALPHA_TEST, should_alpha_mask);
|
||||||
|
|
||||||
if (isTextureVisible(TEX_EYES_BAKED) || (getOverallAppearance() == AOA_JELLYDOLL && !isControlAvatar()) || isUIAvatar())
|
if (isTextureVisible(TEX_EYES_BAKED) || (getOverallAppearance() == AOA_JELLYDOLL && !isControlAvatar()) || isUIAvatar())
|
||||||
{
|
{
|
||||||
LLViewerJoint* eyeball_left = getViewerJoint(MESH_ID_EYEBALL_LEFT);
|
LLViewerJoint* eyeball_left = getViewerJoint(MESH_ID_EYEBALL_LEFT);
|
||||||
|
|
|
||||||
|
|
@ -960,7 +960,7 @@ void LLVOAvatarSelf::updateRegion(LLViewerRegion *regionp)
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
// draw tractor beam when editing objects
|
// draw tractor (selection) beam when editing objects
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
//virtual
|
//virtual
|
||||||
void LLVOAvatarSelf::idleUpdateTractorBeam()
|
void LLVOAvatarSelf::idleUpdateTractorBeam()
|
||||||
|
|
@ -1248,6 +1248,27 @@ BOOL LLVOAvatarSelf::detachObject(LLViewerObject *viewer_object)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LLVOAvatarSelf::hasAttachmentsInTrash()
|
||||||
|
{
|
||||||
|
const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
|
||||||
|
|
||||||
|
for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter)
|
||||||
|
{
|
||||||
|
LLViewerJointAttachment *attachment = iter->second;
|
||||||
|
for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
|
||||||
|
attachment_iter != attachment->mAttachedObjects.end();
|
||||||
|
++attachment_iter)
|
||||||
|
{
|
||||||
|
LLViewerObject *attached_object = attachment_iter->get();
|
||||||
|
if (attached_object && gInventory.isObjectDescendentOf(attached_object->getAttachmentItemID(), trash_id))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
BOOL LLVOAvatarSelf::detachAttachmentIntoInventory(const LLUUID &item_id)
|
BOOL LLVOAvatarSelf::detachAttachmentIntoInventory(const LLUUID &item_id)
|
||||||
{
|
{
|
||||||
|
|
@ -2799,10 +2820,12 @@ BOOL LLVOAvatarSelf::needsRenderBeam()
|
||||||
LLTool *tool = LLToolMgr::getInstance()->getCurrentTool();
|
LLTool *tool = LLToolMgr::getInstance()->getCurrentTool();
|
||||||
|
|
||||||
BOOL is_touching_or_grabbing = (tool == LLToolGrab::getInstance() && LLToolGrab::getInstance()->isEditing());
|
BOOL is_touching_or_grabbing = (tool == LLToolGrab::getInstance() && LLToolGrab::getInstance()->isEditing());
|
||||||
if (LLToolGrab::getInstance()->getEditingObject() &&
|
LLViewerObject* objp = LLToolGrab::getInstance()->getEditingObject();
|
||||||
LLToolGrab::getInstance()->getEditingObject()->isAttachment())
|
if (objp // might need to be "!objp ||" instead of "objp &&".
|
||||||
|
&& (objp->isAttachment() || objp->isAvatar()))
|
||||||
{
|
{
|
||||||
// don't render selection beam on hud objects
|
// don't render grab tool's selection beam on hud objects,
|
||||||
|
// attachments or avatars
|
||||||
is_touching_or_grabbing = FALSE;
|
is_touching_or_grabbing = FALSE;
|
||||||
}
|
}
|
||||||
return is_touching_or_grabbing || (getAttachmentState() & AGENT_STATE_EDITING && LLSelectMgr::getInstance()->shouldShowSelection());
|
return is_touching_or_grabbing || (getAttachmentState() & AGENT_STATE_EDITING && LLSelectMgr::getInstance()->shouldShowSelection());
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,8 @@ public:
|
||||||
/*virtual*/ BOOL detachObject(LLViewerObject *viewer_object);
|
/*virtual*/ BOOL detachObject(LLViewerObject *viewer_object);
|
||||||
static BOOL detachAttachmentIntoInventory(const LLUUID& item_id);
|
static BOOL detachAttachmentIntoInventory(const LLUUID& item_id);
|
||||||
|
|
||||||
|
bool hasAttachmentsInTrash();
|
||||||
|
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
// HUDs
|
// HUDs
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,17 @@
|
||||||
<text name="ItemcountText">
|
<text name="ItemcountText">
|
||||||
Genstande:
|
Genstande:
|
||||||
</text>
|
</text>
|
||||||
|
<layout_stack name="top_stack">
|
||||||
|
<layout_panel name="filter_layout_panel">
|
||||||
<filter_editor label="Filter" name="inventory search editor"/>
|
<filter_editor label="Filter" name="inventory search editor"/>
|
||||||
|
<button name="options_gear_btn" tool_tip="Vis yderligere valg"/>
|
||||||
|
<button name="add_btn" tool_tip="Tilføj ny genstand"/>
|
||||||
|
</layout_panel>
|
||||||
|
</layout_stack>
|
||||||
|
<panel name="default_inventory_panel">
|
||||||
<tab_container name="inventory filter tabs">
|
<tab_container name="inventory filter tabs">
|
||||||
<inventory_panel label="Alle ting" name="All Items"/>
|
<inventory_panel label="Alle ting" name="All Items"/>
|
||||||
<recent_inventory_panel label="Nye ting" name="Recent Items"/>
|
<recent_inventory_panel label="Nye ting" name="Recent Items"/>
|
||||||
</tab_container>
|
</tab_container>
|
||||||
<layout_stack name="bottom_panel">
|
</panel>
|
||||||
<layout_panel name="options_gear_btn_panel">
|
|
||||||
<button name="options_gear_btn" tool_tip="Vis yderligere valg"/>
|
|
||||||
</layout_panel>
|
|
||||||
<layout_panel name="add_btn_panel">
|
|
||||||
<button name="add_btn" tool_tip="Tilføj ny genstand"/>
|
|
||||||
</layout_panel>
|
|
||||||
<layout_panel name="trash_btn_panel">
|
|
||||||
<dnd_button name="trash_btn" tool_tip="Fjern valgte genstand"/>
|
|
||||||
</layout_panel>
|
|
||||||
</layout_stack>
|
|
||||||
</panel>
|
</panel>
|
||||||
|
|
|
||||||
|
|
@ -13,27 +13,24 @@
|
||||||
<text name="ItemcountText">
|
<text name="ItemcountText">
|
||||||
Objekte:
|
Objekte:
|
||||||
</text>
|
</text>
|
||||||
<filter_editor label="Suchtext eingeben" name="inventory search editor"/>
|
<layout_stack name="top_stack">
|
||||||
|
<layout_panel name="filter_layout_panel">
|
||||||
<combo_box name="search_type">
|
<combo_box name="search_type">
|
||||||
<item label="Name" name="Name" value="search_by_name"/>
|
<item label="Name" name="Name" value="search_by_name"/>
|
||||||
<item label="Ersteller" name="Creator" value="search_by_creator"/>
|
<item label="Ersteller" name="Creator" value="search_by_creator"/>
|
||||||
<item label="Beschreibung" name="Description" value="search_by_description"/>
|
<item label="Beschreibung" name="Description" value="search_by_description"/>
|
||||||
<item label="UUID" name="UUID" value="search_by_UUID"/>
|
<item label="UUID" name="UUID" value="search_by_UUID"/>
|
||||||
</combo_box>
|
</combo_box>
|
||||||
|
<filter_editor label="Suchtext eingeben" name="inventory search editor"/>
|
||||||
|
<menu_button name="options_gear_btn" tool_tip="Zusätzliche Optionen anzeigen"/>
|
||||||
|
<button name="add_btn" tool_tip="Neues Objekt hinzufügen"/>
|
||||||
|
</layout_panel>
|
||||||
|
</layout_stack>
|
||||||
|
<panel name="default_inventory_panel">
|
||||||
<tab_container name="inventory filter tabs">
|
<tab_container name="inventory filter tabs">
|
||||||
<inventory_panel label="MEIN INVENTAR" name="All Items"/>
|
<inventory_panel label="MEIN INVENTAR" name="All Items"/>
|
||||||
<recent_inventory_panel label="AKTUELL" name="Recent Items"/>
|
<recent_inventory_panel label="AKTUELL" name="Recent Items"/>
|
||||||
<inventory_panel label="GETRAGEN" name="Worn Items"/>
|
<inventory_panel label="GETRAGEN" name="Worn Items"/>
|
||||||
</tab_container>
|
</tab_container>
|
||||||
<layout_stack name="bottom_panel">
|
</panel>
|
||||||
<layout_panel name="options_gear_btn_panel">
|
|
||||||
<menu_button name="options_gear_btn" tool_tip="Zusätzliche Optionen anzeigen"/>
|
|
||||||
</layout_panel>
|
|
||||||
<layout_panel name="add_btn_panel">
|
|
||||||
<button name="add_btn" tool_tip="Neues Objekt hinzufügen"/>
|
|
||||||
</layout_panel>
|
|
||||||
<layout_panel name="trash_btn_panel">
|
|
||||||
<dnd_button name="trash_btn" tool_tip="Auswahl löschen"/>
|
|
||||||
</layout_panel>
|
|
||||||
</layout_stack>
|
|
||||||
</panel>
|
</panel>
|
||||||
|
|
|
||||||
|
|
@ -564,6 +564,14 @@
|
||||||
<menu_item_separator
|
<menu_item_separator
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
name="Gesture Separator" />
|
name="Gesture Separator" />
|
||||||
|
<menu_item_call
|
||||||
|
label="Play"
|
||||||
|
layout="topleft"
|
||||||
|
name="PlayGesture">
|
||||||
|
<menu_item_call.on_click
|
||||||
|
function="Inventory.DoToSelected"
|
||||||
|
parameter="play" />
|
||||||
|
</menu_item_call>
|
||||||
<menu_item_call
|
<menu_item_call
|
||||||
label="Activate"
|
label="Activate"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,8 @@
|
||||||
top_pad="10"
|
top_pad="10"
|
||||||
left="2"
|
left="2"
|
||||||
right="-4"
|
right="-4"
|
||||||
orientation="horizontal">
|
orientation="horizontal"
|
||||||
|
name="top_stack">
|
||||||
<layout_panel
|
<layout_panel
|
||||||
border="false"
|
border="false"
|
||||||
bevel_style="in"
|
bevel_style="in"
|
||||||
|
|
@ -101,7 +102,8 @@
|
||||||
user_resize="false"
|
user_resize="false"
|
||||||
height="25"
|
height="25"
|
||||||
width="381"
|
width="381"
|
||||||
visible="true">
|
visible="true"
|
||||||
|
name="filter_layout_panel">
|
||||||
<combo_box
|
<combo_box
|
||||||
height="23"
|
height="23"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
|
|
|
||||||
|
|
@ -259,7 +259,7 @@
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
height="24"
|
height="24"
|
||||||
width="52"
|
width="53"
|
||||||
left_delta="2"
|
left_delta="2"
|
||||||
top_pad="1"
|
top_pad="1"
|
||||||
halign="right"
|
halign="right"
|
||||||
|
|
@ -271,7 +271,7 @@
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
enabled="false"
|
enabled="false"
|
||||||
top_delta="3"
|
top_delta="3"
|
||||||
left_pad="21"
|
left_pad="20"
|
||||||
height="20"
|
height="20"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
name="edt_invname_alt1"
|
name="edt_invname_alt1"
|
||||||
|
|
@ -305,7 +305,7 @@
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
height="24"
|
height="24"
|
||||||
width="52"
|
width="53"
|
||||||
left_delta="2"
|
left_delta="2"
|
||||||
top_pad="1"
|
top_pad="1"
|
||||||
halign="right"
|
halign="right"
|
||||||
|
|
@ -317,7 +317,7 @@
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
enabled="false"
|
enabled="false"
|
||||||
top_delta="3"
|
top_delta="3"
|
||||||
left_pad="21"
|
left_pad="20"
|
||||||
height="20"
|
height="20"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
name="edt_invname_alt2"
|
name="edt_invname_alt2"
|
||||||
|
|
@ -351,7 +351,7 @@
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
height="25"
|
height="25"
|
||||||
width="52"
|
width="53"
|
||||||
left_delta="2"
|
left_delta="2"
|
||||||
top_pad="1"
|
top_pad="1"
|
||||||
halign="right"
|
halign="right"
|
||||||
|
|
@ -363,7 +363,7 @@
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
enabled="false"
|
enabled="false"
|
||||||
top_delta="3"
|
top_delta="3"
|
||||||
left_pad="21"
|
left_pad="20"
|
||||||
height="20"
|
height="20"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
name="edt_invname_alt3"
|
name="edt_invname_alt3"
|
||||||
|
|
@ -460,7 +460,7 @@
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
height="12"
|
height="12"
|
||||||
width="52"
|
width="53"
|
||||||
left_delta="2"
|
left_delta="2"
|
||||||
top_pad="2"
|
top_pad="2"
|
||||||
halign="right"
|
halign="right"
|
||||||
|
|
@ -477,7 +477,7 @@
|
||||||
mouse_opaque="false"
|
mouse_opaque="false"
|
||||||
visible="true"
|
visible="true"
|
||||||
top_delta="-3"
|
top_delta="-3"
|
||||||
left_pad="2"/>
|
left_pad="1"/>
|
||||||
<line_editor
|
<line_editor
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
enabled="false"
|
enabled="false"
|
||||||
|
|
@ -516,7 +516,7 @@
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
height="12"
|
height="12"
|
||||||
width="52"
|
width="53"
|
||||||
left_delta="2"
|
left_delta="2"
|
||||||
top_pad="2"
|
top_pad="2"
|
||||||
halign="right"
|
halign="right"
|
||||||
|
|
@ -533,7 +533,7 @@
|
||||||
mouse_opaque="false"
|
mouse_opaque="false"
|
||||||
visible="true"
|
visible="true"
|
||||||
top_delta="-3"
|
top_delta="-3"
|
||||||
left_pad="2"/>
|
left_pad="1"/>
|
||||||
<line_editor
|
<line_editor
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
enabled="false"
|
enabled="false"
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue